session-orchestrator 5.2.0 → 5.4.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 +5 -1
- package/.agents/skills/autopilot/agents/openai.yaml +5 -0
- package/.agents/skills/bootstrap/SKILL.md +5 -1
- package/.agents/skills/bootstrap/agents/openai.yaml +5 -0
- package/.agents/skills/brainstorm/SKILL.md +5 -1
- package/.agents/skills/brainstorm/agents/openai.yaml +5 -0
- package/.agents/skills/claude-md-drift-check/SKILL.md +4 -2
- package/.agents/skills/close/SKILL.md +5 -1
- package/.agents/skills/close/agents/openai.yaml +5 -0
- package/.agents/skills/convergence-monitoring/SKILL.md +4 -2
- package/.agents/skills/debug/SKILL.md +5 -1
- package/.agents/skills/debug/agents/openai.yaml +5 -0
- package/.agents/skills/discovery/SKILL.md +5 -1
- package/.agents/skills/discovery/agents/openai.yaml +5 -0
- package/.agents/skills/dispatcher/SKILL.md +5 -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 +5 -1
- package/.agents/skills/eli5/agents/openai.yaml +5 -0
- package/.agents/skills/eval/SKILL.md +6 -2
- package/.agents/skills/eval/agents/openai.yaml +5 -0
- package/.agents/skills/evolve/SKILL.md +6 -2
- 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 +5 -1
- package/.agents/skills/go/agents/openai.yaml +5 -0
- package/.agents/skills/grill/SKILL.md +5 -1
- package/.agents/skills/grill/agents/openai.yaml +5 -0
- package/.agents/skills/harness-audit/SKILL.md +5 -1
- 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 +5 -1
- package/.agents/skills/memory-cleanup/agents/openai.yaml +5 -0
- package/.agents/skills/mode-selector/SKILL.md +3 -1
- package/.agents/skills/navigator/SKILL.md +22 -0
- package/.agents/skills/navigator/agents/openai.yaml +5 -0
- 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 +5 -1
- package/.agents/skills/persona-panel/agents/openai.yaml +5 -0
- package/.agents/skills/plan/SKILL.md +5 -1
- package/.agents/skills/plan/agents/openai.yaml +5 -0
- package/.agents/skills/playwright-driver/SKILL.md +3 -1
- package/.agents/skills/portfolio/SKILL.md +5 -1
- package/.agents/skills/portfolio/agents/openai.yaml +5 -0
- package/.agents/skills/quality-gates/SKILL.md +3 -1
- package/.agents/skills/reconcile/SKILL.md +5 -1
- package/.agents/skills/reconcile/agents/openai.yaml +5 -0
- package/.agents/skills/release/SKILL.md +5 -1
- 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 +5 -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 +5 -1
- package/.agents/skills/spinout/agents/openai.yaml +5 -0
- package/.agents/skills/sunset-review/SKILL.md +5 -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 +5 -1
- package/.agents/skills/test/agents/openai.yaml +5 -0
- package/.agents/skills/test-audit/SKILL.md +21 -0
- package/.agents/skills/test-audit/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 +5 -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/claude-md-drift-check/SKILL.md +1 -1
- package/.codex-plugin/skills/convergence-monitoring/SKILL.md +1 -3
- package/.codex-plugin/skills/eval/SKILL.md +1 -1
- package/.codex-plugin/skills/evolve/SKILL.md +1 -1
- package/.codex-plugin/skills/navigator/SKILL.md +22 -0
- package/.codex-plugin/skills/{convergence-monitoring → navigator}/agents/openai.yaml +2 -2
- package/.codex-plugin/skills/npm-publish/SKILL.md +1 -3
- package/.codex-plugin/skills/session/SKILL.md +1 -1
- package/.codex-plugin/skills/test-audit/SKILL.md +21 -0
- package/.codex-plugin/skills/{npm-publish → test-audit}/agents/openai.yaml +1 -1
- package/.cursor/commands/eval.md +1 -1
- package/.cursor/commands/navigator.md +15 -0
- package/.cursor/commands/session.md +1 -1
- package/.cursor/commands/test-audit.md +14 -0
- package/.cursor/rules/000-session-orchestrator.mdc +0 -2
- package/.cursor/rules/050-plan.mdc +1 -1
- package/.cursor/rules/060-evolve.mdc +27 -12
- package/.cursor/skills/claude-md-drift-check/SKILL.md +1 -1
- package/.cursor/skills/convergence-monitoring/SKILL.md +1 -0
- package/.cursor/skills/eval/SKILL.md +1 -1
- package/.cursor/skills/navigator/SKILL.md +13 -0
- package/.cursor/skills/npm-publish/SKILL.md +1 -0
- package/.cursor/skills/test-audit/SKILL.md +12 -0
- package/.cursor-plugin/plugin.json +1 -1
- package/.orchestrator/policy/blocked-commands.json +12 -3
- package/AGENTS.md +9 -30
- package/CHANGELOG.md +206 -0
- package/README.md +13 -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 +7 -3
- package/docs/agent-authoring.md +2 -2
- package/docs/baseline.md +22 -7
- package/docs/ci-setup.md +57 -4
- package/docs/codex-setup.md +14 -2
- package/docs/components.md +12 -10
- package/docs/events-schema.md +65 -15
- package/docs/persona-panel.md +5 -4
- package/docs/rule-authoring.md +84 -17
- package/docs/scope-collision-guard.md +26 -4
- package/docs/session-config-reference.md +154 -12
- package/docs/session-config-template.md +41 -3
- package/docs/telemetry.md +2 -0
- package/hooks/_lib/hook-import-set.json +87 -6
- package/hooks/_lib/subagent-transcript.mjs +582 -31
- 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-cursor.json +5 -0
- package/hooks/hooks-pi.json +5 -0
- package/hooks/hooks.json +6 -1
- package/hooks/loop-guard.mjs +11 -3
- package/hooks/on-session-end.mjs +58 -23
- package/hooks/on-session-start.mjs +101 -11
- package/hooks/on-stop.mjs +170 -22
- package/hooks/operator-steer.mjs +11 -3
- package/hooks/post-bash-issue-budget-refund.mjs +18 -8
- package/hooks/post-bash-write-verify.mjs +3 -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 +202 -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 +15 -2
- package/hooks/pre-bash-destructive-guard.mjs +80 -9
- package/hooks/pre-bash-issue-budget.mjs +16 -11
- package/hooks/pre-bash-memory-propose-audit.mjs +137 -56
- package/hooks/pre-bash-sessions-ledger-guard.mjs +419 -22
- package/hooks/pre-bash-staging-fence.mjs +335 -31
- package/hooks/pre-bash-templates-first.mjs +19 -14
- package/hooks/pre-edit-sessions-ledger-guard.mjs +162 -0
- package/hooks/pre-task-scope-disjoint.mjs +792 -74
- package/hooks/subagent-telemetry.mjs +111 -38
- 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 +3 -3
- package/pi/prompts/eval.md +1 -1
- package/pi/prompts/navigator.md +12 -0
- package/pi/prompts/session.md +1 -1
- package/pi/prompts/test-audit.md +12 -0
- package/rules/README.md +7 -2
- package/rules/_index.md +3 -3
- package/rules/always-on/bash-harness-pitfalls.md +7 -1
- package/rules/opt-in-domain/prompt-caching.md +6 -2
- package/rules/opt-in-stack/backend-data.md +10 -2
- package/rules/opt-in-stack/backend.md +10 -4
- package/rules/opt-in-stack/frontend.md +7 -2
- package/rules/opt-in-stack/security-web.md +10 -4
- package/rules/opt-in-stack/swift.md +6 -2
- package/scripts/apply-session-learnings.mjs +492 -0
- package/scripts/archive-closed-prds.mjs +2 -1
- package/scripts/autopilot.mjs +70 -53
- package/scripts/backfill-abandoned-sessions.mjs +118 -16
- package/scripts/baseline-archetypes.mjs +1 -1
- package/scripts/check-package-manager.mjs +62 -5
- package/scripts/check-sessions-integrity.mjs +300 -0
- package/scripts/cursor-install.mjs +1 -4
- package/scripts/dialectic-deriver.mjs +50 -13
- package/scripts/emit-session.mjs +371 -31
- package/scripts/eval-session.mjs +65 -3
- package/scripts/export-hw-learnings.mjs +22 -3
- package/scripts/fleet-checkin.mjs +147 -0
- package/scripts/generate-agents-skills.mjs +102 -29
- package/scripts/generate-cursor-adapter.mjs +61 -16
- package/scripts/lib/agent-frontmatter.mjs +3 -3
- 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/auto-dream.mjs +7 -6
- package/scripts/lib/autopilot/flags.mjs +18 -2
- package/scripts/lib/autopilot/kill-switches.mjs +6 -3
- package/scripts/lib/autopilot/loop.mjs +19 -2
- package/scripts/lib/autopilot/stall-sampler.mjs +80 -23
- package/scripts/lib/baseline-archetypes.mjs +41 -22
- package/scripts/lib/ci-status-banner.mjs +376 -16
- package/scripts/lib/command-blocker.mjs +275 -28
- package/scripts/lib/config/dialectic.mjs +12 -3
- package/scripts/lib/config/gate.mjs +74 -0
- package/scripts/lib/config/host-paths.mjs +79 -0
- package/scripts/lib/config/persona-gate-wave.mjs +3 -3
- package/scripts/lib/config/reaper.mjs +163 -0
- package/scripts/lib/config-schema.mjs +13 -0
- package/scripts/lib/config.mjs +85 -10
- package/scripts/lib/convergence-monitor.mjs +74 -11
- package/scripts/lib/ecosystem-health.mjs +11 -0
- 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 +545 -7
- package/scripts/lib/fleet-protocol.mjs +270 -0
- package/scripts/lib/frontmatter-guard.mjs +21 -10
- package/scripts/lib/gates/gate-baseline.mjs +40 -5
- package/scripts/lib/gates/gate-full.mjs +35 -5
- package/scripts/lib/gates/gate-helpers.mjs +336 -23
- package/scripts/lib/gates/gate-incremental.mjs +49 -23
- package/scripts/lib/gates/gate-per-file.mjs +42 -8
- package/scripts/lib/gitlab-portfolio/markdown-writer.mjs +6 -1
- package/scripts/lib/instruction-budget-guard.mjs +208 -8
- package/scripts/lib/io.mjs +42 -8
- 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/learnings/expiry-sweep.mjs +34 -3
- package/scripts/lib/learnings/io.mjs +154 -26
- package/scripts/lib/learnings/schema.mjs +163 -15
- package/scripts/lib/learnings/sizing-subject.mjs +2 -2
- package/scripts/lib/maintenance-due-banner.mjs +53 -88
- package/scripts/lib/memory-proposals/schema.mjs +11 -5
- package/scripts/lib/named-baseline-resolver.mjs +53 -28
- package/scripts/lib/named-vault-resolver.mjs +104 -5
- package/scripts/lib/orphan-reaper/defaults.mjs +41 -0
- package/scripts/lib/orphan-reaper/ps-snapshot.mjs +181 -0
- package/scripts/lib/orphan-reaper/reaper-audit.mjs +291 -0
- package/scripts/lib/orphan-reaper/reaper-decide.mjs +395 -0
- package/scripts/lib/orphan-reaper/scan-throttle.mjs +92 -0
- package/scripts/lib/orphan-reaper/scan.mjs +506 -0
- package/scripts/lib/orphan-reaper.mjs +316 -0
- package/scripts/lib/owner-yaml.mjs +104 -6
- package/scripts/lib/peer-cards/merger.mjs +48 -10
- package/scripts/lib/peer-cards/reader.mjs +78 -2
- package/scripts/lib/persona-panel/catalog-loader.mjs +1 -1
- package/scripts/lib/process-group.mjs +950 -0
- package/scripts/lib/project-hygiene.mjs +21 -1
- package/scripts/lib/quality-gate.mjs +152 -33
- package/scripts/lib/reconcile/backlog.mjs +368 -0
- package/scripts/lib/reconcile/engine.mjs +55 -188
- package/scripts/lib/reconcile/rule-expiry-sweep.mjs +302 -60
- 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/resource-probe.mjs +1 -1
- package/scripts/lib/rule-loader.mjs +141 -74
- package/scripts/lib/rules-sync.mjs +27 -12
- package/scripts/lib/scope-baseline.mjs +3 -4
- package/scripts/lib/scope-echo.mjs +39 -2
- package/scripts/lib/scope-gate.mjs +605 -1
- package/scripts/lib/session-close-backfill.mjs +33 -6
- package/scripts/lib/session-id.mjs +9 -20
- package/scripts/lib/session-invocation.mjs +20 -0
- package/scripts/lib/session-record-repair.mjs +183 -26
- package/scripts/lib/session-registry.mjs +27 -2
- package/scripts/lib/session-schema/constants.mjs +56 -2
- package/scripts/lib/session-schema/normalizer.mjs +56 -4
- package/scripts/lib/session-schema/validator.mjs +28 -1
- package/scripts/lib/session-schema.mjs +8 -3
- package/scripts/lib/session-start-probes.mjs +95 -10
- package/scripts/lib/session-token-rollup.mjs +245 -52
- package/scripts/lib/sessions-canonical.mjs +118 -4
- package/scripts/lib/sessions-integrity-banner.mjs +7 -1
- package/scripts/lib/sessions-staleness-banner.mjs +193 -51
- package/scripts/lib/sh-quote.mjs +29 -0
- 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/subagents-schema.mjs +30 -3
- package/scripts/lib/tail-window.mjs +56 -0
- package/scripts/lib/telemetry/pricing.mjs +118 -71
- 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 +140 -23
- package/scripts/lib/tmux-layout/layouts.mjs +1 -11
- package/scripts/lib/tmux-layout/telemetry-stats.mjs +72 -13
- package/scripts/lib/tmux-layout/tmux-shell.mjs +2 -11
- package/scripts/lib/user-invocable-skills.mjs +23 -3
- package/scripts/lib/ux-grill/reconcile.mjs +48 -22
- package/scripts/lib/validate/check-agents-skills.mjs +26 -15
- package/scripts/lib/validate/check-agents.mjs +1 -1
- package/scripts/lib/validate/check-cursor-adapter.mjs +1 -0
- package/scripts/lib/validate/check-entry-guard.mjs +13 -50
- package/scripts/lib/validate/check-hook-entry-guards.mjs +636 -0
- package/scripts/lib/validate/check-hooks-symmetry.mjs +5 -0
- package/scripts/lib/validate/check-learnings-shell-writes.mjs +214 -0
- 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 +9 -1
- package/scripts/lib/validate/check-skill-script-paths.mjs +239 -27
- package/scripts/lib/validate/check-test-git-config-target.mjs +24 -34
- package/scripts/lib/validate/check-test-value-bans.mjs +87 -14
- package/scripts/lib/validate/check-unicode-safety.mjs +1 -0
- package/scripts/lib/validate/check-untracked-test-deps.mjs +7 -102
- package/scripts/lib/validate/check-unwired-features.mjs +130 -27
- package/scripts/lib/validate/check-validator-registration.mjs +34 -10
- package/scripts/lib/validate/confidential-names.mjs +10 -0
- package/scripts/lib/validate-vendored-rules.mjs +210 -84
- 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-mirror/telemetry.mjs +8 -1
- package/scripts/lib/vault-status/board-writer.mjs +48 -11
- package/scripts/lib/vault-status/narrative-mirror.mjs +71 -16
- package/scripts/lib/vault-yaml.mjs +118 -0
- package/scripts/lib/wave-executor/dispatch-common.mjs +10 -18
- package/scripts/lib/wave-executor/foreign-dispatch.mjs +1 -1
- package/scripts/lib/wave-executor/remote-dispatch.mjs +1 -1
- package/scripts/lib/wave-resource-gate.mjs +34 -8
- package/scripts/lib/worktree/lifecycle.mjs +153 -1
- package/scripts/memory-propose.mjs +18 -5
- package/scripts/release-session-lock.mjs +306 -0
- package/scripts/release.mjs +40 -8
- package/scripts/repair-invalid-sessions.mjs +37 -4
- package/scripts/resolve-session-invocation.mjs +59 -0
- package/scripts/run-migrate-v2-cross-repo.mjs +158 -30
- package/scripts/run-quality-gate.mjs +357 -197
- package/scripts/site-numbers.mjs +163 -5
- package/scripts/sweep-expired-learnings.mjs +56 -5
- package/scripts/sweep-expired-rules.mjs +16 -4
- package/scripts/validate-plugin.mjs +19 -0
- package/scripts/validate-wave-scope.mjs +32 -105
- package/scripts/vault-integration-watcher.mjs +2 -1
- package/scripts/vault-mirror.mjs +81 -47
- package/scripts/wave-scope-binding.mjs +2 -1
- package/skills/_shared/config-reading.md +3 -3
- package/skills/_shared/fleet-protocol.md +131 -0
- package/skills/_shared/platform-tools.md +23 -11
- package/skills/_shared/state-ownership.md +5 -0
- package/skills/autopilot/SKILL.md +47 -18
- package/skills/bootstrap/SKILL.md +1 -1
- package/skills/bootstrap/_shared-template.md +1 -1
- package/skills/bootstrap/fast-template.md +1 -1
- package/skills/bootstrap/private-contract.md +2 -1
- package/skills/bootstrap/public-fallback.md +6 -3
- package/skills/bootstrap/references/bootstrap-retroactive-flow.md +2 -1
- package/skills/claude-md-drift-check/SKILL.md +10 -10
- package/skills/claude-md-drift-check/checker.mjs +26 -18
- 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/eval/SKILL.md +39 -24
- package/skills/eval/rubric-v1.md +1 -0
- package/skills/eval/rubric-v2.md +457 -0
- package/skills/evolve/SKILL.md +3 -3
- package/skills/evolve/references/evolve-analyze-mode.md +10 -6
- package/skills/evolve/references/evolve-dialectic-mode.md +42 -25
- package/skills/gitlab-ops/SKILL.md +3 -2
- package/skills/memory-cleanup/SKILL.md +1 -1
- package/skills/navigator/SKILL.md +193 -0
- package/skills/navigator/references/account-check.sh +87 -0
- package/skills/navigator/references/fleet-decisions.mjs +231 -0
- package/skills/navigator/references/slot-check.sh +114 -0
- package/skills/npm-publish/SKILL.md +1 -1
- package/skills/persona-panel/SKILL.md +2 -2
- package/skills/persona-panel/persona-format.md +2 -2
- package/skills/persona-panel/presets/designer-lens.md +1 -1
- package/skills/persona-panel/presets/engineer-lens.md +1 -1
- package/skills/persona-panel/presets/pm-lens.md +1 -1
- package/skills/plan/mode-retro.md +19 -4
- package/skills/quality-gates/SKILL.md +6 -4
- package/skills/reconcile/SKILL.md +11 -0
- package/skills/remote-offload/SKILL.md +4 -2
- package/skills/session-end/SKILL.md +13 -16
- package/skills/session-end/discovery-scan.md +1 -1
- package/skills/session-end/learning-patterns.md +22 -12
- package/skills/session-end/metrics-collection.md +5 -6
- package/skills/session-end/phase-3-6-tail.md +55 -9
- package/skills/session-end/references/phase-3-documentation-updates.md +2 -2
- package/skills/session-end/references/phase-4a-worktree-cleanup.md +2 -0
- package/skills/session-end/references/phase-5-issue-cleanup.md +9 -14
- package/skills/session-end/references/session-summary-template.md +1 -1
- package/skills/session-end/session-metrics-write.md +32 -60
- package/skills/session-plan/SKILL.md +17 -8
- package/skills/session-plan/references/session-plan-task-classification.md +2 -2
- package/skills/session-start/SKILL.md +6 -0
- package/skills/session-start/references/operations-contract.md +4 -0
- package/skills/session-start/references/phase-1-7-vault-status-board.md +1 -1
- package/skills/session-start/references/phase-4-ssot-environment-check.md +4 -3
- package/skills/test-audit/SKILL.md +152 -0
- package/skills/test-audit/references/campaign.md +111 -0
- package/skills/test-audit/references/mutate.sh +50 -0
- package/skills/ux-grill/SKILL.md +1 -1
- package/skills/wave-executor/SKILL.md +9 -5
- package/skills/wave-executor/circuit-breaker.md +2 -0
- package/skills/wave-executor/references/wave-executor-state-init.md +8 -3
- package/skills/wave-executor/references/wave-loop-dispatch.md +5 -2
- package/skills/wave-executor/references/wave-loop-review.md +3 -5
- package/templates/personas/README.md +5 -5
- package/templates/personas/accounting-compliance.v1.md +1 -1
- package/templates/personas/accounting-tax-advisor.v1.md +1 -1
- package/templates/personas/buyer-p1-cto.v1.md +1 -1
- package/templates/personas/buyer-p2-kanzlei.v1.md +1 -1
- package/templates/personas/buyer-p3-build.v1.md +1 -1
- package/templates/personas/buyer-p4-tech-veto.v1.md +1 -1
- package/templates/personas/buyer-p5-solo.v1.md +1 -1
- package/templates/personas/buyer-p6-ld.v1.md +1 -1
- package/templates/personas/klima-ai-expert.v1.md +1 -1
- package/templates/personas/klima-physicist.v1.md +1 -1
- package/.cursor/commands/convergence-monitoring.md +0 -13
- package/.cursor/commands/npm-publish.md +0 -13
- package/pi/prompts/convergence-monitoring.md +0 -11
- package/pi/prompts/npm-publish.md +0 -11
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* fleet-protocol.mjs — Fleet protocol v1: file-based navigator lease + session
|
|
3
|
+
* check-ins under `~/.config/navigator/` (#1462, PRD !53 § 3).
|
|
4
|
+
*
|
|
5
|
+
* The protocol state lives in plain files so every platform (Claude Code, Codex,
|
|
6
|
+
* a headless run) can read and write it without a server:
|
|
7
|
+
*
|
|
8
|
+
* <navigatorDir>/leases/navigator.json — the navigator lease
|
|
9
|
+
* <navigatorDir>/checkin/<session_id>.json — one check-in per session
|
|
10
|
+
* <navigatorDir>/auflagen/<session_id>.json — conditions issued to a session
|
|
11
|
+
*
|
|
12
|
+
* `<navigatorDir>` is `NAVIGATOR_CONFIG_DIR` (trimmed; empty/whitespace counts as
|
|
13
|
+
* unset) or `<home>/.config/navigator`. Files are 0600, directories 0700,
|
|
14
|
+
* timestamps `date -u +%FT%TZ` (`YYYY-MM-DDTHH:MM:SSZ`).
|
|
15
|
+
*
|
|
16
|
+
* "A navigator is active" means exactly one thing: a readable, well-formed lease
|
|
17
|
+
* whose `laeuft_ab` lies in the future. Everything else is fail-closed — an
|
|
18
|
+
* expired lease is `none`, a lease that cannot be read or validated is
|
|
19
|
+
* `unreadable`, and neither is ever `active`.
|
|
20
|
+
*
|
|
21
|
+
* Session ids become file names, so {@link isSafeSessionId} is the security
|
|
22
|
+
* boundary between check-in input and the filesystem: every path builder that
|
|
23
|
+
* takes a session id refuses an unsafe one with a TypeError.
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
import { promises as fs } from 'node:fs';
|
|
27
|
+
import os from 'node:os';
|
|
28
|
+
import path from 'node:path';
|
|
29
|
+
|
|
30
|
+
/** Env var that overrides the navigator directory. */
|
|
31
|
+
export const NAVIGATOR_CONFIG_DIR_ENV = 'NAVIGATOR_CONFIG_DIR';
|
|
32
|
+
|
|
33
|
+
/** Minutes a session waits for a navigator before it falls back (PRD !53 § 3). */
|
|
34
|
+
export const CHECKIN_FALLBACK_MIN = 10;
|
|
35
|
+
|
|
36
|
+
const SESSION_ID_RE = /^[A-Za-z0-9._-]+$/;
|
|
37
|
+
const SESSION_ID_MAX = 128;
|
|
38
|
+
const UTC_SECONDS_RE = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z$/;
|
|
39
|
+
|
|
40
|
+
/** Lease timestamps: UTC with `Z`, seconds or milliseconds — never a local-time reading. */
|
|
41
|
+
const LEASE_TS_RE = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{1,3})?Z$/;
|
|
42
|
+
|
|
43
|
+
const PLATTFORMEN = ['claude', 'codex', 'kopflos'];
|
|
44
|
+
const MODI = ['housekeeping', 'feature', 'deep', 'operations'];
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Resolve the navigator directory.
|
|
48
|
+
* @param {{ env?: Record<string, string|undefined>, home?: string }} [opts]
|
|
49
|
+
* @returns {string} absolute path
|
|
50
|
+
*/
|
|
51
|
+
export function navigatorDir({ env = process.env, home = os.homedir() } = {}) {
|
|
52
|
+
const raw = env[NAVIGATOR_CONFIG_DIR_ENV];
|
|
53
|
+
const override = typeof raw === 'string' ? raw.trim() : '';
|
|
54
|
+
if (override) return path.resolve(override);
|
|
55
|
+
return path.join(home, '.config', 'navigator');
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Path of the navigator lease file.
|
|
60
|
+
* @param {{ env?: Record<string, string|undefined>, home?: string }} [opts]
|
|
61
|
+
* @returns {string}
|
|
62
|
+
*/
|
|
63
|
+
export function leasePath(opts) {
|
|
64
|
+
return path.join(navigatorDir(opts), 'leases', 'navigator.json');
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* True when `id` is safe to use as a single file-name component: only
|
|
69
|
+
* `[A-Za-z0-9._-]`, 1..128 chars, and not `.` / `..`. This excludes `/`, `\`,
|
|
70
|
+
* NUL and every other separator by construction.
|
|
71
|
+
* @param {unknown} id
|
|
72
|
+
* @returns {boolean}
|
|
73
|
+
*/
|
|
74
|
+
export function isSafeSessionId(id) {
|
|
75
|
+
return (
|
|
76
|
+
typeof id === 'string' &&
|
|
77
|
+
id.length > 0 &&
|
|
78
|
+
id.length <= SESSION_ID_MAX &&
|
|
79
|
+
id !== '.' &&
|
|
80
|
+
id !== '..' &&
|
|
81
|
+
SESSION_ID_RE.test(id)
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/** @param {unknown} sessionId */
|
|
86
|
+
function assertSafeSessionId(sessionId) {
|
|
87
|
+
if (!isSafeSessionId(sessionId)) {
|
|
88
|
+
throw new TypeError(`unsafe session id: ${JSON.stringify(String(sessionId)).slice(0, 80)}`);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Path of a session's check-in file.
|
|
94
|
+
* @param {string} sessionId
|
|
95
|
+
* @param {{ env?: Record<string, string|undefined>, home?: string }} [opts]
|
|
96
|
+
* @returns {string}
|
|
97
|
+
* @throws {TypeError} when `sessionId` fails {@link isSafeSessionId}
|
|
98
|
+
*/
|
|
99
|
+
export function checkinPath(sessionId, opts) {
|
|
100
|
+
assertSafeSessionId(sessionId);
|
|
101
|
+
return path.join(navigatorDir(opts), 'checkin', `${sessionId}.json`);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Path of a session's conditions ("Auflagen") file.
|
|
106
|
+
* @param {string} sessionId
|
|
107
|
+
* @param {{ env?: Record<string, string|undefined>, home?: string }} [opts]
|
|
108
|
+
* @returns {string}
|
|
109
|
+
* @throws {TypeError} when `sessionId` fails {@link isSafeSessionId}
|
|
110
|
+
*/
|
|
111
|
+
export function auflagenPath(sessionId, opts) {
|
|
112
|
+
assertSafeSessionId(sessionId);
|
|
113
|
+
return path.join(navigatorDir(opts), 'auflagen', `${sessionId}.json`);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Format a date as `YYYY-MM-DDTHH:MM:SSZ` (the output of `date -u +%FT%TZ`).
|
|
118
|
+
* @param {Date} [date]
|
|
119
|
+
* @returns {string}
|
|
120
|
+
*/
|
|
121
|
+
export function utcSecondsTimestamp(date = new Date()) {
|
|
122
|
+
return date.toISOString().replace(/\.\d{3}Z$/, 'Z');
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/** @param {unknown} v */
|
|
126
|
+
function isPlainObject(v) {
|
|
127
|
+
return v !== null && typeof v === 'object' && !Array.isArray(v);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/** @param {unknown} v */
|
|
131
|
+
function isNonEmptyString(v) {
|
|
132
|
+
return typeof v === 'string' && v.length > 0;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/** @param {unknown} v */
|
|
136
|
+
function isIsoTimestamp(v) {
|
|
137
|
+
return typeof v === 'string' && !Number.isNaN(Date.parse(v));
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function isLeaseTimestamp(v) {
|
|
141
|
+
return typeof v === 'string' && LEASE_TS_RE.test(v) && isIsoTimestamp(v);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const REQUIRED_CHECKIN_FIELDS = [
|
|
145
|
+
'session', 'plattform', 'repo', 'modus', 'auftrag_ref',
|
|
146
|
+
'kandidaten', 'schreibbereich', 'rueckfall', 'zeit',
|
|
147
|
+
];
|
|
148
|
+
|
|
149
|
+
/** Repo name shape accepted in a check-in: `name` or `group/name`, no path parts. */
|
|
150
|
+
const REPO_NAME_RE = /^[A-Za-z0-9._-]{1,100}(?:\/[A-Za-z0-9._-]{1,100})?$/;
|
|
151
|
+
|
|
152
|
+
/** Lease `adresse` shape (a ListAgents peer name): no whitespace, no control bytes. */
|
|
153
|
+
const ADRESSE_RE = /^[A-Za-z0-9._:@-]{1,128}$/;
|
|
154
|
+
|
|
155
|
+
/** True for an issue reference: a non-negative integer or a `#123` / `123` string. */
|
|
156
|
+
function isIssueRef(v) {
|
|
157
|
+
if (typeof v === 'number') return Number.isInteger(v) && v >= 0;
|
|
158
|
+
return typeof v === 'string' && /^#?\d{1,9}$/.test(v);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Per-field type checks. Each returns an error message or null. Applied to every
|
|
163
|
+
* PRESENT field; presence of the required ones is checked separately.
|
|
164
|
+
* @type {Record<string, (v: unknown) => string|null>}
|
|
165
|
+
*/
|
|
166
|
+
const CHECKIN_FIELD_CHECKS = {
|
|
167
|
+
session: (v) =>
|
|
168
|
+
isSafeSessionId(v) ? null : 'session must be a safe id: [A-Za-z0-9._-], 1..128 chars, not . or ..',
|
|
169
|
+
plattform: (v) => (PLATTFORMEN.includes(/** @type {string} */ (v)) ? null : `plattform must be one of ${PLATTFORMEN.join('|')}`),
|
|
170
|
+
// A repo NAME (optionally `group/name`), never a filesystem path: the value
|
|
171
|
+
// travels in `orchestrator.fleet.checkin`, which may leave the host over the
|
|
172
|
+
// optional events webhook (docs/events-schema.md: "never the absolute root").
|
|
173
|
+
repo: (v) => (typeof v === 'string' && REPO_NAME_RE.test(v) ? null : 'repo must be a repo name [A-Za-z0-9._-] (optionally group/name), not a path'),
|
|
174
|
+
repo_id: (v) => (v === null || typeof v === 'string' ? null : 'repo_id must be a string or null'),
|
|
175
|
+
worktree: (v) => (typeof v === 'boolean' ? null : 'worktree must be a boolean'),
|
|
176
|
+
modus: (v) => (MODI.includes(/** @type {string} */ (v)) ? null : `modus must be one of ${MODI.join('|')}`),
|
|
177
|
+
auftrag_ref: (v) => (isNonEmptyString(v) ? null : 'auftrag_ref must be a non-empty string'),
|
|
178
|
+
konto_slot: (v) =>
|
|
179
|
+
v === null || typeof v === 'string' || (typeof v === 'number' && Number.isFinite(v))
|
|
180
|
+
? null
|
|
181
|
+
: 'konto_slot must be a number, a string or null',
|
|
182
|
+
quota: (v) => (typeof v === 'string' ? null : 'quota must be a string'),
|
|
183
|
+
stand: (v) => (isPlainObject(v) ? null : 'stand must be an object'),
|
|
184
|
+
// Issue numbers only (42 or "#42"), at most 64 — same webhook reasoning as `repo`.
|
|
185
|
+
kandidaten: (v) =>
|
|
186
|
+
Array.isArray(v) && v.length <= 64 && v.every(isIssueRef)
|
|
187
|
+
? null
|
|
188
|
+
: 'kandidaten must be an array of at most 64 issue numbers (42 or "#42")',
|
|
189
|
+
schreibbereich: (v) =>
|
|
190
|
+
Array.isArray(v) && v.every((e) => typeof e === 'string') ? null : 'schreibbereich must be an array of strings',
|
|
191
|
+
bedarf: (v) => (isPlainObject(v) ? null : 'bedarf must be an object'),
|
|
192
|
+
vorab_geschrieben: (v) => (Array.isArray(v) ? null : 'vorab_geschrieben must be an array'),
|
|
193
|
+
rueckfall: (v) => (isNonEmptyString(v) ? null : 'rueckfall must be a non-empty string'),
|
|
194
|
+
zeit: (v) =>
|
|
195
|
+
typeof v === 'string' && UTC_SECONDS_RE.test(v) && isIsoTimestamp(v)
|
|
196
|
+
? null
|
|
197
|
+
: 'zeit must be a UTC timestamp YYYY-MM-DDTHH:MM:SSZ',
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Validate a check-in record (PRD !53 table 3.2).
|
|
202
|
+
* @param {unknown} obj
|
|
203
|
+
* @returns {string[]} error messages; empty means valid
|
|
204
|
+
*/
|
|
205
|
+
export function validateCheckin(obj) {
|
|
206
|
+
if (!isPlainObject(obj)) return ['check-in must be a JSON object'];
|
|
207
|
+
const rec = /** @type {Record<string, unknown>} */ (obj);
|
|
208
|
+
const errors = [];
|
|
209
|
+
for (const field of REQUIRED_CHECKIN_FIELDS) {
|
|
210
|
+
if (rec[field] === undefined) errors.push(`missing required field: ${field}`);
|
|
211
|
+
}
|
|
212
|
+
for (const [field, check] of Object.entries(CHECKIN_FIELD_CHECKS)) {
|
|
213
|
+
if (rec[field] === undefined) continue;
|
|
214
|
+
const err = check(rec[field]);
|
|
215
|
+
if (err) errors.push(err);
|
|
216
|
+
}
|
|
217
|
+
return errors;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Validate a parsed lease; returns the first problem or null.
|
|
222
|
+
* @param {unknown} lease
|
|
223
|
+
* @returns {string|null}
|
|
224
|
+
*/
|
|
225
|
+
function leaseProblem(lease) {
|
|
226
|
+
if (!isPlainObject(lease)) return 'lease is not a JSON object';
|
|
227
|
+
const l = /** @type {Record<string, unknown>} */ (lease);
|
|
228
|
+
// Shape-checked at the source ("escaped at generation"): both values can reach
|
|
229
|
+
// the operator's session-start banner, so control bytes are refused here.
|
|
230
|
+
if (!isSafeSessionId(l.session_id)) return 'lease.session_id missing or not a safe id';
|
|
231
|
+
if (!PLATTFORMEN.includes(/** @type {string} */ (l.plattform))) return `lease.plattform must be one of ${PLATTFORMEN.join('|')}`;
|
|
232
|
+
if (l.adresse !== undefined && l.adresse !== null && !(typeof l.adresse === 'string' && ADRESSE_RE.test(l.adresse))) {
|
|
233
|
+
return 'lease.adresse must be null or a peer name [A-Za-z0-9._:@-] (1..128 chars)';
|
|
234
|
+
}
|
|
235
|
+
if (l.uebergabe_an !== undefined && l.uebergabe_an !== null && typeof l.uebergabe_an !== 'string') {
|
|
236
|
+
return 'lease.uebergabe_an must be a string or null';
|
|
237
|
+
}
|
|
238
|
+
// Strict UTC form: a `Z`-less value would be read in the host's local time and
|
|
239
|
+
// could EXTEND the lease west of UTC — a fail-open reading, hence unreadable.
|
|
240
|
+
if (!isLeaseTimestamp(l.seit)) return 'lease.seit missing or not a UTC timestamp (…Z)';
|
|
241
|
+
if (!isLeaseTimestamp(l.laeuft_ab)) return 'lease.laeuft_ab missing or not a UTC timestamp (…Z)';
|
|
242
|
+
return null;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Read the navigator lease. Never throws; fail-closed.
|
|
247
|
+
*
|
|
248
|
+
* @param {{ now?: number, env?: Record<string, string|undefined>, home?: string }} [opts]
|
|
249
|
+
* @returns {Promise<{ state: 'active', lease: object } | { state: 'none' } | { state: 'unreadable', reason: string }>}
|
|
250
|
+
*/
|
|
251
|
+
export async function readNavigatorLease({ now = Date.now(), env, home } = {}) {
|
|
252
|
+
let raw;
|
|
253
|
+
try {
|
|
254
|
+
raw = await fs.readFile(leasePath({ env, home }), 'utf8');
|
|
255
|
+
} catch (err) {
|
|
256
|
+
if (err && /** @type {NodeJS.ErrnoException} */ (err).code === 'ENOENT') return { state: 'none' };
|
|
257
|
+
const code = err && /** @type {NodeJS.ErrnoException} */ (err).code;
|
|
258
|
+
return { state: 'unreadable', reason: `read failed: ${code || 'unknown error'}` };
|
|
259
|
+
}
|
|
260
|
+
let lease;
|
|
261
|
+
try {
|
|
262
|
+
lease = JSON.parse(raw);
|
|
263
|
+
} catch {
|
|
264
|
+
return { state: 'unreadable', reason: 'lease is not valid JSON' };
|
|
265
|
+
}
|
|
266
|
+
const problem = leaseProblem(lease);
|
|
267
|
+
if (problem) return { state: 'unreadable', reason: problem };
|
|
268
|
+
if (Date.parse(lease.laeuft_ab) <= now) return { state: 'none' };
|
|
269
|
+
return { state: 'active', lease };
|
|
270
|
+
}
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
|
|
13
13
|
import { digestSha256Short } from './crypto-digest-utils.mjs';
|
|
14
14
|
import { resolveHostPath } from './config/host-paths.mjs';
|
|
15
|
+
import { maskSource } from './js-mask.mjs';
|
|
15
16
|
import { readFileSync, statSync } from 'node:fs';
|
|
16
17
|
import { homedir } from 'node:os';
|
|
17
18
|
import { dirname, join, resolve } from 'node:path';
|
|
@@ -117,25 +118,34 @@ let _warnedFallback = false;
|
|
|
117
118
|
*/
|
|
118
119
|
const _cache = { mtime: null, result: null };
|
|
119
120
|
|
|
121
|
+
/** One quoted string literal — its interior in group 2. */
|
|
122
|
+
const STRING_LITERAL_RE = /(['"`])((?:\\.|(?!\1)[^\\\n])*)\1/g;
|
|
123
|
+
|
|
120
124
|
/**
|
|
121
|
-
* Parse the
|
|
125
|
+
* Parse the enum values out of a Zod `z.enum([...])` call in source text.
|
|
122
126
|
*
|
|
123
|
-
*
|
|
127
|
+
* `codeText` must be the source with its COMMENTS blanked
|
|
128
|
+
* (`maskSource(text, { keepLiterals: true })`): the values are the quoted
|
|
129
|
+
* literals of the enum body and nothing else. Splitting the raw body on commas
|
|
130
|
+
* rendered comment prose between the literals as enum values — 6 of 16
|
|
131
|
+
* `statusEnum` entries against the real baseline schema, one fused onto
|
|
132
|
+
* `'maintenance'` (#1298) — and every one reached agent prompts as an allowed
|
|
133
|
+
* `status`. Masking via the shared lexer also keeps a `//` inside a literal
|
|
134
|
+
* intact and a `]` inside a comment from ending the body early.
|
|
135
|
+
*
|
|
136
|
+
* @param {string} codeText - comment-masked source
|
|
124
137
|
* @param {string} exportName - e.g. 'vaultNoteTypeSchema'
|
|
125
138
|
* @returns {string[]}
|
|
126
139
|
*/
|
|
127
|
-
function _extractEnum(
|
|
140
|
+
function _extractEnum(codeText, exportName) {
|
|
128
141
|
// Match: export const <name> = z.enum([ ...values... ]);
|
|
129
142
|
const re = new RegExp(
|
|
130
143
|
`export\\s+const\\s+${exportName}\\s*=\\s*z\\.enum\\(\\s*\\[([^\\]]+)\\]`,
|
|
131
144
|
's',
|
|
132
145
|
);
|
|
133
|
-
const match =
|
|
146
|
+
const match = codeText.match(re);
|
|
134
147
|
if (!match) return [];
|
|
135
|
-
return match[1]
|
|
136
|
-
.split(',')
|
|
137
|
-
.map((s) => s.trim().replace(/^['"]|['"]$/g, ''))
|
|
138
|
-
.filter(Boolean);
|
|
148
|
+
return [...match[1].matchAll(STRING_LITERAL_RE)].map((m) => m[2]).filter(Boolean);
|
|
139
149
|
}
|
|
140
150
|
|
|
141
151
|
/**
|
|
@@ -145,8 +155,9 @@ function _extractEnum(text, exportName) {
|
|
|
145
155
|
* @returns {{ typeEnum: string[], statusEnum: string[], requiredFields: string[], idRegex: string, tagsRegex: string, schemaText: string }}
|
|
146
156
|
*/
|
|
147
157
|
function _parseSchema(text) {
|
|
148
|
-
const
|
|
149
|
-
const
|
|
158
|
+
const codeText = maskSource(text, { keepLiterals: true });
|
|
159
|
+
const typeEnum = _extractEnum(codeText, 'vaultNoteTypeSchema');
|
|
160
|
+
const statusEnum = _extractEnum(codeText, 'vaultNoteStatusSchema');
|
|
150
161
|
|
|
151
162
|
// Required fields are id, type, created, updated — stable; extracted
|
|
152
163
|
// from the schema object declaration (non-optional fields).
|
|
@@ -5,11 +5,36 @@
|
|
|
5
5
|
// Required env: TYPECHECK_CMD, TEST_CMD
|
|
6
6
|
// Each may equal "skip" or empty string → treated as skip.
|
|
7
7
|
|
|
8
|
-
import { runCheck } from './gate-helpers.mjs';
|
|
8
|
+
import { runCheck, commandKillFields } from './gate-helpers.mjs';
|
|
9
9
|
|
|
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,17 +48,27 @@ 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 = {
|
|
34
59
|
variant: 'baseline',
|
|
35
|
-
|
|
36
|
-
|
|
60
|
+
// Each command object also carries its INNER kill ladder (`timed_out`,
|
|
61
|
+
// `kill_signals`, `survivors` pids) when `runCheck` spawned it — omitted for
|
|
62
|
+
// skip/stub (#1457). `run-quality-gate.mjs` joins them into its event.
|
|
63
|
+
typecheck: { status: tcResult.status, ...commandKillFields(tcResult) },
|
|
64
|
+
test: { status: testResult.status, ...commandKillFields(testResult) },
|
|
65
|
+
// Stub map, same shape as gate-full: a command `runCheck` recognised as a
|
|
66
|
+
// stub (`echo …`/`true`) spawned nothing, so `joinGateKillFields` may count
|
|
67
|
+
// it as measured instead of declaring the whole inner ladder unknown.
|
|
68
|
+
stubbed: {
|
|
69
|
+
...(tcResult.stubbed ? { typecheck: tcResult.stubbed } : {}),
|
|
70
|
+
...(testResult.stubbed ? { test: testResult.stubbed } : {}),
|
|
71
|
+
},
|
|
37
72
|
typecheck_output: tcResult.output,
|
|
38
73
|
test_output: testResult.output,
|
|
39
74
|
};
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
extractTestCounts,
|
|
12
12
|
extractFailedTestFiles,
|
|
13
13
|
collectDebugArtifacts,
|
|
14
|
+
commandKillFields,
|
|
14
15
|
} from './gate-helpers.mjs';
|
|
15
16
|
|
|
16
17
|
const typecheckCmd = process.env.TYPECHECK_CMD;
|
|
@@ -18,6 +19,31 @@ const testCmd = process.env.TEST_CMD;
|
|
|
18
19
|
const lintCmd = process.env.LINT_CMD;
|
|
19
20
|
const sessionStartRef = process.env.SESSION_START_REF ?? '';
|
|
20
21
|
|
|
22
|
+
/**
|
|
23
|
+
* Per-command wall-clock ceiling, published by `scripts/run-quality-gate.mjs`
|
|
24
|
+
* as `GATE_TIMEOUT_MS` (#1425 A3 / #1432). It carries the ALREADY-RESOLVED
|
|
25
|
+
* value — operator override `SO_GATE_TIMEOUT_MS` > Session Config
|
|
26
|
+
* `gate.timeout-path-b-ms` > 900 000 — so this script only has to read it.
|
|
27
|
+
*
|
|
28
|
+
* Absent or non-numeric (this gate invoked directly, not through the wrapper)
|
|
29
|
+
* → the option is OMITTED, and `runCheck` falls back to `resolveGateTimeoutMs()`
|
|
30
|
+
* exactly as before. An empty object is deliberate: passing `timeoutMs:
|
|
31
|
+
* undefined` would NOT trigger that fallback in every spread order.
|
|
32
|
+
*/
|
|
33
|
+
const CHECK_OPTS = (() => {
|
|
34
|
+
const raw = Number((process.env.GATE_TIMEOUT_MS || '').trim());
|
|
35
|
+
const opts = Number.isFinite(raw) && raw > 0 ? { timeoutMs: raw } : {};
|
|
36
|
+
// GATE_LEDGER_ROOT (#1425 A4, W5 fix-pass): the wrapper publishes the ledger
|
|
37
|
+
// root it resolved (--ledger-root > repo root) so the gate-process register
|
|
38
|
+
// is written where the WRAPPER decided, not where this sub-script happens to
|
|
39
|
+
// run. Without it every runCheck() here defaulted to process.cwd(), and a test
|
|
40
|
+
// that spawned this script from the checkout wrote real lines into the live
|
|
41
|
+
// .orchestrator/runtime/gate-processes.jsonl — the reaper's kill population.
|
|
42
|
+
const ledgerRoot = (process.env.GATE_LEDGER_ROOT || "").trim();
|
|
43
|
+
if (ledgerRoot && ledgerRoot.startsWith("/")) opts.repoRoot = ledgerRoot;
|
|
44
|
+
return opts;
|
|
45
|
+
})();
|
|
46
|
+
|
|
21
47
|
if (!typecheckCmd) {
|
|
22
48
|
process.stderr.write('TYPECHECK_CMD must be set\n');
|
|
23
49
|
process.exit(1);
|
|
@@ -34,7 +60,7 @@ if (!lintCmd) {
|
|
|
34
60
|
const startTime = Date.now();
|
|
35
61
|
|
|
36
62
|
// --- Typecheck ---
|
|
37
|
-
const tcResult = runCheck(typecheckCmd);
|
|
63
|
+
const tcResult = await runCheck(typecheckCmd, CHECK_OPTS);
|
|
38
64
|
const tcErrorCount =
|
|
39
65
|
tcResult.status === 'fail'
|
|
40
66
|
? extractCount(tcResult.fullOutput ?? tcResult.output, /error TS\d+/)
|
|
@@ -44,7 +70,7 @@ const tcErrorCount =
|
|
|
44
70
|
// NOTE: `testCounts`, NOT `failed` — a local `failed` is already bound near
|
|
45
71
|
// the bottom of this file and drives `process.exit(failed ? 2 : 0)`.
|
|
46
72
|
// Shadowing it would corrupt the gate's exit code.
|
|
47
|
-
const testResult = runCheck(testCmd);
|
|
73
|
+
const testResult = await runCheck(testCmd, CHECK_OPTS);
|
|
48
74
|
const testCounts =
|
|
49
75
|
testResult.status !== 'skip'
|
|
50
76
|
? extractTestCounts(testResult.fullOutput ?? testResult.output)
|
|
@@ -98,7 +124,7 @@ const fileFields = testCounts.files
|
|
|
98
124
|
: {};
|
|
99
125
|
|
|
100
126
|
// --- Lint ---
|
|
101
|
-
const lintResult = runCheck(lintCmd);
|
|
127
|
+
const lintResult = await runCheck(lintCmd, CHECK_OPTS);
|
|
102
128
|
const lintWarnings =
|
|
103
129
|
lintResult.status !== 'skip'
|
|
104
130
|
? extractCount(lintResult.fullOutput ?? lintResult.output, /warning/i)
|
|
@@ -120,7 +146,10 @@ if (lintResult.stubbed) stubbed.lint = lintResult.stubbed;
|
|
|
120
146
|
const output = {
|
|
121
147
|
variant: 'full-gate',
|
|
122
148
|
duration_seconds: durationSeconds,
|
|
123
|
-
|
|
149
|
+
// Each command object also carries its INNER kill ladder (`timed_out`,
|
|
150
|
+
// `kill_signals`, `survivors` pids) when `runCheck` spawned it — omitted for
|
|
151
|
+
// skip/stub (#1457). `run-quality-gate.mjs` joins them into its event.
|
|
152
|
+
typecheck: { status: tcResult.status, error_count: tcErrorCount, ...commandKillFields(tcResult) },
|
|
124
153
|
// `failed` is published explicitly (#967 item 1) rather than left to be
|
|
125
154
|
// re-derived as `total - passed` downstream: the derivation and the parse can
|
|
126
155
|
// disagree, and only an explicit third number lets a consumer check
|
|
@@ -136,8 +165,9 @@ const output = {
|
|
|
136
165
|
// read just passed/failed/total, so the stdout JSON contract stays one
|
|
137
166
|
// document and the test-case triple keeps `total === passed + failed`.
|
|
138
167
|
...fileFields,
|
|
168
|
+
...commandKillFields(testResult),
|
|
139
169
|
},
|
|
140
|
-
lint: { status: lintResult.status, warnings: lintWarnings },
|
|
170
|
+
lint: { status: lintResult.status, warnings: lintWarnings, ...commandKillFields(lintResult) },
|
|
141
171
|
debug_artifacts: debugArtifacts,
|
|
142
172
|
stubbed,
|
|
143
173
|
};
|