@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
package/docs/SPEC.md ADDED
@@ -0,0 +1,855 @@
1
+ # SPEC: agile-workflow
2
+
3
+ Technical contracts for the agile-workflow plugin: manifest, frontmatter,
4
+ file layouts, distribution, scripts, hooks, tooling. Implementation must
5
+ conform to this spec; deviations are bugs.
6
+
7
+ ## Plugin manifest
8
+
9
+ Located at `plugins/agile-workflow/.claude-plugin/plugin.json`:
10
+
11
+ ```json
12
+ {
13
+ "name": "agile-workflow",
14
+ "description": "Markdown-based work-tracking substrate for AI-driven projects. Items as files in .work/, late-binding releases, gates that produce items, goal-backed autopilot queue runner. See docs/VISION.md.",
15
+ "version": "0.10.0",
16
+ "author": { "name": "nklisch" },
17
+ "repository": "https://github.com/nklisch/skills",
18
+ "license": "MIT"
19
+ }
20
+ ```
21
+
22
+ Auto-discovery picks up:
23
+ - `commands/*.md` — slash commands
24
+ - `skills/<name>/SKILL.md` — skills
25
+ - `hooks/hooks.json` — hook configurations
26
+ - `scripts/` — utility scripts (referenced via `${CLAUDE_PLUGIN_ROOT}`)
27
+
28
+ For hook script paths, use the portable plugin-root form
29
+ `${PLUGIN_ROOT:-${CLAUDE_PLUGIN_ROOT}}`. Codex sets `PLUGIN_ROOT` /
30
+ `PLUGIN_DATA` and also exports Claude-compatible variables; Claude sets
31
+ `CLAUDE_PLUGIN_ROOT` / `CLAUDE_PLUGIN_DATA`.
32
+
33
+ ## Frontmatter contract
34
+
35
+ Every item file in `.work/active/` and `.work/releases/` has YAML
36
+ frontmatter at the top, validated by every skill that reads or writes items.
37
+
38
+ ```yaml
39
+ ---
40
+ id: <slug> # required, unique within active tier
41
+ kind: epic|feature|story|release # required
42
+ stage: <see stage flow> # required
43
+ tags: [<tag>, ...] # required (may be [])
44
+ parent: <slug>|null # required (null for top-level)
45
+ depends_on: [<slug>, ...] # required (may be [])
46
+ release_binding: <version>|null # required
47
+ gate_origin: <gate-name>|null # required (null unless gate-produced)
48
+ research_refs: [<slug>...] # optional (research artifacts this item tracks/consumes)
49
+ research_origin: <slug>|null # optional (research artifact that spawned this item)
50
+ scan_origin: <slug>|null # optional (scan campaign that spawned this item)
51
+ created: YYYY-MM-DD # required
52
+ updated: YYYY-MM-DD # required, auto-bumped by PostToolUse hook
53
+ ---
54
+ ```
55
+
56
+ ### Field semantics
57
+
58
+ | Field | Type | Notes |
59
+ |---|---|---|
60
+ | `id` | slug string | kebab-case. Unique within `.work/active/`. Child slugs qualify with parent prefix (e.g., `playout-architecture-phase1`, not `phase1`). |
61
+ | `kind` | enum | `epic`, `feature`, `story`, or `release`. `task` items have no files — they're checklist lines in a parent's body. |
62
+ | `stage` | enum | Per-kind valid values; see Stage flow below. |
63
+ | `tags` | array of slug strings | Routing tags from the project's taxonomy in `.work/CONVENTIONS.md`. May be empty. Kebab-case. |
64
+ | `parent` | slug or null | **Hierarchy.** `null` for top-level. Points to a parent item's `id`. |
65
+ | `depends_on` | array of slugs | **Sequencing.** Items this cannot start until all listed are at `stage: done`. May be empty. Distinct from `parent`. |
66
+ | `release_binding` | version string or null | Late-binding. `null` until the user binds. Format matches the release file's version (e.g., `v1.2.0`). |
67
+ | `gate_origin` | gate name or null | `null` for user-scoped items. One of `security`, `tests`, `cruft`, `docs`, `patterns`, `refactor` when produced by a gate (`refactor` is the opt-in gate's value). |
68
+ | `research_refs` | array of slug strings | **Optional; defaults to `[]`.** The research artifacts (`.research/` slugs or handles) this work item tracks or consumes — the Arrow 1 coordination link. Missing → `[]`. Query: `work-view --research-refs <slug>` (membership). See `plugins/agentic-research/docs/HANDOFF.md` for the cross-tier contract. |
69
+ | `research_origin` | slug or null | **Optional; defaults to `null`.** The research artifact that spawned this work item — the Arrow 2 grounding link. Mirrors `gate_origin`. Missing/empty/`"null"` → `null`. Query: `work-view --research-origin <slug>` (or `null`). See `plugins/agentic-research/docs/HANDOFF.md`. |
70
+ | `scan_origin` | slug or null | **Optional; defaults to `null`.** The scan campaign (`scan-<goal>`) that produced this work item — the `deep-code-scan` linkage. Mirrors `research_origin`. Missing/empty/`"null"` → `null`. Query: `work-view --scan-origin <slug>` (or `null`). |
71
+ | `created` | ISO date | `YYYY-MM-DD`. Set on creation; never modified. FIFO tie-break in autopilot. |
72
+ | `updated` | ISO date | `YYYY-MM-DD`. Auto-bumped by PostToolUse hook on every item edit. |
73
+
74
+ ### Stage flow per kind
75
+
76
+ ```
77
+ epic: drafting → implementing → review → done
78
+ feature: drafting → implementing → review → done
79
+ story: implementing → review → done (often skips drafting)
80
+ release: planned → quality-gate → released
81
+ task: [ ] → [x] (checklist line in parent body)
82
+ ```
83
+
84
+ Stages advance only when work completes. No pre-population.
85
+
86
+ ### Backlog item shape
87
+
88
+ Items in `.work/backlog/` are leaner — `kind` and `stage` are unknown until
89
+ a scoping pass fixes them.
90
+
91
+ ```yaml
92
+ ---
93
+ id: <slug>
94
+ created: YYYY-MM-DD
95
+ updated: YYYY-MM-DD # optional; written by park (== created), bumped by the hook on edit
96
+ tags: [<tag>, ...]
97
+ ---
98
+ ```
99
+
100
+ `updated` is **optional** on the backlog contract (it is not in
101
+ `BACKLOG_REQUIRED`). `park` writes it equal to `created` at creation, and the
102
+ PostToolUse hook bumps it on every edit. When absent (e.g. a legacy item parked
103
+ before this contract), consumers treat the last-touched date as `created`. This
104
+ gives a backlog grooming/staleness query a reliable last-touched signal without
105
+ requiring the field — see `backlog_staleness_days` under §`.work/CONVENTIONS.md`.
106
+
107
+ Body: an unscoped capture sized to the input. Simple ideas can be one
108
+ paragraph; richer context notes or roadmap-style multi-arc thoughts can keep
109
+ bullets, references, and current-situation context. Kind, stage, parent,
110
+ dependencies, release binding, and decomposition remain unknown until `scope`
111
+ promotes the item.
112
+
113
+ ## Substrate file contracts
114
+
115
+ ### `.work/CONVENTIONS.md`
116
+
117
+ Project-owned file written by `convert` interactively. Every operational
118
+ skill reads this at session start (via the SessionStart hook or directly).
119
+
120
+ ```markdown
121
+ # Project Conventions
122
+
123
+ ## Release mapping
124
+ <branch-held | tag-based | release-branch | none>
125
+
126
+ ## Tag taxonomy
127
+ <list of tags this project uses, with one-line semantics>
128
+ - security auth, validation, secrets, supply chain
129
+ - perf throughput, latency, memory — routes to perf-design
130
+ - refactor behavior-preserving structural change ONLY — fails the black-box test (any observable behavior change for callers) means NOT a refactor — routes to refactor-design
131
+ - prose no-code-surface deliverable (docs, conventions, copy) — routes to prose-author (lean authoring lane: brief-as-design, inline implement) # optional — omit if `prose` already means something else in your project (token name may change before v1.0)
132
+ - research grounded research engagement — an input, not a shippable — routes cross-plugin to agentic-research:research-orchestrator; carries a research_dials: block (the commissioning subset of the registration), does not bind to a release, gates run inline (only when the agentic-research plugin is installed)
133
+
134
+ ## Slug conventions
135
+ <format and prefix rules>
136
+
137
+ ## Stage overrides
138
+ <only if the project deviates from the master>
139
+
140
+ ## Terminal-tier retention
141
+ <delete-refs | retain-bodies>
142
+
143
+ ## Gate config
144
+ gates_for_release: [security, tests, cruft, docs, patterns]
145
+ gate_finding_routing:
146
+ critical: implementing
147
+ high: implementing
148
+ medium: drafting
149
+ low: backlog
150
+ info: skip
151
+ gate_refactor_scan_library_roots:
152
+ - .agents/skills
153
+ - .claude/skills
154
+ binding_guard: warn
155
+ epic_cohesion: phased
156
+ backlog_staleness_days: 90
157
+ ```
158
+
159
+ The default `gates_for_release` order is fixed: **security → tests → cruft
160
+ → docs → patterns**. Override only if the project has a justified reason.
161
+
162
+ **`gate-refactor` is an opt-in gate** — not in the default list. Add it when your project has
163
+ scan-rule libraries installed under `gate_refactor_scan_library_roots` (defaults:
164
+ `{project}/.agents/skills/scan-*/SKILL.md`, then
165
+ `{project}/.claude/skills/scan-*/SKILL.md`). The gate discovers and loads all libraries it finds,
166
+ then checks the release bundle's changed files against every rule. With no libraries installed it
167
+ logs a graceful skip and continues — not an error. Example opt-in:
168
+
169
+ ```yaml
170
+ gates_for_release: [security, tests, cruft, docs, patterns, refactor]
171
+ ```
172
+
173
+ **`gate_finding_routing`** controls how item-producing gates place findings after
174
+ each gate normalizes its local vocabulary. Defaults preserve the built-in routing:
175
+ `critical` and `high` create active stories at `stage: implementing`, `medium`
176
+ creates active stories at `stage: drafting`, `low` writes a backlog file, and
177
+ `info` is skipped. Valid target values are `implementing`, `drafting`, `backlog`,
178
+ and `skip`; missing keys fall back to defaults. `skip` means no work item is
179
+ emitted, but the gate still reports skipped counts in its conversational output
180
+ and in any durable gate-run record it already writes. Gate vocabularies stay
181
+ gate-local: security and tests normalize `Critical|High|Medium|Low`, docs
182
+ normalizes `High|Medium`, and cruft/refactor normalize `High|Medium|Low`.
183
+
184
+ **`gate_refactor_scan_library_roots`** controls the parent directories
185
+ `gate-refactor` searches for scan-rule libraries. Defaults preserve current
186
+ behavior: `.agents/skills` first, `.claude/skills` second. Relative paths resolve
187
+ from the project/substrate root; absolute paths are allowed. The gate still only
188
+ loads libraries matching `scan-*/SKILL.md` below each configured root. Duplicate
189
+ libraries are deduped by derived library tag in configured root order, so the
190
+ first discovered library wins. Roots outside the project tree expand the trust
191
+ boundary because the gate loads instructions and reference files from those
192
+ locations.
193
+
194
+ **`binding_guard`** (default **`warn`** when absent) controls the Phase 3.5 binding-consistency check
195
+ in `release-deploy`. Values: `warn` | `halt` | `off`. `warn` runs all three checks and records any
196
+ finding as a durable warning in the release body (replace-or-skip, not appended), then continues.
197
+ `halt` stops the release on any acted-on finding (for projects that hold the no-cross-version-drift
198
+ invariant). `off` skips the checks entirely (short-circuits before any walk).
199
+
200
+ **`epic_cohesion`** (default **`phased`** when absent) governs the severity of an *unbound child of
201
+ a bound parent* (an INCOMPLETE finding). `phased` treats INCOMPLETE entries as informational —
202
+ listed in the warn report, never counting toward a halt (an epic may ship across releases). `total`
203
+ treats them as mismatches acted on per `binding_guard`, like CONFLICTs (the project holds "epics
204
+ ship whole"). CONFLICTs (a child bound to a *different* version than its bound parent, or a done
205
+ parent unbound while its children are bound) always follow `binding_guard` regardless of this dial.
206
+
207
+ **`backlog_staleness_days`** (integer; **absent ⇒ feature inert**) is the age threshold for the
208
+ opt-in backlog staleness query `work-view --stale`. When set, `--stale` lists `.work/backlog/`
209
+ items whose last-touched date — `updated` if present, else `created` — is more than this many days
210
+ before today (local time). When the key is absent, `--stale` surfaces nothing and prints a
211
+ one-line "no `backlog_staleness_days` configured" notice (exit 0) — a project that does not opt in
212
+ sees no behavior change. This is the query surface a backlog grooming capability consumes; it does
213
+ not auto-prune anything.
214
+
215
+ `terminal-tier retention` (default **`delete-refs`**) is **one merged convention** covering the
216
+ whole terminal lifecycle — archival, late-binding, and release collapse — not just on-disk byte
217
+ retention. With `delete-refs`:
218
+
219
+ 1. **Archive (decoupled from release).** A `done` item with no `release_binding` becomes a
220
+ **bodyless stub** (frontmatter + `# Title` + `git_ref:` + `archived_atop:`). Its body is pruned to
221
+ git, so terminal prose (which carries zero design authority) cannot leak to future agents.
222
+ `archived_atop` records the release baseline the item was done atop — stamped once, immutable.
223
+ 2. **Late-bind unbound archived stubs.** A release pulls in **all unbound archived stubs**
224
+ (`release_binding: null`), confirms the set with the user, and sets their `release_binding`. Each
225
+ stub's `archived_atop` records the baseline it was done atop and is kept as provenance (it surfaces
226
+ in the release summary), NOT used as the gather filter — filtering by strict `archived_atop ==
227
+ prior tag` would strand a stub forever whenever a release is skipped. Late-bound archived stubs
228
+ are re-gated during the release. Gates that need the item body must hydrate it from the stub's
229
+ `git_ref`; a pruned stub body is a lookup requirement, not a reason to skip the item.
230
+ 3. **One-summary release.** All bound items (active done + late-bound stubs) collapse into a single
231
+ `.work/releases/<version>/release-<version>.md` table (id, title, kind, `archived_atop`, git ref).
232
+ No per-item placement; full bodies live only in git history.
233
+
234
+ `retain-bodies` is the legacy opt-out (full bodies kept under `.work/archive/` and
235
+ `.work/releases/<version>/`); it keeps the same `archived_atop`/late-binding semantics, just without
236
+ pruning bodies. A repo that already carries a bespoke "Done-item archival" convention describing this
237
+ model should be **converged** into this one merged convention, not left as a duplicate (see
238
+ `convert`).
239
+
240
+ #### Archive stub shape
241
+
242
+ A `done` item with no `release_binding` and no active parent is archived as a bodyless stub. The
243
+ stub is frontmatter + the first `# <Title>` line only; the body is pruned to git history. Its
244
+ frontmatter carries two extra fields beyond the standard contract:
245
+
246
+ - `archived_atop: <release | pre-release>` — the release baseline the item was done atop (see
247
+ computation below). Stamped **once** at archival; immutable thereafter.
248
+ - `git_ref: <sha>` — the commit where the full body lives. Recover it with
249
+ `git show <git_ref>:<path>`.
250
+
251
+ ```yaml
252
+ ---
253
+ id: <id>
254
+ kind: <kind>
255
+ stage: done
256
+ tags: [...]
257
+ parent: null
258
+ depends_on: []
259
+ release_binding: null # null until a release late-binds it
260
+ archived_atop: <release | pre-release>
261
+ git_ref: <sha>
262
+ created: <orig>
263
+ updated: <today>
264
+ ---
265
+
266
+ # <Title>
267
+ ```
268
+
269
+ #### `archived_atop` computation
270
+
271
+ `archived_atop` is the **latest released version at archival time**, computed deterministically:
272
+
273
+ 1. The newest git tag matching the project's release tag shape (e.g. `git describe --tags
274
+ --abbrev=0`), OR
275
+ 2. the newest `.work/releases/<version>/` summary, when the project does not tag releases.
276
+ 3. If neither exists yet — no release has ever shipped — use the `pre-release` sentinel.
277
+
278
+ Stamp it once when the stub is written and never rewrite it (it is the immutable baseline recorded as
279
+ provenance). Idempotent re-archival preserves the existing `archived_atop`. The
280
+ `/agile-workflow:release-deploy` bind phase gathers **all unbound archived stubs** (not only those
281
+ whose `archived_atop` matches the prior tag) and records each stub's `archived_atop` in the release
282
+ summary as provenance.
283
+
284
+ ### AGENTS.md section
285
+
286
+ `convert` appends or replaces, idempotently, a section in the project's
287
+ canonical AGENTS target delimited by HTML comment markers. It detects
288
+ `AGENTS.md`, `.agents/AGENTS.md`, and `.claude/AGENTS.md`, preferring root
289
+ `AGENTS.md` when present and otherwise creating a root symlink/shim so Codex has
290
+ a root-readable entrypoint.
291
+
292
+ ```markdown
293
+ <!-- agile-workflow:start -->
294
+ ## Agile-Workflow Substrate
295
+
296
+ Work tracked in `.work/` as markdown items with YAML frontmatter
297
+ (`kind, stage, tags, parent, depends_on, release_binding, research_refs, research_origin`).
298
+ Layout: `.work/active/{epics,features,stories}/`, `.work/backlog/`,
299
+ `.work/releases/<version>/`, `.work/archive/`.
300
+
301
+ [slim dense pointers + work-view query patterns + a MANDATORY
302
+ "read `.agents/rules/*.md` before designing/implementing/reviewing"
303
+ read-directive — see ARCHITECTURE.md]
304
+ <!-- agile-workflow:end -->
305
+ ```
306
+
307
+ The managed section is **slim**: substrate orientation, `work-view` query
308
+ patterns, and grep-able pointers to the canonical rules file
309
+ `.agents/rules/agile-workflow.md` and the `patterns` skill, plus a mandatory
310
+ read-directive. `convert --update` replaces everything between the markers
311
+ without touching the rest of the selected AGENTS target.
312
+
313
+ ### `.agents/rules/` agent rules
314
+
315
+ Dense behavioral rules (tag semantics, test integrity, advisory review, entry
316
+ points) live in the plugin-managed `.agents/rules/agile-workflow.md`, delimited
317
+ by `<!-- agile-workflow:rules:start/end -->` markers. The agile-workflow hook
318
+ force-loads every `.agents/rules/*.md` into agent context (see Hook contracts).
319
+ `convert` writes and verifies `.agents/rules/agile-workflow.md` BEFORE slimming
320
+ the AGENTS section, so the managed-section overwrite can never drop dense rule
321
+ content.
322
+
323
+ User-owned and migrated legacy rule prose lives in separate user-owned
324
+ `.agents/rules/<name>.md` files (e.g. `project.md`), never inside the plugin
325
+ `agile-workflow:rules` markers. When an older repo has `.claude/rules/*.md`,
326
+ `convert`'s content-integrity gate parses each file into Markdown-aware blocks,
327
+ routes structural patterns to `.agents/skills/patterns/` and rule prose to
328
+ `.agents/rules/<name>.md`, verifies every block landed at its canonical home,
329
+ and only then replaces the legacy path with a shim. `.claude/rules/patterns.md`
330
+ is not a supported canonical rules file; its content migrates the same way.
331
+
332
+ ### CLAUDE.md compatibility
333
+
334
+ Claude instruction files are not canonical. `convert` detects `CLAUDE.md`,
335
+ `.claude/CLAUDE.md`, and `.agents/CLAUDE.md`, then preserves Claude Code
336
+ compatibility by making each one a symlink to the selected AGENTS target where
337
+ possible. If symlinks are not available, it writes a short shim telling Claude
338
+ Code to read `AGENTS.md`.
339
+
340
+ When migrating an existing regular `CLAUDE.md`, `convert` imports non-duplicate
341
+ content into `AGENTS.md` before replacing it with the symlink or shim.
342
+
343
+ When migrating an older `.claude/rules/*.md`, `convert` follows the same
344
+ preservation rule via the content-integrity gate: route each block to its
345
+ canonical home (`.agents/skills/patterns/` for structural patterns,
346
+ `.agents/rules/<name>.md` for rule prose), verify every block landed, then leave
347
+ only a compatibility shim at the legacy path.
348
+
349
+ ## File and directory layout
350
+
351
+ ### Plugin source layout
352
+
353
+ ```
354
+ plugins/agile-workflow/
355
+ ├── .claude-plugin/
356
+ │ └── plugin.json
357
+ ├── .codex-plugin/
358
+ │ └── plugin.json
359
+ ├── docs/
360
+ │ ├── VISION.md
361
+ │ ├── SPEC.md
362
+ │ ├── ARCHITECTURE.md
363
+ │ ├── PRINCIPLES.md
364
+ │ └── MIGRATION.md
365
+ ├── skills/<skill-name>/
366
+ │ ├── SKILL.md
367
+ │ └── references/<topic>.md
368
+ ├── hooks/
369
+ │ ├── hooks.json
370
+ │ └── scripts/
371
+ │ ├── prompt-context.py
372
+ │ └── substrate-maintainer.py
373
+ ├── scripts/
374
+ │ ├── install-work-view.sh
375
+ │ ├── work-view.sh
376
+ │ └── work-board.sh
377
+ ├── work-view/ # Rust workspace: compiled work-view binary + board host
378
+ │ ├── crates/
379
+ │ │ ├── core/ # work-view-core: parse, model, index, graph, filter
380
+ │ │ └── cli/ # work-view binary (CLI + board host)
381
+ │ │ ├── src/board/ # board subcommand: server, feed, assets + embedded assets/
382
+ │ │ └── .work-view-version # plugin-version stamp (lockstep with plugin.json)
383
+ │ └── dist/<target-triple>/work-view # prebuilt per-platform binaries
384
+ ├── CHANGELOG.md
385
+ └── README.md
386
+ ```
387
+
388
+ ### Bootstrapped project layout
389
+
390
+ After `convert` runs in a project repo:
391
+
392
+ ```
393
+ <project-root>/
394
+ ├── .work/
395
+ │ ├── active/{epics,features,stories}/<id>.md
396
+ │ ├── backlog/<id>.md
397
+ │ ├── releases/<version>/release-<version>.md # one summary doc (delete-refs)
398
+ │ ├── archive/<id>.md # bodyless ref stubs w/ archived_atop + git_ref (delete-refs)
399
+ │ ├── bin/work-view
400
+ │ └── CONVENTIONS.md
401
+ ├── AGENTS.md
402
+ ├── CLAUDE.md -> AGENTS.md
403
+ ├── docs/
404
+ │ ├── VISION.md
405
+ │ ├── SPEC.md
406
+ │ └── ARCHITECTURE.md
407
+ ```
408
+
409
+ ## Distribution
410
+
411
+ The plugin distributes through three equal channels:
412
+
413
+ - Claude Code via the repo-root `.claude-plugin/marketplace.json` and
414
+ `plugins/agile-workflow/.claude-plugin/plugin.json`.
415
+ - OpenAI Codex via the same marketplace index and
416
+ `plugins/agile-workflow/.codex-plugin/plugin.json`.
417
+ - Pi via package metadata in the plugin root, with the same `skills/` directory
418
+ plus Pi-native extensions or prompt templates where they improve substrate
419
+ ergonomics.
420
+
421
+ The three channel metadata files stay in lockstep on name, version,
422
+ description, repository, and license. Pi-specific runtime surfaces wrap the same
423
+ `.work/` substrate; they do not fork the workflow model.
424
+
425
+ **Channel parity posture:** when agile-workflow adds or changes Claude/Codex hook
426
+ behavior, Pi must receive a native extension adapter in the same change unless a
427
+ host capability is explicitly impossible. The adapter should call the shared
428
+ hook scripts or read the same generated sources rather than reimplementing rules
429
+ in a parallel language. Drift is guarded by
430
+ `plugins/agile-workflow/scripts/tests/channel-parity.test.sh`.
431
+
432
+ ## Version strategy
433
+
434
+ - Plugin version lives in `plugins/agile-workflow/.claude-plugin/plugin.json`
435
+ - Bumped via existing `./scripts/bump-version.sh agile-workflow <major|minor|patch>`
436
+ - Bump rules:
437
+ - **patch** — new skill, bug fix, or minor update to an existing skill
438
+ - **minor** — significant new capability or breaking change to a skill's workflow
439
+ - **major** — plugin restructure, frontmatter contract change, or
440
+ backwards-incompatible substrate evolution
441
+ - v0.1.0 is the initial release. Pre-1.0 signals that frontmatter and stage
442
+ shapes may shift during the first real-project shakedown.
443
+ - Promote to v1.0.0 when the substrate has carried 2+ projects through a
444
+ complete release cycle without contract changes.
445
+
446
+ ### work-view `--version` lockstep
447
+
448
+ Both work-view implementations report the plugin semver so a deployed copy can
449
+ be checked against the plugin with a single string compare:
450
+
451
+ - The Rust CLI and the bash fallback (`scripts/work-view.sh`) each accept
452
+ `--version` / `-V` and print `work-view <semver>\n` (exit 0). The reported
453
+ `<semver>` is the agile-workflow **plugin** version from `plugin.json`, not the
454
+ crate version. Output is byte-identical across both implementations (a parity
455
+ test enforces this). The bash `--version` answer is emitted by a POSIX-safe
456
+ prelude that runs before the Bash-4 guard, so it works even on macOS system
457
+ bash 3.2.
458
+ - `plugin.json` is the single source of truth for the version.
459
+ `bump-version.sh` projects it, in lockstep, into the Rust stamp file
460
+ `work-view/crates/cli/.work-view-version` (compiled in via `include_str!`, no
461
+ trailing newline) and the `WORK_VIEW_VERSION` literal in
462
+ `scripts/work-view.sh`. A `cargo test` assertion fails loudly if the stamp
463
+ drifts from `plugin.json`.
464
+ - The four prebuilt `dist/<triple>/work-view` binaries are compiled by CI **from
465
+ the stamped source**, so they must be rebuilt on the **post-bump** commit:
466
+ after `bump-version.sh` commits and pushes the bump, trigger the "Build
467
+ work-view binaries" workflow (`workflow_dispatch`, `commit_binaries=true`)
468
+ against the bumped commit so the shipped binaries self-report the new semver.
469
+ Rebuilding before the bump would compile the old stamp and ship
470
+ version-mismatched binaries. The installer rejects supported-platform
471
+ prebuilts whose `--version` mismatches the plugin instead of silently
472
+ downgrading to the Bash fallback, so the CI binary refresh must land before a
473
+ bumped plugin is considered publishable.
474
+
475
+ ## work-view binary
476
+
477
+ `convert` installs `work-view` into `.work/bin/work-view` via
478
+ `plugins/agile-workflow/scripts/install-work-view.sh`. The project-side tracked
479
+ entrypoint is the installed `work-view` artifact: a version-verified prebuilt
480
+ Rust binary on supported platforms, or the version-stamped Bash fallback only on
481
+ unsupported platforms. The entrypoint is kept fresh by the session hook
482
+ self-heal step, with convert using the same installer as a backstop. It is
483
+ git-tracked, not gitignored, and its `--version` stamp is compared to the plugin
484
+ version when hook or convert freshness checks run.
485
+
486
+ Prebuilt Rust binaries live under
487
+ `plugins/agile-workflow/work-view/dist/<target-triple>/work-view` and are the
488
+ standard install path. The compiled binary provides both the query filters and
489
+ `work-view board`. The Bash fallback (`scripts/work-view.sh`) is a degraded CLI
490
+ fallback for unsupported platforms only; it does not support the interactive
491
+ board. Supported prebuilt target triples:
492
+
493
+ | Triple | Platform |
494
+ |---|---|
495
+ | `x86_64-unknown-linux-musl` | Linux x86_64 (static) |
496
+ | `aarch64-unknown-linux-musl` | Linux aarch64 / ARM64 (static) |
497
+ | `x86_64-apple-darwin` | macOS Intel |
498
+ | `aarch64-apple-darwin` | macOS Apple Silicon (M1/M2/M3) |
499
+
500
+ Prebuilt binaries are produced by `.github/workflows/build-work-view.yml` and
501
+ committed to `dist/` via the manual refresh job after `bump-version.sh` commits
502
+ and pushes the version bump, so CI builds from the bumped source stamp. Users
503
+ need no Rust toolchain — only CI does.
504
+
505
+ The Bash fallback (`work-view.sh`) is pure bash, optional enhancement via `yq`
506
+ if installed, falls back to `grep`+`sed` otherwise. It is a **frozen, degraded
507
+ fallback** for platforms without a shipped prebuilt binary: it preserves the
508
+ core filter surface but intentionally does **not** implement newer Rust-only
509
+ features — no `--scope` (it always queries all tiers, the pre-scope default) and
510
+ no `work-view board`. bash<->Rust byte-parity is no longer enforced. Full
511
+ retirement of the Bash fallback (Rust-only) is tracked as a parked epic.
512
+
513
+ ### Flag set
514
+
515
+ | Flag | Argument | Effect |
516
+ |---|---|---|
517
+ | `--stage` | `<stage>` | Filter to items at the given stage |
518
+ | `--tag` | `<tag>` | Filter to items with the given tag (repeatable, AND semantics) |
519
+ | `--kind` | `<kind>` | Filter to items of the given kind |
520
+ | `--parent` | `<id>` | Filter to direct children of the given item |
521
+ | `--release` | `<version>` | Filter by `release_binding` |
522
+ | `--gate` | `<gate>` | Filter by `gate_origin` |
523
+ | `--ready` | (none) | Active-tier items at `stage: drafting`, `implementing`, or `review` with all `depends_on` terminal (`done`/`released`, or resident in `releases/`/`archive/`). **Excludes `[scan]`-tagged items** — they are engagement-owned by `deep-code-scan` and must not be drained by autopilot. |
524
+ | `--blocked` | (none) | Active-tier items at `stage: drafting`, `implementing`, or `review` with at least one non-terminal dependency. Also excludes `[scan]`-tagged items. |
525
+ | `--blocking` | `<id>` | Reverse lookup: items that depend on `<id>` |
526
+ | `--scope` | `<active\|backlog\|releases\|archive\|all>` | Tiers to surface. Default (flag absent) = **active + backlog** (non-terminal); terminal tiers (`releases`/`archive`) are hidden unless requested. `--release`/`--gate` auto-widen to `all` unless `--scope` is set explicitly |
527
+ | `--research-origin` | `<slug>` | Filter by `research_origin` (use `null` to select items with no origin) |
528
+ | `--research-refs` | `<slug>` | Items whose `research_refs` list contains `<slug>` |
529
+ | `--scan-origin` | `<slug>` | Filter by `scan_origin` (use `null` to select items with no scan origin) |
530
+ | `--paths` | (none) | Output only file paths (grep-pipe-friendly) |
531
+ | `--cat` | (none) | Output full bodies of matching items |
532
+ | `--count` | (none) | Output only the match count |
533
+ | `--help` | (none) | Show usage and exit |
534
+
535
+ Multiple filters compose with AND semantics. By default `work-view` surfaces only
536
+ the non-terminal tiers (active + backlog); the unbounded `releases/`/`archive/`
537
+ history is opt-in via `--scope`. Because release-bound and gate-origin items
538
+ migrate into the terminal `releases/` tier when a release ships, `--release` and
539
+ `--gate` are treated as lifecycle queries and auto-widen to all tiers unless an
540
+ explicit `--scope` overrides them. `--scope` is a Rust-binary feature; the frozen
541
+ Bash fallback does not implement it (see below).
542
+
543
+ ### Output modes
544
+
545
+ - **Default (tabular):** `id | kind | stage | tags | parent` — padded rows
546
+ - **`--paths`:** one path per line
547
+ - **`--cat`:** full file bodies separated by `---` lines
548
+ - **`--count`:** integer
549
+
550
+ ### Exit codes
551
+
552
+ | Code | Meaning |
553
+ |---|---|
554
+ | `0` | Success |
555
+ | `1` | Usage error (bad flag, conflicting flags) |
556
+ | `2` | Substrate not found (no `.work/CONVENTIONS.md` in CWD or ancestor) |
557
+ | `3` | Internal error (corrupted item file, etc.) |
558
+
559
+ ## Interactive board
560
+
561
+ The human board surface is `plugins/agile-workflow/skills/board/SKILL.md` and
562
+ the compiled `work-view board` subcommand. It serves a live localhost view of
563
+ the `.work/` substrate, backed by the Rust query core and embedded assets. The
564
+ server binds `127.0.0.1`, scans upward from the requested port when that port is
565
+ busy, and never exposes the user's absolute paths or raw item frontmatter in the
566
+ browser API. Requests must carry a loopback `Host` authority (`127.0.0.1`,
567
+ `localhost`, or `[::1]`) so a page on a non-local origin cannot use DNS
568
+ rebinding to read the board feed.
569
+
570
+ `plugins/agile-workflow/scripts/work-board.sh` remains only as a compatibility
571
+ shim for older invocations. It does not render HTML. It checks for a
572
+ board-capable `.work/bin/work-view`, execs `.work/bin/work-view board "$@"` when
573
+ available, and otherwise prints an actionable compiled-binary requirement.
574
+
575
+ ### Flag set
576
+
577
+ | Flag | Argument | Effect |
578
+ |---|---|---|
579
+ | `--port` | `<port>` | Bind localhost starting at this port, default `8181` |
580
+ | `--no-open` | (none) | Do not launch a browser after binding |
581
+ | `--print` | (none) | Alias for `--no-open` |
582
+ | `--help` | (none) | Show usage and exit |
583
+
584
+ ### Swimlanes and stage columns
585
+
586
+ The kanban view groups items into **swimlanes by parent** — the item's `parent`
587
+ id, else its owning epic id, else a `(no parent)` lane. Within each lane it
588
+ renders one **column per distinct stage value**, in preferred order:
589
+
590
+ ```
591
+ drafting → implementing → review → done → released
592
+ ```
593
+
594
+ Any other stage values present in the items (e.g. `planned`, `quality-gate`)
595
+ follow as their own columns after the preferred order, with a trailing column
596
+ for items that have no stage. Stages are shown **verbatim** — the board does not
597
+ collapse or rename them. Backlog items are surfaced through the kind filter
598
+ (`backlog` is a selectable kind), not a dedicated column.
599
+
600
+ ### Browser API shape
601
+
602
+ `GET /api/substrate` returns JSON with project metadata, diagnostics, and an
603
+ array of item objects:
604
+
605
+ ```ts
606
+ {
607
+ work_view_version: string;
608
+ project: string;
609
+ root_rel: string;
610
+ diagnostics: {
611
+ parse_errors: Array<{ rel_path: string; reason: string }>;
612
+ validation_warnings: Array<ItemDiagnostic>;
613
+ duplicate_ids: Array<ItemDiagnostic>;
614
+ };
615
+ items: Array<{
616
+ id: string;
617
+ kind: 'epic' | 'feature' | 'story' | 'release' | null;
618
+ tier: 'active' | 'backlog' | 'releases' | 'archive';
619
+ stage: string | null;
620
+ parent: string | null;
621
+ release_binding: string | null;
622
+ gate_origin: string | null;
623
+ research_origin: string | null;
624
+ research_refs: string[];
625
+ scan_origin: string | null;
626
+ created: string | null;
627
+ updated: string | null;
628
+ tags: string[];
629
+ depends_on: string[];
630
+ unmet_deps: string[];
631
+ dependents: string[];
632
+ children: string[];
633
+ ready: boolean;
634
+ blocked: boolean;
635
+ is_terminal: boolean;
636
+ body: string;
637
+ rel_path: string;
638
+ }>;
639
+ }
640
+
641
+ type ItemDiagnostic = {
642
+ rel_path: string;
643
+ tier: 'active' | 'backlog' | 'releases' | 'archive';
644
+ id: string | null;
645
+ field: string | null;
646
+ reason: string;
647
+ };
648
+ ```
649
+
650
+ The API intentionally omits absolute filesystem paths and the raw item text
651
+ envelope. Markdown body content is returned for display, while frontmatter is
652
+ represented by parsed fields.
653
+
654
+ ### Exit codes
655
+
656
+ | Code | Meaning |
657
+ |---|---|
658
+ | `0` | Success |
659
+ | `1` | Usage error (bad flag) |
660
+ | `2` | Substrate not found |
661
+
662
+ ## Hook contracts
663
+
664
+ Hooks live in `plugins/agile-workflow/hooks/hooks.json`.
665
+
666
+ ### SessionStart / PostCompact hooks
667
+
668
+ ```json
669
+ {
670
+ "SessionStart": [
671
+ {
672
+ "matcher": "startup|resume|clear|compact",
673
+ "hooks": [
674
+ {
675
+ "type": "command",
676
+ "command": "PYTHONDONTWRITEBYTECODE=1 python3 ${PLUGIN_ROOT:-${CLAUDE_PLUGIN_ROOT}}/hooks/scripts/prompt-context.py",
677
+ "timeout": 5
678
+ }
679
+ ]
680
+ }
681
+ ],
682
+ "PostCompact": [
683
+ {
684
+ "matcher": "manual|auto",
685
+ "hooks": [
686
+ {
687
+ "type": "command",
688
+ "command": "PYTHONDONTWRITEBYTECODE=1 python3 ${PLUGIN_ROOT:-${CLAUDE_PLUGIN_ROOT}}/hooks/scripts/prompt-context.py",
689
+ "timeout": 5
690
+ }
691
+ ]
692
+ }
693
+ ]
694
+ }
695
+ ```
696
+
697
+ **Activation:** resolves the substrate root by walking up from the hook payload's
698
+ `cwd` (then `CLAUDE_PROJECT_DIR`, then the process working directory) until a
699
+ `.work/CONVENTIONS.md` is found; runs only if one exists, otherwise exits 0 with
700
+ no output.
701
+
702
+ **Effect:** updates prompt-context state under the host-provided plugin data
703
+ directory (`PLUGIN_DATA` / `CLAUDE_PLUGIN_DATA`), falling back to
704
+ `XDG_STATE_HOME`, `~/.local/state`, or the system temp directory only when no
705
+ plugin data directory is available. `SessionStart` resets the per-session epoch
706
+ and seen-set; `PostCompact` bumps the epoch so context re-injects after
707
+ compaction. This lets principles capsules fire once per session, and once again
708
+ after resume/compaction, without dirtying normal project worktrees. It does not
709
+ inject queue context at session start.
710
+
711
+ These events are also the **primary firing** of the `.agents/rules/`
712
+ rules-injection contract where the host accepts hook-specific context. They emit
713
+ the concatenated `.agents/rules/*.md` content as
714
+ `hookSpecificOutput.additionalContext` directly (after the epoch reset/bump),
715
+ unconditionally — no prompt to gate on. Codex is the exception for
716
+ `PostCompact`: Codex's `PostCompact` output schema does not allow
717
+ `hookSpecificOutput`, so that event only bumps the epoch and leaves context
718
+ reload to `SessionStart` with `source: compact`. This
719
+ keeps rules reloading at session start and after compaction, even during
720
+ auto-continuation with no user prompt, mirroring the legacy Claude-only
721
+ `.claude/rules/` force-load.
722
+
723
+ ### `.agents/rules/` rules-injection contract
724
+
725
+ The hook force-loads every `.agents/rules/*.md` file into agent context so
726
+ producers (`convert`, `gate-patterns`, or the user) can drop content-agnostic
727
+ rule files there and have them reliably reach the agent in both Claude Code and
728
+ Codex. The contract:
729
+
730
+ - **Fires on:** `SessionStart` and host-supported `PostCompact` context output,
731
+ unconditionally. On Codex, `PostCompact` is side-effect-only because its
732
+ output schema does not allow `hookSpecificOutput`; `SessionStart` with
733
+ `source: compact` carries the context.
734
+ - **Content:** all `<root>/.agents/rules/*.md` files, sorted by name,
735
+ concatenated under a `## Project Rules (.agents/rules/)` heading.
736
+ - **Dedup:** per-session epoch + SHA-256 content hash. Rules inject exactly once
737
+ per `(epoch, content-hash)` — re-injecting after a `PostCompact` epoch bump or
738
+ when any `.agents/rules/*.md` file changes, but not otherwise.
739
+ - **Substrate gate:** only fires when `${CLAUDE_PROJECT_DIR}/.work/CONVENTIONS.md`
740
+ exists, like all the prompt-context hooks.
741
+ - **CONVENTIONS flag + byte cap:** `.work/CONVENTIONS.md` may set
742
+ `rules_context: on|off` (default `on`) to disable injection, and
743
+ `rules_context_max_bytes: <int>` (default 12000) to cap the emitted size. A
744
+ malformed CONVENTIONS keeps the enabled defaults so rules are never silently
745
+ dropped; oversized content is truncated with a "read the files for full
746
+ content" notice (the hash is computed over the untruncated content so any edit
747
+ re-injects).
748
+
749
+ ### UserPromptSubmit hook
750
+
751
+ ```json
752
+ {
753
+ "UserPromptSubmit": [
754
+ {
755
+ "hooks": [
756
+ {
757
+ "type": "command",
758
+ "command": "PYTHONDONTWRITEBYTECODE=1 python3 ${PLUGIN_ROOT:-${CLAUDE_PLUGIN_ROOT}}/hooks/scripts/prompt-context.py",
759
+ "timeout": 10
760
+ }
761
+ ]
762
+ }
763
+ ]
764
+ }
765
+ ```
766
+
767
+ **Activation:** runs only if a substrate exists. Principles capsules additionally
768
+ require an actionable agile-workflow move: queue operations, stage movement,
769
+ explicit workflow verbs, or a known item id.
770
+
771
+ **Effect:** returns `hookSpecificOutput.additionalContext` with:
772
+ - The smallest relevant principles capsule, at most once per session per
773
+ capsule: code design, dispatch economy, or advisory review.
774
+
775
+ It does not inject `.agents/rules/*.md` or queue snapshots at prompt time. Queue
776
+ state remains available through explicit `work-view`, `/aw`, or board commands.
777
+
778
+ ### Pi hook parity adapter
779
+
780
+ Pi packages do not consume `hooks/hooks.json`. The Pi package therefore exposes
781
+ `plugins/agile-workflow/extensions/agile-workflow.ts` as a **Pi hook parity
782
+ adapter**. It maps Pi-native events onto the same deterministic hook scripts:
783
+
784
+ - `session_start` → `prompt-context.py` with `hook_event_name: SessionStart` for
785
+ epoch reset and work-view self-heal side effects.
786
+ - `session_compact` → `prompt-context.py` with `hook_event_name: PostCompact` for
787
+ epoch bump side effects.
788
+ - `before_agent_start` → `prompt-context.py` twice: synthetic
789
+ `hook_event_name: PiBeforeAgentStart` with `force_rules_context: true` to
790
+ append `.agents/rules/*.md` into Pi's rebuilt-per-turn system prompt, then
791
+ `hook_event_name: UserPromptSubmit` for the same prompt-gated principles
792
+ capsules Claude/Codex receive. Principles are returned as an injected Pi
793
+ message (`customType: agile-workflow-principles`) rather than hidden permanent
794
+ instructions, matching hook-specific context visibility.
795
+ - `tool_result` for mutating tools (`write`, `edit`, `apply_patch`) →
796
+ `substrate-maintainer.py` with `hook_event_name: PostToolUse`; validation
797
+ output is appended to the tool result so the next model step sees the same
798
+ substrate warnings.
799
+
800
+ This adapter is intentionally thin: `.agents/rules/`, `prompt-context.py`, and
801
+ `substrate-maintainer.py` remain the source of truth across Claude Code, Codex,
802
+ and Pi. Any change to hook behavior must update the adapter and the parity check
803
+ script in the same patch.
804
+
805
+ ### PostToolUse hook
806
+
807
+ ```json
808
+ {
809
+ "PostToolUse": [
810
+ {
811
+ "matcher": "Write|Edit|apply_patch",
812
+ "hooks": [
813
+ {
814
+ "type": "command",
815
+ "command": "PYTHONDONTWRITEBYTECODE=1 python3 ${PLUGIN_ROOT:-${CLAUDE_PLUGIN_ROOT}}/hooks/scripts/substrate-maintainer.py",
816
+ "timeout": 5
817
+ }
818
+ ]
819
+ }
820
+ ]
821
+ }
822
+ ```
823
+
824
+ **Activation:** runs only if the modified file path is under `.work/active/`,
825
+ `.work/backlog/`, `.work/releases/`, or `.work/archive/`. Otherwise exits 0.
826
+
827
+ **Effect:** auto-bumps the `updated:` frontmatter field of modified active or
828
+ backlog item files to today's date in **local time**. It then validates cheap
829
+ structural invariants for the touched item(s): required frontmatter, valid
830
+ kind/stage, filename/id match, duplicate id conflicts involving the touched
831
+ item, existing parents and dependencies, and `depends_on` cycles reachable from
832
+ the touched item. Validation issues are returned as
833
+ `hookSpecificOutput.additionalContext`; the hook does not invoke a model.
834
+
835
+ ## Tooling requirements
836
+
837
+ ### Required
838
+
839
+ - **bash** ≥ 4.0 — for the `work-view` bash fallback (`work-view.sh`) and the install helper (`install-work-view.sh`)
840
+ - **python3** — for plugin hook scripts
841
+ - **grep** — POSIX-compatible
842
+ - **git** — substrate's audit trail; `work-view`'s history queries call `git log`
843
+
844
+ ### Optional (auto-detected, falls back gracefully)
845
+
846
+ - **yq** — cleaner YAML parsing in `work-view` (faster on > 100 items)
847
+ - **jq** — JSON manipulation in some hook scripts
848
+ - **gh** — required only if the project's release mapping is `branch-held`
849
+ (used by `release-deploy` to merge bound PRs)
850
+
851
+ ### Not required
852
+
853
+ - No Node, Python, Rust, or other language toolchains
854
+ - No MCP server
855
+ - No external services or auth