@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,552 @@
1
+ #!/usr/bin/env bash
2
+ # bump-version.test.sh — unit tests for channel-metadata lockstep and the
3
+ # version projection blocks in scripts/bump-version.sh (repo-root script).
4
+ #
5
+ # The load-bearing mechanisms under test are the two projection blocks:
6
+ #
7
+ # agile-workflow / work-view block:
8
+ # * the Rust stamp `work-view/crates/cli/.work-view-version` (NO trailing newline)
9
+ # * the bash fallback's `WORK_VIEW_VERSION="x.y.z"` literal in scripts/work-view.sh
10
+ #
11
+ # agentic-research / research-view block:
12
+ # * the Rust stamp `research-view/crates/cli/.research-view-version` (NO trailing newline)
13
+ # * the bash fallback's `RESEARCH_VIEW_VERSION="x.y.z"` literal in scripts/research-view.sh
14
+ #
15
+ # Both blocks use a Fail-Fast `grep` postcondition that exits 1 if the anchored
16
+ # sed pattern no longer matches the literal.
17
+ #
18
+ # We run the REAL bump-version.sh (not a reimplementation) inside a throwaway
19
+ # `git init` scratch repo whose tree mirrors the paths the script touches. To
20
+ # stop the script's trailing `git commit` / `git push` from escaping, a fake
21
+ # `git` shim is placed first on PATH: it forwards read/stage subcommands
22
+ # (init/config/add/diff/status/rev-parse) to the real git inside the scratch
23
+ # repo but turns `commit` and `push` into no-ops. This still exercises the real
24
+ # projection AND the real `git add` staging.
25
+ #
26
+ # Runnable locally and from CI. Exits non-zero if any assertion fails.
27
+
28
+ set -uo pipefail
29
+
30
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
31
+ # scripts/tests/ -> scripts/ -> agile-workflow/ -> plugins/ -> repo root
32
+ REPO_ROOT="$(cd "${SCRIPT_DIR}/../../../.." && pwd)"
33
+ BUMP="${REPO_ROOT}/scripts/bump-version.sh"
34
+ REAL_GIT="$(command -v git)"
35
+
36
+ # ---------------------------------------------------------------------------
37
+ # Counters
38
+ # ---------------------------------------------------------------------------
39
+ PASS=0
40
+ FAIL=0
41
+ ERRORS=()
42
+
43
+ assert_eq() {
44
+ local label="$1" expected="$2" actual="$3"
45
+ if [ "$expected" = "$actual" ]; then
46
+ echo " PASS: $label"
47
+ ((PASS++))
48
+ else
49
+ echo " FAIL: $label"
50
+ echo " expected: $(printf '%q' "$expected")"
51
+ echo " actual: $(printf '%q' "$actual")"
52
+ ((FAIL++))
53
+ ERRORS+=("$label")
54
+ fi
55
+ }
56
+
57
+ assert_true() {
58
+ local label="$1"
59
+ if eval "$2"; then
60
+ echo " PASS: $label"
61
+ ((PASS++))
62
+ else
63
+ echo " FAIL: $label"
64
+ echo " condition false: $2"
65
+ ((FAIL++))
66
+ ERRORS+=("$label")
67
+ fi
68
+ }
69
+
70
+ assert_false() {
71
+ local label="$1"
72
+ if ! eval "$2"; then
73
+ echo " PASS: $label"
74
+ ((PASS++))
75
+ else
76
+ echo " FAIL: $label"
77
+ echo " expected false but was true: $2"
78
+ ((FAIL++))
79
+ ERRORS+=("$label")
80
+ fi
81
+ }
82
+
83
+ # ---------------------------------------------------------------------------
84
+ # Preflight
85
+ # ---------------------------------------------------------------------------
86
+ echo ""
87
+ echo "=== Preflight: bump-version.sh present and jq available ==="
88
+ assert_true "bump-version.sh exists" "[ -f '$BUMP' ]"
89
+ if ! command -v jq >/dev/null 2>&1; then
90
+ echo "ERROR: jq is required to run bump-version.sh and these tests" >&2
91
+ exit 1
92
+ fi
93
+ if [ ! -f "$BUMP" ]; then
94
+ echo "ERROR: cannot locate bump-version.sh at $BUMP" >&2
95
+ exit 1
96
+ fi
97
+
98
+ # ---------------------------------------------------------------------------
99
+ # make_fake_git_dir — produce a dir holding a `git` shim first on PATH.
100
+ # Forwards safe subcommands to the real git; no-ops commit/push so the script
101
+ # under test cannot reach a remote or write a published commit.
102
+ # ---------------------------------------------------------------------------
103
+ make_fake_git_dir() {
104
+ local dir
105
+ dir="$(mktemp -d)"
106
+ cat > "${dir}/git" <<EOF
107
+ #!/usr/bin/env bash
108
+ sub="\${1:-}"
109
+ case "\$sub" in
110
+ commit|push)
111
+ # Swallow — these would escape the scratch repo / publish a bump commit.
112
+ exit 0
113
+ ;;
114
+ *)
115
+ exec "${REAL_GIT}" "\$@"
116
+ ;;
117
+ esac
118
+ EOF
119
+ chmod +x "${dir}/git"
120
+ printf '%s' "$dir"
121
+ }
122
+
123
+ # ---------------------------------------------------------------------------
124
+ # new_scratch_repo — build a throwaway repo mirroring the paths bump-version.sh
125
+ # touches, with a clean working tree (the script refuses a dirty plugin dir).
126
+ # $1 = plugin.json version to use for agile-workflow (and agentic-research)
127
+ # $2 = optional extra plugin name to seed (for the non-agile-workflow guard)
128
+ # Echoes the repo path.
129
+ # ---------------------------------------------------------------------------
130
+ new_scratch_repo() {
131
+ local aw_version="$1" extra_plugin="${2:-}"
132
+ local repo
133
+ repo="$(mktemp -d)"
134
+
135
+ # agile-workflow plugin manifests + work-view artifacts.
136
+ mkdir -p \
137
+ "${repo}/plugins/agile-workflow/.claude-plugin" \
138
+ "${repo}/plugins/agile-workflow/.codex-plugin" \
139
+ "${repo}/plugins/agile-workflow/work-view/crates/cli" \
140
+ "${repo}/plugins/agile-workflow/scripts"
141
+
142
+ printf '{\n "name": "agile-workflow",\n "version": "%s"\n}\n' "$aw_version" \
143
+ > "${repo}/plugins/agile-workflow/.claude-plugin/plugin.json"
144
+ printf '{\n "name": "agile-workflow",\n "version": "%s"\n}\n' "$aw_version" \
145
+ > "${repo}/plugins/agile-workflow/.codex-plugin/plugin.json"
146
+ printf '{\n "name": "@nklisch/pi-agile-workflow",\n "version": "%s",\n "keywords": ["pi-package"],\n "pi": { "skills": ["./skills"] }\n}\n' "$aw_version" \
147
+ > "${repo}/plugins/agile-workflow/package.json"
148
+
149
+ # Rust stamp — start with a placeholder, no trailing newline (mirrors prod).
150
+ printf '%s' "$aw_version" \
151
+ > "${repo}/plugins/agile-workflow/work-view/crates/cli/.work-view-version"
152
+
153
+ # Bash fallback with the anchored WORK_VIEW_VERSION literal.
154
+ cat > "${repo}/plugins/agile-workflow/scripts/work-view.sh" <<EOF
155
+ #!/usr/bin/env bash
156
+ WORK_VIEW_VERSION="${aw_version}"
157
+ echo "work-view \${WORK_VIEW_VERSION}"
158
+ EOF
159
+
160
+ # agentic-research plugin manifests + research-view artifacts.
161
+ mkdir -p \
162
+ "${repo}/plugins/agentic-research/.claude-plugin" \
163
+ "${repo}/plugins/agentic-research/.codex-plugin" \
164
+ "${repo}/plugins/agentic-research/research-view/crates/cli" \
165
+ "${repo}/plugins/agentic-research/scripts"
166
+
167
+ printf '{\n "name": "agentic-research",\n "version": "%s"\n}\n' "$aw_version" \
168
+ > "${repo}/plugins/agentic-research/.claude-plugin/plugin.json"
169
+ printf '{\n "name": "agentic-research",\n "version": "%s"\n}\n' "$aw_version" \
170
+ > "${repo}/plugins/agentic-research/.codex-plugin/plugin.json"
171
+ printf '{\n "name": "@nklisch/pi-agentic-research",\n "version": "%s",\n "keywords": ["pi-package"],\n "pi": { "skills": ["./skills"] }\n}\n' "$aw_version" \
172
+ > "${repo}/plugins/agentic-research/package.json"
173
+
174
+ # Rust stamp — no trailing newline.
175
+ printf '%s' "$aw_version" \
176
+ > "${repo}/plugins/agentic-research/research-view/crates/cli/.research-view-version"
177
+
178
+ # Bash fallback with the anchored RESEARCH_VIEW_VERSION literal.
179
+ cat > "${repo}/plugins/agentic-research/scripts/research-view.sh" <<EOF
180
+ #!/usr/bin/env bash
181
+ RESEARCH_VIEW_VERSION="${aw_version}"
182
+ echo "research-view \${RESEARCH_VIEW_VERSION}"
183
+ EOF
184
+
185
+ if [ -n "$extra_plugin" ]; then
186
+ mkdir -p "${repo}/plugins/${extra_plugin}/.claude-plugin" \
187
+ "${repo}/plugins/${extra_plugin}/.codex-plugin"
188
+ printf '{\n "name": "%s",\n "version": "%s"\n}\n' "$extra_plugin" "$aw_version" \
189
+ > "${repo}/plugins/${extra_plugin}/.claude-plugin/plugin.json"
190
+ printf '{\n "name": "%s",\n "version": "%s"\n}\n' "$extra_plugin" "$aw_version" \
191
+ > "${repo}/plugins/${extra_plugin}/.codex-plugin/plugin.json"
192
+ printf '{\n "name": "@nklisch/pi-%s",\n "version": "%s",\n "keywords": ["pi-package"],\n "pi": { "skills": ["./skills"] }\n}\n' "$extra_plugin" "$aw_version" \
193
+ > "${repo}/plugins/${extra_plugin}/package.json"
194
+ fi
195
+
196
+ # Initialize a clean git repo (real git; commit happens via real git here,
197
+ # only the script-under-test's commit/push are shimmed away later).
198
+ (
199
+ cd "$repo"
200
+ "$REAL_GIT" init -q
201
+ "$REAL_GIT" config user.email test@example.com
202
+ "$REAL_GIT" config user.name "Test"
203
+ "$REAL_GIT" config commit.gpgsign false
204
+ "$REAL_GIT" add -A
205
+ "$REAL_GIT" commit -q -m "seed" >/dev/null 2>&1
206
+ )
207
+ printf '%s' "$repo"
208
+ }
209
+
210
+ # run_bump <repo> <plugin> <bump> — run the real script with the fake git shim
211
+ # first on PATH. Captures stdout/stderr/rc.
212
+ run_bump() {
213
+ local repo="$1" plugin="$2" bump="$3"
214
+ local fakedir
215
+ fakedir="$(make_fake_git_dir)"
216
+ BUMP_ERR="${repo}/.bump.err"
217
+ BUMP_OUT=$(
218
+ cd "$repo" &&
219
+ PATH="${fakedir}:${PATH}" bash "$BUMP" "$plugin" "$bump" 2>"$BUMP_ERR"
220
+ )
221
+ BUMP_RC=$?
222
+ rm -rf "$fakedir"
223
+ }
224
+
225
+ # staged? <repo> <relpath> — true if the path is staged in the repo index.
226
+ is_staged() {
227
+ local repo="$1" rel="$2"
228
+ ( cd "$repo" && "$REAL_GIT" diff --cached --name-only -- "$rel" | grep -qx "$rel" )
229
+ }
230
+
231
+ VER_REL="plugins/agile-workflow/work-view/crates/cli/.work-view-version"
232
+ WV_REL="plugins/agile-workflow/scripts/work-view.sh"
233
+
234
+ RV_VER_REL="plugins/agentic-research/research-view/crates/cli/.research-view-version"
235
+ RV_SH_REL="plugins/agentic-research/scripts/research-view.sh"
236
+
237
+ # ---------------------------------------------------------------------------
238
+ # Test group 1: patch bump projects new semver into BOTH implementations
239
+ # ---------------------------------------------------------------------------
240
+ echo ""
241
+ echo "=== Test group 1: agile-workflow patch bump projects lockstep ==="
242
+
243
+ REPO1="$(new_scratch_repo "1.2.3")"
244
+ run_bump "$REPO1" agile-workflow patch
245
+
246
+ assert_eq "patch bump exits 0" "0" "$BUMP_RC"
247
+
248
+ # .work-view-version equals the new semver with NO trailing newline.
249
+ VER_CONTENT="$(cat "${REPO1}/${VER_REL}")"
250
+ assert_eq ".work-view-version content == new semver" "1.2.4" "$VER_CONTENT"
251
+ VER_BYTES="$(wc -c < "${REPO1}/${VER_REL}" | tr -d ' ')"
252
+ assert_eq ".work-view-version is exactly 5 bytes (no trailing newline)" "5" "$VER_BYTES"
253
+ LAST_BYTE="$(tail -c1 "${REPO1}/${VER_REL}" | od -An -tx1 | tr -d ' \n')"
254
+ assert_false ".work-view-version last byte is not a newline (0a)" "[ '$LAST_BYTE' = '0a' ]"
255
+
256
+ # work-view.sh WORK_VIEW_VERSION literal equals the new semver.
257
+ assert_true "work-view.sh WORK_VIEW_VERSION literal == new" \
258
+ "grep -q '^WORK_VIEW_VERSION=\"1.2.4\"\$' '${REPO1}/${WV_REL}'"
259
+ assert_false "old WORK_VIEW_VERSION literal is gone" \
260
+ "grep -q '^WORK_VIEW_VERSION=\"1.2.3\"\$' '${REPO1}/${WV_REL}'"
261
+
262
+ # plugin.json manifests and package metadata advanced too (sanity that this was
263
+ # a real bump).
264
+ assert_eq "claude plugin.json bumped" "1.2.4" \
265
+ "$(jq -r '.version' "${REPO1}/plugins/agile-workflow/.claude-plugin/plugin.json")"
266
+ assert_eq "codex plugin.json bumped" "1.2.4" \
267
+ "$(jq -r '.version' "${REPO1}/plugins/agile-workflow/.codex-plugin/plugin.json")"
268
+ assert_eq "package.json bumped" "1.2.4" \
269
+ "$(jq -r '.version' "${REPO1}/plugins/agile-workflow/package.json")"
270
+
271
+ # Both work-view files are git-staged.
272
+ assert_true "claude plugin.json is staged" \
273
+ "is_staged '$REPO1' 'plugins/agile-workflow/.claude-plugin/plugin.json'"
274
+ assert_true "codex plugin.json is staged" \
275
+ "is_staged '$REPO1' 'plugins/agile-workflow/.codex-plugin/plugin.json'"
276
+ assert_true ".work-view-version is staged" "is_staged '$REPO1' '$VER_REL'"
277
+ assert_true "work-view.sh is staged" "is_staged '$REPO1' '$WV_REL'"
278
+ assert_true "agile-workflow package.json is staged" \
279
+ "is_staged '$REPO1' 'plugins/agile-workflow/package.json'"
280
+
281
+ # agile-workflow bump must NOT touch research-view artifacts.
282
+ assert_eq "aw-bump: .research-view-version unchanged" "1.2.3" \
283
+ "$(cat "${REPO1}/${RV_VER_REL}")"
284
+ assert_true "aw-bump: research-view.sh RESEARCH_VIEW_VERSION unchanged" \
285
+ "grep -q '^RESEARCH_VIEW_VERSION=\"1.2.3\"\$' '${REPO1}/${RV_SH_REL}'"
286
+ assert_false "aw-bump: .research-view-version NOT staged" \
287
+ "is_staged '$REPO1' '$RV_VER_REL'"
288
+ assert_false "aw-bump: research-view.sh NOT staged" \
289
+ "is_staged '$REPO1' '$RV_SH_REL'"
290
+
291
+ rm -rf "$REPO1"
292
+
293
+ # ---------------------------------------------------------------------------
294
+ # Test group 2: bumping a NON-agile-workflow plugin leaves work-view untouched
295
+ # ---------------------------------------------------------------------------
296
+ echo ""
297
+ echo "=== Test group 2: non-agile-workflow plugin does not touch work-view ==="
298
+
299
+ REPO2="$(new_scratch_repo "1.2.3" "ux-ui-design")"
300
+ VER_BEFORE="$(cat "${REPO2}/${VER_REL}")"
301
+ WV_BEFORE="$(cat "${REPO2}/${WV_REL}")"
302
+ run_bump "$REPO2" ux-ui-design patch
303
+
304
+ assert_eq "non-aw bump exits 0" "0" "$BUMP_RC"
305
+ assert_eq "non-aw plugin.json bumped" "1.2.4" \
306
+ "$(jq -r '.version' "${REPO2}/plugins/ux-ui-design/.claude-plugin/plugin.json")"
307
+ assert_eq "non-aw package.json bumped" "1.2.4" \
308
+ "$(jq -r '.version' "${REPO2}/plugins/ux-ui-design/package.json")"
309
+ assert_eq ".work-view-version unchanged after non-aw bump" "$VER_BEFORE" \
310
+ "$(cat "${REPO2}/${VER_REL}")"
311
+ assert_eq "work-view.sh unchanged after non-aw bump" "$WV_BEFORE" \
312
+ "$(cat "${REPO2}/${WV_REL}")"
313
+ assert_false ".work-view-version NOT staged after non-aw bump" "is_staged '$REPO2' '$VER_REL'"
314
+ assert_false "work-view.sh NOT staged after non-aw bump" "is_staged '$REPO2' '$WV_REL'"
315
+ assert_true "non-aw claude plugin.json is staged" \
316
+ "is_staged '$REPO2' 'plugins/ux-ui-design/.claude-plugin/plugin.json'"
317
+ assert_true "non-aw codex plugin.json is staged" \
318
+ "is_staged '$REPO2' 'plugins/ux-ui-design/.codex-plugin/plugin.json'"
319
+ assert_true "non-aw package.json is staged" \
320
+ "is_staged '$REPO2' 'plugins/ux-ui-design/package.json'"
321
+
322
+ rm -rf "$REPO2"
323
+
324
+ # ---------------------------------------------------------------------------
325
+ # Test group 3: Fail-Fast postcondition fires on a renamed/indented literal
326
+ # ---------------------------------------------------------------------------
327
+ echo ""
328
+ echo "=== Test group 3: postcondition exits 1 when literal anchor no longer matches ==="
329
+
330
+ REPO3="$(new_scratch_repo "1.2.3")"
331
+ # Break the anchored literal: indent it so `^WORK_VIEW_VERSION="..."` (and the
332
+ # anchored sed substitution) no longer matches. sed exits 0 (no match), so the
333
+ # projection silently no-ops and the grep postcondition must catch it.
334
+ cat > "${REPO3}/${WV_REL}" <<'EOF'
335
+ #!/usr/bin/env bash
336
+ WORK_VIEW_VERSION="1.2.3"
337
+ echo "work-view ${WORK_VIEW_VERSION}"
338
+ EOF
339
+ ( cd "$REPO3" && "$REAL_GIT" add -A && "$REAL_GIT" commit -q -m "indent literal" >/dev/null 2>&1 )
340
+
341
+ run_bump "$REPO3" agile-workflow patch
342
+
343
+ assert_eq "indented-literal bump exits 1 (postcondition fires)" "1" "$BUMP_RC"
344
+ POST_ERR="$(cat "$BUMP_ERR" 2>/dev/null || true)"
345
+ assert_true "postcondition reports failed projection" \
346
+ "printf '%s' \"\$POST_ERR\" | grep -q 'failed to project version into'"
347
+ # The indented literal was not rewritten (anchored pattern never matched).
348
+ assert_false "indented literal NOT rewritten to new semver" \
349
+ "grep -q 'WORK_VIEW_VERSION=\"1.2.4\"' '${REPO3}/${WV_REL}'"
350
+
351
+ rm -rf "$REPO3"
352
+
353
+ # ---------------------------------------------------------------------------
354
+ # Test group 4: minor/major bumps project correctly too
355
+ # ---------------------------------------------------------------------------
356
+ echo ""
357
+ echo "=== Test group 4: minor and major bumps project lockstep ==="
358
+
359
+ REPO4="$(new_scratch_repo "1.2.3")"
360
+ run_bump "$REPO4" agile-workflow minor
361
+ assert_eq "minor bump exits 0" "0" "$BUMP_RC"
362
+ assert_eq "minor .work-view-version == 1.3.0" "1.3.0" "$(cat "${REPO4}/${VER_REL}")"
363
+ assert_true "minor work-view.sh literal == 1.3.0" \
364
+ "grep -q '^WORK_VIEW_VERSION=\"1.3.0\"\$' '${REPO4}/${WV_REL}'"
365
+ rm -rf "$REPO4"
366
+
367
+ REPO5="$(new_scratch_repo "1.2.3")"
368
+ run_bump "$REPO5" agile-workflow major
369
+ assert_eq "major bump exits 0" "0" "$BUMP_RC"
370
+ assert_eq "major .work-view-version == 2.0.0" "2.0.0" "$(cat "${REPO5}/${VER_REL}")"
371
+ assert_true "major work-view.sh literal == 2.0.0" \
372
+ "grep -q '^WORK_VIEW_VERSION=\"2.0.0\"\$' '${REPO5}/${WV_REL}'"
373
+ rm -rf "$REPO5"
374
+
375
+ # ---------------------------------------------------------------------------
376
+ # Test group 5: package.json mismatch fails before projection
377
+ # ---------------------------------------------------------------------------
378
+ echo ""
379
+ echo "=== Test group 5: package.json mismatch fails before projection ==="
380
+
381
+ REPO6="$(new_scratch_repo "1.2.3")"
382
+ jq '.version = "9.9.9"' "${REPO6}/plugins/agile-workflow/package.json" \
383
+ > "${REPO6}/plugins/agile-workflow/package.json.tmp"
384
+ mv "${REPO6}/plugins/agile-workflow/package.json.tmp" \
385
+ "${REPO6}/plugins/agile-workflow/package.json"
386
+ ( cd "$REPO6" && "$REAL_GIT" add -A && "$REAL_GIT" commit -q -m "mismatch package version" >/dev/null 2>&1 )
387
+
388
+ run_bump "$REPO6" agile-workflow patch
389
+
390
+ assert_eq "package-mismatch bump exits 1" "1" "$BUMP_RC"
391
+ MISMATCH_ERR="$(cat "$BUMP_ERR" 2>/dev/null || true)"
392
+ assert_true "package mismatch reports channel metadata mismatch" \
393
+ "printf '%s' \"\$MISMATCH_ERR\" | grep -q 'version mismatch between channel metadata'"
394
+ assert_eq "claude manifest remains old after package mismatch" "1.2.3" \
395
+ "$(jq -r '.version' "${REPO6}/plugins/agile-workflow/.claude-plugin/plugin.json")"
396
+ assert_eq "package manifest remains mismatched after failed bump" "9.9.9" \
397
+ "$(jq -r '.version' "${REPO6}/plugins/agile-workflow/package.json")"
398
+ assert_eq ".work-view-version unchanged after package mismatch" "1.2.3" \
399
+ "$(cat "${REPO6}/${VER_REL}")"
400
+ assert_false "claude plugin.json NOT staged after package mismatch" \
401
+ "is_staged '$REPO6' 'plugins/agile-workflow/.claude-plugin/plugin.json'"
402
+ assert_false "codex plugin.json NOT staged after package mismatch" \
403
+ "is_staged '$REPO6' 'plugins/agile-workflow/.codex-plugin/plugin.json'"
404
+ assert_false "package.json NOT staged after package mismatch" \
405
+ "is_staged '$REPO6' 'plugins/agile-workflow/package.json'"
406
+
407
+ rm -rf "$REPO6"
408
+
409
+ # ---------------------------------------------------------------------------
410
+ # Test group 6: plugin without package.json still bumps Claude/Codex metadata
411
+ # ---------------------------------------------------------------------------
412
+ echo ""
413
+ echo "=== Test group 6: missing package.json remains supported ==="
414
+
415
+ REPO7="$(new_scratch_repo "1.2.3" "workflow")"
416
+ rm "${REPO7}/plugins/workflow/package.json"
417
+ ( cd "$REPO7" && "$REAL_GIT" add -A && "$REAL_GIT" commit -q -m "remove package metadata" >/dev/null 2>&1 )
418
+
419
+ run_bump "$REPO7" workflow patch
420
+
421
+ assert_eq "no-package plugin bump exits 0" "0" "$BUMP_RC"
422
+ assert_eq "no-package claude plugin.json bumped" "1.2.4" \
423
+ "$(jq -r '.version' "${REPO7}/plugins/workflow/.claude-plugin/plugin.json")"
424
+ assert_eq "no-package codex plugin.json bumped" "1.2.4" \
425
+ "$(jq -r '.version' "${REPO7}/plugins/workflow/.codex-plugin/plugin.json")"
426
+ assert_false "no-package plugin does not create package.json" \
427
+ "[ -f '${REPO7}/plugins/workflow/package.json' ]"
428
+
429
+ rm -rf "$REPO7"
430
+
431
+ # ---------------------------------------------------------------------------
432
+ # Test group 7: agentic-research patch bump projects new semver into BOTH
433
+ # research-view implementations
434
+ # ---------------------------------------------------------------------------
435
+ echo ""
436
+ echo "=== Test group 7: agentic-research patch bump projects lockstep ==="
437
+
438
+ REPO8="$(new_scratch_repo "1.2.3")"
439
+ run_bump "$REPO8" agentic-research patch
440
+
441
+ assert_eq "ar patch bump exits 0" "0" "$BUMP_RC"
442
+
443
+ # .research-view-version equals the new semver with NO trailing newline.
444
+ RV_VER_CONTENT="$(cat "${REPO8}/${RV_VER_REL}")"
445
+ assert_eq "ar: .research-view-version content == new semver" "1.2.4" "$RV_VER_CONTENT"
446
+ RV_VER_BYTES="$(wc -c < "${REPO8}/${RV_VER_REL}" | tr -d ' ')"
447
+ assert_eq "ar: .research-view-version is exactly 5 bytes (no trailing newline)" "5" "$RV_VER_BYTES"
448
+ LAST_BYTE_RV="$(tail -c1 "${REPO8}/${RV_VER_REL}" | od -An -tx1 | tr -d ' \n')"
449
+ assert_false "ar: .research-view-version last byte is not a newline (0a)" "[ '$LAST_BYTE_RV' = '0a' ]"
450
+
451
+ # research-view.sh RESEARCH_VIEW_VERSION literal equals the new semver.
452
+ assert_true "ar: research-view.sh RESEARCH_VIEW_VERSION literal == new" \
453
+ "grep -q '^RESEARCH_VIEW_VERSION=\"1.2.4\"\$' '${REPO8}/${RV_SH_REL}'"
454
+ assert_false "ar: old RESEARCH_VIEW_VERSION literal is gone" \
455
+ "grep -q '^RESEARCH_VIEW_VERSION=\"1.2.3\"\$' '${REPO8}/${RV_SH_REL}'"
456
+
457
+ # plugin.json manifests and package metadata advanced.
458
+ assert_eq "ar: claude plugin.json bumped" "1.2.4" \
459
+ "$(jq -r '.version' "${REPO8}/plugins/agentic-research/.claude-plugin/plugin.json")"
460
+ assert_eq "ar: codex plugin.json bumped" "1.2.4" \
461
+ "$(jq -r '.version' "${REPO8}/plugins/agentic-research/.codex-plugin/plugin.json")"
462
+ assert_eq "ar: package.json bumped" "1.2.4" \
463
+ "$(jq -r '.version' "${REPO8}/plugins/agentic-research/package.json")"
464
+
465
+ # Both research-view files are git-staged.
466
+ assert_true "ar: claude plugin.json is staged" \
467
+ "is_staged '$REPO8' 'plugins/agentic-research/.claude-plugin/plugin.json'"
468
+ assert_true "ar: codex plugin.json is staged" \
469
+ "is_staged '$REPO8' 'plugins/agentic-research/.codex-plugin/plugin.json'"
470
+ assert_true "ar: .research-view-version is staged" "is_staged '$REPO8' '$RV_VER_REL'"
471
+ assert_true "ar: research-view.sh is staged" "is_staged '$REPO8' '$RV_SH_REL'"
472
+ assert_true "ar: agentic-research package.json is staged" \
473
+ "is_staged '$REPO8' 'plugins/agentic-research/package.json'"
474
+
475
+ # agentic-research bump must NOT touch work-view artifacts.
476
+ assert_eq "ar-bump: .work-view-version unchanged" "1.2.3" \
477
+ "$(cat "${REPO8}/${VER_REL}")"
478
+ assert_true "ar-bump: work-view.sh WORK_VIEW_VERSION unchanged" \
479
+ "grep -q '^WORK_VIEW_VERSION=\"1.2.3\"\$' '${REPO8}/${WV_REL}'"
480
+ assert_false "ar-bump: .work-view-version NOT staged" \
481
+ "is_staged '$REPO8' '$VER_REL'"
482
+ assert_false "ar-bump: work-view.sh NOT staged" \
483
+ "is_staged '$REPO8' '$WV_REL'"
484
+
485
+ rm -rf "$REPO8"
486
+
487
+ # ---------------------------------------------------------------------------
488
+ # Test group 8: agentic-research Fail-Fast postcondition fires on indented literal
489
+ # ---------------------------------------------------------------------------
490
+ echo ""
491
+ echo "=== Test group 8: ar postcondition exits 1 when literal anchor no longer matches ==="
492
+
493
+ REPO9="$(new_scratch_repo "1.2.3")"
494
+ # Break the anchored literal: indent it so `^RESEARCH_VIEW_VERSION="..."` no longer
495
+ # matches. sed exits 0 (no match), so the projection silently no-ops and the grep
496
+ # postcondition must catch it.
497
+ cat > "${REPO9}/${RV_SH_REL}" <<'EOF'
498
+ #!/usr/bin/env bash
499
+ RESEARCH_VIEW_VERSION="1.2.3"
500
+ echo "research-view ${RESEARCH_VIEW_VERSION}"
501
+ EOF
502
+ ( cd "$REPO9" && "$REAL_GIT" add -A && "$REAL_GIT" commit -q -m "indent rv literal" >/dev/null 2>&1 )
503
+
504
+ run_bump "$REPO9" agentic-research patch
505
+
506
+ assert_eq "ar: indented-literal bump exits 1 (postcondition fires)" "1" "$BUMP_RC"
507
+ AR_POST_ERR="$(cat "$BUMP_ERR" 2>/dev/null || true)"
508
+ assert_true "ar: postcondition reports failed projection" \
509
+ "printf '%s' \"\$AR_POST_ERR\" | grep -q 'failed to project version into'"
510
+ # The indented literal was not rewritten.
511
+ assert_false "ar: indented literal NOT rewritten to new semver" \
512
+ "grep -q 'RESEARCH_VIEW_VERSION=\"1.2.4\"' '${REPO9}/${RV_SH_REL}'"
513
+
514
+ rm -rf "$REPO9"
515
+
516
+ # ---------------------------------------------------------------------------
517
+ # Test group 9: agentic-research minor/major bumps project correctly
518
+ # ---------------------------------------------------------------------------
519
+ echo ""
520
+ echo "=== Test group 9: ar minor and major bumps project lockstep ==="
521
+
522
+ REPO10="$(new_scratch_repo "1.2.3")"
523
+ run_bump "$REPO10" agentic-research minor
524
+ assert_eq "ar: minor bump exits 0" "0" "$BUMP_RC"
525
+ assert_eq "ar: minor .research-view-version == 1.3.0" "1.3.0" "$(cat "${REPO10}/${RV_VER_REL}")"
526
+ assert_true "ar: minor research-view.sh literal == 1.3.0" \
527
+ "grep -q '^RESEARCH_VIEW_VERSION=\"1.3.0\"\$' '${REPO10}/${RV_SH_REL}'"
528
+ rm -rf "$REPO10"
529
+
530
+ REPO11="$(new_scratch_repo "1.2.3")"
531
+ run_bump "$REPO11" agentic-research major
532
+ assert_eq "ar: major bump exits 0" "0" "$BUMP_RC"
533
+ assert_eq "ar: major .research-view-version == 2.0.0" "2.0.0" "$(cat "${REPO11}/${RV_VER_REL}")"
534
+ assert_true "ar: major research-view.sh literal == 2.0.0" \
535
+ "grep -q '^RESEARCH_VIEW_VERSION=\"2.0.0\"\$' '${REPO11}/${RV_SH_REL}'"
536
+ rm -rf "$REPO11"
537
+
538
+ # ---------------------------------------------------------------------------
539
+ # Summary
540
+ # ---------------------------------------------------------------------------
541
+ echo ""
542
+ echo "============================================================"
543
+ echo "Results: ${PASS} passed, ${FAIL} failed"
544
+ if [ ${#ERRORS[@]} -gt 0 ]; then
545
+ echo "Failed tests:"
546
+ for e in "${ERRORS[@]}"; do
547
+ echo " - $e"
548
+ done
549
+ fi
550
+ echo "============================================================"
551
+
552
+ [ "$FAIL" -eq 0 ] || exit 1
@@ -0,0 +1,117 @@
1
+ #!/usr/bin/env bash
2
+ # channel-parity.test.sh — guard agile-workflow's Claude/Codex/Pi behavior parity.
3
+
4
+ set -uo pipefail
5
+
6
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
7
+ REPO_ROOT="$(cd "${SCRIPT_DIR}/../../../.." && pwd)"
8
+ PLUGIN_ROOT="${REPO_ROOT}/plugins/agile-workflow"
9
+ ROOT_PACKAGE="${REPO_ROOT}/package.json"
10
+
11
+ PASS=0
12
+ FAIL=0
13
+ ERRORS=()
14
+
15
+ assert_true() {
16
+ local label="$1"
17
+ if eval "$2"; then
18
+ echo " PASS: $label"
19
+ ((PASS++))
20
+ else
21
+ echo " FAIL: $label"
22
+ echo " condition false: $2"
23
+ ((FAIL++))
24
+ ERRORS+=("$label")
25
+ fi
26
+ }
27
+
28
+ assert_eq() {
29
+ local label="$1" expected="$2" actual="$3"
30
+ if [ "$expected" = "$actual" ]; then
31
+ echo " PASS: $label"
32
+ ((PASS++))
33
+ else
34
+ echo " FAIL: $label"
35
+ echo " expected: $(printf '%q' "$expected")"
36
+ echo " actual: $(printf '%q' "$actual")"
37
+ ((FAIL++))
38
+ ERRORS+=("$label")
39
+ fi
40
+ }
41
+
42
+ json_value() {
43
+ jq -r "$1" "$2"
44
+ }
45
+
46
+ echo ""
47
+ echo "=== Agile-workflow channel parity ==="
48
+ assert_true "jq is available" "command -v jq >/dev/null 2>&1"
49
+
50
+ HOOKS_JSON="${PLUGIN_ROOT}/hooks/hooks.json"
51
+ PROMPT_CONTEXT="${PLUGIN_ROOT}/hooks/scripts/prompt-context.py"
52
+ MAINTAINER="${PLUGIN_ROOT}/hooks/scripts/substrate-maintainer.py"
53
+ PI_EXTENSION="${PLUGIN_ROOT}/extensions/agile-workflow.ts"
54
+ PI_TEST="${PLUGIN_ROOT}/extensions/agile-workflow.test.ts"
55
+
56
+ assert_true "shared hooks manifest exists" "[ -f '${HOOKS_JSON}' ]"
57
+ assert_true "shared prompt-context hook exists" "[ -f '${PROMPT_CONTEXT}' ]"
58
+ assert_true "shared substrate-maintainer hook exists" "[ -f '${MAINTAINER}' ]"
59
+
60
+ assert_eq "Codex manifest points at shared hooks" "./hooks/hooks.json" \
61
+ "$(json_value '.hooks // empty' "${PLUGIN_ROOT}/.codex-plugin/plugin.json")"
62
+ assert_true "Claude plugin relies on hooks auto-discovery" \
63
+ "[ -f '${PLUGIN_ROOT}/.claude-plugin/plugin.json' ] && [ -f '${HOOKS_JSON}' ]"
64
+ assert_eq "Pi package loads agile extension directory" "[\"./extensions\"]" \
65
+ "$(jq -c '.pi.extensions' "${PLUGIN_ROOT}/package.json")"
66
+ assert_true "root Pi package includes agile-workflow extension" \
67
+ "jq -e '.pi.extensions | index(\"./plugins/agile-workflow/extensions/agile-workflow.ts\")' '${ROOT_PACKAGE}' >/dev/null"
68
+
69
+ assert_true "hooks manifest has SessionStart" "jq -e '.hooks.SessionStart' '${HOOKS_JSON}' >/dev/null"
70
+ assert_true "hooks manifest has UserPromptSubmit" "jq -e '.hooks.UserPromptSubmit' '${HOOKS_JSON}' >/dev/null"
71
+ assert_true "hooks manifest has PostCompact" "jq -e '.hooks.PostCompact' '${HOOKS_JSON}' >/dev/null"
72
+ assert_true "hooks manifest has PostToolUse" "jq -e '.hooks.PostToolUse' '${HOOKS_JSON}' >/dev/null"
73
+
74
+ assert_true "Pi extension invokes prompt-context shared script" \
75
+ "grep -q 'prompt-context.py' '${PI_EXTENSION}'"
76
+ assert_true "Pi extension invokes substrate-maintainer shared script" \
77
+ "grep -q 'substrate-maintainer.py' '${PI_EXTENSION}'"
78
+ assert_true "Pi extension maps before_agent_start" \
79
+ "grep -q 'before_agent_start' '${PI_EXTENSION}'"
80
+ assert_true "Pi extension maps session_start" \
81
+ "grep -q 'session_start' '${PI_EXTENSION}'"
82
+ assert_true "Pi extension maps session_compact" \
83
+ "grep -q 'session_compact' '${PI_EXTENSION}'"
84
+ assert_true "Pi extension maps tool_result" \
85
+ "grep -q 'tool_result' '${PI_EXTENSION}'"
86
+ assert_true "Pi extension uses synthetic PiBeforeAgentStart rules path" \
87
+ "grep -q 'PiBeforeAgentStart' '${PI_EXTENSION}' && grep -q 'PiBeforeAgentStart' '${PROMPT_CONTEXT}'"
88
+ assert_true "prompt-context supports forced rules for Pi rebuilt prompts" \
89
+ "grep -q 'force_rules_context' '${PI_EXTENSION}' && grep -q 'force: bool = False' '${PROMPT_CONTEXT}'"
90
+ assert_true "Pi extension injects principles as visible hook-context message" \
91
+ "grep -q 'agile-workflow-principles' '${PI_EXTENSION}'"
92
+
93
+ assert_true "Pi tests cover before_agent_start parity" \
94
+ "grep -q 'before_agent_start' '${PI_TEST}' && grep -q 'shared .agents/rules content' '${PI_TEST}'"
95
+ assert_true "Pi tests cover substrate maintenance parity" \
96
+ "grep -q 'runs substrate maintenance after mutating tools' '${PI_TEST}'"
97
+
98
+ assert_true "AGENTS states channel parity posture" \
99
+ "grep -q 'Channel parity posture' '${REPO_ROOT}/AGENTS.md'"
100
+
101
+ assert_true "SPEC documents Pi hook parity adapter" \
102
+ "grep -q 'Pi hook parity adapter' '${PLUGIN_ROOT}/docs/SPEC.md'"
103
+ assert_true "ARCHITECTURE documents Pi hook parity adapter" \
104
+ "grep -q 'Pi hook parity adapter' '${PLUGIN_ROOT}/docs/ARCHITECTURE.md'"
105
+
106
+ echo ""
107
+ echo "============================================================"
108
+ echo "Results: ${PASS} passed, ${FAIL} failed"
109
+ if [ ${#ERRORS[@]} -gt 0 ]; then
110
+ echo "Failed tests:"
111
+ for e in "${ERRORS[@]}"; do
112
+ echo " - $e"
113
+ done
114
+ fi
115
+ echo "============================================================"
116
+
117
+ [ "$FAIL" -eq 0 ] || exit 1