@opengsd/gsd-core 1.14.0 → 1.16.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 +85 -5
- package/README.ja-JP.md +3 -3
- package/README.ko-KR.md +3 -3
- package/README.pt-BR.md +3 -3
- package/README.zh-CN.md +3 -3
- package/agents/gsd-code-fixer.compact.md +7 -6
- package/agents/gsd-code-fixer.md +9 -8
- package/agents/gsd-code-reviewer.compact.md +5 -3
- package/agents/gsd-code-reviewer.md +8 -6
- package/agents/gsd-debug-session-manager.compact.md +17 -2
- package/agents/gsd-debug-session-manager.md +17 -2
- package/agents/gsd-debugger.md +3 -3
- package/agents/gsd-eval-auditor.compact.md +1 -1
- package/agents/gsd-eval-auditor.md +1 -1
- package/agents/gsd-executor.md +17 -12
- package/agents/gsd-intel-updater.compact.md +1 -1
- package/agents/gsd-intel-updater.md +1 -1
- package/agents/gsd-mempalace-curator.md +2 -2
- package/agents/gsd-phase-researcher.md +19 -11
- package/agents/gsd-plan-checker.md +15 -9
- package/agents/gsd-planner.md +15 -11
- package/agents/gsd-project-researcher.compact.md +1 -1
- package/agents/gsd-project-researcher.md +1 -1
- package/agents/gsd-research-synthesizer.compact.md +1 -1
- package/agents/gsd-research-synthesizer.md +1 -1
- package/agents/gsd-ui-auditor.compact.md +21 -30
- package/agents/gsd-ui-auditor.md +166 -37
- package/agents/gsd-ui-researcher.compact.md +1 -1
- package/agents/gsd-ui-researcher.md +1 -1
- package/agents/gsd-verifier.md +37 -14
- package/bin/install.js +764 -287
- package/commands/gsd/add-tests.md +6 -1
- package/commands/gsd/ai-integration-phase.md +6 -1
- package/commands/gsd/audit-fix.md +5 -0
- package/commands/gsd/audit-milestone.md +6 -1
- package/commands/gsd/autonomous.md +7 -2
- package/commands/gsd/capture.md +9 -5
- package/commands/gsd/code-review.md +7 -2
- package/commands/gsd/complete-milestone.md +4 -0
- package/commands/gsd/config.md +7 -3
- package/commands/gsd/debug.md +11 -7
- package/commands/gsd/discuss-phase.md +7 -3
- package/commands/gsd/docs-update.md +12 -7
- package/commands/gsd/eval-review.md +6 -1
- package/commands/gsd/execute-phase.md +12 -7
- package/commands/gsd/extract-learnings.md +5 -0
- package/commands/gsd/fast.md +4 -0
- package/commands/gsd/forensics.md +5 -1
- package/commands/gsd/graphify.md +10 -6
- package/commands/gsd/health.md +5 -0
- package/commands/gsd/help.md +7 -2
- package/commands/gsd/import.md +7 -3
- package/commands/gsd/inbox.md +5 -0
- package/commands/gsd/ingest-docs.md +5 -1
- package/commands/gsd/manager.md +6 -1
- package/commands/gsd/map-codebase.md +7 -3
- package/commands/gsd/mempalace-capture.md +12 -4
- package/commands/gsd/mempalace-recall.md +5 -1
- package/commands/gsd/milestone-summary.md +5 -1
- package/commands/gsd/mvp-phase.md +8 -3
- package/commands/gsd/new-milestone.md +6 -1
- package/commands/gsd/new-project.md +5 -0
- package/commands/gsd/next.md +6 -1
- package/commands/gsd/ns-context.md +4 -0
- package/commands/gsd/ns-ideate.md +4 -0
- package/commands/gsd/ns-manage.md +4 -0
- package/commands/gsd/ns-project.md +4 -0
- package/commands/gsd/ns-review.md +4 -0
- package/commands/gsd/ns-workflow.md +4 -0
- package/commands/gsd/onboard.md +6 -1
- package/commands/gsd/pause-work.md +5 -1
- package/commands/gsd/phase.md +8 -4
- package/commands/gsd/plan-phase.md +6 -1
- package/commands/gsd/plan-review-convergence.md +11 -7
- package/commands/gsd/pr-branch.md +4 -0
- package/commands/gsd/profile-user.md +5 -1
- package/commands/gsd/progress.md +7 -2
- package/commands/gsd/quick-batch.md +21 -9
- package/commands/gsd/quick.md +12 -7
- package/commands/gsd/review.md +7 -4
- package/commands/gsd/secure-phase.md +6 -1
- package/commands/gsd/ship.md +5 -0
- package/commands/gsd/sketch.md +7 -2
- package/commands/gsd/spec-phase.md +5 -1
- package/commands/gsd/spike.md +8 -3
- package/commands/gsd/surface.md +5 -1
- package/commands/gsd/thread.md +4 -0
- package/commands/gsd/ui-phase.md +6 -1
- package/commands/gsd/ui-review.md +6 -1
- package/commands/gsd/ultraplan-phase.md +5 -1
- package/commands/gsd/undo.md +5 -1
- package/commands/gsd/update.md +6 -2
- package/commands/gsd/validate-phase.md +6 -1
- package/commands/gsd/verify-work.md +6 -1
- package/commands/gsd/workspace.md +7 -3
- package/gsd-core/bin/gsd-tools.cjs +477 -78
- package/gsd-core/bin/lib/active-workstream-store.cjs +15 -0
- package/gsd-core/bin/lib/adr-parser.cjs +3 -1
- package/gsd-core/bin/lib/agent-install-check.cjs +4 -1
- package/gsd-core/bin/lib/audit.cjs +144 -42
- package/gsd-core/bin/lib/broken-windows.cjs +13 -13
- package/gsd-core/bin/lib/capability-activation.cjs +9 -4
- package/gsd-core/bin/lib/capability-registry.cjs +197 -222
- package/gsd-core/bin/lib/capability-validator.cjs +16 -1
- package/gsd-core/bin/lib/check-auto-mode.cjs +35 -0
- package/gsd-core/bin/lib/check-command-router.cjs +164 -1625
- package/gsd-core/bin/lib/cjs-command-router-adapter.cjs +13 -2
- package/gsd-core/bin/lib/cli-exit.cjs +12 -0
- package/gsd-core/bin/lib/codex-agent-toml.cjs +32 -33
- package/gsd-core/bin/lib/command-aliases.cjs +7 -0
- package/gsd-core/bin/lib/command-routing-hub.cjs +48 -1
- package/gsd-core/bin/lib/commands.cjs +343 -207
- package/gsd-core/bin/lib/complexity-trigger.cjs +8 -7
- package/gsd-core/bin/lib/config-loader.cjs +65 -4
- package/gsd-core/bin/lib/config.cjs +76 -19
- package/gsd-core/bin/lib/core-utils.cjs +6 -1
- package/gsd-core/bin/lib/coverage.cjs +4 -8
- package/gsd-core/bin/lib/decision-coverage-support.cjs +259 -0
- package/gsd-core/bin/lib/decisions.cjs +30 -14
- package/gsd-core/bin/lib/drift.cjs +177 -42
- package/gsd-core/bin/lib/frontmatter-fence.cjs +90 -0
- package/gsd-core/bin/lib/frontmatter-splice.cjs +494 -0
- package/gsd-core/bin/lib/frontmatter.cjs +426 -234
- package/gsd-core/bin/lib/gap-checker.cjs +72 -29
- package/gsd-core/bin/lib/gate-api-coverage-verify-pre.cjs +381 -0
- package/gsd-core/bin/lib/gate-args.cjs +53 -0
- package/gsd-core/bin/lib/gate-codebase-drift.cjs +285 -0
- package/gsd-core/bin/lib/gate-config.cjs +46 -0
- package/gsd-core/bin/lib/gate-context-drift.cjs +141 -0
- package/gsd-core/bin/lib/gate-decision-coverage-plan.cjs +169 -0
- package/gsd-core/bin/lib/gate-decision-coverage-verify.cjs +126 -0
- package/gsd-core/bin/lib/gate-evaluation-scope.cjs +555 -0
- package/gsd-core/bin/lib/gate-evidence.cjs +138 -0
- package/gsd-core/bin/lib/gate-exit.cjs +27 -0
- package/gsd-core/bin/lib/gate-gap-analysis-plan-post.cjs +61 -0
- package/gsd-core/bin/lib/gate-phase-context.cjs +170 -0
- package/gsd-core/bin/lib/gate-predicate-evaluator.cjs +1 -1
- package/gsd-core/bin/lib/gate-predicate.cjs +165 -0
- package/gsd-core/bin/lib/gate-prohibition-enforcement.cjs +94 -0
- package/gsd-core/bin/lib/gate-schema-drift.cjs +165 -0
- package/gsd-core/bin/lib/gate-tdd-red-evidence.cjs +100 -0
- package/gsd-core/bin/lib/gate-tdd-review-checkpoint.cjs +182 -0
- package/gsd-core/bin/lib/gate-ui-plan.cjs +86 -0
- package/gsd-core/bin/lib/gate-ui-safety.cjs +80 -0
- package/gsd-core/bin/lib/gate-verdict.cjs +64 -0
- package/gsd-core/bin/lib/gate-verify-command-paths.cjs +78 -0
- package/gsd-core/bin/lib/gate-verify-failure-directions.cjs +41 -0
- package/gsd-core/bin/lib/graphify.cjs +10 -2
- package/gsd-core/bin/lib/health-diagnostic-rules/phase-structure.cjs +43 -47
- package/gsd-core/bin/lib/health-diagnostic.cjs +45 -8
- package/gsd-core/bin/lib/host-runtime-detection.cjs +9 -0
- package/gsd-core/bin/lib/init.cjs +364 -132
- package/gsd-core/bin/lib/install-engine.cjs +30 -33
- package/gsd-core/bin/lib/install-profiles.cjs +7 -4
- package/gsd-core/bin/lib/installer-migrations.cjs +8 -1
- package/gsd-core/bin/lib/io.cjs +122 -3
- package/gsd-core/bin/lib/loop-resolver.cjs +95 -0
- package/gsd-core/bin/lib/markdown-sectionizer.cjs +75 -1
- package/gsd-core/bin/lib/milestone.cjs +37 -6
- package/gsd-core/bin/lib/model-resolver.cjs +171 -62
- package/gsd-core/bin/lib/observability/event.cjs +1 -1
- package/gsd-core/bin/lib/observability/logger.cjs +46 -1
- package/gsd-core/bin/lib/pattern.cjs +10 -0
- package/gsd-core/bin/lib/phase-command-router.cjs +20 -5
- package/gsd-core/bin/lib/phase-estimation.cjs +5 -4
- package/gsd-core/bin/lib/phase-id-card.cjs +32 -0
- package/gsd-core/bin/lib/phase-id-display.cjs +78 -0
- package/gsd-core/bin/lib/phase-id.cjs +110 -8
- package/gsd-core/bin/lib/phase-lifecycle.cjs +9 -2
- package/gsd-core/bin/lib/phase-locator.cjs +29 -10
- package/gsd-core/bin/lib/phase-status.cjs +360 -0
- package/gsd-core/bin/lib/phase.cjs +489 -88
- package/gsd-core/bin/lib/plan-document.cjs +142 -20
- package/gsd-core/bin/lib/plan-drift-guard.cjs +5 -0
- package/gsd-core/bin/lib/planning-document.cjs +692 -0
- package/gsd-core/bin/lib/planning-inspect.cjs +60 -9
- package/gsd-core/bin/lib/planning-snapshot.cjs +18 -0
- package/gsd-core/bin/lib/planning-workspace.cjs +83 -55
- package/gsd-core/bin/lib/pr-branch-patterns.cjs +57 -0
- package/gsd-core/bin/lib/pristine-baseline.cjs +10 -0
- package/gsd-core/bin/lib/probe-core.cjs +7 -1
- package/gsd-core/bin/lib/profile-output.cjs +6 -3
- package/gsd-core/bin/lib/prohibition-enforcement.cjs +0 -55
- package/gsd-core/bin/lib/project-root.cjs +41 -2
- package/gsd-core/bin/lib/quick-batch-command-router.cjs +35 -9
- package/gsd-core/bin/lib/quick-batch-dispatch.cjs +11 -8
- package/gsd-core/bin/lib/real-home-guard.cjs +9 -1
- package/gsd-core/bin/lib/report-parser.cjs +269 -0
- package/gsd-core/bin/lib/review-lane-descriptor.cjs +10 -30
- package/gsd-core/bin/lib/review-reviewer-selection.cjs +2 -2
- package/gsd-core/bin/lib/roadmap-command-router.cjs +25 -19
- package/gsd-core/bin/lib/roadmap-parser.cjs +242 -15
- package/gsd-core/bin/lib/roadmap-upgrade.cjs +1653 -65
- package/gsd-core/bin/lib/roadmap.cjs +405 -88
- package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +373 -187
- package/gsd-core/bin/lib/runtime-artifact-install-plan.cjs +5 -2
- package/gsd-core/bin/lib/runtime-artifact-layout.cjs +57 -1
- package/gsd-core/bin/lib/runtime-homes.cjs +14 -7
- package/gsd-core/bin/lib/runtime-hooks-surface.cjs +522 -624
- package/gsd-core/bin/lib/runtime-name-policy.cjs +246 -21
- package/gsd-core/bin/lib/runtime-slash.cjs +47 -30
- package/gsd-core/bin/lib/shell-command-projection.cjs +47 -8
- package/gsd-core/bin/lib/smart-entry.cjs +19 -3
- package/gsd-core/bin/lib/stale-bake-guard.cjs +32 -48
- package/gsd-core/bin/lib/state-contract.cjs +15 -18
- package/gsd-core/bin/lib/state-document.cjs +100 -22
- package/gsd-core/bin/lib/state-transition.cjs +39 -2
- package/gsd-core/bin/lib/state.cjs +256 -105
- package/gsd-core/bin/lib/surface.cjs +19 -2
- package/gsd-core/bin/lib/tdd-red-evidence.cjs +48 -79
- package/gsd-core/bin/lib/uat-predicate.cjs +359 -38
- package/gsd-core/bin/lib/uat.cjs +432 -7
- package/gsd-core/bin/lib/ui-consideration-probe.cjs +15 -2
- package/gsd-core/bin/lib/ui-frontend-evidence.cjs +167 -40
- package/gsd-core/bin/lib/undo-commit-selection.cjs +131 -0
- package/gsd-core/bin/lib/vendor/README.md +31 -9
- package/gsd-core/bin/lib/vendor/saxes.cjs +1934 -0
- package/gsd-core/bin/lib/vendor/saxes.cjs.LICENSE.txt +92 -0
- package/gsd-core/bin/lib/vendor/tap-parser.cjs +8927 -0
- package/gsd-core/bin/lib/vendor/tap-parser.cjs.LICENSE.txt +152 -0
- package/gsd-core/bin/lib/verification-command-router.cjs +2 -1
- package/gsd-core/bin/lib/verification.cjs +1378 -274
- package/gsd-core/bin/lib/verify-command-grounding.cjs +46 -2
- package/gsd-core/bin/lib/verify-command-router.cjs +18 -7
- package/gsd-core/bin/lib/verify.cjs +357 -531
- package/gsd-core/bin/lib/workstream-inventory-builder.cjs +14 -6
- package/gsd-core/bin/lib/workstream-inventory.cjs +31 -21
- package/gsd-core/bin/lib/workstream-name-policy.cjs +31 -1
- package/gsd-core/bin/lib/workstream.cjs +11 -2
- package/gsd-core/bin/lib/worktree-base-ref.cjs +482 -73
- package/gsd-core/bin/lib/worktree-safety.cjs +784 -51
- package/gsd-core/bin/shared/config-defaults.manifest.json +8 -0
- package/gsd-core/bin/shared/config-schema.manifest.json +5 -0
- package/gsd-core/references/autonomous-smart-discuss.md +2 -1
- package/gsd-core/references/autonomous-ui-design-contract.md +3 -3
- package/gsd-core/references/checkpoints.md +5 -3
- package/gsd-core/references/edge-probe-fixtures/01-round-half-even/expected-coverage.json +28 -3
- package/gsd-core/references/edge-probe-fixtures/02-merge-intervals/expected-coverage.json +37 -4
- package/gsd-core/references/edge-probe-fixtures/03-truncate-graphemes/expected-coverage.json +28 -3
- package/gsd-core/references/edge-probe-fixtures/04-money-rounding/expected-coverage.json +28 -3
- package/gsd-core/references/edge-probe-fixtures/05-list-dedupe/expected-coverage.json +37 -4
- package/gsd-core/references/edge-probe-fixtures/06-resolved-mixed/expected-coverage.json +37 -4
- package/gsd-core/references/edge-probe.md +195 -21
- package/gsd-core/references/execute-mvp-tdd.md +5 -10
- package/gsd-core/references/execute-phase-between-wave-reset.md +10 -6
- package/gsd-core/references/execute-phase-response-language.md +1 -1
- package/gsd-core/references/execute-phase-wave-guard.md +22 -11
- package/gsd-core/references/gsd-run-resolver.md +1 -1
- package/gsd-core/references/loop-hook-dispatch.md +7 -1
- package/gsd-core/references/model-profiles.md +1 -1
- package/gsd-core/references/offer-next.md +1 -1
- package/gsd-core/references/phase-argument-parsing.md +9 -7
- package/gsd-core/references/phase-id-convention.md +28 -0
- package/gsd-core/references/planner-gap-closure.md +2 -0
- package/gsd-core/references/planner-load-graph-context.md +24 -13
- package/gsd-core/references/planner-verify-command-grounding.md +14 -0
- package/gsd-core/references/planning-config.md +12 -3
- package/gsd-core/references/spidr-splitting.md +1 -1
- package/gsd-core/references/tdd.md +37 -8
- package/gsd-core/references/ui-consideration-probe.md +10 -5
- package/gsd-core/references/verifier-phase-gates.md +5 -2
- package/gsd-core/references/verify-command-path-resolvability.md +10 -2
- package/gsd-core/references/verify-mvp-mode.md +2 -2
- package/gsd-core/references/workstream-flag.md +33 -3
- package/gsd-core/references/worktree-path-safety.md +321 -0
- package/gsd-core/templates/README.md +1 -1
- package/gsd-core/templates/UAT.md +17 -1
- package/gsd-core/templates/config.json +2 -11
- package/gsd-core/templates/verification-report.md +1 -1
- package/gsd-core/workflows/_runtime-launcher.snippet.sh +1 -1
- package/gsd-core/workflows/add-backlog.md +1 -1
- package/gsd-core/workflows/add-phase.md +8 -7
- package/gsd-core/workflows/add-tests.md +4 -3
- package/gsd-core/workflows/add-todo.md +6 -5
- package/gsd-core/workflows/ai-integration-phase.md +13 -4
- package/gsd-core/workflows/audit-fix.md +1 -1
- package/gsd-core/workflows/audit-milestone.md +4 -3
- package/gsd-core/workflows/audit-uat.md +1 -1
- package/gsd-core/workflows/autonomous/steps/converge-fail-fast.md +9 -18
- package/gsd-core/workflows/autonomous.md +43 -23
- package/gsd-core/workflows/check-todos.md +7 -6
- package/gsd-core/workflows/cleanup.md +2 -2
- package/gsd-core/workflows/code-review/steps/dispatch-fix.md +4 -3
- package/gsd-core/workflows/code-review/steps/structural-pre-pass.md +20 -13
- package/gsd-core/workflows/code-review-fix.md +112 -25
- package/gsd-core/workflows/code-review.md +146 -135
- package/gsd-core/workflows/complete-milestone/detail/elaboration.md +4 -3
- package/gsd-core/workflows/complete-milestone.md +13 -8
- package/gsd-core/workflows/debug.md +32 -7
- package/gsd-core/workflows/diagnose-issues.md +3 -2
- package/gsd-core/workflows/discuss-phase/modes/advisor.md +1 -1
- package/gsd-core/workflows/discuss-phase/modes/chain.md +1 -1
- package/gsd-core/workflows/discuss-phase-assumptions.md +4 -3
- package/gsd-core/workflows/discuss-phase.md +4 -3
- package/gsd-core/workflows/do.md +2 -2
- package/gsd-core/workflows/docs-update.md +6 -5
- package/gsd-core/workflows/edit-phase.md +4 -3
- package/gsd-core/workflows/eval-review.md +14 -5
- package/gsd-core/workflows/execute-phase/detail/elaboration.md +2 -2
- package/gsd-core/workflows/execute-phase/steps/code-review-disposition.md +1019 -0
- package/gsd-core/workflows/execute-phase/steps/codebase-drift-gate.md +15 -4
- package/gsd-core/workflows/execute-phase/steps/completion-reconciliation.md +10 -7
- package/gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md +37 -3
- package/gsd-core/workflows/execute-phase/steps/gap-closure-artifacts.md +3 -1
- package/gsd-core/workflows/execute-phase/steps/partial-wave.md +2 -2
- package/gsd-core/workflows/execute-phase/steps/per-plan-executor-routing.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/per-plan-worktree-gate.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/post-merge-gate.md +46 -9
- package/gsd-core/workflows/execute-phase/steps/protected-branch.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/ready-wave-gate.md +37 -0
- package/gsd-core/workflows/execute-phase/steps/regression-gate-run.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/regression-gate.md +2 -0
- package/gsd-core/workflows/execute-phase/steps/tdd-applicability-resolution.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/threat-id-gate.md +28 -0
- package/gsd-core/workflows/execute-phase/steps/verify-phase-goal.md +187 -0
- package/gsd-core/workflows/execute-phase/steps/wave-post-gate-hooks.md +2 -3
- package/gsd-core/workflows/execute-phase/steps/worktree-base-check.md +25 -0
- package/gsd-core/workflows/execute-phase.md +96 -178
- package/gsd-core/workflows/execute-plan.md +18 -23
- package/gsd-core/workflows/explore.md +4 -4
- package/gsd-core/workflows/extract-learnings.md +4 -2
- package/gsd-core/workflows/fast.md +1 -1
- package/gsd-core/workflows/forensics.md +1 -1
- package/gsd-core/workflows/graduation.md +1 -1
- package/gsd-core/workflows/health.md +3 -2
- package/gsd-core/workflows/help/modes/full.compact.md +3 -3
- package/gsd-core/workflows/help/modes/full.md +5 -5
- package/gsd-core/workflows/help/modes/topic.md +15 -5
- package/gsd-core/workflows/import.md +4 -3
- package/gsd-core/workflows/inbox.md +2 -2
- package/gsd-core/workflows/ingest-docs.md +3 -3
- package/gsd-core/workflows/insert-phase.md +4 -3
- package/gsd-core/workflows/list-seeds.md +1 -1
- package/gsd-core/workflows/list-workspaces.md +1 -1
- package/gsd-core/workflows/manager.md +6 -4
- package/gsd-core/workflows/map-codebase.md +5 -4
- package/gsd-core/workflows/milestone-summary.md +3 -2
- package/gsd-core/workflows/mvp-phase.md +14 -14
- package/gsd-core/workflows/new-milestone.md +11 -11
- package/gsd-core/workflows/new-project/steps/auto-mode-config.md +3 -3
- package/gsd-core/workflows/new-project/steps/codebase-map-offer.md +1 -1
- package/gsd-core/workflows/new-project.md +7 -7
- package/gsd-core/workflows/new-workspace.md +2 -2
- package/gsd-core/workflows/next.md +1 -1
- package/gsd-core/workflows/note.md +1 -1
- package/gsd-core/workflows/onboard.md +1 -1
- package/gsd-core/workflows/pause-work.md +2 -2
- package/gsd-core/workflows/plan-phase/detail/elaboration.md +1 -1
- package/gsd-core/workflows/plan-phase/steps/chunked-planning-mode.md +17 -5
- package/gsd-core/workflows/plan-phase/steps/closed-phase-gate.md +1 -1
- package/gsd-core/workflows/plan-phase/steps/prd-express-path.md +1 -1
- package/gsd-core/workflows/plan-phase/steps/stall-detection-helpers.md +23 -5
- package/gsd-core/workflows/plan-phase.md +50 -24
- package/gsd-core/workflows/plan-review-convergence.md +21 -5
- package/gsd-core/workflows/plant-seed.md +62 -20
- package/gsd-core/workflows/pr-branch.md +113 -13
- package/gsd-core/workflows/profile-user.md +2 -2
- package/gsd-core/workflows/progress.md +19 -49
- package/gsd-core/workflows/quick/steps/plan-checker-loop.md +27 -0
- package/gsd-core/workflows/quick/steps/quick-verification.md +4 -4
- package/gsd-core/workflows/quick/steps/worktree-pre-dispatch-commit.md +30 -11
- package/gsd-core/workflows/quick-batch/steps/batch-init.md +1 -1
- package/gsd-core/workflows/quick-batch/steps/completion.md +1 -1
- package/gsd-core/workflows/quick-batch/steps/merge-wave.md +1 -1
- package/gsd-core/workflows/quick-batch/steps/planner-wave.md +1 -1
- package/gsd-core/workflows/quick-batch/steps/research-phase.md +1 -1
- package/gsd-core/workflows/quick-batch/steps/resume-mode.md +1 -1
- package/gsd-core/workflows/quick-batch/steps/verification-wave.md +9 -3
- package/gsd-core/workflows/quick-batch/steps/worktree-dispatch.md +1 -1
- package/gsd-core/workflows/quick-batch.md +15 -10
- package/gsd-core/workflows/quick.md +62 -39
- package/gsd-core/workflows/reapply-patches.md +9 -3
- package/gsd-core/workflows/remove-phase.md +3 -2
- package/gsd-core/workflows/remove-workspace.md +2 -2
- package/gsd-core/workflows/resume-project.md +3 -2
- package/gsd-core/workflows/review.md +33 -17
- package/gsd-core/workflows/scan.md +3 -2
- package/gsd-core/workflows/secure-phase.md +13 -13
- package/gsd-core/workflows/settings-advanced.md +30 -10
- package/gsd-core/workflows/settings-integrations.md +2 -3
- package/gsd-core/workflows/settings.md +4 -4
- package/gsd-core/workflows/ship.md +14 -13
- package/gsd-core/workflows/sketch-wrap-up.md +1 -1
- package/gsd-core/workflows/sketch.md +1 -1
- package/gsd-core/workflows/smart-entry.md +2 -2
- package/gsd-core/workflows/spec-phase.md +15 -5
- package/gsd-core/workflows/spike-wrap-up.md +1 -1
- package/gsd-core/workflows/spike.md +1 -1
- package/gsd-core/workflows/stats.md +1 -1
- package/gsd-core/workflows/sync-skills.md +5 -5
- package/gsd-core/workflows/thread.md +2 -2
- package/gsd-core/workflows/transition.md +13 -23
- package/gsd-core/workflows/ui-phase.md +48 -11
- package/gsd-core/workflows/ui-review.md +21 -6
- package/gsd-core/workflows/ultraplan-phase.md +3 -2
- package/gsd-core/workflows/undo.md +339 -20
- package/gsd-core/workflows/update.md +7 -7
- package/gsd-core/workflows/validate-phase.md +12 -13
- package/gsd-core/workflows/verify-work/detail/elaboration.md +43 -3
- package/gsd-core/workflows/verify-work/steps/automated-ui-verification.md +1 -1
- package/gsd-core/workflows/verify-work/steps/mvp-uat-framing.md +5 -3
- package/gsd-core/workflows/verify-work.md +129 -55
- package/hooks/dist/gsd-agent-isolation-guard.js +32 -0
- package/hooks/dist/gsd-check-update-worker.js +8 -0
- package/hooks/dist/gsd-check-update.js +8 -0
- package/hooks/dist/gsd-context-monitor.js +31 -8
- package/hooks/dist/gsd-cursor-subagent-start.js +8 -0
- package/hooks/dist/gsd-secret-read-guard.js +161 -4
- package/hooks/dist/gsd-statusline.js +85 -20
- package/hooks/dist/gsd-update-banner.js +8 -0
- package/hooks/dist/gsd-validate-commit.sh +63 -4
- package/hooks/dist/gsd-windsurf-pre-write.js +11 -2
- package/hooks/dist/gsd-workflow-guard.js +5 -4
- package/hooks/dist/gsd-worktree-path-guard.js +6 -2
- package/hooks/dist/lib/cli-exit.js +12 -0
- package/hooks/dist/lib/git-probe.js +17 -1
- package/hooks/dist/lib/isolation-sentinel.js +2 -2
- package/hooks/gsd-agent-isolation-guard.js +32 -0
- package/hooks/gsd-check-update-worker.js +8 -0
- package/hooks/gsd-check-update.js +8 -0
- package/hooks/gsd-context-monitor.js +31 -8
- package/hooks/gsd-cursor-subagent-start.js +8 -0
- package/hooks/gsd-secret-read-guard.js +161 -4
- package/hooks/gsd-statusline.js +85 -20
- package/hooks/gsd-update-banner.js +8 -0
- package/hooks/gsd-validate-commit.sh +63 -4
- package/hooks/gsd-windsurf-pre-write.js +11 -2
- package/hooks/gsd-workflow-guard.js +5 -4
- package/hooks/gsd-worktree-path-guard.js +6 -2
- package/hooks/hooks.json +5 -5
- package/hooks/lib/cli-exit.js +12 -0
- package/hooks/lib/git-probe.js +17 -1
- package/hooks/lib/isolation-sentinel.js +2 -2
- package/package.json +22 -4
- package/scripts/build-hooks.js +15 -6
- package/scripts/changeset/parse.cjs +52 -4
- package/scripts/check-contract-drift.cjs +127 -11
- package/scripts/ci-timeout-report.cjs +770 -4
- package/scripts/command-contract-helpers.cjs +15 -8
- package/scripts/docs-guard-registry.cjs +34 -0
- package/scripts/gen-features.cjs +13 -8
- package/scripts/gen-hooks-cli-exit.cjs +12 -28
- package/scripts/gen-loop-host-contract.cjs +79 -1
- package/scripts/gen-platform-conformance-tier.cjs +187 -1
- package/scripts/gen-plugin-skills.cjs +87 -1
- package/scripts/gen-research-agents.cjs +24 -31
- package/scripts/gen-scripts-cli-exit.cjs +30 -3
- package/scripts/gen-test-timings.cjs +32 -7
- package/scripts/lib/cli-exit.cjs +12 -0
- package/scripts/lib/macos-conformance-tier.generated.cjs +34 -2
- package/scripts/lib/ndjson-reporter.cjs +31 -5
- package/scripts/lib/platform-conformance-tier.generated.cjs +45 -5
- package/scripts/lib/registration-ledger-preload.cjs +155 -0
- package/scripts/lib/vendor-bundle.cjs +59 -0
- package/scripts/lib/vendor-licenses/saxes-6.0.0.txt +64 -0
- 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-completion-predicate-drift.cjs +18 -19
- package/scripts/lint-descriptions.cjs +7 -3
- package/scripts/lint-docs-guard-registration.exempt-baseline.cjs +38 -1
- package/scripts/lint-eslint-glob-coverage.allowlist.json +20 -0
- package/scripts/lint-frontmatter-fence-drift.cjs +313 -0
- package/scripts/lint-frontmatter-scalar-broad-grep.cjs +122 -16
- package/scripts/lint-phase-arg-assignment.cjs +257 -0
- package/scripts/lint-phase-enumeration-drift.cjs +12 -8
- package/scripts/lint-phase-id-drift.cjs +319 -5
- package/scripts/lint-planning-document-positive-control.cjs +329 -0
- package/scripts/lint-pr-branch-pattern-drift.cjs +148 -0
- package/scripts/lint-response-language-coverage.cjs +3 -0
- package/scripts/lint-retired-runtime-name.cjs +619 -0
- package/scripts/lint-skill-deps.cjs +7 -3
- package/scripts/lint-state-write-path-drift.cjs +93 -0
- package/scripts/lint-test-file-count.allowlist.json +38 -9
- package/scripts/lint-test-file-count.cjs +34 -1
- package/scripts/lint-vendored-deps.cjs +41 -5
- package/scripts/lint-workflow-shellcheck-baseline.json +25 -10
- package/scripts/mutation-matrix.cjs +50 -5
- package/scripts/prompt-injection-scan.sh +4 -0
- package/scripts/release-tarball-smoke.cjs +194 -1
- package/scripts/require-issue-link-policy.cjs +6 -2
- package/scripts/sync-runtime-launcher.cjs +184 -2
- package/scripts/verify-npm-publish.cjs +76 -20
- package/skills/gsd-add-tests/SKILL.md +6 -1
- package/skills/gsd-ai-integration-phase/SKILL.md +6 -1
- package/skills/gsd-audit-fix/SKILL.md +5 -0
- package/skills/gsd-audit-milestone/SKILL.md +6 -1
- package/skills/gsd-autonomous/SKILL.md +7 -2
- package/skills/gsd-capture/SKILL.md +9 -5
- package/skills/gsd-code-review/SKILL.md +7 -2
- package/skills/gsd-complete-milestone/SKILL.md +4 -0
- package/skills/gsd-config/SKILL.md +8 -4
- package/skills/gsd-debug/SKILL.md +11 -7
- package/skills/gsd-discuss-phase/SKILL.md +7 -3
- package/skills/gsd-docs-update/SKILL.md +12 -7
- package/skills/gsd-eval-review/SKILL.md +6 -1
- package/skills/gsd-execute-phase/SKILL.md +12 -7
- package/skills/gsd-extract-learnings/SKILL.md +5 -0
- package/skills/gsd-fast/SKILL.md +4 -0
- package/skills/gsd-forensics/SKILL.md +5 -1
- package/skills/gsd-graphify/SKILL.md +10 -6
- package/skills/gsd-health/SKILL.md +5 -0
- package/skills/gsd-help/SKILL.md +7 -2
- package/skills/gsd-import/SKILL.md +7 -3
- package/skills/gsd-inbox/SKILL.md +5 -0
- package/skills/gsd-ingest-docs/SKILL.md +5 -1
- package/skills/gsd-manager/SKILL.md +6 -1
- package/skills/gsd-map-codebase/SKILL.md +7 -3
- package/skills/gsd-mempalace-capture/SKILL.md +12 -4
- package/skills/gsd-mempalace-recall/SKILL.md +5 -1
- package/skills/gsd-milestone-summary/SKILL.md +5 -1
- package/skills/gsd-mvp-phase/SKILL.md +8 -3
- package/skills/gsd-new-milestone/SKILL.md +6 -1
- package/skills/gsd-new-project/SKILL.md +5 -0
- package/skills/gsd-next/SKILL.md +6 -1
- package/skills/gsd-ns-context/SKILL.md +4 -0
- package/skills/gsd-ns-ideate/SKILL.md +4 -0
- package/skills/gsd-ns-manage/SKILL.md +4 -0
- package/skills/gsd-ns-project/SKILL.md +4 -0
- package/skills/gsd-ns-review/SKILL.md +4 -0
- package/skills/gsd-ns-workflow/SKILL.md +4 -0
- package/skills/gsd-onboard/SKILL.md +6 -1
- package/skills/gsd-pause-work/SKILL.md +5 -1
- package/skills/gsd-phase/SKILL.md +8 -4
- package/skills/gsd-plan-phase/SKILL.md +6 -1
- package/skills/gsd-plan-review-convergence/SKILL.md +10 -6
- package/skills/gsd-pr-branch/SKILL.md +4 -0
- package/skills/gsd-profile-user/SKILL.md +5 -1
- package/skills/gsd-progress/SKILL.md +7 -2
- package/skills/gsd-quick/SKILL.md +16 -10
- package/skills/gsd-quick-batch/SKILL.md +21 -9
- package/skills/gsd-review/SKILL.md +7 -4
- package/skills/gsd-review-backlog/SKILL.md +3 -2
- package/skills/gsd-secure-phase/SKILL.md +6 -1
- package/skills/gsd-ship/SKILL.md +5 -0
- package/skills/gsd-sketch/SKILL.md +7 -2
- package/skills/gsd-spec-phase/SKILL.md +5 -1
- package/skills/gsd-spike/SKILL.md +8 -3
- package/skills/gsd-surface/SKILL.md +5 -1
- package/skills/gsd-thread/SKILL.md +4 -0
- package/skills/gsd-ui-phase/SKILL.md +6 -1
- package/skills/gsd-ui-review/SKILL.md +6 -1
- package/skills/gsd-ultraplan-phase/SKILL.md +5 -1
- package/skills/gsd-undo/SKILL.md +5 -1
- package/skills/gsd-update/SKILL.md +6 -2
- package/skills/gsd-validate-phase/SKILL.md +6 -1
- package/skills/gsd-verify-work/SKILL.md +6 -1
- package/skills/gsd-workspace/SKILL.md +7 -3
- package/skills/gsd-workstreams/SKILL.md +6 -6
- package/vscode/package.json +1 -1
|
@@ -12,6 +12,11 @@
|
|
|
12
12
|
* enforced by both consumers.
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
|
+
// The one frontmatter fence owner (src/frontmatter-fence.cts, built to bin/lib by
|
|
16
|
+
// build:lib). Every consumer of this module runs after the build: lint:ci (CI builds
|
|
17
|
+
// bin/lib first) and the test suite (pretest).
|
|
18
|
+
const { locateFrontmatterFence } = require('../gsd-core/bin/lib/frontmatter-fence.cjs');
|
|
19
|
+
|
|
15
20
|
const CANONICAL_TOOLS = new Set([
|
|
16
21
|
'Read', 'Write', 'Edit', 'Bash', 'Glob', 'Grep',
|
|
17
22
|
'Task', 'Agent', 'Skill', 'SlashCommand',
|
|
@@ -22,16 +27,15 @@ const CANONICAL_TOOLS = new Set([
|
|
|
22
27
|
]);
|
|
23
28
|
|
|
24
29
|
function parseFrontmatter(content) {
|
|
25
|
-
//
|
|
26
|
-
//
|
|
27
|
-
//
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
if (end === -1) return {};
|
|
30
|
+
// The block is the one the one fence owner finds (found while implementing
|
|
31
|
+
// #5105) — BOM, CRLF (a Windows autocrlf checkout), an adjacent empty block
|
|
32
|
+
// and a `----` look-alike all read exactly as every runtime reader sees them.
|
|
33
|
+
// Its lines are split CRLF-tolerantly so a trailing \r never leaks into a value.
|
|
34
|
+
const fence = locateFrontmatterFence(content);
|
|
35
|
+
if (!fence || !fence.closed) return {};
|
|
32
36
|
const fm = {};
|
|
33
37
|
let key = null;
|
|
34
|
-
for (const line of
|
|
38
|
+
for (const line of content.slice(fence.openEnd, fence.bodyEnd).split(/\r?\n/)) {
|
|
35
39
|
const kv = line.match(/^([a-zA-Z0-9_-]+):\s*(.*)/);
|
|
36
40
|
if (kv) { key = kv[1]; fm[key] = kv[2].trim(); }
|
|
37
41
|
else if (key && line.match(/^\s+-\s+/)) {
|
|
@@ -890,6 +894,7 @@ const VIOLATION_KINDS = Object.freeze({
|
|
|
890
894
|
LEGACY_READ_TAG: 'legacy_read_tag',
|
|
891
895
|
READ_TAG_GATE_MISSING: 'read_tag_gate_missing',
|
|
892
896
|
UNMATCHED_CONSUMER_TOKEN: 'unmatched_consumer_token',
|
|
897
|
+
UNRESOLVED_REFERENCE_INCLUDE: 'unresolved_reference_include',
|
|
893
898
|
});
|
|
894
899
|
|
|
895
900
|
/**
|
|
@@ -925,6 +930,8 @@ const REMEDIES = Object.freeze({
|
|
|
925
930
|
"add the <required_reading> MUST-Read gate clause to this agent's instructions (see docs/AGENTS.md)",
|
|
926
931
|
unmatched_consumer_token:
|
|
927
932
|
'declare the producing agent and marker in the registry, or remove the dispatch — nothing emits this token',
|
|
933
|
+
unresolved_reference_include:
|
|
934
|
+
'fix the gsd-core/references/ pointer so it names an existing reference file whole and ends at the name (whitespace, end of line, or closing punctuation)',
|
|
928
935
|
});
|
|
929
936
|
|
|
930
937
|
/**
|
|
@@ -174,6 +174,7 @@ const DOCS_GUARD_TESTS = {
|
|
|
174
174
|
// entry).
|
|
175
175
|
'tests/learnings.test.cjs': ['docs/FEATURES.md'],
|
|
176
176
|
'tests/analyze-dependencies.test.cjs': ['docs/COMMANDS.md'],
|
|
177
|
+
'tests/auto-select-attribute.test.cjs': ['docs/reference/plan-md.md'],
|
|
177
178
|
'tests/autonomous-converge.test.cjs': [
|
|
178
179
|
'docs/COMMANDS.md',
|
|
179
180
|
'docs/how-to/run-phases-autonomously.md',
|
|
@@ -200,6 +201,9 @@ const DOCS_GUARD_TESTS = {
|
|
|
200
201
|
'tests/compact-content-4139.test.cjs': ['docs/CONFIGURATION.md'],
|
|
201
202
|
'tests/config-field-docs.test.cjs': ['docs/CONFIGURATION.md'],
|
|
202
203
|
'tests/config.test.cjs': ['docs/CONFIGURATION.md'],
|
|
204
|
+
// #4974: doc/workflow content-parity checks read docs/CONFIGURATION.md's
|
|
205
|
+
// Gate Settings section directly (see tests/gates-confirmation-toggle-config.test.cjs).
|
|
206
|
+
'tests/gates-confirmation-toggle-config.test.cjs': ['docs/CONFIGURATION.md'],
|
|
203
207
|
'tests/context-index-sync.test.cjs': ['docs/CONTEXT-INDEX.json'],
|
|
204
208
|
'tests/context-predicates-query.test.cjs': ['docs/contributor-standards.md'],
|
|
205
209
|
// SCAN_DIRS includes 'docs' and recursively walks every .md file under it
|
|
@@ -272,6 +276,32 @@ const DOCS_GUARD_TESTS = {
|
|
|
272
276
|
// seeding, so a content edit to any of them (e.g. renaming a landmark
|
|
273
277
|
// heading/string a hostile-input test targets) can change this test's
|
|
274
278
|
// fixture assumptions.
|
|
279
|
+
// #4728: pins the retirement of Gemini CLI prose from every localized
|
|
280
|
+
// how-to/ARCHITECTURE/USER-GUIDE/CONFIGURATION/context-monitor mirror, plus
|
|
281
|
+
// the PRESERVE (Antigravity) and MODEL-AXIS (Gemini 2.5 Pro) negative-space
|
|
282
|
+
// checks in the same locales.
|
|
283
|
+
'tests/gemini-runtime-removed.test.cjs': [
|
|
284
|
+
'docs/ja-JP/how-to/install-on-your-runtime.md',
|
|
285
|
+
'docs/ko-KR/how-to/install-on-your-runtime.md',
|
|
286
|
+
'docs/pt-BR/how-to/install-on-your-runtime.md',
|
|
287
|
+
'docs/zh-CN/how-to/install-on-your-runtime.md',
|
|
288
|
+
'docs/ja-JP/ARCHITECTURE.md',
|
|
289
|
+
'docs/ko-KR/ARCHITECTURE.md',
|
|
290
|
+
'docs/pt-BR/ARCHITECTURE.md',
|
|
291
|
+
'docs/zh-CN/ARCHITECTURE.md',
|
|
292
|
+
'docs/ja-JP/USER-GUIDE.md',
|
|
293
|
+
'docs/ko-KR/USER-GUIDE.md',
|
|
294
|
+
'docs/pt-BR/USER-GUIDE.md',
|
|
295
|
+
'docs/zh-CN/USER-GUIDE.md',
|
|
296
|
+
'docs/ja-JP/CONFIGURATION.md',
|
|
297
|
+
'docs/ko-KR/CONFIGURATION.md',
|
|
298
|
+
'docs/pt-BR/CONFIGURATION.md',
|
|
299
|
+
'docs/zh-CN/CONFIGURATION.md',
|
|
300
|
+
'docs/ja-JP/context-monitor.md',
|
|
301
|
+
'docs/ko-KR/context-monitor.md',
|
|
302
|
+
'docs/pt-BR/context-monitor.md',
|
|
303
|
+
'docs/zh-CN/context-monitor.md',
|
|
304
|
+
],
|
|
275
305
|
'tests/gen-state-md-docs.test.cjs': [
|
|
276
306
|
'docs/reference/state-md.md',
|
|
277
307
|
'docs/ja-JP/reference/state-md.md',
|
|
@@ -376,6 +406,7 @@ const DOCS_GUARD_TESTS = {
|
|
|
376
406
|
'docs/how-to/plan-a-phase.md',
|
|
377
407
|
'docs/AGENTS.md',
|
|
378
408
|
],
|
|
409
|
+
'tests/ui-interaction-capture.test.cjs': ['docs/CONFIGURATION.md'],
|
|
379
410
|
'tests/ui-spec-inventory-provenance.test.cjs': [
|
|
380
411
|
'docs/FEATURES.md',
|
|
381
412
|
'docs/how-to/design-a-ui-phase.md',
|
|
@@ -390,6 +421,9 @@ const DOCS_GUARD_TESTS = {
|
|
|
390
421
|
'tests/verifier-behavior-unverified.test.cjs': ['docs/reference/planning-artifacts.md'],
|
|
391
422
|
'tests/verifier-coincidental-reliance.test.cjs': ['docs/AGENTS.md'],
|
|
392
423
|
'tests/verify.test.cjs': ['docs/reference/plan-md.md'],
|
|
424
|
+
// #5118 V43: asserts the deleted execute-phase/steps/stale-reverification.md
|
|
425
|
+
// is absent from the generated inventory manifest.
|
|
426
|
+
'tests/verify-lifecycle-writes-e2e.test.cjs': ['docs/INVENTORY-MANIFEST.json'],
|
|
393
427
|
'tests/workflow-fragments.test.cjs': ['docs/reference/workflow-fragments.md'],
|
|
394
428
|
};
|
|
395
429
|
|
package/scripts/gen-features.cjs
CHANGED
|
@@ -66,6 +66,10 @@ const fs = require('node:fs');
|
|
|
66
66
|
const path = require('node:path');
|
|
67
67
|
|
|
68
68
|
const { ExitError, runMain } = require('./lib/cli-exit.cjs');
|
|
69
|
+
// The one frontmatter fence owner (src/frontmatter-fence.cts, built by build:lib).
|
|
70
|
+
// Every entry point runs after the build: lint:generated-sync inside lint:ci (CI
|
|
71
|
+
// builds bin/lib first), regen:derived (`npm run build` first), and the tests.
|
|
72
|
+
const { locateFrontmatterFence } = require('../gsd-core/bin/lib/frontmatter-fence.cjs');
|
|
69
73
|
|
|
70
74
|
const ROOT = path.resolve(__dirname, '..');
|
|
71
75
|
const FEATURES_DIR = path.join(ROOT, 'docs', 'features');
|
|
@@ -255,13 +259,12 @@ function renderFrontmatter(data, body) {
|
|
|
255
259
|
*/
|
|
256
260
|
function parseFrontmatter(text) {
|
|
257
261
|
const normalized = String(text).replace(/\r\n/g, '\n');
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
if (end === -1) return { data: null, body: normalized };
|
|
262
|
+
// The block is the one the one fence owner finds (found while implementing #5105).
|
|
263
|
+
const fence = locateFrontmatterFence(normalized);
|
|
264
|
+
if (!fence || !fence.closed) return { data: null, body: normalized };
|
|
262
265
|
|
|
263
266
|
const data = {};
|
|
264
|
-
for (const line of normalized.slice(
|
|
267
|
+
for (const line of normalized.slice(fence.openEnd, fence.bodyEnd).split('\n')) {
|
|
265
268
|
if (line.trim() === '') continue;
|
|
266
269
|
const sep = line.indexOf(':');
|
|
267
270
|
if (sep === -1) continue;
|
|
@@ -270,9 +273,11 @@ function parseFrontmatter(text) {
|
|
|
270
273
|
data[key] = parseScalar(line.slice(sep + 1).trim());
|
|
271
274
|
}
|
|
272
275
|
|
|
273
|
-
//
|
|
274
|
-
// closing fence is consumed here so body
|
|
275
|
-
|
|
276
|
+
// Past the closing fence line and its line ending; the blank line
|
|
277
|
+
// renderFrontmatter emits after the closing fence is consumed here so body
|
|
278
|
+
// text starts at its first real line.
|
|
279
|
+
let body = normalized.slice(fence.closingFenceEnd);
|
|
280
|
+
if (body.startsWith('\n')) body = body.slice(1);
|
|
276
281
|
if (body.startsWith('\n')) body = body.slice(1);
|
|
277
282
|
return { data, body };
|
|
278
283
|
}
|
|
@@ -18,6 +18,17 @@
|
|
|
18
18
|
* target (`.js`, not `.cjs`, to match the hooks/lib/*.js convention) would
|
|
19
19
|
* tangle a script that is otherwise simple and single-purpose.
|
|
20
20
|
*
|
|
21
|
+
* compileToTemp() itself IS shared (required from ./gen-scripts-cli-exit.cjs)
|
|
22
|
+
* rather than duplicated: it compiles only src/cli-exit.cts (via a throwaway
|
|
23
|
+
* tsconfig that extends tsconfig.build.json with files/include scoped to
|
|
24
|
+
* that one module) rather than the whole project, since the emit this
|
|
25
|
+
* script needs depends only on cli-exit.cts's own import closure. A
|
|
26
|
+
* whole-project `-p tsconfig.build.json` type-check of every .cts file under
|
|
27
|
+
* a fixed COMPILE_TIMEOUT_MS ceiling can get killed by that timeout on a
|
|
28
|
+
* CPU-contended shared bench even though the one file this script needs
|
|
29
|
+
* compiles in a fraction of the time — and the compile strategy itself has
|
|
30
|
+
* nothing sibling-specific to fork.
|
|
31
|
+
*
|
|
21
32
|
* Usage:
|
|
22
33
|
* node scripts/gen-hooks-cli-exit.cjs # same as --write
|
|
23
34
|
* node scripts/gen-hooks-cli-exit.cjs --write # write hooks/lib/cli-exit.js
|
|
@@ -35,14 +46,12 @@
|
|
|
35
46
|
|
|
36
47
|
'use strict';
|
|
37
48
|
|
|
38
|
-
const { execFileSync } = require('node:child_process');
|
|
39
49
|
const fs = require('node:fs');
|
|
40
|
-
const os = require('node:os');
|
|
41
50
|
const path = require('node:path');
|
|
51
|
+
const { compileToTemp } = require('./gen-scripts-cli-exit.cjs');
|
|
42
52
|
|
|
43
53
|
const REPO_ROOT = path.resolve(__dirname, '..');
|
|
44
54
|
const OUTPUT_PATH = path.join(REPO_ROOT, 'hooks', 'lib', 'cli-exit.js');
|
|
45
|
-
const COMPILE_TIMEOUT_MS = 60_000;
|
|
46
55
|
|
|
47
56
|
/** Frozen reason codes so tests assert on structure, not prose. */
|
|
48
57
|
const REASON = Object.freeze({
|
|
@@ -78,31 +87,6 @@ const BANNER = [
|
|
|
78
87
|
'',
|
|
79
88
|
].join('\n');
|
|
80
89
|
|
|
81
|
-
/** Compile the whole project to a throwaway outDir so the work tree is untouched. */
|
|
82
|
-
function compileToTemp() {
|
|
83
|
-
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-hooks-cli-exit-'));
|
|
84
|
-
try {
|
|
85
|
-
execFileSync(
|
|
86
|
-
process.execPath,
|
|
87
|
-
[
|
|
88
|
-
path.join(REPO_ROOT, 'node_modules', 'typescript', 'bin', 'tsc'),
|
|
89
|
-
'-p', path.join(REPO_ROOT, 'tsconfig.build.json'),
|
|
90
|
-
'--outDir', tmp,
|
|
91
|
-
// A throwaway outDir must not reuse the in-tree incremental state, or
|
|
92
|
-
// tsc skips emit for files it believes are already current.
|
|
93
|
-
'--incremental', 'false',
|
|
94
|
-
'--tsBuildInfoFile', 'null',
|
|
95
|
-
],
|
|
96
|
-
{ cwd: REPO_ROOT, encoding: 'utf8', stdio: 'pipe', timeout: COMPILE_TIMEOUT_MS },
|
|
97
|
-
);
|
|
98
|
-
return { ok: true, dir: tmp };
|
|
99
|
-
} catch (err) {
|
|
100
|
-
fs.rmSync(tmp, { recursive: true, force: true });
|
|
101
|
-
const detail = [err.stdout, err.stderr].filter(Boolean).join('\n').trim();
|
|
102
|
-
return { ok: false, detail };
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
|
|
106
90
|
/**
|
|
107
91
|
* Compile src/cli-exit.cts to a throwaway outDir and return the expected
|
|
108
92
|
* generated content (banner + compiled bytes, with the sibling registry
|
|
@@ -37,7 +37,16 @@ const STEP_WORKFLOWS = [
|
|
|
37
37
|
{ file: 'quick.md', point: 'plan:pre', kinds: ['contribution'], into: 'planner' },
|
|
38
38
|
],
|
|
39
39
|
},
|
|
40
|
-
{
|
|
40
|
+
{
|
|
41
|
+
file: 'execute-phase.md',
|
|
42
|
+
step: 'execute',
|
|
43
|
+
// #5118: execute:post's step AND gate dispatch (code_review_gate with
|
|
44
|
+
// its TDD escalation) lives in the ONE shared verification step that
|
|
45
|
+
// execute-phase includes (and verify-work includes too) — no per-caller copy.
|
|
46
|
+
auxiliaryHosts: [
|
|
47
|
+
{ file: 'execute-phase/steps/verify-phase-goal.md', point: 'execute:post', kinds: ['step', 'gate'] },
|
|
48
|
+
],
|
|
49
|
+
},
|
|
41
50
|
{ file: 'verify-work.md', step: 'verify' },
|
|
42
51
|
{ file: 'ship.md', step: 'ship' },
|
|
43
52
|
];
|
|
@@ -78,6 +87,23 @@ const ROLE_TO_AGENT = {
|
|
|
78
87
|
verifier: 'gsd-verifier',
|
|
79
88
|
};
|
|
80
89
|
|
|
90
|
+
// #4740 — Role → family mapping. Three families: orchestration, planning,
|
|
91
|
+
// execution. This constrains what a workflow may DECLARE in agent-roles for
|
|
92
|
+
// a given step (admissibility), a separate concern from ROLE_TO_AGENT's
|
|
93
|
+
// agent-file presence check above.
|
|
94
|
+
const ROLE_FAMILY = {
|
|
95
|
+
orchestrator: 'orchestration',
|
|
96
|
+
researcher: 'planning', planner: 'planning', checker: 'planning',
|
|
97
|
+
executor: 'execution', verifier: 'execution',
|
|
98
|
+
};
|
|
99
|
+
const EXPECTED_FAMILY_BY_STEP = {
|
|
100
|
+
discuss: 'orchestration',
|
|
101
|
+
plan: 'planning',
|
|
102
|
+
execute: 'execution',
|
|
103
|
+
verify: 'orchestration',
|
|
104
|
+
ship: 'orchestration',
|
|
105
|
+
};
|
|
106
|
+
|
|
81
107
|
// ─── Parser ───────────────────────────────────────────────────────────────────
|
|
82
108
|
|
|
83
109
|
/**
|
|
@@ -228,6 +254,52 @@ function crossCheckRoles(content, agentRoles, fileName) {
|
|
|
228
254
|
return errors;
|
|
229
255
|
}
|
|
230
256
|
|
|
257
|
+
// ─── Cross-check: declared roles vs. their permitted family (#4740) ──────────
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* For a step, verify every role declared in agentRoles belongs to that step's
|
|
261
|
+
* expected family (orchestration / planning / execution).
|
|
262
|
+
*
|
|
263
|
+
* Unlike `assertPointsCoverage`'s `if (!expected) continue // caught
|
|
264
|
+
* elsewhere` guard, an unknown step here fails CLOSED: for points
|
|
265
|
+
* there is a second net (the canonical-set and duplicate checks), but nothing
|
|
266
|
+
* else in the repo validates role families, so failing open on an unknown
|
|
267
|
+
* step would make it the one input that silently bypasses this gate — the
|
|
268
|
+
* "unknown resolving to a safe known" failure mode this check exists to close.
|
|
269
|
+
*
|
|
270
|
+
* Pure: never sorts, de-dupes, or otherwise mutates `agentRoles` — the same
|
|
271
|
+
* array `buildContract` puts into the generated contract.
|
|
272
|
+
*
|
|
273
|
+
* @param {string} step The step named in the marker block.
|
|
274
|
+
* @param {string[]} agentRoles Roles declared in the block.
|
|
275
|
+
* @param {string} fileName For error messages.
|
|
276
|
+
* @returns {string[]} Array of error strings; empty = OK.
|
|
277
|
+
*/
|
|
278
|
+
function crossCheckRoleFamilies(step, agentRoles, fileName) {
|
|
279
|
+
const expectedFamily = EXPECTED_FAMILY_BY_STEP[step];
|
|
280
|
+
if (!expectedFamily) {
|
|
281
|
+
return [fileName + ': step "' + step + '" has no entry in EXPECTED_FAMILY_BY_STEP'];
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
const errors = [];
|
|
285
|
+
for (const role of agentRoles) {
|
|
286
|
+
const family = ROLE_FAMILY[role];
|
|
287
|
+
if (!family) {
|
|
288
|
+
errors.push(
|
|
289
|
+
fileName + ': declared agent-role "' + role + '" has no entry in ROLE_FAMILY mapping',
|
|
290
|
+
);
|
|
291
|
+
continue;
|
|
292
|
+
}
|
|
293
|
+
if (family !== expectedFamily) {
|
|
294
|
+
errors.push(
|
|
295
|
+
fileName + ': declared agent-role "' + role + '" (family "' + family +
|
|
296
|
+
'") is not permitted at step "' + step + '" (expected family "' + expectedFamily + '")',
|
|
297
|
+
);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
return errors;
|
|
301
|
+
}
|
|
302
|
+
|
|
231
303
|
// ─── 12-points coverage assertion ────────────────────────────────────────────
|
|
232
304
|
|
|
233
305
|
/**
|
|
@@ -331,6 +403,10 @@ function buildContract(workflowsDir) {
|
|
|
331
403
|
const roleErrors = crossCheckRoles(content, entry.agentRoles, file);
|
|
332
404
|
allErrors.push(...roleErrors);
|
|
333
405
|
|
|
406
|
+
// Cross-check role families (#4740)
|
|
407
|
+
const roleFamilyErrors = crossCheckRoleFamilies(entry.step, entry.agentRoles, file);
|
|
408
|
+
allErrors.push(...roleFamilyErrors);
|
|
409
|
+
|
|
334
410
|
for (const auxiliary of auxiliaryHosts) {
|
|
335
411
|
let auxiliaryContent;
|
|
336
412
|
try {
|
|
@@ -667,6 +743,7 @@ function getWiredLoopPoints(repoRoot) {
|
|
|
667
743
|
module.exports = {
|
|
668
744
|
parseLoopHostBlock,
|
|
669
745
|
crossCheckRoles,
|
|
746
|
+
crossCheckRoleFamilies,
|
|
670
747
|
assertPointsCoverage,
|
|
671
748
|
buildContract,
|
|
672
749
|
serializeContract,
|
|
@@ -676,6 +753,7 @@ module.exports = {
|
|
|
676
753
|
CANONICAL_POINTS,
|
|
677
754
|
EXPECTED_POINTS_BY_STEP,
|
|
678
755
|
ROLE_TO_AGENT,
|
|
756
|
+
ROLE_FAMILY,
|
|
679
757
|
scanWiredPoints,
|
|
680
758
|
getWiredLoopPoints,
|
|
681
759
|
coveredKindsInRegion,
|
|
@@ -97,6 +97,26 @@
|
|
|
97
97
|
* point the CLI at a small temp fixture tree instead of this repo's real,
|
|
98
98
|
* 900+-file tests/ tree. Production usage (package.json's lint:generated-sync
|
|
99
99
|
* / regen:derived chains) passes no flags and gets the real repo paths.
|
|
100
|
+
*
|
|
101
|
+
* #5074 — the `.platform` sibling rule. A handful of the heaviest tier files
|
|
102
|
+
* carry their real Windows signal in only a few tests out of hundreds; the
|
|
103
|
+
* fix is to move those tests into a same-directory `X.platform.test.cjs`
|
|
104
|
+
* sibling of `X.test.cjs`, so only the sibling (not the whole base) needs
|
|
105
|
+
* real-OS coverage. That split is enforced here as a FAILING invariant
|
|
106
|
+
* (`findPlatformSplitViolations`, wired into `classifyTree`) rather than as a
|
|
107
|
+
* silent filename-based exclusion rule, for two reasons: epic #4589 Phase 2
|
|
108
|
+
* requires real-OS overrides to be centrally enumerated, not implied by a
|
|
109
|
+
* naming convention (a bare "base excluded whenever a sibling exists" rule
|
|
110
|
+
* would BE such a convention); and a platform-sensitive test later added
|
|
111
|
+
* back into a split base must not silently re-admit the whole base to
|
|
112
|
+
* Linux-only on the Windows tier — it must fail generation loudly instead, at
|
|
113
|
+
* the same `lint:generated-sync` gate this module already backs. This
|
|
114
|
+
* invariant is scoped to the Windows CATEGORIES and wired only into
|
|
115
|
+
* `classifyTree`; `classifyMacosTree` stays pure content selection by design,
|
|
116
|
+
* so a macOS-only signal (e.g. case-sensitivity wording) left in a split base
|
|
117
|
+
* re-admits that base to the macOS tier instead of failing — over-inclusion,
|
|
118
|
+
* the safe direction. See docs/how-to/split-platform-sensitive-tests.md for
|
|
119
|
+
* the authoring rules this invariant enforces.
|
|
100
120
|
*/
|
|
101
121
|
|
|
102
122
|
const fs = require('node:fs');
|
|
@@ -316,6 +336,140 @@ function classifyMacosContent(content) {
|
|
|
316
336
|
return { needsRealOs: signals.length > 0, signals };
|
|
317
337
|
}
|
|
318
338
|
|
|
339
|
+
/**
|
|
340
|
+
* The `.platform` sibling suffix (#5074). A sibling of `X.test.cjs` is a file
|
|
341
|
+
* whose basename ENDS with this suffix and has a non-empty stem before it —
|
|
342
|
+
* `foo.platform.test.cjs` is a sibling of `foo.test.cjs`, but a bare
|
|
343
|
+
* `platform.test.cjs` (empty stem) is not, and neither is a hyphenated name
|
|
344
|
+
* like `x-platform.test.cjs` or `platform-conformance-tier.test.cjs` (this
|
|
345
|
+
* module's own test file) — the rule keys on the literal dotted suffix, not
|
|
346
|
+
* on the substring "platform" appearing anywhere in the name.
|
|
347
|
+
*/
|
|
348
|
+
const PLATFORM_SIBLING_SUFFIX = '.platform.test.cjs';
|
|
349
|
+
|
|
350
|
+
/**
|
|
351
|
+
* Parse `rel` (a POSIX `tests/...`-relative path) as a `.platform` sibling.
|
|
352
|
+
* Returns `{ dir, stem }` when it is one (dir is the POSIX dirname, stem is
|
|
353
|
+
* the basename with `PLATFORM_SIBLING_SUFFIX` stripped), or `null` when it is
|
|
354
|
+
* not — either because the basename does not end with the suffix, or because
|
|
355
|
+
* the stem would be empty (the `platform.test.cjs` negative-space case).
|
|
356
|
+
*
|
|
357
|
+
* @param {string} rel
|
|
358
|
+
* @returns {{ dir: string, stem: string } | null}
|
|
359
|
+
*/
|
|
360
|
+
function parsePlatformSibling(rel) {
|
|
361
|
+
const dir = path.posix.dirname(rel);
|
|
362
|
+
const base = path.posix.basename(rel);
|
|
363
|
+
if (!base.endsWith(PLATFORM_SIBLING_SUFFIX)) return null;
|
|
364
|
+
const stem = base.slice(0, -PLATFORM_SIBLING_SUFFIX.length);
|
|
365
|
+
if (stem.length === 0) return null;
|
|
366
|
+
return { dir, stem };
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
/** Resolve `X.test.cjs`'s rel path for a `.platform` sibling's `{ dir, stem }`. */
|
|
370
|
+
function baseRelFor({ dir, stem }) {
|
|
371
|
+
return (dir === '.' ? '' : dir + '/') + stem + '.test.cjs';
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
/** Resolve `X.platform.test.cjs`'s rel path for a base rel path (inverse of baseRelFor). */
|
|
375
|
+
function siblingRelForBase(baseRel) {
|
|
376
|
+
const dir = path.posix.dirname(baseRel);
|
|
377
|
+
const stem = path.posix.basename(baseRel).slice(0, -'.test.cjs'.length);
|
|
378
|
+
return (dir === '.' ? '' : dir + '/') + stem + PLATFORM_SIBLING_SUFFIX;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
/**
|
|
382
|
+
* The `.platform` sibling invariant (#5074), pure over an explicit `entries`
|
|
383
|
+
* array so it can be driven directly against fixtures (and against the real
|
|
384
|
+
* `ALWAYS_REAL_OS` map, which no temp fixture tree can key into) without
|
|
385
|
+
* touching the filesystem. `entries` is the ELIGIBLE (unit-suite) test-file
|
|
386
|
+
* set — a suite-tagged base is never a member, which is what makes it
|
|
387
|
+
* "never eligible" per the design doc's negative space rather than something
|
|
388
|
+
* this function has to special-case.
|
|
389
|
+
*
|
|
390
|
+
* For every `.platform` sibling present in `entries` (see
|
|
391
|
+
* `parsePlatformSibling`), checks three independent things against its base
|
|
392
|
+
* (`X.test.cjs`, same directory):
|
|
393
|
+
* - `base-has-signal` — the base IS in `entries` and still carries a
|
|
394
|
+
* genuine Windows signal (`classifyContent` on its own content) — the
|
|
395
|
+
* split did not actually remove the signal from the base.
|
|
396
|
+
* - `sibling-without-signal` — the sibling's OWN content carries no
|
|
397
|
+
* Windows signal, regardless of whether its base exists — a `.platform`
|
|
398
|
+
* file whose header claims a platform reason the classifier cannot see
|
|
399
|
+
* in its own text.
|
|
400
|
+
* - `base-always-real-os` — the base is a key of `alwaysRealOs`: the
|
|
401
|
+
* escape hatch says the WHOLE base needs real-OS coverage, which
|
|
402
|
+
* contradicts splitting only some of its tests into a sibling.
|
|
403
|
+
*
|
|
404
|
+
* A base that is absent from `entries` (suite-tagged, deleted, or renamed)
|
|
405
|
+
* never triggers `base-has-signal` — there is nothing to read.
|
|
406
|
+
*
|
|
407
|
+
* @param {{rel: string, content: string}[]} entries
|
|
408
|
+
* @param {Map<string, string>} [alwaysRealOs]
|
|
409
|
+
* @returns {{file: string, kind: 'base-has-signal'|'sibling-without-signal'|'base-always-real-os', signals: string[]}[]}
|
|
410
|
+
* sorted by file, then kind.
|
|
411
|
+
*/
|
|
412
|
+
function findPlatformSplitViolations(entries, alwaysRealOs = ALWAYS_REAL_OS) {
|
|
413
|
+
const byRel = new Map(entries.map((entry) => [entry.rel, entry]));
|
|
414
|
+
const violations = [];
|
|
415
|
+
|
|
416
|
+
for (const entry of entries) {
|
|
417
|
+
const parsed = parsePlatformSibling(entry.rel);
|
|
418
|
+
if (!parsed) continue;
|
|
419
|
+
const baseRel = baseRelFor(parsed);
|
|
420
|
+
|
|
421
|
+
const base = byRel.get(baseRel);
|
|
422
|
+
if (base !== undefined) {
|
|
423
|
+
const { signals } = classifyContent(base.content);
|
|
424
|
+
if (signals.length > 0) {
|
|
425
|
+
violations.push({ file: baseRel, kind: 'base-has-signal', signals });
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
const { signals: siblingSignals } = classifyContent(entry.content);
|
|
430
|
+
if (siblingSignals.length === 0) {
|
|
431
|
+
violations.push({ file: entry.rel, kind: 'sibling-without-signal', signals: [] });
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
if (alwaysRealOs.has(baseRel)) {
|
|
435
|
+
violations.push({ file: baseRel, kind: 'base-always-real-os', signals: [] });
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
violations.sort((a, b) => {
|
|
440
|
+
if (a.file !== b.file) return a.file < b.file ? -1 : 1;
|
|
441
|
+
if (a.kind !== b.kind) return a.kind < b.kind ? -1 : 1;
|
|
442
|
+
return 0;
|
|
443
|
+
});
|
|
444
|
+
return violations;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
/**
|
|
448
|
+
* Render `findPlatformSplitViolations`' result as the Error message
|
|
449
|
+
* `classifyTree` throws: starts with the literal phrase callers/tests grep
|
|
450
|
+
* for, lists one line per violation (file, kind, residual signal names, and
|
|
451
|
+
* — for `base-has-signal` only — the sibling path those tests belong in),
|
|
452
|
+
* and points at the how-to doc for the authoring rules.
|
|
453
|
+
*
|
|
454
|
+
* @param {ReturnType<typeof findPlatformSplitViolations>} violations
|
|
455
|
+
* @returns {string}
|
|
456
|
+
*/
|
|
457
|
+
function formatPlatformSplitViolations(violations) {
|
|
458
|
+
const lines = violations.map((v) => {
|
|
459
|
+
let line = ` - ${v.file} [${v.kind}]`;
|
|
460
|
+
if (v.signals.length > 0) line += ` residual signal(s): ${v.signals.join(', ')}`;
|
|
461
|
+
if (v.kind === 'base-has-signal') {
|
|
462
|
+
line += ` — move these tests into ${siblingRelForBase(v.file)}`;
|
|
463
|
+
}
|
|
464
|
+
return line;
|
|
465
|
+
});
|
|
466
|
+
return (
|
|
467
|
+
`platform split invariant violated (#5074) — ${violations.length} file(s):\n` +
|
|
468
|
+
lines.join('\n') +
|
|
469
|
+
'\nSee docs/how-to/split-platform-sensitive-tests.md for the split-invariant rules.\n'
|
|
470
|
+
);
|
|
471
|
+
}
|
|
472
|
+
|
|
319
473
|
/**
|
|
320
474
|
* Recursively collect every `*.test.cjs` file beneath `dir`.
|
|
321
475
|
* @param {string} dir
|
|
@@ -355,10 +509,15 @@ function classifyTree(testsDir) {
|
|
|
355
509
|
// tier — see the header doc-comment for why suite-tagged files are excluded
|
|
356
510
|
// entirely rather than merely deprioritized.
|
|
357
511
|
const unitFiles = absoluteFiles.filter((absPath) => suiteOf(absPath) === null);
|
|
512
|
+
const entries = [];
|
|
358
513
|
const flagged = [];
|
|
359
514
|
for (const absPath of unitFiles) {
|
|
360
515
|
const rel = 'tests/' + path.relative(testsDir, absPath).replace(/\\/g, '/');
|
|
361
516
|
const content = fs.readFileSync(absPath, 'utf8');
|
|
517
|
+
// Read once, reused below both for this file's own flagging AND (via
|
|
518
|
+
// `entries`) for the #5074 split-invariant check — a second read of the
|
|
519
|
+
// same file for that check would double the I/O for no reason.
|
|
520
|
+
entries.push({ rel, content });
|
|
362
521
|
const { needsRealOs } = classifyContent(content);
|
|
363
522
|
// The ALWAYS_REAL_OS escape hatch (Windows tier only — see its doc
|
|
364
523
|
// comment) is unioned in HERE, keyed off a file that this walk actually
|
|
@@ -369,6 +528,19 @@ function classifyTree(testsDir) {
|
|
|
369
528
|
flagged.push(rel);
|
|
370
529
|
}
|
|
371
530
|
}
|
|
531
|
+
|
|
532
|
+
// #5074: a `.platform` sibling pair that violates the split invariant
|
|
533
|
+
// (residual signal left in the base, a sibling with no signal of its own,
|
|
534
|
+
// or a base that ALWAYS_REAL_OS says needs the whole file on real OS) fails
|
|
535
|
+
// generation loudly rather than being silently included or excluded. This
|
|
536
|
+
// check is Windows-CATEGORIES-only and wired here, not into
|
|
537
|
+
// classifyMacosTree below — a macOS-only signal in a split base is handled
|
|
538
|
+
// there by ordinary content selection (over-inclusion), not this invariant.
|
|
539
|
+
const violations = findPlatformSplitViolations(entries, ALWAYS_REAL_OS);
|
|
540
|
+
if (violations.length > 0) {
|
|
541
|
+
throw new Error(formatPlatformSplitViolations(violations));
|
|
542
|
+
}
|
|
543
|
+
|
|
372
544
|
const result = [...new Set(flagged)].sort();
|
|
373
545
|
return { total: absoluteFiles.length, files: result };
|
|
374
546
|
}
|
|
@@ -484,7 +656,19 @@ function main() {
|
|
|
484
656
|
const isMacos = target === 'macos';
|
|
485
657
|
const label = isMacos ? 'gen-platform-conformance-tier --target macos' : 'gen-platform-conformance-tier';
|
|
486
658
|
const exportKey = isMacos ? 'MACOS_CONFORMANCE_TIER_FILES' : 'CONFORMANCE_TIER_FILES';
|
|
487
|
-
|
|
659
|
+
// classifyTree throws a plain Error on a #5074 split-invariant violation
|
|
660
|
+
// (it has no ExitError/exit-code opinion of its own — it is also called
|
|
661
|
+
// in-process by tests). runMain only special-cases ExitError (stderr gets
|
|
662
|
+
// err.message; anything else gets the full err.stack); wrapping it here
|
|
663
|
+
// keeps the CLI's stderr output to the clean, purpose-built violation
|
|
664
|
+
// report instead of a raw stack trace.
|
|
665
|
+
let total, files;
|
|
666
|
+
try {
|
|
667
|
+
({ total, files } = isMacos ? classifyMacosTree(testsDir) : classifyTree(testsDir));
|
|
668
|
+
} catch (err) {
|
|
669
|
+
if (err instanceof ExitError) throw err;
|
|
670
|
+
throw new ExitError(1, err && err.message ? err.message : String(err));
|
|
671
|
+
}
|
|
488
672
|
|
|
489
673
|
if (mode === 'write') {
|
|
490
674
|
fs.mkdirSync(path.dirname(outPath), { recursive: true });
|
|
@@ -554,4 +738,6 @@ module.exports = {
|
|
|
554
738
|
MACOS_CATEGORIES,
|
|
555
739
|
classifyMacosTree,
|
|
556
740
|
renderMacosGeneratedFile,
|
|
741
|
+
PLATFORM_SIBLING_SUFFIX,
|
|
742
|
+
findPlatformSplitViolations,
|
|
557
743
|
};
|