internal/ai/skills implements archive_title v1: input is the week's dinner names in day order, output {title}; validators require 1–48 characters, no emoji, no surrounding quotes. It runs when a list is first generated and stores shopping_lists.title; if it fails, the list is still created with title null and the UI shows the week label instead, and the title is retried on the next generation (title failure never blocks the list). Golden tests with the Fake provider cover a valid title, a too-long title repaired, an emoji rejected, and the failure path leaving the list intact.
Scope
The skill and its wiring into generation. No freeze/archive API (7ac36526), no UI.
Points for review
The skill follows the existing pattern (Skill interface, harness Run with its one repair turn, embedded prompt template plus checksums file, golden tests under testdata): confirm no second way to declare a skill and that the validators reject empty, over-48, emoji and quote-wrapped titles through the repair turn rather than by silent truncation.
The failure path: a title failure leaves the list created with title null, logs a warning, records the failure in ai_runs, and never blocks the list — and nothing fabricates a title.
The retry semantics, which the developer had to define: after its transaction commits, every Generate/GeneratePushed checks whether the stored list is still untitled and titles it if so; the store is conditional (title IS NULL AND status='current' AND frozen_at IS NULL), so a titled list is never re-titled (even after a swap) and two concurrent generations cannot overwrite each other. Confirm that is airtight and that a title is written exactly once.
Two things the developer flagged as hand-offs, which deserve your judgement:
The title call is synchronous, so POST /shopping/{weekStart}/generateand the Push to Calendar response now wait for it, bounded by the harness's 60-second default timeout. Judge whether adding up to a minute to the push response is acceptable, or whether it needs to be decoupled/asynchronous.
An untitled list that is frozen into the Supply archive stays untitled forever (generating a frozen list returns ErrFrozen), and the developer suggests task 7ac36526 may want to decide whether to title at freeze time. Say whether that is acceptable or should be recorded for that task.
The title call runs outside the transaction, with the harness scope <weekStart>@<plan revision>; confirm it never holds the database writer and that the scope is the right single-flight key.
cmd/server/main.go wiring, the shopping.NewService signature change and the updated internal/server/*_test.go call sites: confirm nothing reviewed regressed and no assertion was weakened.
No new dependency; no migration; the new SetShoppingListTitle query committed and not hand-edited; no web change.
Test adequacy (the developer reports mutations each caught, including titling off and re-titling every generation), maintainability, error handling.
## Task
Maestro task `fa4b94c9-6623-41ab-8d2a-0ca7d1e6401a` — "archive_title skill" (Feature 17, task 3 of 7).
## Done-condition
`internal/ai/skills` implements `archive_title` v1: input is the week's dinner names in day order, output `{title}`; validators require 1–48 characters, no emoji, no surrounding quotes. It runs when a list is first generated and stores `shopping_lists.title`; if it fails, the list is still created with `title` null and the UI shows the week label instead, and the title is retried on the next generation (title failure never blocks the list). Golden tests with the Fake provider cover a valid title, a too-long title repaired, an emoji rejected, and the failure path leaving the list intact.
## Scope
The skill and its wiring into generation. No freeze/archive API (`7ac36526`), no UI.
## Points for review
- The skill follows the existing pattern (Skill interface, harness `Run` with its one repair turn, embedded prompt template plus checksums file, golden tests under `testdata`): confirm no second way to declare a skill and that the validators reject empty, over-48, emoji and quote-wrapped titles through the repair turn rather than by silent truncation.
- The failure path: a title failure leaves the list created with `title` null, logs a warning, records the failure in `ai_runs`, and never blocks the list — and nothing fabricates a title.
- **The retry semantics**, which the developer had to define: after its transaction commits, every `Generate`/`GeneratePushed` checks whether the stored list is still untitled and titles it if so; the store is conditional (`title IS NULL AND status='current' AND frozen_at IS NULL`), so a titled list is never re-titled (even after a swap) and two concurrent generations cannot overwrite each other. Confirm that is airtight and that a title is written exactly once.
- **Two things the developer flagged as hand-offs, which deserve your judgement:**
1. **The title call is synchronous**, so `POST /shopping/{weekStart}/generate` **and the Push to Calendar response** now wait for it, bounded by the harness's 60-second default timeout. Judge whether adding up to a minute to the push response is acceptable, or whether it needs to be decoupled/asynchronous.
2. An **untitled list that is frozen** into the Supply archive stays untitled forever (generating a frozen list returns `ErrFrozen`), and the developer suggests task `7ac36526` may want to decide whether to title at freeze time. Say whether that is acceptable or should be recorded for that task.
- The title call runs outside the transaction, with the harness scope `<weekStart>@<plan revision>`; confirm it never holds the database writer and that the scope is the right single-flight key.
- `cmd/server/main.go` wiring, the `shopping.NewService` signature change and the updated `internal/server/*_test.go` call sites: confirm nothing reviewed regressed and no assertion was weakened.
- No new dependency; no migration; the new `SetShoppingListTitle` query committed and not hand-edited; no web change.
- Test adequacy (the developer reports mutations each caught, including titling off and re-titling every generation), maintainability, error handling.
## Evidence (orchestrator-run gate at fdfd0123fb12d7cc95ca3a65131dafdf438ec13f)
- `gofmt -l .` — empty; `go vet ./...` — clean
- `go test -race -count=1 ./...` — 27 packages ok, 0 failures (skills 26.6s, shopping 9.5s, server 215.9s)
- `CGO_ENABLED=0 go build ./...` — ok
- `make sqlc-check SQLC=$(go env GOPATH)/bin/sqlc` — no drift
- `cd web && npm run typecheck && npm test && npm run build` — PASS (no web change)
Implementation commit: `fdfd0123fb12d7cc95ca3a65131dafdf438ec13f`
archive_title v1 writes a Supply run's post title for the Supply archive
from the week's dinner names in day order. Validation requires 1-48
characters on one line, no emoji and no surrounding quotation marks; a
reply breaking a rule gets the harness's one repair turn.
shopping.Generate and GeneratePushed run it, outside the generation's
transaction, whenever the stored list is still untitled, and store
shopping_lists.title only while the list is untitled and not frozen. A
title failure never fails the generation: the list stays untitled, the
failure is logged and recorded in ai_runs, and the week's next
generation tries again. A titled list keeps its title on remakes.
Maestro task fa4b94c9-6623-41ab-8d2a-0ca7d1e6401a.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
archive_title's single-flight scope was <weekStart>@<plan revision>, but
renaming a planned dinner changes the names to title without changing
the plan revision, so two overlapping generations across a rename could
share one model result. The scope is now
<weekStart>@<plan revision>/<sha256 of the ordered dinner names>, the
same list the skill is given, through the harness's own guard.
Push to Calendar and POST /shopping/{weekStart}/generate also waited
for the title call. Generation now enqueues a title_supply_run job
(service-only, in the generation's transaction) for a list still
untitled and returns without waiting; the list reads untitled until the
job stores its title. A failed title still leaves the list untitled, is
logged, recorded in ai_runs, fails the job title_failed, and is retried
by the week's next generation. A titled list enqueues no job.
Maestro task fa4b94c9-6623-41ab-8d2a-0ca7d1e6401a.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Task
Maestro task
fa4b94c9-6623-41ab-8d2a-0ca7d1e6401a— "archive_title skill" (Feature 17, task 3 of 7).Done-condition
internal/ai/skillsimplementsarchive_titlev1: input is the week's dinner names in day order, output{title}; validators require 1–48 characters, no emoji, no surrounding quotes. It runs when a list is first generated and storesshopping_lists.title; if it fails, the list is still created withtitlenull and the UI shows the week label instead, and the title is retried on the next generation (title failure never blocks the list). Golden tests with the Fake provider cover a valid title, a too-long title repaired, an emoji rejected, and the failure path leaving the list intact.Scope
The skill and its wiring into generation. No freeze/archive API (
7ac36526), no UI.Points for review
Runwith its one repair turn, embedded prompt template plus checksums file, golden tests undertestdata): confirm no second way to declare a skill and that the validators reject empty, over-48, emoji and quote-wrapped titles through the repair turn rather than by silent truncation.titlenull, logs a warning, records the failure inai_runs, and never blocks the list — and nothing fabricates a title.Generate/GeneratePushedchecks whether the stored list is still untitled and titles it if so; the store is conditional (title IS NULL AND status='current' AND frozen_at IS NULL), so a titled list is never re-titled (even after a swap) and two concurrent generations cannot overwrite each other. Confirm that is airtight and that a title is written exactly once.POST /shopping/{weekStart}/generateand the Push to Calendar response now wait for it, bounded by the harness's 60-second default timeout. Judge whether adding up to a minute to the push response is acceptable, or whether it needs to be decoupled/asynchronous.ErrFrozen), and the developer suggests task7ac36526may want to decide whether to title at freeze time. Say whether that is acceptable or should be recorded for that task.<weekStart>@<plan revision>; confirm it never holds the database writer and that the scope is the right single-flight key.cmd/server/main.gowiring, theshopping.NewServicesignature change and the updatedinternal/server/*_test.gocall sites: confirm nothing reviewed regressed and no assertion was weakened.SetShoppingListTitlequery committed and not hand-edited; no web change.Evidence (orchestrator-run gate at
fdfd0123fb)gofmt -l .— empty;go vet ./...— cleango test -race -count=1 ./...— 27 packages ok, 0 failures (skills 26.6s, shopping 9.5s, server 215.9s)CGO_ENABLED=0 go build ./...— okmake sqlc-check SQLC=$(go env GOPATH)/bin/sqlc— no driftcd web && npm run typecheck && npm test && npm run build— PASS (no web change)Implementation commit:
fdfd0123fb12d7cc95ca3a65131dafdf438ec13fIndependent Codex gpt-6-sol review: CHANGES_REQUIRED for this exact commit.
archive_title's single-flight scope was <weekStart>@<plan revision>, but renaming a planned dinner changes the names to title without changing the plan revision, so two overlapping generations across a rename could share one model result. The scope is now <weekStart>@<plan revision>/<sha256 of the ordered dinner names>, the same list the skill is given, through the harness's own guard. Push to Calendar and POST /shopping/{weekStart}/generate also waited for the title call. Generation now enqueues a title_supply_run job (service-only, in the generation's transaction) for a list still untitled and returns without waiting; the list reads untitled until the job stores its title. A failed title still leaves the list untitled, is logged, recorded in ai_runs, fails the job title_failed, and is retried by the week's next generation. A titled list enqueues no job. Maestro task fa4b94c9-6623-41ab-8d2a-0ca7d1e6401a. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>Independent Codex gpt-6-sol review: APPROVED for this exact commit.