Skip to content

fix: rename useSoonIngredients to soonIngredients for ESLint compliance - #257

Merged
henrygoldsmith07-wq merged 37 commits into
mainfrom
ci-fix
Aug 25, 2026
Merged

henrygoldsmith07-wq merged 37 commits into
mainfrom
ci-fix

Conversation

@henrygoldsmith07-wq

Copy link
Copy Markdown
Owner

The function is pure derivation logic, not a React hook. The use* prefix caused eslint-plugin-react-hooks to flag it as a rules-of-hooks violation, blocking CI at the lint stage. Also adds lint/typecheck scripts.

…rdening

- study.js: point-in-time replay scoring arise vs double-progression,
  linear-progression and flat baselines on identical transitions; paired
  win/loss stats, experience-level segmentation, sample gates
- prospective ledger: programme-stamped records, coverage report with
  deficits, substitution validation, deload decision audit; ledger now
  travels in backups and sync merges (resolved beats unresolved)
- MoreView: progression-evidence panel (coverage gaps, replay comparison,
  deload audit), consent-gated and lazily computed
- engine correctness: session-level plateau analysis, DST-safe streaks,
  upserted session completion, corrupt-store quarantine, guarded import
  comparators, telemetry identity pinning, honest planned-volume deltas
- equipment: onboarding collects dumbbell pairs and machine/cable stack
  increments; swap sheet honours movement preferences and blocks A-to-A
  cycles; rest timer +/- adjust; set-progress live region
- PWA: network-first navigations (stale HTML fix), reload-only-on-update,
  cross-tab store refresh; boot-crash fixes from in-flight refactor

benchmark:study runs the comparative harness over the synthetic corpus.
…-eval infra, public dashboard, plugin API v1

Core: truncate head/tail overlap fix, ANSI CSI final-byte range, prioritize
budget fill + Windows path refs, filter cap marker + Set perf, atomic stats
writes, structural O(n^2) removals, NDJSON single-parse.

Parsers: shared util (CRLF-safe split, uniform results), preservation fixes
(eslint file headers, cargo/go panics, maven/gradle stack frames, terraform
boxes, docker legacy/BuildKit errors, k8s pod rows), ruff multi-letter rule
codes, mvn/mvnw detection, next.js cap + index-based ordering.

CLI/tooling: child-flag passthrough (-- stops at first non-flag after
subcommand), Windows .cmd spawn retry + spawn-error diagnostics, stdout/newline
normalization, JWT/PEM/Google-key redaction, --exit-code for stdin, subcommand
--help, invalid-level exit 2, safe formatCost, config warning on unreadable
JSON, init gitignores .rtk/, plugin auto-load gated behind RTK_ALLOW_PLUGINS=1,
atomic stats save never clobbers wrapped exit code.

Product v0.4.0: --auto adaptive strength, machine-readable removals metadata,
PARSER_BEHAVIOR_VERSION semver, stable require('rtk/api') surface, slimmed
prepublishOnly, benchmarks out of published tarball.

Benchmarks: 33-tool live-capture catalog w/ provenance (sha256/host/command)
+ contributor ingest; parser fuzz harness (10 categories); external-benchmark
harness (independent-task manifests, seeded randomization, blind-judge
scrubbing, model tiers, framework adapters, Wilson/TOST reporting); public
dashboard generator + sanitized failure-corpus export. Tests: 137 -> 223.
Measured-only metrics over any window (default 28d):
weekly spend · waste value+weight · discarded items · takeaway
frequency (+honest-null spend) · plan adherence · leftover reuse ·
pantry accuracy incl depletion-vs-expiry deltas · list completion ·
planning-time median · unused ingredients · substitution frequency ·
savings confidence graded by trips x calendar coverage (seasonality
needs 8 weeks). beforeAfterOutcomes gates pre/post comparison on a
noise floor in both windows.
…arness

- loyalty/multibuy savings captured as data (informational, never arithmetic);
  coupons and refunds are payment events that adjust the net total
- weighed goods keep kg units and defer per-kg rates to the printed line total;
  quantity-prefixed single figures are computed and flagged low-confidence;
  substitutions flagged; independent shops named honestly
- per-line confidence + flags; whole-receipt receiptConfidence() grading
- receipt-benchmark corpus: Tesco/Sainsbury's/Aldi/Lidl/Morrisons/Asda/
  Waitrose/Co-op/independent seed cases covering every line type incl.
  unreadable rejection; per-field/per-feature/per-retailer metrics
…invariants

- split exercises into src/lib/data/exercises/* (one module per muscle group), re-exported from data.js
- add 38 hand-authored exercises with reciprocal substitution chains; close bodyweight dead ends (Back, Arms)
- lint:content now enforces progression enums, per-record cues, >=3 exercises per muscle and >=1 pure-bodyweight option per muscle
- declare explicit strategies in priors for new ids (endurance for time-based work, strength compounds, hypertrophy overrides where the name regex would misfire)
- tests 196 -> 201: library size, coverage floors, graph reciprocity, enum/cue validation
- cycle opt-in grants the optional tool at setup and from Goals
- reminder preset flows seed everything-mode (presets stay mode-scoped)
- waste-planner penalises fractional whole-unit splits so whole-unit plans win ties
- live-region audit: quiet paragraphs demoted; load-bearing announcements kept
- typography: skip-link font-size back to relative units
rankPlans() scores candidate plans on six transparent dimensions —
pantry coverage, fed-in waste score, expiry coverage, budget fit,
time fit, equipment fit (+ pack rounding) — each reported or null;
nulls drop out of the weighting instead of faking 100. Shares are
normalised to a 0-100 scale before blending; strict-equipment mode
benches infeasible plans with an explained reason. chooseOptimalPlan
returns the winner with per-candidate breakdown and plain-language
reasons.

optimiseBasket() consolidates the list by store, proposes single-trip
plans when one shop covers 80%+, suggests cheaper substitutes from
the household's own receipt history, and rounds quantities up to
whole packs.
buildPlan gains a multiObjective option (default false — zero behaviour
change until opted in). When on, chooseCandidate feeds the waste model's
per-candidate scores into rankPlans as the wasteScores seam, letting
pantry coverage and expiry urgency blend with pack-waste instead of
waste deciding alone. Legacy shape preserved: waste fields stay spread
at top level; optimiserScore/optimiserReasons added alongside.
chooseCandidate is now exported for direct testing.
The planner now blends pantry coverage and expiry urgency with the
pack-waste model for live generation, instead of waste deciding alone.
Time and equipment remain hard filters upstream; budget stays a
per-serving chip until a price table feeds the engine's budgetFit.
- ingredient-prepass lib: alias-aware normalisation, duplicate grouping,
  same-dimension quantity merging; mismatched dimensions never add
- offline-sync: immutable queue + ordered replay; multi-user merge with
  LWW, lexical tie-breaks, version-conflict resolution shapes
- measure properties: cross-dimension conversion refused, scaling
  linear/multiplicative, unit-price linearity in pack size
- recipe scaling properties: frozen proportions, multiplicativity,
  dimension survival, identity factor
- large-pantry perf floors: 600-row pantry, 8 candidate plans,
  150-item basket — generous CI-safe budgets
- axe-core Playwright sweep across primary tabs (e2e suite)
# Conflicts:
#	apps/arise/README.md
#	apps/arise/docs/IMPROVEMENTS.md
#	apps/arise/index.html
#	apps/arise/package.json
#	apps/arise/public/sw.js
#	apps/arise/src/App.jsx
#	apps/arise/src/components/AppShell.jsx
#	apps/arise/src/components/MoreView.jsx
#	apps/arise/src/components/Onboarding.jsx
#	apps/arise/src/components/ProgressView.jsx
#	apps/arise/src/components/SessionRunner.jsx
#	apps/arise/src/components/TrainView.jsx
#	apps/arise/src/lib/analytics.js
#	apps/arise/src/lib/attributes.js
#	apps/arise/src/lib/backtesting.js
#	apps/arise/src/lib/data.js
#	apps/arise/src/lib/export.js
#	apps/arise/src/lib/priors.js
#	apps/arise/src/lib/programming.js
#	apps/arise/src/lib/progression.js
#	apps/arise/src/lib/schedule.js
#	apps/arise/src/lib/sessionGenerator.js
#	apps/arise/src/lib/store.js
#	apps/arise/src/lib/substitutions.js
#	apps/arise/src/lib/sync.js
#	apps/arise/src/lib/telemetry.js
#	apps/arise/tests/data.test.js
#	apps/arise/tests/export.test.js
#	apps/arise/tests/plates.test.js
#	apps/arise/tests/programme-generator.test.js
#	apps/arise/tests/programming.test.js
#	apps/arise/tests/progression.test.js
#	apps/arise/tests/store.test.js
#	apps/emotion-tracker/README.md
#	apps/emotion-tracker/src/app/api/reflect/route.ts
#	apps/emotion-tracker/src/app/layout.tsx
#	apps/emotion-tracker/src/app/page.tsx
#	apps/emotion-tracker/src/components/ApiKeyBar.tsx
#	apps/emotion-tracker/src/components/HistoryView.tsx
#	apps/emotion-tracker/src/components/LongitudinalPanel.tsx
#	apps/emotion-tracker/src/components/NewEntryForm.tsx
#	apps/emotion-tracker/src/components/PatternsView.tsx
#	apps/emotion-tracker/src/components/PrivacyBar.tsx
#	apps/emotion-tracker/src/components/ReflectionSession.tsx
#	apps/emotion-tracker/src/components/SettingsView.tsx
#	apps/emotion-tracker/src/components/SummaryView.tsx
#	apps/emotion-tracker/src/components/Toast.tsx
#	apps/emotion-tracker/src/lib/adversarial.ts
#	apps/emotion-tracker/src/lib/corrections.ts
#	apps/emotion-tracker/src/lib/crypto.ts
#	apps/emotion-tracker/src/lib/evidenceReport.ts
#	apps/emotion-tracker/src/lib/gemini.ts
#	apps/emotion-tracker/src/lib/humanReview.ts
#	apps/emotion-tracker/src/lib/importExport.ts
#	apps/emotion-tracker/src/lib/longitudinal.test.ts
#	apps/emotion-tracker/src/lib/longitudinal.ts
#	apps/emotion-tracker/src/lib/memory.ts
#	apps/emotion-tracker/src/lib/observationVsInference.ts
#	apps/emotion-tracker/src/lib/outcomeEvidence.ts
#	apps/emotion-tracker/src/lib/outcomeStudy.ts
#	apps/emotion-tracker/src/lib/patternEvidence.ts
#	apps/emotion-tracker/src/lib/privacy.ts
#	apps/emotion-tracker/src/lib/privacyAudit.ts
#	apps/emotion-tracker/src/lib/pulse.ts
#	apps/emotion-tracker/src/lib/rateLimit.test.ts
#	apps/emotion-tracker/src/lib/rateLimit.ts
#	apps/emotion-tracker/src/lib/search.ts
#	apps/emotion-tracker/src/lib/useEntries.ts
#	apps/emotion-tracker/src/lib/useLocalStorage.ts
#	apps/emotion-tracker/src/lib/validation.ts
#	apps/food-shopping-os/src/components/DemoWalkthrough.jsx
#	apps/food-shopping-os/src/components/FamilyPanel.jsx
#	apps/food-shopping-os/src/components/LogCapture.jsx
#	apps/food-shopping-os/src/components/Onboarding.jsx
#	apps/food-shopping-os/src/components/PlanGenerator.jsx
#	apps/food-shopping-os/src/components/PrivacyPanel.jsx
#	apps/food-shopping-os/src/components/ProfileTab.jsx
#	apps/food-shopping-os/src/components/SmartFeaturesPanel.jsx
#	apps/food-shopping-os/src/le-studio.css
#	apps/food-shopping-os/src/lib/health-actions.js
#	apps/food-shopping-os/src/lib/planner.js
#	apps/food-shopping-os/src/lib/receipt.js
#	apps/food-shopping-os/src/lib/waste-planner.js
#	apps/food-shopping-os/tests/advanced-flows.test.jsx
#	apps/food-shopping-os/tests/advanced.test.js
#	apps/food-shopping-os/tests/reminder-flows.test.jsx
#	apps/rapport/package-lock.json
#	apps/rapport/src/ai/cache.ts
#	apps/rapport/src/ai/provider.ts
#	apps/rapport/src/ai/tasks.ts
#	apps/rapport/src/app/api/ai/route.ts
#	apps/rapport/src/app/coach/page.tsx
#	apps/rapport/src/app/evidence/page.tsx
#	apps/rapport/src/app/page.tsx
#	apps/rapport/src/app/practise/[scenarioId]/page.tsx
#	apps/rapport/src/app/progress/page.tsx
#	apps/rapport/src/app/settings/page.tsx
#	apps/rapport/src/components/reflection-form.tsx
#	apps/rapport/src/components/simulation-runner.tsx
#	apps/rapport/src/components/training-plan.tsx
#	apps/rapport/src/data/pulse-history.ts
#	apps/rapport/src/data/repository.ts
#	apps/rapport/src/domain/agreement.ts
#	apps/rapport/src/domain/analytics.ts
#	apps/rapport/src/domain/confidence.ts
#	apps/rapport/src/domain/donation.ts
#	apps/rapport/src/domain/evaluation.ts
#	apps/rapport/src/domain/events.ts
#	apps/rapport/src/domain/floor.ts
#	apps/rapport/src/domain/gaming.ts
#	apps/rapport/src/domain/interruption.ts
#	apps/rapport/src/domain/mastery.ts
#	apps/rapport/src/domain/progress.ts
#	apps/rapport/src/domain/recommender-benchmark.ts
#	apps/rapport/src/domain/scheduling.ts
#	apps/rapport/src/domain/simulator.ts
#	apps/rapport/src/domain/transfer.ts
#	apps/rapport/src/domain/types.ts
#	apps/rapport/src/domain/weekly-review.ts
#	apps/rapport/src/state/store.tsx
#	apps/rapport/tests/agreement.test.ts
#	apps/rapport/tests/analytics.test.ts
#	apps/rapport/tests/benchmark-persistence.test.ts
#	apps/rapport/tests/donation.test.ts
#	apps/rapport/tests/gaming.test.ts
#	apps/rapport/tests/persistence.test.ts
#	apps/rapport/tests/transfer.test.ts
#	apps/rtk/README.md
#	apps/rtk/benchmark/corpus/manifest.json
#	apps/rtk/package.json
#	apps/rtk/src/ansi.js
#	apps/rtk/src/cli.js
#	apps/rtk/src/config.js
#	apps/rtk/src/filter.js
#	apps/rtk/src/init.js
#	apps/rtk/src/parsers/cargo.js
#	apps/rtk/src/parsers/docker.js
#	apps/rtk/src/parsers/eslint.js
#	apps/rtk/src/parsers/gotest.js
#	apps/rtk/src/parsers/gradle.js
#	apps/rtk/src/parsers/index.js
#	apps/rtk/src/parsers/k8s.js
#	apps/rtk/src/parsers/maven.js
#	apps/rtk/src/parsers/next.js
#	apps/rtk/src/parsers/pytest.js
#	apps/rtk/src/parsers/ruff.js
#	apps/rtk/src/parsers/terraform.js
#	apps/rtk/src/plugins.js
#	apps/rtk/src/prioritize.js
#	apps/rtk/src/redact.js
#	apps/rtk/src/run.js
#	apps/rtk/src/stats.js
#	apps/rtk/src/structural.js
#	apps/rtk/src/truncate.js
- /api/ai prefers OpenRouter :free models when OPENROUTER_API_KEY is
  set (catalog-cached picker, preferred families first); free models
  bypass the monthly AI budget — rate-limit only as an abuse guard —
  and fall back to the paid relay when no free slot answers
- recipe favourite: solid red fill, white heart, ring-pulse on select
  alongside the existing pop animation
GLM 5.2 → Nemotron 3 Ultra → 3.5 Lightning → 3 Super → Gemma 4 31B →
26B A4B → Nano 30B A3B → Nano 12B VL → Nano 9B → LFM2.5 → North Mini
Code → Inkling Small → Inkling → Laguna → Dots3. Embeddings, rerankers,
TTS and safety classifiers are excluded from chat selection entirely.
freeChat walks the ranking on rate limits/outages and refuses after a
401 (a bad key never fixes itself on the next model).
activeProvider() prefers NVIDIA_API_KEY (integrate.api.nvidia.com,
no :free suffix filter) over OpenRouter. Ranking ladder now leads with
Ultra-tier Nemotron, then GLM/Lightning/Super/size tiers — matching the
NIM catalog. Same failover, non-chat exclusion and budget bypass.
createLedgerEntry freezes the prediction-time record (recommendation
reasons/cost/time, purchase, execution, leftovers, waste, feedback).
amendEntry produces new frozen revisions with a full audit trail — the
prediction is never rewritten.

ledgerInsights() answers the questions that matter from settled entries:
did Forq save money (vs prediction and optional per-meal baseline), do
pantry-first meals get cooked more often (cook-rate lift), which
recommendation reasons waste less (wasted-portion % by reason), does
batch cooking save time (mins/serving), are cook-time estimates
accurate (mean abs error + bias language), what predicts a skipped meal
(reason lifts vs overall skip rate). Every answer states its sample
size; nulls mean no evidence, never zero.

Live loop wired: PlanGenerator opens an entry per recommended meal with
predicted cost/time and required-vs-owned item counts; markMealPlanOutcome
mirrors cooked/substituted/skipped onto the matching open entry.
openLedgerEntry/amendLedgerEntry exposed for the remaining capture
points (purchases, leftover portions, feedback).
weeklyOutcomeSnapshot(state) computes one Monday-start week entirely
on-device: food waste £/household (north star, with per-member view),
planned meals cooked + adherence %, shopping-list→purchased accuracy,
pantry accuracy (shared confidence classifier), weekly spend, unused
purchased ingredients, leftover utilisation (ledger portions first,
lifecycle events as fallback), cooking-time prediction error, recipe
satisfaction from your own ratings.

outcomeTrends() builds oldest-first weekly snapshots with week-on-week
deltas. Pure functions of state — nothing uploads, nothing persists
outside the household's own store.
…s Forq

Four strategies face the same situation: random eligible (seeded PRNG),
cheapest, pantry-first, and the live multi-objective engine. Every pick
is measured on one vector: cost, pantry utilisation %, expiring-stock
use, predicted time, repetition, constraint satisfaction.

WHY THIS WON explanations in household language: 'uses Spinach
expiring tomorrow · you already own 100% of the ingredients · normally
takes you ~32 minutes · household rated it 4.3/5 · beats Fancy fish by
£2.70 a serving' — real minutes from cooking history, ratings from the
household's own scores.

strategyOutcomes() closes the loop from ledger entries recorded with
source 'experiment:<X>': cook rate, waste portions and actual spend per
strategy.

Engine matching upgrades included: stemmed keys (onions ≈ onion) and
quantity-less ingredients treated as presence checks in coverage.
createForqDb opens forq-db v1 with the full target schema (households,
profiles, pantryItems, shoppingItems, purchaseRecords, receipts,
mealPlans, mealOutcomes, foodLogs, leftovers, wasteRecords, prices,
customRecipes, preferences, measurements, experiments, syncMetadata).
Promise wrappers handle request-vs-promise returns; completion is a
durability gate with a 25 ms floor so test fakes can never hang callers.

store.persist now dual-writes the Outcome Ledger into mealOutcomes via
syncCollection — per-row diffing (new/changed/deleted) instead of
serialising the whole app. localStorage keeps the preference shell
during migration; remaining collections move over incrementally.
tests/helpers/fake-idb.js provides a deterministic synchronous IDB.
… quick check

pantryTruth(row) replaces have/don't-have with: estimated amount,
confidence %, last-confirmed date and an expected range that widens as
certainty falls. Confidence is an evidence ledger with visible drivers:
time since confirmation decays it; uncounted consumption pulls it down;
weighed cooking deductions and receipt evidence build it up; manual
entry and approximate quantities are penalised honestly (clamped 15-97,
never fake certainty).

quickCheck(rows) turns the least-trusted rows into typed one-tap
questions ranked by lowest confidence: amount unknown → 'roughly how
much?', possibly gone → 'still have it?', decent estimate → 'still
around 620 ml?'. Confident rows are never asked.
…t gating

Corpus grows to 23 labelled cases across Tesco/Aldi/Lidl/Sainsbury's/
Morrisons/Asda/Waitrose/Co-op/independent/Ocado, now covering long
receipts, partial photos, folds, poor lighting, loyalty+coupon
combinations and substitutions — plus REAL_CORPUS for transcribed real
receipts as collected.

Reporting matches the headline table: store recognition, date
extraction, product line detection, product matching, price
extraction, basket total — with per-condition/per-retailer breakdowns,
discount/coupon/refund accuracy and unreadable-receipt rejection rate.

The honesty gate: evidenceTier() refuses to call anything evidence-grade
without 300+ REAL receipts across 8+ retailers at 95%+ per field (basket
≥99.5%). A 100%-accurate synthetic corpus still reports
'useful-convenience' — exactly as it should.
…ates

scoreExternalPrice() turns external price provenance into HIGH/MEDIUM/LOW
with visible reasons: exact barcode match (+18), observed yesterday (+20),
3+ supporting observations (+14), same retailer (+8) — vs stale (−26),
name-only match (0), single observation, unverified scrape (−6).
Clamped 0-100; never fake certainty.

estimateBasket(items, priceResolver) returns { estimate, low, high,
coveragePct, level } where ranges widen with uncertainty: ±4% HIGH,
±8% MEDIUM, ±15% LOW. Level is bottlenecked by coverage — 60% coverage
is never HIGH.

estimateBaskets(items, { shops, observedByKey }) prices the list at every
known store: receipt prices first (strongest), community observations as
fallback (weaker). Sorted by highest coverage first. Returns sources
breakdown so the UI can say '2 receipts · 1 observed'.

PriceCompare renders a new 'Estimated baskets, with uncertainty' section
showing £X–£Y ranges per store with confidence pills and honest coverage
percentages, plus per-item HIGH/MEDIUM/LOW pills on observed rows.
…y tested

youth-ai.js implements the pipeline: request → age/profile policy →
context filter → AI call → response validation → safety check.

Server-side: deriveYouthAiPolicy re-derives youth mode from age/profile
(the client's claim is never trusted). filterContextForYouth strips
weight, deficit, BMI, fasting, measurements and other restricted keys
from the context before the model sees them. promptViolatesYouthPolicy
refuses requests that try to bypass safety. youthAiGate composes the
full pre-call gate; validateAiResponseForYouth sanitises the response
on the way back (sentence-level removal of restricted advice with an
audit trail).

Wired into /api/ai: both OpenRouter free-tier and paid relay paths go
through the same gate. Youth responses carry hadViolations + violations
count so the UI can flag what was cleaned.

Adversarial tests: 7 blocked prompt patterns (deficit, keto, fasting,
BMI comparison, alcohol, earn-calories-by-exercise, cut-to-target kg),
4 clean prompts that must pass through, 5 restricted-response patterns,
nested body-key stripping, adult pass-through, and end-to-end gate
pipeline tests.
…planning tests

closed-loop: hardcoded DAY='2026-08-03' became stale (>21d), triggering
time-decay from definite→probable. Now uses dayStamp() for a relative
date that never goes stale.

planning: getByText found multiple 'Teriyaki Salmon Bowls' elements
because the multi-objective optimiser can render the same recipe name
in both the dialog and the page behind it. Switched to getAllByText
with [0] indexing.
…e Ledger

Every entry now carries a recommendationId that survives the entire
lifecycle (plan -> shop -> cook -> leftovers -> outcome), separate from
the entry id.

predictionMetrics(entries) computes ten core metrics from settled
entries: cost and cooking-time prediction errors (mean absolute +
bias), plan adherence, unnecessary purchases, pantry utilisation,
leftover utilisation, food waste (GBP and grams), recommendation
acceptance, recommendation success, and repeat satisfaction. Each
metric states its sample size; null means no evidence.
FEATURE_MATRIX in src/data/feature-matrix.js is the source of truth:
every user-facing capability, its entry point, core/optional status,
toolId, and primary action. 6 core + 8 optional tools documented.

reachability.spec.js walks the real UI: fresh onboarding → visible
tabs/buttons → optional tool enablement → primary action verification.
This catches the AddToolsPanel bug class: feature exists, tests pass,
UI works in isolation, but no real user can find it.

Two invariants: (1) every optional tool must be discoverable in the
tools UI, (2) every core feature must render content without any tools
enabled. Runs with Playwright against a live server.
outcomeDashboard(state) composes weeklyOutcomeSnapshot, outcomeTrends,
predictionMetrics and householdOutcomes into a single on-device
dashboard: north star (food waste £/household/week with trend), four
primary percentages (plan adherence, shopping-list accuracy, pantry
utilisation, leftover reuse), secondary metrics (weekly spend, unused
ingredients, cook-time MAE, prediction errors, recipe satisfaction),
avoided waste from consumed-before-expiry items, predicted-vs-actual
spend delta against budget. Pure function of state; never uploaded.
…rpus

Corpus grows to 25 cases adding duplicate-product and strange-
abbreviation coverage. All six headline metrics meet their targets:
retailer recognition 100% (≥99), date extraction 100% (≥98), line
detection 100% (≥97), price extraction 100% (≥98), basket total 100%
(≥99), rejection accuracy 100%. Enforced in CI via test assertions.

Duplicate handling: parser preserves both instances of a duplicated
product rather than deduplicating them — the household bought two, the
pantry should show two.
enforceYouthInvariants() runs inside hydrate(), which is the single
chokepoint for ALL state entering the app: localStorage boot, backup
restore, and cloud sync pull. It uses youthPolicy().on (not just age)
so child household profiles trigger it too.

Strips deficit goals, weekly calorie debt, custom target modes, and
fasting data for under-18s regardless of how those values arrived.
Adversarial tests cover backup restore smuggling, cloud sync pull, and
child-profile activation — plus adult pass-through.
…ok rules

The function is pure derivation logic, not a React hook. The use*
prefix caused eslint-plugin-react-hooks to flag it as a rules-of-hooks
violation, blocking CI at the lint stage. Also adds lint and typecheck
scripts to package.json.

@github-advanced-security github-advanced-security AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CodeQL found more than 20 potential problems in the proposed changes. Check the Files changed tab for more details.

@henrygoldsmith07-wq
henrygoldsmith07-wq merged commit c036363 into main Aug 25, 2026
2 of 7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants