engsys 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (173) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +202 -0
  3. package/core/agents/aaron.md +152 -0
  4. package/core/agents/bert.md +115 -0
  5. package/core/agents/isabelle.md +136 -0
  6. package/core/agents/jody.md +150 -0
  7. package/core/agents/leith.md +111 -0
  8. package/core/agents/marcelo.md +282 -0
  9. package/core/agents/melvin.md +101 -0
  10. package/core/agents/nyx.md +152 -0
  11. package/core/agents/otto.md +168 -0
  12. package/core/agents/patricia.md +283 -0
  13. package/core/commands/design-audit-local.md +155 -0
  14. package/core/commands/design-audit.md +235 -0
  15. package/core/commands/design-critique.md +96 -0
  16. package/core/commands/file-issue.md +22 -0
  17. package/core/commands/generate-project.md +45 -0
  18. package/core/commands/implement-issue.md +37 -0
  19. package/core/commands/implement-project.md +40 -0
  20. package/core/commands/naturalize.md +61 -0
  21. package/core/commands/pre-push.md +29 -0
  22. package/core/commands/prep-review-collect.md +130 -0
  23. package/core/commands/prep-review-finalize.md +121 -0
  24. package/core/commands/prep-review-publish.md +113 -0
  25. package/core/commands/prep-review.md +65 -0
  26. package/core/commands/project-closeout.md +25 -0
  27. package/core/skills/agentic-eval/SKILL.md +195 -0
  28. package/core/skills/chrome-devtools/SKILL.md +97 -0
  29. package/core/skills/code-review/SKILL.md +26 -0
  30. package/core/skills/gh-cli/SKILL.md +2202 -0
  31. package/core/skills/git-commit/SKILL.md +124 -0
  32. package/core/skills/git-workflow-agents/SKILL.md +462 -0
  33. package/core/skills/git-workflow-agents/reference.md +220 -0
  34. package/core/skills/github-actions/SKILL.md +190 -0
  35. package/core/skills/github-issues/SKILL.md +154 -0
  36. package/core/skills/llm-structured-outputs/SKILL.md +323 -0
  37. package/core/skills/llm-structured-outputs/references/provider-details.md +392 -0
  38. package/core/skills/pre-push/SKILL.md +115 -0
  39. package/core/skills/refactor/SKILL.md +645 -0
  40. package/core/skills/web-design-reviewer/SKILL.md +371 -0
  41. package/core/skills/webapp-testing/SKILL.md +127 -0
  42. package/core/skills/webapp-testing/test-helper.js +56 -0
  43. package/core/templates/CLAUDE.md.tmpl +98 -0
  44. package/core/templates/adr-template.md +67 -0
  45. package/core/templates/gh-issue-templates/bug.md +39 -0
  46. package/core/templates/gh-issue-templates/content.md +42 -0
  47. package/core/templates/gh-issue-templates/enhancement.md +36 -0
  48. package/core/templates/gh-issue-templates/feature.md +39 -0
  49. package/core/templates/gh-issue-templates/infrastructure.md +41 -0
  50. package/core/templates/post-edit-reminders.sh.tmpl +19 -0
  51. package/core/templates/settings.json.tmpl +90 -0
  52. package/core/templates/settings.local.json.tmpl +3 -0
  53. package/core/workflows/agent-implementation-workflow.md +346 -0
  54. package/core/workflows/generate-project.md +258 -0
  55. package/core/workflows/implement-project-workflow.md +190 -0
  56. package/core/workflows/issue-tracking.md +89 -0
  57. package/core/workflows/project-closeout-ceremony.md +77 -0
  58. package/core/workflows/review-workflow.md +266 -0
  59. package/engsys.config.example.yaml +46 -0
  60. package/install +202 -0
  61. package/lessons-library/README.md +80 -0
  62. package/lessons-library/async-callbacks-verify-liveness.md +15 -0
  63. package/lessons-library/change-isnt-done-until-every-surface-updated.md +15 -0
  64. package/lessons-library/claim-then-act-for-irreversible-ops.md +16 -0
  65. package/lessons-library/co-commit-entangled-work.md +15 -0
  66. package/lessons-library/dependabot-triage-playbook.md +17 -0
  67. package/lessons-library/deploy-by-digest-and-verify-the-running-revision.md +15 -0
  68. package/lessons-library/enforce-your-guarantee-at-your-boundary.md +16 -0
  69. package/lessons-library/gate-changes-on-measurement-not-vibes.md +15 -0
  70. package/lessons-library/iac-first-no-console-changes.md +15 -0
  71. package/lessons-library/independent-objective-review-gate.md +15 -0
  72. package/lessons-library/keep-an-immutable-source-of-truth.md +15 -0
  73. package/lessons-library/long-agent-runs-checkpoint-not-poll.md +15 -0
  74. package/lessons-library/model-identity-with-stable-ids-and-provenance.md +15 -0
  75. package/lessons-library/operator-choices-are-first-class.md +15 -0
  76. package/lessons-library/prefer-tool-enforced-structured-output.md +15 -0
  77. package/lessons-library/prove-causation-before-acting.md +15 -0
  78. package/lessons-library/re-read-state-before-acting.md +14 -0
  79. package/lessons-library/read-layer-tolerates-unbackfilled-rows.md +15 -0
  80. package/lessons-library/shell-safety-pipefail-and-validate-before-teardown.md +14 -0
  81. package/lessons-library/shift-correctness-left-and-distrust-false-greens.md +15 -0
  82. package/lessons-library/stray-control-bytes-hide-changes.md +14 -0
  83. package/lessons-library/tests-can-assert-the-bug.md +15 -0
  84. package/lessons-library/verify-ground-truth-not-reports.md +15 -0
  85. package/lessons-library/worktrees-need-bootstrap-from-origin-main.md +15 -0
  86. package/lib/commands.js +356 -0
  87. package/lib/generate-team-avatars.mjs +251 -0
  88. package/lib/manifest.js +155 -0
  89. package/lib/render.js +135 -0
  90. package/lib/selftest.js +90 -0
  91. package/lib/util.js +89 -0
  92. package/lib/yaml.js +156 -0
  93. package/optional-agents/gary.md +86 -0
  94. package/optional-agents/jos.md +136 -0
  95. package/optional-agents/sandy.md +101 -0
  96. package/optional-agents/steve.md +161 -0
  97. package/package.json +43 -0
  98. package/stacks/cloud/aws/claude.fragment.md +17 -0
  99. package/stacks/cloud/aws/settings.fragment.json +39 -0
  100. package/stacks/cloud/aws/skills/aws-deployment-preflight/SKILL.md +165 -0
  101. package/stacks/cloud/aws/skills/cloud-architecture-aws/SKILL.md +265 -0
  102. package/stacks/cloud/azure/claude.fragment.md +17 -0
  103. package/stacks/cloud/azure/settings.fragment.json +45 -0
  104. package/stacks/cloud/azure/skills/azure-deployment-preflight/SKILL.md +175 -0
  105. package/stacks/cloud/azure/skills/cloud-architecture-azure/SKILL.md +211 -0
  106. package/stacks/cloud/cloudflare/claude.fragment.md +21 -0
  107. package/stacks/cloud/cloudflare/settings.fragment.json +31 -0
  108. package/stacks/cloud/cloudflare/skills/cloud-architecture-cloudflare/SKILL.md +294 -0
  109. package/stacks/cloud/cloudflare/skills/cloudflare-deployment-preflight/SKILL.md +175 -0
  110. package/stacks/cloud/gcp/claude.fragment.md +17 -0
  111. package/stacks/cloud/gcp/settings.fragment.json +40 -0
  112. package/stacks/cloud/gcp/skills/cloud-architecture-gcp/SKILL.md +208 -0
  113. package/stacks/cloud/gcp/skills/gcp-deployment-preflight/SKILL.md +137 -0
  114. package/stacks/db/mongo/skills/mongo-conventions/SKILL.md +96 -0
  115. package/stacks/db/prisma/claude.fragment.md +49 -0
  116. package/stacks/db/prisma/skills/docker-database-package-copy/SKILL.md +44 -0
  117. package/stacks/db/prisma/skills/prisma-conventions/SKILL.md +37 -0
  118. package/stacks/domain/mobile-growth/skills/apple-ads/SKILL.md +184 -0
  119. package/stacks/domain/mobile-growth/skills/apple-ads/references/benchmark-notes.md +47 -0
  120. package/stacks/domain/mobile-growth/skills/apple-ads/references/official-links.md +53 -0
  121. package/stacks/domain/mobile-growth/skills/google-play-growth/SKILL.md +197 -0
  122. package/stacks/domain/mobile-growth/skills/google-play-growth/references/benchmark-notes.md +47 -0
  123. package/stacks/domain/mobile-growth/skills/google-play-growth/references/official-links.md +45 -0
  124. package/stacks/iac/bicep/claude.fragment.md +14 -0
  125. package/stacks/iac/bicep/settings.fragment.json +20 -0
  126. package/stacks/iac/bicep/skills/iac-bicep/SKILL.md +113 -0
  127. package/stacks/iac/cdk/claude.fragment.md +14 -0
  128. package/stacks/iac/cdk/settings.fragment.json +23 -0
  129. package/stacks/iac/cdk/skills/iac-cdk/SKILL.md +104 -0
  130. package/stacks/iac/terraform/claude.fragment.md +13 -0
  131. package/stacks/iac/terraform/settings.fragment.json +25 -0
  132. package/stacks/iac/terraform/skills/iac-terraform/SKILL.md +93 -0
  133. package/stacks/iac/terraform/skills/terraform-conventions/SKILL.md +87 -0
  134. package/stacks/lang/kotlin/skills/android-testing/SKILL.md +263 -0
  135. package/stacks/lang/kotlin/skills/jetpack-compose/SKILL.md +264 -0
  136. package/stacks/lang/kotlin/skills/kotlin-coroutines/SKILL.md +329 -0
  137. package/stacks/lang/python/skills/python-conventions/SKILL.md +61 -0
  138. package/stacks/lang/shell/skills/shell-scripting/SKILL.md +110 -0
  139. package/stacks/lang/swift/skills/swift-concurrency/SKILL.md +423 -0
  140. package/stacks/lang/swift/skills/swift-concurrency/references/approachable-concurrency.md +80 -0
  141. package/stacks/lang/swift/skills/swift-concurrency/references/concurrency-patterns.md +233 -0
  142. package/stacks/lang/swift/skills/swift-concurrency/references/swiftui-concurrency.md +187 -0
  143. package/stacks/lang/swift/skills/swift-concurrency/references/synchronization-primitives.md +341 -0
  144. package/stacks/lang/swift/skills/swift-testing/SKILL.md +497 -0
  145. package/stacks/lang/swift/skills/swift-testing/references/testing-advanced.md +106 -0
  146. package/stacks/lang/swift/skills/swift-testing/references/testing-patterns.md +504 -0
  147. package/stacks/lang/swift/skills/swiftdata/SKILL.md +334 -0
  148. package/stacks/lang/swift/skills/swiftdata/references/core-data-coexistence.md +504 -0
  149. package/stacks/lang/swift/skills/swiftdata/references/swiftdata-advanced.md +975 -0
  150. package/stacks/lang/swift/skills/swiftdata/references/swiftdata-queries.md +675 -0
  151. package/stacks/lang/swift/skills/swiftui-patterns/SKILL.md +371 -0
  152. package/stacks/lang/swift/skills/swiftui-patterns/references/architecture-patterns.md +486 -0
  153. package/stacks/lang/swift/skills/swiftui-patterns/references/deprecated-migration.md +1097 -0
  154. package/stacks/lang/swift/skills/swiftui-patterns/references/design-polish.md +780 -0
  155. package/stacks/lang/swift/skills/swiftui-patterns/references/platform-and-sharing.md +696 -0
  156. package/stacks/lang/typescript/skills/typescript-conventions/SKILL.md +91 -0
  157. package/stacks/platform/android/claude.fragment.md +40 -0
  158. package/stacks/platform/android/hooks/pre-push-gradle.sh +70 -0
  159. package/stacks/platform/android/settings.fragment.json +13 -0
  160. package/stacks/platform/android/skills/android-build-conventions/SKILL.md +247 -0
  161. package/stacks/platform/ios/claude.fragment.md +24 -0
  162. package/stacks/platform/ios/hooks/pre-push-xcodebuild.sh +82 -0
  163. package/stacks/platform/ios/settings.fragment.json +21 -0
  164. package/stacks/platform/ios/skills/xcodebuildmcp-simulator-logs/SKILL.md +76 -0
  165. package/stacks/platform/web/skills/frontend-testing/SKILL.md +246 -0
  166. package/stacks/platform/web/skills/react-conventions/SKILL.md +261 -0
  167. package/stacks/platform/web/skills/web-platform-conventions/SKILL.md +55 -0
  168. package/stacks/tooling/issue-tracker-github/claude.fragment.md +10 -0
  169. package/stacks/tooling/issue-tracker-github/settings.fragment.json +24 -0
  170. package/stacks/tooling/issue-tracker-github/skills/issue-tracker-github/SKILL.md +278 -0
  171. package/stacks/tooling/issue-tracker-linear/claude.fragment.md +17 -0
  172. package/stacks/tooling/issue-tracker-linear/settings.fragment.json +9 -0
  173. package/stacks/tooling/issue-tracker-linear/skills/issue-tracker-linear/SKILL.md +183 -0
@@ -0,0 +1,190 @@
1
+ # Implement Project Workflow
2
+
3
+ Walk a tracker ProjectV2 board phase-by-phase, opening one PR per phase, pausing on material review findings. Builds on [agent-implementation-workflow.md](agent-implementation-workflow.md) — that doc still governs the per-phase mechanics (worktree, commits, local review, push, PR). This doc only adds the outer loop that reads the project board and decides what to do next.
4
+
5
+ Invocation: `/implement-project <number>` (e.g. `/implement-project 21`).
6
+
7
+ This workflow is the explicit authorization for the routine implementation cycle — see [agent-implementation-workflow.md § Start Command Authorization](agent-implementation-workflow.md).
8
+
9
+ Board reads/writes (Phase/Priority/Owner/Status) and work-item operations go through the project's installed **issue-tracker skill** (`.claude/skills/issue-tracker-*/`) via its contract operations (`query-board`, `set-board-field`, `update-issue`, `comment-issue`, `link-pr`). The skill maps them onto the active backend; the GitHub `gh` / `gh api graphql` commands shown below are what it runs on a GitHub project. PR creation (`gh pr create`) and CI stay on GitHub.
10
+
11
+ ---
12
+
13
+ ## Preconditions
14
+
15
+ The project **must** have a `Phase` single-select field with `P<n>: <name>` options (e.g. `P0: Foundation`, `P1: Core`). Without it, this command cannot batch.
16
+
17
+ If the project lacks a `Phase` field, stop and report:
18
+
19
+ > "Project #<num> has no `Phase` field. Run Jody first to retrofit (see [generate-project.md § 5](generate-project.md)), or implement issues one at a time via `/implement-issue <num>`."
20
+
21
+ Do not attempt to invent phases on the fly — that's Jody's job and requires the design loop.
22
+
23
+ ---
24
+
25
+ ## Phase 0: Read the Board
26
+
27
+ Use the issue-tracker skill's **`query-board`** operation to fetch the project's fields + items grouped by Phase. On GitHub the skill runs `gh api graphql` (the `github` MCP doesn't support ProjectV2), querying `projectV2(number:)` for: field definitions (`ProjectV2SingleSelectField { id name options }`) and items (`content { ... on Issue { number title state url } }` plus their `fieldValues`).
28
+
29
+ ```bash
30
+ gh api graphql -f query='
31
+ query($owner: String!, $num: Int!) {
32
+ user(login: $owner) { # or organization(login: $owner)
33
+ projectV2(number: $num) {
34
+ id title
35
+ fields(first: 30) { nodes { ... on ProjectV2SingleSelectField { id name options { id name } } } }
36
+ items(first: 100) {
37
+ nodes {
38
+ id
39
+ content { ... on Issue { number title state url repository { nameWithOwner } } }
40
+ fieldValues(first: 20) {
41
+ nodes { ... on ProjectV2ItemFieldSingleSelectValue {
42
+ field { ... on ProjectV2SingleSelectField { name } } name } }
43
+ }
44
+ }
45
+ }
46
+ }
47
+ }
48
+ }' -f owner=<OWNER> -F num=<PROJECT_NUMBER>
49
+ ```
50
+
51
+ Group items by their `Phase` value. Skip items whose `Status` is `Done` or whose underlying issue `state` is `CLOSED`. Within each phase, preserve original board order (use `Priority` as a tiebreaker when present).
52
+
53
+ Phase order is determined by the numeric prefix in the `P<n>: …` option name. Treat `P-1` as before `P0`. Treat `P1.5` as between `P1` and `P2`.
54
+
55
+ ---
56
+
57
+ ## Phase 1: Pick the Next Batch
58
+
59
+ The "next batch" is the lowest-numbered phase that still has at least one open issue.
60
+
61
+ Print the operator a short plan before starting:
62
+
63
+ ```text
64
+ Project #21 "Metrics v2 — KPI Dashboard"
65
+ Next phase: P1: Schema & DTOs (4 issues open, 0 already merged)
66
+ - #1562 ...
67
+ - #1563 ...
68
+ After this phase: P2: Settings UI (6 issues), P3: Hardening (2 issues)
69
+ ```
70
+
71
+ Do not pause for operator approval — the slash command itself is the authorization. But if any pre-flight check is failing (auth scope, dirty main, uncommitted local changes), stop and report.
72
+
73
+ ---
74
+
75
+ ## Phase 2: Walk One Phase
76
+
77
+ For the chosen phase, follow [agent-implementation-workflow.md](agent-implementation-workflow.md) end-to-end:
78
+
79
+ 1. Assign every issue in the phase to `@me` (skill `update-issue`).
80
+ 2. Create the worktree + branch: `agent/project-<num>-phase-<n>-<slug>` (e.g. `agent/project-21-phase-1-schema-dtos`), from `origin/main`.
81
+ 3. Set the board `Status` to `In Progress` on every item in this phase (skill `set-board-field`; on GitHub GraphQL `updateProjectV2ItemFieldValue`).
82
+ 4. Implement issues sequentially — **one commit per issue** with `(#<num>)` in the subject. Match the patterns of nearby code; do not refactor opportunistically.
83
+ 5. Run the project's pre-push gate / precheck (build, lint, unit tests, and path-gated checks for E2E, migrations, IaC, containers). See `/pre-push`.
84
+ 6. **Run a local code review against `origin/main` before push.** Fix Critical + Warning findings, re-run once to confirm clean, cap at ~2 passes.
85
+ 7. Push once, open one **draft** PR via `gh pr create --draft` (PR creation stays on GitHub), linking/closing each work item per the skill's `link-pr` operation (GitHub: `Closes #<num>` on its own line for every issue in the phase), then post the local review findings onto the work item via the skill's `comment-issue` operation (on GitHub, the marked PR comment).
86
+ 8. Once the local review is clean and the gate is green, mark **Ready for review** (`gh pr ready <n>`) to trigger any expensive ready-for-review CI matrix.
87
+
88
+ ---
89
+
90
+ ## Phase 3: Triage Review Findings, Then Decide
91
+
92
+ Triage every finding from the Phase 2 local review (done before push). For each:
93
+
94
+ - **Material**: a real bug, security risk, missed edge case, broken assumption, or anything that would change shipped behavior. Fix it before push; re-run the review.
95
+ - **Cosmetic**: nit-level (naming, formatting, doc wording). Fix if the change is trivially safe and obviously correct; otherwise note it in the findings comment.
96
+ - **Wrong**: the review misread the code. Note why in the findings comment.
97
+
98
+ After resolving all findings, classify the _batch outcome_:
99
+
100
+ | Outcome | Next action |
101
+ | ---------------------------------------------------------- | ------------------------------------------------------------------------------------------ |
102
+ | No material findings (clean or only cosmetic) | Continue autonomously: jump to Phase 4, then loop back to Phase 1 for the next phase |
103
+ | ≥1 material finding fixed, review confirms clean on re-run | Continue autonomously |
104
+ | Material finding exposes a product/architecture decision | **Stop**. Note it in the findings comment, report to the operator, do not start the next phase |
105
+ | Review errored / couldn't run | **Stop**. Report to the operator with the PR URL and what's missing |
106
+
107
+ "Pause on review findings" means: pause only when a finding is material AND it reveals something the operator should weigh in on. Cosmetic and trivial fixes do not stop the loop. Use judgment; when in doubt, pause.
108
+
109
+ ---
110
+
111
+ ## Phase 3.5: Objective Independent Review (before any merge)
112
+
113
+ This is the merge gate the `/implement-project` command enforces. Once the phase PR is open and the implementer's local review is clean, the **orchestrator** (not the implementer) launches a *fresh, independent* reviewer subagent — general-purpose on **Opus**, with **no stake in the code**. It must:
114
+
115
+ - re-run the full pre-push gate from scratch (confirm or refute the author's numbers);
116
+ - verify **each** issue's acceptance criteria against the diff;
117
+ - probe security/correctness;
118
+ - hunt for regressions and reconciliation artifacts (duplicate/dead code, dropped work, stray exports).
119
+
120
+ It ends with a decisive **`VERDICT: CLEAN`** (no Critical/Warning — safe to merge) or **`VERDICT: FINDINGS`** (severity-tagged, `file:line`). **Never merge a phase PR that has not passed this gate.**
121
+
122
+ - **CLEAN** → safe to merge; proceed to Phase 4.
123
+ - **FINDINGS** → route back to the implementer to fix + re-review, or **stop** and report if it exposes a product/architecture decision.
124
+
125
+ ---
126
+
127
+ ## Phase 4: Mark Phase Done, Loop
128
+
129
+ After the PR is opened, the local review resolved, and the objective review returns CLEAN:
130
+
131
+ 1. Update the board `Status` to `Done` on every item in the completed phase (skill `set-board-field`) — **but only after the phase PR has merged** if the board has an "Auto-close issue" workflow enabled (otherwise `Status=Done` can prematurely close the linked issue while its PR is still a draft). If you need an interim state, use `Status: In Review`.
132
+ 2. Do **not** wait for the PR to merge before *starting* the next phase (each phase is a separate branch from `main`; rebasing across them after merges is normal) — unless the auto-close caveat above forces you to gate on merge.
133
+ 3. Loop to Phase 1 and pick the next unfinished phase.
134
+
135
+ Stop when every phase has `Status: Done` (or `In Review`) on all its items. Report a one-line summary per phase with PR links.
136
+
137
+ ---
138
+
139
+ ## Stop Conditions (Always Pause)
140
+
141
+ Stop and report to the operator if any of these fire mid-loop:
142
+
143
+ - Pre-flight: missing `project` auth scope, dirty main, uncommitted local changes, `gh auth` failed.
144
+ - A phase missing entirely (e.g. an issue in the project has no `Phase` value) — fix the project, don't paper over it.
145
+ - An issue body too vague for safe implementation — file a clarification comment on the issue and stop.
146
+ - The pre-push gate fails and the fix isn't obvious within ~15 min of work.
147
+ - A material review finding requires product/architecture judgment.
148
+ - Any destructive or non-routine action would be needed (force push beyond `--force-with-lease` on the agent branch, `git reset --hard`, removing unrelated files).
149
+ - Three consecutive autonomous phases complete without a manual operator check-in — courtesy pause.
150
+
151
+ ---
152
+
153
+ ## What This Workflow Does Not Do
154
+
155
+ - Does not merge PRs without a CLEAN objective review. Human-in-the-loop merges every batch (or the orchestrator only with explicit operator authorization).
156
+ - Does not refactor existing code beyond the issues' scope.
157
+ - Does not file new issues for out-of-scope work it discovers — that goes through the deferring-work flow as a Jody hand-off.
158
+ - Does not skip the local review, even when phases are small.
159
+ - Does not retry the same failing pre-push gate more than twice without escalating.
160
+
161
+ ---
162
+
163
+ ## Completion Report
164
+
165
+ After the final phase (or after stopping), print:
166
+
167
+ ```text
168
+ Project #<num> "<title>" — implementation summary
169
+
170
+ Phases completed (PRs awaiting merge):
171
+ P0: <name> #PR_A (4 issues, review clean)
172
+ P1: <name> #PR_B (3 issues, 1 review fix applied)
173
+
174
+ Phases remaining:
175
+ P2: <name> (5 issues, not started)
176
+
177
+ Stopped because: <reason or "all phases complete">
178
+ Next action: <human merges P0/P1, then re-run /implement-project <num> for P2>
179
+ ```
180
+
181
+ When the operator confirms the project's **last** PR has merged (all phases done), run the **Project Closeout Ceremony** — don't treat the completion report as the end. See [project-closeout-ceremony.md](project-closeout-ceremony.md) / `/project-closeout <num>`: verify all PRs merged, clean up this project's worktrees + branches, mine ALL marked review-findings PR comments (+ any static-analysis alerts) across the project for recurring mistake _families_, and memorialize durable lessons (repo `docs/agent-lessons/` for team-wide patterns; the engsys lessons-library for generalizable ones; personal memory for orchestration judgment).
182
+
183
+ ---
184
+
185
+ ## Cross-References
186
+
187
+ - [agent-implementation-workflow.md](agent-implementation-workflow.md) — per-phase mechanics (worktree, commits, local review, PR)
188
+ - [project-closeout-ceremony.md](project-closeout-ceremony.md) — the mandatory close-out after the last PR merges
189
+ - [generate-project.md § 5](generate-project.md) — how the `Phase` field gets created in the first place
190
+ - `CLAUDE.md` § Pre-push gate, § Git / PR conventions
@@ -0,0 +1,89 @@
1
+ # Issue Tracking Workflow
2
+
3
+ How to investigate, file, and track tracker issues. Covers Bert's investigation + filing role. For implementation, see [agent-implementation-workflow.md](agent-implementation-workflow.md).
4
+
5
+ This workflow relies on the project's installed **issue-tracker skill** (`.claude/skills/issue-tracker-*/`) for the actual issue reads/writes. It calls the skill's contract operations by name (`create-issue`, `list-issues`, `get-issue`, `update-issue`, `comment-issue`, `close-issue`, `link-pr`); the skill maps them onto the active backend (GitHub `gh` shown below, or Linear).
6
+
7
+ ---
8
+
9
+ ## Filing an Issue
10
+
11
+ ### 1. Investigate first
12
+
13
+ Before creating an issue, locate the root cause — don't file on symptoms.
14
+
15
+ - Search the codebase for the relevant code paths.
16
+ - Read the actual files (don't guess from filenames).
17
+ - Identify affected files with line numbers.
18
+ - Check `docs/architecture/` for context.
19
+ - Formulate the **correct, robust solution** — no workarounds, no "fix later" stubs.
20
+
21
+ ### 2. Create the issue body in tmp/
22
+
23
+ **Never use HEREDOC or pipes for issue bodies. Always write to `tmp/` first** — this tmp/-file discipline is universal across trackers.
24
+
25
+ Write the body with the Write tool → `tmp/issue-body-<slug>.md`, then create the issue via the issue-tracker skill's **`create-issue`** operation, passing the body file, title, and `<type>,<area>` label(s). On GitHub the skill runs:
26
+
27
+ ```bash
28
+ # After tmp/issue-body-<slug>.md is written:
29
+ gh issue create \
30
+ --title "<emoji> <Type>: <description>" \
31
+ --body-file tmp/issue-body-<slug>.md \
32
+ --label "<type>,<area>"
33
+
34
+ rm tmp/issue-body-<slug>.md
35
+ ```
36
+
37
+ ### 3. The issue body must include
38
+
39
+ - **Summary** — what's broken or what's being added.
40
+ - **Root cause** (bugs) or **motivation** (features/enhancements).
41
+ - **Affected files** with line numbers.
42
+ - **Recommended solution** — the correct approach, not a workaround.
43
+ - **Acceptance criteria** — testable, specific.
44
+ - **Dependencies** — what must be done first.
45
+ - **Effort** — S / M / L.
46
+ - **Owner** — which agent persona.
47
+
48
+ ---
49
+
50
+ ## Title Format
51
+
52
+ | Type | Format |
53
+ | -------------- | ------------------------------- |
54
+ | Bug | `🐛 Bug: <description>` |
55
+ | Feature | `✨ Feature: <description>` |
56
+ | Enhancement | `💄 Enhancement: <description>` |
57
+ | Content | `📄 Content: <description>` |
58
+ | Infrastructure | `🏗️ Infra: <description>` |
59
+ | Epic | `Epic: <description>` |
60
+
61
+ ---
62
+
63
+ ## Labels
64
+
65
+ **Type** (pick one): `bug`, `feature`, `enhancement`, `content`, `infrastructure`.
66
+
67
+ **Area** (pick one): the project's service/module areas (declared in `CLAUDE.md` / the project's label set).
68
+
69
+ **Implementation-ready signal**: if the issue has clear acceptance criteria and no architectural ambiguity, note it in the body (e.g. "Self-contained, ready for implementation").
70
+
71
+ ---
72
+
73
+ ## Commit Message Format
74
+
75
+ Reference the issue number in every commit on the implementing branch:
76
+
77
+ ```text
78
+ feat(<scope>): add invite endpoint (#131)
79
+ fix(auth): clean up placeholder user on login (#136)
80
+ refactor(tenant): extract PLAN_LIMITS constant (#133)
81
+ ```
82
+
83
+ ---
84
+
85
+ ## When Implementation is Ready
86
+
87
+ Follow [agent-implementation-workflow.md](agent-implementation-workflow.md) — worktree setup, verify, push, PR.
88
+
89
+ Link/close the work item per the issue-tracker skill's **`link-pr`** operation. On GitHub, `Closes #<number>` in the PR body auto-closes the issue on merge; batch PRs must use one closing keyword per issue, one per line (a comma-list only closes the first). Other trackers close the work item per the skill. (PR creation itself stays on `gh pr create` — PRs are a code-host concern.)
@@ -0,0 +1,77 @@
1
+ # Project Closeout Ceremony
2
+
3
+ Run this **every time a project's last PR merges** (the final phase of `/implement-project`, or any multi-PR effort that reaches "all phases merged"). It is the deliberate close-out: clean up the workspace, mine the review feedback for _recurring_ mistakes, and memorialize durable lessons so the next project starts smarter. The per-phase reflection line in `/implement-project` is a stub — this is the full ceremony.
4
+
5
+ **Trigger:** the project's last open PR is MERGED (operator confirms, or `gh pr view` shows MERGED for every phase + follow-up PR). Also run it after a deliberate project-pause if no more phases are coming soon.
6
+
7
+ Work-item reads (board status, finding records) and closing the board go through the project's installed **issue-tracker skill** (`.claude/skills/issue-tracker-*/`) via `list-issues` / `get-issue` / `query-board` / `close-board`. PR state, PR comments, and CI alerts stay on `gh` / GitHub (PRs are a code-host concern).
8
+
9
+ **Why it exists:** review comments tend to get addressed inline, one at a time — yet a post-hoc mining pass routinely reveals several were the _same mistake family repeating across PRs_ (e.g. cross-method TOCTOU 4×, missing migration constraints 4×). Fixing inline isn't learning; generalizing the family into the checklist is. This ceremony forces that step instead of leaving it to chance.
10
+
11
+ ---
12
+
13
+ ## Step 1 — Verify everything merged
14
+
15
+ - `gh pr view <n> --json state` for every project + follow-up PR → confirm all `MERGED` (squash-merge makes `git branch --merged` report 0; trust PR state, not merge-base).
16
+ - Confirm the project board items are all `Done` / `In Review` (skill `query-board`). Any still `In Progress` → the project isn't closed; stop and report what's outstanding.
17
+
18
+ ## Step 2 — Clean up worktrees & branches
19
+
20
+ - Remove **only this project's** worktrees: `git worktree remove <path> --force` (force handles leftover `node_modules`/`dist`/build output); then `git worktree prune`. Leave unrelated/locked worktrees alone.
21
+ - Delete the merged local branches (`git branch -D <branch>`) and any dangling remote branches (`git push origin --delete <branch>` — UI-merged PRs don't auto-delete the remote branch).
22
+ - `git checkout main && git pull --ff-only origin main` in the main checkout if it was parked on a feature branch.
23
+
24
+ ## Step 3 — Close the project board (when every issue is closed)
25
+
26
+ Once Step 1 confirms the PRs are merged, close the project board — but **only if every issue on it is closed** — via the installed issue-tracker skill's `close-board` operation. Don't trust the board's own status field for this (on GitHub ProjectV2 the `item-list` `state` field is unreliable, often null/`?`); the skill gates on the **real** issue state, intersecting the board's issue numbers with the still-open issues and requiring the intersection to be empty.
27
+
28
+ - **Gate:** if _any_ issue is still open, do **not** close the board — the project isn't actually done (same stop-and-report rule as Step 1). Closing a board over a live issue hides work.
29
+ - Draft cards / notes (non-issue board items) do **not** gate the close — only real issues do.
30
+ - Reversible: closing archives the board from the active view (GitHub: `gh project close --undo` reopens it; Linear: set the project state back). It never deletes the project or its items.
31
+
32
+ The concrete commands live in the tracker skill's `close-board` (GitHub closes the ProjectV2; Linear marks the project Completed) — keep this step backend-agnostic.
33
+
34
+ ## Step 4 — Mine ALL review findings (the core of the ceremony)
35
+
36
+ Don't rely on memory of what you fixed inline. The durable record is the marked review-findings comment each PR/work item posts after its local review (plus any static-analysis / security-scanning alerts CI still produces). Pull them from **every** work item in the project into one corpus via the skill's `list-issues` / `get-issue` operations. On GitHub the findings live as the marked comment on each PR — pull them directly with `gh`:
37
+
38
+ ```bash
39
+ for pr in <all-project-PR-numbers>; do
40
+ gh pr view "$pr" --json comments \
41
+ --jq '.comments[] | select(.body | contains("<!-- local-review-findings -->")) | .body'
42
+ done
43
+ ```
44
+
45
+ (If a PR is missing its findings comment, note the gap — that PR went out without a recorded review, which is itself a process finding worth a lesson.)
46
+
47
+ Then classify by **mistake family**, not by individual fix. For each family ask:
48
+
49
+ - How many times did it recur, across how many distinct PRs? (≥2 PRs = a real pattern.)
50
+ - Is it already in `docs/agent-lessons/` (or the engsys lessons-library)? If yes but it still recurred → the lesson exists but wasn't _applied at authoring time_; strengthen the trigger/check, don't just re-file.
51
+ - Was it Critical/Major, or a nitpick? Prioritize the families that produced real bugs.
52
+
53
+ ## Step 5 — Memorialize (team-facing, in the repo)
54
+
55
+ Automatic behaviors that apply to **anyone** in the repo go in the repo, not personal memory:
56
+
57
+ - A **new, recurring** failure family → a new `docs/agent-lessons/<slug>.md` (trigger / failure mode / correct behavior / check / source).
58
+ - A family already documented but still recurring → strengthen the existing lesson + add the new variant + a Start-of-PR-check line.
59
+ - A reusable workflow/agent/command gap exposed by the run → update the relevant `.claude/workflows/*.md`, `.claude/agents/*.md`, or `CLAUDE.md`.
60
+ - **Generalizable lessons** (not tied to this project's stack/domain) → also open a PR back to the engsys `lessons-library/` so other projects inherit them.
61
+ - Commit on an `agent/project-<num>-reflection-lessons` branch → one docs PR. (Docs-only; if a local markdown-lint hook can't spawn, `--no-verify` is acceptable — the pre-push local review is the md backstop.)
62
+
63
+ ## Step 6 — Update personal memory (judgment/orchestration that's yours)
64
+
65
+ Insights about _how you operate_ (orchestration cadence, verification habits, tooling gotchas) go in your memory dir, not the repo. Append to existing feedback files rather than creating duplicates; point them at the strengthened repo checklist rather than copying it.
66
+
67
+ ## Step 7 — Final report
68
+
69
+ Print: PRs merged + their lesson contributions, worktrees/branches cleaned, **whether the project board was closed (or why not — which issues are still open)**, new/strengthened lessons (with the recurrence count that justified each), personal-memory updates, and any deferred follow-ups or operator-gated work still outstanding.
70
+
71
+ ---
72
+
73
+ ## Cross-References
74
+
75
+ - [implement-project-workflow.md](implement-project-workflow.md) — the phase loop this closes out
76
+ - [agent-implementation-workflow.md](agent-implementation-workflow.md) — where the marked review-findings comment is posted
77
+ - `CLAUDE.md` § Memory & rules (repo-vs-personal split) and § Post-merge cleanup (worktree removal mechanics)
@@ -0,0 +1,266 @@
1
+ # Review workflow (`/prep-review` family)
2
+
3
+ Shared reference for the four slash commands that take a markdown proposal in the repo, get it in front of stakeholders via a shared drive (Docs/Slides) + chat, harvest feedback, and close the loop with an ADR.
4
+
5
+ The intended audience is stakeholders who have the org's document drive + chat but **no repo access** — this workflow is the bridge. (Tooling: a **document-drive MCP** like Google Drive and a **chat MCP** like Slack. Tool names are project-defined; this doc is tool-agnostic.)
6
+
7
+ ## The four commands
8
+
9
+ | Phase | Command | What it does |
10
+ | ----- | ---------------------------------------------- | ----------------------------------------------------------------------------------------------------------------- |
11
+ | 1 | `/prep-review <path> [--as doc\|slides\|auto]` | Analyze source, pick surface, generate the review package, stage in `docs/reviews/<slug>/`, **gate on operator**. |
12
+ | 2 | `/prep-review-publish <slug> [--channel #x]` | Upload to the drive as a native Doc/Slides, post a chat announcement, persist IDs in `state.yaml`. |
13
+ | 3 | `/prep-review-collect <slug>` | Pull the current Drive doc body, render `feedback.md`, classify comments, **force operator resolution** on conflicts. |
14
+ | 4 | `/prep-review-finalize <slug>` | Draft an ADR matching repo style, post a chat thread summary, promote the final spec to `docs/specs/`. |
15
+
16
+ Each command can be run in a fresh session — all state lives in the repo, not the conversation.
17
+
18
+ ## Directory layout
19
+
20
+ ```text
21
+ docs/reviews/<slug>/
22
+ ├── state.yaml # workflow state, IDs, phase — canonical
23
+ ├── source.md # snapshot of the source markdown at /prep-review time
24
+ ├── package.md # rendered version that went to the drive
25
+ ├── feedback.md # comments + body diff, rendered at /prep-review-collect
26
+ └── decisions.md # operator decisions on each disagreement (challenge phase)
27
+ ```
28
+
29
+ On `/prep-review-finalize`:
30
+
31
+ - ADR → `docs/architecture/adr/NNN-<slug>.md` (next number in sequence)
32
+ - Final reviewed spec → `docs/specs/<slug>.md`
33
+ - `docs/reviews/<slug>/` stays as the audit trail (do **not** delete)
34
+
35
+ ## `state.yaml` schema
36
+
37
+ ```yaml
38
+ slug: opportunity-led-pivot
39
+ source: docs/specs/opportunity-led-pivot.md
40
+ created_at: 2026-05-26T18:42:00Z
41
+ phase: drafted # drafted | published | feedback-collected | finalized
42
+ surface: doc # doc | slides
43
+ surface_reasoning: |
44
+ One-paragraph explanation of why this surface was picked. Captured at
45
+ /prep-review time. Useful for audit and for forcing honesty when --as auto picks wrong.
46
+ review_objective: |
47
+ Decide between (A) shipping now with stubs, or (B) holding until end-to-end.
48
+
49
+ drive:
50
+ shared_drive_id: 0AHj... # the org shared drive
51
+ folder_id: 1AbCdEf... # the review subfolder
52
+ file_id: 1XyZ...
53
+ web_view_link: https://docs.google.com/document/d/1XyZ.../edit
54
+ uploaded_at: 2026-05-26T19:10:00Z
55
+
56
+ chat:
57
+ channel_id: C0123ABCDEF
58
+ channel_name: content-review
59
+ message_ts: "1716750600.123456"
60
+ permalink: https://.../archives/C.../p1716750600123456
61
+ posted_at: 2026-05-26T19:12:00Z
62
+
63
+ feedback:
64
+ collected_at: 2026-05-27T14:00:00Z
65
+ body_changed: true
66
+ classification:
67
+ clarification: 3
68
+ disagreement: 2
69
+ plus_one: 4
70
+ side_discussion: 1
71
+ unresolved_disagreements: 0 # must be 0 before finalize
72
+
73
+ adr:
74
+ number: 21
75
+ path: docs/architecture/adr/021-opportunity-led-pivot.md
76
+ drafted_at: 2026-05-27T15:20:00Z
77
+
78
+ final_spec:
79
+ path: docs/specs/opportunity-led-pivot.md
80
+ ```
81
+
82
+ Only the fields relevant to the current phase need to be populated. Later commands read what they need.
83
+
84
+ ## Surface selection (`/prep-review` heuristic)
85
+
86
+ Default `--as auto`. Recommend based on source shape; the operator confirms at the gate.
87
+
88
+ | Signal in source | Surface |
89
+ | --------------------------------------------------------- | ------------------------------------------------------------- |
90
+ | > 300 lines, multiple `##` sections, heavy prose | **Doc** |
91
+ | Heavy bulleted / outline structure, < 100 lines | **Slides** (if "deck", "pitch", "presentation" in name or H1) |
92
+ | Decision-focused, prose + 1–2 diagrams | **Doc** |
93
+ | Pitch-shaped (problem → solution → ask, narrative arc) | **Slides** |
94
+ | Tied to an existing ADR pattern (spec / proposal / brief) | **Doc** |
95
+
96
+ If ambiguous: prefer **Doc**. Suggesting mode + threaded comments beat slides for actual review work.
97
+
98
+ ## Drive folder convention
99
+
100
+ Review docs land in a dedicated review folder inside the org shared drive — everyone on the team already has access, so no per-doc sharing is needed.
101
+
102
+ - `/prep-review-publish` first searches the drive for the review folder by name (keep the query simple — some MCPs reject `trashed` / owner query fields). Shared-drive folders generally surface in search despite a misleading `canAddChildren: false` on the result.
103
+ - Persist `drive.folder_id` (and `drive.shared_drive_id` from the parent metadata) to `state.yaml` after the first lookup — subsequent publishes skip the search.
104
+
105
+ ## Upload SOP — operator drag-drop, command picks up via folder polling
106
+
107
+ `/prep-review-publish` generates the artifact locally with a descriptive filename derived from the source H1 (the drive uses the filename as the artifact's title). The operator drag-drops it into the review folder. The command **polls the folder** to detect the new file — no copy-paste of URLs required.
108
+
109
+ **About the .docx-in-Docs mode**: the drive does **not** reliably auto-prompt to convert `.docx` → a native Doc (workspace-config dependent). That's fine — Docs supports full `.docx` editing (comments, suggesting, real-time collab). The file stays as `.docx` (mime `application/vnd.openxmlformats-officedocument.wordprocessingml.document`), comments get stored in `word/comments.xml` inside the file, and `/prep-review-collect` benefits because the file downloads as-is without an export step. The only cosmetic trade-off: the title in the drive UI shows the `.docx` extension.
110
+
111
+ **The mechanics:**
112
+
113
+ 1. Verify the converter is on `PATH` (e.g. `pandoc`; bail with install instructions if missing).
114
+ 2. Derive the filename: source H1 with `:` swapped for `—`, plus the extension. Example: `Proposal: Review workflow` → `Proposal — Review workflow.docx`.
115
+ 3. Convert: `pandoc -f gfm -t docx docs/reviews/<slug>/package.md -o "docs/reviews/<slug>/<Derived>.docx"`.
116
+ 4. Baseline the folder (record existing file IDs).
117
+ 5. Print one clear instruction: "Drag `<path>` into `<folder URL>` — I'll detect it automatically."
118
+ 6. Poll the folder every ~7s for up to ~3 min, diff against the baseline.
119
+ 7. New file detected → fetch metadata for mime/title/web_view_link, persist to `state.yaml`, continue to the chat draft.
120
+ 8. Polling timeout → ask the operator to paste the URL as a fallback.
121
+
122
+ **Why not fully automated** (recorded so we don't re-litigate it):
123
+
124
+ - Creating the file from `text/markdown` / `text/html` — the drive keeps the source mime type; no native Doc conversion.
125
+ - Creating from `text/plain` — auto-converts to a Doc, but the body shows literal `## heading` / `**bold**` chars. Rejected.
126
+ - Creating a `.docx` via base64 upload — technically works but the base64 string round-trips through LLM context; impractically slow and token-heavy. Hard no.
127
+ - Some chat workspaces gate canvas/upload features behind a paid tier.
128
+
129
+ **If the MCP ever adds** an "upload from local path" drive tool, a "convert source X to native Doc" parameter, or the chat workspace upgrades — revisit. Until then, drag-drop is SOP.
130
+
131
+ ## Pulling comments back from the drive
132
+
133
+ The drive stores comments as a **separate resource** from the doc body. The MCP may not expose a direct `list_comments` tool, but the download/export accepts an `exportMimeType` — and several export formats **preserve comments inline**:
134
+
135
+ | Export MIME type | Comments included? |
136
+ | --------------------------------------------------------------------------------- | ------------------------------------------ |
137
+ | `application/vnd.openxmlformats-officedocument.wordprocessingml.document` (.docx) | **Yes** — as native Word comments in OOXML |
138
+ | `application/vnd.oasis.opendocument.text` (.odt) | **Yes** — as ODF annotations |
139
+ | `text/html` | **Yes** — typically as footnotes / sidebar |
140
+ | `application/pdf` | No (export drops them) |
141
+ | `text/plain` / `text/markdown` | No |
142
+
143
+ **Primary path** (`/prep-review-collect`):
144
+
145
+ 1. Export as `.docx` (the `wordprocessingml.document` export MIME type).
146
+ 2. Decode the base64 to `docs/reviews/<slug>/drive-export.docx`.
147
+ 3. Parse the `.docx` (it's a zip; `word/comments.xml` holds comment text + author + ids; `word/document.xml` holds the anchors). Use a docx skill or unzip+XML directly.
148
+ 4. Extract: comment text, author, anchor selection, resolved state (if present).
149
+ 5. Also pull current body text from the .docx for the diff against `package.md`.
150
+
151
+ **Known caveat — resolved comments**: the drive's `.docx` export typically includes **open** comments but may omit **resolved** ones. If the operator's review pattern resolves comments before collect runs, those are effectively lost to the export. Mitigations: ask reviewers to leave comments open until collect runs (note this in the announcement); for any "I resolved that already" comments, fall back to operator-paste during classification.
152
+
153
+ **Fallback path** — if export parsing fails or yields nothing useful, ask the operator to paste comment text directly.
154
+
155
+ ## Comment classification (`/prep-review-collect`)
156
+
157
+ For every comment / suggestion, classify as one of:
158
+
159
+ - **`clarification`** — reviewer asks a question or requests a tweak; incorporated into the next draft.
160
+ - **`disagreement`** — reviewer pushes back on a decision; **the operator must explicitly choose** accept / reject / explore-3rd-option before finalize. Recorded in `decisions.md`.
161
+ - **`plus_one`** — endorsement / "looks good"; tally only.
162
+ - **`side_discussion`** — adjacent thought, not blocking; surfaces in finalize as a "follow-ups" section.
163
+
164
+ `unresolved_disagreements > 0` in `state.yaml` **blocks** `/prep-review-finalize`. Run collect again after operator resolution.
165
+
166
+ ## Chat post conventions
167
+
168
+ Channel default: the project's review channel (override with `--channel`).
169
+
170
+ First post (publish phase):
171
+
172
+ ```text
173
+ 📋 *Review needed:* <title>
174
+
175
+ <2–4 sentence summary of the proposal — what's being decided>
176
+
177
+ *Review objective:* <review_objective from state.yaml>
178
+
179
+ *How to weigh in:* Open the doc and leave inline comments / suggestions. Reactions on this message also count. _Please leave comments open (don't resolve them) until I collect feedback — resolved comments may not survive the export._
180
+
181
+ 📎 <Drive link>
182
+ ```
183
+
184
+ Finalize-thread reply:
185
+
186
+ ```text
187
+ ✅ *Decision recorded:* <ADR title>
188
+
189
+ <2–3 sentences: decision, key consequence>
190
+
191
+ 📄 ADR: `docs/architecture/adr/NNN-<slug>.md` (in repo)
192
+ 📄 Final spec: `docs/specs/<slug>.md`
193
+
194
+ Thanks for the feedback — N comments incorporated, M side-discussions tracked as follow-ups.
195
+ ```
196
+
197
+ Use the draft-message tool for both — let the operator review before it ships.
198
+
199
+ ## Commits
200
+
201
+ Per `CLAUDE.md` — never auto-commit. Each command **stages files and proposes a commit message**; the operator runs the commit. Suggested messages (add a `(#issue)` suffix if the project requires it):
202
+
203
+ - After `/prep-review`: `docs(reviews): draft review package for <slug>`
204
+ - After `/prep-review-publish`: `docs(reviews): publish <slug> review package (Drive + chat)`
205
+ - After `/prep-review-collect`: `docs(reviews): collect feedback for <slug>`
206
+ - After `/prep-review-finalize`: `docs(adr): ADR-NNN <title>` + `docs(specs): finalize <slug>`
207
+
208
+ ## Idempotency and re-runs
209
+
210
+ - All commands check `state.yaml.phase` first. Running out-of-order → bail with the right next step.
211
+ - `/prep-review` on an already-drafted slug → require `--force` (overwrites `source.md`, `package.md`; keeps Drive/chat state if present).
212
+ - `/prep-review-publish` after a successful publish → no-op + report existing links. `--republish` deletes the old Drive file and posts a new chat message (use sparingly; resets comments).
213
+ - `/prep-review-collect` is naturally idempotent — re-running re-renders `feedback.md` from the current Drive state.
214
+ - `/prep-review-finalize` is idempotent on the ADR file (warns if the ADR number was already claimed).
215
+
216
+ ## ADR style (for finalize)
217
+
218
+ Match the established shape — read 2–3 recent ADRs from `docs/architecture/adr/` first. The pattern:
219
+
220
+ ```markdown
221
+ # ADR-NNN: <title>
222
+
223
+ | | |
224
+ | ----------- | ---------------------------- |
225
+ | **Status** | Proposed |
226
+ | **Date** | YYYY-MM-DD |
227
+ | **Context** | 1–2 sentences + link to spec |
228
+ | **Related** | links to prior ADRs / issues |
229
+
230
+ ---
231
+
232
+ ## Context
233
+
234
+ <3–5 bullets of the situation>
235
+
236
+ ## Decision N — <sub-decision title>
237
+
238
+ <the decision; sometimes a table>
239
+
240
+ ## Consequences
241
+
242
+ - **Easier:** ...
243
+ - **Harder:** ...
244
+ ```
245
+
246
+ Status defaults to `Accepted` on finalize (by the time finalize runs, the operator has incorporated feedback and resolved disagreements via `decisions.md`). The operator can downgrade to `Proposed` manually to leave the decision open.
247
+
248
+ ## Failure paths
249
+
250
+ | Failure | Action |
251
+ | ------------------------------------- | ------------------------------------------------------------------------------------------------- |
252
+ | Drive auth lapsed | Report clearly, instruct the operator to re-auth the MCP; do not write partial state |
253
+ | Chat post fails after Drive upload | Persist Drive IDs to `state.yaml` (so we don't re-upload); retry chat via `/prep-review-publish` |
254
+ | Source markdown missing | Bail immediately, no state changes |
255
+ | `docs/reviews/<slug>/` already exists | `/prep-review` requires `--force`; other commands proceed (they expect it) |
256
+
257
+ ## Why four commands, not one
258
+
259
+ Each phase has a natural pause and a different operator decision:
260
+
261
+ 1. **draft → publish**: "Does the package look right? Is this the right surface?"
262
+ 2. **publish → collect**: wait for humans to review (hours to days).
263
+ 3. **collect → finalize**: "I've thought about the disagreements. Here's my call."
264
+ 4. **finalize**: emit the durable artifacts.
265
+
266
+ Cramming this into one command either skips gates or holds open a long-running session.