@opengsd/gsd-core 1.10.0 → 1.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/agents/gsd-code-fixer.md +1 -1
- package/agents/gsd-debug-session-manager.md +12 -1
- package/agents/gsd-debugger.md +1 -1
- package/agents/gsd-doc-synthesizer.md +2 -4
- package/agents/gsd-dom-verifier.md +169 -0
- package/agents/gsd-eval-auditor.md +1 -1
- package/agents/gsd-executor.md +22 -14
- package/agents/gsd-framework-selector.md +1 -3
- package/agents/gsd-intel-updater.md +1 -1
- package/agents/gsd-mempalace-curator.md +5 -3
- package/agents/gsd-pattern-mapper.md +11 -0
- package/agents/gsd-phase-researcher.md +23 -2
- package/agents/gsd-plan-checker.md +50 -53
- package/agents/gsd-planner.md +50 -50
- package/agents/gsd-project-researcher.md +1 -1
- package/agents/gsd-research-synthesizer.md +2 -2
- package/agents/gsd-roadmapper.md +15 -11
- package/agents/gsd-ui-checker.md +63 -4
- package/agents/gsd-ui-researcher.md +41 -3
- package/agents/gsd-user-profiler.md +3 -0
- package/agents/gsd-verifier.md +13 -4
- package/bin/install.js +1448 -1103
- package/commands/gsd/code-review.md +1 -1
- package/commands/gsd/discuss-phase.md +1 -1
- package/commands/gsd/execute-phase.md +1 -1
- package/commands/gsd/import.md +1 -1
- package/commands/gsd/map-codebase.md +1 -1
- package/commands/gsd/mempalace-capture.md +1 -1
- package/commands/gsd/mempalace-recall.md +1 -1
- package/commands/gsd/new-milestone.md +1 -1
- package/commands/gsd/quick.md +9 -5
- package/commands/gsd/review-backlog.md +2 -1
- package/commands/gsd/verify-work.md +1 -1
- package/gsd-core/bin/gsd-tools.cjs +1035 -138
- package/gsd-core/bin/lib/active-workstream-store.cjs +146 -22
- package/gsd-core/bin/lib/adr-parser.cjs +13 -7
- package/gsd-core/bin/lib/agent-install-check.cjs +392 -32
- package/gsd-core/bin/lib/api-coverage.cjs +33 -14
- package/gsd-core/bin/lib/artifacts.cjs +5 -0
- package/gsd-core/bin/lib/assumption-delta.cjs +32 -15
- package/gsd-core/bin/lib/audit-command-router.cjs +9 -2
- package/gsd-core/bin/lib/audit.cjs +1026 -268
- package/gsd-core/bin/lib/broken-windows.cjs +306 -28
- 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-lock.cjs +10 -4
- package/gsd-core/bin/lib/capability-registry.cjs +845 -130
- package/gsd-core/bin/lib/capability-source.cjs +92 -0
- package/gsd-core/bin/lib/capability-state.cjs +18 -3
- package/gsd-core/bin/lib/capability-trust.cjs +444 -25
- package/gsd-core/bin/lib/capability-validator.cjs +700 -40
- package/gsd-core/bin/lib/capability-writer.cjs +3 -2
- package/gsd-core/bin/lib/check-command-router.cjs +216 -42
- package/gsd-core/bin/lib/claude-orchestration.cjs +56 -3
- package/gsd-core/bin/lib/cli-exit.cjs +496 -10
- package/gsd-core/bin/lib/code-review-depth.cjs +288 -0
- package/gsd-core/bin/lib/codex-agent-toml.cjs +735 -0
- package/gsd-core/bin/lib/command-aliases.cjs +22 -0
- package/gsd-core/bin/lib/command-arg-projection.cjs +144 -14
- package/gsd-core/bin/lib/command-roster.cjs +44 -1
- package/gsd-core/bin/lib/command-routing-hub.cjs +31 -2
- package/gsd-core/bin/lib/commands.cjs +1172 -108
- package/gsd-core/bin/lib/commonjs-marker.cjs +12 -6
- package/gsd-core/bin/lib/complexity-trigger.cjs +1192 -0
- package/gsd-core/bin/lib/config-loader.cjs +187 -23
- package/gsd-core/bin/lib/config.cjs +102 -3
- package/gsd-core/bin/lib/configuration.cjs +129 -37
- package/gsd-core/bin/lib/core-utils.cjs +208 -33
- package/gsd-core/bin/lib/decisions.cjs +23 -0
- package/gsd-core/bin/lib/edge-probe.cjs +9 -1
- package/gsd-core/bin/lib/estimate-cli.cjs +55 -11
- package/gsd-core/bin/lib/exit-code-registry.cjs +98 -0
- package/gsd-core/bin/lib/fallow-runner.cjs +20 -44
- package/gsd-core/bin/lib/frontmatter.cjs +899 -229
- package/gsd-core/bin/lib/gap-checker.cjs +95 -10
- package/gsd-core/bin/lib/git-base-branch.cjs +276 -39
- package/gsd-core/bin/lib/gsd2-import.cjs +10 -1
- package/gsd-core/bin/lib/health-diagnostic-rules/agent-install.cjs +101 -0
- package/gsd-core/bin/lib/health-diagnostic-rules/config-validation.cjs +348 -0
- package/gsd-core/bin/lib/health-diagnostic-rules/consistency.cjs +149 -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 +268 -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 +187 -0
- package/gsd-core/bin/lib/health-diagnostic-types.cjs +68 -0
- package/gsd-core/bin/lib/health-diagnostic.cjs +451 -0
- package/gsd-core/bin/lib/host-integration.cjs +39 -6
- package/gsd-core/bin/lib/host-runtime-detection.cjs +134 -0
- package/gsd-core/bin/lib/init-command-router.cjs +118 -21
- package/gsd-core/bin/lib/init.cjs +439 -168
- package/gsd-core/bin/lib/install-effort-resolver.cjs +73 -30
- package/gsd-core/bin/lib/install-engine.cjs +811 -259
- package/gsd-core/bin/lib/install-fs-adapter.cjs +262 -0
- package/gsd-core/bin/lib/install-model-override-resolver.cjs +235 -0
- package/gsd-core/bin/lib/install-profiles.cjs +212 -61
- 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-report.cjs +3 -0
- package/gsd-core/bin/lib/installer-migrations/010-antigravity-retire-confighome-artifacts.cjs +169 -0
- package/gsd-core/bin/lib/installer-migrations.cjs +148 -38
- package/gsd-core/bin/lib/intel.cjs +101 -26
- package/gsd-core/bin/lib/io.cjs +170 -15
- package/gsd-core/bin/lib/learnings.cjs +85 -14
- package/gsd-core/bin/lib/legacy-cleanup.cjs +8 -2
- package/gsd-core/bin/lib/markdown-sectionizer.cjs +2 -1
- package/gsd-core/bin/lib/markdown-table.cjs +183 -22
- package/gsd-core/bin/lib/milestone-lock.cjs +248 -0
- package/gsd-core/bin/lib/milestone.cjs +842 -73
- package/gsd-core/bin/lib/model-catalog.cjs +232 -16
- package/gsd-core/bin/lib/model-resolver.cjs +193 -68
- package/gsd-core/bin/lib/normalize-test-command.cjs +1 -1
- package/gsd-core/bin/lib/onboard-projection.cjs +5 -1
- package/gsd-core/bin/lib/pattern.cjs +122 -0
- package/gsd-core/bin/lib/phase-estimation.cjs +18 -9
- package/gsd-core/bin/lib/phase-id.cjs +514 -40
- package/gsd-core/bin/lib/phase-lifecycle.cjs +52 -19
- package/gsd-core/bin/lib/phase-locator.cjs +262 -34
- package/gsd-core/bin/lib/phase.cjs +1038 -214
- package/gsd-core/bin/lib/plan-dependency-graph.cjs +72 -1
- package/gsd-core/bin/lib/plan-document.cjs +263 -0
- package/gsd-core/bin/lib/plan-drift-guard.cjs +120 -0
- package/gsd-core/bin/lib/plan-scan.cjs +98 -3
- package/gsd-core/bin/lib/planning-command-router.cjs +61 -0
- package/gsd-core/bin/lib/planning-inspect.cjs +1168 -0
- package/gsd-core/bin/lib/planning-scope.cjs +31 -0
- package/gsd-core/bin/lib/planning-snapshot.cjs +894 -0
- package/gsd-core/bin/lib/planning-workspace.cjs +112 -6
- package/gsd-core/bin/lib/probe-core.cjs +5 -2
- package/gsd-core/bin/lib/profile-output.cjs +1 -1
- package/gsd-core/bin/lib/profile-pipeline-command-router.cjs +50 -7
- package/gsd-core/bin/lib/profile-pipeline.cjs +6 -3
- package/gsd-core/bin/lib/real-home-guard.cjs +419 -0
- package/gsd-core/bin/lib/refactor-trigger-command-router.cjs +766 -0
- package/gsd-core/bin/lib/retired-artifact-cleanup.cjs +11 -6
- package/gsd-core/bin/lib/review-lane-descriptor.cjs +22 -13
- package/gsd-core/bin/lib/review-lane-invocation.cjs +30 -0
- package/gsd-core/bin/lib/review-lane-runner.cjs +421 -66
- package/gsd-core/bin/lib/review-reviewer-selection.cjs +13 -18
- package/gsd-core/bin/lib/roadmap-command-router.cjs +59 -11
- package/gsd-core/bin/lib/roadmap-parser.cjs +1006 -184
- package/gsd-core/bin/lib/roadmap-upgrade.cjs +37 -10
- package/gsd-core/bin/lib/roadmap.cjs +442 -96
- package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +702 -52
- package/gsd-core/bin/lib/runtime-artifact-install-plan.cjs +14 -2
- package/gsd-core/bin/lib/runtime-artifact-layout.cjs +459 -55
- package/gsd-core/bin/lib/runtime-config-adapter-registry.cjs +3 -2
- package/gsd-core/bin/lib/runtime-homes.cjs +69 -3
- package/gsd-core/bin/lib/runtime-hooks-surface.cjs +402 -58
- package/gsd-core/bin/lib/runtime-identity.cjs +234 -0
- package/gsd-core/bin/lib/runtime-name-policy.cjs +3 -1
- package/gsd-core/bin/lib/runtime-slash.cjs +96 -8
- package/gsd-core/bin/lib/security.cjs +104 -5
- package/gsd-core/bin/lib/shell-command-projection.cjs +342 -7
- package/gsd-core/bin/lib/smart-entry.cjs +133 -23
- package/gsd-core/bin/lib/spec-section.cjs +12 -7
- package/gsd-core/bin/lib/state-command-router.cjs +52 -19
- package/gsd-core/bin/lib/state-contract.cjs +359 -0
- package/gsd-core/bin/lib/state-document.cjs +338 -8
- package/gsd-core/bin/lib/state-md-schema.cjs +221 -0
- package/gsd-core/bin/lib/state-transition.cjs +846 -176
- package/gsd-core/bin/lib/state.cjs +2589 -369
- package/gsd-core/bin/lib/surface.cjs +33 -11
- package/gsd-core/bin/lib/task-command-router.cjs +111 -1
- package/gsd-core/bin/lib/task-content-resolution.cjs +368 -0
- package/gsd-core/bin/lib/teams-status.cjs +4 -1
- 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 +67 -23
- package/gsd-core/bin/lib/uat.cjs +1761 -167
- package/gsd-core/bin/lib/ui-consideration-probe.cjs +9 -1
- package/gsd-core/bin/lib/ui-frontend-evidence.cjs +157 -0
- package/gsd-core/bin/lib/ui-safety-gate.cjs +51 -12
- package/gsd-core/bin/lib/unusable-input.cjs +37 -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-command-router.cjs +2 -2
- package/gsd-core/bin/lib/validate.cjs +20 -6
- package/gsd-core/bin/lib/vendor/README.md +75 -0
- package/gsd-core/bin/lib/vendor/js-yaml.cjs +3014 -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 +272 -9
- package/gsd-core/bin/lib/verify-command-grounding.cjs +846 -0
- package/gsd-core/bin/lib/verify.cjs +453 -918
- package/gsd-core/bin/lib/workstream-inventory-builder.cjs +53 -32
- package/gsd-core/bin/lib/workstream-inventory.cjs +63 -10
- package/gsd-core/bin/lib/workstream-name-policy.cjs +25 -4
- package/gsd-core/bin/lib/workstream.cjs +2 -2
- package/gsd-core/bin/lib/worktree-base-ref.cjs +66 -12
- package/gsd-core/bin/lib/worktree-safety.cjs +341 -18
- package/gsd-core/bin/shared/config-defaults.manifest.json +8 -1
- package/gsd-core/bin/shared/config-schema.manifest.json +12 -1
- package/gsd-core/bin/shared/exit-codes.json +8 -0
- package/gsd-core/bin/shared/exit-codes.sh +20 -0
- package/gsd-core/bin/shared/model-catalog.json +8 -1
- package/gsd-core/references/agent-contracts.md +44 -26
- package/gsd-core/references/api-coverage.md +24 -2
- package/gsd-core/references/autonomous-smart-discuss.md +3 -3
- package/gsd-core/references/checkpoints.md +39 -21
- package/gsd-core/references/context-budget.md +1 -1
- package/gsd-core/references/decimal-phase-calculation.md +5 -5
- package/gsd-core/references/dispatch-isolation-gate.md +138 -0
- package/gsd-core/references/doc-conflict-engine.md +1 -1
- package/gsd-core/references/edge-probe.md +8 -0
- package/gsd-core/references/execute-mvp-tdd.md +4 -6
- package/gsd-core/references/execute-phase-between-wave-reset.md +15 -14
- 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 +17 -11
- package/gsd-core/references/failing-direction.md +78 -0
- package/gsd-core/references/gate-prompts.md +1 -1
- package/gsd-core/references/git-integration.md +5 -5
- package/gsd-core/references/git-planning-commit.md +5 -4
- package/gsd-core/references/gsd-run-resolver.md +1 -1
- package/gsd-core/references/loop-hook-dispatch.md +61 -2
- package/gsd-core/references/model-profiles.md +12 -4
- package/gsd-core/references/mvp-concepts.md +9 -9
- package/gsd-core/references/nyquist-compliance.md +74 -0
- package/gsd-core/references/offer-next.md +3 -5
- package/gsd-core/references/phase-argument-parsing.md +3 -3
- package/gsd-core/references/planner-failing-direction.md +53 -0
- package/gsd-core/references/planner-guidance.md +3 -9
- package/gsd-core/references/planner-human-verify-mode.md +15 -1
- package/gsd-core/references/planner-preconditions.md +1 -1
- package/gsd-core/references/planner-reviews.md +1 -1
- package/gsd-core/references/planner-revision.md +1 -1
- package/gsd-core/references/planner-verify-command-grounding.md +17 -0
- package/gsd-core/references/planning-config.md +44 -13
- package/gsd-core/references/reviewer-instances.md +31 -0
- package/gsd-core/references/revision-loop.md +1 -1
- package/gsd-core/references/runtime-aware-dispatch.md +1 -1
- package/gsd-core/references/specless-probe-fallback.md +1 -1
- package/gsd-core/references/tdd.md +1 -3
- package/gsd-core/references/ui-brand.md +65 -21
- package/gsd-core/references/ui-consideration-probe.md +1 -1
- package/gsd-core/references/universal-anti-patterns.md +5 -5
- package/gsd-core/references/verifier-phase-gates.md +192 -0
- package/gsd-core/references/verify-command-path-resolvability.md +42 -0
- package/gsd-core/references/verify-mvp-mode.md +2 -2
- package/gsd-core/references/workstream-flag.md +33 -17
- package/gsd-core/templates/README.md +1 -1
- package/gsd-core/templates/SECURITY.md +3 -3
- package/gsd-core/templates/UI-SPEC.md +25 -3
- package/gsd-core/templates/VALIDATION.md +3 -3
- package/gsd-core/templates/discussion-log.md +1 -1
- package/gsd-core/templates/phase-prompt.md +5 -4
- package/gsd-core/templates/state.md +11 -4
- package/gsd-core/templates/verification-report.md +9 -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 +3 -3
- package/gsd-core/workflows/add-tests.md +3 -8
- package/gsd-core/workflows/add-todo.md +1 -1
- package/gsd-core/workflows/ai-integration-phase.md +13 -20
- package/gsd-core/workflows/audit-fix.md +12 -3
- package/gsd-core/workflows/audit-milestone.md +9 -9
- package/gsd-core/workflows/audit-uat.md +17 -2
- package/gsd-core/workflows/autonomous/steps/converge-fail-fast.md +2 -2
- package/gsd-core/workflows/autonomous.md +11 -27
- package/gsd-core/workflows/check-todos.md +1 -1
- package/gsd-core/workflows/cleanup.md +64 -5
- package/gsd-core/workflows/code-review/steps/structural-pre-pass.md +14 -4
- package/gsd-core/workflows/code-review-fix.md +38 -11
- package/gsd-core/workflows/code-review.md +159 -52
- package/gsd-core/workflows/complete-milestone.md +151 -23
- package/gsd-core/workflows/debug.md +12 -8
- package/gsd-core/workflows/diagnose-issues.md +47 -15
- package/gsd-core/workflows/discuss-phase/modes/advisor.md +1 -1
- package/gsd-core/workflows/discuss-phase/modes/chain.md +5 -8
- package/gsd-core/workflows/discuss-phase/modes/default.md +1 -1
- package/gsd-core/workflows/discuss-phase/modes/text.md +1 -1
- package/gsd-core/workflows/discuss-phase-assumptions/steps/auto-advance-dispatch.md +1 -3
- package/gsd-core/workflows/discuss-phase-assumptions.md +4 -3
- package/gsd-core/workflows/discuss-phase.md +1 -1
- package/gsd-core/workflows/do.md +3 -6
- package/gsd-core/workflows/docs-update.md +5 -4
- package/gsd-core/workflows/edit-phase.md +27 -2
- package/gsd-core/workflows/eval-review.md +7 -14
- package/gsd-core/workflows/execute-phase/steps/codebase-drift-gate.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md +142 -15
- package/gsd-core/workflows/execute-phase/steps/gap-closure-artifacts.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/partial-wave.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/per-plan-executor-routing.md +77 -0
- package/gsd-core/workflows/execute-phase/steps/per-plan-worktree-gate.md +24 -4
- package/gsd-core/workflows/execute-phase/steps/post-merge-gate.md +2 -2
- package/gsd-core/workflows/execute-phase/steps/protected-branch.md +21 -0
- package/gsd-core/workflows/execute-phase/steps/regression-gate-run.md +2 -2
- package/gsd-core/workflows/execute-phase/steps/wave-post-gate-hooks.md +39 -0
- package/gsd-core/workflows/execute-phase.md +72 -100
- package/gsd-core/workflows/execute-plan.md +52 -15
- package/gsd-core/workflows/explore.md +131 -4
- package/gsd-core/workflows/extract-learnings.md +1 -1
- package/gsd-core/workflows/fast.md +10 -2
- package/gsd-core/workflows/forensics.md +1 -1
- package/gsd-core/workflows/graduation.md +5 -5
- package/gsd-core/workflows/health.md +76 -10
- package/gsd-core/workflows/import.md +18 -15
- package/gsd-core/workflows/inbox.md +4 -5
- package/gsd-core/workflows/ingest-docs.md +49 -16
- package/gsd-core/workflows/insert-phase.md +5 -5
- package/gsd-core/workflows/list-seeds.md +5 -3
- package/gsd-core/workflows/list-workspaces.md +1 -1
- package/gsd-core/workflows/manager.md +12 -23
- package/gsd-core/workflows/map-codebase.md +1 -1
- package/gsd-core/workflows/milestone-summary.md +1 -1
- package/gsd-core/workflows/mvp-phase.md +8 -5
- package/gsd-core/workflows/new-milestone.md +22 -29
- package/gsd-core/workflows/new-project/steps/auto-mode-config.md +1 -1
- package/gsd-core/workflows/new-project.md +26 -40
- package/gsd-core/workflows/new-workspace.md +1 -1
- package/gsd-core/workflows/next.md +14 -2
- package/gsd-core/workflows/pause-work.md +1 -1
- package/gsd-core/workflows/plan-phase/steps/adr-ingest-express-path.md +1 -1
- package/gsd-core/workflows/plan-phase/steps/chunked-planning-mode.md +1 -1
- package/gsd-core/workflows/plan-phase/steps/prd-express-path.md +2 -4
- package/gsd-core/workflows/plan-phase/steps/stall-detection-helpers.md +3 -3
- package/gsd-core/workflows/plan-phase.md +162 -59
- package/gsd-core/workflows/plan-review-convergence.md +96 -11
- package/gsd-core/workflows/plant-seed.md +2 -2
- package/gsd-core/workflows/pr-branch.md +187 -51
- package/gsd-core/workflows/profile-user.md +16 -14
- package/gsd-core/workflows/progress.md +61 -18
- package/gsd-core/workflows/quick/steps/discussion-phase.md +1 -3
- package/gsd-core/workflows/quick/steps/plan-checker-loop.md +5 -7
- package/gsd-core/workflows/quick/steps/quick-verification.md +28 -9
- package/gsd-core/workflows/quick/steps/research-phase.md +4 -6
- package/gsd-core/workflows/quick/steps/worktree-pre-dispatch-commit.md +3 -3
- package/gsd-core/workflows/quick.md +55 -44
- package/gsd-core/workflows/remove-phase.md +4 -4
- package/gsd-core/workflows/remove-workspace.md +2 -2
- package/gsd-core/workflows/resume-project.md +8 -12
- package/gsd-core/workflows/review.md +219 -20
- package/gsd-core/workflows/scan.md +1 -1
- package/gsd-core/workflows/secure-phase.md +3 -3
- package/gsd-core/workflows/session-report.md +2 -1
- package/gsd-core/workflows/settings-advanced.md +7 -9
- package/gsd-core/workflows/settings-integrations.md +64 -31
- package/gsd-core/workflows/settings.md +69 -7
- package/gsd-core/workflows/ship.md +116 -50
- package/gsd-core/workflows/sketch-wrap-up.md +11 -17
- package/gsd-core/workflows/sketch.md +12 -18
- package/gsd-core/workflows/smart-entry.md +3 -5
- package/gsd-core/workflows/spec-phase.md +53 -13
- package/gsd-core/workflows/spike-wrap-up.md +7 -11
- package/gsd-core/workflows/spike.md +20 -31
- package/gsd-core/workflows/stats.md +2 -2
- package/gsd-core/workflows/sync-skills.md +64 -9
- package/gsd-core/workflows/thread.md +11 -7
- package/gsd-core/workflows/transition.md +49 -14
- package/gsd-core/workflows/ui-phase.md +15 -21
- package/gsd-core/workflows/ui-review.md +8 -12
- package/gsd-core/workflows/ultraplan-phase.md +5 -13
- package/gsd-core/workflows/undo.md +8 -16
- package/gsd-core/workflows/update.md +7 -11
- package/gsd-core/workflows/validate-phase.md +3 -3
- package/gsd-core/workflows/verify-work/steps/automated-ui-verification.md +25 -1
- package/gsd-core/workflows/verify-work/steps/mvp-uat-framing.md +1 -1
- package/gsd-core/workflows/verify-work.md +66 -25
- package/hooks/dist/gsd-agent-isolation-guard.js +158 -30
- package/hooks/dist/gsd-check-update-worker.js +56 -13
- package/hooks/dist/gsd-check-update.js +19 -1
- package/hooks/dist/gsd-config-reload.js +18 -12
- package/hooks/dist/gsd-context-monitor.js +19 -10
- package/hooks/dist/gsd-cursor-post-tool.js +3 -1
- package/hooks/dist/gsd-cursor-pre-tool.js +2 -3
- package/hooks/dist/gsd-cursor-session-start.js +2 -1
- package/hooks/dist/gsd-cursor-stop.js +2 -1
- package/hooks/dist/gsd-cursor-subagent-start.js +83 -3
- package/hooks/dist/gsd-cursor-subagent-stop.js +6 -3
- package/hooks/dist/gsd-ensure-canonical-path.js +2 -1
- package/hooks/dist/gsd-graphify-update.sh +22 -18
- package/hooks/dist/gsd-node-runner.sh +76 -0
- package/hooks/dist/gsd-phase-boundary.sh +1 -0
- package/hooks/dist/gsd-prompt-guard.js +37 -27
- package/hooks/dist/gsd-read-guard.js +16 -7
- package/hooks/dist/gsd-read-injection-scanner.js +55 -32
- package/hooks/dist/gsd-session-state.sh +1 -0
- package/hooks/dist/gsd-statusline.js +231 -24
- package/hooks/dist/gsd-update-banner.js +22 -1
- package/hooks/dist/gsd-validate-commit.sh +80 -6
- package/hooks/dist/gsd-windsurf-pre-command.js +16 -11
- package/hooks/dist/gsd-windsurf-pre-write.js +22 -13
- package/hooks/dist/gsd-workflow-guard.js +162 -46
- package/hooks/dist/gsd-worktree-path-guard.js +36 -21
- package/hooks/dist/gsd-write-guard.js +35 -25
- package/hooks/dist/lib/cli-exit.js +560 -0
- package/hooks/dist/lib/exit-code-registry.js +98 -0
- package/hooks/dist/lib/git-cmd.js +92 -59
- package/hooks/dist/lib/git-probe.js +84 -0
- package/hooks/dist/lib/hook-exit.js +81 -0
- package/hooks/dist/lib/injection-patterns.js +45 -0
- package/hooks/dist/lib/isolation-deny-reason.js +39 -0
- package/hooks/dist/lib/isolation-sentinel.js +9 -0
- package/hooks/dist/managed-hooks-registry.cjs +3 -0
- package/hooks/gsd-agent-isolation-guard.js +158 -30
- package/hooks/gsd-check-update-worker.js +56 -13
- package/hooks/gsd-check-update.js +19 -1
- package/hooks/gsd-config-reload.js +18 -12
- package/hooks/gsd-context-monitor.js +19 -10
- package/hooks/gsd-cursor-post-tool.js +3 -1
- package/hooks/gsd-cursor-pre-tool.js +2 -3
- package/hooks/gsd-cursor-session-start.js +2 -1
- package/hooks/gsd-cursor-stop.js +2 -1
- package/hooks/gsd-cursor-subagent-start.js +83 -3
- package/hooks/gsd-cursor-subagent-stop.js +6 -3
- package/hooks/gsd-ensure-canonical-path.js +2 -1
- package/hooks/gsd-graphify-update.sh +22 -18
- package/hooks/gsd-node-runner.sh +76 -0
- package/hooks/gsd-phase-boundary.sh +1 -0
- package/hooks/gsd-prompt-guard.js +37 -27
- package/hooks/gsd-read-guard.js +16 -7
- package/hooks/gsd-read-injection-scanner.js +55 -32
- package/hooks/gsd-session-state.sh +1 -0
- package/hooks/gsd-statusline.js +231 -24
- package/hooks/gsd-update-banner.js +22 -1
- package/hooks/gsd-validate-commit.sh +80 -6
- package/hooks/gsd-windsurf-pre-command.js +16 -11
- package/hooks/gsd-windsurf-pre-write.js +22 -13
- package/hooks/gsd-workflow-guard.js +162 -46
- package/hooks/gsd-worktree-path-guard.js +36 -21
- package/hooks/gsd-write-guard.js +35 -25
- package/hooks/lib/cli-exit.js +560 -0
- package/hooks/lib/exit-code-registry.js +98 -0
- package/hooks/lib/git-cmd.js +92 -59
- package/hooks/lib/git-probe.js +84 -0
- package/hooks/lib/hook-exit.js +81 -0
- package/hooks/lib/injection-patterns.js +45 -0
- package/hooks/lib/isolation-deny-reason.js +39 -0
- package/hooks/lib/isolation-sentinel.js +9 -0
- package/hooks/managed-hooks-registry.cjs +3 -0
- package/package.json +28 -11
- package/pi/gsd.cjs +19 -5
- package/scripts/base64-scan.sh +74 -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 +5 -0
- package/scripts/changeset/lint.cjs +60 -5
- package/scripts/check-alias-drift.cjs +7 -43
- package/scripts/check-contract-drift.cjs +297 -0
- package/scripts/check-glossary-refs.cjs +77 -15
- package/scripts/check-mutation-score-ratchet.cjs +156 -0
- package/scripts/ci-check-job-near-cap.cjs +49 -0
- package/scripts/ci-pr-mergeability.cjs +262 -0
- package/scripts/ci-test-scope.cjs +64 -14
- package/scripts/ci-timeout-report.cjs +230 -0
- package/scripts/command-contract-helpers.cjs +903 -1
- package/scripts/docs-guard-registry.cjs +396 -0
- package/scripts/gen-adr-index.cjs +728 -38
- package/scripts/gen-capability-registry.cjs +11 -21
- package/scripts/gen-context-index.cjs +2 -11
- package/scripts/gen-exit-code-docs.cjs +318 -0
- package/scripts/gen-exit-code-registry.cjs +891 -0
- package/scripts/gen-features.cjs +836 -0
- package/scripts/gen-health-docs.cjs +390 -0
- package/scripts/gen-hooks-cli-exit.cjs +239 -0
- package/scripts/gen-install-tree-fixtures.cjs +2 -2
- package/scripts/gen-inventory-manifest.cjs +50 -4
- package/scripts/gen-loop-host-contract.cjs +138 -25
- package/scripts/gen-registry.cjs +3 -14
- package/scripts/gen-scripts-cli-exit.cjs +185 -0
- package/scripts/gen-state-md-docs.cjs +727 -0
- package/scripts/{test-failure-reasons.cjs → gsd-test-gate-reasons.cjs} +6 -0
- package/scripts/lib/alias-drift-families.cjs +46 -0
- package/scripts/lib/ci-job-timing.cjs +72 -0
- package/scripts/lib/cli-exit.cjs +546 -44
- package/scripts/lib/drift-scan.cjs +308 -0
- package/scripts/lib/exit-code-registry.cjs +98 -0
- package/scripts/lib/ndjson-reporter.cjs +119 -0
- package/scripts/lint-allow-test-rule-refs.allowlist.json +1 -26
- package/scripts/lint-allow-test-rule-refs.effective-ceiling.json +4 -0
- package/scripts/lint-allow-test-rule-refs.unverified-ceiling.json +3 -0
- package/scripts/lint-canary-version-leak.cjs +73 -0
- package/scripts/lint-command-contract.cjs +96 -13
- package/scripts/lint-completion-predicate-drift.cjs +933 -0
- package/scripts/lint-completion-ratio-drift.cjs +214 -0
- package/scripts/lint-default-flip-documentation.cjs +193 -0
- package/scripts/lint-docs-guard-registration.cjs +495 -0
- package/scripts/lint-docs-guard-registration.exempt-baseline.cjs +193 -0
- package/scripts/lint-eslint-glob-coverage.allowlist.json +38 -0
- package/scripts/lint-eslint-glob-coverage.cjs +340 -0
- package/scripts/{lint-fix-has-regression-test.cjs → lint-fix-has-regression-tests.cjs} +12 -6
- package/scripts/lint-frontmatter-scalar-broad-grep.cjs +237 -0
- package/scripts/lint-health-diagnostic-rule-table.cjs +461 -0
- package/scripts/lint-hooks-runtime-build-seam.cjs +262 -0
- package/scripts/lint-milestone-window-drift.cjs +468 -0
- package/scripts/lint-mutation-test-derivation-drift.cjs +86 -0
- package/scripts/lint-phase-enumeration-drift.cjs +492 -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 +471 -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 +488 -0
- package/scripts/lint-seam-enforcement.cjs +182 -0
- package/scripts/lint-slug-derivation-drift.cjs +921 -0
- package/scripts/lint-source-test-name-collision.cjs +241 -0
- package/scripts/lint-state-field-drift.cjs +805 -0
- package/scripts/lint-state-write-path-drift.cjs +950 -0
- package/scripts/lint-test-file-count.allowlist.json +137 -8
- package/scripts/lint-test-file-count.cjs +25 -3
- package/scripts/lint-unreachable-guard-drift.cjs +830 -0
- package/scripts/lint-vendored-deps.cjs +297 -0
- package/scripts/mutation-matrix.cjs +599 -50
- package/scripts/pr-changed-files.cjs +63 -0
- package/scripts/pr-template-policy.cjs +14 -4
- package/scripts/prompt-injection-scan.sh +100 -14
- package/scripts/require-issue-link-policy.cjs +192 -0
- package/scripts/secret-scan.sh +75 -13
- package/scripts/select-docs-guards.cjs +56 -0
- package/scripts/sync-runtime-launcher.cjs +24 -7
- package/skills/gsd-autonomous/SKILL.md +0 -1
- package/skills/gsd-code-review/SKILL.md +1 -1
- package/skills/gsd-discuss-phase/SKILL.md +1 -1
- package/skills/gsd-execute-phase/SKILL.md +1 -2
- package/skills/gsd-import/SKILL.md +1 -1
- package/skills/gsd-map-codebase/SKILL.md +1 -1
- package/skills/gsd-mempalace-capture/SKILL.md +1 -1
- package/skills/gsd-mempalace-recall/SKILL.md +1 -1
- package/skills/gsd-new-milestone/SKILL.md +1 -1
- package/skills/gsd-next/SKILL.md +0 -1
- package/skills/gsd-plan-phase/SKILL.md +0 -1
- package/skills/gsd-progress/SKILL.md +0 -1
- package/skills/gsd-quick/SKILL.md +9 -5
- 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/bin/lib/ui-safety-gate.cjs +0 -107
- package/gsd-core/workflows/discovery-phase.md +0 -298
- package/gsd-core/workflows/plan-milestone-gaps.md +0 -281
- package/gsd-core/workflows/verify-phase.md +0 -574
- package/scripts/affected-tests-lib.cjs +0 -554
- package/scripts/lint-allow-test-rule-refs.cjs +0 -162
- package/scripts/lint-emitted-drift-ack.cjs +0 -344
- package/scripts/run-affected-tests.cjs +0 -7
- package/scripts/run-tests.cjs +0 -1051
|
@@ -65,7 +65,7 @@ const TRACKED_PREFIXES = [
|
|
|
65
65
|
const TRACKED_EXACT = new Set(['bin/install.js', 'package.json']);
|
|
66
66
|
|
|
67
67
|
/**
|
|
68
|
-
* Paths CONTEXT.md documents whose ABSENCE is the healthy steady state
|
|
68
|
+
* Paths CONTEXT.md documents whose ABSENCE is the healthy steady state.
|
|
69
69
|
*
|
|
70
70
|
* `TRACKED_PREFIXES` skips claims this gate *cannot* check. This is the narrower
|
|
71
71
|
* third case: a claim it must not check, because "does not exist" is the correct
|
|
@@ -78,14 +78,43 @@ const TRACKED_EXACT = new Set(['bin/install.js', 'package.json']);
|
|
|
78
78
|
* shipping an empty stub. So the file is absent on a healthy `next` and present
|
|
79
79
|
* only inside a PR that needs it, and asserting either way is wrong.
|
|
80
80
|
*
|
|
81
|
-
*
|
|
82
|
-
*
|
|
83
|
-
* `
|
|
84
|
-
*
|
|
85
|
-
* by
|
|
86
|
-
*
|
|
81
|
+
* The emitted-attribution family (`tests/fixtures/golden-install-parity`,
|
|
82
|
+
* `tests/golden-install-parity.test.cjs`, `scripts/gen-golden-install-parity-zcode.cjs`,
|
|
83
|
+
* `tests/agent-size-baseline.json`, `tests/workflow-size-baseline.json`,
|
|
84
|
+
* `scripts/git-merge-regen-driver.cjs`, `scripts/update-size-baseline.cjs`) was RETIRED
|
|
85
|
+
* by the #2724 cutover — the differential attribution check replaced the committed
|
|
86
|
+
* baselines and their generator/bridge tooling. CONTEXT.md's RULESET.EMITTED_ATTRIBUTION
|
|
87
|
+
* predicate documents that retirement ("Historically …"), so the mentions are history,
|
|
88
|
+
* not live claims, and their absence is exactly the healthy state the retirement
|
|
89
|
+
* produced. The whole documented family is listed, not just the members today's tick
|
|
90
|
+
* parity happens to hide — the two tooling paths were invisible only because of where
|
|
91
|
+
* line 585's inner backticks sat, which is the #2778 luck this gate must not rely on.
|
|
92
|
+
*
|
|
93
|
+
* `scripts/eslint-rules` appears only inside a CONTRASTIVE mention ("the local
|
|
94
|
+
* plugin lives at `eslint-rules/` (repo root, NOT `scripts/eslint-rules/`)") — the
|
|
95
|
+
* predicate asserts where the directory is NOT, so non-existence is the claim
|
|
96
|
+
* being made, not drift away from one.
|
|
97
|
+
*
|
|
98
|
+
* Until #2778 this set's first entry passed only by accident: CONTEXT.md's
|
|
99
|
+
* `RULESET.` entries are themselves backtick-wrapped and contain backticks, so the
|
|
100
|
+
* sequential pairing in `extractTrackedRefs` happened to leave this token outside a
|
|
101
|
+
* code span. Any edit that shifted the parity — such as #2778's own — exposed it.
|
|
102
|
+
* A gate that passes by luck is not passing; naming the exemption makes the intent
|
|
103
|
+
* explicit and survives the next edit. #3604 removed the luck itself (pairing is
|
|
104
|
+
* per line), which is what surfaced the entries above: each names a path whose
|
|
105
|
+
* absence is deliberate, so each is exempted by name for the same reason.
|
|
87
106
|
*/
|
|
88
|
-
const INTENTIONALLY_ABSENT = new Set([
|
|
107
|
+
const INTENTIONALLY_ABSENT = new Set([
|
|
108
|
+
'tests/emitted-drift-ack.json',
|
|
109
|
+
'tests/fixtures/golden-install-parity',
|
|
110
|
+
'tests/golden-install-parity.test.cjs',
|
|
111
|
+
'scripts/gen-golden-install-parity-zcode.cjs',
|
|
112
|
+
'tests/agent-size-baseline.json',
|
|
113
|
+
'tests/workflow-size-baseline.json',
|
|
114
|
+
'scripts/git-merge-regen-driver.cjs',
|
|
115
|
+
'scripts/update-size-baseline.cjs',
|
|
116
|
+
'scripts/eslint-rules',
|
|
117
|
+
]);
|
|
89
118
|
|
|
90
119
|
/**
|
|
91
120
|
* Shape a backticked token must have to even be considered a path candidate:
|
|
@@ -119,18 +148,51 @@ function isWithinRoot(token) {
|
|
|
119
148
|
/**
|
|
120
149
|
* Every distinct, trackable file-path token referenced in `text`, with any
|
|
121
150
|
* trailing `:<line>` suffix stripped.
|
|
151
|
+
*
|
|
152
|
+
* #3604: pairing is per LINE, not over the whole text. A single whole-text pass
|
|
153
|
+
* `[^`]+` crosses newlines, so one odd-backtick line (the `RULESET.*` predicate
|
|
154
|
+
* format is backtick-wrapped and its values sometimes contain backticks) shifted
|
|
155
|
+
* the pairing of every later line — a tracked token's visibility depended on
|
|
156
|
+
* where it sat, which is how a renamed test file stayed invisible for weeks.
|
|
157
|
+
*
|
|
158
|
+
* The fact-store predicate lines (one `CLASS.subkey=value` fact per line,
|
|
159
|
+
* backtick-wrapped as a whole) carry REAL paths in their values; the span itself
|
|
160
|
+
* is not path-shaped (spaces, `=`), so a second pass harvests path-shaped tracked
|
|
161
|
+
* tokens from inside any predicate-shaped span rather than letting the outer
|
|
162
|
+
* wrapper hide them.
|
|
163
|
+
*
|
|
164
|
+
* Fragment guards: a real path in this repo never ends in `-` or `.` — those are
|
|
165
|
+
* remnants of glob/template mentions (`scripts/gen-*.cjs`, `tests/foo.*.test.cjs`)
|
|
166
|
+
* split at the `*` — and `NNNN` is the ADR filename template token
|
|
167
|
+
* (CONTRIBUTING's "Do not compute a next number locally"), never a real path.
|
|
122
168
|
*/
|
|
123
169
|
function extractTrackedRefs(text) {
|
|
124
170
|
const tokens = new Set();
|
|
125
|
-
const
|
|
126
|
-
|
|
127
|
-
while ((m = re.exec(text)) !== null) {
|
|
128
|
-
const raw = m[1];
|
|
129
|
-
if (!PATH_TOKEN_RE.test(raw)) continue;
|
|
171
|
+
const add = (raw) => {
|
|
172
|
+
if (!PATH_TOKEN_RE.test(raw)) return;
|
|
130
173
|
const token = raw.replace(/:\d+$/, '');
|
|
131
|
-
|
|
132
|
-
|
|
174
|
+
// Fragment guard, on the EMITTED token (after the :line strip, so
|
|
175
|
+
// `src/foo-:12` is judged on `src/foo-`): glob/template remnants end in `-`
|
|
176
|
+
// or `.`, a real path in this repo never does.
|
|
177
|
+
if (!/[A-Za-z0-9_]$/.test(token)) return;
|
|
178
|
+
if (token.includes('NNNN')) return;
|
|
179
|
+
if (!isTracked(token)) return;
|
|
180
|
+
if (!isWithinRoot(token)) return;
|
|
133
181
|
tokens.add(token);
|
|
182
|
+
};
|
|
183
|
+
const subTokenRe = /[\w.-]+(?:\/[\w.-]+)*/g;
|
|
184
|
+
for (const line of text.split(/\r?\n/)) {
|
|
185
|
+
const re = /`([^`]+)`/g;
|
|
186
|
+
let m;
|
|
187
|
+
while ((m = re.exec(line)) !== null) {
|
|
188
|
+
const span = m[1];
|
|
189
|
+
if (PATH_TOKEN_RE.test(span)) {
|
|
190
|
+
add(span);
|
|
191
|
+
continue;
|
|
192
|
+
}
|
|
193
|
+
if (!/^[A-Z][A-Za-z0-9_.-]*=/.test(span)) continue;
|
|
194
|
+
for (const sub of span.matchAll(subTokenRe)) add(sub[0]);
|
|
195
|
+
}
|
|
134
196
|
}
|
|
135
197
|
return tokens;
|
|
136
198
|
}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* scripts/check-mutation-score-ratchet.cjs
|
|
6
|
+
*
|
|
7
|
+
* #3881 follow-up ("one YAML parser" mutation-matrix piece 3): makes each
|
|
8
|
+
* module's `minScore` floor (scripts/mutation-matrix.cjs COVERED) ratchet
|
|
9
|
+
* UPWARD instead of sitting wherever it was last hand-set.
|
|
10
|
+
*
|
|
11
|
+
* Two halves of "never lowerable without a reasoned marker":
|
|
12
|
+
* 1. `tests/mutation-matrix-ratchet.test.cjs`'s RATCHET_BASELINE already
|
|
13
|
+
* enforces the DOWN side: any change to a module's minScore — up or
|
|
14
|
+
* down — must land in the same diff as a matching RATCHET_BASELINE
|
|
15
|
+
* edit, making the change visible in code review (mirrors this repo's
|
|
16
|
+
* other shrink-only baselines, e.g. scripts/lint-unreachable-guard-drift.cjs's
|
|
17
|
+
* $comment convention: a number cannot move silently).
|
|
18
|
+
* 2. THIS script enforces the UP side: after a real CI Stryker run, if the
|
|
19
|
+
* module's ACHIEVED mutation score exceeds its declared floor by more
|
|
20
|
+
* than RATCHET_SLACK points, that is not "run-to-run variance" (this
|
|
21
|
+
* file's own header documents variance as 1-2 points; see
|
|
22
|
+
* scripts/mutation-matrix.cjs's "Floors are measured scores minus 1-2
|
|
23
|
+
* pts") — it is unclaimed headroom, and the floor should have been
|
|
24
|
+
* raised. Exits 1 telling the author exactly what to raise it to, so a
|
|
25
|
+
* module that improves cannot silently keep a stale, low bar forever.
|
|
26
|
+
*
|
|
27
|
+
* Consumes Stryker's `json` reporter output (mutation-testing-report-schema
|
|
28
|
+
* document; stryker.config.mjs's `reporters` includes `'json'`, default path
|
|
29
|
+
* `reports/mutation/mutation.json`) via `mutation-testing-metrics` — the same
|
|
30
|
+
* package Stryker itself uses internally to compute a report's score — rather
|
|
31
|
+
* than re-deriving killed/survived counts by hand.
|
|
32
|
+
*
|
|
33
|
+
* Wired into `.github/workflows/mutation.yml`'s `mutate` job as a step AFTER
|
|
34
|
+
* `npx stryker run`, gated on the shard having passed (a shard that failed
|
|
35
|
+
* MUTATION_BREAK is a floor problem, not a ratchet problem — Stryker's own
|
|
36
|
+
* exit code already reports that).
|
|
37
|
+
*
|
|
38
|
+
* CLI:
|
|
39
|
+
* node scripts/check-mutation-score-ratchet.cjs --module <name> [--report <path>] [--matrix <path>]
|
|
40
|
+
* `--report` defaults to reports/mutation/mutation.json (Stryker's own default).
|
|
41
|
+
* `--matrix` defaults to this repo's own scripts/mutation-matrix.cjs; it is an injectable seam
|
|
42
|
+
* so tests can point the CLI at a synthetic COVERED fixture instead of the real, live-ratcheting
|
|
43
|
+
* config (see tests/mutation-score-ratchet.test.cjs — a test asserting this script's fail/pass
|
|
44
|
+
* behaviour must never hardcode a real module's numeric floor, because that floor is exactly
|
|
45
|
+
* what this mechanism exists to move).
|
|
46
|
+
*/
|
|
47
|
+
|
|
48
|
+
const fs = require('node:fs');
|
|
49
|
+
const path = require('node:path');
|
|
50
|
+
const { ExitError, runMain } = require('./lib/cli-exit.cjs');
|
|
51
|
+
|
|
52
|
+
// Slack, in mutation-score points, above a module's declared minScore floor before this
|
|
53
|
+
// ratchet demands the floor be raised. Deliberately larger than the 1-2 point run-to-run
|
|
54
|
+
// TIMEOUT variance margin scripts/mutation-matrix.cjs's floors are already set with (its own
|
|
55
|
+
// header: "Floors are measured scores minus 1-2 pts for run-to-run variance") — that smaller
|
|
56
|
+
// margin is calibrated for the noise BETWEEN two runs of the SAME test list against the SAME
|
|
57
|
+
// source. RATCHET_SLACK instead has to tolerate the noise this file's own history already
|
|
58
|
+
// shows for the frontmatter module across genuine measurement events (63.35 -> 66.67 ->
|
|
59
|
+
// 60.58 -> pending, each a real CI measurement, not pure jitter, spanning >3 points on its
|
|
60
|
+
// own). 5 points sits comfortably above that observed band without being so wide that a
|
|
61
|
+
// module sitting well above TARGET_MUTATION_SCORE (80) could coast for years without its
|
|
62
|
+
// floor ever being asked to move.
|
|
63
|
+
const RATCHET_SLACK = 5;
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Pure: does `achievedScore` (0-100) exceed `floor` (minScore) by more than `slack`?
|
|
67
|
+
* Returns `{ shouldRatchet, suggestedFloor }` — `suggestedFloor` follows the SAME formula
|
|
68
|
+
* scripts/mutation-matrix.cjs's own header documents for setting a floor from a measured
|
|
69
|
+
* score: `floor(measured) - 1`.
|
|
70
|
+
*/
|
|
71
|
+
function evaluateRatchet(achievedScore, floor, slack = RATCHET_SLACK) {
|
|
72
|
+
const shouldRatchet = achievedScore - floor > slack;
|
|
73
|
+
return {
|
|
74
|
+
shouldRatchet,
|
|
75
|
+
suggestedFloor: shouldRatchet ? Math.floor(achievedScore) - 1 : floor,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Extract the achieved mutation score from a Stryker `json` reporter document via
|
|
81
|
+
* `mutation-testing-metrics` (the same library Stryker itself uses to compute scores).
|
|
82
|
+
* Throws a descriptive error if the document has no scoreable mutants at all (an empty
|
|
83
|
+
* `files` map, or every file having zero mutants) — that is a wiring bug in the caller
|
|
84
|
+
* (wrong report path, or the shard mutated nothing), never a score of 0.
|
|
85
|
+
*/
|
|
86
|
+
function extractAchievedScore(report) {
|
|
87
|
+
// Lazy require: this dependency is only needed on the actual score-extraction path, not
|
|
88
|
+
// when this module is required purely for evaluateRatchet (used by unit tests without the
|
|
89
|
+
// mutation-testing-metrics package's schema-validation overhead).
|
|
90
|
+
const { calculateMutationTestMetrics } = require('mutation-testing-metrics');
|
|
91
|
+
const metrics = calculateMutationTestMetrics(report);
|
|
92
|
+
const score = metrics && metrics.systemUnderTestMetrics && metrics.systemUnderTestMetrics.metrics
|
|
93
|
+
? metrics.systemUnderTestMetrics.metrics.mutationScore
|
|
94
|
+
: undefined;
|
|
95
|
+
if (typeof score !== 'number' || Number.isNaN(score)) {
|
|
96
|
+
throw new Error('mutation report produced no scoreable mutants — check --report points at the right shard\'s reports/mutation/mutation.json');
|
|
97
|
+
}
|
|
98
|
+
return score;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function parseArgs(argv) {
|
|
102
|
+
const out = { module: null, report: 'reports/mutation/mutation.json', matrix: null };
|
|
103
|
+
for (let i = 0; i < argv.length; i++) {
|
|
104
|
+
const arg = argv[i];
|
|
105
|
+
if (arg === '--module') {
|
|
106
|
+
out.module = argv[++i];
|
|
107
|
+
} else if (arg === '--report') {
|
|
108
|
+
out.report = argv[++i];
|
|
109
|
+
} else if (arg === '--matrix') {
|
|
110
|
+
out.matrix = argv[++i];
|
|
111
|
+
} else {
|
|
112
|
+
throw new Error(`unknown argument: ${arg}`);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
if (!out.module) throw new Error('--module <name> is required');
|
|
116
|
+
return out;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function main() {
|
|
120
|
+
const args = parseArgs(process.argv.slice(2));
|
|
121
|
+
const matrixPath = args.matrix ? path.resolve(args.matrix) : path.join(__dirname, 'mutation-matrix.cjs');
|
|
122
|
+
const { COVERED } = require(matrixPath);
|
|
123
|
+
const entry = COVERED[args.module];
|
|
124
|
+
if (!entry) {
|
|
125
|
+
throw new ExitError(1, `check-mutation-score-ratchet: unknown module '${args.module}' — not in ${args.matrix ? matrixPath : 'scripts/mutation-matrix.cjs'} COVERED`);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const reportPath = path.resolve(args.report);
|
|
129
|
+
if (!fs.existsSync(reportPath)) {
|
|
130
|
+
throw new ExitError(1, `check-mutation-score-ratchet: report not found at ${reportPath} — run \`npx stryker run\` first (with the 'json' reporter enabled)`);
|
|
131
|
+
}
|
|
132
|
+
let report;
|
|
133
|
+
try {
|
|
134
|
+
report = JSON.parse(fs.readFileSync(reportPath, 'utf8'));
|
|
135
|
+
} catch (err) {
|
|
136
|
+
throw new ExitError(1, `check-mutation-score-ratchet: ${reportPath} is not valid JSON: ${err.message}`);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const achieved = extractAchievedScore(report);
|
|
140
|
+
const { shouldRatchet, suggestedFloor } = evaluateRatchet(achieved, entry.minScore);
|
|
141
|
+
|
|
142
|
+
if (!shouldRatchet) {
|
|
143
|
+
console.log(`ok mutation-score-ratchet: ${args.module} achieved ${achieved.toFixed(2)}% against floor ${entry.minScore} (within ${RATCHET_SLACK}-point slack)`);
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
console.error(`mutation-score-ratchet: ${args.module} achieved ${achieved.toFixed(2)}%, more than ${RATCHET_SLACK} points above its declared floor (${entry.minScore}).`);
|
|
148
|
+
console.error(` This is unclaimed headroom, not run-to-run variance — raise the floor.`);
|
|
149
|
+
console.error(` remedy: in scripts/mutation-matrix.cjs, set COVERED['${args.module}'].minScore = ${suggestedFloor} (floor(achieved) - 1, this file's own convention),`);
|
|
150
|
+
console.error(` and update the matching entry in tests/mutation-matrix-ratchet.test.cjs's RATCHET_BASELINE in the same diff.`);
|
|
151
|
+
throw new ExitError(1);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
module.exports = { RATCHET_SLACK, evaluateRatchet, extractAchievedScore };
|
|
155
|
+
|
|
156
|
+
if (require.main === module) runMain(main);
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const fs = require('node:fs');
|
|
5
|
+
const { computeElapsedPct, isNearCap, formatNearCapNotice } = require('./lib/ci-job-timing.cjs');
|
|
6
|
+
|
|
7
|
+
function run(env = process.env, nowMs = Date.now(), appendFileSync = fs.appendFileSync) {
|
|
8
|
+
const label = env.CI_JOB_LABEL;
|
|
9
|
+
const startEpochMs = Number(env.CI_JOB_START_EPOCH_MS);
|
|
10
|
+
const timeoutMinutes = Number(env.CI_JOB_TIMEOUT_MINUTES);
|
|
11
|
+
|
|
12
|
+
if (!label || !Number.isFinite(startEpochMs) || !Number.isFinite(timeoutMinutes) || timeoutMinutes <= 0) {
|
|
13
|
+
console.error(
|
|
14
|
+
'ci-check-job-near-cap: missing or invalid CI_JOB_LABEL / CI_JOB_START_EPOCH_MS / '
|
|
15
|
+
+ 'CI_JOB_TIMEOUT_MINUTES — skipping near-cap check (advisory only, not a failure).',
|
|
16
|
+
);
|
|
17
|
+
return { skipped: true };
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const { elapsedMs, capMs, pct } = computeElapsedPct({
|
|
21
|
+
startedAt: new Date(startEpochMs).toISOString(),
|
|
22
|
+
completedAt: new Date(nowMs).toISOString(),
|
|
23
|
+
timeoutMinutes,
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
if (!isNearCap(pct)) {
|
|
27
|
+
return { skipped: false, nearCap: false, pct };
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const { warningLine, summaryMarkdown } = formatNearCapNotice({ label, pct, elapsedMs, capMs });
|
|
31
|
+
console.log(warningLine);
|
|
32
|
+
|
|
33
|
+
if (env.GITHUB_STEP_SUMMARY) {
|
|
34
|
+
try {
|
|
35
|
+
appendFileSync(env.GITHUB_STEP_SUMMARY, `${summaryMarkdown}\n`);
|
|
36
|
+
} catch (err) {
|
|
37
|
+
console.error(`ci-check-job-near-cap: could not write GITHUB_STEP_SUMMARY: ${err.message}`);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return { skipped: false, nearCap: true, pct };
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
module.exports = { run };
|
|
45
|
+
|
|
46
|
+
if (require.main === module) {
|
|
47
|
+
run();
|
|
48
|
+
process.exitCode = 0;
|
|
49
|
+
}
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
// ci-pr-mergeability.cjs — #3833 PR mergeability preflight.
|
|
5
|
+
//
|
|
6
|
+
// Risk asymmetry drives this design: a false positive (a clean PR wrongly
|
|
7
|
+
// classified CONFLICTED) skips every gated compute lane in all eight gated
|
|
8
|
+
// workflows on EVERY PR — a repo-wide outage. A false negative (a real
|
|
9
|
+
// conflict missed) is today's behavior; the per-job scripts/ci-rebase-check.cjs
|
|
10
|
+
// backstop still catches it there. So the weight is entirely on the
|
|
11
|
+
// false-positive surface: the single predicate `mergeable === false`, and
|
|
12
|
+
// every falsy-but-not-false value around it.
|
|
13
|
+
//
|
|
14
|
+
// GitHub's `mergeable` field on GET /repos/:owner/:repo/pulls/:number is a
|
|
15
|
+
// documented tri-state: `true`, `false`, or `null` while a background
|
|
16
|
+
// computation is still running. `null` is falsy — `if (!mergeable)` would
|
|
17
|
+
// misclassify "still computing" as "conflicted" and would have red every PR
|
|
18
|
+
// in the repo the moment this job started running. Only strict `=== false`
|
|
19
|
+
// counts as a conflict.
|
|
20
|
+
//
|
|
21
|
+
// `mergeable_state` is read only for the human-facing annotation, never for
|
|
22
|
+
// the verdict: its `blocked` value means "required checks have not passed",
|
|
23
|
+
// which is true for every PR while THIS preflight job itself is one of the
|
|
24
|
+
// required checks still running — classifying on it would self-deadlock the
|
|
25
|
+
// pipeline (the gate can never go green because it is itself ungreen).
|
|
26
|
+
//
|
|
27
|
+
// Because `mergeable` starts null and GitHub computes it asynchronously, this
|
|
28
|
+
// polls with linear backoff up to a small budget, then fails OPEN
|
|
29
|
+
// (INDETERMINATE => exit 0) rather than blocking the world on an API that
|
|
30
|
+
// hasn't finished thinking.
|
|
31
|
+
|
|
32
|
+
const { ExitError, runMain } = require('./lib/cli-exit.cjs');
|
|
33
|
+
|
|
34
|
+
const VERDICT = Object.freeze({
|
|
35
|
+
MERGEABLE: 'MERGEABLE',
|
|
36
|
+
CONFLICTED: 'CONFLICTED',
|
|
37
|
+
INDETERMINATE: 'INDETERMINATE',
|
|
38
|
+
SKIPPED_NOT_A_PR: 'SKIPPED_NOT_A_PR',
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
const MAX_ATTEMPTS = 5;
|
|
42
|
+
const BASE_DELAY_MS = 2000;
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Pure, total classifier: payload -> VERDICT atom. Never throws.
|
|
46
|
+
* Deliberately reads ONLY `mergeable`, and only with strict equality — see
|
|
47
|
+
* the header comment for why `mergeable_state` and truthiness are excluded.
|
|
48
|
+
*/
|
|
49
|
+
function classifyMergeability(payload) {
|
|
50
|
+
if (payload === null || typeof payload !== 'object') return VERDICT.INDETERMINATE;
|
|
51
|
+
if (payload.mergeable === true) return VERDICT.MERGEABLE;
|
|
52
|
+
if (payload.mergeable === false) return VERDICT.CONFLICTED;
|
|
53
|
+
return VERDICT.INDETERMINATE;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Linear backoff: attempt 1..N-1 gives 1x..(N-1)x baseMs. Non-positive/NaN
|
|
58
|
+
* attempts clamp to one base interval rather than producing 0 or NaN delays.
|
|
59
|
+
*/
|
|
60
|
+
function nextDelayMs(attempt, { baseMs = BASE_DELAY_MS } = {}) {
|
|
61
|
+
const n = Number.isFinite(attempt) && attempt > 0 ? Math.floor(attempt) : 1;
|
|
62
|
+
return baseMs * n;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Dependency-injected poll loop. Resolves as soon as GitHub reports a
|
|
67
|
+
* definite mergeable/conflicted state, or gives up after maxAttempts and
|
|
68
|
+
* reports INDETERMINATE (fail-open).
|
|
69
|
+
*
|
|
70
|
+
* @returns {Promise<{verdict:string, attempts:number, payload:*, reason:string}>}
|
|
71
|
+
*/
|
|
72
|
+
async function resolveMergeability({
|
|
73
|
+
fetchPr,
|
|
74
|
+
sleep,
|
|
75
|
+
eventName,
|
|
76
|
+
prNumber,
|
|
77
|
+
maxAttempts = MAX_ATTEMPTS,
|
|
78
|
+
baseDelayMs = BASE_DELAY_MS,
|
|
79
|
+
} = {}) {
|
|
80
|
+
if (eventName !== 'pull_request') {
|
|
81
|
+
return { verdict: VERDICT.SKIPPED_NOT_A_PR, attempts: 0, payload: null, reason: 'not-a-pull-request' };
|
|
82
|
+
}
|
|
83
|
+
if (!Number.isInteger(prNumber) || prNumber <= 0) {
|
|
84
|
+
return { verdict: VERDICT.INDETERMINATE, attempts: 0, payload: null, reason: 'no-pr-number' };
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
let attempts = 0;
|
|
88
|
+
let lastPayload = null;
|
|
89
|
+
let lastError = null;
|
|
90
|
+
|
|
91
|
+
for (let i = 1; i <= maxAttempts; i++) {
|
|
92
|
+
attempts = i;
|
|
93
|
+
let payload;
|
|
94
|
+
try {
|
|
95
|
+
payload = await fetchPr(prNumber);
|
|
96
|
+
lastPayload = payload;
|
|
97
|
+
lastError = null;
|
|
98
|
+
} catch (err) {
|
|
99
|
+
lastError = err;
|
|
100
|
+
payload = null;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const verdict = classifyMergeability(payload);
|
|
104
|
+
if (verdict === VERDICT.MERGEABLE || verdict === VERDICT.CONFLICTED) {
|
|
105
|
+
return { verdict, attempts, payload, reason: 'resolved' };
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (i < maxAttempts) {
|
|
109
|
+
await sleep(nextDelayMs(i, { baseMs: baseDelayMs }));
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return {
|
|
114
|
+
verdict: VERDICT.INDETERMINATE,
|
|
115
|
+
attempts,
|
|
116
|
+
payload: lastPayload,
|
|
117
|
+
reason: lastError ? 'fetch-failed' : 'mergeability-not-computed',
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function usage() {
|
|
122
|
+
return [
|
|
123
|
+
'Usage:',
|
|
124
|
+
' node scripts/ci-pr-mergeability.cjs',
|
|
125
|
+
'',
|
|
126
|
+
'CI-only preflight: polls the GitHub API for a PR\'s mergeable state and',
|
|
127
|
+
'fails the job (exit 1) only on a definite conflict. Everything else',
|
|
128
|
+
'(unknown, unreadable, not a PR) fails open (exit 0).',
|
|
129
|
+
'',
|
|
130
|
+
'Environment variables read:',
|
|
131
|
+
' GITHUB_EVENT_NAME workflow trigger event; only "pull_request" is polled',
|
|
132
|
+
' GITHUB_REPOSITORY owner/repo',
|
|
133
|
+
' GITHUB_TOKEN optional bearer token for the API read',
|
|
134
|
+
' GITHUB_BASE_REF PR base branch name (default: next)',
|
|
135
|
+
' GITHUB_API_URL GitHub API base URL (default: https://api.github.com)',
|
|
136
|
+
' PR_NUMBER the pull request number',
|
|
137
|
+
' GITHUB_OUTPUT path to append verdict=/mergeable= step outputs to',
|
|
138
|
+
' GITHUB_STEP_SUMMARY path to append a human-readable summary to',
|
|
139
|
+
' GSD_PR_MERGEABILITY_BASE_DELAY_MS optional numeric override of the base backoff, for tests',
|
|
140
|
+
].join('\n');
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function parseArgs(argv) {
|
|
144
|
+
for (const arg of argv) {
|
|
145
|
+
if (arg === '--help' || arg === '-h') {
|
|
146
|
+
process.stdout.write(`${usage()}\n`);
|
|
147
|
+
throw new ExitError(0);
|
|
148
|
+
}
|
|
149
|
+
throw new Error(`unknown argument: ${arg}`);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function writeOutput(lines) {
|
|
154
|
+
const outputPath = process.env.GITHUB_OUTPUT;
|
|
155
|
+
if (typeof outputPath !== 'string' || outputPath === '') return;
|
|
156
|
+
try {
|
|
157
|
+
const fs = require('node:fs');
|
|
158
|
+
fs.appendFileSync(outputPath, `${lines.join('\n')}\n`);
|
|
159
|
+
} catch (err) {
|
|
160
|
+
// A failure while REPORTING the verdict must never invert the gate.
|
|
161
|
+
process.stderr.write(`::warning::failed to write GITHUB_OUTPUT: ${err.message}\n`);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function writeSummary(text) {
|
|
166
|
+
const summaryPath = process.env.GITHUB_STEP_SUMMARY;
|
|
167
|
+
if (typeof summaryPath !== 'string' || summaryPath === '') return;
|
|
168
|
+
try {
|
|
169
|
+
const fs = require('node:fs');
|
|
170
|
+
fs.appendFileSync(summaryPath, `${text}\n`);
|
|
171
|
+
} catch (err) {
|
|
172
|
+
process.stderr.write(`::warning::failed to write GITHUB_STEP_SUMMARY: ${err.message}\n`);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// `argv` defaults to real CLI argv but is a parameter so tests can call
|
|
177
|
+
// main() in-process (e.g. main([])) without inheriting the test runner's own
|
|
178
|
+
// argv, which would otherwise trip parseArgs's "unknown argument" branch.
|
|
179
|
+
async function main(argv = process.argv.slice(2)) {
|
|
180
|
+
parseArgs(argv);
|
|
181
|
+
|
|
182
|
+
const eventName = process.env.GITHUB_EVENT_NAME;
|
|
183
|
+
const repo = process.env.GITHUB_REPOSITORY || '';
|
|
184
|
+
const token = process.env.GITHUB_TOKEN || '';
|
|
185
|
+
const baseBranch = process.env.GITHUB_BASE_REF || 'next';
|
|
186
|
+
const apiBase = process.env.GITHUB_API_URL || 'https://api.github.com';
|
|
187
|
+
const prNumberRaw = process.env.PR_NUMBER;
|
|
188
|
+
const prNumber = Number.parseInt(prNumberRaw, 10);
|
|
189
|
+
|
|
190
|
+
const overrideDelay = Number(process.env.GSD_PR_MERGEABILITY_BASE_DELAY_MS);
|
|
191
|
+
const baseDelayMs = Number.isFinite(overrideDelay) && overrideDelay >= 0 ? overrideDelay : BASE_DELAY_MS;
|
|
192
|
+
|
|
193
|
+
const fetchPr = async (number) => {
|
|
194
|
+
const url = `${apiBase}/repos/${repo}/pulls/${number}`;
|
|
195
|
+
const headers = {
|
|
196
|
+
accept: 'application/vnd.github+json',
|
|
197
|
+
'x-github-api-version': '2022-11-28',
|
|
198
|
+
'user-agent': 'gsd-core-ci-pr-mergeability',
|
|
199
|
+
};
|
|
200
|
+
if (token) headers.authorization = `Bearer ${token}`;
|
|
201
|
+
const response = await fetch(url, { headers, signal: AbortSignal.timeout(10000) });
|
|
202
|
+
if (!response.ok) {
|
|
203
|
+
throw new Error(`GitHub API responded ${response.status}`);
|
|
204
|
+
}
|
|
205
|
+
const text = await response.text();
|
|
206
|
+
try {
|
|
207
|
+
return JSON.parse(text);
|
|
208
|
+
} catch {
|
|
209
|
+
return null;
|
|
210
|
+
}
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
214
|
+
|
|
215
|
+
const result = await resolveMergeability({
|
|
216
|
+
fetchPr,
|
|
217
|
+
sleep,
|
|
218
|
+
eventName,
|
|
219
|
+
prNumber: Number.isNaN(prNumber) ? NaN : prNumber,
|
|
220
|
+
maxAttempts: MAX_ATTEMPTS,
|
|
221
|
+
baseDelayMs,
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
const mergeable = result.verdict === VERDICT.CONFLICTED ? 'false' : 'true';
|
|
225
|
+
writeOutput([`verdict=${result.verdict}`, `mergeable=${mergeable}`]);
|
|
226
|
+
writeSummary(`PR mergeability preflight: ${result.verdict}`);
|
|
227
|
+
|
|
228
|
+
if (result.verdict === VERDICT.CONFLICTED) {
|
|
229
|
+
process.stderr.write(
|
|
230
|
+
`::error::This pull request cannot cleanly merge ${baseBranch}. Rebase onto current ${baseBranch} and push again; no CI will run until the conflict is resolved.\n`,
|
|
231
|
+
);
|
|
232
|
+
const state = result.payload && typeof result.payload === 'object' ? result.payload.mergeable_state : undefined;
|
|
233
|
+
if (state) {
|
|
234
|
+
process.stderr.write(`::error::mergeable_state: ${state}\n`);
|
|
235
|
+
}
|
|
236
|
+
return 1;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
if (result.verdict === VERDICT.INDETERMINATE) {
|
|
240
|
+
process.stderr.write(
|
|
241
|
+
'::warning::GitHub did not report mergeability within the polling budget; proceeding (fail-open). scripts/ci-rebase-check.cjs remains the in-job backstop.\n',
|
|
242
|
+
);
|
|
243
|
+
return 0;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
process.stdout.write(`PR mergeability preflight: ${result.verdict}\n`);
|
|
247
|
+
return 0;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
if (require.main === module) {
|
|
251
|
+
runMain(main);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
module.exports = {
|
|
255
|
+
VERDICT,
|
|
256
|
+
MAX_ATTEMPTS,
|
|
257
|
+
BASE_DELAY_MS,
|
|
258
|
+
classifyMergeability,
|
|
259
|
+
nextDelayMs,
|
|
260
|
+
resolveMergeability,
|
|
261
|
+
main,
|
|
262
|
+
};
|