@house365/comate 0.9.5 → 0.9.7
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/bin/init.mjs +32 -11
- package/docs/COMATE_MANUAL.md +11 -9
- package/package.json +1 -1
- package/skills/comate/SKILL.md +151 -23
- package/skills/comate/reference/decision-points.md +2 -2
- package/skills/comate/scripts/comate-lib.sh +5 -1
- package/skills/comate/scripts/comate.sh +98 -16
- package/skills/comate/tests/comate.bats +96 -1
- package/skills/comate-build/SKILL.md +83 -6
- package/skills/comate-design/SKILL.md +75 -3
- package/skills/comate-fix/SKILL.md +9 -5
- package/skills/comate-gate/SKILL.md +7 -7
- package/skills/comate-open/SKILL.md +62 -9
- package/skills/comate-verify/SKILL.md +19 -10
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
# sources find <file> — reverse lookup: changes citing a file
|
|
20
20
|
# gate <prd> review — create/refresh gate.yaml next to the PRD, pin hash
|
|
21
21
|
# gate <prd> set <field> <value> — write score | blocking_open | gate_status | …
|
|
22
|
-
# gate <prd> pass [--tbd] —
|
|
22
|
+
# gate <prd> pass [--tbd] — pass clean gate or explicitly accepted TBD blockers
|
|
23
23
|
# gate <prd> spawned <change> — record a change fanned out from this PRD
|
|
24
24
|
# gate <prd> status [--json] — print gate status (computes stale on hash drift)
|
|
25
25
|
# gate <prd> check — precondition guard before spawning a change
|
|
@@ -60,7 +60,7 @@ Usage: comate.sh <command> ...
|
|
|
60
60
|
state <name> init <workflow> full | fix
|
|
61
61
|
state <name> get <field>
|
|
62
62
|
state <name> set <field> <value>
|
|
63
|
-
state <name> transition <event> open-complete|design-complete|build-complete|verify-pass|verify-fail|archived
|
|
63
|
+
state <name> transition <event> open-complete|design-complete|build-complete|verify-pass|verify-fail|fix-upgrade|archived
|
|
64
64
|
state <name> check <phase> [--json]
|
|
65
65
|
state <name> recover <phase>
|
|
66
66
|
state <name> scale
|
|
@@ -72,9 +72,10 @@ Usage: comate.sh <command> ...
|
|
|
72
72
|
sources <name> check
|
|
73
73
|
sources <name> list
|
|
74
74
|
sources find <file>
|
|
75
|
+
dependency <name> archived verify an exact archived change exists
|
|
75
76
|
gate <prd> review create/refresh gate.yaml next to PRD, pin hash
|
|
76
77
|
gate <prd> set <field> <value> score | blocking_open | gate_status | …
|
|
77
|
-
gate <prd> pass [--tbd]
|
|
78
|
+
gate <prd> pass [--tbd] pass: zero blockers; --tbd: accepted blockers
|
|
78
79
|
gate <prd> spawned <change> record a change fanned out from this PRD
|
|
79
80
|
gate <prd> status [--json]
|
|
80
81
|
gate <prd> check precondition guard before spawning a change
|
|
@@ -138,6 +139,8 @@ state_init() {
|
|
|
138
139
|
schema_version: $COMATE_SCHEMA_VERSION
|
|
139
140
|
workflow: $workflow
|
|
140
141
|
phase: open
|
|
142
|
+
phase_step: null
|
|
143
|
+
phase_step_data: null
|
|
141
144
|
build_mode: $build_mode
|
|
142
145
|
tdd_mode: $tdd_mode
|
|
143
146
|
isolation: $isolation
|
|
@@ -237,7 +240,7 @@ state_transition() {
|
|
|
237
240
|
validate_change_name "$name"
|
|
238
241
|
validate_enum_event() {
|
|
239
242
|
case "$1" in
|
|
240
|
-
open-complete|design-complete|build-complete|verify-pass|verify-fail|archived) return 0 ;;
|
|
243
|
+
open-complete|design-complete|build-complete|verify-pass|verify-fail|fix-upgrade|archived) return 0 ;;
|
|
241
244
|
*) die "$COMATE_USAGE" "ERROR: unknown event '$1'" ;;
|
|
242
245
|
esac
|
|
243
246
|
}
|
|
@@ -246,6 +249,13 @@ state_transition() {
|
|
|
246
249
|
local yaml; yaml=$(require_yaml "$name")
|
|
247
250
|
validate_flat_yaml "$yaml"
|
|
248
251
|
|
|
252
|
+
# phase_step is local to the current phase. Carrying it into the next phase
|
|
253
|
+
# makes identical step numbers ambiguous and can skip mandatory work.
|
|
254
|
+
_clear_phase_progress() {
|
|
255
|
+
yaml_set "$yaml" phase_step null
|
|
256
|
+
yaml_set "$yaml" phase_step_data null
|
|
257
|
+
}
|
|
258
|
+
|
|
249
259
|
case "$event" in
|
|
250
260
|
open-complete)
|
|
251
261
|
_require_phase "$yaml" open
|
|
@@ -255,10 +265,12 @@ state_transition() {
|
|
|
255
265
|
else
|
|
256
266
|
yaml_set "$yaml" phase build
|
|
257
267
|
fi
|
|
268
|
+
_clear_phase_progress
|
|
258
269
|
;;
|
|
259
270
|
design-complete)
|
|
260
271
|
_require_phase "$yaml" design
|
|
261
272
|
yaml_set "$yaml" phase build
|
|
273
|
+
_clear_phase_progress
|
|
262
274
|
;;
|
|
263
275
|
build-complete)
|
|
264
276
|
_require_phase "$yaml" build
|
|
@@ -267,6 +279,7 @@ state_transition() {
|
|
|
267
279
|
yaml_set "$yaml" verify_result pending
|
|
268
280
|
yaml_set "$yaml" verification_report null
|
|
269
281
|
yaml_set "$yaml" branch_status pending
|
|
282
|
+
_clear_phase_progress
|
|
270
283
|
;;
|
|
271
284
|
verify-pass)
|
|
272
285
|
_require_phase "$yaml" verify
|
|
@@ -274,12 +287,36 @@ state_transition() {
|
|
|
274
287
|
yaml_set "$yaml" verify_result pass
|
|
275
288
|
yaml_set "$yaml" phase archive
|
|
276
289
|
yaml_set "$yaml" verified_at "$(today)"
|
|
290
|
+
_clear_phase_progress
|
|
277
291
|
;;
|
|
278
292
|
verify-fail)
|
|
279
293
|
_require_phase "$yaml" verify
|
|
280
|
-
|
|
294
|
+
# The failure is captured in the report; once routed back to build the
|
|
295
|
+
# active verification state must be pending or the orchestrator loops.
|
|
296
|
+
yaml_set "$yaml" verify_result pending
|
|
281
297
|
yaml_set "$yaml" phase build
|
|
298
|
+
yaml_set "$yaml" verification_report null
|
|
299
|
+
yaml_set "$yaml" branch_status pending
|
|
300
|
+
_clear_phase_progress
|
|
301
|
+
;;
|
|
302
|
+
fix-upgrade)
|
|
303
|
+
[ "$(yaml_get "$yaml" workflow)" = "fix" ] || \
|
|
304
|
+
die "$COMATE_BLOCK" "ERROR: fix-upgrade requires workflow=fix"
|
|
305
|
+
case "$(yaml_get "$yaml" phase)" in
|
|
306
|
+
open|build) ;;
|
|
307
|
+
*) die "$COMATE_BLOCK" "ERROR: fix-upgrade is only valid from open or build" ;;
|
|
308
|
+
esac
|
|
309
|
+
yaml_set "$yaml" workflow full
|
|
310
|
+
yaml_set "$yaml" phase design
|
|
311
|
+
yaml_set "$yaml" build_mode null
|
|
312
|
+
yaml_set "$yaml" tdd_mode null
|
|
313
|
+
yaml_set "$yaml" isolation null
|
|
314
|
+
yaml_set "$yaml" verify_mode null
|
|
315
|
+
yaml_set "$yaml" direct_override false
|
|
316
|
+
yaml_set "$yaml" verify_result pending
|
|
317
|
+
yaml_set "$yaml" verification_report null
|
|
282
318
|
yaml_set "$yaml" branch_status pending
|
|
319
|
+
_clear_phase_progress
|
|
283
320
|
;;
|
|
284
321
|
archived)
|
|
285
322
|
_require_phase "$yaml" archive
|
|
@@ -498,15 +535,19 @@ state_recover() {
|
|
|
498
535
|
yaml="$dir/.comate.yaml"
|
|
499
536
|
[ -f "$yaml" ] || die "$COMATE_CORRUPT" "ERROR: .comate.yaml not found at $yaml"
|
|
500
537
|
|
|
501
|
-
local phase workflow
|
|
538
|
+
local phase workflow phase_step phase_step_data
|
|
502
539
|
phase=$(yaml_get "$yaml" phase)
|
|
503
540
|
workflow=$(yaml_get "$yaml" workflow)
|
|
541
|
+
phase_step=$(yaml_get "$yaml" phase_step)
|
|
542
|
+
phase_step_data=$(yaml_get "$yaml" phase_step_data)
|
|
504
543
|
# If a phase arg was passed, honor it for reporting; else use stored phase.
|
|
505
544
|
[ -n "$phase_arg" ] && phase="$phase_arg"
|
|
506
545
|
|
|
507
546
|
echo "=== Recovery Context: $name ==="
|
|
508
547
|
echo "Phase: $phase"
|
|
509
548
|
echo "Workflow: $workflow"
|
|
549
|
+
echo "Phase step: ${phase_step:-null}"
|
|
550
|
+
echo "Phase step data: ${phase_step_data:-null}"
|
|
510
551
|
echo ""
|
|
511
552
|
|
|
512
553
|
local design_doc plan verify_result verify_mode verification_report
|
|
@@ -578,7 +619,11 @@ state_recover() {
|
|
|
578
619
|
echo "Tasks: tasks.md MISSING"
|
|
579
620
|
fi
|
|
580
621
|
echo ""
|
|
581
|
-
if [ "$
|
|
622
|
+
if [[ "$phase_step" == build.blocked-by-* ]]; then
|
|
623
|
+
local dep="$phase_step_data"
|
|
624
|
+
if [ -z "$dep" ] || [ "$dep" = "null" ]; then dep="${phase_step#build.blocked-by-}"; fi
|
|
625
|
+
echo "Recovery action: Blocked by $dep. Run: comate.sh dependency $dep archived"
|
|
626
|
+
elif [ "$isolation" = "null" ] || [ -z "$isolation" ]; then
|
|
582
627
|
echo "Recovery action: Isolation not selected. AskUserQuestion for branch/worktree."
|
|
583
628
|
elif [ "$build_mode" = "null" ] || [ -z "$build_mode" ]; then
|
|
584
629
|
echo "Recovery action: Build mode not selected. AskUserQuestion for execution method."
|
|
@@ -600,7 +645,9 @@ state_recover() {
|
|
|
600
645
|
_field_status "branch_status" "$branch_status"
|
|
601
646
|
_field_status "impl_hash" "$impl_hash"
|
|
602
647
|
echo ""
|
|
603
|
-
if [ "$
|
|
648
|
+
if [ "$branch_status" = "discarded" ]; then
|
|
649
|
+
echo "Recovery action: Work was discarded. Run verify-fail to return to build before reimplementing, or explicitly abandon the change."
|
|
650
|
+
elif [ "$verify_result" = "pass" ] && [ "$branch_status" = "handled" ]; then
|
|
604
651
|
echo "Recovery action: Verification complete. Run guard to transition to archive."
|
|
605
652
|
elif [ "$verify_result" = "pass" ]; then
|
|
606
653
|
echo "Recovery action: Verified but branch not handled. Complete branch handling, set branch_status=handled."
|
|
@@ -1345,6 +1392,23 @@ sources_find() {
|
|
|
1345
1392
|
return 0
|
|
1346
1393
|
}
|
|
1347
1394
|
|
|
1395
|
+
# dependency <name> archived — prove completion from archived state, rather
|
|
1396
|
+
# than inferring it from absence in `openspec list`.
|
|
1397
|
+
dependency_archived() {
|
|
1398
|
+
local name="$1"
|
|
1399
|
+
validate_change_name "$name"
|
|
1400
|
+
[ ! -d "openspec/changes/$name" ] || \
|
|
1401
|
+
die "$COMATE_BLOCK" "BLOCK: dependency '$name' is still active"
|
|
1402
|
+
local found="" yaml
|
|
1403
|
+
while IFS= read -r yaml; do
|
|
1404
|
+
[ "$(yaml_get "$yaml" archived)" = "true" ] && found="$yaml"
|
|
1405
|
+
done < <(find openspec/changes/archive -mindepth 2 -maxdepth 2 \
|
|
1406
|
+
-path "*/????-??-??-${name}/.comate.yaml" -type f 2>/dev/null | sort)
|
|
1407
|
+
[ -n "$found" ] || \
|
|
1408
|
+
die "$COMATE_BLOCK" "BLOCK: no archived=true record found for dependency '$name'"
|
|
1409
|
+
log_pass "[DEPENDENCY] archived: $name ($found)"
|
|
1410
|
+
}
|
|
1411
|
+
|
|
1348
1412
|
# =====================================================================
|
|
1349
1413
|
# gate — phase 0: PRD quality gate + split precondition
|
|
1350
1414
|
# =====================================================================
|
|
@@ -1416,10 +1480,8 @@ gate_set() {
|
|
|
1416
1480
|
log_pass "[GATE] set $field=$value"
|
|
1417
1481
|
}
|
|
1418
1482
|
|
|
1419
|
-
# gate <prd> pass [--tbd] — flip to passed/passed-with-tbd.
|
|
1420
|
-
#
|
|
1421
|
-
# edit invalidates the pass. --tbd records that the user authorised proceeding
|
|
1422
|
-
# with unresolved TBDs (spawned changes must carry ⚠ markers).
|
|
1483
|
+
# gate <prd> pass [--tbd] — flip to passed/passed-with-tbd. Normal pass requires
|
|
1484
|
+
# zero blockers; --tbd requires at least one explicitly accepted blocker.
|
|
1423
1485
|
gate_pass() {
|
|
1424
1486
|
local prd; prd=$(require_prd "$1")
|
|
1425
1487
|
local tbd=0
|
|
@@ -1427,9 +1489,22 @@ gate_pass() {
|
|
|
1427
1489
|
local gate; gate=$(gate_path_for "$prd")
|
|
1428
1490
|
[ -f "$gate" ] || die "$COMATE_CORRUPT" "ERROR: gate not found (run 'gate review' first): $gate"
|
|
1429
1491
|
|
|
1430
|
-
local
|
|
1431
|
-
|
|
1432
|
-
|
|
1492
|
+
local score blocking changes_map
|
|
1493
|
+
score=$(yaml_get "$gate" score)
|
|
1494
|
+
blocking=$(yaml_get "$gate" blocking_open)
|
|
1495
|
+
changes_map=$(yaml_get "$gate" changes_map)
|
|
1496
|
+
[[ "$score" =~ ^[0-9]+$ ]] && [ "$score" -ge 0 ] && [ "$score" -le 100 ] || \
|
|
1497
|
+
die "$COMATE_BLOCK" "BLOCK: score must be an integer from 0 to 100 (got '$score')"
|
|
1498
|
+
[[ "$blocking" =~ ^[0-9]+$ ]] || \
|
|
1499
|
+
die "$COMATE_BLOCK" "BLOCK: blocking_open must be a non-negative integer (got '$blocking')"
|
|
1500
|
+
if [ "$tbd" -eq 1 ]; then
|
|
1501
|
+
[ "$blocking" -gt 0 ] || \
|
|
1502
|
+
die "$COMATE_BLOCK" "BLOCK: --tbd requires at least one recorded unresolved blocker"
|
|
1503
|
+
elif [ "$blocking" -ne 0 ]; then
|
|
1504
|
+
die "$COMATE_BLOCK" "BLOCK: blocking_open='$blocking' — resolve all blocking issues or use --tbd after explicit user authorization"
|
|
1505
|
+
fi
|
|
1506
|
+
if [ -z "$changes_map" ] || [ "$changes_map" = "null" ]; then
|
|
1507
|
+
die "$COMATE_BLOCK" "BLOCK: changes_map is required before passing the gate"
|
|
1433
1508
|
fi
|
|
1434
1509
|
local report; report=$(yaml_get "$gate" review_report)
|
|
1435
1510
|
if [ -z "$report" ] || [ "$report" = "null" ] || ! file_nonempty "$report"; then
|
|
@@ -1703,7 +1778,7 @@ status() {
|
|
|
1703
1778
|
echo "No active changes."
|
|
1704
1779
|
return 0
|
|
1705
1780
|
fi
|
|
1706
|
-
local any=0 d name yaml phase workflow build_mode vr dd plan total done tasks_file
|
|
1781
|
+
local any=0 d name yaml phase phase_step workflow build_mode vr dd plan total done tasks_file
|
|
1707
1782
|
for d in "$changes_dir"/*/; do
|
|
1708
1783
|
[ -d "$d" ] || continue
|
|
1709
1784
|
name=$(basename "$d")
|
|
@@ -1713,6 +1788,7 @@ status() {
|
|
|
1713
1788
|
[ "$(yaml_get "$yaml" archived)" = "true" ] && continue
|
|
1714
1789
|
any=1
|
|
1715
1790
|
phase=$(yaml_get "$yaml" phase)
|
|
1791
|
+
phase_step=$(yaml_get "$yaml" phase_step)
|
|
1716
1792
|
workflow=$(yaml_get "$yaml" workflow)
|
|
1717
1793
|
build_mode=$(yaml_get "$yaml" build_mode)
|
|
1718
1794
|
vr=$(yaml_get "$yaml" verify_result)
|
|
@@ -1726,6 +1802,7 @@ status() {
|
|
|
1726
1802
|
fi
|
|
1727
1803
|
echo "• $name [phase: $phase, $done/$total tasks]"
|
|
1728
1804
|
echo " workflow: $workflow | build_mode: $build_mode"
|
|
1805
|
+
[ -n "$phase_step" ] && [ "$phase_step" != "null" ] && echo " step: $phase_step"
|
|
1729
1806
|
[ -n "$dd" ] && [ "$dd" != "null" ] && echo " design: $dd"
|
|
1730
1807
|
[ -n "$plan" ] && [ "$plan" != "null" ] && echo " plan: $plan"
|
|
1731
1808
|
[ "$phase" = "verify" ] && echo " verify_result: $vr"
|
|
@@ -1870,6 +1947,11 @@ case "$CMD" in
|
|
|
1870
1947
|
esac
|
|
1871
1948
|
fi
|
|
1872
1949
|
;;
|
|
1950
|
+
dependency)
|
|
1951
|
+
NAME="${1:-}"; SUB="${2:-}"
|
|
1952
|
+
[ -n "$NAME" ] && [ "$SUB" = "archived" ] || { usage_top; exit "$COMATE_USAGE"; }
|
|
1953
|
+
dependency_archived "$NAME"
|
|
1954
|
+
;;
|
|
1873
1955
|
gate)
|
|
1874
1956
|
PRD="${1:-}"; SUB="${2:-}"
|
|
1875
1957
|
[ -n "$PRD" ] && [ -n "$SUB" ] || { usage_top; exit "$COMATE_USAGE"; }
|
|
@@ -144,8 +144,10 @@ seed_open() {
|
|
|
144
144
|
|
|
145
145
|
@test "open-complete routes full to design" {
|
|
146
146
|
seed_open
|
|
147
|
+
comate state demo set phase_step open.review-confirmed >/dev/null
|
|
147
148
|
comate state demo transition open-complete
|
|
148
149
|
[ "$(comate state demo get phase)" = "design" ]
|
|
150
|
+
[ "$(comate state demo get phase_step)" = "null" ]
|
|
149
151
|
}
|
|
150
152
|
|
|
151
153
|
@test "open-complete routes fix to build" {
|
|
@@ -201,6 +203,56 @@ seed_open() {
|
|
|
201
203
|
[ "$status" -eq 1 ]
|
|
202
204
|
}
|
|
203
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 "recover reports phase-local progress" {
|
|
241
|
+
comate state demo init full
|
|
242
|
+
comate state demo set phase_step open.review-awaiting >/dev/null
|
|
243
|
+
run comate state demo recover open
|
|
244
|
+
[ "$status" -eq 0 ]
|
|
245
|
+
[[ "$output" == *"Phase step: open.review-awaiting"* ]]
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
@test "status exposes awaiting decisions" {
|
|
249
|
+
comate state demo init full
|
|
250
|
+
comate state demo set phase_step open.review-awaiting >/dev/null
|
|
251
|
+
run comate status
|
|
252
|
+
[ "$status" -eq 0 ]
|
|
253
|
+
[[ "$output" == *"step: open.review-awaiting"* ]]
|
|
254
|
+
}
|
|
255
|
+
|
|
204
256
|
# --- guard open ---
|
|
205
257
|
|
|
206
258
|
@test "guard open passes with full artifacts and applies" {
|
|
@@ -436,6 +488,18 @@ SH
|
|
|
436
488
|
[ "$status" -eq 0 ]
|
|
437
489
|
}
|
|
438
490
|
|
|
491
|
+
@test "dependency archived requires an archived=true record" {
|
|
492
|
+
comate state dep init full
|
|
493
|
+
run comate dependency dep archived
|
|
494
|
+
[ "$status" -eq 1 ]
|
|
495
|
+
comate state dep set phase archive >/dev/null
|
|
496
|
+
comate state dep transition archived >/dev/null
|
|
497
|
+
mkdir -p openspec/changes/archive
|
|
498
|
+
mv openspec/changes/dep openspec/changes/archive/2026-08-20-dep
|
|
499
|
+
run comate dependency dep archived
|
|
500
|
+
[ "$status" -eq 0 ]
|
|
501
|
+
}
|
|
502
|
+
|
|
439
503
|
@test "top-level usage error returns code 2" {
|
|
440
504
|
run comate frobnicate
|
|
441
505
|
[ "$status" -eq 2 ]
|
|
@@ -503,16 +567,41 @@ seed_prd() {
|
|
|
503
567
|
comate gate "$prd" review >/dev/null
|
|
504
568
|
comate gate "$prd" set score 90 >/dev/null
|
|
505
569
|
comate gate "$prd" set blocking_open 0 >/dev/null
|
|
570
|
+
comate gate "$prd" set changes_map "demo:功能项1" >/dev/null
|
|
506
571
|
run comate gate "$prd" pass
|
|
507
572
|
[ "$status" -eq 1 ]
|
|
508
573
|
[[ "$output" == *"report"* ]]
|
|
509
574
|
}
|
|
510
575
|
|
|
576
|
+
@test "gate pass blocks without a confirmed changes map" {
|
|
577
|
+
local prd; prd=$(seed_prd)
|
|
578
|
+
comate gate "$prd" review >/dev/null
|
|
579
|
+
comate gate "$prd" set score 90 >/dev/null
|
|
580
|
+
comate gate "$prd" set blocking_open 0 >/dev/null
|
|
581
|
+
printf '# report\n' > "docs/knowledge-base/requirements/test/PRD-001-REVIEW.md"
|
|
582
|
+
run comate gate "$prd" pass
|
|
583
|
+
[ "$status" -eq 1 ]
|
|
584
|
+
[[ "$output" == *"changes_map"* ]]
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
@test "gate pass --tbd requires unresolved blockers" {
|
|
588
|
+
local prd; prd=$(seed_prd)
|
|
589
|
+
comate gate "$prd" review >/dev/null
|
|
590
|
+
comate gate "$prd" set score 90 >/dev/null
|
|
591
|
+
comate gate "$prd" set blocking_open 0 >/dev/null
|
|
592
|
+
comate gate "$prd" set changes_map "demo:功能项1" >/dev/null
|
|
593
|
+
printf '# report\n' > "docs/knowledge-base/requirements/test/PRD-001-REVIEW.md"
|
|
594
|
+
run comate gate "$prd" pass --tbd
|
|
595
|
+
[ "$status" -eq 1 ]
|
|
596
|
+
[[ "$output" == *"requires at least one"* ]]
|
|
597
|
+
}
|
|
598
|
+
|
|
511
599
|
@test "gate pass succeeds and check passes after report written" {
|
|
512
600
|
local prd; prd=$(seed_prd)
|
|
513
601
|
comate gate "$prd" review >/dev/null
|
|
514
602
|
comate gate "$prd" set score 90 >/dev/null
|
|
515
603
|
comate gate "$prd" set blocking_open 0 >/dev/null
|
|
604
|
+
comate gate "$prd" set changes_map "demo:功能项1" >/dev/null
|
|
516
605
|
printf '# 评审报告\n' > \
|
|
517
606
|
"docs/knowledge-base/requirements/test/PRD-001-REVIEW.md"
|
|
518
607
|
run comate gate "$prd" pass
|
|
@@ -525,7 +614,8 @@ seed_prd() {
|
|
|
525
614
|
local prd; prd=$(seed_prd)
|
|
526
615
|
comate gate "$prd" review >/dev/null
|
|
527
616
|
comate gate "$prd" set score 65 >/dev/null
|
|
528
|
-
comate gate "$prd" set blocking_open
|
|
617
|
+
comate gate "$prd" set blocking_open 2 >/dev/null
|
|
618
|
+
comate gate "$prd" set changes_map "demo:功能项1" >/dev/null
|
|
529
619
|
printf '# 评审报告\n' > \
|
|
530
620
|
"docs/knowledge-base/requirements/test/PRD-001-REVIEW.md"
|
|
531
621
|
run comate gate "$prd" pass --tbd
|
|
@@ -539,6 +629,7 @@ seed_prd() {
|
|
|
539
629
|
comate gate "$prd" review >/dev/null
|
|
540
630
|
comate gate "$prd" set score 90 >/dev/null
|
|
541
631
|
comate gate "$prd" set blocking_open 0 >/dev/null
|
|
632
|
+
comate gate "$prd" set changes_map "demo:功能项1" >/dev/null
|
|
542
633
|
printf '# 评审报告\n' > \
|
|
543
634
|
"docs/knowledge-base/requirements/test/PRD-001-REVIEW.md"
|
|
544
635
|
comate gate "$prd" pass >/dev/null
|
|
@@ -554,6 +645,7 @@ seed_prd() {
|
|
|
554
645
|
comate gate "$prd" review >/dev/null
|
|
555
646
|
comate gate "$prd" set score 90 >/dev/null
|
|
556
647
|
comate gate "$prd" set blocking_open 0 >/dev/null
|
|
648
|
+
comate gate "$prd" set changes_map "demo:功能项1" >/dev/null
|
|
557
649
|
printf '# 评审报告\n' > \
|
|
558
650
|
"docs/knowledge-base/requirements/test/PRD-001-REVIEW.md"
|
|
559
651
|
comate gate "$prd" pass >/dev/null
|
|
@@ -572,6 +664,7 @@ seed_prd() {
|
|
|
572
664
|
comate gate "$prd" review >/dev/null
|
|
573
665
|
comate gate "$prd" set score 90 >/dev/null
|
|
574
666
|
comate gate "$prd" set blocking_open 0 >/dev/null
|
|
667
|
+
comate gate "$prd" set changes_map "login-flow:功能项1" >/dev/null
|
|
575
668
|
printf '# 报告\n' > \
|
|
576
669
|
"docs/knowledge-base/requirements/test/PRD-001-REVIEW.md"
|
|
577
670
|
comate gate "$prd" pass >/dev/null
|
|
@@ -587,6 +680,7 @@ seed_prd() {
|
|
|
587
680
|
comate gate "$prd" review >/dev/null
|
|
588
681
|
comate gate "$prd" set score 90 >/dev/null
|
|
589
682
|
comate gate "$prd" set blocking_open 0 >/dev/null
|
|
683
|
+
comate gate "$prd" set changes_map "login-flow:功能项1;logout-flow:功能项2" >/dev/null
|
|
590
684
|
printf '# 报告\n' > \
|
|
591
685
|
"docs/knowledge-base/requirements/test/PRD-001-REVIEW.md"
|
|
592
686
|
comate gate "$prd" pass >/dev/null
|
|
@@ -616,6 +710,7 @@ seed_prd() {
|
|
|
616
710
|
comate gate "$prd" review >/dev/null
|
|
617
711
|
comate gate "$prd" set score 90 >/dev/null
|
|
618
712
|
comate gate "$prd" set blocking_open 0 >/dev/null
|
|
713
|
+
comate gate "$prd" set changes_map "demo:功能项1" >/dev/null
|
|
619
714
|
printf '# 报告\n' > \
|
|
620
715
|
"docs/knowledge-base/requirements/test/PRD-001-REVIEW.md"
|
|
621
716
|
comate gate "$prd" pass >/dev/null
|
|
@@ -25,7 +25,43 @@ fi
|
|
|
25
25
|
bash "$COMATE" state <name> check build
|
|
26
26
|
```
|
|
27
27
|
|
|
28
|
-
|
|
28
|
+
**幂等性检查**:
|
|
29
|
+
|
|
30
|
+
1. 读取 `.comate.yaml` 的 `plan`、`isolation`、`build_mode`、`tdd_mode`、`phase_step`、`phase_step_data`
|
|
31
|
+
|
|
32
|
+
2. **检查是否被依赖阻塞**(最高优先级检查):
|
|
33
|
+
- 若 `phase_step` 为 `build.blocked-by-<dependency>`:
|
|
34
|
+
- 从 `phase_step` 或 `phase_step_data` 中提取依赖的 change 名称
|
|
35
|
+
- 运行 `bash "$COMATE" dependency <dependency> archived` 检查归档目录中是否存在该 change 且其 `.comate.yaml` 明确记录 `archived: true`;不得用“不在活跃列表”推断归档完成
|
|
36
|
+
- **若依赖已归档**:
|
|
37
|
+
```bash
|
|
38
|
+
bash "$COMATE" state <name> set phase_step build.options-confirmed
|
|
39
|
+
bash "$COMATE" state <name> set phase_step_data null
|
|
40
|
+
```
|
|
41
|
+
清除阻塞标记,说明「依赖 change <dependency> 已完成,继续当前 change 的 build」,继续第 3 步检查
|
|
42
|
+
- **若依赖未归档**:提示「当前 change 正在等待 <dependency> 完成。请先完成 <dependency> 的全流程后再继续。」,**立即退出**
|
|
43
|
+
|
|
44
|
+
3. **已完成 Step 3(工作方式已选定)** 的判定条件:
|
|
45
|
+
- `isolation`、`build_mode`、`tdd_mode` **三个字段全部已设置且非 null**
|
|
46
|
+
- 或 `phase_step` 值为 `build.options-confirmed`
|
|
47
|
+
|
|
48
|
+
4. **幂等分支**:
|
|
49
|
+
- **三个决策字段全部已设置** → 跳过 Step 3a-3c,说明「工作方式已选定:isolation={value}, build_mode={value}, tdd_mode={value}」,直接进入执行(加载对应技能)
|
|
50
|
+
- **任一字段未设置或为 null**:
|
|
51
|
+
- 若 `phase_step` 为 `build.plan-created` 或 `build.plan-recorded` → 继续对应步骤(见下方第 5 点)
|
|
52
|
+
- 若 `plan` 未设置 → 从 Step 1 开始
|
|
53
|
+
- 若 `plan` 已设置但决策未完成 → 从 Step 3 开始
|
|
54
|
+
|
|
55
|
+
5. **断点恢复**(根据 `phase_step` 值):
|
|
56
|
+
- `phase_step` 为 `build.plan-created`(或旧版 `1`)→ 从 Step 2 开始并改写新格式
|
|
57
|
+
- `phase_step` 为 `build.plan-recorded` / `build.options-awaiting`(或旧版 `2`)→ 从 Step 3 恢复并改写新格式
|
|
58
|
+
- `phase_step` 为 `build.options-confirmed`(或旧版 `3-confirmed`)→ 直接加载执行技能并改写新格式
|
|
59
|
+
- `phase_step` 为 `build.blocked-by-*` → 见第 2 点的依赖阻塞检查
|
|
60
|
+
|
|
61
|
+
6. **已进入执行阶段**(plan 已设置、决策已完成):
|
|
62
|
+
- 读 tasks.md 找第一个未勾选任务继续
|
|
63
|
+
- 已提交的任务不得重复提交
|
|
64
|
+
- 上下文压缩恢复时,重新加载对应的执行技能,然后继续未完成的任务
|
|
29
65
|
|
|
30
66
|
### 1. 制定计划
|
|
31
67
|
|
|
@@ -44,10 +80,17 @@ base-ref: <实施前的 git rev-parse HEAD>
|
|
|
44
80
|
|
|
45
81
|
`base-ref` 用于验证阶段跨提交统计改动规模。创建计划时先 `git rev-parse HEAD` 记录。
|
|
46
82
|
|
|
83
|
+
**步骤完成后记录**:
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
bash "$COMATE" state <name> set phase_step build.plan-created
|
|
87
|
+
```
|
|
88
|
+
|
|
47
89
|
### 2. 记录计划路径
|
|
48
90
|
|
|
49
91
|
```bash
|
|
50
92
|
bash "$COMATE" state <name> set plan docs/superpowers/plans/YYYY-MM-DD-feature.md
|
|
93
|
+
bash "$COMATE" state <name> set phase_step build.plan-recorded
|
|
51
94
|
```
|
|
52
95
|
|
|
53
96
|
无需手动改 phase,guard 会在退出条件满足后自动流转。
|
|
@@ -79,11 +122,17 @@ bash "$COMATE" state <name> set plan docs/superpowers/plans/YYYY-MM-DD-feature.m
|
|
|
79
122
|
|
|
80
123
|
#### 3c. 联合决策(仍有 ≥2 有效选项的维度才问)
|
|
81
124
|
|
|
82
|
-
对仍有多个有效选项的维度,**一次 AskUserQuestion 问清全部**,选 `branch` 时**分支名也在同一次确认**,不得先问是否继续再二次追问配置或命名。已在 3b 自动写入的维度不再列入问题。每个选项附推荐理由(推荐只说明倾向,不代替用户拍板):
|
|
125
|
+
对仍有多个有效选项的维度,**一次 AskUserQuestion 问清全部**,选 `branch` 时**分支名也在同一次确认**,不得先问是否继续再二次追问配置或命名。已在 3b 自动写入的维度不再列入问题。每个选项附推荐理由(推荐只说明倾向,不代替用户拍板):
|
|
83
126
|
|
|
84
127
|
- **工作区隔离**:`branch` 新建分支(简单快速,≤3 文件推荐)/ `worktree` 独立工作区(需并行或当前分支有未提交工作时推荐)。
|
|
85
128
|
- **执行方式**:`subagent-driven-development`(任务 ≥3、独立、需双阶段审查)/ `executing-plans`(任务 ≤2 且无跨模块依赖,或来自 fix 预设)。
|
|
86
|
-
- **TDD 模式**:`tdd`(涉及业务逻辑/新功能/API,推荐)/ `direct`(无需测试覆盖或用户明确跳过)。
|
|
129
|
+
- **TDD 模式**:`tdd`(涉及业务逻辑/新功能/API,推荐)/ `direct`(无需测试覆盖或用户明确跳过)。
|
|
130
|
+
|
|
131
|
+
提问前先持久化:
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
bash "$COMATE" state <name> set phase_step build.options-awaiting
|
|
135
|
+
```
|
|
87
136
|
|
|
88
137
|
用户选择后(连同 3b 已自动应用的维度)写入:
|
|
89
138
|
|
|
@@ -91,6 +140,7 @@ bash "$COMATE" state <name> set plan docs/superpowers/plans/YYYY-MM-DD-feature.m
|
|
|
91
140
|
bash "$COMATE" state <name> set isolation <branch|worktree>
|
|
92
141
|
bash "$COMATE" state <name> set build_mode <subagent-driven-development|executing-plans|direct>
|
|
93
142
|
bash "$COMATE" state <name> set tdd_mode <tdd|direct>
|
|
143
|
+
bash "$COMATE" state <name> set phase_step build.options-confirmed
|
|
94
144
|
```
|
|
95
145
|
|
|
96
146
|
`isolation` 是脚本级硬约束,保持 `null` 时 `build → verify` 的 guard 和 `state transition build-complete` 都会失败。
|
|
@@ -136,9 +186,36 @@ bash "$COMATE" state <name> set build_mode direct
|
|
|
136
186
|
|------|------|------|
|
|
137
187
|
| 小 | 遗漏验收场景、边界条件 | 直接编辑 delta spec + design.md,追加 tasks.md |
|
|
138
188
|
| 中 | 接口变更、新增组件、数据流变化 | **AskUserQuestion 确认后**加载 `superpowers:brainstorming` 更新 Design Doc + delta spec |
|
|
139
|
-
| 大 | 全新 capability | **AskUserQuestion 确认拆分后**用 `/comate-open` 创建独立 change |
|
|
140
|
-
|
|
141
|
-
**50% 阈值**:以 tasks.md 初始任务总数为基准,新增任务超过总数一半即视为超范围,**必须 AskUserQuestion 暂停**决定是否拆分。创建独立 change 必须调 `/comate-open`,不得直接调 `/opsx:new`。
|
|
189
|
+
| 大 | 全新 capability | **AskUserQuestion 确认拆分后**用 `/comate-open` 创建独立 change,**当前 change 的 build 流程立即终止** |
|
|
190
|
+
|
|
191
|
+
**50% 阈值**:以 tasks.md 初始任务总数为基准,新增任务超过总数一半即视为超范围,**必须 AskUserQuestion 暂停**决定是否拆分。创建独立 change 必须调 `/comate-open`,不得直接调 `/opsx:new`。
|
|
192
|
+
|
|
193
|
+
凡需 AskUserQuestion 决定范围扩张时,提问前先写 `phase_step: build.scope-awaiting`;用户决定继续当前 change 后恢复为 `build.options-confirmed`。
|
|
194
|
+
|
|
195
|
+
**大规模扩张(全新 capability)确认拆分后的流程终止规则**:
|
|
196
|
+
|
|
197
|
+
1. 用 `/comate-open` 创建独立 change `<new-change-name>`
|
|
198
|
+
2. **当前 change `<current>` 的 build 流程立即暂停**,记录依赖关系:
|
|
199
|
+
```bash
|
|
200
|
+
bash "$COMATE" state <current> set phase_step "build.blocked-by-<new-change-name>"
|
|
201
|
+
bash "$COMATE" state <current> set phase_step_data "<new-change-name>"
|
|
202
|
+
```
|
|
203
|
+
3. 提示用户:
|
|
204
|
+
```
|
|
205
|
+
已创建独立 change <new-change-name>。
|
|
206
|
+
当前 change <current> 的 build 流程已暂停,等待 <new-change-name> 完成。
|
|
207
|
+
|
|
208
|
+
请先完成 <new-change-name> 的全流程(design → build → verify → archive),
|
|
209
|
+
然后再继续 <current> 的 build。
|
|
210
|
+
```
|
|
211
|
+
4. **立即退出当前 /comate 调用**,不继续执行当前 change 的后续任务,不进入 Step 5
|
|
212
|
+
|
|
213
|
+
**恢复被阻塞的 change**:
|
|
214
|
+
|
|
215
|
+
当用户稍后运行 `/comate <current>` 时,Step 0 幂等性检查会发现 `phase_step` 包含 `blocked-by` 标记:
|
|
216
|
+
- 检查依赖的 change 是否已归档
|
|
217
|
+
- 若 `dependency <name> archived` 通过 → 清除阻塞标记,恢复 `phase_step` 为 `build.options-confirmed`,继续执行 tasks.md 中未完成的任务
|
|
218
|
+
- 若未归档 → 提示用户先完成依赖 change,退出
|
|
142
219
|
|
|
143
220
|
### 5. 上下文管理与 build 交接包
|
|
144
221
|
|