session-orchestrator 5.11.0 → 5.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +12 -1
- package/.claude-plugin/plugin.json +4 -1
- package/.codex-plugin/agents/explorer.toml +1 -1
- package/.codex-plugin/agents/session-reviewer.toml +2 -2
- package/.codex-plugin/agents/wave-worker.toml +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/.cursor-plugin/plugin.json +1 -1
- package/.orchestrator/policy/blocked-commands.json +8 -0
- package/.orchestrator/policy/public-export.json +30 -0
- package/CHANGELOG.md +180 -3
- package/README.md +126 -117
- package/agents/analyst.md +7 -2
- package/agents/architect-reviewer.md +7 -2
- package/agents/code-implementer.md +4 -2
- package/agents/db-specialist.md +9 -2
- package/agents/dialectic-deriver.md +9 -3
- package/agents/docs-writer.md +7 -2
- package/agents/eval-judge.md +7 -1
- package/agents/qa-strategist.md +7 -2
- package/agents/security-reviewer.md +9 -2
- package/agents/session-reviewer.md +18 -2
- package/agents/skill-applied-judge.md +7 -1
- package/agents/test-writer.md +9 -2
- package/agents/ui-developer.md +9 -2
- package/agents/ux-evaluator.md +6 -1
- package/commands/session.md +4 -2
- package/commands/templates-ack.md +89 -18
- package/docs/agent-authoring.md +30 -11
- package/docs/ci-setup.md +38 -2
- package/docs/codex-setup.md +33 -9
- package/docs/components.md +13 -2
- package/docs/cursor-setup.md +3 -3
- package/docs/events-schema.md +5 -4
- package/docs/evidence-candidates.md +63 -0
- package/docs/fleet-checkin-audit.md +7 -4
- package/docs/github-mirror-protection.md +150 -3
- package/docs/install.md +102 -25
- package/docs/instruction-delivery.md +1 -1
- package/docs/migration-v4.md +2 -2
- package/docs/mods.md +118 -0
- package/docs/owner-config-schema.md +1 -0
- package/docs/plugin-architecture-v3.md +1 -1
- package/docs/pm-skills-marketplace.md +4 -3
- package/docs/rule-authoring.md +24 -13
- package/docs/scope-collision-guard.md +2 -1
- package/docs/session-config-reference.md +55 -21
- package/docs/session-config-template.md +32 -24
- package/docs/sessions-ledger-repair.md +137 -0
- package/docs/vault-docs-architecture.md +1 -1
- package/hooks/_lib/hook-import-set.json +406 -73
- package/hooks/_lib/hook-root.mjs +301 -0
- package/hooks/_lib/lock-bootstrap.mjs +434 -4
- package/hooks/_lib/orchestrator-ignore.mjs +3 -0
- package/hooks/_lib/subagent-transcript.mjs +2 -2
- package/hooks/_lib/vcs-create-matcher.mjs +13 -17
- package/hooks/cwd-change-restore.mjs +5 -1
- package/hooks/enforce-commands.mjs +4 -3
- package/hooks/enforce-scope.mjs +11 -1
- package/hooks/hooks-codex.json +48 -2
- package/hooks/hooks.json +31 -31
- package/hooks/on-session-end.mjs +38 -15
- package/hooks/on-session-start.mjs +158 -59
- package/hooks/on-stop.mjs +11 -6
- package/hooks/post-bash-issue-budget-refund.mjs +6 -1
- package/hooks/post-bash-write-verify.mjs +8 -4
- package/hooks/post-edit-validate.mjs +45 -52
- package/hooks/post-subagent-discovery-validator.mjs +2 -2
- package/hooks/post-tool-batch-wave-signal.mjs +8 -4
- package/hooks/post-tool-failure-corrective-context.mjs +5 -1
- package/hooks/pre-bash-destructive-guard.mjs +220 -8
- package/hooks/pre-bash-issue-budget.mjs +6 -1
- package/hooks/pre-bash-sessions-ledger-guard.mjs +691 -123
- package/hooks/pre-bash-staging-fence.mjs +258 -14
- package/hooks/pre-bash-templates-first.mjs +28 -27
- package/hooks/run-node.sh +1 -1
- package/hooks/skill-invocation-telemetry.mjs +15 -8
- package/hooks/subagent-telemetry.mjs +77 -14
- package/hooks/wave-scope-commit-guard.mjs +143 -14
- package/mods/so-hud/.claude-plugin/plugin.json +11 -0
- package/mods/so-hud/README.md +57 -0
- package/mods/so-hud/hooks/beacon-io.mjs +101 -0
- package/mods/so-hud/hooks/heredoc-scan.ts +394 -0
- package/mods/so-hud/hooks/hooks.json +1 -0
- package/mods/so-hud/hooks/logic.ts +182 -0
- package/mods/so-hud/hooks/register.tsx +670 -0
- package/mods/so-hud/hooks/rules.ts +2998 -0
- package/mods/so-hud/tsconfig.json +8 -0
- package/mods/so-hud/types/index.d.ts +36 -0
- package/package.json +4 -3
- package/rules/_index.md +1 -1
- package/rules/always-on/ask-via-tool.md +1 -1
- package/rules/always-on/loop-and-monitor.md +17 -194
- package/rules/always-on/parallel-sessions.md +3 -1
- package/rules/opt-in-stack/backend-data.md +1 -1
- package/rules/opt-in-stack/backend.md +1 -1
- package/rules/opt-in-stack/security-web.md +5 -1
- package/scripts/agent-usage-census.mjs +79 -0
- package/scripts/apply-session-learnings.mjs +34 -6
- package/scripts/archive-closed-prds.mjs +21 -5
- package/scripts/autopilot.mjs +11 -5
- package/scripts/backfill-abandoned-sessions.mjs +13 -3
- package/scripts/backfill-evidence-digest.mjs +2 -1
- package/scripts/backfill-learnings-from-vault.mjs +61 -6
- package/scripts/backfill-sessions.mjs +16 -2
- package/scripts/check-package-manager.mjs +26 -8
- package/scripts/check-sessions-integrity.mjs +4 -1
- package/scripts/ci/assert-vitest-green.mjs +3 -0
- package/scripts/ci/windows-smoke.mjs +201 -0
- package/scripts/close-precheck.mjs +55 -0
- package/scripts/dialectic-deriver.mjs +20 -12
- package/scripts/emit-event.mjs +2 -2
- package/scripts/emit-session.mjs +239 -20
- package/scripts/eval-session.mjs +17 -3
- package/scripts/export-hw-learnings.mjs +29 -2
- package/scripts/fleet-checkin-audit.mjs +8 -4
- package/scripts/fleet-checkin.mjs +73 -3
- package/scripts/gc-stale-worktrees.mjs +6 -1
- package/scripts/github-export.mjs +128 -0
- package/scripts/github-protection-audit.mjs +3 -1
- package/scripts/handover-queue-check.mjs +209 -0
- package/scripts/harness-audit.mjs +6 -4
- package/scripts/lib/agent-frontmatter.mjs +57 -10
- package/scripts/lib/agent-status.mjs +125 -7
- package/scripts/lib/agent-usage-census.mjs +394 -0
- package/scripts/lib/auto-dialectic.mjs +17 -10
- package/scripts/lib/auto-dream.mjs +22 -14
- package/scripts/lib/autopilot/loop.mjs +3 -1
- package/scripts/lib/autopilot/recent-runs.mjs +4 -2
- package/scripts/lib/autopilot/stall-sampler.mjs +2 -1
- package/scripts/lib/autopilot/telemetry.mjs +5 -1
- package/scripts/lib/autopilot/worktree-pipeline.mjs +5 -5
- package/scripts/lib/baseline-archetypes.mjs +10 -2
- package/scripts/lib/blocked-commands-policy.mjs +4 -3
- package/scripts/lib/build-live-signals.mjs +13 -9
- package/scripts/lib/ci-status-banner.mjs +329 -24
- package/scripts/lib/codex/plugin-contract.mjs +8 -0
- package/scripts/lib/cold-start-detector.mjs +5 -2
- package/scripts/lib/command-blocker.mjs +603 -43
- package/scripts/lib/common.mjs +3 -2
- package/scripts/lib/config/cross-repo.mjs +74 -7
- package/scripts/lib/config/host-paths.mjs +6 -2
- package/scripts/lib/config/remote-hosts.mjs +2 -2
- package/scripts/lib/config/section-extractor.mjs +65 -11
- package/scripts/lib/config/template-keys.mjs +153 -0
- package/scripts/lib/config/vault-integration.mjs +1 -0
- package/scripts/lib/config.mjs +3 -3
- package/scripts/lib/coordinator-snapshot.mjs +1 -1
- package/scripts/lib/discovery/triage-state.mjs +2 -1
- package/scripts/lib/dispatcher/rank.mjs +5 -7
- package/scripts/lib/ecosystem-health.mjs +2 -2
- package/scripts/lib/eval/engine.mjs +3 -2
- package/scripts/lib/eval/session-resolve.mjs +6 -2
- package/scripts/lib/eval/sink.mjs +4 -3
- package/scripts/lib/events-retention-banner.mjs +3 -2
- package/scripts/lib/events-schema.mjs +10 -0
- package/scripts/lib/events.mjs +23 -12
- package/scripts/lib/evolve/autonomy-verdict.mjs +4 -9
- package/scripts/lib/evolve/autopilot-effectiveness.mjs +8 -18
- package/scripts/lib/fleet-protocol.mjs +21 -0
- package/scripts/lib/gates/gate-full.mjs +172 -11
- package/scripts/lib/gates/gate-helpers.mjs +236 -23
- package/scripts/lib/git-config-drift.mjs +141 -7
- package/scripts/lib/github-export.mjs +447 -0
- package/scripts/lib/gitlab-portfolio/cli.mjs +16 -5
- package/scripts/lib/handover-queue-marks.mjs +159 -0
- package/scripts/lib/hardware-pattern-detector.mjs +103 -13
- package/scripts/lib/harness-audit/categories/category1.mjs +16 -4
- package/scripts/lib/harness-audit/categories/category4.mjs +11 -8
- package/scripts/lib/harness-audit/categories/category6.mjs +25 -5
- package/scripts/lib/harness-audit/categories/category9.mjs +10 -4
- package/scripts/lib/host-identity.mjs +9 -5
- package/scripts/lib/http-request.mjs +64 -0
- package/scripts/lib/instruction-budget-guard.mjs +30 -22
- package/scripts/lib/io.mjs +6 -3
- package/scripts/lib/issue-budget-reconcile.mjs +3 -2
- package/scripts/lib/issue-budget.mjs +36 -7
- package/scripts/lib/learnings/evidence-candidates.mjs +154 -0
- package/scripts/lib/learnings/expiry-sweep.mjs +99 -7
- package/scripts/lib/learnings/io.mjs +289 -15
- package/scripts/lib/learnings/select.mjs +53 -4
- package/scripts/lib/learnings/store-role.mjs +209 -0
- package/scripts/lib/learnings/surface.mjs +46 -7
- package/scripts/lib/ledger-path.mjs +113 -0
- package/scripts/lib/maintenance-due-banner.mjs +63 -12
- package/scripts/lib/memory-banner.mjs +5 -6
- package/scripts/lib/memory-proposals/collector.mjs +5 -2
- package/scripts/lib/memory-proposals/sink.mjs +7 -4
- package/scripts/lib/memory-proposals/store.mjs +22 -5
- package/scripts/lib/mode-selector-accuracy.mjs +2 -1
- package/scripts/lib/no-follow-open.mjs +57 -0
- package/scripts/lib/orphan-reaper/reaper-audit.mjs +4 -2
- package/scripts/lib/own-lock-root.mjs +95 -0
- package/scripts/lib/owner-interview.mjs +2 -1
- package/scripts/lib/owner-yaml.mjs +70 -7
- package/scripts/lib/platform.mjs +161 -9
- package/scripts/lib/plugin-update-banner.mjs +15 -11
- package/scripts/lib/pre-push-local-partial.mjs +117 -0
- package/scripts/lib/pre-push-remote-gate.mjs +151 -25
- package/scripts/lib/process-group.mjs +270 -34
- package/scripts/lib/project-hygiene.mjs +28 -0
- package/scripts/lib/quality-gate.mjs +33 -9
- package/scripts/lib/reconcile/backlog.mjs +29 -15
- package/scripts/lib/reconcile/engine.mjs +26 -2
- package/scripts/lib/reconcile/idempotency.mjs +65 -3
- package/scripts/lib/reconcile/rule-expiry-sweep.mjs +53 -12
- package/scripts/lib/reconcile/verdict-ledger.mjs +224 -0
- package/scripts/lib/reconcile-nudge-banner.mjs +40 -6
- package/scripts/lib/resource-probe/evaluate.mjs +32 -9
- package/scripts/lib/resource-probe/probe-platform.mjs +3 -1
- package/scripts/lib/rule-loader.mjs +20 -10
- package/scripts/lib/scope-baseline.mjs +123 -8
- package/scripts/lib/scope-echo.mjs +5 -2
- package/scripts/lib/scope-gate.mjs +55 -20
- package/scripts/lib/session-close-backfill.mjs +75 -7
- package/scripts/lib/session-end/close-precheck.mjs +151 -0
- package/scripts/lib/session-end/phase-skip.mjs +8 -7
- package/scripts/lib/session-end/tail-runner.mjs +7 -2
- package/scripts/lib/session-end/vault-orphans.mjs +205 -0
- package/scripts/lib/session-id.mjs +9 -1
- package/scripts/lib/session-identity/own-session.mjs +63 -0
- package/scripts/lib/session-invocation.mjs +14 -3
- package/scripts/lib/session-lock.mjs +114 -0
- package/scripts/lib/session-record-repair.mjs +245 -35
- package/scripts/lib/session-registry.mjs +85 -12
- package/scripts/lib/session-schema/constants.mjs +9 -0
- package/scripts/lib/session-schema/filters.mjs +56 -1
- package/scripts/lib/session-schema/validator.mjs +15 -5
- package/scripts/lib/session-start-probes.mjs +149 -22
- package/scripts/lib/session-token-rollup.mjs +2 -1
- package/scripts/lib/session-work-trees.mjs +166 -0
- package/scripts/lib/sessions-canonical.mjs +61 -3
- package/scripts/lib/sessions-integrity-banner.mjs +17 -39
- package/scripts/lib/sessions-staleness-banner.mjs +8 -5
- package/scripts/lib/sh-quote.mjs +1 -0
- package/scripts/lib/skill-evidence-window.mjs +5 -1
- package/scripts/lib/skill-health/join.mjs +10 -9
- package/scripts/lib/skill-invocations-schema.mjs +13 -0
- package/scripts/lib/skill-judgments-schema.mjs +6 -2
- package/scripts/lib/snapshot-probe.mjs +414 -0
- package/scripts/lib/so-hud/collect.mjs +174 -0
- package/scripts/lib/so-hud/runtime.mjs +87 -0
- package/scripts/lib/spiral-carryover.mjs +8 -1
- package/scripts/lib/state-md/frontmatter-mutators.mjs +266 -8
- package/scripts/lib/state-md/idle-reset.mjs +248 -0
- package/scripts/lib/state-md/mission-status.mjs +5 -1
- package/scripts/lib/state-md/recommendations.mjs +26 -4
- package/scripts/lib/state-md-peer-guard.mjs +2 -0
- package/scripts/lib/state-md.mjs +31 -8
- package/scripts/lib/sunset/walker.mjs +4 -3
- package/scripts/lib/telemetry/consent.mjs +4 -2
- package/scripts/lib/telemetry/queue.mjs +3 -3
- package/scripts/lib/telemetry/sync.mjs +45 -9
- package/scripts/lib/test-runner/artifact-paths.mjs +10 -2
- package/scripts/lib/tmux-layout/layouts.mjs +20 -3
- package/scripts/lib/ux-grill/paths.mjs +5 -1
- package/scripts/lib/validate/check-agents.mjs +37 -6
- package/scripts/lib/validate/check-component-paths.mjs +25 -1
- package/scripts/lib/validate/check-guard-requires-parity.mjs +3 -3
- package/scripts/lib/validate/check-hooks-symmetry.mjs +53 -32
- package/scripts/lib/validate/check-json-files.mjs +3 -1
- package/scripts/lib/validate/check-learning-provenance.mjs +3 -2
- package/scripts/lib/validate/check-learnings-shell-writes.mjs +10 -4
- package/scripts/lib/validate/check-metrics-ledger-root.mjs +234 -0
- package/scripts/lib/validate/check-owner-leakage.mjs +159 -11
- package/scripts/lib/validate/check-test-posix-shell.mjs +246 -0
- package/scripts/lib/validate/check-unwired-features.mjs +4 -2
- package/scripts/lib/validate/check-vcs-repo-flag-positional.mjs +58 -0
- package/scripts/lib/validate/check-vcs-repo-flag.mjs +259 -73
- package/scripts/lib/validate/check-workflow-continue-on-error.mjs +63 -0
- package/scripts/lib/validate/confidential-names.mjs +3 -1
- package/scripts/lib/vault-mirror/migrate-ids.mjs +321 -0
- package/scripts/lib/vault-mirror/namespace.mjs +22 -9
- package/scripts/lib/vault-mirror/process.mjs +197 -65
- package/scripts/lib/vault-mirror/prune.mjs +5 -2
- package/scripts/lib/vault-mirror/render-learnings.mjs +8 -7
- package/scripts/lib/vault-mirror/render-sessions.mjs +36 -53
- package/scripts/lib/vault-mirror/retention.mjs +2 -2
- package/scripts/lib/vault-mirror/utils.mjs +106 -13
- package/scripts/lib/vault-staleness-banner.mjs +4 -7
- package/scripts/lib/vault-status/board-writer.mjs +34 -5
- package/scripts/lib/vault-status/narrative-mirror.mjs +66 -7
- package/scripts/lib/wave-executor/foreign-dispatch.mjs +20 -0
- package/scripts/lib/win-process-tree.mjs +137 -0
- package/scripts/lib/workspace.mjs +1 -1
- package/scripts/lib/worktree/constants.mjs +1 -1
- package/scripts/lib/worktree/lifecycle.mjs +1 -1
- package/scripts/lib/worktree/listing.mjs +1 -1
- package/scripts/lib/zx.mjs +50 -0
- package/scripts/mcp-server.mjs +16 -7
- package/scripts/mcp-server.sh +13 -83
- package/scripts/measure-policy-cache-effectiveness.mjs +7 -5
- package/scripts/memory-propose.mjs +10 -3
- package/scripts/migrate-cold-start-seed.mjs +7 -0
- package/scripts/migrate-legacy-learnings.sh +22 -0
- package/scripts/migrate-sessions-jsonl.mjs +16 -2
- package/scripts/migrate-vault-paths.mjs +29 -13
- package/scripts/parse-config.mjs +71 -14
- package/scripts/print-learnings-index.mjs +25 -2
- package/scripts/release-session-lock.mjs +94 -7
- package/scripts/release.mjs +451 -24
- package/scripts/relocate-vault-corpus.mjs +11 -7
- package/scripts/repair-invalid-sessions.mjs +68 -3
- package/scripts/resolve-session-invocation.mjs +57 -8
- package/scripts/run-migrate-v2-cross-repo.mjs +56 -1
- package/scripts/run-quality-gate.mjs +33 -7
- package/scripts/self-update.mjs +56 -7
- package/scripts/session-commit-files.mjs +133 -0
- package/scripts/site-numbers.mjs +52 -9
- package/scripts/snapshot-probe.mjs +156 -0
- package/scripts/so-hud.mjs +68 -0
- package/scripts/state-md-idle-reset.mjs +82 -0
- package/scripts/sweep-expired-learnings.mjs +54 -6
- package/scripts/sweep-expired-rules.mjs +4 -1
- package/scripts/validate-plugin.mjs +22 -0
- package/scripts/vault-integration-watcher.mjs +54 -6
- package/scripts/vault-mirror.mjs +9 -0
- package/scripts/vault-orphans.mjs +171 -0
- package/skills/_shared/fleet-protocol.md +166 -3
- package/skills/_shared/instruction-file-resolution.md +1 -1
- package/skills/_shared/monitor-patterns.md +44 -25
- package/skills/_shared/platform-tools.md +7 -0
- package/skills/_shared/state-ownership.md +5 -4
- package/skills/architecture/references/domain-model.md +1 -1
- package/skills/autopilot/SKILL.md +3 -2
- package/skills/bootstrap/SKILL.md +3 -1
- package/skills/bootstrap/_shared-template.md +4 -4
- package/skills/bootstrap/fast-template.md +1 -1
- package/skills/bootstrap/private-contract.md +1 -1
- package/skills/bootstrap/references/bootstrap-retroactive-flow.md +17 -9
- package/skills/bootstrap/templates/agents/project-code-review.md +5 -1
- package/skills/bootstrap/templates/agents/project-discovery.md +5 -1
- package/skills/bootstrap/templates/agents/project-quality-gate.md +5 -1
- package/skills/claude-md-drift-check/SKILL.md +3 -3
- package/skills/claude-md-drift-check/checker.mjs +61 -101
- package/skills/close/SKILL.md +20 -7
- package/skills/discovery/probes/docs-staleness.mjs +7 -5
- package/skills/discovery/probes/frontend-slop.mjs +7 -3
- package/skills/discovery/probes/ssot-code-diff.mjs +5 -3
- package/skills/discovery/probes/supply-chain-slopcheck.mjs +10 -8
- package/skills/discovery/probes/vault-narrative-staleness.mjs +30 -10
- package/skills/discovery/probes/vault-staleness.mjs +5 -3
- package/skills/discovery/probes-session.md +3 -0
- package/skills/discovery/probes-vault.md +2 -0
- package/skills/dispatcher/SKILL.md +1 -1
- package/skills/eval/SKILL.md +3 -1
- package/skills/evolve/SKILL.md +2 -0
- package/skills/evolve/references/evolve-analyze-mode.md +3 -3
- package/skills/evolve/references/evolve-dialectic-mode.md +1 -1
- package/skills/gitlab-ops/SKILL.md +7 -6
- package/skills/gitlab-portfolio/SKILL.md +2 -2
- package/skills/go/SKILL.md +13 -1
- package/skills/npm-publish/SKILL.md +5 -2
- package/skills/plan/SKILL.md +23 -10
- package/skills/plan/mode-feature.md +13 -8
- package/skills/plan/mode-new.md +6 -14
- package/skills/plan/prd-feature-template.md +19 -0
- package/skills/plan/prd-full-template.md +12 -5
- package/skills/plan/prd-premortem-panel.md +48 -0
- package/skills/plan/prd-reviewer-prompt.md +11 -3
- package/skills/plan/soul.md +1 -1
- package/skills/portfolio/SKILL.md +5 -6
- package/skills/quality-gates/SKILL.md +2 -4
- package/skills/reconcile/SKILL.md +27 -4
- package/skills/release/SKILL.md +16 -8
- package/skills/session-end/SKILL.md +97 -13
- package/skills/session-end/phase-3-2-docs-verification.md +1 -0
- package/skills/session-end/phase-3-6-tail.md +11 -7
- package/skills/session-end/phase-3-7a-recommendations.md +9 -2
- package/skills/session-end/plan-verification.md +16 -1
- package/skills/session-end/references/phase-3-7e-vault-orphans.md +63 -0
- package/skills/session-end/references/phase-3-documentation-updates.md +25 -13
- package/skills/session-end/references/phase-5-issue-cleanup.md +1 -1
- package/skills/session-end/references/session-summary-template.md +8 -0
- package/skills/session-end/session-metrics-write.md +7 -4
- package/skills/session-plan/SKILL.md +5 -3
- package/skills/session-start/SKILL.md +10 -4
- package/skills/session-start/phase-4-5-resource-health.md +11 -5
- package/skills/session-start/phase-7-5-mode-selector.md +3 -1
- package/skills/session-start/phase-8-5-express-path.md +4 -3
- package/skills/session-start/references/phase-1-2-session-lock.md +7 -5
- package/skills/session-start/references/phase-1-5-session-continuity.md +13 -5
- 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 +3 -2
- package/skills/tmux-layout/SKILL.md +2 -2
- package/skills/vault-sync/validator.mjs +3 -2
- package/skills/wave-executor/SKILL.md +5 -4
- package/skills/wave-executor/references/wave-executor-state-init.md +3 -3
- package/skills/wave-executor/references/wave-loop-dispatch.md +11 -5
- package/skills/wave-executor/references/wave-loop-review.md +13 -5
- package/skills/wave-executor/references/wave-loop-scope-manifest.md +1 -1
- package/AGENTS.md +0 -207
- package/assets/og-card.png +0 -0
- package/assets/og-card.svg +0 -47
- package/assets/wave-lifecycle.svg +0 -98
|
@@ -32,6 +32,8 @@ Validation runs automatically via `scripts/parse-config.mjs` → `scripts/valida
|
|
|
32
32
|
|
|
33
33
|
Bypass via `SO_SKIP_CONFIG_VALIDATION=1`. Missing fields can be patched into an existing config file via `/bootstrap --retroactive`.
|
|
34
34
|
|
|
35
|
+
**Unknown keys (#1562).** `parse-config.mjs` compares every top-level key of the local `## Session Config` block with the keys of the plugin's own `docs/session-config-template.md` (the starter block plus the opt-in baseline, read by `knownTemplateKeys()` in `scripts/lib/config/template-keys.mjs`) and with every top-level key `parseSessionConfig()` emits, so `skill-evolution` and `dispatcher-autonomy` (parsed from the whole file) count as known inside the block too. It reads the block the way the runtime parser does (`collectTopLevelKeyLines()` in `scripts/lib/config/section-extractor.mjs`): keys inside an HTML comment are ignored, nested-block headers count as top-level keys, indented children do not. Each unknown key produces one stderr line, `⚠ <file>:<line>: unknown Session Config key '<key>' — …`, under `enforcement: warn` and `strict`; it never refuses the run, because a key from a newer plugin version is not a defect of the repo. `enforcement: off` silences it. If the template cannot be read, one line says the check did not run, so "not measured" never reads as "no unknown keys". stdout JSON is unchanged.
|
|
36
|
+
|
|
35
37
|
## Parser Gotcha: No-Inline-Comment Block Headers
|
|
36
38
|
|
|
37
39
|
**General contract, applies to every nested (block-shaped) Session Config key, not just the handful annotated below.** A top-level block key like `eval:`, `custom-phases:`, `moc-staleness:`, `context-coverage:`, or `worktree-orphans:` is opened by the shared bold-tolerant matcher `matchBlockHeader(line, key)` (`scripts/lib/config/block-header.mjs`) rather than by a per-key regex. As of the #830 generalisation, **38 files** under `scripts/lib/config/` import it — 37 block-shaped parsers (`auto-dream`, `broken-window`, `cold-start`, `config-protection`, `context-coverage`, `cross-repo`, `custom-phases`, `dialectic`, `discovery-validator`, `dispatcher-autonomy`, `dispatcher-autonomy-capture`, `docs-orchestrator`, `docs-staleness`, `drift-check`, `eval`, `events-rotation`, `evolve`, `frontend-slop-hook`, `gitlab-portfolio`, `handover-gate`, `loop-guard`, `memory`, `moc-staleness`, `persona-gate-wave`, `reconcile`, `skill-evolution`, `slopcheck`, `state-md-lock`, `templates-first`, `test`, `vault-integration`, `vault-mirror-quality`, `vault-staleness`, `vault-sync`, `verification-auto-fix`, `wave-reviewers`, `worktree-orphans`) plus `block-header.mjs` itself — confirmed via `grep -rln "matchBlockHeader" scripts/lib/config/ | wc -l` → `38`.
|
|
@@ -114,7 +116,7 @@ Raw caps are the `agents-per-wave` value BEFORE the session-type override resolv
|
|
|
114
116
|
| Read/write API | `readSessionProfile` / `setSessionProfile` / `SESSION_PROFILE_FIELD` in `scripts/lib/state-md.mjs` |
|
|
115
117
|
| Absent means | No profile. Never an empty string, never `none` — `readSessionProfile` returns `null` |
|
|
116
118
|
| Session record | Optional `session_profile` field (`scripts/lib/session-schema/constants.mjs` `OPTIONAL_FIELDS`); records without it validate unchanged |
|
|
117
|
-
| Defined values | `ultradeep` (7 waves, coordinator-direct Synthesis-Gate at wave 2) — spec:
|
|
119
|
+
| Defined values | `ultradeep` (7 waves, coordinator-direct Synthesis-Gate at wave 2) — spec: the internal ultradeep session-profile PRD (2026-09-06, not in the public mirror) |
|
|
118
120
|
|
|
119
121
|
`session-type` NEVER becomes `ultradeep`: that value is a closed set in `scripts/lib/session-schema/constants.mjs`, `scripts/lib/wave-sizing.mjs` and `scripts/lib/session-close-backfill.mjs`, and an unknown member degrades SILENTLY there (telemetry maps it to `"other"`, the close-backfill labels it `housekeeping`). The profile field exists so no closed set has to change.
|
|
120
122
|
|
|
@@ -163,8 +165,10 @@ agent-mapping: { impl: cursor:composer-2.5, test: cursor:cursor-grok-4.6-high, s
|
|
|
163
165
|
| Field | Type | Default | Description |
|
|
164
166
|
|-------|------|---------|-------------|
|
|
165
167
|
| `vcs` | string | auto-detect | Version control platform: `github` or `gitlab`. Auto-detected from git remote URL if not set. |
|
|
168
|
+
| `project-name` | string | none | Informational. `/bootstrap` writes it (`skills/bootstrap/fast-template.md`, `templates/_minimal/CLAUDE.md.tmpl`) to name the repo for human and agent readers; no runtime code reads it. Documented so the unknown-key report (#1562) does not flag it. |
|
|
169
|
+
| `vault` | object | none | Product-vault registration (`path`, `product-domain`, `persona-db`) that `/bootstrap` appends for repos with product data (`skills/bootstrap/_shared-template.md`). Its presence is the marker `hasVaultConfig()` (`scripts/lib/product-repo-detect.mjs`) reads so bootstrap does not ask again; `parseSessionConfig()` does not emit it. Distinct from `vault-integration`. |
|
|
166
170
|
| `gitlab-host` | string | from remote | Custom GitLab hostname. Only needed if the host cannot be inferred from the git remote URL. |
|
|
167
|
-
| `mirror` | string | absent (`null`) | GitHub mirror push at session-end Phase 4.4 (`skills/session-end/SKILL.md` § 4.4, read via `parse-config.mjs`). `github` or **absent** → push `HEAD` to the `github` remote when one exists (absent keeps the pre-#1034 remote-based behaviour, so keyless repos keep mirroring). `none` → never push, even with a `github` remote; unlike other string keys an explicit `none` parses to the string `'none'`, not `null`. Any other value → WARN, nothing pushed, exit 1. Only `none` switches the push off: `mirror: null` parses as an absent key and therefore pushes when a `github` remote exists. Without a `github` remote the key is not read at all — the block skips with exit 0 before it. Not a per-commit hook: the push happens once per `/close`. |
|
|
171
|
+
| `mirror` | string | absent (`null`) | GitHub mirror push at session-end Phase 4.4 (`skills/session-end/SKILL.md` § 4.4, read via `parse-config.mjs`). `github` or **absent** → push `HEAD` to the `github` remote when one exists (absent keeps the pre-#1034 remote-based behaviour, so keyless repos keep mirroring). `none` → never push, even with a `github` remote; unlike other string keys an explicit `none` parses to the string `'none'`, not `null`. `curated` (#1631, #1594) → never push `HEAD`. Only when `HEAD` is on branch `main` AND equals `git ls-remote origin refs/heads/main` (any other state skips with exit 0, nothing pushed — GitHub main deploys production), `node scripts/github-export.mjs --source <that sha>` builds ONE squash commit on `refs/heads/github-export`: source tree minus the paths in `.orchestrator/policy/public-export.json`, parent ALWAYS the public GitHub main (`refs/remotes/github/main`, required to equal `git ls-remote github refs/heads/main`), trailer `Source-Commit: <sha>`, strict leakage scan plus excluded-path citation guard on the snapshot. Export exit 3 = nothing to publish (no push); exit 1 (refused) → WARN, nothing pushed, exit 1. The push is bound to the scanned sha (read from the export's stdout only): the push URL of `github` must equal its fetch URL, then `github-export.mjs --verify <sha> --source <origin main>` re-checks it right before the push (one commit on the `ls-remote`-verified github main, trailer == origin's main, no excluded path in its tree), and a plain fast-forward `git push github <sha>:refs/heads/main` sends exactly that sha. Details: `docs/github-mirror-protection.md` § Curated public mirror. The private history is never reachable from the public ref. Any other value → WARN, nothing pushed, exit 1 (plugin copies that predate `curated` treat it the same way). Only `none` switches the push off: `mirror: null` parses as an absent key and therefore pushes when a `github` remote exists. Without a `github` remote the key is not read at all — the block skips with exit 0 before it. Not a per-commit hook: the push happens once per `/close`. |
|
|
168
172
|
| `cross-repos` | list | none | Related repositories under `~/Projects/`. The orchestrator checks their git state and critical issues during session start. |
|
|
169
173
|
| `cross-repo.projects` | list | `[]` | Repos to process when running cross-repo maintenance scripts (`run-migrate-v2-cross-repo.mjs`, `vault-integration-watcher.mjs`, `promote-vault-strict.mjs`). Each entry is a path (absolute, `~`-prefixed, or bare name resolved under `~/Projects/`). When this list is empty or absent, those scripts emit a one-line notice and exit 0 — they never error on an empty list. Example: `[~/Projects/my-app, ~/Projects/another-app]`. |
|
|
170
174
|
| `pencil` | string | none | Path to a `.pen` design file (relative to project root). Enables design-code alignment reviews after Impl-Core and Impl-Polish waves. |
|
|
@@ -207,7 +211,7 @@ cross-repo:
|
|
|
207
211
|
projects: [~/Projects/my-app, ~/Projects/another-app] # INLINE list only
|
|
208
212
|
```
|
|
209
213
|
|
|
210
|
-
Parsed by `_parseCrossRepo()` (`scripts/lib/config/cross-repo.mjs:
|
|
214
|
+
Parsed by `_parseCrossRepo()` (`scripts/lib/config/cross-repo.mjs:98-132`). Only the inline form on the `projects:` line is read — a block list of `- ` items under `projects:` yields `[]` without a warning (measured 2026-09-18). An entry containing anything outside `[A-Za-z0-9._~/-]` (a shell metacharacter, a space) is dropped with one stderr line `cross-repo: rejected project entry with shell metacharacter: …`. Absent, empty, `none` or `null` → `[]`. Readers additionally drop entries resolving outside the confinement root (`getConfinementRoot()`), which is also the root the host-wide sweeps scan. It resolves host-locally (#1562), highest first: env `CROSS_REPO_CONFINEMENT_ROOT` (test-only) > env `SO_PROJECTS_ROOT` > `owner.yaml` `paths.projects-root` > `~/Projects`. A leading `~` is expanded on the two host-local tiers; a value that is still not absolute is ignored with one stderr WARN naming its source, and the next tier is tried. It is deliberately not a Session Config key: a personal absolute path does not belong in a committed CLAUDE.md (#653). Read by `scripts/run-migrate-v2-cross-repo.mjs`, `scripts/vault-integration-watcher.mjs`, `scripts/promote-vault-strict.mjs`, and — as a secondary repo source after the filesystem scan — `scripts/lib/dispatcher/enumerate.mjs:303`.
|
|
211
215
|
|
|
212
216
|
## Auto-Skill Dispatch (#337)
|
|
213
217
|
|
|
@@ -304,11 +308,11 @@ issue-budget:
|
|
|
304
308
|
| `issue-budget.mode` | string | `strict` | `strict` blocks over-cap creations (exit 2 from the hook) and parks them as overflow; `warn` allows them with a stderr notice; `off` disables the gate entirely (no counter is written). |
|
|
305
309
|
| `issue-budget.overflow` | string | `collect-issue` | Where session-end drains parked creations. `collect-issue` files exactly ONE `[Backlog-Sammel] <session-id>, N zurückgestellte Punkte` issue (`type::backlog`, `priority::low`) whose body is a checklist of the parked items; `vault-note` writes a single Markdown file under `vault/00-inbox/` instead. |
|
|
306
310
|
|
|
307
|
-
**Override resolution (#1163-adjacent, load-bearing split).** `_parseIssueBudget()` (`scripts/lib/config/issue-budget.mjs`) returns TWO keys: `"max-per-session"` (always a plain number — the resolved `.default`, so the three existing consumers that treat it as a number never see `[object Object]`) and `"max-per-session-raw"` (the full parsed value, number or override object). `loadIssueBudgetConfig()` (`scripts/lib/issue-budget.mjs`) resolves the effective cap for the CURRENT session by reading `session-type:` off the active STATE.md frontmatter (`readSessionTypeFromStateMd()`, never throws — a missing/unparseable STATE.md yields `null`, read as "use the default") and calling `resolveMaxPerSession(cfg, sessionType)`, whose precedence is `raw[sessionType] ?? raw.default ?? cfg['max-per-session']`. The resolved session type is also returned as `"session-type-resolved"` (`null` when STATE.md carries none) so a caller can tell the resolution actually happened rather than fallen through.
|
|
311
|
+
**Override resolution (#1163-adjacent, load-bearing split).** `_parseIssueBudget()` (`scripts/lib/config/issue-budget.mjs`) returns TWO keys: `"max-per-session"` (always a plain number — the resolved `.default`, so the three existing consumers that treat it as a number never see `[object Object]`) and `"max-per-session-raw"` (the full parsed value, number or override object). `loadIssueBudgetConfig()` (`scripts/lib/issue-budget.mjs`) resolves the effective cap for the CURRENT session by reading `session-type:` off the active STATE.md frontmatter (`readSessionTypeFromStateMd()`, never throws — a missing/unparseable STATE.md yields `null`, read as "use the default"; since #1636 a STATE.md that `resolveStateMdWriteTarget()` rejects as foreign counts as missing, so a peer's `session-type` never sets this session's cap) and calling `resolveMaxPerSession(cfg, sessionType)`, whose precedence is `raw[sessionType] ?? raw.default ?? cfg['max-per-session']`. The resolved session type is also returned as `"session-type-resolved"` (`null` when STATE.md carries none) so a caller can tell the resolution actually happened rather than fallen through.
|
|
308
312
|
|
|
309
313
|
**Exemptions (load-bearing).** `priority::critical`, the carryover class (`[Carryover]`, `[SPIRAL]`/`[FAILED]`, `type::carryover`, a bare `carryover` label) and `broken-window` closure issues bypass the cap unconditionally. Without those exemptions the cap would break the standing session-end promises in `skills/session-end/SKILL.md` (Phase 1.8 "non-deselectable" SPIRAL/FAILED carryover, and the Critical Rule "ALWAYS create issues for unfinished PLANNED work"). Exempt creations are counted in the state file's `exempt` field for observability but never blocked.
|
|
310
314
|
|
|
311
|
-
**Counter file:** `.orchestrator/runtime/issue-budget/<sha256(sessionId)[0..16]>.json` — `{ sessionId, count, exempt, overflow: [...] }`, ONE file per session (#1141: the former single `issue-budget.json` slot was reset by whichever session wrote last, so two sessions in one working copy silently disabled each other's cap). Identity-less callers still use the legacy flat path; `budgetStatePath(repoRoot, sessionId)` in `scripts/lib/issue-budget.mjs` is the resolver.
|
|
315
|
+
**Counter file:** `.orchestrator/runtime/issue-budget/<sha256(sessionId)[0..16]>.json` — `{ sessionId, count, exempt, overflow: [...] }`, ONE file per session (#1141: the former single `issue-budget.json` slot was reset by whichever session wrote last, so two sessions in one working copy silently disabled each other's cap). Identity-less callers still use the legacy flat path; `budgetStatePath(repoRoot, sessionId)` in `scripts/lib/issue-budget.mjs` is the resolver. It roots both paths at `resolveEventsLedgerRoot(repoRoot)` (#1729): a linked worktree maps to its main checkout, every other root stays as it is. The hook (launch directory, usually the main checkout) and session-end (the worktree) therefore open the same file, and `reapStaleBudgetFiles` sweeps where the files are.
|
|
312
316
|
|
|
313
317
|
**Close-time reconcile (#1163).** The cap is enforced by a PreToolUse hook, which only sees the shell routes it pattern-matches — every unmatched creation route (a GUI-created issue, an unmatched CLI form) is a silent zero: no ledger line, no error, indistinguishable from "created nothing". `scripts/lib/issue-budget-reconcile.mjs` closes that gap at session-end by comparing two independently-produced numbers: `recorded` (`record.issues_created.length` from the session record) against `charged + exempt` (summed over BOTH the semantic-id and raw-id ledger keys — which key a given session's counter file used depends on a condition, `resolveIssueBudgetSessionId`, that can change mid-session). It emits `orchestrator.issue_budget.reconciled` with a `verdict`: `match` (recorded ≤ charged + exempt), `escaped` (`recorded > charged + exempt` — some creations bypassed the hook), `no-ledger` (`recorded > 0` and no counter file existed under either key — the hook never ran once), or `stale-record` (a ledger exists but the record shows 0 recorded with a positive charge). Fail-open by contract: nothing in this module throws, and a close-time cross-check that could abort the close would be strictly worse than one that reports `no-ledger`.
|
|
314
318
|
|
|
@@ -357,9 +361,9 @@ slopcheck:
|
|
|
357
361
|
| `auto-commit-per-wave` | boolean | `false` | **Planned, currently a no-op (#1219).** Intended to commit each wave's work after the Quality-Lite gate passes, as checkpoints against data loss from `git stash` collisions in parallel sessions; when `false`, all work is committed at session-end via `/close`. Today no code reads the flag for a commit: the procedural sequence (`scripts/lib/auto-commit.mjs`) does not exist, and the only consumer is the contract prose in `skills/wave-executor/references/wave-loop-review.md`. Setting it to `true` changes nothing — no commit per wave, and no session-start warning is emitted by any script or hook (measured 2026-10-05: `rg "not yet active" skills scripts hooks` hits only that prose file). The key is declared in `docs/session-config-template.md` and allowlisted in `check-unwired-features.mjs`; `parse-config` drops it. <!-- path-check: planned #1219 --> |
|
|
358
362
|
|
|
359
363
|
**Guard coverage (moved from CLAUDE.md Critical Gotchas, 2026-09-25).** **Destructive-Command Guard is active in main + subagent waves on harnesses with a compatible command hook.** On Claude Code, and through the supported Cursor and Pi bridges, `hooks/pre-bash-destructive-guard.mjs` applies `.orchestrator/policy/blocked-commands.json`:
|
|
360
|
-
**
|
|
361
|
-
The 16th is `ledger-delete-protected` (#1401): `rm`/`mv`/`unlink` on `.orchestrator/metrics/**` — added after a wave agent deleted a rotated 53.896-line ledger archive that no rule covered, because `rm` existed only as the pattern `rm -rf` and the redirect denylist's `*.jsonl` did not match `events.jsonl.1`.
|
|
362
|
-
On Codex
|
|
364
|
+
**13 of its 17 rules are `severity: block`, the other 4 only warn** (measured 2026-10-22: `jq '[.rules[].severity]|group_by(.)|map({(.[0]):length})|add' .orchestrator/policy/blocked-commands.json`).
|
|
365
|
+
The 17th is `git-hooks-missing-in-worktree` (#1732): `git commit`/`git push` in a linked worktree whose relative `core.hooksPath` (husky: `.husky/_`) is missing there while the main checkout has it — such a worktree runs no hook at all, silently. The fix, each step as its own command: install the dependencies from the lockfile (`npm ci` / `pnpm install --frozen-lockfile` — a bare `npx husky` without them would fetch husky from the public registry past SEC-020 and leave hooks that fail on missing tools), then `npx husky`. Not judged, and on purpose not named in the deny message: a directory the guard cannot read (variable, `cd -`, a `cd` inside a subshell or command substitution, `--git-dir`/`--work-tree`, `-c core.hooksPath=…`) and an explicit skip (`--no-verify`, `commit -n`/`-nm`, `HUSKY=0`). The 16th is `ledger-delete-protected` (#1401): `rm`/`mv`/`unlink` on `.orchestrator/metrics/**` — added after a wave agent deleted a rotated 53.896-line ledger archive that no rule covered, because `rm` existed only as the pattern `rm -rf` and the redirect denylist's `*.jsonl` did not match `events.jsonl.1`.
|
|
366
|
+
On Codex the guard is wired in `hooks/hooks-codex.json` too: Codex sends shell calls as `tool_name: "Bash"` (#1669) and honours the deny — measured 2026-10-20 on codex-cli 0.160.1, a `printf x > CLAUDE.md` was blocked and the file stayed unchanged (#1691). Rule source of truth: [`.claude/rules/parallel-sessions.md`](../.claude/rules/parallel-sessions.md) (PSA-003). Per-session bypass via Session Config: `allow-destructive-ops: true` (intentional maintenance only).
|
|
363
367
|
|
|
364
368
|
### enforcement-gates: the five gate keys (#800/#915)
|
|
365
369
|
|
|
@@ -559,7 +563,7 @@ resource-thresholds:
|
|
|
559
563
|
ram-free-min-gb: 4 # soft memory signal (see precedence below)
|
|
560
564
|
ram-free-critical-gb: 2 # hard memory signal → coordinator-direct
|
|
561
565
|
cpu-load-max-pct: 90 # soft CPU signal, judged on min(1m, 5m)
|
|
562
|
-
concurrent-sessions-warn: 5 #
|
|
566
|
+
concurrent-sessions-warn: 5 # REPORTED at this many live peer SESSIONS; never a soft signal (#1654)
|
|
563
567
|
ssh-no-docker: true # when session is over SSH, steer the plan away from Docker-based tests
|
|
564
568
|
zombie-threshold-min: 30 # age (minutes) above which an idle Claude/Node process is a zombie candidate
|
|
565
569
|
```
|
|
@@ -584,8 +588,16 @@ hosts with 24-128 GB installed — gating on it fired `ram-free-critical-gb` on
|
|
|
584
588
|
comes from the session registry (`detectPeers()` — self excluded,
|
|
585
589
|
heartbeat-fresh). Until #1089 it was compared against `claude_processes_count`, a
|
|
586
590
|
measured 6x unit error (median processes:sessions = 6.0) that fired the threshold
|
|
587
|
-
on 93.6% of starts instead of 4.2%.
|
|
588
|
-
|
|
591
|
+
on 93.6% of starts instead of 4.2%. The 4.2% did not hold: re-measured on
|
|
592
|
+
2026-10-17 (#1650), `peer_count >= 5` fired on 67.4% of M4 and 25.7% of M5
|
|
593
|
+
starts since 2026-08-24, so the banner icon now follows a start wave instead.
|
|
594
|
+
Since #1654 the key is **report-only** in `evaluate()` too: reaching it adds an
|
|
595
|
+
`info:` reason but never a soft signal, so it can neither cap a wave alone nor
|
|
596
|
+
pair with another signal into a cap (HR-104 — a standing condition is no second
|
|
597
|
+
opinion). `/evolve`'s `concurrent-session-pressure` detector no longer reads it
|
|
598
|
+
either; it keys on the start-wave field `recent_starts_count`.
|
|
599
|
+
When the registry is unreadable the probe
|
|
600
|
+
falls back to the process count rescaled by that same factor (equally report-only).
|
|
589
601
|
|
|
590
602
|
**`zombie-threshold-min`** (default: `30`): When set, the resource probe runs a secondary `ps` pass that counts Claude and Node processes older than this many minutes **and** with CPU% ≤ 1%. These are "zombie candidates" — stale sessions or orphaned workers that still hold RAM. The probe exposes them via `zombie_processes_count` in the snapshot. Since #1089 this is a *soft* signal: it is reported when `zombie_processes_count >= 1` **and** there is a live peer/process context, but on its own it caps nothing — sweeping stale sessions is housekeeping advice, not a reason to shrink a wave. The reason string surfaces the threshold and count so the session-start banner gives actionable context. Set to `0` to disable zombie detection entirely (the field is omitted from the default snapshot when absent from config).
|
|
591
603
|
|
|
@@ -793,6 +805,8 @@ vault-integration:
|
|
|
793
805
|
>
|
|
794
806
|
> A leading `~` is expanded to the home directory: `path-prefix` when matching, and the `path` / `paths.vault-dir` value the two owner.yaml tiers return (`match`, `owner`). The `env` and `committed` tiers pass through unexpanded; `scripts/vault-mirror.mjs` expands its `--vault-dir` at entry, so a `~/…` from any tier reaches the vault rather than `<cwd>/~/…`.
|
|
795
807
|
>
|
|
808
|
+
> `projects-root` (#1562) — the cross-repo confinement and scan root — resolves through the same layer but has no committed tier: `SO_PROJECTS_ROOT` env > `owner.yaml` `paths.projects-root` > `~/Projects` (env `CROSS_REPO_CONFINEMENT_ROOT`, test-only, outranks all three). A leading `~` is expanded; a relative value is ignored with a WARN. Resolver: `getConfinementRoot()` in `scripts/lib/config/cross-repo.mjs`; see § `cross-repo.projects` block form.
|
|
809
|
+
>
|
|
796
810
|
> The parsed config reports which tier won as `vault-integration.vault-dir-source` (`env`, `match`, `owner`, `committed`). This keeps maintainer-specific absolute paths out of version control. Resolvers: `scripts/lib/config.mjs` (`resolveVaultDir`, exported; `scripts/vault-backfill.mjs` resolves its stub root through it, #1094), `scripts/lib/config/host-paths.mjs` (`resolveHostPath`, used for `plan-baseline-path`) and `scripts/lib/named-baseline-resolver.mjs` (the `baselines:` / `vault-dirs:` match tier).
|
|
797
811
|
>
|
|
798
812
|
> **Fake-repoRoot trap (moved from CLAUDE.md Critical Gotchas, 2026-09-25).** A throwaway invocation of a vault writer (`mirrorNarrative()`, `vault-mirror.mjs`, the board writer) with a synthetic `repoRoot` does **not** land in a sandbox — the fake repo path only fakes the SOURCE, never the DESTINATION. A reviewer in this repo walked into exactly that.
|
|
@@ -812,6 +826,26 @@ vault-integration:
|
|
|
812
826
|
| `vault-integration.vault-name` | string or null | `null` | Optional override for the per-project vault namespace segment (#660). When set (or via CLI `--vault-name`), vault writes go to `40-learnings/<vault-name>/` and `50-sessions/<vault-name>/`, sanitised to a kebab slug. When null/absent, the namespace is derived from the git origin via `deriveRepo()`. Owner-privacy leaks (personal home path / private slug / personal name) are redacted to `redacted-repo`. NOT a filesystem path → NOT host-path-resolved. **Coverage gap:** portfolio board rows are NOT yet namespace-aware — the override only reaches `40-learnings/`, `50-sessions/`, and the per-repo narrative mirror (`narrative-mirror.mjs`, #832 item 2); tracked separately in #832. Resolver: `scripts/lib/vault-mirror/namespace.mjs` (`resolveRepoNamespace`). |
|
|
813
827
|
| `vault-integration.gitlab-groups` | string[] or null | `null` | List of GitLab group paths to scan for repos missing `.vault.yaml`. Parsed by `_parseVaultIntegration` (`scripts/lib/config/vault-integration.mjs`) and consumed by `scripts/vault-backfill.mjs`. Accepts a comma string, `[a, b]`, or a YAML block list; in the inline-object form more than one group needs `[a, b]`; an empty list is `null`. YAML semantics: `null`, `~` and an empty value mean unset (as a list element too, where they are dropped), and each element loses one pair of matching quotes — `["a"]` is the group `a`, and only a quoted `"null"` element is a group named `null`. Also consumed by the `/plan retro` vault-backfill sub-mode (`skills/plan/mode-retro.md` Phase 1.6 Step 1). When null/unset and neither `--groups` nor `--yes` is passed, the backfill CLI exits 1 with `no GitLab groups specified — pass --groups <CSV> or set vault-integration.gitlab-groups in CLAUDE.md`. |
|
|
814
828
|
|
|
829
|
+
### Learnings store role (host-local, #1516)
|
|
830
|
+
|
|
831
|
+
The store role is a **host-wide** switch: it applies to every repo on this host, not to one repo. It exists for repos with versioned learnings that are synced between two hosts — only one host may rewrite `.orchestrator/metrics/learnings.jsonl` wholesale, because every rewrite replaces the whole file and two rewriting hosts conflict on every merge. Setting `inbox` on a host therefore makes that host an inbox for **all** its repos. The role is host-local and is not a Session Config key. Precedence: env `SO_LEARNINGS_STORE_ROLE=writer|inbox` > `owner.yaml` `learnings.store-role` > `writer`. Resolver: `scripts/lib/learnings/store-role.mjs` (`resolveStoreRole()`).
|
|
832
|
+
|
|
833
|
+
**Limit:** a repo that lives only on an inbox host, with no writer host anywhere, silently freezes: nothing ever sweeps, prunes or rewrites its store, and confidence updates never land — new learnings pile up in `learnings-inbox.jsonl`. There is no per-repo role. For such a repo, run the write command with a one-shot override, e.g. `SO_LEARNINGS_STORE_ROLE=writer node scripts/sweep-expired-learnings.mjs --apply`; the override ingests the inbox records on that run.
|
|
834
|
+
|
|
835
|
+
**Fail-closed:** only "nothing configured" (no env value, no owner.yaml, no `learnings` key, an empty `learnings:`, or a `learnings` section without `store-role`) means `writer`. A role that is set but unusable resolves to `invalid`: a non-empty `SO_LEARNINGS_STORE_ROLE` that is not `writer`/`inbox`, an owner.yaml that exists but cannot be read, or an owner.yaml with a top-level `learnings` key whose `store-role` does not load as a valid role (typo such as `inbx`, any key other than `store-role` such as the misspelt `store_role`, unparseable file, missing YAML parser). With an invalid role every write path for the canonical store refuses and writes nothing: `appendLearning()`, `rewriteLearnings()` (not on `dryRun`), and the applies of the expiry sweep, `pruneLearnings()` and `dropMalformedLines()` throw `LearningsStoreRoleError` before any lock, archive append or rewrite; `apply-session-learnings.mjs --apply` exits 4, `export-hw-learnings.mjs --promote` exits 1, `run-migrate-v2-cross-repo.mjs --apply` exits 4 (repos reported `status: 'refused-store-role-invalid'`), and `migrate-legacy-learnings.sh` exits 4 (`{"status":"refused_store_role_invalid"}`, also when `node` cannot resolve the role). Readers keep working. Ceiling: a top-level `learnings` mapping written in YAML flow style (`{learnings: {...}}`) is not detected, so a broken one of those still resolves to `writer`.
|
|
836
|
+
|
|
837
|
+
- **`writer`** (default): unchanged behaviour. A rewrite of the live store also takes in the inbox records it carries, and an expiry sweep or prune archives inbox records directly; the writer appends one `{id, ingested_at}` receipt line per record it ingested or archived to `learnings-inbox-ingested.jsonl`. The archive itself is not synced between hosts, so the receipt is what keeps an archived record hidden on the inbox host.
|
|
838
|
+
- **`inbox`**: the live store is never written on this host. New learnings are appended to `learnings-inbox.jsonl` beside it. Readers (`readLearnings()`, the session-start banner, the learnings index, the dialectic deriver and the auto-dialectic signal count) show the live records plus every inbox record that is not live, archived or receipted. `backfill-learnings-from-vault.mjs` counts an id found in the inbox as present (`found_in: inbox`): on an inbox host every inbox id, so a second `--apply` there appends nothing; on any other host only a visible one (not receipted, not archived), so a record that was ingested and then lost from the live store is still restored. Two readers deliberately read only the live store (#1685): `vault-mirror.mjs` (an inbox record gets its vault note on the first mirror run after ingest) and the rule-expiry sweep (an inbox-only learning-id resolves as absent: a consolidated pair stays kept, a single-entry rule falls back to its own `expires-at`).
|
|
839
|
+
- Writes that only make sense on the writer refuse on an inbox host: the expiry sweep and `pruneLearnings()` archive nothing (result field `skipped: 'store-role-inbox'`; the prune still appends new ids to the inbox; session-end 3.6.4 returns `{ran: false, reason: 'store-role-inbox'}` without `orchestrator.learnings.sweep_applied`, and `sweep-expired-learnings.mjs` emits no `orchestrator.evolve.completed` and ends its human line with `skipped=store-role-inbox`), `dropMalformedLines()` writes nothing, `export-hw-learnings.mjs --promote` exits 1, `run-migrate-v2-cross-repo.mjs --apply` exits 3 and `migrate-legacy-learnings.sh` exits 3.
|
|
840
|
+
- **Sync:** both inbox files are append-only, so the consumer's `.gitattributes` needs `merge=union` for them:
|
|
841
|
+
|
|
842
|
+
```
|
|
843
|
+
.orchestrator/metrics/learnings-inbox.jsonl merge=union
|
|
844
|
+
.orchestrator/metrics/learnings-inbox-ingested.jsonl merge=union
|
|
845
|
+
```
|
|
846
|
+
|
|
847
|
+
- **Ceilings:** an inbox host drops confidence updates, decay and sweeps of existing learnings; only records with a new id reach the writer. The inbox file is never truncated; records already ingested or archived stay in it and are hidden by their receipts.
|
|
848
|
+
|
|
815
849
|
### Environment override: `VAULT_MIRROR_CANONICAL_SUFFIX`
|
|
816
850
|
|
|
817
851
|
`vault-mirror.mjs` refuses to mirror into any directory whose `git remote get-url origin` does not end with a canonical-vault path suffix — a network-of-trust guard (#600 D2) that stops notes from silently accumulating in a wrong or typo'd vault location. A non-matching suffix fails the **whole run** (`process.exit(2)`), not a per-entry skip — mirroring even one note into the wrong place is the bug it prevents.
|
|
@@ -1059,7 +1093,7 @@ dialectic:
|
|
|
1059
1093
|
|
|
1060
1094
|
Opt-in configuration for the Standard v1 evaluation harness and the `/eval` skill (Session-Prozess-Eval). The current workflow is documented in `skills/eval/SKILL.md`.
|
|
1061
1095
|
|
|
1062
|
-
The original config proposal is in the archived PRD
|
|
1096
|
+
The original config proposal is in the archived LLM-eval PRD of 2026-07-16, §S6 (Epic #803; internal, not in the public mirror).
|
|
1063
1097
|
|
|
1064
1098
|
All fields live under a top-level `eval` object in your Session Config host file (`CLAUDE.md` or `AGENTS.md`), for example:
|
|
1065
1099
|
|
|
@@ -1260,8 +1294,8 @@ gitlab-portfolio:
|
|
|
1260
1294
|
|
|
1261
1295
|
| Field | Type | Default | Allowed values | Description |
|
|
1262
1296
|
|-------|------|---------|----------------|-------------|
|
|
1263
|
-
| `gitlab-portfolio.enabled` | boolean | `false` | `true` / `false` | Only the literal `true` enables it. When false, `/portfolio` prints `disabled` and exits 0 (`cli.mjs:
|
|
1264
|
-
| `gitlab-portfolio.mode` | string | `warn` | `warn` / `strict` / `off` | `strict` makes the CLI exit 2 when any repo failed to fetch (`cli.mjs:
|
|
1297
|
+
| `gitlab-portfolio.enabled` | boolean | `false` | `true` / `false` | Only the literal `true` enables it. When false, `/portfolio` prints `disabled` and exits 0 (`cli.mjs:195`), and session-start Phase 2.7 skips its dry-run banner (`skills/session-start/SKILL.md` § Phase 2.7). |
|
|
1298
|
+
| `gitlab-portfolio.mode` | string | `warn` | `warn` / `strict` / `off` | `strict` makes the CLI exit 2 when any repo failed to fetch (`cli.mjs:386`). `off` makes the CLI exit 0 with one line before it resolves the vault-dir or scans any repo, and writes nothing (`cli.mjs:203`, #1619); it also suppresses the session-start banner. `warn` exits 0 with partial data. An unknown value falls back to `warn`. |
|
|
1265
1299
|
| `gitlab-portfolio.stale-days` | integer | `30` | integer ≥ 1 | An open issue whose `updated_at` is older than this counts as stale (`scripts/lib/gitlab-portfolio/aggregator.mjs`). An invalid value falls back to `30`. |
|
|
1266
1300
|
| `gitlab-portfolio.critical-labels` | array of string | `[priority::critical, priority::high]` | non-empty strings | Labels that count an issue as critical; matching is scope-tolerant (`priority:critical` also matches). **Block list only:** items need their own `- ` lines indented at least 3 spaces. The inline form `critical-labels: [a, b]` is NOT read — the parser silently keeps the default (measured 2026-09-18 via `_parseGitlabPortfolio`). |
|
|
1267
1301
|
|
|
@@ -1357,7 +1391,7 @@ Opt-in declaration of ssh-reachable hosts a heavy wave role may be OFFLOADED to
|
|
|
1357
1391
|
|
|
1358
1392
|
```yaml
|
|
1359
1393
|
remote-hosts:
|
|
1360
|
-
- alias:
|
|
1394
|
+
- alias: build-host # required, SAFE slug; reaches argv as `-H <alias>`
|
|
1361
1395
|
roles-allowed: [test, ui, perf] # subset of test|ui|perf (default: all three)
|
|
1362
1396
|
repo-path: ~/Projects/Alice # optional; SAFE path; default null
|
|
1363
1397
|
claude-path: ~/.local/bin/claude # optional; SAFE path; default null
|
|
@@ -1373,7 +1407,7 @@ remote-hosts:
|
|
|
1373
1407
|
|
|
1374
1408
|
**Two enums, never conflated.** `roles-allowed` holds `agent-mapping` roles (`test`, `ui`, `perf`) — NOT wave roles (`Impl-Core`, `Quality`, …). The wave→role translation is `OFFLOADABLE_WAVE_ROLES` in `scripts/lib/wave-resource-gate.mjs`; a wave role absent from that map is local-only by default.
|
|
1375
1409
|
|
|
1376
|
-
**Placement contract.** The gate applies its offload arm only after the HR-004 heavy-repo cap, and only when the resource verdict was `reduce` or `coordinator-direct` — or, with `offload-first: true`, on `proceed` as well (see § `offload-first` below). It does NOT probe the network: the coordinator supplies a readiness witness (`remoteReady: {
|
|
1410
|
+
**Placement contract.** The gate applies its offload arm only after the HR-004 heavy-repo cap, and only when the resource verdict was `reduce` or `coordinator-direct` — or, with `offload-first: true`, on `proceed` as well (see § `offload-first` below). It does NOT probe the network: the coordinator supplies a readiness witness (`remoteReady: { 'build-host': true }`, or an async `probeFn`). With no witness, no host counts as ready and the decision 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`) is never offloaded regardless.
|
|
1377
1411
|
|
|
1378
1412
|
**agent-mapping interaction.** A declared alias is what an `agent-mapping` value of the form `<role>: ssh:<alias>` validates against; naming an undeclared host throws at parse time, naming the `ssh` channel with no target throws as for any other channel.
|
|
1379
1413
|
|
|
@@ -1396,7 +1430,7 @@ offload-first: false # true = offloadable roles go to a witnessed-ready remote
|
|
|
1396
1430
|
**Behaviour with `offload-first: true`.**
|
|
1397
1431
|
|
|
1398
1432
|
- Only roles in `OFFLOADABLE_WAVE_ROLES` (`scripts/lib/wave-resource-gate.mjs`) move: wave roles `quality` / `test` → `test`, `ui` → `ui`, `perf` → `perf`. The table is NOT extended by this switch — build, lint and audit work is covered by the Quality wave's `test` role. `impl-core`, `security-review`, `migration`, `release`, `secrets`, `incident` and `refactor-crosscut` never leave the local host.
|
|
1399
|
-
- The target is the FIRST declared `remote-hosts` entry whose `roles-allowed` carries the mapped role AND that is witnessed ready (`remoteReady: {
|
|
1433
|
+
- The target is the FIRST declared `remote-hosts` entry whose `roles-allowed` carries the mapped role AND that is witnessed ready (`remoteReady: { 'build-host': true }` or an async `probeFn`). The gate still never probes the network itself, so the witness is mandatory for the switch to have any effect: without one, nothing offloads even with the switch on.
|
|
1400
1434
|
- The HR-004 heavy-repo cap still binds the offloaded agent count.
|
|
1401
1435
|
- No ready host for the role → the verdict is unchanged (`proceed`, or the `reduce`/`coordinator-direct` the resource rules computed) and `reasons` carries `offload-first: no ready host for '<role>' — staying local`.
|
|
1402
1436
|
- Switch on but no `remote-hosts` declared → the verdict is unchanged and `reasons` carries `offload-first: true but no remote-hosts declared — staying local`. `validateSessionConfig()` (`scripts/lib/config-schema.mjs`) additionally emits a warn-level cross-field finding `offload-first-cross-field` for that combination; it never flips `ok`.
|
|
@@ -1531,7 +1565,7 @@ Implementation: `scripts/lib/recommendations-v0.mjs` → `computeV0Recommendatio
|
|
|
1531
1565
|
|
|
1532
1566
|
- Pre-v1.1 STATE.md files (produced by sessions before Epic #271 shipped) simply do not contain these fields. The parser returns `null` from `parseRecommendations`; the banner silently no-ops.
|
|
1533
1567
|
- `schema-version` is NOT bumped to `1.1` in the frontmatter — the existing `schema-version: 1` remains canonical. The v1.1 label is documentation-only, describing the additive surface.
|
|
1534
|
-
- Idle Reset (session-start Phase 1.5)
|
|
1568
|
+
- Idle Reset (session-start Phase 1.5, `scripts/state-md-idle-reset.mjs`) copies these fields into the `## Previous Session` body block of STATE.md as a human-readable block and KEEPS them in the frontmatter: they are the handoff the Phase 7.5 Mode-Selector reads after the reset (#1560). Deleting them, as the reset did before #1560, left the selector at confidence 0 after every clean `/close`. The next session-end Phase 3.7a overwrites them.
|
|
1535
1569
|
|
|
1536
1570
|
### Reader behavior
|
|
1537
1571
|
|
|
@@ -1552,8 +1586,8 @@ The banner renders with one of the following shapes:
|
|
|
1552
1586
|
|
|
1553
1587
|
- **Writer:** `skills/session-end/SKILL.md` § Phase 3.7a "Compute and Write Recommendations" (runs after Phase 3.7 sessions.jsonl write, before Phase 3.4 `status: completed` setting).
|
|
1554
1588
|
- **Reader:** `skills/session-start/SKILL.md` § Phase 1.5 "Recommendations Banner" (renders on `status: completed` branch only).
|
|
1555
|
-
- **Archival:** `skills/session-start/
|
|
1556
|
-
- **
|
|
1589
|
+
- **Archival:** `skills/session-start/references/phase-1-5-session-continuity.md` § Idle Reset rule 6 (keeps the fields in the frontmatter, prepends a readable block to `## Previous Session`).
|
|
1590
|
+
- **Mode-Selector:** `skills/session-start/phase-7-5-mode-selector.md` reads these fields through `buildLiveSignals()` (`scripts/lib/build-live-signals.mjs`) as its primary input.
|
|
1557
1591
|
|
|
1558
1592
|
## Frontend-Slop Hook (#684) — deprecated
|
|
1559
1593
|
|
|
@@ -1628,7 +1662,7 @@ reaper:
|
|
|
1628
1662
|
| `reaper.max-hook-latency-ms` | integer (≥ 1) | `50` | Ceiling the TRIGGER may delay a hook by — the config read, the marker `stat` and the detached spawn, measured with `performance.now()` in `maybeTriggerOrphanScan()`. Overrunning it writes ONE stderr WARN line and nothing else: no event (a `PostToolBatch`-class hook emitting per fire is the always-on signal HR-101 calls a broken instrument) and no abort (the child is already detached by then). The scan itself is not covered — that is the point of spawning detached. |
|
|
1629
1663
|
| `reaper.false-alarm-window` | integer (≥ 1) | `50` | Rolling window of audit decisions the HR-101 10 % false-alarm rate is judged over — a window in DECISIONS, not calendar time, so the rate has a population on quiet hosts too. Reaches the scan as `--false-alarm-window` on the detached child's argv and becomes `runOrphanScan`'s `falseAlarmWindow`; the CLI refuses `0`, which `falseAlarmRate()` would read as “every firing ever recorded” rather than as a window. |
|
|
1630
1664
|
|
|
1631
|
-
**Used by:** `hooks/post-tool-batch-wave-signal.mjs`, `hooks/on-stop.mjs` (both via `maybeTriggerOrphanScan()` from `scripts/lib/orphan-reaper/trigger.mjs`), `scripts/lib/orphan-reaper.mjs`, `scripts/lib/config/reaper.mjs` (`_parseReaper`). PRD
|
|
1665
|
+
**Used by:** `hooks/post-tool-batch-wave-signal.mjs`, `hooks/on-stop.mjs` (both via `maybeTriggerOrphanScan()` from `scripts/lib/orphan-reaper/trigger.mjs`), `scripts/lib/orphan-reaper.mjs`, `scripts/lib/config/reaper.mjs` (`_parseReaper`). the internal process-group-kill and orphan-reaper PRD (2026-09-20, not in the public mirror), Epic #1425, issue #1432.
|
|
1632
1666
|
|
|
1633
1667
|
## Gate Timeout — Path B (#1425 A3)
|
|
1634
1668
|
|
|
@@ -8,11 +8,29 @@
|
|
|
8
8
|
|
|
9
9
|
1. Pick the host file: `CLAUDE.md (or AGENTS.md on Codex CLI)`. Never both.
|
|
10
10
|
2. Add a single `## Session Config` H2. The header must match exactly — `skills/_shared/config-reading.md` parses the block under that heading.
|
|
11
|
-
3. Start from the [
|
|
11
|
+
3. Start from the [Starter config](#starter-config-copy-paste) directly below. Add opt-in blocks one at a time as you adopt features; the [Full opt-in baseline](#full-opt-in-baseline-copy-paste) near the end shows every block at once.
|
|
12
12
|
4. Run `/bootstrap --retroactive` to schema-validate the result.
|
|
13
13
|
5. Validate at any time with `node scripts/parse-config.mjs --json`.
|
|
14
14
|
|
|
15
|
-
The format is the same on every platform.
|
|
15
|
+
The format is the same on every platform. A top-level key this template does not list has no effect, so `node scripts/parse-config.mjs` reports it on stderr with file, line and key — a typo, or a key from a newer plugin version. The report appears under `enforcement: warn` and `strict` and never refuses the run; `enforcement: off` silences it.
|
|
16
|
+
|
|
17
|
+
## Starter config (copy-paste)
|
|
18
|
+
|
|
19
|
+
The smallest valid Session Config — passes `validate-config` without warnings on a typical Node/TS repo:
|
|
20
|
+
|
|
21
|
+
```yaml
|
|
22
|
+
## Session Config
|
|
23
|
+
|
|
24
|
+
test-command: npm test
|
|
25
|
+
typecheck-command: npm run typecheck
|
|
26
|
+
lint-command: npm run lint
|
|
27
|
+
agents-per-wave: 6
|
|
28
|
+
waves: 5
|
|
29
|
+
persistence: true
|
|
30
|
+
enforcement: warn
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
That's enough for `/session feature` → `/go` → `/close` to work end-to-end. Every other field falls back to documented defaults.
|
|
16
34
|
|
|
17
35
|
## Where this file lives
|
|
18
36
|
|
|
@@ -68,7 +86,7 @@ Read by: `skills/session-start/SKILL.md` (Phase 4.5), `skills/session-plan/SKILL
|
|
|
68
86
|
```yaml
|
|
69
87
|
vcs: gitlab # github | gitlab — auto-detected from remote when unset
|
|
70
88
|
gitlab-host: gitlab.example.com # only if remote URL doesn't expose it
|
|
71
|
-
mirror: github # session-end pushes to the `github` remote (none | github; absent = push if remote exists)
|
|
89
|
+
mirror: github # session-end pushes to the `github` remote (none | github | curated; absent = push if remote exists; curated = public snapshot via scripts/github-export.mjs)
|
|
72
90
|
cross-repos: [related-repo-1] # repos under ~/Projects/ to snapshot at session-start
|
|
73
91
|
pencil: path/to/design.pen # design-code alignment input
|
|
74
92
|
ecosystem-health: true # toggle health-endpoint probes
|
|
@@ -172,7 +190,7 @@ resource-thresholds:
|
|
|
172
190
|
ram-free-min-gb: 4 # below: cap agents-per-wave at 2
|
|
173
191
|
ram-free-critical-gb: 2 # below: recommend coordinator-direct
|
|
174
192
|
cpu-load-max-pct: 80 # sustained above: cap agents-per-wave at 2
|
|
175
|
-
concurrent-sessions-warn: 5 #
|
|
193
|
+
concurrent-sessions-warn: 5 # report-only: names peer sessions, never caps (#1654)
|
|
176
194
|
ssh-no-docker: true # SSH session: avoid Docker-based tests
|
|
177
195
|
zombie-threshold-min: 30 # min idle time before stale-process flag (#178)
|
|
178
196
|
```
|
|
@@ -393,7 +411,7 @@ Opt-in declaration of ssh-reachable hosts that heavy wave roles may be OFFLOADED
|
|
|
393
411
|
|
|
394
412
|
```yaml
|
|
395
413
|
remote-hosts:
|
|
396
|
-
- alias:
|
|
414
|
+
- alias: build-host # required, SAFE slug ([A-Za-z0-9._-]); reaches argv as `-H <alias>`
|
|
397
415
|
roles-allowed: [test, ui, perf] # subset of test|ui|perf (default: all three)
|
|
398
416
|
repo-path: ~/Projects/Alice # optional SAFE path (default: null)
|
|
399
417
|
claude-path: ~/.local/bin/claude # optional SAFE path (default: null)
|
|
@@ -660,24 +678,6 @@ Read by: `skills/session-plan/SKILL.md`, `skills/wave-executor/wave-loop.md`.
|
|
|
660
678
|
|
|
661
679
|
The dispatch resolution is three-tier: project agents (`.claude/agents/`) > plugin agents (`session-orchestrator:*`) > `general-purpose`.
|
|
662
680
|
|
|
663
|
-
## Full minimal baseline (copy-paste)
|
|
664
|
-
|
|
665
|
-
The smallest valid Session Config — passes `validate-config` without warnings on a typical Node/TS repo:
|
|
666
|
-
|
|
667
|
-
```yaml
|
|
668
|
-
## Session Config
|
|
669
|
-
|
|
670
|
-
test-command: npm test
|
|
671
|
-
typecheck-command: npm run typecheck
|
|
672
|
-
lint-command: npm run lint
|
|
673
|
-
agents-per-wave: 6
|
|
674
|
-
waves: 5
|
|
675
|
-
persistence: true
|
|
676
|
-
enforcement: warn
|
|
677
|
-
```
|
|
678
|
-
|
|
679
|
-
That's enough for `/session feature` → `/go` → `/close` to work end-to-end. Every other field falls back to documented defaults.
|
|
680
|
-
|
|
681
681
|
## Full opt-in baseline (copy-paste)
|
|
682
682
|
|
|
683
683
|
Everything turned on for a project that wants the full feature surface (vault, docs, drift checks, env-aware sizing). Trim to taste:
|
|
@@ -697,6 +697,7 @@ enforcement: warn
|
|
|
697
697
|
# Session structure
|
|
698
698
|
recent-commits: 20
|
|
699
699
|
special: "follow .claude/rules/parallel-sessions.md"
|
|
700
|
+
project-name: my-project # informational: written by /bootstrap, names the repo for readers; no runtime reader
|
|
700
701
|
|
|
701
702
|
# VCS & infrastructure
|
|
702
703
|
vcs: gitlab
|
|
@@ -793,6 +794,13 @@ vault-integration:
|
|
|
793
794
|
vault-name: # optional (#660) — per-project vault namespace override; null/absent → deriveRepo()
|
|
794
795
|
gitlab-groups: []
|
|
795
796
|
|
|
797
|
+
# Product-vault registration — written by /bootstrap (skills/bootstrap/_shared-template.md);
|
|
798
|
+
# its presence is the marker hasVaultConfig() reads so bootstrap does not ask again
|
|
799
|
+
vault:
|
|
800
|
+
path: ~/Projects/vault
|
|
801
|
+
product-domain: my-domain
|
|
802
|
+
persona-db:
|
|
803
|
+
|
|
796
804
|
# GitLab portfolio dashboard — /portfolio (GH #41); needs vault-integration.vault-dir
|
|
797
805
|
gitlab-portfolio:
|
|
798
806
|
enabled: false # opt-in; only the literal true enables it
|
|
@@ -890,7 +898,7 @@ custom-phases:
|
|
|
890
898
|
|
|
891
899
|
# Remote hosts — ssh-reachable offload targets for heavy roles (#1160)
|
|
892
900
|
remote-hosts:
|
|
893
|
-
- alias:
|
|
901
|
+
- alias: build-host # required, SAFE slug; reaches argv as `-H <alias>`
|
|
894
902
|
roles-allowed: [test, ui, perf] # subset of test|ui|perf (default: all three)
|
|
895
903
|
repo-path: ~/Projects/Alice # optional SAFE path (default: null)
|
|
896
904
|
claude-path: ~/.local/bin/claude # optional SAFE path (default: null)
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
# Session ledger diagnosis and repair
|
|
2
|
+
|
|
3
|
+
The session ledger is `.orchestrator/metrics/sessions.jsonl`. Diagnose schema
|
|
4
|
+
failures separately from records that vault-mirror cannot render. Repairing a
|
|
5
|
+
ledger is a write operation; a guard allowance does not authorize repairing a
|
|
6
|
+
consumer's history. Obtain a separate task before applying repairs to a real
|
|
7
|
+
consumer or vault ledger.
|
|
8
|
+
|
|
9
|
+
## Missing metrics are unknown
|
|
10
|
+
|
|
11
|
+
`validateSession` accepts an absent or `null` `effectiveness`. All three mirror
|
|
12
|
+
renderers accept that same shape and display unknown metrics as `n/a` (or the
|
|
13
|
+
existing table placeholder). A measured zero remains zero. Optional missing
|
|
14
|
+
subfields likewise remain unknown; neither rendering nor repair invents issue
|
|
15
|
+
counts or completion rates. A malformed non-object `effectiveness`, including
|
|
16
|
+
an array, still fails rendering.
|
|
17
|
+
|
|
18
|
+
Legacy V2 and V3 records can render while failing the canonical writer schema.
|
|
19
|
+
V2 lacks canonical aggregate fields; V3 uses scalar `waves` instead of the
|
|
20
|
+
required array. Those are separate schema defects, not evidence that missing
|
|
21
|
+
`effectiveness` needs a migration. An already schema-valid historical line is
|
|
22
|
+
preserved byte-for-byte by the repair helper, with one exception (#1705): a
|
|
23
|
+
valid record whose `completed_at` copies a valid, monotonic `ended_at` but is
|
|
24
|
+
still listed in `_backfill_incomplete_fields` is healed (status `repaired`,
|
|
25
|
+
counted `healed_valid`, defect `completed_at_flag_stale`; the prior list is kept
|
|
26
|
+
once in `_backfill_incomplete_fields_raw`).
|
|
27
|
+
|
|
28
|
+
## Read-only diagnosis
|
|
29
|
+
|
|
30
|
+
Use the installed plugin's actual absolute script path and the actual project
|
|
31
|
+
root. These examples use generic paths:
|
|
32
|
+
|
|
33
|
+
```sh
|
|
34
|
+
node /opt/plugin/scripts/check-sessions-integrity.mjs --repo-root /work/repo --json
|
|
35
|
+
node /opt/plugin/scripts/repair-invalid-sessions.mjs --dry-run --repo-root /work/repo --json
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
The integrity command and session-start banner evaluate validation and the real
|
|
39
|
+
mirror render path independently. The banner reports `schemaInvalid` and
|
|
40
|
+
`mirrorSkipped`; a render-only failure is not proof of a schema failure or of
|
|
41
|
+
how a record was written. A schema-invalid record that still renders produces
|
|
42
|
+
a warning; a mirror skip produces an alert. Abandoned session filtering applies
|
|
43
|
+
to this banner population.
|
|
44
|
+
|
|
45
|
+
Repair dry-run reports schema defects, projected repairs, and the lines that
|
|
46
|
+
would remain unrepairable. It is a repairability preview, not an integrity
|
|
47
|
+
verdict: exit 0 alone does not prove that every record currently validates and
|
|
48
|
+
renders. Compare named lines and defect classes, accounting for banner filters
|
|
49
|
+
and renderer-only failures rather than expecting all totals to be identical.
|
|
50
|
+
Dry-run writes nothing and is the CLI default. The CLI can diagnose a metrics
|
|
51
|
+
directory outside a Git repository; the new guard allowance for apply cannot.
|
|
52
|
+
|
|
53
|
+
## Apply and recovery
|
|
54
|
+
|
|
55
|
+
The repair CLI retains its existing contract: before replacing the file it
|
|
56
|
+
creates a mandatory `<file>.bak-<stamp>` backup, writes replacement content to
|
|
57
|
+
a temporary file, and atomically renames it over the target. It verifies the
|
|
58
|
+
result against validation and, for the canonical ledger path, integrity. Failed
|
|
59
|
+
post-verification restores the backup byte-for-byte. Repairable rows retain
|
|
60
|
+
their position; valid rows (except the stale-flag heal above) and unrepairable
|
|
61
|
+
rows retain their original bytes.
|
|
62
|
+
Duplicate IDs are observed, never deduplicated. Repeating a successful repair
|
|
63
|
+
preserves ledger bytes; `--apply` still creates a backup and atomically replaces
|
|
64
|
+
the file.
|
|
65
|
+
|
|
66
|
+
The CLI restricts targets to `<repo-root>/.orchestrator/metrics/`, resolving
|
|
67
|
+
parent symlinks for containment. Its broader CLI flags do not broaden the
|
|
68
|
+
guard's apply allowance.
|
|
69
|
+
|
|
70
|
+
| Exit | Meaning |
|
|
71
|
+
| --- | --- |
|
|
72
|
+
| 0 | Completed; no unrepairable remainder reported (dry-run may propose repairs) |
|
|
73
|
+
| 1 | Argument or target error |
|
|
74
|
+
| 2 | System or I/O error |
|
|
75
|
+
| 3 | Post-verification failed; backup restored, no repairs kept |
|
|
76
|
+
| 4 | Repairable lines handled; unrepairable lines remain unchanged |
|
|
77
|
+
|
|
78
|
+
Exit 3 takes precedence over exit 4. Inspect the summary and retained backup
|
|
79
|
+
before deciding on further work; never bypass the ledger guard to force a repair.
|
|
80
|
+
|
|
81
|
+
## Guarded apply commands
|
|
82
|
+
|
|
83
|
+
The existing exact relative form remains supported:
|
|
84
|
+
|
|
85
|
+
```sh
|
|
86
|
+
node scripts/repair-invalid-sessions.mjs --apply
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
The additional allowance accepts only the installed plugin's canonical absolute
|
|
90
|
+
repair script, with `--apply` followed by mandatory `--repo-root` and the
|
|
91
|
+
absolute current Git top-level:
|
|
92
|
+
|
|
93
|
+
```sh
|
|
94
|
+
node /opt/plugin/scripts/repair-invalid-sessions.mjs --apply --repo-root /work/repo
|
|
95
|
+
node '/opt/plugin with spaces/scripts/repair-invalid-sessions.mjs' --apply --repo-root '/work/repo with spaces'
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Use literal path values, with simple single quotes for spaces. Do not place
|
|
99
|
+
`$PWD`, environment expansions, command substitutions, escaping, redirections,
|
|
100
|
+
pipes, chained commands, or shell wrappers in the tool command. Additional
|
|
101
|
+
flags (including `--file`, `--json`, or `--dry-run`), reordered arguments,
|
|
102
|
+
alternative Node spellings, and foreign script paths do not qualify.
|
|
103
|
+
|
|
104
|
+
The script and all path components must exist at canonical absolute paths
|
|
105
|
+
without symlinks. The measured Git top-level for the hook process and payload
|
|
106
|
+
working directory must agree. The measured root must also contain both physical
|
|
107
|
+
working directories; a local `core.worktree` setting that reports a root
|
|
108
|
+
elsewhere grants no apply allowance. The hook's project resolver, including
|
|
109
|
+
launcher-provided context, must resolve to that same root. The explicit root
|
|
110
|
+
must exactly equal the measured realpath: no trailing slash, `..`, subdirectory
|
|
111
|
+
or another checkout. Omitting `--repo-root` is denied: a child CLI without the
|
|
112
|
+
hook's launcher variables could otherwise select a nested `CLAUDE.md` root.
|
|
113
|
+
Git discovery/configuration overrides,
|
|
114
|
+
a symlinked `.git` marker, context disagreement, or a failed Git measurement
|
|
115
|
+
deny the additional allowance. A sandbox Git error is a denial, not evidence
|
|
116
|
+
that the native target context works.
|
|
117
|
+
|
|
118
|
+
For synthetic mirror or repair probes, set `SO_VAULT_DIR` to a fresh temporary
|
|
119
|
+
directory before running the probe and let child processes inherit it. Apply
|
|
120
|
+
tests belong only on disposable ledgers with isolated configuration. Native
|
|
121
|
+
path and process behavior requires a suitable native runner; sandbox results
|
|
122
|
+
do not establish macOS acceptance.
|
|
123
|
+
|
|
124
|
+
The absolute repair allowance denies `--repo-root` equal to or inside the
|
|
125
|
+
realpath-resolved vault directory. The existing resolver gives precedence to
|
|
126
|
+
`SO_VAULT_DIR`, owner configuration (`vault-dirs`, then `paths.vault-dir`), then
|
|
127
|
+
the committed `vault-dir`. A measurable configured vault takes precedence over
|
|
128
|
+
the fallback. If resolution has no value, fails, or cannot be canonicalized,
|
|
129
|
+
only the realpath-resolved `<canonical HOME>/Projects/vault` and its descendants
|
|
130
|
+
are denied. Prefix neighbors such as `vault-x` and other `vault` segments under
|
|
131
|
+
`Projects` are not fallback targets. If the fallback also cannot be measured,
|
|
132
|
+
it adds no denial; the other absolute-allowance checks still apply.
|
|
133
|
+
|
|
134
|
+
This check applies only to the absolute repair allowance. The exact relative
|
|
135
|
+
repair exception is unchanged; the check is not a general vault-write boundary.
|
|
136
|
+
Vault ledgers remain forbidden repair targets under this runbook, regardless
|
|
137
|
+
of whether a command receives a guard allowance.
|
|
@@ -11,7 +11,7 @@ to recover when something breaks.
|
|
|
11
11
|
re-measured against the code that writes it — `scripts/lib/vault-status/narrative-mirror.mjs`
|
|
12
12
|
(`resolveNarrativePath`) and `scripts/lib/vault-status/board-writer.mjs` (`resolveBoardPath`) —
|
|
13
13
|
after the pre-#673/#674/#675 layout in this file was found stale by the 2026-09-06
|
|
14
|
-
360°-Audit (
|
|
14
|
+
360°-Audit (its docs-drift report; audit reports are not part of the public mirror). The `daily` skill
|
|
15
15
|
rows are gone with the skill itself (§ 5A of that audit).
|
|
16
16
|
|
|
17
17
|
---
|