@opengsd/gsd-core 1.12.0 → 1.14.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-advisor-researcher.compact.md +85 -0
- package/agents/gsd-ai-researcher.compact.md +96 -0
- package/agents/gsd-assumptions-analyzer.compact.md +81 -0
- package/agents/gsd-code-fixer.compact.md +458 -0
- package/agents/gsd-code-fixer.md +5 -5
- package/agents/gsd-code-reviewer.compact.md +269 -0
- package/agents/gsd-code-reviewer.md +15 -3
- package/agents/gsd-codebase-mapper.compact.md +760 -0
- package/agents/gsd-debug-session-manager.compact.md +345 -0
- package/agents/gsd-doc-classifier.compact.md +192 -0
- package/agents/gsd-doc-synthesizer.compact.md +200 -0
- package/agents/gsd-doc-verifier.compact.md +143 -0
- package/agents/gsd-doc-writer.compact.md +440 -0
- package/agents/gsd-dom-verifier.compact.md +138 -0
- package/agents/gsd-domain-researcher.compact.md +141 -0
- package/agents/gsd-eval-auditor.compact.md +160 -0
- package/agents/gsd-eval-planner.compact.md +137 -0
- package/agents/gsd-executor.md +63 -35
- package/agents/gsd-framework-selector.compact.md +82 -0
- package/agents/gsd-integration-checker.compact.md +245 -0
- package/agents/gsd-intel-updater.compact.md +226 -0
- package/agents/gsd-mempalace-curator.compact.md +45 -0
- package/agents/gsd-nyquist-auditor.compact.md +179 -0
- package/agents/gsd-pattern-mapper.compact.md +275 -0
- package/agents/gsd-plan-checker.md +76 -57
- package/agents/gsd-planner.md +14 -0
- package/agents/gsd-project-researcher.compact.md +587 -0
- package/agents/gsd-research-synthesizer.compact.md +212 -0
- package/agents/gsd-roadmapper.compact.md +454 -0
- package/agents/gsd-roadmapper.md +13 -0
- package/agents/gsd-security-auditor.compact.md +162 -0
- package/agents/gsd-ui-auditor.compact.md +404 -0
- package/agents/gsd-ui-checker.compact.md +277 -0
- package/agents/gsd-ui-checker.md +19 -3
- package/agents/gsd-ui-researcher.compact.md +282 -0
- package/agents/gsd-ui-researcher.md +29 -0
- package/agents/gsd-user-profiler.compact.md +108 -0
- package/agents/gsd-verifier.md +23 -1
- package/bin/install.js +444 -134
- package/commands/gsd/cleanup.md +1 -0
- package/commands/gsd/code-review.md +2 -1
- package/commands/gsd/complete-milestone.md +1 -0
- package/commands/gsd/config.md +1 -0
- package/commands/gsd/debug.md +1 -0
- package/commands/gsd/execute-phase.md +1 -1
- package/commands/gsd/graphify.md +1 -0
- package/commands/gsd/health.md +1 -0
- package/commands/gsd/mempalace-capture.md +1 -0
- package/commands/gsd/mempalace-recall.md +1 -0
- package/commands/gsd/new-milestone.md +1 -0
- package/commands/gsd/new-project.md +1 -0
- package/commands/gsd/next.md +1 -0
- package/commands/gsd/ns-workflow.md +2 -1
- package/commands/gsd/pause-work.md +1 -0
- package/commands/gsd/phase.md +2 -1
- package/commands/gsd/pr-branch.md +1 -0
- package/commands/gsd/quick-batch.md +105 -0
- package/commands/gsd/resume-work.md +1 -0
- package/commands/gsd/review-backlog.md +1 -0
- package/commands/gsd/settings.md +2 -1
- package/commands/gsd/stats.md +1 -0
- package/commands/gsd/surface.md +18 -8
- package/commands/gsd/thread.md +1 -0
- package/commands/gsd/workspace.md +1 -0
- package/commands/gsd/workstreams.md +1 -0
- package/gsd-core/bin/check-latest-version.cjs +8 -3
- package/gsd-core/bin/gsd-tools.cjs +532 -174
- package/gsd-core/bin/lib/adr-parser.cjs +1 -1
- package/gsd-core/bin/lib/artifacts.cjs +2 -1
- package/gsd-core/bin/lib/audit.cjs +39 -22
- package/gsd-core/bin/lib/broken-windows.cjs +168 -49
- package/gsd-core/bin/lib/capability-activation.cjs +27 -0
- package/gsd-core/bin/lib/capability-lifecycle.cjs +10 -6
- package/gsd-core/bin/lib/capability-loader.cjs +135 -1
- package/gsd-core/bin/lib/capability-registry.cjs +528 -116
- package/gsd-core/bin/lib/capability-source.cjs +19 -2
- package/gsd-core/bin/lib/capability-state.cjs +7 -1
- package/gsd-core/bin/lib/capability-validator.cjs +134 -5
- package/gsd-core/bin/lib/capability-writer.cjs +14 -4
- package/gsd-core/bin/lib/check-command-router.cjs +198 -38
- package/gsd-core/bin/lib/claude-orchestration.cjs +10 -25
- package/gsd-core/bin/lib/clusters.cjs +1 -0
- package/gsd-core/bin/lib/code-review-depth.cjs +2 -2
- package/gsd-core/bin/lib/command-aliases.cjs +16 -0
- package/gsd-core/bin/lib/commands.cjs +981 -79
- package/gsd-core/bin/lib/config-loader.cjs +4 -0
- package/gsd-core/bin/lib/config.cjs +153 -38
- package/gsd-core/bin/lib/core-utils.cjs +34 -7
- package/gsd-core/bin/lib/coverage.cjs +1 -1
- package/gsd-core/bin/lib/decisions.cjs +343 -28
- package/gsd-core/bin/lib/edge-probe.cjs +14 -1
- package/gsd-core/bin/lib/external-descriptor-trust.cjs +29 -14
- package/gsd-core/bin/lib/file-overlap-partitioner.cjs +74 -0
- package/gsd-core/bin/lib/frontmatter.cjs +137 -23
- package/gsd-core/bin/lib/gap-checker.cjs +22 -13
- package/gsd-core/bin/lib/git-base-branch.cjs +10 -2
- package/gsd-core/bin/lib/gsd2-import.cjs +1 -2
- 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 +54 -11
- package/gsd-core/bin/lib/health-diagnostic-rules/state-consistency.cjs +87 -23
- package/gsd-core/bin/lib/health-diagnostic-rules/worktree-health.cjs +1 -1
- package/gsd-core/bin/lib/host-integration.cjs +57 -5
- package/gsd-core/bin/lib/init-command-router.cjs +14 -0
- package/gsd-core/bin/lib/init.cjs +539 -60
- package/gsd-core/bin/lib/install-engine.cjs +199 -14
- package/gsd-core/bin/lib/install-model-override-resolver.cjs +45 -0
- package/gsd-core/bin/lib/install-profiles.cjs +36 -14
- package/gsd-core/bin/lib/installer-migration-report.cjs +1 -0
- package/gsd-core/bin/lib/installer-migrations.cjs +33 -4
- package/gsd-core/bin/lib/io.cjs +35 -0
- package/gsd-core/bin/lib/loop-resolver.cjs +64 -39
- package/gsd-core/bin/lib/markdown-table.cjs +123 -0
- package/gsd-core/bin/lib/mcp-catalog.cjs +2 -2
- package/gsd-core/bin/lib/milestone.cjs +41 -10
- package/gsd-core/bin/lib/model-resolver.cjs +101 -10
- package/gsd-core/bin/lib/phase-command-router.cjs +20 -7
- package/gsd-core/bin/lib/phase-id.cjs +412 -31
- package/gsd-core/bin/lib/phase-lifecycle.cjs +61 -0
- package/gsd-core/bin/lib/phase.cjs +941 -98
- package/gsd-core/bin/lib/plan-document.cjs +10 -0
- package/gsd-core/bin/lib/planning-inspect.cjs +34 -18
- package/gsd-core/bin/lib/planning-snapshot.cjs +206 -30
- package/gsd-core/bin/lib/planning-workspace.cjs +153 -29
- package/gsd-core/bin/lib/pristine-baseline.cjs +182 -0
- package/gsd-core/bin/lib/prohibition-enforcement.cjs +91 -4
- 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/refactor-trigger-command-router.cjs +61 -2
- package/gsd-core/bin/lib/research-store.cjs +11 -12
- package/gsd-core/bin/lib/review-lane-descriptor.cjs +53 -5
- package/gsd-core/bin/lib/review-lane-invocation.cjs +96 -1
- package/gsd-core/bin/lib/review-lane-runner.cjs +136 -10
- package/gsd-core/bin/lib/reviewer-step-dispatch.cjs +337 -0
- package/gsd-core/bin/lib/roadmap-parser.cjs +555 -41
- package/gsd-core/bin/lib/roadmap.cjs +292 -69
- package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +260 -43
- package/gsd-core/bin/lib/runtime-artifact-install-plan.cjs +28 -20
- package/gsd-core/bin/lib/runtime-artifact-layout.cjs +294 -108
- package/gsd-core/bin/lib/runtime-hooks-surface.cjs +408 -47
- package/gsd-core/bin/lib/security.cjs +126 -7
- package/gsd-core/bin/lib/shell-command-projection.cjs +4 -0
- package/gsd-core/bin/lib/smart-entry.cjs +7 -9
- package/gsd-core/bin/lib/state-document.cjs +159 -32
- package/gsd-core/bin/lib/state-md-schema.cjs +44 -27
- package/gsd-core/bin/lib/state-transition.cjs +465 -62
- package/gsd-core/bin/lib/state.cjs +906 -151
- package/gsd-core/bin/lib/surface.cjs +83 -10
- package/gsd-core/bin/lib/task-command-router.cjs +12 -6
- package/gsd-core/bin/lib/tdd-red-evidence.cjs +133 -0
- package/gsd-core/bin/lib/uat.cjs +1420 -516
- package/gsd-core/bin/lib/update-context.cjs +36 -26
- package/gsd-core/bin/lib/validate.cjs +230 -12
- package/gsd-core/bin/lib/vendor/js-yaml.cjs +11 -3
- package/gsd-core/bin/lib/verification-command-router.cjs +2 -1
- package/gsd-core/bin/lib/verification.cjs +316 -23
- package/gsd-core/bin/lib/verify-command-grounding.cjs +1 -1
- package/gsd-core/bin/lib/verify-command-router.cjs +1 -0
- package/gsd-core/bin/lib/verify.cjs +531 -36
- package/gsd-core/bin/lib/workstream-inventory.cjs +21 -2
- package/gsd-core/bin/lib/worktree-safety.cjs +21 -7
- package/gsd-core/bin/shared/config-defaults.manifest.json +1 -0
- package/gsd-core/bin/shared/config-schema.manifest.json +13 -0
- package/gsd-core/bin/verify-reapply-patches.cjs +507 -81
- package/gsd-core/references/agent-contracts.md +3 -3
- package/gsd-core/references/compact-content-gate.md +66 -0
- package/gsd-core/references/edge-probe.md +17 -13
- package/gsd-core/references/execute-mvp-tdd.md +18 -16
- package/gsd-core/references/execute-phase-response-language.md +6 -0
- package/gsd-core/references/executor-examples.md +42 -0
- package/gsd-core/references/few-shot-examples/plan-checker.md +15 -15
- package/gsd-core/references/loop-hook-dispatch.md +18 -0
- package/gsd-core/references/model-profiles.md +12 -3
- package/gsd-core/references/mvp-concepts.md +2 -2
- 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-quick-batch.md +71 -0
- package/gsd-core/references/planner-reviews.md +47 -0
- package/gsd-core/references/planner-revision.md +75 -2
- package/gsd-core/references/planning-config.md +5 -1
- package/gsd-core/references/response-language-directive.md +9 -0
- package/gsd-core/references/revision-loop.md +118 -11
- package/gsd-core/references/tdd.md +17 -9
- package/gsd-core/references/thinking-models-planning.md +18 -2
- package/gsd-core/references/verification-patterns.md +17 -4
- package/gsd-core/references/verifier-evidence-gate.md +160 -0
- package/gsd-core/references/worktree-path-safety.md +112 -2
- package/gsd-core/templates/README.md +7 -1
- package/gsd-core/templates/phase-prompt.md +4 -0
- package/gsd-core/templates/state.md +6 -3
- package/gsd-core/templates/summary.compact.md +212 -0
- package/gsd-core/templates/user-setup.compact.md +199 -0
- package/gsd-core/templates/user-setup.md +0 -9
- package/gsd-core/templates/verification-report.md +5 -0
- package/gsd-core/workflows/add-backlog.md +2 -0
- package/gsd-core/workflows/add-phase.md +2 -0
- package/gsd-core/workflows/add-tests.md +1 -1
- package/gsd-core/workflows/add-todo.md +4 -3
- package/gsd-core/workflows/ai-integration-phase.md +1 -1
- package/gsd-core/workflows/analyze-dependencies.md +2 -0
- package/gsd-core/workflows/audit-fix.md +2 -0
- package/gsd-core/workflows/audit-milestone.md +2 -0
- package/gsd-core/workflows/audit-uat.md +2 -0
- package/gsd-core/workflows/autonomous.md +15 -10
- package/gsd-core/workflows/check-todos.md +5 -3
- package/gsd-core/workflows/cleanup.md +4 -2
- package/gsd-core/workflows/code-review/steps/structural-pre-pass.md +22 -13
- package/gsd-core/workflows/code-review-fix.md +5 -3
- package/gsd-core/workflows/code-review.md +211 -43
- package/gsd-core/workflows/complete-milestone/detail/elaboration.md +274 -0
- package/gsd-core/workflows/complete-milestone.md +40 -254
- package/gsd-core/workflows/debug.md +1 -1
- package/gsd-core/workflows/diagnose-issues.md +5 -1
- package/gsd-core/workflows/discuss-phase/modes/advisor.md +2 -0
- 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 +2 -0
- 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 +2 -0
- 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.md +1 -1
- package/gsd-core/workflows/discuss-phase-power.md +2 -0
- package/gsd-core/workflows/discuss-phase.md +1 -1
- package/gsd-core/workflows/do.md +43 -13
- package/gsd-core/workflows/docs-update/detail/elaboration.md +179 -0
- package/gsd-core/workflows/docs-update.md +15 -156
- package/gsd-core/workflows/edit-phase.md +2 -0
- package/gsd-core/workflows/eval-review.md +1 -1
- package/gsd-core/workflows/execute-phase/detail/elaboration.md +124 -0
- package/gsd-core/workflows/execute-phase/steps/codebase-drift-gate.md +20 -3
- package/gsd-core/workflows/execute-phase/steps/completion-reconciliation.md +56 -0
- package/gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md +24 -3
- package/gsd-core/workflows/execute-phase/steps/executor-progress-policy.md +43 -0
- package/gsd-core/workflows/execute-phase/steps/per-plan-worktree-gate.md +8 -2
- package/gsd-core/workflows/execute-phase/steps/regression-gate-run.md +2 -0
- package/gsd-core/workflows/execute-phase/steps/sequential-root-pin.md +35 -0
- package/gsd-core/workflows/execute-phase/steps/tdd-applicability-resolution.md +25 -0
- package/gsd-core/workflows/execute-phase/steps/worktree-recovery-policy.md +2 -0
- package/gsd-core/workflows/execute-phase.md +78 -159
- package/gsd-core/workflows/execute-plan.md +28 -15
- package/gsd-core/workflows/explore.md +2 -0
- package/gsd-core/workflows/extract-learnings.md +2 -0
- package/gsd-core/workflows/fast.md +6 -0
- package/gsd-core/workflows/forensics.md +2 -0
- package/gsd-core/workflows/graduation.md +1 -1
- package/gsd-core/workflows/health.md +1 -1
- 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.compact.md +398 -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 +3 -1
- package/gsd-core/workflows/import.md +3 -3
- package/gsd-core/workflows/inbox.md +1 -1
- package/gsd-core/workflows/ingest-docs.md +1 -1
- package/gsd-core/workflows/insert-phase.md +2 -0
- package/gsd-core/workflows/list-phase-assumptions.md +2 -0
- package/gsd-core/workflows/list-seeds.md +2 -0
- package/gsd-core/workflows/list-workspaces.md +2 -0
- package/gsd-core/workflows/manager.md +3 -3
- package/gsd-core/workflows/map-codebase.md +52 -3
- package/gsd-core/workflows/milestone-summary.md +2 -0
- package/gsd-core/workflows/mvp-phase.md +1 -1
- package/gsd-core/workflows/new-milestone.md +55 -13
- package/gsd-core/workflows/new-project/detail/elaboration.md +216 -0
- package/gsd-core/workflows/new-project.md +37 -205
- package/gsd-core/workflows/new-workspace.md +1 -1
- package/gsd-core/workflows/next.md +2 -0
- 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 +19 -4
- package/gsd-core/workflows/plan-phase/detail/elaboration.md +209 -0
- package/gsd-core/workflows/plan-phase/steps/chunked-planning-mode.md +100 -18
- package/gsd-core/workflows/plan-phase/steps/prd-express-path.md +2 -0
- package/gsd-core/workflows/plan-phase/steps/stall-detection-helpers.md +9 -0
- package/gsd-core/workflows/plan-phase.md +144 -185
- package/gsd-core/workflows/plan-review-convergence.md +102 -10
- package/gsd-core/workflows/plant-seed.md +1 -1
- package/gsd-core/workflows/pr-branch.md +30 -10
- package/gsd-core/workflows/profile-user.md +1 -1
- package/gsd-core/workflows/progress/steps/forensic-audit.md +1 -1
- package/gsd-core/workflows/progress.md +25 -3
- package/gsd-core/workflows/quick/steps/plan-checker-loop.md +37 -2
- package/gsd-core/workflows/quick/steps/research-phase.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 +21 -4
- package/gsd-core/workflows/reapply-patches.md +79 -3
- package/gsd-core/workflows/remove-phase.md +2 -0
- package/gsd-core/workflows/remove-workspace.md +1 -1
- package/gsd-core/workflows/resume-project.md +6 -2
- package/gsd-core/workflows/review.md +215 -10
- package/gsd-core/workflows/scan.md +2 -0
- package/gsd-core/workflows/section-manifest.json +12 -0
- package/gsd-core/workflows/secure-phase.md +1 -1
- package/gsd-core/workflows/session-report.md +2 -0
- package/gsd-core/workflows/settings-advanced.md +2 -0
- package/gsd-core/workflows/settings-integrations.md +9 -8
- package/gsd-core/workflows/settings.md +19 -6
- package/gsd-core/workflows/ship.md +10 -10
- package/gsd-core/workflows/sketch-wrap-up.md +2 -0
- package/gsd-core/workflows/sketch.md +1 -1
- package/gsd-core/workflows/smart-entry.md +1 -1
- package/gsd-core/workflows/spec-phase.md +24 -19
- package/gsd-core/workflows/spike-wrap-up.md +2 -0
- package/gsd-core/workflows/spike.md +1 -1
- package/gsd-core/workflows/stats.md +2 -0
- package/gsd-core/workflows/sync-skills.md +12 -4
- package/gsd-core/workflows/thread.md +2 -0
- package/gsd-core/workflows/transition.md +2 -0
- package/gsd-core/workflows/ui-phase.md +26 -5
- package/gsd-core/workflows/ui-review.md +1 -1
- package/gsd-core/workflows/ultraplan-phase.md +2 -0
- package/gsd-core/workflows/undo.md +1 -1
- package/gsd-core/workflows/update.md +48 -43
- package/gsd-core/workflows/validate-phase.md +1 -1
- package/gsd-core/workflows/verify-work/detail/elaboration.md +230 -0
- package/gsd-core/workflows/verify-work.md +68 -182
- package/hooks/dist/gsd-agent-isolation-guard.js +42 -16
- package/hooks/dist/gsd-check-update-worker.js +19 -2
- package/hooks/dist/gsd-context-monitor.js +371 -27
- package/hooks/dist/gsd-cursor-subagent-start.js +34 -14
- package/hooks/dist/gsd-node-runner.sh +1 -0
- package/hooks/dist/gsd-prompt-guard.js +30 -5
- package/hooks/dist/gsd-read-guard.js +2 -0
- package/hooks/dist/gsd-read-injection-scanner.js +5 -5
- package/hooks/dist/gsd-secret-read-guard.js +1105 -0
- package/hooks/dist/gsd-statusline.js +18 -10
- package/hooks/dist/gsd-validate-commit.sh +474 -7
- package/hooks/dist/gsd-workflow-guard.js +2 -1
- package/hooks/dist/gsd-worktree-path-guard.js +25 -14
- package/hooks/dist/gsd-write-guard.js +46 -1
- package/hooks/dist/lib/dispatch-identity.js +187 -0
- package/hooks/dist/lib/filename-classification.js +64 -0
- package/hooks/dist/lib/git-cmd.js +210 -1
- package/hooks/dist/lib/injection-patterns.js +36 -6
- package/hooks/dist/lib/isolation-deny-reason.js +53 -1
- package/hooks/dist/lib/isolation-sentinel.js +58 -19
- package/hooks/dist/managed-hooks-registry.cjs +1 -0
- package/hooks/gsd-agent-isolation-guard.js +42 -16
- package/hooks/gsd-check-update-worker.js +19 -2
- package/hooks/gsd-context-monitor.js +371 -27
- package/hooks/gsd-cursor-subagent-start.js +34 -14
- package/hooks/gsd-node-runner.sh +1 -0
- package/hooks/gsd-prompt-guard.js +30 -5
- package/hooks/gsd-read-guard.js +2 -0
- package/hooks/gsd-read-injection-scanner.js +5 -5
- package/hooks/gsd-secret-read-guard.js +1105 -0
- package/hooks/gsd-statusline.js +18 -10
- package/hooks/gsd-validate-commit.sh +474 -7
- package/hooks/gsd-workflow-guard.js +2 -1
- package/hooks/gsd-worktree-path-guard.js +25 -14
- package/hooks/gsd-write-guard.js +46 -1
- package/hooks/hooks.json +6 -0
- package/hooks/lib/dispatch-identity.js +187 -0
- package/hooks/lib/filename-classification.js +64 -0
- package/hooks/lib/git-cmd.js +210 -1
- package/hooks/lib/injection-patterns.js +36 -6
- package/hooks/lib/isolation-deny-reason.js +53 -1
- package/hooks/lib/isolation-sentinel.js +58 -19
- package/hooks/managed-hooks-registry.cjs +1 -0
- package/package.json +13 -9
- package/scripts/benchmark-compact-content-variants.cjs +298 -0
- package/scripts/benchmark-compact-content.cjs +368 -0
- package/scripts/build-hooks.js +11 -4
- package/scripts/check-contract-drift.cjs +4 -1
- package/scripts/check-env.cjs +36 -8
- package/scripts/check-glossary-refs.cjs +25 -21
- package/scripts/ci-next-health.cjs +271 -0
- package/scripts/ci-prepare-test-scope.cjs +7 -7
- package/scripts/ci-test-scope.cjs +133 -20
- package/scripts/ci-timeout-report.cjs +1 -1
- package/scripts/diff-touches-shipped-paths.cjs +1 -1
- package/scripts/docs-guard-registry.cjs +17 -2
- package/scripts/gen-adr-index.cjs +8 -2
- package/scripts/gen-inventory-manifest.cjs +12 -0
- package/scripts/gen-loop-host-contract.cjs +67 -15
- package/scripts/gen-platform-conformance-tier.cjs +557 -0
- package/scripts/lib/drift-scan.cjs +1 -1
- package/scripts/lib/macos-conformance-tier.generated.cjs +210 -0
- package/scripts/lib/npm-version-check-diagnosis.cjs +59 -0
- package/scripts/lib/platform-conformance-tier.generated.cjs +276 -0
- package/scripts/lib/shellcheck-fetch.cjs +247 -0
- package/scripts/lib/suite-detection.cjs +32 -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-allowed-tools-parity.cjs +221 -0
- package/scripts/lint-docs-guard-registration.exempt-baseline.cjs +24 -2
- package/scripts/lint-phase-enumeration-drift.cjs +24 -6
- package/scripts/lint-phase-id-drift.cjs +465 -15
- package/scripts/lint-portable-grep.cjs +176 -0
- package/scripts/lint-response-language-coverage.cjs +530 -0
- package/scripts/lint-source-test-name-collision.cjs +1 -1
- package/scripts/lint-test-file-count.allowlist.json +4 -1
- package/scripts/lint-vendored-deps.cjs +128 -17
- package/scripts/lint-workflow-shellcheck-baseline.json +1112 -0
- package/scripts/lint-workflow-shellcheck.cjs +614 -0
- package/scripts/npm-audit-baseline.cjs +376 -0
- package/scripts/prompt-injection-scan.sh +22 -0
- package/scripts/require-issue-link-policy.cjs +16 -1
- package/scripts/workflow-size.cjs +139 -0
- package/skills/gsd-cleanup/SKILL.md +1 -0
- package/skills/gsd-code-review/SKILL.md +2 -1
- package/skills/gsd-complete-milestone/SKILL.md +1 -0
- package/skills/gsd-config/SKILL.md +1 -0
- package/skills/gsd-debug/SKILL.md +1 -0
- package/skills/gsd-execute-phase/SKILL.md +1 -1
- package/skills/gsd-graphify/SKILL.md +1 -0
- package/skills/gsd-health/SKILL.md +1 -0
- package/skills/gsd-mempalace-capture/SKILL.md +1 -0
- package/skills/gsd-mempalace-recall/SKILL.md +1 -0
- package/skills/gsd-new-milestone/SKILL.md +1 -0
- package/skills/gsd-new-project/SKILL.md +1 -0
- package/skills/gsd-next/SKILL.md +1 -0
- package/skills/gsd-ns-workflow/SKILL.md +1 -0
- package/skills/gsd-pause-work/SKILL.md +1 -0
- package/skills/gsd-phase/SKILL.md +2 -1
- package/skills/gsd-pr-branch/SKILL.md +1 -0
- package/skills/gsd-quick-batch/SKILL.md +105 -0
- package/skills/gsd-resume-work/SKILL.md +1 -0
- package/skills/gsd-review-backlog/SKILL.md +1 -0
- package/skills/gsd-settings/SKILL.md +2 -1
- package/skills/gsd-stats/SKILL.md +1 -0
- package/skills/gsd-surface/SKILL.md +18 -8
- package/skills/gsd-thread/SKILL.md +1 -0
- package/skills/gsd-workspace/SKILL.md +1 -0
- package/skills/gsd-workstreams/SKILL.md +1 -0
- package/vscode/package.json +1 -1
- package/gsd-core/templates/claude-md.md +0 -145
- package/gsd-core/templates/codebase/concerns.md +0 -310
- package/gsd-core/templates/codebase/conventions.md +0 -307
- package/gsd-core/templates/codebase/integrations.md +0 -280
- package/gsd-core/templates/codebase/structure.md +0 -285
- package/gsd-core/templates/codebase/testing.md +0 -480
- package/gsd-core/templates/debug-subagent-prompt.md +0 -91
- package/gsd-core/templates/discovery.md +0 -146
|
@@ -56,6 +56,7 @@
|
|
|
56
56
|
|
|
57
57
|
const fs = require('fs');
|
|
58
58
|
const path = require('path');
|
|
59
|
+
const { parseDispatchIdentity } = require('./dispatch-identity.js');
|
|
59
60
|
|
|
60
61
|
// Isolation modes ADR-1239 declares (mirrors gsd-tools.cjs
|
|
61
62
|
// routeDispatchIsolation / routeRecordDispatchIsolation).
|
|
@@ -223,26 +224,40 @@ function readSentinel(cwd, { clock = Date } = {}) {
|
|
|
223
224
|
}
|
|
224
225
|
|
|
225
226
|
/**
|
|
226
|
-
* #3045 SECURITY F2: extract the `{plan, phase}` a specific
|
|
227
|
-
* dispatch is FOR
|
|
228
|
-
*
|
|
229
|
-
*
|
|
230
|
-
*
|
|
231
|
-
*
|
|
232
|
-
*
|
|
233
|
-
*
|
|
234
|
-
*
|
|
235
|
-
*
|
|
236
|
-
*
|
|
237
|
-
*
|
|
238
|
-
*
|
|
239
|
-
*
|
|
227
|
+
* #3045 SECURITY F2 / #4594: extract the `{plan, phase}` a specific
|
|
228
|
+
* Agent()/Task() dispatch is FOR. Variadic — accepts any number of text
|
|
229
|
+
* sources (short description, full prompt body, etc) and delegates to
|
|
230
|
+
* `hooks/lib/dispatch-identity.js::parseDispatchIdentity`, the one canonical
|
|
231
|
+
* owner of both the `[gsd:dispatch phase="…" plan="…"]` marker format and its
|
|
232
|
+
* prose fallback (see `.gsd/phase/fix-4594-dispatch-identity-seam/40-design.md`).
|
|
233
|
+
*
|
|
234
|
+
* MARKER-FIRST CONTRACT: producers embed a structured marker carrying the
|
|
235
|
+
* exact shell values the sentinel itself records (`$PHASE_NUMBER`,
|
|
236
|
+
* `$plan_id`), so producer and consumer agree by construction, independent
|
|
237
|
+
* of how the prose reads or whether a model paraphrases the dispatch
|
|
238
|
+
* sentence. Only when no marker is found anywhere in the supplied texts does
|
|
239
|
+
* this fall back to scanning for the prose frame "execute plan <token> of
|
|
240
|
+
* phase <PHASE TOKEN>".
|
|
241
|
+
*
|
|
242
|
+
* The prose fallback is now CORRECT-OR-ABSENT rather than possibly-wrong:
|
|
243
|
+
* the phase token is bounded by the same grammar `src/phase-id.cts` owns
|
|
244
|
+
* (ADR-2121), so a directory-name slug or trailing punctuation can no longer
|
|
245
|
+
* leak into the phase value, and the prose plan token is never reported at
|
|
246
|
+
* all (it lives in a different namespace than the sentinel's phase-prefixed,
|
|
247
|
+
* slugged `plan_id` — reporting it was the #4594 false-mismatch bug).
|
|
248
|
+
*
|
|
249
|
+
* This is still a best-effort, NOT a guaranteed, extraction: a dispatch that
|
|
250
|
+
* carries neither a marker nor a matching prose frame in ANY supplied text
|
|
251
|
+
* returns `{ plan: null, phase: null }`, and the caller MUST NEVER treat
|
|
252
|
+
* that as a mismatch — see `sentinelAppliesToDispatch`, whose whole
|
|
253
|
+
* contract depends on "missing" and "wrong" being distinguishable.
|
|
254
|
+
*
|
|
255
|
+
* Returns only the two-field `{ plan, phase }` shape existing callers
|
|
256
|
+
* depend on — `parseDispatchIdentity`'s `source` field is discarded here.
|
|
240
257
|
*/
|
|
241
|
-
function extractDispatchIdentifiers(
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
if (!m) return { plan: null, phase: null };
|
|
245
|
-
return { plan: m[1], phase: m[2] };
|
|
258
|
+
function extractDispatchIdentifiers(...texts) {
|
|
259
|
+
const { phase, plan } = parseDispatchIdentity(...texts);
|
|
260
|
+
return { plan, phase };
|
|
246
261
|
}
|
|
247
262
|
|
|
248
263
|
/**
|
|
@@ -265,6 +280,29 @@ function sentinelAppliesToDispatch(sentinel, dispatchIds) {
|
|
|
265
280
|
return true;
|
|
266
281
|
}
|
|
267
282
|
|
|
283
|
+
/**
|
|
284
|
+
* #4594 F3: build the structured "a fresh sentinel was present but did not
|
|
285
|
+
* apply to this dispatch" descriptor, mirroring the exact comparison
|
|
286
|
+
* `sentinelAppliesToDispatch` performs. Returns `null` when the sentinel is
|
|
287
|
+
* absent, stale, malformed, or DOES apply — i.e. exactly when there is
|
|
288
|
+
* nothing to report as discarded. Otherwise returns the nested
|
|
289
|
+
* `{ sentinel: {phase, plan}, dispatch: {phase, plan} }` shape, reusing the
|
|
290
|
+
* `{phase, plan}` pair already flowing through this module end to end rather
|
|
291
|
+
* than renaming its fields into an ad hoc `sentinelPhase`/`dispatchPlan` bag
|
|
292
|
+
* (previously rebuilt identically at two call sites in the guard hooks).
|
|
293
|
+
*/
|
|
294
|
+
function buildSentinelDiscard(sentinel, dispatchIds) {
|
|
295
|
+
if (!sentinel || !sentinel.present || sentinel.stale) return null;
|
|
296
|
+
if (sentinelAppliesToDispatch(sentinel, dispatchIds)) return null;
|
|
297
|
+
return {
|
|
298
|
+
sentinel: { phase: sentinel.phase ?? null, plan: sentinel.plan ?? null },
|
|
299
|
+
dispatch: {
|
|
300
|
+
phase: dispatchIds ? (dispatchIds.phase ?? null) : null,
|
|
301
|
+
plan: dispatchIds ? (dispatchIds.plan ?? null) : null,
|
|
302
|
+
},
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
|
|
268
306
|
module.exports = {
|
|
269
307
|
VALID_ISOLATION,
|
|
270
308
|
SENTINEL_RELATIVE_PATH,
|
|
@@ -274,4 +312,5 @@ module.exports = {
|
|
|
274
312
|
readSentinel,
|
|
275
313
|
extractDispatchIdentifiers,
|
|
276
314
|
sentinelAppliesToDispatch,
|
|
315
|
+
buildSentinelDiscard,
|
|
277
316
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@opengsd/gsd-core",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.14.0",
|
|
4
4
|
"description": "GSD Core is a meta-prompting, context engineering, and spec-driven development system for AI coding agents.",
|
|
5
5
|
"main": ".opencode/plugins/gsd-core.js",
|
|
6
6
|
"bin": {
|
|
@@ -75,7 +75,8 @@
|
|
|
75
75
|
"espree": "^10.4.0",
|
|
76
76
|
"fast-check": "^4.8.0",
|
|
77
77
|
"globals": "^16.5.0",
|
|
78
|
-
"
|
|
78
|
+
"gpt-tokenizer": "4.0.0",
|
|
79
|
+
"js-yaml": "^4.3.2",
|
|
79
80
|
"mutation-testing-metrics": "^3.7.3",
|
|
80
81
|
"re2js": "^2.8.6",
|
|
81
82
|
"typescript": "^6.0.3",
|
|
@@ -84,7 +85,8 @@
|
|
|
84
85
|
"overrides": {
|
|
85
86
|
"qs": ">=6.15.2",
|
|
86
87
|
"body-parser": ">=2.3.0",
|
|
87
|
-
"@hono/node-server": ">=2.0.5"
|
|
88
|
+
"@hono/node-server": ">=2.0.5",
|
|
89
|
+
"hono": ">=4.13.5"
|
|
88
90
|
},
|
|
89
91
|
"optionalDependencies": {
|
|
90
92
|
"fallow": "^2.70.0"
|
|
@@ -108,7 +110,7 @@
|
|
|
108
110
|
"gen:registry": "node scripts/gen-registry.cjs --write",
|
|
109
111
|
"gen:install-tree": "node scripts/gen-install-tree-fixtures.cjs",
|
|
110
112
|
"gen:section-manifest": "node scripts/gen-section-manifest.cjs --write",
|
|
111
|
-
"regen:derived": "npm run build && npm run gen:registry && node scripts/gen-adr-index.cjs --write && node scripts/gen-features.cjs --write && node scripts/gen-capability-matrix.cjs --write && node scripts/gen-inventory-manifest.cjs --write && node scripts/gen-context-index.cjs --write && node scripts/gen-state-md-docs.cjs --write && npm run gen:section-manifest && node scripts/sync-manifest-versions.cjs && npm run gen:install-tree && node scripts/gen-scripts-cli-exit.cjs --write && node scripts/gen-hooks-cli-exit.cjs --write && node scripts/gen-exit-code-registry.cjs --write && node scripts/gen-exit-code-docs.cjs --write",
|
|
113
|
+
"regen:derived": "npm run build && npm run gen:registry && node scripts/gen-adr-index.cjs --write && node scripts/gen-features.cjs --write && node scripts/gen-capability-matrix.cjs --write && node scripts/gen-inventory-manifest.cjs --write && node scripts/gen-context-index.cjs --write && node scripts/gen-state-md-docs.cjs --write && npm run gen:section-manifest && node scripts/sync-manifest-versions.cjs && npm run gen:install-tree && node scripts/gen-scripts-cli-exit.cjs --write && node scripts/gen-hooks-cli-exit.cjs --write && node scripts/gen-exit-code-registry.cjs --write && node scripts/gen-exit-code-docs.cjs --write && node scripts/gen-platform-conformance-tier.cjs --write && node scripts/gen-platform-conformance-tier.cjs --target macos --write",
|
|
112
114
|
"validate:registry": "node scripts/validate-registry.cjs",
|
|
113
115
|
"prepack": "npm run build:lib",
|
|
114
116
|
"prepare": "npm run build:lib",
|
|
@@ -121,7 +123,8 @@
|
|
|
121
123
|
"lint:table-schema-drift": "node scripts/lint-table-schema-drift.cjs",
|
|
122
124
|
"lint:frontmatter-scalar-broad-grep": "node scripts/lint-frontmatter-scalar-broad-grep.cjs",
|
|
123
125
|
"lint:removed-but-needed": "node scripts/lint-removed-but-needed.cjs",
|
|
124
|
-
"lint:
|
|
126
|
+
"lint:response-language": "node scripts/lint-response-language-coverage.cjs",
|
|
127
|
+
"lint:ci": "npm run lint && npm run lint:skill-deps && npm run lint:generated-sync && node scripts/lint-test-file-count.cjs && node scripts/lint-command-contract.cjs && node scripts/lint-pr-check-project-dir.cjs && npm run lint:legacy-name && node scripts/lint-regression-test-names.cjs && node scripts/lint-allow-test-rule-refs.cjs && node scripts/lint-resolution-provenance.cjs && node scripts/lint-portable-timeout.cjs && node scripts/lint-portable-grep.cjs && node scripts/lint-allowed-tools-parity.cjs && node scripts/validate-registry.cjs && node scripts/lint-table-schema-drift.cjs && node scripts/lint-fix-has-regression-tests.cjs && node scripts/lint-example-parser-parity.cjs && node scripts/lint-docs-command-form.cjs && node scripts/lint-plan-count-drift.cjs && node scripts/lint-milestone-window-drift.cjs && node scripts/lint-phase-enumeration-drift.cjs && node scripts/lint-planning-prompt-drift.cjs && node scripts/lint-unreachable-guard-drift.cjs && node scripts/lint-completion-ratio-drift.cjs && node scripts/lint-slug-derivation-drift.cjs && node scripts/lint-state-field-drift.cjs && node scripts/lint-state-write-path-drift.cjs && node scripts/lint-completion-predicate-drift.cjs && node scripts/lint-planning-snapshot-bypass-drift.cjs && node scripts/lint-health-diagnostic-rule-table.cjs && node scripts/lint-planning-artifact-writer-drift.cjs && node scripts/lint-frontmatter-scalar-broad-grep.cjs && node scripts/lint-removed-but-needed.cjs && node scripts/lint-no-adhoc-regex-escape.cjs && node scripts/lint-vendored-deps.cjs && node scripts/lint-docs-guard-registration.cjs && node scripts/lint-source-test-name-collision.cjs && npm run lint:hooks-runtime-build-seam && node scripts/check-contract-drift.cjs && node scripts/lint-mutation-test-derivation-drift.cjs && node scripts/lint-seam-enforcement.cjs && node scripts/lint-workflow-shellcheck.cjs && npm run lint:response-language",
|
|
125
128
|
"lint:allow-test-rule-refs": "node scripts/lint-allow-test-rule-refs.cjs",
|
|
126
129
|
"lint:regression-names": "node scripts/lint-regression-test-names.cjs",
|
|
127
130
|
"lint:descriptions": "node scripts/lint-descriptions.cjs",
|
|
@@ -129,7 +132,7 @@
|
|
|
129
132
|
"lint:test-file-count": "node scripts/lint-test-file-count.cjs",
|
|
130
133
|
"lint:pr-checks": "node scripts/lint-pr-check-project-dir.cjs",
|
|
131
134
|
"lint:changeset": "node scripts/changeset/lint.cjs",
|
|
132
|
-
"lint:generated-sync": "node scripts/gen-capability-registry.cjs --check && node scripts/gen-loop-host-contract.cjs --check && node scripts/gen-capability-matrix.cjs --check && node scripts/sync-manifest-versions.cjs --check && node scripts/gen-inventory-manifest.cjs --check && node scripts/generate-package-identity.cjs --check && node scripts/gen-plugin-skills.cjs --check && node scripts/gen-registry.cjs --check && node scripts/gen-adr-index.cjs --check && node scripts/gen-features.cjs --check && node scripts/check-glossary-refs.cjs --check && node scripts/lint-compiled-artifact-sync.cjs --check && node scripts/gen-context-index.cjs --check && node scripts/gen-section-manifest.cjs --check && node scripts/gen-health-docs.cjs --check && node scripts/gen-state-md-docs.cjs --check && node scripts/gen-scripts-cli-exit.cjs --check && node scripts/gen-hooks-cli-exit.cjs --check && node scripts/gen-exit-code-registry.cjs --check && node scripts/gen-exit-code-docs.cjs --check",
|
|
135
|
+
"lint:generated-sync": "node scripts/gen-capability-registry.cjs --check && node scripts/gen-loop-host-contract.cjs --check && node scripts/gen-capability-matrix.cjs --check && node scripts/sync-manifest-versions.cjs --check && node scripts/gen-inventory-manifest.cjs --check && node scripts/generate-package-identity.cjs --check && node scripts/gen-plugin-skills.cjs --check && node scripts/gen-registry.cjs --check && node scripts/gen-adr-index.cjs --check && node scripts/gen-features.cjs --check && node scripts/check-glossary-refs.cjs --check && node scripts/lint-compiled-artifact-sync.cjs --check && node scripts/gen-context-index.cjs --check && node scripts/gen-section-manifest.cjs --check && node scripts/gen-health-docs.cjs --check && node scripts/gen-state-md-docs.cjs --check && node scripts/gen-scripts-cli-exit.cjs --check && node scripts/gen-hooks-cli-exit.cjs --check && node scripts/gen-exit-code-registry.cjs --check && node scripts/gen-exit-code-docs.cjs --check && node scripts/gen-platform-conformance-tier.cjs --check && node scripts/gen-platform-conformance-tier.cjs --target macos --check",
|
|
133
136
|
"lint:docs": "node scripts/lint-docs-required.cjs",
|
|
134
137
|
"lint:qa-smells": "node scripts/qa-smell-ratchet.cjs",
|
|
135
138
|
"lint:legacy-name": "node scripts/lint-legacy-dir-name.cjs",
|
|
@@ -137,6 +140,8 @@
|
|
|
137
140
|
"lint:docs-command-form": "node scripts/lint-docs-command-form.cjs",
|
|
138
141
|
"lint:hooks-runtime-build-seam": "node scripts/lint-hooks-runtime-build-seam.cjs",
|
|
139
142
|
"ci:test-scope": "node scripts/ci-test-scope.cjs",
|
|
143
|
+
"benchmark:compact-content": "node scripts/benchmark-compact-content.cjs --check",
|
|
144
|
+
"benchmark:compact-content-variants": "node scripts/benchmark-compact-content-variants.cjs --check",
|
|
140
145
|
"changeset": "node scripts/changeset/new.cjs",
|
|
141
146
|
"changelog:render": "node scripts/changeset/cli.cjs render",
|
|
142
147
|
"test": "node scripts/run-tests.cjs",
|
|
@@ -148,13 +153,12 @@
|
|
|
148
153
|
"test:qa": "node scripts/run-tests.cjs --suite qa",
|
|
149
154
|
"test:affected": "node scripts/run-affected-tests.cjs",
|
|
150
155
|
"test:coverage": "c8 --check-coverage --lines 70 --branches 60 --reporter text --include 'gsd-core/bin/lib/**/*.cjs' --exclude 'tests/**' --all node scripts/run-tests.cjs",
|
|
151
|
-
"test:coverage:scripts-floor": "c8 check-coverage --lines 55 --include 'scripts/**/*.cjs' --exclude 'tests/**' --all",
|
|
156
|
+
"test:coverage:scripts-floor": "c8 report --check-coverage --lines 55 --merge-async --include 'scripts/**/*.cjs' --exclude 'tests/**' --all",
|
|
152
157
|
"test:coverage:unit": "c8 --reporter text --reporter json-summary --merge-async --include 'gsd-core/bin/lib/**/*.cjs' --exclude 'tests/**' --all node scripts/run-tests.cjs --suite unit && node scripts/check-coverage-gate.cjs",
|
|
153
|
-
"test:coverage:unit:raw": "c8 --reporter none node scripts/run-tests.cjs --suite unit",
|
|
158
|
+
"test:coverage:unit:raw": "c8 --reporter none --merge-async node scripts/run-tests.cjs --suite unit",
|
|
154
159
|
"test:coverage:report": "c8 report --reporter text --reporter json-summary --merge-async --include 'gsd-core/bin/lib/**/*.cjs' --exclude 'tests/**' --all && node scripts/check-coverage-gate.cjs",
|
|
155
160
|
"test:coverage:all": "npm run test:coverage",
|
|
156
161
|
"test:mutation": "stryker run",
|
|
157
|
-
"test:mutation:since": "stryker run --incremental --since origin/next",
|
|
158
162
|
"gen:features": "node scripts/gen-features.cjs",
|
|
159
163
|
"gen:state-md-docs": "node scripts/gen-state-md-docs.cjs"
|
|
160
164
|
},
|
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Benchmarks the token-count effect of every registered variant-swap pair
|
|
6
|
+
* (ADR-4139, Phase 6 #4406 — `gsd-core/workflows/<name>/{modes,steps,templates}/*.compact.md`
|
|
7
|
+
* and `gsd-core/templates/**\/*.compact.md`). Sibling to
|
|
8
|
+
* `scripts/benchmark-compact-content.cjs` (Phase 3's spine/detail benchmark) rather than an
|
|
9
|
+
* extension of it — the two are different data shapes (a variant pair is two independent,
|
|
10
|
+
* deliberately-overlapping complete files; a spine/detail split is one document partitioned in
|
|
11
|
+
* two disjoint halves), and mixing them into one report would conflate an "off" total that means
|
|
12
|
+
* something different in each case.
|
|
13
|
+
*
|
|
14
|
+
* For every registered pair it reports the "off" token count (the canonical file — what
|
|
15
|
+
* `workflow.compact_content=false`, the default, pays at that call site) against the "on" token
|
|
16
|
+
* count (the `.compact.md` sibling — what `workflow.compact_content=true` pays once the gate
|
|
17
|
+
* resolves to it). See `gsd-core/references/compact-content-gate.md` §"Streams 1b and 4" for the
|
|
18
|
+
* resolution rule this measures.
|
|
19
|
+
*
|
|
20
|
+
* PROXY-TOKENIZER CAVEAT: same as the sibling script — `gpt-tokenizer` is a stand-in tokenizer;
|
|
21
|
+
* Anthropic publishes none for Claude 3+. The on/off comparison is exact under this one pinned
|
|
22
|
+
* tokenizer applied identically to both sides; the absolute counts are not Claude's real counts.
|
|
23
|
+
*
|
|
24
|
+
* Discovery is REIMPLEMENTED here rather than imported from
|
|
25
|
+
* `tests/helpers/compact-content-variant.cjs`, for the same reason
|
|
26
|
+
* `benchmark-compact-content.cjs` reimplements spine/detail discovery instead of importing it: a
|
|
27
|
+
* `scripts/` reporting tool depending on a test-only helper module inverts this repo's normal
|
|
28
|
+
* layering, and a test-only module changing shape should never be able to break a benchmark.
|
|
29
|
+
*
|
|
30
|
+
* Usage:
|
|
31
|
+
* node scripts/benchmark-compact-content-variants.cjs # print JSON to stdout
|
|
32
|
+
* node scripts/benchmark-compact-content-variants.cjs --write # write the committed baseline
|
|
33
|
+
* node scripts/benchmark-compact-content-variants.cjs --check # recompute, diff vs committed baseline
|
|
34
|
+
* node scripts/benchmark-compact-content-variants.cjs --check --baseline-path=<path>
|
|
35
|
+
*
|
|
36
|
+
* Same CRITICAL contract as the sibling script: this — and `--check` especially — MUST NEVER
|
|
37
|
+
* exit non-zero because a baseline is drifted, stale, or missing. Only a genuine I/O error
|
|
38
|
+
* reading a SOURCE file the benchmark measures may throw. This is a reporting instrument, never
|
|
39
|
+
* a gate.
|
|
40
|
+
*/
|
|
41
|
+
|
|
42
|
+
const fs = require('node:fs');
|
|
43
|
+
const path = require('node:path');
|
|
44
|
+
|
|
45
|
+
const { countTokens } = require('gpt-tokenizer');
|
|
46
|
+
const { runMain } = require('./lib/cli-exit.cjs');
|
|
47
|
+
|
|
48
|
+
const ROOT = path.resolve(__dirname, '..');
|
|
49
|
+
// #4407: agents/ joins the scan. Reachability there is a code seam
|
|
50
|
+
// (cmdAgentSkills), not a markdown literal reference, but token accounting
|
|
51
|
+
// doesn't care how a pair is reached — only that it's registered.
|
|
52
|
+
const VARIANT_ROOTS = [
|
|
53
|
+
path.join(ROOT, 'gsd-core', 'workflows'),
|
|
54
|
+
path.join(ROOT, 'gsd-core', 'templates'),
|
|
55
|
+
path.join(ROOT, 'agents'),
|
|
56
|
+
];
|
|
57
|
+
const BASELINE_PATH = path.join(ROOT, 'tests', 'fixtures', 'compact-content-variant-benchmark-baseline.json');
|
|
58
|
+
const COMPACT_SUFFIX = '.compact.md';
|
|
59
|
+
|
|
60
|
+
function getTokenizerVersion() {
|
|
61
|
+
const pkgPath = require.resolve('gpt-tokenizer/package.json');
|
|
62
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
|
|
63
|
+
return pkg.version;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Discover every registered variant pair under `roots`: a `.compact.md` file
|
|
68
|
+
* with a same-directory, same-stem canonical `.md` sibling. Mirrors
|
|
69
|
+
* `tests/helpers/compact-content-variant.cjs`'s `discoverRegisteredVariants`
|
|
70
|
+
* in shape but is a from-scratch, self-contained implementation (see module
|
|
71
|
+
* header for why this is not a shared import). Skips an orphaned compact
|
|
72
|
+
* file with no canonical sibling — that is the guard's problem, not this
|
|
73
|
+
* benchmark's; a pair with no canonical baseline has no "off" number to
|
|
74
|
+
* report against.
|
|
75
|
+
*
|
|
76
|
+
* @param {string[]} [roots]
|
|
77
|
+
* @returns {Array<{name: string, canonicalPath: string, compactPath: string}>}
|
|
78
|
+
*/
|
|
79
|
+
function discoverRegisteredVariants(roots = VARIANT_ROOTS) {
|
|
80
|
+
const pairs = [];
|
|
81
|
+
|
|
82
|
+
function walk(dir) {
|
|
83
|
+
let entries;
|
|
84
|
+
try {
|
|
85
|
+
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
86
|
+
} catch {
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
for (const entry of entries) {
|
|
90
|
+
const full = path.join(dir, entry.name);
|
|
91
|
+
if (entry.isDirectory()) {
|
|
92
|
+
walk(full);
|
|
93
|
+
} else if (entry.isFile() && entry.name.endsWith(COMPACT_SUFFIX)) {
|
|
94
|
+
const stem = entry.name.slice(0, -COMPACT_SUFFIX.length);
|
|
95
|
+
const canonicalPath = path.join(dir, `${stem}.md`);
|
|
96
|
+
if (fs.existsSync(canonicalPath)) {
|
|
97
|
+
const name = path.relative(ROOT, canonicalPath).split(path.sep).join('/');
|
|
98
|
+
pairs.push({ name, canonicalPath, compactPath: full });
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
for (const root of roots) walk(root);
|
|
105
|
+
return pairs.sort((a, b) => a.name.localeCompare(b.name));
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Compute the off/on/reduction numbers for ONE registered variant pair.
|
|
110
|
+
* Throws on a genuine read failure of a source file (the one thing allowed
|
|
111
|
+
* to throw, per the module-header CRITICAL note).
|
|
112
|
+
*
|
|
113
|
+
* @param {{canonicalPath: string, compactPath: string}} pair
|
|
114
|
+
* @returns {{offTokens: number, onTokens: number, reductionPct: number}}
|
|
115
|
+
*/
|
|
116
|
+
function computePairTokens(pair) {
|
|
117
|
+
const offTokens = countTokens(fs.readFileSync(pair.canonicalPath, 'utf8'));
|
|
118
|
+
const onTokens = countTokens(fs.readFileSync(pair.compactPath, 'utf8'));
|
|
119
|
+
const reductionPct = offTokens === 0 ? 0 : round2(((offTokens - onTokens) / offTokens) * 100);
|
|
120
|
+
return { offTokens, onTokens, reductionPct };
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function round2(n) {
|
|
124
|
+
return Math.round(n * 100) / 100;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Aggregate per-pair numbers from SUMMED off/on totals, never averaged
|
|
129
|
+
* per-pair percentages. Reports `0` (not `NaN`) for zero registered pairs.
|
|
130
|
+
* @param {Record<string, {offTokens: number, onTokens: number}>} pairResults
|
|
131
|
+
*/
|
|
132
|
+
function computeAggregate(pairResults) {
|
|
133
|
+
let offTokens = 0;
|
|
134
|
+
let onTokens = 0;
|
|
135
|
+
for (const key of Object.keys(pairResults)) {
|
|
136
|
+
offTokens += pairResults[key].offTokens;
|
|
137
|
+
onTokens += pairResults[key].onTokens;
|
|
138
|
+
}
|
|
139
|
+
const reductionPct = offTokens === 0 ? 0 : round2(((offTokens - onTokens) / offTokens) * 100);
|
|
140
|
+
return { offTokens, onTokens, reductionPct };
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const LABEL =
|
|
144
|
+
"PROXY-TOKENIZER DELTA — gpt-tokenizer is a stand-in; Anthropic publishes no tokenizer for Claude 3+. " +
|
|
145
|
+
"The on/off COMPARISON is exact under this pinned tokenizer; absolute counts are not Claude's real token counts.";
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* @param {string[]} [roots]
|
|
149
|
+
* @returns {object}
|
|
150
|
+
*/
|
|
151
|
+
function buildReport(roots = VARIANT_ROOTS) {
|
|
152
|
+
const pairs = discoverRegisteredVariants(roots);
|
|
153
|
+
const pairReports = {};
|
|
154
|
+
for (const pair of pairs) {
|
|
155
|
+
pairReports[pair.name] = computePairTokens(pair);
|
|
156
|
+
}
|
|
157
|
+
return {
|
|
158
|
+
schema_version: 1,
|
|
159
|
+
generated_by: 'scripts/benchmark-compact-content-variants.cjs',
|
|
160
|
+
tokenizer: { name: 'gpt-tokenizer', version: getTokenizerVersion() },
|
|
161
|
+
label: LABEL,
|
|
162
|
+
pairs: pairReports,
|
|
163
|
+
aggregate: computeAggregate(pairReports),
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Format a human-readable drift summary between a (possibly missing/invalid)
|
|
169
|
+
* committed baseline and a freshly-computed live report. Never throws.
|
|
170
|
+
* @param {string} baselinePath
|
|
171
|
+
* @param {object} live
|
|
172
|
+
* @returns {string}
|
|
173
|
+
*/
|
|
174
|
+
function formatDriftReport(baselinePath, live) {
|
|
175
|
+
const lines = [];
|
|
176
|
+
let baseline = null;
|
|
177
|
+
let baselineReadError = null;
|
|
178
|
+
try {
|
|
179
|
+
const raw = fs.readFileSync(baselinePath, 'utf8');
|
|
180
|
+
try {
|
|
181
|
+
baseline = JSON.parse(raw);
|
|
182
|
+
} catch (parseErr) {
|
|
183
|
+
baselineReadError = `baseline at ${baselinePath} could not be parsed as JSON: ${parseErr.message}`;
|
|
184
|
+
}
|
|
185
|
+
} catch {
|
|
186
|
+
baselineReadError = `no baseline found at ${baselinePath}`;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
if (baselineReadError) {
|
|
190
|
+
lines.push(`DRIFT: ${baselineReadError} — treating as fully drifted (this is reported, not an error).`);
|
|
191
|
+
lines.push('Live pairs:');
|
|
192
|
+
for (const name of Object.keys(live.pairs).sort()) {
|
|
193
|
+
const p = live.pairs[name];
|
|
194
|
+
lines.push(` + ${name}: off=${p.offTokens} on=${p.onTokens} reduction=${p.reductionPct}%`);
|
|
195
|
+
}
|
|
196
|
+
lines.push(
|
|
197
|
+
`Live aggregate: off=${live.aggregate.offTokens} on=${live.aggregate.onTokens} ` +
|
|
198
|
+
`reduction=${live.aggregate.reductionPct}%`,
|
|
199
|
+
);
|
|
200
|
+
return lines.join('\n');
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
const baselinePairs = (baseline && typeof baseline === 'object' && baseline.pairs) || {};
|
|
204
|
+
const livePairs = live.pairs;
|
|
205
|
+
const allNames = new Set([...Object.keys(baselinePairs), ...Object.keys(livePairs)]);
|
|
206
|
+
let anyDrift = false;
|
|
207
|
+
|
|
208
|
+
if (!baseline || typeof baseline.label !== 'string' || !baseline.label.includes('PROXY-TOKENIZER')) {
|
|
209
|
+
anyDrift = true;
|
|
210
|
+
lines.push('DRIFT: committed baseline is missing the required "PROXY-TOKENIZER" label.');
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
for (const name of [...allNames].sort()) {
|
|
214
|
+
const b = baselinePairs[name];
|
|
215
|
+
const l = livePairs[name];
|
|
216
|
+
if (!b) {
|
|
217
|
+
anyDrift = true;
|
|
218
|
+
lines.push(`DRIFT: pair "${name}" is new (not in committed baseline) — live off=${l.offTokens} on=${l.onTokens} reduction=${l.reductionPct}%`);
|
|
219
|
+
} else if (!l) {
|
|
220
|
+
anyDrift = true;
|
|
221
|
+
lines.push(`DRIFT: pair "${name}" was removed (present in committed baseline, not found live) — baseline off=${b.offTokens} on=${b.onTokens} reduction=${b.reductionPct}%`);
|
|
222
|
+
} else if (b.offTokens !== l.offTokens || b.onTokens !== l.onTokens || b.reductionPct !== l.reductionPct) {
|
|
223
|
+
anyDrift = true;
|
|
224
|
+
lines.push(
|
|
225
|
+
`DRIFT: pair "${name}": off ${b.offTokens} -> ${l.offTokens} (${l.offTokens - b.offTokens >= 0 ? '+' : ''}${l.offTokens - b.offTokens}), ` +
|
|
226
|
+
`on ${b.onTokens} -> ${l.onTokens} (${l.onTokens - b.onTokens >= 0 ? '+' : ''}${l.onTokens - b.onTokens}), ` +
|
|
227
|
+
`reduction ${b.reductionPct}% -> ${l.reductionPct}% (${round2(l.reductionPct - b.reductionPct) >= 0 ? '+' : ''}${round2(l.reductionPct - b.reductionPct)}pp)`,
|
|
228
|
+
);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
const ba = (baseline && baseline.aggregate) || {};
|
|
233
|
+
const la = live.aggregate;
|
|
234
|
+
if (ba.offTokens !== la.offTokens || ba.onTokens !== la.onTokens || ba.reductionPct !== la.reductionPct) {
|
|
235
|
+
anyDrift = true;
|
|
236
|
+
lines.push(
|
|
237
|
+
`DRIFT: aggregate: off ${ba.offTokens} -> ${la.offTokens}, on ${ba.onTokens} -> ${la.onTokens}, ` +
|
|
238
|
+
`reduction ${ba.reductionPct}% -> ${la.reductionPct}%`,
|
|
239
|
+
);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
if (!anyDrift) {
|
|
243
|
+
lines.push(`Baseline at ${baselinePath} is up to date with the live recompute.`);
|
|
244
|
+
} else {
|
|
245
|
+
lines.push('');
|
|
246
|
+
lines.push('Run `node scripts/benchmark-compact-content-variants.cjs --write` to refresh the committed baseline.');
|
|
247
|
+
lines.push('(This is a REPORT, not a gate — exiting 0 regardless of drift, per this script\'s own contract.)');
|
|
248
|
+
}
|
|
249
|
+
return lines.join('\n');
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
function parseArgs(argv) {
|
|
253
|
+
const opts = { write: false, check: false, baselinePath: BASELINE_PATH };
|
|
254
|
+
for (const arg of argv) {
|
|
255
|
+
if (arg === '--write') opts.write = true;
|
|
256
|
+
else if (arg === '--check') opts.check = true;
|
|
257
|
+
else if (arg.startsWith('--baseline-path=')) opts.baselinePath = arg.slice('--baseline-path='.length);
|
|
258
|
+
}
|
|
259
|
+
return opts;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
function main() {
|
|
263
|
+
const opts = parseArgs(process.argv.slice(2));
|
|
264
|
+
|
|
265
|
+
if (opts.write) {
|
|
266
|
+
const report = buildReport();
|
|
267
|
+
fs.mkdirSync(path.dirname(BASELINE_PATH), { recursive: true });
|
|
268
|
+
fs.writeFileSync(BASELINE_PATH, JSON.stringify(report, null, 2) + '\n');
|
|
269
|
+
process.stdout.write(`Wrote ${BASELINE_PATH}\n`);
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
if (opts.check) {
|
|
274
|
+
const live = buildReport();
|
|
275
|
+
process.stdout.write(formatDriftReport(opts.baselinePath, live) + '\n');
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
process.stdout.write(JSON.stringify(buildReport(), null, 2) + '\n');
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/* c8 ignore next 3 -- CLI entry guard; this repo measures coverage with c8, which does not honor istanbul pragmas */
|
|
283
|
+
if (require.main === module) {
|
|
284
|
+
runMain(main);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
module.exports = {
|
|
288
|
+
discoverRegisteredVariants,
|
|
289
|
+
computePairTokens,
|
|
290
|
+
computeAggregate,
|
|
291
|
+
buildReport,
|
|
292
|
+
formatDriftReport,
|
|
293
|
+
getTokenizerVersion,
|
|
294
|
+
parseArgs,
|
|
295
|
+
LABEL,
|
|
296
|
+
BASELINE_PATH,
|
|
297
|
+
VARIANT_ROOTS,
|
|
298
|
+
};
|