@opengsd/gsd-core 1.11.0 → 1.13.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/.opencode/plugins/gsd-core.js +12 -0
- package/agents/gsd-code-fixer.md +1 -1
- package/agents/gsd-debug-session-manager.md +1 -1
- package/agents/gsd-debugger.md +1 -1
- package/agents/gsd-dom-verifier.md +169 -0
- package/agents/gsd-eval-auditor.md +1 -1
- package/agents/gsd-executor.md +78 -42
- package/agents/gsd-framework-selector.md +1 -3
- package/agents/gsd-intel-updater.md +1 -1
- package/agents/gsd-mempalace-curator.md +0 -1
- package/agents/gsd-pattern-mapper.md +11 -0
- package/agents/gsd-phase-researcher.md +3 -1
- package/agents/gsd-plan-checker.md +91 -112
- package/agents/gsd-planner.md +20 -4
- package/agents/gsd-project-researcher.md +1 -1
- package/agents/gsd-research-synthesizer.md +2 -2
- package/agents/gsd-roadmapper.md +15 -11
- package/agents/gsd-ui-checker.md +82 -7
- package/agents/gsd-ui-researcher.md +70 -3
- package/agents/gsd-verifier.md +24 -2
- package/bin/install.js +847 -200
- package/commands/gsd/discuss-phase.md +1 -1
- package/commands/gsd/execute-phase.md +1 -1
- package/commands/gsd/import.md +1 -1
- package/commands/gsd/ns-workflow.md +2 -1
- package/commands/gsd/phase.md +1 -1
- package/commands/gsd/quick-batch.md +105 -0
- package/commands/gsd/quick.md +8 -4
- package/commands/gsd/surface.md +18 -8
- package/gsd-core/bin/gsd-tools.cjs +761 -100
- package/gsd-core/bin/lib/active-workstream-store.cjs +8 -0
- package/gsd-core/bin/lib/adr-parser.cjs +13 -7
- package/gsd-core/bin/lib/agent-install-check.cjs +162 -0
- package/gsd-core/bin/lib/api-coverage.cjs +30 -9
- package/gsd-core/bin/lib/artifacts.cjs +2 -0
- package/gsd-core/bin/lib/assumption-delta.cjs +30 -11
- package/gsd-core/bin/lib/audit.cjs +163 -41
- package/gsd-core/bin/lib/broken-windows.cjs +306 -28
- package/gsd-core/bin/lib/capability-activation.cjs +27 -0
- package/gsd-core/bin/lib/capability-lock.cjs +10 -4
- package/gsd-core/bin/lib/capability-registry.cjs +785 -144
- package/gsd-core/bin/lib/capability-state.cjs +25 -4
- package/gsd-core/bin/lib/capability-validator.cjs +321 -18
- package/gsd-core/bin/lib/capability-writer.cjs +14 -4
- package/gsd-core/bin/lib/check-command-router.cjs +229 -6
- package/gsd-core/bin/lib/claude-orchestration.cjs +10 -25
- package/gsd-core/bin/lib/cli-exit.cjs +496 -10
- package/gsd-core/bin/lib/clusters.cjs +1 -0
- package/gsd-core/bin/lib/code-review-depth.cjs +288 -0
- package/gsd-core/bin/lib/codex-agent-toml.cjs +410 -4
- package/gsd-core/bin/lib/command-aliases.cjs +16 -0
- package/gsd-core/bin/lib/command-arg-projection.cjs +144 -14
- package/gsd-core/bin/lib/command-routing-hub.cjs +31 -2
- package/gsd-core/bin/lib/commands.cjs +877 -54
- package/gsd-core/bin/lib/complexity-trigger.cjs +26 -6
- package/gsd-core/bin/lib/config-loader.cjs +121 -29
- package/gsd-core/bin/lib/config.cjs +92 -2
- package/gsd-core/bin/lib/configuration.cjs +129 -37
- package/gsd-core/bin/lib/core-utils.cjs +118 -14
- package/gsd-core/bin/lib/decisions.cjs +213 -1
- package/gsd-core/bin/lib/edge-probe.cjs +23 -2
- package/gsd-core/bin/lib/estimate-cli.cjs +55 -11
- package/gsd-core/bin/lib/exit-code-registry.cjs +98 -0
- package/gsd-core/bin/lib/file-overlap-partitioner.cjs +74 -0
- package/gsd-core/bin/lib/frontmatter.cjs +975 -326
- package/gsd-core/bin/lib/gap-checker.cjs +41 -8
- package/gsd-core/bin/lib/git-base-branch.cjs +182 -39
- package/gsd-core/bin/lib/health-diagnostic-rules/consistency.cjs +7 -3
- package/gsd-core/bin/lib/health-diagnostic-rules/phase-structure.cjs +8 -2
- package/gsd-core/bin/lib/health-diagnostic-rules/roadmap-disk-consistency.cjs +60 -14
- package/gsd-core/bin/lib/health-diagnostic-rules/state-consistency.cjs +75 -22
- package/gsd-core/bin/lib/health-diagnostic-rules/worktree-health.cjs +22 -8
- package/gsd-core/bin/lib/health-diagnostic.cjs +23 -3
- package/gsd-core/bin/lib/host-integration.cjs +96 -11
- package/gsd-core/bin/lib/init-command-router.cjs +132 -21
- package/gsd-core/bin/lib/init.cjs +252 -56
- package/gsd-core/bin/lib/install-engine.cjs +252 -15
- package/gsd-core/bin/lib/install-model-override-resolver.cjs +78 -1
- package/gsd-core/bin/lib/install-profiles.cjs +100 -18
- package/gsd-core/bin/lib/installer-migration-report.cjs +4 -0
- package/gsd-core/bin/lib/installer-migrations/010-antigravity-retire-confighome-artifacts.cjs +169 -0
- package/gsd-core/bin/lib/installer-migrations.cjs +10 -7
- package/gsd-core/bin/lib/intel.cjs +101 -26
- package/gsd-core/bin/lib/io.cjs +195 -15
- package/gsd-core/bin/lib/learnings.cjs +85 -14
- package/gsd-core/bin/lib/legacy-cleanup.cjs +8 -2
- package/gsd-core/bin/lib/loop-resolver.cjs +14 -8
- package/gsd-core/bin/lib/markdown-table.cjs +175 -4
- package/gsd-core/bin/lib/milestone.cjs +112 -7
- package/gsd-core/bin/lib/model-catalog.cjs +177 -19
- package/gsd-core/bin/lib/model-resolver.cjs +10 -28
- package/gsd-core/bin/lib/onboard-projection.cjs +5 -1
- package/gsd-core/bin/lib/phase-command-router.cjs +13 -6
- package/gsd-core/bin/lib/phase-estimation.cjs +17 -8
- package/gsd-core/bin/lib/phase-id.cjs +321 -13
- package/gsd-core/bin/lib/phase-lifecycle.cjs +24 -16
- package/gsd-core/bin/lib/phase-locator.cjs +138 -17
- package/gsd-core/bin/lib/phase.cjs +1175 -115
- package/gsd-core/bin/lib/plan-document.cjs +273 -0
- package/gsd-core/bin/lib/plan-scan.cjs +13 -2
- package/gsd-core/bin/lib/planning-command-router.cjs +61 -0
- package/gsd-core/bin/lib/planning-inspect.cjs +1168 -0
- package/gsd-core/bin/lib/planning-snapshot.cjs +165 -34
- package/gsd-core/bin/lib/planning-workspace.cjs +159 -28
- package/gsd-core/bin/lib/probe-core.cjs +4 -1
- package/gsd-core/bin/lib/profile-pipeline-command-router.cjs +50 -7
- package/gsd-core/bin/lib/profile-pipeline.cjs +6 -3
- package/gsd-core/bin/lib/quick-batch-command-router.cjs +285 -0
- package/gsd-core/bin/lib/quick-batch-dispatch.cjs +250 -0
- package/gsd-core/bin/lib/quick-batch.cjs +840 -0
- package/gsd-core/bin/lib/real-home-guard.cjs +419 -0
- package/gsd-core/bin/lib/refactor-trigger-command-router.cjs +71 -45
- package/gsd-core/bin/lib/review-lane-descriptor.cjs +62 -14
- package/gsd-core/bin/lib/review-lane-invocation.cjs +73 -1
- package/gsd-core/bin/lib/review-lane-runner.cjs +136 -10
- package/gsd-core/bin/lib/roadmap-command-router.cjs +45 -31
- package/gsd-core/bin/lib/roadmap-parser.cjs +577 -41
- package/gsd-core/bin/lib/roadmap.cjs +248 -64
- package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +329 -41
- package/gsd-core/bin/lib/runtime-artifact-install-plan.cjs +16 -17
- package/gsd-core/bin/lib/runtime-artifact-layout.cjs +320 -109
- package/gsd-core/bin/lib/runtime-hooks-surface.cjs +487 -83
- package/gsd-core/bin/lib/runtime-identity.cjs +234 -0
- package/gsd-core/bin/lib/runtime-slash.cjs +72 -2
- package/gsd-core/bin/lib/shell-command-projection.cjs +75 -8
- package/gsd-core/bin/lib/smart-entry.cjs +19 -31
- package/gsd-core/bin/lib/spec-section.cjs +12 -7
- package/gsd-core/bin/lib/state-command-router.cjs +47 -18
- package/gsd-core/bin/lib/state-contract.cjs +359 -0
- package/gsd-core/bin/lib/state-document.cjs +216 -5
- package/gsd-core/bin/lib/state-md-schema.cjs +231 -0
- package/gsd-core/bin/lib/state-transition.cjs +850 -145
- package/gsd-core/bin/lib/state.cjs +1629 -287
- package/gsd-core/bin/lib/surface.cjs +33 -10
- package/gsd-core/bin/lib/task-command-router.cjs +111 -1
- package/gsd-core/bin/lib/task-content-resolution.cjs +368 -0
- package/gsd-core/bin/lib/tdd-red-evidence.cjs +133 -0
- package/gsd-core/bin/lib/teams-status.cjs +4 -1
- package/gsd-core/bin/lib/uat-predicate.cjs +58 -20
- package/gsd-core/bin/lib/uat.cjs +2542 -387
- package/gsd-core/bin/lib/ui-consideration-probe.cjs +9 -1
- package/gsd-core/bin/lib/ui-safety-gate.cjs +37 -7
- package/gsd-core/bin/lib/unusable-input.cjs +13 -0
- package/gsd-core/bin/lib/update-context.cjs +6 -2
- package/gsd-core/bin/lib/validate-command-router.cjs +2 -2
- package/gsd-core/bin/lib/validate.cjs +230 -12
- package/gsd-core/bin/lib/vendor/README.md +43 -5
- package/gsd-core/bin/lib/vendor/js-yaml.cjs +3014 -0
- package/gsd-core/bin/lib/verification-command-router.cjs +2 -1
- package/gsd-core/bin/lib/verification.cjs +287 -13
- package/gsd-core/bin/lib/verify-command-grounding.cjs +846 -0
- package/gsd-core/bin/lib/verify-command-router.cjs +1 -0
- package/gsd-core/bin/lib/verify.cjs +441 -56
- package/gsd-core/bin/lib/workstream-inventory.cjs +20 -2
- package/gsd-core/bin/lib/workstream-name-policy.cjs +25 -4
- package/gsd-core/bin/lib/worktree-base-ref.cjs +66 -12
- package/gsd-core/bin/lib/worktree-safety.cjs +185 -21
- package/gsd-core/bin/shared/config-defaults.manifest.json +7 -1
- package/gsd-core/bin/shared/config-schema.manifest.json +13 -0
- package/gsd-core/bin/shared/exit-codes.json +8 -0
- package/gsd-core/bin/shared/exit-codes.sh +20 -0
- package/gsd-core/bin/shared/model-catalog.json +8 -1
- package/gsd-core/bin/verify-reapply-patches.cjs +70 -3
- package/gsd-core/references/agent-contracts.md +6 -5
- package/gsd-core/references/api-coverage.md +24 -2
- package/gsd-core/references/autonomous-smart-discuss.md +3 -3
- package/gsd-core/references/checkpoints.md +37 -19
- package/gsd-core/references/decimal-phase-calculation.md +5 -5
- package/gsd-core/references/edge-probe.md +17 -5
- package/gsd-core/references/execute-mvp-tdd.md +18 -18
- package/gsd-core/references/execute-phase-between-wave-reset.md +9 -12
- package/gsd-core/references/execute-phase-response-language.md +6 -0
- package/gsd-core/references/execute-phase-wave-guard.md +11 -9
- package/gsd-core/references/executor-examples.md +42 -0
- package/gsd-core/references/failing-direction.md +78 -0
- package/gsd-core/references/few-shot-examples/plan-checker.md +15 -15
- package/gsd-core/references/gate-prompts.md +1 -1
- package/gsd-core/references/git-integration.md +5 -5
- package/gsd-core/references/git-planning-commit.md +3 -3
- package/gsd-core/references/gsd-run-resolver.md +1 -1
- package/gsd-core/references/loop-hook-dispatch.md +22 -0
- package/gsd-core/references/model-profiles.md +1 -1
- package/gsd-core/references/mvp-concepts.md +2 -2
- package/gsd-core/references/nyquist-compliance.md +74 -0
- package/gsd-core/references/offer-next.md +3 -5
- package/gsd-core/references/phase-argument-parsing.md +3 -3
- package/gsd-core/references/plan-checker-examples.md +41 -0
- package/gsd-core/references/planner-antipatterns.md +25 -0
- package/gsd-core/references/planner-chunked.md +5 -1
- package/gsd-core/references/planner-coupling.md +42 -0
- package/gsd-core/references/planner-failing-direction.md +53 -0
- package/gsd-core/references/planner-human-verify-mode.md +15 -1
- package/gsd-core/references/planner-quick-batch.md +71 -0
- package/gsd-core/references/planner-reviews.md +47 -0
- package/gsd-core/references/planner-revision.md +76 -3
- package/gsd-core/references/planner-verify-command-grounding.md +17 -0
- package/gsd-core/references/planning-config.md +39 -9
- package/gsd-core/references/response-language-directive.md +9 -0
- package/gsd-core/references/reviewer-instances.md +31 -0
- package/gsd-core/references/revision-loop.md +118 -11
- package/gsd-core/references/runtime-aware-dispatch.md +1 -1
- package/gsd-core/references/tdd.md +15 -12
- package/gsd-core/references/ui-brand.md +65 -21
- package/gsd-core/references/ui-consideration-probe.md +1 -1
- package/gsd-core/references/universal-anti-patterns.md +2 -2
- package/gsd-core/references/verifier-evidence-gate.md +160 -0
- package/gsd-core/references/verify-command-path-resolvability.md +42 -0
- package/gsd-core/references/verify-mvp-mode.md +1 -1
- package/gsd-core/references/workstream-flag.md +11 -11
- package/gsd-core/templates/README.md +1 -1
- package/gsd-core/templates/SECURITY.md +3 -3
- package/gsd-core/templates/UI-SPEC.md +25 -3
- package/gsd-core/templates/VALIDATION.md +3 -3
- package/gsd-core/templates/phase-prompt.md +7 -0
- package/gsd-core/templates/state.md +7 -0
- package/gsd-core/templates/verification-report.md +5 -0
- package/gsd-core/workflows/_runtime-launcher.snippet.sh +1 -1
- package/gsd-core/workflows/add-backlog.md +3 -1
- package/gsd-core/workflows/add-phase.md +5 -3
- package/gsd-core/workflows/add-tests.md +4 -9
- package/gsd-core/workflows/add-todo.md +2 -2
- package/gsd-core/workflows/ai-integration-phase.md +5 -10
- package/gsd-core/workflows/analyze-dependencies.md +2 -0
- package/gsd-core/workflows/audit-fix.md +14 -3
- package/gsd-core/workflows/audit-milestone.md +11 -9
- package/gsd-core/workflows/audit-uat.md +19 -2
- package/gsd-core/workflows/autonomous/steps/converge-fail-fast.md +2 -2
- package/gsd-core/workflows/autonomous.md +12 -26
- package/gsd-core/workflows/check-todos.md +2 -2
- package/gsd-core/workflows/cleanup.md +3 -3
- package/gsd-core/workflows/code-review/steps/structural-pre-pass.md +16 -14
- package/gsd-core/workflows/code-review-fix.md +3 -1
- package/gsd-core/workflows/code-review.md +192 -69
- package/gsd-core/workflows/complete-milestone.md +28 -14
- package/gsd-core/workflows/debug.md +6 -4
- package/gsd-core/workflows/diagnose-issues.md +17 -7
- package/gsd-core/workflows/discuss-phase/modes/advisor.md +3 -1
- package/gsd-core/workflows/discuss-phase/modes/all.md +2 -0
- package/gsd-core/workflows/discuss-phase/modes/analyze.md +2 -0
- package/gsd-core/workflows/discuss-phase/modes/auto.md +2 -0
- package/gsd-core/workflows/discuss-phase/modes/batch.md +2 -0
- package/gsd-core/workflows/discuss-phase/modes/chain.md +5 -7
- package/gsd-core/workflows/discuss-phase/modes/default.md +2 -0
- package/gsd-core/workflows/discuss-phase/modes/power.md +2 -0
- package/gsd-core/workflows/discuss-phase/modes/text.md +3 -1
- package/gsd-core/workflows/discuss-phase/templates/context.md +2 -0
- package/gsd-core/workflows/discuss-phase/templates/discussion-log.md +2 -0
- package/gsd-core/workflows/discuss-phase-assumptions/steps/auto-advance-dispatch.md +1 -3
- package/gsd-core/workflows/discuss-phase-assumptions.md +3 -3
- package/gsd-core/workflows/discuss-phase-power.md +2 -0
- package/gsd-core/workflows/discuss-phase.md +2 -2
- package/gsd-core/workflows/do.md +46 -19
- package/gsd-core/workflows/docs-update.md +6 -5
- package/gsd-core/workflows/edit-phase.md +3 -1
- package/gsd-core/workflows/eval-review.md +5 -10
- package/gsd-core/workflows/execute-phase/steps/codebase-drift-gate.md +3 -1
- package/gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md +129 -11
- package/gsd-core/workflows/execute-phase/steps/gap-closure-artifacts.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/partial-wave.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/per-plan-executor-routing.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/per-plan-worktree-gate.md +29 -5
- package/gsd-core/workflows/execute-phase/steps/post-merge-gate.md +2 -2
- package/gsd-core/workflows/execute-phase/steps/protected-branch.md +21 -0
- package/gsd-core/workflows/execute-phase/steps/regression-gate-run.md +4 -2
- package/gsd-core/workflows/execute-phase/steps/tdd-applicability-resolution.md +25 -0
- package/gsd-core/workflows/execute-phase/steps/wave-post-gate-hooks.md +39 -0
- package/gsd-core/workflows/execute-phase/steps/worktree-recovery-policy.md +2 -0
- package/gsd-core/workflows/execute-phase.md +68 -66
- package/gsd-core/workflows/execute-plan.md +25 -20
- package/gsd-core/workflows/explore.md +3 -1
- package/gsd-core/workflows/extract-learnings.md +3 -1
- package/gsd-core/workflows/fast.md +8 -2
- package/gsd-core/workflows/forensics.md +3 -1
- package/gsd-core/workflows/graduation.md +6 -6
- package/gsd-core/workflows/health.md +4 -7
- package/gsd-core/workflows/help/modes/brief.md +2 -0
- package/gsd-core/workflows/help/modes/default.md +2 -0
- package/gsd-core/workflows/help/modes/full.md +12 -0
- package/gsd-core/workflows/help/modes/topic.md +2 -0
- package/gsd-core/workflows/help.md +2 -0
- package/gsd-core/workflows/import.md +17 -14
- package/gsd-core/workflows/inbox.md +5 -6
- package/gsd-core/workflows/ingest-docs.md +45 -12
- package/gsd-core/workflows/insert-phase.md +7 -5
- package/gsd-core/workflows/list-phase-assumptions.md +2 -0
- package/gsd-core/workflows/list-seeds.md +7 -3
- package/gsd-core/workflows/list-workspaces.md +3 -1
- package/gsd-core/workflows/manager.md +15 -26
- package/gsd-core/workflows/map-codebase.md +3 -1
- package/gsd-core/workflows/milestone-summary.md +3 -1
- package/gsd-core/workflows/mvp-phase.md +3 -3
- package/gsd-core/workflows/new-milestone.md +10 -22
- package/gsd-core/workflows/new-project/steps/auto-mode-config.md +1 -1
- package/gsd-core/workflows/new-project.md +17 -29
- package/gsd-core/workflows/new-workspace.md +2 -2
- package/gsd-core/workflows/next.md +4 -2
- package/gsd-core/workflows/node-repair.md +2 -0
- package/gsd-core/workflows/note.md +2 -0
- package/gsd-core/workflows/onboard.md +1 -1
- package/gsd-core/workflows/pause-work.md +20 -5
- package/gsd-core/workflows/plan-phase/steps/adr-ingest-express-path.md +1 -1
- package/gsd-core/workflows/plan-phase/steps/chunked-planning-mode.md +100 -18
- package/gsd-core/workflows/plan-phase/steps/prd-express-path.md +4 -4
- package/gsd-core/workflows/plan-phase/steps/stall-detection-helpers.md +12 -3
- package/gsd-core/workflows/plan-phase.md +251 -54
- package/gsd-core/workflows/plan-review-convergence.md +148 -19
- package/gsd-core/workflows/plant-seed.md +3 -3
- package/gsd-core/workflows/pr-branch.md +195 -51
- package/gsd-core/workflows/profile-user.md +17 -15
- package/gsd-core/workflows/progress/steps/forensic-audit.md +1 -1
- package/gsd-core/workflows/progress.md +52 -15
- package/gsd-core/workflows/quick/steps/discussion-phase.md +1 -3
- package/gsd-core/workflows/quick/steps/plan-checker-loop.md +38 -5
- package/gsd-core/workflows/quick/steps/quick-verification.md +2 -4
- package/gsd-core/workflows/quick/steps/research-phase.md +5 -7
- package/gsd-core/workflows/quick/steps/worktree-pre-dispatch-commit.md +3 -3
- package/gsd-core/workflows/quick-batch/steps/batch-init.md +55 -0
- package/gsd-core/workflows/quick-batch/steps/completion.md +65 -0
- package/gsd-core/workflows/quick-batch/steps/merge-wave.md +100 -0
- package/gsd-core/workflows/quick-batch/steps/plan-checker-loop.md +147 -0
- package/gsd-core/workflows/quick-batch/steps/planner-wave.md +158 -0
- package/gsd-core/workflows/quick-batch/steps/research-phase.md +95 -0
- package/gsd-core/workflows/quick-batch/steps/resume-mode.md +49 -0
- package/gsd-core/workflows/quick-batch/steps/verification-wave.md +73 -0
- package/gsd-core/workflows/quick-batch/steps/worktree-dispatch.md +169 -0
- package/gsd-core/workflows/quick-batch.md +203 -0
- package/gsd-core/workflows/quick.md +33 -32
- package/gsd-core/workflows/reapply-patches.md +2 -0
- package/gsd-core/workflows/remove-phase.md +6 -4
- package/gsd-core/workflows/remove-workspace.md +3 -3
- package/gsd-core/workflows/resume-project.md +14 -14
- package/gsd-core/workflows/review.md +404 -21
- package/gsd-core/workflows/scan.md +3 -1
- package/gsd-core/workflows/section-manifest.json +12 -0
- package/gsd-core/workflows/secure-phase.md +3 -3
- package/gsd-core/workflows/session-report.md +2 -0
- package/gsd-core/workflows/settings-advanced.md +9 -9
- package/gsd-core/workflows/settings-integrations.md +66 -32
- package/gsd-core/workflows/settings.md +4 -6
- package/gsd-core/workflows/ship.md +22 -16
- package/gsd-core/workflows/sketch-wrap-up.md +13 -17
- package/gsd-core/workflows/sketch.md +13 -19
- package/gsd-core/workflows/smart-entry.md +4 -6
- package/gsd-core/workflows/spec-phase.md +31 -4
- package/gsd-core/workflows/spike-wrap-up.md +9 -11
- package/gsd-core/workflows/spike.md +21 -32
- package/gsd-core/workflows/stats.md +4 -2
- package/gsd-core/workflows/sync-skills.md +13 -5
- package/gsd-core/workflows/thread.md +13 -7
- package/gsd-core/workflows/transition.md +7 -5
- package/gsd-core/workflows/ui-phase.md +36 -21
- package/gsd-core/workflows/ui-review.md +7 -11
- package/gsd-core/workflows/ultraplan-phase.md +7 -13
- package/gsd-core/workflows/undo.md +9 -17
- package/gsd-core/workflows/update.md +47 -48
- package/gsd-core/workflows/validate-phase.md +3 -3
- package/gsd-core/workflows/verify-work/steps/automated-ui-verification.md +25 -1
- package/gsd-core/workflows/verify-work/steps/mvp-uat-framing.md +1 -1
- package/gsd-core/workflows/verify-work.md +106 -21
- package/hooks/dist/gsd-agent-isolation-guard.js +77 -38
- package/hooks/dist/gsd-check-update-worker.js +19 -2
- package/hooks/dist/gsd-config-reload.js +18 -12
- package/hooks/dist/gsd-context-monitor.js +302 -22
- package/hooks/dist/gsd-cursor-post-tool.js +3 -1
- package/hooks/dist/gsd-cursor-pre-tool.js +3 -1
- package/hooks/dist/gsd-cursor-session-start.js +2 -1
- package/hooks/dist/gsd-cursor-stop.js +2 -1
- package/hooks/dist/gsd-cursor-subagent-start.js +28 -23
- package/hooks/dist/gsd-cursor-subagent-stop.js +3 -1
- package/hooks/dist/gsd-ensure-canonical-path.js +2 -1
- package/hooks/dist/gsd-graphify-update.sh +22 -18
- package/hooks/dist/gsd-node-runner.sh +77 -0
- package/hooks/dist/gsd-phase-boundary.sh +1 -0
- package/hooks/dist/gsd-prompt-guard.js +46 -12
- package/hooks/dist/gsd-read-guard.js +18 -7
- package/hooks/dist/gsd-read-injection-scanner.js +22 -13
- package/hooks/dist/gsd-secret-read-guard.js +1079 -0
- package/hooks/dist/gsd-session-state.sh +1 -0
- package/hooks/dist/gsd-statusline.js +222 -29
- package/hooks/dist/gsd-validate-commit.sh +523 -12
- package/hooks/dist/gsd-windsurf-pre-command.js +16 -11
- package/hooks/dist/gsd-windsurf-pre-write.js +22 -13
- package/hooks/dist/gsd-workflow-guard.js +36 -17
- package/hooks/dist/gsd-worktree-path-guard.js +36 -21
- package/hooks/dist/gsd-write-guard.js +35 -25
- package/hooks/dist/lib/cli-exit.js +560 -0
- package/hooks/dist/lib/exit-code-registry.js +98 -0
- package/hooks/dist/lib/git-cmd.js +210 -1
- package/hooks/dist/lib/git-probe.js +84 -0
- package/hooks/dist/lib/hook-exit.js +81 -0
- package/hooks/dist/lib/injection-patterns.js +36 -6
- package/hooks/dist/managed-hooks-registry.cjs +4 -0
- package/hooks/gsd-agent-isolation-guard.js +77 -38
- package/hooks/gsd-check-update-worker.js +19 -2
- package/hooks/gsd-config-reload.js +18 -12
- package/hooks/gsd-context-monitor.js +302 -22
- package/hooks/gsd-cursor-post-tool.js +3 -1
- package/hooks/gsd-cursor-pre-tool.js +3 -1
- package/hooks/gsd-cursor-session-start.js +2 -1
- package/hooks/gsd-cursor-stop.js +2 -1
- package/hooks/gsd-cursor-subagent-start.js +28 -23
- package/hooks/gsd-cursor-subagent-stop.js +3 -1
- package/hooks/gsd-ensure-canonical-path.js +2 -1
- package/hooks/gsd-graphify-update.sh +22 -18
- package/hooks/gsd-node-runner.sh +77 -0
- package/hooks/gsd-phase-boundary.sh +1 -0
- package/hooks/gsd-prompt-guard.js +46 -12
- package/hooks/gsd-read-guard.js +18 -7
- package/hooks/gsd-read-injection-scanner.js +22 -13
- package/hooks/gsd-secret-read-guard.js +1079 -0
- package/hooks/gsd-session-state.sh +1 -0
- package/hooks/gsd-statusline.js +222 -29
- package/hooks/gsd-validate-commit.sh +523 -12
- package/hooks/gsd-windsurf-pre-command.js +16 -11
- package/hooks/gsd-windsurf-pre-write.js +22 -13
- package/hooks/gsd-workflow-guard.js +36 -17
- package/hooks/gsd-worktree-path-guard.js +36 -21
- package/hooks/gsd-write-guard.js +35 -25
- package/hooks/hooks.json +6 -0
- package/hooks/lib/cli-exit.js +560 -0
- package/hooks/lib/exit-code-registry.js +98 -0
- package/hooks/lib/git-cmd.js +210 -1
- package/hooks/lib/git-probe.js +84 -0
- package/hooks/lib/hook-exit.js +81 -0
- package/hooks/lib/injection-patterns.js +36 -6
- package/hooks/managed-hooks-registry.cjs +4 -0
- package/package.json +14 -9
- package/scripts/base64-scan.sh +74 -12
- package/scripts/build-hooks.js +12 -0
- package/scripts/check-glossary-refs.cjs +77 -15
- package/scripts/check-mutation-score-ratchet.cjs +156 -0
- package/scripts/ci-check-job-near-cap.cjs +49 -0
- package/scripts/ci-pr-mergeability.cjs +262 -0
- package/scripts/ci-test-scope.cjs +52 -12
- package/scripts/ci-timeout-report.cjs +230 -0
- package/scripts/docs-guard-registry.cjs +406 -0
- package/scripts/gen-capability-registry.cjs +8 -6
- package/scripts/gen-exit-code-docs.cjs +318 -0
- package/scripts/gen-exit-code-registry.cjs +891 -0
- package/scripts/gen-features.cjs +836 -0
- package/scripts/gen-hooks-cli-exit.cjs +239 -0
- package/scripts/gen-install-tree-fixtures.cjs +2 -2
- package/scripts/gen-loop-host-contract.cjs +189 -4
- package/scripts/gen-scripts-cli-exit.cjs +185 -0
- package/scripts/gen-state-md-docs.cjs +727 -0
- package/scripts/{test-failure-reasons.cjs → gsd-test-gate-reasons.cjs} +6 -0
- package/scripts/lib/ci-job-timing.cjs +72 -0
- package/scripts/lib/cli-exit.cjs +546 -44
- package/scripts/lib/drift-scan.cjs +32 -2
- package/scripts/lib/exit-code-registry.cjs +98 -0
- package/scripts/lib/ndjson-reporter.cjs +119 -0
- package/scripts/lib/shellcheck-fetch.cjs +247 -0
- package/scripts/lint-allow-test-rule-refs.allowlist.json +0 -6
- package/scripts/lint-allow-test-rule-refs.effective-ceiling.json +1 -1
- package/scripts/lint-allow-test-rule-refs.unverified-ceiling.json +1 -1
- package/scripts/lint-docs-guard-registration.cjs +495 -0
- package/scripts/lint-docs-guard-registration.exempt-baseline.cjs +198 -0
- package/scripts/lint-eslint-glob-coverage.allowlist.json +4 -0
- package/scripts/{lint-fix-has-regression-test.cjs → lint-fix-has-regression-tests.cjs} +12 -6
- package/scripts/lint-health-diagnostic-rule-table.cjs +65 -8
- package/scripts/lint-mutation-test-derivation-drift.cjs +86 -0
- package/scripts/lint-phase-enumeration-drift.cjs +45 -14
- package/scripts/lint-phase-id-drift.cjs +133 -8
- package/scripts/lint-planning-prompt-drift.cjs +38 -1
- package/scripts/lint-portable-grep.cjs +176 -0
- package/scripts/lint-removed-but-needed.cjs +184 -16
- package/scripts/lint-response-language-coverage.cjs +524 -0
- package/scripts/lint-seam-enforcement.cjs +182 -0
- package/scripts/lint-slug-derivation-drift.cjs +921 -0
- package/scripts/lint-source-test-name-collision.cjs +241 -0
- package/scripts/lint-state-write-path-drift.cjs +337 -432
- package/scripts/lint-test-file-count.allowlist.json +124 -4
- package/scripts/lint-test-file-count.cjs +25 -3
- package/scripts/lint-unreachable-guard-drift.cjs +51 -64
- package/scripts/lint-vendored-deps.cjs +208 -35
- package/scripts/lint-workflow-shellcheck-baseline.json +1027 -0
- package/scripts/lint-workflow-shellcheck.cjs +614 -0
- package/scripts/mutation-matrix.cjs +599 -50
- package/scripts/npm-audit-baseline.cjs +376 -0
- package/scripts/prompt-injection-scan.sh +83 -14
- package/scripts/require-issue-link-policy.cjs +16 -1
- package/scripts/secret-scan.sh +75 -13
- package/scripts/select-docs-guards.cjs +56 -0
- package/scripts/sync-runtime-launcher.cjs +22 -3
- package/skills/gsd-discuss-phase/SKILL.md +1 -1
- package/skills/gsd-execute-phase/SKILL.md +1 -1
- package/skills/gsd-import/SKILL.md +1 -1
- package/skills/gsd-ns-workflow/SKILL.md +1 -0
- package/skills/gsd-phase/SKILL.md +1 -1
- package/skills/gsd-quick/SKILL.md +8 -4
- package/skills/gsd-quick-batch/SKILL.md +105 -0
- package/skills/gsd-surface/SKILL.md +18 -8
- package/vscode/package.json +1 -1
- package/bin/lib/ui-safety-gate.cjs +0 -109
- package/scripts/lint-emitted-drift-ack.cjs +0 -344
- package/scripts/state-write-path-drift-baseline.json +0 -19
|
@@ -39,13 +39,13 @@ const ioMod = require("./io.cjs");
|
|
|
39
39
|
const { output: coreOutput, error: coreError } = ioMod;
|
|
40
40
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
41
41
|
const activationMod = require("./capability-activation.cjs");
|
|
42
|
-
const { _resolveActivationValue } = activationMod;
|
|
42
|
+
const { _resolveActivationValue, _resolvePointGate } = activationMod;
|
|
43
43
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
44
44
|
const configLoaderMod = require("./config-loader.cjs");
|
|
45
45
|
const { loadConfig } = configLoaderMod;
|
|
46
46
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
47
47
|
const installProfilesMod = require("./install-profiles.cjs");
|
|
48
|
-
const { readActiveProfile, loadSkillsManifest, resolveProfile, parseRequires, parseCallsAgents } = installProfilesMod;
|
|
48
|
+
const { readActiveProfile, loadSkillsManifest, resolveProfile, parseRequires, parseCallsAgents, workflowAgentRefs } = installProfilesMod;
|
|
49
49
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
50
50
|
const surfaceMod = require("./surface.cjs");
|
|
51
51
|
const { resolveSurface } = surfaceMod;
|
|
@@ -179,6 +179,12 @@ function resolveCapabilityState(input) {
|
|
|
179
179
|
// (mirrors loop-resolver.isActive: `typeof when !== 'string' || when.length === 0` → false)
|
|
180
180
|
configured = false;
|
|
181
181
|
}
|
|
182
|
+
// #3661: optional point-selection gate, ANDed in — mirrors loop-resolver.isActive
|
|
183
|
+
// EXACTLY (see capability-activation.cts's _resolvePointGate doc comment; this
|
|
184
|
+
// parity is load-bearing, see tests/capability-precedence-parity.test.cjs).
|
|
185
|
+
if (configured) {
|
|
186
|
+
configured = _resolvePointGate(h['pointFrom'], point, config, cwd, registry);
|
|
187
|
+
}
|
|
182
188
|
// Hook active = capability-level active AND hook's own config gate.
|
|
183
189
|
// The capability's `active` constant (= enabled && configActivation) is
|
|
184
190
|
// used here so that a config-disabled capability (active=false) cannot
|
|
@@ -299,7 +305,17 @@ function _loadInstalledSkillsManifest(configDir) {
|
|
|
299
305
|
* no gsd-*.md files (so _resolveManifest can use size>0 as the "flat layout
|
|
300
306
|
* present" signal and fall through to the installed-skills branch otherwise).
|
|
301
307
|
*/
|
|
302
|
-
function _loadFlatCommandsGsdManifest(commandsParentDir) {
|
|
308
|
+
function _loadFlatCommandsGsdManifest(commandsParentDir, workflowsDir) {
|
|
309
|
+
// #3798: derive agents from the command body PLUS the workflow files it
|
|
310
|
+
// references, exactly like loadSkillsManifest does for the nested layout —
|
|
311
|
+
// the flat (#1858) layout retained the original defect otherwise, and the
|
|
312
|
+
// parity test in tests/capability-state.test.cjs asserts the two loaders
|
|
313
|
+
// produce identical _calls_agents_* sets. Default: <pkg>/gsd-core/workflows
|
|
314
|
+
// one level above the commands dir — the shape of both the repo checkout
|
|
315
|
+
// (<repo>/commands/) and a flat runtime install (the runtime config dir's
|
|
316
|
+
// commands/ with the package's gsd-core/ beside it).
|
|
317
|
+
const flatWorkflowsDir = workflowsDir
|
|
318
|
+
|| node_path_1.default.resolve(commandsParentDir, '..', 'gsd-core', 'workflows');
|
|
303
319
|
const manifest = new Map();
|
|
304
320
|
let entries;
|
|
305
321
|
try {
|
|
@@ -327,7 +343,12 @@ function _loadFlatCommandsGsdManifest(commandsParentDir) {
|
|
|
327
343
|
try {
|
|
328
344
|
const content = node_fs_1.default.readFileSync(node_path_1.default.join(commandsParentDir, entry.name), 'utf8');
|
|
329
345
|
manifest.set(stem, parseRequires(content));
|
|
330
|
-
manifest.set(`_calls_agents_${stem}`,
|
|
346
|
+
manifest.set(`_calls_agents_${stem}`, [
|
|
347
|
+
...new Set([
|
|
348
|
+
...parseCallsAgents(content),
|
|
349
|
+
...workflowAgentRefs(content, flatWorkflowsDir),
|
|
350
|
+
]),
|
|
351
|
+
]);
|
|
331
352
|
}
|
|
332
353
|
catch {
|
|
333
354
|
manifest.set(stem, []);
|
|
@@ -396,6 +396,11 @@ function validateCapability(cap, folderId) {
|
|
|
396
396
|
errors.push(...validateRuntimeBody(cap));
|
|
397
397
|
// A host that is ALSO a reviewer keeps exactly one manifest (ADR-2782 D1).
|
|
398
398
|
errors.push(...validateReviewerBody(cap));
|
|
399
|
+
// ADR-3646: a runtime capability installs a host CLI, it does not resolve
|
|
400
|
+
// task content — taskContentResolver is feature-only.
|
|
401
|
+
if (cap.taskContentResolver !== undefined) {
|
|
402
|
+
errors.push('role:runtime capability must not have a "taskContentResolver" body (feature-only field)');
|
|
403
|
+
}
|
|
399
404
|
} else if (cap.role === 'reviewer') {
|
|
400
405
|
// ADR-2782 D3 — a lane that is not an install target. No runtime body, no
|
|
401
406
|
// install surface, no runtimeCompat (it surfaces through no host runtime).
|
|
@@ -718,6 +723,118 @@ function validateFeatureBody(cap) {
|
|
|
718
723
|
}
|
|
719
724
|
}
|
|
720
725
|
|
|
726
|
+
// ADR-3646: optional per-task external-tracker content-resolution seam.
|
|
727
|
+
errors.push(...validateTaskContentResolver(cap));
|
|
728
|
+
|
|
729
|
+
return errors;
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
/**
|
|
733
|
+
* ADR-3646 — validate an OPTIONAL `taskContentResolver` body on a `role:
|
|
734
|
+
* "feature"` capability. Absence is never an error (most manifests won't
|
|
735
|
+
* have one); presence is strictly validated.
|
|
736
|
+
*
|
|
737
|
+
* Wrapped in try/catch to degrade any unexpected throw (a hostile Proxy, a
|
|
738
|
+
* throwing getter, etc.) to a single validation error rather than crashing
|
|
739
|
+
* every consumer of loadRegistry, per the #1461 OVL-1 discipline that
|
|
740
|
+
* `validateReviewerBody` follows.
|
|
741
|
+
*
|
|
742
|
+
* @param {object} cap The parsed capability manifest.
|
|
743
|
+
* @returns {string[]} Array of error strings; empty = valid or absent.
|
|
744
|
+
*/
|
|
745
|
+
function validateTaskContentResolver(cap) {
|
|
746
|
+
try {
|
|
747
|
+
return validateTaskContentResolverFields(cap);
|
|
748
|
+
} catch (err) {
|
|
749
|
+
return ['capability taskContentResolver body could not be validated: ' + safeErrorMessage(err)];
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
/**
|
|
754
|
+
* Upper bound for `taskContentResolver.invoke.timeoutMs`, specific to this
|
|
755
|
+
* field only. `isPositiveIntegerMs()` has no ceiling and stays that way — it
|
|
756
|
+
* is shared with the reviewer lane's `timeoutFloorMs` and probe `timeoutMs`,
|
|
757
|
+
* which may legitimately need a longer or unbounded value. Without a ceiling
|
|
758
|
+
* here, a manifest could declare `Number.MAX_SAFE_INTEGER` and let
|
|
759
|
+
* `resolve-content` hang near-indefinitely on a stuck/malicious resolver,
|
|
760
|
+
* defeating the feature's "bounded subprocess" design intent.
|
|
761
|
+
*/
|
|
762
|
+
const TASK_CONTENT_RESOLVER_TIMEOUT_CEILING_MS = 120000;
|
|
763
|
+
|
|
764
|
+
function validateTaskContentResolverFields(cap) {
|
|
765
|
+
const errors = [];
|
|
766
|
+
if (typeof cap !== 'object' || cap === null || Array.isArray(cap)) return errors;
|
|
767
|
+
|
|
768
|
+
const tcr = cap.taskContentResolver;
|
|
769
|
+
if (tcr === undefined) return errors; // optional — never an error to omit
|
|
770
|
+
|
|
771
|
+
const ctx = 'capability "' + (typeof cap.id === 'string' ? cap.id : '(unknown)') + '"';
|
|
772
|
+
|
|
773
|
+
if (typeof tcr !== 'object' || tcr === null || Array.isArray(tcr)) {
|
|
774
|
+
const got = tcr === null ? 'null' : Array.isArray(tcr) ? 'array' : typeof tcr;
|
|
775
|
+
errors.push(
|
|
776
|
+
ctx + ' taskContentResolver must be an object (got: ' + got + '). ' +
|
|
777
|
+
'Omit the key entirely to declare no resolver — an explicit null is not an omission.',
|
|
778
|
+
);
|
|
779
|
+
return errors; // cannot validate fields of a non-object
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
// ── trackerPrefix — same grammar as a capability id ─────────────────────
|
|
783
|
+
if (typeof tcr.trackerPrefix !== 'string' || tcr.trackerPrefix.length === 0 || !KEBAB_RE.test(tcr.trackerPrefix)) {
|
|
784
|
+
errors.push(
|
|
785
|
+
ctx + ' taskContentResolver.trackerPrefix must be a non-empty kebab-case string matching ' +
|
|
786
|
+
String(KEBAB_RE) + ' (got: ' + describeValue(tcr.trackerPrefix) + ')',
|
|
787
|
+
);
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
// ── invoke ────────────────────────────────────────────────────────────
|
|
791
|
+
const inv = tcr.invoke;
|
|
792
|
+
if (typeof inv !== 'object' || inv === null || Array.isArray(inv)) {
|
|
793
|
+
const got = inv === null ? 'null' : Array.isArray(inv) ? 'array' : typeof inv;
|
|
794
|
+
errors.push(ctx + ' taskContentResolver.invoke must be an object (got: ' + got + ')');
|
|
795
|
+
return errors; // cannot validate sub-fields of a non-object
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
if (typeof inv.binary !== 'string' || inv.binary.length === 0) {
|
|
799
|
+
errors.push(ctx + ' taskContentResolver.invoke.binary must be a non-empty string');
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
if (!Array.isArray(inv.args)) {
|
|
803
|
+
errors.push(ctx + ' taskContentResolver.invoke.args must be an array of strings');
|
|
804
|
+
} else {
|
|
805
|
+
let hasPlaceholder = false;
|
|
806
|
+
for (const a of inv.args) {
|
|
807
|
+
if (typeof a !== 'string') {
|
|
808
|
+
errors.push(ctx + ' taskContentResolver.invoke.args entries must be strings (got: ' + describeValue(a) + ')');
|
|
809
|
+
} else if (a === '{{id}}') {
|
|
810
|
+
hasPlaceholder = true;
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
if (!hasPlaceholder) {
|
|
814
|
+
errors.push(
|
|
815
|
+
ctx + ' taskContentResolver.invoke.args must contain a "{{id}}" placeholder — ' +
|
|
816
|
+
'without it the resolved tracker id could never reach the resolver subprocess',
|
|
817
|
+
);
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
if (!isPositiveIntegerMs(inv.timeoutMs)) {
|
|
822
|
+
errors.push(
|
|
823
|
+
ctx + ' taskContentResolver.invoke.timeoutMs must be a positive integer of milliseconds — ' +
|
|
824
|
+
'an unbounded resolver call could hang task execution indefinitely ' +
|
|
825
|
+
'(got: ' + describeValue(inv.timeoutMs) + ')',
|
|
826
|
+
);
|
|
827
|
+
} else if (inv.timeoutMs > TASK_CONTENT_RESOLVER_TIMEOUT_CEILING_MS) {
|
|
828
|
+
// Ceiling specific to this field — `isPositiveIntegerMs()` itself stays
|
|
829
|
+
// unbounded because it is shared with the reviewer lane's
|
|
830
|
+
// `timeoutFloorMs`/probe `timeoutMs`, which have no such ceiling.
|
|
831
|
+
errors.push(
|
|
832
|
+
ctx + ' taskContentResolver.invoke.timeoutMs must not exceed ' +
|
|
833
|
+
TASK_CONTENT_RESOLVER_TIMEOUT_CEILING_MS + 'ms — an unbounded-in-practice value defeats the ' +
|
|
834
|
+
'"bounded subprocess" design intent (got: ' + inv.timeoutMs + ')',
|
|
835
|
+
);
|
|
836
|
+
}
|
|
837
|
+
|
|
721
838
|
return errors;
|
|
722
839
|
}
|
|
723
840
|
|
|
@@ -943,7 +1060,7 @@ const HTTP_ONLY_INVOKE_FIELDS = ['hostConfigKey', 'defaultHost', 'path', 'model
|
|
|
943
1060
|
|
|
944
1061
|
// Feature-only fields are as forbidden on a lane-only capability as on a runtime
|
|
945
1062
|
// one; a `role: "reviewer"` capability owns no artefacts and wires no loop point.
|
|
946
|
-
const FEATURE_FIELDS_FORBIDDEN_ON_REVIEWER = ['skills', 'agents', 'steps', 'contributions', 'gates', 'hooks', 'activationKey'];
|
|
1063
|
+
const FEATURE_FIELDS_FORBIDDEN_ON_REVIEWER = ['skills', 'agents', 'steps', 'contributions', 'gates', 'hooks', 'activationKey', 'taskContentResolver'];
|
|
947
1064
|
|
|
948
1065
|
// GATE A: installSurface → allowed hooksSurface values (DEFECT.GENERATIVE-FIX: parity invariant)
|
|
949
1066
|
// Derived from the actual pairings in the 16 real runtime descriptors.
|
|
@@ -1526,6 +1643,28 @@ function validateRuntimeBody(cap) {
|
|
|
1526
1643
|
' (or "undocumented") (got: ' + JSON.stringify(d.isolation) + ')',
|
|
1527
1644
|
);
|
|
1528
1645
|
}
|
|
1646
|
+
|
|
1647
|
+
// maxConcurrency — ADR-1239 Phase 1 (#3673). A numeric dispatch
|
|
1648
|
+
// sub-field (not a closed-vocabulary enum member — same numeric
|
|
1649
|
+
// dispatch sub-field treatment as maxDepth above; unlike maxDepth,
|
|
1650
|
+
// 0 and negative values are invalid — 1 is the fail-closed floor, not
|
|
1651
|
+
// a legitimate "no concurrency" declaration).
|
|
1652
|
+
// OPTIONAL, like isolation: added after existing descriptors, so an
|
|
1653
|
+
// omitted maxConcurrency is legitimate — negotiateHostCapabilities
|
|
1654
|
+
// degrades it to 1 (the safe floor) and warns, exactly as for any
|
|
1655
|
+
// other undeclared dispatch sub-field. Only a PRESENT value is
|
|
1656
|
+
// checked against the positive-safe-integer contract.
|
|
1657
|
+
if (d.maxConcurrency === undefined) {
|
|
1658
|
+
// absent — nothing to validate; negotiateHostCapabilities fails it closed.
|
|
1659
|
+
} else if (
|
|
1660
|
+
d.maxConcurrency !== 'undocumented' &&
|
|
1661
|
+
(!Number.isSafeInteger(d.maxConcurrency) || d.maxConcurrency < 1)
|
|
1662
|
+
) {
|
|
1663
|
+
errors.push(
|
|
1664
|
+
'runtime.hostIntegration.dispatch.maxConcurrency must be a positive safe integer or "undocumented" ' +
|
|
1665
|
+
'(got: ' + JSON.stringify(d.maxConcurrency) + ')',
|
|
1666
|
+
);
|
|
1667
|
+
}
|
|
1529
1668
|
}
|
|
1530
1669
|
}
|
|
1531
1670
|
|
|
@@ -1588,6 +1727,16 @@ function validateRuntimeBody(cap) {
|
|
|
1588
1727
|
'runtime.orchestratorExec.promptFlag must be a string or null (got: ' + JSON.stringify(oe.promptFlag) + ')',
|
|
1589
1728
|
);
|
|
1590
1729
|
}
|
|
1730
|
+
|
|
1731
|
+
// modelFlag — optional; string or null (#3714). `null`/absent means the
|
|
1732
|
+
// host offers no per-invocation model override on this exec path; a
|
|
1733
|
+
// string names the flag that pins the executor's model (codex: --model).
|
|
1734
|
+
// Deliberately asymmetric: only codex declares this today.
|
|
1735
|
+
if (oe.modelFlag !== undefined && oe.modelFlag !== null && typeof oe.modelFlag !== 'string') {
|
|
1736
|
+
errors.push(
|
|
1737
|
+
'runtime.orchestratorExec.modelFlag must be a string or null (got: ' + JSON.stringify(oe.modelFlag) + ')',
|
|
1738
|
+
);
|
|
1739
|
+
}
|
|
1591
1740
|
}
|
|
1592
1741
|
}
|
|
1593
1742
|
|
|
@@ -1697,9 +1846,29 @@ const KNOWN_REVIEWER_FIELDS = new Set([
|
|
|
1697
1846
|
// missed a configured model and silently disabled the pinned-model escape hatch
|
|
1698
1847
|
// #2073 added. A convention one shipped lane already violates is not a contract.
|
|
1699
1848
|
'modelConfigKey',
|
|
1849
|
+
// `timeoutConfigKey` added by #3274, same optional/backward-compatible shape as
|
|
1850
|
+
// `modelConfigKey` above: a manifest authored before this field existed must keep validating.
|
|
1851
|
+
'timeoutConfigKey',
|
|
1852
|
+
// `effortConfigKey` / `defaultEffort` added by #4255, same optional/backward-compatible shape
|
|
1853
|
+
// as the two above. Lane effort used to be resolved by querying the `gsd-plan-checker` AGENT
|
|
1854
|
+
// through a hardcoded id, so every prompt-fed lane ran at that verifier's frontmatter effort;
|
|
1855
|
+
// it is a property of the review, so the lane declares it. A manifest authored before these
|
|
1856
|
+
// fields existed must keep validating — absent is read as "this lane declares no review
|
|
1857
|
+
// effort", which emits no effort argument at all.
|
|
1858
|
+
'effortConfigKey',
|
|
1859
|
+
'defaultEffort',
|
|
1700
1860
|
'handler',
|
|
1701
1861
|
]);
|
|
1702
1862
|
|
|
1863
|
+
/**
|
|
1864
|
+
* The effort levels a lane may declare as its review default (#4255, #3533 vocabulary).
|
|
1865
|
+
*
|
|
1866
|
+
* `inherit` is deliberately NOT a member. It is a legitimate CONFIGURED value — it selects "emit
|
|
1867
|
+
* no argument, the CLI's own configuration decides" — but as a DECLARED default it would be a
|
|
1868
|
+
* second spelling of `null` and split one behaviour across two shapes.
|
|
1869
|
+
*/
|
|
1870
|
+
const REVIEWER_EFFORT_LEVELS = new Set(['minimal', 'low', 'medium', 'high', 'xhigh', 'max']);
|
|
1871
|
+
|
|
1703
1872
|
/**
|
|
1704
1873
|
* The closed `runtime.hostBehaviors` vocabulary (ADR-1016, closed via #2801).
|
|
1705
1874
|
*
|
|
@@ -2244,6 +2413,50 @@ function validateReviewerBodyFields(cap) {
|
|
|
2244
2413
|
);
|
|
2245
2414
|
}
|
|
2246
2415
|
|
|
2416
|
+
// OPTIONAL, mirroring modelConfigKey's D4 forward/backward-compat treatment (#3274): a manifest
|
|
2417
|
+
// authored before this field existed must keep validating. Absent/null means "no override for
|
|
2418
|
+
// this lane, use timeoutFloorMs". An empty string is neither absent nor a key, and is rejected.
|
|
2419
|
+
if (r.timeoutConfigKey !== undefined && r.timeoutConfigKey !== null &&
|
|
2420
|
+
(typeof r.timeoutConfigKey !== 'string' || r.timeoutConfigKey.length === 0)) {
|
|
2421
|
+
errors.push(
|
|
2422
|
+
ctx + ' reviewer.timeoutConfigKey must be a dotted config key or null ' +
|
|
2423
|
+
'(got: ' + describeValue(r.timeoutConfigKey) + ')',
|
|
2424
|
+
);
|
|
2425
|
+
}
|
|
2426
|
+
|
|
2427
|
+
// OPTIONAL, mirroring the two keys above (#4255). Absent/null means "this lane declares no
|
|
2428
|
+
// review effort", which is the shape that emits no effort argument at all — the correct state
|
|
2429
|
+
// for the nine lanes with no effort channel to feed. An empty string is neither.
|
|
2430
|
+
if (r.effortConfigKey !== undefined && r.effortConfigKey !== null &&
|
|
2431
|
+
(typeof r.effortConfigKey !== 'string' || r.effortConfigKey.length === 0)) {
|
|
2432
|
+
errors.push(
|
|
2433
|
+
ctx + ' reviewer.effortConfigKey must be a dotted config key or null ' +
|
|
2434
|
+
'(got: ' + describeValue(r.effortConfigKey) + ')',
|
|
2435
|
+
);
|
|
2436
|
+
}
|
|
2437
|
+
|
|
2438
|
+
// A declared default must be a level the effort axis knows, or the lane would render an
|
|
2439
|
+
// argument the reviewer CLI rejects and kill itself on every run. Closed vocabulary, checked
|
|
2440
|
+
// here rather than at resolution time so a malformed manifest fails at the trust boundary.
|
|
2441
|
+
if (r.defaultEffort !== undefined && r.defaultEffort !== null
|
|
2442
|
+
&& !(typeof r.defaultEffort === 'string' && REVIEWER_EFFORT_LEVELS.has(r.defaultEffort))) {
|
|
2443
|
+
errors.push(
|
|
2444
|
+
ctx + ' reviewer.defaultEffort must be one of ' +
|
|
2445
|
+
Array.from(REVIEWER_EFFORT_LEVELS).join('/') + ' or null ' +
|
|
2446
|
+
'(got: ' + describeValue(r.defaultEffort) + ')',
|
|
2447
|
+
);
|
|
2448
|
+
}
|
|
2449
|
+
|
|
2450
|
+
// A default with nothing to configure it through is a value the operator cannot change — the
|
|
2451
|
+
// shape #4255 exists to end. Declared together or not at all.
|
|
2452
|
+
if ((r.defaultEffort !== undefined && r.defaultEffort !== null)
|
|
2453
|
+
&& (r.effortConfigKey === undefined || r.effortConfigKey === null)) {
|
|
2454
|
+
errors.push(
|
|
2455
|
+
ctx + ' reviewer.defaultEffort is declared without an effortConfigKey, so the level ' +
|
|
2456
|
+
'could never be overridden',
|
|
2457
|
+
);
|
|
2458
|
+
}
|
|
2459
|
+
|
|
2247
2460
|
// `null` is the declared "no per-lane budget"; an empty string is not.
|
|
2248
2461
|
if (r.promptBudgetKey !== null && (typeof r.promptBudgetKey !== 'string' || r.promptBudgetKey.length === 0)) {
|
|
2249
2462
|
errors.push(
|
|
@@ -2731,6 +2944,10 @@ function validateStep(step, prefix, declaredSkills, declaredAgents) {
|
|
|
2731
2944
|
errors.push(prefix + '.when must be a string if present');
|
|
2732
2945
|
}
|
|
2733
2946
|
|
|
2947
|
+
if (step.pointFrom !== undefined && typeof step.pointFrom !== 'string') {
|
|
2948
|
+
errors.push(prefix + '.pointFrom must be a string if present');
|
|
2949
|
+
}
|
|
2950
|
+
|
|
2734
2951
|
if (step.fragment !== undefined) {
|
|
2735
2952
|
errors.push(...validateFragment(step.fragment, prefix + '.fragment'));
|
|
2736
2953
|
}
|
|
@@ -2870,6 +3087,31 @@ function validateAgainstContract(cap, capId) {
|
|
|
2870
3087
|
}
|
|
2871
3088
|
}
|
|
2872
3089
|
|
|
3090
|
+
// pointFrom (#3661): selects which of possibly several same-capability steps is
|
|
3091
|
+
// active for its own `point`, based on an enum config key. Require it references
|
|
3092
|
+
// an enum key in cap.config whose values include THIS step's own point — otherwise
|
|
3093
|
+
// the step could never activate (a silently-dead step).
|
|
3094
|
+
for (const step of cap.steps) {
|
|
3095
|
+
if (step.pointFrom !== undefined) {
|
|
3096
|
+
if (typeof step.pointFrom !== 'string') continue; // already reported above
|
|
3097
|
+
const slice = typeof cap.config === 'object' && cap.config !== null ? cap.config[step.pointFrom] : undefined;
|
|
3098
|
+
if (!slice || typeof slice !== 'object') {
|
|
3099
|
+
errors.push(
|
|
3100
|
+
prefix + ' step.pointFrom "' + step.pointFrom + '" is not defined in capability config keys',
|
|
3101
|
+
);
|
|
3102
|
+
} else if (slice.type !== 'enum') {
|
|
3103
|
+
errors.push(
|
|
3104
|
+
prefix + ' step.pointFrom "' + step.pointFrom + '" must reference an enum config key (got type: ' + slice.type + ')',
|
|
3105
|
+
);
|
|
3106
|
+
} else if (!Array.isArray(slice.values) || !slice.values.includes(step.point)) {
|
|
3107
|
+
errors.push(
|
|
3108
|
+
prefix + ' step.pointFrom "' + step.pointFrom + '" enum values do not include this step\'s own point "' +
|
|
3109
|
+
step.point + '" — the step could never activate',
|
|
3110
|
+
);
|
|
3111
|
+
}
|
|
3112
|
+
}
|
|
3113
|
+
}
|
|
3114
|
+
|
|
2873
3115
|
for (const contrib of cap.contributions) {
|
|
2874
3116
|
if (contrib.when !== undefined) {
|
|
2875
3117
|
if (typeof contrib.when !== 'string') continue;
|
|
@@ -3174,6 +3416,11 @@ function validateCrossCapability(capMap, centralKeys, centralPatterns = []) {
|
|
|
3174
3416
|
const laneSlugClaims = new Map(); // slug → capId[]
|
|
3175
3417
|
const laneFlagClaims = new Map(); // flag → capId[]
|
|
3176
3418
|
const laneSectionClaims = new Map(); // reviewsSection → capId[]
|
|
3419
|
+
// ADR-3646: task-content resolver tracker-prefix uniqueness across the
|
|
3420
|
+
// MERGED first-party ∪ overlay set, mirroring the reviewer-lane collision
|
|
3421
|
+
// pattern above — two resolvers claiming the same prefix would make
|
|
3422
|
+
// `execute:task` dispatch ambiguous (which capability's resolver runs?).
|
|
3423
|
+
const trackerPrefixClaims = new Map(); // trackerPrefix → capId[]
|
|
3177
3424
|
|
|
3178
3425
|
// Claims are ACCUMULATED and reported after the sweep, never reported on the
|
|
3179
3426
|
// second claimant. Reporting pairwise-on-collision looks equivalent and is not:
|
|
@@ -3194,6 +3441,15 @@ function validateCrossCapability(capMap, centralKeys, centralPatterns = []) {
|
|
|
3194
3441
|
};
|
|
3195
3442
|
|
|
3196
3443
|
for (const [capId, cap] of capMap) {
|
|
3444
|
+
// ADR-3646: a MALFORMED taskContentResolver body was already reported by
|
|
3445
|
+
// validateCapability — do not double-report; only claim well-shaped
|
|
3446
|
+
// bodies. Independent of the reviewer-lane checks below, so it runs even
|
|
3447
|
+
// for capabilities that carry no `reviewer` body at all.
|
|
3448
|
+
const tcr = cap.taskContentResolver;
|
|
3449
|
+
if (typeof tcr === 'object' && tcr !== null && !Array.isArray(tcr)) {
|
|
3450
|
+
claim(trackerPrefixClaims, tcr.trackerPrefix, capId);
|
|
3451
|
+
}
|
|
3452
|
+
|
|
3197
3453
|
const r = cap.reviewer;
|
|
3198
3454
|
// A capability with no lane contributes to no uniqueness set. A MALFORMED
|
|
3199
3455
|
// body was already reported by validateCapability — do not double-report.
|
|
@@ -3215,6 +3471,7 @@ function validateCrossCapability(capMap, centralKeys, centralPatterns = []) {
|
|
|
3215
3471
|
[laneSlugClaims, 'slug'],
|
|
3216
3472
|
[laneFlagClaims, 'flag'],
|
|
3217
3473
|
[laneSectionClaims, 'reviewsSection'],
|
|
3474
|
+
[trackerPrefixClaims, 'taskContentResolver.trackerPrefix'],
|
|
3218
3475
|
]) {
|
|
3219
3476
|
for (const [key, claimants] of claims) {
|
|
3220
3477
|
if (claimants.length < 2) continue;
|
|
@@ -3435,26 +3692,56 @@ function topoSortContributions(entries) {
|
|
|
3435
3692
|
|
|
3436
3693
|
// ─── Gen-time wired guard ─────────────────────────────────────────────────────
|
|
3437
3694
|
|
|
3695
|
+
/**
|
|
3696
|
+
* Hook group (capability.json array name) → hook kind (dispatch discriminator).
|
|
3697
|
+
* Single source of truth for both validateHooksWired and the scanner-parity
|
|
3698
|
+
* test in tests/capability-registry.test.cjs (#3606).
|
|
3699
|
+
*/
|
|
3700
|
+
const HOOK_GROUP_KINDS = Object.freeze({
|
|
3701
|
+
steps: 'step',
|
|
3702
|
+
contributions: 'contribution',
|
|
3703
|
+
gates: 'gate',
|
|
3704
|
+
});
|
|
3705
|
+
|
|
3438
3706
|
/**
|
|
3439
3707
|
* Validate that every hook point declared by a capability has a corresponding
|
|
3440
|
-
* `loop render-hooks <point>` call site in one of the host-loop workflow files
|
|
3708
|
+
* `loop render-hooks <point>` call site in one of the host-loop workflow files,
|
|
3709
|
+
* AND — #3606 — that the call site's dispatch text covers the hook's KIND.
|
|
3710
|
+
*
|
|
3711
|
+
* A call site proves hooks are rendered, not dispatched: a consumer that
|
|
3712
|
+
* iterates only `kind == "gate"` (or narrows `kind == "step"` to one
|
|
3713
|
+
* `ref.skill`) silently drops every other registered kind — a capability can
|
|
3714
|
+
* be wired, enabled, resolved active, and never run. See
|
|
3715
|
+
* gsd-core/references/loop-hook-dispatch.md ("A point whose workflow
|
|
3716
|
+
* hand-rolls one kind does not implement this contract").
|
|
3441
3717
|
*
|
|
3442
3718
|
* Only valid loop points (in VALID_LOOP_POINTS) are checked here. Invalid points
|
|
3443
3719
|
* are already caught by validateStep/validateContribution/validateGate — do not
|
|
3444
3720
|
* double-report.
|
|
3445
3721
|
*
|
|
3722
|
+
* KNOWN LIMITATION (#3606): coverage is the UNION across all call sites for a
|
|
3723
|
+
* point in HOST_LOOP_FILES. Quick's plan:pre planner contribution seam has an
|
|
3724
|
+
* additional generator-owned per-file check. Other consumers outside that
|
|
3725
|
+
* universe (autonomous.md, code-review*.md, audit-milestone.md,
|
|
3726
|
+
* secure-phase.md, validate-phase.md) are not per-file checked — a narrowed
|
|
3727
|
+
* consumer there passes as long as one host file covers the point. More
|
|
3728
|
+
* per-file coverage maps are the tightening path.
|
|
3729
|
+
*
|
|
3446
3730
|
* @param {object} cap Validated capability object.
|
|
3447
|
-
* @param {Set<string
|
|
3448
|
-
*
|
|
3731
|
+
* @param {Map<string, Set<string>>} wiredKinds Per point, the hook kinds the
|
|
3732
|
+
* host workflows' call-site dispatch text covers (getWiredKinds). A point
|
|
3733
|
+
* absent from the map is unwired.
|
|
3734
|
+
* @returns {string[]} Array of error strings; empty means all points
|
|
3735
|
+
* are wired and every registered kind is covered.
|
|
3449
3736
|
*/
|
|
3450
|
-
function validateHooksWired(cap,
|
|
3737
|
+
function validateHooksWired(cap, wiredKinds) {
|
|
3451
3738
|
const errors = [];
|
|
3452
3739
|
const capId = cap.id || '(unknown)';
|
|
3453
3740
|
|
|
3454
|
-
function checkPoint(point, groupName, idx) {
|
|
3741
|
+
function checkPoint(point, groupName, kind, idx) {
|
|
3455
3742
|
// Only flag valid points that are unwired — invalid points are schema-validator's job.
|
|
3456
3743
|
if (!VALID_LOOP_POINTS.has(point)) return;
|
|
3457
|
-
if (!
|
|
3744
|
+
if (!wiredKinds.has(point)) {
|
|
3458
3745
|
errors.push(
|
|
3459
3746
|
'capability "' + capId + '" ' + groupName + '[' + idx + '].point "' + point +
|
|
3460
3747
|
'" is declared but not wired in any host-loop workflow ' +
|
|
@@ -3462,20 +3749,34 @@ function validateHooksWired(cap, wiredSet) {
|
|
|
3462
3749
|
'Wire the call site in the host workflow ' +
|
|
3463
3750
|
'(see scripts/gen-loop-host-contract.cjs STEP_WORKFLOWS) or remove the hook.',
|
|
3464
3751
|
);
|
|
3752
|
+
return;
|
|
3753
|
+
}
|
|
3754
|
+
const covered = wiredKinds.get(point);
|
|
3755
|
+
if (covered.size === 0) {
|
|
3756
|
+
errors.push(
|
|
3757
|
+
'capability "' + capId + '" ' + groupName + '[' + idx + '].point "' + point +
|
|
3758
|
+
'" has `loop render-hooks ' + point + '` call site(s), but their dispatch text covers NO ' +
|
|
3759
|
+
'hook kind — every consumer is narrowed to specific hooks. Dispatch every registered kind ' +
|
|
3760
|
+
'per gsd-core/references/loop-hook-dispatch.md.',
|
|
3761
|
+
);
|
|
3762
|
+
return;
|
|
3763
|
+
}
|
|
3764
|
+
if (!covered.has(kind)) {
|
|
3765
|
+
errors.push(
|
|
3766
|
+
'capability "' + capId + '" ' + groupName + '[' + idx + '].point "' + point +
|
|
3767
|
+
'" registers a ' + kind + ' hook, but the host call site\'s dispatch text never ' +
|
|
3768
|
+
'covers `kind == "' + kind + '"` (it covers: ' + [...covered].sort().join(', ') + '). ' +
|
|
3769
|
+
'A hand-rolled single-kind consumer silently never dispatches the other kinds — ' +
|
|
3770
|
+
'dispatch every registered kind per gsd-core/references/loop-hook-dispatch.md.',
|
|
3771
|
+
);
|
|
3465
3772
|
}
|
|
3466
3773
|
}
|
|
3467
3774
|
|
|
3468
|
-
for (
|
|
3469
|
-
|
|
3470
|
-
|
|
3471
|
-
|
|
3472
|
-
|
|
3473
|
-
const hook = cap.contributions[i];
|
|
3474
|
-
if (hook.point !== undefined) checkPoint(hook.point, 'contributions', i);
|
|
3475
|
-
}
|
|
3476
|
-
for (let i = 0; i < (cap.gates || []).length; i++) {
|
|
3477
|
-
const hook = cap.gates[i];
|
|
3478
|
-
if (hook.point !== undefined) checkPoint(hook.point, 'gates', i);
|
|
3775
|
+
for (const [group, kind] of Object.entries(HOOK_GROUP_KINDS)) {
|
|
3776
|
+
for (let i = 0; i < (cap[group] || []).length; i++) {
|
|
3777
|
+
const hook = cap[group][i];
|
|
3778
|
+
if (hook.point !== undefined) checkPoint(hook.point, group, kind, i);
|
|
3779
|
+
}
|
|
3479
3780
|
}
|
|
3480
3781
|
|
|
3481
3782
|
return errors;
|
|
@@ -3681,6 +3982,7 @@ module.exports = {
|
|
|
3681
3982
|
validateCommandEntry,
|
|
3682
3983
|
validateRuntimeCompat,
|
|
3683
3984
|
validateFeatureBody,
|
|
3985
|
+
validateTaskContentResolver,
|
|
3684
3986
|
validateConfigHome,
|
|
3685
3987
|
validateArtifactKindEntry,
|
|
3686
3988
|
validateArtifactLayout,
|
|
@@ -3699,6 +4001,7 @@ module.exports = {
|
|
|
3699
4001
|
topoSortSteps,
|
|
3700
4002
|
topoSortContributions,
|
|
3701
4003
|
validateHooksWired,
|
|
4004
|
+
HOOK_GROUP_KINDS,
|
|
3702
4005
|
validateConfigSliceEntry,
|
|
3703
4006
|
classifyCrossErrors,
|
|
3704
4007
|
runConfigFormatParityGate,
|
|
@@ -184,6 +184,7 @@ function setCapabilityState(cwd, runtimeConfigDir, desired, opts) {
|
|
|
184
184
|
};
|
|
185
185
|
}
|
|
186
186
|
// ── Step 4: APPLY PASS ────────────────────────────────────────────────────
|
|
187
|
+
let pendingSurfaceForCommit;
|
|
187
188
|
// ── Surface writes ────────────────────────────────────────────────────────
|
|
188
189
|
if (needsSurface && (idsToDisable.length > 0 || idsToEnable.length > 0)) {
|
|
189
190
|
const existing = readSurface(resolvedConfigDir);
|
|
@@ -242,13 +243,19 @@ function setCapabilityState(cwd, runtimeConfigDir, desired, opts) {
|
|
|
242
243
|
}
|
|
243
244
|
}
|
|
244
245
|
if (surfaceChanged) {
|
|
245
|
-
|
|
246
|
+
pendingSurfaceForCommit = pendingSurface;
|
|
246
247
|
}
|
|
247
248
|
}
|
|
248
249
|
// ── Config writes (once, batched) ─────────────────────────────────────────
|
|
249
250
|
if (pendingGateWrites.length > 0) {
|
|
250
251
|
setConfigValues(cwd, pendingGateWrites);
|
|
251
252
|
}
|
|
253
|
+
// State-only callers preserve the existing direct-write behavior. A caller
|
|
254
|
+
// that requests materialization lets applySurface publish the candidate only
|
|
255
|
+
// after every artifact kind has staged and synchronized successfully.
|
|
256
|
+
if (pendingSurfaceForCommit && !opts?.materialize) {
|
|
257
|
+
writeSurface(resolvedConfigDir, pendingSurfaceForCommit);
|
|
258
|
+
}
|
|
252
259
|
// ── Materialize (optional) ────────────────────────────────────────────────
|
|
253
260
|
if (opts?.materialize) {
|
|
254
261
|
const { runtime, scope } = opts.materialize;
|
|
@@ -274,9 +281,12 @@ function setCapabilityState(cwd, runtimeConfigDir, desired, opts) {
|
|
|
274
281
|
// folded:issue-1575-agent-descriptor-parity describe block in
|
|
275
282
|
// tests/golden-parity-single-source.test.cjs).
|
|
276
283
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
|
277
|
-
applySurface(resolvedConfigDir, layout, manifest, undefined, registry,
|
|
278
|
-
? {
|
|
279
|
-
|
|
284
|
+
applySurface(resolvedConfigDir, layout, manifest, undefined, registry, {
|
|
285
|
+
...(pendingSurfaceForCommit ? { surfaceState: pendingSurfaceForCommit } : {}),
|
|
286
|
+
...(opts.materialize.resolveAttribution
|
|
287
|
+
? { resolveAttribution: opts.materialize.resolveAttribution }
|
|
288
|
+
: {}),
|
|
289
|
+
});
|
|
280
290
|
}
|
|
281
291
|
catch (err) {
|
|
282
292
|
const msg = err instanceof Error ? err.message : String(err);
|