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,231 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* skills/navigator/references/fleet-decisions.mjs
|
|
3
|
+
*
|
|
4
|
+
* Pure decision functions for the navigator's headless fleet: whether a new
|
|
5
|
+
* headless run may start on this host (slot gate), whether a target repo is
|
|
6
|
+
* already occupied by another session (busy gate), and which account the host
|
|
7
|
+
* should run on (account watch).
|
|
8
|
+
*
|
|
9
|
+
* Measurement lives elsewhere — `slot-check.sh` and `account-check.sh` in this
|
|
10
|
+
* directory collect the raw numbers and pipe them in here. This module reads
|
|
11
|
+
* no environment variables, no files and no clock except the `now` argument,
|
|
12
|
+
* so every rule below is testable with plain values.
|
|
13
|
+
*
|
|
14
|
+
* Shared principle: a value that could not be measured is never read as
|
|
15
|
+
* "free". Every gate fails closed on missing, NaN or non-number input; only
|
|
16
|
+
* an explicit `override: true` (an owner decision) bypasses a gate.
|
|
17
|
+
*
|
|
18
|
+
* Slot gate thresholds (free memory % from the OS pressure verdict):
|
|
19
|
+
* free >= 50 → cap 4
|
|
20
|
+
* free >= 35 → cap 3
|
|
21
|
+
* otherwise → cap 0
|
|
22
|
+
* load1 > 40 → cap 0 regardless of memory
|
|
23
|
+
* allowed = runs < cap
|
|
24
|
+
*
|
|
25
|
+
* Busy gate: busy when any Codex rollout for the repo is younger than 30 min
|
|
26
|
+
* or any `session.lock` is younger than 6 h (counts measured by the caller).
|
|
27
|
+
*
|
|
28
|
+
* Account watch: usable = 5-hour and 7-day usage both below 97 %. Among usable
|
|
29
|
+
* rows the earliest 7-day reset within 3 days wins (tie-break lower 7-day
|
|
30
|
+
* usage); if none is that near, the lowest 7-day usage wins. The active row is
|
|
31
|
+
* kept when its reset lies within 1800 s of the target's, either side (same
|
|
32
|
+
* reset, no flapping). A reset date in the past is stale data and the row is
|
|
33
|
+
* skipped. Rows are identified by `slot` only — the alias never reaches the
|
|
34
|
+
* output (SEC-008).
|
|
35
|
+
*
|
|
36
|
+
* CLI: `node fleet-decisions.mjs slot|busy|account` reads one JSON document
|
|
37
|
+
* from stdin and prints the decision as one JSON line.
|
|
38
|
+
* exit 0 — allowed / not busy / stay or switch
|
|
39
|
+
* exit 1 — refused / busy / no usable account
|
|
40
|
+
* exit 2 — unparseable input or unknown subcommand
|
|
41
|
+
*/
|
|
42
|
+
|
|
43
|
+
import { readFileSync } from 'node:fs';
|
|
44
|
+
import { fileURLToPath } from 'node:url';
|
|
45
|
+
|
|
46
|
+
// ---------------------------------------------------------------------------
|
|
47
|
+
// Constants
|
|
48
|
+
// ---------------------------------------------------------------------------
|
|
49
|
+
|
|
50
|
+
const FREE_PCT_CAP4 = 50;
|
|
51
|
+
const FREE_PCT_CAP3 = 35;
|
|
52
|
+
const LOAD1_MAX = 40;
|
|
53
|
+
const USAGE_LIMIT_PCT = 97;
|
|
54
|
+
const NEAR_RESET_MS = 3 * 24 * 60 * 60 * 1000;
|
|
55
|
+
const RESET_TIE_MS = 1800 * 1000;
|
|
56
|
+
|
|
57
|
+
/** @param {unknown} v @returns {v is number} */
|
|
58
|
+
function isNum(v) {
|
|
59
|
+
return typeof v === 'number' && Number.isFinite(v);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// ---------------------------------------------------------------------------
|
|
63
|
+
// Slot gate
|
|
64
|
+
// ---------------------------------------------------------------------------
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Decide whether one more headless run may start on this host.
|
|
68
|
+
*
|
|
69
|
+
* @param {{ freePct?: unknown, load1?: unknown, runs?: unknown, override?: unknown }} input
|
|
70
|
+
* @returns {{ allowed: boolean, cap: number, reason: string }}
|
|
71
|
+
*/
|
|
72
|
+
export function slotDecision({ freePct, load1, runs, override } = {}) {
|
|
73
|
+
const memOk = isNum(freePct);
|
|
74
|
+
const loadOk = isNum(load1);
|
|
75
|
+
let cap = 0;
|
|
76
|
+
if (memOk && loadOk) {
|
|
77
|
+
if (freePct >= FREE_PCT_CAP4) cap = 4;
|
|
78
|
+
else if (freePct >= FREE_PCT_CAP3) cap = 3;
|
|
79
|
+
if (load1 > LOAD1_MAX) cap = 0;
|
|
80
|
+
}
|
|
81
|
+
if (override === true) return { allowed: true, cap, reason: 'override' };
|
|
82
|
+
|
|
83
|
+
const ctx = `(free ${memOk ? `${freePct}%` : '?'}, load1 ${loadOk ? load1 : '?'})`;
|
|
84
|
+
if (!memOk) return { allowed: false, cap: 0, reason: `no slot: free memory not measurable ${ctx}` };
|
|
85
|
+
if (!loadOk) return { allowed: false, cap: 0, reason: `no slot: load1 not measurable ${ctx}` };
|
|
86
|
+
if (!isNum(runs) || !Number.isInteger(runs) || runs < 0) {
|
|
87
|
+
return { allowed: false, cap, reason: `no slot: run count not measurable ${ctx}` };
|
|
88
|
+
}
|
|
89
|
+
const allowed = runs < cap;
|
|
90
|
+
return { allowed, cap, reason: `${allowed ? 'slot free' : 'no slot'}: ${runs} runs, cap ${cap} ${ctx}` };
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// ---------------------------------------------------------------------------
|
|
94
|
+
// Busy gate
|
|
95
|
+
// ---------------------------------------------------------------------------
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Decide whether a repo is occupied by another session.
|
|
99
|
+
*
|
|
100
|
+
* @param {{ codexRolloutsFresh?: unknown, lockFresh?: unknown, override?: unknown }} input
|
|
101
|
+
* @returns {{ busy: boolean, reason: string }}
|
|
102
|
+
*/
|
|
103
|
+
export function busyDecision({ codexRolloutsFresh, lockFresh, override } = {}) {
|
|
104
|
+
if (override === true) return { busy: false, reason: 'override' };
|
|
105
|
+
const valid = (v) => isNum(v) && v >= 0;
|
|
106
|
+
if (!valid(codexRolloutsFresh) || !valid(lockFresh)) {
|
|
107
|
+
return { busy: true, reason: 'busy: occupancy not measurable (codex rollouts or session.lock count missing)' };
|
|
108
|
+
}
|
|
109
|
+
const counts = `${codexRolloutsFresh} codex rollouts (30 min), ${lockFresh} session.lock (6 h)`;
|
|
110
|
+
if (codexRolloutsFresh > 0 || lockFresh > 0) return { busy: true, reason: `busy: ${counts}` };
|
|
111
|
+
return { busy: false, reason: `free: ${counts}` };
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// ---------------------------------------------------------------------------
|
|
115
|
+
// Account watch
|
|
116
|
+
// ---------------------------------------------------------------------------
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Reduce one raw account row to the fields the decision needs, or null when it
|
|
120
|
+
* is not measurable. Copies nothing else — in particular not the alias. Never
|
|
121
|
+
* throws: a hostile row (throwing getter, proxy) counts as not measurable.
|
|
122
|
+
*
|
|
123
|
+
* @param {unknown} r
|
|
124
|
+
* @param {number} nowMs
|
|
125
|
+
* @returns {{ id: string|number, active: boolean, p7: number, p5: number, resetMs: number } | null}
|
|
126
|
+
*/
|
|
127
|
+
function parseAccountRow(r, nowMs) {
|
|
128
|
+
try {
|
|
129
|
+
if (!r || typeof r !== 'object') return null;
|
|
130
|
+
const { slot, active, p7, p5, resetAt } = /** @type {Record<string, unknown>} */ (r);
|
|
131
|
+
const slotOk = (typeof slot === 'string' && slot !== '') || isNum(slot);
|
|
132
|
+
const resetMs = typeof resetAt === 'string' ? Date.parse(resetAt) : NaN;
|
|
133
|
+
if (!slotOk || !isNum(p7) || !isNum(p5) || !Number.isFinite(resetMs) || resetMs < nowMs) return null;
|
|
134
|
+
return { id: /** @type {string|number} */ (slot), active: active === true, p7, p5, resetMs };
|
|
135
|
+
} catch {
|
|
136
|
+
return null;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Pick the account the host should run on. Never throws; malformed rows
|
|
142
|
+
* (non-string/non-number slot, missing usage, unparseable or past reset) are
|
|
143
|
+
* skipped and counted in the reason. The output carries the slot only, never
|
|
144
|
+
* the alias (SEC-008).
|
|
145
|
+
*
|
|
146
|
+
* @param {Array<{ slot: unknown, alias?: string, active: boolean, p7: number|null, p5: number|null, resetAt: string|null }>} rows
|
|
147
|
+
* @param {Date|number} now
|
|
148
|
+
* @returns {{ action: 'stay'|'switch'|'none', target: string|number|null, reason: string }}
|
|
149
|
+
*/
|
|
150
|
+
export function chooseAccount(rows, now) {
|
|
151
|
+
const nowMs = now instanceof Date ? now.getTime() : Number(now);
|
|
152
|
+
if (!Number.isFinite(nowMs)) return { action: 'none', target: null, reason: 'no decision: now is not a valid time' };
|
|
153
|
+
const list = Array.isArray(rows) ? rows : [];
|
|
154
|
+
|
|
155
|
+
let skipped = 0;
|
|
156
|
+
const parsed = [];
|
|
157
|
+
for (const r of list) {
|
|
158
|
+
const row = parseAccountRow(r, nowMs);
|
|
159
|
+
if (row === null) skipped += 1;
|
|
160
|
+
else parsed.push(row);
|
|
161
|
+
}
|
|
162
|
+
const skipNote = skipped > 0 ? `; ${skipped} row(s) skipped without data` : '';
|
|
163
|
+
|
|
164
|
+
const usable = parsed.filter((r) => r.p5 < USAGE_LIMIT_PCT && r.p7 < USAGE_LIMIT_PCT);
|
|
165
|
+
const active = parsed.find((r) => r.active === true) ?? null;
|
|
166
|
+
const activeUsable = active !== null && usable.includes(active);
|
|
167
|
+
|
|
168
|
+
if (usable.length === 0) {
|
|
169
|
+
return { action: 'none', target: null, reason: `no usable account (all >= ${USAGE_LIMIT_PCT}% or without data)${skipNote}` };
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const near = usable.filter((r) => r.resetMs - nowMs <= NEAR_RESET_MS);
|
|
173
|
+
let target;
|
|
174
|
+
if (near.length > 0) {
|
|
175
|
+
target = near.reduce((a, b) => (b.resetMs < a.resetMs || (b.resetMs === a.resetMs && b.p7 < a.p7) ? b : a));
|
|
176
|
+
} else {
|
|
177
|
+
target = usable.reduce((a, b) => (b.p7 < a.p7 ? b : a));
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
if (activeUsable && active !== target && Math.abs(active.resetMs - target.resetMs) <= RESET_TIE_MS) {
|
|
181
|
+
target = active;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
const desc = (r) => `${r.id} (7d ${r.p7}%, reset in ${((r.resetMs - nowMs) / 86400000).toFixed(1)} d)`;
|
|
185
|
+
if (active === target) return { action: 'stay', target: target.id, reason: `stay on ${desc(target)}${skipNote}` };
|
|
186
|
+
return {
|
|
187
|
+
action: 'switch',
|
|
188
|
+
target: target.id,
|
|
189
|
+
reason: `switch ${active ? active.id : '-'} -> ${desc(target)}${skipNote}`,
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// ---------------------------------------------------------------------------
|
|
194
|
+
// CLI
|
|
195
|
+
// ---------------------------------------------------------------------------
|
|
196
|
+
|
|
197
|
+
function runCli(argv) {
|
|
198
|
+
const mode = argv[2];
|
|
199
|
+
if (mode !== 'slot' && mode !== 'busy' && mode !== 'account') {
|
|
200
|
+
process.stderr.write('usage: node fleet-decisions.mjs slot|busy|account < input.json\n');
|
|
201
|
+
return 2;
|
|
202
|
+
}
|
|
203
|
+
let input;
|
|
204
|
+
try {
|
|
205
|
+
input = JSON.parse(readFileSync(0, 'utf8'));
|
|
206
|
+
} catch {
|
|
207
|
+
process.stderr.write('fleet-decisions: stdin is not valid JSON\n');
|
|
208
|
+
return 2;
|
|
209
|
+
}
|
|
210
|
+
if (mode === 'slot' || mode === 'busy') {
|
|
211
|
+
if (!input || typeof input !== 'object' || Array.isArray(input)) {
|
|
212
|
+
process.stderr.write(`fleet-decisions: ${mode} expects one JSON object\n`);
|
|
213
|
+
return 2;
|
|
214
|
+
}
|
|
215
|
+
const d = mode === 'slot' ? slotDecision(input) : busyDecision(input);
|
|
216
|
+
process.stdout.write(`${JSON.stringify(d)}\n`);
|
|
217
|
+
return mode === 'slot' ? (d.allowed ? 0 : 1) : d.busy ? 1 : 0;
|
|
218
|
+
}
|
|
219
|
+
const rows = Array.isArray(input) ? input : input && Array.isArray(input.accounts) ? input.accounts : null;
|
|
220
|
+
if (!rows) {
|
|
221
|
+
process.stderr.write('fleet-decisions: account expects a JSON array or {"accounts": [...]}\n');
|
|
222
|
+
return 2;
|
|
223
|
+
}
|
|
224
|
+
const d = chooseAccount(rows, Date.now());
|
|
225
|
+
process.stdout.write(`${JSON.stringify(d)}\n`);
|
|
226
|
+
return d.action === 'none' ? 1 : 0;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
if (process.argv[1] === fileURLToPath(import.meta.url)) {
|
|
230
|
+
process.exitCode = runCli(process.argv);
|
|
231
|
+
}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# slot-check.sh — measure the slot gate and the busy gate for one repo, decide via fleet-decisions.mjs.
|
|
3
|
+
#
|
|
4
|
+
# Usage: slot-check.sh <repo-path>
|
|
5
|
+
#
|
|
6
|
+
# Measures only; every rule lives in fleet-decisions.mjs next to this file.
|
|
7
|
+
# slot gate: free memory % (memory_pressure), 1-min load (vm.loadavg, integer),
|
|
8
|
+
# running headless runs (`timeout -k 60 <n>[smhd] claude -p` + `... codex exec`);
|
|
9
|
+
# anchored on the timeout wrapper because one Codex run spawns three processes matching `codex exec`
|
|
10
|
+
# busy gate: Codex rollouts for <repo-path> younger than 30 min, <repo-path>/.orchestrator/session.lock
|
|
11
|
+
# younger than 6 h
|
|
12
|
+
# A value that cannot be measured is passed as null; the decision module fails closed on it.
|
|
13
|
+
#
|
|
14
|
+
# Env:
|
|
15
|
+
# NAVIGATOR_CODEX_HOME Codex home directory (default: $HOME/.codex; empty/whitespace = unset)
|
|
16
|
+
# FLEET_SLOT_OVERRIDE=1 owner override for the slot gate
|
|
17
|
+
# FLEET_BUSY_OVERRIDE=1 owner override for the busy gate
|
|
18
|
+
#
|
|
19
|
+
# Exit: 0 both gates pass, 1 at least one gate refuses, 2 usage error.
|
|
20
|
+
set -u
|
|
21
|
+
|
|
22
|
+
if [ "$#" -ne 1 ] || [ -z "$1" ]; then
|
|
23
|
+
echo "usage: slot-check.sh <repo-path>" >&2
|
|
24
|
+
exit 2
|
|
25
|
+
fi
|
|
26
|
+
repo=${1%/}
|
|
27
|
+
here=$(cd "$(dirname "$0")" && pwd)
|
|
28
|
+
decide="$here/fleet-decisions.mjs"
|
|
29
|
+
|
|
30
|
+
# Trim leading/trailing whitespace; a whitespace-only value counts as unset.
|
|
31
|
+
trim() {
|
|
32
|
+
local v=$1
|
|
33
|
+
v=${v#"${v%%[![:space:]]*}"}
|
|
34
|
+
v=${v%"${v##*[![:space:]]}"}
|
|
35
|
+
printf '%s' "$v"
|
|
36
|
+
}
|
|
37
|
+
codex_home=$(trim "${NAVIGATOR_CODEX_HOME:-}")
|
|
38
|
+
[ -n "$codex_home" ] || codex_home=$HOME/.codex
|
|
39
|
+
|
|
40
|
+
# Rollouts record the cwd as the session saw it; match both the logical and the physical path.
|
|
41
|
+
repo_abs=$(cd "$repo" 2>/dev/null && pwd)
|
|
42
|
+
repo_phys=$(cd "$repo" 2>/dev/null && pwd -P)
|
|
43
|
+
|
|
44
|
+
# Print the argument if it is a non-negative integer, else the JSON literal null.
|
|
45
|
+
num_or_null() {
|
|
46
|
+
case "$1" in
|
|
47
|
+
''|*[!0-9]*) printf 'null' ;;
|
|
48
|
+
*) printf '%s' "$1" ;;
|
|
49
|
+
esac
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
# --- slot gate measurements ---------------------------------------------------
|
|
53
|
+
free_pct=$(memory_pressure 2>/dev/null | awk '/free percentage/{gsub("%","",$NF); print $NF}')
|
|
54
|
+
load1=$(sysctl -n vm.loadavg 2>/dev/null | awk '{print int($2)}')
|
|
55
|
+
# Count pgrep matches: rc 0 → count, rc 1 → 0 (no match), rc >= 2 → empty (not measurable).
|
|
56
|
+
count_runs() {
|
|
57
|
+
local out rc
|
|
58
|
+
out=$(pgrep -f "$1")
|
|
59
|
+
rc=$?
|
|
60
|
+
case "$rc" in
|
|
61
|
+
0) printf '%s' "$out" | grep -c . ;;
|
|
62
|
+
1) printf '0' ;;
|
|
63
|
+
*) printf '' ;;
|
|
64
|
+
esac
|
|
65
|
+
}
|
|
66
|
+
runs=''
|
|
67
|
+
if command -v pgrep >/dev/null 2>&1; then
|
|
68
|
+
claude_runs=$(count_runs '^timeout -k 60 [0-9]+[smhd]? claude -p')
|
|
69
|
+
codex_runs=$(count_runs '^timeout -k 60 [0-9]+[smhd]? codex exec')
|
|
70
|
+
if [ -n "$claude_runs" ] && [ -n "$codex_runs" ]; then
|
|
71
|
+
runs=$((claude_runs + codex_runs))
|
|
72
|
+
fi
|
|
73
|
+
fi
|
|
74
|
+
|
|
75
|
+
# --- busy gate measurements ---------------------------------------------------
|
|
76
|
+
# Only the first line of each rollout is matched, as a fixed string; file content is never evaluated.
|
|
77
|
+
if [ -z "$repo_abs" ] || [ -z "$repo_phys" ]; then
|
|
78
|
+
# Repo path not resolvable: both busy counts are unknown, the decision fails closed.
|
|
79
|
+
codex_fresh=''
|
|
80
|
+
lock_fresh=''
|
|
81
|
+
else
|
|
82
|
+
codex_fresh=0
|
|
83
|
+
if [ -d "$codex_home/sessions" ]; then
|
|
84
|
+
codex_fresh=$(find "$codex_home/sessions" -name 'rollout-*.jsonl' -mmin -30 2>/dev/null |
|
|
85
|
+
while IFS= read -r f; do
|
|
86
|
+
head -1 "$f" 2>/dev/null |
|
|
87
|
+
grep -qF -e "\"cwd\":\"$repo_abs\"" -e "\"cwd\":\"$repo_phys\"" && echo x
|
|
88
|
+
done | wc -l | tr -d ' ')
|
|
89
|
+
fi
|
|
90
|
+
lock_fresh=$(find "$repo_abs/.orchestrator" -maxdepth 1 -name session.lock -mmin -360 2>/dev/null | wc -l | tr -d ' ')
|
|
91
|
+
fi
|
|
92
|
+
|
|
93
|
+
slot_override=false
|
|
94
|
+
[ "${FLEET_SLOT_OVERRIDE:-0}" = 1 ] && slot_override=true
|
|
95
|
+
busy_override=false
|
|
96
|
+
[ "${FLEET_BUSY_OVERRIDE:-0}" = 1 ] && busy_override=true
|
|
97
|
+
|
|
98
|
+
slot_json=$(printf '{"freePct":%s,"load1":%s,"runs":%s,"override":%s}' \
|
|
99
|
+
"$(num_or_null "$free_pct")" "$(num_or_null "$load1")" "$(num_or_null "$runs")" "$slot_override")
|
|
100
|
+
busy_json=$(printf '{"codexRolloutsFresh":%s,"lockFresh":%s,"override":%s}' \
|
|
101
|
+
"$(num_or_null "$codex_fresh")" "$(num_or_null "$lock_fresh")" "$busy_override")
|
|
102
|
+
|
|
103
|
+
slot_out=$(printf '%s' "$slot_json" | node "$decide" slot)
|
|
104
|
+
slot_rc=$?
|
|
105
|
+
busy_out=$(printf '%s' "$busy_json" | node "$decide" busy)
|
|
106
|
+
busy_rc=$?
|
|
107
|
+
|
|
108
|
+
echo "slot: $slot_out"
|
|
109
|
+
echo "busy: $busy_out"
|
|
110
|
+
|
|
111
|
+
if [ "$slot_rc" -ne 0 ] || [ "$busy_rc" -ne 0 ]; then
|
|
112
|
+
exit 1
|
|
113
|
+
fi
|
|
114
|
+
exit 0
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: npm-publish
|
|
3
|
-
user-invocable:
|
|
3
|
+
user-invocable: false
|
|
4
4
|
model: sonnet
|
|
5
5
|
description: Use when publishing this package to npm — a version release (npm publish), verifying the registry/pi.dev listing, or diagnosing npm auth failures (E403 2FA/token errors). Token-based flow via NPM_TOKEN in .env.local with a temp userconfig, the leakage gate before every publish, post-publish verification and marker/badge upkeep. Trigger on "publish to npm", "npm release", "E403 publish error".
|
|
6
6
|
---
|
|
@@ -184,8 +184,8 @@ Dispatch one Agent per persona from the active catalog set.
|
|
|
184
184
|
|
|
185
185
|
**Model selection per persona:**
|
|
186
186
|
- If `persona.model` is a full Claude model ID (`MODEL_ID_RE`): use it as-is.
|
|
187
|
-
- If `persona.model` is `opus` or unset AND `persona.tier == 'domain-expert'`:
|
|
188
|
-
`
|
|
187
|
+
- If `persona.model` is `opus` or unset AND `persona.tier == 'domain-expert'`: use the alias
|
|
188
|
+
`opus`, which tracks the current Opus — pin a full ID only for a named reproducibility need (empirically validated — Opus finds real problems Sonnet misses; see vault
|
|
189
189
|
learning `[[persona-opus-finds-real-failing-cibadge]]`).
|
|
190
190
|
- Otherwise: use the persona's declared model alias.
|
|
191
191
|
|
|
@@ -28,7 +28,7 @@ name: ai-expert
|
|
|
28
28
|
schema_version: 1
|
|
29
29
|
version: "2"
|
|
30
30
|
role: "AI/ML domain expert — evaluates technical accuracy and implementation quality"
|
|
31
|
-
model:
|
|
31
|
+
model: opus
|
|
32
32
|
tier: domain-expert
|
|
33
33
|
output_contract:
|
|
34
34
|
type: object
|
|
@@ -59,7 +59,7 @@ evaluation_criteria:
|
|
|
59
59
|
| `schema_version` | integer | Must be `1`. |
|
|
60
60
|
| `version` | string | Non-empty string. Persona content version. Increment on any output-affecting change. Used in sidecar + trend-tracking (#459). |
|
|
61
61
|
| `role` | string | Identity statement. Injected verbatim as prompt opener. Keep under 200 chars. |
|
|
62
|
-
| `model` | string | Full model ID (`MODEL_ID_RE`) or alias (`inherit|sonnet|opus|haiku`). Validated at load time. Recommend `
|
|
62
|
+
| `model` | string | Full model ID (`MODEL_ID_RE`) or alias (`inherit|sonnet|opus|haiku`). Validated at load time. Recommend the Opus tier via alias `opus` for `domain-expert` and `compliance` tiers (Opus finds real issues Sonnet misses — vault learning `[[persona-opus-finds-real-failing-cibadge]]`); the alias tracks the current Opus; pin a full model ID only for a named reproducibility need, and name the reason. |
|
|
63
63
|
| `output_contract` | object | Inline JSON Schema Draft 2020-12. `$ref/$defs/allOf/anyOf` FORBIDDEN (H3). Must require `verdict` + `rationale`. AJV compile wrapped in 2s AbortSignal timeout. |
|
|
64
64
|
| `evaluation_criteria` | array | Non-empty. Each string max 512 chars. Injected wrapped in `<persona-criteria>` delimiters (M1). Write as statements, not questions. |
|
|
65
65
|
| `tier` | enum | `domain-expert` \| `buyer-persona` \| `auditor` \| `compliance` \| `reviewer` \| `custom`. Affects model selection (Phase 3). |
|
|
@@ -3,7 +3,7 @@ name: designer-lens
|
|
|
3
3
|
schema_version: 1
|
|
4
4
|
version: "1"
|
|
5
5
|
role: "Designer lens — evaluates interaction flow, cognitive load, consistency, and accessibility"
|
|
6
|
-
model:
|
|
6
|
+
model: opus
|
|
7
7
|
tier: domain-expert
|
|
8
8
|
evaluation_criteria:
|
|
9
9
|
- "The interaction flow is legible on first encounter — a new user can predict what happens next without hidden state."
|
|
@@ -3,7 +3,7 @@ name: engineer-lens
|
|
|
3
3
|
schema_version: 1
|
|
4
4
|
version: "1"
|
|
5
5
|
role: "Engineer lens — evaluates feasibility, complexity, failure modes, and long-term maintainability"
|
|
6
|
-
model:
|
|
6
|
+
model: opus
|
|
7
7
|
tier: domain-expert
|
|
8
8
|
evaluation_criteria:
|
|
9
9
|
- "Failure modes are enumerated — what breaks under load, bad input, or partial failure, and how the system responds."
|
|
@@ -3,7 +3,7 @@ name: pm-lens
|
|
|
3
3
|
schema_version: 1
|
|
4
4
|
version: "1"
|
|
5
5
|
role: "Product Manager lens — evaluates user value, outcome-metric clarity, and scope discipline"
|
|
6
|
-
model:
|
|
6
|
+
model: opus
|
|
7
7
|
tier: domain-expert
|
|
8
8
|
evaluation_criteria:
|
|
9
9
|
- "The change is tied to a specific, named user or business problem — not a solution in search of one."
|
|
@@ -236,10 +236,11 @@ If issues were created and a 5+-action Epic wrapper was used (see `SKILL.md` §
|
|
|
236
236
|
|
|
237
237
|
### 3.3 Update Learnings
|
|
238
238
|
|
|
239
|
-
|
|
239
|
+
Collect new patterns as learning objects in the same strict schema session-end uses (canonical field reference: `skills/session-end/learning-patterns.md` § 3.5a):
|
|
240
240
|
|
|
241
241
|
```json
|
|
242
242
|
{
|
|
243
|
+
"schema_version": 1,
|
|
243
244
|
"id": "<uuid-v4>",
|
|
244
245
|
"type": "fragile-file|effective-sizing|recurring-issue|scope-guidance|deviation-pattern",
|
|
245
246
|
"subject": "<what the learning is about>",
|
|
@@ -247,11 +248,25 @@ Append new patterns to `.orchestrator/metrics/learnings.jsonl`. Use the same JSO
|
|
|
247
248
|
"evidence": "<supporting data>",
|
|
248
249
|
"confidence": 0.5,
|
|
249
250
|
"source_session": "retro-YYYY-MM-DD",
|
|
250
|
-
"
|
|
251
|
-
"
|
|
251
|
+
"scope": "local",
|
|
252
|
+
"host_class": null,
|
|
253
|
+
"anonymized": false,
|
|
254
|
+
"file_paths": ["<repo-relative path(s) this learning applies to>"],
|
|
255
|
+
"created_at": "<ISO 8601>"
|
|
252
256
|
}
|
|
253
257
|
```
|
|
254
258
|
|
|
259
|
+
`scope` MUST be `local|private|public` — a file path goes in `file_paths`, never in `scope` (GH#69). `schema_version` is the integer `1`. Omit `expires_at`: the CLI derives it per `type` via `deriveExpiresAt()`.
|
|
260
|
+
|
|
261
|
+
Write them **only** through `scripts/apply-session-learnings.mjs` (#1446) — never by hand, never with a shell redirect. Put `{"confidence_updates": [...], "new_learnings": [...]}` in a sidecar under `.orchestrator/tmp/` via the Write tool, preview, then apply. `--decay-rate 0` keeps a retro from applying the per-session passive decay a second time:
|
|
262
|
+
|
|
263
|
+
```bash
|
|
264
|
+
node scripts/apply-session-learnings.mjs --input .orchestrator/tmp/retro-learnings.json --decay-rate 0 --json
|
|
265
|
+
node scripts/apply-session-learnings.mjs --input .orchestrator/tmp/retro-learnings.json --decay-rate 0 --apply --json
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
Exit `1` means a record failed validation (the message names its id) and nothing was written.
|
|
269
|
+
|
|
255
270
|
Typical retro learning types:
|
|
256
271
|
- `fragile-file` — hotspots correlating with failures or carryover
|
|
257
272
|
- `effective-sizing` — wave/agent configs producing best completion rates
|
|
@@ -259,7 +274,7 @@ Typical retro learning types:
|
|
|
259
274
|
- `scope-guidance` — session scope calibration insights
|
|
260
275
|
- `deviation-pattern` — systematic plan deviations indicating planning gaps
|
|
261
276
|
|
|
262
|
-
Before writing, check existing entries for matching `type` + `subject`.
|
|
277
|
+
Before writing, check existing entries for matching `type` + `subject`. A match is not a new learning: list its `id` in `confidence_updates` with `"operation": "confirm"` (+0.15, cap 1.0) or `"contradict"` (-0.2) — the CLI applies both. New learnings start at 0.5.
|
|
263
278
|
|
|
264
279
|
### 3.4 Optional Baseline Update
|
|
265
280
|
|
|
@@ -71,9 +71,11 @@ as the session baseline for later comparison.
|
|
|
71
71
|
|
|
72
72
|
**Script output schema (Baseline):**
|
|
73
73
|
```json
|
|
74
|
-
{"variant": "baseline", "typecheck": {"status": "pass|fail|skip"
|
|
74
|
+
{"variant": "baseline", "typecheck": {"status": "pass|fail|skip"}, "test": {"status": "pass|fail|skip"}, "typecheck_output": "string", "test_output": "string"}
|
|
75
75
|
```
|
|
76
76
|
|
|
77
|
+
In every variant, each command object that was actually spawned also carries its per-command kill ladder: `timed_out` (boolean), `kill_signals` (string[]) and `survivors` (pid[]). A skipped or stubbed command has none of the three keys (absent, never `false`/`[]`). `scripts/run-quality-gate.mjs` joins them into `orchestrator.quality_gate.{passed,failed}` (#1457). A run whose joined `timed_out` is `true` is recorded as `.failed` in every variant, although Baseline, Incremental and Per-File still exit 0 (#1459, decided 2026-10-01).
|
|
78
|
+
|
|
77
79
|
## Variant 2: Incremental
|
|
78
80
|
|
|
79
81
|
**Used by:** wave-executor (after implementation waves)
|
|
@@ -91,8 +93,8 @@ Metrics output (for consuming skills to capture):
|
|
|
91
93
|
{
|
|
92
94
|
"variant": "incremental",
|
|
93
95
|
"duration_seconds": null,
|
|
94
|
-
"typecheck": "pass|fail|skip",
|
|
95
|
-
"test": "pass|fail|skip",
|
|
96
|
+
"typecheck": {"status": "pass|fail|skip"},
|
|
97
|
+
"test": {"status": "pass|fail|skip"},
|
|
96
98
|
"errors": []
|
|
97
99
|
}
|
|
98
100
|
```
|
|
@@ -182,7 +184,7 @@ node "${CLAUDE_PLUGIN_ROOT:-${CODEX_PLUGIN_ROOT:-$PLUGIN_ROOT}}/scripts/run-qual
|
|
|
182
184
|
node "${CLAUDE_PLUGIN_ROOT:-${CODEX_PLUGIN_ROOT:-$PLUGIN_ROOT}}/scripts/run-quality-gate.mjs" --variant per-file --config "$CONFIG" --files specific-file.ts
|
|
183
185
|
```
|
|
184
186
|
|
|
185
|
-
The script handles graceful degradation (missing tools → skip), structured JSON output matching the schemas above, and proper exit codes (0=pass, 1=error, 2=gate-failed).
|
|
187
|
+
The script handles graceful degradation (missing tools → skip), structured JSON output matching the schemas above, and proper exit codes (0=pass, 1=error, 2=gate-failed). Only Full Gate exits 2: the three reporting variants exit 0 after a failing or killed check, so their verdict is in the JSON envelope (a kill also turns the event into `.failed`; a plain failing check does not).
|
|
186
188
|
|
|
187
189
|
## Baseline Cache (#258)
|
|
188
190
|
|
|
@@ -394,6 +394,17 @@ consolidation survives the next `/reconcile`:
|
|
|
394
394
|
regenerates that learning as a standalone file on the next run.
|
|
395
395
|
- **A merged file's `expires-at` is the EARLIEST of its parts**, never the
|
|
396
396
|
latest: it must not outlive its shortest-lived content.
|
|
397
|
+
- **An EXPIRED file no longer dedupes** (#1387): the provenance reader skips it
|
|
398
|
+
whole, so its learnings become re-proposable — fail-open on an unparseable
|
|
399
|
+
date. Two known limits, measured 2026-09-18: this is the on-disk half only
|
|
400
|
+
(79 of this repo's 92 provenance keys are ALSO sidecar-terminal, so just 13
|
|
401
|
+
return), and a file the sweep cannot split or cannot read
|
|
402
|
+
(`no-1to1-mapping`, `no-provenance-block`, `unreadable`, and since GH#70
|
|
403
|
+
`no-counter-sentence` — the fail-closed branch that touches such a file not
|
|
404
|
+
at all, deletion included; none in the live tree today, `skipped: []` over 7
|
|
405
|
+
files) that expires legitimately
|
|
406
|
+
re-proposes on every run with no mechanical exit. Detail:
|
|
407
|
+
`docs/rule-authoring.md` § Consolidated rules.
|
|
397
408
|
- **A dropped learning must be STAMPED before deletion, or it regenerates.**
|
|
398
409
|
`rm .claude/rules/<slug>.md` alone leaves `isProcessed()` false and no
|
|
399
410
|
on-disk marker, so the engine re-proposes it. Stamp it terminal first with
|
|
@@ -28,12 +28,14 @@ Exit codes (`offload --help`, measured 2026-09-02): `0` ok · `1` usage/config
|
|
|
28
28
|
|
|
29
29
|
## 1. Decision rule — offload vs reduce
|
|
30
30
|
|
|
31
|
-
The gate decides, not the coordinator. `applyOffloadDecision()` in `scripts/lib/wave-resource-gate.mjs`
|
|
31
|
+
The gate decides, not the coordinator. `applyOffloadDecision()` in `scripts/lib/wave-resource-gate.mjs` fires when the resource verdict is `reduce` or `coordinator-direct` (and, with `offload-first: true`, on `proceed` as well — see below), and only AFTER the HR-004 heavy-repo cap — a capped wave that offloads still respects the cap. It never probes the network; the coordinator supplies a readiness WITNESS:
|
|
32
32
|
|
|
33
33
|
- `opts.remoteReady` — `{ [alias]: boolean }`, built from the SessionStart banner line `Offload <alias>: ready=yes …`, or
|
|
34
34
|
- `opts.probeFn` — an async `(alias) => boolean` fallback, consulted only for aliases `remoteReady` doesn't answer for (backed by `remoteDoctor()`, i.e. `offload doctor -H <alias> --brief` parsed by `parseDoctorLine()`).
|
|
35
35
|
|
|
36
|
-
With neither supplied, no host counts as ready and the wave stays local — the gate fails toward local, never toward an unverified host. A role in `NEVER_FOREIGN_ROLES` (`scripts/lib/wave-executor/dispatch-common.mjs`: `impl-core`, `security-review`, `migration`, `release`, `secrets`) is never offloaded regardless of readiness.
|
|
36
|
+
With neither supplied, no host counts as ready and the wave stays local — the gate fails toward local, never toward an unverified host. A role in `NEVER_FOREIGN_ROLES` (`scripts/lib/wave-executor/dispatch-common.mjs`: `impl-core`, `security-review`, `migration`, `release`, `secrets`, `incident`, `refactor-crosscut`) is never offloaded regardless of readiness.
|
|
37
|
+
|
|
38
|
+
With `offload-first: true` in Session Config (#1465, default `false`) the placement arm also fires on `proceed`, so an offloadable role goes to a witnessed-ready host without waiting for local pressure. The witness stays mandatory — without `remoteReady`/`probeFn` nothing moves — and a wave that stays local carries a named reason (`offload-first: no ready host for '<role>' — staying local`, or `… no remote-hosts declared — staying local`).
|
|
37
39
|
|
|
38
40
|
## 2. What is declared where
|
|
39
41
|
|
|
@@ -151,23 +151,20 @@ discipline as `createSpiralCarryoverIssue`).
|
|
|
151
151
|
|
|
152
152
|
> Gate: Only run if `persistence` is `true` in Session Config. Skip silently otherwise.
|
|
153
153
|
|
|
154
|
-
After STATE.md is finalized with `status: completed` (Phase 3.4) and Recommendations are written (Phase 3.7a), release the distributed session-lock
|
|
154
|
+
After STATE.md is finalized with `status: completed` (Phase 3.4) and Recommendations are written (Phase 3.7a), release the distributed session-lock with ONE verifying command:
|
|
155
155
|
|
|
156
|
-
```
|
|
157
|
-
|
|
158
|
-
// sessionId is the physical raw value established by session-start Phase 1.2
|
|
159
|
-
// and stored in .orchestrator/session.lock `session_id`. It is not STATE.md
|
|
160
|
-
// `session:` or `semantic_session_id`, both of which are attribution labels.
|
|
161
|
-
const rawSessionId = sessionId;
|
|
162
|
-
const result = release({ sessionId: rawSessionId, repoRoot: process.cwd() });
|
|
163
|
-
// result.ok is always true unless a filesystem error occurred.
|
|
164
|
-
// result.deleted === true → lock file removed successfully.
|
|
165
|
-
// result.deleted === false → lock was absent or had a different raw session_id.
|
|
156
|
+
```bash
|
|
157
|
+
node scripts/release-session-lock.mjs --session-id "<raw session_id>" --json
|
|
166
158
|
```
|
|
167
159
|
|
|
168
|
-
|
|
160
|
+
`--session-id` is the PHYSICAL raw value established by session-start Phase 1.2 and stored in `.orchestrator/session.lock` `session_id` — never STATE.md `session:` and never `semantic_session_id`, both of which are attribution labels.
|
|
161
|
+
|
|
162
|
+
The command releases the lock only when that raw id owns it, emits the terminal `orchestrator.session.lock.released` breadcrumb, and then RE-READS the lock path: **exit 0 means the lock is provably gone.** It replaces the hand-executed `release()` this phase used to prescribe, which deleted the lock silently — the SessionEnd hook then found `status: 'absent'` and emitted nothing either, so a lock lifecycle ended with no terminal event at all (#1395: 5 `lock.acquired` against 0 `lock.released` in this repo). `outcome: "absent"` is a normal exit-0 result (idempotent re-run; nothing was released, so no event is written).
|
|
163
|
+
|
|
164
|
+
A non-zero exit is a WARN, never a blocker — log `⚠ session-lock: <outcome>` and continue; the TTL provides automatic expiry for the next session:
|
|
169
165
|
|
|
170
|
-
|
|
166
|
+
- **exit 1** — the lock is owned by a different raw session id, so nothing was touched. That is ambiguous by design: do **not** retry with an equal `semantic_session_id`, STATE.md `session`, or owner proof. Leave that live lock for its TTL/Reaper lifecycle.
|
|
167
|
+
- **exit 2** — system error (unreadable/corrupt lock, owner-proof mismatch, the lock still present after release, or the breadcrumb could not be written).
|
|
171
168
|
|
|
172
169
|
The lock is released here — AFTER all STATE.md writes are complete and BEFORE the commit is staged in Phase 4.1. This ordering ensures a clean handover when the current raw owner releases it: the lock file is absent from the working tree when the commit is assembled, so it is not accidentally staged.
|
|
173
170
|
|
|
@@ -260,7 +257,7 @@ swallow it with `|| true`.
|
|
|
260
257
|
|
|
261
258
|
## Phase 5: Issue Cleanup
|
|
262
259
|
|
|
263
|
-
> Always runs. Closes resolved issues (
|
|
260
|
+
> Always runs. Closes resolved issues via the single `node "$PLUGIN_ROOT/scripts/lib/issue-close-strip-labels.mjs" --close [--vcs gitlab|github] [-R <spec>] <id>...` CLI call (strips `status:*` labels, closes, then re-reads each id; a `closed: false` in its per-id JSON output is a finding, not done — #308), updates partially-done issues, and in Step 3 FILES the Phase 1.65 gate's carry-list — the deferred `createSpiralCarryoverIssue` call for SPIRAL/FAILED items and the `markOpenQuestionAnsweredOnDisk` write live here, not in Phase 1.65 (atomicity). Step 3b folds non-exempt over-cap creations into one `[Backlog-Sammel]` collector under the `issue-budget` cap; discovery findings from Phase 1.5 are filed at the end. Full procedure: [`references/phase-5-issue-cleanup.md`](references/phase-5-issue-cleanup.md).
|
|
264
261
|
|
|
265
262
|
## Phase 6: Final Report
|
|
266
263
|
|
|
@@ -280,7 +277,7 @@ Present to the user the **Session Summary**: Completed / Carried Over / Dropped
|
|
|
280
277
|
| `references/phase-3-documentation-updates.md` | Phase 3 full procedural body — final heartbeat (#590-3), 3.0 Defensive Cleanup, 3.1 SSOT files, 3.2/3.2a docs + handover, 3.3 rules freshness, 3.4/3.4a STATE.md write + snapshot cleanup, 3.45 Telemetry Flush, 3.5/3.5a/3.6.x memory + learnings + tail dispatcher, 3.7/3.7a/3.7b/3.7c/3.7d metrics, recommendations, durable commit, vault board, session-eval |
|
|
281
278
|
| `phase-3-2-docs-verification.md` | Phase 3.2 full procedural body — docs-tasks load, SESSION_START_REF, per-task loop, mode-gated report, Documentation Coverage block |
|
|
282
279
|
| `learning-patterns.md` | Phases 3.5a + 3.6 extraction heuristics, confidence updates, passive decay, and JSONL write procedure |
|
|
283
|
-
| `phase-3-6-tail.md` | Phase 3.6.x tail — detail procedures for the tail phases: 3.6.3 Memory-Proposals Collection (`collectProposals` + AUQ multiSelect + `promoteAndClear`, composing `writeApproved` + `clearProposalsJsonl` behind a mechanical write-before-clear guard, #828), **3.6.4 Expired-Learnings Sweep — MECHANICAL since 2026-09-09**: after `planTailPhases()`, call `runTailPhases({ repoRoot, plan })` from `scripts/lib/session-end/tail-runner.mjs` (delegating to `runExpiredSweep`) and report `result['3.6.4']` (`ran`, `scanned`, `archived`); the event `orchestrator.learnings.sweep_applied` is the proof it ran (Epic #723 B4), **3.6.5 Auto-Dream — RETIRED** and **3.6.7 Auto-Dialectic — RETIRED** (both replaced by the session-start `maintenance-due` probe, `checkMaintenanceDue` in `scripts/lib/maintenance-due-banner.mjs`; headings kept as two-line stubs because other docs cite them), 3.6.6 Skill-Applied Judge (#645 L3 — `runSkillJudge
|
|
280
|
+
| `phase-3-6-tail.md` | Phase 3.6.x tail — detail procedures for the tail phases: 3.6.3 Memory-Proposals Collection (`collectProposals` + AUQ multiSelect + `promoteAndClear`, composing `writeApproved` + `clearProposalsJsonl` behind a mechanical write-before-clear guard, #828), **3.6.4 Expired-Learnings Sweep — MECHANICAL since 2026-09-09**: after `planTailPhases()`, call `runTailPhases({ repoRoot, plan })` from `scripts/lib/session-end/tail-runner.mjs` (delegating to `runExpiredSweep`) and report `result['3.6.4']` (`ran`, `scanned`, `archived`); the event `orchestrator.learnings.sweep_applied` is the proof it ran (Epic #723 B4), **3.6.5 Auto-Dream — RETIRED** and **3.6.7 Auto-Dialectic — RETIRED** (both replaced by the session-start `maintenance-due` probe, `checkMaintenanceDue` in `scripts/lib/maintenance-due-banner.mjs`; headings kept as two-line stubs because other docs cite them), 3.6.6 Skill-Applied Judge (#645 L3 — two calls, not one: `buildSkillEvidence` from `scripts/lib/skill-evidence-window.mjs` renders this session's transcript into the evidence window, then `runSkillJudge` judges it and the coordinator writes each judgment via `appendSkillJudgment`; on empty evidence text `runSkillJudge` returns `status: 'no-evidence'` and does NOT dispatch, #1399 — the exact call shape is in the sub-file, do not reconstruct it here), 3.6.8 Reconciliation Rule Proposals (#696 FA3 — `runReconcile` + AUQ + `writeApprovedRules`). Loaded on demand by the SKILL.md skip-plan dispatcher (#724) — only phases with `run: true` in the `planTailPhases()` plan execute |
|
|
284
281
|
| `scripts/lib/session-end/phase-skip.mjs` | Phase 3.6.x tail skip-plan aggregator (#724) — `planTailPhases({repoRoot, config, sessionId, platform})` → `{plan, skippedReport}`; side-effect-free (reconcile/sweep via dry-run — no writes), never-throws (per-phase probe error fail-opens to `run: true`). Since 2026-09-09 it plans FOUR phases, not six: the 3.6.5 (auto-dream) and 3.6.7 (auto-dialectic) deciders were removed with those phases' retirement. Its APPLY half for 3.6.4 is `scripts/lib/session-end/tail-runner.mjs` (`runTailPhases`, `runExpiredSweep`) — the planner fails OPEN, the runner fails CLOSED |
|
|
285
282
|
| `references/phase-3-documentation-updates.md` § 3.45 | Telemetry Flush (advisory, #844; MECHANICAL since #1138 — `hooks/on-session-end.mjs` calls `flush()` itself at the end of every teardown and emits an `orchestrator.telemetry.flush` breadcrumb, so this phase is the DESCRIPTION and the fallback, never the trigger; a coordinator that skips it changes nothing) — `flush()` from `scripts/lib/telemetry/sync.mjs` drains the host-local send-queue fire-and-forget; no config key (send-gate is `resolveConsent()` inside the module, fail-closed); skip when `persistence: false`; never-throw + ~3s-bounded, offline → bounded oldest-dropped queue, optional `Telemetry: sent/queued/gated` close-summary line, NEVER an error banner; runs late in the close after Phase 3.7 |
|
|
286
283
|
| `session-metrics-write.md` | Phase 3.7 JSONL append, vault-mirror invocation, durable narrative mirror (`mirrorNarrative`, #675), and behavior matrix |
|
|
@@ -292,7 +289,7 @@ Present to the user the **Session Summary**: Completed / Carried Over / Dropped
|
|
|
292
289
|
| (inline) Phase 4 | Commit & Push — stage individually (PSA-004), commit, push to origin, then the `github-mirror-push` block (4 states: not-a-repo / no `github` remote / pushed / push failed) |
|
|
293
290
|
| `references/phase-4a-worktree-cleanup.md` | Phase 4a full procedural body — auto-promoted-worktree detection (`detectAutoPromotedWorktree`, marker-keyed since #1069), clean-check, clean auto-remove path, dirty 3-option AUQ (`Behalten`/`Löschen`/`Manuell`), PSA-003 + #490 ordering rationale |
|
|
294
291
|
| `references/phase-4b-worktree-orphan-sweep.md` | Phase 4b full procedural body — `checkWorktreeOrphans()` read-only proposal set, the coordinator-rendered AUQ, opt-in `worktree-orphans.enabled` gate |
|
|
295
|
-
| `references/phase-5-issue-cleanup.md` | Phase 5 full procedural body — close resolved issues (`
|
|
292
|
+
| `references/phase-5-issue-cleanup.md` | Phase 5 full procedural body — close resolved issues via `node "$PLUGIN_ROOT/scripts/lib/issue-close-strip-labels.mjs" --close` (strip `status:*` → close → re-read; `closed: false` is a finding, not done, #308), Step 3 filing of the Phase 1.65 carry-list incl. the deferred `createSpiralCarryoverIssue` and `markOpenQuestionAnsweredOnDisk`, Step 3b `[Backlog-Sammel]` overflow, discovery-issue creation |
|
|
296
293
|
| `references/phase-5-issue-cleanup.md` § Step 3b.2 (issue-budget reconcile) | Phase 5 issue-budget cross-check — `reconcileIssueBudget({ repoRoot, record, sessionId, rawSessionId, config })` from `scripts/lib/issue-budget-reconcile.mjs` runs on the in-memory session record BEFORE it is appended to `sessions.jsonl`, `emitIssueBudgetReconciled` records `orchestrator.issue_budget.reconciled`, and `formatIssueBudgetReconcileWarn(result)` prints the verdict (`match` / `no-ledger` / `escaped` / `stale-record`) in the Final Report. Ordered AFTER the overflow drain (which resets `overflow[]`) and BEFORE `reapStaleBudgetFiles` (which must not remove the file being read) |
|
|
297
294
|
| `references/session-summary-template.md` | Phase 6 Final Report — the full Session Summary template (Completed / Carried Over / Dropped at Handover Gate / New Issues / Unresolved Review Findings / Metrics incl. Docs Health + Custom Phases / Next Session Recommendations) plus the Test-delta and Documentation-Coverage anchors |
|
|
298
295
|
|
|
@@ -27,7 +27,7 @@ Agent({
|
|
|
27
27
|
run_in_background: false
|
|
28
28
|
})
|
|
29
29
|
```
|
|
30
|
-
|
|
30
|
+
Use the native delegation tools actually exposed by the current runtime, following [Platform Tool Adaptation](../_shared/platform-tools.md). If the runtime exposes no suitable delegation tool, execute the probes sequentially within the current context.
|
|
31
31
|
- Collect verified findings from the discovery output
|
|
32
32
|
- Parse the discovery output for the **findings** array and **stats** object (see Parsing callout below)
|
|
33
33
|
- Store the stats object for Phase 1.7 metrics collection (`discovery_stats` field)
|