/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]>
1 file changed · +84 −0
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) | |