syntaxai/tdd.md · commit 9b78ba7

/goal pending: migrate-historical-goals

First commit of PR — dogfoods the workflow this very goal defines.
status: pending, merge_sha: null. Updated to shipped + filled SHA in
the final commit before deploy.

Co-Authored-By: Claude Opus 4.7 <[email protected]>
author
syntaxai <[email protected]>
date
2026-05-25 14:36:22 +01:00
parent
6aa4ff4
commit
9b78ba71b6f74e60436e336c20187534f01c52a6

1 file changed · +84 −0

added goals/migrate-historical-goals.md +84 −0
@@ -0,0 +1,84 @@
1+---
2+slug: migrate-historical-goals
3+title: Migrate historical /goals + lock down the authoring workflow
4+date: 2026-05-25
5+branch: migrate-historical-goals
6+pr_number: null
7+merge_sha: null
8+status: pending
9+related_posts: [sama-v2-goal-chain-gap]
10+---
11+
12+Goal: Recover every /goal command this session and earlier sessions produced from the PR descriptions, commit bodies, and conversation context that captured them; populate goals/ with one .md per recoverable goal classified honestly by recovery fidelity; mark unrecoverable goals as status: lost in the registry without polluting /goals with stub files; and lock down the authoring workflow so future /goals automatically land as committed files BEFORE any code is written AND get embedded verbatim into the PR body so they survive even if goals/ is later corrupted. Closes the "we lose /goals" gap with eyes open about historical losses.
13+
14+Extend GoalStatus first (one-line type change):
15+- src/a31_goals.ts: `type GoalStatus = "pending" | "shipped" | "lossy" | "lost" | "abandoned"`
16+- Semantics:
17+ * shipped — /goal text recovered VERBATIM from the PR body or conversation. The goals/<slug>.md file contains the exact text the user typed. Audit-grade.
18+ * lossy — /goal text recovered from conversation context that was itself paraphrased (e.g. the prior-session summary at the start of this conversation). File exists with the recovered text PLUS an explicit note: "> ⚠ Recovered from conversation summary, not verbatim from PR body. Original text may differ in wording from what the user typed."
19+ * lost — no recoverable text. Entry in ALL_GOALS, NO file on disk. The detail page renders metadata-only (PR link, commit link, related posts) with an honest "the /goal text could not be recovered from any source" message.
20+ * pending — in-flight goal, not yet merged. mergeSha and prNumber are null.
21+ * abandoned — the work was started but the PR was closed without merging. Used for future cases, no historical entries expected.
22+
23+Detail handler change (src/d21_handlers_goals.ts):
24+- For status: "lost", DO NOT call Bun.file().exists() → skip the file-read entirely. Render the badges header + a "Source could not be recovered" callout block + the related_posts links. Return 200, not 404.
25+- For status: "shipped" and "lossy", keep the existing file-read path. For "lossy", prepend the warning block to the rendered body.
26+
27+Done when:
28+- gh pr list --state merged --limit 30 --json number,title,body,headRefName,mergedAt,mergeCommit fetched and inspected. Every PR classified as one of:
29+ a. /goal-driven AND verbatim /goal text in body → shipped
30+ b. /goal-driven AND only summary in body BUT recoverable from conversation context → lossy
31+ c. /goal-driven AND no recoverable text → lost
32+ d. NOT /goal-driven (Containerfile hotfix, image redesigns, blog-post-only PRs, typo fixes) → excluded from migration entirely
33+- Classification rule for "is this PR /goal-driven?": the conversation-or-PR-body source text MUST start with literal "Goal:" AND contain "Done when:" AND contain "Constraints" or "Constraints (anti-fudge):" AND contain "Load-bearing files" or "Load-bearing files to read FIRST:". All four markers required — anything missing → not a /goal-driven PR, excluded.
34+- For every shipped + lossy entry: a goals/<slug>.md file exists with full frontmatter (slug, title, date from mergedAt, branch from headRefName, pr_number, merge_sha as 7-char short, status, related_posts).
35+- For every lost entry: a registry-only entry in ALL_GOALS with status: lost, NO file on disk. Detail page renders metadata-only at 200.
36+- ALL_GOALS in src/a31_goals.ts contains every classified entry, sorted by date descending. The git-url-drop-owner entry already in ALL_GOALS stays.
37+- /goals index shows every entry with its status badge (✓ shipped / ⚠ lossy / ✗ lost / ⏳ pending / ✗ abandoned).
38+- /goals/<slug> for a lost goal returns 200 with metadata-only content, not 404.
39+- /goals/<slug> for a lossy goal returns 200 with the warning banner prepended to the recovered body.
40+- src/b32_goals_meta.test.ts gains 2 new test cases: status: "lossy" parses correctly; status: "lost" parses correctly.
41+
42+Workflow lock-in (defense-in-depth):
43+- Write a NEW feedback memory file at /var/home/scri/.claude/projects/-var-home-scri-Documents-tdd-md/memory/feedback_goal_authoring_workflow.md with this content (paraphrased — the memory file itself should be in your normal feedback-memory style with **Why:** + **How to apply:** sections):
44+ Rule: When the user fires a /goal slash command, the agent's FIRST tool call (before any Read, Bash, or other Edit) is to write the verbatim /goal body to goals/<slug>.md with frontmatter status: pending, merge_sha: null, pr_number: null, date: <today>, branch: <intended branch name>, title: <one-line title from the Goal: paragraph>, related_posts: []. Commit this on a new branch as the FIRST commit of the PR.
45+ Additionally, when creating the PR via `gh pr create`, the --body MUST include the verbatim /goal text as the first section (under a "## /goal" heading), followed by the existing "## Summary" + "## Test plan" sections. This is the defense-in-depth: even if goals/ is corrupted, the PR body always has the verbatim text.
46+ After merge, the agent's FINAL commit before deploy updates merge_sha + flips status to shipped in the same goals/<slug>.md file.
47+ Why: the empirical chain has historically had a hole the shape of goal.md (see /blog/sama-v2-goal-chain-gap). The two redundant captures — goals/ file AND PR body — close the hole twice.
48+ How to apply: triggered on the literal token "/goal" in user message OR when user pastes a "Goal: ... Done when: ... Constraints: ... Load-bearing files:"-shaped block. Skip if user is asking ABOUT a /goal rather than firing one (e.g. "should I fire this /goal?", "look at this /goal" — those are questions not invocations).
49+- Add a one-line index entry in MEMORY.md pointing at the new file, between feedback_jolo_mode and feedback_flatpak_host_tools (the related JOLO pacing memory + the github-flow memory are this rule's neighbors).
50+- Add `/goal.md` (the repo-root scratch file) to .gitignore so it can never accidentally land in a commit. Do NOT delete goal.md — it may have in-flight content; just ignore it.
51+
52+Containerfile anti-fudge (lessons-learned from PR #46):
53+- Verify that no new top-level directory was added during this /goal's work. If one was (e.g. an inadvertent goals_archive/ or similar), it MUST have a corresponding `COPY <dir> ./<dir>` line in Containerfile and a live-verify step that fetches a file from that directory after deploy.
54+- The existing goals/ COPY line from PR #46 stays. We don't need a second one; this clause is forward-looking for any future migration.
55+
56+Anti-fudge constraints:
57+- Recovered text is what it is. Don't paraphrase to look better. lossy means lossy; mark it with the warning banner.
58+- Don't invent /goal text for PRs that don't have any. lost is lost.
59+- The 4-marker classification rule (Goal: + Done when: + Constraints + Load-bearing files) is the SINGLE filter. Don't add fuzzy "this feels like a /goal" judgments.
60+- New feedback memory is ADDITIVE — does not edit feedback_jolo_mode or feedback_bypass_permissions_pacing.
61+- goal.md at repo root: .gitignore'd, not deleted.
62+- Site language English-only.
63+- GitHub flow via flatpak-spawn (branch → PR → merge → push p620 → deploy).
64+- /sama/v2/verify still 7/7 ✓ after deploy (anti-fudge).
65+- All 400+ tests still pass; new tests for lossy/lost status parsing.
66+
67+Live-verify after deploy:
68+- curl https://tdd.md/goals → 200 with multiple rows, each with a status badge
69+- For at least one lost entry: curl https://tdd.md/goals/<slug> → 200 with "Source could not be recovered" message
70+- For at least one lossy entry: curl https://tdd.md/goals/<slug> → 200 with the "Recovered from conversation summary" warning visible
71+- curl https://tdd.md/sitemap.xml | grep -c /goals/ → matches the count of shipped + lossy + lost entries (lost entries DO get sitemap URLs — they're still indexable pages)
72+- /sama/v2/verify → 7/7 ✓
73+
74+Load-bearing files to read FIRST:
75+- src/a31_goals.ts (GoalStatus type — extend with lossy + lost)
76+- src/b32_goals_meta.ts (frontmatter parser — verify it accepts the two new status values mechanically once STATUS_VALUES is extended)
77+- src/b32_goals_meta.test.ts (extend with two new status-parsing cases)
78+- src/d21_handlers_goals.ts (detail handler — branch on status: "lost" before file-read)
79+- The output of: flatpak-spawn --host gh pr list --state merged --limit 30 --json number,title,body,headRefName,mergedAt,mergeCommit
80+- The output of: flatpak-spawn --host gh pr view <num> --json body for any PR whose `body` field in the list output looks /goal-shaped (saves a second fetch for the long body text)
81+- /var/home/scri/.claude/projects/-var-home-scri-Documents-tdd-md/memory/MEMORY.md (the auto-memory index)
82+- /var/home/scri/.claude/projects/-var-home-scri-Documents-tdd-md/memory/feedback_jolo_mode.md (existing pacing memory — new workflow memory's neighbor; cite it as related)
83+- .gitignore at repo root (add /goal.md line)
84+- content/blog/sama-v2-goal-chain-gap.md (the drama post that motivates this — re-read so the recovery narrative stays consistent)