# Nutrition-day contract → UI → mapping-queue audit

Use this reference for read-only source audits of a nutrition-day API that feeds both a central day view and a separate nutrition/mapping workspace.

## Required trace

1. Record the exact repository revision and clean/dirty state before inspection; make no writes when the request is explicitly read-only.
2. Trace both day surfaces separately:
   - the central `/day/{date}` payload, which may contain only a summary;
   - the detailed `/nutrition/day/{date}` payload used for meals, products, macros, nutrients, mapping and histamine fields.
3. Map route allowlisting, scalar/date/query parsing, future-date policy, row limits, read-only connection, no-store response handling, provider SQL, response construction and any exact-shape validator.
4. Write the current payload structurally, including alternate no-data/error shapes. Do not infer one stable shape when documented and empty responses differ.
5. Inventory schemas by table and field role: daily summary, meal summary, item, item nutrient, classification, review queue, action log/provenance and personal-tolerance stores. Do not inspect productive rows for a source-only audit.
6. Trace the day controller’s request lifecycle: timeout, active abort controller, request-generation token, stale checks before and after async rendering, future-day behavior and retry state.
7. Trace the mapping workspace end to end: embedded bundle or queue API → opaque queue identity → form → server validation → private queue → worker exact-match/collision checks → affected-day recomputation.
8. Map fixtures and unit/browser tests, then provide an exact `file:line` implementation plan.

## Safe public surface

Prefer an exact allowlist. A detailed day response may expose only:

- bounded date/timezone/status and version/definition metadata;
- nullable macro totals with fixed semantic units;
- nutrients selected by exact key **and** exact unit;
- bounded meal enums and item counts;
- bounded product display text, amount and allowlisted amount unit;
- nullable classification score/status/confidence;
- an opaque mapping queue key only when the server proves one unique open queue match.

Never expose source item/product IDs, raw JSON, hashes, filesystem paths, URLs, normalized queue names, DB IDs, provenance notes, personal-tolerance notes, or secret/provider metadata. For reports requested without PHI, show structural fields and key/unit inventories only—no productive values, product names, brands, aliases, notes, or raw examples.

## Queue identity and deep-link rule

- Derive the opaque queue key server-side from the queue’s canonical normalized identity.
- Do not hash a displayed product name in the browser.
- Attach a queue key to a day item only after an exact, unique, open queue match; ambiguity must fail closed.
- Deep links should contain only an exact view plus opaque key, e.g. `?view=nutrition&mapping=<fixed-format-key>`.
- Validate duplicate/unknown query parameters and the key's exact format on initial load and every `popstate`; malformed or unknown keys must fail closed and be removed/rejected rather than preserved as inert URL text.
- Use one shared route resolver for click, initial load, reload, Back, and Forward. Re-rendering the Nutrition view is not enough: after every route restoration, open exactly the matching card, mark it selected, scroll it into view, and move focus to its `<summary>` (or equivalent accessible control).
- Clear prior selection markers before resolving a new key. Browser evidence must assert URL, selected-card count, open state, and `document.activeElement` after click, reload, Back, and Forward—not only immediately after the originating click.
- Keep product/alias/canonical text out of history and URLs.
- Keep the key derivation in one shared implementation; duplicated hash functions are a drift risk.

## Exact queue-link audit probe

Do not infer a valid queue link merely because the day item and queue use the same deterministic hash helper. Trace the join predicate itself. The day provider must query the current queue state and emit a key only when exactly one row matches the canonical normalized identity **and** that row is open. A preloaded `set` of open normalized names is acceptable only after verifying that the deployed schema enforces uniqueness on that canonical identity; otherwise query/count candidates explicitly and require `COUNT(*) = 1` (or equivalent). Review fixture and migration/legacy schemas rather than inferring production uniqueness from one synthetic database.

Add three disposable synthetic counterexamples:

1. an unmapped day item with no queue row → `mapping_queue_key` is null and no UI action appears;
2. an unmapped item whose queue row is closed/resolved → key is null;
3. duplicate/collision-equivalent open candidates → fail closed rather than selecting one.

A positive browser test that clicks an existing fixture’s “review mapping” button proves only the happy path. It does not prove absent, closed, or ambiguous identities are safe. Also review whether a deterministic unkeyed digest can be dictionary-guessed from a URL; if keys cross trust boundaries or persist in browser history, prefer a server-bound/keyed opaque identifier.

## Async day-view pattern

When the central day endpoint and nutrition-detail endpoint are separate:

1. Increment a day request generation.
2. Abort the previous detail request.
3. Fetch the central day payload.
4. Skip detail fetch for unsupported/future/no-data states when endpoint policy differs.
5. Fetch detail with an external abort signal plus bounded timeout.
6. Check generation after every awaited fetch and again before DOM commit.
7. Treat superseded abort as silent; show timeout/API failures in the selected day only.
8. Render null as unknown/not documented—never zero.

A generation token without abort prevents stale commits but still wastes work; abort without a generation token is insufficient because completion and rendering can race.

## Common risks to call out

- Detailed documented and no-data responses have different shapes.
- Hand-built endpoint payload has no exact contract validator.
- Summary and detail endpoints disagree on future-date policy.
- Unknown source meal is emitted outside the display sort enum and crashes sorting.
- Nutrient fallback runs only when a table is absent, not when the selected day has no nutrient rows.
- Per-item mapping deep-link identity is absent, tempting unsafe client-side derivation.
- Embedded bundle queue and live mapping-queue API drift.
- Lexical meal ordering differs from display order.
- Macro units are implicit in field names while nutrient units are explicit.
- Existing mapped classifications must not be overwritten by a new queue action.

## Verification matrix

Unit/API tests should cover exact documented and empty shapes, nutrient key+unit allowlisting, null-vs-zero, item/row bounds, unknown meal normalization, unique opaque queue linkage, collision fail-closed behavior, forbidden-field scans, query rejection and future dates.

Browser tests should cover complete/incomplete/no-data days, full meal/product rendering, two rapid day selections with delayed out-of-order detail responses, abort and stale protection, timeout/retry, future-day behavior, mapping deep-link reload/back/forward/focus, duplicate or malformed parameters, mobile overflow/touch targets, and absence of names or sensitive identifiers in URLs. Include a no-open-queue fixture and assert that no “review mapping” action or dead deep link is rendered; key-format correctness alone is not linkage evidence.
