@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
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* check-contract-drift.cjs
|
|
4
|
+
*
|
|
5
|
+
* Enforces that gsd-core/references/agent-contracts.md's `## Agent Registry`
|
|
6
|
+
* table stays in sync with reality:
|
|
7
|
+
*
|
|
8
|
+
* 1. The table itself must parse cleanly (no malformed rows).
|
|
9
|
+
* 2. Every agents/*.md file must have its fenced code blocks properly
|
|
10
|
+
* closed (an unclosed fence makes in-fence marker detection unreliable).
|
|
11
|
+
* 3. Every marker an agent actually emits in-fence, and every marker the
|
|
12
|
+
* registry declares for it, must agree (contractViolations' declared/
|
|
13
|
+
* emitted checks) -- unless the row opts out via `kind:
|
|
14
|
+
* artifact+query`/`structured-return`, in which case any emitted
|
|
15
|
+
* marker is itself a violation (vestigial_marker).
|
|
16
|
+
* 4. Every `sentinel-match` row's declared markers must have at least one
|
|
17
|
+
* exact-case consumer somewhere under gsd-core/workflows/, commands/,
|
|
18
|
+
* or agents/ (excluding the producing agent's own file).
|
|
19
|
+
* 5. Registry roster coverage: every agents/*.md file has exactly one
|
|
20
|
+
* row, every row names an agent file that exists
|
|
21
|
+
* (agent_without_contract / duplicate_registry_row / unknown_producer),
|
|
22
|
+
* and every file-shaped `Consumed by` entry resolves (unknown_consumer).
|
|
23
|
+
* 6. Read-tag arm: no `<files_to_read>` survives anywhere in the consumer
|
|
24
|
+
* corpus (legacy_read_tag), and whenever a declared consumer emits
|
|
25
|
+
* `<required_reading>` the producing agent's file must reference the
|
|
26
|
+
* gate (read_tag_gate_missing).
|
|
27
|
+
* 7. Reverse direction: a workflow/command matching a quoted `## TOKEN`
|
|
28
|
+
* no agent declares or emits is dispatch-on-phantom
|
|
29
|
+
* (unmatched_consumer_token) — F9's shape from the consumer side.
|
|
30
|
+
*
|
|
31
|
+
* Exit 0 = clean. Exit 1 = violations (with diagnostics on stderr).
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
'use strict';
|
|
35
|
+
|
|
36
|
+
const fs = require('fs');
|
|
37
|
+
const path = require('path');
|
|
38
|
+
|
|
39
|
+
function resolveRoot(argv) {
|
|
40
|
+
const idx = argv.indexOf('--root');
|
|
41
|
+
if (idx === -1) return path.join(__dirname, '..');
|
|
42
|
+
const value = argv[idx + 1];
|
|
43
|
+
if (!value) {
|
|
44
|
+
throw new Error('check-contract-drift: --root requires a directory argument');
|
|
45
|
+
}
|
|
46
|
+
return path.resolve(value);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const ROOT = resolveRoot(process.argv.slice(2));
|
|
50
|
+
const CONTRACTS_FILE = path.join(ROOT, 'gsd-core', 'references', 'agent-contracts.md');
|
|
51
|
+
const AGENTS_DIR = path.join(ROOT, 'agents');
|
|
52
|
+
const WORKFLOWS_DIR = path.join(ROOT, 'gsd-core', 'workflows');
|
|
53
|
+
const COMMANDS_DIR = path.join(ROOT, 'commands');
|
|
54
|
+
|
|
55
|
+
const {
|
|
56
|
+
extractMarkers,
|
|
57
|
+
parseAgentContracts,
|
|
58
|
+
contractViolations,
|
|
59
|
+
readTagViolations,
|
|
60
|
+
parseConsumedByCell,
|
|
61
|
+
unmatchedConsumerTokens,
|
|
62
|
+
sanitizeEcho,
|
|
63
|
+
REMEDIES,
|
|
64
|
+
} = require('./command-contract-helpers.cjs');
|
|
65
|
+
|
|
66
|
+
const { runMain } = require('./lib/cli-exit.cjs');
|
|
67
|
+
|
|
68
|
+
// ─── helpers ────────────────────────────────────────────────────────────────
|
|
69
|
+
|
|
70
|
+
function walkMarkdownFiles(dir, acc) {
|
|
71
|
+
let entries;
|
|
72
|
+
try {
|
|
73
|
+
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
74
|
+
} catch {
|
|
75
|
+
return acc;
|
|
76
|
+
}
|
|
77
|
+
for (const entry of entries) {
|
|
78
|
+
const full = path.join(dir, entry.name);
|
|
79
|
+
if (entry.isDirectory()) {
|
|
80
|
+
walkMarkdownFiles(full, acc);
|
|
81
|
+
} else if (entry.isFile() && entry.name.endsWith('.md')) {
|
|
82
|
+
acc.push(full);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return acc;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function toRepoRelative(absPath) {
|
|
89
|
+
return path.relative(ROOT, absPath).split(path.sep).join('/');
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* referenceIncludes(content)
|
|
94
|
+
*
|
|
95
|
+
* Plain scan for `@~/.claude/gsd-core/references/*.md` tokens anywhere in an
|
|
96
|
+
* agent file's content -- inside an `<execution_context>` block (already
|
|
97
|
+
* covered structurally by `executionContextRefs` in command-contract-helpers,
|
|
98
|
+
* but a raw regex over the whole string picks those up too) and, just as
|
|
99
|
+
* importantly, OUTSIDE one: agents frequently point at a reference doc from
|
|
100
|
+
* plain prose (e.g. "See @~/.claude/gsd-core/references/planner-guidance.md
|
|
101
|
+
* for ...") rather than from the eager `<execution_context>` include list.
|
|
102
|
+
* An agent's completion-marker contract can be authored in such a reference
|
|
103
|
+
* file rather than the agent file itself -- gsd-planner declares
|
|
104
|
+
* `PLANNING COMPLETE` in its registry row, but the example heading itself
|
|
105
|
+
* lives in `gsd-core/references/planner-guidance.md`, which the agent only
|
|
106
|
+
* `@`-includes -- so the producer scan below must follow these includes to
|
|
107
|
+
* see markers an agent's contract legitimately delegates to a reference doc.
|
|
108
|
+
* Returns ROOT-relative paths (`gsd-core/references/foo.md`), de-duplicated.
|
|
109
|
+
*/
|
|
110
|
+
function referenceIncludes(content) {
|
|
111
|
+
const seen = new Set();
|
|
112
|
+
const re = /@~\/\.claude\/gsd-core\/references\/[A-Za-z0-9._-]+\.md/g;
|
|
113
|
+
let m;
|
|
114
|
+
while ((m = re.exec(content)) !== null) {
|
|
115
|
+
const relPath = 'gsd-core/references/' + m[0].slice('@~/.claude/gsd-core/references/'.length);
|
|
116
|
+
seen.add(relPath);
|
|
117
|
+
}
|
|
118
|
+
return [...seen];
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function remedyFor(kind) {
|
|
122
|
+
return REMEDIES[kind] || 'review the registry row and agent file for drift';
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// ─── run ─────────────────────────────────────────────────────────────────────
|
|
126
|
+
|
|
127
|
+
function main() {
|
|
128
|
+
if (!fs.existsSync(CONTRACTS_FILE)) {
|
|
129
|
+
process.stderr.write(`\nERROR check-contract-drift: contracts file not found at ${toRepoRelative(CONTRACTS_FILE)}\n\n`);
|
|
130
|
+
return 1;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const contractsMd = fs.readFileSync(CONTRACTS_FILE, 'utf-8');
|
|
134
|
+
const { rows: registry, errors: parseErrors } = parseAgentContracts(contractsMd);
|
|
135
|
+
|
|
136
|
+
// knownMarkers: every marker string declared anywhere in the registry --
|
|
137
|
+
// extractMarkers only ever resolves a heading against this vocabulary, it
|
|
138
|
+
// never falls back to guessing a shape. Includes `(unconsumed: …)` entries
|
|
139
|
+
// so their declared↔emitted agreement is checked too.
|
|
140
|
+
const knownMarkers = new Set();
|
|
141
|
+
for (const row of registry) {
|
|
142
|
+
for (const m of row.completion_markers || []) knownMarkers.add(m);
|
|
143
|
+
for (const m of row.unconsumed_markers || []) knownMarkers.add(m);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// producerMarkers: agent -> [in-fence marker strings that matched knownMarkers]
|
|
147
|
+
// candidateMarkers: agent -> [in-fence marker-shaped headings NOT in knownMarkers,
|
|
148
|
+
// deduped per marker — "emitted but undeclared" is a fact about the
|
|
149
|
+
// marker, not about each line or file it appears in]
|
|
150
|
+
// agentTexts: agent -> file content CONCATENATED with every
|
|
151
|
+
// references/*.md file the agent @-includes, for the read-tag arm's gate
|
|
152
|
+
// check. The fold is load-bearing: planner/executor/phase-researcher
|
|
153
|
+
// deliver the MUST-Read gate via the shared mandatory-initial-read.md
|
|
154
|
+
// include, so the agent file alone would report a gate that actually
|
|
155
|
+
// arrives (the same producer-scope gap referenceIncludes() fixes for
|
|
156
|
+
// markers, one layer up).
|
|
157
|
+
const producerMarkers = new Map();
|
|
158
|
+
const candidateMarkers = new Map();
|
|
159
|
+
const agentTexts = new Map();
|
|
160
|
+
const unclosedFenceViolations = [];
|
|
161
|
+
|
|
162
|
+
const agentFiles = fs.existsSync(AGENTS_DIR)
|
|
163
|
+
? fs.readdirSync(AGENTS_DIR).filter(f => f.endsWith('.md'))
|
|
164
|
+
: [];
|
|
165
|
+
|
|
166
|
+
for (const file of agentFiles) {
|
|
167
|
+
const agent = file.replace(/\.md$/, '');
|
|
168
|
+
const abs = path.join(AGENTS_DIR, file);
|
|
169
|
+
const content = fs.readFileSync(abs, 'utf-8');
|
|
170
|
+
// Single pass over the agent's @-included references: each file is read
|
|
171
|
+
// once and feeds BOTH the read-tag fold (agentTexts) and marker
|
|
172
|
+
// extraction (producer/candidate attribution).
|
|
173
|
+
const includeTexts = [];
|
|
174
|
+
for (const refRelPath of referenceIncludes(content)) {
|
|
175
|
+
try {
|
|
176
|
+
includeTexts.push(fs.readFileSync(path.join(ROOT, refRelPath), 'utf-8'));
|
|
177
|
+
} catch {
|
|
178
|
+
// include miss — lint-command-contract rule 4 owns @-ref existence
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
agentTexts.set(agent, [content, ...includeTexts].join('\n'));
|
|
182
|
+
const { markers, candidates, unclosedFence } = extractMarkers(agentTexts.get(agent), knownMarkers);
|
|
183
|
+
const inFenceMarkers = markers.filter(m => m.inFence).map(m => m.marker);
|
|
184
|
+
const inFenceCandidates = candidates.filter(m => m.inFence).map(m => m.marker);
|
|
185
|
+
|
|
186
|
+
producerMarkers.set(agent, inFenceMarkers);
|
|
187
|
+
candidateMarkers.set(agent, [...new Set(inFenceCandidates)]);
|
|
188
|
+
|
|
189
|
+
if (unclosedFence) {
|
|
190
|
+
unclosedFenceViolations.push({
|
|
191
|
+
kind: 'unclosed_fence',
|
|
192
|
+
agent,
|
|
193
|
+
marker: null,
|
|
194
|
+
detail: `${toRepoRelative(abs)} has an unterminated code fence`,
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// consumerTexts: every *.md under gsd-core/workflows/, commands/, agents/
|
|
200
|
+
// — plus every file-shaped `Consumed by` entry that resolves on disk, so
|
|
201
|
+
// a row citing a reference doc or an ADR (e.g. intel-updater's
|
|
202
|
+
// docs/adr/22-plan-drift-guard.md) is validated against the real file and
|
|
203
|
+
// its text participates in consumer matching, not just workflows.
|
|
204
|
+
const consumerTexts = new Map();
|
|
205
|
+
const consumerDirs = [WORKFLOWS_DIR, COMMANDS_DIR, AGENTS_DIR];
|
|
206
|
+
for (const dir of consumerDirs) {
|
|
207
|
+
for (const abs of walkMarkdownFiles(dir, [])) {
|
|
208
|
+
consumerTexts.set(toRepoRelative(abs), fs.readFileSync(abs, 'utf-8'));
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
for (const row of registry) {
|
|
212
|
+
for (const rel of parseConsumedByCell(row.consumed_by)) {
|
|
213
|
+
if (consumerTexts.has(rel)) continue;
|
|
214
|
+
const abs = path.join(ROOT, rel);
|
|
215
|
+
try {
|
|
216
|
+
consumerTexts.set(rel, fs.readFileSync(abs, 'utf-8'));
|
|
217
|
+
} catch {
|
|
218
|
+
// absent — contractViolations reports it as unknown_consumer
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
const contractViolationsList = contractViolations({ registry, producerMarkers, candidateMarkers, consumerTexts });
|
|
224
|
+
const readTagViolationsList = readTagViolations({ registry, agentTexts, consumerTexts });
|
|
225
|
+
const reverseViolationsList = unmatchedConsumerTokens({ consumerTexts, vocabulary: knownMarkers });
|
|
226
|
+
|
|
227
|
+
const parseViolations = parseErrors.map(e => ({
|
|
228
|
+
kind: 'parse_error',
|
|
229
|
+
agent: null,
|
|
230
|
+
marker: null,
|
|
231
|
+
detail: `agent-contracts.md:${e.line} — ${e.reason}`,
|
|
232
|
+
}));
|
|
233
|
+
|
|
234
|
+
const allViolations = [
|
|
235
|
+
...parseViolations,
|
|
236
|
+
...unclosedFenceViolations,
|
|
237
|
+
...contractViolationsList,
|
|
238
|
+
...readTagViolationsList,
|
|
239
|
+
...reverseViolationsList,
|
|
240
|
+
];
|
|
241
|
+
|
|
242
|
+
const agentCount = registry.length;
|
|
243
|
+
const markerCount = registry.reduce((n, r) => n + (r.completion_markers || []).length, 0);
|
|
244
|
+
|
|
245
|
+
// --json: the typed surface tests consume (CONTRIBUTING "Raw Text
|
|
246
|
+
// Matching" rule — the human formatter below is for operators only).
|
|
247
|
+
if (process.argv.includes('--json')) {
|
|
248
|
+
console.log(
|
|
249
|
+
JSON.stringify({
|
|
250
|
+
check: 'check-contract-drift',
|
|
251
|
+
status: allViolations.length === 0 ? 'ok' : 'violations',
|
|
252
|
+
agents: agentCount,
|
|
253
|
+
markers: markerCount,
|
|
254
|
+
violations: allViolations.map((v) => ({
|
|
255
|
+
kind: v.kind,
|
|
256
|
+
agent: v.agent ?? null,
|
|
257
|
+
marker: v.marker ?? null,
|
|
258
|
+
detail: sanitizeEcho(v.detail),
|
|
259
|
+
})),
|
|
260
|
+
}),
|
|
261
|
+
);
|
|
262
|
+
return allViolations.length === 0 ? 0 : 1;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
if (allViolations.length === 0) {
|
|
266
|
+
console.log(`ok check-contract-drift: ${agentCount} agents, ${markerCount} markers, 0 violations`);
|
|
267
|
+
return 0;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// group by violation kind
|
|
271
|
+
const byKind = new Map();
|
|
272
|
+
for (const v of allViolations) {
|
|
273
|
+
if (!byKind.has(v.kind)) byKind.set(v.kind, []);
|
|
274
|
+
byKind.get(v.kind).push(v);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
process.stderr.write(
|
|
278
|
+
`\nERROR check-contract-drift: ${allViolations.length} violation(s) across ${byKind.size} kind(s)\n\n`,
|
|
279
|
+
);
|
|
280
|
+
|
|
281
|
+
for (const [kind, violations] of byKind) {
|
|
282
|
+
process.stderr.write(` ${kind} (${violations.length}):\n`);
|
|
283
|
+
for (const v of violations) {
|
|
284
|
+
const agentLabel = v.agent ? sanitizeEcho(v.agent) : '(registry)';
|
|
285
|
+
const markerLabel = v.marker ? ` marker "${sanitizeEcho(v.marker)}"` : '';
|
|
286
|
+
process.stderr.write(` - ${agentLabel}${markerLabel}: ${sanitizeEcho(v.detail)}\n`);
|
|
287
|
+
process.stderr.write(` remedy: ${remedyFor(kind)}\n`);
|
|
288
|
+
}
|
|
289
|
+
process.stderr.write('\n');
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
process.stderr.write('See gsd-core/references/agent-contracts.md for the registry contract spec.\n\n');
|
|
293
|
+
|
|
294
|
+
return 1;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
runMain(main);
|
|
@@ -300,6 +300,23 @@ const RULES = [
|
|
|
300
300
|
'tests/require-fs-op-fallback.rule.test.cjs',
|
|
301
301
|
],
|
|
302
302
|
},
|
|
303
|
+
{
|
|
304
|
+
// ADR-3212 Phase 4 (#3415): no-unbounded-quantifier and the shared
|
|
305
|
+
// readfilesync-trace helper it uses (also now imported by
|
|
306
|
+
// no-crlf-fragile-split). NOT part of the ADR-1703 portability family above
|
|
307
|
+
// — kept as its own bucket so this rule's tests re-run without pulling in
|
|
308
|
+
// the ADR-1703 disable-ban / vocab-drift suites it is not governed by.
|
|
309
|
+
name: 'no-unbounded-quantifier + readfilesync-trace (ADR-3212 Phase 4)',
|
|
310
|
+
match: path => [
|
|
311
|
+
'eslint-rules/no-unbounded-quantifier.cjs',
|
|
312
|
+
'eslint-rules/lib/readfilesync-trace.cjs',
|
|
313
|
+
'eslint-rules/no-crlf-fragile-split.cjs',
|
|
314
|
+
].includes(path),
|
|
315
|
+
tests: [
|
|
316
|
+
'tests/no-unbounded-quantifier.rule.test.cjs',
|
|
317
|
+
'tests/readfilesync-trace-parity.test.cjs',
|
|
318
|
+
],
|
|
319
|
+
},
|
|
303
320
|
];
|
|
304
321
|
|
|
305
322
|
/**
|
|
@@ -474,8 +491,8 @@ function classify(files) {
|
|
|
474
491
|
// the scoped windows lane instead of triggering the three full parity
|
|
475
492
|
// lanes. (full_matrix fired on 15/15 sampled PRs because test-driven
|
|
476
493
|
// PRs always touch tests/, costing ~25 runner-minutes each.) Changed
|
|
477
|
-
// tests already run on
|
|
478
|
-
// residual macOS / windows
|
|
494
|
+
// tests already run on the two ubuntu-24 lanes via targeted_tests; the
|
|
495
|
+
// residual macOS / windows cross-product is covered by the full
|
|
479
496
|
// matrix on every push to next.
|
|
480
497
|
windows.add(file);
|
|
481
498
|
}
|