session-orchestrator 5.1.0 → 5.3.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/.agents/skills/architecture/SKILL.md +3 -1
- package/.agents/skills/autopilot/SKILL.md +6 -1
- package/.agents/skills/autopilot/agents/openai.yaml +5 -0
- package/.agents/skills/bootstrap/SKILL.md +7 -1
- package/.agents/skills/bootstrap/agents/openai.yaml +5 -0
- package/.agents/skills/brainstorm/SKILL.md +8 -1
- package/.agents/skills/brainstorm/agents/openai.yaml +5 -0
- package/.agents/skills/claude-md-drift-check/SKILL.md +3 -1
- package/.agents/skills/close/SKILL.md +21 -0
- package/.agents/skills/close/agents/openai.yaml +5 -0
- package/.agents/skills/convergence-monitoring/SKILL.md +4 -2
- package/.agents/skills/debug/SKILL.md +7 -1
- package/.agents/skills/debug/agents/openai.yaml +5 -0
- package/.agents/skills/discovery/SKILL.md +7 -2
- package/.agents/skills/discovery/agents/openai.yaml +5 -0
- package/.agents/skills/dispatcher/SKILL.md +7 -1
- package/.agents/skills/dispatcher/agents/openai.yaml +5 -0
- package/.agents/skills/docs-orchestrator/SKILL.md +3 -1
- package/.agents/skills/ecosystem-health/SKILL.md +3 -1
- package/.agents/skills/eli5/SKILL.md +7 -1
- package/.agents/skills/eli5/agents/openai.yaml +5 -0
- package/.agents/skills/eval/SKILL.md +7 -2
- package/.agents/skills/eval/agents/openai.yaml +5 -0
- package/.agents/skills/evolve/SKILL.md +8 -3
- package/.agents/skills/evolve/agents/openai.yaml +5 -0
- package/.agents/skills/frontmatter-guard/SKILL.md +3 -1
- package/.agents/skills/gitlab-ops/SKILL.md +3 -1
- package/.agents/skills/gitlab-portfolio/SKILL.md +3 -1
- package/.agents/skills/go/SKILL.md +22 -0
- package/.agents/skills/go/agents/openai.yaml +5 -0
- package/.agents/skills/grill/SKILL.md +7 -1
- package/.agents/skills/grill/agents/openai.yaml +5 -0
- package/.agents/skills/harness-audit/SKILL.md +20 -0
- package/.agents/skills/harness-audit/agents/openai.yaml +5 -0
- package/.agents/skills/hook-development/SKILL.md +3 -1
- package/.agents/skills/mcp-builder/SKILL.md +3 -1
- package/.agents/skills/memory-cleanup/SKILL.md +6 -1
- package/.agents/skills/memory-cleanup/agents/openai.yaml +5 -0
- package/.agents/skills/mode-selector/SKILL.md +3 -1
- package/.agents/skills/npm-publish/SKILL.md +4 -2
- package/.agents/skills/peekaboo-driver/SKILL.md +3 -1
- package/.agents/skills/persona-panel/SKILL.md +6 -1
- package/.agents/skills/persona-panel/agents/openai.yaml +5 -0
- package/.agents/skills/plan/SKILL.md +8 -2
- package/.agents/skills/plan/agents/openai.yaml +5 -0
- package/.agents/skills/playwright-driver/SKILL.md +3 -1
- package/.agents/skills/portfolio/SKILL.md +21 -0
- package/.agents/skills/portfolio/agents/openai.yaml +5 -0
- package/.agents/skills/quality-gates/SKILL.md +3 -1
- package/.agents/skills/reconcile/SKILL.md +6 -1
- package/.agents/skills/reconcile/agents/openai.yaml +5 -0
- package/.agents/skills/release/SKILL.md +22 -0
- package/.agents/skills/release/agents/openai.yaml +5 -0
- package/.agents/skills/remote-offload/SKILL.md +3 -1
- package/.agents/skills/repo-audit/SKILL.md +6 -1
- package/.agents/skills/repo-audit/agents/openai.yaml +5 -0
- package/.agents/skills/session/SKILL.md +21 -0
- package/.agents/skills/session/agents/openai.yaml +5 -0
- package/.agents/skills/session-end/SKILL.md +3 -1
- package/.agents/skills/session-plan/SKILL.md +3 -1
- package/.agents/skills/session-start/SKILL.md +3 -1
- package/.agents/skills/spinout/SKILL.md +6 -1
- package/.agents/skills/spinout/agents/openai.yaml +5 -0
- package/.agents/skills/sunset-review/SKILL.md +7 -1
- package/.agents/skills/sunset-review/agents/openai.yaml +5 -0
- package/.agents/skills/templates-ack/SKILL.md +21 -0
- package/.agents/skills/templates-ack/agents/openai.yaml +5 -0
- package/.agents/skills/test/SKILL.md +21 -0
- package/.agents/skills/test/agents/openai.yaml +5 -0
- package/.agents/skills/test-runner/SKILL.md +3 -1
- package/.agents/skills/tmux-layout/SKILL.md +3 -1
- package/.agents/skills/using-orchestrator/SKILL.md +3 -1
- package/.agents/skills/ux-grill/SKILL.md +7 -1
- package/.agents/skills/ux-grill/agents/openai.yaml +5 -0
- package/.agents/skills/vault-mirror/SKILL.md +3 -1
- package/.agents/skills/vault-sync/SKILL.md +3 -1
- package/.agents/skills/wave-executor/SKILL.md +3 -1
- package/.agents/skills/write-executable-plan/SKILL.md +3 -1
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +4 -4
- package/.codex-plugin/skills/autopilot/SKILL.md +5 -4
- package/.codex-plugin/skills/bootstrap/SKILL.md +8 -4
- package/.codex-plugin/skills/brainstorm/SKILL.md +11 -4
- package/.codex-plugin/skills/close/SKILL.md +3 -3
- package/.codex-plugin/skills/convergence-monitoring/SKILL.md +1 -1
- package/.codex-plugin/skills/debug/SKILL.md +11 -4
- package/.codex-plugin/skills/discovery/SKILL.md +8 -4
- package/.codex-plugin/skills/dispatcher/SKILL.md +4 -4
- package/.codex-plugin/skills/eli5/SKILL.md +9 -4
- package/.codex-plugin/skills/eval/SKILL.md +9 -4
- package/.codex-plugin/skills/evolve/SKILL.md +9 -4
- package/.codex-plugin/skills/go/SKILL.md +3 -3
- package/.codex-plugin/skills/grill/SKILL.md +11 -4
- package/.codex-plugin/skills/harness-audit/SKILL.md +4 -3
- package/.codex-plugin/skills/memory-cleanup/SKILL.md +9 -4
- package/.codex-plugin/skills/npm-publish/SKILL.md +1 -1
- package/.codex-plugin/skills/persona-panel/SKILL.md +5 -5
- package/.codex-plugin/skills/plan/SKILL.md +8 -4
- package/.codex-plugin/skills/portfolio/SKILL.md +3 -3
- package/.codex-plugin/skills/reconcile/SKILL.md +9 -4
- package/.codex-plugin/skills/release/SKILL.md +3 -3
- package/.codex-plugin/skills/repo-audit/SKILL.md +6 -4
- package/.codex-plugin/skills/session/SKILL.md +1 -1
- package/.codex-plugin/skills/spinout/SKILL.md +4 -4
- package/.codex-plugin/skills/sunset-review/SKILL.md +5 -4
- package/.codex-plugin/skills/test/SKILL.md +3 -3
- package/.codex-plugin/skills/ux-grill/SKILL.md +11 -4
- package/.cursor/commands/autopilot.md +4 -4
- package/.cursor/commands/bootstrap.md +5 -4
- package/.cursor/commands/brainstorm.md +5 -4
- package/.cursor/commands/close.md +4 -3
- package/.cursor/commands/debug.md +4 -4
- package/.cursor/commands/discovery.md +4 -4
- package/.cursor/commands/dispatcher.md +4 -4
- package/.cursor/commands/eli5.md +4 -4
- package/.cursor/commands/eval.md +4 -4
- package/.cursor/commands/evolve.md +4 -4
- package/.cursor/commands/go.md +4 -3
- package/.cursor/commands/grill.md +4 -4
- package/.cursor/commands/harness-audit.md +3 -3
- package/.cursor/commands/memory-cleanup.md +4 -4
- package/.cursor/commands/persona-panel.md +4 -4
- package/.cursor/commands/plan.md +5 -4
- package/.cursor/commands/portfolio.md +3 -3
- package/.cursor/commands/reconcile.md +4 -4
- package/.cursor/commands/release.md +4 -3
- package/.cursor/commands/repo-audit.md +4 -4
- package/.cursor/commands/session.md +1 -1
- package/.cursor/commands/spinout.md +4 -4
- package/.cursor/commands/sunset-review.md +4 -4
- package/.cursor/commands/test.md +3 -3
- package/.cursor/commands/ux-grill.md +4 -4
- package/.cursor/rules/000-session-orchestrator.mdc +0 -2
- package/.cursor/rules/010-session-workflow.mdc +2 -2
- package/.cursor/rules/050-plan.mdc +1 -1
- package/.cursor/skills/bootstrap/SKILL.md +1 -0
- package/.cursor/skills/close/SKILL.md +13 -0
- package/.cursor/skills/convergence-monitoring/SKILL.md +1 -0
- package/.cursor/skills/debug/SKILL.md +0 -1
- package/.cursor/skills/discovery/SKILL.md +0 -1
- package/.cursor/skills/dispatcher/SKILL.md +0 -1
- package/.cursor/skills/eli5/SKILL.md +0 -1
- package/.cursor/skills/eval/SKILL.md +1 -1
- package/.cursor/skills/evolve/SKILL.md +0 -1
- package/.cursor/skills/go/SKILL.md +13 -0
- package/.cursor/skills/grill/SKILL.md +0 -1
- package/.cursor/skills/harness-audit/SKILL.md +12 -0
- package/.cursor/skills/npm-publish/SKILL.md +1 -0
- package/.cursor/skills/portfolio/SKILL.md +12 -0
- package/.cursor/skills/release/SKILL.md +13 -0
- package/.cursor/skills/repo-audit/SKILL.md +0 -1
- package/.cursor/skills/sunset-review/SKILL.md +0 -1
- package/.cursor/skills/test/SKILL.md +12 -0
- package/.cursor/skills/ux-grill/SKILL.md +0 -1
- package/.cursor-plugin/plugin.json +1 -1
- package/.orchestrator/policy/blocked-commands.json +13 -4
- package/AGENTS.md +3 -2
- package/CHANGELOG.md +197 -0
- package/README.md +11 -9
- package/SECURITY.md +12 -0
- package/agents/dialectic-deriver.md +13 -10
- package/agents/eval-judge.md +67 -45
- package/agents/skill-applied-judge.md +34 -19
- package/commands/session.md +17 -3
- package/docs/baseline.md +12 -6
- package/docs/ci-setup.md +53 -0
- package/docs/codex-setup.md +15 -3
- package/docs/components.md +13 -6
- package/docs/events-schema.md +59 -9
- package/docs/install.md +16 -0
- package/docs/persona-panel.md +1 -1
- package/docs/pi-setup.md +1 -1
- package/docs/rule-authoring.md +135 -14
- package/docs/scope-collision-guard.md +2 -0
- package/docs/session-config-reference.md +106 -11
- package/docs/session-config-template.md +31 -2
- package/docs/telemetry.md +2 -0
- package/hooks/_lib/hook-import-set.json +125 -8
- package/hooks/_lib/subagent-paths.mjs +15 -0
- package/hooks/_lib/subagent-transcript.mjs +582 -31
- package/hooks/_lib/vcs-create-matcher.mjs +217 -62
- package/hooks/config-protection.mjs +11 -3
- package/hooks/cwd-change-restore.mjs +11 -3
- package/hooks/enforce-commands.mjs +70 -23
- package/hooks/enforce-scope.mjs +143 -33
- package/hooks/hooks-codex.json +1 -1
- package/hooks/hooks.json +1 -1
- package/hooks/loop-guard.mjs +11 -3
- package/hooks/on-session-end.mjs +72 -25
- package/hooks/on-session-start.mjs +48 -11
- package/hooks/on-stop.mjs +211 -23
- package/hooks/operator-steer.mjs +11 -3
- package/hooks/post-bash-issue-budget-refund.mjs +18 -8
- package/hooks/post-bash-write-verify.mjs +6 -2
- package/hooks/post-edit-import-probe.mjs +17 -9
- package/hooks/post-edit-validate.mjs +13 -5
- package/hooks/post-subagent-discovery-validator.mjs +98 -13
- package/hooks/post-tool-batch-wave-signal.mjs +200 -38
- package/hooks/post-tool-failure-corrective-context.mjs +11 -5
- package/hooks/post-tooluse-frontend-slop.mjs +10 -4
- package/hooks/pre-auq-clarity.mjs +18 -2
- package/hooks/pre-bash-destructive-guard.mjs +80 -9
- package/hooks/pre-bash-issue-budget.mjs +119 -28
- package/hooks/pre-bash-memory-propose-audit.mjs +86 -54
- package/hooks/pre-bash-sessions-ledger-guard.mjs +391 -20
- package/hooks/pre-bash-staging-fence.mjs +335 -31
- package/hooks/pre-bash-templates-first.mjs +19 -14
- package/hooks/pre-task-scope-disjoint.mjs +385 -5
- package/hooks/skill-invocation-telemetry.mjs +2 -1
- package/hooks/subagent-telemetry.mjs +15 -19
- package/hooks/wave-scope-commit-guard.mjs +197 -100
- package/monitors/monitors.json +1 -1
- package/output-styles/wave-summary.md +1 -1
- package/package.json +2 -1
- package/pi/prompts/autopilot.md +3 -3
- package/pi/prompts/bootstrap.md +3 -3
- package/pi/prompts/brainstorm.md +3 -3
- package/pi/prompts/close.md +2 -2
- package/pi/prompts/debug.md +3 -3
- package/pi/prompts/discovery.md +3 -3
- package/pi/prompts/dispatcher.md +3 -3
- package/pi/prompts/eli5.md +3 -3
- package/pi/prompts/eval.md +3 -3
- package/pi/prompts/evolve.md +3 -3
- package/pi/prompts/go.md +2 -2
- package/pi/prompts/grill.md +3 -3
- package/pi/prompts/harness-audit.md +2 -3
- package/pi/prompts/memory-cleanup.md +3 -3
- package/pi/prompts/persona-panel.md +3 -3
- package/pi/prompts/plan.md +3 -3
- package/pi/prompts/portfolio.md +2 -2
- package/pi/prompts/reconcile.md +3 -3
- package/pi/prompts/release.md +3 -3
- package/pi/prompts/repo-audit.md +3 -4
- package/pi/prompts/session.md +2 -2
- package/pi/prompts/spinout.md +3 -3
- package/pi/prompts/sunset-review.md +3 -3
- package/pi/prompts/templates-ack.md +1 -1
- package/pi/prompts/test.md +3 -3
- package/pi/prompts/ux-grill.md +3 -3
- package/rules/README.md +1 -1
- package/rules/opt-in-domain/prompt-caching.md +1 -1
- package/rules/opt-in-stack/backend-data.md +1 -1
- package/rules/opt-in-stack/backend.md +3 -3
- package/rules/opt-in-stack/frontend.md +1 -1
- package/rules/opt-in-stack/security-web.md +3 -3
- package/rules/opt-in-stack/swift.md +1 -1
- package/scripts/archive-closed-prds.mjs +2 -2
- package/scripts/auq-audit.mjs +2 -3
- package/scripts/autopilot.mjs +23 -2
- package/scripts/backfill-abandoned-sessions.mjs +171 -15
- package/scripts/backfill-evidence-digest.mjs +2 -1
- package/scripts/backfill-learnings-from-vault.mjs +2 -2
- package/scripts/check-package-manager.mjs +2 -2
- package/scripts/check-sessions-integrity.mjs +300 -0
- package/scripts/ci/assert-vitest-green.mjs +2 -1
- package/scripts/dialectic-deriver.mjs +50 -13
- package/scripts/emit-session.mjs +77 -32
- package/scripts/eval-session.mjs +65 -3
- package/scripts/export-hw-learnings.mjs +2 -1
- package/scripts/express-path.mjs +1 -1
- package/scripts/gc-stale-worktrees.mjs +2 -1
- package/scripts/generate-agents-skills.mjs +102 -29
- package/scripts/generate-codex-skills.mjs +48 -4
- package/scripts/generate-cursor-adapter.mjs +220 -11
- package/scripts/generate-hook-import-set.mjs +12 -27
- package/scripts/generate-pi-prompts.mjs +183 -13
- package/scripts/github-protection-audit.mjs +2 -3
- package/scripts/lib/agent-frontmatter.mjs +23 -1
- package/scripts/lib/agent-status.mjs +2 -31
- package/scripts/lib/auq/clarity.mjs +10 -2
- package/scripts/lib/auq/parse.mjs +12 -31
- package/scripts/lib/auq/schema.mjs +56 -41
- package/scripts/lib/auto-dialectic.mjs +304 -15
- package/scripts/lib/autopilot/flags.mjs +12 -1
- package/scripts/lib/autopilot/kill-switches.mjs +6 -3
- package/scripts/lib/autopilot/loop.mjs +14 -1
- package/scripts/lib/autopilot/stall-sampler.mjs +80 -23
- package/scripts/lib/ci-status-banner.mjs +376 -16
- package/scripts/lib/claude-md-budget-lint.mjs +2 -5
- package/scripts/lib/command-blocker.mjs +408 -33
- package/scripts/lib/config/dialectic.mjs +12 -3
- package/scripts/lib/config/drift-check.mjs +19 -0
- package/scripts/lib/config/gate.mjs +74 -0
- package/scripts/lib/config/reaper.mjs +162 -0
- package/scripts/lib/config.mjs +14 -0
- package/scripts/lib/convergence-monitor.mjs +76 -13
- package/scripts/lib/cursor-hook-bridge.mjs +2 -2
- package/scripts/lib/description-surface.mjs +2 -5
- package/scripts/lib/dispatcher/cli.mjs +2 -1
- package/scripts/lib/ecosystem-health.mjs +11 -0
- package/scripts/lib/ecosystem-wizard.mjs +2 -1
- package/scripts/lib/eval/engine.mjs +421 -53
- package/scripts/lib/eval/judge.mjs +463 -40
- package/scripts/lib/eval/schema.mjs +10 -1
- package/scripts/lib/events-rotation.mjs +221 -25
- package/scripts/lib/events-schema.mjs +114 -0
- package/scripts/lib/events.mjs +524 -5
- package/scripts/lib/fetch-baseline.mjs +3 -8
- package/scripts/lib/frontmatter-guard.mjs +21 -10
- package/scripts/lib/gates/gate-baseline.mjs +27 -2
- package/scripts/lib/gates/gate-full.mjs +28 -3
- package/scripts/lib/gates/gate-helpers.mjs +243 -21
- package/scripts/lib/gates/gate-incremental.mjs +28 -3
- package/scripts/lib/gates/gate-per-file.mjs +27 -2
- package/scripts/lib/gitlab-ops/stale-mr-sweep.mjs +2 -1
- package/scripts/lib/gitlab-portfolio/cli.mjs +2 -1
- package/scripts/lib/gitlab-portfolio/markdown-writer.mjs +6 -1
- package/scripts/lib/instruction-budget-guard.mjs +332 -50
- package/scripts/lib/io.mjs +42 -8
- package/scripts/lib/is-main-module.mjs +82 -0
- package/scripts/lib/issue-close-strip-labels.mjs +207 -49
- package/scripts/lib/js-mask.mjs +197 -0
- package/scripts/lib/learnings/evolve-telemetry.mjs +11 -7
- package/scripts/lib/locks/index.mjs +32 -25
- package/scripts/lib/maintenance-due-banner.mjs +122 -91
- package/scripts/lib/orphan-reaper.mjs +1588 -0
- package/scripts/lib/peer-cards/merger.mjs +48 -10
- package/scripts/lib/peer-cards/reader.mjs +78 -2
- package/scripts/lib/peer-discovery.mjs +2 -5
- package/scripts/lib/playwright-driver/runner.mjs +2 -1
- package/scripts/lib/process-group.mjs +899 -0
- package/scripts/lib/quality-gate.mjs +107 -28
- package/scripts/lib/reconcile/backlog.mjs +368 -0
- package/scripts/lib/reconcile/engine.mjs +55 -188
- package/scripts/lib/reconcile/rule-expiry-sweep.mjs +884 -0
- package/scripts/lib/reconcile/sanitize.mjs +69 -3
- package/scripts/lib/reconcile-nudge-banner.mjs +138 -45
- package/scripts/lib/resource-probe/parsers.mjs +31 -0
- package/scripts/lib/rule-loader.mjs +41 -12
- package/scripts/lib/rules-sync.mjs +2 -5
- package/scripts/lib/scope-echo.mjs +429 -7
- package/scripts/lib/scope-gate.mjs +605 -1
- package/scripts/lib/session-close-backfill.mjs +91 -12
- package/scripts/lib/session-id.mjs +9 -20
- package/scripts/lib/session-invocation.mjs +20 -0
- package/scripts/lib/session-schema/constants.mjs +30 -2
- package/scripts/lib/session-schema/normalizer.mjs +56 -4
- package/scripts/lib/session-schema.mjs +8 -3
- package/scripts/lib/session-start-probes.mjs +95 -10
- package/scripts/lib/sessions-canonical.mjs +23 -0
- package/scripts/lib/sessions-integrity-banner.mjs +7 -1
- package/scripts/lib/sessions-staleness-banner.mjs +193 -51
- package/scripts/lib/skill-evidence-window.mjs +891 -0
- package/scripts/lib/skill-evolution/candidate-intake.mjs +133 -12
- package/scripts/lib/skill-evolution/engine.mjs +18 -9
- package/scripts/lib/skill-judge.mjs +45 -3
- package/scripts/lib/state-md.mjs +84 -3
- package/scripts/lib/sunset/walker.mjs +31 -4
- package/scripts/lib/tail-window.mjs +56 -0
- package/scripts/lib/telemetry/schema.mjs +30 -0
- package/scripts/lib/telemetry/sync.mjs +61 -6
- package/scripts/lib/telemetry-flush-health-banner.mjs +4 -22
- package/scripts/lib/test-runner/issue-reconcile.mjs +48 -16
- package/scripts/lib/tests-src-ratio.mjs +2 -6
- package/scripts/lib/tmux-layout/telemetry-stats.mjs +74 -14
- package/scripts/lib/user-invocable-skills.mjs +205 -0
- package/scripts/lib/ux-grill/reconcile.mjs +48 -22
- package/scripts/lib/validate/check-agents-skills.mjs +26 -15
- package/scripts/lib/validate/check-banner-parity.mjs +2 -2
- package/scripts/lib/validate/check-cursor-adapter.mjs +3 -2
- package/scripts/lib/validate/check-dead-bridge.mjs +2 -2
- package/scripts/lib/validate/check-doc-cli-commands.mjs +2 -2
- package/scripts/lib/validate/check-entry-guard.mjs +329 -0
- package/scripts/lib/validate/check-guard-requires-parity.mjs +2 -2
- package/scripts/lib/validate/check-hook-entry-guards.mjs +636 -0
- package/scripts/lib/validate/check-hooks-emit-event-guard.mjs +2 -2
- package/scripts/lib/validate/check-learning-provenance.mjs +2 -2
- package/scripts/lib/validate/check-pi-prompts.mjs +1 -0
- package/scripts/lib/validate/check-rules.mjs +7 -5
- package/scripts/lib/validate/check-skill-links.mjs +35 -6
- package/scripts/lib/validate/check-skill-script-paths.mjs +241 -29
- package/scripts/lib/validate/check-test-git-config-target.mjs +26 -36
- package/scripts/lib/validate/check-unicode-safety.mjs +2 -2
- package/scripts/lib/validate/check-untracked-test-deps.mjs +9 -104
- package/scripts/lib/validate/check-unwired-features.mjs +220 -33
- package/scripts/lib/validate/check-validator-registration.mjs +36 -12
- package/scripts/lib/validate/check-vcs-repo-flag.mjs +2 -2
- package/scripts/lib/validate/confidential-names.mjs +10 -0
- package/scripts/lib/validate-vendored-rules.mjs +39 -12
- package/scripts/lib/vault-mirror/namespace.mjs +46 -8
- package/scripts/lib/vault-mirror/process.mjs +10 -3
- package/scripts/lib/vault-mirror/render-sessions.mjs +12 -2
- package/scripts/lib/vault-status/narrative-mirror.mjs +31 -7
- package/scripts/lib/vault-yaml.mjs +118 -0
- package/scripts/lib/wave-transcript-tail.mjs +2 -2
- package/scripts/lib/worktree/lifecycle.mjs +153 -1
- package/scripts/lock-reaper.mjs +2 -1
- package/scripts/materialize-wave-scope.mjs +87 -4
- package/scripts/migrate-sessions-jsonl.mjs +2 -1
- package/scripts/migrate-vault-paths.mjs +2 -3
- package/scripts/release-session-lock.mjs +305 -0
- package/scripts/release.mjs +109 -39
- package/scripts/relocate-vault-corpus.mjs +2 -3
- package/scripts/repair-invalid-sessions.mjs +2 -2
- package/scripts/resolve-session-invocation.mjs +59 -0
- package/scripts/run-quality-gate.mjs +156 -17
- package/scripts/session-shape.mjs +2 -2
- package/scripts/site-numbers.mjs +35 -11
- package/scripts/sweep-expired-rules.mjs +227 -0
- package/scripts/validate-plugin.mjs +21 -0
- package/scripts/validate-wave-scope.mjs +32 -105
- package/scripts/vault-consolidate.mjs +2 -2
- package/scripts/vault-mirror.mjs +11 -4
- package/scripts/wave-scope-binding.mjs +2 -3
- package/skills/_shared/bootstrap-gate.md +1 -1
- package/skills/_shared/monitor-patterns.md +1 -1
- package/skills/_shared/platform-tools.md +23 -11
- package/skills/_shared/research-evidence.md +53 -0
- package/skills/_shared/state-ownership.md +3 -0
- package/skills/autopilot/SKILL.md +80 -11
- package/skills/bootstrap/SKILL.md +51 -1
- package/skills/brainstorm/SKILL.md +16 -0
- package/skills/claude-md-drift-check/SKILL.md +1 -1
- package/skills/claude-md-drift-check/checker.mjs +49 -11
- package/{commands/close.md → skills/close/SKILL.md} +9 -3
- package/skills/convergence-monitoring/README.md +8 -1
- package/skills/convergence-monitoring/SIGNALS.md +50 -6
- package/skills/convergence-monitoring/SKILL.md +15 -6
- package/skills/debug/SKILL.md +10 -0
- package/skills/discovery/SKILL.md +24 -1
- package/skills/discovery/probes-session.md +2 -2
- package/skills/dispatcher/SKILL.md +38 -7
- package/skills/eli5/SKILL.md +11 -0
- package/skills/eval/SKILL.md +52 -23
- package/skills/eval/rubric-v1.md +1 -0
- package/skills/eval/rubric-v2.md +457 -0
- package/skills/evolve/SKILL.md +9 -2
- package/skills/evolve/references/evolve-dialectic-mode.md +46 -25
- package/skills/gitlab-ops/SKILL.md +3 -2
- package/{commands/go.md → skills/go/SKILL.md} +9 -1
- package/skills/grill/SKILL.md +19 -0
- package/{commands/harness-audit.md → skills/harness-audit/SKILL.md} +7 -2
- package/skills/hook-development/SKILL.md +46 -41
- package/skills/memory-cleanup/SKILL.md +7 -0
- package/skills/npm-publish/SKILL.md +2 -2
- package/skills/persona-panel/SKILL.md +56 -1
- package/skills/persona-panel/persona-format.md +1 -1
- package/skills/plan/SKILL.md +28 -1
- package/{commands/portfolio.md → skills/portfolio/SKILL.md} +8 -2
- package/skills/reconcile/SKILL.md +21 -0
- package/{commands/release.md → skills/release/SKILL.md} +16 -2
- package/skills/repo-audit/SKILL.md +7 -0
- package/skills/session-end/SKILL.md +13 -16
- package/skills/session-end/discovery-scan.md +1 -1
- package/skills/session-end/phase-3-6-tail.md +55 -9
- package/skills/session-end/plan-verification.md +2 -2
- package/skills/session-end/references/phase-5-issue-cleanup.md +9 -14
- package/skills/session-end/session-metrics-write.md +10 -0
- package/skills/session-plan/SKILL.md +18 -6
- package/skills/session-plan/references/session-plan-task-classification.md +2 -2
- package/skills/session-start/SKILL.md +5 -4
- package/skills/session-start/phase-8-5-express-path.md +6 -6
- package/skills/session-start/references/phase-1-5-session-continuity.md +1 -1
- package/skills/session-start/references/phase-2-7-portfolio-snapshot.md +1 -1
- package/skills/session-start/references/phase-4-ssot-environment-check.md +6 -4
- package/skills/spinout/SKILL.md +12 -1
- package/skills/sunset-review/SKILL.md +13 -0
- package/{commands/test.md → skills/test/SKILL.md} +10 -4
- package/skills/ux-grill/SKILL.md +20 -2
- package/skills/wave-executor/SKILL.md +14 -7
- package/skills/wave-executor/circuit-breaker.md +2 -0
- package/skills/wave-executor/references/wave-executor-state-init.md +18 -4
- package/skills/wave-executor/references/wave-loop-dispatch.md +5 -2
- package/skills/wave-executor/references/wave-loop-review.md +17 -1
- package/commands/autopilot.md +0 -80
- package/commands/bootstrap.md +0 -56
- package/commands/brainstorm.md +0 -48
- package/commands/debug.md +0 -36
- package/commands/discovery.md +0 -32
- package/commands/dispatcher.md +0 -59
- package/commands/eli5.md +0 -33
- package/commands/eval.md +0 -28
- package/commands/evolve.md +0 -10
- package/commands/grill.md +0 -45
- package/commands/memory-cleanup.md +0 -26
- package/commands/persona-panel.md +0 -121
- package/commands/plan.md +0 -15
- package/commands/reconcile.md +0 -23
- package/commands/repo-audit.md +0 -24
- package/commands/spinout.md +0 -15
- package/commands/sunset-review.md +0 -27
- package/commands/ux-grill.md +0 -51
|
@@ -10,6 +10,31 @@ import { runCheck } from './gate-helpers.mjs';
|
|
|
10
10
|
const typecheckCmd = process.env.TYPECHECK_CMD;
|
|
11
11
|
const testCmd = process.env.TEST_CMD;
|
|
12
12
|
|
|
13
|
+
/**
|
|
14
|
+
* Per-command wall-clock ceiling, published by `scripts/run-quality-gate.mjs`
|
|
15
|
+
* as `GATE_TIMEOUT_MS` (#1425 A3 / #1432). It carries the ALREADY-RESOLVED
|
|
16
|
+
* value — operator override `SO_GATE_TIMEOUT_MS` > Session Config
|
|
17
|
+
* `gate.timeout-path-b-ms` > 900 000 — so this script only has to read it.
|
|
18
|
+
*
|
|
19
|
+
* Absent or non-numeric (this gate invoked directly, not through the wrapper)
|
|
20
|
+
* → the option is OMITTED, and `runCheck` falls back to `resolveGateTimeoutMs()`
|
|
21
|
+
* exactly as before. An empty object is deliberate: passing `timeoutMs:
|
|
22
|
+
* undefined` would NOT trigger that fallback in every spread order.
|
|
23
|
+
*/
|
|
24
|
+
const CHECK_OPTS = (() => {
|
|
25
|
+
const raw = Number((process.env.GATE_TIMEOUT_MS || '').trim());
|
|
26
|
+
const opts = Number.isFinite(raw) && raw > 0 ? { timeoutMs: raw } : {};
|
|
27
|
+
// GATE_LEDGER_ROOT (#1425 A4, W5 fix-pass): the wrapper publishes the ledger
|
|
28
|
+
// root it resolved (--ledger-root > repo root) so the gate-process register
|
|
29
|
+
// is written where the WRAPPER decided, not where this sub-script happens to
|
|
30
|
+
// run. Without it every runCheck() here defaulted to process.cwd(), and a test
|
|
31
|
+
// that spawned this script from the checkout wrote real lines into the live
|
|
32
|
+
// .orchestrator/runtime/gate-processes.jsonl — the reaper's kill population.
|
|
33
|
+
const ledgerRoot = (process.env.GATE_LEDGER_ROOT || "").trim();
|
|
34
|
+
if (ledgerRoot && ledgerRoot.startsWith("/")) opts.repoRoot = ledgerRoot;
|
|
35
|
+
return opts;
|
|
36
|
+
})();
|
|
37
|
+
|
|
13
38
|
if (!typecheckCmd) {
|
|
14
39
|
process.stderr.write('TYPECHECK_CMD must be set\n');
|
|
15
40
|
process.exit(1);
|
|
@@ -23,11 +48,11 @@ let tcResult = { status: 'skip', output: '' };
|
|
|
23
48
|
let testResult = { status: 'skip', output: '' };
|
|
24
49
|
|
|
25
50
|
if (typecheckCmd !== 'skip') {
|
|
26
|
-
tcResult = await runCheck(typecheckCmd);
|
|
51
|
+
tcResult = await runCheck(typecheckCmd, CHECK_OPTS);
|
|
27
52
|
}
|
|
28
53
|
|
|
29
54
|
if (testCmd !== 'skip') {
|
|
30
|
-
testResult = await runCheck(testCmd);
|
|
55
|
+
testResult = await runCheck(testCmd, CHECK_OPTS);
|
|
31
56
|
}
|
|
32
57
|
|
|
33
58
|
const result = {
|
|
@@ -18,6 +18,31 @@ const testCmd = process.env.TEST_CMD;
|
|
|
18
18
|
const lintCmd = process.env.LINT_CMD;
|
|
19
19
|
const sessionStartRef = process.env.SESSION_START_REF ?? '';
|
|
20
20
|
|
|
21
|
+
/**
|
|
22
|
+
* Per-command wall-clock ceiling, published by `scripts/run-quality-gate.mjs`
|
|
23
|
+
* as `GATE_TIMEOUT_MS` (#1425 A3 / #1432). It carries the ALREADY-RESOLVED
|
|
24
|
+
* value — operator override `SO_GATE_TIMEOUT_MS` > Session Config
|
|
25
|
+
* `gate.timeout-path-b-ms` > 900 000 — so this script only has to read it.
|
|
26
|
+
*
|
|
27
|
+
* Absent or non-numeric (this gate invoked directly, not through the wrapper)
|
|
28
|
+
* → the option is OMITTED, and `runCheck` falls back to `resolveGateTimeoutMs()`
|
|
29
|
+
* exactly as before. An empty object is deliberate: passing `timeoutMs:
|
|
30
|
+
* undefined` would NOT trigger that fallback in every spread order.
|
|
31
|
+
*/
|
|
32
|
+
const CHECK_OPTS = (() => {
|
|
33
|
+
const raw = Number((process.env.GATE_TIMEOUT_MS || '').trim());
|
|
34
|
+
const opts = Number.isFinite(raw) && raw > 0 ? { timeoutMs: raw } : {};
|
|
35
|
+
// GATE_LEDGER_ROOT (#1425 A4, W5 fix-pass): the wrapper publishes the ledger
|
|
36
|
+
// root it resolved (--ledger-root > repo root) so the gate-process register
|
|
37
|
+
// is written where the WRAPPER decided, not where this sub-script happens to
|
|
38
|
+
// run. Without it every runCheck() here defaulted to process.cwd(), and a test
|
|
39
|
+
// that spawned this script from the checkout wrote real lines into the live
|
|
40
|
+
// .orchestrator/runtime/gate-processes.jsonl — the reaper's kill population.
|
|
41
|
+
const ledgerRoot = (process.env.GATE_LEDGER_ROOT || "").trim();
|
|
42
|
+
if (ledgerRoot && ledgerRoot.startsWith("/")) opts.repoRoot = ledgerRoot;
|
|
43
|
+
return opts;
|
|
44
|
+
})();
|
|
45
|
+
|
|
21
46
|
if (!typecheckCmd) {
|
|
22
47
|
process.stderr.write('TYPECHECK_CMD must be set\n');
|
|
23
48
|
process.exit(1);
|
|
@@ -34,7 +59,7 @@ if (!lintCmd) {
|
|
|
34
59
|
const startTime = Date.now();
|
|
35
60
|
|
|
36
61
|
// --- Typecheck ---
|
|
37
|
-
const tcResult = runCheck(typecheckCmd);
|
|
62
|
+
const tcResult = await runCheck(typecheckCmd, CHECK_OPTS);
|
|
38
63
|
const tcErrorCount =
|
|
39
64
|
tcResult.status === 'fail'
|
|
40
65
|
? extractCount(tcResult.fullOutput ?? tcResult.output, /error TS\d+/)
|
|
@@ -44,7 +69,7 @@ const tcErrorCount =
|
|
|
44
69
|
// NOTE: `testCounts`, NOT `failed` — a local `failed` is already bound near
|
|
45
70
|
// the bottom of this file and drives `process.exit(failed ? 2 : 0)`.
|
|
46
71
|
// Shadowing it would corrupt the gate's exit code.
|
|
47
|
-
const testResult = runCheck(testCmd);
|
|
72
|
+
const testResult = await runCheck(testCmd, CHECK_OPTS);
|
|
48
73
|
const testCounts =
|
|
49
74
|
testResult.status !== 'skip'
|
|
50
75
|
? extractTestCounts(testResult.fullOutput ?? testResult.output)
|
|
@@ -98,7 +123,7 @@ const fileFields = testCounts.files
|
|
|
98
123
|
: {};
|
|
99
124
|
|
|
100
125
|
// --- Lint ---
|
|
101
|
-
const lintResult = runCheck(lintCmd);
|
|
126
|
+
const lintResult = await runCheck(lintCmd, CHECK_OPTS);
|
|
102
127
|
const lintWarnings =
|
|
103
128
|
lintResult.status !== 'skip'
|
|
104
129
|
? extractCount(lintResult.fullOutput ?? lintResult.output, /warning/i)
|
|
@@ -8,10 +8,176 @@
|
|
|
8
8
|
|
|
9
9
|
import { execSync } from 'node:child_process';
|
|
10
10
|
import { readFileSync } from 'node:fs';
|
|
11
|
+
|
|
12
|
+
import {
|
|
13
|
+
DEFAULT_GATE_TIMEOUT_MS,
|
|
14
|
+
buildCommandSignature,
|
|
15
|
+
spawnInGroup,
|
|
16
|
+
} from '../process-group.mjs';
|
|
11
17
|
import { detectStubCommand } from './echo-stub-detect.mjs';
|
|
12
18
|
|
|
13
19
|
const RUN_CHECK_MAX_BUFFER_BYTES = 64 * 1024 * 1024;
|
|
14
20
|
|
|
21
|
+
/** Lines of captured output kept in the human-facing `output` tail. */
|
|
22
|
+
const OUTPUT_TAIL_LINES = 5;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Name of the per-invocation override for the gate wall-clock ceiling.
|
|
26
|
+
*
|
|
27
|
+
* Read at CALL time, never at module load, so a test (or a caller that sets it
|
|
28
|
+
* for one child) is not defeated by import order.
|
|
29
|
+
*
|
|
30
|
+
* Named ceiling (BV-004): this is an ENV var, and an env var is inherited by
|
|
31
|
+
* every descendant — the exact shape that made `SO_GATE_LEDGER_ROOT` reach
|
|
32
|
+
* every vitest worker on 2026-09-06 (`scripts/run-quality-gate.mjs`, the
|
|
33
|
+
* `--ledger-root` block). It is acceptable here only because the value is a
|
|
34
|
+
* CEILING every descendant should honour anyway. A future Session-Config
|
|
35
|
+
* wiring of `gate.timeout-path-b-ms` should prefer the explicit `timeoutMs`
|
|
36
|
+
* option below — which reaches exactly one call — over exporting this name.
|
|
37
|
+
*/
|
|
38
|
+
export const GATE_TIMEOUT_ENV = 'SO_GATE_TIMEOUT_MS';
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* The wall-clock ceiling one gate command is allowed, in ms.
|
|
42
|
+
*
|
|
43
|
+
* Precedence: `process.env.SO_GATE_TIMEOUT_MS` when set to a finite positive
|
|
44
|
+
* number, else {@link DEFAULT_GATE_TIMEOUT_MS} (900_000 — the PRD's
|
|
45
|
+
* `gate.timeout-path-b-ms`, deliberately the same 15 min the synchronous
|
|
46
|
+
* path A already had, so both gate paths are allowed exactly as long).
|
|
47
|
+
*
|
|
48
|
+
* A non-numeric or non-positive value is IGNORED rather than honoured: a typo
|
|
49
|
+
* that parsed as 0 would disable the cap, which is the failure this whole
|
|
50
|
+
* change exists to remove.
|
|
51
|
+
*
|
|
52
|
+
* @returns {number} positive milliseconds
|
|
53
|
+
*/
|
|
54
|
+
export function resolveGateTimeoutMs() {
|
|
55
|
+
const raw = (process.env[GATE_TIMEOUT_ENV] || '').trim();
|
|
56
|
+
if (!raw) return DEFAULT_GATE_TIMEOUT_MS;
|
|
57
|
+
const parsed = Number(raw);
|
|
58
|
+
if (!Number.isFinite(parsed) || parsed <= 0) return DEFAULT_GATE_TIMEOUT_MS;
|
|
59
|
+
return parsed;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Last {@link OUTPUT_TAIL_LINES} lines of a captured text, trimmed.
|
|
64
|
+
*
|
|
65
|
+
* @param {string} text
|
|
66
|
+
* @returns {string}
|
|
67
|
+
*/
|
|
68
|
+
function tailOf(text) {
|
|
69
|
+
return String(text ?? '').split('\n').slice(-OUTPUT_TAIL_LINES).join('\n').trim();
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* The one line a timed-out gate command MUST carry.
|
|
74
|
+
*
|
|
75
|
+
* A timeout is a FAILURE with a name, never a silent `fail`: without this line
|
|
76
|
+
* a killed command is indistinguishable in the envelope from a command that
|
|
77
|
+
* exited non-zero on its own, and the operator has nothing to act on. It names
|
|
78
|
+
* the ceiling that fired, the process GROUP that was signalled, the ladder that
|
|
79
|
+
* ran, and — the part an exit code can never carry — anything that SURVIVED
|
|
80
|
+
* SIGKILL (PRD B6: a sent signal proves nothing).
|
|
81
|
+
*
|
|
82
|
+
* @param {number} timeoutMs
|
|
83
|
+
* @param {{pgid: number, killSignals: string[], survivors: number[]}} run
|
|
84
|
+
* @returns {string}
|
|
85
|
+
*/
|
|
86
|
+
function timeoutLine(timeoutMs, run) {
|
|
87
|
+
const ladder = (run.killSignals ?? []).join('\u2192') || 'no signal sent';
|
|
88
|
+
return `gate: TIMEOUT after ${timeoutMs} ms \u2014 process group ${run.pgid} ${ladder}, `
|
|
89
|
+
+ `survivors: [${(run.survivors ?? []).join(', ')}]`;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* The stdout envelope `scripts/run-quality-gate.mjs` publishes when the GATE
|
|
94
|
+
* SUB-SCRIPT itself was killed on the wall-clock ceiling.
|
|
95
|
+
*
|
|
96
|
+
* Lives here, beside the other envelope helpers, because the CLI that consumes
|
|
97
|
+
* it is a top-level script with no exports — a pure function there would be
|
|
98
|
+
* untestable without executing the CLI.
|
|
99
|
+
*
|
|
100
|
+
* Deliberately carries no `test`/`typecheck`/`lint` object: a killed gate
|
|
101
|
+
* measured nothing, and `suiteCountsFromGateStdout` must return `null` for it
|
|
102
|
+
* (absent is not zero). `error: 'gate-timeout'` is the machine-readable
|
|
103
|
+
* discriminator; the exit code is 124, the same value `spawnInGroup` reports
|
|
104
|
+
* and the same one coreutils `timeout(1)` uses.
|
|
105
|
+
*
|
|
106
|
+
* @param {object} args
|
|
107
|
+
* @param {string} args.variant The `--variant` value the run was started with.
|
|
108
|
+
* @param {number} args.timeoutMs Ceiling that fired.
|
|
109
|
+
* @param {{pgid: number, durationMs: number, killSignals: string[], survivors: number[]}} args.run
|
|
110
|
+
* The {@link spawnInGroup} result.
|
|
111
|
+
* @returns {{variant: string, error: 'gate-timeout', timeout_ms: number, duration_ms: number,
|
|
112
|
+
* pgid: number, kill_signals: string[], survivors: number[]}}
|
|
113
|
+
*/
|
|
114
|
+
export function gateTimeoutEnvelope({ variant, timeoutMs, run }) {
|
|
115
|
+
return {
|
|
116
|
+
variant,
|
|
117
|
+
error: 'gate-timeout',
|
|
118
|
+
timeout_ms: timeoutMs,
|
|
119
|
+
duration_ms: run?.durationMs ?? 0,
|
|
120
|
+
pgid: run?.pgid ?? -1,
|
|
121
|
+
kill_signals: run?.killSignals ?? [],
|
|
122
|
+
survivors: run?.survivors ?? [],
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Decide WHAT a finished gate sub-script run publishes — stdout, stderr,
|
|
128
|
+
* exit code and operator warnings — without performing any of the writes.
|
|
129
|
+
*
|
|
130
|
+
* ## Why this is a pure function and not four `process.*.write` calls
|
|
131
|
+
*
|
|
132
|
+
* The timeout branch in `scripts/run-quality-gate.mjs` was unreachable by any
|
|
133
|
+
* test: reaching it required a REAL gate sub-script to exceed
|
|
134
|
+
* `resolveGateTimeoutMs() + GATE_OUTER_TIMEOUT_RESERVE_MS` (15 min + 60 s, a
|
|
135
|
+
* hard constant with no injection seam), so the four decisions it makes —
|
|
136
|
+
* suppress the partial capture on stdout, re-publish it on stderr, emit ONE
|
|
137
|
+
* complete `gate-timeout` envelope, warn about survivors — were pinned by
|
|
138
|
+
* nothing. The CLI now decides here and only WRITES there, so each decision is
|
|
139
|
+
* testable against a synthetic {@link spawnInGroup} result.
|
|
140
|
+
*
|
|
141
|
+
* ## The one-document contract on stdout
|
|
142
|
+
*
|
|
143
|
+
* A killed child never wrote its envelope, so its capture is at best a partial
|
|
144
|
+
* JSON document. Publishing that hands every stdout consumer a parse error
|
|
145
|
+
* where a named failure belongs; publishing BOTH the partial text and an
|
|
146
|
+
* envelope breaks the "one JSON document" contract. Hence: capture → stderr,
|
|
147
|
+
* envelope → stdout, and `stdout` carries EXACTLY the envelope line.
|
|
148
|
+
*
|
|
149
|
+
* @param {object} args
|
|
150
|
+
* @param {{fullOutput?: string, exitCode?: number, timedOut?: boolean, pgid?: number,
|
|
151
|
+
* durationMs?: number, killSignals?: string[], survivors?: number[]}} args.result
|
|
152
|
+
* The {@link spawnInGroup} result for the gate sub-script.
|
|
153
|
+
* @param {string} args.variant The `--variant` value the run was started with.
|
|
154
|
+
* @param {number} args.timeoutMs The OUTER ceiling that applied to the sub-script.
|
|
155
|
+
* @returns {{stdout: string, stderr: string, exitCode: number, warnings: string[]}}
|
|
156
|
+
* `stdout`/`stderr` are written verbatim (empty string = write nothing);
|
|
157
|
+
* `warnings` go through the caller's `warn()`; `exitCode` is the gate's own,
|
|
158
|
+
* which `spawnInGroup` reports as 124 on the timeout path.
|
|
159
|
+
*/
|
|
160
|
+
export function publishGateOutcome({ result, variant, timeoutMs }) {
|
|
161
|
+
const capture = String(result?.fullOutput ?? '');
|
|
162
|
+
const exitCode = result?.exitCode ?? 0;
|
|
163
|
+
|
|
164
|
+
if (!result?.timedOut) {
|
|
165
|
+
return { stdout: capture, stderr: '', exitCode, warnings: [] };
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
const survivors = result.survivors ?? [];
|
|
169
|
+
return {
|
|
170
|
+
stdout: `${JSON.stringify(gateTimeoutEnvelope({ variant, timeoutMs, run: result }))}\n`,
|
|
171
|
+
stderr: capture.trim()
|
|
172
|
+
? `\n\u2500\u2500\u2500\u2500 gate TIMED OUT \u2014 captured output before the kill \u2500\u2500\u2500\u2500\n${capture}\n\u2500\u2500\u2500\u2500 end \u2500\u2500\u2500\u2500\n`
|
|
173
|
+
: '',
|
|
174
|
+
exitCode,
|
|
175
|
+
warnings: survivors.length > 0
|
|
176
|
+
? [`gate process group ${result.pgid} left survivors after SIGKILL: ${survivors.join(', ')}`]
|
|
177
|
+
: [],
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
|
|
15
181
|
// ---------------------------------------------------------------------------
|
|
16
182
|
// Internal pattern helpers
|
|
17
183
|
// ---------------------------------------------------------------------------
|
|
@@ -39,7 +205,8 @@ function isTestFile(filePath) {
|
|
|
39
205
|
// ---------------------------------------------------------------------------
|
|
40
206
|
|
|
41
207
|
/**
|
|
42
|
-
* Execute a shell command
|
|
208
|
+
* Execute a shell command as the LEADER OF ITS OWN PROCESS GROUP, under a
|
|
209
|
+
* wall-clock ceiling, and return a structured result.
|
|
43
210
|
*
|
|
44
211
|
* `output` is a bounded TAIL for humans. `fullOutput` is the complete captured
|
|
45
212
|
* text and is what every COUNT parse must read.
|
|
@@ -52,10 +219,47 @@ function isTestFile(filePath) {
|
|
|
52
219
|
* "the runner never produced results" and hides WHICH test failed. An hour was
|
|
53
220
|
* spent chasing that phantom before the real cause (one red test) was found.
|
|
54
221
|
*
|
|
222
|
+
* ## Why a process group, and why a timeout (Epic #1425 A3)
|
|
223
|
+
*
|
|
224
|
+
* This used to be `execSync(cmd, { maxBuffer })` — no timeout at all, and the
|
|
225
|
+
* shell as the only signalled process. On 2026-09-20 four `tsgo --noEmit`
|
|
226
|
+
* grandchildren of gate runs outlived their parents at PPID 1 with up to 8.0 GB
|
|
227
|
+
* RSS each and took the host to 13 % free memory. {@link spawnInGroup} spawns
|
|
228
|
+
* `detached`, so `process.kill(-pgid, …)` reaches every descendant, and it runs
|
|
229
|
+
* the SIGTERM→grace→SIGKILL ladder with a read-back verification.
|
|
230
|
+
*
|
|
231
|
+
* ## What changed for callers
|
|
232
|
+
*
|
|
233
|
+
* 1. It is ASYNC. Every call site must `await`.
|
|
234
|
+
* 2. `fullOutput` now interleaves stdout AND stderr on the PASS path too
|
|
235
|
+
* (`execSync` discarded stderr when the command succeeded). A runner that
|
|
236
|
+
* prints its summary to stderr is therefore no longer invisible to
|
|
237
|
+
* {@link extractTestCounts}.
|
|
238
|
+
* 3. Three fields are added — `timedOut`, `killSignals`, `survivors` — and they
|
|
239
|
+
* are present ONLY when a process actually ran. A skipped or stubbed command
|
|
240
|
+
* spawned nothing, so it carries no `timedOut: false`: absent is not a
|
|
241
|
+
* measured false, the same contract `counts` and `files` already keep.
|
|
242
|
+
*
|
|
243
|
+
* A timeout is a REPORTED failure: `status: 'fail'`, `exitCode: 124`, and a
|
|
244
|
+
* `gate: TIMEOUT after …` line appended to `output`/`fullOutput` naming the
|
|
245
|
+
* ceiling, the group, the signal ladder and any survivor. It is never a silent
|
|
246
|
+
* `fail`.
|
|
247
|
+
*
|
|
55
248
|
* @param {string} cmd - Shell command to run, or `"skip"` / empty to skip.
|
|
56
|
-
* @
|
|
249
|
+
* @param {object} [opts] - Forwarded verbatim to {@link spawnInGroup}, and it
|
|
250
|
+
* OVERRIDES the defaults below (`timeoutMs`, `maxOutputBytes`, `repoRoot`,
|
|
251
|
+
* `commandSignature`). This is also the seam tests inject `spawnFn` /
|
|
252
|
+
* `killFn` / `isAliveFn` through.
|
|
253
|
+
* @param {number|null} [opts.timeoutMs] - Wall-clock ceiling; defaults to
|
|
254
|
+
* {@link resolveGateTimeoutMs} (`SO_GATE_TIMEOUT_MS` or 900_000). `null`
|
|
255
|
+
* disables the clock — the byte cap still applies.
|
|
256
|
+
* @param {string} [opts.repoRoot] - Root whose gate-process ledger the spawn is
|
|
257
|
+
* recorded in; defaults to `process.cwd()`, which is the tree under test.
|
|
258
|
+
* @returns {Promise<{ status: 'pass'|'fail'|'skip', output: string, fullOutput: string,
|
|
259
|
+
* exitCode: number, timedOut?: boolean, killSignals?: string[], survivors?: number[],
|
|
260
|
+
* stubbed?: { kind: 'echo'|'noop' } }>}
|
|
57
261
|
*/
|
|
58
|
-
export function runCheck(cmd) {
|
|
262
|
+
export async function runCheck(cmd, opts = {}) {
|
|
59
263
|
if (!cmd || cmd === 'skip') {
|
|
60
264
|
return { status: 'skip', output: '', fullOutput: '', exitCode: 0 };
|
|
61
265
|
}
|
|
@@ -65,26 +269,44 @@ export function runCheck(cmd) {
|
|
|
65
269
|
return { status: 'pass', output: `(stubbed: ${stub.kind})`, fullOutput: `(stubbed: ${stub.kind})`, exitCode: 0, stubbed: { kind: stub.kind } };
|
|
66
270
|
}
|
|
67
271
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
}
|
|
272
|
+
const timeoutMs = opts.timeoutMs === undefined ? resolveGateTimeoutMs() : opts.timeoutMs;
|
|
273
|
+
const run = await spawnInGroup(cmd, {
|
|
274
|
+
maxOutputBytes: RUN_CHECK_MAX_BUFFER_BYTES,
|
|
275
|
+
repoRoot: process.cwd(),
|
|
276
|
+
commandSignature: buildCommandSignature(cmd),
|
|
277
|
+
...opts,
|
|
278
|
+
timeoutMs,
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
// Reported in every returned shape below, so a consumer never has to ask a
|
|
282
|
+
// second question to learn whether the group is actually gone.
|
|
283
|
+
const groupFields = { killSignals: run.killSignals, survivors: run.survivors };
|
|
284
|
+
|
|
285
|
+
if (run.timedOut) {
|
|
286
|
+
const fullOutput = `${run.fullOutput}\n${timeoutLine(timeoutMs, run)}\n`;
|
|
287
|
+
return {
|
|
288
|
+
status: 'fail',
|
|
289
|
+
output: tailOf(fullOutput),
|
|
290
|
+
fullOutput,
|
|
291
|
+
exitCode: run.exitCode,
|
|
292
|
+
timedOut: true,
|
|
293
|
+
...groupFields,
|
|
294
|
+
};
|
|
295
|
+
}
|
|
83
296
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
return { status: '
|
|
297
|
+
// Exit code 127 means command not found — treat as skip.
|
|
298
|
+
if (run.exitCode === 127) {
|
|
299
|
+
return { status: 'skip', output: 'command not found', fullOutput: '', exitCode: 127, timedOut: false, ...groupFields };
|
|
87
300
|
}
|
|
301
|
+
|
|
302
|
+
return {
|
|
303
|
+
status: run.exitCode === 0 ? 'pass' : 'fail',
|
|
304
|
+
output: tailOf(run.fullOutput),
|
|
305
|
+
fullOutput: run.fullOutput,
|
|
306
|
+
exitCode: run.exitCode,
|
|
307
|
+
timedOut: false,
|
|
308
|
+
...groupFields,
|
|
309
|
+
};
|
|
88
310
|
}
|
|
89
311
|
|
|
90
312
|
/**
|
|
@@ -13,6 +13,31 @@ const testCmd = process.env.TEST_CMD;
|
|
|
13
13
|
const files = process.env.FILES ?? '';
|
|
14
14
|
const sessionStartRef = process.env.SESSION_START_REF ?? '';
|
|
15
15
|
|
|
16
|
+
/**
|
|
17
|
+
* Per-command wall-clock ceiling, published by `scripts/run-quality-gate.mjs`
|
|
18
|
+
* as `GATE_TIMEOUT_MS` (#1425 A3 / #1432). It carries the ALREADY-RESOLVED
|
|
19
|
+
* value — operator override `SO_GATE_TIMEOUT_MS` > Session Config
|
|
20
|
+
* `gate.timeout-path-b-ms` > 900 000 — so this script only has to read it.
|
|
21
|
+
*
|
|
22
|
+
* Absent or non-numeric (this gate invoked directly, not through the wrapper)
|
|
23
|
+
* → the option is OMITTED, and `runCheck` falls back to `resolveGateTimeoutMs()`
|
|
24
|
+
* exactly as before. An empty object is deliberate: passing `timeoutMs:
|
|
25
|
+
* undefined` would NOT trigger that fallback in every spread order.
|
|
26
|
+
*/
|
|
27
|
+
const CHECK_OPTS = (() => {
|
|
28
|
+
const raw = Number((process.env.GATE_TIMEOUT_MS || '').trim());
|
|
29
|
+
const opts = Number.isFinite(raw) && raw > 0 ? { timeoutMs: raw } : {};
|
|
30
|
+
// GATE_LEDGER_ROOT (#1425 A4, W5 fix-pass): the wrapper publishes the ledger
|
|
31
|
+
// root it resolved (--ledger-root > repo root) so the gate-process register
|
|
32
|
+
// is written where the WRAPPER decided, not where this sub-script happens to
|
|
33
|
+
// run. Without it every runCheck() here defaulted to process.cwd(), and a test
|
|
34
|
+
// that spawned this script from the checkout wrote real lines into the live
|
|
35
|
+
// .orchestrator/runtime/gate-processes.jsonl — the reaper's kill population.
|
|
36
|
+
const ledgerRoot = (process.env.GATE_LEDGER_ROOT || "").trim();
|
|
37
|
+
if (ledgerRoot && ledgerRoot.startsWith("/")) opts.repoRoot = ledgerRoot;
|
|
38
|
+
return opts;
|
|
39
|
+
})();
|
|
40
|
+
|
|
16
41
|
if (!typecheckCmd) {
|
|
17
42
|
process.stderr.write('TYPECHECK_CMD must be set\n');
|
|
18
43
|
process.exit(1);
|
|
@@ -28,7 +53,7 @@ let testStatus;
|
|
|
28
53
|
let errors = [];
|
|
29
54
|
|
|
30
55
|
// --- typecheck (always runs unless cmd is "skip") ---
|
|
31
|
-
const tcResult = await runCheck(typecheckCmd);
|
|
56
|
+
const tcResult = await runCheck(typecheckCmd, CHECK_OPTS);
|
|
32
57
|
const tcStatus = tcResult.status;
|
|
33
58
|
if (tcStatus === 'fail') {
|
|
34
59
|
errors = errors.concat(extractErrorLinesJson(tcResult.output, /error TS\d+/));
|
|
@@ -41,7 +66,7 @@ if (testCmd === 'skip') {
|
|
|
41
66
|
const testFiles = await resolveTestFiles(files, sessionStartRef);
|
|
42
67
|
if (testFiles.length > 0) {
|
|
43
68
|
const fileArgs = testFiles.join(' ');
|
|
44
|
-
const testResult = await runCheck(`${testCmd} -- ${fileArgs}
|
|
69
|
+
const testResult = await runCheck(`${testCmd} -- ${fileArgs}`, CHECK_OPTS);
|
|
45
70
|
testStatus = testResult.status;
|
|
46
71
|
if (testStatus === 'fail') {
|
|
47
72
|
const testErrors = extractErrorLinesJson(testResult.output, /(fail|error|FAIL)/i);
|
|
@@ -49,7 +74,7 @@ if (testCmd === 'skip') {
|
|
|
49
74
|
}
|
|
50
75
|
} else if (!files && !sessionStartRef) {
|
|
51
76
|
// No FILES or SESSION_START_REF supplied: run the full test suite
|
|
52
|
-
const testResult = await runCheck(testCmd);
|
|
77
|
+
const testResult = await runCheck(testCmd, CHECK_OPTS);
|
|
53
78
|
testStatus = testResult.status;
|
|
54
79
|
if (testStatus === 'fail') {
|
|
55
80
|
const testErrors = extractErrorLinesJson(testResult.output, /(fail|error|FAIL)/i);
|
|
@@ -14,6 +14,31 @@ import { runCheck, csvToJsonArray } from './gate-helpers.mjs';
|
|
|
14
14
|
const typecheckCmd = process.env.TYPECHECK_CMD;
|
|
15
15
|
const testCmd = process.env.TEST_CMD;
|
|
16
16
|
|
|
17
|
+
/**
|
|
18
|
+
* Per-command wall-clock ceiling, published by `scripts/run-quality-gate.mjs`
|
|
19
|
+
* as `GATE_TIMEOUT_MS` (#1425 A3 / #1432). It carries the ALREADY-RESOLVED
|
|
20
|
+
* value — operator override `SO_GATE_TIMEOUT_MS` > Session Config
|
|
21
|
+
* `gate.timeout-path-b-ms` > 900 000 — so this script only has to read it.
|
|
22
|
+
*
|
|
23
|
+
* Absent or non-numeric (this gate invoked directly, not through the wrapper)
|
|
24
|
+
* → the option is OMITTED, and `runCheck` falls back to `resolveGateTimeoutMs()`
|
|
25
|
+
* exactly as before. An empty object is deliberate: passing `timeoutMs:
|
|
26
|
+
* undefined` would NOT trigger that fallback in every spread order.
|
|
27
|
+
*/
|
|
28
|
+
const CHECK_OPTS = (() => {
|
|
29
|
+
const raw = Number((process.env.GATE_TIMEOUT_MS || '').trim());
|
|
30
|
+
const opts = Number.isFinite(raw) && raw > 0 ? { timeoutMs: raw } : {};
|
|
31
|
+
// GATE_LEDGER_ROOT (#1425 A4, W5 fix-pass): the wrapper publishes the ledger
|
|
32
|
+
// root it resolved (--ledger-root > repo root) so the gate-process register
|
|
33
|
+
// is written where the WRAPPER decided, not where this sub-script happens to
|
|
34
|
+
// run. Without it every runCheck() here defaulted to process.cwd(), and a test
|
|
35
|
+
// that spawned this script from the checkout wrote real lines into the live
|
|
36
|
+
// .orchestrator/runtime/gate-processes.jsonl — the reaper's kill population.
|
|
37
|
+
const ledgerRoot = (process.env.GATE_LEDGER_ROOT || "").trim();
|
|
38
|
+
if (ledgerRoot && ledgerRoot.startsWith("/")) opts.repoRoot = ledgerRoot;
|
|
39
|
+
return opts;
|
|
40
|
+
})();
|
|
41
|
+
|
|
17
42
|
if (!typecheckCmd) {
|
|
18
43
|
process.stderr.write('gate-per-file: TYPECHECK_CMD must be set\n');
|
|
19
44
|
process.exit(1);
|
|
@@ -33,14 +58,14 @@ if (files.length === 0) {
|
|
|
33
58
|
}
|
|
34
59
|
|
|
35
60
|
// Typecheck runs on the whole project, not per-file
|
|
36
|
-
const tcResult = runCheck(typecheckCmd);
|
|
61
|
+
const tcResult = await runCheck(typecheckCmd, CHECK_OPTS);
|
|
37
62
|
const tcStatus = tcResult.status;
|
|
38
63
|
|
|
39
64
|
let testStatus = 'skip';
|
|
40
65
|
|
|
41
66
|
if (testCmd !== 'skip' && files.length > 0) {
|
|
42
67
|
const fileArgs = files.join(' ');
|
|
43
|
-
const testResult = runCheck(`${testCmd} -- ${fileArgs}
|
|
68
|
+
const testResult = await runCheck(`${testCmd} -- ${fileArgs}`, CHECK_OPTS);
|
|
44
69
|
testStatus = testResult.status;
|
|
45
70
|
}
|
|
46
71
|
|
|
@@ -24,6 +24,7 @@ import { promisify } from 'node:util';
|
|
|
24
24
|
|
|
25
25
|
import { detectVcsForRepo, discoverVaultRepos } from '../gitlab-portfolio/vcs-detect.mjs';
|
|
26
26
|
import { resolveRepoSpec } from '../vcs-repo-spec.mjs';
|
|
27
|
+
import { isMainModule } from '../is-main-module.mjs';
|
|
27
28
|
|
|
28
29
|
const realExecFile = promisify(execFileCb);
|
|
29
30
|
|
|
@@ -455,7 +456,7 @@ export async function main(argv, deps = {}) {
|
|
|
455
456
|
|
|
456
457
|
// ── CLI guard — prevent process.exit during test-time imports ─────────────────
|
|
457
458
|
|
|
458
|
-
if (import.meta.url
|
|
459
|
+
if (isMainModule(import.meta.url)) {
|
|
459
460
|
main(process.argv.slice(2))
|
|
460
461
|
.then((result) => {
|
|
461
462
|
process.exit(result.exitCode);
|
|
@@ -36,6 +36,7 @@ import { expandTilde } from '../common.mjs';
|
|
|
36
36
|
import { discoverVaultRepos } from './vcs-detect.mjs';
|
|
37
37
|
import { fetchIssuesMultiRepo, summarizeRepo } from './aggregator.mjs';
|
|
38
38
|
import { renderPortfolio, writePortfolio } from './markdown-writer.mjs';
|
|
39
|
+
import { isMainModule } from '../is-main-module.mjs';
|
|
39
40
|
|
|
40
41
|
// ── Helpers ───────────────────────────────────────────────────────────────────
|
|
41
42
|
|
|
@@ -406,7 +407,7 @@ EXIT CODES
|
|
|
406
407
|
|
|
407
408
|
// ── CLI guard — prevent process.exit during test-time imports ─────────────────
|
|
408
409
|
|
|
409
|
-
if (import.meta.url
|
|
410
|
+
if (isMainModule(import.meta.url)) {
|
|
410
411
|
main(process.argv.slice(2)).then((result) => {
|
|
411
412
|
process.exit(result.exitCode);
|
|
412
413
|
}).catch((err) => {
|
|
@@ -139,7 +139,12 @@ export function renderPortfolio(summaries, opts) {
|
|
|
139
139
|
// Frontmatter
|
|
140
140
|
lines.push('---');
|
|
141
141
|
lines.push(`_generator: ${GENERATOR_MARKER}`);
|
|
142
|
-
|
|
142
|
+
// `id` + `type` satisfy the vault frontmatter schema (skills/vault-sync/validator.mjs).
|
|
143
|
+
// `type: dashboard` was not in its enum and `id` was missing, so the generated file
|
|
144
|
+
// failed the hard gate at /close (#1144). `board` is the enum member added for
|
|
145
|
+
// generated dashboards (#738) — same shape as vault-status/board-writer.mjs.
|
|
146
|
+
lines.push('id: portfolio');
|
|
147
|
+
lines.push('type: board');
|
|
143
148
|
lines.push(`created: ${createdValue}`);
|
|
144
149
|
lines.push(`updated: ${updatedValue}`);
|
|
145
150
|
lines.push('---');
|