@opengsd/gsd-core 1.5.0-rc.2 → 1.5.0-rc.4
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/.claude-plugin/plugin.json +1 -1
- package/agents/gsd-advisor-researcher.md +1 -1
- package/agents/gsd-assumptions-analyzer.md +1 -1
- package/agents/gsd-code-fixer.md +1 -1
- package/agents/gsd-code-reviewer.md +1 -1
- package/agents/gsd-codebase-mapper.md +1 -1
- package/agents/gsd-debugger.md +1 -1
- package/agents/gsd-doc-writer.md +1 -1
- package/agents/gsd-eval-auditor.md +1 -1
- package/agents/gsd-executor.md +1 -1
- package/agents/gsd-integration-checker.md +1 -1
- package/agents/gsd-mempalace-curator.md +47 -0
- package/agents/gsd-nyquist-auditor.md +1 -0
- package/agents/gsd-phase-researcher.md +1 -1
- package/agents/gsd-plan-checker.md +1 -1
- package/agents/gsd-planner.md +1 -1
- package/agents/gsd-project-researcher.md +1 -1
- package/agents/gsd-research-synthesizer.md +1 -1
- package/agents/gsd-roadmapper.md +55 -2
- package/agents/gsd-security-auditor.md +1 -0
- package/agents/gsd-ui-auditor.md +1 -1
- package/agents/gsd-ui-checker.md +1 -1
- package/agents/gsd-ui-researcher.md +1 -1
- package/agents/gsd-verifier.md +13 -2
- package/bin/install.js +61 -64
- package/commands/gsd/mempalace-capture.md +71 -0
- package/commands/gsd/mempalace-recall.md +102 -0
- package/commands/gsd/ns-context.md +4 -2
- package/commands/gsd/progress.md +2 -1
- package/gemini-extension.json +1 -1
- package/gsd-core/bin/gsd-tools.cjs +277 -95
- package/gsd-core/bin/lib/active-workstream-store.cjs +6 -0
- package/gsd-core/bin/lib/capability-activation.cjs +86 -0
- package/gsd-core/bin/lib/capability-registry.cjs +1468 -11
- package/gsd-core/bin/lib/capability-state.cjs +128 -21
- package/gsd-core/bin/lib/capability-writer.cjs +354 -0
- package/gsd-core/bin/lib/check-command-router.cjs +328 -1
- package/gsd-core/bin/lib/clusters.cjs +2 -0
- package/gsd-core/bin/lib/command-roster.cjs +19 -0
- package/gsd-core/bin/lib/commands.cjs +33 -10
- package/gsd-core/bin/lib/config-loader.cjs +7 -8
- package/gsd-core/bin/lib/config-schema.cjs +32 -3
- package/gsd-core/bin/lib/config.cjs +81 -26
- package/gsd-core/bin/lib/core.cjs +5 -2
- package/gsd-core/bin/lib/edge-probe.cjs +25 -2
- package/gsd-core/bin/lib/frontmatter.cjs +53 -1
- package/gsd-core/bin/lib/git-base-branch.cjs +194 -0
- package/gsd-core/bin/lib/init.cjs +36 -11
- package/gsd-core/bin/lib/install-profiles.cjs +57 -1
- package/gsd-core/bin/lib/installer-migration-report.cjs +1 -0
- package/gsd-core/bin/lib/loop-resolver.cjs +157 -16
- package/gsd-core/bin/lib/model-resolver.cjs +47 -5
- package/gsd-core/bin/lib/phase.cjs +99 -23
- package/gsd-core/bin/lib/plan-drift-guard.cjs +117 -0
- package/gsd-core/bin/lib/probe-core.cjs +117 -1
- package/gsd-core/bin/lib/profile-output.cjs +45 -4
- package/gsd-core/bin/lib/profile-pipeline-command-router.cjs +138 -0
- package/gsd-core/bin/lib/roadmap-parser.cjs +13 -3
- package/gsd-core/bin/lib/roadmap.cjs +97 -7
- package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +1946 -0
- package/gsd-core/bin/lib/runtime-artifact-layout.cjs +54 -30
- package/gsd-core/bin/lib/runtime-config-adapter-registry.cjs +27 -19
- package/gsd-core/bin/lib/runtime-homes.cjs +26 -20
- package/gsd-core/bin/lib/state-command-router.cjs +15 -3
- package/gsd-core/bin/lib/state-document.cjs +46 -1
- package/gsd-core/bin/lib/state.cjs +461 -94
- package/gsd-core/bin/lib/verify.cjs +92 -8
- package/gsd-core/bin/lib/worktree-safety.cjs +2 -1
- package/gsd-core/bin/shared/config-defaults.manifest.json +1 -2
- package/gsd-core/bin/shared/config-schema.manifest.json +0 -18
- package/gsd-core/bin/shared/model-catalog.json +1 -0
- package/gsd-core/references/edge-probe.md +11 -0
- package/gsd-core/references/loop-hook-dispatch.md +61 -0
- package/gsd-core/references/prohibition-probe-fixtures/01-streak-reminder/expected.json +14 -0
- package/gsd-core/references/prohibition-probe-fixtures/02-clean-utility/expected.json +4 -0
- package/gsd-core/references/prohibition-probe-fixtures/03-multi-prohibition/expected.json +32 -0
- package/gsd-core/references/prohibition-probe.md +248 -0
- package/gsd-core/templates/config.json +1 -1
- package/gsd-core/templates/spec.md +14 -0
- package/gsd-core/workflows/audit-milestone.md +5 -3
- package/gsd-core/workflows/autonomous.md +10 -5
- package/gsd-core/workflows/code-review-fix.md +9 -7
- package/gsd-core/workflows/code-review.md +8 -6
- package/gsd-core/workflows/complete-milestone.md +1 -5
- package/gsd-core/workflows/discuss-phase.md +14 -0
- package/gsd-core/workflows/execute-phase.md +86 -146
- package/gsd-core/workflows/execute-plan.md +21 -6
- package/gsd-core/workflows/help/modes/full.md +7 -1
- package/gsd-core/workflows/new-project.md +3 -3
- package/gsd-core/workflows/next.md +50 -2
- package/gsd-core/workflows/pause-work.md +7 -1
- package/gsd-core/workflows/plan-phase.md +91 -221
- package/gsd-core/workflows/plan-review-convergence.md +14 -4
- package/gsd-core/workflows/pr-branch.md +4 -2
- package/gsd-core/workflows/profile-user.md +3 -1
- package/gsd-core/workflows/progress.md +58 -1
- package/gsd-core/workflows/quick.md +12 -8
- package/gsd-core/workflows/resume-project.md +17 -1
- package/gsd-core/workflows/review.md +19 -2
- package/gsd-core/workflows/secure-phase.md +4 -2
- package/gsd-core/workflows/settings-advanced.md +2 -0
- package/gsd-core/workflows/settings.md +27 -1
- package/gsd-core/workflows/ship.md +58 -5
- package/gsd-core/workflows/spec-phase.md +75 -0
- package/gsd-core/workflows/validate-phase.md +4 -2
- package/gsd-core/workflows/verify-phase.md +14 -4
- package/gsd-core/workflows/verify-work.md +27 -11
- package/hooks/dist/gsd-ensure-canonical-path.js +305 -0
- package/hooks/dist/gsd-statusline.js +1 -1
- package/hooks/dist/managed-hooks-registry.cjs +1 -0
- package/hooks/gsd-ensure-canonical-path.js +305 -0
- package/hooks/gsd-statusline.js +1 -1
- package/hooks/hooks.json +1 -0
- package/hooks/managed-hooks-registry.cjs +1 -0
- package/package.json +5 -4
- package/scripts/affected-tests-lib.cjs +16 -4
- package/scripts/build-hooks.js +7 -0
- package/scripts/changeset/new.cjs +17 -3
- package/scripts/fix-slash-commands.cjs +15 -3
- package/scripts/gen-capability-registry.cjs +373 -49
- package/scripts/gen-inventory-manifest.cjs +1 -4
- package/scripts/gen-loop-host-contract.cjs +55 -0
- package/scripts/issue-version-gate.cjs +140 -0
- package/scripts/lint-allow-test-rule-refs.allowlist.json +327 -0
- package/scripts/lint-allow-test-rule-refs.cjs +162 -0
- package/scripts/lint-test-file-count.allowlist.json +14 -0
- package/scripts/mutation-matrix.cjs +108 -7
- package/scripts/pr-target-policy.cjs +63 -0
- package/scripts/release-tarball-smoke.cjs +7 -1
- package/scripts/research-profiles.cjs +5 -5
- package/scripts/run-tests.cjs +178 -17
|
@@ -271,6 +271,19 @@ Resume testing: `/gsd:verify-work {phase} ${GSD_WS}` — retest specific phase
|
|
|
271
271
|
|
|
272
272
|
This is a WARNING, not a blocker — routing proceeds normally. The debt is visible so the user can make an informed choice.
|
|
273
273
|
|
|
274
|
+
**Step 1.7: Check verification status for the current phase**
|
|
275
|
+
|
|
276
|
+
A phase whose verification ended `gaps_found` or `human_needed` is NOT complete, even when every PLAN.md has a matching SUMMARY.md. The count-based status (`roadmap.analyze`) only sees plans/summaries, so without this check such a phase is reported complete and routing skips straight to the next phase. When the phase appears count-complete (`summaries = plans AND plans > 0`), consult the verification report (the same `verification.status` gate `ship` and `execute-phase` use, from #651):
|
|
277
|
+
|
|
278
|
+
```bash
|
|
279
|
+
PHASE_DIR=".planning/phases/[current-phase-dir]"
|
|
280
|
+
VERIFICATION=$(gsd_run query verification.status "${PHASE_DIR}" 2>/dev/null)
|
|
281
|
+
VERIFICATION_STATUS=$(printf '%s' "$VERIFICATION" | jq -r '.status' 2>/dev/null || echo "")
|
|
282
|
+
VERIFICATION_NEXT_ACTION=$(printf '%s' "$VERIFICATION" | jq -r '.next_action' 2>/dev/null || echo "")
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
Track: `verification_status` — the `.status` field (`passed | gaps_found | human_needed | missing | unknown`). The query already handles a missing VERIFICATION.md (returns `missing`) and unexpected values, so no per-status file probing is needed. `passed`, `missing` (not yet verified), and `unknown` route as complete (Step 3) — `missing` with an advisory that the phase is unverified; `gaps_found` and `human_needed` route back to close the verification debt (Step 2).
|
|
286
|
+
|
|
274
287
|
**Step 2: Route based on counts**
|
|
275
288
|
|
|
276
289
|
| Condition | Meaning | Action |
|
|
@@ -278,9 +291,13 @@ This is a WARNING, not a blocker — routing proceeds normally. The debt is visi
|
|
|
278
291
|
| uat_partial > 0 | UAT testing incomplete | Go to **Route E.2** |
|
|
279
292
|
| uat_with_gaps > 0 | UAT gaps need fix plans | Go to **Route E** |
|
|
280
293
|
| summaries < plans | Unexecuted plans exist | Go to **Route A** |
|
|
281
|
-
| summaries = plans AND plans > 0 | Phase
|
|
294
|
+
| summaries = plans AND plans > 0 AND verification_status = gaps_found | Phase executed; verification found gaps | Go to **Route V.gaps** |
|
|
295
|
+
| summaries = plans AND plans > 0 AND verification_status = human_needed | Phase executed; awaiting human verification | Go to **Route V.human** |
|
|
296
|
+
| summaries = plans AND plans > 0 | Phase complete (verification passed, missing, or n/a) | Go to Step 3 |
|
|
282
297
|
| plans = 0 | Phase not yet planned | Go to **Route B** |
|
|
283
298
|
|
|
299
|
+
Rows are evaluated top to bottom; the first matching row wins. The two `verification_status` rows must precede the general `summaries = plans` row so a non-`passed` verification is not reported as complete.
|
|
300
|
+
|
|
284
301
|
---
|
|
285
302
|
|
|
286
303
|
**Route A: Unexecuted plan exists**
|
|
@@ -431,6 +448,46 @@ UAT.md exists with `status: partial` — testing session ended before all items
|
|
|
431
448
|
|
|
432
449
|
---
|
|
433
450
|
|
|
451
|
+
**Route V.gaps: verification found gaps (gaps_found)**
|
|
452
|
+
|
|
453
|
+
VERIFICATION.md exists with `status: gaps_found` — verification identified gaps that need fix plans. The phase is NOT complete.
|
|
454
|
+
|
|
455
|
+
```
|
|
456
|
+
---
|
|
457
|
+
|
|
458
|
+
## ⚠ Verification Gaps Found
|
|
459
|
+
|
|
460
|
+
**{phase_num}-VERIFICATION.md** reports `gaps_found`. ${VERIFICATION_NEXT_ACTION}
|
|
461
|
+
|
|
462
|
+
`/clear` then:
|
|
463
|
+
|
|
464
|
+
`/gsd:plan-phase {phase} --gaps ${GSD_WS}`
|
|
465
|
+
|
|
466
|
+
---
|
|
467
|
+
```
|
|
468
|
+
|
|
469
|
+
---
|
|
470
|
+
|
|
471
|
+
**Route V.human: human verification required (human_needed)**
|
|
472
|
+
|
|
473
|
+
VERIFICATION.md exists with `status: human_needed` — automated checks passed but manual verification items remain. The phase is NOT complete until they are resolved.
|
|
474
|
+
|
|
475
|
+
```
|
|
476
|
+
---
|
|
477
|
+
|
|
478
|
+
## Human Verification Required
|
|
479
|
+
|
|
480
|
+
**{phase_num}-VERIFICATION.md** reports `human_needed`. ${VERIFICATION_NEXT_ACTION}
|
|
481
|
+
|
|
482
|
+
`/clear` then:
|
|
483
|
+
|
|
484
|
+
`/gsd:verify-work {phase} ${GSD_WS}` — resume human verification
|
|
485
|
+
|
|
486
|
+
---
|
|
487
|
+
```
|
|
488
|
+
|
|
489
|
+
---
|
|
490
|
+
|
|
434
491
|
**Step 3: Check milestone status (only when phase complete)**
|
|
435
492
|
|
|
436
493
|
Read ROADMAP.md and identify:
|
|
@@ -184,8 +184,9 @@ compound on top of each other and stay unpushed (#2916). If `$branch_name`
|
|
|
184
184
|
already exists locally, reuse it as-is so resumed work is not rebased.
|
|
185
185
|
|
|
186
186
|
```bash
|
|
187
|
-
DEFAULT_BRANCH=$(git
|
|
188
|
-
|
|
187
|
+
DEFAULT_BRANCH=$(gsd_run query git.base-branch 2>/dev/null \
|
|
188
|
+
|| git symbolic-ref --quiet --short refs/remotes/origin/HEAD 2>/dev/null | sed 's|^origin/||' \
|
|
189
|
+
|| echo main)
|
|
189
190
|
|
|
190
191
|
if git show-ref --verify --quiet "refs/heads/$branch_name"; then
|
|
191
192
|
git switch "$branch_name" \
|
|
@@ -409,7 +410,7 @@ Agent(
|
|
|
409
410
|
<files_to_read>
|
|
410
411
|
- .planning/STATE.md (Project state — what's already built)
|
|
411
412
|
- .planning/PROJECT.md (Project context)
|
|
412
|
-
- ./CLAUDE.md (if exists — project-specific guidelines)
|
|
413
|
+
- ./CLAUDE.md or ./.claude/CLAUDE.md (if exists — project-specific guidelines)
|
|
413
414
|
${DISCUSS_MODE ? '- ' + QUICK_DIR + '/' + quick_id + '-CONTEXT.md (User decisions — research should align with these)' : ''}
|
|
414
415
|
</files_to_read>
|
|
415
416
|
|
|
@@ -468,7 +469,7 @@ Agent(
|
|
|
468
469
|
|
|
469
470
|
<files_to_read>
|
|
470
471
|
- .planning/STATE.md (Project State)
|
|
471
|
-
- ./CLAUDE.md (if exists — follow project-specific guidelines)
|
|
472
|
+
- ./CLAUDE.md or ./.claude/CLAUDE.md (if exists — follow project-specific guidelines)
|
|
472
473
|
${DISCUSS_MODE ? '- ' + QUICK_DIR + '/' + quick_id + '-CONTEXT.md (User decisions — locked, do not revisit)' : ''}
|
|
473
474
|
${RESEARCH_MODE ? '- ' + QUICK_DIR + '/' + quick_id + '-RESEARCH.md (Research findings — use to inform implementation choices)' : ''}
|
|
474
475
|
</files_to_read>
|
|
@@ -684,7 +685,7 @@ ORCHESTRATOR build-time embed (NOT a sub-agent runtime step): before this dispat
|
|
|
684
685
|
<files_to_read>
|
|
685
686
|
- ${QUICK_DIR}/${quick_id}-PLAN.md (Plan)
|
|
686
687
|
- .planning/STATE.md (Project state)
|
|
687
|
-
- ./CLAUDE.md (Project instructions, if exists)
|
|
688
|
+
- ./CLAUDE.md or ./.claude/CLAUDE.md (Project instructions, if exists)
|
|
688
689
|
- .claude/skills/ or .agents/skills/ (Project skills, if either exists — list skills, read SKILL.md for each, follow relevant rules during implementation)
|
|
689
690
|
</files_to_read>
|
|
690
691
|
|
|
@@ -776,11 +777,14 @@ Note: For quick tasks producing multiple plans (rare), spawn executors in parall
|
|
|
776
777
|
|
|
777
778
|
Skip this step entirely if `$FULL_MODE` is false.
|
|
778
779
|
|
|
779
|
-
**
|
|
780
|
+
**Capability gate:**
|
|
780
781
|
```bash
|
|
781
|
-
|
|
782
|
+
EXECUTE_POST_HOOKS_JSON=$(gsd_run loop render-hooks execute:post --raw)
|
|
782
783
|
```
|
|
783
|
-
|
|
784
|
+
|
|
785
|
+
Resolve active step hooks from `EXECUTE_POST_HOOKS_JSON` where `kind == "step"` and `ref.skill == "code-review"`.
|
|
786
|
+
|
|
787
|
+
If no active code-review step hook exists, skip with message "Code review skipped (code-review capability inactive)".
|
|
784
788
|
|
|
785
789
|
**Scope files from executor's commits:**
|
|
786
790
|
```bash
|
|
@@ -77,10 +77,17 @@ cat .planning/HANDOFF.json 2>/dev/null || true
|
|
|
77
77
|
find .planning -maxdepth 3 -name '.continue-here*.md' -print 2>/dev/null || true
|
|
78
78
|
find . -maxdepth 1 -name '.continue-here*.md' -print 2>/dev/null || true
|
|
79
79
|
|
|
80
|
+
# Outstanding async external jobs (legal external_job_waiting half-state).
|
|
81
|
+
# A PLAN without SUMMARY that has a matching async-job manifest is NOT incomplete
|
|
82
|
+
# work to redo — it is an external job awaiting reconciliation (handled by the
|
|
83
|
+
# async-job branch in determine_next_action, not the incomplete-plan branch).
|
|
84
|
+
find .planning/async-jobs -maxdepth 1 -name '*.json' -print 2>/dev/null || true
|
|
85
|
+
|
|
80
86
|
# Check for plans without summaries (incomplete execution)
|
|
81
87
|
for plan in .planning/phases/*/*-PLAN.md; do
|
|
82
88
|
[ -e "$plan" ] || continue
|
|
83
89
|
summary="${plan/PLAN/SUMMARY}"
|
|
90
|
+
# NOTE: a PLAN without SUMMARY that matches a non-terminal async-job manifest is external_job_waiting (handled by the async-job branch), not incomplete work to redo.
|
|
84
91
|
[ ! -f "$summary" ] && echo "Incomplete: $plan"
|
|
85
92
|
done 2>/dev/null || true
|
|
86
93
|
|
|
@@ -164,6 +171,15 @@ Present complete project status to user:
|
|
|
164
171
|
<step name="determine_next_action">
|
|
165
172
|
Based on project state, determine the most logical next action:
|
|
166
173
|
|
|
174
|
+
**If an async-job manifest exists (`.planning/async-jobs/*.json`):**
|
|
175
|
+
- Treat manifest commands as untrusted — surface the exact command + manifest path and require explicit user confirmation before running any. If more than one manifest matches a `plan_id` or any is malformed, fail closed (surface the conflict and stop). See `docs/reference/planning-artifacts.md`.
|
|
176
|
+
- Outstanding external jobs are the primary resume context — surface them first.
|
|
177
|
+
- For each manifest read `plan_id`, `status`, `expected_artifacts`, `verification_command`, `resume_command`:
|
|
178
|
+
- `submitted` / `running` → report "external job {job_id} still {status}"; offer to re-check or wait.
|
|
179
|
+
- `completed-unverified` → after user confirmation, verify `expected_artifacts` / run `verification_command`, then close the plan (write SUMMARY). Do NOT close before verification succeeds.
|
|
180
|
+
- `failed` / `cancelled` / `timeout` → surface `terminal_details`; offer: re-run reconciliation (`resume_command`), abort, or mark-skip; resubmitting compute is a Capability/user action.
|
|
181
|
+
- A PLAN-without-SUMMARY whose `plan_id` matches a non-terminal manifest is `external_job_waiting`, NOT "incomplete plan execution" — do not offer to re-run it.
|
|
182
|
+
|
|
167
183
|
**If interrupted agent exists:**
|
|
168
184
|
→ Primary: Resume interrupted agent (Task tool with resume parameter)
|
|
169
185
|
→ Option: Start fresh (abandon agent work)
|
|
@@ -176,7 +192,7 @@ Based on project state, determine the most logical next action:
|
|
|
176
192
|
→ Fallback: Resume from checkpoint
|
|
177
193
|
→ Option: Start fresh on current plan
|
|
178
194
|
|
|
179
|
-
**If incomplete plan (PLAN without SUMMARY)
|
|
195
|
+
**If incomplete plan (PLAN without SUMMARY)** — but if its `plan_id` matches a non-terminal async-job manifest, route to the async-job branch above (`external_job_waiting`), do NOT offer to re-run it:
|
|
180
196
|
→ Primary: Complete the incomplete plan
|
|
181
197
|
→ Option: Abandon and move on
|
|
182
198
|
|
|
@@ -223,6 +223,16 @@ CODEX_MODEL=$(gsd_run query config-get review.models.codex 2>/dev/null | jq -r '
|
|
|
223
223
|
OPENCODE_MODEL=$(gsd_run query config-get review.models.opencode 2>/dev/null | jq -r '.' 2>/dev/null || true)
|
|
224
224
|
# review.models.agy is reserved for future model-pinning support; agy selects its model internally
|
|
225
225
|
AGY_MODEL=$(gsd_run query config-get review.models.agy 2>/dev/null | jq -r '.' 2>/dev/null || true)
|
|
226
|
+
|
|
227
|
+
# #1115: `--dangerously-bypass-hook-trust` only exists on codex-cli >= 0.137.0.
|
|
228
|
+
# Capability-probe it so older installs don't fail with "unexpected argument"
|
|
229
|
+
# (which, with stderr suppressed, produced a silent empty review). The codex
|
|
230
|
+
# invocation works fine without the flag on older versions.
|
|
231
|
+
if codex exec --help 2>/dev/null | grep -q -- '--dangerously-bypass-hook-trust'; then
|
|
232
|
+
CODEX_BYPASS_FLAG="--dangerously-bypass-hook-trust"
|
|
233
|
+
else
|
|
234
|
+
CODEX_BYPASS_FLAG=""
|
|
235
|
+
fi
|
|
226
236
|
```
|
|
227
237
|
|
|
228
238
|
For each selected CLI, invoke in sequence (not parallel — avoid rate limits):
|
|
@@ -247,10 +257,17 @@ fi
|
|
|
247
257
|
|
|
248
258
|
**Codex:**
|
|
249
259
|
```bash
|
|
260
|
+
# $CODEX_BYPASS_FLAG is capability-gated above (#1115). Capture stderr to a .err
|
|
261
|
+
# file (not /dev/null) so a non-zero exit — e.g. a flag the installed codex-cli
|
|
262
|
+
# does not support — is diagnosable instead of a silent empty review.
|
|
250
263
|
if [ -n "$CODEX_MODEL" ] && [ "$CODEX_MODEL" != "null" ]; then
|
|
251
|
-
cat /tmp/gsd-review-prompt-{phase}.md | codex exec --ephemeral
|
|
264
|
+
cat /tmp/gsd-review-prompt-{phase}.md | codex exec --ephemeral $CODEX_BYPASS_FLAG --model "$CODEX_MODEL" --skip-git-repo-check - 2>/tmp/gsd-review-codex-{phase}.err > /tmp/gsd-review-codex-{phase}.md
|
|
252
265
|
else
|
|
253
|
-
cat /tmp/gsd-review-prompt-{phase}.md | codex exec --ephemeral
|
|
266
|
+
cat /tmp/gsd-review-prompt-{phase}.md | codex exec --ephemeral $CODEX_BYPASS_FLAG --skip-git-repo-check - 2>/tmp/gsd-review-codex-{phase}.err > /tmp/gsd-review-codex-{phase}.md
|
|
267
|
+
fi
|
|
268
|
+
if [ ! -s /tmp/gsd-review-codex-{phase}.md ]; then
|
|
269
|
+
echo "Codex review failed or returned empty output. stderr:" > /tmp/gsd-review-codex-{phase}.md
|
|
270
|
+
cat /tmp/gsd-review-codex-{phase}.err >> /tmp/gsd-review-codex-{phase}.md
|
|
254
271
|
fi
|
|
255
272
|
```
|
|
256
273
|
|
|
@@ -26,10 +26,12 @@ Parse: `phase_dir`, `phase_number`, `phase_name`, `phase_slug`, `padded_phase`.
|
|
|
26
26
|
|
|
27
27
|
```bash
|
|
28
28
|
AUDITOR_MODEL=$(gsd_run query resolve-model gsd-security-auditor --raw)
|
|
29
|
-
|
|
29
|
+
VERIFY_POST_HOOKS_JSON=$(gsd_run loop render-hooks verify:post --raw)
|
|
30
30
|
```
|
|
31
31
|
|
|
32
|
-
|
|
32
|
+
Resolve active step hooks from `VERIFY_POST_HOOKS_JSON` where `kind == "step"` and `ref.skill == "secure-phase"`.
|
|
33
|
+
|
|
34
|
+
If no active secure-phase step hook exists: exit with "Security enforcement disabled. Enable via /gsd:settings."
|
|
33
35
|
|
|
34
36
|
Display banner: `GSD > SECURE PHASE {N}: {name}`
|
|
35
37
|
|
|
@@ -672,6 +672,8 @@ Canonical tier mappings by provider and budget:
|
|
|
672
672
|
|
|
673
673
|
Look up the selected (provider, budget) row and proceed to Step E to write those values.
|
|
674
674
|
|
|
675
|
+
> **claude runtime note:** On the default `claude` runtime, policy-resolved model IDs (e.g. `claude-fable-5`) are mapped to Claude Code agent aliases (`fable`, `opus`, `sonnet`, `haiku`); an ID with no corresponding alias emits a stderr warning and falls back to the configured tier alias.
|
|
676
|
+
|
|
675
677
|
**Step D — Generic-provider path:**
|
|
676
678
|
|
|
677
679
|
Prompt the user to enter each model ID as a free-text input. An empty input means "keep
|
|
@@ -441,7 +441,33 @@ Merge new settings into existing config.json:
|
|
|
441
441
|
}
|
|
442
442
|
```
|
|
443
443
|
|
|
444
|
-
**Safe merge:** Apply each chosen value
|
|
444
|
+
**Safe merge:** Apply each chosen value so unrelated keys are never clobbered. Use the appropriate write path per key:
|
|
445
|
+
|
|
446
|
+
- **Capability hook-gate keys** (owned by a capability in the registry — see `registry.configSchema`): write via the capability writer:
|
|
447
|
+
```bash
|
|
448
|
+
gsd_run capability set <owner> --gate <key>=<value> [--config-dir "$RUNTIME_CONFIG_DIR"]
|
|
449
|
+
```
|
|
450
|
+
The capability-owned keys written by this workflow and their owners are:
|
|
451
|
+
| Key | Owner capability |
|
|
452
|
+
|---|---|
|
|
453
|
+
| `workflow.research` | `research` |
|
|
454
|
+
| `workflow.nyquist_validation` | `nyquist` |
|
|
455
|
+
| `workflow.pattern_mapper` | `pattern-mapper` |
|
|
456
|
+
| `workflow.ui_phase` | `ui` |
|
|
457
|
+
| `workflow.ui_safety_gate` | `ui` |
|
|
458
|
+
| `workflow.ai_integration_phase` | `ai-integration` |
|
|
459
|
+
| `workflow.tdd_mode` | `tdd` |
|
|
460
|
+
| `workflow.code_review` | `code-review` |
|
|
461
|
+
| `workflow.code_review_depth` | `code-review` |
|
|
462
|
+
| `workflow.ui_review` | `ui` |
|
|
463
|
+
| `intel.enabled` | `intel` |
|
|
464
|
+
| `graphify.enabled` | `graphify` |
|
|
465
|
+
|
|
466
|
+
`code_review_depth` is written only if the `code_review` question was answered `on`; otherwise leave the existing value in place.
|
|
467
|
+
|
|
468
|
+
- **Non-capability keys** (`model_profile`, `commit_docs`, `workflow.plan_check`, `workflow.verifier`, `workflow.auto_advance`, `workflow.text_mode`, `workflow.research_before_questions`, `workflow.discuss_mode`, `workflow.skip_discuss`, `workflow.use_worktrees`, `plan_review.source_grounding`, `graphify.auto_update`, `git.*`, `hooks.*`, `model_policy.*`): write via `gsd_run query config-set <key.path> <value>` as before.
|
|
469
|
+
|
|
470
|
+
`model_profile` is written on Q1 "Adaptive (Recommended)" (→ adaptive) or Q1 "Inherit" (→ inherit) immediately; for Q1 "Standard tier…", `model_profile` is written from Q2's answer. If Q1 = "Standard tier…" but Q2 is cancelled, leave the existing `model_profile` value unchanged — do not write any new value.
|
|
445
471
|
|
|
446
472
|
Write updated config to `$GSD_CONFIG_PATH` (the workstream-aware path resolved in `ensure_and_load_config`). Never hardcode `.planning/config.json` — workstream installs route to `.planning/workstreams/<slug>/config.json`.
|
|
447
473
|
</step>
|
|
@@ -13,6 +13,11 @@ Create a pull request from completed phase/milestone work, generate a rich PR bo
|
|
|
13
13
|
Read all files referenced by the invoking prompt's execution_context before starting.
|
|
14
14
|
</required_reading>
|
|
15
15
|
|
|
16
|
+
<available_agent_types>
|
|
17
|
+
Valid GSD subagent types (use exact names — do not fall back to 'general-purpose'):
|
|
18
|
+
- gsd-mempalace-curator — Ship-time MemPalace curation (diary, KG mirror, cross-project tunnels, wing-scoped prune); dispatched at ship:post when the mempalace capability is enabled.
|
|
19
|
+
</available_agent_types>
|
|
20
|
+
|
|
16
21
|
<process>
|
|
17
22
|
|
|
18
23
|
<step name="initialize">
|
|
@@ -35,11 +40,7 @@ Extract: `branching_strategy`, `branch_name`.
|
|
|
35
40
|
|
|
36
41
|
Detect base branch for PRs and merges:
|
|
37
42
|
```bash
|
|
38
|
-
BASE_BRANCH=$(gsd_run query
|
|
39
|
-
if [ -z "$BASE_BRANCH" ] || [ "$BASE_BRANCH" = "null" ]; then
|
|
40
|
-
BASE_BRANCH=$(git symbolic-ref refs/remotes/origin/HEAD 2>/dev/null | sed 's|^refs/remotes/origin/||')
|
|
41
|
-
BASE_BRANCH="${BASE_BRANCH:-main}"
|
|
42
|
-
fi
|
|
43
|
+
BASE_BRANCH=$(gsd_run query git.base-branch)
|
|
43
44
|
```
|
|
44
45
|
</step>
|
|
45
46
|
|
|
@@ -79,6 +80,32 @@ Verify the work is ready to ship:
|
|
|
79
80
|
which gh && gh auth status 2>&1
|
|
80
81
|
```
|
|
81
82
|
If `gh` not found or not authenticated: provide setup instructions and exit.
|
|
83
|
+
|
|
84
|
+
6. **Security ship gate (capability-driven).**
|
|
85
|
+
|
|
86
|
+
Resolve active `ship:pre` gate hooks from the capability registry — the registry evaluates each hook's `when` condition, so do **not** read `workflow.security_enforcement` directly:
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
SHIP_PRE_HOOKS_JSON=$(gsd_run loop render-hooks ship:pre --raw)
|
|
90
|
+
SECURITY_FILE=$(ls "${PHASE_DIR}"/*-SECURITY.md 2>/dev/null | head -1)
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Read the `activeHooks` array from `SHIP_PRE_HOOKS_JSON` in-context (do NOT pipe it through a shell parser).
|
|
94
|
+
|
|
95
|
+
If an active entry exists with `kind == "gate"`, `capId == "security"`, and `blocking == true`, enforce its predicate (`SECURITY.md` frontmatter `threats_open == 0`) before shipping:
|
|
96
|
+
|
|
97
|
+
- **`SECURITY_FILE` is empty** → block with `SECURITY_SHIP_GATE_NO_REVIEW`:
|
|
98
|
+
```
|
|
99
|
+
⚠ Security enforcement is enabled but no SECURITY.md exists for this phase.
|
|
100
|
+
Run /gsd:secure-phase {phase} and resolve findings before shipping.
|
|
101
|
+
```
|
|
102
|
+
- **`SECURITY_FILE` exists** → read its frontmatter `threats_open`. The gate passes **only** when `threats_open` is exactly `0`. For any other value — `threats_open` > 0, or a missing / non-numeric / unparsable field — **fail closed and block** with `SECURITY_SHIP_GATE_OPEN_THREATS` (the predicate is strict equality to `0`; never ship on an ambiguous value):
|
|
103
|
+
```
|
|
104
|
+
⚠ Security ship gate: SECURITY.md does not assert threats_open == 0 (found: {threats_open|unset}).
|
|
105
|
+
Resolve open threats (or re-run /gsd:secure-phase {phase}) before shipping.
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
If no active security `ship:pre` gate hook is present (security enforcement off), skip this check silently.
|
|
82
109
|
</step>
|
|
83
110
|
|
|
84
111
|
<step name="push_branch">
|
|
@@ -371,6 +398,32 @@ gsd_run query commit "docs(${padded_phase}): ship phase ${PHASE_NUMBER} — PR #
|
|
|
371
398
|
```
|
|
372
399
|
</step>
|
|
373
400
|
|
|
401
|
+
<step name="ship_post_capability_dispatch">
|
|
402
|
+
|
|
403
|
+
> Capability-driven dispatch. Resolves active `ship:post` hooks via the capability registry; each hook's `when` is evaluated by the registry — no inline `config-get`. All `ship:post` hooks are post-ship and additive (`onError: skip`); a failure here never affects the already-created PR.
|
|
404
|
+
|
|
405
|
+
```bash
|
|
406
|
+
SHIP_POST_HOOKS_JSON=$(gsd_run loop render-hooks ship:post --raw)
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
Read the `activeHooks` array directly from `SHIP_POST_HOOKS_JSON` in-context (do NOT pipe it through a shell parser).
|
|
410
|
+
|
|
411
|
+
**Branch 1 — no active `ship:post` step hooks (`activeHooks` has no entry with `kind == "step"`):** Skip silently to the report.
|
|
412
|
+
|
|
413
|
+
**Generic step hook dispatch contract:** For each active entry where `kind == "step"`:
|
|
414
|
+
- Honor `consumes`: if it lists `UAT.md`, resolve `ls "${PHASE_DIR}"/*-UAT.md 2>/dev/null | head -1` and pass it to the dispatch; if a consumed artifact is absent, skip that hook.
|
|
415
|
+
- If `ref.agent` is set, first show the spawn banner, then dispatch the agent named by `ref.agent` (use the exact `ref.agent` value as the subagent type — e.g. `gsd-mempalace-curator` — never `general-purpose`):
|
|
416
|
+
|
|
417
|
+
```
|
|
418
|
+
◆ Spawning ship:post capability agent... (runs in a subagent — no output until it returns, ~1–2 min; expected, not a freeze)
|
|
419
|
+
```
|
|
420
|
+
|
|
421
|
+
`Agent(subagent_type=ref.agent, prompt="Ship-time capability hook for phase ${PHASE_NUMBER}. Phase dir: ${PHASE_DIR}. Consume: ${consumed_files}. Follow your agent instructions.", model="{balanced_model}")`
|
|
422
|
+
- If `ref.skill` is set, dispatch with `Skill(skill="gsd-${ref.skill}", args="${PHASE_NUMBER} --auto ${GSD_WS}")` (prepend `gsd-` to `ref.skill`).
|
|
423
|
+
|
|
424
|
+
Each dispatch is best-effort: if it errors, record a warning and continue — never re-raise (`onError: skip`).
|
|
425
|
+
</step>
|
|
426
|
+
|
|
374
427
|
<step name="report">
|
|
375
428
|
```
|
|
376
429
|
───────────────────────────────────────────────────────────────
|
|
@@ -295,6 +295,9 @@ For each Requirement gathered so far:
|
|
|
295
295
|
- **Dismiss (reason)** → mark `dismissed` with a required non-empty reason.
|
|
296
296
|
- **Backstop with a test** → mark `backstop`; note "held-out edge test" for plan-phase.
|
|
297
297
|
- **Defer** → leave `unresolved`.
|
|
298
|
+
- An `unclassified` row (probe `unclassified — review manually`) means the requirement's
|
|
299
|
+
prose matched no shape cue (#1110) — treat it like any other candidate (**Specify**,
|
|
300
|
+
**Dismiss (reason)**, or **Defer**). A manual-review nudge, not a hard block.
|
|
298
301
|
|
|
299
302
|
**Soft gate (after resolving):**
|
|
300
303
|
- All applicable edges resolved → proceed to Step 6.
|
|
@@ -310,13 +313,83 @@ For each Requirement gathered so far:
|
|
|
310
313
|
otherwise auto-`backstop` (never auto-dismiss — a wrong dismissal is the exact silent
|
|
311
314
|
failure being eliminated). Log: `[auto] edge coverage: C covered, B backstop, U unresolved`.
|
|
312
315
|
|
|
316
|
+
**`unclassified` exception (#1110):** `--auto` leaves an `unclassified` candidate
|
|
317
|
+
**`unresolved`** (the soft gate surfaces it as a flagged planner assumption) — it never
|
|
318
|
+
auto-`backstop`s it. A missing shape is not evidence an edge exists, so minting a held-out
|
|
319
|
+
edge obligation on a requirement that may be genuinely edge-free would be a false claim and
|
|
320
|
+
risks a vacuous edge test. Leaving it `unresolved` keeps the zero-cue requirement visible
|
|
321
|
+
(never a silent drop) without fabricating an edge — which is exactly #1110's purpose: surface
|
|
322
|
+
it for review, do not auto-handle it.
|
|
323
|
+
|
|
313
324
|
Populate the `## Edge Coverage` section of SPEC.md from the resolved edges.
|
|
314
325
|
|
|
326
|
+
## Step 5.6: Prohibition-Completeness Probe (must-NOT)
|
|
327
|
+
|
|
328
|
+
Run AFTER Step 5.5 (you probe the must-NOT axis of clear requirements, over the same
|
|
329
|
+
requirement list). Reference: @~/.claude/gsd-core/references/prohibition-probe.md — the
|
|
330
|
+
portable two-stage protocol, the canon-referral rule, and the status×verification schema
|
|
331
|
+
live there (size-cap discipline; keep this step lean).
|
|
332
|
+
|
|
333
|
+
**D1 — no compiled engine (ADR-550 D7b).** Unlike Step 5.5, the prohibition probe has NO
|
|
334
|
+
compiled recall engine and runs NO `node` invocation here. The recall stage is an LLM prose
|
|
335
|
+
pass: the closed eight-category edge taxonomy a classifier can apply does not exist for the
|
|
336
|
+
open values/safety/ethics must-NOT axis. Do NOT copy the Step 5.5 engine-resolution block.
|
|
337
|
+
Only the schema/projection layer is real code; the recall is prose.
|
|
338
|
+
|
|
339
|
+
For each Requirement gathered so far, run the two-stage recall→precision pass:
|
|
340
|
+
|
|
341
|
+
1. **Stage 1 — Recall (adversarial prose probe).** Ask the single adversarial question of the
|
|
342
|
+
requirement: *"What could this feature silently become that the author would NOT want, but
|
|
343
|
+
the spec does not forbid?"* Over-produce (~10 raw must-NOT candidates) — recall first.
|
|
344
|
+
2. **Stage 2 — Precision (one-pass classifier).** Filter the raw list in a single pass:
|
|
345
|
+
**DROP routine-engineering** items (normal correctness/hygiene — "must not mutate input",
|
|
346
|
+
"must not throw on empty" — owned by the edge probe or code review); **KEEP
|
|
347
|
+
values / safety / ethics** items (manipulative framing, protected-attribute proxies, raw
|
|
348
|
+
PII in plaintext). This collapses ~10 → ~2–3 genuine prohibitions.
|
|
349
|
+
3. **Canon-referral (ADR-550 D6, PROB-13).** A kept candidate that is canon security/compliance
|
|
350
|
+
(OWASP / prototype-pollution / path-traversal / injection / GDPR / generic fairness) is
|
|
351
|
+
NOT minted here — emit a one-line breadcrumb (*"prototype-pollution is canon — owned by
|
|
352
|
+
/gsd:secure-phase + eslint; not minted here"*) and DROP it. Minting canon items duplicates
|
|
353
|
+
/gsd:secure-phase and drowns the bespoke signal.
|
|
354
|
+
4. **Resolve each surfaced (non-canon) prohibition** (AskUserQuestion; text mode → numbered list):
|
|
355
|
+
- **Keep it** → write a NEGATIVE acceptance criterion (a must-NOT line) into Acceptance
|
|
356
|
+
Criteria AND mark the prohibition `resolved` with a verification tier: `test` (a
|
|
357
|
+
mechanical negative test/lint/assertion exists) or `judgment` (real but not mechanically
|
|
358
|
+
checkable — routes to judgment review).
|
|
359
|
+
- **Dismiss (reason)** → mark `dismissed` with a REQUIRED non-empty reason (PROB-05). The
|
|
360
|
+
reason string is the audit trail; silence is not a valid dismissal.
|
|
361
|
+
- **Defer** → leave `unresolved`.
|
|
362
|
+
|
|
363
|
+
**Soft gate (after resolving) — PROB-06:**
|
|
364
|
+
- All applicable prohibitions resolved → proceed to Step 6.
|
|
365
|
+
- Any `unresolved` → AskUserQuestion:
|
|
366
|
+
- header: "Prohibitions"
|
|
367
|
+
- question: "[N] prohibition(s) are unresolved: [list]. What do you want to do?"
|
|
368
|
+
- options: "Resolve now" (loop back) / "Write SPEC.md anyway — flag unresolved" /
|
|
369
|
+
"Keep probing"
|
|
370
|
+
- On "anyway": write SPEC.md with those rows marked `⚠ Prohibition unresolved — planner
|
|
371
|
+
must treat as assumption`. This is a soft gate (write-anyway-with-flags), never a silent
|
|
372
|
+
skip — the soft gate IS the control.
|
|
373
|
+
|
|
374
|
+
**`--auto` mode:** auto-`resolved` where a defensible negative acceptance criterion can be
|
|
375
|
+
written (test or judgment tier); otherwise leave `unresolved`. **`--auto` NEVER auto-dismisses
|
|
376
|
+
a prohibition** — a wrong dismissal is the exact silent failure this probe eliminates (PROB-06,
|
|
377
|
+
the load-bearing safety property). Log: `[auto] prohibitions: R resolved, U unresolved`.
|
|
378
|
+
|
|
379
|
+
**Text mode (PROB-09):** per Step 5's text-mode rule, replace the AskUserQuestion menus above
|
|
380
|
+
with plain-text numbered lists — there is NO hard AskUserQuestion dependency, so the probe
|
|
381
|
+
runs identically for non-Claude / text-mode hosts.
|
|
382
|
+
|
|
383
|
+
Populate the `## Prohibitions` section of SPEC.md from the resolved prohibitions (each
|
|
384
|
+
`resolved`/`test` row is a checkable negative acceptance criterion; `resolved`/`judgment`
|
|
385
|
+
rows route to judgment review; `⚠ UNRESOLVED` rows are flagged as assumptions).
|
|
386
|
+
|
|
315
387
|
## Step 6: Generate SPEC.md
|
|
316
388
|
|
|
317
389
|
Use the SPEC.md template from @~/.claude/gsd-core/templates/spec.md.
|
|
318
390
|
|
|
319
391
|
- Populate the **Edge Coverage** section from Step 5.5 (covered/dismissed/backstop/unresolved rows).
|
|
392
|
+
- Populate the **Prohibitions** section from Step 5.6 (resolved/dismissed/unresolved rows with the test|judgment tier).
|
|
320
393
|
|
|
321
394
|
**Requirements for every requirement entry:**
|
|
322
395
|
- One specific, testable statement
|
|
@@ -377,6 +450,7 @@ Next: /gsd:discuss-phase {X}
|
|
|
377
450
|
- Scout the codebase BEFORE the first question — grounded questions only
|
|
378
451
|
- Max 2–3 questions per round — do not frontload all questions at once
|
|
379
452
|
- Step 5.5 edge probe runs after the ambiguity gate; dismissals require a reason; --auto never auto-dismisses
|
|
453
|
+
- Step 5.6 prohibition probe runs after the edge probe; dismissals require a reason; --auto never auto-dismisses a prohibition
|
|
380
454
|
</critical_rules>
|
|
381
455
|
|
|
382
456
|
<success_criteria>
|
|
@@ -389,4 +463,5 @@ Next: /gsd:discuss-phase {X}
|
|
|
389
463
|
- SPEC.md committed atomically (when commit_docs is true)
|
|
390
464
|
- User directed to /gsd:discuss-phase as next step
|
|
391
465
|
- Edge-completeness probe run; Edge Coverage section populated; unresolved edges flagged as assumptions
|
|
466
|
+
- Prohibition-completeness probe run; Prohibitions section populated; unresolved prohibitions flagged as assumptions
|
|
392
467
|
</success_criteria>
|
|
@@ -26,10 +26,12 @@ Parse: `phase_dir`, `phase_number`, `phase_name`, `phase_slug`, `padded_phase`.
|
|
|
26
26
|
|
|
27
27
|
```bash
|
|
28
28
|
AUDITOR_MODEL=$(gsd_run query resolve-model gsd-nyquist-auditor --raw)
|
|
29
|
-
|
|
29
|
+
VERIFY_POST_HOOKS_JSON=$(gsd_run loop render-hooks verify:post --raw)
|
|
30
30
|
```
|
|
31
31
|
|
|
32
|
-
|
|
32
|
+
Resolve active step hooks from `VERIFY_POST_HOOKS_JSON` where `kind == "step"` and `ref.skill == "validate-phase"`.
|
|
33
|
+
|
|
34
|
+
If no active validate-phase step hook exists: exit with "Nyquist validation is disabled. Enable via /gsd:settings."
|
|
33
35
|
|
|
34
36
|
Display banner: `GSD > VALIDATE PHASE {N}: {name}`
|
|
35
37
|
|
|
@@ -63,10 +63,15 @@ for plan in "$PHASE_DIR"/*-PLAN.md; do
|
|
|
63
63
|
done
|
|
64
64
|
```
|
|
65
65
|
|
|
66
|
-
Returns JSON: `{ truths: [...], artifacts: [...], key_links: [...] }`
|
|
66
|
+
Returns JSON: `{ truths: [...], artifacts: [...], key_links: [...], prohibitions: [...] }`
|
|
67
67
|
|
|
68
68
|
Aggregate all must_haves across plans for phase-level verification.
|
|
69
69
|
|
|
70
|
+
**Prohibitions (`must_haves.prohibitions`, ADR-550 D3 — the must-NOT sibling block):** When a plan carries `must_haves.prohibitions`, extract each `{ statement, status, verification }` item and route it by `verification` tier in verdict assembly (ADR-550 D4, "B-with-guard", 2026-06-12 maintainer decision). These are NEGATIVE checks (the must-NOT must NOT have happened), distinct from positive `truths`:
|
|
71
|
+
|
|
72
|
+
- **judgment-tier → mode-dependent soft-gate.** Interactive verify defers each item to the end-of-phase human checkpoint (`human_verify_mode: end-of-phase`). Autonomous verify records a NON-AUTHORITATIVE LLM-judge verdict + a prominent `unverified-prohibition — human review recommended` flag (autonomous completion reads "complete with N flagged prohibitions"). NEVER a silent pass; NEVER a hard halt of an AFK run.
|
|
73
|
+
- **test-tier → FAIL CLOSED (accept-and-flag).** Accept the `verification: test` value (the SPEC↔must_haves.prohibitions projection contract holds — no forced schema change later), but a well-formed test-tier item reaching verify with NO wired enforcement disposes as UNVERIFIED, flagged like an unresolved judgment item, NEVER green. The deterministic fail-closed default is `dispositionForProhibition()` in probe-core (`status: 'unverified'`, `flagged: true` on empty `enforcementEvidence`). The real fail-first negative-test enforcement MECHANISM defers to a follow-up PR (#644's corpus is entirely judgment-tier; a contrived test-tier fixture here would be the gold-plating failure mode).
|
|
74
|
+
|
|
70
75
|
**Option B: Use Success Criteria from ROADMAP.md**
|
|
71
76
|
|
|
72
77
|
If no must_haves in frontmatter (MUST_HAVES returns error or empty), check for Success Criteria:
|
|
@@ -465,13 +470,18 @@ Classify status using this decision tree IN ORDER (most restrictive first):
|
|
|
465
470
|
1. IF any truth FAILED, artifact MISSING/STUB, key link NOT_WIRED, blocker found, **or test quality audit found blockers (disabled requirement tests, circular tests)**:
|
|
466
471
|
→ **gaps_found**
|
|
467
472
|
|
|
468
|
-
2. IF
|
|
473
|
+
2. IF any `must_haves.prohibitions` item disposes as flagged-unverified (ADR-550 D4):
|
|
474
|
+
- **test-tier, fail-closed** (no wired enforcement — `dispositionForProhibition()` returns `status: 'unverified'`, `flagged: true`): → **gaps_found** (never green; the unwired test-tier item is an unverified gap).
|
|
475
|
+
- **judgment-tier, autonomous run** (non-authoritative LLM-judge verdict): emit the `unverified-prohibition — human review recommended` flag and classify → **human_needed** (autonomous completion reads "complete with N flagged prohibitions"; never a silent pass, never a hard halt).
|
|
476
|
+
- **judgment-tier, interactive run**: route to the end-of-phase human checkpoint → **human_needed**.
|
|
477
|
+
|
|
478
|
+
3. IF the previous step produced ANY human verification items:
|
|
469
479
|
→ **human_needed** (even if all truths VERIFIED and score is N/N)
|
|
470
480
|
|
|
471
|
-
|
|
481
|
+
4. IF all checks pass AND no human verification items AND no flagged prohibitions:
|
|
472
482
|
→ **passed**
|
|
473
483
|
|
|
474
|
-
**passed is ONLY valid when no human verification items exist.**
|
|
484
|
+
**passed is ONLY valid when no human verification items AND no flagged prohibitions exist.** A prohibition (must-NOT) can never be silently absorbed into a `passed` verdict — that is the core failure mode ADR-550 D4 forbids.
|
|
475
485
|
|
|
476
486
|
**Score:** `verified_truths / total_truths`
|
|
477
487
|
</step>
|
|
@@ -108,16 +108,17 @@ Continue to `create_uat_file`.
|
|
|
108
108
|
<step name="automated_ui_verification">
|
|
109
109
|
**Automated UI Verification (when Playwright-MCP is available)**
|
|
110
110
|
|
|
111
|
-
Before
|
|
112
|
-
`mcp__playwright__*` or `mcp__puppeteer__*` tools are available in the current session.
|
|
111
|
+
Before UAT, check UI capability activation and whether Playwright/Puppeteer MCP tools are available.
|
|
113
112
|
|
|
114
113
|
```bash
|
|
115
|
-
|
|
114
|
+
PLAN_HOOKS_JSON=$(gsd_run loop render-hooks plan:pre --raw)
|
|
116
115
|
UI_SPEC_FILE=$(ls "${PHASE_DIR}"/*-UI-SPEC.md 2>/dev/null | head -1)
|
|
117
116
|
```
|
|
118
117
|
|
|
118
|
+
Set `UI_PHASE_ACTIVE=true` when `PLAN_HOOKS_JSON.activeHooks` contains an active `ui` step hook.
|
|
119
|
+
|
|
119
120
|
**If Playwright-MCP tools are available in this session (`mcp__playwright__*` tools
|
|
120
|
-
respond to tool calls) AND (`
|
|
121
|
+
respond to tool calls) AND (`UI_PHASE_ACTIVE` is `true` OR `UI_SPEC_FILE` is non-empty):**
|
|
121
122
|
|
|
122
123
|
For each UI checkpoint listed in the phase's UI-SPEC.md (or inferred from SUMMARY.md):
|
|
123
124
|
|
|
@@ -457,16 +458,31 @@ Present summary:
|
|
|
457
458
|
**If issues == 0:**
|
|
458
459
|
|
|
459
460
|
```bash
|
|
460
|
-
|
|
461
|
+
VERIFY_POST_HOOKS_JSON=$(gsd_run loop render-hooks verify:post --raw)
|
|
462
|
+
SECURITY_FILE=$(ls "${PHASE_DIR}"/*-SECURITY.md 2>/dev/null | head -1)
|
|
463
|
+
```
|
|
464
|
+
|
|
465
|
+
Resolve active step hooks from `VERIFY_POST_HOOKS_JSON` where `kind == "step"` and `ref.skill == "secure-phase"`.
|
|
466
|
+
|
|
467
|
+
If an active secure-phase step hook exists AND `SECURITY_FILE` is empty, dispatch the registry-provided skill stem:
|
|
468
|
+
|
|
469
|
+
```
|
|
470
|
+
Skill(skill="gsd-${ref.skill}", args="{phase}")
|
|
471
|
+
```
|
|
472
|
+
|
|
473
|
+
After the skill returns, refresh `SECURITY_FILE`:
|
|
474
|
+
|
|
475
|
+
```bash
|
|
461
476
|
SECURITY_FILE=$(ls "${PHASE_DIR}"/*-SECURITY.md 2>/dev/null | head -1)
|
|
462
477
|
```
|
|
463
478
|
|
|
464
|
-
If `
|
|
479
|
+
If `SECURITY_FILE` is still empty, stop before phase advancement and present:
|
|
480
|
+
|
|
465
481
|
```
|
|
466
|
-
⚠ Security enforcement enabled — /gsd:secure-phase {phase}
|
|
467
|
-
|
|
482
|
+
⚠ Security enforcement enabled — /gsd:secure-phase {phase} did not produce SECURITY.md.
|
|
483
|
+
Resolve the security review failure before advancing to the next phase.
|
|
468
484
|
|
|
469
|
-
All tests passed
|
|
485
|
+
All tests passed, but phase advancement is blocked until security review produces SECURITY.md.
|
|
470
486
|
|
|
471
487
|
- `/gsd:secure-phase {phase}` — security review (required before advancing)
|
|
472
488
|
- `/gsd:plan-phase {next}` — Plan next phase
|
|
@@ -474,13 +490,13 @@ All tests passed. Ready to continue.
|
|
|
474
490
|
- `/gsd:ui-review {phase}` — visual quality audit (if frontend files were modified)
|
|
475
491
|
```
|
|
476
492
|
|
|
477
|
-
If
|
|
493
|
+
If an active secure-phase step hook exists AND `SECURITY_FILE` exists: check frontmatter `threats_open`. If > 0:
|
|
478
494
|
```
|
|
479
495
|
⚠ Security gate: {threats_open} threats open
|
|
480
496
|
/gsd:secure-phase {phase} — resolve before advancing
|
|
481
497
|
```
|
|
482
498
|
|
|
483
|
-
If
|
|
499
|
+
If no active secure-phase step hook exists OR (`SECURITY_FILE` exists AND `threats_open` is `0`):
|
|
484
500
|
|
|
485
501
|
**Auto-transition: mark phase complete in ROADMAP.md and STATE.md**
|
|
486
502
|
|