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>
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>