@nklisch/pi-agile-workflow 0.15.3

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 (203) hide show
  1. package/.claude-plugin/plugin.json +10 -0
  2. package/.codex-plugin/plugin.json +28 -0
  3. package/CHANGELOG.md +591 -0
  4. package/README.md +107 -0
  5. package/docs/ARCHITECTURE.md +716 -0
  6. package/docs/MIGRATION.md +419 -0
  7. package/docs/PRINCIPLES.md +464 -0
  8. package/docs/ROADMAP.md +95 -0
  9. package/docs/SPEC.md +855 -0
  10. package/docs/VISION.md +106 -0
  11. package/extensions/agile-workflow.test.ts +419 -0
  12. package/extensions/agile-workflow.ts +467 -0
  13. package/hooks/hooks.json +52 -0
  14. package/hooks/scripts/prompt-context.py +628 -0
  15. package/hooks/scripts/substrate-maintainer.py +359 -0
  16. package/hooks/scripts/test_prompt_context.py +757 -0
  17. package/package.json +30 -0
  18. package/scripts/install-work-view.sh +165 -0
  19. package/scripts/tests/agent-metadata.test.sh +75 -0
  20. package/scripts/tests/bump-version.test.sh +552 -0
  21. package/scripts/tests/channel-parity.test.sh +117 -0
  22. package/scripts/tests/convert-content-integrity.test.sh +246 -0
  23. package/scripts/tests/convert-install-routing.test.sh +159 -0
  24. package/scripts/tests/install-work-view.test.sh +411 -0
  25. package/scripts/tests/pi-package-metadata.test.sh +119 -0
  26. package/scripts/tests/work-board-shim.test.sh +239 -0
  27. package/scripts/tests/work-view-dist-version.test.sh +105 -0
  28. package/scripts/work-board.sh +64 -0
  29. package/scripts/work-view.sh +423 -0
  30. package/skills/autopilot/SKILL.md +330 -0
  31. package/skills/autopilot/agents/openai.yaml +6 -0
  32. package/skills/board/SKILL.md +62 -0
  33. package/skills/board/agents/openai.yaml +6 -0
  34. package/skills/bold-refactor/SKILL.md +244 -0
  35. package/skills/bold-refactor/agents/openai.yaml +6 -0
  36. package/skills/bug-scan/SKILL.md +378 -0
  37. package/skills/bug-scan/agents/openai.yaml +6 -0
  38. package/skills/bug-scan/references/async-promises.md +119 -0
  39. package/skills/bug-scan/references/concurrency-races.md +148 -0
  40. package/skills/bug-scan/references/data-layer.md +167 -0
  41. package/skills/bug-scan/references/error-handling.md +197 -0
  42. package/skills/bug-scan/references/gate-item-template.md +94 -0
  43. package/skills/bug-scan/references/language-footguns.md +219 -0
  44. package/skills/bug-scan/references/parked-item-template.md +103 -0
  45. package/skills/bug-scan/references/report-template.md +115 -0
  46. package/skills/bug-scan/references/resource-leaks.md +199 -0
  47. package/skills/bug-scan/references/state-closures.md +148 -0
  48. package/skills/bug-scan/references/time-numbers.md +158 -0
  49. package/skills/convert/SKILL.md +1463 -0
  50. package/skills/convert/agents/openai.yaml +6 -0
  51. package/skills/convert/references/legacy-overlap-migration.md +179 -0
  52. package/skills/deep-code-scan/SKILL.md +378 -0
  53. package/skills/deep-code-scan/agents/openai.yaml +6 -0
  54. package/skills/deep-code-scan/references/artifact-ledger.md +148 -0
  55. package/skills/deep-code-scan/references/consolidation.md +91 -0
  56. package/skills/deep-code-scan/references/decomposition.md +127 -0
  57. package/skills/deep-code-scan/references/item-templates.md +263 -0
  58. package/skills/deep-code-scan/references/lane-catalog.md +129 -0
  59. package/skills/deep-code-scan/references/review-gauntlet.md +108 -0
  60. package/skills/deep-code-scan/references/scanner-brief.md +152 -0
  61. package/skills/e2e-test-design/SKILL.md +517 -0
  62. package/skills/e2e-test-design/references/anti-tautology.md +261 -0
  63. package/skills/e2e-test-design/references/service-mocks.md +234 -0
  64. package/skills/e2e-test-design/references/test-taxonomy.md +211 -0
  65. package/skills/epic-design/SKILL.md +476 -0
  66. package/skills/epicize/SKILL.md +210 -0
  67. package/skills/epicize/agents/openai.yaml +6 -0
  68. package/skills/feature-design/SKILL.md +472 -0
  69. package/skills/fix/SKILL.md +173 -0
  70. package/skills/gate-cruft/SKILL.md +258 -0
  71. package/skills/gate-docs/SKILL.md +278 -0
  72. package/skills/gate-patterns/SKILL.md +389 -0
  73. package/skills/gate-refactor/SKILL.md +408 -0
  74. package/skills/gate-security/SKILL.md +275 -0
  75. package/skills/gate-tests/SKILL.md +367 -0
  76. package/skills/groom/SKILL.md +162 -0
  77. package/skills/groom/agents/openai.yaml +6 -0
  78. package/skills/ideate/SKILL.md +185 -0
  79. package/skills/ideate/agents/openai.yaml +6 -0
  80. package/skills/implement/SKILL.md +288 -0
  81. package/skills/implement-orchestrator/SKILL.md +604 -0
  82. package/skills/park/SKILL.md +111 -0
  83. package/skills/perf-design/SKILL.md +427 -0
  84. package/skills/perf-scout/SKILL.md +404 -0
  85. package/skills/perf-scout/agents/openai.yaml +6 -0
  86. package/skills/perf-scout/references/algorithmic-and-data-structures.md +126 -0
  87. package/skills/perf-scout/references/approximation-and-precomputation.md +136 -0
  88. package/skills/perf-scout/references/caching-and-memoization.md +123 -0
  89. package/skills/perf-scout/references/compiler-runtime-and-language.md +136 -0
  90. package/skills/perf-scout/references/database-and-storage-internals.md +144 -0
  91. package/skills/perf-scout/references/distributed-systems.md +150 -0
  92. package/skills/perf-scout/references/game-engine-and-realtime.md +136 -0
  93. package/skills/perf-scout/references/gpu-and-accelerators.md +130 -0
  94. package/skills/perf-scout/references/idea-ranking.md +75 -0
  95. package/skills/perf-scout/references/io-and-batching.md +135 -0
  96. package/skills/perf-scout/references/memory-and-data-locality.md +137 -0
  97. package/skills/perf-scout/references/parallelism-and-vectorization.md +126 -0
  98. package/skills/perf-scout/references/parked-item-template.md +117 -0
  99. package/skills/perf-scout/references/peer-review-pass.md +150 -0
  100. package/skills/perf-scout/references/report-template.md +148 -0
  101. package/skills/principles/SKILL.md +678 -0
  102. package/skills/principles/references/models.md +191 -0
  103. package/skills/principles/references/subagents.md +158 -0
  104. package/skills/prose-author/SKILL.md +185 -0
  105. package/skills/refactor-conventions-creator/SKILL.md +359 -0
  106. package/skills/refactor-conventions-creator/agents/openai.yaml +6 -0
  107. package/skills/refactor-conventions-creator/references/common-structures.md +91 -0
  108. package/skills/refactor-conventions-creator/references/common-styles.md +90 -0
  109. package/skills/refactor-design/SKILL.md +384 -0
  110. package/skills/release-deploy/SKILL.md +667 -0
  111. package/skills/release-deploy/agents/openai.yaml +6 -0
  112. package/skills/research/SKILL.md +166 -0
  113. package/skills/research/agents/openai.yaml +6 -0
  114. package/skills/review/SKILL.md +267 -0
  115. package/skills/review/references/deep-review.md +98 -0
  116. package/skills/review/references/review-lenses.md +68 -0
  117. package/skills/review/references/substrate-side-effects.md +161 -0
  118. package/skills/review/references/target-resolution.md +52 -0
  119. package/skills/scope/SKILL.md +486 -0
  120. package/work-view/Cargo.lock +486 -0
  121. package/work-view/Cargo.toml +9 -0
  122. package/work-view/crates/cli/.work-view-version +1 -0
  123. package/work-view/crates/cli/Cargo.toml +18 -0
  124. package/work-view/crates/cli/src/actionable.rs +500 -0
  125. package/work-view/crates/cli/src/args.rs +792 -0
  126. package/work-view/crates/cli/src/board/assets/board.css +855 -0
  127. package/work-view/crates/cli/src/board/assets/board.js +259 -0
  128. package/work-view/crates/cli/src/board/assets/card.js +139 -0
  129. package/work-view/crates/cli/src/board/assets/components.css +290 -0
  130. package/work-view/crates/cli/src/board/assets/dependency.js +1193 -0
  131. package/work-view/crates/cli/src/board/assets/detail.js +256 -0
  132. package/work-view/crates/cli/src/board/assets/filters.js +389 -0
  133. package/work-view/crates/cli/src/board/assets/index.html +77 -0
  134. package/work-view/crates/cli/src/board/assets/kanban.js +231 -0
  135. package/work-view/crates/cli/src/board/assets/markdown.js +260 -0
  136. package/work-view/crates/cli/src/board/assets/motion.css +131 -0
  137. package/work-view/crates/cli/src/board/assets/state.js +245 -0
  138. package/work-view/crates/cli/src/board/assets/table.js +304 -0
  139. package/work-view/crates/cli/src/board/assets/tokens.css +602 -0
  140. package/work-view/crates/cli/src/board/assets/views.js +41 -0
  141. package/work-view/crates/cli/src/board/assets.rs +162 -0
  142. package/work-view/crates/cli/src/board/feed.rs +204 -0
  143. package/work-view/crates/cli/src/board/mod.rs +246 -0
  144. package/work-view/crates/cli/src/board/open.rs +144 -0
  145. package/work-view/crates/cli/src/board/server.rs +407 -0
  146. package/work-view/crates/cli/src/main.rs +195 -0
  147. package/work-view/crates/cli/src/render.rs +354 -0
  148. package/work-view/crates/cli/src/scope.rs +157 -0
  149. package/work-view/crates/cli/src/stale.rs +581 -0
  150. package/work-view/crates/cli/tests/board-js/dependency-table.test.mjs +64 -0
  151. package/work-view/crates/cli/tests/board-js/expanded-browsing.test.mjs +79 -0
  152. package/work-view/crates/cli/tests/board-js/harness-smoke.test.mjs +11 -0
  153. package/work-view/crates/cli/tests/board-js/harness.mjs +437 -0
  154. package/work-view/crates/cli/tests/board-js/kanban-detail.test.mjs +75 -0
  155. package/work-view/crates/cli/tests/board-js/markdown-filter.test.mjs +60 -0
  156. package/work-view/crates/cli/tests/fixtures/golden/.work/CONVENTIONS.md +3 -0
  157. package/work-view/crates/cli/tests/fixtures/golden/.work/active/epics/epic-alpha.md +16 -0
  158. package/work-view/crates/cli/tests/fixtures/golden/.work/active/features/feat-a.md +16 -0
  159. package/work-view/crates/cli/tests/fixtures/golden/.work/active/features/feat-b.md +16 -0
  160. package/work-view/crates/cli/tests/fixtures/golden/.work/active/stories/story-alpha-1.md +16 -0
  161. package/work-view/crates/cli/tests/fixtures/golden/.work/active/stories/story-research-1.md +19 -0
  162. package/work-view/crates/cli/tests/fixtures/golden/.work/archive/feat-done.md +16 -0
  163. package/work-view/crates/cli/tests/fixtures/golden/.work/archive/feat-shipped.md +18 -0
  164. package/work-view/crates/cli/tests/fixtures/golden/.work/backlog/idea-backlog.md +7 -0
  165. package/work-view/crates/cli/tests/fixtures/golden/.work/releases/v1.0/v1.0.md +16 -0
  166. package/work-view/crates/cli/tests/fixtures/ready-drafting/.work/CONVENTIONS.md +6 -0
  167. package/work-view/crates/cli/tests/fixtures/ready-drafting/.work/active/features/feat-dep-done.md +17 -0
  168. package/work-view/crates/cli/tests/fixtures/ready-drafting/.work/active/features/feat-design-ready.md +17 -0
  169. package/work-view/crates/cli/tests/fixtures/ready-drafting/.work/active/features/feat-impl-ready.md +17 -0
  170. package/work-view/crates/cli/tests/fixtures/ready-drafting/.work/active/stories/story-review-ready.md +17 -0
  171. package/work-view/crates/cli/tests/fixtures/with-malformed/.work/CONVENTIONS.md +4 -0
  172. package/work-view/crates/cli/tests/fixtures/with-malformed/.work/active/epics/malformed-no-closing-fence.md +8 -0
  173. package/work-view/crates/cli/tests/fixtures/with-malformed/.work/active/features/good-item.md +16 -0
  174. package/work-view/crates/cli/tests/integration.rs +2833 -0
  175. package/work-view/crates/core/Cargo.toml +13 -0
  176. package/work-view/crates/core/src/error.rs +61 -0
  177. package/work-view/crates/core/src/filter.rs +804 -0
  178. package/work-view/crates/core/src/graph.rs +324 -0
  179. package/work-view/crates/core/src/index.rs +590 -0
  180. package/work-view/crates/core/src/lib.rs +54 -0
  181. package/work-view/crates/core/src/model.rs +202 -0
  182. package/work-view/crates/core/src/parse.rs +494 -0
  183. package/work-view/crates/core/tests/fixtures/golden/.work/CONVENTIONS.md +15 -0
  184. package/work-view/crates/core/tests/fixtures/golden/.work/active/epics/epic-core.md +16 -0
  185. package/work-view/crates/core/tests/fixtures/golden/.work/active/features/feat-parser.md +16 -0
  186. package/work-view/crates/core/tests/fixtures/golden/.work/active/features/feat-query.md +16 -0
  187. package/work-view/crates/core/tests/fixtures/golden/.work/active/stories/malformed-no-id.md +15 -0
  188. package/work-view/crates/core/tests/fixtures/golden/.work/active/stories/story-parse-unit.md +16 -0
  189. package/work-view/crates/core/tests/fixtures/golden/.work/active/stories/story-security-gate.md +16 -0
  190. package/work-view/crates/core/tests/fixtures/golden/.work/archive/feat-old.md +16 -0
  191. package/work-view/crates/core/tests/fixtures/golden/.work/archive/feat-parser.md +17 -0
  192. package/work-view/crates/core/tests/fixtures/golden/.work/backlog/idea-future-board.md +7 -0
  193. package/work-view/crates/core/tests/fixtures/golden/.work/releases/v1.0/v1.0.md +16 -0
  194. package/work-view/crates/core/tests/fixtures/precedence/.work/CONVENTIONS.md +10 -0
  195. package/work-view/crates/core/tests/fixtures/precedence/.work/archive/dup-item.md +18 -0
  196. package/work-view/crates/core/tests/fixtures/precedence/.work/releases/v2.0/dup-item.md +18 -0
  197. package/work-view/crates/core/tests/integration.rs +541 -0
  198. package/work-view/dist/.gitattributes +1 -0
  199. package/work-view/dist/README.md +42 -0
  200. package/work-view/dist/aarch64-apple-darwin/work-view +0 -0
  201. package/work-view/dist/aarch64-unknown-linux-musl/work-view +0 -0
  202. package/work-view/dist/x86_64-apple-darwin/work-view +0 -0
  203. package/work-view/dist/x86_64-unknown-linux-musl/work-view +0 -0
@@ -0,0 +1,667 @@
1
+ ---
2
+ name: release-deploy
3
+ description: >
4
+ Cut a release in the agile-workflow substrate. Interactively binds items to a version, advances the
5
+ release stage from planned to quality-gate, runs all configured gates in CONVENTIONS.md order
6
+ (default: security, tests, cruft, docs, patterns), waits until all bound items + gate-produced items
7
+ reach stage:done, ships per release mapping (tag-based / branch-held / release-branch / none),
8
+ collapses bound items into one release summary and disposes their bodies per the terminal-tier
9
+ retention convention (delete-refs prunes to git history; retain-bodies keeps bodies on disk),
10
+ advances release to released. Idempotent — safe to re-run after fixing gate findings.
11
+ ---
12
+
13
+ # Release-Deploy
14
+
15
+ You orchestrate a release. The work is in three movements: **bind** items to a
16
+ version — both active done items and all unbound archived stubs late-bound here (each stub's
17
+ `archived_atop` records the baseline it was done atop, kept as provenance) — **gate** the bundle
18
+ (each gate produces items, not pass/fail; archived stubs are re-gated by hydrating their historical
19
+ full bodies from `git_ref` when needed), **ship**
20
+ when readiness criteria are met.
21
+
22
+ The release file at `.work/active/<release-id>.md` is the orchestration state.
23
+ Its stage advances `planned → quality-gate → released` as the release proceeds.
24
+
25
+ ## Arguments
26
+
27
+ - `release-deploy <version>` — start (or resume) a release with the given version
28
+ - `release-deploy` — discover the active release (one at `stage: planned` or
29
+ `quality-gate`); resume it. If multiple are active, ask the user.
30
+
31
+ ## Workflow
32
+
33
+ ### Phase 1: Read CONVENTIONS
34
+
35
+ Read `.work/CONVENTIONS.md`:
36
+ - Release mapping: `branch-held | tag-based | release-branch | none`
37
+ - Gate config: `gates_for_release: [...]` (default if absent: `[security, tests, cruft, docs, patterns]`)
38
+ - Terminal-tier retention: `delete-refs | retain-bodies` (default if absent: `delete-refs`) —
39
+ governs Phase 7's body disposition
40
+
41
+ If the mapping is `none`, continue with a gate/archive-only release flow:
42
+ release-deploy binds items, runs gates, waits for every bound item to reach
43
+ `done`, drafts the changelog, and collapses the bundle into one summary at
44
+ `.work/releases/<version>/release-<version>.md` (bound item bodies pruned or retained
45
+ per terminal-tier retention; git holds history). It does **not** tag, branch, merge, push, or bump
46
+ versions. Publishing/version bumping is external to release-deploy and must be
47
+ handled by the project-specific release mechanism.
48
+
49
+ ### Phase 2: Locate or create the release file
50
+
51
+ If `release-deploy <version>` was invoked and the file doesn't exist, create:
52
+
53
+ ```yaml
54
+ ---
55
+ id: release-<version>
56
+ kind: release
57
+ stage: planned
58
+ tags: []
59
+ parent: null
60
+ depends_on: []
61
+ release_binding: <version>
62
+ gate_origin: null
63
+ created: YYYY-MM-DD
64
+ updated: YYYY-MM-DD
65
+ ---
66
+
67
+ # Release <version>
68
+
69
+ ## Bound items
70
+ <populated in Phase 3>
71
+
72
+ ## Gate runs
73
+ <populated in Phase 5>
74
+ ```
75
+
76
+ Write to `.work/active/release-<version>.md` (release files live in active until
77
+ shipped, then move to `.work/releases/<version>/`).
78
+
79
+ ### Phase 3: Bind items (if at stage: planned)
80
+
81
+ A release binds two kinds of work: **active done items** (bound the way they always were, by setting
82
+ `release_binding`) and **archived stubs late-bound here** (all unbound archived stubs, pulled in even
83
+ though archiving was decoupled from any release; each stub's `archived_atop` records which baseline it
84
+ was done atop, kept as provenance).
85
+
86
+ If the release is at `stage: planned`:
87
+
88
+ 1. **Active done candidates (straggler sweep).** Projects typically bind items as they pass
89
+ review — the item carries `release_binding` before flipping `done` — so this gather only
90
+ sweeps done items the en-route binding missed. Show items at `stage: done` without a
91
+ `release_binding`. Note `--release ""` is NOT an unbound filter (work-view skips an empty
92
+ value entirely); filter the frontmatter directly. When the `agentic-research` plugin is
93
+ installed, also exclude `tags: [research]` items here — before the user-facing
94
+ confirmation — via work-view's real tag parse (never a hand-rolled tags regex):
95
+ ```bash
96
+ agentic_research_installed=false
97
+ if [ -d ".research" ] ||
98
+ [ -f "plugins/agentic-research/skills/research-orchestrator/SKILL.md" ] ||
99
+ [ -f ".agents/skills/research-orchestrator/SKILL.md" ] ||
100
+ [ -f ".claude/skills/research-orchestrator/SKILL.md" ]; then
101
+ agentic_research_installed=true
102
+ fi
103
+ research_paths=""
104
+ if [ "$agentic_research_installed" = true ]; then
105
+ research_paths=$(.work/bin/work-view --tag research --paths | sort)
106
+ fi
107
+ .work/bin/work-view --stage done --paths | while IFS= read -r item; do
108
+ [ -n "$research_paths" ] && printf '%s\n' "$research_paths" | grep -qxF "$item" && continue
109
+ binding=$(grep -m1 '^release_binding:' "$item" | awk '{print $2}')
110
+ { [ "$binding" = "null" ] || [ -z "$binding" ]; } && echo "$item"
111
+ done
112
+ ```
113
+ (Research engagements are inputs that ground other work, not release members; they never
114
+ bind. Without `agentic-research`, `[research]` is an inert project tag — items with it
115
+ bind normally, and the probe above leaves the gather unfiltered.)
116
+
117
+ 2. **Archived-stub candidates (`archived_atop` late-binding).** Gather **all unbound archived
118
+ stubs** (`release_binding: null`) regardless of their `archived_atop` value. Each unbound stub is
119
+ work done atop some prior baseline that no release has yet claimed; this version claims whatever
120
+ remains. `archived_atop` is recorded provenance (it surfaces in the Phase 7 summary column), **not**
121
+ the gather filter. Gathering by strict `archived_atop == prior` equality strands a stub forever
122
+ whenever a release is skipped: if a stub atop `N` is deselected (or its baseline differs) at `N+1`,
123
+ then at `N+2` the prior tag has advanced and that stub — still `archived_atop: N`,
124
+ `release_binding: null` — would never be gathered again. Claiming all unbound stubs prevents the
125
+ leak.
126
+ ```bash
127
+ # The prior shipped tag is still useful as recorded provenance / display context.
128
+ prior=$(git describe --tags --abbrev=0 2>/dev/null \
129
+ || ls -d .work/releases/*/ 2>/dev/null | sort -V | tail -1 | xargs -r basename)
130
+ prior=${prior:-pre-release}
131
+ # Gather ALL unbound archived stubs (recurse: ROADMAP-phase epics archive to
132
+ # .work/archive/epics/, not just the flat .work/archive/ root).
133
+ # When agentic-research is installed: [research] items are research inputs, not
134
+ # release members — exclude them. Use work-view's real tag parse for the exclusion
135
+ # set, never a hand-rolled tags regex (a regex misreads block-style tag lists and
136
+ # false-positives on tags like research-ops). Without agentic-research, [research]
137
+ # is an inert project tag and must not be filtered.
138
+ agentic_research_installed=false
139
+ if [ -d ".research" ] ||
140
+ [ -f "plugins/agentic-research/skills/research-orchestrator/SKILL.md" ] ||
141
+ [ -f ".agents/skills/research-orchestrator/SKILL.md" ] ||
142
+ [ -f ".claude/skills/research-orchestrator/SKILL.md" ]; then
143
+ agentic_research_installed=true
144
+ fi
145
+ research_paths=""
146
+ if [ "$agentic_research_installed" = true ]; then
147
+ research_paths=$(.work/bin/work-view --scope archive --tag research --paths | sort)
148
+ fi
149
+ find .work/archive -name '*.md' -type f | while read -r p; do
150
+ [ -n "$research_paths" ] && printf '%s\n' "$research_paths" | grep -qxF "$p" && continue
151
+ binding=$(grep -m1 '^release_binding:' "$p" | awk '{print $2}')
152
+ [[ "$binding" == "null" ]] && echo "$p"
153
+ done
154
+ ```
155
+ (Skip stubs already carrying a `release_binding` — they belong to an earlier release. Show each
156
+ stub's `archived_atop` so the user sees the baseline it was done atop when confirming. **When the
157
+ `agentic-research` plugin is installed, skip `tags: [research]` stubs** — a research engagement
158
+ is an input that grounds other work, not a shippable bundle member, so it never binds to a
159
+ release; without `agentic-research`, `[research]` is an inert project tag and stubs bind
160
+ normally. See the `[research]` tag semantics.)
161
+
162
+ 3. Use structured question tool to confirm the full set (active done items + gathered archived stubs).
163
+ Default: all active done items without binding plus all unbound archived stubs go in. Confirming
164
+ the archived set explicitly is required, since late-binding pulls in work the user never bound by
165
+ hand; the user may deselect any stub (it stays unbound for a later release).
166
+
167
+ 4. For each chosen item — active done item OR archived stub — edit its frontmatter:
168
+ `release_binding: <version>`. The PostToolUse hook bumps `updated:`. Do **not** touch a stub's
169
+ `archived_atop` (it is immutable) or `git_ref`.
170
+
171
+ 5. Update the release file's body with the bound items list (note which were late-bound archived
172
+ stubs).
173
+
174
+ 6. Advance the release file: `stage: planned → quality-gate`.
175
+
176
+ 7. Commit:
177
+ ```bash
178
+ git add .work/active/release-<version>.md <bound-item-files> <bound-archive-stubs>
179
+ git commit -m "release-deploy: bind <N> items to <version>"
180
+ ```
181
+
182
+ ### Phase 3.5: Binding-consistency guard
183
+
184
+ Read `binding_guard:` from `.work/CONVENTIONS.md` (same read as `gates_for_release` in Phase 1).
185
+ Values: `warn` (default when the key is absent) | `halt` | `off`. The guard reports two classes of
186
+ finding (defined below): **CONFLICT** (always acted on) and **INCOMPLETE** (severity governed by the
187
+ `epic_cohesion` convention).
188
+
189
+ - `off` — skip all three checks; log one line: "binding-consistency guard is off (CONVENTIONS
190
+ binding_guard: off); skipping." Continue to Phase 4. The guard short-circuits here **before** any
191
+ gathering or walking — the CONVENTIONS read is the first thing the bash block does.
192
+ - `warn` — run all three checks; if findings are present, print the full report into the
193
+ conversation AND record it in the release file body under a `### Binding-consistency warnings`
194
+ subsection (so the record is durable), then **continue** the release flow. The record is
195
+ **replace-or-skip**: a re-invoke rewrites the existing subsection with the current report rather
196
+ than appending a second block (current state, not history — release-deploy is idempotent).
197
+ - `halt` — run all three checks; if any **acted-on** finding is present (every CONFLICT, plus
198
+ INCOMPLETEs only under `epic_cohesion: total`), report and stop. The user resolves and re-runs
199
+ `/agile-workflow:release-deploy <version>`. Informational INCOMPLETEs (under `phased`) never halt.
200
+
201
+ **Two report classes.** The guard's hard invariant is *no cross-version drift*; whether an epic
202
+ must ship *whole* is a project convention (`epic_cohesion`), so the two are split:
203
+
204
+ - **CONFLICT** — a child bound to a *different* version than its bound parent, or a done parent left
205
+ unbound while its children are bound. Always a genuine coherence violation; always acted on per
206
+ `binding_guard` mode.
207
+ - **INCOMPLETE** — an unbound child of a bound parent. This is legitimate phased delivery for some
208
+ projects and drift for others, so its severity is governed by **`epic_cohesion`** (read from
209
+ `.work/CONVENTIONS.md`): `phased` (default) treats INCOMPLETE entries as informational — listed in
210
+ the warn report, never counting toward a halt; `total` treats them as mismatches acted on per
211
+ `binding_guard` mode, exactly like CONFLICTs.
212
+
213
+ Projects that hold the stronger "epics ship whole" convention set `epic_cohesion: total` (and
214
+ typically `binding_guard: halt`); the defaults (`phased` + `warn`) surface drift without imposing
215
+ total cohesion — so `halt` stays usable for phased / multi-release epics.
216
+
217
+ Before any gate runs, walk every item bound to this release plus every done-but-unbound parent
218
+ across active/archive, and verify three invariants. Any acted-on finding halts (or warns, per
219
+ `binding_guard`) — do NOT rebind anything implicitly. The user must resolve any inconsistency
220
+ flagged at `halt` level and re-run `/agile-workflow:release-deploy <version>`.
221
+
222
+ Throughout this guard a **parent** is an item of kind epic OR feature: Check 1 walks children of
223
+ bound epics/features, and Checks 2/3 walk done-unbound epics/features directly, so a bound feature
224
+ whose stories drift cross-version is caught just as a bound epic's features are.
225
+
226
+ This guard catches drift where a child's review-pass bound it to the release but its parent (epic or
227
+ feature) was forgotten, or where a done parent's children were bound without the parent itself being
228
+ bound.
229
+
230
+ Note: the plugin's archive tier may hold **bodyless stubs** (when `terminal-tier retention:
231
+ delete-refs` is in effect). Stubs retain their frontmatter (`id`, `parent`, `stage`,
232
+ `release_binding`), so the parent-walk works identically for stubs and live items — read the
233
+ frontmatter fields to resolve parentage and binding regardless of whether a body is present.
234
+
235
+ ```bash
236
+ version=<version> # substitute the target release version here (single substitution point)
237
+
238
+ # Read the gate config FIRST so `off` short-circuits before any gathering or walking.
239
+ # (Reading after the loop would run every check and then discard the result, contradicting
240
+ # "off — skip all three checks".)
241
+ binding_guard=$(grep -m1 '^binding_guard:' .work/CONVENTIONS.md | awk '{print $2}')
242
+ binding_guard="${binding_guard:-warn}" # default: warn
243
+ epic_cohesion=$(grep -m1 '^epic_cohesion:' .work/CONVENTIONS.md | awk '{print $2}')
244
+ epic_cohesion="${epic_cohesion:-phased}" # default: phased (an epic may ship across releases)
245
+
246
+ if [ "$binding_guard" = "off" ]; then
247
+ echo "binding-consistency guard is off (CONVENTIONS binding_guard: off); skipping."
248
+ # Continue to Phase 4 — do NOT gather or walk anything.
249
+ return 0 2>/dev/null || exit 0
250
+ fi
251
+
252
+ # Gather all items bound to this release (auto-widens to active + archive tiers).
253
+ # Exclude the release orchestration item itself.
254
+ bound_items=$(.work/bin/work-view --release "$version" --paths \
255
+ | xargs grep -lm1 'kind: \(epic\|feature\|story\)' 2>/dev/null)
256
+
257
+ # ONLY when the agentic-research plugin is installed, additionally apply Phase 3's [research]
258
+ # exclusion here too (research engagements never bind; defensively drop any that slipped into
259
+ # the bind set, e.g. bound before the plugin was adopted). Without the plugin, [research] is
260
+ # an inert project tag — its items bind normally and MUST be walked like any other item.
261
+ agentic_research_installed=false
262
+ if [ -d ".research" ] ||
263
+ [ -f "plugins/agentic-research/skills/research-orchestrator/SKILL.md" ] ||
264
+ [ -f ".agents/skills/research-orchestrator/SKILL.md" ] ||
265
+ [ -f ".claude/skills/research-orchestrator/SKILL.md" ]; then
266
+ agentic_research_installed=true
267
+ fi
268
+ if [ "$agentic_research_installed" = true ]; then
269
+ research_paths=$(.work/bin/work-view --scope all --tag research --paths | sort)
270
+ bound_items=$(printf '%s\n' $bound_items | while IFS= read -r p; do
271
+ [ -n "$p" ] || continue
272
+ printf '%s\n' "$research_paths" | grep -qxF "$p" || echo "$p"
273
+ done)
274
+ fi
275
+
276
+ # Two report classes, split by severity (the guard's hard invariant is no-cross-version-drift;
277
+ # whether an epic must ship *whole* is a project convention, carried by epic_cohesion):
278
+ # conflicts[] — CONFLICT: a child bound to a DIFFERENT version than its bound parent, or a
279
+ # done parent unbound while its children are bound. Always a genuine coherence
280
+ # violation; always acted on per binding_guard mode.
281
+ # incompletes[] — INCOMPLETE: an unbound child of a bound parent. Legitimate phased delivery
282
+ # under epic_cohesion: phased (informational only); a mismatch under
283
+ # epic_cohesion: total (acted on per binding_guard mode, like a CONFLICT).
284
+ conflicts=()
285
+ incompletes=()
286
+
287
+ # Note on search scope: the parent/child walks grep `.work/active .work/archive` only and
288
+ # deliberately exclude `.work/releases`. Collapsed release items are not valid parents under
289
+ # either retention model (delete-refs prunes to a single summary doc; retain-bodies keeps bodies
290
+ # but the items are terminal), so a `parent:` pointing into releases/ is an orphan by construction
291
+ # and would never resolve a live binding here.
292
+
293
+ for item_path in $bound_items; do
294
+ kind=$(grep -m1 '^kind:' "$item_path" | awk '{print $2}')
295
+ item_id=$(grep -m1 '^id:' "$item_path" | awk '{print $2}')
296
+ item_binding=$(grep -m1 '^release_binding:' "$item_path" | awk '{print $2}')
297
+ item_stage=$(grep -m1 '^stage:' "$item_path" | awk '{print $2}')
298
+ parent_id=$(grep -m1 '^parent:' "$item_path" | awk '{print $2}')
299
+
300
+ # Check 1: children of a bound parent (epic OR feature) are consistent with it.
301
+ # Walk this item's children (items whose parent: field matches this item's id).
302
+ if [ "$kind" = "epic" ] || [ "$kind" = "feature" ]; then
303
+ # Find all children of this parent across active and archive tiers (see scope note above).
304
+ while IFS= read -r child_path; do
305
+ child_binding=$(grep -m1 '^release_binding:' "$child_path" | awk '{print $2}')
306
+ child_id=$(grep -m1 '^id:' "$child_path" | awk '{print $2}')
307
+ if [ "$child_binding" != "$version" ] && [ "$child_binding" != "null" ] && [ "$child_binding" != "" ]; then
308
+ # CONFLICT: child bound to a different version than its bound parent.
309
+ conflicts+=("CONFLICT — child $child_id is bound to $child_binding, but its parent $kind $item_id is bound to $version")
310
+ elif [ "$child_binding" = "null" ] || [ "$child_binding" = "" ]; then
311
+ # INCOMPLETE: unbound child of a bound parent. Severity depends on epic_cohesion.
312
+ incompletes+=("INCOMPLETE — child $child_id (unbound) has parent $kind $item_id bound to $version; under epic_cohesion: total all children of a bound parent must share the same release binding")
313
+ fi
314
+ done < <(grep -rl "^parent: ${item_id}$" .work/active .work/archive 2>/dev/null)
315
+ fi
316
+ done
317
+
318
+ # Checks 2 & 3: a done parent (epic OR feature) left unbound while any child is bound is itself a
319
+ # CONFLICT (orphan risk). These parents are NOT in `bound_items` by definition, so walk all active
320
+ # and archived epics/features directly. The two invariants share one predicate — done + unbound +
321
+ # a bound child — so emit a single CONFLICT per parent (no double-report).
322
+ while IFS= read -r parent_path; do
323
+ kind=$(grep -m1 '^kind:' "$parent_path" | awk '{print $2}')
324
+ item_id=$(grep -m1 '^id:' "$parent_path" | awk '{print $2}')
325
+ item_binding=$(grep -m1 '^release_binding:' "$parent_path" | awk '{print $2}')
326
+ item_stage=$(grep -m1 '^stage:' "$parent_path" | awk '{print $2}')
327
+
328
+ [ "$item_stage" = "done" ] || continue
329
+ { [ "$item_binding" = "null" ] || [ -z "$item_binding" ]; } || continue
330
+
331
+ # Look for any child that is bound to this release (see scope note above).
332
+ if grep -rl "^parent: ${item_id}$" .work/active .work/archive 2>/dev/null \
333
+ | xargs grep -lm1 "^release_binding: ${version}$" 2>/dev/null | grep -q .; then
334
+ conflicts+=("CONFLICT — $kind $item_id is at stage: done and unbound while its children are bound to $version (orphan risk); bind the $kind to $version before proceeding")
335
+ fi
336
+ done < <(grep -rl '^kind: \(epic\|feature\)$' .work/active .work/archive 2>/dev/null)
337
+
338
+ # Severity assembly. CONFLICTs always count toward halt/warn. INCOMPLETEs count only under
339
+ # epic_cohesion: total; under phased they are informational and never trigger a halt.
340
+ acted=("${conflicts[@]}")
341
+ informational=()
342
+ if [ "$epic_cohesion" = "total" ]; then
343
+ acted+=("${incompletes[@]}")
344
+ else
345
+ informational=("${incompletes[@]}")
346
+ fi
347
+
348
+ if [ "${#acted[@]}" -gt 0 ] || [ "${#informational[@]}" -gt 0 ]; then
349
+ report="BINDING CONSISTENCY — release $version (epic_cohesion: $epic_cohesion):"
350
+ for m in "${acted[@]}"; do
351
+ report+=$'\n'" • $m"
352
+ done
353
+ for m in "${informational[@]}"; do
354
+ report+=$'\n'" • $m [informational under epic_cohesion: phased]"
355
+ done
356
+ report+=$'\n'
357
+ report+="Resolve each CONFLICT manually (edit release_binding or parent frontmatter as appropriate),"
358
+ report+=$'\n'"then re-run: /agile-workflow:release-deploy $version"
359
+
360
+ if [ "$binding_guard" = "halt" ] && [ "${#acted[@]}" -gt 0 ]; then
361
+ # halt only on acted-on entries (CONFLICTs always; INCOMPLETEs only under total).
362
+ echo "BINDING CONSISTENCY FAILURES (halt) — $report"
363
+ exit 1
364
+ else
365
+ # warn (or halt with only informational INCOMPLETEs): surface in conversation + record durably.
366
+ echo "BINDING CONSISTENCY WARNINGS (warn) — $report"
367
+ # Replace-or-skip the durable record so re-invokes don't accumulate duplicate subsections
368
+ # (release-deploy is idempotent — current state, not history).
369
+ release_file=.work/active/release-"$version".md
370
+ subsection="### Binding-consistency warnings"
371
+ new_block=$(printf '%s\n\n%s\n' "$subsection" "$report")
372
+ if grep -qF "$subsection" "$release_file"; then
373
+ # Rewrite the existing subsection in place: drop the old block (from its heading up to the
374
+ # next "### "/"## " heading or EOF) and splice the current report at the same spot.
375
+ awk -v block="$new_block" '
376
+ $0 == "### Binding-consistency warnings" { skip=1; print block; next }
377
+ skip && /^#{2,3} / && $0 != "### Binding-consistency warnings" { skip=0 }
378
+ !skip { print }
379
+ ' "$release_file" > "$release_file.tmp" && mv "$release_file.tmp" "$release_file"
380
+ else
381
+ printf '\n%s\n' "$new_block" >> "$release_file"
382
+ fi
383
+ fi
384
+ fi
385
+ ```
386
+
387
+ Act on the result per `binding_guard`:
388
+
389
+ - **`off`**: the bash block short-circuits at its first step (the CONVENTIONS read) before gathering
390
+ or walking anything; log one line and continue to Phase 4.
391
+ - **`halt`**: if any acted-on finding is present (every CONFLICT, plus INCOMPLETEs only under
392
+ `epic_cohesion: total`), stop the release flow. Do not proceed to gates. Do not rebind anything.
393
+ The user is the only actor who can resolve a binding inconsistency. Informational INCOMPLETEs
394
+ (under `epic_cohesion: phased`) are recorded but do not halt.
395
+ - **`warn`** (default): if any finding is present, print the report into the conversation AND record
396
+ it in the release body (replace-or-skip the `### Binding-consistency warnings` subsection rather
397
+ than appending a duplicate), then continue to Phase 4. Do not rebind anything.
398
+
399
+ ### Phase 4: Gate execution
400
+
401
+ **Gates run over every bound non-release item, including late-bound archived stubs.** Archived stubs
402
+ are `done`, but release binding may happen long after the item left active work. The gate phase MUST
403
+ re-examine those stubs for the release bundle. A gate that needs an archived item's body must
404
+ recover the historical full body from the stub's `git_ref` (trying the archive path and former
405
+ active/backlog paths for the item id) rather than treating the pruned stub body as missing evidence.
406
+
407
+ Each gate enforces this itself: gates build their bundle from
408
+ `work-view --release <version> --paths` (which auto-widens to all tiers), ignore only the release
409
+ orchestration item (`kind: release`), and include active done items plus archived stubs. release-deploy
410
+ does not pass a pre-filtered set to the gates.
411
+
412
+ If the release is at `stage: quality-gate`:
413
+
414
+ For each gate in `gates_for_release` order, invoke it via the `Skill` tool (or
415
+ spawn a sub-agent that invokes it):
416
+
417
+ ```
418
+ Skill(skill="agile-workflow:gate-<name>", args="<version>")
419
+ ```
420
+
421
+ Each gate is idempotent — re-running doesn't duplicate items. If a gate has
422
+ already run and the items it produced are at `stage: done`, the gate confirms
423
+ "no new findings" and moves on.
424
+
425
+ After each gate, append to the release body:
426
+
427
+ ```markdown
428
+ ### Gate runs
429
+ - **gate-security** (YYYY-MM-DD) — N findings (3 critical, 2 high, 5 medium)
430
+ - **gate-tests** (YYYY-MM-DD) — M coverage gaps (4 critical, 6 medium)
431
+ - ...
432
+ ```
433
+
434
+ ### Phase 5: Wait for readiness
435
+
436
+ Readiness condition: **every item with `release_binding: <version>` is at
437
+ `stage: done`.** This spans active done items and archived stubs alike (both carry the
438
+ binding); `work-view --release <version>` finds both tiers.
439
+
440
+ Archived stubs are `stage: done` by construction and their bodies are pruned — readiness checks only
441
+ their `stage`, never body presence. A missing stub body NEVER counts as pending and never blocks a
442
+ release.
443
+
444
+ ```bash
445
+ # Items still active
446
+ .work/bin/work-view --release <version> --paths | while read p; do
447
+ s=$(grep -m1 '^stage:' "$p" | awk '{print $2}')
448
+ [[ "$s" != "done" ]] && echo "PENDING: $p ($s)"
449
+ done
450
+ ```
451
+
452
+ If pending items remain, halt with the list and:
453
+
454
+ > "Release `<version>` is not ready. The following items are still in flight:
455
+ > [list]. Drive these to done (via `/agile-workflow:implement`,
456
+ > `/agile-workflow:review`, etc.), then re-run `/agile-workflow:release-deploy
457
+ > <version>` to resume."
458
+
459
+ `release-deploy` is idempotent — the user runs it again later and you pick up
460
+ where you left off.
461
+
462
+ ### Phase 5.5: Draft the changelog entry
463
+
464
+ Before shipping, draft a CHANGELOG.md entry from the bound items + their commits:
465
+
466
+ ```bash
467
+ # Commits since the last tag — all commits when no tag exists (an empty $prior drops the
468
+ # range; the old `$(git describe ...)..HEAD || git log` form never fired its fallback,
469
+ # because `..HEAD` with an empty left side is a valid empty range that exits 0). The
470
+ # `|| true` keeps the no-tag assignment from aborting under `set -e`.
471
+ prior=$(git describe --tags --abbrev=0 2>/dev/null || true)
472
+ git log --oneline ${prior:+${prior}..}HEAD
473
+ ```
474
+
475
+ Group changes:
476
+ - **Features** — new capabilities (look for items with no specialized tag)
477
+ - **Fixes** — bug fixes (items with `tags: [bug]` or from `/agile-workflow:fix`)
478
+ - **Refactor** — items with `tags: [refactor]`
479
+ - **Performance** — items with `tags: [perf]`
480
+ - **Security** — items with `gate_origin: security`
481
+ - **Documentation** — items with `gate_origin: docs` or `tags: [documentation]`
482
+ - **Internal** — anything else worth noting; otherwise omit
483
+
484
+ Use concise bullets — one per logical change, not per commit. Omit noise:
485
+ version-bump commits, merge commits, typo/formatting-only commits.
486
+
487
+ Format the entry header as `## v<version>` and prepend to CHANGELOG.md (preserve
488
+ existing content). If CHANGELOG.md doesn't exist, create it with this entry.
489
+
490
+ Show the user the drafted changelog and ask via structured question tool: "Does this look
491
+ correct? Reply yes to proceed with shipping, or provide edits." Apply edits and
492
+ re-confirm before shipping.
493
+
494
+ ### Phase 6: Ship per release mapping
495
+
496
+ When all bound items are `done` AND the changelog entry is confirmed:
497
+
498
+ #### Mapping: `tag-based`
499
+
500
+ ```bash
501
+ # 1. Find the version source (package.json / Cargo.toml / pyproject.toml / version.txt / etc.)
502
+ # 2. Update version
503
+ # 3. Find the project's release script (scripts/release*, package.json scripts,
504
+ # Makefile target, etc.)
505
+ # 4. Run the project's release script (which typically tags + pushes + triggers CI)
506
+ # Or, manually: git tag <version> && git push origin <version>
507
+ ```
508
+
509
+ #### Mapping: `branch-held`
510
+
511
+ ```bash
512
+ # Items implement on feature branches; release-deploy merges bound PRs
513
+ gh pr list --search "<id>" --json number,headRefName # for each bound item
514
+ gh pr merge <pr-number> --squash --delete-branch
515
+ # Then trigger the project's release CI as usual
516
+ ```
517
+
518
+ #### Mapping: `release-branch`
519
+
520
+ ```bash
521
+ # Long-running 'next' branch holds work; cut by merging next -> main -> tag
522
+ git checkout main && git merge next --ff-only
523
+ git tag <version> && git push origin main <version>
524
+ ```
525
+
526
+ #### Mapping: `none`
527
+
528
+ No tag, branch, merge, push, or version bump happens inside release-deploy.
529
+ Proceed directly to archiving after readiness and changelog confirmation.
530
+ Record the external publishing mechanism in the release summary.
531
+
532
+ If the mapping requires user action (CI credentials, manual confirmation), pause
533
+ and prompt.
534
+
535
+ ### Phase 7: Collapse into one summary; dispose bodies per retention
536
+
537
+ Both retention modes produce the same single summary doc; they differ only in step 4's body
538
+ disposition (the Phase 1 `terminal-tier retention` read; SPEC §Terminal-tier retention).
539
+
540
+ Under `delete-refs` (default): bound items do **not** move to `.work/releases/<version>/` as
541
+ bodies. Collapse them into a single summary doc and `git rm` their bodies — git history retains
542
+ the full content. Terminal prose never persists on disk (it carries zero design authority; see
543
+ the "Zero Design Authority" convention).
544
+
545
+ Under `retain-bodies` (the legacy opt-out): the summary doc and shipped-items table are still
546
+ produced, but full bodies stay on disk — kept under `.work/archive/` and
547
+ `.work/releases/<version>/` — and nothing is pruned.
548
+
549
+ 1. Collect every item with `release_binding: <version>` — active done items AND archived stubs
550
+ (`work-view --release <version>` spans both tiers):
551
+
552
+ ```bash
553
+ .work/bin/work-view --release <version> --paths
554
+ ```
555
+
556
+ 2. Resolve each item's git ref (the commit where its full body can be recovered, at the item's
557
+ former active path) and its `archived_atop`:
558
+ - archived item → reuse its `git_ref:` and `archived_atop:` frontmatter fields (under
559
+ `retain-bodies` the body is also still on disk; if no `git_ref` was stamped, record `—`).
560
+ - active done item → `git_ref=$(git rev-parse --short HEAD)` (the body is present at HEAD,
561
+ before Phase 7's disposition commit). It has no `archived_atop` (it was bound directly,
562
+ never archived); record `—` in that column.
563
+
564
+ 3. Turn the release file into the single summary doc — move it and append a shipped-items table:
565
+
566
+ ```bash
567
+ mkdir -p .work/releases/<version>
568
+ git mv .work/active/release-<version>.md .work/releases/<version>/release-<version>.md
569
+ ```
570
+
571
+ Append to its body:
572
+
573
+ ```markdown
574
+ ## Shipped items
575
+
576
+ Bodies live in git history (delete-refs) or on disk (retain-bodies) — `git show
577
+ <git ref>:<former active path>` recovers any pruned body.
578
+
579
+ | id | title | kind | archived_atop | git ref |
580
+ |----|-------|------|---------------|---------|
581
+ | <id> | <title> | <kind> | <release \| pre-release \| —> | <git_ref> |
582
+ ```
583
+
584
+ The `archived_atop` column records the baseline each late-bound archived stub was done atop
585
+ (`—` for active items bound directly). This is the durable trace of the late-binding query.
586
+
587
+ 4. Dispose of each bound item body per retention (never the release summary):
588
+
589
+ Under `delete-refs` (default) — prune:
590
+
591
+ ```bash
592
+ git rm .work/active/<kind>s/<id>.md # active done items
593
+ git rm .work/archive/<id>.md # archived stubs
594
+ ```
595
+
596
+ The release folder ends holding exactly one file: `release-<version>.md`.
597
+
598
+ Under `retain-bodies` — keep full bodies on disk; nothing is pruned:
599
+
600
+ ```bash
601
+ git mv .work/active/<kind>s/<id>.md .work/releases/<version>/ # active done items
602
+ # late-bound archived items keep their full bodies in place under .work/archive/
603
+ ```
604
+
605
+ The release folder ends holding the summary plus the directly-bound items' bodies; the
606
+ shipped-items table is still appended (the `archived_atop` provenance applies under both
607
+ modes; the git-ref column records what step 2 resolved).
608
+
609
+ ### Phase 8: Advance to released
610
+
611
+ Edit the release summary's frontmatter: `stage: quality-gate → released`.
612
+
613
+ Ensure its body records (alongside the Phase 7 shipped-items table):
614
+ - Date shipped
615
+ - Mapping used
616
+ - Total items shipped
617
+ - Gate finding totals
618
+ - The external publishing mechanism (for `none` mapping)
619
+
620
+ ### Phase 9: Commit
621
+
622
+ Phase 7's body disposition (`git rm`s or `git mv`s) is already staged; stage the summary and the
623
+ changelog and commit:
624
+
625
+ ```bash
626
+ git add .work/releases/<version>/ CHANGELOG.md <version-file>
627
+ git commit -m "release-deploy: <version> shipped (<N> items)"
628
+ ```
629
+
630
+ The commit captures both the one summary doc and the body disposition. For tag-based mappings,
631
+ the project's release script handles its own commits. Don't double-commit.
632
+
633
+ ## Output
634
+
635
+ In conversation:
636
+ - **Release**: `<version>` shipped
637
+ - **Items**: count, listed
638
+ - **Gates run**: list with finding counts per gate
639
+ - **Mapping**: tag-based / branch-held / release-branch / none
640
+ - **Next**: bound items collapsed into `.work/releases/<version>/release-<version>.md`; full
641
+ bodies in git history (`delete-refs`) or kept on disk (`retain-bodies`)
642
+
643
+ If the run halted at readiness (pending items), output the pending list and the
644
+ re-run instruction.
645
+
646
+ ## Guardrails
647
+
648
+ - `release-deploy` is idempotent. Safe to re-run at any stage. The release file's
649
+ frontmatter is the state.
650
+ - Gates produce items, not blocking errors. The release ships when all items
651
+ (bound + gate-produced) are `done`.
652
+ - Don't bypass gates. If `gates_for_release` lists 5 gates, run all 5. If a gate
653
+ fails to produce items, that's a finding (or "no new findings"), not a reason
654
+ to skip.
655
+ - Re-gate late-bound archived stubs. Release gates run over every bound non-release item, including
656
+ stubs that were archived before this release claimed them. A gate that needs body text hydrates it
657
+ from the stub's `git_ref`; a missing on-disk stub body must never block a release.
658
+ - Don't ship if any bound item is not `done`. Halt and surface the pending list. (Archived stubs are
659
+ `done` by construction; readiness checks their `stage`, not body presence.)
660
+ - Tag-based and release-branch mappings rely on the project's existing release
661
+ script. branch-held requires `gh` CLI for PR merges. none performs no
662
+ publishing action inside release-deploy.
663
+ - The release file moves to `.work/releases/<version>/` (becoming the single summary doc) ONLY when the
664
+ release is actually shipped. Until then it stays in `.work/active/`. Bound item bodies move there
665
+ only under `retain-bodies`; under `delete-refs` (default) they are pruned and live in git history.
666
+ - Stage transitions: `planned → quality-gate` happens at bind. `quality-gate →
667
+ released` happens at ship. Don't pre-populate.
@@ -0,0 +1,6 @@
1
+ interface:
2
+ display_name: "Release Deploy"
3
+ short_description: "Use Release Deploy for repo workflows"
4
+ default_prompt: "Use $release-deploy for this repository."
5
+ policy:
6
+ allow_implicit_invocation: false