Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 16 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,16 +94,21 @@ Resolution is deterministic and ordered:
1. exact phrase in the user's alias table, pointing to an exact local cache name;
2. exact match in the user's `food_cache`;
3. token-overlap search in that cache;
4. strict Open Food Facts free-text search;
5. a safe USDA FoodData Central generic proxy, when a setup key or
4. an exact Open Food Facts product only when the input explicitly matches its brand/SKU;
5. a safe USDA FoodData Central Foundation/SR Legacy generic proxy, when a setup key or
`NOMNOM_USDA_KEY` is configured;
6. actionable JSON error—never a guessed food.
6. a strictly matching Open Food Facts generic proxy, including a labelled branded candidate only
when its returned product name/type is safe for the unbranded input;
7. actionable JSON error—never a guessed food.

Open Food Facts candidates need complete normalized query-token coverage from the returned product
name/brand, a matching food type/category, a source identity/barcode, and complete positive finite
kcal, protein, fat, and carbs. A branded source identity is cached as `exact_product`; an unbranded
source identity is truthfully cached as `generic_proxy` and remains subject to the configured proxy
policy. Rejected results are neither cached nor logged.
name, a matching food type/category, a source identity/barcode, and complete positive finite kcal,
protein, fat, and carbs. An exact product requires a user-supplied barcode, an explicitly matched
brand/SKU, or an exact local pin/alias. Candidate confidence never establishes exact identity. A
safe branded OFF candidate used for unbranded text is always a `generic_proxy`, retains its source,
brand, barcode, and explicit assumption, and remains subject to the configured proxy policy. An
unsafe candidate returns `food_needs_source`. Rejected results are neither cached nor logged, and a
generic-proxy cache entry cannot satisfy a later branded query.

Successful API results are cached in the user's database, so the same food can resolve locally
later. Existing cache records, logs, recipes, and aliases are preserved when v0.4 opens the
Expand Down Expand Up @@ -154,7 +159,7 @@ confidence floor. Weak matches return `usda_low_confidence` with candidate alter
never cached.

The default generic policy is `allow_for_unbranded`. A USDA result becomes a generic proxy only
when it is an unbranded Foundation, SR Legacy, or Survey (FNDDS) record with an FDC id and every
when it is an unbranded Foundation or SR Legacy record with an FDC id and every
normalized query token is covered by its name. Brand/SKU-like or unmatched input and branded USDA
records return `exact_resolution_required` without cache or log writes. Accepted proxies expose
`resolution_mode=generic_proxy`, `source=usda`, the FDC `source_id`, `provenance=usda`, confidence,
Expand Down Expand Up @@ -241,7 +246,8 @@ required; modifiers may express a fraction, size, or explicit per-piece mass.

An agent translates the user's language into this contract before invoking nomnom. Translation may
choose a canonical food name already known to the user cache or an explicit user alias. Nutrition
resolution is a separate deterministic step: alias → local cache → Open Food Facts → USDA → error.
resolution is a separate deterministic step: alias → local cache → exact intent or generic
provider proxy → error.
The agent must not translate by inventing nutrition values or silently substituting another food.

## Quantities, sizes, and dishes
Expand Down Expand Up @@ -286,7 +292,7 @@ an `error` object and exit with status 2. Important codes include:
- `usda_low_confidence`: inspect the FDC candidate/data type/category and retry more specifically;
no near match was cached.
- `usda_invalid_nutrition`: every USDA candidate lacked one or more complete positive core values.
- `generic_proxy_confirmation_required`: show the named USDA candidate and ask before changing the
- `generic_proxy_confirmation_required`: show the named provider candidate and ask before changing the
configured policy; nothing was cached or logged.
- `exact_resolution_required`: request the barcode or a package photo for source-backed capture.
- `invalid_barcode` / `barcode_not_found` / `barcode_nutrition_incomplete`: correct the barcode or
Expand Down
56 changes: 56 additions & 0 deletions docs/plans.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,61 @@
# Plans

## Issue #29 Source
- Task: Ensure unbranded foods resolve only to truthful generic proxies, never arbitrary branded exact products.
- Canonical input: GitHub issue #29 and the user's strict TDD, smoke, commit, and no-push requirements.
- Repo context: resolver intent, OFF/USDA selection, local cache/aliases, CLI/API JSON, docs, and agent skill.
- Last updated: 2026-07-21

## Issue #29 Assumptions
- Exact identity is established only by user barcode capture, an explicit brand/SKU match in the query, or an exact local pin/alias; provider confidence alone cannot establish exact identity.
- USDA generic lookup should request and rank Foundation/SR Legacy records ahead of any branded data, without embedding nutrition facts or food aliases in production.
- A branded OFF source may represent an unbranded query only as an explicitly assumed generic proxy after strict name/category/core-nutrient checks.

## Issue #29 Milestone Order
| ID | Title | Depends on | Status |
| --- | --- | --- | --- |
| M25 | Freeze identity, provider, cache, policy, and literal contracts | M24 | [x] |
| M26 | Implement intent-aware generic proxy resolution | M25 | [x] |
| M27 | Document identity semantics and verify release gates | M26 | [x] |

## M25. Freeze issue #29 contracts `[x]`
### Goal
- Focused tests reproduce arbitrary branded OFF exact matches, realistic USDA generic-versus-branded ranking, exact user intent, cache isolation, all policies, and the literal acceptance list.

### Validation
```sh
PYTHONPATH=. pytest -q tests/test_foods.py tests/test_usda.py tests/test_off.py
```

### Stop-and-Fix Rule
- Record the expected RED failures before changing resolver/provider production behavior.

## M26. Implement intent-aware generic proxy resolution `[x]`
### Goal
- Unbranded input returns only a clearly sourced safe generic proxy or structured `food_needs_source`; exact user intent remains exact-only and cache entries cannot cross identity boundaries.

### Validation
```sh
PYTHONPATH=. pytest -q tests/test_foods.py tests/test_usda.py tests/test_off.py tests/test_cli.py
```

### Stop-and-Fix Rule
- Any arbitrary branded `exact_product`, generic fallback for branded/barcode intent, unsafe cache write, or missing provenance blocks documentation work.

## M27. Document identity semantics and verify release gates `[x]`
### Goal
- README and agent skill distinguish exact products from generic proxies, and all tests, lint, literal isolated smoke, diff audit, and local commit requirements pass.

### Validation
```sh
PYTHONPATH=. pytest -q
ruff check .
git diff --check
```

### Stop-and-Fix Rule
- Do not commit until the full suite, Ruff, clean temp-data smoke, and scoped diff audit pass.

## Issue #27 Source
- Task: Add safe backdated meal logging and local-date-scoped stats.
- Canonical input: GitHub issue #27 and the user's strict TDD, smoke, commit, and no-push requirements.
Expand Down
15 changes: 12 additions & 3 deletions docs/status.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
# Status

## Snapshot
- Current phase: issue #27 implemented and independently verified
- Current phase: issue #29 implemented and independently verified
- Plan file: `docs/plans.md`
- Status: green
- Last updated: 2026-07-21

## Done
- Completed issue #29 with intent-aware exact identity, USDA Foundation/SR Legacy preference, strict OFF proxy type matching, branded-proxy provenance, and cache intent isolation.
- Recorded RED runs for 16 primary regressions plus two cache edges and missing-category evidence, then passed 114 focused tests, 204 full tests, and Ruff.
- Ran all six literal translated inputs against mocked providers in a fresh temporary data directory; all six were explicit `generic_proxy` rows with source, brand, barcode, and assumption.
- Completed issue #27 with strict local-date parsing, deterministic local-noon backdating for both log forms, additive timestamp/date JSON, exact date stats, no-write failures, and preserved no-date/today/week behavior.
- Documented `--date` for remembered meals and that humans/agents must never manipulate SQLite directly.
- Passed 189 tests, Ruff, diff checks, checkout import proof, and the clean disposable installed-CLI literal smoke.
Expand All @@ -24,12 +27,15 @@
- Passed 177 tests, Ruff, checkout-import guard, shell syntax, and the disposable checkout-built installer/provider-stub smoke.

## In Progress
- No implementation work pending; the feature is ready for the issue #27 pull request.
- No implementation work pending; issue #29 is verified and committed locally.

## Next
- Push the scoped commit and open the requested pull request.
- Do not push or open a pull request unless the user requests it separately.

## Decisions Made
- Issue #29: resolution mode follows user identity intent plus source identity, never candidate confidence alone.
- Issue #29: generic USDA searches send the documented `dataType` array for Foundation and SR Legacy and rank eligible non-branded provenance ahead of branded payload rows.
- Issue #29: a branded OFF candidate needs product-name token coverage plus category/type evidence to serve only as a labelled generic proxy.
- Issue #27: reuse the existing offset-aware `logged_at` field; no migration is needed.
- Issue #27: explicit dates become local noon and stats use `[local midnight, next local midnight)` boundaries.
- Issue #23: a healthy no-key install is complete base coverage; USDA is only an optional enhancement for broader no-photo generic/raw-food resolution.
Expand Down Expand Up @@ -81,6 +87,8 @@ ruff check .
| 2026-07-21 | M21–M22 | CLI date tests and `nomnomcli/cli.py` | focused RED then GREEN pytest | 7 expected failures; then 7 pass | M23 |
| 2026-07-21 | M23 | database/CLI date stats tests and query implementation | focused RED then GREEN pytest | 2 expected failures; then 8 pass | M24 |
| 2026-07-21 | M24 | README, skill, changed code/tests, disposable installed checkout | focused/full pytest; Ruff; diff check; literal temp-DB smoke | 40/189 pass; clean; smoke 150 kcal | local commit |
| 2026-07-21 | M25 | resolver/provider regression tests | focused pytest | RED: 16 primary failures; 2 cache failures; 1 category-evidence failure | M26 |
| 2026-07-21 | M26–M27 | resolver, USDA, docs, skill, tests | `pytest -q`; `ruff check .`; temp-data mocked-provider smoke | 114 focused; 204 full; clean; 6/6 generic proxies | local commit |

## Smoke / Demo Checklist
- [x] Fresh temp DB: help/version, capture label, alias, log, and invalid structured capture error.
Expand All @@ -100,3 +108,4 @@ ruff check .
- [x] Setup reports `base_ready/base` without USDA and `connected/enhanced` with the local USDA stub.
- [x] Safe no-key miss returns `food_needs_source` with photo/barcode/label/cache options and optional USDA.
- [x] Literal parsed/direct `2026-07-20` logs persist local noon and date stats return only that local day.
- [x] Issue #29 literal six-item temp-data smoke returns only explicit generic proxies with audited OFF candidate identity.
22 changes: 22 additions & 0 deletions docs/test-plan.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,27 @@
# Test Plan

## Issue #29 Validation
- In scope: resolver intent, OFF safe generic proxy semantics, USDA request/ranking provenance, exact barcode/brand/pin/alias paths, cache isolation, generic policy writes, CLI/API JSON, and literal translated inputs.
- Fixtures: mocked OFF and USDA payloads only, temporary SQLite user databases, no live traffic or personal data.
- Critical negative cases: HSN soy isolate, arbitrary cream-cheese barcode, Menguy's peanuts, branded USDA outranking attempts, unsafe branded queries, and rejected-candidate no-write behavior.
- Literal smoke inputs: `milk 3% 625 ml`, `soy protein isolate 30 g`, `chicken pastrami 150 g`, `whole wheat bread 140 g`, `cream cheese 40 g`, and `peanuts 55 g`; every success must be `generic_proxy`, and unsafe resolution must be structured.

### Issue #29 Acceptance Gates
- [x] Focused tests observed RED before production changes.
- [x] Focused resolver/provider/CLI/docs tests pass (114 tests).
- [x] Full `PYTHONPATH=. pytest -q` passes (204 tests).
- [x] `ruff check .` and `git diff --check` pass.
- [x] Fresh temp-data-dir literal mocked-provider smoke proves modes/provenance and no unsafe writes.
- [x] Scoped conventional commit exists; worktree is clean; nothing is pushed.

### Issue #29 Command Matrix
```sh
PYTHONPATH=. pytest -q tests/test_foods.py tests/test_usda.py tests/test_off.py tests/test_cli.py
PYTHONPATH=. pytest -q
ruff check .
git diff --check
```

## Issue #27 Source
- Task: Validate safe backdated logging and local-date-scoped stats.
- Plan file: `docs/plans.md`
Expand Down
Loading
Loading