@opengsd/gsd-core 1.9.1 → 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 +2 -3
- package/.opencode/plugins/gsd-core.js +8 -1
- package/agents/gsd-code-fixer.md +27 -3
- package/agents/gsd-debug-session-manager.md +11 -0
- package/agents/gsd-debugger.md +12 -246
- package/agents/gsd-doc-synthesizer.md +2 -4
- package/agents/gsd-executor.md +12 -10
- package/agents/gsd-integration-checker.md +3 -0
- package/agents/gsd-mempalace-curator.md +5 -2
- package/agents/gsd-phase-researcher.md +20 -1
- package/agents/gsd-plan-checker.md +46 -0
- package/agents/gsd-planner.md +49 -54
- package/agents/gsd-roadmapper.md +21 -3
- package/agents/gsd-user-profiler.md +3 -0
- package/agents/gsd-verifier.md +26 -73
- package/bin/install.js +1272 -1238
- 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 +2 -2
- package/commands/gsd/mempalace-recall.md +1 -1
- package/commands/gsd/new-milestone.md +2 -2
- package/commands/gsd/plan-phase.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 +1009 -115
- package/gsd-core/bin/lib/active-workstream-store.cjs +153 -12
- package/gsd-core/bin/lib/agent-install-check.cjs +268 -38
- package/gsd-core/bin/lib/api-coverage.cjs +123 -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 +926 -202
- package/gsd-core/bin/lib/broken-windows.cjs +36 -6
- 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 +608 -148
- 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 +507 -24
- package/gsd-core/bin/lib/capability-writer.cjs +3 -2
- package/gsd-core/bin/lib/check-command-router.cjs +114 -38
- 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 +94 -0
- package/gsd-core/bin/lib/command-roster.cjs +44 -1
- package/gsd-core/bin/lib/commands.cjs +665 -99
- package/gsd-core/bin/lib/commonjs-marker.cjs +142 -0
- package/gsd-core/bin/lib/complexity-trigger.cjs +1172 -0
- package/gsd-core/bin/lib/config-loader.cjs +76 -0
- package/gsd-core/bin/lib/config.cjs +22 -2
- package/gsd-core/bin/lib/context-composer.cjs +278 -0
- package/gsd-core/bin/lib/context-predicates.cjs +506 -0
- package/gsd-core/bin/lib/core-utils.cjs +217 -40
- package/gsd-core/bin/lib/decisions.cjs +23 -0
- package/gsd-core/bin/lib/docs.cjs +3 -2
- package/gsd-core/bin/lib/external-job.cjs +19 -4
- package/gsd-core/bin/lib/fallow-runner.cjs +20 -44
- package/gsd-core/bin/lib/frontmatter.cjs +239 -32
- package/gsd-core/bin/lib/gap-checker.cjs +68 -7
- package/gsd-core/bin/lib/gate-predicate-evaluator.cjs +57 -6
- package/gsd-core/bin/lib/git-base-branch.cjs +160 -15
- package/gsd-core/bin/lib/graphify.cjs +142 -27
- package/gsd-core/bin/lib/gsd2-import.cjs +37 -5
- 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-integration.cjs +13 -1
- package/gsd-core/bin/lib/host-runtime-detection.cjs +134 -0
- package/gsd-core/bin/lib/init-command-router.cjs +83 -8
- package/gsd-core/bin/lib/init.cjs +1325 -169
- package/gsd-core/bin/lib/install-effort-resolver.cjs +73 -30
- package/gsd-core/bin/lib/install-engine.cjs +805 -264
- 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 +160 -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-migration-authoring.cjs +3 -1
- package/gsd-core/bin/lib/installer-migration-report.cjs +4 -0
- package/gsd-core/bin/lib/installer-migrations/007-retire-config-root-commonjs-marker.cjs +149 -0
- package/gsd-core/bin/lib/installer-migrations/008-cursor-retire-commands-surface.cjs +55 -0
- package/gsd-core/bin/lib/installer-migrations/009-pi-retire-reserved-hooks-dir.cjs +199 -0
- package/gsd-core/bin/lib/installer-migrations.cjs +206 -13
- package/gsd-core/bin/lib/io.cjs +38 -3
- package/gsd-core/bin/lib/markdown-sectionizer.cjs +8 -1
- package/gsd-core/bin/lib/markdown-table.cjs +133 -20
- package/gsd-core/bin/lib/mcp-catalog.cjs +518 -0
- package/gsd-core/bin/lib/mcp-server.cjs +135 -3
- package/gsd-core/bin/lib/milestone-lock.cjs +248 -0
- package/gsd-core/bin/lib/milestone.cjs +821 -109
- 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 +507 -36
- package/gsd-core/bin/lib/phase-lifecycle.cjs +28 -3
- package/gsd-core/bin/lib/phase-locator.cjs +258 -58
- package/gsd-core/bin/lib/phase.cjs +891 -156
- package/gsd-core/bin/lib/plan-dependency-graph.cjs +303 -0
- 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 +60 -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/prompt-budget.cjs +128 -165
- package/gsd-core/bin/lib/refactor-trigger-command-router.cjs +740 -0
- package/gsd-core/bin/lib/retired-artifact-cleanup.cjs +85 -0
- package/gsd-core/bin/lib/review-lane-descriptor.cjs +108 -0
- package/gsd-core/bin/lib/review-lane-invocation.cjs +30 -0
- package/gsd-core/bin/lib/review-lane-runner.cjs +447 -68
- package/gsd-core/bin/lib/review-reviewer-selection.cjs +13 -18
- package/gsd-core/bin/lib/roadmap-command-router.cjs +76 -9
- package/gsd-core/bin/lib/roadmap-parser.cjs +1035 -194
- package/gsd-core/bin/lib/roadmap-upgrade.cjs +37 -10
- package/gsd-core/bin/lib/roadmap.cjs +405 -84
- package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +795 -100
- package/gsd-core/bin/lib/runtime-artifact-install-plan.cjs +14 -2
- package/gsd-core/bin/lib/runtime-artifact-layout.cjs +440 -57
- package/gsd-core/bin/lib/runtime-config-adapter-registry.cjs +3 -2
- package/gsd-core/bin/lib/runtime-homes.cjs +220 -41
- package/gsd-core/bin/lib/runtime-hooks-surface.cjs +220 -44
- 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/section-manifest.cjs +209 -0
- package/gsd-core/bin/lib/security.cjs +104 -5
- package/gsd-core/bin/lib/shell-command-projection.cjs +388 -30
- package/gsd-core/bin/lib/smart-entry.cjs +154 -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 +424 -105
- package/gsd-core/bin/lib/state.cjs +1927 -401
- package/gsd-core/bin/lib/surface.cjs +35 -10
- 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 +20 -4
- package/gsd-core/bin/lib/uat.cjs +706 -64
- 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 +33 -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 +287 -20
- package/gsd-core/bin/lib/verify.cjs +368 -880
- package/gsd-core/bin/lib/workflow-fragments.cjs +557 -0
- package/gsd-core/bin/lib/workstream-inventory-builder.cjs +203 -19
- package/gsd-core/bin/lib/workstream-inventory.cjs +576 -31
- package/gsd-core/bin/lib/workstream.cjs +8 -2
- package/gsd-core/bin/lib/worktree-base-ref.cjs +50 -6
- package/gsd-core/bin/lib/worktree-safety.cjs +450 -125
- package/gsd-core/bin/shared/config-defaults.manifest.json +1 -0
- package/gsd-core/bin/shared/config-schema.manifest.json +9 -1
- package/gsd-core/references/agent-contracts.md +43 -26
- package/gsd-core/references/artifact-types.md +10 -3
- package/gsd-core/references/autonomous-ui-design-contract.md +42 -0
- package/gsd-core/references/checkpoints.md +2 -2
- package/gsd-core/references/context-budget.md +1 -1
- package/gsd-core/references/debugger-techniques.md +255 -0
- 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/research-documentation-lookup.md +5 -3
- package/gsd-core/references/revision-loop.md +1 -1
- package/gsd-core/references/specless-probe-fallback.md +8 -7
- package/gsd-core/references/universal-anti-patterns.md +3 -3
- package/gsd-core/references/verifier-phase-gates.md +192 -0
- package/gsd-core/references/verifier-wiring-patterns.md +100 -0
- package/gsd-core/references/verify-mvp-mode.md +1 -1
- package/gsd-core/references/workstream-flag.md +22 -6
- package/gsd-core/references/worktree-branch-check.md +2 -2
- 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/summary-complex.md +2 -0
- package/gsd-core/templates/summary-minimal.md +2 -0
- package/gsd-core/templates/summary-standard.md +2 -0
- package/gsd-core/templates/summary.md +2 -0
- package/gsd-core/templates/verification-report.md +9 -1
- package/gsd-core/workflows/ai-integration-phase.md +9 -11
- package/gsd-core/workflows/audit-milestone.md +3 -0
- package/gsd-core/workflows/autonomous/steps/converge-banner.md +1 -0
- package/gsd-core/workflows/autonomous/steps/converge-dispatch-bg.md +11 -0
- package/gsd-core/workflows/autonomous/steps/converge-dispatch-inline.md +7 -0
- package/gsd-core/workflows/autonomous/steps/converge-fail-fast.md +21 -0
- package/gsd-core/workflows/autonomous/steps/converge-loop.md +7 -0
- package/gsd-core/workflows/autonomous.md +33 -70
- package/gsd-core/workflows/cleanup.md +62 -3
- package/gsd-core/workflows/code-review/steps/dispatch-fix.md +39 -0
- package/gsd-core/workflows/code-review/steps/structural-pre-pass.md +93 -0
- package/gsd-core/workflows/code-review-fix.md +37 -10
- package/gsd-core/workflows/code-review.md +74 -166
- package/gsd-core/workflows/complete-milestone/steps/git-tag.md +29 -0
- package/gsd-core/workflows/complete-milestone.md +160 -95
- package/gsd-core/workflows/debug.md +16 -17
- package/gsd-core/workflows/diagnose-issues.md +56 -8
- 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/steps/auto-advance-dispatch.md +15 -0
- package/gsd-core/workflows/discuss-phase-assumptions.md +7 -17
- package/gsd-core/workflows/docs-update/steps/dispatch-monorepo-packages.md +51 -0
- package/gsd-core/workflows/docs-update.md +8 -51
- 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 +64 -7
- package/gsd-core/workflows/execute-phase/steps/gap-closure-artifacts.md +50 -0
- package/gsd-core/workflows/execute-phase/steps/partial-wave.md +31 -0
- 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 +21 -0
- package/gsd-core/workflows/execute-phase/steps/regression-gate-run.md +42 -0
- package/gsd-core/workflows/execute-phase/steps/regression-gate.md +43 -37
- package/gsd-core/workflows/execute-phase.md +103 -187
- 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/help/modes/full.md +6 -1
- package/gsd-core/workflows/import.md +4 -4
- package/gsd-core/workflows/ingest-docs.md +7 -6
- package/gsd-core/workflows/mvp-phase.md +6 -3
- package/gsd-core/workflows/new-milestone/steps/project-md-milestone-write.md +16 -0
- package/gsd-core/workflows/new-milestone/steps/reset-phase-safety.md +19 -0
- package/gsd-core/workflows/new-milestone.md +35 -47
- package/gsd-core/workflows/new-project/steps/auto-mode-config.md +176 -0
- package/gsd-core/workflows/new-project/steps/auto-mode-detection.md +32 -0
- package/gsd-core/workflows/new-project/steps/codebase-map-offer.md +18 -0
- package/gsd-core/workflows/new-project.md +27 -240
- package/gsd-core/workflows/next.md +12 -0
- package/gsd-core/workflows/plan-phase/steps/adr-ingest-express-path.md +15 -0
- package/gsd-core/workflows/plan-phase/steps/chunked-planning-mode.md +110 -0
- package/gsd-core/workflows/plan-phase/steps/prd-express-gate.md +8 -0
- package/gsd-core/workflows/plan-phase/steps/research-only-early-exit.md +17 -0
- package/gsd-core/workflows/plan-phase/steps/research-only-modifiers.md +16 -0
- package/gsd-core/workflows/plan-phase/steps/reviews-prerequisite.md +17 -0
- package/gsd-core/workflows/plan-phase/steps/stall-detection-helpers.md +149 -0
- package/gsd-core/workflows/plan-phase.md +89 -209
- package/gsd-core/workflows/plan-review-convergence.md +50 -2
- package/gsd-core/workflows/progress/steps/forensic-audit.md +125 -0
- package/gsd-core/workflows/progress/steps/mvp-display.md +18 -0
- package/gsd-core/workflows/progress.md +45 -159
- package/gsd-core/workflows/quick/steps/discussion-phase.md +124 -0
- package/gsd-core/workflows/quick/steps/plan-checker-loop.md +111 -0
- package/gsd-core/workflows/quick/steps/quick-verification.md +67 -0
- package/gsd-core/workflows/quick/steps/research-phase.md +72 -0
- package/gsd-core/workflows/quick/steps/worktree-pre-dispatch-commit.md +37 -0
- package/gsd-core/workflows/quick.md +55 -405
- package/gsd-core/workflows/resume-project.md +3 -0
- package/gsd-core/workflows/review/steps/reviewer-instances-note-1.md +4 -0
- package/gsd-core/workflows/review/steps/reviewer-instances-note-2.md +3 -0
- package/gsd-core/workflows/review.md +41 -13
- package/gsd-core/workflows/section-manifest.json +219 -0
- 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/sketch.md +1 -1
- package/gsd-core/workflows/spec-phase.md +41 -20
- package/gsd-core/workflows/spike-wrap-up.md +20 -5
- package/gsd-core/workflows/spike.md +50 -16
- package/gsd-core/workflows/sync-skills.md +106 -13
- package/gsd-core/workflows/transition/steps/workstream-collision-check.md +17 -0
- package/gsd-core/workflows/transition.md +53 -31
- package/gsd-core/workflows/ui-phase.md +13 -12
- package/gsd-core/workflows/ui-review.md +2 -2
- package/gsd-core/workflows/update/steps/channel-banner.md +7 -0
- package/gsd-core/workflows/update.md +19 -8
- package/gsd-core/workflows/validate-phase.md +1 -1
- package/gsd-core/workflows/verify-work/steps/automated-ui-verification.md +36 -0
- package/gsd-core/workflows/verify-work/steps/mvp-uat-framing.md +21 -0
- package/gsd-core/workflows/verify-work.md +17 -65
- package/hooks/dist/gsd-agent-isolation-guard.js +517 -0
- package/hooks/dist/gsd-check-update-worker.js +64 -12
- 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 +607 -26
- 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 +45 -24
- package/hooks/dist/gsd-statusline.js +90 -6
- package/hooks/dist/gsd-update-banner.js +22 -1
- package/hooks/dist/gsd-workflow-guard.js +134 -36
- package/hooks/dist/gsd-worktree-path-guard.js +2 -1
- package/hooks/dist/gsd-write-guard.js +359 -0
- 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 +277 -0
- package/hooks/dist/managed-hooks-registry.cjs +2 -0
- package/hooks/gsd-agent-isolation-guard.js +517 -0
- package/hooks/gsd-check-update-worker.js +64 -12
- package/hooks/gsd-check-update.js +19 -1
- package/hooks/gsd-cursor-pre-tool.js +0 -3
- package/hooks/gsd-cursor-subagent-start.js +607 -26
- package/hooks/gsd-cursor-subagent-stop.js +3 -2
- package/hooks/gsd-prompt-guard.js +21 -20
- package/hooks/gsd-read-injection-scanner.js +45 -24
- package/hooks/gsd-statusline.js +90 -6
- package/hooks/gsd-update-banner.js +22 -1
- package/hooks/gsd-workflow-guard.js +134 -36
- package/hooks/gsd-worktree-path-guard.js +2 -1
- package/hooks/gsd-write-guard.js +359 -0
- package/hooks/hooks.json +12 -0
- 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 +277 -0
- package/hooks/managed-hooks-registry.cjs +2 -0
- package/package.json +31 -10
- package/pi/gsd.cjs +71 -12
- 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/build-hooks.js +9 -0
- package/scripts/changeset/lint.cjs +68 -6
- package/scripts/changeset/serialize.cjs +5 -1
- 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-matrix.cjs +1 -1
- package/scripts/gen-capability-registry.cjs +3 -15
- package/scripts/gen-context-index.cjs +439 -0
- package/scripts/gen-health-docs.cjs +390 -0
- package/scripts/gen-inventory-manifest.cjs +150 -4
- package/scripts/gen-loop-host-contract.cjs +4 -24
- package/scripts/gen-prompt-budget-parity-corpus.cjs +645 -0
- package/scripts/gen-registry.cjs +3 -14
- package/scripts/gen-section-manifest.cjs +638 -0
- package/scripts/generate-package-identity.cjs +4 -2
- 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 +15 -54
- 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-compiled-artifact-sync.cjs +6 -1
- 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-docs-command-form.cjs +195 -0
- package/scripts/lint-docs-required.cjs +9 -1
- package/scripts/lint-emitted-drift-ack.cjs +215 -20
- package/scripts/lint-eslint-glob-coverage.allowlist.json +34 -0
- package/scripts/lint-eslint-glob-coverage.cjs +340 -0
- package/scripts/lint-example-parser-parity.cjs +395 -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 +40 -3
- package/scripts/lint-unreachable-guard-drift.cjs +843 -0
- package/scripts/lint-vendored-deps.cjs +124 -0
- package/scripts/mutation-matrix.cjs +13 -0
- package/scripts/pr-changed-files.cjs +63 -0
- package/scripts/pr-template-policy.cjs +14 -4
- package/scripts/prompt-injection-scan.sh +52 -6
- 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 +2 -2
- package/skills/gsd-mempalace-recall/SKILL.md +1 -1
- package/skills/gsd-new-milestone/SKILL.md +2 -2
- package/skills/gsd-next/SKILL.md +0 -1
- package/skills/gsd-plan-phase/SKILL.md +1 -2
- 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 -577
- package/scripts/affected-tests-lib.cjs +0 -554
- package/scripts/gen-emitted-baseline.cjs +0 -145
- package/scripts/lint-allow-test-rule-refs.cjs +0 -162
- package/scripts/run-affected-tests.cjs +0 -7
- package/scripts/run-tests.cjs +0 -1050
|
@@ -22,7 +22,7 @@ const shell_command_projection_cjs_1 = require("./shell-command-projection.cjs")
|
|
|
22
22
|
const clock_cjs_1 = require("./clock.cjs");
|
|
23
23
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
24
24
|
const activeWorkstreamStore = require("./active-workstream-store.cjs");
|
|
25
|
-
const { createSharedPointerAdapter, createSessionScopedPointerAdapter, createMemoryPointerAdapter, getActiveWorkstream: getStoredActiveWorkstream, setActiveWorkstream: setStoredActiveWorkstream, clearActiveWorkstream: clearStoredActiveWorkstream, } = activeWorkstreamStore;
|
|
25
|
+
const { createSharedPointerAdapter, createSessionScopedPointerAdapter, createMemoryPointerAdapter, getActiveWorkstream: getStoredActiveWorkstream, peekActiveWorkstream: peekStoredActiveWorkstream, setActiveWorkstream: setStoredActiveWorkstream, clearActiveWorkstream: clearStoredActiveWorkstream, diagnoseUnresolvedActiveWorkstream: diagnoseUnresolvedStoredActiveWorkstream, } = activeWorkstreamStore;
|
|
26
26
|
// Track .planning/.lock files held by this process so they can be removed on exit.
|
|
27
27
|
const _heldPlanningLocks = new Set();
|
|
28
28
|
process.on('exit', () => {
|
|
@@ -136,6 +136,15 @@ function listAvailableWorkstreams(cwd) {
|
|
|
136
136
|
return [];
|
|
137
137
|
}
|
|
138
138
|
}
|
|
139
|
+
// #2142: the quick-task directory. Exported as its own function (not only as a
|
|
140
|
+
// `planningPaths` key) because `audit.cts`'s `scanQuickTasks` receives an
|
|
141
|
+
// already-resolved planning base rather than a `cwd`, so it cannot reach
|
|
142
|
+
// `planningPaths`. Without this shared helper, adding the `quick` key would
|
|
143
|
+
// leave TWO composers of `<planning>/quick` — the DEFECT.GENERATIVE-FIX shape
|
|
144
|
+
// the `debug` key (#3149) was introduced to eliminate.
|
|
145
|
+
function quickDirFrom(planningBase) {
|
|
146
|
+
return node_path_1.default.join(planningBase, 'quick');
|
|
147
|
+
}
|
|
139
148
|
function planningPaths(cwd, ws) {
|
|
140
149
|
const base = planningDir(cwd, ws);
|
|
141
150
|
return {
|
|
@@ -146,6 +155,12 @@ function planningPaths(cwd, ws) {
|
|
|
146
155
|
config: node_path_1.default.join(base, 'config.json'),
|
|
147
156
|
phases: node_path_1.default.join(base, 'phases'),
|
|
148
157
|
requirements: node_path_1.default.join(base, 'REQUIREMENTS.md'),
|
|
158
|
+
// #3149: the debug-session directory. Single source for both `state.load`'s
|
|
159
|
+
// `debug_dir` field and `init.debug`'s — previously each composed its own
|
|
160
|
+
// `path.join(planning, 'debug')` (DEFECT.GENERATIVE-FIX).
|
|
161
|
+
debug: node_path_1.default.join(base, 'debug'),
|
|
162
|
+
// #2142: quick-task directory, composed via the shared quickDirFrom helper.
|
|
163
|
+
quick: quickDirFrom(base),
|
|
149
164
|
};
|
|
150
165
|
}
|
|
151
166
|
/**
|
|
@@ -168,11 +183,15 @@ function withPlanningLock(cwd, fn, clock) {
|
|
|
168
183
|
// on every call with no self-heal. Mirrors acquireStateLock's deadmanCeilingMs.
|
|
169
184
|
const deadmanCeilingMs = 60000;
|
|
170
185
|
const start = clock.now();
|
|
171
|
-
// Ensure .planning/ exists
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
186
|
+
// Ensure .planning/ exists. A genuine failure here (EACCES/ENOSPC/EROFS/EMFILE)
|
|
187
|
+
// MUST surface immediately: the prior `catch { /* ok */ }` swallowed it, the lock
|
|
188
|
+
// write below then failed with ENOENT (parent dir missing), and ENOENT is retryable
|
|
189
|
+
// (PLANNING_LOCK_RETRY_ERRNOS — added for a Docker overlay-fs race), so the loop
|
|
190
|
+
// spun the full 10s budget and reported a PHANTOM "held by a live process"
|
|
191
|
+
// contention pointing at a nonexistent holder (epic #1879 / F16, #1884).
|
|
192
|
+
// `mkdirSync(recursive:true)` does not throw on an existing dir, so the normal
|
|
193
|
+
// path (dir already present) is unaffected; only real creation failures propagate.
|
|
194
|
+
(0, shell_command_projection_cjs_1.platformEnsureDir)(planningDir(cwd));
|
|
176
195
|
function acquireLock() {
|
|
177
196
|
// Atomic create — fails if file exists
|
|
178
197
|
node_fs_1.default.writeFileSync(lockPath, JSON.stringify({
|
|
@@ -341,9 +360,40 @@ function createPlanningWorkspace(cwd, opts = {}) {
|
|
|
341
360
|
function getActiveWorkstream(cwd) {
|
|
342
361
|
return getStoredActiveWorkstream(cwd);
|
|
343
362
|
}
|
|
363
|
+
// #3579 root-cause fix: read-only sibling of getActiveWorkstream, thin
|
|
364
|
+
// pass-through to active-workstream-store's non-mutating peek. Callers that
|
|
365
|
+
// only need to KNOW whether a workstream resolves (a guard's initial check,
|
|
366
|
+
// a bootstrap that will re-derive the answer anyway) must use this instead
|
|
367
|
+
// of getActiveWorkstream — the mutating variant self-heals (clears) a
|
|
368
|
+
// present-but-unresolvable chain[0] value, and a later read in the SAME
|
|
369
|
+
// process (another getActiveWorkstream call, or diagnoseUnresolvedActiveWorkstream)
|
|
370
|
+
// would then observe already-cleared state instead of the original evidence,
|
|
371
|
+
// silently changing the answer or losing the diagnostic reason. Self-heal
|
|
372
|
+
// still happens — exactly once, wherever the real consuming call site invokes
|
|
373
|
+
// getActiveWorkstream — this sibling just avoids triggering it prematurely.
|
|
374
|
+
function peekActiveWorkstream(cwd) {
|
|
375
|
+
return peekStoredActiveWorkstream(cwd);
|
|
376
|
+
}
|
|
344
377
|
function setActiveWorkstream(cwd, name) {
|
|
345
378
|
setStoredActiveWorkstream(cwd, name);
|
|
346
379
|
}
|
|
380
|
+
// #3579 item 1: read-only diagnostic sibling of getActiveWorkstream, thin
|
|
381
|
+
// pass-through to active-workstream-store's chain walk. Lets the #1912/#2028
|
|
382
|
+
// fail-safe guards (init.progress, phase.complete) distinguish "no marker at
|
|
383
|
+
// all" from "a marker exists but didn't resolve" without duplicating the
|
|
384
|
+
// resolution predicate.
|
|
385
|
+
function diagnoseUnresolvedActiveWorkstream(cwd) {
|
|
386
|
+
return diagnoseUnresolvedStoredActiveWorkstream(cwd);
|
|
387
|
+
}
|
|
388
|
+
// #3579 item 1: human-readable clause for diagnoseUnresolvedActiveWorkstream's
|
|
389
|
+
// `reason`, shared by the init.progress and phase.complete fail-safe guards so
|
|
390
|
+
// the two error messages describe the same failure the same way instead of
|
|
391
|
+
// drifting (CLAUDE.md's Generative Fix Divergence anti-pattern).
|
|
392
|
+
function describeUnresolvedWorkstreamReason(reason) {
|
|
393
|
+
if (reason === 'invalid_name')
|
|
394
|
+
return 'the name is not a valid workstream name';
|
|
395
|
+
return "its workstream directory doesn't exist (it may have been renamed or removed)";
|
|
396
|
+
}
|
|
347
397
|
/**
|
|
348
398
|
* Locate the CONTEXT.md file in a phase directory, handling both the bare
|
|
349
399
|
* form (`CONTEXT.md`) and the padded-prefix convention (`NN-CONTEXT.md`,
|
|
@@ -389,9 +439,13 @@ module.exports = {
|
|
|
389
439
|
planningRoot,
|
|
390
440
|
listAvailableWorkstreams,
|
|
391
441
|
planningPaths,
|
|
442
|
+
quickDirFrom,
|
|
392
443
|
withPlanningLock,
|
|
393
444
|
getActiveWorkstream,
|
|
445
|
+
peekActiveWorkstream,
|
|
394
446
|
setActiveWorkstream,
|
|
447
|
+
diagnoseUnresolvedActiveWorkstream,
|
|
448
|
+
describeUnresolvedWorkstreamReason,
|
|
395
449
|
findContextMdIn,
|
|
396
450
|
// Test seam (audit M1): inject a deterministic isPidAlive so the liveness-gated
|
|
397
451
|
// steal decision is exercised without real pids. Mirrors capability-lock.cts.
|
|
@@ -372,7 +372,7 @@ function dispositionForProhibition(prohibition, context = {}) {
|
|
|
372
372
|
};
|
|
373
373
|
}
|
|
374
374
|
// D4 GUARD: a judgment-tier (or unknown-tier) prohibition is NEVER a silent green from this
|
|
375
|
-
// deterministic helper — it always routes to human/LLM judgment review (ADR-550 D4;
|
|
375
|
+
// deterministic helper — it always routes to human/LLM judgment review (ADR-550 D4; gsd-verifier.md + references/verifier-phase-gates.md).
|
|
376
376
|
// Only a test-tier item with wired enforcement evidence may go green; the producer that supplies
|
|
377
377
|
// that evidence (`prohibition-enforcement`, #1259) runs the wired check and requires a genuine pass.
|
|
378
378
|
if (tier === 'test') {
|
|
@@ -477,7 +477,7 @@ function generateSkillsSection(cwd) {
|
|
|
477
477
|
*/
|
|
478
478
|
function extractSkillFrontmatter(content) {
|
|
479
479
|
const result = { name: '', description: '' };
|
|
480
|
-
const fmMatch = content.match(/^---\s*\n([\s\S]*?)\n---/);
|
|
480
|
+
const fmMatch = content.match(/^---\s*\r?\n([\s\S]*?)\r?\n---/);
|
|
481
481
|
if (!fmMatch)
|
|
482
482
|
return result;
|
|
483
483
|
const fmBlock = fmMatch[1];
|
|
@@ -10,7 +10,12 @@
|
|
|
10
10
|
*
|
|
11
11
|
* Used by the review pipeline to support small-context models.
|
|
12
12
|
*
|
|
13
|
-
* Trim priority (in order — never violate)
|
|
13
|
+
* Trim priority (in order — never violate). As of #2929 (epic #1671 Phase 2)
|
|
14
|
+
* the ladder itself is executed by the shared `context-composer` seam
|
|
15
|
+
* (`composeWithinBudget`); this module builds the fragment list in this
|
|
16
|
+
* exact order, delegates the decision of what survives/shrinks/truncates,
|
|
17
|
+
* and keeps only the prompt-specific rendering (`renderNote`,
|
|
18
|
+
* `assemblePrompt`) and the hard-fail response shapes here:
|
|
14
19
|
* 1. Instructions: ALWAYS kept verbatim
|
|
15
20
|
* 2. Reserve note tokens FIRST when any trim is anticipated
|
|
16
21
|
* 3. Roadmap: ALWAYS kept verbatim
|
|
@@ -22,9 +27,20 @@
|
|
|
22
27
|
* 9. Hard-fail: if minimum-set exceeds effectiveBudget
|
|
23
28
|
*/
|
|
24
29
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
30
|
+
exports.PLAN_FLOOR_CHARS = void 0;
|
|
25
31
|
exports.estimateTokens = estimateTokens;
|
|
32
|
+
exports.buildBudgetFragments = buildBudgetFragments;
|
|
26
33
|
exports.applyBudget = applyBudget;
|
|
34
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports -- context-composer.cjs is an export= CommonJS module
|
|
35
|
+
const contextComposer = require("./context-composer.cjs");
|
|
27
36
|
const NOTE_RESERVE_TOKENS = 80;
|
|
37
|
+
/**
|
|
38
|
+
* Floor per plan when proportionally truncating and for the minimum-set
|
|
39
|
+
* check. Exported so consumers (notably the load-bearing contract gate,
|
|
40
|
+
* issue #3065) never need to re-declare this value locally.
|
|
41
|
+
*/
|
|
42
|
+
exports.PLAN_FLOOR_CHARS = 1024;
|
|
43
|
+
const MIN_PLAN_BYTES = exports.PLAN_FLOOR_CHARS;
|
|
28
44
|
const DEFAULT_NOTE_TEMPLATE = [
|
|
29
45
|
'<note>',
|
|
30
46
|
'Prompt automatically trimmed to fit a {budget}-token budget.',
|
|
@@ -51,30 +67,6 @@ function renderNote(template, budget, omitted, planTruncationPct) {
|
|
|
51
67
|
.replace('{omittedList}', omittedList)
|
|
52
68
|
.replace('{planTruncationPct}', String(Math.round(planTruncationPct)));
|
|
53
69
|
}
|
|
54
|
-
/**
|
|
55
|
-
* Head-shrink a string to at most `maxLines` lines.
|
|
56
|
-
*/
|
|
57
|
-
function headShrink(text, maxLines) {
|
|
58
|
-
if (maxLines <= 0)
|
|
59
|
-
return '';
|
|
60
|
-
let idx = -1;
|
|
61
|
-
let seen = 0;
|
|
62
|
-
while (seen < maxLines) {
|
|
63
|
-
idx = text.indexOf('\n', idx + 1);
|
|
64
|
-
if (idx === -1)
|
|
65
|
-
return text;
|
|
66
|
-
seen += 1;
|
|
67
|
-
}
|
|
68
|
-
return text.slice(0, idx);
|
|
69
|
-
}
|
|
70
|
-
/**
|
|
71
|
-
* Tail-truncate a string to at most `maxChars` characters.
|
|
72
|
-
*/
|
|
73
|
-
function tailTruncate(text, maxChars) {
|
|
74
|
-
if (text.length <= maxChars)
|
|
75
|
-
return text;
|
|
76
|
-
return text.slice(0, maxChars);
|
|
77
|
-
}
|
|
78
70
|
/**
|
|
79
71
|
* Assemble the final prompt string from its sections.
|
|
80
72
|
*/
|
|
@@ -99,38 +91,92 @@ function assemblePrompt(parts) {
|
|
|
99
91
|
blocks.push('## Requirements\n\n' + requirements);
|
|
100
92
|
return blocks.join('\n\n');
|
|
101
93
|
}
|
|
94
|
+
/**
|
|
95
|
+
* Build the `composeWithinBudget` fragment array for a set of review prompt
|
|
96
|
+
* sections, with each fragment's declared trim strategy attached.
|
|
97
|
+
*
|
|
98
|
+
* Extracted (issue #3065) so the load-bearing contract gate
|
|
99
|
+
* (tests/load-bearing-contract-gate.test.cjs) can assert directly over the
|
|
100
|
+
* REAL declared strategies used by `applyBudget`, rather than a hand-copied
|
|
101
|
+
* duplicate array. A duplicate would let production silently diverge from
|
|
102
|
+
* the gate (e.g. flipping `roadmap` from `verbatim` to `drop` would keep the
|
|
103
|
+
* gate green if it read from a copy) — exactly the
|
|
104
|
+
* `DEFECT.GENERATIVE-FIX` divergence class this extraction exists to
|
|
105
|
+
* prevent. Pure; performs no I/O and has no side effects.
|
|
106
|
+
*/
|
|
107
|
+
function buildBudgetFragments(sections, projectMdHeadLines) {
|
|
108
|
+
const { instructions, roadmap, plans, projectMd: projectMdRaw = null, context: contextRaw = null, research: researchRaw = null, requirements: requirementsRaw = null, } = sections;
|
|
109
|
+
// Unique per-plan fragment ids: plain `plan:<file>` unless a filename
|
|
110
|
+
// collision forces disambiguation by index — composeWithinBudget throws on
|
|
111
|
+
// duplicate ids, and legitimate input may repeat a filename across plans.
|
|
112
|
+
const usedPlanIds = new Set();
|
|
113
|
+
const planIds = plans.map((p, idx) => {
|
|
114
|
+
let id = `plan:${p.file}`;
|
|
115
|
+
if (usedPlanIds.has(id))
|
|
116
|
+
id = `plan:${idx}:${p.file}`;
|
|
117
|
+
usedPlanIds.add(id);
|
|
118
|
+
return id;
|
|
119
|
+
});
|
|
120
|
+
const planFragments = plans.map((p, idx) => ({
|
|
121
|
+
id: planIds[idx],
|
|
122
|
+
content: p.content,
|
|
123
|
+
wrapper: `### ${p.file}\n\n`,
|
|
124
|
+
strategy: { kind: 'proportional-truncate', floorChars: MIN_PLAN_BYTES },
|
|
125
|
+
group: 'plans',
|
|
126
|
+
required: true,
|
|
127
|
+
}));
|
|
128
|
+
return [
|
|
129
|
+
{ id: 'instructions', content: instructions, wrapper: '', strategy: { kind: 'verbatim' }, required: true },
|
|
130
|
+
{ id: 'roadmap', content: roadmap, wrapper: '## Roadmap\n\n', strategy: { kind: 'verbatim' }, required: true },
|
|
131
|
+
{
|
|
132
|
+
id: 'projectMd',
|
|
133
|
+
content: projectMdRaw ?? '',
|
|
134
|
+
wrapper: '## Project\n\n',
|
|
135
|
+
strategy: { kind: 'head-shrink', maxLines: projectMdHeadLines },
|
|
136
|
+
},
|
|
137
|
+
{ id: 'plans-header', content: '', wrapper: '## Plans\n\n', strategy: { kind: 'verbatim' }, required: true },
|
|
138
|
+
...planFragments,
|
|
139
|
+
{ id: 'context', content: contextRaw ?? '', wrapper: '## Context\n\n', strategy: { kind: 'drop' } },
|
|
140
|
+
{ id: 'research', content: researchRaw ?? '', wrapper: '## Research\n\n', strategy: { kind: 'drop' } },
|
|
141
|
+
{ id: 'requirements', content: requirementsRaw ?? '', wrapper: '## Requirements\n\n', strategy: { kind: 'drop' } },
|
|
142
|
+
];
|
|
143
|
+
}
|
|
102
144
|
/**
|
|
103
145
|
* Apply a token budget to a set of review prompt sections.
|
|
104
146
|
* Returns the trimmed prompt and structured metadata.
|
|
105
147
|
*/
|
|
106
148
|
function applyBudget({ sections, budget, options = {} }) {
|
|
107
149
|
const { safetyMarginPct = 10, noteTemplate = DEFAULT_NOTE_TEMPLATE, projectMdHeadLines = 40, } = options;
|
|
108
|
-
const
|
|
109
|
-
const
|
|
110
|
-
//
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
150
|
+
const { plans } = sections;
|
|
151
|
+
const fragments = buildBudgetFragments(sections, projectMdHeadLines);
|
|
152
|
+
// Recover the per-plan fragment ids buildBudgetFragments assigned (in
|
|
153
|
+
// `plans` declaration order) rather than re-deriving the id-collision
|
|
154
|
+
// logic here — a single source of truth for id assignment.
|
|
155
|
+
const planIds = fragments.filter((f) => f.group === 'plans').map((f) => f.id);
|
|
156
|
+
const composed = contextComposer.composeWithinBudget({
|
|
157
|
+
fragments,
|
|
158
|
+
budget,
|
|
159
|
+
measure: estimateTokens,
|
|
160
|
+
options: {
|
|
161
|
+
safetyMarginPct,
|
|
162
|
+
reserve: NOTE_RESERVE_TOKENS,
|
|
163
|
+
charsPerUnit: 4,
|
|
164
|
+
// Minimum-set floor: instructions + roadmap (in full) + 1KB per plan.
|
|
165
|
+
// NOTE_RESERVE_TOKENS is intentionally excluded — a note is only
|
|
166
|
+
// injected when trimming actually occurs, and a prompt that fits
|
|
167
|
+
// without any trim needs no note at all. Wrappers are also excluded
|
|
168
|
+
// (this checks the floor, not the steady-state budget).
|
|
169
|
+
minimumFor: (f) => {
|
|
170
|
+
if (f.id === 'instructions' || f.id === 'roadmap')
|
|
171
|
+
return f.content;
|
|
172
|
+
if (f.id.startsWith('plan:'))
|
|
173
|
+
return f.content.slice(0, MIN_PLAN_BYTES);
|
|
174
|
+
return null;
|
|
175
|
+
},
|
|
176
|
+
},
|
|
177
|
+
});
|
|
178
|
+
const effectiveBudget = composed.metadata.effectiveBudget;
|
|
179
|
+
if (composed.metadata.hardFailed) {
|
|
134
180
|
return {
|
|
135
181
|
prompt: '',
|
|
136
182
|
metadata: {
|
|
@@ -145,127 +191,45 @@ function applyBudget({ sections, budget, options = {} }) {
|
|
|
145
191
|
},
|
|
146
192
|
};
|
|
147
193
|
}
|
|
148
|
-
|
|
149
|
-
const
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
const
|
|
156
|
-
const
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
:
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
const getCurrentBaseTokens = () => staticBaseTokens +
|
|
175
|
-
projectTokens +
|
|
176
|
-
planContentTokens +
|
|
177
|
-
contextTokens +
|
|
178
|
-
researchTokens +
|
|
179
|
-
requirementsTokens;
|
|
180
|
-
let currentBaseTokens = getCurrentBaseTokens();
|
|
181
|
-
// Detect budget pressure: is ANY trim needed?
|
|
182
|
-
// Pressure exists when the current base tokens already exceed the effective
|
|
183
|
-
// budget. Only when pressure is real do we reserve NOTE_RESERVE_TOKENS so
|
|
184
|
-
// the note itself fits after trimming. Checking against
|
|
185
|
-
// effectiveBudget - NOTE_RESERVE_TOKENS (the old threshold) would cause
|
|
186
|
-
// spurious pressure 80 tokens early, dropping sections that fit fine.
|
|
187
|
-
const baseTokens = currentBaseTokens;
|
|
188
|
-
const budgetUnderPressure = baseTokens > effectiveBudget;
|
|
189
|
-
// Available for content (reserve note slot when under pressure)
|
|
190
|
-
const contentBudget = budgetUnderPressure
|
|
191
|
-
? effectiveBudget - NOTE_RESERVE_TOKENS
|
|
192
|
-
: effectiveBudget;
|
|
193
|
-
// ── Trim step 1: head-shrink PROJECT.md ───────────────────────────────────
|
|
194
|
-
if (currentBaseTokens > contentBudget && projectMd) {
|
|
195
|
-
const shrunk = headShrink(projectMd, projectMdHeadLines);
|
|
196
|
-
if (shrunk !== projectMd) {
|
|
197
|
-
projectMd = shrunk;
|
|
198
|
-
projectMdShrunk = true;
|
|
199
|
-
projectTokens = TOKENS_PROJECT_HEADER + estimateTokens(projectMd);
|
|
200
|
-
currentBaseTokens = getCurrentBaseTokens();
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
// ── Trim step 2: proportional plan truncation ─────────────────────────────
|
|
204
|
-
if (currentBaseTokens > contentBudget) {
|
|
205
|
-
// Compute tokens available for plan content only
|
|
206
|
-
const overhead = staticBaseTokens +
|
|
207
|
-
projectTokens +
|
|
208
|
-
contextTokens +
|
|
209
|
-
researchTokens +
|
|
210
|
-
requirementsTokens;
|
|
211
|
-
const planBudgetTokens = contentBudget - overhead;
|
|
212
|
-
const totalPlanTokens = planContentTokens;
|
|
213
|
-
if (planBudgetTokens > 0 && planBudgetTokens < totalPlanTokens) {
|
|
214
|
-
// Proportional share per plan (at least 1KB per plan)
|
|
215
|
-
const totalOriginalChars = plans.reduce((sum, p) => sum + p.content.length, 0);
|
|
216
|
-
const totalPlanCharsBudget = planBudgetTokens * 4;
|
|
217
|
-
workingPlans = workingPlans.map((p) => {
|
|
218
|
-
const proportionalShare = totalOriginalChars > 0
|
|
219
|
-
? Math.floor((p.content.length / totalOriginalChars) * totalPlanCharsBudget)
|
|
220
|
-
: 0;
|
|
221
|
-
const maxChars = Math.max(proportionalShare, MIN_PLAN_BYTES);
|
|
222
|
-
return { file: p.file, content: tailTruncate(p.content, maxChars) };
|
|
223
|
-
});
|
|
224
|
-
const newTotalChars = workingPlans.reduce((sum, p) => sum + p.content.length, 0);
|
|
225
|
-
if (totalOriginalChars > 0) {
|
|
226
|
-
planTruncationPct =
|
|
227
|
-
((totalOriginalChars - newTotalChars) / totalOriginalChars) * 100;
|
|
228
|
-
}
|
|
229
|
-
planContentTokens = workingPlans.reduce((sum, p) => sum + estimateTokens(p.content), 0);
|
|
230
|
-
currentBaseTokens = getCurrentBaseTokens();
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
// ── Trim step 3: drop context ─────────────────────────────────────────────
|
|
234
|
-
if (currentBaseTokens > contentBudget && context) {
|
|
235
|
-
context = null;
|
|
236
|
-
omitted.push('context');
|
|
237
|
-
contextTokens = 0;
|
|
238
|
-
currentBaseTokens = getCurrentBaseTokens();
|
|
239
|
-
}
|
|
240
|
-
// ── Trim step 4: drop research ────────────────────────────────────────────
|
|
241
|
-
if (currentBaseTokens > contentBudget && research) {
|
|
242
|
-
research = null;
|
|
243
|
-
omitted.push('research');
|
|
244
|
-
researchTokens = 0;
|
|
245
|
-
currentBaseTokens = getCurrentBaseTokens();
|
|
246
|
-
}
|
|
247
|
-
// ── Trim step 5: drop requirements (last resort) ──────────────────────────
|
|
248
|
-
if (currentBaseTokens > contentBudget && requirements) {
|
|
249
|
-
requirements = null;
|
|
250
|
-
omitted.push('requirements');
|
|
251
|
-
requirementsTokens = 0;
|
|
252
|
-
currentBaseTokens = getCurrentBaseTokens();
|
|
253
|
-
}
|
|
194
|
+
const byId = new Map(composed.fragments.map((f) => [f.id, f]));
|
|
195
|
+
const get = (id) => {
|
|
196
|
+
const found = byId.get(id);
|
|
197
|
+
if (!found)
|
|
198
|
+
throw new Error(`applyBudget: missing composed fragment "${id}"`);
|
|
199
|
+
return found;
|
|
200
|
+
};
|
|
201
|
+
const instructionsOut = get('instructions').content;
|
|
202
|
+
const roadmapOut = get('roadmap').content;
|
|
203
|
+
const projectMdFragment = get('projectMd');
|
|
204
|
+
const projectMd = projectMdFragment.present ? projectMdFragment.content : null;
|
|
205
|
+
const contextFragment = get('context');
|
|
206
|
+
const context = contextFragment.present ? contextFragment.content : null;
|
|
207
|
+
const researchFragment = get('research');
|
|
208
|
+
const research = researchFragment.present ? researchFragment.content : null;
|
|
209
|
+
const requirementsFragment = get('requirements');
|
|
210
|
+
const requirements = requirementsFragment.present ? requirementsFragment.content : null;
|
|
211
|
+
// Plans are never dropped — only their content may be truncated — so map
|
|
212
|
+
// deliberately by original index/file rather than relying on `present`.
|
|
213
|
+
const workingPlans = plans.map((p, idx) => ({
|
|
214
|
+
file: p.file,
|
|
215
|
+
content: get(planIds[idx]).content,
|
|
216
|
+
}));
|
|
217
|
+
const omitted = composed.metadata.omitted;
|
|
218
|
+
const projectMdShrunk = composed.metadata.shrunk.includes('projectMd');
|
|
219
|
+
const planTruncationPct = composed.metadata.truncationPct;
|
|
254
220
|
// ── Decide whether note is actually needed ────────────────────────────────
|
|
255
221
|
const anyTrimOccurred = omitted.length > 0 || projectMdShrunk || planTruncationPct > 0;
|
|
256
222
|
let note = null;
|
|
223
|
+
let noteInjected = false;
|
|
257
224
|
if (anyTrimOccurred) {
|
|
258
225
|
note = renderNote(noteTemplate, budget, omitted, planTruncationPct);
|
|
259
226
|
noteInjected = true;
|
|
260
227
|
}
|
|
261
|
-
// Suppress unused variable warning — currentBaseTokens is used via the
|
|
262
|
-
// closure in getCurrentBaseTokens(); the final value is not used directly.
|
|
263
|
-
void currentBaseTokens;
|
|
264
228
|
// ── Assemble ──────────────────────────────────────────────────────────────
|
|
265
229
|
const prompt = assemblePrompt({
|
|
266
|
-
instructions,
|
|
230
|
+
instructions: instructionsOut,
|
|
267
231
|
note,
|
|
268
|
-
roadmap,
|
|
232
|
+
roadmap: roadmapOut,
|
|
269
233
|
projectMd,
|
|
270
234
|
plans: workingPlans,
|
|
271
235
|
context,
|
|
@@ -274,7 +238,6 @@ function applyBudget({ sections, budget, options = {} }) {
|
|
|
274
238
|
});
|
|
275
239
|
const estimatedTokens = estimateTokens(prompt);
|
|
276
240
|
if (estimatedTokens > effectiveBudget) {
|
|
277
|
-
hardFailed = true;
|
|
278
241
|
return {
|
|
279
242
|
prompt: '',
|
|
280
243
|
metadata: {
|
|
@@ -284,7 +247,7 @@ function applyBudget({ sections, budget, options = {} }) {
|
|
|
284
247
|
omitted,
|
|
285
248
|
projectMdShrunk,
|
|
286
249
|
planTruncationPct,
|
|
287
|
-
hardFailed,
|
|
250
|
+
hardFailed: true,
|
|
288
251
|
noteInjected,
|
|
289
252
|
},
|
|
290
253
|
};
|
|
@@ -298,7 +261,7 @@ function applyBudget({ sections, budget, options = {} }) {
|
|
|
298
261
|
omitted,
|
|
299
262
|
projectMdShrunk,
|
|
300
263
|
planTruncationPct,
|
|
301
|
-
hardFailed,
|
|
264
|
+
hardFailed: false,
|
|
302
265
|
noteInjected,
|
|
303
266
|
},
|
|
304
267
|
};
|