@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
|
@@ -11,7 +11,7 @@ Sync managed `gsd-*` skill directories from one canonical runtime's skills root
|
|
|
11
11
|
| Flag | Required | Default | Description |
|
|
12
12
|
|------|----------|---------|-------------|
|
|
13
13
|
| `--from <runtime>` | Yes | *(none)* | Source runtime — the canonical runtime to copy from |
|
|
14
|
-
| `--to <runtime\|all>` | Yes | *(none)* | Destination runtime or `all` supported runtimes |
|
|
14
|
+
| `--to <runtime\|all>` | Yes | *(none)* | Destination runtime or `all` supported runtimes. **Must equal `--from`** — cross-runtime sync is refused (#3025: skill content/layout is runtime-specific and produced by the installer's per-runtime converters; use the installer for a different runtime). |
|
|
15
15
|
| `--dry-run` | No | *on by default* | Preview changes without writing anything |
|
|
16
16
|
| `--apply` | No | *off* | Execute the diff (overrides dry-run) |
|
|
17
17
|
|
|
@@ -50,6 +50,61 @@ fi
|
|
|
50
50
|
- If `--from` is missing or unrecognized: print error and exit
|
|
51
51
|
- If `--to` is missing or unrecognized: print error and exit
|
|
52
52
|
- If `--from` == `--to` (single destination): print `[no-op: source and destination are the same runtime]` and exit
|
|
53
|
+
- If any `--to` destination differs from `--from` (cross-runtime): REFUSE with the installer pointer below and exit. sync only supports identity sync — see the guard.
|
|
54
|
+
- If `--from` or any `--to` value is not a runtime-id shape (`^[a-z0-9][a-z0-9-]*$`): REFUSE and exit — runtime ids are lowercase alphanumeric (+ hyphen); this rejects shell metacharacters before any interpolation (see security guard).
|
|
55
|
+
|
|
56
|
+
**#3025 — Runtime-id shape validation (security: run BEFORE any interpolation):**
|
|
57
|
+
|
|
58
|
+
`--from`/`--to` are interpolated into later `echo`/heredoc/`[[ ]]` contexts. Reject any value that is not a runtime-id shape BEFORE it reaches them, so a hostile value (e.g. `--to '$(cmd)'`, captured wholesale by the parser) cannot execute via command substitution in an error message.
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
# #3025 (security): runtime ids are lowercase alphanumeric (+ hyphen). Reject
|
|
62
|
+
# anything else BEFORE any echo/heredoc/[[ ]] so a hostile --from/--to value
|
|
63
|
+
# cannot execute via command substitution in a later error message.
|
|
64
|
+
is_runtime_id() { [[ "$1" =~ ^[a-z0-9][a-z0-9-]*$ ]]; }
|
|
65
|
+
if ! is_runtime_id "$FROM_RUNTIME"; then
|
|
66
|
+
echo "error: invalid --from runtime id (not lowercase alphanumeric): '$FROM_RUNTIME'" >&2
|
|
67
|
+
exit 1
|
|
68
|
+
fi
|
|
69
|
+
for DEST in "${TO_RUNTIMES[@]}"; do
|
|
70
|
+
if ! is_runtime_id "$DEST"; then
|
|
71
|
+
echo "error: invalid --to runtime id (not lowercase alphanumeric): '$DEST'" >&2
|
|
72
|
+
exit 1
|
|
73
|
+
fi
|
|
74
|
+
done
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
**#3025 — Cross-runtime refuse guard (run BEFORE Step 2 resolution / Step 5 copy):**
|
|
78
|
+
|
|
79
|
+
Skill content and directory layout are runtime-specific. The installer applies per-runtime
|
|
80
|
+
converters, adapter headers, brand swaps, and layout rules at install time, and two runtimes
|
|
81
|
+
(`grok`, `gemini`) resolve to ANOTHER runtime's skills root. A verbatim copy from one runtime's
|
|
82
|
+
skills root therefore produces content the installer would never have written for the destination,
|
|
83
|
+
and can damage a runtime the user never named. Every cross-runtime pair is unsafe (content and/or
|
|
84
|
+
layout and/or aliasing); only identity (`--from` == `--to`) is safe. Refuse cross-runtime and point
|
|
85
|
+
the user at the installer — the only path that produces correctly converted skills.
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
# #3025: refuse cross-runtime skill sync before any resolution or copy.
|
|
89
|
+
for DEST in "${TO_RUNTIMES[@]}"; do
|
|
90
|
+
if [[ "$DEST" != "$FROM_RUNTIME" ]]; then
|
|
91
|
+
cat >&2 <<EOF
|
|
92
|
+
error: cross-runtime skill sync is not supported (--from $FROM_RUNTIME --to $DEST).
|
|
93
|
+
Skill content and directory layout are runtime-specific: the installer applies
|
|
94
|
+
per-runtime converters, adapter headers, brand swaps, and layout rules that a
|
|
95
|
+
verbatim copy cannot reproduce, and some runtimes share another runtime's skills
|
|
96
|
+
root — so a cross-runtime sync can damage a runtime you did not name.
|
|
97
|
+
To install correctly-converted skills for the '$DEST' runtime, run the GSD
|
|
98
|
+
installer for that runtime (not sync):
|
|
99
|
+
npx -y @opengsd/gsd-core@latest --global --<runtime>
|
|
100
|
+
(grok and gemini have no dedicated installer flag — they alias the codex and
|
|
101
|
+
claude skills roots respectively, which is itself why sync refuses them.)
|
|
102
|
+
sync only supports identity sync, where --from and --to are the same runtime.
|
|
103
|
+
EOF
|
|
104
|
+
exit 1
|
|
105
|
+
fi
|
|
106
|
+
done
|
|
107
|
+
```
|
|
53
108
|
|
|
54
109
|
---
|
|
55
110
|
|
|
@@ -175,12 +230,12 @@ DEST_ROOT=$(gsd_run query skills-root "$DEST_RUNTIME" --raw)
|
|
|
175
230
|
|
|
176
231
|
mkdir -p "$DEST_ROOT"
|
|
177
232
|
|
|
178
|
-
# #3025:
|
|
179
|
-
#
|
|
180
|
-
#
|
|
181
|
-
#
|
|
182
|
-
#
|
|
183
|
-
#
|
|
233
|
+
# #3025: cross-runtime sync is refused in Step 1's guard (skill content/layout is
|
|
234
|
+
# runtime-specific; a verbatim copy corrupts destinations and can alias another
|
|
235
|
+
# runtime's root). This loop is therefore reached only for IDENTITY sync, where
|
|
236
|
+
# every skill is SKIP (source == destination) and the create/update lists are
|
|
237
|
+
# empty. If per-runtime conversion is ever wired in, this is where it would go;
|
|
238
|
+
# until then the cp -r must never run for a destination != source.
|
|
184
239
|
|
|
185
240
|
for SKILL in $CREATE_LIST $UPDATE_LIST; do
|
|
186
241
|
rm -rf "$DEST_ROOT/$SKILL"
|
|
@@ -215,6 +270,6 @@ Sync complete: <N> skills synced to <M> runtime(s).
|
|
|
215
270
|
|
|
216
271
|
## Limitations
|
|
217
272
|
|
|
218
|
-
- Sync copies files verbatim and does not apply runtime-specific content transformations.
|
|
273
|
+
- Sync copies files verbatim and does not apply runtime-specific content transformations. **Cross-runtime sync is refused** (#3025): skill content and layout are runtime-specific, and some runtimes alias another runtime's skills root, so a verbatim cross-runtime copy corrupts the destination (and can damage a runtime you did not name). Only identity sync (`--from` == `--to`) is supported. To install skills for a different runtime, run the GSD installer for that runtime (`npx -y @opengsd/gsd-core@latest --global --<runtime>`).
|
|
219
274
|
- Cross-project skills (`.agents/skills/`) are out of scope — this command only touches global runtime skills roots.
|
|
220
275
|
- Bidirectional sync is not supported. Choose one canonical source with `--from`.
|
|
@@ -36,6 +36,31 @@ Mark current phase complete and advance to next. This is the natural point where
|
|
|
36
36
|
|
|
37
37
|
<process>
|
|
38
38
|
|
|
39
|
+
<step name="post_completion_mode" priority="first">
|
|
40
|
+
|
|
41
|
+
**Invocation mode — read this FIRST.** This workflow runs two ways:
|
|
42
|
+
|
|
43
|
+
1. **Standalone transition** (normal path): the phase is being marked complete AND
|
|
44
|
+
transitioned by this workflow. Run EVERY step below in order — `verify_completion`,
|
|
45
|
+
`update_roadmap_and_state` (which calls `gsd_run query phase.complete`), then the
|
|
46
|
+
post-processing.
|
|
47
|
+
|
|
48
|
+
2. **Post-completion delegation** (invoked by `execute-phase` after its auto-chain
|
|
49
|
+
completion — #1526): `phase.complete` was already called by execute-phase's
|
|
50
|
+
`update_roadmap` step and verification already passed in execute-phase's
|
|
51
|
+
`verify_phase_goal`. SKIP `verify_completion` and `update_roadmap_and_state`
|
|
52
|
+
(re-running `phase.complete` would double-write STATE.md/ROADMAP.md). Run
|
|
53
|
+
`cleanup_handoff` (stale `.continue-here` handoffs are still cleared post-completion),
|
|
54
|
+
then BEGIN at `evolve_project` and run every step from there through
|
|
55
|
+
`offer_next_phase` (this is the post-processing parity set: graduation scan,
|
|
56
|
+
session-continuity, project-reference, accumulated-context, current-position/progress).
|
|
57
|
+
`archive_prompts` is a documented no-op in either mode.
|
|
58
|
+
|
|
59
|
+
Detect post-completion mode when the caller states that phase completion and
|
|
60
|
+
verification have already run. When in doubt, run standalone (mode 1) — it is
|
|
61
|
+
idempotent enough to be safe, just slower.
|
|
62
|
+
</step>
|
|
63
|
+
|
|
39
64
|
<step name="load_project_state" priority="first">
|
|
40
65
|
|
|
41
66
|
Before transition, read project state:
|
|
@@ -77,12 +102,22 @@ cat .planning/config.json 2>/dev/null || true
|
|
|
77
102
|
**Check for verification debt in this phase:**
|
|
78
103
|
|
|
79
104
|
```bash
|
|
80
|
-
|
|
81
|
-
#
|
|
105
|
+
_GSD_SHIM_NAME="gsd-tools.cjs"; _GSD_RUNTIME_ROOT="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}"; GSD_TOOLS="${_GSD_RUNTIME_ROOT}/gsd-core/bin/${_GSD_SHIM_NAME}"; if [ -f "$GSD_TOOLS" ]; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${_GSD_RUNTIME_ROOT}/.claude/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${_GSD_RUNTIME_ROOT}/.claude/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${_GSD_RUNTIME_ROOT}/.codex/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${_GSD_RUNTIME_ROOT}/.codex/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif command -v gsd-tools >/dev/null 2>&1; then GSD_TOOLS="$(command -v gsd-tools)"; gsd_run() { "$GSD_TOOLS" "$@"; }; elif [ -f "${CLAUDE_CONFIG_DIR:-$HOME/.claude}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CLAUDE_CONFIG_DIR:-$HOME/.claude}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${HERMES_HOME:-$HOME/.hermes}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${HERMES_HOME:-$HOME/.hermes}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CURSOR_CONFIG_DIR:-$HOME/.cursor}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CURSOR_CONFIG_DIR:-$HOME/.cursor}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CODEX_HOME:-$HOME/.codex}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CODEX_HOME:-$HOME/.codex}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${GEMINI_CONFIG_DIR:-$HOME/.gemini}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${GEMINI_CONFIG_DIR:-$HOME/.gemini}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${COPILOT_CONFIG_DIR:-$HOME/.copilot}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${COPILOT_CONFIG_DIR:-$HOME/.copilot}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${WINDSURF_CONFIG_DIR:-$HOME/.codeium/windsurf}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${WINDSURF_CONFIG_DIR:-$HOME/.codeium/windsurf}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${AUGMENT_CONFIG_DIR:-$HOME/.augment}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${AUGMENT_CONFIG_DIR:-$HOME/.augment}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${TRAE_CONFIG_DIR:-$HOME/.trae}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${TRAE_CONFIG_DIR:-$HOME/.trae}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${QWEN_CONFIG_DIR:-$HOME/.qwen}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${QWEN_CONFIG_DIR:-$HOME/.qwen}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CODEBUDDY_CONFIG_DIR:-$HOME/.codebuddy}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CODEBUDDY_CONFIG_DIR:-$HOME/.codebuddy}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CLINE_CONFIG_DIR:-$HOME/.cline}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CLINE_CONFIG_DIR:-$HOME/.cline}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${GROK_AGENTS_HOME:-$HOME/.agents}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${GROK_AGENTS_HOME:-$HOME/.agents}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${ANTIGRAVITY_CONFIG_DIR:-$HOME/.gemini/antigravity}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${ANTIGRAVITY_CONFIG_DIR:-$HOME/.gemini/antigravity}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${OPENCODE_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/opencode}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${OPENCODE_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/opencode}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${KILO_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/kilo}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${KILO_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/kilo}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH. Run: npx -y @opengsd/gsd-core@latest --claude --local" >&2; exit 1; fi; if [ -n "${CLAUDE_ENV_FILE:-}" ] && [ -n "${GSD_TOOLS:-}" ]; then printf "export PATH='%s':\"\$PATH\"\n" "${GSD_TOOLS%/*}" >> "$CLAUDE_ENV_FILE" 2>/dev/null || true; fi
|
|
106
|
+
# #3492: resolve THIS phase's own report through the single shared seam
|
|
107
|
+
# (src/verification.cts resolveVerificationFile) instead of a blind
|
|
108
|
+
# `*-VERIFICATION.md` glob — a stray ad-hoc worksheet (e.g.
|
|
109
|
+
# `03-CORRECTION-VERIFICATION.md`) alphabetically outranks the real report
|
|
110
|
+
# and previously fed this awk parse the wrong file.
|
|
111
|
+
VERIFICATION_FILE=$(gsd_run query verification.resolve-file .planning/phases/XX-current --raw 2>/dev/null)
|
|
82
112
|
# awk extracts only the status: field between the two --- fences to avoid
|
|
83
113
|
# false positives from historical body text (e.g. previous_status: gaps_found).
|
|
84
|
-
|
|
85
|
-
|
|
114
|
+
# FNR (not NR) re-arms the frontmatter scan per input file: NR only ever arms
|
|
115
|
+
# on the very FIRST line of the very first file, so a multi-file input would
|
|
116
|
+
# silently read empty status for every file after the first. The resolver
|
|
117
|
+
# above always hands back a single path, but the parse stays correct even if
|
|
118
|
+
# that ever changes.
|
|
119
|
+
VERIFY_STATUS=$(awk 'FNR==1&&/^---$/{in_fm=1;next}in_fm&&/^---$/{exit}in_fm&&/^status: /{print $2}' \
|
|
120
|
+
"$VERIFICATION_FILE" 2>/dev/null | head -1)
|
|
86
121
|
```
|
|
87
122
|
|
|
88
123
|
**If VERIFY_STATUS is not `passed`:**
|
|
@@ -95,10 +130,10 @@ Verification incomplete: ${VERIFY_STATUS:-missing}
|
|
|
95
130
|
Resolve before transition. Review: `/gsd:audit-uat`
|
|
96
131
|
```
|
|
97
132
|
|
|
98
|
-
This preliminary check blocks obviously unresolved verification
|
|
99
|
-
|
|
100
|
-
authoritative stale-aware gate and
|
|
101
|
-
status is `passed`.
|
|
133
|
+
This preliminary check blocks obviously unresolved verification early, ahead
|
|
134
|
+
of the authoritative gate below. `gsd-tools.cjs query phase.complete` (in
|
|
135
|
+
`update_roadmap_and_state`) remains the authoritative stale-aware gate and
|
|
136
|
+
fail-closes unless canonical verification status is `passed`.
|
|
102
137
|
|
|
103
138
|
**If all plans complete:**
|
|
104
139
|
|
|
@@ -165,7 +200,6 @@ If found, delete them — phase is complete, handoffs are stale.
|
|
|
165
200
|
**Delegate ROADMAP.md and STATE.md updates to `gsd-tools.cjs query phase.complete`:**
|
|
166
201
|
|
|
167
202
|
```bash
|
|
168
|
-
_GSD_SHIM_NAME="gsd-tools.cjs"; _GSD_RUNTIME_ROOT="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}"; GSD_TOOLS="${_GSD_RUNTIME_ROOT}/gsd-core/bin/${_GSD_SHIM_NAME}"; if [ -f "$GSD_TOOLS" ]; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${_GSD_RUNTIME_ROOT}/.claude/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${_GSD_RUNTIME_ROOT}/.claude/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${_GSD_RUNTIME_ROOT}/.codex/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${_GSD_RUNTIME_ROOT}/.codex/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif command -v gsd-tools >/dev/null 2>&1; then GSD_TOOLS="$(command -v gsd-tools)"; gsd_run() { "$GSD_TOOLS" "$@"; }; elif [ -f "${CLAUDE_CONFIG_DIR:-$HOME/.claude}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CLAUDE_CONFIG_DIR:-$HOME/.claude}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${HERMES_HOME:-$HOME/.hermes}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${HERMES_HOME:-$HOME/.hermes}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CURSOR_CONFIG_DIR:-$HOME/.cursor}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CURSOR_CONFIG_DIR:-$HOME/.cursor}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CODEX_HOME:-$HOME/.codex}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CODEX_HOME:-$HOME/.codex}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${GEMINI_CONFIG_DIR:-$HOME/.gemini}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${GEMINI_CONFIG_DIR:-$HOME/.gemini}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${COPILOT_CONFIG_DIR:-$HOME/.copilot}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${COPILOT_CONFIG_DIR:-$HOME/.copilot}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${WINDSURF_CONFIG_DIR:-$HOME/.codeium/windsurf}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${WINDSURF_CONFIG_DIR:-$HOME/.codeium/windsurf}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${AUGMENT_CONFIG_DIR:-$HOME/.augment}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${AUGMENT_CONFIG_DIR:-$HOME/.augment}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${TRAE_CONFIG_DIR:-$HOME/.trae}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${TRAE_CONFIG_DIR:-$HOME/.trae}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${QWEN_CONFIG_DIR:-$HOME/.qwen}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${QWEN_CONFIG_DIR:-$HOME/.qwen}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CODEBUDDY_CONFIG_DIR:-$HOME/.codebuddy}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CODEBUDDY_CONFIG_DIR:-$HOME/.codebuddy}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CLINE_CONFIG_DIR:-$HOME/.cline}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CLINE_CONFIG_DIR:-$HOME/.cline}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${GROK_AGENTS_HOME:-$HOME/.agents}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${GROK_AGENTS_HOME:-$HOME/.agents}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${ANTIGRAVITY_CONFIG_DIR:-$HOME/.gemini/antigravity}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${ANTIGRAVITY_CONFIG_DIR:-$HOME/.gemini/antigravity}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${OPENCODE_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/opencode}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${OPENCODE_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/opencode}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${KILO_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/kilo}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${KILO_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/kilo}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH. Run: npx -y @opengsd/gsd-core@latest --claude --local" >&2; exit 1; fi; if [ -n "${CLAUDE_ENV_FILE:-}" ] && [ -n "${GSD_TOOLS:-}" ]; then printf "export PATH='%s':\"\$PATH\"\n" "${GSD_TOOLS%/*}" >> "$CLAUDE_ENV_FILE" 2>/dev/null || true; fi
|
|
169
203
|
TRANSITION=$(gsd_run query phase.complete "${current_phase}")
|
|
170
204
|
```
|
|
171
205
|
|
|
@@ -194,7 +228,8 @@ Evolve PROJECT.md to reflect learnings from completed phase.
|
|
|
194
228
|
**Read phase summaries:**
|
|
195
229
|
|
|
196
230
|
```bash
|
|
197
|
-
|
|
231
|
+
_SUMMARIES=( .planning/phases/XX-current/*-SUMMARY.md )
|
|
232
|
+
if [ -e "${_SUMMARIES[0]}" ]; then cat "${_SUMMARIES[@]}"; fi
|
|
198
233
|
```
|
|
199
234
|
|
|
200
235
|
**Assess requirement changes:**
|
|
@@ -417,7 +452,7 @@ ROADMAP=$(gsd_run query roadmap.analyze)
|
|
|
417
452
|
|
|
418
453
|
This returns all phases with goals, disk status, and completion info.
|
|
419
454
|
|
|
420
|
-
**Section-manifest gate (#2994):** `gsd_run` is already established above (`
|
|
455
|
+
**Section-manifest gate (#2994):** `gsd_run` is already established above (`verify_completion` step) — fetch the dedicated `init.transition` bundle for the workstream-collision-check gate below:
|
|
421
456
|
|
|
422
457
|
```bash
|
|
423
458
|
INIT_TRANSITION=$(gsd_run query init.transition)
|
|
@@ -132,14 +132,14 @@ Create UI design contract for Phase {phase_number}: {phase_name}
|
|
|
132
132
|
Answer: "What visual and interaction contracts does this phase need?"
|
|
133
133
|
</objective>
|
|
134
134
|
|
|
135
|
-
<
|
|
135
|
+
<required_reading>
|
|
136
136
|
- {state_path} (Project State)
|
|
137
137
|
- {roadmap_path} (Roadmap)
|
|
138
138
|
- {requirements_path} (Requirements)
|
|
139
139
|
- {context_path} (USER DECISIONS from /gsd:discuss-phase)
|
|
140
140
|
- {research_path} (Technical Research — stack decisions)
|
|
141
141
|
- {SKETCH_FINDINGS_PATH} (Sketch Findings — validated design decisions, CSS patterns, visual direction from /gsd:sketch, if exists)
|
|
142
|
-
</
|
|
142
|
+
</required_reading>
|
|
143
143
|
|
|
144
144
|
${AGENT_SKILLS_UI}
|
|
145
145
|
|
|
@@ -155,7 +155,7 @@ padded_phase: {padded_phase}
|
|
|
155
155
|
</config>
|
|
156
156
|
```
|
|
157
157
|
|
|
158
|
-
Omit null file paths from `<
|
|
158
|
+
Omit null file paths from `<required_reading>`.
|
|
159
159
|
|
|
160
160
|
<!-- #2508 runtime-aware-dispatch -->
|
|
161
161
|
|
|
@@ -205,11 +205,11 @@ Validate UI design contract for Phase {phase_number}: {phase_name}
|
|
|
205
205
|
Check all 6 dimensions. Return APPROVED or BLOCKED.
|
|
206
206
|
</objective>
|
|
207
207
|
|
|
208
|
-
<
|
|
208
|
+
<required_reading>
|
|
209
209
|
- {phase_dir}/{padded_phase}-UI-SPEC.md (UI Design Contract — PRIMARY INPUT)
|
|
210
210
|
- {context_path} (USER DECISIONS — check compliance)
|
|
211
211
|
- {research_path} (Technical Research — check stack alignment)
|
|
212
|
-
</
|
|
212
|
+
</required_reading>
|
|
213
213
|
|
|
214
214
|
${AGENT_SKILLS_UI_CHECKER}
|
|
215
215
|
|
|
@@ -84,12 +84,12 @@ Conduct 6-pillar visual audit of Phase {phase_number}: {phase_name}
|
|
|
84
84
|
{If no UI-SPEC: "Audit against abstract 6-pillar standards."}
|
|
85
85
|
</objective>
|
|
86
86
|
|
|
87
|
-
<
|
|
87
|
+
<required_reading>
|
|
88
88
|
- {summary_paths} (Execution summaries)
|
|
89
89
|
- {plan_paths} (Execution plans — what was intended)
|
|
90
90
|
- {ui_spec_path} (UI Design Contract — audit baseline, if exists)
|
|
91
91
|
- {context_path} (User decisions, if exists)
|
|
92
|
-
</
|
|
92
|
+
</required_reading>
|
|
93
93
|
|
|
94
94
|
${AGENT_SKILLS_UI_REVIEWER}
|
|
95
95
|
|
|
@@ -78,7 +78,7 @@ Parse output:
|
|
|
78
78
|
- Line 4 = resolved GSD config dir (e.g. `/Users/me/.claude`, `/Users/me/.gemini`); empty if scope is `UNKNOWN`. Capture this as `GSD_DIR` and pass it to subsequent steps so they don't re-derive the runtime path.
|
|
79
79
|
- If scope is `UNKNOWN`, proceed to install using the `--claude --global` fallback.
|
|
80
80
|
|
|
81
|
-
`update-context` reproduces the previous detection cascade — preferred-config-dir fast path, local-over-global with same-path dedup (so `CWD=$HOME` does not misdetect as LOCAL), env-var overrides (`CLAUDE_CONFIG_DIR`, `OPENCODE_CONFIG_DIR`, `KILO_CONFIG`, `XDG_CONFIG_HOME`, `CODEX_HOME`, …), and semver validation — but as a tested projection rather than ~280 lines of inline bash. Branch coverage lives in `tests/
|
|
81
|
+
`update-context` reproduces the previous detection cascade — preferred-config-dir fast path, local-over-global with same-path dedup (so `CWD=$HOME` does not misdetect as LOCAL), env-var overrides (`CLAUDE_CONFIG_DIR`, `OPENCODE_CONFIG_DIR`, `KILO_CONFIG`, `XDG_CONFIG_HOME`, `CODEX_HOME`, …), and semver validation — but as a tested projection rather than ~280 lines of inline bash. Branch coverage lives in `tests/update-context.test.cjs`.
|
|
82
82
|
|
|
83
83
|
If multiple runtime installs are detected and the invoking runtime cannot be determined from execution_context, ask the user which runtime to update before running install.
|
|
84
84
|
|
|
@@ -110,7 +110,7 @@ Print: `◆ Spawning nyquist auditor... (runs in a subagent — no output until
|
|
|
110
110
|
```
|
|
111
111
|
Agent(
|
|
112
112
|
prompt="Read ~/.claude/agents/gsd-nyquist-auditor.md for instructions.\n\n" +
|
|
113
|
-
"<
|
|
113
|
+
"<required_reading>{PLAN, SUMMARY, impl files, VALIDATION.md}</required_reading>" +
|
|
114
114
|
"<gaps>{gap list}</gaps>" +
|
|
115
115
|
"<test_infrastructure>{framework, config, commands}</test_infrastructure>" +
|
|
116
116
|
"<constraints>Never modify impl files. Max 3 debug iterations. Escalate impl bugs.</constraints>" +
|
|
@@ -70,6 +70,8 @@ VERIFY_PRE_HOOKS_JSON=$(gsd_run loop render-hooks verify:pre --raw)
|
|
|
70
70
|
PHASE_DIR=$(printf '%s' "$INIT" | jq -r '.phase_dir // empty')
|
|
71
71
|
```
|
|
72
72
|
|
|
73
|
+
⚠ **Validate `check` before shell use** (third-party manifest input) — `loop-hook-dispatch.md` § `gate`.
|
|
74
|
+
|
|
73
75
|
Resolve active gate hooks from `VERIFY_PRE_HOOKS_JSON` where `kind == "gate"`.
|
|
74
76
|
For each active gate hook, run its declared check (a `check.query` gate runs
|
|
75
77
|
`gsd_run check ${hook.check.query} "${PHASE_DIR}" --raw`; a `predicate` gate
|
|
@@ -570,7 +572,7 @@ If execution verification is waiting only on human UAT and this session recorded
|
|
|
570
572
|
|
|
571
573
|
```bash
|
|
572
574
|
PHASE_DIR=$(printf '%s' "$INIT" | jq -r '.phase_dir // empty')
|
|
573
|
-
VERIFICATION_FILE=$(
|
|
575
|
+
VERIFICATION_FILE=$(gsd_run query verification.resolve-file "$PHASE_DIR" --raw 2>/dev/null)
|
|
574
576
|
VERIFICATION_STATUS=$(gsd_run query verification.status "$PHASE_DIR" 2>/dev/null)
|
|
575
577
|
VERIFICATION_STATUS_VALUE=$(printf '%s' "$VERIFICATION_STATUS" | jq -r '.status // empty' 2>/dev/null || echo "")
|
|
576
578
|
PHASE_VERIFICATION_STATUS="$VERIFICATION_STATUS_VALUE"
|
|
@@ -704,11 +706,11 @@ Agent(
|
|
|
704
706
|
**Phase:** {phase_number}
|
|
705
707
|
**Mode:** gap_closure
|
|
706
708
|
|
|
707
|
-
<
|
|
709
|
+
<required_reading>
|
|
708
710
|
- {phase_dir}/{phase_num}-UAT.md (UAT with diagnoses)
|
|
709
711
|
- {state_path} (Project State)
|
|
710
712
|
- {roadmap_path} (Roadmap)
|
|
711
|
-
</
|
|
713
|
+
</required_reading>
|
|
712
714
|
|
|
713
715
|
${AGENT_SKILLS_PLANNER}
|
|
714
716
|
|
|
@@ -769,9 +771,9 @@ Agent(
|
|
|
769
771
|
**Phase:** {phase_number}
|
|
770
772
|
**Phase Goal:** Close diagnosed gaps from UAT
|
|
771
773
|
|
|
772
|
-
<
|
|
774
|
+
<required_reading>
|
|
773
775
|
- {phase_dir}/*-PLAN.md (Plans to verify)
|
|
774
|
-
</
|
|
776
|
+
</required_reading>
|
|
775
777
|
|
|
776
778
|
${AGENT_SKILLS_CHECKER}
|
|
777
779
|
|
|
@@ -813,9 +815,9 @@ Agent(
|
|
|
813
815
|
**Phase:** {phase_number}
|
|
814
816
|
**Mode:** revision
|
|
815
817
|
|
|
816
|
-
<
|
|
818
|
+
<required_reading>
|
|
817
819
|
- {phase_dir}/*-PLAN.md (Existing plans)
|
|
818
|
-
</
|
|
820
|
+
</required_reading>
|
|
819
821
|
|
|
820
822
|
${AGENT_SKILLS_PLANNER}
|
|
821
823
|
|
|
@@ -43,8 +43,9 @@
|
|
|
43
43
|
// authoritative — `none`/`orchestrator-worktree` ALLOW immediately
|
|
44
44
|
// (sequential/orchestrator-managed dispatch is legitimate, not a bug); an
|
|
45
45
|
// absent/stale sentinel falls back to a conservative registry+config check
|
|
46
|
-
// (GSD_RUNTIME env > .planning/config.json `runtime`
|
|
47
|
-
//
|
|
46
|
+
// (GSD_RUNTIME env > .planning/config.json `runtime` > the per-install
|
|
47
|
+
// `.gsd-runtime` marker, #3566 — no confident signal degrades to inert rather
|
|
48
|
+
// than guessing 'claude', see resolveRegistryIsolation)
|
|
48
49
|
// gated additionally by `workflow.use_worktrees` — read directly, in-process,
|
|
49
50
|
// no subprocess spawn.
|
|
50
51
|
//
|
|
@@ -63,6 +64,15 @@ const fs = require('fs');
|
|
|
63
64
|
const path = require('path');
|
|
64
65
|
const os = require('os');
|
|
65
66
|
const { readSentinel, VALID_ISOLATION, extractDispatchIdentifiers, sentinelAppliesToDispatch } = require('./lib/isolation-sentinel.js');
|
|
67
|
+
const { REASON_CODE } = require('./lib/isolation-deny-reason.js');
|
|
68
|
+
// #3582: gsd-core/bin/lib/*.cjs (runtime-name-policy.cjs, capability-registry.cjs
|
|
69
|
+
// below) are tsc build artifacts (ADR-457), gitignored and absent on a raw
|
|
70
|
+
// plugin-marketplace / git-clone install that never ran `npm run build:lib`.
|
|
71
|
+
// Self-heal before the first such require (resolveRegistryIsolation, below) —
|
|
72
|
+
// see ensureRuntimeBuild's own header for the full rationale. This module
|
|
73
|
+
// itself (gsd-core/bin/ensure-runtime-build.cjs) depends on nothing under
|
|
74
|
+
// ./lib, so requiring it here is always safe.
|
|
75
|
+
const { ensureRuntimeBuild, RuntimeBuildError } = require('../gsd-core/bin/ensure-runtime-build.cjs');
|
|
66
76
|
|
|
67
77
|
// No other executor-shaped subagent_type exists in agents/ today
|
|
68
78
|
// (verified: only agents/gsd-executor.md). A Set, not a bare string compare,
|
|
@@ -85,6 +95,42 @@ function parseHarnessFlag(flag) {
|
|
|
85
95
|
return { param: m[1], value: m[2] };
|
|
86
96
|
}
|
|
87
97
|
|
|
98
|
+
// ─── #3566: per-install runtime marker ────────────────────────────────────────
|
|
99
|
+
// bin/install.js writes `<install>/gsd-core/.gsd-runtime` for EVERY runtime
|
|
100
|
+
// install (#2297), co-located with VERSION. Unlike `~/.gsd/defaults.json` —
|
|
101
|
+
// which is host-wide and names whichever runtime's install ran LAST, the exact
|
|
102
|
+
// leakage #2840's config.cjs change exists to prevent — the marker describes
|
|
103
|
+
// THIS install, which is the property runtime identity needs on a machine
|
|
104
|
+
// with 2+ runtimes. Mirrors readInstallRuntimeMarker in src/model-resolver.cts
|
|
105
|
+
// (same cache + test-seam shape); this hook cannot import that module without
|
|
106
|
+
// dragging the whole model-resolution stack into a PreToolUse hot path, so the
|
|
107
|
+
// 5-line read lives here against the same sibling-layout assumption the hook's
|
|
108
|
+
// own require('../gsd-core/bin/lib/…') already makes. Epic #3473 B3 owns
|
|
109
|
+
// consolidating every marker reader into one shared seam.
|
|
110
|
+
let _installMarkerCache; // undefined = unread; null = known absent; string = value
|
|
111
|
+
|
|
112
|
+
function readInstallRuntimeMarker() {
|
|
113
|
+
if (_installMarkerCache !== undefined) return _installMarkerCache;
|
|
114
|
+
try {
|
|
115
|
+
const markerPath = path.join(__dirname, '..', 'gsd-core', '.gsd-runtime');
|
|
116
|
+
const raw = fs.readFileSync(markerPath, 'utf-8').trim();
|
|
117
|
+
_installMarkerCache = raw || null;
|
|
118
|
+
} catch {
|
|
119
|
+
// No marker: dev/source tree, or an install predating #2297 — "no signal
|
|
120
|
+
// from this rung", never a resolution failure. Falls through to the
|
|
121
|
+
// defaults rung below.
|
|
122
|
+
_installMarkerCache = null;
|
|
123
|
+
}
|
|
124
|
+
return _installMarkerCache;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Test seam for the marker rung (the dev/source tree has no marker file, so
|
|
128
|
+
// the read always bottoms out at null there — same seam contract as
|
|
129
|
+
// model-resolver.cts's _setInstallRuntimeMarkerForTests, #2297).
|
|
130
|
+
function _setInstallRuntimeMarkerForTests(value) {
|
|
131
|
+
_installMarkerCache = value;
|
|
132
|
+
}
|
|
133
|
+
|
|
88
134
|
/**
|
|
89
135
|
* Resolve this project's declared `runtime` identity WITHOUT defaulting to
|
|
90
136
|
* 'claude' when no explicit signal exists (#3045 MAJOR 2).
|
|
@@ -100,9 +146,10 @@ function parseHarnessFlag(flag) {
|
|
|
100
146
|
*
|
|
101
147
|
* Returns `{ runtimeId, confident }`. `confident` is true only when an
|
|
102
148
|
* explicit signal exists (GSD_RUNTIME env override, a `runtime` key literally
|
|
103
|
-
* present in config.json,
|
|
104
|
-
* by the installer — see
|
|
105
|
-
*
|
|
149
|
+
* present in config.json, the per-install `.gsd-runtime` marker, or a
|
|
150
|
+
* `runtime` persisted to `~/.gsd/defaults.json` by the installer — see
|
|
151
|
+
* below); false means "cannot determine" and callers must NOT silently
|
|
152
|
+
* substitute 'claude' — see resolveRegistryIsolation.
|
|
106
153
|
*
|
|
107
154
|
* #3045 BLOCKER 2 fix: precedence is GSD_RUNTIME env > config.json `runtime`
|
|
108
155
|
* key > `~/.gsd/defaults.json` `runtime`. The first two are unchanged; the
|
|
@@ -117,6 +164,17 @@ function parseHarnessFlag(flag) {
|
|
|
117
164
|
* (`nativeModelAliases` short-circuits it) and therefore correctly still rely
|
|
118
165
|
* on config.json/env. Reading the installer's own persisted signal makes
|
|
119
166
|
* "confident" the common case instead.
|
|
167
|
+
*
|
|
168
|
+
* #3566: the per-install `.gsd-runtime` marker now sits BETWEEN config.json
|
|
169
|
+
* and defaults.json. defaults.json is host-wide and names whichever runtime
|
|
170
|
+
* installed LAST — on a 2-runtime machine that confidently resolves the WRONG
|
|
171
|
+
* runtime (a Codex install's `runtime:"codex"` leaking into Claude projects),
|
|
172
|
+
* and when the wrong runtime declares no harnessIsolationFlag the guard goes
|
|
173
|
+
* silently inert. The marker describes THIS install (written for every
|
|
174
|
+
* runtime since #2297), which is the source #2840's config.cjs change names
|
|
175
|
+
* as correct. defaults.json stays as the final rung so single-runtime default
|
|
176
|
+
* installs and pre-#2297 installs (no marker on disk) keep the #3045
|
|
177
|
+
* BLOCKER 2 behavior.
|
|
120
178
|
*/
|
|
121
179
|
function resolveRuntimeIdentity(cwd, configPath, resolveRuntimeNameFromCandidates) {
|
|
122
180
|
const envRuntime = resolveRuntimeNameFromCandidates(process.env.GSD_RUNTIME);
|
|
@@ -132,6 +190,13 @@ function resolveRuntimeIdentity(cwd, configPath, resolveRuntimeNameFromCandidate
|
|
|
132
190
|
if (configRuntime) return { runtimeId: configRuntime, confident: true };
|
|
133
191
|
}
|
|
134
192
|
|
|
193
|
+
// #3566: the per-install marker, above the host-wide defaults — see the
|
|
194
|
+
// block comment on readInstallRuntimeMarker. An empty/whitespace-only file
|
|
195
|
+
// or an unknown value degrades exactly like the other rungs (no signal /
|
|
196
|
+
// future-runtime tolerance via resolveRuntimeNameFromCandidates).
|
|
197
|
+
const markerRuntime = resolveRuntimeNameFromCandidates(readInstallRuntimeMarker());
|
|
198
|
+
if (markerRuntime) return { runtimeId: markerRuntime, confident: true };
|
|
199
|
+
|
|
135
200
|
// #3045 BLOCKER 2: fall back to the installer-persisted default. Read
|
|
136
201
|
// defensively — an absent/corrupt/non-object defaults.json is "no signal",
|
|
137
202
|
// never a resolution failure (this function only ever throws for the
|
|
@@ -183,6 +248,15 @@ function resolveHarnessFlag(runtimeId, runtimes) {
|
|
|
183
248
|
* run, e.g. a manual Agent() call before any sentinel has been written).
|
|
184
249
|
*/
|
|
185
250
|
function resolveRegistryIsolation(cwd, configPath) {
|
|
251
|
+
// #3582: self-heal the compiled runtime library BEFORE either require
|
|
252
|
+
// below — this is the only reaching path to both (resolveRegistryIsolation
|
|
253
|
+
// is the sole caller of each), so one call here covers both. Throws
|
|
254
|
+
// RuntimeBuildError on an unbuildable tree; the caller (resolveIsolationState)
|
|
255
|
+
// already wraps this whole function in try/catch and folds any error into
|
|
256
|
+
// its fail-closed `error` result — evaluateDispatch below distinguishes a
|
|
257
|
+
// RuntimeBuildError there so it surfaces this seam's actionable message
|
|
258
|
+
// instead of being misreported as an unreadable config.json (#3050 lesson).
|
|
259
|
+
ensureRuntimeBuild();
|
|
186
260
|
const { resolveRuntimeNameFromCandidates } = require('../gsd-core/bin/lib/runtime-name-policy.cjs');
|
|
187
261
|
const { runtimes } = require('../gsd-core/bin/lib/capability-registry.cjs');
|
|
188
262
|
|
|
@@ -360,13 +434,27 @@ function evaluateDispatch(data, { clock = Date } = {}) {
|
|
|
360
434
|
if (!state.gsdProject) return { action: 'allow' };
|
|
361
435
|
|
|
362
436
|
if (state.error) {
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
437
|
+
// #3582: a missing/unbuildable compiled runtime library (RuntimeBuildError,
|
|
438
|
+
// thrown by ensureRuntimeBuild in resolveRegistryIsolation) is a DIFFERENT,
|
|
439
|
+
// actionable failure from an unreadable/unparsable config.json — surface
|
|
440
|
+
// its own message instead of misreporting it as the generic
|
|
441
|
+
// "could not read or resolve ... configuration" text (the exact #3050
|
|
442
|
+
// misreport this issue exists to fix). Both cases still fail closed
|
|
443
|
+
// (block); only the message differs.
|
|
444
|
+
const isBuildFailure = state.error instanceof RuntimeBuildError;
|
|
445
|
+
const reason = isBuildFailure
|
|
446
|
+
? `Agent isolation guard: cannot resolve this project's dispatch-isolation ` +
|
|
447
|
+
`configuration because the GSD runtime library failed to self-build. ` +
|
|
448
|
+
`${state.error.message} Refusing to dispatch subagent_type="${subagentType}" until ` +
|
|
449
|
+
`the runtime library is built — a guard that cannot verify must not answer "safe" ` +
|
|
450
|
+
`(#3050).`
|
|
451
|
+
: `Agent isolation guard: could not read or resolve this project's dispatch-isolation ` +
|
|
452
|
+
`configuration ('.planning/config.json' under '${cwd}'). Refusing to dispatch ` +
|
|
453
|
+
`subagent_type="${subagentType}" without being able to verify whether isolation is ` +
|
|
454
|
+
`required — a guard that cannot verify must not answer "safe" (#3050). Retry once the ` +
|
|
455
|
+
`project configuration is readable.`;
|
|
456
|
+
const reasonCode = isBuildFailure ? REASON_CODE.RUNTIME_BUILD_FAILED : REASON_CODE.CONFIG_UNREADABLE;
|
|
457
|
+
return { action: 'block', reason, reasonCode };
|
|
370
458
|
}
|
|
371
459
|
|
|
372
460
|
if (state.isolation !== 'harness-worktree') return { action: 'allow' };
|
|
@@ -382,7 +470,7 @@ function evaluateDispatch(data, { clock = Date } = {}) {
|
|
|
382
470
|
`${parsed.param}="${parsed.value}". Add ${parsed.param}="${parsed.value}" to the Agent() ` +
|
|
383
471
|
`call so the executor runs in an isolated worktree instead of the primary checkout ` +
|
|
384
472
|
`(gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md).`;
|
|
385
|
-
return { action: 'block', reason };
|
|
473
|
+
return { action: 'block', reason, reasonCode: REASON_CODE.HARNESS_FLAG_MISSING };
|
|
386
474
|
}
|
|
387
475
|
|
|
388
476
|
/* istanbul ignore next -- stdin adapter, exercised via spawnSync in tests */
|
|
@@ -397,7 +485,7 @@ function main() {
|
|
|
397
485
|
const data = JSON.parse(input);
|
|
398
486
|
const decision = evaluateDispatch(data);
|
|
399
487
|
if (decision.action === 'block') {
|
|
400
|
-
const out = { decision: 'block', reason: decision.reason };
|
|
488
|
+
const out = { decision: 'block', reason: decision.reason, reason_code: decision.reasonCode };
|
|
401
489
|
process.stdout.write(JSON.stringify(out));
|
|
402
490
|
// Kimi feeds stderr (not stdout) back to the model on exit 2.
|
|
403
491
|
process.stderr.write(decision.reason);
|
|
@@ -425,4 +513,5 @@ module.exports = {
|
|
|
425
513
|
resolveHarnessFlag,
|
|
426
514
|
resolveRegistryIsolation,
|
|
427
515
|
parseHarnessFlag,
|
|
516
|
+
_setInstallRuntimeMarkerForTests,
|
|
428
517
|
};
|
|
@@ -11,18 +11,61 @@
|
|
|
11
11
|
|
|
12
12
|
const fs = require('fs');
|
|
13
13
|
const path = require('path');
|
|
14
|
-
|
|
15
|
-
//
|
|
16
|
-
//
|
|
17
|
-
//
|
|
18
|
-
//
|
|
19
|
-
//
|
|
20
|
-
//
|
|
21
|
-
//
|
|
22
|
-
//
|
|
23
|
-
//
|
|
24
|
-
|
|
25
|
-
|
|
14
|
+
|
|
15
|
+
// #3582: gsd-core/bin/lib/semver-compare.cjs and package-identity.cjs (and,
|
|
16
|
+
// transitively, check-latest-version.cjs's own gsd-core/bin/lib/cli-exit.cjs
|
|
17
|
+
// + shell-command-projection.cjs) are tsc build artifacts (ADR-457),
|
|
18
|
+
// gitignored and absent on a raw plugin-marketplace / git-clone install that
|
|
19
|
+
// never ran `npm run build:lib`. This worker is a DETACHED SessionStart
|
|
20
|
+
// background process (spawned with stdio: 'ignore') — a build failure here
|
|
21
|
+
// must DEGRADE to the no-signal fallbacks below (mirroring the
|
|
22
|
+
// managed-hooks-registry.cjs degrade just below) so the worker still runs to
|
|
23
|
+
// completion and writes a result cache record, rather than dying silently
|
|
24
|
+
// with no visible signal and no cache-file write at all.
|
|
25
|
+
//
|
|
26
|
+
// This try/require/ensureRuntimeBuild/require/catch shape repeats (with
|
|
27
|
+
// different destructured names) in hooks/gsd-check-update.js and
|
|
28
|
+
// hooks/gsd-update-banner.js. It is deliberately NOT extracted into a shared
|
|
29
|
+
// hooks/lib/ helper: scripts/lint-hooks-runtime-build-seam.cjs enforces this
|
|
30
|
+
// exact seam textually, PER FILE — it greps each hooks/ file for its OWN
|
|
31
|
+
// literal `require('.../ensure-runtime-build.cjs')` + `ensureRuntimeBuild(`
|
|
32
|
+
// call co-occurring with its OWN literal `require('.../gsd-core/bin/lib/*.cjs')`.
|
|
33
|
+
// A generic helper taking the compiled module's path as a variable would move
|
|
34
|
+
// the literal compiled-lib require OUT of this file and into the helper,
|
|
35
|
+
// called with a non-literal argument — the scan's regex (see that script's
|
|
36
|
+
// "Known limitations") cannot see a require() called with a variable, so this
|
|
37
|
+
// file would then read as "requires nothing" and the lint would stop
|
|
38
|
+
// protecting it. A ceremony-only helper (just the ensureRuntimeBuild call,
|
|
39
|
+
// each caller keeping its own literal compiled-lib require) fails the SAME
|
|
40
|
+
// way from the other side: it would remove this file's own literal
|
|
41
|
+
// `require('.../ensure-runtime-build.cjs')` + `ensureRuntimeBuild(` call,
|
|
42
|
+
// which the lint also requires to be textually present in THIS file. Either
|
|
43
|
+
// shape needs the lint script itself widened to special-case the helper,
|
|
44
|
+
// which is a bigger, riskier change than the ~6 duplicated lines it would
|
|
45
|
+
// save; kept inline instead.
|
|
46
|
+
let isSemverNewer = () => false;
|
|
47
|
+
let checkLatestVersion = () => ({ ok: false });
|
|
48
|
+
let PACKAGE_NAME = null;
|
|
49
|
+
try {
|
|
50
|
+
const { ensureRuntimeBuild } = require('../gsd-core/bin/ensure-runtime-build.cjs');
|
|
51
|
+
ensureRuntimeBuild();
|
|
52
|
+
({ isSemverNewer } = require('../gsd-core/bin/lib/semver-compare.cjs'));
|
|
53
|
+
// Latest-version lookup is delegated to the single deterministic adapter
|
|
54
|
+
// (#498). checkLatestVersion() owns the npm-view call, the timeout/semver
|
|
55
|
+
// policy, and the package name — sourced from the baked Package Identity seam.
|
|
56
|
+
// The previous `require('../package.json').name` (#378) never yielded a name in
|
|
57
|
+
// the installed tree — at the time it resolved to the synthetic
|
|
58
|
+
// {"type":"commonjs"} marker GSD wrote at the config root, which has no `.name`,
|
|
59
|
+
// so the background check never reported updates. Since #2544 GSD writes no
|
|
60
|
+
// marker there at all, so that require would now fail to resolve outright.
|
|
61
|
+
// Either way the name must come from the baked seam, never a walk-up.
|
|
62
|
+
({ checkLatestVersion } = require('../gsd-core/bin/check-latest-version.cjs'));
|
|
63
|
+
({ PACKAGE_NAME } = require('../gsd-core/bin/lib/package-identity.cjs'));
|
|
64
|
+
} catch (e) {
|
|
65
|
+
// Runtime library missing/broken and could not self-build — degrade to the
|
|
66
|
+
// no-signal fallbacks declared above; the worker still writes a result
|
|
67
|
+
// cache record (package_name: null, update_available: false).
|
|
68
|
+
}
|
|
26
69
|
// Authoritative list of managed hooks — shared with tests to retire source-grep
|
|
27
70
|
// assertions (pending-migration-to-typed-ir [#455]).
|
|
28
71
|
// NOTE: managed-hooks-registry.cjs must be in HOOKS_TO_COPY (scripts/build-hooks.js)
|
|
@@ -63,7 +106,7 @@ try {
|
|
|
63
106
|
// (e.g., gsd-intel-*.js) must be ignored to avoid permanent stale warnings (#1750)
|
|
64
107
|
// MANAGED_HOOKS is imported from ./managed-hooks-registry.cjs above.
|
|
65
108
|
|
|
66
|
-
|
|
109
|
+
const staleHooks = [];
|
|
67
110
|
if (configDir) {
|
|
68
111
|
// #3023: the bundle's directory name is runtime-descriptor-driven (pi stages
|
|
69
112
|
// it as `gsd-hooks/`), so deriving it as `<configDir>/hooks` silently scanned
|