Groq menu highlights API#15

open4
main
t3code/groq-highlights
opened5d agobyKKainoa Newton
View on GitHubTwo-way sync enabledPR last imported 5d ago

Adds an optional highlights object to today's hall menus. It holds up to five offerings per meal, picked in the background by openai/gpt-oss-120b on Groq (low reasoning, strict JSON). The upcoming cross-hall Overview tab in 5CDining is meant to be built on it. Not deployed.

The first commit is the Sept 29 – Oct 4 WIP snapshot, kept as history. The three commits after it converge, harden and document it.

Contract (full spec in docs/menu-highlights.md)

  • Highlights are on by default on /v1/menus and /v1/menus/:hall.
  • ?highlights=false returns the original response shape and skips the extra KV read. Any value other than true/false, or a repeated parameter, returns 400 invalid_highlights.
  • highlights.status is one of ready, pending, unavailable (with reason: attempts_exhausted, daily_budget_exhausted, input_too_large or generation_failed), disabled or not_applicable. Every status except ready has meals: [].
  • On ready, each meal has mealIndex, name, items and groups.
    • A group is { title, kind: "single" | "variants", sourceIds, sourceItems }.
    • sourceItems are the original school rows, unchanged, plus id and station.
    • Groups have no aggregate dietary, allergen or nutrition fields.
    • items is a flat convenience list: the first source of each group.
  • IDs such as m1:s0:i2 are positions within one menu version, not dish IDs. A menu edit invalidates the old picks (status returns to pending).

What the app should render

  • Show groups[].title in the order given.
  • For variants, list the choices from sourceItems, and take dietary badges from each source item, never from the group.
  • Link back to the full menu with mealIndex and the source id.
  • For any status other than ready, just show the full menu. Hide highlights UI when the status is disabled.

Configuring Groq

sh
1npx wrangler secret put GROQ_API_KEY # enables highlights
2# optional kill switch: set the variable HIGHLIGHTS_ENABLED="false"

Locally, put GROQ_API_KEY=... in the gitignored .dev.vars. Without a key, every hall reports disabled. No public request can trigger generation.

How it works

  • After the hourly snapshot is saved, the collector's Durable Object alarm sends one Groq request per hall-day (all meals at once), at most one request per alarm, spaced at least 65 s apart.
  • Attempts are reserved in DO storage before network I/O.
  • Limits are 3 attempts per menu version and 21 per California day. Retry-After pauses all halls.
  • Results are reused until the menu content hash changes. The hash covers food, model, prompt version and prompt text.
  • Public requests never call Groq. The projection cost was measured at 0.35 ms for all seven halls.

Changes in this PR, after the snapshot

Converged on one pipeline.

  • Kept the v8 "minimal" contract and removed the v5 flat policy, the full grouped/program-component contract, both grouped prompt strategies, the inventory pilot, and their eval scripts and tests.
  • The 9 highlight modules became 3: highlight-selection.ts, highlights.ts and highlight-jobs.ts.
  • One validator now checks both model output and saved records on read.

Validation.

  • Every meal key appears exactly once.
  • Each source belongs to its meal, with no shared ownership. Limits are ≤5 groups per meal and 1–4 sources per group.
  • Single titles always come from the menu.
  • Combined titles:
    • must be plain text;
    • may use only words found in every source name, apart from connectors and the words that distinguish variants (protein, cooking style);
    • must share a dish word.
  • So dietary or safety wording such as "peanut-free", "keto", "vegan" or "GF" must be in every source.
  • A bad combined title splits into source-named singles instead of discarding the hall-day.

Robustness.

  • Retryable failures (429, 5xx, timeout, malformed JSON, failed validation, json_validate_failed) stay pending until attempts run out.
  • 413 maps to input_too_large.
  • Logs carry a short reason, never provider bodies.
  • Stale menus that still have food remain eligible. Menus with no items are not_applicable.
  • Saved results that stop validating are requeued within budget.

Docs. The contract doc is rewritten. Nine overlapping experiment docs are collapsed into docs/highlights-history.md; the originals are archived in docs/reports/.

An independent review (Opus subagent) found five issues, all fixed with regression tests: dietary bypasses in combined titles, halls stuck pending while their menu was stale, dedupe on truncated titles, a doc mismatch for daily_budget_exhausted, and validator changes leaving halls unavailable.

Verification

  • npm run check passes (typecheck plus 136 tests), as do npm run test:runtime (real workerd, SQLite DO, actual alarm delivery) and npm run build.
  • Live wrangler dev with the real Groq key, Oct 23: checked the full contract, opt-out parity, future-date and invalid_highlights behavior. All 68 served sources matched their original menu rows field for field, and a repeated scheduled run made zero extra calls.
  • First live run exposed two real problems:
    • Hoch hit json_validate_failed twice before succeeding. That error was previously terminal.
    • Frary lost the whole hall to one rejected combined title. That led to the split fallback.

Known imperfect (please read)

Selection quality is decent but not curated. Audit of the final prompt (docs/reports/highlight-audit-2026-10-23.md): 68 picks, 62 credible, 5 weak, 1 clear side error. The earlier v9 prompt made 4 clear errors (3 pastries and a station label).

  • Ranking among real mains is uneven. Generic grill and pizza items sometimes beat named programs: McConnell missed its ceviche bar and pad thai bowl.
  • Programs and their components can overlap. McConnell's Oasis program was picked alongside its own steak. Nothing in code knows which rows belong to a program.
  • Protein variants are rarely grouped. Both available pairs (Hoch bao, Frary banh mi) were split, with only one side selected. Combined groups were 0 of 68.
  • Weak staples or sides still appear occasionally. Examples: hard-boiled egg, breakfast sausage, plain quinoa. Thin breakfasts can now be under-filled: Collins got one pick.
  • Labels are assistant judgments, not human-approved, and cover a single day.
  • Frank's Pomona source failed to fetch on every date during testing. That's unrelated to highlights and not investigated here.
  • Inference is bounded, not exactly-once. A crash after Groq accepts a request can cause one repeat call.

Untested here: production deployment, multi-day behavior, and real account token limits under menu churn.

🤖 Generated with Claude Code


Note

Medium Risk Adds optional third-party inference, extra KV reads per uncached menu request, and background job state in the collector DO; misconfiguration or validation bugs could affect API shape or stall highlights without blocking base menus when disabled or opted out.

Overview Ships optional AI menu highlights for today's halls: up to five ranked offerings per meal, generated in the background via Groq (gpt-oss-120b) and served from KV—public routes never call Groq.

API: Menu responses gain a highlights object (ready / pending / unavailable / disabled / not_applicable) with per-meal groups (single or variants) and unchanged sourceItems. ?highlights=false restores the old shape and skips the extra KV read; invalid values return invalid_highlights.

Runtime: After hourly collection, MenuCollector schedules DO alarms that run highlight-jobs (paced calls, 21/day and 3/version caps, version hashing, retries). Selection logic lives in highlight-selection.ts with strict validation, combined-title rules, and split-to-singles fallback. npm run highlights:audit and expanded runtime tests cover the path.

Docs: README and new guides (menu-highlights.md, highlights-history.md) document the contract, ops, and known quality limits; experiment reports are archived under docs/reports/.

Reviewed by Cursor Bugbot for commit 8ce5a54b0aefa690c99d7dc8e134dfa3bbae0a78. Bugbot is set up for automated code reviews on this repo. Configure here.


Summary by cubic

Adds optional highlights to today's hall menus, generated by openai/gpt-oss-120b on Groq in the background and served from KV. Highlights are on by default; ?highlights=false restores the original response shape and skips the extra read. Selection is decent but not curated (62 of 68 live picks credible); ranking among mains, program overlap, variant grouping, and exactly-once inference remain imperfect. Not deployed.

Contract

  • highlights.status is ready, pending, unavailable (with reason), disabled, or not_applicable; only ready carries meals.
  • ready returns up to five groups per meal: title, kind (single or variants), sourceIds, and sourceItems (original rows plus id and station).
  • IDs like m1:s0:i2 are positions within one menu version, not dish IDs; a menu edit returns the status to pending.
  • One validator covers model output and saved records: every meal key exactly once, ≤5 groups per meal, 1–4 sources per group, and plain-text combined titles that may reuse only words present in every source name.
  • Invalid combined titles split into source-named singles instead of failing the whole hall-day.

How it runs

  • After the hourly snapshot, the collector's Durable Object alarm sends one request per hall-day, spaced 65+ s apart, with attempts reserved before any network I/O.
  • Limits are 3 attempts per menu version and 21 per California day; Retry-After pauses all halls.
  • Results are reused until the menu content hash changes; retryable failures (429, 5xx, bad JSON, failed validation) stay pending until attempts run out.
  • Public reads never call Groq (projection cost 0.35 ms for seven halls). Set the GROQ_API_KEY secret to enable; HIGHLIGHTS_ENABLED="false" is the kill switch.
  • Nine experiment modules and docs collapsed into three source files plus docs/menu-highlights.md and docs/highlights-history.md; 136 tests pass under npm run check along with runtime tests.

Written for commit 8ce5a54b0aefa690c99d7dc8e134dfa3bbae0a78. Summary will update on new commits.

View guided diff Turn on auto-fix

Checks

1 failing · 5 successful · 1 neutral
Some checks failed

Failing1

8ce5a54

Added 4 commits

  • Converge highlights on one validated selection pipeline42f5b91
  • WIP: Groq menu highlights API (uncommitted work from Sept 29 to Oct 4)5fa1220
  • Document the highlights contract and archive experiment reports8ce5a54
  • Tighten highlight prompt after the October 23 audit9818787
Synced from GitHub

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: ASSERTIVE
  • Plan: Advanced
  • Run ID: e2b80636-4c98-4068-b634-64141112d337

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Synced from GitHub

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: 1a684ca7-9d47-4023-a8b7-b4c13687f698)

Synced from GitHub

RetriggerView in GreptileConfidence Score: 4/5

[Medium impact] The change reaches the menu API's optional highlights workflow and its external Groq calls.

The PR appears safe to merge, with non-blocking improvements needed in logs, retry pauses and audit reuse.

Findings

  1. P2 Provider text enters logs ▶
  2. P2 Date-based pauses are ignored ▶
  3. P2 Audits mix old and new ▶
Fix with agent prompt
markdown
1### Issue 1
2src/highlight-jobs.ts:138-140
3`runHighlightJob` logs raw `error.message`, including errors from both `JSON.parse` calls in `generateHighlights`. Those errors can contain fragments of the provider body or model output, despite the promise that logs never contain provider bodies. The 200-character limit does not remove that text. Replace parse errors with fixed messages and test the logged output for malformed responses.
4
5### Issue 2
6src/highlights.ts:72-74
7If Groq sends an HTTP-date in `Retry-After`, `Number(...)` returns `NaN` and `retryAfterMs` falls back to 65 seconds. A longer pause is then ignored for all halls, which can waste the limited attempts on early retries. Support both numeric and HTTP-date values, and test a future date.
8
9### Issue 3
10scripts/capture-highlight-audit.ts:24
11The script resumes an existing file without checking its saved `promptVersion` against the current selector. After a prompt or model change, completed halls keep old picks while unfinished halls use the new code. This makes the audit unreliable for comparing changes. Reject mismatched runs using a saved model and prompt hash, or require a separate output file.
12
13---
14
15For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Summary

Adds background Groq highlights for today's menus.

  • The collector generates and saves menu highlights in the background.
  • Each highlight stays tied to the exact meal rows it represents.
  • The menu API can return saved highlights alongside each hall's menu.
  • Contributors can capture a highlight audit for later review.
  • The experiment trail sits apart from the current highlights guide.

Acknowledged by notkainoa, and not reported again: uneven ranking, program/component overlap, rare variant grouping, occasional sides and staples, under-filled breakfasts, assistant-authored quality labels, Frank's unrelated source failures, and possible repeat inference after a crash.

Diagram

mermaid
1%%{init: {'theme': 'neutral'}}%%
2flowchart TD
3 A[Hourly collection] --> B[Save menu snapshot]
4 B --> C[Schedule collector alarm]
5 C --> D[Check versions and budgets]
6 D --> E[Reserve one attempt]
7 E --> F[Call Groq]
8 F --> G[Check picks and titles]
9 G --> H[Save job state and publish picks]
10 H --> C
11 I[Public menu request] --> J[Read menu snapshot]
12 J --> K{Highlights requested?}
13 K -->|No| L[Return original menu]
14 K -->|Yes| M[Read saved picks]
15 M --> N[Check date, menu version and sources]
16 N --> O[Return menu with highlights status]

Reviews (1) · Last reviewed commit: "Document the highlights contract and arc..." · Reviewed by Greptile

src/highlights.tslines 72–74Open thread
……@@ -0,0 +1,153 @@
71+
72+function retryAfterMs(response: Response): number {
73+ const seconds = Number(response.headers.get('retry-after') ?? NaN);
74+ return Number.isFinite(seconds) ? Math.max(CALL_INTERVAL_MS, seconds * 1_000) : CALL_INTERVAL_MS;
Ggreptile-appsBoton lines 72–745d ago

P2 Date-based pauses are ignored

If Groq sends an HTTP-date in Retry-After, Number(...) returns NaN and retryAfterMs falls back to 65 seconds. A longer pause is then ignored for all halls, which can waste the limited attempts on early retries. Support both numeric and HTTP-date values, and test a future date.

Prompt To Fix With AI
markdown
1This is a comment left during a code review.
2Path: src/highlights.ts
3Line: 72-74
4
5Comment:
6**Date-based pauses are ignored**
7
8If Groq sends an HTTP-date in `Retry-After`, `Number(...)` returns `NaN` and `retryAfterMs` falls back to 65 seconds. A longer pause is then ignored for all halls, which can waste the limited attempts on early retries. Support both numeric and HTTP-date values, and test a future date.
9
10---
11
12For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.
src/highlight-jobs.tslines 138–140Open thread
……@@ -0,0 +1,143 @@
137+ else delete job.record.reason;
138+ // Our own messages carry no secrets or provider bodies; bound them anyway.
139+ console.warn(JSON.stringify({ event: 'highlights_failed', hall: menu.hall, date, attempt: job.attempts, willRetry: !job.finished,
140+ reason: error instanceof Error ? `${error.name}: ${error.message}`.slice(0, 200) : 'Unknown failure' }));
Ggreptile-appsBoton lines 138–1405d ago

P2 Provider text enters logs

runHighlightJob logs raw error.message, including errors from both JSON.parse calls in generateHighlights. Those errors can contain fragments of the provider body or model output, despite the promise that logs never contain provider bodies. The 200-character limit does not remove that text. Replace parse errors with fixed messages and test the logged output for malformed responses.

Prompt To Fix With AI
markdown
1This is a comment left during a code review.
2Path: src/highlight-jobs.ts
3Line: 138-140
4
5Comment:
6**Provider text enters logs**
7
8`runHighlightJob` logs raw `error.message`, including errors from both `JSON.parse` calls in `generateHighlights`. Those errors can contain fragments of the provider body or model output, despite the promise that logs never contain provider bodies. The 200-character limit does not remove that text. Replace parse errors with fixed messages and test the logged output for malformed responses.
9
10---
11
12For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.
……@@ -0,0 +1,87 @@
21+ if (error.code !== 'ENOENT') throw error;
22+ return null;
23+});
24+const run = existing ?? { date, capturedAt: new Date().toISOString(), promptVersion: PROMPT_VERSION, halls: [], attempts: 0, nextCallAt: 0 };
Ggreptile-appsBoton line 245d ago

P2 Audits mix old and new

The script resumes an existing file without checking its saved promptVersion against the current selector. After a prompt or model change, completed halls keep old picks while unfinished halls use the new code. This makes the audit unreliable for comparing changes. Reject mismatched runs using a saved model and prompt hash, or require a separate output file.

Prompt To Fix With AI
markdown
1This is a comment left during a code review.
2Path: scripts/capture-highlight-audit.ts
3Line: 24
4
5Comment:
6**Audits mix old and new**
7
8The script resumes an existing file without checking its saved `promptVersion` against the current selector. After a prompt or model change, completed halls keep old picks while unfinished halls use the new code. This makes the audit unreliable for comparing changes. Reject mismatched runs using a saved model and prompt hash, or require a separate output file.
9
10---
11
12For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.
Ccubic-dev-aiBotreviewed · 37 inline threads5d ago
Synced from GitHub

37 issues found across 31 files

Prompt for AI agents (unresolved issues)
text
1
2Check if these issues are valid — if so, understand the root cause of each and fix them. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. If appropriate, use sub-agents to investigate and fix each issue separately.
3
4
5<file name="src/http.ts">
6
7<violation number="1" location="src/http.ts:199">
8P3: This comment is now inaccurate. With highlights enabled for today, the handler also reads `highlights:v1`, so responses use two KV reads, not one. Update the comment to name both reads.</violation>
9</file>
10
11<file name="src/highlights.ts">
12
13<violation number="1" location="src/highlights.ts:73">
14P2: `Retry-After` also permits an HTTP-date, but `Number(...)` treats that value as invalid and retries after only 65 seconds, potentially before the requested cooldown. Parse both date and delay-seconds forms before applying the minimum interval.</violation>
15
16<violation number="2" location="src/highlights.ts:152">
17P3: This catch-all returns `unavailable` without logging, so a saved record that fails validation or a bug in this block is invisible in logs. Log the error (without the record contents) before returning the empty result.</violation>
18</file>
19
20<file name="docs/menu-highlights.md">
21
22<violation number="1" location="docs/menu-highlights.md:147">
23P2: A menu edit can still return the cached old menu and highlights for up to 60 seconds. Say old picks are invalidated on the next uncached response.</violation>
24</file>
25
26<file name="docs/reports/highlight-audit-2026-10-23.md">
27
28<violation number="1" location="docs/reports/highlight-audit-2026-10-23.md:15">
29P3: The Frary account conflicts with the shipped code. A bad combined title splits in the same call and is never retried, so three failed calls cannot be explained by it. Either state that run 1 used an earlier pipeline, or correct the cause and say whether Frary ended ready or exhausted.</violation>
30</file>
31
32<file name="docs/reports/group-screen-review-sol.md">
33
34<violation number="1" location="docs/reports/group-screen-review-sol.md:3">
35P2: The evidence this review rests on lives only in the gitignored `.wrangler/group-eval/` directory, so it is absent from the repo. Commit the screening snapshot, rubric, and prompt, or state the exact archive location, so the 12-result analysis and prompt hash can be verified.</violation>
36</file>
37
38<file name="test/highlights.test.ts">
39
40<violation number="1" location="test/highlights.test.ts:146">
41P2: This `assert.fail` inside the fetcher is swallowed by `runHighlightJob`'s catch, so the test cannot fail when Groq is called. Count the calls and assert zero instead.</violation>
42</file>
43
44<file name="docs/reports/highlight-selections.md">
45
46<violation number="1" location="docs/reports/highlight-selections.md:30">
47P3: The backticks in "General Tso`s" and "Alberto`s" pair up inside the same table cell and render as an inline code span, which garbles that row. Escape them as \` so the source name renders literally, or use the apostrophe if that matches the source name.</violation>
48</file>
49
50<file name="README.md">
51
52<violation number="1" location="README.md:60">
53P3: The ready `highlights` example omits `provider`, `model`, `generatedAt`, and `menuVersion`, which every ready response includes. Add them so clients see the full public shape.</violation>
54</file>
55
56<file name="docs/reports/highlight-pick-audit.md">
57
58<violation number="1" location="docs/reports/highlight-pick-audit.md:45">
59P2: The build command here points to `docs/reports/build-highlight-pick-audit.py`, which is not in this PR. Commit the builder or remove the instruction, since the report's counts and validation claims depend on it.</violation>
60
61<violation number="2" location="docs/reports/highlight-pick-audit.md:47">
62P2: The audit's source data lives only in the gitignored `.wrangler/` directory and is not committed. Reviewers cannot re-derive the table counts or pick verdicts from the repo. Commit a sanitized copy of the inputs or name where the data is archived.</violation>
63</file>
64
65<file name="docs/reports/group-review-glm-flash.md">
66
67<violation number="1" location="docs/reports/group-review-glm-flash.md:174">
68P3: The methodology claim is self-contradictory. §3 was written after reading the v5 Oct 4 outputs (§1, and the collins 'endorse the actual v5 run' note), so it cannot be a blind gold set. Either drop the 'before seeing any output' claim or build the holdout labels from the menus alone before reading model results.</violation>
69</file>
70
71<file name="docs/reports/highlight-grouping.md">
72
73<violation number="1" location="docs/reports/highlight-grouping.md:66">
74P2: The reproduce commands point to scripts that are not in the repo, so none of them can run as written. Either add the scripts in this PR or correct the commands to the scripts that exist.</violation>
75</file>
76
77<file name="docs/reports/highlight-comparison.md">
78
79<violation number="1" location="docs/reports/highlight-comparison.md:33">
80P2: The reproduction command points to `docs/reports/build-highlight-comparison.py`, which is not in the repository, so the documented graphic cannot be rebuilt. Commit the builder or correct the command to the script that actually exists.</violation>
81</file>
82
83<file name="docs/reports/menu-highlights-live-test.md">
84
85<violation number="1" location="docs/reports/menu-highlights-live-test.md:17">
86P3: The Total row does not match the per-hall rows. Completion tokens should be 2,346 and total tokens should be 25,190. Fix both values so the table reconciles, since this report is the usage record for the quota.</violation>
87
88<violation number="2" location="docs/reports/menu-highlights-live-test.md:29">
89P3: The test count here (114) differs from the 136 tests stated in the PR description. Confirm the count from the final run and update whichever figure is stale.</violation>
90</file>
91
92<file name="src/highlight-selection.ts">
93
94<violation number="1" location="src/highlight-selection.ts:124">
95P2: `sourceTitle` throws when a selected item name strips to nothing, and the single-source branch in `validateSelections` does not catch it. One such item rejects the whole meal's highlights. Fall back to a non-empty display title or skip that source instead of throwing.</violation>
96
97<violation number="2" location="src/highlight-selection.ts:224">
98P3: A multi-source group whose sources all have the same name is accepted, which the prompt forbids ("Represent a meal program once"). Skip or collapse variant groups whose normalized source names are identical, using the same name key as the single-source dedupe.</violation>
99</file>
100
101<file name="docs/reports/highlight-refinement.md">
102
103<violation number="1" location="docs/reports/highlight-refinement.md:42">
104P3: The test count here (117) conflicts with the 136 tests stated in the PR description. Update this figure to match the actual test run output.</violation>
105
106<violation number="2" location="docs/reports/highlight-refinement.md:51">
107P2: The reproduce commands reference `npm run highlights:eval`, which is not defined in package.json, and no evaluator or scorer script exists under `scripts/`. Either add the script and its fixtures or remove this section, since the claim that the checkout reproduces the results is currently false.</violation>
108</file>
109
110<file name="docs/reports/group-blind-review-sol.md">
111
112<violation number="1" location="docs/reports/group-blind-review-sol.md:3">
113P3: The review cites `blind-development.json` as its source, but that file is not in the repo. Commit the input or the rubric it was checked against, or state where it lives, so the labels and IDs can be reproduced.</violation>
114</file>
115
116<file name="docs/reports/group-review-muse.md">
117
118<violation number="1" location="docs/reports/group-review-muse.md:13">
119P2: The text says malott/m1 should yield 4 highlights, but the proposed JSON gives it 5. Either the count or the malott/m1 list is wrong. Reconcile them before this report is used as a reference.</violation>
120
121<violation number="2" location="docs/reports/group-review-muse.md:69">
122P3: The Italian Pitzer Sunday members omit m1:s0:i3, which §1 identifies as a pasta in the same bar. Either add it or document why it is excluded, since members are meant to be all merged variants or components.</violation>
123
124<violation number="3" location="docs/reports/group-review-muse.md:153">
125P2: This report tunes on the Oct 4 holdout it declares off-limits. The §2–§3 Oct 4 'proposed' groups and policy fixes came from inspecting Oct 4 outputs, so Oct 4 can no longer serve as a holdout for these changes. Move these Oct 4 labels to a dev-only fixture or mark the holdout as spent.</violation>
126</file>
127
128<file name="test/highlight-selection.test.ts">
129
130<violation number="1" location="test/highlight-selection.test.ts:114">
131P2: `assert.throws` here has no error matcher, so any exception passes, including a TypeError from a regression. Pass a matcher per case, such as `/Invalid model output|Meal keys|Invalid meal groups|Invalid group|belong|more than one group/`, or pair each input with its expected message.</violation>
132</file>
133
134<file name="src/collector.ts">
135
136<violation number="1" location="src/collector.ts:37">
137P2: If `setAlarm` fails here, `refresh()` rejects even though the snapshot is already saved, so the scheduled run is reported as failed. Highlight scheduling is best-effort, so log the failure instead of propagating it.</violation>
138</file>
139
140<file name="scripts/test-runtime.ts">
141
142<violation number="1" location="scripts/test-runtime.ts:220">
143P3: Use the exported `HIGHLIGHTS_KEY` instead of the literal. Import it with `import { HIGHLIGHTS_KEY } from '../src/highlights';`. If the key changes, this poll would otherwise read a stale key and fail only with the generic 10 s timeout.</violation>
144
145<violation number="2" location="scripts/test-runtime.ts:237">
146P2: The repeat `/test/tick` check cannot exercise result reuse. The next alarm is about 65 s out and `scheduleHighlights` does nothing while an alarm exists, so `groqCalls === 1` passes even if reuse broke. Remove this block and the "saved results reused" claim in the final log, since reuse is covered by `test/highlights.test.ts`.</violation>
147</file>
148
149<file name="docs/highlights-history.md">
150
151<violation number="1" location="docs/highlights-history.md:19">
152P2: This overstates the model comparison. The archived holdout shows Qwen with higher coverage than 20B (35/51 vs 27/51), and GPT-OSS 120B was never run on the Oct 5 holdout because of quota. Limit the claim to the Oct 4 development run and note the untested holdout. Qwen's validation gap is one response in one run, so 'more often' overstates it.</violation>
153
154<violation number="2" location="docs/highlights-history.md:26">
155P3: The claim that v8 'did no worse' without station-guide hints is not backed by any archived comparison. Either cite the run that measured it or state that it was not measured.</violation>
156</file>
157
158<file name="scripts/capture-highlight-audit.ts">
159
160<violation number="1" location="scripts/capture-highlight-audit.ts:16">
161P3: This check accepts impossible calendar dates such as `2026-99-99`, which are then sent to the providers and stored as an audit. Use the shared `isValidDate` validator.</violation>
162
163<violation number="2" location="scripts/capture-highlight-audit.ts:24">
164P2: Resuming an audit file keeps halls captured under an older `PROMPT_VERSION`, and nothing compares the saved version. After a prompt bump the output mixes old and new picks. Fail fast when the saved version differs.</violation>
165
166<violation number="3" location="scripts/capture-highlight-audit.ts:24">
167P2: A resumed audit never checks its saved `promptVersion`, so it can print old selections as results from the current pipeline after a version bump. Reject a mismatched saved version and require deleting the file to restart.</violation>
168
169<violation number="4" location="scripts/capture-highlight-audit.ts:31">
170P2: Resume skips any hall that has an entry, even when generation failed or the fetch failed. A 429, 5xx, timeout, or fetch error is never retried on rerun. Re-run halls that have no saved selections instead.</violation>
171
172<violation number="5" location="scripts/capture-highlight-audit.ts:43">
173P3: This runs Groq for ok menus that have no items. Production skips them through `hasItems`. Add the same check so the audit does not spend an attempt on an empty hall-day. This needs `hasItems` imported from `../src/highlight-selection`.</violation>
174
175<violation number="6" location="scripts/capture-highlight-audit.ts:44">
176P3: The audit allows two attempts per hall, but production allows `MAX_VERSION_ATTEMPTS` (3) in src/highlight-jobs.ts. Import that constant and use it so the audit matches the shipped retry policy. Adjust the 14-attempt cap to match.</violation>
177
178<violation number="7" location="scripts/capture-highlight-audit.ts:84">
179P3: Variant groups print item names without their stations, which breaks the header's promise to print each pick next to its station. Add the station to each variant item.</violation>
180</file>

Reply with feedback, questions, or to request a fix.

View guided diff | Turn on auto-fix | Re-trigger cubic

docs/menu-highlights.mdline 147Open thread
……@@ -0,0 +1,188 @@
144+
145+- Highlights cover **today only** (America/Los_Angeles). Future dates are always `not_applicable`.
146+- The hourly collector refreshes menus, then schedules generation for halls whose menu content changed. The first picks of the day usually arrive within about ten minutes of the first hourly collection. Halls are generated one at a time, at least 65 seconds apart.
147+- A content hash covers the hall, date, every meal, station and item field, the model, the prompt version, and the prompt text. Any change to food, labels or ordering hides old picks immediately. Freshness timestamps and meal hours do not count as changes.
Ccubic-dev-aiBoton line 1475d ago

P2: A menu edit can still return the cached old menu and highlights for up to 60 seconds. Say old picks are invalidated on the next uncached response.

Prompt for AI agents
text
1Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At docs/menu-highlights.md, line 147:
2
3<comment>A menu edit can still return the cached old menu and highlights for up to 60 seconds. Say old picks are invalidated on the next uncached response.</comment>
4
5<file context>
6@@ -0,0 +1,188 @@
7+
8+- Highlights cover **today only** (America/Los_Angeles). Future dates are always `not_applicable`.
9+- The hourly collector refreshes menus, then schedules generation for halls whose menu content changed. The first picks of the day usually arrive within about ten minutes of the first hourly collection. Halls are generated one at a time, at least 65 seconds apart.
10+- A content hash covers the hall, date, every meal, station and item field, the model, the prompt version, and the prompt text. Any change to food, labels or ordering hides old picks immediately. Freshness timestamps and meal hours do not count as changes.
11+- Responses are edge-cached for up to 60 seconds, so a newly ready result can take that long to appear.
12+
13</file context>
suggestion
1- A content hash covers the hall, date, every meal, station and item field, the model, the prompt version, and the prompt text. Any change to food, labels or ordering invalidates old picks on the next uncached response; cached responses can persist for up to 60 seconds. Freshness timestamps and meal hours do not count as changes.
src/highlights.tslines 73–74Open thread
……@@ -0,0 +1,153 @@
71+
72+function retryAfterMs(response: Response): number {
73+ const seconds = Number(response.headers.get('retry-after') ?? NaN);
74+ return Number.isFinite(seconds) ? Math.max(CALL_INTERVAL_MS, seconds * 1_000) : CALL_INTERVAL_MS;
Ccubic-dev-aiBoton lines 73–745d ago

P2: Retry-After also permits an HTTP-date, but Number(...) treats that value as invalid and retries after only 65 seconds, potentially before the requested cooldown. Parse both date and delay-seconds forms before applying the minimum interval.

Prompt for AI agents
text
1Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At src/highlights.ts, line 73:
2
3<comment>`Retry-After` also permits an HTTP-date, but `Number(...)` treats that value as invalid and retries after only 65 seconds, potentially before the requested cooldown. Parse both date and delay-seconds forms before applying the minimum interval.</comment>
4
5<file context>
6@@ -0,0 +1,153 @@
7+}
8+
9+function retryAfterMs(response: Response): number {
10+ const seconds = Number(response.headers.get('retry-after') ?? NaN);
11+ return Number.isFinite(seconds) ? Math.max(CALL_INTERVAL_MS, seconds * 1_000) : CALL_INTERVAL_MS;
12+}
13</file context>
suggestion
1 const value = response.headers.get('retry-after');
2 const seconds = Number(value);
3 const delayMs = value === null ? NaN : Number.isFinite(seconds) ? seconds * 1_000 : Date.parse(value) - Date.now();
4 return Number.isFinite(delayMs) ? Math.max(CALL_INTERVAL_MS, delayMs) : CALL_INTERVAL_MS;
……@@ -0,0 +1,49 @@
1+# Group screening review, GPT-6.1 Sol
2+
3+This is an assistant-provisional semantic review, not a human-approved benchmark. I inspected the full Oct 4 source menus, the current grouped policy, and exactly 12 completed results from `.wrangler/group-eval/screen-r2.json`. That snapshot contains Hoch and Malott only, each with three models and two strategies, one run per condition. I did not inspect holdout results or call Groq. The recorded prompt hash is `9be6051c4a3c888d8850eea88e7c171846371a7db7501aa8d15163ad89663442`.
Ccubic-dev-aiBoton line 35d ago

P2: The evidence this review rests on lives only in the gitignored .wrangler/group-eval/ directory, so it is absent from the repo. Commit the screening snapshot, rubric, and prompt, or state the exact archive location, so the 12-result analysis and prompt hash can be verified.

Prompt for AI agents
text
1Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At docs/reports/group-screen-review-sol.md, line 3:
2
3<comment>The evidence this review rests on lives only in the gitignored `.wrangler/group-eval/` directory, so it is absent from the repo. Commit the screening snapshot, rubric, and prompt, or state the exact archive location, so the 12-result analysis and prompt hash can be verified.</comment>
4
5<file context>
6@@ -0,0 +1,49 @@
7+# Group screening review, GPT-6.1 Sol
8+
9+This is an assistant-provisional semantic review, not a human-approved benchmark. I inspected the full Oct 4 source menus, the current grouped policy, and exactly 12 completed results from `.wrangler/group-eval/screen-r2.json`. That snapshot contains Hoch and Malott only, each with three models and two strategies, one run per condition. I did not inspect holdout results or call Groq. The recorded prompt hash is `9be6051c4a3c888d8850eea88e7c171846371a7db7501aa8d15163ad89663442`.
10+
11+## Reliability in this snapshot
12</file context>
src/highlight-selection.tsline 124Open thread
……@@ -0,0 +1,238 @@
121+ .replace(/(?:https?:\/\/|www\.)\S+/giu, ' ')
122+ .replace(/\b[\p{L}\p{N}-]+\.[a-z]{2,}(?:\/\S*)?/giu, ' ')
123+ .replace(/[<>\p{C}\p{Zl}\p{Zp}]/gu, ' ').replace(/\s+/gu, ' ').trim();
124+ if (!plain) throw new Error('Empty source title');
Ccubic-dev-aiBoton line 1245d ago

P2: sourceTitle throws when a selected item name strips to nothing, and the single-source branch in validateSelections does not catch it. One such item rejects the whole meal's highlights. Fall back to a non-empty display title or skip that source instead of throwing.

Prompt for AI agents
text
1Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At src/highlight-selection.ts, line 124:
2
3<comment>`sourceTitle` throws when a selected item name strips to nothing, and the single-source branch in `validateSelections` does not catch it. One such item rejects the whole meal's highlights. Fall back to a non-empty display title or skip that source instead of throwing.</comment>
4
5<file context>
6@@ -0,0 +1,238 @@
7+ .replace(/(?:https?:\/\/|www\.)\S+/giu, ' ')
8+ .replace(/\b[\p{L}\p{N}-]+\.[a-z]{2,}(?:\/\S*)?/giu, ' ')
9+ .replace(/[<>\p{C}\p{Zl}\p{Zp}]/gu, ' ').replace(/\s+/gu, ' ').trim();
10+ if (!plain) throw new Error('Empty source title');
11+ const chars = [...plain];
12+ if (chars.length <= MAX_TITLE_LENGTH) return plain;
13</file context>
……@@ -0,0 +1,51 @@
44+
45+Build it with `rtk proxy python3 docs/reports/build-highlight-pick-audit.py`. It writes the standalone page and detailed audit JSON into `.wrangler/highlight-comparison/`. The builder validates selected item membership, counts, limits and all alternative dish references against the exact menu records.
46+
47+Sources are `.wrangler/groq-live-results-oct2.json`, `.wrangler/groq-live-results.json`, `.wrangler/highlight-eval/1791086353782-live-2026-10-02.json` and the fresh `.wrangler/day-audit/2026-10-04.json`. The new capture script is `scripts/capture-highlight-audit.ts`; repeating it resumes saved results rather than generating a fresh result again.
Ccubic-dev-aiBoton line 475d ago

P2: The audit's source data lives only in the gitignored .wrangler/ directory and is not committed. Reviewers cannot re-derive the table counts or pick verdicts from the repo. Commit a sanitized copy of the inputs or name where the data is archived.

Prompt for AI agents
text
1Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At docs/reports/highlight-pick-audit.md, line 47:
2
3<comment>The audit's source data lives only in the gitignored `.wrangler/` directory and is not committed. Reviewers cannot re-derive the table counts or pick verdicts from the repo. Commit a sanitized copy of the inputs or name where the data is archived.</comment>
4
5<file context>
6@@ -0,0 +1,51 @@
7+
8+Build it with `rtk proxy python3 docs/reports/build-highlight-pick-audit.py`. It writes the standalone page and detailed audit JSON into `.wrangler/highlight-comparison/`. The builder validates selected item membership, counts, limits and all alternative dish references against the exact menu records.
9+
10+Sources are `.wrangler/groq-live-results-oct2.json`, `.wrangler/groq-live-results.json`, `.wrangler/highlight-eval/1791086353782-live-2026-10-02.json` and the fresh `.wrangler/day-audit/2026-10-04.json`. The new capture script is `scripts/capture-highlight-audit.ts`; repeating it resumes saved results rather than generating a fresh result again.
11+
12+TypeScript checks passed. Desktop and 390px mobile layouts had no horizontal overflow or browser console errors. Original-side filtering, fresh-pick filtering, full menus, missing saved selections and other-main references were checked in the collaborative browser.
13</file context>
……@@ -0,0 +1,75 @@
63+Run commands through `rtk`, as required by this workspace:
64+
65+```sh
66+rtk proxy npx tsx scripts/capture-group-holdout.ts 2026-10-05
Ccubic-dev-aiBoton line 665d ago

P2: The reproduce commands point to scripts that are not in the repo, so none of them can run as written. Either add the scripts in this PR or correct the commands to the scripts that exist.

Prompt for AI agents
text
1Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At docs/reports/highlight-grouping.md, line 66:
2
3<comment>The reproduce commands point to scripts that are not in the repo, so none of them can run as written. Either add the scripts in this PR or correct the commands to the scripts that exist.</comment>
4
5<file context>
6@@ -0,0 +1,75 @@
7+Run commands through `rtk`, as required by this workspace:
8+
9+```sh
10+rtk proxy npx tsx scripts/capture-group-holdout.ts 2026-10-05
11+rtk proxy npx tsx scripts/grouped-highlight-eval.ts repeat
12+rtk proxy npx tsx scripts/grouped-highlight-eval.ts holdout
13</file context>
……@@ -0,0 +1,51 @@
42+
43+The AgentDrop report now includes all four sets, every pick's verdict and reason, a full saved-menu view, and other credible mains in the same meal. It replaces the earlier checklist-only graphic on the same private site.
44+
45+Build it with `rtk proxy python3 docs/reports/build-highlight-pick-audit.py`. It writes the standalone page and detailed audit JSON into `.wrangler/highlight-comparison/`. The builder validates selected item membership, counts, limits and all alternative dish references against the exact menu records.
Ccubic-dev-aiBoton line 455d ago

P2: The build command here points to docs/reports/build-highlight-pick-audit.py, which is not in this PR. Commit the builder or remove the instruction, since the report's counts and validation claims depend on it.

Prompt for AI agents
text
1Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At docs/reports/highlight-pick-audit.md, line 45:
2
3<comment>The build command here points to `docs/reports/build-highlight-pick-audit.py`, which is not in this PR. Commit the builder or remove the instruction, since the report's counts and validation claims depend on it.</comment>
4
5<file context>
6@@ -0,0 +1,51 @@
7+
8+The AgentDrop report now includes all four sets, every pick's verdict and reason, a full saved-menu view, and other credible mains in the same meal. It replaces the earlier checklist-only graphic on the same private site.
9+
10+Build it with `rtk proxy python3 docs/reports/build-highlight-pick-audit.py`. It writes the standalone page and detailed audit JSON into `.wrangler/highlight-comparison/`. The builder validates selected item membership, counts, limits and all alternative dish references against the exact menu records.
11+
12+Sources are `.wrangler/groq-live-results-oct2.json`, `.wrangler/groq-live-results.json`, `.wrangler/highlight-eval/1791086353782-live-2026-10-02.json` and the fresh `.wrangler/day-audit/2026-10-04.json`. The new capture script is `scripts/capture-highlight-audit.ts`; repeating it resumes saved results rather than generating a fresh result again.
13</file context>
test/highlights.test.tsline 146Open thread
……@@ -0,0 +1,506 @@
143+ if (mode === 'closed') f.snapshot.menus[menu.date].hoch = { ...menu, status: 'closed', meals: [] };
144+ if (mode === 'future') f.snapshot.menus = { '2026-09-30': { hoch: { ...menu, date: '2026-09-30' } } };
145+ f.kv.set(SNAPSHOT_KEY, JSON.stringify(f.snapshot));
146+ await runHighlightJob(f.env, f.storage, now, async () => { assert.fail('Unexpected Groq call'); });
Ccubic-dev-aiBoton line 1465d ago

P2: This assert.fail inside the fetcher is swallowed by runHighlightJob's catch, so the test cannot fail when Groq is called. Count the calls and assert zero instead.

Prompt for AI agents
text
1Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At test/highlights.test.ts, line 146:
2
3<comment>This `assert.fail` inside the fetcher is swallowed by `runHighlightJob`'s catch, so the test cannot fail when Groq is called. Count the calls and assert zero instead.</comment>
4
5<file context>
6@@ -0,0 +1,506 @@
7+ if (mode === 'closed') f.snapshot.menus[menu.date].hoch = { ...menu, status: 'closed', meals: [] };
8+ if (mode === 'future') f.snapshot.menus = { '2026-09-30': { hoch: { ...menu, date: '2026-09-30' } } };
9+ f.kv.set(SNAPSHOT_KEY, JSON.stringify(f.snapshot));
10+ await runHighlightJob(f.env, f.storage, now, async () => { assert.fail('Unexpected Groq call'); });
11+ }
12+});
13</file context>
……@@ -0,0 +1,208 @@
111+ output(Array(6).fill(0).map((_, i) => one([String(i)]))),
112+ output([one([])]), output([one(['0', '1', '2', '3', '4'], 'bulgogi')]),
113+ { meals: { m0: [one()], m1: [], m2: [] } }, null, [], { meals: [] },
114+ ]) assert.throws(() => resolveModelOutput(value, menu));
Ccubic-dev-aiBoton line 1145d ago

P2: assert.throws here has no error matcher, so any exception passes, including a TypeError from a regression. Pass a matcher per case, such as /Invalid model output|Meal keys|Invalid meal groups|Invalid group|belong|more than one group/, or pair each input with its expected message.

Prompt for AI agents
text
1Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At test/highlight-selection.test.ts, line 114:
2
3<comment>`assert.throws` here has no error matcher, so any exception passes, including a TypeError from a regression. Pass a matcher per case, such as `/Invalid model output|Meal keys|Invalid meal groups|Invalid group|belong|more than one group/`, or pair each input with its expected message.</comment>
4
5<file context>
6@@ -0,0 +1,208 @@
7+ output(Array(6).fill(0).map((_, i) => one([String(i)]))),
8+ output([one([])]), output([one(['0', '1', '2', '3', '4'], 'bulgogi')]),
9+ { meals: { m0: [one()], m1: [], m2: [] } }, null, [], { meals: [] },
10+ ]) assert.throws(() => resolveModelOutput(value, menu));
11+ assert.deepEqual(resolveModelOutput({ meals: {} }, { ...menu, meals: null }), {});
12+});
13</file context>
……@@ -0,0 +1,155 @@
150+### 6. Fair Groq A/B + evaluation
151+
152+- Models: `openai/gpt-oss-120b`, `llama-3.3-70b-versatile`, `moonshotai/kimi-k2-instruct` (or current Groq equivalents), same `highlightInput`, `reasoning_effort low`, temp 0. Blind, randomized order.
153+- Split: tune on `2026-09-30` only; holdout `2026-10-04`; never tune on holdout. Do not treat prior audit/reviewer votes as truth — use fresh double-blind food judges with guidelines.
Ccubic-dev-aiBoton line 1535d ago

P2: This report tunes on the Oct 4 holdout it declares off-limits. The §2–§3 Oct 4 'proposed' groups and policy fixes came from inspecting Oct 4 outputs, so Oct 4 can no longer serve as a holdout for these changes. Move these Oct 4 labels to a dev-only fixture or mark the holdout as spent.

Prompt for AI agents
text
1Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At docs/reports/group-review-muse.md, line 153:
2
3<comment>This report tunes on the Oct 4 holdout it declares off-limits. The §2–§3 Oct 4 'proposed' groups and policy fixes came from inspecting Oct 4 outputs, so Oct 4 can no longer serve as a holdout for these changes. Move these Oct 4 labels to a dev-only fixture or mark the holdout as spent.</comment>
4
5<file context>
6@@ -0,0 +1,155 @@
7+### 6. Fair Groq A/B + evaluation
8+
9+- Models: `openai/gpt-oss-120b`, `llama-3.3-70b-versatile`, `moonshotai/kimi-k2-instruct` (or current Groq equivalents), same `highlightInput`, `reasoning_effort low`, temp 0. Blind, randomized order.
10+- Split: tune on `2026-09-30` only; holdout `2026-10-04`; never tune on holdout. Do not treat prior audit/reviewer votes as truth — use fresh double-blind food judges with guidelines.
11+- Metrics: program recall (Italian/slider/nacho/pasta/bagel/omelet found?), singleton precision (no sides/sauces/desserts/headings), duplicate rate (components + bar both picked), plant precision (named entree vs forced side), cap compliance, pairwise usefulness preference. Report per-hall, not pooled.
12+
13</file context>
……@@ -0,0 +1,46 @@
30+
31+## Reproduce the graphic
32+
33+Run `python3 docs/reports/build-highlight-comparison.py`. It builds a standalone HTML page with all before-and-after selections, per-hall charts, remaining misses, and methodology into `.wrangler/highlight-comparison/index.html`.
Ccubic-dev-aiBoton line 335d ago

P2: The reproduction command points to docs/reports/build-highlight-comparison.py, which is not in the repository, so the documented graphic cannot be rebuilt. Commit the builder or correct the command to the script that actually exists.

Prompt for AI agents
text
1Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At docs/reports/highlight-comparison.md, line 33:
2
3<comment>The reproduction command points to `docs/reports/build-highlight-comparison.py`, which is not in the repository, so the documented graphic cannot be rebuilt. Commit the builder or correct the command to the script that actually exists.</comment>
4
5<file context>
6@@ -0,0 +1,46 @@
7+
8+## Reproduce the graphic
9+
10+Run `python3 docs/reports/build-highlight-comparison.py`. It builds a standalone HTML page with all before-and-after selections, per-hall charts, remaining misses, and methodology into `.wrangler/highlight-comparison/index.html`.
11+
12+Frozen October 2 runs:
13</file context>
suggestion
1Run the builder that is committed to this repo (none is present for this report); until it is added, remove the command and state that the graphic is generated locally.
……@@ -0,0 +1,155 @@
10+ - Policy says “prefer bar ID, don’t spend slots on components” but also “exclude station headings, never combine names.” Model cannot emit `Chicken or beef bulgogi` or `Italian pasta bar`. So v5 emits singletons: `m1:s1:i1 beef bulgogi + m1:s1:i3 orange tofu + m1:s2:i5 brisket + m1:s2:i2 mac + m1:s3:i0 slider`. Misses Italian entirely, double-counts Korean bar, fills 5th slot with a side.
11+2. **Heading vs program classifier missing.** `italian pitzer sunday`, `grill slider bar`, `NACHO BAR`, `BURGER BAR`, `PASTA BAR`, `OMELET BAR`, `LOX AND BAGEL BAR`, `deluxe bagel bar`, `Mongolian stir fry`, `build your own omelet burrito` are items that *are* programs. `TODAY'S SOUP/SALADS/SMOOTHIE`, `WHOLE EATS STATION...`, `Self-serve`, `Have A Nice Day`, `made without top-9...`, `OUTDOOR PATIO - Omelettes...` (descriptive duplicate of next item), `NACHO BAR` duplicated in `frank/m2 s0:i0` + `s3:i0` are empty headings/duplicates. Current prompt: “exclude all station headings” with no test. Result: Italian skipped as “heading”, slider picked correctly by luck, Frank nacho double-count risk.
12+3. **Overlapping parent/child not enforced cross-station.** Same nacho proteins in `frank/m2 s0:i1,i2` and `s3:i1,i2`; pasta bar `frary/m2 s2:i3 PASTA BAR` vs `i4 penne,i5 tortellini`; burger bar `m2:s3:i0` vs `i1-i4 patties`; Korean burrito vs bulgogi vs tofu (same banchan). V5 has no dedup rule beyond “avoid near-duplicates.”
13+4. **Plant preference forces bad sides when no entree exists.** Correct behavior per prompt (“never elevate a side”) but v5 violates it: picks `plant-based chicken strips` (generic patty), `creamy mac` as “variety”, misses real entrees `eggplant parmesan m1:s4:i0`, `stuffed tomatoes m1:s2:i4`, `marinated herb tofu`. In halls with no plant main (`malott/m1 dinner`, `frank/m2 dinner`) correct is 4 highlights, not 5 with cauliflower/rice.
Ccubic-dev-aiBoton line 135d ago

P2: The text says malott/m1 should yield 4 highlights, but the proposed JSON gives it 5. Either the count or the malott/m1 list is wrong. Reconcile them before this report is used as a reference.

Prompt for AI agents
text
1Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At docs/reports/group-review-muse.md, line 13:
2
3<comment>The text says malott/m1 should yield 4 highlights, but the proposed JSON gives it 5. Either the count or the malott/m1 list is wrong. Reconcile them before this report is used as a reference.</comment>
4
5<file context>
6@@ -0,0 +1,155 @@
7+ - Policy says “prefer bar ID, don’t spend slots on components” but also “exclude station headings, never combine names.” Model cannot emit `Chicken or beef bulgogi` or `Italian pasta bar`. So v5 emits singletons: `m1:s1:i1 beef bulgogi + m1:s1:i3 orange tofu + m1:s2:i5 brisket + m1:s2:i2 mac + m1:s3:i0 slider`. Misses Italian entirely, double-counts Korean bar, fills 5th slot with a side.
8+2. **Heading vs program classifier missing.** `italian pitzer sunday`, `grill slider bar`, `NACHO BAR`, `BURGER BAR`, `PASTA BAR`, `OMELET BAR`, `LOX AND BAGEL BAR`, `deluxe bagel bar`, `Mongolian stir fry`, `build your own omelet burrito` are items that *are* programs. `TODAY'S SOUP/SALADS/SMOOTHIE`, `WHOLE EATS STATION...`, `Self-serve`, `Have A Nice Day`, `made without top-9...`, `OUTDOOR PATIO - Omelettes...` (descriptive duplicate of next item), `NACHO BAR` duplicated in `frank/m2 s0:i0` + `s3:i0` are empty headings/duplicates. Current prompt: “exclude all station headings” with no test. Result: Italian skipped as “heading”, slider picked correctly by luck, Frank nacho double-count risk.
9+3. **Overlapping parent/child not enforced cross-station.** Same nacho proteins in `frank/m2 s0:i1,i2` and `s3:i1,i2`; pasta bar `frary/m2 s2:i3 PASTA BAR` vs `i4 penne,i5 tortellini`; burger bar `m2:s3:i0` vs `i1-i4 patties`; Korean burrito vs bulgogi vs tofu (same banchan). V5 has no dedup rule beyond “avoid near-duplicates.”
10+4. **Plant preference forces bad sides when no entree exists.** Correct behavior per prompt (“never elevate a side”) but v5 violates it: picks `plant-based chicken strips` (generic patty), `creamy mac` as “variety”, misses real entrees `eggplant parmesan m1:s4:i0`, `stuffed tomatoes m1:s2:i4`, `marinated herb tofu`. In halls with no plant main (`malott/m1 dinner`, `frank/m2 dinner`) correct is 4 highlights, not 5 with cauliflower/rice.
11+5. **Brunch/lunch hybrid + `featured` noise.** `mcconnell/m0` mixes breakfast + `global i4 impossible meat loaf,i5 fried chicken` + `stocks`. V5 picks plain `waffle`, plain `egg sandwich` over `brioche french toast casserole`, `beef machaca burritos`, `just eggs scrambled`. `malott` `featured:true` on everything (including `pesto 17940 cal`, `teriyaki`, allergen notice) is ignored correctly by v5 except waste.
12+
13</file context>
scripts/test-runtime.tslines 237–240Open thread
……@@ -156,3 +157,85 @@ try {
237+ const repeat = await highlightRuntime.dispatchFetch('https://menu.test/test/tick');
238+ assert.equal(repeat.status, 200);
239+ assert.equal(groqCalls, 1);
240+ console.log('Highlights runtime passed: refresh → actual scheduled SQLite alarm → mocked Groq → KV → original item data; opt-out cache isolation; saved results reused.');
Ccubic-dev-aiBoton lines 237–2405d ago

P2: The repeat /test/tick check cannot exercise result reuse. The next alarm is about 65 s out and scheduleHighlights does nothing while an alarm exists, so groqCalls === 1 passes even if reuse broke. Remove this block and the "saved results reused" claim in the final log, since reuse is covered by test/highlights.test.ts.

Prompt for AI agents
text
1Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At scripts/test-runtime.ts, line 237:
2
3<comment>The repeat `/test/tick` check cannot exercise result reuse. The next alarm is about 65 s out and `scheduleHighlights` does nothing while an alarm exists, so `groqCalls === 1` passes even if reuse broke. Remove this block and the "saved results reused" claim in the final log, since reuse is covered by `test/highlights.test.ts`.</comment>
4
5<file context>
6@@ -156,3 +157,85 @@ try {
7+ const optOut = await highlightRuntime.dispatchFetch('https://menu.test/v1/menus/hoch?highlights=false');
8+ assert.ok(!('highlights' in (await optOut.json() as object)));
9+ assert.notEqual(optOut.headers.get('etag'), response.headers.get('etag'));
10+ const repeat = await highlightRuntime.dispatchFetch('https://menu.test/test/tick');
11+ assert.equal(repeat.status, 200);
12+ assert.equal(groqCalls, 1);
13</file context>
suggestion
1 console.log('Highlights runtime passed: refresh → actual scheduled SQLite alarm → mocked Groq → KV → original item data; opt-out cache isolation.');
docs/highlights-history.mdline 19Open thread
……@@ -0,0 +1,39 @@
16+
17+Kept:
18+
19+- **GPT-OSS 120B with low reasoning.** No other model or setting showed a better result. Medium reasoning produced 413/400 errors or no gain. The 20B model picked clear sides. Qwen picked fewer sides but failed validation more often.
Ccubic-dev-aiBoton line 195d ago

P2: This overstates the model comparison. The archived holdout shows Qwen with higher coverage than 20B (35/51 vs 27/51), and GPT-OSS 120B was never run on the Oct 5 holdout because of quota. Limit the claim to the Oct 4 development run and note the untested holdout. Qwen's validation gap is one response in one run, so 'more often' overstates it.

Prompt for AI agents
text
1Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At docs/highlights-history.md, line 19:
2
3<comment>This overstates the model comparison. The archived holdout shows Qwen with higher coverage than 20B (35/51 vs 27/51), and GPT-OSS 120B was never run on the Oct 5 holdout because of quota. Limit the claim to the Oct 4 development run and note the untested holdout. Qwen's validation gap is one response in one run, so 'more often' overstates it.</comment>
4
5<file context>
6@@ -0,0 +1,39 @@
7+
8+Kept:
9+
10+- **GPT-OSS 120B with low reasoning.** No other model or setting showed a better result. Medium reasoning produced 413/400 errors or no gain. The 20B model picked clear sides. Qwen picked fewer sides but failed validation more often.
11+- **The minimal contract.** Each offering is one source ID, or two to four true variants with a short grounded title. Single titles always come from the menu.
12+
13</file context>
suggestion
1- **GPT-OSS 120B with low reasoning.** On the Oct 4 development run it had the best coverage (27/36, versus 20B 17/36 and Qwen 20/36). It was not measured on the Oct 5 holdout because the daily quota ran out. Medium reasoning produced 413/400 errors or no gain. The 20B model picked clear sides. Qwen picked fewer sides, but its holdout had higher coverage than 20B (35/51 vs 27/51) and was rejected for one hall.
src/collector.tsline 37Open thread
……@@ -21,13 +33,17 @@ export class MenuCollector extends DurableObject<Env> {
2335 const last = await this.ctx.storage.get<{ hour: string; summary: CollectionSummary }>('last-run');
24- if (last?.hour === hour) return last.summary;
36+ if (last?.hour === hour) {
37+ await this.scheduleHighlights();
Ccubic-dev-aiBoton line 375d ago

P2: If setAlarm fails here, refresh() rejects even though the snapshot is already saved, so the scheduled run is reported as failed. Highlight scheduling is best-effort, so log the failure instead of propagating it.

Prompt for AI agents
text
1Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At src/collector.ts, line 37:
2
3<comment>If `setAlarm` fails here, `refresh()` rejects even though the snapshot is already saved, so the scheduled run is reported as failed. Highlight scheduling is best-effort, so log the failure instead of propagating it.</comment>
4
5<file context>
6@@ -21,13 +33,17 @@ export class MenuCollector extends DurableObject<Env> {
7 const last = await this.ctx.storage.get<{ hour: string; summary: CollectionSummary }>('last-run');
8- if (last?.hour === hour) return last.summary;
9+ if (last?.hour === hour) {
10+ await this.scheduleHighlights();
11+ return last.summary;
12+ }
13</file context>
……@@ -0,0 +1,62 @@
48+The fixtures and scorer in this checkout reproduce the original failures without inference:
49+
50+```sh
51+rtk proxy npm run highlights:eval -- baseline 2026-10-02 hoch,malott,mcconnell,collins,frank,frary
Ccubic-dev-aiBoton line 515d ago

P2: The reproduce commands reference npm run highlights:eval, which is not defined in package.json, and no evaluator or scorer script exists under scripts/. Either add the script and its fixtures or remove this section, since the claim that the checkout reproduces the results is currently false.

Prompt for AI agents
text
1Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At docs/reports/highlight-refinement.md, line 51:
2
3<comment>The reproduce commands reference `npm run highlights:eval`, which is not defined in package.json, and no evaluator or scorer script exists under `scripts/`. Either add the script and its fixtures or remove this section, since the claim that the checkout reproduces the results is currently false.</comment>
4
5<file context>
6@@ -0,0 +1,62 @@
7+The fixtures and scorer in this checkout reproduce the original failures without inference:
8+
9+```sh
10+rtk proxy npm run highlights:eval -- baseline 2026-10-02 hoch,malott,mcconnell,collins,frank,frary
11+```
12+
13</file context>
src/http.tsline 199Open thread
……@@ -190,8 +195,15 @@ export async function handleRequest(request: Request, env: Env, now = new Date()
191196 }
192197
198+ const includeHighlights = url.searchParams.get('highlights') !== 'false';
199+ const records = includeHighlights && highlightsEnabled(env) && input.date === californiaDate(now) ? await readHighlights(env) : {};
Ccubic-dev-aiBoton line 1995d ago

P3: This comment is now inaccurate. With highlights enabled for today, the handler also reads highlights:v1, so responses use two KV reads, not one. Update the comment to name both reads.

Prompt for AI agents
text
1Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At src/http.ts, line 199:
2
3<comment>This comment is now inaccurate. With highlights enabled for today, the handler also reads `highlights:v1`, so responses use two KV reads, not one. Update the comment to name both reads.</comment>
4
5<file context>
6@@ -190,8 +195,15 @@ export async function handleRequest(request: Request, env: Env, now = new Date()
7 }
8
9+ const includeHighlights = url.searchParams.get('highlights') !== 'false';
10+ const records = includeHighlights && highlightsEnabled(env) && input.date === californiaDate(now) ? await readHighlights(env) : {};
11+ const present = async (id: HallId) => {
12+ const menu = menuFor(snapshot, id, input.date, now);
13</file context>
……@@ -0,0 +1,183 @@
171+
172+**Strategy B — two-pass generate→critic.** Pass 1: same hall-day input, model lists **all** candidate mains with one-word role tags (`main | bar | heading | side`), no cap. Pass 2 (cheap/short, deterministic prompt): apply the elimination checklist — drop headings/sides, collapse program children into parents, merge protein twins — and return ≤5 ordered IDs. The structural constraint is enforced between passes by code, and the critic sees the full candidate inventory (fixing the "fill the list as you read station 1" failure mode directly). Costs one extra call per hall-day; keep `CALL_INTERVAL_MS` budgeting and fall back to Strategy A grouping if pass 2 is empty.
173+
174+## 7. Fair multi-model Groq A/B + evaluation
Ccubic-dev-aiBoton line 1745d ago

P3: The methodology claim is self-contradictory. §3 was written after reading the v5 Oct 4 outputs (§1, and the collins 'endorse the actual v5 run' note), so it cannot be a blind gold set. Either drop the 'before seeing any output' claim or build the holdout labels from the menus alone before reading model results.

Prompt for AI agents
text
1Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At docs/reports/group-review-glm-flash.md, line 174:
2
3<comment>The methodology claim is self-contradictory. §3 was written after reading the v5 Oct 4 outputs (§1, and the collins 'endorse the actual v5 run' note), so it cannot be a blind gold set. Either drop the 'before seeing any output' claim or build the holdout labels from the menus alone before reading model results.</comment>
4
5<file context>
6@@ -0,0 +1,183 @@
7+
8+**Strategy B — two-pass generate→critic.** Pass 1: same hall-day input, model lists **all** candidate mains with one-word role tags (`main | bar | heading | side`), no cap. Pass 2 (cheap/short, deterministic prompt): apply the elimination checklist — drop headings/sides, collapse program children into parents, merge protein twins — and return ≤5 ordered IDs. The structural constraint is enforced between passes by code, and the critic sees the full candidate inventory (fixing the "fill the list as you read station 1" failure mode directly). Costs one extra call per hall-day; keep `CALL_INTERVAL_MS` budgeting and fall back to Strategy A grouping if pass 2 is empty.
9+
10+## 7. Fair multi-model Groq A/B + evaluation
11+
12+- **Arms**: `openai/gpt-oss-120b` (current) vs a comparable Groq model (e.g. `llama-3.3-70b-versatile`), crossed with prompt v5 vs Strategy-B (2×2), or at minimum model-only. Identical `reasoning_effort`, token budget, temp 0.
13</file context>
……@@ -0,0 +1,87 @@
28+const save = () => writeFile(path, JSON.stringify(run, null, 2));
29+
30+for (const hall of HALLS) {
31+ if (run.halls.some((h: HallMenu) => h.hall === hall.id)) continue;
Ccubic-dev-aiBoton line 315d ago

P2: Resume skips any hall that has an entry, even when generation failed or the fetch failed. A 429, 5xx, timeout, or fetch error is never retried on rerun. Re-run halls that have no saved selections instead.

Prompt for AI agents
text
1Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At scripts/capture-highlight-audit.ts, line 31:
2
3<comment>Resume skips any hall that has an entry, even when generation failed or the fetch failed. A 429, 5xx, timeout, or fetch error is never retried on rerun. Re-run halls that have no saved selections instead.</comment>
4
5<file context>
6@@ -0,0 +1,87 @@
7+const save = () => writeFile(path, JSON.stringify(run, null, 2));
8+
9+for (const hall of HALLS) {
10+ if (run.halls.some((h: HallMenu) => h.hall === hall.id)) continue;
11+ let menu: HallMenu = unavailableMenu(hall.id, date);
12+ try {
13</file context>
……@@ -0,0 +1,41 @@
27+| 2026-09-30 | collins | Dinner | butter chicken; massor dal; superfood power bowl; chicken philly cheesesteak; vietnamese style tempeh with agave lime sauce |
28+| 2026-09-30 | frary | Breakfast | OMELET BAR - Scrambled Eggs and Egg Whites, Assorted Veggies, Cheese and Meats (Bacon, Ham, Turkey); Breakfast Quesadilla with Scrambled Eggs, Jack and Cheddar Cheese; Scrambled Whole Eggs; Polenta Scramble with Oat Milk; Beyond Breakfast Sausage |
29+| 2026-09-30 | frary | Lunch | BURGER BAR; Shrimp Scampi; Pumpkin Butter Chicken; Chicken Tenders; Vegetarian Tenders |
30+| 2026-09-30 | frary | Dinner | Sweet and Sour Pork; Spicy Savory Mapo Tofu; General Tso`s Chicken Bowl; Alberto`s BLT on Sourdough with Avocado Mayo; Vegetable Chow Mein Noodles |
Ccubic-dev-aiBoton line 305d ago

P3: The backticks in "General Tsos" and "Albertos" pair up inside the same table cell and render as an inline code span, which garbles that row. Escape them as ` so the source name renders literally, or use the apostrophe if that matches the source name.

Prompt for AI agents
text
1Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At docs/reports/highlight-selections.md, line 30:
2
3<comment>The backticks in "General Tso`s" and "Alberto`s" pair up inside the same table cell and render as an inline code span, which garbles that row. Escape them as \` so the source name renders literally, or use the apostrophe if that matches the source name.</comment>
4
5<file context>
6@@ -0,0 +1,41 @@
7+| 2026-09-30 | collins | Dinner | butter chicken; massor dal; superfood power bowl; chicken philly cheesesteak; vietnamese style tempeh with agave lime sauce |
8+| 2026-09-30 | frary | Breakfast | OMELET BAR - Scrambled Eggs and Egg Whites, Assorted Veggies, Cheese and Meats (Bacon, Ham, Turkey); Breakfast Quesadilla with Scrambled Eggs, Jack and Cheddar Cheese; Scrambled Whole Eggs; Polenta Scramble with Oat Milk; Beyond Breakfast Sausage |
9+| 2026-09-30 | frary | Lunch | BURGER BAR; Shrimp Scampi; Pumpkin Butter Chicken; Chicken Tenders; Vegetarian Tenders |
10+| 2026-09-30 | frary | Dinner | Sweet and Sour Pork; Spicy Savory Mapo Tofu; General Tso`s Chicken Bowl; Alberto`s BLT on Sourdough with Avocado Mayo; Vegetable Chow Mein Noodles |
11+| 2026-09-30 | malott | Breakfast | egg shakshuka; southwestern tofu and chickpea scramble; impossible savory oat hash; bacon & egg breakfast burrito; whole grain french toast |
12+| 2026-09-30 | malott | Lunch | jackfruit with coconut milk-green curry sauce; seasoned grilled chicken breast; pork pot roast; mortadella sandwich on focaccia with lemon ricotta & basil parmesan pesto; impossible burger on udi's bun |
13</file context>
suggestion
1| 2026-09-30 | frary | Dinner | Sweet and Sour Pork; Spicy Savory Mapo Tofu; General Tso\`s Chicken Bowl; Alberto\`s BLT on Sourdough with Avocado Mayo; Vegetable Chow Mein Noodles |
……@@ -0,0 +1,87 @@
21+ if (error.code !== 'ENOENT') throw error;
22+ return null;
23+});
24+const run = existing ?? { date, capturedAt: new Date().toISOString(), promptVersion: PROMPT_VERSION, halls: [], attempts: 0, nextCallAt: 0 };
Ccubic-dev-aiBoton line 245d ago

P2: Resuming an audit file keeps halls captured under an older PROMPT_VERSION, and nothing compares the saved version. After a prompt bump the output mixes old and new picks. Fail fast when the saved version differs.

Prompt for AI agents
text
1Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At scripts/capture-highlight-audit.ts, line 24:
2
3<comment>Resuming an audit file keeps halls captured under an older `PROMPT_VERSION`, and nothing compares the saved version. After a prompt bump the output mixes old and new picks. Fail fast when the saved version differs.</comment>
4
5<file context>
6@@ -0,0 +1,87 @@
7+ if (error.code !== 'ENOENT') throw error;
8+ return null;
9+});
10+const run = existing ?? { date, capturedAt: new Date().toISOString(), promptVersion: PROMPT_VERSION, halls: [], attempts: 0, nextCallAt: 0 };
11+let key = process.env.GROQ_API_KEY;
12+if (!key) key = (await readFile('.dev.vars', 'utf8')).match(/^GROQ_API_KEY\s*=\s*["']?([^\s"']+)/m)?.[1];
13</file context>
README.mdline 60Open thread
……@@ -49,7 +49,29 @@ Individual response example, with illustrative food:
57+
58+```json
59+"highlights": {
60+ "status": "ready",
Ccubic-dev-aiBoton line 605d ago

P3: The ready highlights example omits provider, model, generatedAt, and menuVersion, which every ready response includes. Add them so clients see the full public shape.

Prompt for AI agents
text
1Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At README.md, line 60:
2
3<comment>The ready `highlights` example omits `provider`, `model`, `generatedAt`, and `menuVersion`, which every ready response includes. Add them so clients see the full public shape.</comment>
4
5<file context>
6@@ -49,7 +49,29 @@ Individual response example, with illustrative food:
7+
8+```json
9+"highlights": {
10+ "status": "ready",
11+ "meals": [{
12+ "mealIndex": 1, "name": "Dinner",
13</file context>
suggestion
1 "status": "ready",
2 "provider": "groq", "model": "openai/gpt-oss-120b", "generatedAt": "2026-09-06T19:00:00.000Z", "menuVersion": "<sha256 hex digest>",
……@@ -0,0 +1,51 @@
26+- CORS, HEAD, ETag/304, malformed query handling, and all six future dates behaved correctly.
27+- Future-date menus had `not_applicable` highlights.
28+- Repeated public reads and another same-hour scheduled trigger left the successful inference count at six, with zero failed inference calls in the final run.
29+- 114 automated tests, Workers runtime tests with actual alarm delivery, and the build passed.
Ccubic-dev-aiBoton line 295d ago

P3: The test count here (114) differs from the 136 tests stated in the PR description. Confirm the count from the final run and update whichever figure is stale.

Prompt for AI agents
text
1Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At docs/reports/menu-highlights-live-test.md, line 29:
2
3<comment>The test count here (114) differs from the 136 tests stated in the PR description. Confirm the count from the final run and update whichever figure is stale.</comment>
4
5<file context>
6@@ -0,0 +1,51 @@
7+- CORS, HEAD, ETag/304, malformed query handling, and all six future dates behaved correctly.
8+- Future-date menus had `not_applicable` highlights.
9+- Repeated public reads and another same-hour scheduled trigger left the successful inference count at six, with zero failed inference calls in the final run.
10+- 114 automated tests, Workers runtime tests with actual alarm delivery, and the build passed.
11+
12+The short response cache briefly continued showing `pending` after the final result was generated. A fresh dated URL exposed the ready result immediately, and the original URL updated after cache expiry. This matches the documented cache behavior.
13</file context>
suggestion
1- 136 automated tests, Workers runtime tests with actual alarm delivery, and the build passed.
src/highlight-selection.tsline 224Open thread
……@@ -0,0 +1,238 @@
221+ for (const { title, sources } of resolved) {
222+ if (sources.length > 1) {
223+ try {
224+ add({ title: variantTitle(title, sources.map(source => source.item.name)), kind: 'variants', sourceIds: sources.map(source => source.id) });
Ccubic-dev-aiBoton line 2245d ago

P3: A multi-source group whose sources all have the same name is accepted, which the prompt forbids ("Represent a meal program once"). Skip or collapse variant groups whose normalized source names are identical, using the same name key as the single-source dedupe.

Prompt for AI agents
text
1Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At src/highlight-selection.ts, line 224:
2
3<comment>A multi-source group whose sources all have the same name is accepted, which the prompt forbids ("Represent a meal program once"). Skip or collapse variant groups whose normalized source names are identical, using the same name key as the single-source dedupe.</comment>
4
5<file context>
6@@ -0,0 +1,238 @@
7+ for (const { title, sources } of resolved) {
8+ if (sources.length > 1) {
9+ try {
10+ add({ title: variantTitle(title, sources.map(source => source.item.name)), kind: 'variants', sourceIds: sources.map(source => source.id) });
11+ continue;
12+ } catch (error) { onSplit?.((error as Error).message); }
13</file context>
……@@ -0,0 +1,62 @@
39+
40+## Verification and reviews
41+
42+117 automated tests, TypeScript checks, the local Workers runtime test with real alarm delivery, and the Wrangler dry-run build passed. The public response shape and highlights=false opt-out remain the same. Original menu names and dietary labels are still resolved from source items. No production deployment or snapshot-page publication occurred.
Ccubic-dev-aiBoton line 425d ago

P3: The test count here (117) conflicts with the 136 tests stated in the PR description. Update this figure to match the actual test run output.

Prompt for AI agents
text
1Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At docs/reports/highlight-refinement.md, line 42:
2
3<comment>The test count here (117) conflicts with the 136 tests stated in the PR description. Update this figure to match the actual test run output.</comment>
4
5<file context>
6@@ -0,0 +1,62 @@
7+
8+## Verification and reviews
9+
10+117 automated tests, TypeScript checks, the local Workers runtime test with real alarm delivery, and the Wrangler dry-run build passed. The public response shape and highlights=false opt-out remain the same. Original menu names and dietary labels are still resolved from source items. No production deployment or snapshot-page publication occurred.
11+
12+[OpenCode Go review opinions](highlight-model-reviews.md) came from Kimi K2.7 Code, MiniMax M2.7 and GLM-5.3. DeepSeek V4.1 could not run under the workspace region setting. Their suggestions were hypotheses, not votes or verified labels.
13</file context>
suggestion
1136 automated tests, TypeScript checks, the local Workers runtime test with real alarm delivery, and the Wrangler dry-run build passed. The public response shape and highlights=false opt-out remain the same. Original menu names and dietary labels are still resolved from source items. No production deployment or snapshot-page publication occurred.
……@@ -0,0 +1,51 @@
14+| collins | 2,559 | 360 | 2,919 |
15+| frank | 632 | 278 | 910 |
16+| frary | 2,420 | 415 | 2,835 |
17+| **Total** | **22,844** | **2,351** | **25,195** |
Ccubic-dev-aiBoton line 175d ago

P3: The Total row does not match the per-hall rows. Completion tokens should be 2,346 and total tokens should be 25,190. Fix both values so the table reconciles, since this report is the usage record for the quota.

Prompt for AI agents
text
1Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At docs/reports/menu-highlights-live-test.md, line 17:
2
3<comment>The Total row does not match the per-hall rows. Completion tokens should be 2,346 and total tokens should be 25,190. Fix both values so the table reconciles, since this report is the usage record for the quota.</comment>
4
5<file context>
6@@ -0,0 +1,51 @@
7+| collins | 2,559 | 360 | 2,919 |
8+| frank | 632 | 278 | 910 |
9+| frary | 2,420 | 415 | 2,835 |
10+| **Total** | **22,844** | **2,351** | **25,195** |
11+
12+Completion counts include reasoning tokens. These totals describe the final six-request run, not earlier diagnostics. Today's separate replay of the earlier Collins failure consumed another 3,021 tokens. Earlier September 30 runs also consumed quota; usage was not captured for every rejected or invalid response.
13</file context>
suggestion
1| **Total** | **22,844** | **2,346** | **25,190** |
……@@ -0,0 +1,61 @@
1+# Blind development food review, GPT-6.1 Sol
2+
3+I reviewed the eighteen anonymous hall responses in `blind-development.json` against the exact Oct 4 menus and old v5 selections. I did not inspect model identities, guess which cell uses which model, read holdout results, or call an API. This is assistant judgment. Only the user's four Pitzer dinner expectations are human-approved. Each cell has one response per hall; this cannot establish stable performance or the benefit of a reasoning setting.
Ccubic-dev-aiBoton line 35d ago

P3: The review cites blind-development.json as its source, but that file is not in the repo. Commit the input or the rubric it was checked against, or state where it lives, so the labels and IDs can be reproduced.

Prompt for AI agents
text
1Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At docs/reports/group-blind-review-sol.md, line 3:
2
3<comment>The review cites `blind-development.json` as its source, but that file is not in the repo. Commit the input or the rubric it was checked against, or state where it lives, so the labels and IDs can be reproduced.</comment>
4
5<file context>
6@@ -0,0 +1,61 @@
7+# Blind development food review, GPT-6.1 Sol
8+
9+I reviewed the eighteen anonymous hall responses in `blind-development.json` against the exact Oct 4 menus and old v5 selections. I did not inspect model identities, guess which cell uses which model, read holdout results, or call an API. This is assistant judgment. Only the user's four Pitzer dinner expectations are human-approved. Each cell has one response per hall; this cannot establish stable performance or the benefit of a reasoning setting.
10+
11+## Overall judgment
12</file context>
scripts/test-runtime.tsline 220Open thread
……@@ -156,3 +157,85 @@ try {
217+ const deadline = Date.now() + 10_000;
218+ let saved = false;
219+ while (Date.now() < deadline) {
220+ const raw = await kv.get('highlights:v1');
Ccubic-dev-aiBoton line 2205d ago

P3: Use the exported HIGHLIGHTS_KEY instead of the literal. Import it with import { HIGHLIGHTS_KEY } from '../src/highlights';. If the key changes, this poll would otherwise read a stale key and fail only with the generic 10 s timeout.

Prompt for AI agents
text
1Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At scripts/test-runtime.ts, line 220:
2
3<comment>Use the exported `HIGHLIGHTS_KEY` instead of the literal. Import it with `import { HIGHLIGHTS_KEY } from '../src/highlights';`. If the key changes, this poll would otherwise read a stale key and fail only with the generic 10 s timeout.</comment>
4
5<file context>
6@@ -156,3 +157,85 @@ try {
7+ const deadline = Date.now() + 10_000;
8+ let saved = false;
9+ while (Date.now() < deadline) {
10+ const raw = await kv.get('highlights:v1');
11+ if (raw && JSON.parse(raw).hoch?.status === 'ready') { saved = true; break; }
12+ await delay(50);
13</file context>
……@@ -0,0 +1,155 @@
66+ {"label": "beef menudo", "anchor": "m0:s7:i0", "members": []}
67+ ]},
68+ "m1": {"groups": [
69+ {"label": "Italian Pitzer Sunday", "anchor": "m1:s0:i0", "members": ["m1:s0:i1","m1:s0:i2","m1:s0:i4","m1:s0:i5","m1:s0:i6"]},
Ccubic-dev-aiBoton line 695d ago

P3: The Italian Pitzer Sunday members omit m1:s0:i3, which §1 identifies as a pasta in the same bar. Either add it or document why it is excluded, since members are meant to be all merged variants or components.

Prompt for AI agents
text
1Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At docs/reports/group-review-muse.md, line 69:
2
3<comment>The Italian Pitzer Sunday members omit m1:s0:i3, which §1 identifies as a pasta in the same bar. Either add it or document why it is excluded, since members are meant to be all merged variants or components.</comment>
4
5<file context>
6@@ -0,0 +1,155 @@
7+ {"label": "beef menudo", "anchor": "m0:s7:i0", "members": []}
8+ ]},
9+ "m1": {"groups": [
10+ {"label": "Italian Pitzer Sunday", "anchor": "m1:s0:i0", "members": ["m1:s0:i1","m1:s0:i2","m1:s0:i4","m1:s0:i5","m1:s0:i6"]},
11+ {"label": "Chicken or Beef Bulgogi", "anchor": "m1:s1:i1", "members": ["m1:s1:i2"]},
12+ {"label": "smoked bbq brisket", "anchor": "m1:s2:i5", "members": []},
13</file context>
……@@ -0,0 +1,87 @@
40+ menu.error = { code: 'SOURCE_FETCH_FAILED', message: error instanceof Error ? error.message : String(error) };
41+ }
42+ const entry = { ...menu, selections: null as unknown, generationError: null as string | null, generatedAt: null as string | null };
43+ if (menu.status === 'ok') {
Ccubic-dev-aiBoton line 435d ago

P3: This runs Groq for ok menus that have no items. Production skips them through hasItems. Add the same check so the audit does not spend an attempt on an empty hall-day. This needs hasItems imported from ../src/highlight-selection.

Prompt for AI agents
text
1Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At scripts/capture-highlight-audit.ts, line 43:
2
3<comment>This runs Groq for ok menus that have no items. Production skips them through `hasItems`. Add the same check so the audit does not spend an attempt on an empty hall-day. This needs `hasItems` imported from `../src/highlight-selection`.</comment>
4
5<file context>
6@@ -0,0 +1,87 @@
7+ menu.error = { code: 'SOURCE_FETCH_FAILED', message: error instanceof Error ? error.message : String(error) };
8+ }
9+ const entry = { ...menu, selections: null as unknown, generationError: null as string | null, generatedAt: null as string | null };
10+ if (menu.status === 'ok') {
11+ for (let attempt = 0; attempt < 2; attempt++) {
12+ if (run.attempts >= 14) throw new Error('Audit stopped at 14 outbound attempts');
13</file context>
src/highlights.tsline 152Open thread
……@@ -0,0 +1,153 @@
149+ };
150+ }),
151+ } };
152+ } catch { return empty('unavailable'); }
Ccubic-dev-aiBoton line 1525d ago

P3: This catch-all returns unavailable without logging, so a saved record that fails validation or a bug in this block is invisible in logs. Log the error (without the record contents) before returning the empty result.

Prompt for AI agents
text
1Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At src/highlights.ts, line 152:
2
3<comment>This catch-all returns `unavailable` without logging, so a saved record that fails validation or a bug in this block is invisible in logs. Log the error (without the record contents) before returning the empty result.</comment>
4
5<file context>
6@@ -0,0 +1,153 @@
7+ };
8+ }),
9+ } };
10+ } catch { return empty('unavailable'); }
11+}
12</file context>
……@@ -0,0 +1,87 @@
81+ const groups = entry.selections[meal.id] ?? [];
82+ const items = meal.items.length;
83+ console.log(`- ${meal.name} [${items} items]: ${groups.map(group => group.sourceIds.length > 1
84+ ? `${group.title} {${group.sourceIds.map(id => meal.items.find(item => item.id === id)!.item.name).join(' | ')}}`
Ccubic-dev-aiBoton line 845d ago

P3: Variant groups print item names without their stations, which breaks the header's promise to print each pick next to its station. Add the station to each variant item.

Prompt for AI agents
text
1Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At scripts/capture-highlight-audit.ts, line 84:
2
3<comment>Variant groups print item names without their stations, which breaks the header's promise to print each pick next to its station. Add the station to each variant item.</comment>
4
5<file context>
6@@ -0,0 +1,87 @@
7+ const groups = entry.selections[meal.id] ?? [];
8+ const items = meal.items.length;
9+ console.log(`- ${meal.name} [${items} items]: ${groups.map(group => group.sourceIds.length > 1
10+ ? `${group.title} {${group.sourceIds.map(id => meal.items.find(item => item.id === id)!.item.name).join(' | ')}}`
11+ : `${group.title} (${meal.items.find(item => item.id === group.sourceIds[0])!.station})`).join('; ') || 'none'}`);
12+ }
13</file context>
……@@ -0,0 +1,87 @@
13+import { indexMenu, type SelectionMap } from '../src/highlight-selection';
14+
15+const date = process.argv[2] ?? californiaDate(new Date());
16+if (!/^\d{4}-\d{2}-\d{2}$/.test(date)) throw new Error('Use YYYY-MM-DD');
Ccubic-dev-aiBoton line 165d ago

P3: This check accepts impossible calendar dates such as 2026-99-99, which are then sent to the providers and stored as an audit. Use the shared isValidDate validator.

Prompt for AI agents
text
1Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At scripts/capture-highlight-audit.ts, line 16:
2
3<comment>This check accepts impossible calendar dates such as `2026-99-99`, which are then sent to the providers and stored as an audit. Use the shared `isValidDate` validator.</comment>
4
5<file context>
6@@ -0,0 +1,87 @@
7+import { indexMenu, type SelectionMap } from '../src/highlight-selection';
8+
9+const date = process.argv[2] ?? californiaDate(new Date());
10+if (!/^\d{4}-\d{2}-\d{2}$/.test(date)) throw new Error('Use YYYY-MM-DD');
11+const dir = '.wrangler/day-audit';
12+await mkdir(dir, { recursive: true });
13</file context>
docs/highlights-history.mdline 26Open thread
……@@ -0,0 +1,39 @@
23+
24+- **The full grouped contract** (`kind: program`, `componentIds`). The model's component bookkeeping was unreliable, and nothing in the shipped pipeline produced it.
25+- **The candidate-inventory pilot and both grouped prompt strategies.** Neither consistently beat v8.
26+- **Station-guide hints from v5.** v8 dropped them, and v8 did no worse.
Ccubic-dev-aiBoton line 265d ago

P3: The claim that v8 'did no worse' without station-guide hints is not backed by any archived comparison. Either cite the run that measured it or state that it was not measured.

Prompt for AI agents
text
1Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At docs/highlights-history.md, line 26:
2
3<comment>The claim that v8 'did no worse' without station-guide hints is not backed by any archived comparison. Either cite the run that measured it or state that it was not measured.</comment>
4
5<file context>
6@@ -0,0 +1,39 @@
7+
8+- **The full grouped contract** (`kind: program`, `componentIds`). The model's component bookkeeping was unreliable, and nothing in the shipped pipeline produced it.
9+- **The candidate-inventory pilot and both grouped prompt strategies.** Neither consistently beat v8.
10+- **Station-guide hints from v5.** v8 dropped them, and v8 did no worse.
11+
12+## Known limits
13</file context>
suggestion
1- **Station-guide hints from v5.** v8 dropped them. No archived run isolates the effect of removing them.
……@@ -0,0 +1,87 @@
41+ }
42+ const entry = { ...menu, selections: null as unknown, generationError: null as string | null, generatedAt: null as string | null };
43+ if (menu.status === 'ok') {
44+ for (let attempt = 0; attempt < 2; attempt++) {
Ccubic-dev-aiBoton line 445d ago

P3: The audit allows two attempts per hall, but production allows MAX_VERSION_ATTEMPTS (3) in src/highlight-jobs.ts. Import that constant and use it so the audit matches the shipped retry policy. Adjust the 14-attempt cap to match.

Prompt for AI agents
text
1Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At scripts/capture-highlight-audit.ts, line 44:
2
3<comment>The audit allows two attempts per hall, but production allows `MAX_VERSION_ATTEMPTS` (3) in src/highlight-jobs.ts. Import that constant and use it so the audit matches the shipped retry policy. Adjust the 14-attempt cap to match.</comment>
4
5<file context>
6@@ -0,0 +1,87 @@
7+ }
8+ const entry = { ...menu, selections: null as unknown, generationError: null as string | null, generatedAt: null as string | null };
9+ if (menu.status === 'ok') {
10+ for (let attempt = 0; attempt < 2; attempt++) {
11+ if (run.attempts >= 14) throw new Error('Audit stopped at 14 outbound attempts');
12+ while (Date.now() < run.nextCallAt) {
13</file context>
……@@ -0,0 +1,87 @@
21+ if (error.code !== 'ENOENT') throw error;
22+ return null;
23+});
24+const run = existing ?? { date, capturedAt: new Date().toISOString(), promptVersion: PROMPT_VERSION, halls: [], attempts: 0, nextCallAt: 0 };
Ccubic-dev-aiBoton line 245d ago

P2: A resumed audit never checks its saved promptVersion, so it can print old selections as results from the current pipeline after a version bump. Reject a mismatched saved version and require deleting the file to restart.

Prompt for AI agents
text
1Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At scripts/capture-highlight-audit.ts, line 24:
2
3<comment>A resumed audit never checks its saved `promptVersion`, so it can print old selections as results from the current pipeline after a version bump. Reject a mismatched saved version and require deleting the file to restart.</comment>
4
5<file context>
6@@ -0,0 +1,87 @@
7+ if (error.code !== 'ENOENT') throw error;
8+ return null;
9+});
10+const run = existing ?? { date, capturedAt: new Date().toISOString(), promptVersion: PROMPT_VERSION, halls: [], attempts: 0, nextCallAt: 0 };
11+let key = process.env.GROQ_API_KEY;
12+if (!key) key = (await readFile('.dev.vars', 'utf8')).match(/^GROQ_API_KEY\s*=\s*["']?([^\s"']+)/m)?.[1];
13</file context>
……@@ -0,0 +1,47 @@
12+Run 1 failures:
13+
14+- Hoch hit `json_validate_failed` twice before succeeding. Under the old code the first of these would have been terminal.
15+- Frary failed three times on a combined title that had no shared dish word. That led to the split fallback, under which a bad combined title no longer sinks the whole hall.
Ccubic-dev-aiBoton line 155d ago

P3: The Frary account conflicts with the shipped code. A bad combined title splits in the same call and is never retried, so three failed calls cannot be explained by it. Either state that run 1 used an earlier pipeline, or correct the cause and say whether Frary ended ready or exhausted.

Prompt for AI agents
text
1Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At docs/reports/highlight-audit-2026-10-23.md, line 15:
2
3<comment>The Frary account conflicts with the shipped code. A bad combined title splits in the same call and is never retried, so three failed calls cannot be explained by it. Either state that run 1 used an earlier pipeline, or correct the cause and say whether Frary ended ready or exhausted.</comment>
4
5<file context>
6@@ -0,0 +1,47 @@
7+Run 1 failures:
8+
9+- Hoch hit `json_validate_failed` twice before succeeding. Under the old code the first of these would have been terminal.
10+- Frary failed three times on a combined title that had no shared dish word. That led to the split fallback, under which a bad combined title no longer sinks the whole hall.
11+- Collins breakfast filled three slots from its `sweets` station: chia pudding, scones and muffins.
12+- McConnell lunch picked `grill - market`, which is the grill station's own label.
13</file context>