@opengsd/gsd-core 1.10.0 → 1.11.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/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/agents/gsd-debug-session-manager.md +11 -0
- package/agents/gsd-doc-synthesizer.md +2 -4
- package/agents/gsd-executor.md +5 -5
- package/agents/gsd-mempalace-curator.md +5 -2
- package/agents/gsd-phase-researcher.md +20 -1
- package/agents/gsd-plan-checker.md +37 -0
- package/agents/gsd-planner.md +44 -46
- package/agents/gsd-user-profiler.md +3 -0
- package/agents/gsd-verifier.md +12 -3
- package/bin/install.js +841 -971
- package/bin/lib/ui-safety-gate.cjs +2 -0
- package/commands/gsd/code-review.md +1 -1
- package/commands/gsd/execute-phase.md +1 -1
- package/commands/gsd/map-codebase.md +1 -1
- package/commands/gsd/mempalace-capture.md +1 -1
- package/commands/gsd/mempalace-recall.md +1 -1
- package/commands/gsd/new-milestone.md +1 -1
- package/commands/gsd/quick.md +1 -1
- package/commands/gsd/review-backlog.md +2 -1
- package/commands/gsd/verify-work.md +1 -1
- package/gsd-core/bin/gsd-tools.cjs +469 -88
- package/gsd-core/bin/lib/active-workstream-store.cjs +138 -22
- package/gsd-core/bin/lib/agent-install-check.cjs +230 -32
- package/gsd-core/bin/lib/api-coverage.cjs +3 -5
- package/gsd-core/bin/lib/artifacts.cjs +3 -0
- package/gsd-core/bin/lib/assumption-delta.cjs +2 -4
- package/gsd-core/bin/lib/audit-command-router.cjs +9 -2
- package/gsd-core/bin/lib/audit.cjs +876 -240
- package/gsd-core/bin/lib/broken-windows.cjs +1 -1
- package/gsd-core/bin/lib/capability-consent.cjs +149 -15
- package/gsd-core/bin/lib/capability-lifecycle.cjs +45 -0
- package/gsd-core/bin/lib/capability-registry.cjs +575 -101
- package/gsd-core/bin/lib/capability-source.cjs +92 -0
- package/gsd-core/bin/lib/capability-trust.cjs +444 -25
- package/gsd-core/bin/lib/capability-validator.cjs +495 -22
- package/gsd-core/bin/lib/capability-writer.cjs +3 -2
- package/gsd-core/bin/lib/check-command-router.cjs +71 -37
- package/gsd-core/bin/lib/claude-orchestration.cjs +56 -3
- package/gsd-core/bin/lib/codex-agent-toml.cjs +329 -0
- package/gsd-core/bin/lib/command-aliases.cjs +22 -0
- package/gsd-core/bin/lib/command-roster.cjs +44 -1
- package/gsd-core/bin/lib/commands.cjs +651 -86
- package/gsd-core/bin/lib/commonjs-marker.cjs +12 -6
- package/gsd-core/bin/lib/complexity-trigger.cjs +1172 -0
- package/gsd-core/bin/lib/config-loader.cjs +75 -0
- package/gsd-core/bin/lib/config.cjs +10 -1
- package/gsd-core/bin/lib/core-utils.cjs +127 -29
- package/gsd-core/bin/lib/decisions.cjs +23 -0
- package/gsd-core/bin/lib/fallow-runner.cjs +20 -44
- package/gsd-core/bin/lib/frontmatter.cjs +155 -20
- package/gsd-core/bin/lib/gap-checker.cjs +68 -7
- package/gsd-core/bin/lib/git-base-branch.cjs +102 -0
- package/gsd-core/bin/lib/gsd2-import.cjs +10 -1
- package/gsd-core/bin/lib/health-diagnostic-rules/agent-install.cjs +101 -0
- package/gsd-core/bin/lib/health-diagnostic-rules/config-validation.cjs +348 -0
- package/gsd-core/bin/lib/health-diagnostic-rules/consistency.cjs +145 -0
- package/gsd-core/bin/lib/health-diagnostic-rules/install-surface-shadowing.cjs +98 -0
- package/gsd-core/bin/lib/health-diagnostic-rules/milestone-archive-hygiene.cjs +100 -0
- package/gsd-core/bin/lib/health-diagnostic-rules/phase-structure.cjs +222 -0
- package/gsd-core/bin/lib/health-diagnostic-rules/roadmap-disk-consistency.cjs +265 -0
- package/gsd-core/bin/lib/health-diagnostic-rules/root-existence.cjs +161 -0
- package/gsd-core/bin/lib/health-diagnostic-rules/state-consistency.cjs +303 -0
- package/gsd-core/bin/lib/health-diagnostic-rules/worktree-health.cjs +173 -0
- package/gsd-core/bin/lib/health-diagnostic-types.cjs +68 -0
- package/gsd-core/bin/lib/health-diagnostic.cjs +431 -0
- package/gsd-core/bin/lib/host-runtime-detection.cjs +134 -0
- package/gsd-core/bin/lib/init.cjs +321 -129
- package/gsd-core/bin/lib/install-effort-resolver.cjs +73 -30
- package/gsd-core/bin/lib/install-engine.cjs +745 -258
- package/gsd-core/bin/lib/install-fs-adapter.cjs +262 -0
- package/gsd-core/bin/lib/install-model-override-resolver.cjs +203 -0
- package/gsd-core/bin/lib/install-profiles.cjs +134 -57
- package/gsd-core/bin/lib/install-scope.cjs +270 -0
- package/gsd-core/bin/lib/install-shadow-report.cjs +385 -0
- package/gsd-core/bin/lib/installed-surface-resolver.cjs +381 -0
- package/gsd-core/bin/lib/installer-migrations.cjs +138 -31
- package/gsd-core/bin/lib/io.cjs +10 -0
- package/gsd-core/bin/lib/markdown-sectionizer.cjs +2 -1
- package/gsd-core/bin/lib/markdown-table.cjs +133 -20
- package/gsd-core/bin/lib/milestone-lock.cjs +248 -0
- package/gsd-core/bin/lib/milestone.cjs +754 -70
- package/gsd-core/bin/lib/model-catalog.cjs +59 -1
- package/gsd-core/bin/lib/model-resolver.cjs +183 -40
- package/gsd-core/bin/lib/normalize-test-command.cjs +1 -1
- package/gsd-core/bin/lib/pattern.cjs +122 -0
- package/gsd-core/bin/lib/phase-estimation.cjs +1 -1
- package/gsd-core/bin/lib/phase-id.cjs +444 -36
- package/gsd-core/bin/lib/phase-lifecycle.cjs +28 -3
- package/gsd-core/bin/lib/phase-locator.cjs +125 -18
- package/gsd-core/bin/lib/phase.cjs +646 -143
- package/gsd-core/bin/lib/plan-dependency-graph.cjs +72 -1
- package/gsd-core/bin/lib/plan-drift-guard.cjs +120 -0
- package/gsd-core/bin/lib/plan-scan.cjs +86 -2
- package/gsd-core/bin/lib/planning-scope.cjs +31 -0
- package/gsd-core/bin/lib/planning-snapshot.cjs +890 -0
- package/gsd-core/bin/lib/planning-workspace.cjs +56 -6
- package/gsd-core/bin/lib/probe-core.cjs +1 -1
- package/gsd-core/bin/lib/profile-output.cjs +1 -1
- package/gsd-core/bin/lib/refactor-trigger-command-router.cjs +740 -0
- package/gsd-core/bin/lib/retired-artifact-cleanup.cjs +11 -6
- package/gsd-core/bin/lib/review-lane-descriptor.cjs +13 -4
- package/gsd-core/bin/lib/review-lane-invocation.cjs +30 -0
- package/gsd-core/bin/lib/review-lane-runner.cjs +421 -66
- package/gsd-core/bin/lib/review-reviewer-selection.cjs +13 -18
- package/gsd-core/bin/lib/roadmap-command-router.cjs +34 -0
- package/gsd-core/bin/lib/roadmap-parser.cjs +943 -184
- package/gsd-core/bin/lib/roadmap-upgrade.cjs +37 -10
- package/gsd-core/bin/lib/roadmap.cjs +385 -94
- package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +608 -46
- package/gsd-core/bin/lib/runtime-artifact-install-plan.cjs +14 -2
- package/gsd-core/bin/lib/runtime-artifact-layout.cjs +426 -55
- package/gsd-core/bin/lib/runtime-config-adapter-registry.cjs +3 -2
- package/gsd-core/bin/lib/runtime-homes.cjs +69 -3
- package/gsd-core/bin/lib/runtime-hooks-surface.cjs +115 -3
- package/gsd-core/bin/lib/runtime-name-policy.cjs +3 -1
- package/gsd-core/bin/lib/runtime-slash.cjs +27 -9
- package/gsd-core/bin/lib/security.cjs +104 -5
- package/gsd-core/bin/lib/shell-command-projection.cjs +275 -3
- package/gsd-core/bin/lib/smart-entry.cjs +142 -22
- package/gsd-core/bin/lib/state-command-router.cjs +5 -1
- package/gsd-core/bin/lib/state-document.cjs +152 -8
- package/gsd-core/bin/lib/state-transition.cjs +371 -117
- package/gsd-core/bin/lib/state.cjs +1794 -357
- package/gsd-core/bin/lib/surface.cjs +23 -9
- package/gsd-core/bin/lib/text-lines.cjs +80 -0
- package/gsd-core/bin/lib/token-scanner.cjs +76 -0
- package/gsd-core/bin/lib/uat-predicate.cjs +9 -3
- package/gsd-core/bin/lib/uat.cjs +399 -56
- package/gsd-core/bin/lib/ui-frontend-evidence.cjs +157 -0
- package/gsd-core/bin/lib/ui-safety-gate.cjs +14 -5
- package/gsd-core/bin/lib/unusable-input.cjs +24 -0
- package/gsd-core/bin/lib/update-context.cjs +8 -2
- package/gsd-core/bin/lib/user-artifact-staging.cjs +705 -0
- package/gsd-core/bin/lib/validate.cjs +20 -6
- package/gsd-core/bin/lib/vendor/README.md +37 -0
- package/gsd-core/bin/lib/vendor/re2js.cjs +6480 -0
- package/gsd-core/bin/lib/vendor/re2js.d.cts +938 -0
- package/gsd-core/bin/lib/verification-command-router.cjs +2 -1
- package/gsd-core/bin/lib/verification.cjs +258 -8
- package/gsd-core/bin/lib/verify.cjs +368 -888
- package/gsd-core/bin/lib/workstream-inventory-builder.cjs +53 -32
- package/gsd-core/bin/lib/workstream-inventory.cjs +63 -10
- package/gsd-core/bin/lib/workstream.cjs +2 -2
- package/gsd-core/bin/lib/worktree-safety.cjs +176 -9
- package/gsd-core/bin/shared/config-defaults.manifest.json +1 -0
- package/gsd-core/bin/shared/config-schema.manifest.json +7 -1
- package/gsd-core/references/agent-contracts.md +43 -26
- package/gsd-core/references/checkpoints.md +2 -2
- package/gsd-core/references/context-budget.md +1 -1
- package/gsd-core/references/dispatch-isolation-gate.md +138 -0
- package/gsd-core/references/doc-conflict-engine.md +1 -1
- package/gsd-core/references/execute-mvp-tdd.md +3 -3
- package/gsd-core/references/execute-phase-between-wave-reset.md +6 -2
- package/gsd-core/references/execute-phase-context-guard.md +1 -1
- package/gsd-core/references/execute-phase-response-language.md +1 -1
- package/gsd-core/references/execute-phase-wave-guard.md +6 -2
- package/gsd-core/references/gate-prompts.md +1 -1
- package/gsd-core/references/git-planning-commit.md +2 -1
- package/gsd-core/references/loop-hook-dispatch.md +39 -2
- package/gsd-core/references/model-profiles.md +12 -4
- package/gsd-core/references/mvp-concepts.md +9 -9
- package/gsd-core/references/planner-guidance.md +3 -9
- package/gsd-core/references/planner-preconditions.md +1 -1
- package/gsd-core/references/planner-reviews.md +1 -1
- package/gsd-core/references/planning-config.md +8 -6
- package/gsd-core/references/revision-loop.md +1 -1
- package/gsd-core/references/specless-probe-fallback.md +1 -1
- package/gsd-core/references/universal-anti-patterns.md +3 -3
- package/gsd-core/references/verifier-phase-gates.md +192 -0
- package/gsd-core/references/verify-mvp-mode.md +1 -1
- package/gsd-core/references/workstream-flag.md +22 -6
- package/gsd-core/templates/discussion-log.md +1 -1
- package/gsd-core/templates/phase-prompt.md +2 -4
- package/gsd-core/templates/state.md +4 -4
- package/gsd-core/templates/verification-report.md +9 -1
- package/gsd-core/workflows/ai-integration-phase.md +9 -11
- package/gsd-core/workflows/autonomous.md +1 -1
- package/gsd-core/workflows/cleanup.md +62 -3
- package/gsd-core/workflows/code-review/steps/structural-pre-pass.md +13 -3
- package/gsd-core/workflows/code-review-fix.md +37 -10
- package/gsd-core/workflows/code-review.md +38 -12
- package/gsd-core/workflows/complete-milestone.md +141 -18
- package/gsd-core/workflows/debug.md +7 -5
- package/gsd-core/workflows/diagnose-issues.md +35 -9
- package/gsd-core/workflows/discuss-phase/modes/chain.md +2 -1
- package/gsd-core/workflows/discuss-phase/modes/default.md +1 -1
- package/gsd-core/workflows/discuss-phase-assumptions.md +2 -1
- package/gsd-core/workflows/edit-phase.md +26 -1
- package/gsd-core/workflows/eval-review.md +3 -5
- package/gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md +31 -6
- package/gsd-core/workflows/execute-phase/steps/per-plan-executor-routing.md +77 -0
- package/gsd-core/workflows/execute-phase/steps/per-plan-worktree-gate.md +2 -0
- package/gsd-core/workflows/execute-phase.md +38 -50
- package/gsd-core/workflows/execute-plan.md +36 -4
- package/gsd-core/workflows/explore.md +131 -4
- package/gsd-core/workflows/fast.md +10 -2
- package/gsd-core/workflows/health.md +73 -4
- package/gsd-core/workflows/import.md +4 -4
- package/gsd-core/workflows/ingest-docs.md +5 -5
- package/gsd-core/workflows/mvp-phase.md +6 -3
- package/gsd-core/workflows/new-milestone.md +14 -9
- package/gsd-core/workflows/new-project.md +14 -14
- package/gsd-core/workflows/next.md +12 -0
- package/gsd-core/workflows/plan-phase.md +41 -17
- package/gsd-core/workflows/plan-review-convergence.md +50 -2
- package/gsd-core/workflows/progress.md +34 -6
- package/gsd-core/workflows/quick/steps/plan-checker-loop.md +4 -4
- package/gsd-core/workflows/quick/steps/quick-verification.md +27 -6
- package/gsd-core/workflows/quick/steps/research-phase.md +2 -2
- package/gsd-core/workflows/quick.md +35 -15
- package/gsd-core/workflows/review.md +26 -5
- package/gsd-core/workflows/secure-phase.md +1 -1
- package/gsd-core/workflows/session-report.md +2 -1
- package/gsd-core/workflows/settings.md +66 -2
- package/gsd-core/workflows/ship.md +104 -44
- package/gsd-core/workflows/spec-phase.md +30 -12
- package/gsd-core/workflows/sync-skills.md +63 -8
- package/gsd-core/workflows/transition.md +46 -11
- package/gsd-core/workflows/ui-phase.md +5 -5
- package/gsd-core/workflows/ui-review.md +2 -2
- package/gsd-core/workflows/update.md +1 -1
- package/gsd-core/workflows/validate-phase.md +1 -1
- package/gsd-core/workflows/verify-work.md +9 -7
- package/hooks/dist/gsd-agent-isolation-guard.js +103 -14
- package/hooks/dist/gsd-check-update-worker.js +56 -13
- package/hooks/dist/gsd-check-update.js +19 -1
- package/hooks/dist/gsd-cursor-pre-tool.js +0 -3
- package/hooks/dist/gsd-cursor-subagent-start.js +77 -2
- package/hooks/dist/gsd-cursor-subagent-stop.js +3 -2
- package/hooks/dist/gsd-prompt-guard.js +21 -20
- package/hooks/dist/gsd-read-injection-scanner.js +38 -24
- package/hooks/dist/gsd-statusline.js +18 -0
- package/hooks/dist/gsd-update-banner.js +22 -1
- package/hooks/dist/gsd-workflow-guard.js +134 -36
- package/hooks/dist/lib/git-cmd.js +92 -59
- package/hooks/dist/lib/injection-patterns.js +45 -0
- package/hooks/dist/lib/isolation-deny-reason.js +39 -0
- package/hooks/dist/lib/isolation-sentinel.js +9 -0
- package/hooks/gsd-agent-isolation-guard.js +103 -14
- package/hooks/gsd-check-update-worker.js +56 -13
- package/hooks/gsd-check-update.js +19 -1
- package/hooks/gsd-cursor-pre-tool.js +0 -3
- package/hooks/gsd-cursor-subagent-start.js +77 -2
- package/hooks/gsd-cursor-subagent-stop.js +3 -2
- package/hooks/gsd-prompt-guard.js +21 -20
- package/hooks/gsd-read-injection-scanner.js +38 -24
- package/hooks/gsd-statusline.js +18 -0
- package/hooks/gsd-update-banner.js +22 -1
- package/hooks/gsd-workflow-guard.js +134 -36
- package/hooks/lib/git-cmd.js +92 -59
- package/hooks/lib/injection-patterns.js +45 -0
- package/hooks/lib/isolation-deny-reason.js +39 -0
- package/hooks/lib/isolation-sentinel.js +9 -0
- package/package.json +21 -9
- package/pi/gsd.cjs +19 -5
- package/scripts/baselines/planning-prompt-drift-baseline.json +4 -0
- package/scripts/baselines/planning-snapshot-bypass-baseline.json +12 -0
- package/scripts/baselines/unreachable-guard-drift-baseline.json +4 -0
- package/scripts/changeset/lint.cjs +60 -5
- package/scripts/check-alias-drift.cjs +7 -43
- package/scripts/check-contract-drift.cjs +297 -0
- package/scripts/ci-test-scope.cjs +19 -2
- package/scripts/command-contract-helpers.cjs +903 -1
- package/scripts/gen-adr-index.cjs +728 -38
- package/scripts/gen-capability-registry.cjs +3 -15
- package/scripts/gen-context-index.cjs +2 -11
- package/scripts/gen-health-docs.cjs +390 -0
- package/scripts/gen-inventory-manifest.cjs +50 -4
- package/scripts/gen-loop-host-contract.cjs +4 -24
- package/scripts/gen-registry.cjs +3 -14
- package/scripts/lib/alias-drift-families.cjs +46 -0
- package/scripts/lib/drift-scan.cjs +278 -0
- package/scripts/lint-allow-test-rule-refs.allowlist.json +1 -26
- package/scripts/lint-allow-test-rule-refs.effective-ceiling.json +4 -0
- package/scripts/lint-allow-test-rule-refs.unverified-ceiling.json +3 -0
- package/scripts/lint-canary-version-leak.cjs +73 -0
- package/scripts/lint-command-contract.cjs +96 -13
- package/scripts/lint-completion-predicate-drift.cjs +933 -0
- package/scripts/lint-completion-ratio-drift.cjs +214 -0
- package/scripts/lint-default-flip-documentation.cjs +193 -0
- package/scripts/lint-eslint-glob-coverage.allowlist.json +34 -0
- package/scripts/lint-eslint-glob-coverage.cjs +340 -0
- package/scripts/lint-frontmatter-scalar-broad-grep.cjs +237 -0
- package/scripts/lint-health-diagnostic-rule-table.cjs +404 -0
- package/scripts/lint-hooks-runtime-build-seam.cjs +262 -0
- package/scripts/lint-milestone-window-drift.cjs +468 -0
- package/scripts/lint-phase-enumeration-drift.cjs +479 -0
- package/scripts/lint-plan-count-drift.cjs +318 -0
- package/scripts/lint-planning-artifact-writer-drift.cjs +398 -0
- package/scripts/lint-planning-prompt-drift.cjs +434 -0
- package/scripts/lint-planning-snapshot-bypass-drift.cjs +544 -0
- package/scripts/lint-regression-test-names.cjs +15 -13
- package/scripts/lint-removed-but-needed.cjs +320 -0
- package/scripts/lint-state-field-drift.cjs +805 -0
- package/scripts/lint-state-write-path-drift.cjs +1045 -0
- package/scripts/lint-test-file-count.allowlist.json +21 -10
- package/scripts/lint-unreachable-guard-drift.cjs +843 -0
- package/scripts/lint-vendored-deps.cjs +124 -0
- package/scripts/pr-changed-files.cjs +63 -0
- package/scripts/pr-template-policy.cjs +14 -4
- package/scripts/prompt-injection-scan.sh +25 -0
- package/scripts/require-issue-link-policy.cjs +192 -0
- package/scripts/state-write-path-drift-baseline.json +19 -0
- package/scripts/sync-runtime-launcher.cjs +2 -4
- package/skills/gsd-autonomous/SKILL.md +0 -1
- package/skills/gsd-code-review/SKILL.md +1 -1
- package/skills/gsd-execute-phase/SKILL.md +1 -2
- package/skills/gsd-map-codebase/SKILL.md +1 -1
- package/skills/gsd-mempalace-capture/SKILL.md +1 -1
- package/skills/gsd-mempalace-recall/SKILL.md +1 -1
- package/skills/gsd-new-milestone/SKILL.md +1 -1
- package/skills/gsd-next/SKILL.md +0 -1
- package/skills/gsd-plan-phase/SKILL.md +0 -1
- package/skills/gsd-progress/SKILL.md +0 -1
- package/skills/gsd-quick/SKILL.md +1 -1
- package/skills/gsd-review-backlog/SKILL.md +2 -1
- package/skills/gsd-stats/SKILL.md +0 -1
- package/skills/gsd-verify-work/SKILL.md +1 -1
- package/vscode/package.json +1 -1
- package/gsd-core/workflows/discovery-phase.md +0 -298
- package/gsd-core/workflows/plan-milestone-gaps.md +0 -281
- package/gsd-core/workflows/verify-phase.md +0 -574
- package/scripts/affected-tests-lib.cjs +0 -554
- package/scripts/lint-allow-test-rule-refs.cjs +0 -162
- package/scripts/run-affected-tests.cjs +0 -7
- package/scripts/run-tests.cjs +0 -1051
|
@@ -30,12 +30,12 @@ Agent(
|
|
|
30
30
|
**Task:** ${DESCRIPTION}
|
|
31
31
|
**Output:** ${QUICK_DIR}/${quick_id}-RESEARCH.md
|
|
32
32
|
|
|
33
|
-
<
|
|
33
|
+
<required_reading>
|
|
34
34
|
- ${STATE_PATH} (Project state — what's already built)
|
|
35
35
|
- ${PROJECT_PATH} (Project context)
|
|
36
36
|
- ./CLAUDE.md or ./.claude/CLAUDE.md (if exists — project-specific guidelines)
|
|
37
37
|
${DISCUSS_MODE ? '- ' + QUICK_DIR + '/' + quick_id + '-CONTEXT.md (User decisions — research should align with these)' : ''}
|
|
38
|
-
</
|
|
38
|
+
</required_reading>
|
|
39
39
|
|
|
40
40
|
${AGENT_SKILLS_PLANNER}
|
|
41
41
|
|
|
@@ -154,12 +154,16 @@ PROJECT_PATH="$(dirname "${quick_dir}")/PROJECT.md"
|
|
|
154
154
|
```bash
|
|
155
155
|
USE_WORKTREES=$(gsd_run query config-get workflow.use_worktrees --raw 2>/dev/null || echo "true")
|
|
156
156
|
RUNTIME=$(gsd_run query config-get runtime --default claude --raw 2>/dev/null || echo "claude")
|
|
157
|
-
if [ "$RUNTIME" != "claude" ] && [ "$USE_WORKTREES" != "false" ]; then
|
|
158
|
-
echo "FATAL: git worktree isolation (isolation=\"worktree\") is unsupported on runtime '$RUNTIME' — it would run executor agents unisolated against the main checkout. Set workflow.use_worktrees=false." >&2
|
|
159
|
-
exit 1
|
|
160
|
-
fi
|
|
161
157
|
```
|
|
162
158
|
|
|
159
|
+
**Resolve isolation now (#2584/#2652).** Read @gsd-core/references/dispatch-isolation-gate.md
|
|
160
|
+
and run its `Resolve ISOLATION`, `Single-agent dispatch sites`, and `Resolve the harness flag`
|
|
161
|
+
blocks in order; they set `ISOLATION`/`HARNESS_FLAG` via `query dispatch-isolation`.
|
|
162
|
+
`ISOLATION` — not `RUNTIME` — gates every worktree decision below. Substitute `{harnessFlag}`
|
|
163
|
+
in Step 6's `Agent()` with `$HARNESS_FLAG`+comma when `ISOLATION = "harness-worktree"`, else
|
|
164
|
+
empty. `{harnessFlag}`
|
|
165
|
+
is a template placeholder, not a shell variable.
|
|
166
|
+
|
|
163
167
|
If `USE_WORKTREES` is not `"false"`, run a startup orphan sweep before spawning any executors. This reaps locked worktrees whose lock-owner process is dead, whose branch is merged into the default branch, and whose lock file mtime is older than 5 minutes. Running it at startup prevents accumulation of orphaned worktrees from prior sessions that exited without cleanup (#3707).
|
|
164
168
|
|
|
165
169
|
```bash
|
|
@@ -309,12 +313,12 @@ Agent(
|
|
|
309
313
|
**Directory:** ${QUICK_DIR}
|
|
310
314
|
**Description:** ${DESCRIPTION}
|
|
311
315
|
|
|
312
|
-
<
|
|
316
|
+
<required_reading>
|
|
313
317
|
- ${STATE_PATH} (Project State)
|
|
314
318
|
- ./CLAUDE.md or ./.claude/CLAUDE.md (if exists — follow project-specific guidelines)
|
|
315
319
|
${DISCUSS_MODE ? '- ' + QUICK_DIR + '/' + quick_id + '-CONTEXT.md (User decisions — locked, do not revisit)' : ''}
|
|
316
320
|
${RESEARCH_MODE ? '- ' + QUICK_DIR + '/' + quick_id + '-RESEARCH.md (Research findings — use to inform implementation choices)' : ''}
|
|
317
|
-
</
|
|
321
|
+
</required_reading>
|
|
318
322
|
|
|
319
323
|
${AGENT_SKILLS_PLANNER}
|
|
320
324
|
|
|
@@ -376,21 +380,37 @@ halts with a base-mismatch fatal — potentially many commits behind, not just o
|
|
|
376
380
|
immediately before capturing `EXPECTED_BASE` so it reflects the most current local state.
|
|
377
381
|
|
|
378
382
|
```bash
|
|
379
|
-
if [ "$
|
|
383
|
+
if [ "$ISOLATION" = "harness-worktree" ] && [ "${USE_WORKTREES:-true}" != "false" ]; then
|
|
380
384
|
_QUICK_SHOULD_DEGRADE=$(gsd_run query worktree.base-check --pick shouldDegrade 2>/dev/null || true)
|
|
381
385
|
if [ "$_QUICK_SHOULD_DEGRADE" = "true" ]; then
|
|
382
386
|
_QUICK_DEGRADE_MSG=$(gsd_run query worktree.base-check --pick message 2>/dev/null || true)
|
|
383
387
|
[ -n "$_QUICK_DEGRADE_MSG" ] && printf '%s\n' "$_QUICK_DEGRADE_MSG" >&2
|
|
384
388
|
echo "⚠ [#1941] Worktree fork base diverged from orchestrator HEAD — auto-degrading to sequential mode for this quick task to avoid a base-mismatch halt." >&2
|
|
385
389
|
USE_WORKTREES=false
|
|
390
|
+
ISOLATION=none
|
|
386
391
|
fi
|
|
387
392
|
fi
|
|
393
|
+
|
|
394
|
+
# Re-resolve (and, as a side effect, re-persist) now that the base-check
|
|
395
|
+
# auto-degrade above may have changed $ISOLATION since the Step 2 gate's
|
|
396
|
+
# `dispatch-isolation` call (#3045). That first call recorded the NATURALLY
|
|
397
|
+
# resolved mode into the run-scoped sentinel the isolation guard hooks read
|
|
398
|
+
# (hooks/gsd-agent-isolation-guard.js, hooks/gsd-cursor-subagent-start.js via
|
|
399
|
+
# hooks/lib/isolation-sentinel.js). The degrade above is decided HERE, in
|
|
400
|
+
# shell — the resolver cannot see it — so without this the sentinel still
|
|
401
|
+
# asserts `harness-worktree` while the dispatch below correctly omits the
|
|
402
|
+
# harness flag, and the guard denies the dispatch with exit 2. `--force-isolation`
|
|
403
|
+
# pushes the FINAL, shell-computed value through that SAME single write path
|
|
404
|
+
# (`none` also clears the stored harnessFlag, since none applies to sequential
|
|
405
|
+
# dispatch). Best-effort: a write failure here must never fail the task — the
|
|
406
|
+
# guards' own sentinel-absent fallback is safe, just less precise.
|
|
407
|
+
gsd_run query dispatch-isolation --raw --force-isolation "$ISOLATION" >/dev/null 2>&1 || true
|
|
388
408
|
```
|
|
389
409
|
|
|
390
410
|
Capture current HEAD before spawning (used for worktree branch check):
|
|
391
411
|
```bash
|
|
392
412
|
EXPECTED_BASE=$(git rev-parse HEAD)
|
|
393
|
-
if [ "$
|
|
413
|
+
if [ "$ISOLATION" = "harness-worktree" ]; then # keyed on ISOLATION like every other dispatch-coupled branch (#2652)
|
|
394
414
|
# BSD/macOS mktemp only randomizes XXXXXX when it is the final path component, so make a
|
|
395
415
|
# suffixless temp then append the extension — portable across BSD + GNU (#1520).
|
|
396
416
|
QUICK_WORKTREE_MANIFEST=$(mktemp "${TMPDIR:-/tmp}/gsd-quick-worktree-XXXXXX") && mv "$QUICK_WORKTREE_MANIFEST" "${QUICK_WORKTREE_MANIFEST}.json" && QUICK_WORKTREE_MANIFEST="${QUICK_WORKTREE_MANIFEST}.json" || exit 1
|
|
@@ -406,7 +426,7 @@ Agent(
|
|
|
406
426
|
prompt="
|
|
407
427
|
Execute quick task ${quick_id}.
|
|
408
428
|
|
|
409
|
-
${
|
|
429
|
+
${ISOLATION === "harness-worktree" ? `
|
|
410
430
|
<worktree_branch_check>
|
|
411
431
|
ORCHESTRATOR build-time embed (NOT a sub-agent runtime step): before this dispatch, read \`gsd-core/references/worktree-branch-check.md\`, substitute \`{EXPECTED_BASE}\` with the base SHA captured above (${EXPECTED_BASE}), substitute \`{EXPECTED_BASE_ALTERNATE}\` with \`${QUICK_PLAN_PARENT}\` when it differs from \`${EXPECTED_BASE}\` (otherwise empty), and replace this note with that fragment's \`<worktree_branch_check>\` block so the dispatched prompt carries the runnable guard verbatim — do not pass this instruction through in its place.
|
|
412
432
|
</worktree_branch_check>
|
|
@@ -426,12 +446,12 @@ fi
|
|
|
426
446
|
\`\`\`
|
|
427
447
|
` : ''}
|
|
428
448
|
|
|
429
|
-
<
|
|
449
|
+
<required_reading>
|
|
430
450
|
- ${QUICK_DIR}/${quick_id}-PLAN.md (Plan)
|
|
431
451
|
- ${STATE_PATH} (Project state)
|
|
432
452
|
- ./CLAUDE.md or ./.claude/CLAUDE.md (Project instructions, if exists)
|
|
433
453
|
- .claude/skills/ or .agents/skills/ (Project skills, if either exists — list skills, read SKILL.md for each, follow relevant rules during implementation)
|
|
434
|
-
</
|
|
454
|
+
</required_reading>
|
|
435
455
|
|
|
436
456
|
${AGENT_SKILLS_EXECUTOR}
|
|
437
457
|
|
|
@@ -480,17 +500,17 @@ SUMMARY.md and stop — the user must rerun with worktrees disabled.
|
|
|
480
500
|
",
|
|
481
501
|
subagent_type="gsd-executor",
|
|
482
502
|
model="{executor_model}",
|
|
483
|
-
|
|
503
|
+
{harnessFlag}
|
|
484
504
|
description="Execute: ${DESCRIPTION}"
|
|
485
505
|
)
|
|
486
506
|
```
|
|
487
507
|
|
|
488
508
|
> **ORCHESTRATOR RULE — CODEX RUNTIME**: After calling Agent() above, stop working on this task immediately. Do not read more files, edit code, or run tests related to this task while the subagent is active. Wait for the subagent to return its result. This prevents duplicate work, conflicting edits, and wasted context. Only resume when the subagent result is available.
|
|
489
509
|
|
|
490
|
-
If the executor ran
|
|
510
|
+
If the executor ran isolated (`ISOLATION = "harness-worktree"` at dispatch), append its returned `{agent_id, worktree_path, branch, expected_base, allowed_bases}` metadata to `QUICK_WORKTREE_MANIFEST` before cleanup. Set `expected_base` to `${EXPECTED_BASE}` and `allowed_bases` to `["${EXPECTED_BASE}", "${QUICK_PLAN_PARENT}"]` with duplicates removed. If any required field is unavailable, stop and ask for recovery; do not discover global worktrees.
|
|
491
511
|
|
|
492
512
|
After executor returns:
|
|
493
|
-
1. **Worktree cleanup:** If the executor ran
|
|
513
|
+
1. **Worktree cleanup:** If the executor ran isolated (`ISOLATION = "harness-worktree"` at dispatch), merge the worktree branch back and clean up:
|
|
494
514
|
```bash
|
|
495
515
|
QUICK_WORKTREE_MANIFEST=${QUICK_WORKTREE_MANIFEST:-$WAVE_WORKTREE_MANIFEST}
|
|
496
516
|
[ -n "${QUICK_WORKTREE_MANIFEST:-}" ] && [ -f "$QUICK_WORKTREE_MANIFEST" ] || {
|
|
@@ -504,7 +524,7 @@ After executor returns:
|
|
|
504
524
|
# Fail closed: SDK refusal (safety guard #3174/#3384) must surface — do not swallow exit 1.
|
|
505
525
|
gsd_run query worktree.cleanup-wave --manifest "$QUICK_WORKTREE_MANIFEST" || exit 1
|
|
506
526
|
```
|
|
507
|
-
If `
|
|
527
|
+
If `ISOLATION` was not `"harness-worktree"` at dispatch (including a #1941 base-check degrade — that is *this* file's degrade; #2649 is the `diagnose-issues.md` / `execute-plan.md` one), skip this step.
|
|
508
528
|
|
|
509
529
|
> **ISOLATED-RUN RECOVERY — FAIL SAFE (#1292):** When an isolated (worktree) run is *rejected* — the user declines to merge it, the orchestrator surfaces recovery guidance for a blocked/halted plan, or the run over-reached the requested scope — the worktree-isolation contract MUST hold through recovery. Do **NOT** propose continuing on `main`/the primary checkout as the default or recommended recovery path. Default to a **safe halt** and offer: (a) re-attempt in a **fresh, narrowly-scoped worktree**, or (b) inspect or discard the rejected worktree without merging. Any path that edits the primary checkout requires an **explicit, clearly-labeled confirmation** from the user first — editing `main` directly is never the proposed or default option for a run the user configured to be isolated.
|
|
510
530
|
|
|
@@ -266,11 +266,13 @@ done
|
|
|
266
266
|
if [ -f ".planning/PROJECT.md" ]; then
|
|
267
267
|
cp .planning/PROJECT.md "${RUN_DIR}/gsd-review-project.md"
|
|
268
268
|
fi
|
|
269
|
-
|
|
270
|
-
|
|
269
|
+
_CTX=( "${PHASE_DIR}"/*-CONTEXT.md )
|
|
270
|
+
if [ ${#_CTX[@]} -gt 0 ]; then
|
|
271
|
+
cat "${_CTX[@]}" > "${RUN_DIR}/gsd-review-context.md"
|
|
271
272
|
fi
|
|
272
|
-
|
|
273
|
-
|
|
273
|
+
_RESEARCH=( "${PHASE_DIR}"/*-RESEARCH.md )
|
|
274
|
+
if [ ${#_RESEARCH[@]} -gt 0 ]; then
|
|
275
|
+
cat "${_RESEARCH[@]}" > "${RUN_DIR}/gsd-review-research.md"
|
|
274
276
|
fi
|
|
275
277
|
if [ -f ".planning/REQUIREMENTS.md" ]; then
|
|
276
278
|
cp .planning/REQUIREMENTS.md "${RUN_DIR}/gsd-review-requirements.md"
|
|
@@ -421,12 +423,31 @@ names, each gets its own `## <Adapter> Review (<instance>)` section, and ≥2 sa
|
|
|
421
423
|
instances print a one-line shared-adapter caveat. Format in
|
|
422
424
|
`gsd-core/references/reviewer-instances.md`.
|
|
423
425
|
|
|
426
|
+
**Resolved model (#2295):** each lane's `review-lane invoke --json` line in
|
|
427
|
+
`{run_dir}/gsd-review-lane-results.jsonl` carries a `model` object; render `models:` from
|
|
428
|
+
its `value` and `model_sources:` from its `source`. Both maps carry exactly one entry per
|
|
429
|
+
reviewer that appears in `reviewers:` — the two key sets always match. Write the literal
|
|
430
|
+
`unknown` rather than omitting a key: an omitted key is indistinguishable from the feature
|
|
431
|
+
not having run, and a reader must be able to tell *no model recorded* from *nothing to
|
|
432
|
+
look at*. Emit every `models:`/`model_sources:` value as a DOUBLE-QUOTED YAML scalar — a
|
|
433
|
+
legitimate model id can contain `:` (`llama3:70b`, `qwen2.5:7b`), which is unquotable as a
|
|
434
|
+
bare scalar; a control character is already refused at the recording seam, so quoting is
|
|
435
|
+
what closes the remaining `:`/`#`/leading-`-` cases. When GSD applied a reasoning effort to
|
|
436
|
+
a lane, its `value` already carries a `(reasoning=<level>)` suffix (e.g.
|
|
437
|
+
`gpt-5.6-sol (reasoning=high)`) — render it as-is, without re-deriving or re-formatting it.
|
|
438
|
+
|
|
424
439
|
```markdown
|
|
425
440
|
---
|
|
426
441
|
phase: {N}
|
|
427
442
|
reviewers: [gemini, claude, codex, coderabbit, opencode, qwen, cursor, antigravity, ollama, lm_studio, llama_cpp] # populate at runtime with only the reviewers actually invoked
|
|
428
443
|
reviewed_at: {ISO timestamp}
|
|
429
444
|
plans_reviewed: [{list of PLAN.md files}]
|
|
445
|
+
models: # resolved model per reviewer; `unknown` when not recoverable
|
|
446
|
+
codex: "gpt-5.6-sol (reasoning=low)"
|
|
447
|
+
antigravity: "unknown"
|
|
448
|
+
model_sources: # how each value above was determined
|
|
449
|
+
codex: "banner"
|
|
450
|
+
antigravity: "unknown"
|
|
430
451
|
trimmed_reviewers: # only present if at least one reviewer was trimmed
|
|
431
452
|
ollama:
|
|
432
453
|
budget: 6000
|
|
@@ -464,7 +485,7 @@ trimmed_reviewers: # only present if at least one reviewer was trimmed
|
|
|
464
485
|
|
|
465
486
|
## Consensus Summary
|
|
466
487
|
|
|
467
|
-
{synthesize common concerns across all reviewers. CodeRabbit is a diff-only reviewer (it never received the source-grounding prompt), so do not weight its verdict as a grounded plan review — fold in its diff findings, but base plan-level consensus on the prompt-fed reviewers. A reviewer output carrying the `[reviewed-without-repo-access]` marker (or beginning with `REVIEWED-WITHOUT-REPO-ACCESS`) ran without repo access (#2176) — treat it the same way: note its concerns, but do not count its verdict at full consensus weight.}
|
|
488
|
+
{synthesize common concerns across all reviewers. CodeRabbit is a diff-only reviewer (it never received the source-grounding prompt), so do not weight its verdict as a grounded plan review — fold in its diff findings, but base plan-level consensus on the prompt-fed reviewers. A reviewer output carrying the `[reviewed-without-repo-access]` marker (or beginning with `REVIEWED-WITHOUT-REPO-ACCESS`) ran without repo access (#2176) — treat it the same way: note its concerns, but do not count its verdict at full consensus weight. A reviewer output carrying the `[reviewed-without-source-citations]` marker (#3194) declared source-grounded evidence but cited no `file:line` evidence, so it reviewed the plan text only — treat it the same way: note its concerns, but do not count its verdict at full consensus weight.}
|
|
468
489
|
|
|
469
490
|
### Agreed Strengths
|
|
470
491
|
{strengths mentioned by 2+ reviewers}
|
|
@@ -115,7 +115,7 @@ Print: `◆ Spawning security auditor... (runs in a subagent — no output until
|
|
|
115
115
|
```
|
|
116
116
|
Agent(
|
|
117
117
|
prompt="Read ~/.claude/agents/gsd-security-auditor.md for instructions.\n\n" +
|
|
118
|
-
"<
|
|
118
|
+
"<required_reading>{PLAN, SUMMARY, impl files, SECURITY.md}</required_reading>" +
|
|
119
119
|
"<threat_register>{threat register}</threat_register>" +
|
|
120
120
|
"<config>asvs_level: {SECURITY_ASVS}, block_on: {SECURITY_BLOCK_ON}</config>" +
|
|
121
121
|
"<constraints>Never modify implementation files. Verify mitigations exist — do not scan for new threats. Escalate implementation gaps. Return a structured verdict only — do NOT write SECURITY.md (the orchestrator owns the file write).</constraints>" +
|
|
@@ -34,7 +34,8 @@ Read `.planning/ROADMAP.md` to get milestone name and goals.
|
|
|
34
34
|
|
|
35
35
|
Check for existing reports:
|
|
36
36
|
```bash
|
|
37
|
-
|
|
37
|
+
_REPORTS=( .planning/reports/SESSION_REPORT*.md )
|
|
38
|
+
if [ -e "${_REPORTS[0]}" ]; then ls -la "${_REPORTS[@]}"; else echo "No previous reports"; fi
|
|
38
39
|
```
|
|
39
40
|
</step>
|
|
40
41
|
|
|
@@ -59,7 +59,7 @@ Parse current values (default to `true` if not present):
|
|
|
59
59
|
- `graphify.auto_update` — opt-in: auto-rebuild graph after main HEAD advances (#3347) (default: `false`)
|
|
60
60
|
- `model_profile` — which model each agent uses (default: `balanced`)
|
|
61
61
|
- `git.branching_strategy` — branching approach (default: `"none"`)
|
|
62
|
-
- `workflow.use_worktrees` — whether parallel executor agents run in worktree isolation (default
|
|
62
|
+
- `workflow.use_worktrees` — whether parallel executor agents run in worktree isolation (honored when the runtime declares a `dispatch.isolation` primitive — `harness-worktree` or `orchestrator-worktree`; runtimes declaring `none` default it to `false` and fail closed on an explicit `true` — #1521, #2486, #2584)
|
|
63
63
|
- `model_policy.provider` — provider slug for model policy (default: `null`; known values: anthropic, openai, google, qwen; set via /gsd:config --advanced)
|
|
64
64
|
- `model_policy.budget` — budget level for model policy (default: `null`; known values: high, medium, low; set via /gsd:config --advanced)
|
|
65
65
|
- `model_policy.high` — model ID for high-cost tier (default: `null`; set via /gsd:config --advanced)
|
|
@@ -87,6 +87,30 @@ configure `model_overrides` manually in .planning/config.json to target specific
|
|
|
87
87
|
models per agent.
|
|
88
88
|
```
|
|
89
89
|
|
|
90
|
+
**Isolation resolution for the Worktrees question (#2486):** resolve the runtime's declared executor-isolation primitive and the current worktrees value before presenting the questions. Use `inspect-dispatch-isolation`, the **sentinel-free** inspection verb — never `dispatch-isolation`, whose #3045 contract records the resolved decision to the executor-dispatch sentinel as an unconditional side effect; a settings menu must not be able to stamp a sentinel the isolation guards then enforce against real dispatches. Sentinel-free is the exact claim: the shared CLI bootstrap still runs, but nothing it does can block a dispatch. The worktrees read deliberately carries no `--default`/fallback: an absent key must stay distinguishable from an explicit `false` (empty output = key absent), which the pre-selection rule below depends on.
|
|
91
|
+
|
|
92
|
+
A failed query is not a capability verdict, so track the two apart: settings still fails closed, but
|
|
93
|
+
says so instead of claiming the runtime declares no primitive. The verb fail-closes an unknown runtime — or an
|
|
94
|
+
internal resolution error — to `none` and exits 0, so `INSPECTED_RESOLVED=false` means only "the query did not answer"
|
|
95
|
+
(#2486 review):
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
_INSPECTED_RAW=$(gsd_run query inspect-dispatch-isolation --raw 2>/dev/null)
|
|
99
|
+
_ISOLATION_RC=$?
|
|
100
|
+
if [ $_ISOLATION_RC -ne 0 ] || [ -z "$_INSPECTED_RAW" ]; then
|
|
101
|
+
INSPECTED_ISOLATION=none
|
|
102
|
+
INSPECTED_RESOLVED=false # no verdict learned — not the same as "declares none"
|
|
103
|
+
else
|
|
104
|
+
INSPECTED_ISOLATION="$_INSPECTED_RAW"
|
|
105
|
+
INSPECTED_RESOLVED=true
|
|
106
|
+
fi
|
|
107
|
+
case "$INSPECTED_ISOLATION" in
|
|
108
|
+
harness-worktree|orchestrator-worktree|none) ;;
|
|
109
|
+
*) INSPECTED_ISOLATION=none; INSPECTED_RESOLVED=false ;; # out of vocabulary is not a verdict either
|
|
110
|
+
esac
|
|
111
|
+
USE_WORKTREES_CURRENT=$(gsd_run query config-get workflow.use_worktrees --raw 2>/dev/null)
|
|
112
|
+
```
|
|
113
|
+
|
|
90
114
|
Use AskUserQuestion with current values pre-selected. Questions are grouped into six visual sections; the first question in each section carries the section-denoting `header` field (AskUserQuestion renders abbreviated section tags for grouping, max 12 chars).
|
|
91
115
|
|
|
92
116
|
Section layout:
|
|
@@ -113,6 +137,46 @@ Context Warnings, Research Qs
|
|
|
113
137
|
|
|
114
138
|
**Conditional visibility — graphify.auto_update:** This question is shown only when the user's chosen `graphify.enabled` value is on. If `graphify.enabled` is off, omit the `graphify.auto_update` question and preserve the existing `graphify.auto_update` value in config (do not overwrite). Implementation: ask Graphify first; only ask Graph auto-update when Graphify is enabled.
|
|
115
139
|
|
|
140
|
+
**Conditional options — Worktrees (#2486):** whether a runtime can honor `workflow.use_worktrees: true` depends on its **declared `dispatch.isolation` capability, not its name** (#2584): `harness-worktree` (the host isolates each executor) and `orchestrator-worktree` (GSD drives the worktrees) both run parallel; `none` fails closed on an explicit `true`. Branch on the same negotiation the execution gate resolves, via the sentinel-free `inspect-dispatch-isolation` verb, which fail-closes unknown/undeclared/`undocumented` to `none`. The rule is one-directional: **never persist a value the isolation gate would fail closed on.** **Do not add a runtime-name test here.** Branch on `$INSPECTED_ISOLATION`:
|
|
141
|
+
|
|
142
|
+
- **If `INSPECTED_ISOLATION` ≠ `none`** (`harness-worktree` or `orchestrator-worktree`): present the Worktrees question exactly as written below — unchanged behavior. The `none` branch is the entirety of the #2486 change.
|
|
143
|
+
- **If `INSPECTED_ISOLATION` = `none`:** replace the question's options with the two below — do NOT offer an enabling option, and NEVER write `workflow.use_worktrees: true` from this workflow when the runtime declares no isolation primitive, regardless of the user's answer:
|
|
144
|
+
|
|
145
|
+
```
|
|
146
|
+
{
|
|
147
|
+
question: "Use git worktrees for parallel agent isolation?",
|
|
148
|
+
header: "Worktrees",
|
|
149
|
+
multiSelect: false,
|
|
150
|
+
options: [
|
|
151
|
+
{ label: "No (Recommended)", description: "Write use_worktrees: false. {FINDING}, so parallel plans run sequentially and {CONSEQUENCE}." },
|
|
152
|
+
{ label: "Leave unchanged", description: "Do not write the key. {ABSENCE}; an existing explicit value is kept intact (e.g. for a worktree-capable install sharing this config)." }
|
|
153
|
+
]
|
|
154
|
+
}
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
**The three placeholders** carry the only difference between a capability GSD resolved and one it could not, so no text in this branch ever asserts a verdict that was not reached. Substitute them everywhere they appear — the two `description`s above, the pre-selection rationale, and the notice — from whichever column applies:
|
|
158
|
+
|
|
159
|
+
| | `INSPECTED_RESOLVED=true` | `INSPECTED_RESOLVED=false` |
|
|
160
|
+
|---|---|---|
|
|
161
|
+
| `{FINDING}` | this runtime has no usable executor-isolation primitive | GSD could not resolve this runtime's executor-isolation capability |
|
|
162
|
+
| `{CONSEQUENCE}` | execution fails closed on an explicit true | GSD cannot tell whether execution will accept an explicit true |
|
|
163
|
+
| `{ABSENCE}` | absent, it resolves to false wherever this emit stamped that default | absent, it is the safe state until the capability resolves |
|
|
164
|
+
|
|
165
|
+
Failing closed is right in both columns — never write `workflow.use_worktrees: true` from this branch either way. Only the explanation changes.
|
|
166
|
+
|
|
167
|
+
Persistence: "No (Recommended)" → write `workflow.use_worktrees: false`; "Leave unchanged" → do not write `workflow.use_worktrees` at all (preserve the existing value or absence).
|
|
168
|
+
|
|
169
|
+
Pre-selection: the generic "current values pre-selected" rule does not apply when `INSPECTED_ISOLATION` is `none` — an explicit `true` has no matching option by design. Pre-select "No (Recommended)" in every case, including an absent key (`$USE_WORKTREES_CURRENT` empty, the no-`--default` read's absent signal): absence resolves to `false` only where this emit stamped that default and to `true` otherwise, so leaving it absent can preserve the very state this branch prevents, while an explicit `false` is correct under either emit (#2486 review). The same applies when it is an explicit non-false value — the broken-inheritance case the notice below covers. "Leave unchanged" remains available for the deliberate shared-config case, but is never the default here. The default must be the repair the "(Recommended)" label points to, so accepting it never keeps a value this branch could not justify. In TEXT_MODE, mark that option as the default in the numbered list.
|
|
170
|
+
|
|
171
|
+
Additionally, if `$USE_WORKTREES_CURRENT` is non-empty and not `false` (the config carries an explicit `true` — e.g. inherited from a worktree-capable install sharing the repo; empty means the key is absent, which needs no notice), prepend this notice before the question:
|
|
172
|
+
|
|
173
|
+
```
|
|
174
|
+
Note: the project config currently sets workflow.use_worktrees: true.
|
|
175
|
+
{FINDING}, so {CONSEQUENCE}. Choose "No" to repair it for this runtime, or
|
|
176
|
+
"Leave unchanged" to keep it for a worktree-capable install that shares this
|
|
177
|
+
config.
|
|
178
|
+
```
|
|
179
|
+
|
|
116
180
|
```
|
|
117
181
|
// Model profile is selected via a two-question split because AskUserQuestion enforces a
|
|
118
182
|
// hard 4-option cap and there are 5 valid profiles (quality, balanced, budget, adaptive,
|
|
@@ -411,7 +475,7 @@ Merge new settings into existing config.json:
|
|
|
411
475
|
"research_before_questions": true/false,
|
|
412
476
|
"discuss_mode": "discuss" | "assumptions",
|
|
413
477
|
"skip_discuss": true/false,
|
|
414
|
-
"use_worktrees": true/false
|
|
478
|
+
"use_worktrees": true/false // never written as true when the runtime's dispatch.isolation is none; omitted entirely when the user chose "Leave unchanged" (#2486)
|
|
415
479
|
},
|
|
416
480
|
"plan_review": {
|
|
417
481
|
"source_grounding": true/false
|
|
@@ -55,14 +55,14 @@ Verify the work is ready to ship:
|
|
|
55
55
|
# The gate decides on ONE read. --pick takes a single field, so the two
|
|
56
56
|
# human-facing fields are read only on the blocking path below — never on the
|
|
57
57
|
# passing path — rather than issuing three queries up front (#2589).
|
|
58
|
-
STATUS=$(gsd_run query verification.status "${PHASE_DIR}" --pick status 2>/dev/null
|
|
58
|
+
STATUS=$(gsd_run query verification.status "${PHASE_DIR}" --pick status 2>/dev/null)
|
|
59
59
|
```
|
|
60
60
|
Only `passed` may ship. If `$STATUS` is `passed`, verification is complete — continue to the next preflight check; do not read any further verification field.
|
|
61
61
|
|
|
62
62
|
Any other value (including `gaps_found`, `human_needed`, `missing`, and `unknown`) blocks with `PHASE_VERIFICATION_INCOMPLETE`. Only then, read the two message fields:
|
|
63
63
|
```bash
|
|
64
|
-
NEXT_ACTION=$(gsd_run query verification.status "${PHASE_DIR}" --pick next_action 2>/dev/null
|
|
65
|
-
NEXT_COMMAND=$(gsd_run query verification.status "${PHASE_DIR}" --pick next_command 2>/dev/null
|
|
64
|
+
NEXT_ACTION=$(gsd_run query verification.status "${PHASE_DIR}" --pick next_action 2>/dev/null)
|
|
65
|
+
NEXT_COMMAND=$(gsd_run query verification.status "${PHASE_DIR}" --pick next_command 2>/dev/null)
|
|
66
66
|
```
|
|
67
67
|
Present `$NEXT_ACTION` to the user and, when `$NEXT_COMMAND` is non-empty, show it as the command to run next. These two are message text only — the block/allow decision has already been made from `$STATUS`, so a concurrent write between the reads cannot change the gate's verdict. The query already handles missing files and unexpected values, so no per-status arm is needed.
|
|
68
68
|
|
|
@@ -91,9 +91,9 @@ Verify the work is ready to ship:
|
|
|
91
91
|
```
|
|
92
92
|
If `gh` not found or not authenticated: provide setup instructions and exit.
|
|
93
93
|
|
|
94
|
-
6. **
|
|
94
|
+
6. **Capability ship gates (generic dispatch).**
|
|
95
95
|
|
|
96
|
-
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:
|
|
96
|
+
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` or `workflow.windows_enforce` directly:
|
|
97
97
|
|
|
98
98
|
```bash
|
|
99
99
|
SHIP_PRE_HOOKS_JSON=$(gsd_run loop render-hooks ship:pre --raw)
|
|
@@ -102,57 +102,83 @@ Verify the work is ready to ship:
|
|
|
102
102
|
|
|
103
103
|
Read the `activeHooks` array from `SHIP_PRE_HOOKS_JSON` in-context (do NOT pipe it through a shell parser).
|
|
104
104
|
|
|
105
|
-
If
|
|
105
|
+
**If `activeHooks` is empty or absent:** skip this check silently and continue to the next preflight step. A capability whose `when` is off contributes no entry, and one that failed to load fails OPEN with its own warning from the resolver — neither is a block.
|
|
106
106
|
|
|
107
|
-
-
|
|
108
|
-
```
|
|
109
|
-
⚠ Security enforcement is enabled but no SECURITY.md exists for this phase.
|
|
110
|
-
Run /gsd:secure-phase {phase} and resolve findings before shipping.
|
|
111
|
-
```
|
|
112
|
-
- **`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):
|
|
113
|
-
```
|
|
114
|
-
⚠ Security ship gate: SECURITY.md does not assert threats_open == 0 (found: {threats_open|unset}).
|
|
115
|
-
Resolve open threats (or re-run /gsd:secure-phase {phase}) before shipping.
|
|
116
|
-
```
|
|
107
|
+
**For each active entry where `kind == "gate"`** (process in array order), following `gsd-core/references/loop-hook-dispatch.md`. Entries of any other `kind` are not gates and are not enforced here. Every gate is visited exactly once by this loop — the named branches below are specializations *within* it, never a separate pass, so no gate is evaluated twice.
|
|
117
108
|
|
|
118
|
-
|
|
109
|
+
**Step 1 — evaluate the gate's `check`.** Dispatch by check shape; read the hook's `check` object in-context to pick the branch (the registry validates exactly one of `query`/`predicate`/`agentVerdict`). Two capability IDs carry a bespoke evaluation whose fail-closed semantics the declared predicate alone does not reproduce — take their branch, then rejoin at step 2:
|
|
119
110
|
|
|
120
|
-
|
|
111
|
+
- **`capId == "security"`** — enforce against `SECURITY_FILE`:
|
|
112
|
+
- **`SECURITY_FILE` is empty** → `block: true`, `SECURITY_SHIP_GATE_NO_REVIEW`:
|
|
113
|
+
```
|
|
114
|
+
⚠ Security enforcement is enabled but no SECURITY.md exists for this phase.
|
|
115
|
+
Run /gsd:secure-phase {phase} and resolve findings before shipping.
|
|
116
|
+
```
|
|
117
|
+
- **`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** with `block: true` and `SECURITY_SHIP_GATE_OPEN_THREATS` (the predicate is strict equality to `0`; never ship on an ambiguous value):
|
|
118
|
+
```
|
|
119
|
+
⚠ Security ship gate: SECURITY.md does not assert threats_open == 0 (found: {threats_open|unset}).
|
|
120
|
+
Resolve open threats (or re-run /gsd:secure-phase {phase}) before shipping.
|
|
121
|
+
```
|
|
121
122
|
|
|
122
|
-
|
|
123
|
+
- **`capId == "broken-windows"`** (issue #1950) — enforce against the ledger's typed status. The ledger lives at the **project root** (cross-phase, not phase-scoped):
|
|
123
124
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
'
|
|
127
|
-
|
|
128
|
-
```
|
|
125
|
+
```bash
|
|
126
|
+
WINDOWS_STATUS_JSON=$(gsd_run windows status --raw 2>/dev/null || echo '')
|
|
127
|
+
WINDOWS_OPEN_COUNT=$(printf '%s' "$WINDOWS_STATUS_JSON" | jq -r '.ledger.open_count // "?"' 2>/dev/null || echo '?')
|
|
128
|
+
```
|
|
129
129
|
|
|
130
|
-
|
|
130
|
+
- **`WINDOWS_OPEN_COUNT == "0"`** → `block: false`; the gate passes.
|
|
131
|
+
- **`WINDOWS_OPEN_COUNT` is a positive integer** → `block: true`, `WINDOWS_SHIP_GATE_OPEN`:
|
|
132
|
+
```
|
|
133
|
+
⚠ Broken-windows ship gate: WINDOWS.md has {WINDOWS_OPEN_COUNT} open window(s).
|
|
134
|
+
Resolve each entry before shipping, or explicitly waive with a recorded reason:
|
|
135
|
+
gsd_run windows fixed <id> # defect resolved
|
|
136
|
+
gsd_run windows waive <id> "<reason>" # justified deferral (reason required)
|
|
137
|
+
Then re-run /gsd:ship.
|
|
138
|
+
```
|
|
139
|
+
- **`WINDOWS_OPEN_COUNT` is `"?"`, empty, or non-numeric** → **fail closed** with `block: true` and `WINDOWS_SHIP_GATE_READ_FAILED` (the gate is strict equality to `0`; never ship on an unreadable ledger):
|
|
140
|
+
```
|
|
141
|
+
⚠ Broken-windows ship gate: could not read open_count from .planning/WINDOWS.md.
|
|
142
|
+
Inspect the file or run `gsd_run windows status --raw` to diagnose. The ledger
|
|
143
|
+
may be malformed; fix it before shipping (an unparseable ledger is a broken window).
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
The ledger is **optional and backward-compatible**: on a project where `gsd_run windows status` returns `open_count: 0` (no `.planning/WINDOWS.md` yet, or an empty ledger), the gate passes silently. It only blocks when at least one entry is `open`.
|
|
147
|
+
|
|
148
|
+
- **Every other `capId`** — run the gate's own declared check through the generic evaluator. This arm is what makes a third-party capability's declared gate enforceable at all (#3559); before it existed, a gate whose `capId` was not named above was resolved and then silently dropped.
|
|
149
|
+
|
|
150
|
+
⚠ **Validate `check` before shell use** (third-party manifest input) — `loop-hook-dispatch.md` § `gate`.
|
|
151
|
+
|
|
152
|
+
For a named-query gate (only a value that has passed validation is run):
|
|
153
|
+
```bash
|
|
154
|
+
GATE_RESULT=$(gsd_run check ${hook.check.query} "${PHASE_DIR}" --raw)
|
|
155
|
+
CHECK_EXIT=$?
|
|
156
|
+
```
|
|
131
157
|
|
|
132
|
-
|
|
133
|
-
WINDOWS_STATUS_JSON=$(gsd_run windows status --raw 2>/dev/null || echo '')
|
|
134
|
-
WINDOWS_OPEN_COUNT=$(printf '%s' "$WINDOWS_STATUS_JSON" | jq -r '.ledger.open_count // "?"' 2>/dev/null || echo '?')
|
|
135
|
-
```
|
|
158
|
+
(The named-query argument convention — a single `"${PHASE_DIR}"` positional — mirrors `verify-work.md`'s `verify:pre` arm verbatim. No capability declares a `check.query` gate at `ship:pre` today; the arm exists so the documented check contract is complete rather than half-implemented.)
|
|
136
159
|
|
|
137
|
-
|
|
138
|
-
|
|
160
|
+
For a `predicate` gate (ADR-2008 / #2008), serialize `hook.check.predicate` to compact JSON and pass it as a **single argv element**:
|
|
161
|
+
```bash
|
|
162
|
+
GATE_RESULT=$(gsd_run check predicate --predicate '<hook.check.predicate as JSON>' --phase-dir "${PHASE_DIR}" --phase-number "${PHASE_NUMBER}" --raw)
|
|
163
|
+
CHECK_EXIT=$?
|
|
139
164
|
```
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
165
|
+
A gate carrying neither — including an `agentVerdict` check, which has no runner at `ship:pre` — cannot be evaluated here. Record a warning naming the `capId` and treat it as a check-command failure routed per step 1a, **never** as a silent pass.
|
|
166
|
+
|
|
167
|
+
**Step 1a — did the CHECK COMMAND itself fail?** (non-zero `CHECK_EXIT`, empty output, or unparseable JSON). The two named branches above cannot reach this state — their failure modes are already folded into a fail-closed `block: true`.
|
|
168
|
+
- **`onError == "halt"`** → stop the ship. Do NOT push, do NOT create a PR. Surface: `⚠ Gate check command failed ({hook.capId}): command error. Resolve before shipping.`
|
|
169
|
+
- **`onError == "skip"`** → record a warning naming the `capId`, then continue to the next gate. Do NOT read `GATE_RESULT.block`.
|
|
170
|
+
|
|
171
|
+
**Step 2 — read the gate's `block` decision.** Only reached when the check produced a verdict.
|
|
172
|
+
|
|
173
|
+
- **`blocking == true` and `block == true`** → HALT the ship — do NOT push, do NOT create a PR — surfacing that gate's own message:
|
|
147
174
|
```
|
|
148
|
-
⚠
|
|
149
|
-
Inspect the file or run `gsd_run windows status --raw` to diagnose. The ledger
|
|
150
|
-
may be malformed; fix it before shipping (an unparseable ledger is a broken window).
|
|
175
|
+
⚠ Ship blocked by capability gate ({hook.capId}): {message}
|
|
151
176
|
```
|
|
177
|
+
This halt is **not** bypassed by `onError` — `onError` covers check-command failure (step 1a), never the gate's block decision.
|
|
178
|
+
- **`blocking == false`** (advisory) → never halts. If `block == true` or the result carries a non-empty message, print `⚠ {hook.capId} advisory: {message}`, then continue.
|
|
179
|
+
- **`blocking == true` and `block == false`** → continue silently.
|
|
152
180
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
If no active `broken-windows` `ship:pre` gate hook is present (gate disabled via `workflow.windows_enforce=false`, the default — tracking continues but the gate is opt-in), skip this check silently.
|
|
181
|
+
**When every active gate has been processed without a halt:** continue to the next preflight check.
|
|
156
182
|
</step>
|
|
157
183
|
|
|
158
184
|
<step name="push_branch">
|
|
@@ -455,7 +481,41 @@ would otherwise trigger (GitHub honors `[ci skip]` / `[skip ci]`):
|
|
|
455
481
|
|
|
456
482
|
```bash
|
|
457
483
|
gsd_run query commit "docs(${padded_phase}): ship phase ${PHASE_NUMBER} — PR #${PR_NUMBER} [ci skip]" --files .planning/STATE.md
|
|
484
|
+
SHIP_NOTE_SHA=$(git rev-parse HEAD)
|
|
458
485
|
git push origin ${CURRENT_BRANCH} 2>&1 || echo "⚠ track_shipping: ship-note push failed — it is local-only; rerun: git push origin ${CURRENT_BRANCH}"
|
|
486
|
+
|
|
487
|
+
# Preserve the skip-token optimization for repositories without a required-check
|
|
488
|
+
# wedge; only synthesize a second CI-triggering commit when GitHub reports one (#2783).
|
|
489
|
+
# Poll mergeStateStatus with backoff to avoid racing GitHub's async state computation.
|
|
490
|
+
# Note: Skip tokens recognized by GitHub Actions are [skip ci], [ci skip], [no ci], [skip actions], [actions skip], and skip-checks:true.
|
|
491
|
+
# The recovery commit message MUST NOT contain any of these tokens.
|
|
492
|
+
|
|
493
|
+
STATUS="UNKNOWN"
|
|
494
|
+
CHECKS=0
|
|
495
|
+
REVIEW_DECISION=""
|
|
496
|
+
for i in {1..5}; do
|
|
497
|
+
PR_STATE=$(gh pr view ${PR_NUMBER} --json headRefOid,mergeStateStatus,statusCheckRollup,reviewDecision -q '{head: .headRefOid, status: .mergeStateStatus, checks: ((.statusCheckRollup // []) | length), review: (.reviewDecision // "")}' 2>/dev/null || echo '{"head":"","status":"UNKNOWN","checks":0,"review":""}')
|
|
498
|
+
HEAD_OID=$(echo "$PR_STATE" | jq -r .head)
|
|
499
|
+
if [ "$HEAD_OID" = "$SHIP_NOTE_SHA" ]; then
|
|
500
|
+
STATUS=$(echo "$PR_STATE" | jq -r .status)
|
|
501
|
+
CHECKS=$(echo "$PR_STATE" | jq -r .checks)
|
|
502
|
+
REVIEW_DECISION=$(echo "$PR_STATE" | jq -r .review)
|
|
503
|
+
fi
|
|
504
|
+
if [ "$HEAD_OID" = "$SHIP_NOTE_SHA" ] && [ "$STATUS" != "UNKNOWN" ]; then
|
|
505
|
+
break
|
|
506
|
+
fi
|
|
507
|
+
sleep 3
|
|
508
|
+
done
|
|
509
|
+
|
|
510
|
+
if [ "$STATUS" = "BLOCKED" ] && [ "$CHECKS" = "0" ] && [ "$REVIEW_DECISION" != "REVIEW_REQUIRED" ] && [ "$REVIEW_DECISION" != "CHANGES_REQUESTED" ] && git log -1 --format=%B "$SHIP_NOTE_SHA" | grep -q '\[ci skip\]'; then
|
|
511
|
+
echo "⚠ PR is BLOCKED with zero checks. The [ci skip] trailer wedged the PR due to required checks."
|
|
512
|
+
echo "Pushing an empty commit to trigger the required pipelines..."
|
|
513
|
+
# gsd_run query commit requires a file list; use git directly for this intentionally empty commit.
|
|
514
|
+
git commit --allow-empty -m "chore: trigger CI (recover from ship-note skip-token)"
|
|
515
|
+
git push origin ${CURRENT_BRANCH} 2>&1 || echo "⚠ track_shipping: recovery push failed — rerun: git push origin ${CURRENT_BRANCH}"
|
|
516
|
+
elif [ "$STATUS" = "UNKNOWN" ]; then
|
|
517
|
+
echo "⚠ track_shipping: PR mergeStateStatus is UNKNOWN after polling; PR may require manual check re-trigger."
|
|
518
|
+
fi
|
|
459
519
|
```
|
|
460
520
|
</step>
|
|
461
521
|
|
|
@@ -251,9 +251,11 @@ if ! node -e 'const a=require(process.argv[1]);if(!Array.isArray(a)||a.length===
|
|
|
251
251
|
exit 1
|
|
252
252
|
fi
|
|
253
253
|
# Invoke the compiled engine and CAPTURE its report — it computes which categories apply per
|
|
254
|
-
# requirement. The
|
|
255
|
-
# verification: explicit|backstop)
|
|
256
|
-
# resolution loop
|
|
254
|
+
# requirement. The report is RENDERED into context below (#3102); its resolved/dismissed/
|
|
255
|
+
# unresolved rows (resolved items carry verification: explicit|backstop) are the deterministic
|
|
256
|
+
# FLOOR the resolution loop consumes and unions with its own classification — the loop no longer
|
|
257
|
+
# re-derives the taxonomy from prose unaided. Floor, never ceiling: the classifier has a measured
|
|
258
|
+
# recall gap (ADR-857 §98 / ADR-550 D7b), so the model still ADDS any category the engine missed.
|
|
257
259
|
# The engine FAILS CLOSED (exit 2) on an invalid authored shape or bad input — so the capture
|
|
258
260
|
# MUST be exit-checked. A bare `COVERAGE=$(node …)` swallows that exit code, leaves $COVERAGE
|
|
259
261
|
# empty, and lets the workflow fall through to prose re-derivation: fail-OPEN at the boundary
|
|
@@ -270,6 +272,16 @@ if ! printf '%s' "$COVERAGE" | node -e 'let s="";process.stdin.on("data",d=>s+=d
|
|
|
270
272
|
echo "ERROR: edge-probe produced an unparseable or malformed coverage report — refusing to proceed with the resolution loop." >&2
|
|
271
273
|
exit 1
|
|
272
274
|
fi
|
|
275
|
+
# Render the validated report into the model's visible context (#3102). Until here $COVERAGE was
|
|
276
|
+
# captured, shape-checked, and reduced to coverage.applicable — the engine's per-requirement
|
|
277
|
+
# items[] never reached the model, so the resolution loop below re-derived edge categories from
|
|
278
|
+
# requirement PROSE (the data-flow twin of #2733's control-flow discard). These rows are the
|
|
279
|
+
# deterministic FLOOR the resolution loop consumes. Printed RAW (not a bespoke table) so this
|
|
280
|
+
# step holds NO knowledge of the item schema: an ADR-550 D7a-style re-cut of the item/coverage
|
|
281
|
+
# shape cannot silently desync a hand-rolled renderer here — the engine stays the single source.
|
|
282
|
+
echo "### Edge-probe coverage report (deterministic proposals — the FLOOR for the resolution loop below):"
|
|
283
|
+
printf '%s\n' "$COVERAGE"
|
|
284
|
+
echo "### (end edge-probe coverage report)"
|
|
273
285
|
# Zero-applicable guard: a report where the engine proposed NO applicable edge across ANY
|
|
274
286
|
# requirement is far more likely a shape-classification miss (or malformed requirements) than
|
|
275
287
|
# a genuinely edge-free spec — the same fail-open shape as an invalid shape yielding
|
|
@@ -287,9 +299,14 @@ edge-free spec, or should we revisit the requirement wording / authored shapes?"
|
|
|
287
299
|
an empty `## Edge Coverage` section after explicit confirmation.
|
|
288
300
|
|
|
289
301
|
For each Requirement gathered so far:
|
|
290
|
-
1.
|
|
291
|
-
|
|
292
|
-
|
|
302
|
+
1. Start from the edge-probe rows RENDERED above — the deterministic `items[]` are the FLOOR:
|
|
303
|
+
every proposed `(requirement_id, category)` MUST be resolved below (Specify / Dismiss-with-
|
|
304
|
+
reason / Backstop / Defer), none silently dropped. Then raise any applicable category the
|
|
305
|
+
engine MISSED — the rows are a floor, never a ceiling: the classifier has a measured recall
|
|
306
|
+
gap on terse prose (ADR-857 §98 / ADR-550 D7b), e.g. a CSV-export requirement whose
|
|
307
|
+
`encoding` edge the shape cue under-fires. Union the engine's rows with your own
|
|
308
|
+
classification (relevance filter — see the taxonomy in the reference); do not narrow to them.
|
|
309
|
+
Reuse any edges the Round-4 Failure Analyst already surfaced as pre-resolved.
|
|
293
310
|
2. For each raised category, propose a CONCRETE candidate edge (not "consider
|
|
294
311
|
boundaries" — e.g. "R2 merges intervals; what about `[[1,2],[2,3]]` that only touch?").
|
|
295
312
|
3. Resolve each with the user (AskUserQuestion; text mode → numbered list):
|
|
@@ -313,9 +330,11 @@ For each Requirement gathered so far:
|
|
|
313
330
|
- On "anyway": write SPEC.md with those rows marked `⚠ Edge unresolved — planner must
|
|
314
331
|
treat as assumption`.
|
|
315
332
|
|
|
316
|
-
**`--auto` mode:**
|
|
317
|
-
|
|
318
|
-
|
|
333
|
+
**`--auto` mode:** resolve over the **same rendered floor** (#3102) — every engine-proposed row
|
|
334
|
+
from Step 5.5's report (step 1) plus any category the classifier missed, never a narrower set.
|
|
335
|
+
For each: auto-`resolved` (verification: explicit) where a defensible acceptance criterion can be
|
|
336
|
+
written; otherwise auto-`resolved` (verification: backstop) (never auto-dismiss — a wrong
|
|
337
|
+
dismissal is the exact silent failure being eliminated). Log:
|
|
319
338
|
`[auto] edge coverage: E explicit, B backstop, U unresolved`.
|
|
320
339
|
|
|
321
340
|
**`unclassified` exception (#1110):** `--auto` leaves an `unclassified` candidate
|
|
@@ -458,11 +477,10 @@ Write to: `{phase_dir}/{padded_phase}-SPEC.md`
|
|
|
458
477
|
## Step 7: Commit
|
|
459
478
|
|
|
460
479
|
```bash
|
|
461
|
-
|
|
462
|
-
git commit -m "spec(phase-${phase_number}): add SPEC.md for ${phase_name} — ${requirement_count} requirements (#2213)" -- "${phase_dir}/${padded_phase}-SPEC.md"
|
|
480
|
+
gsd_run query commit "spec(phase-${phase_number}): add SPEC.md for ${phase_name} — ${requirement_count} requirements (#2213)" --files "${phase_dir}/${padded_phase}-SPEC.md"
|
|
463
481
|
```
|
|
464
482
|
|
|
465
|
-
If `commit_docs` is false
|
|
483
|
+
If `commit_docs` is false the CLI returns `skipped`; SPEC.md is written, not committed.
|
|
466
484
|
|
|
467
485
|
## Step 8: Wrap Up
|
|
468
486
|
|