Add the archive_title skill and title Supply runs on generation
CI / ci (push) Successful in 4m48s
CI / ci (pull_request) Successful in 4m53s

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>
This commit is contained in:
2026-10-08 05:31:59 -04:00
co-authored by Claude Opus 5.5
parent c4ea7a855e
commit fdfd0123fb
16 changed files with 822 additions and 21 deletions
+8 -2
View File
@@ -211,16 +211,22 @@ func newTestPlan(t *testing.T) *plan.Service {
}
// newTestShopping returns a shopping service over a migrated temp
// database.
// database, whose Supply runs stay untitled.
func newTestShopping(t *testing.T) *shopping.Service {
t.Helper()
svc, err := shopping.NewService(newTestStore(t))
svc, err := shopping.NewService(newTestStore(t), untitled, slog.New(slog.NewTextHandler(io.Discard, nil)))
if err != nil {
t.Fatal(err)
}
return svc
}
// untitled is a shopping.Titler with no model: every Supply run it is
// asked to title stays untitled.
func untitled(context.Context, string, skills.ArchiveTitleRequest) (skills.ArchiveTitle, error) {
return skills.ArchiveTitle{}, errors.New("no model in this test")
}
// newTestBox returns a secret box under a random key.
func newTestBox(t *testing.T) *secretbox.Box {
t.Helper()