@house365/comate 0.10.3 → 1.0.2-next.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -21
- package/README.md +104 -64
- package/bin/cli.mjs +197 -190
- package/bin/doctor.mjs +29 -41
- package/bin/init-run.mjs +310 -368
- package/bin/init.mjs +53 -71
- package/bin/postinstall.mjs +56 -56
- package/docs/CHANGELOG-fix-simplification.md +145 -0
- package/docs/COMATE_BETA.md +90 -0
- package/docs/COMATE_MANUAL.md +239 -665
- package/docs/superpowers/plans/2026-10-14-review-fixes.md +48 -0
- package/docs/superpowers/plans/2026-10-14-workflow-quality.md +48 -0
- package/package.json +54 -55
- package/skills/comate/SKILL.md +42 -325
- package/skills/comate/reference/archive.md +50 -0
- package/skills/comate/reference/build.md +67 -0
- package/skills/comate/reference/fix.md +150 -0
- package/skills/comate/reference/open.md +162 -0
- package/skills/comate/reference/verify.md +123 -0
- package/skills/comate/scaffold/comate/README.md +18 -0
- package/skills/{comate-gate/scaffold/docs → comate/scaffold/comate}/architecture/README.md +5 -6
- package/skills/comate/scaffold/comate/comate/config.json +10 -0
- package/skills/comate/scaffold/comate/knowledge-base/README.md +42 -0
- package/skills/{comate-gate/scaffold/docs → comate/scaffold/comate}/knowledge-base/integrations/_TEMPLATE/README.md +2 -2
- package/skills/{comate-gate/scaffold/docs → comate/scaffold/comate}/knowledge-base/integrations/_TEMPLATE/api-snapshot.md +47 -47
- package/skills/{comate-gate/scaffold/docs → comate/scaffold/comate}/knowledge-base/requirements/_templates/PRD.md +4 -4
- package/skills/comate/scripts/comate.mjs +147 -0
- package/skills/comate/scripts/lib/acceptance.mjs +112 -0
- package/skills/comate/scripts/lib/brief.mjs +50 -0
- package/skills/comate/scripts/lib/checks.mjs +178 -0
- package/skills/comate/scripts/lib/cmd-archive.mjs +152 -0
- package/skills/comate/scripts/lib/cmd-decide.mjs +116 -0
- package/skills/comate/scripts/lib/cmd-init.mjs +75 -0
- package/skills/comate/scripts/lib/cmd-loop.mjs +273 -0
- package/skills/comate/scripts/lib/cmd-open.mjs +161 -0
- package/skills/comate/scripts/lib/cmd-status.mjs +121 -0
- package/skills/comate/scripts/lib/common.mjs +85 -0
- package/skills/comate/scripts/lib/docs.mjs +45 -0
- package/skills/comate/scripts/lib/finish.mjs +108 -0
- package/skills/comate/scripts/lib/git.mjs +112 -0
- package/skills/comate/scripts/lib/md.mjs +85 -0
- package/skills/comate/scripts/lib/next.mjs +217 -0
- package/skills/comate/scripts/lib/paths.mjs +96 -0
- package/skills/comate/scripts/lib/render.mjs +46 -0
- package/skills/comate/scripts/lib/spec.mjs +106 -0
- package/skills/comate/scripts/lib/state.mjs +128 -0
- package/skills/comate/scripts/lib/tasks.mjs +53 -0
- package/skills/comate/scripts/lib/templates.mjs +58 -0
- package/skills/comate/scripts/lib/util.mjs +108 -0
- package/skills/comate-kb-integration/SKILL.md +128 -128
- package/skills/comate-kb-integration/scripts/fetch_rendered_doc.py +84 -84
- package/bin/cleanup-duplicate-superpowers.mjs +0 -119
- package/docs/COMATE_BEGINNER_GUIDE.md +0 -188
- package/docs/COMATE_INIT_DIRECTORY_GUIDE.md +0 -201
- package/docs/superpowers/plans/2026-09-13-fast-workflow.md +0 -62
- package/skills/comate/reference/decision-points.md +0 -103
- package/skills/comate/reference/dirty-worktree.md +0 -58
- package/skills/comate/reference/prerequisites.md +0 -89
- package/skills/comate/scripts/comate-lib.sh +0 -559
- package/skills/comate/scripts/comate.sh +0 -2147
- package/skills/comate/tests/comate.bats +0 -1019
- package/skills/comate/tests/run.sh +0 -101
- package/skills/comate-archive/SKILL.md +0 -63
- package/skills/comate-build/SKILL.md +0 -278
- package/skills/comate-design/SKILL.md +0 -178
- package/skills/comate-fix/SKILL.md +0 -195
- package/skills/comate-gate/SKILL.md +0 -319
- package/skills/comate-gate/scaffold/docs/README.md +0 -36
- package/skills/comate-gate/scaffold/docs/knowledge-base/README.md +0 -47
- package/skills/comate-grill/SKILL.md +0 -185
- package/skills/comate-open/SKILL.md +0 -240
- package/skills/comate-verify/SKILL.md +0 -212
- /package/skills/{comate-gate/scaffold/docs → comate/scaffold/comate}/knowledge-base/integrations/README.md +0 -0
|
@@ -1,1019 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bats
|
|
2
|
-
# comate state-machine + library tests.
|
|
3
|
-
# Run with: bats skills/comate/tests/comate.bats
|
|
4
|
-
# (or: bash skills/comate/tests/run.sh — plain-bash fallback, no bats needed)
|
|
5
|
-
|
|
6
|
-
setup() {
|
|
7
|
-
COMATE_ROOT="$(cd "$BATS_TEST_DIRNAME/../.." && pwd)"
|
|
8
|
-
COMATE="$COMATE_ROOT/comate/scripts/comate.sh"
|
|
9
|
-
[ -f "$COMATE" ] || COMATE="$BATS_TEST_DIRNAME/../scripts/comate.sh"
|
|
10
|
-
export COMATE_SKIP_BUILD=1
|
|
11
|
-
WORK="$(mktemp -d)"
|
|
12
|
-
cd "$WORK"
|
|
13
|
-
git init -q
|
|
14
|
-
git config user.email t@t.t
|
|
15
|
-
git config user.name t
|
|
16
|
-
git commit -q --allow-empty -m init
|
|
17
|
-
mkdir -p openspec/changes
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
teardown() {
|
|
21
|
-
cd /
|
|
22
|
-
rm -rf "$WORK"
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
comate() { bash "$COMATE" "$@"; }
|
|
26
|
-
|
|
27
|
-
seed_open() {
|
|
28
|
-
comate state demo init full >/dev/null
|
|
29
|
-
local d="openspec/changes/demo"
|
|
30
|
-
printf '# proposal\n' > "$d/proposal.md"
|
|
31
|
-
printf '# design\n' > "$d/design.md"
|
|
32
|
-
printf -- '- [ ] t1\n' > "$d/tasks.md"
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
# --- init / get / set ---
|
|
36
|
-
|
|
37
|
-
@test "init writes canonical field order with schema_version" {
|
|
38
|
-
comate state demo init full
|
|
39
|
-
run comate state demo get schema_version
|
|
40
|
-
[ "$status" -eq 0 ]
|
|
41
|
-
[ "$output" = "1" ]
|
|
42
|
-
run comate state demo get phase
|
|
43
|
-
[ "$output" = "open" ]
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
@test "init full leaves build decisions null" {
|
|
47
|
-
comate state demo init full
|
|
48
|
-
[ "$(comate state demo get build_mode)" = "null" ]
|
|
49
|
-
[ "$(comate state demo get tdd_mode)" = "null" ]
|
|
50
|
-
[ "$(comate state demo get isolation)" = "null" ]
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
@test "init fix prefills preset defaults" {
|
|
54
|
-
comate state demo init fix
|
|
55
|
-
[ "$(comate state demo get build_mode)" = "direct" ]
|
|
56
|
-
[ "$(comate state demo get tdd_mode)" = "direct" ]
|
|
57
|
-
[ "$(comate state demo get isolation)" = "branch" ]
|
|
58
|
-
[ "$(comate state demo get verify_mode)" = "light" ]
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
@test "init records base_ref and base_branch" {
|
|
62
|
-
comate state demo init full
|
|
63
|
-
run comate state demo get base_ref
|
|
64
|
-
[ "$output" != "null" ]
|
|
65
|
-
run comate state demo get base_branch
|
|
66
|
-
[ "$output" != "null" ]
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
@test "init twice fails with usage error" {
|
|
70
|
-
comate state demo init full
|
|
71
|
-
run comate state demo init full
|
|
72
|
-
[ "$status" -eq 2 ]
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
@test "set rejects unknown field" {
|
|
76
|
-
comate state demo init full
|
|
77
|
-
run comate state demo set bogus_field x
|
|
78
|
-
[ "$status" -eq 2 ]
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
@test "set rejects bad enum value" {
|
|
82
|
-
comate state demo init full
|
|
83
|
-
run comate state demo set phase nonsense
|
|
84
|
-
[ "$status" -eq 2 ]
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
@test "set rejects bad tdd_mode value" {
|
|
88
|
-
comate state demo init full
|
|
89
|
-
run comate state demo set tdd_mode maybe
|
|
90
|
-
[ "$status" -eq 2 ]
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
@test "set is idempotent and keeps single key" {
|
|
94
|
-
comate state demo init full
|
|
95
|
-
comate state demo set verify_mode light >/dev/null
|
|
96
|
-
comate state demo set verify_mode full >/dev/null
|
|
97
|
-
local count
|
|
98
|
-
count=$(grep -c '^verify_mode:' "openspec/changes/demo/.comate.yaml")
|
|
99
|
-
[ "$count" -eq 1 ]
|
|
100
|
-
[ "$(comate state demo get verify_mode)" = "full" ]
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
# --- input validation ---
|
|
104
|
-
|
|
105
|
-
@test "path traversal in change name rejected" {
|
|
106
|
-
run comate state "../evil" get phase
|
|
107
|
-
[ "$status" -eq 2 ]
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
@test "get on missing yaml returns corrupt code" {
|
|
111
|
-
run comate state nope get phase
|
|
112
|
-
[ "$status" -eq 4 ]
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
# --- flat yaml validation ---
|
|
116
|
-
|
|
117
|
-
@test "validate passes on clean init" {
|
|
118
|
-
comate state demo init full
|
|
119
|
-
run comate state demo validate
|
|
120
|
-
[ "$status" -eq 0 ]
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
@test "validate rejects nested yaml with corrupt code" {
|
|
124
|
-
comate state demo init full
|
|
125
|
-
printf 'nested:\n child: 1\n' >> "openspec/changes/demo/.comate.yaml"
|
|
126
|
-
run comate state demo validate
|
|
127
|
-
[ "$status" -eq 4 ]
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
@test "validate rejects list items" {
|
|
131
|
-
comate state demo init full
|
|
132
|
-
printf -- '- item\n' >> "openspec/changes/demo/.comate.yaml"
|
|
133
|
-
run comate state demo validate
|
|
134
|
-
[ "$status" -eq 4 ]
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
# --- state machine transitions ---
|
|
138
|
-
|
|
139
|
-
@test "transition rejects wrong-phase event" {
|
|
140
|
-
comate state demo init full
|
|
141
|
-
run comate state demo transition design-complete
|
|
142
|
-
[ "$status" -eq 1 ]
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
@test "open-complete routes full to design" {
|
|
146
|
-
seed_open
|
|
147
|
-
comate state demo set phase_step open.review-confirmed >/dev/null
|
|
148
|
-
comate state demo transition open-complete
|
|
149
|
-
[ "$(comate state demo get phase)" = "design" ]
|
|
150
|
-
[ "$(comate state demo get phase_step)" = "null" ]
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
@test "open-complete routes fix to build" {
|
|
154
|
-
comate state demo init fix
|
|
155
|
-
comate state demo transition open-complete
|
|
156
|
-
[ "$(comate state demo get phase)" = "build" ]
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
@test "build-complete blocked without isolation" {
|
|
160
|
-
comate state demo init fix
|
|
161
|
-
comate state demo transition open-complete
|
|
162
|
-
comate state demo set isolation null >/dev/null
|
|
163
|
-
run comate state demo transition build-complete
|
|
164
|
-
[ "$status" -eq 1 ]
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
@test "full direct build blocked without override" {
|
|
168
|
-
comate state demo init full
|
|
169
|
-
comate state demo set isolation branch >/dev/null
|
|
170
|
-
comate state demo set build_mode direct >/dev/null
|
|
171
|
-
comate state demo set tdd_mode direct >/dev/null
|
|
172
|
-
# force phase to build to isolate the decision check
|
|
173
|
-
comate state demo set phase build >/dev/null
|
|
174
|
-
run comate state demo transition build-complete
|
|
175
|
-
[ "$status" -eq 1 ]
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
@test "build-complete blocked without tdd_mode" {
|
|
179
|
-
comate state demo init full
|
|
180
|
-
comate state demo set isolation branch >/dev/null
|
|
181
|
-
comate state demo set build_mode executing-plans >/dev/null
|
|
182
|
-
comate state demo set phase build >/dev/null
|
|
183
|
-
run comate state demo transition build-complete
|
|
184
|
-
[ "$status" -eq 1 ]
|
|
185
|
-
[[ "$output" == *"tdd_mode"* ]]
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
@test "build-complete accepts tdd_mode tdd" {
|
|
189
|
-
comate state demo init full
|
|
190
|
-
comate state demo set isolation branch >/dev/null
|
|
191
|
-
comate state demo set build_mode executing-plans >/dev/null
|
|
192
|
-
comate state demo set tdd_mode tdd >/dev/null
|
|
193
|
-
comate state demo set phase build >/dev/null
|
|
194
|
-
run comate state demo transition build-complete
|
|
195
|
-
[ "$status" -eq 0 ]
|
|
196
|
-
[ "$(comate state demo get phase)" = "verify" ]
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
@test "verify-pass blocked without report" {
|
|
200
|
-
comate state demo init full
|
|
201
|
-
comate state demo set phase verify >/dev/null
|
|
202
|
-
run comate state demo transition verify-pass
|
|
203
|
-
[ "$status" -eq 1 ]
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
@test "fix-upgrade resets preset decisions and enters design" {
|
|
207
|
-
comate state demo init fix
|
|
208
|
-
comate state demo transition open-complete
|
|
209
|
-
comate state demo set phase_step build.options-confirmed >/dev/null
|
|
210
|
-
comate state demo transition fix-upgrade
|
|
211
|
-
[ "$(comate state demo get workflow)" = "full" ]
|
|
212
|
-
[ "$(comate state demo get phase)" = "design" ]
|
|
213
|
-
[ "$(comate state demo get build_mode)" = "null" ]
|
|
214
|
-
[ "$(comate state demo get tdd_mode)" = "null" ]
|
|
215
|
-
[ "$(comate state demo get isolation)" = "null" ]
|
|
216
|
-
[ "$(comate state demo get phase_step)" = "null" ]
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
@test "discarded branch cannot pass verification" {
|
|
220
|
-
comate state demo init full
|
|
221
|
-
comate state demo set phase verify >/dev/null
|
|
222
|
-
printf '# pass\n' > report.md
|
|
223
|
-
comate state demo set verification_report report.md >/dev/null
|
|
224
|
-
comate state demo set branch_status discarded >/dev/null
|
|
225
|
-
run comate state demo transition verify-pass
|
|
226
|
-
[ "$status" -eq 1 ]
|
|
227
|
-
[ "$(comate state demo get phase)" = "verify" ]
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
@test "verify-fail returns to build without a failure routing loop" {
|
|
231
|
-
comate state demo init full
|
|
232
|
-
comate state demo set phase verify >/dev/null
|
|
233
|
-
comate state demo set verify_result fail >/dev/null
|
|
234
|
-
comate state demo transition verify-fail
|
|
235
|
-
[ "$(comate state demo get phase)" = "build" ]
|
|
236
|
-
[ "$(comate state demo get verify_result)" = "pending" ]
|
|
237
|
-
[ "$(comate state demo get verification_report)" = "null" ]
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
@test "verify-fail keeps build authoritative when tasks remain checked" {
|
|
241
|
-
comate state demo init full
|
|
242
|
-
printf '%s\n' '# Tasks' '- [x] already implemented' > openspec/changes/demo/tasks.md
|
|
243
|
-
comate state demo set phase verify >/dev/null
|
|
244
|
-
comate state demo set verify_result fail >/dev/null
|
|
245
|
-
comate state demo transition verify-fail
|
|
246
|
-
[ "$(comate state demo get phase)" = "build" ]
|
|
247
|
-
[ "$(comate state demo get verify_result)" = "pending" ]
|
|
248
|
-
# The router must use the persisted phase; task completion is not a phase.
|
|
249
|
-
[ "$(comate state demo get phase)" != "verify" ]
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
@test "recover reports phase-local progress" {
|
|
253
|
-
comate state demo init full
|
|
254
|
-
comate state demo set phase_step open.review-awaiting >/dev/null
|
|
255
|
-
run comate state demo recover open
|
|
256
|
-
[ "$status" -eq 0 ]
|
|
257
|
-
[[ "$output" == *"Phase step: open.review-awaiting"* ]]
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
@test "status exposes awaiting decisions" {
|
|
261
|
-
comate state demo init full
|
|
262
|
-
comate state demo set phase_step open.review-awaiting >/dev/null
|
|
263
|
-
run comate status
|
|
264
|
-
[ "$status" -eq 0 ]
|
|
265
|
-
[[ "$output" == *"step: open.review-awaiting"* ]]
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
# --- guard open ---
|
|
269
|
-
|
|
270
|
-
@test "guard open passes with full artifacts and applies" {
|
|
271
|
-
seed_open
|
|
272
|
-
printf -- '- [x] t1\n' > "openspec/changes/demo/tasks.md"
|
|
273
|
-
run comate guard demo open --apply
|
|
274
|
-
[ "$status" -eq 0 ]
|
|
275
|
-
[ "$(comate state demo get phase)" = "design" ]
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
@test "fix guards continuously advance open to build to verify" {
|
|
279
|
-
comate state demo init fix
|
|
280
|
-
local d="openspec/changes/demo"
|
|
281
|
-
printf '# p\n' > "$d/proposal.md"
|
|
282
|
-
printf '# d\n' > "$d/design.md"
|
|
283
|
-
printf -- '- [x] t1\n' > "$d/tasks.md"
|
|
284
|
-
|
|
285
|
-
comate guard demo open --apply >/dev/null 2>&1
|
|
286
|
-
[ "$(comate state demo get phase)" = "build" ]
|
|
287
|
-
|
|
288
|
-
comate handoff demo build --write >/dev/null 2>&1
|
|
289
|
-
comate guard demo build --apply >/dev/null 2>&1
|
|
290
|
-
[ "$(comate state demo get phase)" = "verify" ]
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
@test "guard open json emits block false" {
|
|
294
|
-
seed_open
|
|
295
|
-
run comate guard demo open --json
|
|
296
|
-
[ "$status" -eq 0 ]
|
|
297
|
-
[[ "$output" == *'"block":false'* ]]
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
# --- handoff design + hash drift ---
|
|
301
|
-
|
|
302
|
-
@test "design handoff writes context and hash" {
|
|
303
|
-
seed_open
|
|
304
|
-
comate guard demo open --apply >/dev/null 2>&1
|
|
305
|
-
comate handoff demo design --write
|
|
306
|
-
run comate state demo get handoff_hash
|
|
307
|
-
[[ "$output" =~ ^[a-f0-9]{64}$ ]]
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
@test "design guard blocks on handoff hash drift" {
|
|
311
|
-
seed_open
|
|
312
|
-
comate guard demo open --apply >/dev/null 2>&1
|
|
313
|
-
comate handoff demo design --write >/dev/null 2>&1
|
|
314
|
-
printf 'edited after handoff\n' >> "openspec/changes/demo/proposal.md"
|
|
315
|
-
run comate guard demo design --json
|
|
316
|
-
[ "$status" -eq 1 ]
|
|
317
|
-
[[ "$output" == *'"block":true'* ]]
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
# --- build->verify drift ---
|
|
321
|
-
|
|
322
|
-
@test "verify guard blocks when tasks change after build handoff" {
|
|
323
|
-
comate state demo init fix
|
|
324
|
-
local d="openspec/changes/demo"
|
|
325
|
-
printf '# p\n' > "$d/proposal.md"; printf '# d\n' > "$d/design.md"; printf -- '- [x] t1\n' > "$d/tasks.md"
|
|
326
|
-
comate guard demo open --apply >/dev/null 2>&1
|
|
327
|
-
comate handoff demo build --write >/dev/null 2>&1
|
|
328
|
-
comate guard demo build --apply >/dev/null 2>&1
|
|
329
|
-
printf 'rep\n' > "$d/rep.md"
|
|
330
|
-
comate state demo set verification_report "$d/rep.md" >/dev/null
|
|
331
|
-
comate state demo set branch_status handled >/dev/null
|
|
332
|
-
# tamper tasks after build exit
|
|
333
|
-
printf -- '- [x] t1\n- [x] sneaked\n' > "$d/tasks.md"
|
|
334
|
-
run comate guard demo verify --json
|
|
335
|
-
[ "$status" -eq 1 ]
|
|
336
|
-
[[ "$output" == *'impl_hash'*'fail'* ]] || [[ "$output" == *'"block":true'* ]]
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
# --- line-ending stability ---
|
|
340
|
-
|
|
341
|
-
@test "impl_hash ignores text line-ending changes" {
|
|
342
|
-
comate state demo init fix
|
|
343
|
-
local d="openspec/changes/demo"
|
|
344
|
-
printf '# p\n' > "$d/proposal.md"
|
|
345
|
-
printf '# d\n' > "$d/design.md"
|
|
346
|
-
printf '# plan\n' > "$d/plan.md"
|
|
347
|
-
printf -- '- [x] t1\n' > "$d/tasks.md"
|
|
348
|
-
comate state demo set plan "$d/plan.md" >/dev/null
|
|
349
|
-
comate guard demo open --apply >/dev/null 2>&1
|
|
350
|
-
comate handoff demo build --write >/dev/null 2>&1
|
|
351
|
-
comate guard demo build --apply >/dev/null 2>&1
|
|
352
|
-
|
|
353
|
-
printf '# plan\r\n' > "$d/plan.md"
|
|
354
|
-
printf -- '- [x] t1\r\n' > "$d/tasks.md"
|
|
355
|
-
printf 'verification\n' > "$d/verification.md"
|
|
356
|
-
comate state demo set verification_report "$d/verification.md" >/dev/null
|
|
357
|
-
comate state demo set branch_status handled >/dev/null
|
|
358
|
-
run comate guard demo verify --json
|
|
359
|
-
[ "$status" -eq 0 ]
|
|
360
|
-
[[ "$output" == *'impl_hash matches current plan+tasks'*'pass'* ]]
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
# --- archive ---
|
|
364
|
-
|
|
365
|
-
@test "archive delegates spec merge to openspec instead of copying delta over main" {
|
|
366
|
-
comate state demo init full
|
|
367
|
-
local d="openspec/changes/demo"
|
|
368
|
-
printf '# p\n' > "$d/proposal.md"
|
|
369
|
-
printf '# d\n' > "$d/design.md"
|
|
370
|
-
printf -- '- [x] t1\n' > "$d/tasks.md"
|
|
371
|
-
mkdir -p "$d/specs/cap" openspec/specs/cap bin
|
|
372
|
-
printf '# Main Spec\n\n## Requirements\n\n### Requirement: Existing\n' > openspec/specs/cap/spec.md
|
|
373
|
-
printf '## ADDED Requirements\n\n### Requirement: Delta Only\n' > "$d/specs/cap/spec.md"
|
|
374
|
-
|
|
375
|
-
cat > bin/openspec <<'SH'
|
|
376
|
-
#!/bin/bash
|
|
377
|
-
set -e
|
|
378
|
-
[ "$1" = "archive" ] || exit 2
|
|
379
|
-
name="$2"
|
|
380
|
-
mkdir -p openspec/changes/archive
|
|
381
|
-
mv "openspec/changes/$name" "openspec/changes/archive/$(date +%Y-%m-%d)-$name"
|
|
382
|
-
SH
|
|
383
|
-
chmod +x bin/openspec
|
|
384
|
-
export PATH="$PWD/bin:$PATH"
|
|
385
|
-
|
|
386
|
-
comate state demo set phase archive >/dev/null
|
|
387
|
-
comate state demo set verify_result pass >/dev/null
|
|
388
|
-
run comate archive demo
|
|
389
|
-
[ "$status" -eq 0 ]
|
|
390
|
-
[[ "$(cat openspec/specs/cap/spec.md)" == *"Requirement: Existing"* ]]
|
|
391
|
-
[[ "$(cat openspec/specs/cap/spec.md)" != *"Delta Only"* ]]
|
|
392
|
-
[ "$(comate state "$(date +%Y-%m-%d)-demo" get archived)" = "true" ]
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
@test "archive repoints .comate.yaml self-pointers to archive path" {
|
|
396
|
-
comate state demo init full
|
|
397
|
-
local d="openspec/changes/demo"
|
|
398
|
-
printf '# p\n' > "$d/proposal.md"; printf '# d\n' > "$d/design.md"; printf -- '- [x] t1\n' > "$d/tasks.md"
|
|
399
|
-
mkdir -p docs/knowledge-base/cb bin
|
|
400
|
-
printf '# source\n' > docs/knowledge-base/cb/source.md
|
|
401
|
-
comate sources demo track docs/knowledge-base/cb/source.md >/dev/null 2>&1
|
|
402
|
-
cat > bin/openspec <<'SH'
|
|
403
|
-
#!/bin/bash
|
|
404
|
-
set -e
|
|
405
|
-
[ "$1" = "archive" ] || exit 2
|
|
406
|
-
mkdir -p openspec/changes/archive
|
|
407
|
-
mv "openspec/changes/$2" "openspec/changes/archive/$(date +%Y-%m-%d)-$2"
|
|
408
|
-
SH
|
|
409
|
-
chmod +x bin/openspec
|
|
410
|
-
export PATH="$PWD/bin:$PATH"
|
|
411
|
-
comate state demo set phase archive >/dev/null
|
|
412
|
-
comate state demo set verify_result pass >/dev/null
|
|
413
|
-
run comate archive demo
|
|
414
|
-
[ "$status" -eq 0 ]
|
|
415
|
-
local an="$(date +%Y-%m-%d)-demo"
|
|
416
|
-
[ "$(comate state "$an" get source_manifest)" = "openspec/changes/archive/$an/.comate/sources.manifest" ]
|
|
417
|
-
run comate sources "$an" check
|
|
418
|
-
[ "$status" -eq 0 ]
|
|
419
|
-
}
|
|
420
|
-
|
|
421
|
-
@test "archive rewrites design doc body references to archive path" {
|
|
422
|
-
comate state demo init full
|
|
423
|
-
local d="openspec/changes/demo"
|
|
424
|
-
printf '# p\n' > "$d/proposal.md"; printf '# d\n' > "$d/design.md"; printf -- '- [x] t1\n' > "$d/tasks.md"
|
|
425
|
-
mkdir -p docs/superpowers/specs bin
|
|
426
|
-
printf -- '---\ncomate_change: demo\nrole: technical-design\ncanonical_spec: openspec\n---\nref: openspec/changes/demo/specs/cap/spec.md\n' > docs/superpowers/specs/dd.md
|
|
427
|
-
comate state demo set design_doc docs/superpowers/specs/dd.md >/dev/null
|
|
428
|
-
cat > bin/openspec <<'SH'
|
|
429
|
-
#!/bin/bash
|
|
430
|
-
set -e
|
|
431
|
-
[ "$1" = "archive" ] || exit 2
|
|
432
|
-
mkdir -p openspec/changes/archive
|
|
433
|
-
mv "openspec/changes/$2" "openspec/changes/archive/$(date +%Y-%m-%d)-$2"
|
|
434
|
-
SH
|
|
435
|
-
chmod +x bin/openspec
|
|
436
|
-
export PATH="$PWD/bin:$PATH"
|
|
437
|
-
comate state demo set phase archive >/dev/null
|
|
438
|
-
comate state demo set verify_result pass >/dev/null
|
|
439
|
-
run comate archive demo
|
|
440
|
-
[ "$status" -eq 0 ]
|
|
441
|
-
local an="$(date +%Y-%m-%d)-demo"
|
|
442
|
-
[[ "$(cat docs/superpowers/specs/dd.md)" == *"openspec/changes/archive/$an/specs/cap/spec.md"* ]]
|
|
443
|
-
[[ "$(cat docs/superpowers/specs/dd.md)" == *"comate_change: demo"* ]]
|
|
444
|
-
[[ "$(cat docs/superpowers/specs/dd.md)" == *"archived-with: $an"* ]]
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
# --- scale ---
|
|
448
|
-
|
|
449
|
-
@test "scale sets verify_mode full when many tasks" {
|
|
450
|
-
comate state demo init full
|
|
451
|
-
local d="openspec/changes/demo"
|
|
452
|
-
printf -- '- [ ] a\n- [ ] b\n- [ ] c\n- [ ] d\n' > "$d/tasks.md"
|
|
453
|
-
comate state demo scale
|
|
454
|
-
[ "$(comate state demo get verify_mode)" = "full" ]
|
|
455
|
-
}
|
|
456
|
-
|
|
457
|
-
@test "scale sets verify_mode light when few tasks" {
|
|
458
|
-
comate state demo init full
|
|
459
|
-
printf -- '- [ ] a\n' > "openspec/changes/demo/tasks.md"
|
|
460
|
-
comate state demo scale
|
|
461
|
-
[ "$(comate state demo get verify_mode)" = "light" ]
|
|
462
|
-
}
|
|
463
|
-
|
|
464
|
-
# --- source tracking ---
|
|
465
|
-
|
|
466
|
-
@test "init defaults source_kind conversation with null manifest" {
|
|
467
|
-
comate state demo init full
|
|
468
|
-
[ "$(comate state demo get source_kind)" = "conversation" ]
|
|
469
|
-
[ "$(comate state demo get source_manifest)" = "null" ]
|
|
470
|
-
}
|
|
471
|
-
|
|
472
|
-
@test "sources check on conversation source is a no-op pass" {
|
|
473
|
-
comate state demo init full
|
|
474
|
-
run comate sources demo check
|
|
475
|
-
[ "$status" -eq 0 ]
|
|
476
|
-
}
|
|
477
|
-
|
|
478
|
-
@test "sources track records kb file and flips source_kind to file" {
|
|
479
|
-
comate state demo init full
|
|
480
|
-
mkdir -p docs/knowledge-base/cb
|
|
481
|
-
printf '# source\n' > docs/knowledge-base/cb/source.md
|
|
482
|
-
run comate sources demo track docs/knowledge-base/cb/source.md --note "main entry"
|
|
483
|
-
[ "$status" -eq 0 ]
|
|
484
|
-
[ "$(comate state demo get source_kind)" = "file" ]
|
|
485
|
-
[ "$(comate state demo get source_manifest)" = "openspec/changes/demo/.comate/sources.manifest" ]
|
|
486
|
-
run comate sources demo list
|
|
487
|
-
[[ "$output" == *"path:docs/knowledge-base/cb/source.md"* ]]
|
|
488
|
-
[[ "$output" == *"note:main entry"* ]]
|
|
489
|
-
}
|
|
490
|
-
|
|
491
|
-
@test "sources track rejects file outside knowledge-base" {
|
|
492
|
-
comate state demo init full
|
|
493
|
-
printf 'x\n' > docs/outside.md
|
|
494
|
-
run comate sources demo track docs/outside.md
|
|
495
|
-
[ "$(comate state demo get source_kind)" = "conversation" ]
|
|
496
|
-
[ "$(comate state demo get source_manifest)" = "null" ]
|
|
497
|
-
}
|
|
498
|
-
|
|
499
|
-
@test "sources check detects drift after file edited" {
|
|
500
|
-
comate state demo init full
|
|
501
|
-
mkdir -p docs/knowledge-base/cb
|
|
502
|
-
printf '# source\n' > docs/knowledge-base/cb/source.md
|
|
503
|
-
comate sources demo track docs/knowledge-base/cb/source.md >/dev/null 2>&1
|
|
504
|
-
printf 'edited\n' >> docs/knowledge-base/cb/source.md
|
|
505
|
-
run comate sources demo check
|
|
506
|
-
[ "$status" -eq 1 ]
|
|
507
|
-
[[ "$output" == *"drifted"* ]]
|
|
508
|
-
}
|
|
509
|
-
|
|
510
|
-
@test "sources find reverse-looks-up a tracked file" {
|
|
511
|
-
comate state demo init full
|
|
512
|
-
mkdir -p docs/knowledge-base/cb
|
|
513
|
-
printf '# source\n' > docs/knowledge-base/cb/source.md
|
|
514
|
-
comate sources demo track docs/knowledge-base/cb/source.md >/dev/null 2>&1
|
|
515
|
-
run comate sources find docs/knowledge-base/cb/source.md
|
|
516
|
-
[ "$status" -eq 0 ]
|
|
517
|
-
[[ "$output" == *"demo"* ]]
|
|
518
|
-
}
|
|
519
|
-
|
|
520
|
-
@test "build guard blocks when tracked source drifts" {
|
|
521
|
-
comate state demo init fix
|
|
522
|
-
local d="openspec/changes/demo"
|
|
523
|
-
printf '# p\n' > "$d/proposal.md"; printf '# d\n' > "$d/design.md"; printf -- '- [x] t1\n' > "$d/tasks.md"
|
|
524
|
-
mkdir -p docs/knowledge-base/cb
|
|
525
|
-
printf '# source\n' > docs/knowledge-base/cb/source.md
|
|
526
|
-
comate sources demo track docs/knowledge-base/cb/source.md >/dev/null 2>&1
|
|
527
|
-
comate guard demo open --apply >/dev/null 2>&1
|
|
528
|
-
comate handoff demo build --write >/dev/null 2>&1
|
|
529
|
-
printf 'edited\n' >> docs/knowledge-base/cb/source.md
|
|
530
|
-
run comate guard demo build --json
|
|
531
|
-
[ "$status" -eq 1 ]
|
|
532
|
-
[[ "$output" == *'"block":true'* ]]
|
|
533
|
-
}
|
|
534
|
-
|
|
535
|
-
# --- doctor + usage ---
|
|
536
|
-
|
|
537
|
-
@test "doctor exits 0 when core tools present" {
|
|
538
|
-
run comate doctor
|
|
539
|
-
[ "$status" -eq 0 ]
|
|
540
|
-
}
|
|
541
|
-
|
|
542
|
-
@test "doctor recognizes Claude Code skills-cli Superpowers layout" {
|
|
543
|
-
local fake_home="$WORK/fake-home"
|
|
544
|
-
mkdir -p "$fake_home/.claude/skills/using-superpowers"
|
|
545
|
-
printf '# using-superpowers\n' > "$fake_home/.claude/skills/using-superpowers/SKILL.md"
|
|
546
|
-
run env HOME="$fake_home" bash "$COMATE" doctor
|
|
547
|
-
[ "$status" -eq 0 ]
|
|
548
|
-
[[ "$output" == *"Superpowers skills found"* ]]
|
|
549
|
-
[[ "$output" != *"Superpowers skills not found"* ]]
|
|
550
|
-
}
|
|
551
|
-
|
|
552
|
-
@test "dependency archived requires an archived=true record" {
|
|
553
|
-
comate state dep init full
|
|
554
|
-
run comate dependency dep archived
|
|
555
|
-
[ "$status" -eq 1 ]
|
|
556
|
-
comate state dep set phase archive >/dev/null
|
|
557
|
-
comate state dep transition archived >/dev/null
|
|
558
|
-
mkdir -p openspec/changes/archive
|
|
559
|
-
mv openspec/changes/dep openspec/changes/archive/2026-08-20-dep
|
|
560
|
-
run comate dependency dep archived
|
|
561
|
-
[ "$status" -eq 0 ]
|
|
562
|
-
}
|
|
563
|
-
|
|
564
|
-
@test "top-level usage error returns code 2" {
|
|
565
|
-
run comate frobnicate
|
|
566
|
-
[ "$status" -eq 2 ]
|
|
567
|
-
}
|
|
568
|
-
|
|
569
|
-
# --- gate ---
|
|
570
|
-
|
|
571
|
-
seed_prd() {
|
|
572
|
-
mkdir -p docs/knowledge-base/requirements/test
|
|
573
|
-
printf '# PRD-001\n功能清单:登录、登出\n' > \
|
|
574
|
-
docs/knowledge-base/requirements/test/PRD-001-demo.md
|
|
575
|
-
echo "docs/knowledge-base/requirements/test/PRD-001-demo.md"
|
|
576
|
-
}
|
|
577
|
-
|
|
578
|
-
seed_gate_skeleton() {
|
|
579
|
-
local prd="$1" change="$2" d="openspec/changes/$2"
|
|
580
|
-
mkdir -p "$d"
|
|
581
|
-
printf 'schema: specs-driven\n' > "$d/.openspec.yaml"
|
|
582
|
-
printf '# proposal\n' > "$d/proposal.md"
|
|
583
|
-
printf '# design\n' > "$d/design.md"
|
|
584
|
-
printf -- '- [ ] task\n' > "$d/tasks.md"
|
|
585
|
-
comate state "$change" init full >/dev/null
|
|
586
|
-
comate sources "$change" track "$prd" >/dev/null
|
|
587
|
-
}
|
|
588
|
-
|
|
589
|
-
@test "gate review creates gate.yaml next to PRD" {
|
|
590
|
-
local prd; prd=$(seed_prd)
|
|
591
|
-
run comate gate "$prd" review
|
|
592
|
-
[ "$status" -eq 0 ]
|
|
593
|
-
[ -f "docs/knowledge-base/requirements/test/PRD-001.gate.yaml" ]
|
|
594
|
-
}
|
|
595
|
-
|
|
596
|
-
@test "gate rejects PRD filenames outside the naming convention" {
|
|
597
|
-
mkdir -p docs/knowledge-base/requirements/test
|
|
598
|
-
printf '# PRD\n' > docs/knowledge-base/requirements/test/PRD-1.md
|
|
599
|
-
run comate gate docs/knowledge-base/requirements/test/PRD-1.md review
|
|
600
|
-
[ "$status" -eq 2 ]
|
|
601
|
-
[[ "$output" == *"PRD-NNN-title.md"* ]]
|
|
602
|
-
}
|
|
603
|
-
|
|
604
|
-
@test "gate names key off PRD-NNN stem, dropping title" {
|
|
605
|
-
local prd; prd=$(seed_prd)
|
|
606
|
-
run comate gate "$prd" review
|
|
607
|
-
[ "$status" -eq 0 ]
|
|
608
|
-
# gate.yaml and review report drop the -demo title, key off PRD-001
|
|
609
|
-
[ -f "docs/knowledge-base/requirements/test/PRD-001.gate.yaml" ]
|
|
610
|
-
[ ! -f "docs/knowledge-base/requirements/test/PRD-001-demo.gate.yaml" ]
|
|
611
|
-
[[ "$output" == *"PRD-001-REVIEW.md"* ]]
|
|
612
|
-
grep -q "review_report: docs/knowledge-base/requirements/test/PRD-001-REVIEW.md" \
|
|
613
|
-
"docs/knowledge-base/requirements/test/PRD-001.gate.yaml"
|
|
614
|
-
}
|
|
615
|
-
|
|
616
|
-
@test "gate review writes reviewing status and pins prd_hash" {
|
|
617
|
-
local prd; prd=$(seed_prd)
|
|
618
|
-
comate gate "$prd" review >/dev/null
|
|
619
|
-
local gate="docs/knowledge-base/requirements/test/PRD-001.gate.yaml"
|
|
620
|
-
run comate gate "$prd" status
|
|
621
|
-
[ "$status" -eq 0 ]
|
|
622
|
-
[[ "$output" == *"reviewing"* ]]
|
|
623
|
-
local hash; hash=$(grep '^prd_hash:' "$gate" | awk '{print $2}')
|
|
624
|
-
[ -n "$hash" ]
|
|
625
|
-
[ "$hash" != "null" ]
|
|
626
|
-
}
|
|
627
|
-
|
|
628
|
-
@test "gate check blocks when status is reviewing" {
|
|
629
|
-
local prd; prd=$(seed_prd)
|
|
630
|
-
comate gate "$prd" review >/dev/null
|
|
631
|
-
run comate gate "$prd" check
|
|
632
|
-
[ "$status" -eq 1 ]
|
|
633
|
-
[[ "$output" == *"BLOCK"* ]]
|
|
634
|
-
}
|
|
635
|
-
|
|
636
|
-
@test "gate pass blocked when blocking_open not zero" {
|
|
637
|
-
local prd; prd=$(seed_prd)
|
|
638
|
-
comate gate "$prd" review >/dev/null
|
|
639
|
-
comate gate "$prd" set score 80 >/dev/null
|
|
640
|
-
run comate gate "$prd" pass
|
|
641
|
-
[ "$status" -eq 1 ]
|
|
642
|
-
[[ "$output" == *"blocking_open"* ]]
|
|
643
|
-
}
|
|
644
|
-
|
|
645
|
-
@test "gate pass blocked when review report missing" {
|
|
646
|
-
local prd; prd=$(seed_prd)
|
|
647
|
-
comate gate "$prd" review >/dev/null
|
|
648
|
-
comate gate "$prd" set score 90 >/dev/null
|
|
649
|
-
comate gate "$prd" set blocking_open 0 >/dev/null
|
|
650
|
-
comate gate "$prd" set changes_map "demo:功能项1" >/dev/null
|
|
651
|
-
run comate gate "$prd" pass
|
|
652
|
-
[ "$status" -eq 1 ]
|
|
653
|
-
[[ "$output" == *"report"* ]]
|
|
654
|
-
}
|
|
655
|
-
|
|
656
|
-
@test "gate pass blocks without a confirmed changes map" {
|
|
657
|
-
local prd; prd=$(seed_prd)
|
|
658
|
-
comate gate "$prd" review >/dev/null
|
|
659
|
-
comate gate "$prd" set score 90 >/dev/null
|
|
660
|
-
comate gate "$prd" set blocking_open 0 >/dev/null
|
|
661
|
-
printf '# report\n' > "docs/knowledge-base/requirements/test/PRD-001-REVIEW.md"
|
|
662
|
-
run comate gate "$prd" pass
|
|
663
|
-
[ "$status" -eq 1 ]
|
|
664
|
-
[[ "$output" == *"changes_map"* ]]
|
|
665
|
-
}
|
|
666
|
-
|
|
667
|
-
@test "gate rejects changes_map entries without a function mapping" {
|
|
668
|
-
local prd; prd=$(seed_prd)
|
|
669
|
-
comate gate "$prd" review >/dev/null
|
|
670
|
-
run comate gate "$prd" set changes_map "login-flow"
|
|
671
|
-
[ "$status" -eq 2 ]
|
|
672
|
-
[[ "$output" == *"malformed changes_map"* ]]
|
|
673
|
-
}
|
|
674
|
-
|
|
675
|
-
@test "gate rejects duplicate change names in changes_map" {
|
|
676
|
-
local prd; prd=$(seed_prd)
|
|
677
|
-
comate gate "$prd" review >/dev/null
|
|
678
|
-
run comate gate "$prd" set changes_map "login-flow:功能项1;login-flow:功能项2"
|
|
679
|
-
[ "$status" -eq 2 ]
|
|
680
|
-
[[ "$output" == *"duplicate change"* ]]
|
|
681
|
-
}
|
|
682
|
-
|
|
683
|
-
@test "gate rejects an empty middle entry in changes_map" {
|
|
684
|
-
local prd; prd=$(seed_prd)
|
|
685
|
-
comate gate "$prd" review >/dev/null
|
|
686
|
-
run comate gate "$prd" set changes_map "login-flow:功能项1;;logout-flow:功能项2"
|
|
687
|
-
[ "$status" -eq 2 ]
|
|
688
|
-
[[ "$output" == *"empty entry"* ]]
|
|
689
|
-
}
|
|
690
|
-
|
|
691
|
-
@test "gate rejects a trailing empty entry in changes_map" {
|
|
692
|
-
local prd; prd=$(seed_prd)
|
|
693
|
-
comate gate "$prd" review >/dev/null
|
|
694
|
-
run comate gate "$prd" set changes_map "login-flow:功能项1;"
|
|
695
|
-
[ "$status" -eq 2 ]
|
|
696
|
-
[[ "$output" == *"empty entry"* ]]
|
|
697
|
-
}
|
|
698
|
-
|
|
699
|
-
@test "gate rejects an empty function mapping" {
|
|
700
|
-
local prd; prd=$(seed_prd)
|
|
701
|
-
comate gate "$prd" review >/dev/null
|
|
702
|
-
run comate gate "$prd" set changes_map "login-flow:"
|
|
703
|
-
[ "$status" -eq 2 ]
|
|
704
|
-
[[ "$output" == *"empty function mapping"* ]]
|
|
705
|
-
}
|
|
706
|
-
|
|
707
|
-
@test "gate rejects an all-whitespace function mapping" {
|
|
708
|
-
local prd; prd=$(seed_prd)
|
|
709
|
-
comate gate "$prd" review >/dev/null
|
|
710
|
-
run comate gate "$prd" set changes_map "login-flow: "
|
|
711
|
-
[ "$status" -eq 2 ]
|
|
712
|
-
[[ "$output" == *"empty function mapping"* ]]
|
|
713
|
-
}
|
|
714
|
-
|
|
715
|
-
@test "gate keeps a passed changes_map immutable except for idempotent writes" {
|
|
716
|
-
local prd; prd=$(seed_prd)
|
|
717
|
-
comate gate "$prd" review >/dev/null
|
|
718
|
-
comate gate "$prd" set score 90 >/dev/null
|
|
719
|
-
comate gate "$prd" set blocking_open 0 >/dev/null
|
|
720
|
-
comate gate "$prd" set changes_map "a:功能项1;b:功能项2;c:功能项3;d:功能项4" >/dev/null
|
|
721
|
-
printf '# 报告\n' > "docs/knowledge-base/requirements/test/PRD-001-REVIEW.md"
|
|
722
|
-
comate gate "$prd" pass >/dev/null
|
|
723
|
-
run comate gate "$prd" set changes_map "a:功能项1;b:功能项2;c:功能项3;d:功能项4"
|
|
724
|
-
[ "$status" -eq 0 ]
|
|
725
|
-
run comate gate "$prd" set changes_map "a:功能项1"
|
|
726
|
-
[ "$status" -eq 1 ]
|
|
727
|
-
[[ "$output" == *"immutable after gate pass"* ]]
|
|
728
|
-
run comate gate "$prd" fanout-status --json
|
|
729
|
-
[[ "$output" == *'"expected":4'* ]]
|
|
730
|
-
}
|
|
731
|
-
|
|
732
|
-
@test "gate pass --tbd requires unresolved blockers" {
|
|
733
|
-
local prd; prd=$(seed_prd)
|
|
734
|
-
comate gate "$prd" review >/dev/null
|
|
735
|
-
comate gate "$prd" set score 90 >/dev/null
|
|
736
|
-
comate gate "$prd" set blocking_open 0 >/dev/null
|
|
737
|
-
comate gate "$prd" set changes_map "demo:功能项1" >/dev/null
|
|
738
|
-
printf '# report\n' > "docs/knowledge-base/requirements/test/PRD-001-REVIEW.md"
|
|
739
|
-
run comate gate "$prd" pass --tbd
|
|
740
|
-
[ "$status" -eq 1 ]
|
|
741
|
-
[[ "$output" == *"requires at least one"* ]]
|
|
742
|
-
}
|
|
743
|
-
|
|
744
|
-
@test "gate pass succeeds and check passes after report written" {
|
|
745
|
-
local prd; prd=$(seed_prd)
|
|
746
|
-
comate gate "$prd" review >/dev/null
|
|
747
|
-
comate gate "$prd" set score 90 >/dev/null
|
|
748
|
-
comate gate "$prd" set blocking_open 0 >/dev/null
|
|
749
|
-
comate gate "$prd" set changes_map "demo:功能项1" >/dev/null
|
|
750
|
-
printf '# 评审报告\n' > \
|
|
751
|
-
"docs/knowledge-base/requirements/test/PRD-001-REVIEW.md"
|
|
752
|
-
run comate gate "$prd" pass
|
|
753
|
-
[ "$status" -eq 0 ]
|
|
754
|
-
run comate gate "$prd" check
|
|
755
|
-
[ "$status" -eq 0 ]
|
|
756
|
-
}
|
|
757
|
-
|
|
758
|
-
@test "gate pass --tbd sets passed-with-tbd" {
|
|
759
|
-
local prd; prd=$(seed_prd)
|
|
760
|
-
comate gate "$prd" review >/dev/null
|
|
761
|
-
comate gate "$prd" set score 65 >/dev/null
|
|
762
|
-
comate gate "$prd" set blocking_open 2 >/dev/null
|
|
763
|
-
comate gate "$prd" set changes_map "demo:功能项1" >/dev/null
|
|
764
|
-
printf '# 评审报告\n' > \
|
|
765
|
-
"docs/knowledge-base/requirements/test/PRD-001-REVIEW.md"
|
|
766
|
-
run comate gate "$prd" pass --tbd
|
|
767
|
-
[ "$status" -eq 0 ]
|
|
768
|
-
run comate gate "$prd" status
|
|
769
|
-
[[ "$output" == *"passed-with-tbd"* ]]
|
|
770
|
-
}
|
|
771
|
-
|
|
772
|
-
@test "gate check blocks after PRD modified post-pass (stale)" {
|
|
773
|
-
local prd; prd=$(seed_prd)
|
|
774
|
-
comate gate "$prd" review >/dev/null
|
|
775
|
-
comate gate "$prd" set score 90 >/dev/null
|
|
776
|
-
comate gate "$prd" set blocking_open 0 >/dev/null
|
|
777
|
-
comate gate "$prd" set changes_map "demo:功能项1" >/dev/null
|
|
778
|
-
printf '# 评审报告\n' > \
|
|
779
|
-
"docs/knowledge-base/requirements/test/PRD-001-REVIEW.md"
|
|
780
|
-
comate gate "$prd" pass >/dev/null
|
|
781
|
-
# mutate PRD after passing
|
|
782
|
-
printf '\n额外内容\n' >> "$prd"
|
|
783
|
-
run comate gate "$prd" check
|
|
784
|
-
[ "$status" -eq 1 ]
|
|
785
|
-
[[ "$output" == *"stale"* ]] || [[ "$output" == *"hash mismatch"* ]]
|
|
786
|
-
}
|
|
787
|
-
|
|
788
|
-
@test "gate status reports stale on hash drift without mutating file" {
|
|
789
|
-
local prd; prd=$(seed_prd)
|
|
790
|
-
comate gate "$prd" review >/dev/null
|
|
791
|
-
comate gate "$prd" set score 90 >/dev/null
|
|
792
|
-
comate gate "$prd" set blocking_open 0 >/dev/null
|
|
793
|
-
comate gate "$prd" set changes_map "demo:功能项1" >/dev/null
|
|
794
|
-
printf '# 评审报告\n' > \
|
|
795
|
-
"docs/knowledge-base/requirements/test/PRD-001-REVIEW.md"
|
|
796
|
-
comate gate "$prd" pass >/dev/null
|
|
797
|
-
printf '\n改动\n' >> "$prd"
|
|
798
|
-
run comate gate "$prd" status
|
|
799
|
-
[ "$status" -eq 0 ]
|
|
800
|
-
[[ "$output" == *"stale"* ]]
|
|
801
|
-
# gate.yaml should still say passed (status is computed, not written by status cmd)
|
|
802
|
-
local gate="docs/knowledge-base/requirements/test/PRD-001.gate.yaml"
|
|
803
|
-
local stored; stored=$(grep '^gate_status:' "$gate" | awk '{print $2}')
|
|
804
|
-
[ "$stored" = "passed" ]
|
|
805
|
-
}
|
|
806
|
-
|
|
807
|
-
@test "gate spawned records change name in spawned_changes" {
|
|
808
|
-
local prd; prd=$(seed_prd)
|
|
809
|
-
comate gate "$prd" review >/dev/null
|
|
810
|
-
comate gate "$prd" set score 90 >/dev/null
|
|
811
|
-
comate gate "$prd" set blocking_open 0 >/dev/null
|
|
812
|
-
comate gate "$prd" set changes_map "login-flow:功能项1" >/dev/null
|
|
813
|
-
printf '# 报告\n' > \
|
|
814
|
-
"docs/knowledge-base/requirements/test/PRD-001-REVIEW.md"
|
|
815
|
-
comate gate "$prd" pass >/dev/null
|
|
816
|
-
seed_gate_skeleton "$prd" login-flow
|
|
817
|
-
run comate gate "$prd" spawned login-flow
|
|
818
|
-
[ "$status" -eq 0 ]
|
|
819
|
-
local gate="docs/knowledge-base/requirements/test/PRD-001.gate.yaml"
|
|
820
|
-
local sc; sc=$(grep '^spawned_changes:' "$gate" | sed 's/spawned_changes: //')
|
|
821
|
-
[[ "$sc" == *"login-flow"* ]]
|
|
822
|
-
}
|
|
823
|
-
|
|
824
|
-
@test "gate spawned appends second change comma-joined" {
|
|
825
|
-
local prd; prd=$(seed_prd)
|
|
826
|
-
comate gate "$prd" review >/dev/null
|
|
827
|
-
comate gate "$prd" set score 90 >/dev/null
|
|
828
|
-
comate gate "$prd" set blocking_open 0 >/dev/null
|
|
829
|
-
comate gate "$prd" set changes_map "login-flow:功能项1;logout-flow:功能项2" >/dev/null
|
|
830
|
-
printf '# 报告\n' > \
|
|
831
|
-
"docs/knowledge-base/requirements/test/PRD-001-REVIEW.md"
|
|
832
|
-
comate gate "$prd" pass >/dev/null
|
|
833
|
-
seed_gate_skeleton "$prd" login-flow
|
|
834
|
-
seed_gate_skeleton "$prd" logout-flow
|
|
835
|
-
comate gate "$prd" spawned login-flow >/dev/null
|
|
836
|
-
comate gate "$prd" spawned logout-flow >/dev/null
|
|
837
|
-
local gate="docs/knowledge-base/requirements/test/PRD-001.gate.yaml"
|
|
838
|
-
local sc; sc=$(grep '^spawned_changes:' "$gate" | sed 's/spawned_changes: //')
|
|
839
|
-
[[ "$sc" == *"login-flow"* ]]
|
|
840
|
-
[[ "$sc" == *"logout-flow"* ]]
|
|
841
|
-
}
|
|
842
|
-
|
|
843
|
-
@test "gate spawned remains idempotent after an already recorded change leaves the active directory" {
|
|
844
|
-
local prd; prd=$(seed_prd)
|
|
845
|
-
comate gate "$prd" review >/dev/null
|
|
846
|
-
comate gate "$prd" set score 90 >/dev/null
|
|
847
|
-
comate gate "$prd" set blocking_open 0 >/dev/null
|
|
848
|
-
comate gate "$prd" set changes_map "login-flow:功能项1" >/dev/null
|
|
849
|
-
printf '# 报告\n' > "docs/knowledge-base/requirements/test/PRD-001-REVIEW.md"
|
|
850
|
-
comate gate "$prd" pass >/dev/null
|
|
851
|
-
seed_gate_skeleton "$prd" login-flow
|
|
852
|
-
comate gate "$prd" spawned login-flow >/dev/null
|
|
853
|
-
mkdir -p openspec/changes/archive
|
|
854
|
-
mv openspec/changes/login-flow openspec/changes/archive/2026-08-28-login-flow
|
|
855
|
-
run comate gate "$prd" spawned login-flow
|
|
856
|
-
[ "$status" -eq 0 ]
|
|
857
|
-
[[ "$output" == *"already recorded"* ]]
|
|
858
|
-
}
|
|
859
|
-
|
|
860
|
-
@test "gate fanout status reports missing changes instead of treating one of four as complete" {
|
|
861
|
-
local prd; prd=$(seed_prd)
|
|
862
|
-
comate gate "$prd" review >/dev/null
|
|
863
|
-
comate gate "$prd" set score 90 >/dev/null
|
|
864
|
-
comate gate "$prd" set blocking_open 0 >/dev/null
|
|
865
|
-
comate gate "$prd" set changes_map "login-flow:功能项1;logout-flow:功能项2;ops:功能项3;report:功能项4" >/dev/null
|
|
866
|
-
printf '# 报告\n' > "docs/knowledge-base/requirements/test/PRD-001-REVIEW.md"
|
|
867
|
-
comate gate "$prd" pass >/dev/null
|
|
868
|
-
seed_gate_skeleton "$prd" login-flow
|
|
869
|
-
comate gate "$prd" spawned login-flow >/dev/null
|
|
870
|
-
run comate gate "$prd" fanout-status --json
|
|
871
|
-
[ "$status" -eq 0 ]
|
|
872
|
-
[[ "$output" == *'"expected":4'* ]]
|
|
873
|
-
[[ "$output" == *'"spawned":1'* ]]
|
|
874
|
-
[[ "$output" == *'"complete":false'* ]]
|
|
875
|
-
[[ "$output" == *'logout-flow'* ]]
|
|
876
|
-
}
|
|
877
|
-
|
|
878
|
-
@test "gate spawned rejects a change outside the confirmed map" {
|
|
879
|
-
local prd; prd=$(seed_prd)
|
|
880
|
-
comate gate "$prd" review >/dev/null
|
|
881
|
-
comate gate "$prd" set score 90 >/dev/null
|
|
882
|
-
comate gate "$prd" set blocking_open 0 >/dev/null
|
|
883
|
-
comate gate "$prd" set changes_map "login-flow:功能项1" >/dev/null
|
|
884
|
-
printf '# 报告\n' > "docs/knowledge-base/requirements/test/PRD-001-REVIEW.md"
|
|
885
|
-
comate gate "$prd" pass >/dev/null
|
|
886
|
-
seed_gate_skeleton "$prd" other-flow
|
|
887
|
-
run comate gate "$prd" spawned other-flow
|
|
888
|
-
[ "$status" -eq 1 ]
|
|
889
|
-
[[ "$output" == *"not present in changes_map"* ]]
|
|
890
|
-
}
|
|
891
|
-
|
|
892
|
-
@test "gate fanout status becomes complete only after every mapped change is spawned" {
|
|
893
|
-
local prd; prd=$(seed_prd)
|
|
894
|
-
comate gate "$prd" review >/dev/null
|
|
895
|
-
comate gate "$prd" set score 90 >/dev/null
|
|
896
|
-
comate gate "$prd" set blocking_open 0 >/dev/null
|
|
897
|
-
comate gate "$prd" set changes_map "login-flow:功能项1;logout-flow:功能项2" >/dev/null
|
|
898
|
-
printf '# 报告\n' > "docs/knowledge-base/requirements/test/PRD-001-REVIEW.md"
|
|
899
|
-
comate gate "$prd" pass >/dev/null
|
|
900
|
-
seed_gate_skeleton "$prd" login-flow
|
|
901
|
-
seed_gate_skeleton "$prd" logout-flow
|
|
902
|
-
comate gate "$prd" spawned login-flow >/dev/null
|
|
903
|
-
comate gate "$prd" spawned logout-flow >/dev/null
|
|
904
|
-
run comate gate "$prd" fanout-status
|
|
905
|
-
[ "$status" -eq 0 ]
|
|
906
|
-
[[ "$output" == *"complete=true"* ]]
|
|
907
|
-
[[ "$output" == *"missing=none"* ]]
|
|
908
|
-
}
|
|
909
|
-
|
|
910
|
-
@test "gate spawned rejects an incomplete change skeleton" {
|
|
911
|
-
local prd; prd=$(seed_prd)
|
|
912
|
-
comate gate "$prd" review >/dev/null
|
|
913
|
-
comate gate "$prd" set score 90 >/dev/null
|
|
914
|
-
comate gate "$prd" set blocking_open 0 >/dev/null
|
|
915
|
-
comate gate "$prd" set changes_map "login-flow:功能项1" >/dev/null
|
|
916
|
-
printf '# 报告\n' > "docs/knowledge-base/requirements/test/PRD-001-REVIEW.md"
|
|
917
|
-
comate gate "$prd" pass >/dev/null
|
|
918
|
-
mkdir -p openspec/changes/login-flow
|
|
919
|
-
run comate gate "$prd" spawned login-flow
|
|
920
|
-
[ "$status" -eq 1 ]
|
|
921
|
-
[[ "$output" == *"skeleton incomplete"* ]]
|
|
922
|
-
}
|
|
923
|
-
|
|
924
|
-
@test "gate spawned blocks a new registration after the PRD becomes stale" {
|
|
925
|
-
local prd; prd=$(seed_prd)
|
|
926
|
-
comate gate "$prd" review >/dev/null
|
|
927
|
-
comate gate "$prd" set score 90 >/dev/null
|
|
928
|
-
comate gate "$prd" set blocking_open 0 >/dev/null
|
|
929
|
-
comate gate "$prd" set changes_map "login-flow:功能项1" >/dev/null
|
|
930
|
-
printf '# 报告\n' > "docs/knowledge-base/requirements/test/PRD-001-REVIEW.md"
|
|
931
|
-
comate gate "$prd" pass >/dev/null
|
|
932
|
-
seed_gate_skeleton "$prd" login-flow
|
|
933
|
-
printf '\nchanged\n' >> "$prd"
|
|
934
|
-
run comate gate "$prd" spawned login-flow
|
|
935
|
-
[ "$status" -eq 1 ]
|
|
936
|
-
[[ "$output" == *"hash mismatch"* ]]
|
|
937
|
-
}
|
|
938
|
-
|
|
939
|
-
@test "gate spawned accepts a normalized PRD path recorded in the source manifest" {
|
|
940
|
-
local prd; prd=$(seed_prd)
|
|
941
|
-
prd="./$prd"
|
|
942
|
-
comate gate "$prd" review >/dev/null
|
|
943
|
-
comate gate "$prd" set score 90 >/dev/null
|
|
944
|
-
comate gate "$prd" set blocking_open 0 >/dev/null
|
|
945
|
-
comate gate "$prd" set changes_map "login-flow:功能项1" >/dev/null
|
|
946
|
-
printf '# 报告\n' > "docs/knowledge-base/requirements/test/PRD-001-REVIEW.md"
|
|
947
|
-
comate gate "$prd" pass >/dev/null
|
|
948
|
-
seed_gate_skeleton "$prd" login-flow
|
|
949
|
-
run comate gate "$prd" spawned login-flow
|
|
950
|
-
[ "$status" -eq 0 ]
|
|
951
|
-
}
|
|
952
|
-
|
|
953
|
-
@test "gate spawned rejects a truncated PRD source record without sha256" {
|
|
954
|
-
local prd; prd=$(seed_prd)
|
|
955
|
-
comate gate "$prd" review >/dev/null
|
|
956
|
-
comate gate "$prd" set score 90 >/dev/null
|
|
957
|
-
comate gate "$prd" set blocking_open 0 >/dev/null
|
|
958
|
-
comate gate "$prd" set changes_map "login-flow:功能项1" >/dev/null
|
|
959
|
-
printf '# 报告\n' > "docs/knowledge-base/requirements/test/PRD-001-REVIEW.md"
|
|
960
|
-
comate gate "$prd" pass >/dev/null
|
|
961
|
-
seed_gate_skeleton "$prd" login-flow
|
|
962
|
-
printf 'path:%s\n' "$prd" > openspec/changes/login-flow/.comate/sources.manifest
|
|
963
|
-
run comate gate "$prd" spawned login-flow
|
|
964
|
-
[ "$status" -eq 1 ]
|
|
965
|
-
[[ "$output" == *"complete path/sha256 record"* ]]
|
|
966
|
-
}
|
|
967
|
-
|
|
968
|
-
@test "gate status returns none when gate does not exist" {
|
|
969
|
-
mkdir -p docs/knowledge-base/requirements/test
|
|
970
|
-
run comate gate "docs/knowledge-base/requirements/test/PRD-001-demo.md" status
|
|
971
|
-
[ "$status" -eq 0 ]
|
|
972
|
-
[[ "$output" == *"none"* ]]
|
|
973
|
-
}
|
|
974
|
-
|
|
975
|
-
@test "gate unknown subcommand returns usage error" {
|
|
976
|
-
local prd; prd=$(seed_prd)
|
|
977
|
-
run comate gate "$prd" frobnicate
|
|
978
|
-
[ "$status" -eq 2 ]
|
|
979
|
-
}
|
|
980
|
-
|
|
981
|
-
@test "gate review re-opens reviewing when PRD changes after pass" {
|
|
982
|
-
local prd; prd=$(seed_prd)
|
|
983
|
-
comate gate "$prd" review >/dev/null
|
|
984
|
-
comate gate "$prd" set score 90 >/dev/null
|
|
985
|
-
comate gate "$prd" set blocking_open 0 >/dev/null
|
|
986
|
-
comate gate "$prd" set changes_map "demo:功能项1" >/dev/null
|
|
987
|
-
printf '# 报告\n' > \
|
|
988
|
-
"docs/knowledge-base/requirements/test/PRD-001-REVIEW.md"
|
|
989
|
-
comate gate "$prd" pass >/dev/null
|
|
990
|
-
printf '\n改动\n' >> "$prd"
|
|
991
|
-
# re-run review after PRD change — should reset to reviewing
|
|
992
|
-
run comate gate "$prd" review
|
|
993
|
-
[ "$status" -eq 0 ]
|
|
994
|
-
run comate gate "$prd" status
|
|
995
|
-
[[ "$output" == *"reviewing"* ]]
|
|
996
|
-
}
|
|
997
|
-
|
|
998
|
-
@test "gate set rejects unknown field" {
|
|
999
|
-
local prd; prd=$(seed_prd)
|
|
1000
|
-
comate gate "$prd" review >/dev/null
|
|
1001
|
-
run comate gate "$prd" set nonexistent_field foo
|
|
1002
|
-
[ "$status" -eq 2 ]
|
|
1003
|
-
}
|
|
1004
|
-
|
|
1005
|
-
@test "gate set rejects invalid gate_status value" {
|
|
1006
|
-
local prd; prd=$(seed_prd)
|
|
1007
|
-
comate gate "$prd" review >/dev/null
|
|
1008
|
-
run comate gate "$prd" set gate_status flying
|
|
1009
|
-
[ "$status" -eq 2 ]
|
|
1010
|
-
}
|
|
1011
|
-
|
|
1012
|
-
@test "gate status --json emits json with status field" {
|
|
1013
|
-
local prd; prd=$(seed_prd)
|
|
1014
|
-
comate gate "$prd" review >/dev/null
|
|
1015
|
-
run comate gate "$prd" status --json
|
|
1016
|
-
[ "$status" -eq 0 ]
|
|
1017
|
-
[[ "$output" == *'"status"'* ]]
|
|
1018
|
-
[[ "$output" == *'"reviewing"'* ]]
|
|
1019
|
-
}
|