Post-Mortem Skill Purpose: Wrap up completed work — validate it shipped correctly, extract learnings, process the knowledge backlog, activate high-value insights, and retire stale knowledge. Runtime note: Hook-driven closeout is runtime-dependent. Claude/OpenCode can wire Phase 2-5 maintenance through lifecycle hooks. Codex CLI v0.115.0+ supports native hooks (same behavior). For older Codex versions without hook surfaces, finish closeout with . Loop position Move 7 (capture evidence + learning, then ratchet) of the operating loop. Two outputs per loop turn: evidence (test names, snapshot key…

; then\n FAILURES=\"${FAILURES}\\n- PHANTOM: $child — generic title '$TITLE', no spec\"\n fi\n\n # Empty or minimal description\n DESC_WORDS=$(echo \"$DESC\" | wc -w | tr -d ' ')\n if [ \"$DESC_WORDS\" -lt 5 ]; then\n FAILURES=\"${FAILURES}\\n- PHANTOM: $child — description has $DESC_WORDS words (min 5)\"\n fi\ndone\n```\n\n**Why this matters:** Phantom beads inflate completion metrics without representing real work. In the na-oh2 audit, 11 children all had \"task\" as their title — only the git commit revealed what they actually did.\n\n### Check 3: Orphaned Children\n\nVerify all children in `bd list` are linked to parent.\n\n```bash\n# Children from parent's perspective\nPARENT_CHILDREN=$(bd show \"$EPIC_ID\" 2>/dev/null | grep '↳' | grep -oP '\\w+-\\w+\\.\\d+')\n\n# Children from list (matching prefix)\nLIST_CHILDREN=$(bd list --all 2>/dev/null | grep \"^. ${EPIC_ID}\\.\" | grep -oP '\\w+-\\w+\\.\\d+')\n\n# Find orphans (in list but not in parent)\nfor child in $LIST_CHILDREN; do\n if ! echo \"$PARENT_CHILDREN\" | grep -q \"^${child}$\"; then\n FAILURES=\"${FAILURES}\\n- ORPHAN: $child — exists in bd list but not linked to $EPIC_ID\"\n fi\ndone\n```\n\n### Check 4: Multi-Wave Regression Detection\n\nFor multi-wave epics (crank), compare each wave's additions against the next wave's deletions.\n\n```bash\n# Get wave commits from crank notes\nWAVE_COMMITS=$(bd show \"$EPIC_ID\" 2>/dev/null | grep 'CRANK_WAVE' | grep -oP 'at \\K\\S+')\n\n# For each consecutive pair, check if Wave N+1 deleted lines Wave N added\nPREV_COMMIT=\"\"\nfor commit in $WAVE_COMMITS; do\n if [ -n \"$PREV_COMMIT\" ]; then\n # Lines added in previous wave\n ADDED=$(git diff \"$PREV_COMMIT\"^..\"$PREV_COMMIT\" 2>/dev/null | grep '^+[^+]' | sort)\n # Lines removed in current wave\n REMOVED=$(git diff \"$commit\"^..\"$commit\" 2>/dev/null | grep '^-[^-]' | sort)\n\n # Intersection = regressions\n REVERTED=$(comm -12 \u003c(echo \"$ADDED\" | sed 's/^+//') \u003c(echo \"$REMOVED\" | sed 's/^-//') 2>/dev/null | head -10)\n\n if [ -n \"$REVERTED\" ]; then\n FAILURES=\"${FAILURES}\\n- REGRESSION: Wave removed lines that prior wave added:\\n$(echo \"$REVERTED\" | head -5)\"\n fi\n fi\n PREV_COMMIT=\"$commit\"\ndone\n```\n\n**Origin:** na-vs9.4 — Wave 1 added vibe checkpoint detection (15 lines), Wave 2 removed it entirely. Both waves passed tests independently. The orphaned checkpoint writer in crank was only caught by manual audit.\n\n### Check 5: Acceptance-Text vs Delivered Drift\n\nFor each closed child in scope, read the bead's `Acceptance:` (or `ACCEPTANCE CRITERIA:`) text and check whether it has obviously drifted from what the closure commit delivered. Catches a real failure mode from the v2.41-evolve-run arc: `soc-w6vh.4` had acceptance \"`check-no-tracked-agents` AND `check-worktree-disposition` pass\", but was closed when the operator-blocker was resolved — the gate itself still failed. The bead's acceptance language and the delivered evidence mismatched.\n\n```bash\nfor child in $CLOSED_CHILDREN; do\n ACCEPT=$(bd show \"$child\" 2>/dev/null \\\n | awk '/^Acceptance:/,/^[A-Z][A-Z]+:|^---/' \\\n | head -50)\n CLOSE_NOTE=$(bd show \"$child\" 2>/dev/null \\\n | awk '/COMMENTS/,0' \\\n | tail -30)\n\n # Look for explicit gate references in the acceptance text. Acceptance that\n # names a specific script as a pass requirement is a drift hazard: closing\n # the bead without running the script (or before the script passes) is the\n # mismatch we want to catch.\n GATE_REFS=$(printf '%s\\n' \"$ACCEPT\" \\\n | grep -oE '(scripts/check-[a-z0-9-]+\\.sh|check-[a-z0-9-]+|pre-push-gate|ci-local-release)' \\\n | sort -u)\n\n if [ -n \"$GATE_REFS\" ]; then\n # For each gate named in the acceptance, look for a corresponding \"PASS\"\n # or \"passes\" reference in the close-note (operator confirmation that the\n # gate ran green at closure time). Missing confirmation → drift WARN.\n for gate in $GATE_REFS; do\n if ! printf '%s\\n' \"$CLOSE_NOTE\" | grep -qiE \"$gate.*pass|$gate.*green|pass.*$gate|$gate.*ok\\\\b\"; then\n FAILURES=\"${FAILURES}\\n- ACCEPTANCE DRIFT: $child — acceptance names gate '$gate' as pass requirement, close-note does not confirm green\"\n fi\n done\n fi\n\n # Second heuristic: acceptance contains \"OR\" alternatives — verify the close\n # note articulates which branch was taken.\n if printf '%s\\n' \"$ACCEPT\" | grep -qE '\\bOR\\b'; then\n if ! printf '%s\\n' \"$CLOSE_NOTE\" | grep -qiE 'chose|picked|path [0-9]|operator chose|alternative'; then\n FAILURES=\"${FAILURES}\\n- ACCEPTANCE BRANCH UNCLEAR: $child — acceptance has OR alternatives, close-note does not state which was satisfied\"\n fi\n fi\ndone\n```\n\nThis check is **WARN-level**, not FAIL — false positives are likely (closure notes might use different wording than the acceptance). The intent is to surface drift for council review, not auto-fail.\n\nOrigin: v2.41-evolve-run cycle 182. `soc-w6vh.4` acceptance: \"`check-no-tracked-agents` AND `check-worktree-disposition` pass\". Close-note correctly described the operator's chosen action (commit the worktree) but did NOT include language showing the worktree-disposition gate was green at close — because it wasn't (still fails on broader fleet state). A correct closure would either (a) explicitly narrow the bead's acceptance before closing, or (b) leave the bead open with a scope-narrowing follow-up filed.\n\n### Check 6: Stretch Goal Audit\n\nFor children tagged \"stretch\" that were closed, verify either implementation exists or deferral is documented.\n\n```bash\nfor child in $(bd children \"$EPIC_ID\" 2>/dev/null | grep -i 'stretch' | grep -oE '[a-z]{2}-[a-z0-9]+\\.[0-9]+' | sort -u); do\n STATUS=$(bd show \"$child\" 2>/dev/null | grep -oP 'CLOSED')\n CLOSE_REASON=$(bd show \"$child\" 2>/dev/null | grep 'Close reason:')\n COMMITS=$(git log --oneline --all --grep=\"$child\" 2>/dev/null | wc -l | tr -d ' ')\n\n if [ -n \"$STATUS\" ] && [ \"$COMMITS\" -eq 0 ]; then\n if ! echo \"$CLOSE_REASON\" | grep -qi 'defer\\|stretch\\|intentional\\|not needed'; then\n FAILURES=\"${FAILURES}\\n- STRETCH CLOSED WITHOUT RATIONALE: $child — no commits, no deferral reason\"\n fi\n fi\ndone\n```\n\n## Output Format\n\nWrite results into the post-mortem report under `## Closure Integrity`:\n\n```markdown\n## Closure Integrity\n\n| Check | Result | Details |\n|-------|--------|---------|\n| Evidence Precedence | PASS/WARN/FAIL | N children resolved by commit/staged/worktree, M without evidence |\n| Phantom Beads | PASS/WARN | N phantom beads detected |\n| Orphaned Children | PASS/WARN | N orphans found |\n| Multi-Wave Regression | PASS/FAIL | N regressions detected |\n| Acceptance Drift | PASS/WARN | N closed beads whose acceptance text references gates that the close-note does not confirm green |\n| Stretch Goals | PASS/WARN | N stretch goals closed without rationale |\n\n### Findings\n- \u003cspecific findings from each check>\n```\n\n## Integration with Council\n\nInclude closure integrity results in the council packet:\n\n```json\n{\n \"context\": {\n \"closure_integrity\": {\n \"git_evidence_failures\": [...],\n \"evidence_modes\": {\n \"commit\": [...],\n \"staged\": [...],\n \"worktree\": [...]\n },\n \"phantom_beads\": [...],\n \"orphaned_children\": [...],\n \"wave_regressions\": [...],\n \"stretch_audit\": [...]\n }\n }\n}\n```\n\nThe `plan-compliance` judge uses these to assess whether the epic should actually be marked complete.\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":18266,"content_sha256":"c20316b50b26f6a150b1ade30d4bc9c15ba0a75cbc4cc5a0fe223c584a0f4828"},{"filename":"references/compound-engineering-retro.md","content":"# Compound-Engineering Retro — Comparative Delta Mode\n\nCompare iteration N vs N-1 of a domain slice using the F5.1 verdict ledger\nand emit a structured learning with the delta.\n\n**Preconditions:** the project has a `docs/domains/\u003cname>/manifest.yaml` (a\ndomain slice) and the verdict ledger at `.agents/goals/verdict-ledger.json`\ncontains ≥2 `iteration` records for at least one of the slice's directives\n(i.e., `ao goals measure` has been run at least twice).\n\n---\n\n## Step CE.0: Identify the slice and its directives\n\n```bash\nSLICE=\"\u003cname>\" # e.g. \"billing\"\nMANIFEST=\"docs/domains/${SLICE}/manifest.yaml\"\ncat \"$MANIFEST\" | grep directive_id # list directive IDs owned by the slice\n```\n\nCollect the stable `d-\u003cid>` directive IDs from the manifest's `directives` list.\n\n## Step CE.1: Extract iteration N and N-1 from the verdict ledger\n\nThe ledger is an append-only JSON file. Each entry with `\"record_type\": \"iteration\"`\nrepresents one completed `ao goals measure` call.\n\n```bash\nLEDGER=\".agents/goals/verdict-ledger.json\"\n\n# For each directive in the slice, pull the last two iteration records:\njq --arg did \"\u003cdirective-id>\" '\n [.records[] | select(.record_type == \"iteration\" and .directive_id == $did)]\n | if length \u003c 2 then error(\"need ≥2 iterations\") else . end\n | .[-2:] | {prev: .[0], curr: .[1]}\n' \"$LEDGER\"\n```\n\nRepeat for every directive in the slice. Collect results into `PREV_MAP` (directive →\nN-1 record) and `CURR_MAP` (directive → N record).\n\n## Step CE.2: Compute the delta\n\nFor each directive, compare N vs N-1 on two axes:\n\n| Axis | Improved | Regressed | Stable |\n|------|----------|-----------|--------|\n| **Verdict** | fail → pass | pass → fail | same |\n| **Satisfaction** | `curr - prev > 0.05` | `curr - prev \u003c -0.05` | within ±0.05 |\n\n```bash\n# Quick shell delta across all directives (reads the ledger directly):\njq --argjson dids '[\"d-foo\",\"d-bar\"]' '\n [ .records[] | select(.record_type == \"iteration\" and ([.directive_id] | inside($dids))) ]\n | group_by(.directive_id)\n | map({\n id: .[0].directive_id,\n prev: .[-2],\n curr: .[-1],\n verdict_delta: (.[-2].scenario_verdict + \"→\" + .[-1].scenario_verdict),\n sat_delta: (.[-1].scenario_satisfaction - .[-2].scenario_satisfaction)\n })\n' \"$LEDGER\"\n```\n\nClassify each directive:\n\n- **Improved**: verdict fail→pass, OR satisfaction up > 0.05\n- **Regressed**: verdict pass→fail, OR satisfaction down > 0.05\n- **Stable**: everything else\n\n## Step CE.3: Measure learning yield\n\nCount learnings drafted since N-1's `run_timestamp`:\n\n```bash\nPREV_TS=\"\u003crun_timestamp of N-1 record>\" # ISO-8601 from ledger\nfind .agents/learnings/ -name \"*.md\" -newer \u003c(date -d \"$PREV_TS\" +%Y-%m-%d) 2>/dev/null | wc -l\n```\n\nAlso note whether any `cooldown` records were written to the ledger between N-1 and N\n(re-steer proposals from the F5.3 engine).\n\n## Step CE.4: Write the delta as a learning\n\nWrite to `.agents/learnings/YYYY-MM-DD-\u003cslice>-iter-delta.md`:\n\n```markdown\n---\nid: learning-YYYY-MM-DD-\u003cslice>-iter-delta\ntype: learning\ndate: YYYY-MM-DD\ncategory: feedback-loop\nconfidence: \u003chigh|medium|low>\nmaturity: provisional\nstatus: draft\nutility: 0.5\nslice: \u003cname>\niter_n_run_id: \u003crun_id of N record>\niter_prev_run_id: \u003crun_id of N-1 record>\n---\n\n# Compound-Engineering Retro: \u003cslice> iteration delta\n\n## What Improved\n\n| Directive | Verdict N-1→N | Satisfaction N-1→N |\n|-----------|---------------|--------------------|\n| d-foo | fail→pass | 0.40→0.85 (+0.45) |\n\n## What Regressed\n\n| Directive | Verdict N-1→N | Satisfaction N-1→N |\n|-----------|---------------|--------------------|\n| d-bar | pass→fail | 0.90→0.60 (−0.30) |\n\n## Stable Directives\n\n- d-baz: pass→pass, 0.70→0.72 (stable)\n\n## Learning Yield (since N-1)\n\nN learnings drafted. N cooldown proposals in ledger.\n\n## Root-Cause Hypotheses\n\n\u003c1-2 sentences on why regressions occurred, if known>\n\n## Follow-Up Actions\n\n- [ ] Investigate d-bar regression: \u003chypothesis>\n- [ ] Promote if delta is validated next run\n```\n\n`status: draft` — human or Tier-3 synthesis promotes to `status: reviewed`.\n\n## Step CE.5: Feed next-work (optional)\n\nIf regressions are high-severity, append an entry to `.agents/rpi/next-work.jsonl`\nper the claim/finalize lifecycle in `references/harvest-next-work.md`.\n\n---\n\n## Quick Reference\n\n| Command | Purpose |\n|---------|---------|\n| `ao goals measure [--goal \u003cid>]` | Run one iteration; appends to ledger |\n| `ao goals history [--goal \u003cid>]` | Browse past iterations (summary view) |\n| `jq '...' .agents/goals/verdict-ledger.json` | Raw ledger inspection |\n| `cat docs/domains/\u003cname>/manifest.yaml` | List directives owned by a slice |\n\nThe ledger artifact path is `.agents/goals/verdict-ledger.json`\n(defined in `cli/internal/verdictledger/verdictledger.go:ArtifactRelPath`).\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":4856,"content_sha256":"0a33a5e6565e7a21826213b5dc22d48d229644247b327953484318d01178897c"},{"filename":"references/context-gathering.md","content":"# Context Gathering\n\nHow to collect rich context from multiple sources for retrospectives.\n\n## Target Identification\n\n### If Epic ID Provided\n\n```bash\nbd show $ARGUMENTS\n# Extract child issue IDs, query each for comments\n```\n\n### If Topic/Plan Provided\n\n```bash\nls .agents/plans/*$ARGUMENTS* 2>/dev/null\nls .agents/research/*$ARGUMENTS* 2>/dev/null\n```\n\n### If No Argument\n\n```bash\nbd list --status closed | head -10\n```\n\n---\n\n## Conversation Analysis\n\nIf a session ID is available, analyze the Claude Code conversation to extract:\n- Decisions made during implementation\n- Friction encountered (errors, retries, workarounds)\n- Patterns discovered or followed\n- Lessons learned\n\n```bash\n# Analyze specific session\npython3 ~/.claude/scripts/analyze-sessions.py --session=$SESSION_ID\n\n# Analyze with extraction limits for large sessions\npython3 ~/.claude/scripts/analyze-sessions.py --session=$SESSION_ID --limit=50\n```\n\n### Conversation Data → Retro Output Mapping\n\n| Conversation Data | Retro Output |\n|-------------------|--------------|\n| `DecisionExtraction` | `.agents/council/` - Decisions section |\n| `QualityResult.issues` | Friction detection |\n| `DocExtraction(type=\"warning\")` | `.agents/learnings/` |\n| `DecisionExtraction(type=\"pattern\")` | `.agents/patterns/` |\n| `DecisionExtraction(type=\"lesson\")` | What Worked / What Didn't |\n\n### Session ID Sources\n\n1. **Environment variable**: `$CLAUDE_SESSION_ID` (set by Claude Code)\n2. **Recent session detection**: Find most recent `.jsonl` in `~/.claude/projects/`\n3. **Beads comment**: Sessions may be recorded in crank state\n\n### When No Session Available\n\nFall back to git analysis and beads comments. Note in retro that conversation\nanalysis was unavailable.\n\n---\n\n## Git Commit Analysis\n\n```bash\ngit log --oneline --since=\"7 days ago\" | grep -E \"(ai-platform-[a-z0-9]+|$TOPIC)\"\ngit show \u003ccommit-hash> --stat\n```\n\n**Extract:** Files modified, commit messages, lines changed.\n\n---\n\n## Beads Comments\n\n```bash\nbd show \u003cepic-id>\nbd show \u003cchild-id>\n```\n\n**Extract:** Decisions, blockers, workarounds, root causes.\n\n---\n\n## Blackboard State\n\n```bash\nls .agents/blackboard/\ncat .agents/blackboard/crank-state.json 2>/dev/null\n```\n\n---\n\n## Closure Integrity Quick-Check\n\nWhen retrospecting an epic, run lightweight closure checks before extracting learnings. Full audit lives in `skills/post-mortem/references/closure-integrity-audit.md`; retro uses the subset below.\n\n### Phantom Bead Detection\n\n```bash\nEPIC_ID=\"$ARGUMENTS\"\nfor child in $(bd children \"$EPIC_ID\" 2>/dev/null | grep -oP '\\S+' | head -1); do\n TITLE=$(bd show \"$child\" 2>/dev/null | head -1 | sed 's/^.*· //' | sed 's/ \\[.*$//')\n if echo \"$TITLE\" | grep -qP '^(task|fix|update|todo|item|work)

Post-Mortem Skill Purpose: Wrap up completed work — validate it shipped correctly, extract learnings, process the knowledge backlog, activate high-value insights, and retire stale knowledge. Runtime note: Hook-driven closeout is runtime-dependent. Claude/OpenCode can wire Phase 2-5 maintenance through lifecycle hooks. Codex CLI v0.115.0+ supports native hooks (same behavior). For older Codex versions without hook surfaces, finish closeout with . Loop position Move 7 (capture evidence + learning, then ratchet) of the operating loop. Two outputs per loop turn: evidence (test names, snapshot key…

; then\n echo \"WARN: $child has generic title '$TITLE' — possible phantom closure\"\n fi\ndone\n```\n\n### Multi-Wave Regression Scan\n\nFor crank epics, check if later waves reverted earlier waves' work:\n\n```bash\n# Quick heuristic: count net lines added per scoped file across all wave commits\n# If a file has 0 net additions but was touched in multiple waves, flag it\nfor f in $(git diff --name-only HEAD~5 2>/dev/null | sort -u); do\n ADDS=$(git log --oneline --numstat HEAD~5..HEAD -- \"$f\" 2>/dev/null | awk '/^[0-9]/{s+=$1}END{print s+0}')\n DELS=$(git log --oneline --numstat HEAD~5..HEAD -- \"$f\" 2>/dev/null | awk '/^[0-9]/{s+=$2}END{print s+0}')\n TOUCHES=$(git log --oneline HEAD~5..HEAD -- \"$f\" 2>/dev/null | wc -l | tr -d ' ')\n if [ \"$TOUCHES\" -gt 1 ] && [ \"$ADDS\" -le \"$DELS\" ]; then\n echo \"WARN: $f touched $TOUCHES times but net additions \u003c= deletions — possible wave regression\"\n fi\ndone\n```\n\n**Why:** In na-vs9.4, Wave 2 removed 15 lines that Wave 1 added. Both waves passed tests independently. This check catches the pattern mechanically.\n\n### Retro Integration\n\n- Include closure warnings in **What Could Be Improved** section\n- If phantom beads found, add learning: \"Require meaningful titles and descriptions for all child beads before closing epic\"\n- If wave regressions found, add learning: \"Add cross-wave diff validation to crank post-wave checks\"\n\n---\n\n## Friction Detection\n\n### Friction Keywords\n\n```bash\n# Look for friction keywords in comments\ngrep -i \"error\\|failed\\|retry\\|workaround\\|fixed by\\|root cause\" \u003ccomments>\n\n# Look for fix commits\ngit log --oneline | grep -i \"fix\\|revert\\|hotfix\\|patch\"\n```\n\n### Search Prior Solutions\n\n```bash\nls .agents/learnings/ | grep -i \"$TOPIC\"\nls .agents/patterns/ | grep -i \"$TOPIC\"\n```\n\n**If found:** Reference in proposal.\n**If not found:** Mark as \"NEW\" for learning extraction.\n\n### Friction Categories\n\n| Category | Indicators |\n|----------|------------|\n| Retry/Failure | \"Error:\", \"Failed:\", test failures |\n| Manual Fix | User corrections after agent action |\n| Blocking | Dependency issues |\n| Pattern Deviation | Didn't follow established pattern |\n| Missing Information | Had to search for docs |\n\n### Friction → Fix Mapping\n\n| Friction | Fix Location |\n|----------|--------------|\n| Command unclear | `.claude/commands/*.md` |\n| Skill trigger missed | `.claude/skills/**/*.md` |\n| Pattern not followed | `.agents/patterns/*.md` |\n| Convention violated | `CLAUDE.md` |\n\n---\n\n## Supersession Check\n\n### Search Existing Artifacts\n\n```bash\nmcp__smart-connections-work__lookup --query=\"$TOPIC\" --limit=10\ngrep -rl \"$TOPIC\" .agents/learnings/ .agents/patterns/ 2>/dev/null\n```\n\n### Supersession Criteria\n\n| Criterion | Supersede? |\n|-----------|------------|\n| Same topic, newer insight | Yes |\n| Same topic, complementary | No (cross-reference) |\n| Obsolete/incorrect info | Yes |\n\n### Metadata\n\nOld artifact:\n```yaml\nsuperseded_by: .agents/learnings/YYYY-MM-DD-new.md\n```\n\nNew artifact:\n```yaml\nsupersedes: .agents/learnings/YYYY-MM-DD-old.md\n```\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":5749,"content_sha256":"4ddb42a07e2310690d040b375148ced88e429a86951abf8497e5e0d724971819"},{"filename":"references/execution-steps.md","content":"## Execution Steps\n\n### Pre-Flight Checks\n\nBefore proceeding, verify:\n1. **Git repo exists:** `git rev-parse --git-dir 2>/dev/null` — if not, error: \"Not in a git repository\"\n2. **Work was done:** `git log --oneline -1 2>/dev/null` — if empty, error: \"No commits found. Run /implement first.\"\n3. **Epic context:** If epic ID provided, verify it has closed children. If 0 closed children, error: \"No completed work to review.\"\n\n**If `--process-only`:** Skip Pre-Flight Checks through Step 3. Jump directly to Phase 3: Process Backlog.\n\n### Step 0.4: Load Reference Documents (MANDATORY)\n\nBefore Step 0.5 and Step 2.5, load required reference docs into context using the Read tool:\n\n```\nREQUIRED_REFS=(\n \"skills/post-mortem/references/checkpoint-policy.md\"\n \"skills/post-mortem/references/metadata-verification.md\"\n \"skills/post-mortem/references/closure-integrity-audit.md\"\n \"skills/post-mortem/references/four-surface-closure.md\"\n)\n```\n\nFor each reference file, use the **Read tool** to load its content and hold it in context for use in later steps. Do NOT just test file existence with `[ -f ]` -- actually read the content so it is available when Steps 0.5 and 2.5 need it.\n\nIf a reference file does not exist (Read returns an error), log a warning and add it as a checkpoint warning in the council context. Proceed only if the missing reference is intentionally deferred.\n\n### Step 0.5: Checkpoint-Policy Preflight (MANDATORY)\n\nRead `references/checkpoint-policy.md` for the full checkpoint-policy preflight procedure. It validates the ratchet chain, checks artifact availability, and runs idempotency checks. BLOCK on prior FAIL verdicts; WARN on everything else.\n\n### Step 1: Identify Completed Work and Record Timing\n\n**Record the post-mortem start time for cycle-time tracking:**\n```bash\nPM_START=$(date +%s)\n```\n\n**If epic/issue ID provided:** Use it directly.\n\n**If no ID:** Find recently completed work:\n```bash\n# Check for closed beads\nbd list --status closed --since \"7 days ago\" 2>/dev/null | head -5\n\n# Or check recent git activity\ngit log --oneline --since=\"7 days ago\" | head -10\n```\n\n### Step 1.5: RPI Session Metrics\n\nRead `.agents/rpi/rpi-state.json` and extract session ID, phase, verdicts, and streak data. If absent or unparseable, skip silently. Prepend a tweetable summary to the report: `> RPI streak: N consecutive days | Sessions: N | Last verdict: PASS/WARN/FAIL`. See [streak-tracking.md](streak-tracking.md) for extraction logic and fallback behavior.\n\n### Step 2: Load the Original Plan/Spec\n\nBefore invoking council, load the original plan for comparison:\n\n1. **If epic/issue ID provided:** `bd show \u003cid>` to get the spec/description\n2. **Search for plan doc:** `ls .agents/plans/ | grep \u003ctarget-keyword>`\n3. **Check git log:** `git log --oneline | head -10` to find the relevant bead reference\n\nIf a plan is found, include it in the council packet's `context.spec` field:\n```json\n{\n \"spec\": {\n \"source\": \"bead na-0042\",\n \"content\": \"\u003cthe original plan/spec text>\"\n }\n}\n```\n\n### Step 2.1: Load Compiled Prevention Context\n\nBefore council and retro synthesis, load compiled prevention outputs when they exist:\n\n- `.agents/planning-rules/*.md`\n- `.agents/pre-mortem-checks/*.md`\n\nUse these compiled artifacts first, then fall back to `.agents/findings/registry.jsonl` only when compiled outputs are missing or incomplete. Carry matched finding IDs into the retro as `Applied findings` / `Known risks applied` context so post-mortem can judge whether the flywheel actually prevented rediscovery.\n\n### Step 2.2: Load Implementation Summary\n\nCheck for a crank-generated phase-2 summary:\n\n```bash\nPHASE2_SUMMARY=$(ls -t .agents/rpi/phase-2-summary-*-crank.md 2>/dev/null | head -1)\nif [ -n \"$PHASE2_SUMMARY\" ]; then\n echo \"Phase-2 summary found: $PHASE2_SUMMARY\"\n # Read the summary with the Read tool for implementation context\nfi\n```\n\nIf available, use the phase-2 summary to understand what was implemented, how many waves ran, and which files were modified.\n\n### Step 2.3: Reconcile Plan vs Delivered Scope\n\nCompare the original plan scope against what was actually delivered:\n\n1. Read the plan from `.agents/plans/` (most recent)\n2. Compare planned issues against closed issues (`bd children \u003cepic-id>`)\n3. Note any scope additions, removals, or modifications\n4. Include scope delta in the post-mortem findings\n\n### Step 2.4: Closure Integrity Audit (MANDATORY)\n\nRead `references/closure-integrity-audit.md` for the full procedure. Mechanically verifies:\n\n1. **Evidence precedence per child** — every closed child resolves on the strongest available evidence in this order: `commit`, then `staged`, then `worktree`\n2. **Phantom bead detection** — flags children with generic titles (\"task\") or empty descriptions\n3. **Orphaned children** — beads in `bd list` but not linked to parent in `bd show`\n4. **Multi-wave regression detection** — for crank epics, checks if a later wave removed code added by an earlier wave\n5. **Stretch goal audit** — verifies deferred stretch goals have documented rationale\n\nInclude results in the council packet as `context.closure_integrity`. WARN on 1-2 findings, FAIL on 3+.\n\nIf a closure is evidence-only, emit a proof artifact with `bash skills/post-mortem/scripts/write-evidence-only-closure.sh` and cite at `.agents/releases/evidence-only-closures/\u003ctarget-id>.json`. Record `evidence_mode` plus repo-state detail for replayability. A valid durable packet is acceptable audit evidence even when the child intentionally has no scoped-file section.\n\n### Step 2.5: Pre-Council Metadata Verification (MANDATORY)\n\nRead `references/metadata-verification.md` for the full verification procedure. Mechanically checks: plan vs actual files, file existence in commits, cross-references in docs, and ASCII diagram integrity. Failures are included in the council packet as `context.metadata_failures`.\n\n### Step 2.6: Pre-Council Deep Audit Sweep\n\n**Skip if `--quick` or `--skip-sweep`.**\n\nBefore council runs, dispatch a deep audit sweep to systematically discover issues across all changed files. This uses the same protocol as `/vibe --deep` — see the deep audit protocol in the vibe skill (`skills/vibe/`) for the full specification.\n\nIn summary:\n\n1. Identify all files in scope (from epic commits or recent changes)\n2. Chunk files into batches of 3-5 by line count (\u003c=100 lines -> batch of 5, 101-300 -> batch of 3, >300 -> solo)\n3. Dispatch up to 8 Explore agents in parallel, each with a mandatory 8-category checklist per file (resource leaks, string safety, dead code, hardcoded values, edge cases, concurrency, error handling, HTTP/web security)\n4. Merge all explorer findings into a sweep manifest at `.agents/council/sweep-manifest.md`\n5. Include sweep manifest in council packet — judges shift to adjudication mode (confirm/reject/reclassify sweep findings + add cross-cutting findings)\n\n**Why:** Post-mortem council judges exhibit satisfaction bias when reviewing monolithic file sets — they stop at ~10 findings regardless of actual issue count. Per-file explorers with category checklists find 3x more issues, and the sweep manifest gives judges structured input to adjudicate rather than discover from scratch.\n\n**Skip conditions:**\n- `--quick` flag -> skip (fast inline path)\n- `--skip-sweep` flag -> skip (old behavior: judges do pure discovery)\n- No source files in scope -> skip (nothing to audit)\n\n### Step 3: Council Validates the Work\n\n## Council Verdict:\n\nRun `/council` with the **retrospective** preset and always 3 judges:\n\n```\n/council --deep --preset=retrospective validate \u003cepic-or-recent>\n```\n\n**Default (3 judges with retrospective perspectives):**\n- `plan-compliance`: What was planned vs what was delivered? What's missing? What was added?\n- `tech-debt`: What shortcuts were taken? What will bite us later? What needs cleanup?\n- `learnings`: What patterns emerged? What should be extracted as reusable knowledge?\n\nPost-mortem always uses 3 judges (`--deep`) because completed work deserves thorough review.\n\n**Four-Surface Closure:** Validate all four surfaces -- Code, Documentation, Examples, and Proof. A PASS verdict requires all four surfaces addressed, not just code correctness. Read `skills/post-mortem/references/four-surface-closure.md` for the closure checklist and common gaps.\n\n**Timeout:** Post-mortem inherits council timeout settings. If judges time out,\nthe council report will note partial results. Post-mortem treats a partial council\nreport the same as a full report — the verdict stands with available judges.\n\nThe plan/spec content is injected into the council packet context so the `plan-compliance` judge can compare planned vs delivered.\n\n**With --quick (inline, no spawning):**\n```\n/council --quick validate \u003cepic-or-recent>\n```\nSingle-agent structured review. Fast wrap-up without spawning.\n\n**With debate mode:**\n```\n/post-mortem --debate epic-123\n```\nEnables adversarial two-round review for post-implementation validation. Use for high-stakes shipped work where missed findings have production consequences. See `/council` docs for full --debate details.\n\n**Advanced options (passed through to council):**\n- `--mixed` — Cross-vendor (Claude + Codex) with retrospective perspectives\n- `--preset=\u003cname>` — Override with different personas (e.g., `--preset=ops` for production readiness)\n- `--explorers=N` — Each judge spawns N explorers to investigate the implementation deeply before judging\n- `--debate` — Two-round adversarial review (judges critique each other's findings before final verdict)\n\n### Step 3.5: Prediction Accuracy (Pre-Mortem Correlation)\n\nWhen a pre-mortem report exists for the current epic (`ls -t .agents/council/*pre-mortem*.md`), cross-reference its prediction IDs against actual vibe/implementation findings. Score each as HIT (prediction confirmed), MISS (did not materialize), or SURPRISE (unpredicted issue). Write a `## Prediction Accuracy` table in the report. Skip silently if no pre-mortem exists. See [prediction-tracking.md](prediction-tracking.md) for the full table format and scoring rules.\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":10094,"content_sha256":"944d8b3db9e176eba66e0fcce50cc0d1b4b52e18198a8146604ae4871c33c190"},{"filename":"references/four-surface-closure.md","content":"# Four-Surface Closure Validation\n\n> From spec-as-leverage-point analysis and 26 uncaptured retro lessons.\n> The #1 post-mortem gap: validating code was changed but not that all surfaces were updated.\n\n## The Four Surfaces\n\nEvery completed feature or fix touches up to four surfaces. Post-mortem must validate ALL four, not just code:\n\n### Surface 1: Code\n- Implementation matches the spec/plan\n- Tests pass (L0-L3 as appropriate)\n- No regressions introduced\n- Complexity budget respected\n\n### Surface 2: Documentation\n- README/docs updated if user-facing behavior changed\n- API docs reflect new endpoints/parameters\n- SKILL.md updated if skill behavior changed\n- CHANGELOG entry added for releases\n- Inline comments updated where logic changed\n\n### Surface 3: Examples\n- Usage examples updated to reflect new behavior\n- Tutorial/guide code samples still work\n- CLI help text reflects new flags/commands\n- Error messages are accurate and helpful\n\n### Surface 4: Proof\n- Acceptance criteria from the plan are satisfied (runnable gates pass)\n- Test coverage exists for the new behavior (not just existing tests still pass)\n- CI/CD pipeline reflects the change (if infrastructure was modified)\n- Security scan passes if security-sensitive code was changed\n- Performance benchmarks pass if performance-critical code was changed\n\n## Closure Checklist\n\nDuring post-mortem Phase 1 (council validation), each judge should check:\n\n| Surface | Check | Pass Criteria |\n|---------|-------|---------------|\n| Code | `git diff --stat` matches planned scope | All planned files modified, no unplanned files |\n| Code | Test suite passes | Zero failures |\n| Docs | Relevant docs updated | No stale references to old behavior |\n| Docs | Skill counts synced (if skills changed) | `validate-doc-release.sh` passes |\n| Examples | Usage examples tested | Examples produce expected output |\n| Examples | CLI help reflects changes | `--help` output matches implementation |\n| Proof | Acceptance criteria gates run | All gates return 0 |\n| Proof | New test coverage exists | New behavior has dedicated tests |\n| Proof | CI pipeline updated (if needed) | Pipeline runs new checks |\n\n## Common Gaps (from 26 uncaptured retro lessons)\n\n1. **Code ships, docs don't** -- implementation complete but README still describes old behavior\n2. **Tests pass, proof missing** -- existing tests pass but no new test covers the new feature\n3. **Examples rot** -- code samples in docs reference removed functions or old APIs\n4. **Skill counts drift** -- skill added/removed but `sync-skill-counts.sh` not run\n5. **CLI docs stale** -- new flag added but `generate-cli-reference.sh` not run\n6. **Proof by assertion** -- \"it works\" without a runnable command demonstrating it\n\n## Integration with Post-Mortem Phases\n\n### Phase 1 (Council Validation)\nAdd four-surface check to judge instructions:\n> Verify all four surfaces are addressed: Code, Documentation, Examples, and Proof. A PASS verdict requires all four surfaces validated, not just code correctness.\n\n### Phase 2 (Learning Extraction)\nExtract learnings from any surface gaps found:\n> If a surface was missed, create a learning: \"Feature X shipped without [surface] update -- add to pre-mortem checklist.\"\n\n### Phase 5 (Retirement)\nRetire learnings about surface gaps that now have mechanical enforcement:\n> If a CI check now catches the gap (e.g., doc-release-gate catches skill count drift), retire the learning and reference the gate.\n\n## Normalization Defect Check\n\nAfter verifying four-surface closure, check for normalization defects in any knowledge artifacts produced during the implementation:\n\n| Defect | Detection | Severity |\n|---|---|---|\n| Placeholder patterns | File has frontmatter but no content after closing `---` | HIGH — pollutes retrieval |\n| Stacked frontmatter | More than 2 `---` delimiters in a single file | MEDIUM — parsing errors |\n| Bundled learnings | Multiple `## Learning` headings in one file | HIGH — breaks citation tracking |\n| Duplicated headings | Identical `## ` headings within a file | LOW — confuses extraction |\n| Stale contradictions | Contradiction report predates latest extraction | MEDIUM — misleading evidence |\n\nFlag any defects found for the next forge/defrag cycle. Do not ship knowledge artifacts with HIGH-severity normalization defects.\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":4318,"content_sha256":"e21d2662c2fbd184428476f4dd74d9398e35e233f7a621a12d35d54703cd7498"},{"filename":"references/harvest-next-work.md","content":"# Harvest Next Work — Implementation Details\n\n## Schema Validation\n\nBefore writing, validate each harvested item against the tracked schema\ncontract in [../../../docs/contracts/next-work.schema.md](../../../docs/contracts/next-work.schema.md):\n\n```bash\nvalidate_next_work_item() {\n local item=\"$1\"\n local title=$(echo \"$item\" | jq -r '.title // empty')\n local type=$(echo \"$item\" | jq -r '.type // empty')\n local severity=$(echo \"$item\" | jq -r '.severity // empty')\n local source=$(echo \"$item\" | jq -r '.source // empty')\n local description=$(echo \"$item\" | jq -r '.description // empty')\n local proof_kind=$(echo \"$item\" | jq -r '.proof_ref.kind // empty')\n local proof_target_id=$(echo \"$item\" | jq -r '.proof_ref.target_id // empty')\n local proof_run_id=$(echo \"$item\" | jq -r '.proof_ref.run_id // empty')\n local proof_path=$(echo \"$item\" | jq -r '.proof_ref.path // empty')\n\n # Required fields\n if [ -z \"$title\" ] || [ -z \"$description\" ]; then\n echo \"SCHEMA VALIDATION FAILED: missing title or description for item\"\n return 1\n fi\n\n # Type enum validation\n case \"$type\" in\n tech-debt|improvement|pattern-fix|process-improvement|feature|bug|task|docs|chore) ;;\n *) echo \"SCHEMA VALIDATION FAILED: invalid type '$type' for item '$title'\"; return 1 ;;\n esac\n\n # Severity enum validation\n case \"$severity\" in\n high|medium|low) ;;\n *) echo \"SCHEMA VALIDATION FAILED: invalid severity '$severity' for item '$title'\"; return 1 ;;\n esac\n\n # Source enum validation\n case \"$source\" in\n council-finding|retro-learning|retro-pattern|evolve-generator|feature-suggestion|backlog-processing|post-mortem-finding|manifest-classification|dream-degraded) ;;\n *) echo \"SCHEMA VALIDATION FAILED: invalid source '$source' for item '$title'\"; return 1 ;;\n esac\n\n # Optional proof reference validation\n if [ -n \"$proof_kind\" ]; then\n case \"$proof_kind\" in\n completed_run)\n [ -n \"$proof_run_id\" ] || {\n echo \"SCHEMA VALIDATION FAILED: completed_run proof_ref requires run_id for item '$title'\"\n return 1\n }\n ;;\n evidence_only_closure)\n [ -n \"$proof_target_id\" ] || {\n echo \"SCHEMA VALIDATION FAILED: evidence_only_closure proof_ref requires target_id for item '$title'\"\n return 1\n }\n ;;\n execution_packet)\n [ -n \"$proof_path\" ] || {\n echo \"SCHEMA VALIDATION FAILED: execution_packet proof_ref requires path for item '$title'\"\n return 1\n }\n ;;\n *)\n echo \"SCHEMA VALIDATION FAILED: invalid proof_ref.kind '$proof_kind' for item '$title'\"\n return 1\n ;;\n esac\n fi\n\n return 0\n}\n\n# Validate each item; drop invalid items (do NOT block the entire harvest)\nVALID_ITEMS=()\nINVALID_COUNT=0\nfor item in \"${HARVESTED_ITEMS[@]}\"; do\n if validate_next_work_item \"$item\"; then\n VALID_ITEMS+=(\"$item\")\n else\n INVALID_COUNT=$((INVALID_COUNT + 1))\n fi\ndone\necho \"Schema validation: ${#VALID_ITEMS[@]}/$((${#VALID_ITEMS[@]} + INVALID_COUNT)) items passed\"\n```\n\n## Write to next-work.jsonl\n\nCanonical path: `.agents/rpi/next-work.jsonl`\n\n```bash\nmkdir -p .agents/rpi\n\n# Resolve current repo name for target_repo default\nCURRENT_REPO=$(bd config --get prefix 2>/dev/null \\\n || basename \"$(git remote get-url origin 2>/dev/null)\" .git 2>/dev/null \\\n || basename \"$(pwd)\")\n\n# Normalize each validated item before writing:\n# process-improvement → \"*\" (applies across all repos)\n# all other types → CURRENT_REPO (scoped to this repo)\nfor i in \"${!VALID_ITEMS[@]}\"; do\n item=\"${VALID_ITEMS[$i]}\"\n item_type=$(echo \"$item\" | jq -r '.type')\n if [ \"$item_type\" = \"process-improvement\" ]; then\n VALID_ITEMS[$i]=$(echo \"$item\" | jq -c '.target_repo = \"*\"')\n else\n VALID_ITEMS[$i]=$(echo \"$item\" | jq -c --arg repo \"$CURRENT_REPO\" '.target_repo = $repo')\n fi\ndone\n\n# Append one entry per epic (schema v1.4: docs/contracts/next-work.schema.md)\n# Only include VALID_ITEMS that passed schema validation\n# Each item: {title, type, severity, source, description, evidence, target_repo, proof_ref?}\n# Entry aggregate fields: source_epic, timestamp, items[], consumed: false,\n# claim_status: \"available\", claimed_by: null, claimed_at: null,\n# consumed_by: null, consumed_at: null\n# Item lifecycle fields are optional on write and are populated by consumers:\n# claim_status, claimed_by, claimed_at, consumed, consumed_by, consumed_at, failed_at\n# Optional proof_ref shape:\n# {kind, target_id?, run_id?, path?}\n# completed_run => run_id required\n# evidence_only_closure => target_id required\n# execution_packet => path required\n# Consumers may rewrite existing lines to claim, release, fail, or consume\n# existing items. The queue is not append-only after initial write.\n```\n\nWhen a harvested item already maps to a known proof surface, preserve that as\n`proof_ref` instead of burying identifiers in `description` or `evidence`. For\nexample:\n\n```json\n{\n \"title\": \"Verify the next-work parity gate after the repair lands\",\n \"type\": \"task\",\n \"severity\": \"medium\",\n \"source\": \"council-finding\",\n \"description\": \"Re-run the targeted contract validator after proof propagation changes land.\",\n \"target_repo\": \"agentops\",\n \"proof_ref\": {\n \"kind\": \"execution_packet\",\n \"run_id\": \"6f36a5640805\",\n \"path\": \".agents/rpi/runs/6f36a5640805/execution-packet.json\"\n }\n}\n```\n\nUse the Write tool to append a single JSON line to `.agents/rpi/next-work.jsonl` with:\n- `source_epic`: the epic ID being post-mortemed\n- `timestamp`: current ISO-8601\n- `items`: array of harvested items (min 0 — if nothing found, write entry with empty items array)\n- `consumed`: false, `claim_status`: \"available\", `claimed_by`: null, `claimed_at`: null, `consumed_by`: null, `consumed_at`: null\n\n## Queue Lifecycle\n\nWriters always append entries in **available** state. Consumers use a claim/finalize lifecycle. In batched entries, lifecycle is tracked per item; the entry-level fields are aggregate summaries only:\n\n1. **available**: item has `consumed=false`, `claim_status=\"available\"` (or omitted status, which consumers treat as available)\n2. **in_progress**: consumer sets item `claim_status=\"in_progress\"`, plus `claimed_by` and `claimed_at`\n3. **consumed**: after a successful `/rpi` cycle and regression gate, consumer sets item `consumed=true`, `claim_status=\"consumed\"`, `consumed_by`, and `consumed_at`\n4. **release on failure**: failed or regressed cycles clear item `claimed_by` / `claimed_at`, reset `claim_status=\"available\"`, keep `consumed=false`, and may record `failed_at`\n\nSelection rules:\n- skip items that are `consumed=true`\n- skip items currently claimed with `claim_status=\"in_progress\"`\n- keep failed items retryable; `failed_at` is audit/retry-order metadata, not dormancy\n- only mark the entry aggregate `consumed=true` once every child item is consumed\n\nNever mark an item consumed at pick-time.\n\n## Prior-Findings Resolution Tracking\n\nCompute resolution tracking from `.agents/rpi/next-work.jsonl`:\n\n```bash\nNEXT_WORK=\".agents/rpi/next-work.jsonl\"\n\nif [ -f \"$NEXT_WORK\" ]; then\n totals=$(jq -Rs '\n split(\"\\n\")\n | map(select(length>0) | fromjson)\n | reduce .[] as $e (\n {entries:0,total:0,resolved:0};\n .entries += 1\n | .total += ($e.items | length)\n | .resolved += ([($e.items // [])[] | select((.consumed // false) == true)] | length)\n )\n | .unresolved = (.total - .resolved)\n | .rate = (if .total > 0 then ((.resolved * 10000 / .total) | round / 100) else 0 end)\n ' \"$NEXT_WORK\")\n\n per_source=$(jq -Rs '\n split(\"\\n\")\n | map(select(length>0) | fromjson)\n | map({\n source_epic,\n total: (.items | length),\n resolved: ([((.items // [])[]) | select((.consumed // false) == true)] | length)\n })\n | group_by(.source_epic)\n | map({\n source_epic: .[0].source_epic,\n total: (map(.total) | add),\n resolved: (map(.resolved) | add),\n unresolved: ((map(.total) | add) - (map(.resolved) | add)),\n rate: (if (map(.total) | add) > 0\n then (((map(.resolved) | add) * 10000 / (map(.total) | add)) | round / 100)\n else 0 end)\n })\n ' \"$NEXT_WORK\")\n\n echo \"Prior findings totals: $totals\"\n echo \"Prior findings by source epic: $per_source\"\nelse\n echo \"No next-work.jsonl found; resolution tracking unavailable.\"\nfi\n```\n\nWrite the totals and per-source rows into `## Prior Findings Resolution Tracking` in the post-mortem report.\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":8501,"content_sha256":"02c25d0ed85da657be12917bba9ecd76ced9b8b1bb4c0f91ac621234561c6021"},{"filename":"references/learning-templates.md","content":"# Learning Templates for Post-Mortem\n\nTemplates for extracting and documenting learnings during Phase 4.\n\n---\n\n## Learning Artifact Template\n\nWrite to: `.agents/learnings/YYYY-MM-DD-{topic}.md`\n\n```markdown\n# Learning: [Concise Title]\n\n**Date:** YYYY-MM-DD\n**Epic:** \u003cepic-id>\n**Tags:** [learning, topic1, topic2]\n**Verified:** yes/no\n\n---\n\n## Context\n\nWhat were we trying to accomplish? What was the situation?\n\n- **Goal:** [What we were building]\n- **Approach:** [How we approached it]\n- **Environment:** [Relevant context]\n\n---\n\n## What We Learned\n\nConcrete insight or pattern discovered. Be specific.\n\n[2-3 sentences describing the learning]\n\n### Key Insight\n\n> [One-sentence summary that could be quoted]\n\n---\n\n## Verification Status\n\n**Verified:** yes / no\n\n**Verification Method:** [How this was verified]\n- Tool-verified: [tool name and output file]\n- Multi-observation: [list of files/commits where pattern appeared]\n- Production-confirmed: [incident or metric that confirmed]\n- Single-observation: [needs more data to confirm]\n\n**NOTE:** Do NOT use confidence scores (0.92, 0.91). Use \"verified: yes/no\" with method.\n\n---\n\n## Evidence\n\n| Source | Detail | Relevance |\n|--------|--------|-----------|\n| Commit | abc123 | [What it shows] |\n| Issue | \u003cissue-id> | [What happened] |\n| Discussion | [link/reference] | [Key point] |\n\n---\n\n## Application\n\nHow to apply this learning in the future:\n\n1. **When to use:** [Trigger conditions]\n2. **How to apply:** [Concrete steps]\n3. **What to avoid:** [Anti-pattern]\n\n---\n\n## Discovery Provenance\n\n| Insight | Source Type | Source Detail |\n|---------|-------------|---------------|\n| [Learning point] | [grep/code-map/etc] | [file:line or query] |\n\n---\n\n## Related\n\n- Previous learnings: [links]\n- Related patterns: [links]\n- Documentation: [links]\n```\n\n---\n\n## Pattern Artifact Template\n\nWrite to: `.agents/patterns/{pattern-name}.md`\n\n```markdown\n# Pattern: [Pattern Name]\n\n**Date:** YYYY-MM-DD\n**Discovered In:** \u003cepic-id>\n**Tags:** [pattern, category, language]\n**Maturity:** experimental | validated | established\n\n---\n\n## Summary\n\n[One paragraph describing what this pattern does and when to use it]\n\n---\n\n## Problem\n\nWhat problem does this pattern solve?\n\n- [Pain point 1]\n- [Pain point 2]\n\n---\n\n## Solution\n\n### Structure\n\n[Describe the pattern structure]\n\n### Code Example\n\n```language\n// Example implementation\n```\n\n### When to Use\n\n- [Condition 1]\n- [Condition 2]\n\n### When NOT to Use\n\n- [Counter-indication 1]\n- [Counter-indication 2]\n\n---\n\n## Trade-offs\n\n| Pro | Con |\n|-----|-----|\n| [Benefit] | [Drawback] |\n\n---\n\n## Real-World Usage\n\n### From Epic \u003cepic-id>\n\n[How we used this pattern in the epic]\n\n**Before:**\n```language\n// Old approach\n```\n\n**After:**\n```language\n// Pattern applied\n```\n\n**Result:** [What improved]\n\n---\n\n## Related Patterns\n\n- [Related pattern 1]: [How they differ]\n- [Related pattern 2]: [How they complement]\n\n---\n\n## References\n\n- [External link 1]\n- [Internal doc 1]\n```\n\n---\n\n## Memory Storage Template\n\nFor MCP `memory_store`:\n\n```python\nmcp__ai-platform__memory_store(\n content=\"[Concise learning statement - max 200 words]\",\n memory_type=\"fact\", # fact | preference | episode\n source=f\"post-mortem:{epic_id}\",\n tags=[\n f\"epic:{epic_id}\",\n \"learning\",\n \"topic:specific-topic\",\n \"rig:rig-name\",\n \"verified:yes\" # or \"verified:no\" - never use confidence scores\n ]\n # Note: Use \"verified:yes/no\" tags, NOT confidence scores (0.92, 0.91).\n # Verification requires source citation or multiple observations.\n)\n```\n\n### Memory Types\n\n| Type | Use For | Example |\n|------|---------|---------|\n| `fact` | Learned information | \"Pre-mortem simulation catches 80% of spec issues\" |\n| `preference` | User/project choices | \"This project prefers snake_case over camelCase\" |\n| `episode` | Significant events | \"Wave 6 timeout issue resolved by increasing limit to 900s\" |\n\n### Tag Conventions\n\n| Tag Prefix | Purpose | Example |\n|------------|---------|---------|\n| `epic:` | Link to source epic | `epic:jc-9tx6` |\n| `topic:` | Subject area | `topic:security` |\n| `rig:` | Which rig | `rig:compile` |\n| `pattern:` | If a pattern | `pattern:pre-mortem` |\n| `tool:` | If about a tool | `tool:upgrade.py` |\n\n---\n\n## Retro Summary Template\n\nWrite to: `.agents/council/YYYY-MM-DD-{epic}.md`\n\n```markdown\n# Retro: [Epic Title]\n\n**Epic:** \u003cepic-id>\n**Date:** YYYY-MM-DD\n**Duration:** N days\n**Mode:** crew | mayor | mixed\n\n---\n\n## Summary\n\n[2-3 sentence overview of what was accomplished]\n\n---\n\n## What Went Well\n\n1. **[Thing 1]:** [Why it went well]\n2. **[Thing 2]:** [Why it went well]\n3. **[Thing 3]:** [Why it went well]\n\n---\n\n## What Could Improve\n\n1. **[Thing 1]:** [What to do differently]\n - **Action:** [Specific improvement]\n2. **[Thing 2]:** [What to do differently]\n - **Action:** [Specific improvement]\n\n---\n\n## Friction Points\n\n| Issue | Impact | Resolution | Learning |\n|-------|--------|------------|----------|\n| [Problem] | [Severity] | [How fixed] | [What we learned] |\n\n---\n\n## Metrics\n\n| Metric | Value | Notes |\n|--------|-------|-------|\n| Issues completed | N | |\n| Issues blocked | M | |\n| Retries | K | |\n| Duration | X days | |\n\n---\n\n## Learnings Extracted\n\n- [Link to learning 1]\n- [Link to learning 2]\n\n## Patterns Discovered\n\n- [Link to pattern 1]\n\n## Memories Stored\n\n- [Summary of memory 1]\n- [Summary of memory 2]\n\n---\n\n## Source Performance\n\n| Source | Tier | Value Score | Deviation |\n|--------|------|-------------|-----------|\n| smart-connections | 2 | 0.85 | +0.05 |\n| grep | 3 | 0.75 | +0.15 |\n\n### Recommendations\n\n- **PROMOTE:** [source] overperforming by X%\n- **DEMOTE:** [source] underperforming by X%\n\n---\n\n## Process Proposals\n\n### Proposal: [Title]\n**Severity:** CRITICAL | RECOMMENDED | OPTIONAL\n**Target:** [file or process]\n\n**Problem:** [What went wrong]\n\n**Proposed Change:** [What to do]\n\n**Status:** pending | approved | rejected\n\n---\n\n## Next Time\n\n- [ ] [Action item 1]\n- [ ] [Action item 2]\n```\n\n---\n\n## Quick Extraction Workflow\n\n```bash\n# 1. Identify learnings from commits\ngit log --oneline --since=\"7 days ago\" --grep=\"$EPIC\" | while read commit; do\n echo \"Commit: $commit\"\n git show $commit --stat\n echo \"---\"\ndone\n\n# 2. Identify friction from beads\nbd list --parent=$EPIC | while read issue; do\n status=$(bd show $issue | grep \"Status:\")\n comments=$(bd show $issue | grep -c \"Comment:\")\n retries=$(bd show $issue | grep -c \"retry\\|Retry\")\n echo \"$issue: status=$status, comments=$comments, retries=$retries\"\ndone\n\n# 3. Create artifacts\nmkdir -p .agents/{learnings,patterns,retro}\n\n# 4. Store memories\nfor learning in \"${LEARNINGS[@]}\"; do\n mcp__ai-platform__memory_store(content=\"$learning\", ...)\ndone\n```\n\n---\n\n## Quality Criteria for Learnings\n\nA good learning:\n- [ ] Is specific (not generic advice)\n- [ ] Has evidence (commits, issues, discussions)\n- [ ] Is actionable (can be applied)\n- [ ] Has context (when it applies)\n- [ ] Is findable (good tags)\n- [ ] Has verification status (verified: yes/no with method, NOT confidence scores)\n\nA good pattern:\n- [ ] Solves a real problem (not hypothetical)\n- [ ] Has been used (not theoretical)\n- [ ] Has trade-offs documented\n- [ ] Has code examples\n- [ ] Explains when NOT to use it\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":7255,"content_sha256":"a1c71711cab8d1495529ea2bf7bea809cc2ba127ac3d5f97b65657ee858bc043"},{"filename":"references/maintenance-phases.md","content":"# Post-Mortem Maintenance Phases (3-6)\n\n> **Extracted from:** `skills/post-mortem/SKILL.md`\n> These phases handle backlog processing, activation, retirement, and harvesting.\n> They run after council validation (Phase 1) and learning extraction (Phase 2).\n> Load when `--process-only` flag is set or when running a full post-mortem.\n\n---\n\n### Phase 3: Process Backlog\n\nScore, deduplicate, and flag stale learnings across the full backlog. This phase runs on ALL learnings, not just those extracted in Phase 2.\n\nRead `references/backlog-processing.md` for detailed scoring formulas, deduplication logic, and staleness criteria.\n\n#### Step BP.1: Load Last-Processed Marker\n\n```bash\nMARKER=\".agents/ao/last-processed\"\nmkdir -p .agents/ao\nif [ ! -f \"$MARKER\" ]; then\n date -v-30d +%Y-%m-%dT%H:%M:%S 2>/dev/null || date -d \"30 days ago\" --iso-8601=seconds > \"$MARKER\"\nfi\nLAST_PROCESSED=$(cat \"$MARKER\")\n```\n\n#### Step BP.2: Scan Unprocessed Learnings\n\n```bash\nfind .agents/learnings/ -name \"*.md\" -newer \"$MARKER\" -not -path \"*/archive/*\" -type f | sort\n```\n\nIf zero files found: report \"Backlog empty — no unprocessed learnings\" and skip to Phase 4.\n\n#### Step BP.3: Deduplicate\n\nFor each pair of unprocessed learnings:\n1. Extract `# Learning:` title\n2. Normalize: lowercase, strip punctuation, collapse whitespace\n3. If two normalized titles share >= 80% word overlap, merge:\n - Keep the file with highest confidence (high > medium > low); if tied, keep most recent\n - Archive the duplicate with a `merged_into:` pointer\n\n#### Step BP.4: Score Each Learning\n\nCompute composite score for each learning:\n\n| Factor | Values | Points |\n|--------|--------|--------|\n| Confidence | high=3, medium=2, low=1 | 1-3 |\n| Citations | default=1, +1 per cite in `.agents/ao/citations.jsonl` | 1+ |\n| Recency | \u003c7d=3, \u003c30d=2, else=1 | 1-3 |\n\n**Score = confidence + citations + recency**\n\n#### Step BP.5: Flag Stale\n\nLearnings that are >30 days old AND have zero citations are flagged for retirement in Phase 5.\n\n```bash\n# Flag but do not archive yet — Phase 5 handles retirement\nif [ \"$DAYS_OLD\" -gt 30 ] && [ \"$CITE_COUNT\" -eq 0 ]; then\n echo \"STALE: $LEARNING_FILE (${DAYS_OLD}d old, 0 citations)\"\nfi\n```\n\n#### Step BP.6: Report\n\n```\nPhase 3 (Process Backlog) Summary:\n- N learnings scanned\n- N duplicates merged\n- N scored (range: X-Y)\n- N flagged stale\n```\n\n### Phase 4: Activate\n\nPromote high-value learnings and feed downstream systems. Read `references/activation-policy.md` for detailed promotion thresholds and procedures.\n\n**If `--skip-activate` is set:** Skip this phase entirely. Report \"Phase 4 skipped (--skip-activate).\"\n\n#### Step ACT.1: Promote to MEMORY.md\n\nLearnings with score >= 6 are promoted:\n1. Read the learning file\n2. Extract title and core insight\n3. Check MEMORY.md for duplicate entries (grep for key phrases)\n4. If no duplicate: append to `## Key Lessons` in MEMORY.md\n\n```markdown\n## Key Lessons\n- **\u003cTitle>** — \u003cone-line insight> (source: `.agents/learnings/\u003cfilename>`)\n```\n\n**Important:** Append only. Never overwrite MEMORY.md.\n\n#### Step ACT.2: Re-Run the Finding Compiler Idempotently\n\nIf registry rows changed during this post-mortem, rerun the compiler before feeding next-work so downstream sessions read the freshest compiled prevention outputs:\n\n```bash\nbash hooks/finding-compiler.sh --quiet 2>/dev/null || true\n```\n\n#### Step ACT.3: Feed Next-Work\n\nActionable improvements identified during processing -> append one schema v1.4\nbatch entry to `.agents/rpi/next-work.jsonl` using the tracked contract in\n[`../../docs/contracts/next-work.schema.md`](../../../docs/contracts/next-work.schema.md)\nand the write procedure in\n[`references/harvest-next-work.md`](harvest-next-work.md):\n\n```bash\nmkdir -p .agents/rpi\n# Build VALID_ITEMS via the schema-validation flow in references/harvest-next-work.md\n# Then append one entry per post-mortem / epic.\nENTRY_TIMESTAMP=\"$(date -Iseconds)\"\nSOURCE_EPIC=\"${EPIC_ID:-recent}\"\nVALID_ITEMS_JSON=\"${VALID_ITEMS_JSON:-[]}\"\n\nprintf '%s\\n' \"$(jq -cn \\\n --arg source_epic \"$SOURCE_EPIC\" \\\n --arg timestamp \"$ENTRY_TIMESTAMP\" \\\n --argjson items \"$VALID_ITEMS_JSON\" \\\n '{\n source_epic: $source_epic,\n timestamp: $timestamp,\n items: $items,\n consumed: false,\n claim_status: \"available\",\n claimed_by: null,\n claimed_at: null,\n consumed_by: null,\n consumed_at: null\n }'\n)\" >> .agents/rpi/next-work.jsonl\n```\n\n#### Step ACT.4: Update Marker\n\n```bash\ndate -Iseconds > .agents/ao/last-processed\n```\n\nThis must be the LAST action in Phase 4.\n\n#### Step ACT.5: Report\n\n```\nPhase 4 (Activate) Summary:\n- N promoted to MEMORY.md\n- N duplicates merged\n- N flagged for retirement\n- N constraints compiled\n- N improvements fed to next-work.jsonl\n```\n\n### Phase 5: Retire Stale\n\nArchive learnings that are no longer earning their keep.\n\n#### Step RET.1: Archive Stale Learnings\n\nLearnings flagged in Phase 3 (>30d old, zero citations):\n\n```bash\nmkdir -p .agents/learnings/archive\nfor f in \u003cstale-files>; do\n mv \"$f\" .agents/learnings/archive/\n echo \"Archived: $f (stale: >30d, 0 citations)\"\ndone\n```\n\n#### Step RET.2: Archive Superseded Learnings\n\nLearnings merged during Phase 3 deduplication were already archived with `merged_into:` pointers. Verify the pointers are valid:\n\n```bash\nfor f in .agents/learnings/archive/*.md; do\n [ -f \"$f\" ] || continue\n MERGED_INTO=$(grep \"^merged_into:\" \"$f\" 2>/dev/null | awk '{print $2}')\n if [ -n \"$MERGED_INTO\" ] && [ ! -f \"$MERGED_INTO\" ]; then\n echo \"WARN: $f points to missing file: $MERGED_INTO\"\n fi\ndone\n```\n\n#### Step RET.3: Clean MEMORY.md References\n\nIf any archived learning was previously promoted to MEMORY.md, remove those entries:\n\n```bash\nfor f in \u003carchived-files>; do\n BASENAME=$(basename \"$f\")\n # Check if MEMORY.md references this file\n if grep -q \"$BASENAME\" MEMORY.md 2>/dev/null; then\n echo \"WARN: MEMORY.md references archived learning: $BASENAME — consider removing\"\n fi\ndone\n```\n\n**Note:** Do not auto-delete MEMORY.md entries. WARN the user and let them decide.\n\n#### Step RET.4: Report\n\n```\nPhase 5 (Retire) Summary:\n- N stale learnings archived\n- N superseded learnings archived\n- N MEMORY.md references to review\n```\n\n### Step 4: Write Post-Mortem Report\n\n**Write to:** `.agents/council/YYYY-MM-DD-post-mortem-\u003ctopic>.md`\n\n```markdown\n---\nid: post-mortem-YYYY-MM-DD-\u003ctopic-slug>\ntype: post-mortem\ndate: YYYY-MM-DD\nsource: \"[[.agents/plans/YYYY-MM-DD-\u003cplan-slug>]]\"\n---\n\n# Post-Mortem: \u003cEpic/Topic>\n\n**Epic:** \u003cepic-id or \"recent\">\n**Duration:** \u003celapsed time from PM_START to now>\n**Cycle-Time Trend:** \u003ccompare against prior post-mortems — is this faster or slower? Check .agents/council/ for prior post-mortem Duration values>\n\n## Council Verdict: PASS / WARN / FAIL\n\n| Judge | Verdict | Key Finding |\n|-------|---------|-------------|\n| Plan-Compliance | ... | ... |\n| Tech-Debt | ... | ... |\n| Learnings | ... | ... |\n\n### Implementation Assessment\n\u003ccouncil summary>\n\n### Concerns\n\u003cany issues found>\n\n## Learnings (from Phase 2)\n\n### What Went Well\n- ...\n\n### What Was Hard\n- ...\n\n### Do Differently Next Time\n- ...\n\n### Patterns to Reuse\n- ...\n\n### Anti-Patterns to Avoid\n- ...\n\n### Footgun Entries (Required)\n\nList discovered footguns — common mistakes or surprising behaviors that cost time:\n\n| Footgun | Impact | Prevention |\n|---------|--------|-----------|\n| description | how it wasted time | how to prevent |\n\nThese entries are promoted to `.agents/learnings/` and injected into future worker prompts to prevent recurrence. Zero-cycle lag between discovery and prevention.\n\n## Knowledge Lifecycle\n\n### Backlog Processing (Phase 3)\n- Scanned: N learnings\n- Merged: N duplicates\n- Flagged stale: N\n\n### Activation (Phase 4)\n- Promoted to MEMORY.md: N\n- Constraints compiled: N\n- Next-work items fed: N\n\n### Retirement (Phase 5)\n- Archived: N learnings\n\n## Proactive Improvement Agenda\n\n| # | Area | Improvement | Priority | Horizon | Effort | Evidence |\n|---|------|-------------|----------|---------|--------|----------|\n| 1 | repo / execution / ci-automation | ... | P0/P1/P2 | now/next-cycle/later | S/M/L | ... |\n\n## Prior Findings Resolution Tracking\n\n| Metric | Value |\n|---|---|\n| Backlog entries analyzed | ... |\n| Prior findings total | ... |\n| Resolved findings | ... |\n| Unresolved findings | ... |\n| Resolution rate | ...% |\n\n| Source Epic | Findings | Resolved | Unresolved | Resolution Rate |\n|---|---:|---:|---:|---:|\n| ... | ... | ... | ... | ...% |\n\n## Command-Surface Parity Checklist\n\n| Command File | Run-path Covered by Test? | Evidence (file:line or test name) | Intentionally Uncovered? | Reason |\n|---|---|---|---|---|\n| cli/cmd/ao/\u003ccommand>.go | yes/no | ... | yes/no | ... |\n\n## BF Assessment\n\n| Level | Exist? | Bugs | Action |\n|-------|--------|------|--------|\n| BF1 | y/n | N | property tests |\n| BF4 | y/n | N | chaos tests |\n\n## Next Work\n\n| # | Title | Type | Severity | Source | Target Repo |\n|---|-------|------|----------|--------|-------------|\n| 1 | \u003ctitle> | tech-debt / improvement / pattern-fix / process-improvement | high / medium / low | council-finding / retro-learning / retro-pattern | \u003crepo-name or *> |\n\n### Recommended Next /rpi\n/rpi \"\u003chighest-value improvement>\"\n\n## Status\n\n[ ] CLOSED - Work complete, learnings captured\n[ ] FOLLOW-UP - Issues need addressing (create new beads)\n```\n\n### Step 4.5: Synthesize Proactive Improvement Agenda (MANDATORY)\n\n**After writing the post-mortem report, analyze extraction + council context and proactively propose improvements to repo quality and execution quality.**\n\nRead the extraction output (from Phase 2) and the council report (from Step 3). For each learning, ask:\n1. **What process does this improve?** (build, test, review, deploy, documentation, automation, etc.)\n2. **What's the concrete change?** (new check, new automation, workflow change, tooling improvement)\n3. **Is it actionable in one RPI cycle?** (if not, split into smaller pieces)\n\nCoverage requirements:\n- Include **ALL** improvements found (no cap).\n- Cover all three surfaces:\n - `repo` (code/contracts/docs quality)\n - `execution` (planning/implementation/review workflow)\n - `ci-automation` (validation/tooling reliability)\n- Include at least **1 quick win** (small, low-risk, same-session viable).\n\nWrite process improvement items with type `process-improvement` (distinct from `tech-debt` or `improvement`). Each item must have:\n- `title`: imperative form, e.g. \"Add pre-commit lint check\"\n- `area`: which part of the development process to improve\n- `description`: 2-3 sentences describing the change and why retro evidence supports it\n- `evidence`: which retro finding or council finding motivates this\n- `priority`: P0 / P1 / P2\n- `horizon`: now / next-cycle / later\n- `effort`: S / M / L\n\n**These items feed directly into Step 5 (Harvest Next Work) alongside council findings. They are the flywheel's growth vector — each cycle makes the system smarter.**\n\nWrite this into the post-mortem report under `## Proactive Improvement Agenda`.\n\nExample output:\n```markdown\n## Proactive Improvement Agenda\n\n| # | Area | Improvement | Priority | Horizon | Effort | Evidence |\n|---|------|-------------|----------|---------|--------|----------|\n| 1 | ci-automation | Add validation metadata requirement for Go tasks | P0 | now | S | Workers shipped untested code when metadata didn't require `go test` |\n| 2 | execution | Add consistency-check finding category in review | P1 | next-cycle | M | Partial refactoring left stale references undetected |\n```\n\n### Step 4.6: Prior-Findings Resolution Tracking (MANDATORY)\n\nAfter Step 4.5, compute and include prior-findings resolution tracking from `.agents/rpi/next-work.jsonl`. Read `references/harvest-next-work.md` for the jq queries that compute totals and per-source resolution rates. Write results into `## Prior Findings Resolution Tracking` in the post-mortem report.\n\n### Step 4.7: Command-Surface Parity Gate (MANDATORY)\n\nBefore marking post-mortem complete, enforce command-surface parity for modified CLI commands:\n\n1. Identify modified command files under `cli/cmd/ao/` from the reviewed scope.\n2. For each file, record at least one tested run-path (unit/integration/e2e) in `## Command-Surface Parity Checklist`.\n3. Any intentionally uncovered command family must be explicitly listed with a reason and follow-up item.\n\nIf any modified command file is missing both coverage evidence and an intentional-uncovered rationale, post-mortem cannot be marked complete.\n\n### Step 4.8: Persist Retro History (Trend Tracking)\n\nAfter writing the post-mortem report, persist a structured summary JSON to `.agents/retro/YYYY-MM-DD-\u003cepic-slug>.json` and append an index line to `.agents/retro/index.jsonl`. When 2+ prior retros exist, compute verdict streak, average cycle time, and learnings-per-retro trend for the report. See [references/retro-history.md](retro-history.md) for the full JSON schema, write rules, and trend queries.\n\n### Step 5: Harvest Next Work\n\nScan the council report and extracted learnings for actionable follow-up items:\n\n1. **Council findings:** Extract tech debt, warnings, and improvement suggestions from the council report (items with severity \"significant\" or \"critical\" that weren't addressed in this epic)\n2. **Retro patterns:** Extract recurring patterns from learnings that warrant dedicated RPIs (items from \"Do Differently Next Time\" and \"Anti-Patterns to Avoid\")\n3. **Process improvements:** Include all items from Step 4.5 (type: `process-improvement`). These are the flywheel's growth vector — each cycle makes development more effective.\n4. **Footgun entries (REQUIRED):** Extract platform-specific gotchas, surprising API behaviors, or silent-failure modes discovered during implementation. Each must include: trigger condition, observable symptom, and fix. Write as type `pattern-fix` with source `retro-learning`. If a footgun was discovered this cycle, it must appear in this harvest — do not defer.\n5. **Write `## Next Work` section** to the post-mortem report:\n\n```markdown\n## Next Work\n\n| # | Title | Type | Severity | Source | Target Repo |\n|---|-------|------|----------|--------|-------------|\n| 1 | \u003ctitle> | tech-debt / improvement / pattern-fix / process-improvement | high / medium / low | council-finding / retro-learning / retro-pattern | \u003crepo-name or *> |\n```\n\n6. **SCHEMA VALIDATION (MANDATORY):** Before writing, validate each harvested item against the tracked contract in [`docs/contracts/next-work.schema.md`](../../../docs/contracts/next-work.schema.md). Read `references/harvest-next-work.md` for the validation function and write procedure. Drop invalid items; do NOT block the entire harvest.\n\n7. **Write to next-work.jsonl** (canonical path: `.agents/rpi/next-work.jsonl`). Read `references/harvest-next-work.md` for the write procedure (target_repo assignment, claim/finalize lifecycle, JSONL format, required fields).\n\n8. **Do NOT auto-create bd issues.** Report the items and suggest: \"Run `/rpi --spawn-next` to create an epic from these items.\"\n\nIf no actionable items found, write: \"No follow-up items identified. Flywheel stable.\"\n\n### Step 6: Feed the Knowledge Flywheel\n\nPost-mortem automatically feeds learnings into the flywheel:\n\n```bash\nif command -v ao &>/dev/null; then\n ao forge markdown .agents/learnings/*.md 2>/dev/null\n echo \"Learnings indexed in knowledge flywheel\"\n\n # Validate and lock artifacts that passed council review\n ao temper validate --min-feedback 0 .agents/learnings/YYYY-MM-DD-*.md 2>/dev/null || true\n echo \"Artifacts validated for tempering\"\n\n # Close session and trigger full flywheel close-loop (includes adaptive feedback)\n ao session close 2>/dev/null || true\n ao flywheel close-loop --quiet 2>/dev/null || true\n echo \"Session closed, flywheel loop triggered\"\nelse\n # Learnings are already in .agents/learnings/ from Phase 2.\n # Without ao CLI, grep-based search in /research and /inject\n # will find them directly — no copy to pending needed.\n\n # Feedback-loop fallback: update confidence for cited learnings\n mkdir -p .agents/ao\n if [ -f .agents/ao/citations.jsonl ]; then\n echo \"Processing citation feedback (ao-free fallback)...\"\n # Read cited learning files and boost confidence notation\n while IFS= read -r line; do\n CITED_FILE=$(echo \"$line\" | grep -o '\"artifact_path\":\"[^\"]*\"' | cut -d'\"' -f4)\n if [ -f \"$CITED_FILE\" ]; then\n # Note: confidence boost tracked via citation count, not file modification\n echo \"Cited: $CITED_FILE\"\n fi\n done \u003c .agents/ao/citations.jsonl\n fi\n\n # Session-outcome fallback: record this session's outcome\n EPIC_ID=\"\u003cepic-id>\"\n echo \"{\\\"epic\\\": \\\"$EPIC_ID\\\", \\\"verdict\\\": \\\"\u003ccouncil-verdict>\\\", \\\"cycle_time_minutes\\\": 0, \\\"timestamp\\\": \\\"$(date -Iseconds)\\\"}\" >> .agents/ao/outcomes.jsonl\n\n # Skip ao temper validate (no fallback needed — tempering is an optimization)\n echo \"Flywheel fed locally (ao CLI not available — learnings searchable via grep)\"\nfi\n```\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":16974,"content_sha256":"ee7bff69434e9ffa0d0ecd22e600e08d75677dbd02dcd35c0917906f6d942d6b"},{"filename":"references/metadata-verification.md","content":"# Pre-Council Metadata Verification\n\n**Mechanically verify delivered artifacts against the plan BEFORE council. Catches metadata errors that LLMs estimate instead of measure (L19, L22, L24).**\n\n```bash\nMETADATA_FAILURES=\"\"\n\n# 1. Plan vs actual file list — did we deliver what we said we would?\nif [ -n \"$PLAN_DOC\" ] && [ -f \"$PLAN_DOC\" ]; then\n # Extract file paths mentioned in plan\n for planned_file in $(grep -oE '`[^`]+\\.(go|py|ts|js|md|yaml|yml|sh)`' \"$PLAN_DOC\" 2>/dev/null | tr -d '`' | sort -u); do\n if [ ! -f \"$planned_file\" ]; then\n METADATA_FAILURES=\"${METADATA_FAILURES}\\n- PLANNED BUT MISSING: $planned_file (in plan but not on disk)\"\n fi\n done\nfi\n\n# 2. Claimed metrics vs measured — line counts, issue counts, file counts\n# Check git log for claimed counts in commit messages\nfor commit_msg in $(git log --oneline --since=\"7 days ago\" --format=\"%s\" 2>/dev/null); do\n # Handled inline during council context building\n :\ndone\n\n# 3. File existence — all paths in recent commits exist\nfor f in $(git diff --name-only HEAD~10 2>/dev/null | sort -u); do\n if [ ! -f \"$f\" ] && ! git log --diff-filter=D --name-only --format=\"\" HEAD~10..HEAD | grep -q \"^${f}$\"; then\n METADATA_FAILURES=\"${METADATA_FAILURES}\\n- MISSING FILE: $f (in commits but not on disk, not intentionally deleted)\"\n fi\ndone\n\n# 4. Cross-references in delivered docs\ndeclare -A METADATA_LINK_ALLOWLIST=()\nif [ -f tests/docs/broken-links-allowlist.txt ]; then\n while IFS= read -r allowlisted_link; do\n [[ -z \"$allowlisted_link\" || \"$allowlisted_link\" == \\#* ]] && continue\n METADATA_LINK_ALLOWLIST[\"$allowlisted_link\"]=1\n done \u003c tests/docs/broken-links-allowlist.txt\nfi\n\nis_illustrative_metadata_path() {\n local ref_path=\"$1\"\n case \"$ref_path\" in\n url|./path/to/*|../path/to/*|*/path/to/*|/Users/me/*)\n return 0\n ;;\n esac\n return 1\n}\n\nfor f in $(git diff --name-only HEAD~10 2>/dev/null | grep -E '\\.(md|txt)

Post-Mortem Skill Purpose: Wrap up completed work — validate it shipped correctly, extract learnings, process the knowledge backlog, activate high-value insights, and retire stale knowledge. Runtime note: Hook-driven closeout is runtime-dependent. Claude/OpenCode can wire Phase 2-5 maintenance through lifecycle hooks. Codex CLI v0.115.0+ supports native hooks (same behavior). For older Codex versions without hook surfaces, finish closeout with . Loop position Move 7 (capture evidence + learning, then ratchet) of the operating loop. Two outputs per loop turn: evidence (test names, snapshot key…

); do\n if [ -f \"$f\" ]; then\n while IFS= read -r ref; do\n [[ -z \"$ref\" ]] && continue\n [[ \"$ref\" == http://* || \"$ref\" == https://* ]] && continue\n [[ \"$ref\" == mailto:* ]] && continue\n [[ \"$ref\" == \\#* ]] && continue\n\n ref=\"${ref%%#*}\"\n ref=\"${ref%% *}\"\n [[ -z \"$ref\" ]] && continue\n is_illustrative_metadata_path \"$ref\" && continue\n\n ref_dir=$(dirname \"$f\")\n allowlist_key=\"${f}:${ref}\"\n [[ -n \"${METADATA_LINK_ALLOWLIST[$allowlist_key]+x}\" ]] && continue\n\n if [ ! -f \"$ref_dir/$ref\" ] && [ ! -f \"$ref\" ]; then\n METADATA_FAILURES=\"${METADATA_FAILURES}\\n- BROKEN LINK: $f references $ref (not found)\"\n fi\n done \u003c \u003c(\n grep -oE '\\]\\([^)]+\\)' \"$f\" 2>/dev/null |\n cut -c3- |\n sed 's/)$//'\n )\n fi\ndone\n\n# 5. ASCII diagram verification (>3 boxes per L22)\nfor f in $(git diff --name-only HEAD~10 2>/dev/null | grep -E '\\.(md|txt)

Post-Mortem Skill Purpose: Wrap up completed work — validate it shipped correctly, extract learnings, process the knowledge backlog, activate high-value insights, and retire stale knowledge. Runtime note: Hook-driven closeout is runtime-dependent. Claude/OpenCode can wire Phase 2-5 maintenance through lifecycle hooks. Codex CLI v0.115.0+ supports native hooks (same behavior). For older Codex versions without hook surfaces, finish closeout with . Loop position Move 7 (capture evidence + learning, then ratchet) of the operating loop. Two outputs per loop turn: evidence (test names, snapshot key…

); do\n if [ -f \"$f\" ]; then\n box_count=$(grep -cP '┌|╔|\\+--' \"$f\" 2>/dev/null || echo 0)\n if [ \"$box_count\" -gt 3 ]; then\n label_count=$(grep -cP '│\\s+\\S' \"$f\" 2>/dev/null || echo 0)\n if [ \"$box_count\" -gt \"$label_count\" ]; then\n METADATA_FAILURES=\"${METADATA_FAILURES}\\n- DIAGRAM CHECK: $f has ${box_count} boxes but only ${label_count} label lines — verify\"\n fi\n fi\n fi\ndone\n\n# Report\nif [ -n \"$METADATA_FAILURES\" ]; then\n echo \"METADATA VERIFICATION FAILURES:\"\n echo -e \"$METADATA_FAILURES\"\nelse\n echo \"Metadata verification: all checks passed\"\nfi\n```\n\n**If failures found:** Include them in the council packet as `context.metadata_failures`. Tag as MECHANICAL — council judges should focus on plan compliance, tech debt, and learnings instead of re-discovering broken links or wrong counts.\n\n**If plan doc found:** Compare planned deliverables (file paths, issue counts) against actual. Mismatches become pre-loaded findings for the `plan-compliance` judge.\n\n**Why:** Post-mortem councils were spending judge cycles on metadata errors (wrong line counts, missing files, broken cross-refs) that are mechanically verifiable. Pre-council verification frees judges to focus on structural assessment and learning extraction.\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":4130,"content_sha256":"4d4503fbff4f0b7a7921a34a427d8e7ac5f0ee27e739227111aa1bff0469069e"},{"filename":"references/output-templates.md","content":"# Post-Mortem Output Templates\n\nCanonical output shapes for `skills/post-mortem/SKILL.md`. When this file and the\nskill disagree, follow the skill workflow and the executable/schema surfaces:\n\n- `skills/post-mortem/SKILL.md`\n- `skills/post-mortem/scripts/write-evidence-only-closure.sh`\n- `schemas/evidence-only-closure.v1.schema.json`\n\n## Post-Mortem Report Template\n\nWrite to `.agents/council/YYYY-MM-DD-post-mortem-\u003ctopic>.md`:\n\n```markdown\n---\nid: post-mortem-YYYY-MM-DD-\u003ctopic-slug>\ntype: post-mortem\ndate: YYYY-MM-DD\nsource: \"[[.agents/plans/YYYY-MM-DD-\u003cplan-slug>]]\"\n---\n\n# Post-Mortem: \u003cEpic/Topic>\n\n**Epic:** \u003cepic-id or \"recent\">\n**Duration:** \u003celapsed time from PM_START to now>\n**Cycle-Time Trend:** \u003cfaster|slower|flat vs prior post-mortems>\n\n## Council Verdict: PASS / WARN / FAIL\n\n| Judge | Verdict | Key Finding |\n|-------|---------|-------------|\n| Plan-Compliance | ... | ... |\n| Tech-Debt | ... | ... |\n| Learnings | ... | ... |\n\n### Implementation Assessment\n\u003ccouncil summary>\n\n### Concerns\n\u003copen issues or residual risks>\n\n## Closure Integrity\n\n| Check | Result | Details |\n|-------|--------|---------|\n| Evidence Precedence | PASS/WARN/FAIL | N children resolved by commit/staged/worktree, M without evidence |\n| Phantom Beads | PASS/WARN | N phantom beads detected |\n| Orphaned Children | PASS/WARN | N orphans found |\n| Multi-Wave Regression | PASS/FAIL | N regressions detected |\n| Stretch Goals | PASS/WARN | N stretch goals closed without rationale |\n\n### Findings\n- \u003cspecific closure-integrity finding>\n\n## Learnings (from Phase 2)\n\n### What Went Well\n- ...\n\n### What Was Hard\n- ...\n\n### Do Differently Next Time\n- ...\n\n### Patterns to Reuse\n- ...\n\n### Anti-Patterns to Avoid\n- ...\n\n### Footgun Entries (Required)\n\n| Footgun | Trigger | Symptom | Fix |\n|---------|---------|---------|-----|\n| Human CLI parsing | Automation parses human-readable CLI output instead of a structured surface such as `--json` | Parser breaks on wording/order changes or mixed prose + JSON output | Use machine-readable output for automation and treat prose output as display-only |\n| Hook git env leakage | Hook/helper shell inherits `GIT_DIR`, `GIT_WORK_TREE`, or `GIT_COMMON_DIR` from another repo/worktree | Git resolves the wrong repo or reports misleading dirty-state evidence | Unset git discovery env before repo resolution, then rerun git from the intended cwd |\n\n## Knowledge Lifecycle\n\n### Backlog Processing (Phase 3)\n- Scanned: N learnings\n- Merged: N duplicates\n- Flagged stale: N\n\n### Activation (Phase 4)\n- Promoted to MEMORY.md: N\n- Constraints compiled: N\n- Next-work items fed: N\n\n### Retirement (Phase 5)\n- Archived: N learnings\n\n## Proactive Improvement Agenda\n\n| # | Area | Improvement | Priority | Horizon | Effort | Evidence |\n|---|------|-------------|----------|---------|--------|----------|\n| 1 | repo / execution / ci-automation | ... | P0/P1/P2 | now/next-cycle/later | S/M/L | ... |\n\n## Prior Findings Resolution Tracking\n\n| Metric | Value |\n|---|---|\n| Backlog entries analyzed | ... |\n| Prior findings total | ... |\n| Resolved findings | ... |\n| Unresolved findings | ... |\n| Resolution rate | ...% |\n\n| Source Epic | Findings | Resolved | Unresolved | Resolution Rate |\n|---|---:|---:|---:|---:|\n| ... | ... | ... | ... | ...% |\n\n## Command-Surface Parity Checklist\n\n| Command File | Run-path Covered by Test? | Evidence (file:line or test name) | Intentionally Uncovered? | Reason |\n|---|---|---|---|---|\n| cli/cmd/ao/\u003ccommand>.go | yes/no | ... | yes/no | ... |\n\n## Next Work\n\n| # | Title | Type | Severity | Source | Target Repo |\n|---|-------|------|----------|--------|-------------|\n| 1 | \u003ctitle> | tech-debt / improvement / pattern-fix / process-improvement | high / medium / low | council-finding / retro-learning / retro-pattern | \u003crepo-name or *> |\n\n### Recommended Next /rpi\n/rpi \"\u003chighest-value improvement>\"\n\n## Status\n\n[ ] CLOSED - Work complete, learnings captured\n[ ] FOLLOW-UP - Issues need addressing (create new beads)\n```\n\nNotes:\n\n- Populate `## Next Work` before `### Recommended Next /rpi`. The suggestion must come from harvested items, not pre-harvest speculation.\n- If no items are harvested, keep the report explicit: `Flywheel stable - no follow-up items identified.`\n- Footgun entries are not optional flavor text. They are harvest inputs for `pattern-fix` items when the cycle discovered real operator/runtime gotchas.\n- Evidence-only or pre-commit closure packets must preserve the durable tracked copy at `.agents/releases/evidence-only-closures/\u003ctarget-id>.json`. The writer also emits a local council copy at `.agents/council/evidence-only-closures/\u003ctarget-id>.json`.\n\nRepo-state excerpt that every full evidence-only closure packet must include\n(the full schema-shaped template follows):\n\n```json\n{\n \"repo_state\": {\n \"repo_root\": \"\u003crepo-root-or-.>\"\n }\n}\n```\n\n## Evidence-Only Closure Artifact\n\nWhen a post-mortem closes an item on validation or policy evidence without a\ncode diff, write a proof artifact to\n`.agents/council/evidence-only-closures/\u003ctarget-id>.json`.\n\nExample producer command:\n\n```bash\nbash skills/post-mortem/scripts/write-evidence-only-closure.sh \\\n --target-id na-a7e.4 \\\n --target-type issue \\\n --producer post-mortem \\\n --evidence-mode auto \\\n --validation-command \"bash tests/hooks/lib-hook-helpers.bats\" \\\n --evidence-summary \"Structured proof artifact added and schema validation passed.\" \\\n --artifact \".agents/council/2026-03-09-post-mortem-na-a7e.md\"\n```\n\nTemplate shape:\n\n```json\n{\n \"$schema\": \"../../../schemas/evidence-only-closure.v1.schema.json\",\n \"schema_version\": 1,\n \"artifact_id\": \"evidence-only-closure-\u003ctarget-id>\",\n \"target_id\": \"\u003ctarget-id>\",\n \"target_type\": \"issue\",\n \"created_at\": \"YYYY-MM-DDTHH:MM:SSZ\",\n \"producer\": \"post-mortem\",\n \"evidence_mode\": \"commit|staged|worktree\",\n \"validation_commands\": [\"bash \u003ccommand>\"],\n \"repo_state\": {\n \"repo_root\": \"/abs/path/to/repo\",\n \"git_branch\": \"main\",\n \"git_dirty\": true,\n \"head_sha\": \"\u003csha>\",\n \"modified_files\": [],\n \"staged_files\": [],\n \"unstaged_files\": [],\n \"untracked_files\": []\n },\n \"evidence\": {\n \"summary\": \"\u003cwhy evidence-only closure is valid>\",\n \"artifacts\": [\"\u003csupporting-path>\"],\n \"notes\": [\"\u003coptional note>\"]\n }\n}\n```\n\nMode guidance:\n\n- `commit`: commit-backed evidence exists and wins for the closed item\n- `staged`: no qualifying commit evidence exists, but the scoped files are staged\n- `worktree`: neither commit nor staged evidence exists, but qualifying unstaged or untracked files exist\n\n`auto` is only the selection input to the writer script. The emitted artifact must\nrecord the resolved mode.\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":6637,"content_sha256":"4b0b46e16bd9d72d5a91d2bd69153efc428305b518897ce84e9f8532a0bcf378"},{"filename":"references/phase-2-extract.md","content":"## Phase 2: Extract Learnings\n\nInline extraction of learnings from the completed work (formerly delegated to the retro skill).\n\n### Step EX.1: Gather Context\n\n```bash\n# Recent commits\ngit log --oneline -20 --since=\"7 days ago\"\n\n# Epic children (if epic ID provided)\nbd children \u003cepic-id> 2>/dev/null | head -20\n\n# Recent plans and research\nls -lt .agents/plans/ .agents/research/ 2>/dev/null | head -10\n```\n\nRead relevant artifacts: research documents, plan documents, commit messages, code changes. Use the Read tool and git commands to understand what was done.\n\n**If retrospecting an epic:** Run the closure integrity quick-check from `references/context-gathering.md` (Phantom Bead Detection + Multi-Wave Regression Scan). Include any warnings in findings.\n\n### Step EX.2: Classify Learnings\n\nAsk these questions:\n\n**What went well?**\n- What approaches worked?\n- What was faster than expected?\n- What should we do again?\n\n**What went wrong?**\n- What failed?\n- What took longer than expected?\n- What would we do differently?\n\n**What did we discover?**\n- New patterns found\n- Codebase quirks learned\n- Tool tips discovered\n- Debugging insights\n- Test pyramid gaps found during implementation or review\n\nFor each learning, capture:\n- **ID**: L1, L2, L3...\n- **Category**: debugging, architecture, process, testing, security\n- **What**: The specific insight\n- **Why it matters**: Impact on future work\n- **Confidence**: high, medium, low\n\n### Step EX.3: Write Learnings\n\n**Write to:** `.agents/learnings/YYYY-MM-DD-\u003ctopic>.md`\n\n```markdown\n---\nid: learning-YYYY-MM-DD-\u003cslug>\ntype: learning\ndate: YYYY-MM-DD\ncategory: \u003ccategory>\nconfidence: \u003chigh|medium|low>\nmaturity: provisional\nutility: 0.5\n---\n\n# Learning: \u003cShort Title>\n\n## What We Learned\n\n\u003c1-2 sentences describing the insight>\n\n## Why It Matters\n\n\u003c1 sentence on impact/value>\n\n## Source\n\n\u003cWhat work this came from>\n\n---\n\n# Learning: \u003cNext Title>\n\n**ID**: L2\n...\n```\n\n### Step EX.3.5: Test Pyramid Gap Analysis\n\nCompare planned vs actual test levels per the test pyramid standard (`test-pyramid.md` in the standards skill). For each closed issue: check planned `test_levels` metadata against actual test files. Write a `## Test Pyramid Assessment` table (Issue | Planned | Actual | Gaps | Action). Gaps with severity >= moderate become `next-work.jsonl` items with type `tech-debt`.\n\n### Step EX.4: Classify Learning Scope\n\nFor each learning extracted in Step EX.3, classify:\n\n**Question:** \"Does this learning reference specific files, packages, or architecture in THIS repo? Or is it a transferable pattern that helps any project?\"\n\n- **Repo-specific** -> Write to `.agents/learnings/` (existing behavior from Step EX.3). Use `git rev-parse --show-toplevel` to resolve repo root — never write relative to cwd.\n- **Cross-cutting/transferable** -> Rewrite to remove repo-specific context (file paths, function names, package names), then:\n 1. Write abstracted version to `~/.agents/learnings/YYYY-MM-DD-\u003cslug>.md` (NOT local — one copy only)\n 2. Run abstraction lint check:\n ```bash\n file=\"\u003cpath-to-written-global-file>\"\n grep -iEn '(internal/|cmd/|\\.go:|/pkg/|/src/|AGENTS\\.md|CLAUDE\\.md)' \"$file\" 2>/dev/null\n grep -En '[A-Z][a-z]+[A-Z][a-z]+\\.(go|py|ts|rs)' \"$file\" 2>/dev/null\n grep -En '\\./[a-z]+/' \"$file\" 2>/dev/null\n ```\n If matches: WARN user with matched lines, ask to proceed or revise. Never block the write.\n\n**Note:** Each learning goes to ONE location (local or global). No `promoted_to` needed — there's no local copy to mark when writing directly to global.\n\n**Example abstraction:**\n- Local: \"Compile's validate package needs O_CREATE|O_EXCL for atomic claims because Zeus spawns concurrent workers\"\n- Global: \"Use O_CREATE|O_EXCL for atomic file creation when multiple processes may race on the same path\"\n\n### Step EX.5: Write Structured Findings to Registry\n\nBefore backlog processing, normalize reusable council findings into `.agents/findings/registry.jsonl`.\n\nUse the tracked contract in `docs/contracts/finding-registry.md`:\n\n- persist only reusable findings that should change future planning or review behavior\n- require `dedup_key`, provenance, `pattern`, `detection_question`, `checklist_item`, `applicable_when`, and `confidence`\n- `applicable_when` must use the controlled vocabulary from the contract\n- append or merge by `dedup_key`\n- use the contract's temp-file-plus-rename atomic write rule\n\nThis registry is the v1 advisory prevention surface. It complements learnings and next-work; it does not replace them.\n\n### Step EX.6: Refresh Compiled Prevention Outputs\n\nAfter the registry mutation, refresh compiled outputs immediately so the same session can benefit from the updated prevention set.\n\nIf `hooks/finding-compiler.sh` exists, run:\n\n```bash\nbash hooks/finding-compiler.sh --quiet 2>/dev/null || true\n```\n\nThis promotes registry rows into `.agents/findings/*.md`, refreshes `.agents/planning-rules/*.md` and `.agents/pre-mortem-checks/*.md`, and rewrites draft constraint metadata under `.agents/constraints/`. Active enforcement still depends on the constraint index lifecycle and runtime hook support, but compilation itself is no longer deferred.\n\n### Step ACT.3: Feed Next-Work\n\nActionable improvements identified during processing -> append one schema v1.4\nbatch entry to `.agents/rpi/next-work.jsonl` using the tracked contract in\n[`../../../docs/contracts/next-work.schema.md`](../../../docs/contracts/next-work.schema.md)\nand the write procedure in\n[`harvest-next-work.md`](harvest-next-work.md).\nFollow the claim/finalize lifecycle documented in `harvest-next-work.md`.\n\n```bash\nmkdir -p .agents/rpi\n# Build VALID_ITEMS via the schema-validation flow in references/harvest-next-work.md\n# Then append one entry per post-mortem / epic.\n# If a harvested item already maps to a known proof surface, preserve it on the\n# item as \"proof_ref\" instead of burying target IDs in free text. Example item:\n# [{\"title\":\"Verify the parity gate after proof propagation lands\",\"type\":\"task\",\"severity\":\"medium\",\"source\":\"council-finding\",\"description\":\"Re-run the targeted validator after the follow-up lands.\",\"target_repo\":\"agentops\",\"proof_ref\":{\"kind\":\"execution_packet\",\"run_id\":\"6f36a5640805\",\"path\":\".agents/rpi/runs/6f36a5640805/execution-packet.json\"}}]\nENTRY_TIMESTAMP=\"$(date -Iseconds)\"\nSOURCE_EPIC=\"${EPIC_ID:-recent}\"\nVALID_ITEMS_JSON=\"${VALID_ITEMS_JSON:-[]}\"\n\nprintf '%s\\n' \"$(jq -cn \\\n --arg source_epic \"$SOURCE_EPIC\" \\\n --arg timestamp \"$ENTRY_TIMESTAMP\" \\\n --argjson items \"$VALID_ITEMS_JSON\" \\\n '{\n source_epic: $source_epic,\n timestamp: $timestamp,\n items: $items,\n consumed: false,\n claim_status: \"available\",\n claimed_by: null,\n claimed_at: null,\n consumed_by: null,\n consumed_at: null\n }'\n)\" >> .agents/rpi/next-work.jsonl\n```\n\n### Step ACT.4: Update Marker\n\n```bash\ndate -Iseconds > .agents/ao/last-processed\n```\n\nThis must be the LAST action in Phase 4.\n\n**Phases 3-6 (Maintenance):** Read `maintenance-phases.md` for backlog processing, activation, retirement, and harvesting phases. Load when `--process-only` flag is set or when running full post-mortem.\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":7145,"content_sha256":"4533db4ec2ed670ba536a51f0b06e2aa50df5363bf5bb5cf44b34886be44decc"},{"filename":"references/plan-compliance-checklist.md","content":"# Plan Compliance Checklist\n\nUse this checklist to mechanically verify implementation against plan.\n\n## How to Use\n\n1. Read the plan file\n2. Extract each TODO/deliverable\n3. For each item, fill in the table\n\n## Checklist Template\n\n| # | Plan Item | Expected File | File Exists? | Implementation Matches? | Evidence |\n|---|-----------|---------------|--------------|------------------------|----------|\n| 1 | \u003ccopy from plan> | \u003cpath> | yes/no | yes/partial/no | file:line |\n| 2 | ... | ... | ... | ... | ... |\n\n## Verification Rules\n\n### \"File Exists?\" Column\n- `yes` = file exists at expected path\n- `no` = file missing (GAP)\n\n### \"Implementation Matches?\" Column\n- `yes` = code does what plan says\n- `partial` = some of it, not all\n- `no` = code does something different\n\n### \"Evidence\" Column\n- Must include file:line reference\n- If no match, explain what's there instead\n\n## Common Gaps\n\n| Gap Type | Example | Action |\n|----------|---------|--------|\n| Missing file | Expected `auth.py`, not found | Create follow-up issue |\n| Partial impl | Tests exist but don't cover edge cases | Document gap |\n| Scope change | Plan said X, we built Y instead | Document rationale |\n\n## Command-Surface Parity Addendum (CLI repos)\n\nWhen plan scope includes `cli/cmd/ao/*.go` changes, add this parity table:\n\n| Command File | Tested Run-path Evidence | Intentionally Uncovered? | Follow-up Issue |\n|---|---|---|---|\n| cli/cmd/ao/\u003ccommand>.go | TestName / file:line | yes/no | ag-xxxx (if yes) |\n\nRules:\n- Every modified command file must have either tested run-path evidence or an intentional-uncovered entry.\n- \"Intentionally uncovered\" requires a concrete follow-up issue ID.\n- Do not close plan-compliance as PASS when command-surface rows are missing.\n\n## Example\n\nFrom a real plan:\n\n| # | Plan Item | Expected File | File Exists? | Implementation Matches? | Evidence |\n|---|-----------|---------------|--------------|------------------------|----------|\n| 1 | Create toolchain-validate.sh | scripts/toolchain-validate.sh | yes | yes | scripts/toolchain-validate.sh:1-375 |\n| 2 | Support --json flag | scripts/toolchain-validate.sh | yes | yes | scripts/toolchain-validate.sh:26,339 |\n| 3 | Add unit tests | tests/scripts/test-toolchain-validate.sh | yes | partial | Missing exit code tests |\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":2288,"content_sha256":"33d168a8e988af24cf4244afa322ff7b549f63dc2ee9e0ba93bb1120e5492a15"},{"filename":"references/prediction-tracking.md","content":"# Pre-Mortem Prediction Tracking\n\nTrack pre-mortem predictions through the lifecycle to measure accuracy.\n\n## Prediction ID Format\n\n```\npm-YYYYMMDD-NNN\n```\n\nExample: `pm-20260312-001`, `pm-20260312-002`\n\nGenerated during pre-mortem report writing (Step 4). Each finding gets a unique prediction ID.\n\n## Report Frontmatter\n\nAdd to pre-mortem report frontmatter:\n\n```yaml\nprediction_ids:\n - pm-20260312-001\n - pm-20260312-002\n```\n\n## Finding Format with Prediction ID\n\nEach finding in the pre-mortem report includes its prediction ID:\n\n```markdown\n| ID | Judge | Finding | Severity | Prediction |\n|----|-------|---------|----------|------------|\n| pm-20260312-001 | Feasibility | Registry write race | significant | Will cause data loss in parallel waves |\n| pm-20260312-002 | Scope | Commit advisor creep | significant | Implementers will add auto-apply |\n```\n\n## Downstream Correlation\n\n### In /vibe (Step 3.6)\n\nWhen a pre-mortem report exists for the current epic:\n1. Load prediction IDs from the most recent pre-mortem report\n2. For each vibe finding, check if it matches a pre-mortem prediction\n3. Tag matched findings with the prediction ID: `predicted_by: pm-20260312-001`\n4. Tag unmatched findings as: `predicted_by: none` (surprise issue)\n\n### In /post-mortem (Phase 2)\n\nAdd \"Prediction Accuracy\" section to the report:\n\n```markdown\n## Prediction Accuracy\n\n| Prediction ID | Predicted | Actual | Hit? |\n|---------------|-----------|--------|------|\n| pm-20260312-001 | Registry write race | No race detected | MISS |\n| pm-20260312-002 | Commit advisor creep | Advisor stayed suggestion-only | MISS |\n| — | — | Vibe found missing test | SURPRISE |\n\n**Accuracy: 0/2 predictions confirmed (0%). 1 surprise issue.**\n```\n\n## Accuracy Scoring\n\n- **HIT**: Pre-mortem prediction matched an actual vibe/implementation finding\n- **MISS**: Pre-mortem prediction did not materialize\n- **SURPRISE**: Actual issue that no pre-mortem prediction covered\n\nHigh miss rate is acceptable — pre-mortem is precautionary. High surprise rate suggests pre-mortem perspectives need expansion.\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":2082,"content_sha256":"efa010232bf666c9b27eaf35a1ff6d1f0a4e44a9f21ff2268c43df0bd020a72f"},{"filename":"references/quick-mode.md","content":"## Quick Mode\n\nGiven `/post-mortem --quick \"insight text\"`:\n\n### Quick Step 1: Generate Slug\n\nCreate a slug from the content: first meaningful words, lowercase, hyphens, max 50 chars.\n\n### Quick Step 2: Write Learning Directly\n\n**Write to:** `.agents/learnings/YYYY-MM-DD-quick-\u003cslug>.md`\n\n```markdown\n---\ntype: learning\nsource: post-mortem-quick\ndate: YYYY-MM-DD\nmaturity: provisional\nutility: 0.5\n---\n\n# Learning: \u003cShort Title>\n\n**Category**: \u003cauto-classify: debugging|architecture|process|testing|security>\n**Confidence**: medium\n\n## What We Learned\n\n\u003cuser's insight text>\n\n## Source\n\nQuick capture via `/post-mortem --quick`\n```\n\nThis skips the full pipeline — writes directly to learnings, no council or backlog processing.\n\n### Quick Step 3: Confirm\n\n```\nLearned: \u003cone-line summary>\nSaved to: .agents/learnings/YYYY-MM-DD-quick-\u003cslug>.md\n\nFor deeper reflection, use `/post-mortem` without --quick.\n```\n\n**Done.** Return immediately after confirmation.\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":960,"content_sha256":"677a9c118ca441fa6f78207bdc88515e68729fbacd6209e52c640baf11800099"},{"filename":"references/retro-history.md","content":"# Persistent Retro History\n\nStore post-mortem summaries in `.agents/retro/` for trend analysis across epics.\n\n## Directory Structure\n\n```\n.agents/retro/\n├── YYYY-MM-DD-\u003cepic-slug>.json # structured summary per post-mortem\n└── index.jsonl # append-only index for fast lookups\n```\n\n## Summary Schema (`YYYY-MM-DD-\u003cepic-slug>.json`)\n\n```json\n{\n \"id\": \"retro-YYYY-MM-DD-\u003cepic-slug>\",\n \"date\": \"YYYY-MM-DD\",\n \"epic_id\": \"\u003cepic-id or 'recent'>\",\n \"verdict\": \"PASS|WARN|FAIL\",\n \"duration_minutes\": 45,\n \"cycle_time_trend\": \"faster|slower|stable\",\n \"learnings_extracted\": 5,\n \"learnings_promoted\": 2,\n \"stale_retired\": 1,\n \"prediction_accuracy\": {\n \"hits\": 1,\n \"misses\": 2,\n \"surprises\": 1,\n \"rate\": 0.33\n },\n \"footguns\": [\"\u003cshort description>\"],\n \"top_learning\": \"\u003csingle most impactful learning>\",\n \"improvements_proposed\": 3,\n \"tags\": [\"\u003ccategory tags from learnings>\"]\n}\n```\n\n## Index Schema (`index.jsonl`)\n\nOne JSON object per line, append-only:\n\n```json\n{\"id\": \"retro-2026-03-12-auth-system\", \"date\": \"2026-03-12\", \"epic_id\": \"ag-abc\", \"verdict\": \"PASS\", \"duration_minutes\": 45, \"learnings_extracted\": 5}\n```\n\n## Write Rules\n\n1. Write the full summary JSON first, then append to `index.jsonl`\n2. Use atomic write (temp file + rename) for the summary JSON\n3. If `.agents/retro/` does not exist, create it: `mkdir -p .agents/retro`\n4. If `index.jsonl` does not exist, create it with the first entry\n5. Dedup by `id` — if a retro with the same id exists, overwrite the summary and skip the index append\n\n## Read Rules (for trend analysis)\n\n### Recent History\n\n```bash\n# Last 5 retros\ntail -5 .agents/retro/index.jsonl | jq -s '.'\n```\n\n### Verdict Trend\n\n```bash\n# Win/loss streak\ntail -10 .agents/retro/index.jsonl | jq -r '.verdict'\n```\n\n### Cycle Time Trend\n\n```bash\n# Duration trend over last 10 retros\ntail -10 .agents/retro/index.jsonl | jq -s '[.[].duration_minutes] | {min: min, max: max, avg: (add/length)}'\n```\n\n### Recurring Footguns\n\n```bash\n# Footguns that appear in 2+ retros\njq -s '[.[].footguns[]?] | group_by(.) | map(select(length > 1) | {footgun: .[0], count: length})' .agents/retro/*.json\n```\n\n## Fail-Open Behavior\n\n- Missing `.agents/retro/` directory → create silently\n- Missing `index.jsonl` → create on first write\n- Malformed existing JSON → warn once, skip that entry\n- Unwritable directory → warn once, continue without persisting history\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":2435,"content_sha256":"523b99fada06c2046f607b9b284dfd1a5c8b51615af56cd6192e17395b693582"},{"filename":"references/security-patterns.md","content":"# Security Patterns for Post-Mortem\n\nSecurity checks to run during post-mortem Phase 3.\n\n---\n\n## Tool Detection\n\nBefore running security tools, check availability:\n\n```bash\ncommand -v gitleaks &>/dev/null && HAS_GITLEAKS=true || HAS_GITLEAKS=false\ncommand -v semgrep &>/dev/null && HAS_SEMGREP=true || HAS_SEMGREP=false\ncommand -v govulncheck &>/dev/null && HAS_GOVULN=true || HAS_GOVULN=false\ncommand -v pip-audit &>/dev/null && HAS_PIPAUDIT=true || HAS_PIPAUDIT=false\n```\n\n---\n\n## Static Analysis Tools\n\n### Gitleaks (Secret Detection)\n\n```bash\n# Full scan\ngitleaks detect --source . --verbose --report-format json --report-path reports/gitleaks.json\n\n# Changed files only\ngit diff --name-only HEAD~10 | xargs gitleaks detect --source\n```\n\n**Severity:**\n- API keys, passwords: CRITICAL\n- Generic secrets: HIGH\n- Potential false positives: MEDIUM\n\n### Semgrep (SAST)\n\n```bash\n# OWASP patterns\nsemgrep --config \"p/owasp-top-ten\" --json -o reports/semgrep.json .\n\n# Python-specific\nsemgrep --config \"p/python\" .\n\n# Go-specific\nsemgrep --config \"p/golang\" .\n```\n\n### Language-Specific Vulnerability Scanners\n\n**Python:**\n```bash\npip-audit -r requirements.txt --format json -o reports/pip-audit.json\nsafety check -r requirements.txt --json > reports/safety.json\n```\n\n**Go:**\n```bash\ngovulncheck -json ./... > reports/govulncheck.json\n```\n\n**JavaScript:**\n```bash\nnpm audit --json > reports/npm-audit.json\n```\n\n---\n\n## Pattern-Based Detection\n\nWhen tools aren't available, use grep patterns:\n\n### SEC-P01: SQL Injection\n\n```bash\n# Python\ngrep -rn \"execute.*%s\\|execute.*format\\|execute.*f\\\"\\|cursor.execute.*+\" --include=\"*.py\" .\n\n# Go\ngrep -rn \"fmt.Sprintf.*SELECT\\|fmt.Sprintf.*INSERT\\|Query.*+\" --include=\"*.go\" .\n```\n\n**Fix:** Use parameterized queries.\n\n### SEC-P02: Command Injection\n\n```bash\n# Python\ngrep -rn \"os.system\\|subprocess.*shell=True\\|eval(\\|exec(\" --include=\"*.py\" .\n\n# Go\ngrep -rn \"exec.Command.*Shell\\|os.Exec\" --include=\"*.go\" .\n```\n\n**Fix:** Use safe APIs, never shell=True with user input.\n\n### SEC-P03: Hardcoded Secrets\n\n```bash\n# All languages\ngrep -rn \"password.*=.*['\\\"].*['\\\"]\" --include=\"*.py\" --include=\"*.go\" --include=\"*.js\" .\ngrep -rn \"api_key.*=.*['\\\"]\" --include=\"*.py\" --include=\"*.go\" --include=\"*.js\" .\ngrep -rn \"secret.*=.*['\\\"].*[a-zA-Z0-9]\" --include=\"*.py\" --include=\"*.go\" --include=\"*.js\" .\n```\n\n**Fix:** Use environment variables or secrets management.\n\n### SEC-P04: Insecure Deserialization\n\n```bash\n# Python\ngrep -rn \"pickle.load\\|yaml.load.*Loader\" --include=\"*.py\" .\n\n# Go\ngrep -rn \"json.Unmarshal.*interface{}\" --include=\"*.go\" .\n```\n\n**Fix:** Use safe loaders, validate before deserializing.\n\n### SEC-P05: XSS Patterns\n\n```bash\n# JavaScript/TypeScript\ngrep -rn \"innerHTML.*=\\|dangerouslySetInnerHTML\\|v-html\" --include=\"*.js\" --include=\"*.ts\" --include=\"*.vue\" .\n\n# Python (templates)\ngrep -rn \"mark_safe\\|{{.*\\|raw}}\\|autoescape false\" --include=\"*.html\" --include=\"*.jinja\" .\n```\n\n**Fix:** Use safe templating, escape output.\n\n### SEC-P06: Path Traversal\n\n```bash\n# All languages\ngrep -rn \"open.*%s\\|open.*format\\|os.path.join.*input\" --include=\"*.py\" .\ngrep -rn \"filepath.Join.*user\" --include=\"*.go\" .\n```\n\n**Fix:** Validate and sanitize file paths.\n\n### SEC-P07: Insecure TLS\n\n```bash\n# Python\ngrep -rn \"verify=False\\|CERT_NONE\" --include=\"*.py\" .\n\n# Go\ngrep -rn \"InsecureSkipVerify.*true\" --include=\"*.go\" .\n```\n\n**Fix:** Never disable certificate verification in production.\n\n### SEC-P08: Weak Cryptography\n\n```bash\n# Python\ngrep -rn \"MD5\\|SHA1\\|DES\\|RC4\" --include=\"*.py\" .\n\n# Go\ngrep -rn \"md5.\\|sha1.\\|des.\\|rc4.\" --include=\"*.go\" .\n```\n\n**Fix:** Use strong algorithms (SHA256+, AES).\n\n---\n\n## OWASP Top 10 Checklist\n\n| OWASP ID | Category | Check | Pattern |\n|----------|----------|-------|---------|\n| A01 | Broken Access Control | Auth checks on routes | Missing `@login_required` |\n| A02 | Cryptographic Failures | Weak crypto, plaintext | SEC-P03, SEC-P08 |\n| A03 | Injection | SQL, command, XSS | SEC-P01, SEC-P02, SEC-P05 |\n| A04 | Insecure Design | Business logic flaws | Manual review |\n| A05 | Security Misconfiguration | Debug mode, defaults | `DEBUG=True`, default passwords |\n| A06 | Vulnerable Components | Old dependencies | pip-audit, npm audit |\n| A07 | Auth Failures | Weak auth, session | Timing attacks, session fixation |\n| A08 | Data Integrity | Deserialization, CI/CD | SEC-P04 |\n| A09 | Logging Failures | Missing logs, PII in logs | grep for sensitive in logs |\n| A10 | SSRF | Server requests | `requests.get(user_input)` |\n\n---\n\n## Expert Agent Delegation\n\nFor CRITICAL findings, spawn security expert:\n\n```python\nTask(\n subagent_type=\"security-expert\",\n prompt=f\"\"\"Deep security review for post-mortem.\n\nFindings to analyze:\n{findings}\n\nChanged files:\n{changed_files}\n\nPlease:\n1. Verify each finding is real (not false positive)\n2. Assess exploitability\n3. Recommend specific fixes\n4. Identify any additional vulnerabilities\n\"\"\"\n)\n```\n\n---\n\n## Report Format\n\n```markdown\n## Security Scan Results\n\n**Date:** YYYY-MM-DD\n**Epic:** \u003cepic-id>\n**Changed Files:** N files\n\n### Summary\n\n| Category | CRITICAL | HIGH | MEDIUM | LOW |\n|----------|----------|------|--------|-----|\n| Secrets | 0 | 0 | 0 | 0 |\n| Vulnerabilities | 0 | 0 | 0 | 0 |\n| Patterns | 0 | 1 | 2 | 0 |\n\n### Findings\n\n#### SEC-001 [HIGH] Potential SQL Injection\n- **File:** services/db.py:42\n- **Pattern:** `cursor.execute(f\"SELECT * FROM {table}\")`\n- **Fix:** Use parameterized query: `cursor.execute(\"SELECT * FROM ?\", (table,))`\n- **Issue Created:** \u003cissue-id>\n\n### Tools Used\n- gitleaks: v8.18.0\n- semgrep: v1.0.0\n- grep patterns: SEC-P01 through SEC-P08\n\n### Recommendations\n1. Fix HIGH findings before merge\n2. Consider adding pre-commit hook for gitleaks\n3. Schedule full security audit (quarterly)\n```\n\n---\n\n## Integration with Post-Mortem\n\nIn Phase 3 of post-mortem:\n\n```python\ndef run_security_scan(changed_files, epic_id):\n findings = []\n\n # 1. Run available tools\n if HAS_GITLEAKS:\n findings += run_gitleaks(changed_files)\n\n if HAS_SEMGREP:\n findings += run_semgrep(changed_files)\n\n # 2. Run grep patterns\n findings += run_grep_patterns(changed_files)\n\n # 3. Check OWASP Top 10\n findings += check_owasp(changed_files)\n\n # 4. Delegate CRITICAL to expert\n critical = [f for f in findings if f.severity == 'CRITICAL']\n if critical:\n expert_review = spawn_security_expert(critical, changed_files)\n findings += expert_review.additional_findings\n\n # 5. Create issues for HIGH+\n for finding in findings:\n if finding.severity in ['CRITICAL', 'HIGH']:\n bd_create(\n title=f\"Security: {finding.title}\",\n type=\"bug\",\n priority=\"P1\" if finding.severity == 'CRITICAL' else \"P2\",\n description=finding.details\n )\n\n return SecurityReport(findings)\n```\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":6858,"content_sha256":"647e1e1f18a84b5ba99a87a6aa1951eb85b00814417843c62137329d342e2faa"},{"filename":"references/streak-tracking.md","content":"# RPI Session Streak Tracking\n\n> Reference for Step 1.5 of the post-mortem skill.\n\n## Purpose\n\nTrack consecutive days of RPI usage to surface workflow adoption trends in post-mortem reports. This is a read-only metric — post-mortem does not write streak data.\n\n## Data Source\n\n**File:** `.agents/rpi/rpi-state.json`\n\nThis file is written by `/rpi` (the RPI orchestrator) on every session start and phase transition. Post-mortem reads it but never writes to it.\n\n## Detection Logic\n\n1. Read `.agents/rpi/rpi-state.json`\n2. If absent or unparseable: **silent no-op** — skip streak reporting entirely\n3. Extract fields:\n - `session_id` — current or most recent RPI session\n - `phase` — current phase (research, plan, implement, validate)\n - `started_at` — ISO 8601 timestamp of session start\n - `verdicts` — array of verdict objects from prior phases\n\n## Streak Calculation\n\nCount consecutive calendar days (UTC) with at least one `rpi-state.json` update:\n\n```bash\n# Read the state file\nRPI_STATE=\".agents/rpi/rpi-state.json\"\nif [ ! -f \"$RPI_STATE\" ]; then\n # Silent no-op — no streak data available\n exit 0\nfi\n\n# Extract last update timestamp\nLAST_UPDATE=$(jq -r '.started_at // .updated_at // empty' \"$RPI_STATE\" 2>/dev/null)\nif [ -z \"$LAST_UPDATE\" ]; then\n exit 0\nfi\n\n# Check if updated today (UTC)\nLAST_DATE=$(date -jf \"%Y-%m-%dT%H:%M:%S\" \"${LAST_UPDATE%%[.+Z]*}\" +%Y-%m-%d 2>/dev/null || \\\n date -d \"${LAST_UPDATE}\" +%Y-%m-%d 2>/dev/null)\nTODAY=$(date -u +%Y-%m-%d)\n```\n\nFor consecutive-day tracking, compare file modification dates of historical state snapshots in `.agents/rpi/`. Each day with at least one `rpi-state.json` write counts as an active day.\n\n## JSON Schema for Streak Data\n\nThe streak summary is computed at read time and included in the post-mortem report. It is NOT persisted separately.\n\n```json\n{\n \"current_streak_days\": 5,\n \"last_rpi_date\": \"2026-03-12\",\n \"total_rpi_sessions\": 14\n}\n```\n\n| Field | Type | Description |\n|-------|------|-------------|\n| `current_streak_days` | integer | Consecutive calendar days with at least 1 rpi-state.json update. Minimum 1 if state file exists. |\n| `last_rpi_date` | string (YYYY-MM-DD) | Date of most recent rpi-state.json update. |\n| `total_rpi_sessions` | integer | Count of distinct `session_id` values found in rpi state history. Falls back to 1 if only current state exists. |\n\n## Counting Rules\n\n- **Session boundary:** Defined by `session_id` in `rpi-state.json`. Do NOT infer sessions from timestamps.\n- **Consecutive days:** A streak breaks if a calendar day (UTC) passes with zero `rpi-state.json` updates.\n- **Minimum streak:** If `rpi-state.json` exists and is valid, the minimum streak is 1.\n- **Historical data:** If `.agents/rpi/` contains archived state files or `outcomes.jsonl`, use those to compute multi-day streaks. Otherwise, streak = 1 (current session only).\n\n## Fallback Behavior\n\n| Condition | Behavior |\n|-----------|----------|\n| `rpi-state.json` absent | Silent no-op. No tweetable line in report. |\n| `rpi-state.json` unparseable (invalid JSON) | Silent no-op. Log warning to stderr only. |\n| `rpi-state.json` missing expected fields | Use available fields, default missing to safe values. |\n| No historical state files | Streak = 1, total sessions = 1 (current only). |\n\n## Tweetable Summary Format\n\nWhen streak data is available, add this line to the TOP of the post-mortem report (before the verdict table):\n\n```\n> RPI streak: 5 consecutive days | Sessions: 14 | Last verdict: PASS\n```\n\nIf no verdict history exists, omit the verdict portion:\n\n```\n> RPI streak: 1 consecutive days | Sessions: 1\n```\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":3633,"content_sha256":"3d70fd95105beaf9f3390f1f19f8b025527713ac9ecf4b8077e235d1aab2b9c1"},{"filename":"references/user-reporting.md","content":"## Step 7: Report to User\n\nTell the user:\n1. Council verdict on implementation\n2. Key learnings\n3. Any follow-up items\n4. Location of post-mortem report\n5. Knowledge flywheel status\n6. **Suggested next `/rpi` command** from the harvested `## Next Work` section (ALWAYS — this is how the flywheel spins itself)\n7. ALL proactive improvements, organized by priority (highlight one quick win)\n8. Knowledge lifecycle summary (Phase 3-5 stats)\n\n**The next `/rpi` suggestion is MANDATORY, not opt-in.** After every post-mortem, present the highest-severity harvested item as a ready-to-copy command:\n\n```markdown\n## Flywheel: Next Cycle\n\nBased on this post-mortem, the highest-priority follow-up is:\n\n> **\u003ctitle>** (\u003ctype>, \u003cseverity>)\n> \u003c1-line description>\n\nReady to run:\n```\n/rpi \"\u003ctitle>\"\n```\n\nOr see all N harvested items in `.agents/rpi/next-work.jsonl`.\n```\n\nIf no items were harvested, write: \"Flywheel stable — no follow-up items identified.\"\n\n---\n\n## Integration with Workflow\n\n```\n/plan epic-123\n |\n v\n/pre-mortem (council on plan)\n |\n v\n/implement\n |\n v\n/vibe (council on code)\n |\n v\nShip it\n |\n v\n/post-mortem \u003c-- You are here\n |\n |-- Phase 1: Council validates implementation\n |-- Phase 2: Extract learnings (inline)\n |-- Phase 3: Process backlog (score, dedup, flag stale)\n |-- Phase 4: Activate (promote to MEMORY.md, compile constraints)\n |-- Phase 5: Retire stale learnings\n |-- Phase 6: Harvest next work\n |-- Suggest next /rpi --------------------+\n |\n +----------------------------------------+\n | (flywheel: learnings become next work)\n v\n/rpi \"\u003chighest-priority enhancement>\"\n```\n\n---\n\n## Examples\n\n### Wrap Up Recent Work\n\n**User says:** `/post-mortem`\n\n**What happens:**\n1. Agent scans recent commits.\n2. Runs `/council --deep validate recent`.\n3. Extracts learnings, processes backlog, and promotes items.\n4. Harvests next-work to `.agents/rpi/next-work.jsonl`.\n\n**Result:** Report with learnings, stats, and a suggested `/rpi` command.\n\n### Other Modes\n\n- **Epic-specific:** `/post-mortem ag-5k2` — review against the target plan\n- **Quick capture:** `/post-mortem --quick \"insight\"` — write a learning without council\n- **Process-only:** `/post-mortem --process-only` — run backlog processing only\n- **Cross-vendor:** `/post-mortem --mixed ag-3b7` — broaden judgment coverage\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":2431,"content_sha256":"029d0c243c719e8dcfea195f590cd1f561e2718d49abb5e1001d3f954e3b680d"},{"filename":"scripts/closure-integrity-audit.sh","content":"#!/usr/bin/env bash\nset -euo pipefail\n\nSCOPE=\"auto\"\nEPIC_ID=\"\"\nCOLLECTION_DETAIL=\"\"\n# Grace window (seconds) for close-before-commit evidence.\n# Commits landing within this window after bead close are still considered valid.\nGRACE_SECONDS=86400 # 24 hours\n\nusage() {\n cat \u003c\u003c'EOF'\nUsage: bash skills/post-mortem/scripts/closure-integrity-audit.sh [--scope auto|commit|staged|worktree] \u003cepic-id>\nEOF\n}\n\nwhile [[ $# -gt 0 ]]; do\n case \"$1\" in\n --scope)\n SCOPE=\"${2:-}\"\n shift 2\n ;;\n -h|--help)\n usage\n exit 0\n ;;\n *)\n if [[ -z \"$EPIC_ID\" ]]; then\n EPIC_ID=\"$1\"\n shift\n else\n echo \"Unknown arg: $1\" >&2\n usage >&2\n exit 2\n fi\n ;;\n esac\ndone\n\ncase \"$SCOPE\" in\n auto|commit|staged|worktree) ;;\n *)\n echo \"Invalid --scope: $SCOPE\" >&2\n usage >&2\n exit 2\n ;;\nesac\n\n[[ -n \"$EPIC_ID\" ]] || {\n echo \"epic id is required\" >&2\n usage >&2\n exit 2\n}\n\ncommand -v jq >/dev/null 2>&1 || {\n echo \"jq is required\" >&2\n exit 1\n}\n\ncommand -v bd >/dev/null 2>&1 || {\n echo \"bd is required\" >&2\n exit 1\n}\n\nFILE_PATH_REGEX='([.[:alnum:]_-]+/)*[.[:alnum:]_-]+\\.[[:alpha:]][[:alnum:]_-]*'\nGENERIC_REPO_PATH_REGEX='([.[:alnum:]_-]+/)+[.[:alnum:]_-]+\\.[[:alpha:]][[:alnum:]_-]*'\n\njson_array_from_stream() {\n if ! sed '/^[[:space:]]*$/d' | sort -u | jq -R . | jq -s .; then\n printf '[]\\n'\n fi\n}\n\nrun_git_clean() {\n env -u GIT_DIR -u GIT_WORK_TREE -u GIT_COMMON_DIR git \"$@\"\n}\n\nregex_escape_extended() {\n printf '%s' \"$1\" | sed -e 's/[][(){}.^$*+?|\\\\-]/\\\\&/g'\n}\n\nbd_show_json() {\n local issue_id=\"$1\"\n bd show \"$issue_id\" --json 2>/dev/null | jq -ec 'if type == \"array\" then .[0] // empty else . end'\n}\n\nextract_description_from_show_text() {\n awk '\n /^DESCRIPTION$/ { in_desc = 1; next }\n in_desc {\n if ($0 ~ /^(LABELS|DEPENDENCIES|DEPENDENTS|CHILDREN|COMMENTS|REFERENCES|NOTES):?[[:space:]]*$/) {\n exit\n }\n print\n }\n '\n}\n\nextract_close_reason_from_show_text() {\n awk '\n /Close reason:/ {\n sub(/^.*Close reason:[[:space:]]*/, \"\", $0)\n print\n exit\n }\n '\n}\n\nissue_audit_text() {\n local child=\"$1\"\n local child_json=\"\"\n local human_output=\"\"\n local description=\"\"\n local close_reason=\"\"\n\n if child_json=\"$(bd_show_json \"$child\" 2>/dev/null)\"; then\n printf '%s\\n' \"$child_json\" \\\n | jq -r '\n [\n (.title // \"\"),\n (.description // \"\"),\n (.acceptance_criteria // \"\"),\n (.close_reason // \"\")\n ]\n | map(select(type == \"string\" and length > 0))\n | join(\"\\n\\n\")\n '\n return 0\n fi\n\n human_output=\"$(bd show \"$child\" 2>/dev/null || true)\"\n description=\"$(printf '%s\\n' \"$human_output\" | extract_description_from_show_text)\"\n close_reason=\"$(printf '%s\\n' \"$human_output\" | extract_close_reason_from_show_text)\"\n\n printf '%s\\n\\n%s\\n' \"$description\" \"$close_reason\"\n}\n\ncollect_children_from_bd_children_json() {\n local json_output\n json_output=\"$(bd children \"$EPIC_ID\" --json 2>/dev/null)\" || return 1\n printf '%s\\n' \"$json_output\" \\\n | jq -er '\n .[]? |\n if type == \"object\" then\n (.id // .child_id // .issue_id // empty)\n elif type == \"string\" then\n .\n else\n empty\n end\n ' 2>/dev/null \\\n | sed '/^[[:space:]]*$/d' \\\n | sort -u\n}\n\ncollect_children_from_bd_show_json() {\n local json_output\n json_output=\"$(bd show \"$EPIC_ID\" --json 2>/dev/null)\" || return 1\n printf '%s\\n' \"$json_output\" \\\n | jq -er '\n .[]? |\n ((.dependents // .children // [])[]? |\n select((.dependency_type // .type // \"parent-child\") == \"parent-child\") |\n (.id // .child_id // .issue_id // empty))\n ' 2>/dev/null \\\n | sed '/^[[:space:]]*$/d' \\\n | sort -u\n}\n\ncollect_children_from_human_output() {\n local human_output\n human_output=\"$(bd show \"$EPIC_ID\" 2>/dev/null)\" || return 1\n printf '%s\\n' \"$human_output\" \\\n | awk '\n /^CHILDREN$/ { in_children = 1; next }\n in_children && /^[[:space:]]*$/ { exit }\n in_children { print }\n ' \\\n | grep -oE '[[:alnum:]]+-[[:alnum:]]+(\\.[0-9]+)+' \\\n | sort -u\n}\n\ncollect_children() {\n local children_output=\"\"\n\n if children_output=\"$(collect_children_from_bd_children_json)\" && [[ -n \"$children_output\" ]]; then\n printf '%s\\n' \"$children_output\"\n return 0\n fi\n\n if children_output=\"$(collect_children_from_bd_show_json)\" && [[ -n \"$children_output\" ]]; then\n printf '%s\\n' \"$children_output\"\n return 0\n fi\n\n if children_output=\"$(collect_children_from_human_output)\" && [[ -n \"$children_output\" ]]; then\n printf '%s\\n' \"$children_output\"\n return 0\n fi\n\n COLLECTION_DETAIL=\"no child issues discovered from bd children/show output\"\n return 1\n}\n\nextract_validation_block_from_text() {\n awk '\n /^```validation[[:space:]]*$/ { in_block = 1; next }\n in_block && /^```[[:space:]]*$/ { exit }\n in_block { print }\n '\n}\n\nextract_validation_files_from_block() {\n local validation_block=\"$1\"\n\n [[ -n \"$validation_block\" ]] || return 0\n printf '%s\\n' \"$validation_block\" \\\n | jq -r '\n def as_items:\n if type == \"array\" then .[]\n else .\n end;\n (\n (.files // [])[]?,\n (.files_exist // [])[]?,\n ((.content_check // empty) | as_items | .file?),\n ((.content_checks // empty) | as_items | .file?),\n ((.paired_files // empty) | as_items | .file?)\n )\n | select(type == \"string\" and length > 0)\n ' 2>/dev/null || true\n}\n\nextract_file_paths_from_stream() {\n grep -oE \"$FILE_PATH_REGEX\" || true\n}\n\nstrip_urls_from_stream() {\n sed -E 's@[[:alpha:]][[:alnum:]+.-]*://[^[:space:])>]+@@g'\n}\n\nextract_first_file_path_from_stream() {\n extract_file_paths_from_stream | head -n 1\n}\n\nextract_files_section_from_text() {\n awk '\n tolower($0) ~ /^[[:space:]]*files:[[:space:]]*$/ { in_files = 1; next }\n tolower($0) ~ /^[[:space:]]*files likely owned:[[:space:]]*$/ { in_files = 1; next }\n tolower($0) ~ /^[[:space:]]*likely files:[[:space:]]*$/ { in_files = 1; next }\n tolower($0) ~ /^[[:space:]]*primary files:[[:space:]]*$/ { in_files = 1; next }\n tolower($0) ~ /^[[:space:]]*scoped files:[[:space:]]*$/ { in_files = 1; next }\n in_files {\n if ($0 ~ /^[[:space:]]*$/ || $0 ~ /^```/) {\n exit\n }\n # Accept lines starting with - or * (bullet points)\n if ($0 !~ /^[[:space:]]*[-*]/) {\n exit\n }\n # Strip bullet prefix and backticks\n sub(/^[[:space:]]*[-*][[:space:]]*/, \"\", $0)\n gsub(/`/, \"\", $0)\n print\n }\n ' | extract_file_paths_from_stream\n}\n\nextract_labeled_files_from_text() {\n local line=\"\"\n local candidate=\"\"\n\n while IFS= read -r line; do\n candidate=\"\"\n\n if [[ \"$line\" =~ (^|[[:space:][:punct:]])New[[:space:]]+[Ff][Ii][Ll][Ee][Ss]?:[[:space:]]*(.*)$ ]]; then\n candidate=\"${BASH_REMATCH[2]}\"\n elif [[ \"$line\" =~ (^|[[:space:][:punct:]])File:[[:space:]]*(.*)$ ]]; then\n candidate=\"${BASH_REMATCH[2]}\"\n fi\n\n if [[ -n \"$candidate\" ]]; then\n printf '%s\\n' \"$candidate\" | extract_first_file_path_from_stream\n fi\n done\n}\n\nextract_repo_relative_paths_from_text() {\n local line=\"\"\n\n while IFS= read -r line; do\n printf '%s\\n' \"$line\" \\\n | strip_urls_from_stream \\\n | grep -oE \"$GENERIC_REPO_PATH_REGEX\" || true\n done\n}\n\nextract_prose_file_paths_from_text() {\n strip_urls_from_stream | extract_file_paths_from_stream | grep -vx 'SKILL[.]md' || true\n}\n\nextract_validation_command_strings_from_block() {\n local validation_block=\"$1\"\n\n [[ -n \"$validation_block\" ]] || return 0\n printf '%s\\n' \"$validation_block\" \\\n | jq -r '\n def roots:\n if type == \"array\" then .[]\n else .\n end;\n roots |\n (\n .command?,\n .commands[]?,\n .test?,\n .tests?,\n .validation_command?,\n .validation_commands[]?\n )\n | select(type == \"string\" and length > 0)\n ' 2>/dev/null || true\n}\n\ntrim_path_punctuation() {\n printf '%s\\n' \"$1\" | sed -E 's@[[:punct:]]+$@@; s@/$@@'\n}\n\nnormalize_command_path() {\n local raw=\"$1\"\n local cd_dir=\"$2\"\n local path=\"$raw\"\n local base=\"\"\n\n path=\"${path#./}\"\n path=\"${path%/}\"\n cd_dir=\"${cd_dir#./}\"\n cd_dir=\"${cd_dir%/}\"\n path=\"$(trim_path_punctuation \"$path\")\"\n cd_dir=\"$(trim_path_punctuation \"$cd_dir\")\"\n\n [[ -n \"$path\" ]] || return 0\n if [[ -n \"$cd_dir\" && \"$raw\" == ./* ]]; then\n printf '%s/%s\\n' \"$cd_dir\" \"$path\"\n elif [[ -n \"$cd_dir\" && \"$raw\" == ../* ]]; then\n base=\"$cd_dir\"\n while [[ \"$path\" == ../* ]]; do\n path=\"${path#../}\"\n if [[ \"$base\" == */* ]]; then\n base=\"${base%/*}\"\n else\n base=\"\"\n fi\n done\n if [[ -n \"$base\" ]]; then\n printf '%s/%s\\n' \"$base\" \"$path\"\n else\n printf '%s\\n' \"$path\"\n fi\n else\n printf '%s\\n' \"$path\"\n fi\n}\n\nextract_paths_from_command_string() {\n local command_text=\"$1\"\n local cd_dir=\"\"\n local cd_regex='(^|[[:space:];|&])cd[[:space:]]+([^[:space:];|&]+)[[:space:]]*&&'\n local raw_path=\"\"\n\n if [[ \"$command_text\" =~ $cd_regex ]]; then\n cd_dir=\"${BASH_REMATCH[2]}\"\n cd_dir=\"${cd_dir%\\\"}\"\n cd_dir=\"${cd_dir#\\\"}\"\n cd_dir=\"${cd_dir%\\'}\"\n cd_dir=\"${cd_dir#\\'}\"\n fi\n\n {\n printf '%s\\n' \"$command_text\" \\\n | strip_urls_from_stream \\\n | grep -oE '(\\./)?([.[:alnum:]_-]+/)+[.[:alnum:]_-]+/?' || true\n printf '%s\\n' \"$command_text\" \\\n | strip_urls_from_stream \\\n | extract_file_paths_from_stream\n } | while IFS= read -r raw_path; do\n normalize_command_path \"$raw_path\" \"$cd_dir\"\n done\n}\n\nfilter_probable_repo_paths() {\n local path=\"\"\n\n while IFS= read -r path; do\n path=\"$(trim_path_punctuation \"$path\")\"\n [[ -n \"$path\" ]] || continue\n if [[ \"$path\" == ./* ]]; then\n path=\"${path#./}\"\n fi\n while [[ \"$path\" == ../* ]]; do\n path=\"${path#../}\"\n done\n\n case \"$path\" in\n .agents/*|.github/*|cli/*|docs/*|schemas/*|scripts/*|skills/*|skills-codex/*|hooks/*|tests/*|evals/*|lib/*)\n printf '%s\\n' \"$path\"\n continue\n ;;\n AGENTS.md|GOALS.md|PRODUCT.md|README.md|SKILL-TIERS.md)\n printf '%s\\n' \"$path\"\n continue\n ;;\n esac\n\n if [[ -e \"$path\" ]]; then\n printf '%s\\n' \"$path\"\n continue\n fi\n if run_git_clean ls-files --error-unmatch -- \"$path\" >/dev/null 2>&1; then\n printf '%s\\n' \"$path\"\n fi\n done\n}\n\nextract_command_paths_from_text() {\n local line=\"\"\n\n while IFS= read -r line; do\n [[ -n \"$line\" ]] || continue\n extract_paths_from_command_string \"$line\"\n done\n}\n\nextract_validation_command_paths_from_block() {\n local validation_block=\"$1\"\n local command_text=\"\"\n\n extract_validation_command_strings_from_block \"$validation_block\" \\\n | while IFS= read -r command_text; do\n extract_paths_from_command_string \"$command_text\"\n done\n}\n\nexpand_scoped_paths_from_stream() {\n local path=\"\"\n local expanded=\"\"\n\n while IFS= read -r path; do\n [[ -n \"$path\" ]] || continue\n printf '%s\\n' \"$path\"\n\n if [[ \"$path\" != */* && \"$path\" == *.* ]]; then\n expanded=\"$(run_git_clean ls-files --cached --others --exclude-standard -- \"$path\" \":(glob)**/$path\" 2>/dev/null || true)\"\n [[ -n \"$expanded\" ]] && printf '%s\\n' \"$expanded\"\n fi\n done\n}\n\nextract_backticked_files_from_text() {\n # Handle backticked filenames across multiple lines, including nested backticks\n # and paths with spaces or special characters inside backticks\n tr '\\n' '\\0' \\\n | grep -zoE \"\\`[^\\`]+\\`\" \\\n | tr '\\0' '\\n' \\\n | tr -d '`' \\\n | grep -E \"$FILE_PATH_REGEX\" \\\n | grep -oE \"$FILE_PATH_REGEX\" || true\n}\n\nextract_scoped_files() {\n local child=\"$1\"\n local audit_text=\"\"\n local validation_block=\"\"\n\n audit_text=\"$(issue_audit_text \"$child\")\"\n\n validation_block=\"$(printf '%s\\n' \"$audit_text\" | extract_validation_block_from_text)\"\n\n {\n extract_validation_files_from_block \"$validation_block\"\n extract_validation_command_paths_from_block \"$validation_block\"\n printf '%s\\n' \"$audit_text\" | extract_labeled_files_from_text\n printf '%s\\n' \"$audit_text\" | extract_files_section_from_text\n printf '%s\\n' \"$audit_text\" | extract_backticked_files_from_text\n printf '%s\\n' \"$audit_text\" | extract_command_paths_from_text\n printf '%s\\n' \"$audit_text\" | extract_repo_relative_paths_from_text\n printf '%s\\n' \"$audit_text\" | extract_prose_file_paths_from_text\n } | sed '/^[[:space:]]*$/d' | expand_scoped_paths_from_stream | filter_probable_repo_paths | sort -u\n}\n\nissue_text_has_file_patterns() {\n # Returns 0 (true) if the bead's own structured text mentions file-like patterns\n # (contains \"/\" or \".go\" or \".sh\" or \".md\"). Used to distinguish a genuine\n # parser miss from a bead that simply has no file scope at all.\n local child=\"$1\"\n local audit_text=\"\"\n\n audit_text=\"$(issue_audit_text \"$child\")\"\n printf '%s\\n' \"$audit_text\" | grep -qE '/|\\.go|\\.sh|\\.md'\n}\n\nissue_timestamp() {\n local child_json=\"$1\"\n local field=\"$2\"\n printf '%s\\n' \"$child_json\" | jq -r --arg field \"$field\" '.[$field] // empty'\n}\n\ncommit_ref_exists() {\n local child=\"$1\"\n local escaped_child\n local pattern\n\n escaped_child=\"$(regex_escape_extended \"$child\")\"\n pattern=\"(^|[^[:alnum:]_.-])${escaped_child}([^[:alnum:]_.-]|$)\"\n run_git_clean log -n 1 --format='%H' --all --extended-regexp --grep=\"$pattern\" 2>/dev/null | grep -q .\n}\n\ntarget_is_closed_non_epic() {\n local target_id=\"$1\"\n local target_json=\"\"\n local issue_type=\"\"\n local human_output=\"\"\n\n if target_json=\"$(bd_show_json \"$target_id\" 2>/dev/null)\"; then\n child_is_closed \"$target_json\" || return 1\n issue_type=\"$(\n printf '%s\\n' \"$target_json\" \\\n | jq -r '(.issue_type // .type // \"\") | ascii_downcase'\n )\"\n [[ \"$issue_type\" == \"epic\" ]] && return 1\n [[ -n \"$issue_type\" ]] && return 0\n fi\n\n human_output=\"$(bd show \"$target_id\" 2>/dev/null || true)\"\n [[ \"$human_output\" == *\"CLOSED\"* ]] || return 1\n [[ \"$human_output\" == *\"[EPIC]\"* ]] && return 1\n [[ \"$human_output\" =~ \\[(TASK|BUG|FEATURE|CHORE|ISSUE|DECISION)\\] ]] || return 1\n return 0\n}\n\nextract_pr_numbers_from_text() {\n grep -Eio '((pull[ -]?request|pr)[[:space:]#:]*[0-9]+|pull/[0-9]+|#[0-9]+)' \\\n | grep -oE '[0-9]+' \\\n | sort -u || true\n}\n\ntask_queue_pr_merge_matches_json() {\n local target_id=\"$1\"\n local audit_text=\"\"\n local pr_number=\"\"\n local commit_sha=\"\"\n local -a matches=()\n\n audit_text=\"$(issue_audit_text \"$target_id\")\"\n while IFS= read -r pr_number; do\n [[ -n \"$pr_number\" ]] || continue\n commit_sha=\"$(\n run_git_clean log -n 1 --format='%H' --all --fixed-strings --grep=\"#${pr_number}\" 2>/dev/null \\\n | head -n 1\n )\"\n if [[ -n \"$commit_sha\" ]]; then\n matches+=(\"#${pr_number} ${commit_sha}\")\n fi\n done \u003c \u003c(printf '%s\\n' \"$audit_text\" | extract_pr_numbers_from_text)\n\n if [[ \"${#matches[@]}\" -eq 0 ]]; then\n printf '[]\\n'\n return 0\n fi\n\n printf '%s\\n' \"${matches[@]}\" | json_array_from_stream\n}\n\ncommit_matches_json() {\n local since=\"$1\"\n local until=\"$2\"\n shift 2\n local file\n local -a matched_files=()\n local -a git_args=(log -n 1 --format=%H --all --diff-filter=ACMR)\n\n [[ -n \"$since\" ]] && git_args+=(\"--since=$since\")\n [[ -n \"$until\" ]] && git_args+=(\"--until=$until\")\n\n for file in \"$@\"; do\n if run_git_clean \"${git_args[@]}\" -- \"$file\" 2>/dev/null | grep -q .; then\n matched_files+=(\"$file\")\n fi\n done\n\n if [[ \"${#matched_files[@]}\" -eq 0 ]]; then\n printf '[]\\n'\n return 0\n fi\n\n printf '%s\\n' \"${matched_files[@]}\" | json_array_from_stream\n}\n\nstaged_matches_json() {\n if [[ \"$#\" -eq 0 ]]; then\n printf '[]\\n'\n return 0\n fi\n run_git_clean diff --cached --name-only --diff-filter=ACMR -- \"$@\" 2>/dev/null | json_array_from_stream\n}\n\nworktree_matches_json() {\n if [[ \"$#\" -eq 0 ]]; then\n printf '[]\\n'\n return 0\n fi\n\n {\n run_git_clean diff --name-only --diff-filter=ACMR -- \"$@\" 2>/dev/null || true\n run_git_clean ls-files --others --exclude-standard -- \"$@\" 2>/dev/null || true\n } | json_array_from_stream\n}\n\nis_discovery_phase_path() {\n # Discovery-phase artifacts are ephemeral seeds for brainstorm/research/discovery\n # sessions. They are NOT durable proof surfaces. A closed bead that cites one\n # but never persisted it should not hard-fail closure-integrity-audit as long\n # as the bead has other real proof (commit referencing the id, a non-discovery\n # scoped file that does have evidence, or an evidence-only packet).\n local path=\"$1\"\n [[ \"$path\" == .agents/brainstorm/* ]] && return 0\n [[ \"$path\" == .agents/research/* ]] && return 0\n [[ \"$path\" == .agents/discovery/* ]] && return 0\n return 1\n}\n\nall_scoped_files_are_discovery() {\n # Returns 0 (true) if every scoped file is a discovery-phase artifact AND\n # there is at least one such file. Empty input returns 1 (false).\n local file\n local any=1\n for file in \"$@\"; do\n any=0\n is_discovery_phase_path \"$file\" || return 1\n done\n return $any\n}\n\nchild_has_nondiscovery_proof_surface() {\n # Returns 0 (true) if the bead has at least one non-discovery proof surface\n # that audits can replay against:\n # - a commit message referencing the bead id\n # - a durable evidence-only packet\n # - a .agents/plans/ or .agents/findings/ file referenced in the bead text\n # that actually exists on disk\n # - any non-discovery file path referenced in the bead text (description +\n # close reason) that has real git history\n # Used only to downgrade discovery-only timing misses to discovery_miss WARN.\n local child=\"$1\"\n local packet_path=\"\"\n\n if commit_ref_exists \"$child\"; then\n return 0\n fi\n if packet_path=\"$(durable_packet_path_for_child \"$child\")\" && packet_is_valid_for_child \"$packet_path\" \"$child\"; then\n return 0\n fi\n\n local human_output\n human_output=\"$(bd show \"$child\" 2>/dev/null || true)\"\n [[ -n \"$human_output\" ]] || return 1\n\n # Collect all file-like paths from the full bd-show text (description +\n # close reason). Filter to non-discovery paths.\n local candidate=\"\"\n while IFS= read -r candidate; do\n [[ -n \"$candidate\" ]] || continue\n is_discovery_phase_path \"$candidate\" && continue\n case \"$candidate\" in\n .agents/plans/*|.agents/findings/*|.agents/council/*|.agents/releases/*)\n [[ -e \"$candidate\" ]] && return 0\n ;;\n esac\n # Any non-discovery file path that exists OR has git history counts.\n if [[ -e \"$candidate\" ]]; then\n return 0\n fi\n if run_git_clean log -n 1 --format=%H --all --diff-filter=ACMR -- \"$candidate\" 2>/dev/null | grep -q .; then\n return 0\n fi\n done \u003c \u003c(\n printf '%s\\n' \"$human_output\" \\\n | strip_urls_from_stream \\\n | extract_file_paths_from_stream \\\n | sed '/^[[:space:]]*$/d' \\\n | sort -u\n )\n\n # Last proof surface: a non-trivial \"Close reason:\" line (>= 24 chars of\n # free text after the prefix). A substantive close reason written at\n # bd-close time is itself auditable evidence that the work was accepted.\n # Empty or generic close reasons do NOT count.\n local close_reason_len=0\n close_reason_len=\"$(\n printf '%s\\n' \"$human_output\" \\\n | awk -F'Close reason:' '/Close reason:/ { print length($2); exit }'\n )\"\n if [[ -n \"$close_reason_len\" && \"$close_reason_len\" -ge 24 ]]; then\n return 0\n fi\n\n return 1\n}\n\nchild_is_closed() {\n local child_json=\"$1\"\n\n printf '%s\\n' \"$child_json\" \\\n | jq -e '\n (.status // \"\" | ascii_downcase) == \"closed\" or\n ((.closed_at // \"\") | length > 0)\n ' >/dev/null 2>&1\n}\n\nadd_grace_to_timestamp() {\n local ts=\"$1\"\n local grace=\"$2\"\n local normalized_ts=\"\"\n local naive_ts=\"\"\n local epoch=\"\"\n\n [[ -n \"$ts\" ]] || return 1\n if date -d \"$ts + ${grace} seconds\" -Iseconds 2>/dev/null; then\n return 0\n fi\n\n # macOS date fallback: parse UTC Z or strip colon from timezone offset for %z.\n if [[ \"$ts\" == *Z ]]; then\n epoch=\"$(date -u -jf '%Y-%m-%dT%H:%M:%SZ' \"$ts\" '+%s' 2>/dev/null)\" || true\n fi\n\n if [[ -z \"$epoch\" ]]; then\n normalized_ts=\"$ts\"\n if [[ \"$normalized_ts\" =~ [+-][0-9]{2}:[0-9]{2}$ ]]; then\n normalized_ts=\"${normalized_ts%:*}${normalized_ts##*:}\"\n fi\n epoch=\"$(date -jf '%Y-%m-%dT%H:%M:%S%z' \"$normalized_ts\" '+%s' 2>/dev/null)\" || true\n fi\n\n if [[ -z \"$epoch\" ]]; then\n # Last resort: parse date portion only (loses TZ accuracy, acceptable for grace)\n naive_ts=\"$(printf '%s\\n' \"$ts\" | sed -E 's/Z$//; s/[+-][0-9]{2}:?[0-9]{2}$//')\"\n epoch=\"$(date -jf '%Y-%m-%dT%H:%M:%S' \"$naive_ts\" '+%s' 2>/dev/null)\" || return 1\n fi\n\n date -u -r $((epoch + grace)) '+%Y-%m-%dT%H:%M:%S+00:00' 2>/dev/null\n}\n\npacket_is_valid_for_child() {\n local packet_path=\"$1\"\n local child=\"$2\"\n\n [[ -f \"$packet_path\" ]] || return 1\n # Accept any packet whose target_id matches and has at least one artifact.\n # The packet's evidence_mode field describes what was happening in the repo at\n # write time (commit/staged/worktree/auto); it does NOT determine whether the\n # packet itself is valid closure proof. The file's presence at the\n # evidence-only-closures path is the proof — do not gatekeep on evidence_mode.\n jq -e --arg child \"$child\" '\n .target_id == $child and\n (.evidence.artifacts | type == \"array\" and length > 0)\n ' \"$packet_path\" >/dev/null 2>&1\n}\n\ndurable_packet_path_for_child() {\n local child=\"$1\"\n local safe_child=\"${child//\\//_}\"\n\n if [[ -f \".agents/releases/evidence-only-closures/${safe_child}.json\" ]]; then\n printf '.agents/releases/evidence-only-closures/%s.json\\n' \"$safe_child\"\n return 0\n fi\n if [[ -f \".agents/council/evidence-only-closures/${safe_child}.json\" ]]; then\n printf '.agents/council/evidence-only-closures/%s.json\\n' \"$safe_child\"\n return 0\n fi\n return 1\n}\n\nhas_evidence_only_packet() {\n # Returns 0 iff a durable evidence-only closure packet exists for the given\n # target id AND parses as JSON containing both `evidence_mode` and\n # `repo_state` keys (the schema written by\n # skills/post-mortem/scripts/write-evidence-only-closure.sh).\n #\n # Used as a top-of-loop short-circuit in classify_child: when this returns 0,\n # the bead is accepted as fully closed (PASS, evidence-only-packet) and ALL\n # other classification paths (parser_miss, timing_miss, discovery_miss) are\n # skipped. This makes evidence-only packets the strongest proof surface.\n local target_id=\"$1\"\n local safe_target=\"${target_id//\\//_}\"\n local packet_path=\"\"\n\n if [[ -f \".agents/releases/evidence-only-closures/${safe_target}.json\" ]]; then\n packet_path=\".agents/releases/evidence-only-closures/${safe_target}.json\"\n elif [[ -f \".agents/council/evidence-only-closures/${safe_target}.json\" ]]; then\n packet_path=\".agents/council/evidence-only-closures/${safe_target}.json\"\n else\n return 1\n fi\n\n jq -e 'has(\"evidence_mode\") and has(\"repo_state\")' \"$packet_path\" >/dev/null 2>&1\n}\n\npacket_matches_json() {\n local packet_path=\"$1\"\n\n jq -c --arg path \"$packet_path\" '[$path, (.evidence.artifacts[]?)] | unique' \"$packet_path\"\n}\n\nbuild_child_result() {\n local child=\"$1\"\n local scoped_json=\"$2\"\n local mode=\"$3\"\n local detail=\"$4\"\n local matches_json=\"$5\"\n local status=\"$6\"\n local closure_mode=\"${7:-}\"\n\n jq -n \\\n --arg child_id \"$child\" \\\n --arg status \"$status\" \\\n --arg evidence_mode \"$mode\" \\\n --arg detail \"$detail\" \\\n --arg closure_mode \"$closure_mode\" \\\n --argjson scoped_files \"$scoped_json\" \\\n --argjson matched_files \"$matches_json\" \\\n '{\n child_id: $child_id,\n status: $status,\n evidence_mode: $evidence_mode,\n detail: $detail,\n scoped_files: $scoped_files,\n matched_files: $matched_files\n }\n + (if ($closure_mode | length) > 0 then {closure_mode: $closure_mode} else {} end)'\n}\n\nclassify_task_queue_target() {\n local target_id=\"$1\"\n local packet_path=\"\"\n local packet_json=\"\"\n local pr_merge_json=\"\"\n\n target_is_closed_non_epic \"$target_id\" || return 1\n COLLECTION_DETAIL=\"${COLLECTION_DETAIL:-no child issues discovered from bd children/show output}; task-queue fallback requires PR merge evidence in git history or a valid evidence-only closure packet\"\n\n if packet_path=\"$(durable_packet_path_for_child \"$target_id\")\" && packet_is_valid_for_child \"$packet_path\" \"$target_id\"; then\n packet_json=\"$(packet_matches_json \"$packet_path\")\"\n build_child_result \"$target_id\" '[]' \"evidence-only-packet\" \"task_queue_closure: matched durable closure proof packet for no-child target\" \"$packet_json\" \"pass\" \"task-queue\"\n return 0\n fi\n\n if commit_ref_exists \"$target_id\"; then\n build_child_result \"$target_id\" '[]' \"commit\" \"task_queue_closure: matched target id in git history for no-child target\" '[]' \"pass\" \"task-queue\"\n return 0\n fi\n\n pr_merge_json=\"$(task_queue_pr_merge_matches_json \"$target_id\")\"\n if echo \"$pr_merge_json\" | jq -e 'length > 0' >/dev/null 2>&1; then\n build_child_result \"$target_id\" '[]' \"commit\" \"task_queue_closure: matched PR merge evidence in git history for no-child target\" \"$pr_merge_json\" \"pass\" \"task-queue\"\n return 0\n fi\n\n return 1\n}\n\nclassify_child() {\n local child=\"$1\"\n local child_json=\"\"\n local human_output=\"\"\n local created_at=\"\"\n local closed_at=\"\"\n local packet_path=\"\"\n local scoped_json commit_json staged_json worktree_json packet_json\n local -a scoped_files=()\n\n if child_json=\"$(bd_show_json \"$child\" 2>/dev/null)\"; then\n if ! child_is_closed \"$child_json\"; then\n return 0\n fi\n created_at=\"$(issue_timestamp \"$child_json\" \"created_at\")\"\n closed_at=\"$(issue_timestamp \"$child_json\" \"closed_at\")\"\n if [[ -z \"$closed_at\" ]]; then\n closed_at=\"$(issue_timestamp \"$child_json\" \"updated_at\")\"\n fi\n else\n human_output=\"$(bd show \"$child\" 2>/dev/null || true)\"\n if [[ \"$human_output\" != *\"CLOSED\"* ]]; then\n return 0\n fi\n fi\n\n # Evidence-only packet short-circuit: when a durable closure packet exists\n # for this bead AND it has the schema written by write-evidence-only-closure.sh\n # (must contain `evidence_mode` and `repo_state` keys), accept the bead as\n # fully closed and skip ALL other classification paths. Evidence-only packets\n # are the strongest proof surface — they bypass parser_miss, timing_miss, and\n # discovery_miss because the packet itself is the durable, replayable proof.\n if has_evidence_only_packet \"$child\"; then\n if packet_path=\"$(durable_packet_path_for_child \"$child\")\"; then\n packet_json=\"$(packet_matches_json \"$packet_path\" 2>/dev/null || printf '[]')\"\n else\n packet_json='[]'\n fi\n build_child_result \"$child\" '[]' \"evidence-only-packet\" \"evidence-only closure packet accepted (short-circuit)\" \"$packet_json\" \"pass\"\n return 0\n fi\n\n mapfile -t scoped_files \u003c \u003c(extract_scoped_files \"$child\")\n scoped_json=\"$(printf '%s\\n' \"${scoped_files[@]}\" | json_array_from_stream)\"\n\n case \"$SCOPE\" in\n auto|commit)\n if commit_ref_exists \"$child\"; then\n build_child_result \"$child\" \"$scoped_json\" \"commit\" \"matched child id in git history\" '[]' \"pass\"\n return 0\n fi\n commit_json=\"$(commit_matches_json \"$created_at\" \"$closed_at\" \"${scoped_files[@]}\")\"\n if echo \"$commit_json\" | jq -e 'length > 0' >/dev/null 2>&1; then\n build_child_result \"$child\" \"$scoped_json\" \"commit\" \"matched scoped files in git history during issue lifetime\" \"$commit_json\" \"pass\"\n return 0\n fi\n # Grace window: check for close-before-commit pattern\n if [[ -n \"$closed_at\" ]]; then\n local grace_until=\"\"\n grace_until=\"$(add_grace_to_timestamp \"$closed_at\" \"$GRACE_SECONDS\" 2>/dev/null)\" || true\n if [[ -n \"$grace_until\" ]]; then\n commit_json=\"$(commit_matches_json \"$created_at\" \"$grace_until\" \"${scoped_files[@]}\")\"\n if echo \"$commit_json\" | jq -e 'length > 0' >/dev/null 2>&1; then\n build_child_result \"$child\" \"$scoped_json\" \"grace-window\" \"matched scoped files in git history within grace window after close (close-before-commit)\" \"$commit_json\" \"pass\"\n return 0\n fi\n fi\n fi\n if [[ \"$SCOPE\" == \"commit\" ]]; then\n # Check evidence-only closure packets before declaring any miss.\n # Maintenance epics that close via proof packets instead of code commits\n # are valid regardless of whether scoped files were found.\n if packet_path=\"$(durable_packet_path_for_child \"$child\")\" && packet_is_valid_for_child \"$packet_path\" \"$child\"; then\n packet_json=\"$(packet_matches_json \"$packet_path\")\"\n if [[ \"${#scoped_files[@]}\" -eq 0 ]]; then\n build_child_result \"$child\" \"$scoped_json\" \"evidence-only-packet\" \"matched durable closure proof packet (no scoped files)\" \"$packet_json\" \"pass\"\n else\n build_child_result \"$child\" \"$scoped_json\" \"evidence-only-packet\" \"matched durable closure proof packet (no commit evidence for scoped files)\" \"$packet_json\" \"pass\"\n fi\n elif [[ \"${#scoped_files[@]}\" -eq 0 ]]; then\n if issue_text_has_file_patterns \"$child\"; then\n build_child_result \"$child\" \"$scoped_json\" \"none\" \"parser_miss: description mentions file-like paths but extraction found 0 scoped files — manual review recommended\" '[]' \"warn\"\n else\n build_child_result \"$child\" \"$scoped_json\" \"none\" \"parser_miss: no scoped files extracted from description\" '[]' \"fail\"\n fi\n else\n if all_scoped_files_are_discovery \"${scoped_files[@]}\" && child_has_nondiscovery_proof_surface \"$child\"; then\n build_child_result \"$child\" \"$scoped_json\" \"discovery-seed-missing\" \"discovery_miss: closed bead cites discovery-phase artifact(s) (.agents/brainstorm/.agents/research/.agents/discovery/) that were never persisted, but other proof surface exists\" '[]' \"warn\"\n return 0\n fi\n build_child_result \"$child\" \"$scoped_json\" \"none\" \"timing_miss: scoped files found but no commit evidence (checked grace window)\" '[]' \"fail\"\n fi\n return 0\n fi\n ;;\n esac\n\n if [[ \"${#scoped_files[@]}\" -eq 0 ]]; then\n # Check evidence-only closure packets before declaring parser_miss\n if packet_path=\"$(durable_packet_path_for_child \"$child\")\" && packet_is_valid_for_child \"$packet_path\" \"$child\"; then\n packet_json=\"$(packet_matches_json \"$packet_path\")\"\n build_child_result \"$child\" \"$scoped_json\" \"evidence-only-packet\" \"matched durable closure proof packet (no scoped files)\" \"$packet_json\" \"pass\"\n else\n if issue_text_has_file_patterns \"$child\"; then\n build_child_result \"$child\" \"$scoped_json\" \"none\" \"parser_miss: description mentions file-like paths but extraction found 0 scoped files — manual review recommended\" '[]' \"warn\"\n else\n build_child_result \"$child\" \"$scoped_json\" \"none\" \"parser_miss: no scoped files extracted from description\" '[]' \"fail\"\n fi\n fi\n return 0\n fi\n\n case \"$SCOPE\" in\n auto|staged)\n staged_json=\"$(staged_matches_json \"${scoped_files[@]}\")\"\n if echo \"$staged_json\" | jq -e 'length > 0' >/dev/null 2>&1; then\n build_child_result \"$child\" \"$scoped_json\" \"staged\" \"matched scoped files in git index\" \"$staged_json\" \"pass\"\n return 0\n fi\n if [[ \"$SCOPE\" == \"staged\" ]]; then\n build_child_result \"$child\" \"$scoped_json\" \"none\" \"timing_miss: scoped files found but no staged evidence\" '[]' \"fail\"\n return 0\n fi\n ;;\n esac\n\n case \"$SCOPE\" in\n auto|worktree)\n worktree_json=\"$(worktree_matches_json \"${scoped_files[@]}\")\"\n if echo \"$worktree_json\" | jq -e 'length > 0' >/dev/null 2>&1; then\n build_child_result \"$child\" \"$scoped_json\" \"worktree\" \"matched scoped files in working tree\" \"$worktree_json\" \"pass\"\n return 0\n fi\n ;;\n esac\n\n if packet_path=\"$(durable_packet_path_for_child \"$child\")\" && packet_is_valid_for_child \"$packet_path\" \"$child\"; then\n packet_json=\"$(packet_matches_json \"$packet_path\")\"\n build_child_result \"$child\" \"$scoped_json\" \"evidence-only-packet\" \"matched durable closure proof packet\" \"$packet_json\" \"pass\"\n return 0\n fi\n\n # Discovery-phase seed artifacts (.agents/brainstorm/, .agents/research/,\n # .agents/discovery/) are ephemeral and commonly not persisted. If EVERY\n # scoped file is such a seed AND the bead has any other proof surface\n # (commit referencing the bead id, evidence-only packet, plan/finding\n # file, or non-discovery file mentioned in bead text with real history),\n # downgrade to WARN (discovery_miss) instead of hard-failing. Non-discovery\n # scoped misses still hard-fail.\n if all_scoped_files_are_discovery \"${scoped_files[@]}\" && child_has_nondiscovery_proof_surface \"$child\"; then\n build_child_result \"$child\" \"$scoped_json\" \"discovery-seed-missing\" \"discovery_miss: closed bead cites discovery-phase artifact(s) (.agents/brainstorm/.agents/research/.agents/discovery/) that were never persisted, but other proof surface exists (commit-ref/packet/plan/finding)\" '[]' \"warn\"\n return 0\n fi\n\n build_child_result \"$child\" \"$scoped_json\" \"none\" \"timing_miss: scoped files found but no evidence in any scope (commit/grace/staged/worktree/packet)\" '[]' \"fail\"\n}\n\nemit_results_summary() {\n local results_file=\"$1\"\n\n jq -s \\\n --arg epic_id \"$EPIC_ID\" \\\n --arg scope \"$SCOPE\" \\\n '{\n epic_id: $epic_id,\n scope: $scope,\n summary: {\n checked_children: length,\n passed: ([.[] | select(.status == \"pass\")] | length),\n warned: ([.[] | select(.status == \"warn\")] | length),\n failed: ([.[] | select(.status == \"fail\")] | length),\n evidence_modes: {\n commit: ([.[] | select(.status == \"pass\" and .evidence_mode == \"commit\") | .child_id] | sort),\n staged: ([.[] | select(.status == \"pass\" and .evidence_mode == \"staged\") | .child_id] | sort),\n worktree: ([.[] | select(.status == \"pass\" and .evidence_mode == \"worktree\") | .child_id] | sort),\n \"evidence-only-packet\": ([.[] | select(.status == \"pass\" and .evidence_mode == \"evidence-only-packet\") | .child_id] | sort),\n \"grace-window\": ([.[] | select(.status == \"pass\" and .evidence_mode == \"grace-window\") | .child_id] | sort),\n \"discovery-seed-missing\": ([.[] | select(.status == \"warn\" and .evidence_mode == \"discovery-seed-missing\") | .child_id] | sort)\n },\n closure_modes: {\n \"task-queue\": ([.[] | select(.closure_mode == \"task-queue\") | .child_id] | sort)\n }\n },\n children: .,\n warnings: [.[] | select(.status == \"warn\") | {child_id, detail, warning_type: (if (.detail | startswith(\"parser_miss\")) then \"parser_miss\" elif (.detail | startswith(\"discovery_miss\")) then \"discovery_miss\" else \"unknown\" end)}],\n failures: [.[] | select(.status == \"fail\") | {child_id, detail, failure_type: (if (.detail | startswith(\"parser_miss\")) then \"parser_miss\" elif (.detail | startswith(\"timing_miss\")) then \"timing_miss\" elif (.detail | startswith(\"discovery_miss\")) then \"discovery_miss\" else \"unknown\" end)}]\n }' \"$results_file\"\n}\n\ntmp_results=\"$(mktemp)\"\nchildren_file=\"$(mktemp)\"\ntrap 'rm -f \"$tmp_results\" \"$children_file\"' EXIT\n\nif ! collect_children >\"$children_file\"; then\n if target_is_closed_non_epic \"$EPIC_ID\"; then\n COLLECTION_DETAIL=\"${COLLECTION_DETAIL:-no child issues discovered from bd children/show output}; task-queue fallback requires PR merge evidence in git history or a valid evidence-only closure packet\"\n if task_result=\"$(classify_task_queue_target \"$EPIC_ID\")\" && [[ -n \"$task_result\" ]]; then\n printf '%s\\n' \"$task_result\" > \"$tmp_results\"\n emit_results_summary \"$tmp_results\"\n exit 0\n fi\n fi\n\n jq -n \\\n --arg epic_id \"$EPIC_ID\" \\\n --arg scope \"$SCOPE\" \\\n --arg detail \"${COLLECTION_DETAIL:-failed to collect child issues}\" \\\n '{\n epic_id: $epic_id,\n scope: $scope,\n summary: {\n checked_children: 0,\n passed: 0,\n failed: 1,\n collection_failed: true\n },\n children: [],\n failures: [\n {\n child_id: null,\n detail: $detail\n }\n ]\n }'\n exit 1\nfi\n\nchildren_output=\"$(cat \"$children_file\")\"\nwhile IFS= read -r child; do\n [[ -n \"$child\" ]] || continue\n child_result=\"$(classify_child \"$child\")\"\n [[ -n \"$child_result\" ]] || continue\n printf '%s\\n' \"$child_result\" >> \"$tmp_results\"\ndone \u003c\u003c\u003c \"$children_output\"\n\nemit_results_summary \"$tmp_results\"\n","content_type":"application/x-sh; charset=utf-8","language":"bash","size":36415,"content_sha256":"d300caec537e5013dce9e086a137530382af2be07adb2b2b2542e4050a9d38dc"},{"filename":"scripts/preflight-refs.sh","content":"#!/usr/bin/env bash\n# preflight-refs.sh — Check required reference docs exist for post-mortem skill.\n#\n# Exit codes:\n# 0 = all refs present (silent)\n# 1 = missing refs + --strict flag (BLOCK)\n# 2 = missing refs without --strict (WARN, non-blocking)\n#\n# Flags:\n# --strict Promote WARN to BLOCK (exit 1) on missing refs\n# --skip-checkpoint-policy Skip checkpoint-policy.md check\n# --json Output structured JSON instead of plain text\nset -euo pipefail\n\nSKILL_DIR=\"$(cd \"$(dirname \"$0\")/..\" && pwd)\"\nREPO_ROOT=\"$(cd \"$SKILL_DIR/../..\" && pwd)\"\n\nREQUIRED_REFS=(\n \"$SKILL_DIR/references/checkpoint-policy.md\"\n \"$SKILL_DIR/references/metadata-verification.md\"\n \"$SKILL_DIR/references/closure-integrity-audit.md\"\n)\n\n# --- Parse flags ---\nstrict=false\nskip_checkpoint_policy=false\njson_output=false\n\nfor arg in \"$@\"; do\n case \"$arg\" in\n --strict) strict=true ;;\n --skip-checkpoint-policy) skip_checkpoint_policy=true ;;\n --json) json_output=true ;;\n *) echo \"WARN: unknown flag: $arg\" >&2 ;;\n esac\ndone\n\n# --- Check refs ---\nmissing=0\nskipped=0\nmissing_list=()\nskipped_list=()\n\nfor ref in \"${REQUIRED_REFS[@]}\"; do\n display_ref=\"${ref#\"$REPO_ROOT\"/}\"\n # Apply skip guard clause\n if [[ \"$skip_checkpoint_policy\" == true ]] && [[ \"$ref\" == *\"checkpoint-policy.md\" ]]; then\n skipped=$((skipped + 1))\n skipped_list+=(\"$display_ref\")\n continue\n fi\n\n if [ ! -f \"$ref\" ] || ! cat \"$ref\" >/dev/null 2>&1; then\n missing=$((missing + 1))\n missing_list+=(\"$display_ref\")\n if [ \"$json_output\" = false ]; then\n echo \"WARN: missing required reference: $display_ref\"\n fi\n fi\ndone\n\n# --- Output ---\nif [ \"$json_output\" = true ]; then\n # Build JSON arrays\n missing_json=\"[\"\n first=true\n for r in \"${missing_list[@]+\"${missing_list[@]}\"}\"; do\n [ \"$first\" = true ] && first=false || missing_json+=\",\"\n missing_json+=\"\\\"$r\\\"\"\n done\n missing_json+=\"]\"\n\n skipped_json=\"[\"\n first=true\n for r in \"${skipped_list[@]+\"${skipped_list[@]}\"}\"; do\n [ \"$first\" = true ] && first=false || skipped_json+=\",\"\n skipped_json+=\"\\\"$r\\\"\"\n done\n skipped_json+=\"]\"\n\n echo \"{\\\"missing\\\":${missing},\\\"skipped\\\":${skipped},\\\"missing_refs\\\":${missing_json},\\\"skipped_refs\\\":${skipped_json}}\"\nfi\n\n# --- Exit ---\nif [ \"$missing\" -gt 0 ]; then\n if [ \"$json_output\" = false ]; then\n echo \"WARN: post-mortem reference preflight incomplete (${missing} missing).\"\n fi\n if [ \"$strict\" = true ]; then\n exit 1\n else\n exit 2\n fi\nfi\n\nexit 0\n","content_type":"application/x-sh; charset=utf-8","language":"bash","size":2547,"content_sha256":"b2c8aadfabd3a3d46c9482cf22146c5124d6285d436c8c626c0c15a7ef0e647b"},{"filename":"scripts/validate.sh","content":"#!/usr/bin/env bash\nset -euo pipefail\nSKILL_DIR=\"$(cd \"$(dirname \"$0\")/..\" && pwd)\"\nPASS=0; FAIL=0\n\ncheck() { if bash -c \"$2\"; then echo \"PASS: $1\"; PASS=$((PASS + 1)); else echo \"FAIL: $1\"; FAIL=$((FAIL + 1)); fi; }\n\ncheck \"SKILL.md exists\" \"[ -f '$SKILL_DIR/SKILL.md' ]\"\ncheck \"SKILL.md has YAML frontmatter\" \"head -1 '$SKILL_DIR/SKILL.md' | grep -q '^---

Post-Mortem Skill Purpose: Wrap up completed work — validate it shipped correctly, extract learnings, process the knowledge backlog, activate high-value insights, and retire stale knowledge. Runtime note: Hook-driven closeout is runtime-dependent. Claude/OpenCode can wire Phase 2-5 maintenance through lifecycle hooks. Codex CLI v0.115.0+ supports native hooks (same behavior). For older Codex versions without hook surfaces, finish closeout with . Loop position Move 7 (capture evidence + learning, then ratchet) of the operating loop. Two outputs per loop turn: evidence (test names, snapshot key…

\"\ncheck \"SKILL.md has name: post-mortem\" \"grep -q '^name: post-mortem' '$SKILL_DIR/SKILL.md'\"\ncheck \"references/ has at least 2 files\" \"[ \\$(ls '$SKILL_DIR/references/' | wc -l) -ge 2 ]\"\ncheck \"SKILL.md mentions harvest\" \"grep -qi 'harvest' '$SKILL_DIR/SKILL.md'\"\ncheck \"skill has Step 2.6 (deep audit sweep)\" \"grep -rqs 'Step 2.6' '$SKILL_DIR/SKILL.md' '$SKILL_DIR/references/'\"\ncheck \"SKILL.md references --skip-sweep\" \"grep -q '\\-\\-skip-sweep' '$SKILL_DIR/SKILL.md'\"\ncheck \"skill mentions compiled prevention inputs\" \"grep -rqs '\\.agents/pre-mortem-checks/\\|\\.agents/planning-rules/' '$SKILL_DIR/SKILL.md' '$SKILL_DIR/references/'\"\ncheck \"skill mentions finding registry\" \"grep -rqs 'registry.jsonl' '$SKILL_DIR/SKILL.md' '$SKILL_DIR/references/'\"\ncheck \"skill mentions dedup_key\" \"grep -rqs 'dedup_key' '$SKILL_DIR/SKILL.md' '$SKILL_DIR/references/'\"\ncheck \"skill refreshes finding compiler after writes\" \"grep -rqs 'finding-compiler.sh' '$SKILL_DIR/SKILL.md' '$SKILL_DIR/references/'\"\ncheck \"closure-integrity audit script exists\" \"[ -f '$SKILL_DIR/scripts/closure-integrity-audit.sh' ]\"\ncheck \"reference preflight passes in strict mode\" \"bash '$SKILL_DIR/scripts/preflight-refs.sh' --strict >/dev/null\"\ncheck \"evidence-only closure writer exists\" \"[ -f '$SKILL_DIR/scripts/write-evidence-only-closure.sh' ]\"\ncheck \"evidence-only closure schema exists\" \"[ -f '$SKILL_DIR/../../schemas/evidence-only-closure.v1.schema.json' ]\"\ncheck \"skill documents durable closure proof path\" \"grep -rqs '\\.agents/releases/evidence-only-closures/' '$SKILL_DIR/SKILL.md' '$SKILL_DIR/references/'\"\ncheck \"closure-integrity reference documents durable closure proof path\" \"grep -q '\\.agents/releases/evidence-only-closures/' '$SKILL_DIR/references/closure-integrity-audit.md'\"\ncheck \"evidence-only closure schema requires supporting artifacts\" \"grep -q '\\\"minItems\\\": 1' '$SKILL_DIR/../../schemas/evidence-only-closure.v1.schema.json'\"\ncheck \"harvest-next-work documents claim lifecycle\" \"grep -q 'Queue Lifecycle' '$SKILL_DIR/references/harvest-next-work.md' && grep -q 'Never mark an item consumed at pick-time' '$SKILL_DIR/references/harvest-next-work.md'\"\ncheck \"harvest-next-work documents evolve-generated sources\" \"grep -q 'feature-suggestion' '$SKILL_DIR/references/harvest-next-work.md'\"\ncheck \"SKILL.md points to claim/finalize lifecycle for next-work\" \"grep -q 'claim/finalize lifecycle' '$SKILL_DIR/SKILL.md'\"\n\necho \"\"; echo \"Results: $PASS passed, $FAIL failed\"\n[ $FAIL -eq 0 ] && exit 0 || exit 1\n","content_type":"application/x-sh; charset=utf-8","language":"bash","size":2856,"content_sha256":"925cc613c7f9b13cec8689f4096d3c4bdb5adc1ecb98b0fc0ba67aae9dc2bca2"},{"filename":"scripts/write-evidence-only-closure.sh","content":"#!/usr/bin/env bash\nset -euo pipefail\n\nSCRIPT_DIR=\"$(cd \"$(dirname \"$0\")\" && pwd)\"\nTARGET_ROOT=\"${REPO_ROOT:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}\"\n\n# WORKSPACE_ROOT must contain both schemas/evidence-only-closure.v1.schema.json\n# and lib/hook-helpers.sh. The canonical layout is the agentops repo root, but\n# the script also ships under skills-codex/ and is cached under\n# ~/.codex/plugins/cache/agentops-marketplace/agentops/local/skills-codex/...\n# where the simple \"../../..\" walk lands inside the cache instead of the\n# packaged repo. Probe several candidates so the script is layout-independent.\n_workspace_has_assets() {\n [[ -f \"$1/schemas/evidence-only-closure.v1.schema.json\" && -f \"$1/lib/hook-helpers.sh\" ]]\n}\nWORKSPACE_ROOT=\"\"\nfor candidate in \\\n \"$(cd \"$SCRIPT_DIR/../../..\" && pwd)\" \\\n \"$(cd \"$SCRIPT_DIR/../..\" && pwd)\" \\\n \"$TARGET_ROOT\"; do\n if _workspace_has_assets \"$candidate\"; then\n WORKSPACE_ROOT=\"$candidate\"\n break\n fi\ndone\nif [[ -z \"$WORKSPACE_ROOT\" ]]; then\n echo \"post-mortem: cannot locate workspace assets (schemas/evidence-only-closure.v1.schema.json, lib/hook-helpers.sh)\" >&2\n echo \" searched: $SCRIPT_DIR/../../.., $SCRIPT_DIR/../.., $TARGET_ROOT\" >&2\n exit 1\nfi\n\nTARGET_ID=\"fixture-evidence-only-closure\"\nTARGET_TYPE=\"task\"\nPRODUCER=\"post-mortem\"\nEVIDENCE_MODE=\"auto\"\nEVIDENCE_SUMMARY=\"Evidence-only closure artifact emitted for follow-up validation.\"\ndeclare -a VALIDATION_COMMANDS=()\ndeclare -a ARTIFACTS=()\ndeclare -a NOTES=()\n\nusage() {\n cat \u003c\u003c'EOF'\nUsage: bash skills/post-mortem/scripts/write-evidence-only-closure.sh [options]\n\nOptions:\n --repo-root \u003cpath> Target repo root. Defaults to $REPO_ROOT or current git root.\n --target-id \u003cid> Target issue/epic/policy identifier. Default: fixture-evidence-only-closure.\n --target-type \u003ctype> Target type (for example: issue, epic, policy). Default: task.\n --producer \u003cname> Producer name recorded in the artifact. Default: post-mortem.\n --evidence-mode \u003cmode> Evidence mode to record: auto, commit, staged, or worktree. Default: auto.\n --validation-command \u003ccmd> Validation command to record. Repeatable. Defaults to manifest validation for the target root.\n --evidence-summary \u003ctext> Human summary for the closure evidence.\n --artifact \u003cpath> Repo-relative or absolute evidence artifact path. Repeatable.\n --note \u003ctext> Additional note to include in the evidence block. Repeatable.\n --help Show this help.\nEOF\n}\n\nwhile [[ $# -gt 0 ]]; do\n case \"$1\" in\n --repo-root)\n TARGET_ROOT=\"${2:-}\"\n shift 2\n ;;\n --target-id)\n TARGET_ID=\"${2:-}\"\n shift 2\n ;;\n --target-type)\n TARGET_TYPE=\"${2:-}\"\n shift 2\n ;;\n --producer)\n PRODUCER=\"${2:-}\"\n shift 2\n ;;\n --evidence-mode)\n EVIDENCE_MODE=\"${2:-}\"\n shift 2\n ;;\n --validation-command)\n VALIDATION_COMMANDS+=(\"${2:-}\")\n shift 2\n ;;\n --evidence-summary)\n EVIDENCE_SUMMARY=\"${2:-}\"\n shift 2\n ;;\n --artifact)\n ARTIFACTS+=(\"${2:-}\")\n shift 2\n ;;\n --note)\n NOTES+=(\"${2:-}\")\n shift 2\n ;;\n --help|-h)\n usage\n exit 0\n ;;\n *)\n echo \"Unknown arg: $1\" >&2\n usage >&2\n exit 2\n ;;\n esac\ndone\n\nif [[ \"$TARGET_ROOT\" != /* ]]; then\n TARGET_ROOT=\"$(cd \"$TARGET_ROOT\" && pwd)\"\nfi\n\n[[ -n \"$TARGET_ID\" ]] || { echo \"--target-id is required\" >&2; exit 1; }\n[[ -n \"$TARGET_TYPE\" ]] || { echo \"--target-type is required\" >&2; exit 1; }\n[[ -n \"$EVIDENCE_SUMMARY\" ]] || { echo \"--evidence-summary is required\" >&2; exit 1; }\ncase \"$EVIDENCE_MODE\" in\n auto|commit|staged|worktree) ;;\n *)\n echo \"--evidence-mode must be one of: auto, commit, staged, worktree\" >&2\n exit 1\n ;;\nesac\n\nmkdir -p \"$TARGET_ROOT/schemas\"\nschema_source=\"$WORKSPACE_ROOT/schemas/evidence-only-closure.v1.schema.json\"\nschema_target=\"$TARGET_ROOT/schemas/evidence-only-closure.v1.schema.json\"\nif [[ \"$(cd \"$(dirname \"$schema_source\")\" && pwd)/$(basename \"$schema_source\")\" != \"$(cd \"$(dirname \"$schema_target\")\" && pwd)/$(basename \"$schema_target\")\" ]]; then\n cp \"$schema_source\" \"$schema_target\"\nfi\n\nexport ROOT=\"$TARGET_ROOT\"\nsource \"$WORKSPACE_ROOT/lib/hook-helpers.sh\"\n\nrun_git_target() {\n env -u GIT_DIR -u GIT_WORK_TREE -u GIT_COMMON_DIR git -C \"$TARGET_ROOT\" \"$@\"\n}\n\nto_target_relative_path() {\n local path=\"$1\"\n local repo_root=\"${TARGET_ROOT%/}\"\n\n case \"$path\" in\n \"$repo_root\"/*)\n printf '.%s\\n' \"${path#\"$repo_root\"}\"\n ;;\n *)\n printf '%s\\n' \"$path\"\n ;;\n esac\n}\n\nif [[ \"${#VALIDATION_COMMANDS[@]}\" -eq 0 ]]; then\n printf -v default_validation_command 'bash scripts/validate-manifests.sh --repo-root %q' \"$TARGET_ROOT\"\n VALIDATION_COMMANDS=(\"$default_validation_command\")\nfi\n\njson_array_from_values() {\n if [[ \"$#\" -eq 0 ]]; then\n printf '[]\\n'\n return 0\n fi\n\n printf '%s\\n' \"$@\" \\\n | sed '/^[[:space:]]*$/d' \\\n | sort -u \\\n | jq -R . \\\n | jq -s .\n}\n\nvalidation_commands_json=\"$(json_array_from_values \"${VALIDATION_COMMANDS[@]}\")\"\nnotes_json=\"$(json_array_from_values \"${NOTES[@]}\")\"\n\ngit_branch=\"$(run_git_target branch --show-current 2>/dev/null || true)\"\nhead_sha=\"$(run_git_target rev-parse HEAD 2>/dev/null || true)\"\nmapfile -t staged_files \u003c \u003c(run_git_target diff --cached --name-only --diff-filter=ACMR 2>/dev/null || true)\nmapfile -t unstaged_files \u003c \u003c(run_git_target diff --name-only --diff-filter=ACMR 2>/dev/null || true)\nmapfile -t untracked_files \u003c \u003c(run_git_target ls-files --others --exclude-standard 2>/dev/null || true)\nmapfile -t modified_files \u003c \u003c(\n printf '%s\\n' \"${staged_files[@]}\" \"${unstaged_files[@]}\" \"${untracked_files[@]}\" \\\n | sed '/^[[:space:]]*$/d' \\\n | sort -u\n)\n\nstaged_files_json=\"$(json_array_from_values \"${staged_files[@]}\")\"\nunstaged_files_json=\"$(json_array_from_values \"${unstaged_files[@]}\")\"\nuntracked_files_json=\"$(json_array_from_values \"${untracked_files[@]}\")\"\nmodified_files_json=\"$(json_array_from_values \"${modified_files[@]}\")\"\n\nif [[ \"${#modified_files[@]}\" -eq 0 ]]; then\n git_dirty='false'\nelse\n git_dirty='true'\nfi\n\nresolve_evidence_mode() {\n case \"$EVIDENCE_MODE\" in\n commit|staged|worktree)\n printf '%s\\n' \"$EVIDENCE_MODE\"\n ;;\n auto)\n if [[ \"${#staged_files[@]}\" -gt 0 ]]; then\n printf 'staged\\n'\n elif [[ \"${#unstaged_files[@]}\" -gt 0 || \"${#untracked_files[@]}\" -gt 0 ]]; then\n printf 'worktree\\n'\n else\n printf 'commit\\n'\n fi\n ;;\n esac\n}\n\nresolved_evidence_mode=\"$(resolve_evidence_mode)\"\n\nsafe_target=\"${TARGET_ID//\\//_}\"\nDURABLE_PACKET_PATH=\".agents/releases/evidence-only-closures/${safe_target}.json\"\nartifacts_with_durable=(\"${ARTIFACTS[@]}\" \"$DURABLE_PACKET_PATH\")\nmapfile -t normalized_artifacts \u003c \u003c(\n for artifact in \"${artifacts_with_durable[@]}\"; do\n [[ -n \"$artifact\" ]] || continue\n to_target_relative_path \"$artifact\"\n done | sed '/^[[:space:]]*$/d' | sort -u\n)\nartifacts_json=\"$(json_array_from_values \"${normalized_artifacts[@]}\")\"\n\nrepo_state_json=\"$(\n jq -n \\\n --arg repo_root \".\" \\\n --arg git_branch \"$git_branch\" \\\n --arg head_sha \"$head_sha\" \\\n --argjson git_dirty \"$git_dirty\" \\\n --argjson modified_files \"$modified_files_json\" \\\n --argjson staged_files \"$staged_files_json\" \\\n --argjson unstaged_files \"$unstaged_files_json\" \\\n --argjson untracked_files \"$untracked_files_json\" \\\n '{\n repo_root: $repo_root,\n git_branch: $git_branch,\n git_dirty: $git_dirty,\n head_sha: $head_sha,\n modified_files: $modified_files,\n staged_files: $staged_files,\n unstaged_files: $unstaged_files,\n untracked_files: $untracked_files\n }'\n)\"\n\nevidence_json=\"$(\n jq -n \\\n --arg summary \"$EVIDENCE_SUMMARY\" \\\n --argjson artifacts \"$artifacts_json\" \\\n --argjson notes \"$notes_json\" \\\n '{\n summary: $summary,\n artifacts: $artifacts,\n notes: $notes\n }'\n)\"\n\nwrite_evidence_only_closure_packet \\\n \"$TARGET_ID\" \\\n \"$TARGET_TYPE\" \\\n \"$PRODUCER\" \\\n \"$resolved_evidence_mode\" \\\n \"$validation_commands_json\" \\\n \"$repo_state_json\" \\\n \"$evidence_json\"\n","content_type":"application/x-sh; charset=utf-8","language":"bash","size":8188,"content_sha256":"5c7ae9f53fabdda6939e34b79b8561a37ef90eb08a9be0e6de2d0364d5dae1bd"}],"content_json":{"type":"doc","content":[{"type":"heading","attrs":{"level":1},"content":[{"text":"Post-Mortem Skill","type":"text"}]},{"type":"blockquote","content":[{"type":"paragraph","content":[{"text":"Purpose:","type":"text","marks":[{"type":"strong"}]},{"text":" Wrap up completed work — validate it shipped correctly, extract learnings, process the knowledge backlog, activate high-value insights, and retire stale knowledge.","type":"text"}]},{"type":"paragraph","content":[{"text":"Runtime note:","type":"text","marks":[{"type":"strong"}]},{"text":" Hook-driven closeout is runtime-dependent. Claude/OpenCode can wire Phase 2-5 maintenance through lifecycle hooks. Codex CLI v0.115.0+ supports native hooks (same behavior). For older Codex versions without hook surfaces, finish closeout with ","type":"text"},{"text":"ao codex stop","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Loop position","type":"text"}]},{"type":"paragraph","content":[{"text":"Move ","type":"text"},{"text":"7 (capture evidence + learning, then ratchet)","type":"text","marks":[{"type":"strong"}]},{"text":" of the ","type":"text"},{"text":"operating loop","type":"text","marks":[{"type":"link","attrs":{"href":"../../docs/architecture/operating-loop.md","title":null}}]},{"text":". Two outputs per loop turn: evidence (test names, snapshot keys, council verdicts, citation events) recorded against the bead and ","type":"text"},{"text":".agents/ratchet/","type":"text","marks":[{"type":"code_inline"}]},{"text":"; learnings promoted only under the ","type":"text"},{"text":"ratchet rules","type":"text","marks":[{"type":"link","attrs":{"href":"../../docs/architecture/operating-loop.md#the-promotion-ratchet","title":null}}]},{"text":" — noticed once stays in the handoff, repeats twice goes to ","type":"text"},{"text":".agents/learnings/","type":"text","marks":[{"type":"code_inline"}]},{"text":", changes future behavior updates a SKILL.md or template, must-never-regress becomes a gate, core doctrine promotes into PRODUCT.md/GOALS.md/docs/cdlc.md. Most observations die at handoff. That is correct.","type":"text"}]},{"type":"paragraph","content":[{"text":"Six phases:","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Council","type":"text","marks":[{"type":"strong"}]},{"text":" — Did we implement it correctly?","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Extract","type":"text","marks":[{"type":"strong"}]},{"text":" — What did we learn?","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Process Backlog","type":"text","marks":[{"type":"strong"}]},{"text":" — Score, deduplicate, and flag stale learnings","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Activate","type":"text","marks":[{"type":"strong"}]},{"text":" — Promote high-value learnings to MEMORY.md and constraints","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Retire","type":"text","marks":[{"type":"strong"}]},{"text":" — Archive stale and superseded learnings","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Harvest","type":"text","marks":[{"type":"strong"}]},{"text":" — Surface next work for the flywheel","type":"text"}]}]}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":2},"content":[{"text":"Quick Start","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"/post-mortem # wraps up recent work\n/post-mortem epic-123 # wraps up specific epic\n/post-mortem --quick \"insight\" # quick-capture single learning (no council)\n/post-mortem --process-only # skip council+extraction, run Phase 3-5 on backlog\n/post-mortem --skip-activate # extract + process but don't write MEMORY.md\n/post-mortem --deep recent # thorough council review\n/post-mortem --mixed epic-123 # cross-vendor (Claude + Codex)\n/post-mortem --skip-checkpoint-policy epic-123 # skip ratchet chain validation","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Codex Closeout","type":"text"}]},{"type":"paragraph","content":[{"text":"Codex CLI v0.115.0+ has native hooks and handles closeout automatically (no extra steps needed). For older Codex versions (hookless fallback), run these after the post-mortem workflow writes learnings and next work:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"ao codex stop\nao codex status","type":"text"}]},{"type":"paragraph","content":[{"text":"ao codex stop","type":"text","marks":[{"type":"code_inline"}]},{"text":" uses the latest transcript or history fallback to queue/persist learnings and run close-loop maintenance without runtime hooks.","type":"text"}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":2},"content":[{"text":"Flags","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Flag","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Default","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Description","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"--quick \"text\"","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"off","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Quick-capture a single learning directly to ","type":"text"},{"text":".agents/learnings/","type":"text","marks":[{"type":"code_inline"}]},{"text":" without running a full post-mortem. Formerly handled by ","type":"text"},{"text":"/retro --quick","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"--process-only","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"off","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Skip council and extraction (Phase 1-2). Run Phase 3-5 on the existing backlog only.","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"--skip-activate","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"off","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Extract and process learnings but do not write to MEMORY.md (skip Phase 4 promotions).","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"--deep","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"off","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"3 judges (default for post-mortem)","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"--mixed","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"off","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Cross-vendor (Claude + Codex) judges","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"--explorers=N","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"off","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Each judge spawns N explorers before judging","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"--debate","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"off","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Two-round adversarial review","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"--skip-checkpoint-policy","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"off","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Skip ratchet chain validation","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"--skip-sweep","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"off","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Skip pre-council deep audit sweep","type":"text"}]}]}]}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":2},"content":[{"text":"Quick Mode","type":"text"}]},{"type":"paragraph","content":[{"text":"Read ","type":"text"},{"text":"references/quick-mode.md","type":"text","marks":[{"type":"link","attrs":{"href":"references/quick-mode.md","title":null}}]},{"text":" when you need the ","type":"text"},{"text":"--quick","type":"text","marks":[{"type":"code_inline"}]},{"text":" flag procedure (slug generation, direct learning write, confirmation).","type":"text"}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":2},"content":[{"text":"Execution Steps","type":"text"}]},{"type":"paragraph","content":[{"text":"Read ","type":"text"},{"text":"references/execution-steps.md","type":"text","marks":[{"type":"link","attrs":{"href":"references/execution-steps.md","title":null}}]},{"text":" when you need the full Phase 1 procedure: pre-flight checks, reference loading (Step 0.4), checkpoint-policy preflight (0.5), plan/spec loading (Steps 1-2.3), closure integrity audit (2.4), metadata verification (2.5), deep audit sweep (2.6), council invocation (Step 3), and prediction accuracy (3.5).","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Step 2.1: Load Compiled Prevention Context","type":"text"}]},{"type":"paragraph","content":[{"text":"Before council and retro synthesis, load compiled prevention outputs when they exist:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":".agents/planning-rules/*.md","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":".agents/pre-mortem-checks/*.md","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"paragraph","content":[{"text":"Use these compiled artifacts first, then fall back to ","type":"text"},{"text":".agents/findings/registry.jsonl","type":"text","marks":[{"type":"code_inline"}]},{"text":" only when compiled outputs are missing or incomplete. Carry matched finding IDs into the retro as ","type":"text"},{"text":"Applied findings","type":"text","marks":[{"type":"code_inline"}]},{"text":" / ","type":"text"},{"text":"Known risks applied","type":"text","marks":[{"type":"code_inline"}]},{"text":" context so post-mortem can judge whether the flywheel actually prevented rediscovery.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Phase 2: Extract Learnings","type":"text"}]},{"type":"paragraph","content":[{"text":"Read ","type":"text"},{"text":"references/phase-2-extract.md","type":"text","marks":[{"type":"link","attrs":{"href":"references/phase-2-extract.md","title":null}}]},{"text":" when you need the inline learning extraction procedure: gather context (EX.1), classify (EX.2), write learnings (EX.3), test pyramid gap analysis (EX.3.5), scope classification (EX.4), findings registry (EX.5-6).","type":"text"}]},{"type":"paragraph","content":[{"text":"Before backlog processing, normalize reusable council findings into ","type":"text"},{"text":".agents/findings/registry.jsonl","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]},{"type":"paragraph","content":[{"text":"Use the tracked contract in ","type":"text"},{"text":"docs/contracts/finding-registry.md","type":"text","marks":[{"type":"code_inline"}]},{"text":":","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"persist only reusable findings that should change future planning or review behavior","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"require ","type":"text"},{"text":"dedup_key","type":"text","marks":[{"type":"code_inline"}]},{"text":", provenance, ","type":"text"},{"text":"pattern","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"detection_question","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"checklist_item","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"applicable_when","type":"text","marks":[{"type":"code_inline"}]},{"text":", and ","type":"text"},{"text":"confidence","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"applicable_when","type":"text","marks":[{"type":"code_inline"}]},{"text":" must use the controlled vocabulary from the contract","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"append or merge by ","type":"text"},{"text":"dedup_key","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"use the contract's temp-file-plus-rename atomic write rule","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"After the registry mutation, refresh compiled outputs immediately so the same session can benefit from the updated prevention set. If ","type":"text"},{"text":"hooks/finding-compiler.sh","type":"text","marks":[{"type":"code_inline"}]},{"text":" exists, run:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"bash hooks/finding-compiler.sh --quiet 2>/dev/null || true","type":"text"}]},{"type":"heading","attrs":{"level":4},"content":[{"text":"Step ACT.3: Feed Next-Work","type":"text"}]},{"type":"paragraph","content":[{"text":"Actionable improvements identified during processing -> append one schema v1.4 batch entry to ","type":"text"},{"text":".agents/rpi/next-work.jsonl","type":"text","marks":[{"type":"code_inline"}]},{"text":" using the tracked contract in ","type":"text"},{"text":"../../docs/contracts/next-work.schema.md","type":"text","marks":[{"type":"link","attrs":{"href":"../../docs/contracts/next-work.schema.md","title":null}},{"type":"code_inline"}]},{"text":" and the write procedure in ","type":"text"},{"text":"references/harvest-next-work.md","type":"text","marks":[{"type":"link","attrs":{"href":"references/harvest-next-work.md","title":null}},{"type":"code_inline"}]},{"text":". Follow the claim/finalize lifecycle documented in ","type":"text"},{"text":"references/harvest-next-work.md","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"mkdir -p .agents/rpi\n# Build VALID_ITEMS via the schema-validation flow in references/harvest-next-work.md\n# Then append one entry per post-mortem / epic.\n# If a harvested item already maps to a known proof surface, preserve it on the\n# item as \"proof_ref\" instead of burying target IDs in free text. Example item:\n# [{\"title\":\"Verify the parity gate after proof propagation lands\",\"type\":\"task\",\"severity\":\"medium\",\"source\":\"council-finding\",\"description\":\"Re-run the targeted validator after the follow-up lands.\",\"target_repo\":\"agentops\",\"proof_ref\":{\"kind\":\"execution_packet\",\"run_id\":\"6f36a5640805\",\"path\":\".agents/rpi/runs/6f36a5640805/execution-packet.json\"}}]\nENTRY_TIMESTAMP=\"$(date -Iseconds)\"\nSOURCE_EPIC=\"${EPIC_ID:-recent}\"\nVALID_ITEMS_JSON=\"${VALID_ITEMS_JSON:-[]}\"\n\nprintf '%s\\n' \"$(jq -cn \\\n --arg source_epic \"$SOURCE_EPIC\" \\\n --arg timestamp \"$ENTRY_TIMESTAMP\" \\\n --argjson items \"$VALID_ITEMS_JSON\" \\\n '{\n source_epic: $source_epic,\n timestamp: $timestamp,\n items: $items,\n consumed: false,\n claim_status: \"available\",\n claimed_by: null,\n claimed_at: null,\n consumed_by: null,\n consumed_at: null\n }'\n)\" >> .agents/rpi/next-work.jsonl","type":"text"}]},{"type":"heading","attrs":{"level":4},"content":[{"text":"Step ACT.4: Update Marker","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"date -Iseconds > .agents/ao/last-processed","type":"text"}]},{"type":"paragraph","content":[{"text":"This must be the LAST action in Phase 4.","type":"text"}]},{"type":"paragraph","content":[{"text":"Phases 3-6 (Maintenance):","type":"text","marks":[{"type":"strong"}]},{"text":" Read ","type":"text"},{"text":"references/maintenance-phases.md","type":"text","marks":[{"type":"link","attrs":{"href":"references/maintenance-phases.md","title":null}}]},{"text":" for backlog processing, activation, retirement, and harvesting phases. Load when ","type":"text"},{"text":"--process-only","type":"text","marks":[{"type":"code_inline"}]},{"text":" flag is set or when running full post-mortem.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Reporting and Workflow","type":"text"}]},{"type":"paragraph","content":[{"text":"Read ","type":"text"},{"text":"references/user-reporting.md","type":"text","marks":[{"type":"link","attrs":{"href":"references/user-reporting.md","title":null}}]},{"text":" when you need the Step 7 report template, mandatory next-","type":"text"},{"text":"/rpi","type":"text","marks":[{"type":"code_inline"}]},{"text":" suggestion format, workflow integration diagram, and example invocations.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Examples","type":"text"}]},{"type":"paragraph","content":[{"text":"Read ","type":"text"},{"text":"references/user-reporting.md","type":"text","marks":[{"type":"link","attrs":{"href":"references/user-reporting.md","title":null}}]},{"text":" for full example invocations and what happens in each mode.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Troubleshooting","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Problem","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Cause","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Solution","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Council times out","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Epic too large or too many files changed","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Split post-mortem into smaller reviews or increase timeout","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"No next-work items harvested","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Council found no tech debt or improvements","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Flywheel stable — write entry with empty items array to next-work.jsonl","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Checkpoint-policy preflight blocks","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Prior FAIL verdict in ratchet chain without fix","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Resolve prior failure (fix + re-vibe) or skip checkpoint-policy via ","type":"text"},{"text":"--skip-checkpoint-policy","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Metadata verification fails","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Plan vs actual files mismatch or missing cross-references","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Include failures in council packet as ","type":"text"},{"text":"context.metadata_failures","type":"text","marks":[{"type":"code_inline"}]},{"text":" — judges assess severity","type":"text"}]}]}]}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":2},"content":[{"text":"Compound-Engineering Retro (","type":"text"},{"text":"--compound","type":"text","marks":[{"type":"code_inline"}]},{"text":")","type":"text"}]},{"type":"paragraph","content":[{"text":"A comparative-delta mode for projects that run ","type":"text"},{"text":"ao goals measure","type":"text","marks":[{"type":"code_inline"}]},{"text":" repeatedly across iterations of the same domain slice. Use when a slice has ≥2 iterations in the verdict ledger and you want to know: what improved, what regressed, and what the learning yield was since the last run.","type":"text"}]},{"type":"paragraph","content":[{"text":"Trigger:","type":"text","marks":[{"type":"strong"}]},{"text":" run this mode after any ","type":"text"},{"text":"ao goals measure","type":"text","marks":[{"type":"code_inline"}]},{"text":" where the slice has a prior iteration record in ","type":"text"},{"text":".agents/goals/verdict-ledger.json","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# Confirm ≥2 iterations exist for a directive in the slice:\njq '[.records[] | select(.record_type==\"iteration\" and .directive_id==\"d-\u003cid>\")] | length' \\\n .agents/goals/verdict-ledger.json\n\n# Run a new iteration (appends one record per directive):\nao goals measure\n\n# Browse iteration history:\nao goals history --goal \u003cdirective-id>","type":"text"}]},{"type":"paragraph","content":[{"text":"Then follow the step-by-step procedure in ","type":"text"},{"text":"references/compound-engineering-retro.md","type":"text","marks":[{"type":"link","attrs":{"href":"references/compound-engineering-retro.md","title":null}}]},{"text":" (Steps CE.0–CE.5): extract N and N-1 records from the ledger, compute the verdict and satisfaction delta, count learning yield, and write the delta as a draft learning to ","type":"text"},{"text":".agents/learnings/YYYY-MM-DD-\u003cslice>-iter-delta.md","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]},{"type":"paragraph","content":[{"text":"The output learning carries ","type":"text"},{"text":"status: draft","type":"text","marks":[{"type":"code_inline"}]},{"text":" and the run IDs of both iterations; human or Tier-3 synthesis promotes it to ","type":"text"},{"text":"status: reviewed","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]},{"type":"paragraph","content":[{"text":"Closing the loop with re-steer.","type":"text","marks":[{"type":"strong"}]},{"text":" When the delta shows a directive failing chronically, the verdict ledger also drives auto re-steer: ","type":"text"},{"text":"ao goals steer recommend","type":"text","marks":[{"type":"code_inline"}]},{"text":" prints policy-driven directive mutations from the same ledger, and ","type":"text"},{"text":"ao goals steer apply","type":"text","marks":[{"type":"code_inline"}]},{"text":" writes the chosen mutation to GOALS.md — human-gated, via the non-lossy patcher (policy ","type":"text"},{"text":"auto_apply","type":"text","marks":[{"type":"code_inline"}]},{"text":" plus explicit confirmation; ADR-0006). The compound retro names ","type":"text"},{"text":"what","type":"text","marks":[{"type":"em"}]},{"text":" regressed; re-steer proposes ","type":"text"},{"text":"how","type":"text","marks":[{"type":"em"}]},{"text":" the directive should change. See the ","type":"text"},{"text":"/goals","type":"text","marks":[{"type":"code_inline"}]},{"text":" skill.","type":"text"}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":2},"content":[{"text":"See Also","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"skills/council/SKILL.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" — Multi-model validation council","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"skills/vibe/SKILL.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" — Council validates code (","type":"text"},{"text":"/vibe","type":"text","marks":[{"type":"code_inline"}]},{"text":" after coding)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"skills/pre-mortem/SKILL.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" — Council validates plans (before implementation)","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Reference Documents","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"references/post-mortem.feature","type":"text","marks":[{"type":"link","attrs":{"href":"references/post-mortem.feature","title":null}}]},{"text":" — Executable spec: validate-shipped, ratcheted learning promotion, next-work harvest, result.json (soc-qk4b.2)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"references/harvest-next-work.md","type":"text","marks":[{"type":"link","attrs":{"href":"references/harvest-next-work.md","title":null}}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"references/learning-templates.md","type":"text","marks":[{"type":"link","attrs":{"href":"references/learning-templates.md","title":null}}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"references/plan-compliance-checklist.md","type":"text","marks":[{"type":"link","attrs":{"href":"references/plan-compliance-checklist.md","title":null}}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"references/closure-integrity-audit.md","type":"text","marks":[{"type":"link","attrs":{"href":"references/closure-integrity-audit.md","title":null}}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"references/security-patterns.md","type":"text","marks":[{"type":"link","attrs":{"href":"references/security-patterns.md","title":null}}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"references/checkpoint-policy.md","type":"text","marks":[{"type":"link","attrs":{"href":"references/checkpoint-policy.md","title":null}}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"references/metadata-verification.md","type":"text","marks":[{"type":"link","attrs":{"href":"references/metadata-verification.md","title":null}}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"references/context-gathering.md","type":"text","marks":[{"type":"link","attrs":{"href":"references/context-gathering.md","title":null}}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"references/output-templates.md","type":"text","marks":[{"type":"link","attrs":{"href":"references/output-templates.md","title":null}}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"references/backlog-processing.md","type":"text","marks":[{"type":"link","attrs":{"href":"references/backlog-processing.md","title":null}}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"references/activation-policy.md","type":"text","marks":[{"type":"link","attrs":{"href":"references/activation-policy.md","title":null}}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"references/prediction-tracking.md","type":"text","marks":[{"type":"link","attrs":{"href":"references/prediction-tracking.md","title":null}}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"references/retro-history.md","type":"text","marks":[{"type":"link","attrs":{"href":"references/retro-history.md","title":null}}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"references/streak-tracking.md","type":"text","marks":[{"type":"link","attrs":{"href":"references/streak-tracking.md","title":null}}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"references/maintenance-phases.md","type":"text","marks":[{"type":"link","attrs":{"href":"references/maintenance-phases.md","title":null}}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"references/four-surface-closure.md","type":"text","marks":[{"type":"link","attrs":{"href":"references/four-surface-closure.md","title":null}}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"references/quick-mode.md","type":"text","marks":[{"type":"link","attrs":{"href":"references/quick-mode.md","title":null}}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"references/execution-steps.md","type":"text","marks":[{"type":"link","attrs":{"href":"references/execution-steps.md","title":null}}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"references/phase-2-extract.md","type":"text","marks":[{"type":"link","attrs":{"href":"references/phase-2-extract.md","title":null}}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"references/user-reporting.md","type":"text","marks":[{"type":"link","attrs":{"href":"references/user-reporting.md","title":null}}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"references/compound-engineering-retro.md","type":"text","marks":[{"type":"link","attrs":{"href":"references/compound-engineering-retro.md","title":null}}]}]}]}]},{"type":"hr","attrs":{"markup":"---"}}]},"metadata":{"date":"2026-06-05","name":"post-mortem","author":"@skillopedia","source":{"stars":375,"repo_name":"agentops","origin_url":"https://github.com/boshu2/agentops/blob/HEAD/packs/agentops/overlay/.claude/skills/post-mortem/SKILL.md","repo_owner":"boshu2","body_sha256":"a096c6ae0a30ad40a4ad866dcf5218212ffdac39004425f5e5b6a7dc8f23a468","cluster_key":"5bb33ff4907234d19b90158efe56d8849cbdfac7355284e48965d4e33bafd3a6","clean_bundle":{"format":"clean-skill-bundle-v1","source":"boshu2/agentops/packs/agentops/overlay/.claude/skills/post-mortem/SKILL.md","attachments":[{"id":"d72392d8-901c-557a-9968-535f6db96b19","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/d72392d8-901c-557a-9968-535f6db96b19/attachment.md","path":"references/activation-policy.md","size":5764,"sha256":"0d1f68696bfc6edf3fc2fe749efb6643c22fee220a0f0232ce13400582929a3d","contentType":"text/markdown; charset=utf-8"},{"id":"01818890-f919-53ab-8905-4015839ae56b","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/01818890-f919-53ab-8905-4015839ae56b/attachment.md","path":"references/backlog-processing.md","size":8399,"sha256":"854f46bab443ef2b306be964667291433dbf9885cbddb7ebcc00f0274448a6f8","contentType":"text/markdown; charset=utf-8"},{"id":"210ec055-b2b5-59df-9066-25c4567ad4a2","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/210ec055-b2b5-59df-9066-25c4567ad4a2/attachment.md","path":"references/checkpoint-policy.md","size":4159,"sha256":"a7d8c69ec9e8ebc74f432ccecc3b1b5853ded4bd3f0d8f886dfbc2ae0029c6f2","contentType":"text/markdown; charset=utf-8"},{"id":"4d48ff1a-bfef-540d-9fa0-ef988209a299","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/4d48ff1a-bfef-540d-9fa0-ef988209a299/attachment.md","path":"references/closure-integrity-audit.md","size":18266,"sha256":"c20316b50b26f6a150b1ade30d4bc9c15ba0a75cbc4cc5a0fe223c584a0f4828","contentType":"text/markdown; charset=utf-8"},{"id":"4a0bbcd4-d9a8-5cd0-823c-da2ae1f45c0a","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/4a0bbcd4-d9a8-5cd0-823c-da2ae1f45c0a/attachment.md","path":"references/compound-engineering-retro.md","size":4856,"sha256":"0a33a5e6565e7a21826213b5dc22d48d229644247b327953484318d01178897c","contentType":"text/markdown; charset=utf-8"},{"id":"a9925332-2ee9-51fe-a222-2db6b8f75f27","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/a9925332-2ee9-51fe-a222-2db6b8f75f27/attachment.md","path":"references/context-gathering.md","size":5749,"sha256":"4ddb42a07e2310690d040b375148ced88e429a86951abf8497e5e0d724971819","contentType":"text/markdown; charset=utf-8"},{"id":"dbd4db15-a808-5128-b360-e27776af1259","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/dbd4db15-a808-5128-b360-e27776af1259/attachment.md","path":"references/execution-steps.md","size":10094,"sha256":"944d8b3db9e176eba66e0fcce50cc0d1b4b52e18198a8146604ae4871c33c190","contentType":"text/markdown; charset=utf-8"},{"id":"c75013db-5a7a-53d5-805c-cfa2a8057a76","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/c75013db-5a7a-53d5-805c-cfa2a8057a76/attachment.md","path":"references/four-surface-closure.md","size":4318,"sha256":"e21d2662c2fbd184428476f4dd74d9398e35e233f7a621a12d35d54703cd7498","contentType":"text/markdown; charset=utf-8"},{"id":"bbc36071-6a16-5e26-9187-0b97318c267f","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/bbc36071-6a16-5e26-9187-0b97318c267f/attachment.md","path":"references/harvest-next-work.md","size":8501,"sha256":"02c25d0ed85da657be12917bba9ecd76ced9b8b1bb4c0f91ac621234561c6021","contentType":"text/markdown; charset=utf-8"},{"id":"afa986ed-445b-5ba0-adba-cc74a9c074da","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/afa986ed-445b-5ba0-adba-cc74a9c074da/attachment.md","path":"references/learning-templates.md","size":7255,"sha256":"a1c71711cab8d1495529ea2bf7bea809cc2ba127ac3d5f97b65657ee858bc043","contentType":"text/markdown; charset=utf-8"},{"id":"5b76c2d2-85f3-5b40-9b43-c72867bf013b","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/5b76c2d2-85f3-5b40-9b43-c72867bf013b/attachment.md","path":"references/maintenance-phases.md","size":16974,"sha256":"ee7bff69434e9ffa0d0ecd22e600e08d75677dbd02dcd35c0917906f6d942d6b","contentType":"text/markdown; charset=utf-8"},{"id":"7c117270-771a-58b7-9c48-753c9efe72cf","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/7c117270-771a-58b7-9c48-753c9efe72cf/attachment.md","path":"references/metadata-verification.md","size":4130,"sha256":"4d4503fbff4f0b7a7921a34a427d8e7ac5f0ee27e739227111aa1bff0469069e","contentType":"text/markdown; charset=utf-8"},{"id":"b4a0927f-cc3c-594c-bbd4-1b6879332054","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/b4a0927f-cc3c-594c-bbd4-1b6879332054/attachment.md","path":"references/output-templates.md","size":6637,"sha256":"4b0b46e16bd9d72d5a91d2bd69153efc428305b518897ce84e9f8532a0bcf378","contentType":"text/markdown; charset=utf-8"},{"id":"ab31b5e6-15a1-5a68-9ba4-462c3781e8cb","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/ab31b5e6-15a1-5a68-9ba4-462c3781e8cb/attachment.md","path":"references/phase-2-extract.md","size":7145,"sha256":"4533db4ec2ed670ba536a51f0b06e2aa50df5363bf5bb5cf44b34886be44decc","contentType":"text/markdown; charset=utf-8"},{"id":"46eb50dc-de5c-51f8-a136-d83eafee901c","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/46eb50dc-de5c-51f8-a136-d83eafee901c/attachment.md","path":"references/plan-compliance-checklist.md","size":2288,"sha256":"33d168a8e988af24cf4244afa322ff7b549f63dc2ee9e0ba93bb1120e5492a15","contentType":"text/markdown; charset=utf-8"},{"id":"b4b55582-c496-53bc-a932-8808fa367193","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/b4b55582-c496-53bc-a932-8808fa367193/attachment.feature","path":"references/post-mortem.feature","size":1660,"sha256":"e8f2a14f28e3713cb6f5cd7e08631674052f242e062db5cd836de9c53b73f687","contentType":"text/plain; charset=utf-8"},{"id":"854686d7-9190-5e62-824a-ef13ccef1d34","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/854686d7-9190-5e62-824a-ef13ccef1d34/attachment.md","path":"references/prediction-tracking.md","size":2082,"sha256":"efa010232bf666c9b27eaf35a1ff6d1f0a4e44a9f21ff2268c43df0bd020a72f","contentType":"text/markdown; charset=utf-8"},{"id":"c7e4fa05-d640-5a2a-a49c-9242cdca6b2d","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/c7e4fa05-d640-5a2a-a49c-9242cdca6b2d/attachment.md","path":"references/quick-mode.md","size":960,"sha256":"677a9c118ca441fa6f78207bdc88515e68729fbacd6209e52c640baf11800099","contentType":"text/markdown; charset=utf-8"},{"id":"a8b0a4cd-30f5-5b8a-90b5-f8f9c259ad3b","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/a8b0a4cd-30f5-5b8a-90b5-f8f9c259ad3b/attachment.md","path":"references/retro-history.md","size":2435,"sha256":"523b99fada06c2046f607b9b284dfd1a5c8b51615af56cd6192e17395b693582","contentType":"text/markdown; charset=utf-8"},{"id":"1f9acb57-bfa0-5cb4-a015-11165e3eb49a","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/1f9acb57-bfa0-5cb4-a015-11165e3eb49a/attachment.md","path":"references/security-patterns.md","size":6858,"sha256":"647e1e1f18a84b5ba99a87a6aa1951eb85b00814417843c62137329d342e2faa","contentType":"text/markdown; charset=utf-8"},{"id":"d395f912-59ee-5a8c-95f8-3c69809dc764","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/d395f912-59ee-5a8c-95f8-3c69809dc764/attachment.md","path":"references/streak-tracking.md","size":3633,"sha256":"3d70fd95105beaf9f3390f1f19f8b025527713ac9ecf4b8077e235d1aab2b9c1","contentType":"text/markdown; charset=utf-8"},{"id":"57e66c1a-c30f-5be0-b6e1-b1946f7feed7","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/57e66c1a-c30f-5be0-b6e1-b1946f7feed7/attachment.md","path":"references/user-reporting.md","size":2431,"sha256":"029d0c243c719e8dcfea195f590cd1f561e2718d49abb5e1001d3f954e3b680d","contentType":"text/markdown; charset=utf-8"},{"id":"f5cdcc84-8ebb-5861-bbbb-7243de7f120c","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/f5cdcc84-8ebb-5861-bbbb-7243de7f120c/attachment.sh","path":"scripts/closure-integrity-audit.sh","size":36415,"sha256":"d300caec537e5013dce9e086a137530382af2be07adb2b2b2542e4050a9d38dc","contentType":"application/x-sh; charset=utf-8"},{"id":"59be8c7b-355e-51d2-a579-924d94b63706","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/59be8c7b-355e-51d2-a579-924d94b63706/attachment.sh","path":"scripts/preflight-refs.sh","size":2547,"sha256":"b2c8aadfabd3a3d46c9482cf22146c5124d6285d436c8c626c0c15a7ef0e647b","contentType":"application/x-sh; charset=utf-8"},{"id":"51a5a98c-04bd-5dc0-8ee3-b67c6dced5b5","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/51a5a98c-04bd-5dc0-8ee3-b67c6dced5b5/attachment.sh","path":"scripts/validate.sh","size":2856,"sha256":"925cc613c7f9b13cec8689f4096d3c4bdb5adc1ecb98b0fc0ba67aae9dc2bca2","contentType":"application/x-sh; charset=utf-8"},{"id":"0dc042e7-1995-55e2-b1bd-a4a535863af1","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/0dc042e7-1995-55e2-b1bd-a4a535863af1/attachment.sh","path":"scripts/write-evidence-only-closure.sh","size":8188,"sha256":"5c7ae9f53fabdda6939e34b79b8561a37ef90eb08a9be0e6de2d0364d5dae1bd","contentType":"application/x-sh; charset=utf-8"}],"bundle_sha256":"70fed5a8c418003fb824438885f8fda7cc9a9fcdd911fd432f165c211fc2bfdc","attachment_count":26,"text_attachments":25,"attachment_storage":"skillopedia-attachments-v1","binary_attachments":1,"excluded_attachments":[]},"cluster_size":1,"skill_md_path":"packs/agentops/overlay/.claude/skills/post-mortem/SKILL.md","import_metadata":{"date":"2026-06-05","author":"@skillopedia","version":"v1","category":"education-research","category_label":"Education"},"exact_dupes_collapsed_into_this":0},"context":{"intent":{"mode":"task"},"window":"fork","sections":{"exclude":["HISTORY"]},"intel_scope":"full"},"version":"v1","category":"education-research","consumes":["implement","vibe","council"],"metadata":{"tier":"judgment","dependencies":["council","beads"]},"produces":["result.json"],"practices":["dora-metrics","sre","lean-startup"],"import_tag":"clean-skills-v1","context_rel":[{"kind":"shared-kernel","with":"standards"}],"description":"Review completed work and learn.","hexagonal_role":"domain","output_contract":"skills/council/schemas/verdict.json","skill_api_version":1}},"renderedAt":1782987600696}

Post-Mortem Skill Purpose: Wrap up completed work — validate it shipped correctly, extract learnings, process the knowledge backlog, activate high-value insights, and retire stale knowledge. Runtime note: Hook-driven closeout is runtime-dependent. Claude/OpenCode can wire Phase 2-5 maintenance through lifecycle hooks. Codex CLI v0.115.0+ supports native hooks (same behavior). For older Codex versions without hook surfaces, finish closeout with . Loop position Move 7 (capture evidence + learning, then ratchet) of the operating loop. Two outputs per loop turn: evidence (test names, snapshot key…