session-orchestrator 5.12.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 +2 -2
- 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 +162 -4
- package/README.md +126 -127
- 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 +1 -1
- 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 +90 -17
- 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 +49 -15
- package/docs/session-config-template.md +30 -22
- 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 +139 -49
- 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 -2
- 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 +39 -27
- 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/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 +2 -2
- 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 +4 -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-id.mjs +9 -1
- package/scripts/lib/session-identity/own-session.mjs +63 -0
- package/scripts/lib/session-lock.mjs +114 -0
- package/scripts/lib/session-record-repair.mjs +245 -35
- package/scripts/lib/session-registry.mjs +72 -9
- 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 +49 -5
- package/scripts/lib/so-hud/runtime.mjs +15 -4
- 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 +14 -5
- package/scripts/lib/state-md/mission-status.mjs +5 -1
- 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 +19 -7
- package/scripts/lib/vault-mirror/process.mjs +56 -27
- package/scripts/lib/vault-mirror/prune.mjs +5 -2
- package/scripts/lib/vault-mirror/render-learnings.mjs +3 -3
- 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 +79 -0
- package/scripts/lib/vault-staleness-banner.mjs +4 -7
- package/scripts/lib/vault-status/board-writer.mjs +2 -1
- 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 +10 -17
- 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 +29 -6
- package/scripts/state-md-idle-reset.mjs +2 -1
- 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/skills/_shared/fleet-protocol.md +142 -1
- 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 +3 -2
- package/skills/architecture/references/domain-model.md +1 -1
- 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 +94 -11
- 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-documentation-updates.md +14 -12
- package/skills/session-end/references/phase-5-issue-cleanup.md +1 -1
- package/skills/session-end/references/session-summary-template.md +4 -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 +9 -3
- package/skills/session-start/phase-4-5-resource-health.md +11 -5
- 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 +2 -2
- 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/assets/og-card.png +0 -0
- package/assets/og-card.svg +0 -47
- package/assets/wave-lifecycle.svg +0 -98
|
@@ -10,11 +10,13 @@
|
|
|
10
10
|
|
|
11
11
|
Acquire a distributed session-lock to detect parallel sessions in the same repo before initializing STATE.md. This prevents two concurrent Claude/Codex sessions from stomping each other's wave state and metrics writes.
|
|
12
12
|
|
|
13
|
-
**Mechanical wiring (Epic #583, 2026-05-27):** The SessionStart hook (`hooks/on-session-start.mjs` → `hooks/_lib/lock-bootstrap.mjs`) now writes `.orchestrator/session.lock` mechanically BEFORE this skill's prose runs. The prose Phase 1.2 becomes confirmatory — it verifies the lock exists with the expected shape via `readLock({ repoRoot:
|
|
13
|
+
**Mechanical wiring (Epic #583, 2026-05-27):** The SessionStart hook (`hooks/on-session-start.mjs` → `hooks/_lib/lock-bootstrap.mjs`) now writes `.orchestrator/session.lock` mechanically BEFORE this skill's prose runs. The prose Phase 1.2 becomes confirmatory — it verifies the lock exists with the expected shape via `readLock({ repoRoot: lockRoot })`, where `lockRoot = resolveOwnLockRootFor({ sessionId }).root` (`scripts/lib/own-lock-root.mjs`, #1730): the checkout whose lock carries the raw id, so a Phase 1.2 run after `EnterWorktree` or a `cd` finds the lock it already holds instead of taking a second one beside the cwd; with no such lock it is the cwd's checkout, as before. Re-call `acquire()` only if `readLock()` returns `null` (mechanical hook failed) OR the existing lock's raw `session_id` does not exactly match the current session's raw id (a rare divergence — surface via AUQ before overwriting). A matching `semantic_session_id`, STATE.md `session`, or owner proof cannot repair that mismatch. The decision flow below still applies to all five outcomes (active / stale / corrupt / unreadable / fs-error) when the prose path needs to acquire. Every `ok: true` from `acquire()` / `forceAcquire()` also rewrites `.orchestrator/runtime/lock-owner-proof.json` for the new lock (#1541), so the `/close` release stays proof-gated; a failed proof write is reported as `result.ownerProof: { ok: false, reason }`, never thrown.
|
|
14
14
|
|
|
15
15
|
```javascript
|
|
16
16
|
import { acquire, forceAcquire } from 'scripts/lib/session-lock.mjs';
|
|
17
|
-
|
|
17
|
+
import { resolveOwnLockRootFor } from 'scripts/lib/own-lock-root.mjs';
|
|
18
|
+
const lockRoot = resolveOwnLockRootFor({ sessionId }).root;
|
|
19
|
+
const result = acquire({ sessionId, mode: sessionType, ttlHours: 4, repoRoot: lockRoot });
|
|
18
20
|
```
|
|
19
21
|
|
|
20
22
|
Where `sessionId` is the physical raw identity for this invocation: the native harness-provided raw id, or a generated UUID when no trustworthy raw id exists. It is the only value passed to `acquire()` and the only live lock/registry ownership key. `semanticSessionId` may be recorded separately as an attribution/history label and may populate STATE.md `session`; neither label is a substitute for `sessionId`. `sessionType` is the session mode (`housekeeping`, `feature`, or `deep`).
|
|
@@ -51,7 +53,7 @@ Where `sessionId` is the physical raw identity for this invocation: the native h
|
|
|
51
53
|
Reply with the number of your choice.
|
|
52
54
|
```
|
|
53
55
|
- On **Abort**: exit session-start cleanly with a brief stderr note (`session-lock: aborted — active lock held by session_id=<id>`). Do NOT initialize STATE.md.
|
|
54
|
-
- On **Force-take**: call `forceAcquire({ sessionId, mode: sessionType, ttlHours: 4, repoRoot:
|
|
56
|
+
- On **Force-take**: call `forceAcquire({ sessionId, mode: sessionType, ttlHours: 4, repoRoot: lockRoot })`. After Phase 1.5 initializes STATE.md, append a deviation via `appendDeviation()`:
|
|
55
57
|
`Force-took session lock from session_id=<existingLock.session_id>, age=<ageHours>h, mode=<existingLock.mode>, pid=<existingLock.pid>`. Continue.
|
|
56
58
|
|
|
57
59
|
3. **`result.ok === false`** with `reason === 'stale-heartbeat'`:
|
|
@@ -99,13 +101,13 @@ Where `sessionId` is the physical raw identity for this invocation: the native h
|
|
|
99
101
|
2. Abort — stops here and writes nothing. The lock file `.orchestrator/session.lock` (it names the process that wrote it) tells you whether that session is still alive.
|
|
100
102
|
Reply with the number of your choice.
|
|
101
103
|
```
|
|
102
|
-
- On **Reclaim**: call `forceAcquire({ sessionId, mode: sessionType, ttlHours: 4, repoRoot:
|
|
104
|
+
- On **Reclaim**: call `forceAcquire({ sessionId, mode: sessionType, ttlHours: 4, repoRoot: lockRoot, onlyIfNotLive: true })` — the single-winner takeover (#1494): if another session reclaimed the lock in the meantime, it returns that session's `active` result instead of overwriting it; follow the decision flow for the returned result. After Phase 1.5 initializes STATE.md, append a deviation:
|
|
103
105
|
`Stale-lock reclaim: replaced lock from session_id=<existingLock.session_id>, age=<ageHours>h, pid=<existingLock.pid>`. Continue.
|
|
104
106
|
- On **Abort**: exit cleanly.
|
|
105
107
|
|
|
106
108
|
4. **`result.ok === false`** with `reason === 'corrupt'`:
|
|
107
109
|
- `.orchestrator/session.lock` exists but is not a lock record (invalid JSON, or JSON without the lock shape). It can never be a live lock: every writer writes it atomically (tmp+link / tmp+rename), so no reader sees a half-written file. The SessionStart hook already reclaims this case mechanically and prints one stderr WARN; reaching it here means the hook did not run or its reclaim failed.
|
|
108
|
-
- No AUQ — the answer is derivable (AUQ-001.2). Log `⚠ session-lock: .orchestrator/session.lock was not a valid lock record — reclaiming.`, call `forceAcquire({ sessionId, mode: sessionType, ttlHours: 4, repoRoot:
|
|
110
|
+
- No AUQ — the answer is derivable (AUQ-001.2). Log `⚠ session-lock: .orchestrator/session.lock was not a valid lock record — reclaiming.`, call `forceAcquire({ sessionId, mode: sessionType, ttlHours: 4, repoRoot: lockRoot, onlyIfNotLive: true })` (a non-ok result means another session got there first — follow the decision flow for it), and after Phase 1.5 append a deviation: `Corrupt-lock reclaim: replaced an unparseable .orchestrator/session.lock`. Continue.
|
|
109
111
|
|
|
110
112
|
5. **`result.ok === false`** with `reason === 'unreadable'`:
|
|
111
113
|
- `.orchestrator/session.lock` exists but cannot be read (`result.error`, e.g. `EACCES`, or `EISDIR` for a directory at the path). Nothing was written, and it is never reclaimed: no lock writer produces such a path, and a directory may hold content nobody here created (PSA-003). The SessionStart hook already printed a stderr WARN and emitted `orchestrator.session.lock.read_anomaly` (`status: 'unreadable'`).
|
|
@@ -104,7 +104,7 @@ Run it as code, after the Recommendations Banner — never rewrite STATE.md by h
|
|
|
104
104
|
node "${PLUGIN_ROOT}/scripts/state-md-idle-reset.mjs" --json
|
|
105
105
|
```
|
|
106
106
|
|
|
107
|
-
It prints `{"changed": <bool>, "reason": <string|null>, "path": "<STATE.md>"}` and writes under the STATE.md lock (`resetStateMdToIdle` in `scripts/lib/state-md/idle-reset.mjs`). A no-op exits 0 with `changed: false` and one of `reason: "status-not-completed"` (any status other than `completed`), `"unparseable"` (no frontmatter)
|
|
107
|
+
It prints `{"changed": <bool>, "reason": <string|null>, "path": "<STATE.md>"}` and writes under the STATE.md lock (`resetStateMdToIdle` in `scripts/lib/state-md/idle-reset.mjs`). A no-op exits 0 with `changed: false` and one of `reason: "status-not-completed"` (any status other than `completed`), `"unparseable"` (no frontmatter) `"missing"` (no or empty STATE.md) or `"foreign-state-md"` (the only STATE.md belongs to another platform's foreign session and is left untouched, #1618 — treat like `missing`). Exit 2 means `--repo-root` is not a directory, the write failed, or a STATE.md guard refused it (`reason` names the guard) — report it as a Deviation and continue. Without a new `## Wave History` (a coordinator-direct session) the earlier `## Previous Session` body is kept below the new recommendations copy. The rules below are the contract that code implements:
|
|
108
108
|
|
|
109
109
|
1. Set frontmatter `status: idle`.
|
|
110
110
|
2. Clear `current-wave` (set to `0`).
|
|
@@ -247,7 +247,7 @@ Also read `<state-dir>/STATUS.md` if it exists for additional project-level cont
|
|
|
247
247
|
|
|
248
248
|
### Session-Profile Persistence (`ultradeep` alias)
|
|
249
249
|
|
|
250
|
-
> **CONTENT ADDITION — not part of the #1157 move.** Applies to the STATE.md initialization write (Phase 1b), the same write that sets `session-type`. Rationale, wave shape and budgets:
|
|
250
|
+
> **CONTENT ADDITION — not part of the #1157 move.** Applies to the STATE.md initialization write (Phase 1b), the same write that sets `session-type`. Rationale, wave shape and budgets: the internal ultradeep session-profile PRD (2026-09-06, not in the public mirror) — do not restate them here. Alias resolution: `commands/session.md` § Argument alias.
|
|
251
251
|
|
|
252
252
|
When the `/session` argument was the `ultradeep` ALIAS, persist the profile alongside the type:
|
|
253
253
|
|
|
@@ -29,7 +29,7 @@ await sweepBoard({
|
|
|
29
29
|
});
|
|
30
30
|
```
|
|
31
31
|
|
|
32
|
-
`sweepBoard` enumerates candidate repos host-wide (`enumerateCandidates` — confinement root `~/Projects` plus any `cross-repo.projects` config-declared repos, issue #676), re-derives the board status for every BUSY repo it finds (`in-progress` or `force-closed`, never `frei`), unions in THIS repo so its own row is always re-derived, and writes the board in one idempotent merge. **A crashed session in ANY repo now renders `force-closed` on the board from THIS repo's session-start** — not only from that repo's own next session-start/-end.
|
|
32
|
+
`sweepBoard` enumerates candidate repos host-wide (`enumerateCandidates` — confinement root from `getConfinementRoot()`, `~/Projects` unless the host sets `SO_PROJECTS_ROOT` or owner.yaml `paths.projects-root` (#1562), plus any `cross-repo.projects` config-declared repos, issue #676), re-derives the board status for every BUSY repo it finds (`in-progress` or `force-closed`, never `frei`), unions in THIS repo so its own row is always re-derived, and writes the board in one idempotent merge. **A crashed session in ANY repo now renders `force-closed` on the board from THIS repo's session-start** — not only from that repo's own next session-start/-end.
|
|
33
33
|
|
|
34
34
|
> **Call-site contract:** `sweepBoard` is now the primary call. `explicitStatus` is **inert for `'in-progress'`** — `collectRows` only honors an explicit per-repo `status: 'closed'` override; THIS repo's `in-progress` row is always rendered from its own **live `session.lock` lease** (already written/heartbeated by Phase 1.2's `acquire()`), never from a passed-in status string. If constructing `repos` manually for a narrower sweep, `collectRows` requires `{ repoRoot }` object descriptors and silently skips bare path strings (`board-writer.mjs` `collectRows` guard) — `sweepBoard`/`buildSweepRepos` already produce the correct shape, so this only matters for a hand-rolled `mirrorBoard({ repos })` call.
|
|
35
35
|
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
a. `.orchestrator/policy/quality-gates.json` — preferred source when present.
|
|
10
10
|
b. Session Config `test-command` / `typecheck-command` / `lint-command` — fallback.
|
|
11
11
|
c. Hardcoded defaults: `npm test`, `npm run typecheck`, `npm run lint`.
|
|
12
|
-
|
|
12
|
+
Execute each complete configured shell command and report `fail` if its binary is missing (exit 127; on Windows `cmd.exe` exits 1, also `fail`). Correct the command or explicitly set it to `skip` in Session Config; explicit `skip` remains `skip`. Report baseline failures without blocking the session.
|
|
13
13
|
3. **Pencil design status**: if `pencil` is configured, verify the `.pen` file exists at the configured path. Report: "Pencil design configured at [path] — design-code alignment reviews will run after Impl-Core and Impl-Polish waves." If file not found, warn: "Pencil path configured but file not found at [path]."
|
|
14
14
|
4. **Plugin freshness**: Determine the session-orchestrator plugin directory (navigate up from this skill's base directory to the plugin root). Run `git -C <plugin-dir> log -1 --format="%ci"` to get the last commit date. If older than `plugin-freshness-days` (default: 30) days, flag a warning in the Session Overview: `"⚠ Session Orchestrator plugin last updated [N] days ago — consider pulling the latest version."` Non-blocking — present in overview, don't halt.
|
|
15
15
|
|
|
@@ -42,6 +42,7 @@
|
|
|
42
42
|
|
|
43
43
|
When `result.status === 'red'`, render a banner alongside the bootstrap-lock and vault-staleness warnings:
|
|
44
44
|
- **Red** (`status === 'red'`): `"🚨 CI RED on HEAD (pipeline #<currentPipelineId>) — last green: #<lastGreen.pipelineId> (commit <SHA-7>, <redCount> pipelines ago). Failing job: <failingJobName>"`
|
|
45
|
+
- **Red on GitHub** (`details.cliUsed === 'gh'`, #1727/#856): GitHub has no pipelines, so the line carries no `(pipeline #…)` and `lastGreen` has no `pipelineId`: `"🚨 CI RED on HEAD — last green: commit <SHA-7> (<ageCommits> commit(s) ago). Failing job: <failingJobName>"`. When the history query failed (`details.reason === 'last-green-query-failed'`) the middle part reads `— last green: unknown (history query failed)`; when no green commit lies in the 16-commit window it is absent. The probe reads the red verdict without the history (`checkCiStatus({ deferHistory: true })`, #1728) and fetches the history in its follow-up, inside what is left of `PROBE_BUDGET_MS`: when that runs out (`details.reason === 'last-green-budget-exceeded'`) the red line still renders, with `— last green: unknown (history not read within the probe budget)`. The reading is for the LOCAL HEAD (`git rev-parse HEAD`), never the remote default branch; an unpushed HEAD is `unknown` with reason `head-sha-not-on-remote` (Unknown branch below).
|
|
45
46
|
- **Green with soft failures** (`status === 'green'` AND `result.allowFailureJobs` is present): `"⚠ CI green on HEAD, but <N> allow_failure job(s) FAILED: <names>. A pipeline reports success regardless of these — a job red on every run stays invisible at the pipeline level."` Render this even though the pipeline passed: the whole point is that pipeline status cannot express it.
|
|
46
47
|
- **Degraded** (`result.degraded` present): `"⚠ ci-status: CI status for HEAD could not be determined (<reason>) — state UNKNOWN, not \"green\"."` — the probe builds this message itself; render it verbatim.
|
|
47
48
|
- **Unknown** (`status === 'unknown'` — HEAD carries no pipeline, e.g. unpushed local commits; #1337): NOT silent — `"<mark> ci-status: CI status for HEAD could not be determined (<reason>) — <hint>"`, where `<mark>` is `🚨` when the last PUSHED commit's pipeline verdict is `red` (severity `alert`) and `⚠` otherwise (severity `warn`). Silence here would read as green when the pushed commit may actually be red.
|
|
@@ -166,7 +167,7 @@
|
|
|
166
167
|
|
|
167
168
|
The return contract has THREE states, not the usual two, and the third is the point: `null` means either "no mirror remote" or "queried successfully, zero open issues"; `{ severity, message, count, repoSpec, issues }` means N > 0; and `{ severity, message, repoSpec, degraded }` means the query did NOT succeed, where `degraded` is one of `cli-missing | timeout | parse-error | auth-error | query-failed`. Render `result.message` verbatim in either non-null case. A `degraded` result must be read as *"the mirror's state is unknown"* — never as clean. `scripts/lib/ci-status-banner.mjs` **was** the unmigrated side until #1031: it collapsed missing-CLI, timeout and unparseable output onto the same `null` an absent remote returns, which in the banner contract reads as "all clear", and that collapse is why this gap survived unseen. It now carries its own `DEGRADED_REASONS` enum (`cli-missing | timeout | parse-error | query-failed | git-error`) and the same three-state shape — see the CI-status paragraph above. The two enums are deliberately NOT merged: each names the failures its own probe can actually reach.
|
|
168
169
|
|
|
169
|
-
The git-config-drift probe (`scripts/lib/git-config-drift.mjs`, `checkGitConfigDrift`) runs in the Phase 4 banner family as `id: 'git-config-drift'` in `scripts/lib/session-start-probes.mjs` `PROBES` (`env` defaults to `process.env`). It reads `git config --local --list` with a FILTERED environment, so an ambient `GIT_DIR` cannot redirect the probe itself at a foreign repository and let it call this one clean. **Three states, not two:** `null` = read and clean; `{ severity: 'warn', message, findings }` = at least one unexpected entry (a local identity override, a local `commit.gpgsign`, a remote on a reserved fixture host, a `core.hooksPath` not pointing at `.husky/_` AND not DECLARED by the repo, or `GIT_DIR`/`GIT_WORK_TREE` set in the environment); `{ …, degraded }` = the config could NOT be read — **never render that as clean.** Render `result.message` alongside the other banners.
|
|
170
|
+
The git-config-drift probe (`scripts/lib/git-config-drift.mjs`, `checkGitConfigDrift`) runs in the Phase 4 banner family as `id: 'git-config-drift'` in `scripts/lib/session-start-probes.mjs` `PROBES` (`env` defaults to `process.env`). It reads `git config --local --list` with a FILTERED environment, so an ambient `GIT_DIR` cannot redirect the probe itself at a foreign repository and let it call this one clean. **Three states, not two:** `null` = read and clean; `{ severity: 'warn', message, findings }` = at least one unexpected entry (a local identity override, a local `commit.gpgsign`, a remote on a reserved fixture host, a `core.hooksPath` not pointing at `.husky/_` AND not DECLARED by the repo, a `.husky/_` hooksPath whose directory git resolves to in THIS working tree has no `pre-push`, has a non-executable `pre-push`, or lacks the `h` helper (each defect is a separate finding) — the gitignored runtime a fresh linked worktree never gets, so commit and push run ungated; repair with the target repo’s canonical PM per SEC-020: `pnpm exec husky` (pnpm), `npm exec --no -- husky` (npm), `yarn exec husky` (Yarn), or `bun run husky` (Bun); when unknown, identify the canonical PM first (#1688) —, a `core.hooksPath` set in NO scope while the repo tracks `.husky/<hook>` files, so git never runs them (`hooks-path-unset`, same repair; a failed query is reported as unknown; #1692), or `GIT_DIR`/`GIT_WORK_TREE` set in the environment); `{ …, degraded }` = the config could NOT be read — **never render that as clean.** Render `result.message` alongside the other banners.
|
|
170
171
|
|
|
171
172
|
The `core.hooksPath` trigger is narrower than "points somewhere other than `.husky/_`" — a hooksPath the repo DECLARES is accepted without any config key. Declared means `git ls-files -- <hooksPath>` finds at least one TRACKED file DIRECTLY under it (not nested deeper) whose BASENAME is a real git hook name (`pre-commit`, `pre-push`, `commit-msg`, …) — a tracked directory of ordinary source, or an untracked file merely named like a hook, does not count. This document does not restate the hook-name list — `GIT_HOOK_NAMES` in `scripts/lib/git-config-drift.mjs` (sourced from `git help hooks`) is the single declaration site (#1158 review N1 — the first cut of this rule accepted any tracked file under the path at all, which a fixture-planted `scripts/pre-commit` left untracked, sitting beside ordinary tracked source, would have bypassed silently).
|
|
172
173
|
|
|
@@ -55,9 +55,9 @@ The skill prints a one-line tmux command. Paste it into a SECOND terminal (do no
|
|
|
55
55
|
| 2 | STATE.md tail | `tail -F <state-dir>/STATE.md` |
|
|
56
56
|
| 3 | CI watch (poll-loop wrapper) | `while true; do clear; glab ci status -R <spec> --output json \| jq -r '.jobs[] \| ...'; sleep 15; done` |
|
|
57
57
|
| 4 | events.jsonl wave/gate filter | `tail -F .orchestrator/metrics/events.jsonl \| jq --unbuffered 'select(.event \| test("wave\|gate\|spiral"))'` |
|
|
58
|
-
| 5 | agent-status telemetry (#565, only with `--with-status-pane`) | `while true; do clear; node --input-type=module -e '...readCurrentStatus({repoRoot:
|
|
58
|
+
| 5 | agent-status telemetry (#565, only with `--with-status-pane`) | `while true; do clear; node --input-type=module -e '...readCurrentStatus({repoRoot:"<own lock root>"})...'; sleep 2; done` — the root holding this session's own lock, resolved once when the layout is rendered (`resolveOwnLockRootFor`, #1730); none found → `process.cwd()` |
|
|
59
59
|
|
|
60
|
-
Pane 5 renders through `readCurrentStatus()` (`scripts/lib/agent-status.mjs`), never the cache file directly (#1342): its header line `agent-status · source=<live-map|rebuilt-log|stale-cache|absent> · at=<ISO|n/a>` says WHAT is on screen, in four states — `live-map` = every shown record came from the current-map cache and the cache was VERIFIED against the ledger, `rebuilt-log` = at least one record was taken from the append-only `agent-status.jsonl` because the cache was behind (or unreadable) for that agent, `stale-cache` = no ledger to check the cache against, `absent` = nothing on disk yet (no ledger, no cache), which stays unmarked because a fresh repo is not a degradation — plus a leading `⚠` and the words STALE / `DEGRADED: <reasons>` in TEXT whenever the view is not verified live. The render command is exported as `buildStatusPaneRenderCommand()` so it can be executed once (outside the poll loop) in tests.
|
|
60
|
+
Pane 5 renders through `readCurrentStatus()` (`scripts/lib/agent-status.mjs`), never the cache file directly (#1342): its header line `agent-status · source=<live-map|rebuilt-log|stale-cache|absent> · at=<ISO|n/a>` says WHAT is on screen, in four states — `live-map` = every shown record came from the current-map cache and the cache was VERIFIED against the ledger, `rebuilt-log` = at least one record was taken from the append-only `agent-status.jsonl` because the cache was behind (or unreadable) for that agent, `stale-cache` = no ledger to check the cache against, `absent` = nothing on disk yet (no ledger, no cache), which stays unmarked because a fresh repo is not a degradation — plus a leading `⚠` and the words STALE / `DEGRADED: <reasons>` in TEXT whenever the view is not verified live. The render command is exported as `buildStatusPaneRenderCommand({ repoRoot })` so it can be executed once (outside the poll loop) in tests; without `repoRoot` it reads its own cwd.
|
|
61
61
|
|
|
62
62
|
`<state-dir>` is resolved via `resolveStateDir()` from `scripts/lib/platform.mjs` (`.claude/`, `.codex/`, or `.cursor/`).
|
|
63
63
|
Pane 3 command is vcs-aware (`glab` for gitlab, `gh pr checks` for github, informational `echo` fallback when no CLI).
|
|
@@ -62,6 +62,7 @@ import { fileURLToPath } from 'node:url';
|
|
|
62
62
|
import { z } from 'zod';
|
|
63
63
|
import YAML from 'yaml';
|
|
64
64
|
import { resolveInstructionFile } from '../../scripts/lib/common.mjs';
|
|
65
|
+
import { resolveLedgerPath } from '../../scripts/lib/ledger-path.mjs';
|
|
65
66
|
import { findSessionConfigBlock } from '../../scripts/lib/config/section-extractor.mjs';
|
|
66
67
|
import {
|
|
67
68
|
computeSchemaHash,
|
|
@@ -810,7 +811,7 @@ if (filesSkippedNoFrontmatter > 0) {
|
|
|
810
811
|
// ── mode=baseline ─────────────────────────────────────────────────────────
|
|
811
812
|
// Serialize current errors + warnings as a snapshot, then exit 0.
|
|
812
813
|
if (mode === 'baseline') {
|
|
813
|
-
const baselinePath =
|
|
814
|
+
const baselinePath = resolveLedgerPath('.orchestrator/metrics/vault-sync-baseline.json', vaultDir);
|
|
814
815
|
writeBaseline(baselinePath, {
|
|
815
816
|
errors,
|
|
816
817
|
warnings,
|
|
@@ -827,7 +828,7 @@ if (mode === 'baseline') {
|
|
|
827
828
|
// ── mode=diff ─────────────────────────────────────────────────────────────
|
|
828
829
|
// Read a prior baseline, compute set-differences, emit JSON diff to stdout.
|
|
829
830
|
if (mode === 'diff') {
|
|
830
|
-
const baselinePath =
|
|
831
|
+
const baselinePath = resolveLedgerPath('.orchestrator/metrics/vault-sync-baseline.json', vaultDir);
|
|
831
832
|
const baseline = readBaseline(baselinePath);
|
|
832
833
|
|
|
833
834
|
if (!baseline) {
|
|
@@ -113,13 +113,13 @@ Example: If user said `/go focus on API endpoints`, each agent prompt includes:
|
|
|
113
113
|
|
|
114
114
|
## Pre-Wave 1a: Capture Session Start Ref
|
|
115
115
|
|
|
116
|
-
Before dispatching Wave 1, capture the current commit as the session baseline
|
|
116
|
+
Before dispatching Wave 1, capture the current commit of the session's own checkout as the session baseline — the tree this session's STATE.md lives in and its consumers diff (repoRoot); in a promoted or EnterWorktree session that is the worktree (#1652). Same value as STATE.md's `session-start-ref`:
|
|
117
117
|
|
|
118
118
|
```bash
|
|
119
|
-
SESSION_START_REF=$(git rev-parse HEAD)
|
|
119
|
+
SESSION_START_REF=$(git -C "<session checkout (repoRoot)>" rev-parse HEAD)
|
|
120
120
|
```
|
|
121
121
|
|
|
122
|
-
Store this value for use throughout the session — it is needed by the simplification pass (Quality wave) and session-reviewer dispatch to determine which files changed during this session. Include it in the coordinator's context, NOT in individual agent prompts.
|
|
122
|
+
Store this value for use throughout the session — it is needed by the simplification pass (Quality wave) and session-reviewer dispatch to determine which files changed during this session. Include it in the coordinator's context, NOT in individual agent prompts. This single ref covers the session's own checkout only: commits made in additional worktrees of the session are recorded by the staging fence as `orchestrator.session.work_tree` events and listed by `node scripts/session-commit-files.mjs` (#1659; remaining limits: `references/wave-executor-state-init.md`).
|
|
123
123
|
|
|
124
124
|
## Pre-Wave 1b: Initialize STATE.md
|
|
125
125
|
|
|
@@ -293,7 +293,7 @@ End with a single commit summarizing all housekeeping work.
|
|
|
293
293
|
|
|
294
294
|
### Ultradeep Profile (`session-profile: ultradeep`)
|
|
295
295
|
|
|
296
|
-
Not a fourth session type — a PROFILE over `session-type: deep`, resolved from the `/session ultradeep` argument alias (`commands/session.md`). Everything below applies only when STATE.md frontmatter carries `session-profile: ultradeep`; every other behaviour in this skill is unchanged, because downstream still reads `deep`. Full spec — wave table, mandatory artefacts, cost model:
|
|
296
|
+
Not a fourth session type — a PROFILE over `session-type: deep`, resolved from the `/session ultradeep` argument alias (`commands/session.md`). Everything below applies only when STATE.md frontmatter carries `session-profile: ultradeep`; every other behaviour in this skill is unchanged, because downstream still reads `deep`. Full spec — wave table, mandatory artefacts, cost model: the internal ultradeep session-profile PRD (2026-09-06, not in the public mirror).
|
|
297
297
|
|
|
298
298
|
- **The wave count and the wave roles come from the shape**, not from this file: `node scripts/session-shape.mjs --repo-root "$PWD" --session-type deep --profile ultradeep --no-event` returns `totalWaves: 7` (Research+Code-Discovery → Synthesis-Gate → Impl-Core → Impl-Polish → Review-Panel → Quality → Release/Finalization) and reports `wavesConfigHonored: false` with the ignored Session Config `waves` value — the profile OWNS its wave count. Role narrative: `skills/session-plan/SKILL.md` § Role-to-Wave Mapping.
|
|
299
299
|
- **Wave 2 is coordinator-direct and dispatches ZERO agents.** Make NO `Agent()` call in this wave. The coordinator consolidates wave 1 into `docs/audits/<YYYY-MM-DD>-<slug>.md`, updates STATE.md, and asks ONE **blocking** `AskUserQuestion` (confirm scope / narrow / abort) per `.claude/rules/ask-via-tool.md`. Wave 3 does not start until that question is answered — this is the one gate the profile exists for, so a silent "no tasks, skip it" is a defect, not an optimisation (`skills/session-plan/SKILL.md` § Empty roles, coordinator-direct exception).
|
|
@@ -352,6 +352,7 @@ After the Finalization wave completes successfully:
|
|
|
352
352
|
1. Report final status to the user
|
|
353
353
|
2. If `persistence: true`, suggest invoking `/close` to finalize the session. If `persistence: false`, note that the session is complete (no STATE.md to close — session-end would be a no-op).
|
|
354
354
|
3. Do NOT auto-commit — `/close` handles that with proper verification
|
|
355
|
+
4. With fleet integration this report is the session's finished or waiting answer: apply `skills/_shared/fleet-protocol.md` § Rücklauf an den Navigator (Pflicht) (wait case `fertig`, or `zug-<name>` when only a release train or pipeline is left) and, in this same turn, § Übergabe und Zug-Manifest — write the file, report „bereit zum Schließen“, stop monitors and background work, start nothing new, and leave `/close` to the coordinator. The report then ends with the sentence `Bereit zum Schließen.` instead of the `/close` suggestion of step 2: the coordinator reads only the last 600 characters, and no question may follow. Without fleet integration, nothing changes.
|
|
355
356
|
|
|
356
357
|
## Vault-Sync Diff Reporting (#327)
|
|
357
358
|
|
|
@@ -21,7 +21,7 @@ issues: [<issue numbers from plan>]
|
|
|
21
21
|
session: <semantic session label, e.g. main-2026-01-01-deep-1>
|
|
22
22
|
session-id: <raw session_id from .orchestrator/session.lock — OMIT the key when no lock exists>
|
|
23
23
|
started_at: <resolveSessionStartedAt({ repoRoot }) — the lock's own started_at, NEVER new Date() at write time (#1368)>
|
|
24
|
-
session-start-ref: <full sha from git rev-parse HEAD at init>
|
|
24
|
+
session-start-ref: <full sha from git -C <session checkout (repoRoot)> rev-parse HEAD at init (#1652)>
|
|
25
25
|
status: active
|
|
26
26
|
current-wave: 0
|
|
27
27
|
total-waves: <from session plan>
|
|
@@ -50,9 +50,9 @@ node -e "import('./scripts/lib/state-md.mjs').then(m => console.log(JSON.stringi
|
|
|
50
50
|
|
|
51
51
|
`resolveSessionStartedAt()` returns `.orchestrator/session.lock`'s own `started_at`, which the `orchestrator.session.lock.acquired` and `orchestrator.session.started` events agree with to the millisecond; it falls back to the current time only when no lock exists (`persistence: false`, or the acquire failed). Writing `new Date()` here instead put STATE.md 48 minutes ahead of the lock (measured 2026-09-13) and made `/close`'s #429 pre-check unable to join STATE.md to its own ledger record.
|
|
52
52
|
|
|
53
|
-
`session-id` is the raw/native harness id (`resolveSessionIds().session_id`), `session` the semantic label (`.semantic_session_id`). Both are OPTIONAL (`schema-version` stays `1`): when the
|
|
53
|
+
`session-id` is the raw/native harness id (`resolveSessionIds().session_id`), `session` the semantic label (`.semantic_session_id`). Both are OPTIONAL (`schema-version` stays `1`): when the command yields `null`, OMIT the key entirely — never write a placeholder. **The guard is in the command, not in you (#1663):** the lock is repo-global and can name a PEER (a lost acquire race, or a pre-#1641 lock without `claude_pid` that stays until its TTL), so `resolveSessionIds()` returns `session_id` only when the lock's raw id is one of `readProcessLocalSessionIds()` (this process's own native id) and `null` otherwise; a provably foreign lock also nulls `semantic_session_id`. A foreign lock id therefore means the key is OMITTED — never copy it from the lock by hand, because `/close` compares STATE.md `session-id` against the own native id (#1661) and a peer's id would make this session's own STATE.md read `foreign`. Neither field grants lock or registry ownership; see `skills/_shared/state-ownership.md`.
|
|
54
54
|
|
|
55
|
-
`session-start-ref` is the commit the session started on (`git rev-parse HEAD`, full SHA); `scripts/emit-session.mjs` derives the ledger's `session_start_ref` from it and, when the key is absent, falls back to the `head_sha` of this session's own `orchestrator.session.started` event (#1443). `hooks/pre-task-scope-disjoint.mjs` no longer reads it: since #1485 its stale-worktree check on `isolation: "worktree"` dispatches compares HEAD against the base the harness actually branches from (`origin/HEAD` unless `worktree.baseRef: "head"`, then HEAD), and DENIES a measured mismatch. The other reader is `scripts/lib/scope-baseline.mjs` (`readBaseline()` reads the key into `sessionStartRef`, `computeDrift()` consumes it): with the key present its numerator is the union of the committed `<ref>..HEAD` diff, the working tree vs HEAD (staged + unstaged) and the untracked files (#1027 Pkt 2), all in the coordinator's own checkout; absent, it resolves a base itself (the preferred remote's default branch, else a local `main`/`master`; nothing resolvable skips with `no-baseline-ref`); a present-but-dead ref skips with `unresolvable-ref`, and so does any failed, oversized or timed-out numerator query.
|
|
55
|
+
`session-start-ref` is the commit the session started on, in the session's OWN checkout (#1652): `git -C <repoRoot> rev-parse HEAD`, full SHA — the tree this STATE.md lives in, because every consumer below runs `<ref>..HEAD` there. In a promoted or EnterWorktree session that tree is the WORKTREE (its STATE.md lives there), never the main checkout it came from. A ref taken from ANOTHER tree is wrong in both directions: a foreign worktree's HEAD or a stale main checkout's HEAD turns the two-dot diff into a diff between two unrelated trees. Commits a coordinator (or its subagents) makes in additional worktrees of its own (coordinator in the main checkout, branches in `_wt/…`) are NOT covered by this single ref; they are attributed separately (#1659): `hooks/pre-bash-staging-fence.mjs` records `(tree, start_ref)` as an `orchestrator.session.work_tree` event before every `git commit`, and `node scripts/session-commit-files.mjs [--session-id <raw-id>] [--json]` lists the files per tree and their union, each tree diffed only inside itself. Remaining limits: commits nested in `bash -c "…"`, `cd` operands needing shell expansion, commits after `pushd`/`popd`/`chdir`/`builtin cd` in the same command and the first commit on an unborn HEAD are not recorded; subshell scope is not modelled, so in `(cd x && git commit); git commit` the second commit is attributed to `x` as well; a commit inside `$( … )` or backticks is recorded for the start directory only when no outer statement changed directory, otherwise not at all (#1670); `SO_DISABLED_HOOKS=pre-bash-staging-fence` disables recording; Codex sessions record nothing (the fence is not in `hooks/hooks-codex.json`); two sessions committing in the same tree after one's start ref cannot be separated by a ref diff. Never substitute `origin/main` or another upstream ref: the consumers run the two-dot tree diff `git diff <ref>..HEAD`, which against a ref that is not an ancestor of HEAD lists the upstream's foreign files too. `scripts/emit-session.mjs` derives the ledger's `session_start_ref` from it and, when the key is absent, falls back to the `head_sha` of this session's own `orchestrator.session.started` event (#1443) — omitted with a WARN when that event measured `behind > 0`, because after a pull `<head_sha>..HEAD` would include the pulled-in upstream commits (#1668); when the session committed in more than its own tree, the record also carries `session_work_trees` (`[{ tree, start_ref }]`, #1659). `hooks/pre-task-scope-disjoint.mjs` no longer reads it: since #1485 its stale-worktree check on `isolation: "worktree"` dispatches compares HEAD against the base the harness actually branches from (`origin/HEAD` unless `worktree.baseRef: "head"`, then HEAD), and DENIES a measured mismatch. The other reader is `scripts/lib/scope-baseline.mjs` (`readBaseline()` reads the key into `sessionStartRef`, `computeDrift()` consumes it): with the key present its numerator is the union of the committed `<ref>..HEAD` diff, the working tree vs HEAD (staged + unstaged) and the untracked files (#1027 Pkt 2), all in the coordinator's own checkout, plus (#1659) the files committed in the session's OTHER own work trees of the same repository, read from its `orchestrator.session.work_tree` records and each diffed `<start_ref>..HEAD` only inside its own tree (the own tree keeps the STATE.md ref; trees of another repository are left out), reported in the additive result field `otherWorkTrees: { counted, skipped, otherRepo, malformed, invalid }`, present only when at least one other own tree is recorded; absent, it resolves a base itself (the preferred remote's default branch, else a local `main`/`master`; nothing resolvable skips with `no-baseline-ref`); a present-but-dead ref skips with `unresolvable-ref`, and so does any failed, oversized or timed-out numerator query. The three own-checkout git queries run with `cwd` = repoRoot, so they never read an agent worktree's contents (the worktree directory itself is excluded from the untracked list); another tree contributes only its committed diff, never its working tree or untracked files. The ref bounds only the committed source, which gives the ratio two edges in opposite directions. Under-report: this template takes HEAD at wave-executor init rather than at session start, so files changed by a commit landed between session start and wave-executor init are missing from the committed source. Over-report: the working-tree and untracked sources are not tied to the ref at all, so files already dirty when the session started and a same-checkout peer session's files count as drift and can raise a false WARN. That second edge is a named ceiling in `computeDrift()`'s JSDoc; removing it needs a start-dirty snapshot persisted at freeze time, a storage change outside this template.
|
|
56
56
|
|
|
57
57
|
Create the `<state-dir>` directory if needed (`mkdir -p <state-dir>`) before writing. This file is the persistent state record — other skills and resumed sessions read it.
|
|
58
58
|
|
|
@@ -14,10 +14,11 @@ Run this ONCE at the start of wave execution (before the first wave — not per-
|
|
|
14
14
|
|
|
15
15
|
```js
|
|
16
16
|
import { appendSkillInvocation } from '$PLUGIN_ROOT/scripts/lib/skill-invocations-schema.mjs';
|
|
17
|
-
import
|
|
17
|
+
import { resolveLedgerPath } from '$PLUGIN_ROOT/scripts/lib/ledger-path.mjs';
|
|
18
18
|
try {
|
|
19
19
|
await appendSkillInvocation(
|
|
20
|
-
|
|
20
|
+
// In a linked worktree the main checkout's ledger when it is gitignored there (#1722).
|
|
21
|
+
resolveLedgerPath('.orchestrator/metrics/skill-invocations.jsonl', process.cwd()),
|
|
21
22
|
{ timestamp: new Date().toISOString(), event: 'selected', skill: 'session-orchestrator:wave-executor', session_id: '<session_id>', phase: 'wave-execution' },
|
|
22
23
|
);
|
|
23
24
|
} catch { /* telemetry is best-effort — swallow and continue to dispatch */ }
|
|
@@ -117,7 +118,7 @@ Use the **Agent tool** to dispatch this wave's agents. **`run_in_background: tru
|
|
|
117
118
|
|
|
118
119
|
Dispatch in **SMALL BATCHES of 3–4 Agent() calls per message** (cumulative up to the wave's `agents-per-wave` cap). Large single-message fan-outs (>4 Agent() calls in one message) remain **FORBIDDEN** — fleet evidence (conf 1.0, 5 sessions) shows they drop Agent() calls SILENTLY (the coordinator receives fewer results than it dispatched, with no error), whereas serial / small-batch dispatch held 13/13 and 8/8. That evidence PREDATES backgrounding and is untouched by it; what backgrounding changes is the *cost* of batching, which is now near zero — a background batch returns its launch acks immediately, so batches no longer serialize the wave. Dispatch a batch, let its acks return, then dispatch the next, until every planned agent is started; then run **Started-Set Verification** below. The archived, private spec `docs/specs/2026-07-02-fleet-mining-followup-grill.md` (C4) records the batching-policy rationale. The `worker-pool.enabled: true` path (above) is the mechanised opt-in alternative to manual batching. <!-- path-check: historical -->
|
|
119
120
|
|
|
120
|
-
**What is NOT the reason for 3–4 (platform caps, as of Claude Code 2.1.232).** The batch size defends against the SILENT DROP measured above. It is not, and never was, a workaround for a platform concurrency limit — and the upstream numbers no longer permit reading it as one: **2.1.217** set the concurrent-subagent cap to **20** (`CLAUDE_CODE_MAX_CONCURRENT_SUBAGENTS`), **2.1.219** set subagent nesting depth to **3**, **2.1.224** REMOVED the former 200-spawn-per-session cap, and **2.1.232** made background the spawn default (above). So neither 3–4, nor `agents-per-wave: 6`, nor `deep: 18` sits anywhere near a platform ceiling; the only ceiling a wave can actually reach is the concurrency cap of 20, and only through the worker-pool `maxParallel` path. The rule stays at 3–4 because the drop evidence is about how many `Agent()` calls survive ONE assistant message — a question no version bump has addressed. **Revisit-Trigger:** a measurement showing a >4-call single message delivering 100% of its calls. A raised cap is not that measurement. Version provenance:
|
|
121
|
+
**What is NOT the reason for 3–4 (platform caps, as of Claude Code 2.1.232).** The batch size defends against the SILENT DROP measured above. It is not, and never was, a workaround for a platform concurrency limit — and the upstream numbers no longer permit reading it as one: **2.1.217** set the concurrent-subagent cap to **20** (`CLAUDE_CODE_MAX_CONCURRENT_SUBAGENTS`), **2.1.219** set subagent nesting depth to **3**, **2.1.224** REMOVED the former 200-spawn-per-session cap, and **2.1.232** made background the spawn default (above). So neither 3–4, nor `agents-per-wave: 6`, nor `deep: 18` sits anywhere near a platform ceiling; the only ceiling a wave can actually reach is the concurrency cap of 20, and only through the worker-pool `maxParallel` path. The rule stays at 3–4 because the drop evidence is about how many `Agent()` calls survive ONE assistant message — a question no version bump has addressed. **Revisit-Trigger:** a measurement showing a >4-call single message delivering 100% of its calls. A raised cap is not that measurement. Version provenance: the Claude Code release-notes report of the 2026-09-06 360° audit (FA-6; internal, not in the public mirror).
|
|
121
122
|
|
|
122
123
|
Read each wave's dispatch metadata from the session plan header (e.g., `(4 agents, parallel, isolation: worktree)`). When the plan specifies `isolation`, use it verbatim. When the plan does not specify, resolve the effective value via `resolveIsolation({ agentCount, sessionType, collisionRisk, configIsolation })` from `scripts/lib/wave-sizing.mjs` — the graduated default (#194) replaces the previous session-type-only switch. Pass the resolved value to each Agent() tool call per `circuit-breaker.md` (omit the parameter when resolved to `none`).
|
|
123
124
|
|
|
@@ -137,7 +138,7 @@ Before dispatching, verify the wave's agent count does not exceed `$CONFIG.agent
|
|
|
137
138
|
| Advancing to the next wave | BLOCKS on the wave's AskUserQuestion. This is the one wave whose purpose is to stop; never auto-advance past it. |
|
|
138
139
|
| STATE.md Wave History (§ 3a in `wave-loop-review.md`) | Record the wave with its role and `agents: 0`, so the wave count stays honest. |
|
|
139
140
|
|
|
140
|
-
Rationale and the first consumer (the ultradeep profile's wave 2, "Synthesis-Gate"):
|
|
141
|
+
Rationale and the first consumer (the ultradeep profile's wave 2, "Synthesis-Gate"): the internal ultradeep session-profile PRD (2026-09-06, not in the public mirror). Not restated here.
|
|
141
142
|
|
|
142
143
|
#### Contract-Lock Serialization (Pattern A, #730/H1)
|
|
143
144
|
|
|
@@ -311,8 +312,11 @@ For each agent in this wave:
|
|
|
311
312
|
- Any repo-state fact carried from an earlier wave: in the ASSERTED/UNVERIFIED form, never as a bare value (see Pre-Dispatch: Fact-Staleness Annotation above)
|
|
312
313
|
- VCS issue reference if applicable
|
|
313
314
|
- What NOT to touch (other agents' files)
|
|
315
|
+
- Git-write ban (PSA-007): "Do NOT run git add, git commit, git stash, git mv, git rm, git push or git reset. The coordinator commits." A wave prompt never carries a WORKTREE-MODE line, so the worktree exception of code-implementer never applies inside a wave.
|
|
316
|
+
- Waiting rule (#1615): "Do not wait longer than 5 minutes for anything (pipeline, remote run, rerun). Report your intermediate state and end; the coordinator waits with Monitor and continues you with SendMessage." A subagent's prompt cache lives 5 minutes, so every longer wait rebuilds its whole context.
|
|
314
317
|
>",
|
|
315
318
|
subagent_type: "<from session plan>", // resolved agent type
|
|
319
|
+
model: "sonnet", // ONLY when subagent_type is "Explore" (#1614): on a subscription the built-in Explore otherwise runs on the opus alias. Plugin agents pin model + effort in their own frontmatter — pass nothing for them.
|
|
316
320
|
run_in_background: true // RECOMMENDED — verify the started set via meta.json sidecars, never via the launch ack (§ Started-Set Verification)
|
|
317
321
|
})
|
|
318
322
|
- Turn budget and status reporting: "You have a maximum of [maxTurns] turns for this task. If you cannot complete within this budget, report STATUS: partial with what was accomplished and what remains. At the end of your work, report STATUS: done (all acceptance criteria met) or STATUS: partial (some criteria unmet — list which ones)."
|
|
@@ -331,6 +335,8 @@ The colon heuristic (§ "How to detect project agents") has **three** readings,
|
|
|
331
335
|
|
|
332
336
|
**Trust boundary.** A foreign run bypasses the ENTIRE hook chain — no `PreToolUse:Agent`, no scope enforcement, no `SubagentStop` telemetry (verified against `hooks/hooks.json`, W1/D4). Everything inside the detached worktree is **untrusted** until a Claude coordinator has read the diff and applied it. The replacement ledger record is the `orchestrator.foreign_dispatch.completed` event the adapter emits (payload in `docs/events-schema.md`), which fires on refusals too — a blocked dispatch is a record, not a silence.
|
|
333
337
|
|
|
338
|
+
**No codebase index (#1683).** The argv carries `--disable-codebase-ref` (`FOREIGN_AGENT_BASE_ARGS`). Without it each run leaves a `worker-server` and a `typescript-language-server` behind with PPID 1, out of reach of the timeout kill. The foreign agent therefore has no semantic search and no LSP lints, only read and grep; the diff review above stays the quality gate. The hidden `--disable-indexing` flag is not needed (#1687): it only switches off the codebase sync inside that `worker-server`, which never starts here — measured 2026-10-20 on cursor-agent 2026.10.01-e373342, 52–71 KB outbound per run with or without it against a 1.5 MB worktree.
|
|
339
|
+
|
|
334
340
|
**Composition.** A foreign run is a background Bash task, so it composes with background `Agent()` dispatch in the same wave. It is **NOT** part of § Started-Set Verification: no `meta.json` sidecar exists for it and no task-notification arrives. Its lifecycle is the adapter's return value — do not count it as planned/started/completed, and do not read its absence from the started set as a drop.
|
|
335
341
|
|
|
336
342
|
**Model selection** is not decided here: the operator's model-routing SSOT (ADR-002) owns it. Working defaults: `composer-2.5` for foreign impl; `cursor-grok-4.6-high` for review / test-writing / judgment roles, at `timeoutSec ≥ 900`. Dogfood evidence (2026-08-25): `composer-2.5` on issue #1105 — 165 s, 2 files, +93 lines, mandatory review passed, merged only after independent test verification.
|
|
@@ -595,7 +601,7 @@ Rules:
|
|
|
595
601
|
|
|
596
602
|
**Resolution chain** (if the plan does not specify `subagent_type` for an agent):
|
|
597
603
|
|
|
598
|
-
1. **Discovery waves** → `"Explore"` (always, read-only)
|
|
604
|
+
1. **Discovery waves** → `"Explore"` (always, read-only; dispatched with `model: "sonnet"`, see the template above)
|
|
599
605
|
2. **Quality review** → `"session-orchestrator:session-reviewer"` (always)
|
|
600
606
|
3. **Impl-Core / Impl-Polish / Quality (test-writing)** → check in order:
|
|
601
607
|
a. Project agent matching the task domain (e.g., `"database-architect"` for DB tasks)
|
|
@@ -497,7 +497,10 @@ After each wave completes and before the progress update, update `<state-dir>/ST
|
|
|
497
497
|
// and stored in .orchestrator/session.lock (session_id field); matches the
|
|
498
498
|
// STATE.md frontmatter `session:` field written during Pre-Wave 1b initialization.
|
|
499
499
|
import { updateHeartbeat } from '../../scripts/lib/session-lock.mjs';
|
|
500
|
-
|
|
500
|
+
import { resolveOwnLockRootFor } from '../../scripts/lib/own-lock-root.mjs';
|
|
501
|
+
// The lock is not always in the cwd's checkout (#1730): a worktree session keeps it in the main checkout,
|
|
502
|
+
// or the other way round. At the wrong root updateHeartbeat writes nothing and the 4h TTL lapses.
|
|
503
|
+
updateHeartbeat({ sessionId, repoRoot: resolveOwnLockRootFor({ sessionId }).root });
|
|
501
504
|
```
|
|
502
505
|
|
|
503
506
|
Skip silently if `persistence: false` in Session Config (no session.lock exists in that mode).
|
|
@@ -525,11 +528,16 @@ The helper is `scripts/lib/agent-status.mjs`. The writers (`setStatus`, `setProg
|
|
|
525
528
|
|
|
526
529
|
```js
|
|
527
530
|
import { setStatus, setProgress } from '../../scripts/lib/agent-status.mjs';
|
|
531
|
+
import { resolveOwnLockRootFor } from '../../scripts/lib/own-lock-root.mjs';
|
|
532
|
+
|
|
533
|
+
// Once per wave: the root beside this session's own lock. The SubagentStart/Stop hooks write there too, and
|
|
534
|
+
// the HUD and the tmux pane read there (#1730); without it the writers default to process.cwd().
|
|
535
|
+
const statusOpts = { repoRoot: resolveOwnLockRootFor({ sessionId }).root };
|
|
528
536
|
|
|
529
537
|
// For each agent dispatched in this wave (i = 0-based position, total = wave agent count):
|
|
530
|
-
await setStatus(agentId, `dispatched — ${subagentType}
|
|
538
|
+
await setStatus(agentId, `dispatched — ${subagentType}`, statusOpts); // free-text variant
|
|
531
539
|
// — or —
|
|
532
|
-
await setProgress(agentId, { step: i + 1, total, label: subagentType }); // progress variant
|
|
540
|
+
await setProgress(agentId, { step: i + 1, total, label: subagentType }, statusOpts); // progress variant
|
|
533
541
|
```
|
|
534
542
|
|
|
535
543
|
`agentId` is a stable per-agent key (e.g. `wave${waveN}-${i}-${subagentType}`). There is **no separate "agent-start" hook distinct from dispatch** — wave agents are in-process `Agent()` calls with no PID/TTY (see `skills/tmux-layout/SKILL.md § When NOT to Use`), so dispatch IS the start signal. Do not invent one.
|
|
@@ -538,14 +546,14 @@ The helper is `scripts/lib/agent-status.mjs`. The writers (`setStatus`, `setProg
|
|
|
538
546
|
|
|
539
547
|
```js
|
|
540
548
|
// status ∈ {'done','partial','failed'} from the agent's STATUS: line
|
|
541
|
-
await setStatus(agentId, status);
|
|
549
|
+
await setStatus(agentId, status, statusOpts);
|
|
542
550
|
```
|
|
543
551
|
|
|
544
552
|
3. **wave-end rollup** — in `### 3a. Post-Wave: Update STATE.md`, beside the `updateHeartbeat` call (step 5), push one wave-level rollup using a wave-scoped key:
|
|
545
553
|
|
|
546
554
|
```js
|
|
547
555
|
// e.g. agentId = `wave${waveN}` ; counts from the wave's per-agent results
|
|
548
|
-
await setStatus(`wave${waveN}`, `wave ${waveN} complete — ${done} done, ${partial} partial, ${failed} failed
|
|
556
|
+
await setStatus(`wave${waveN}`, `wave ${waveN} complete — ${done} done, ${partial} partial, ${failed} failed`, statusOpts);
|
|
549
557
|
```
|
|
550
558
|
|
|
551
559
|
A push failure (timeout, fs-error, invalid-input) is logged to the wave progress update at most as a one-line WARN — never block, never retry, never surface to the user. If `agent-status.mjs` is absent (older plugin checkout), wrap the import defensively and no-op, exactly as `layouts.mjs` does for its telemetry import.
|
|
@@ -165,7 +165,7 @@ Before each wave dispatch:
|
|
|
165
165
|
5. Delete `<state-dir>/wave-scope.json`
|
|
166
166
|
6. Write Phase 2 wave-scope.json with test file allowedPaths (`**/*.test.*`, `**/*.spec.*`, `**/__tests__/**`)
|
|
167
167
|
7. Dispatch test/review agents
|
|
168
|
-
7. After the final wave completes, delete `<state-dir>/wave-scope.json` (cleanup). Delete `<state-dir>/filescopes/` in the same step — the per-agent scope files (§ 3.1) are wave-local working state, and a stale `wave-<N>/` directory left behind is a scope claim nobody re-verified. Delete THIS session's dispatch ledger in the same step too — `hooks/pre-task-scope-disjoint.mjs` keeps one file per session since #1493, `.orchestrator/wave-dispatch-scopes.<session-id>.json` plus its `.lock`, under the session root (the repo root, or the worktree the session entered). Only your own: a file beside it named after another id holds a peer session's live claims. Delete only an id that is already path-safe (the hook names the file after it unchanged); anything else is left behind, inert, because no other session reads it:
|
|
168
|
+
7. After the final wave completes, delete `<state-dir>/wave-scope.json` (cleanup). Make the session's own commits BEFORE this step: while the last wave's manifest is active, `wave-scope-commit-guard` admits files of earlier waves only through their `filescopes/wave-<k>.scopes.json` sidecars (#1680). Delete `<state-dir>/filescopes/` in the same step — the per-agent scope files (§ 3.1) are wave-local working state, and a stale `wave-<N>/` directory left behind is a scope claim nobody re-verified. Delete THIS session's dispatch ledger in the same step too — `hooks/pre-task-scope-disjoint.mjs` keeps one file per session since #1493, `.orchestrator/wave-dispatch-scopes.<session-id>.json` plus its `.lock`, under the session root (the repo root, or the worktree the session entered). Only your own: a file beside it named after another id holds a peer session's live claims. Delete only an id that is already path-safe (the hook names the file after it unchanged); anything else is left behind, inert, because no other session reads it:
|
|
169
169
|
|
|
170
170
|
```bash
|
|
171
171
|
SID="${CLAUDE_CODE_SESSION_ID:-}"
|
package/assets/og-card.png
DELETED
|
Binary file
|
package/assets/og-card.svg
DELETED
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1280 640" role="img" aria-label="Session Orchestrator">
|
|
2
|
-
<title>Session Orchestrator</title>
|
|
3
|
-
<desc>Wave-based Claude Code sessions with quality gates</desc>
|
|
4
|
-
|
|
5
|
-
<!-- Background: GitHub-dark -->
|
|
6
|
-
<rect width="1280" height="640" fill="#0d1117"/>
|
|
7
|
-
|
|
8
|
-
<!-- Subtle gradient accent in top-right -->
|
|
9
|
-
<defs>
|
|
10
|
-
<radialGradient id="accent" cx="80%" cy="20%" r="50%">
|
|
11
|
-
<stop offset="0%" stop-color="#1f6feb" stop-opacity="0.18"/>
|
|
12
|
-
<stop offset="100%" stop-color="#0d1117" stop-opacity="0"/>
|
|
13
|
-
</radialGradient>
|
|
14
|
-
<linearGradient id="waveStroke" x1="0" y1="0" x2="1" y2="0">
|
|
15
|
-
<stop offset="0%" stop-color="#1f6feb" stop-opacity="0.55"/>
|
|
16
|
-
<stop offset="50%" stop-color="#238636" stop-opacity="0.55"/>
|
|
17
|
-
<stop offset="100%" stop-color="#d29922" stop-opacity="0.55"/>
|
|
18
|
-
</linearGradient>
|
|
19
|
-
</defs>
|
|
20
|
-
<rect width="1280" height="640" fill="url(#accent)"/>
|
|
21
|
-
|
|
22
|
-
<!-- Five wave glyphs as watermark (echo of icon.svg motif) on right side -->
|
|
23
|
-
<g transform="translate(740, 140)" opacity="0.85">
|
|
24
|
-
<path d="M 0 40 Q 100 0 200 40 T 400 40" fill="none" stroke="url(#waveStroke)" stroke-width="10" stroke-linecap="round"/>
|
|
25
|
-
<path d="M 0 110 Q 100 70 200 110 T 400 110" fill="none" stroke="url(#waveStroke)" stroke-width="10" stroke-linecap="round"/>
|
|
26
|
-
<path d="M 0 180 Q 100 140 200 180 T 400 180" fill="none" stroke="url(#waveStroke)" stroke-width="10" stroke-linecap="round"/>
|
|
27
|
-
<path d="M 0 250 Q 100 210 200 250 T 400 250" fill="none" stroke="url(#waveStroke)" stroke-width="10" stroke-linecap="round"/>
|
|
28
|
-
<path d="M 0 320 Q 100 280 200 320 T 400 320" fill="none" stroke="url(#waveStroke)" stroke-width="10" stroke-linecap="round"/>
|
|
29
|
-
</g>
|
|
30
|
-
|
|
31
|
-
<!-- Title block (left-aligned) -->
|
|
32
|
-
<g font-family="system-ui, -apple-system, Segoe UI, Roboto, sans-serif">
|
|
33
|
-
<!-- Eyebrow -->
|
|
34
|
-
<text x="80" y="170" font-size="24" font-weight="500" fill="#7d8590" letter-spacing="4">CLAUDE CODE PLUGIN</text>
|
|
35
|
-
|
|
36
|
-
<!-- Title -->
|
|
37
|
-
<text x="80" y="265" font-size="80" font-weight="700" fill="#f0f6fc">Session</text>
|
|
38
|
-
<text x="80" y="355" font-size="80" font-weight="700" fill="#f0f6fc">Orchestrator</text>
|
|
39
|
-
|
|
40
|
-
<!-- Subtitle -->
|
|
41
|
-
<text x="80" y="430" font-size="32" font-weight="400" fill="#c9d1d9">Wave-based Claude Code sessions</text>
|
|
42
|
-
<text x="80" y="472" font-size="32" font-weight="400" fill="#c9d1d9">with quality gates</text>
|
|
43
|
-
|
|
44
|
-
<!-- Footer -->
|
|
45
|
-
<text x="80" y="570" font-size="24" font-weight="400" fill="#7d8590">Claude Code · Codex CLI · Cursor IDE · MIT</text>
|
|
46
|
-
</g>
|
|
47
|
-
</svg>
|
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1062 372" width="1062" height="372" role="img" aria-labelledby="wl-title wl-desc" font-family="system-ui, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif">
|
|
2
|
-
<title id="wl-title">Session Orchestrator wave lifecycle</title>
|
|
3
|
-
<desc id="wl-desc">Flow diagram. Precondition: /bootstrap runs once per repository, then /session agrees the scope, then /go executes. /go runs five typed waves left to right: 1 Discovery (read-only audit), gate G1, 2 Impl-Core (primary code), gate G2, 3 Impl-Polish (integration and edge cases), gate G3, 4 Quality (simplification pass, then tests), gate G4 Full Gate, 5 Finalization (commit and close). Gates G1 to G3 are Quality-Lite (typecheck, lint, test); G4 is the Full Gate and additionally runs the full suite plus a debug-artifact scan. After wave 5, /close verifies every planned item, runs the Full Gate, files carryover issues, commits, and writes the session record.</desc>
|
|
4
|
-
|
|
5
|
-
<defs>
|
|
6
|
-
<marker id="wl-arrow" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse">
|
|
7
|
-
<path d="M 0 0 L 10 5 L 0 10 z" fill="#94A3B8"/>
|
|
8
|
-
</marker>
|
|
9
|
-
</defs>
|
|
10
|
-
|
|
11
|
-
<!-- heading -->
|
|
12
|
-
<rect x="28" y="16" width="430" height="32" rx="6" fill="#1E293B"/>
|
|
13
|
-
<text x="44" y="37" font-size="14" font-weight="600" fill="#F8FAFC">Session Orchestrator — wave lifecycle and gates</text>
|
|
14
|
-
|
|
15
|
-
<!-- row A: the three commands -->
|
|
16
|
-
<rect x="28" y="68" width="196" height="48" rx="8" fill="#475569"/>
|
|
17
|
-
<text x="126" y="90" font-size="14" font-weight="600" fill="#F8FAFC" text-anchor="middle">/bootstrap</text>
|
|
18
|
-
<text x="126" y="106" font-size="11" fill="#E2E8F0" text-anchor="middle">once per repo — writes bootstrap.lock</text>
|
|
19
|
-
|
|
20
|
-
<line x1="228" y1="92" x2="248" y2="92" stroke="#94A3B8" stroke-width="2" marker-end="url(#wl-arrow)"/>
|
|
21
|
-
|
|
22
|
-
<rect x="252" y="68" width="196" height="48" rx="8" fill="#1F6FEB"/>
|
|
23
|
-
<text x="350" y="90" font-size="14" font-weight="600" fill="#FFFFFF" text-anchor="middle">/session [type]</text>
|
|
24
|
-
<text x="350" y="106" font-size="11" fill="#DBEAFE" text-anchor="middle">research + Q&A — agree the scope</text>
|
|
25
|
-
|
|
26
|
-
<line x1="452" y1="92" x2="472" y2="92" stroke="#94A3B8" stroke-width="2" marker-end="url(#wl-arrow)"/>
|
|
27
|
-
|
|
28
|
-
<rect x="476" y="68" width="140" height="48" rx="8" fill="#1F6FEB"/>
|
|
29
|
-
<text x="546" y="90" font-size="14" font-weight="600" fill="#FFFFFF" text-anchor="middle">/go</text>
|
|
30
|
-
<text x="546" y="106" font-size="11" fill="#DBEAFE" text-anchor="middle">executes the waves</text>
|
|
31
|
-
|
|
32
|
-
<!-- /go down into wave 1 -->
|
|
33
|
-
<polyline points="546,116 546,134 93,134 93,146" fill="none" stroke="#94A3B8" stroke-width="2" marker-end="url(#wl-arrow)"/>
|
|
34
|
-
|
|
35
|
-
<!-- row B: waves and gates -->
|
|
36
|
-
<rect x="28" y="150" width="130" height="76" rx="8" fill="#0F766E"/>
|
|
37
|
-
<text x="93" y="180" font-size="13" font-weight="600" fill="#FFFFFF" text-anchor="middle">1 · Discovery</text>
|
|
38
|
-
<text x="93" y="198" font-size="11" fill="#CCFBF1" text-anchor="middle">read-only audit</text>
|
|
39
|
-
<text x="93" y="213" font-size="11" fill="#CCFBF1" text-anchor="middle">no writes</text>
|
|
40
|
-
|
|
41
|
-
<line x1="160" y1="188" x2="170" y2="188" stroke="#94A3B8" stroke-width="2" marker-end="url(#wl-arrow)"/>
|
|
42
|
-
<rect x="172" y="166" width="56" height="44" rx="6" fill="#64748B"/>
|
|
43
|
-
<text x="200" y="185" font-size="13" font-weight="700" fill="#FFFFFF" text-anchor="middle">G1</text>
|
|
44
|
-
<text x="200" y="200" font-size="9" fill="#F1F5F9" text-anchor="middle">lite</text>
|
|
45
|
-
|
|
46
|
-
<line x1="230" y1="188" x2="240" y2="188" stroke="#94A3B8" stroke-width="2" marker-end="url(#wl-arrow)"/>
|
|
47
|
-
<rect x="242" y="150" width="130" height="76" rx="8" fill="#1F6FEB"/>
|
|
48
|
-
<text x="307" y="180" font-size="13" font-weight="600" fill="#FFFFFF" text-anchor="middle">2 · Impl-Core</text>
|
|
49
|
-
<text x="307" y="198" font-size="11" fill="#DBEAFE" text-anchor="middle">primary code</text>
|
|
50
|
-
<text x="307" y="213" font-size="11" fill="#DBEAFE" text-anchor="middle">architecture first</text>
|
|
51
|
-
|
|
52
|
-
<line x1="374" y1="188" x2="384" y2="188" stroke="#94A3B8" stroke-width="2" marker-end="url(#wl-arrow)"/>
|
|
53
|
-
<rect x="386" y="166" width="56" height="44" rx="6" fill="#64748B"/>
|
|
54
|
-
<text x="414" y="185" font-size="13" font-weight="700" fill="#FFFFFF" text-anchor="middle">G2</text>
|
|
55
|
-
<text x="414" y="200" font-size="9" fill="#F1F5F9" text-anchor="middle">lite</text>
|
|
56
|
-
|
|
57
|
-
<line x1="444" y1="188" x2="454" y2="188" stroke="#94A3B8" stroke-width="2" marker-end="url(#wl-arrow)"/>
|
|
58
|
-
<rect x="456" y="150" width="130" height="76" rx="8" fill="#3B82F6"/>
|
|
59
|
-
<text x="521" y="180" font-size="13" font-weight="600" fill="#FFFFFF" text-anchor="middle">3 · Impl-Polish</text>
|
|
60
|
-
<text x="521" y="198" font-size="11" fill="#EFF6FF" text-anchor="middle">integration</text>
|
|
61
|
-
<text x="521" y="213" font-size="11" fill="#EFF6FF" text-anchor="middle">edge cases</text>
|
|
62
|
-
|
|
63
|
-
<line x1="588" y1="188" x2="598" y2="188" stroke="#94A3B8" stroke-width="2" marker-end="url(#wl-arrow)"/>
|
|
64
|
-
<rect x="600" y="166" width="56" height="44" rx="6" fill="#64748B"/>
|
|
65
|
-
<text x="628" y="185" font-size="13" font-weight="700" fill="#FFFFFF" text-anchor="middle">G3</text>
|
|
66
|
-
<text x="628" y="200" font-size="9" fill="#F1F5F9" text-anchor="middle">lite</text>
|
|
67
|
-
|
|
68
|
-
<line x1="658" y1="188" x2="668" y2="188" stroke="#94A3B8" stroke-width="2" marker-end="url(#wl-arrow)"/>
|
|
69
|
-
<rect x="670" y="150" width="130" height="76" rx="8" fill="#7C3AED"/>
|
|
70
|
-
<text x="735" y="180" font-size="13" font-weight="600" fill="#FFFFFF" text-anchor="middle">4 · Quality</text>
|
|
71
|
-
<text x="735" y="198" font-size="11" fill="#EDE9FE" text-anchor="middle">simplify first,</text>
|
|
72
|
-
<text x="735" y="213" font-size="11" fill="#EDE9FE" text-anchor="middle">then write tests</text>
|
|
73
|
-
|
|
74
|
-
<line x1="802" y1="188" x2="812" y2="188" stroke="#94A3B8" stroke-width="2" marker-end="url(#wl-arrow)"/>
|
|
75
|
-
<rect x="814" y="160" width="76" height="56" rx="6" fill="#D29922"/>
|
|
76
|
-
<text x="852" y="182" font-size="13" font-weight="700" fill="#1F2937" text-anchor="middle">G4</text>
|
|
77
|
-
<text x="852" y="199" font-size="10" font-weight="600" fill="#1F2937" text-anchor="middle">Full Gate</text>
|
|
78
|
-
|
|
79
|
-
<line x1="892" y1="188" x2="902" y2="188" stroke="#94A3B8" stroke-width="2" marker-end="url(#wl-arrow)"/>
|
|
80
|
-
<rect x="904" y="150" width="130" height="76" rx="8" fill="#238636"/>
|
|
81
|
-
<text x="969" y="180" font-size="13" font-weight="600" fill="#FFFFFF" text-anchor="middle">5 · Finalization</text>
|
|
82
|
-
<text x="969" y="198" font-size="11" fill="#DCFCE7" text-anchor="middle">commit + close</text>
|
|
83
|
-
|
|
84
|
-
<!-- gate legend -->
|
|
85
|
-
<rect x="28" y="246" width="880" height="30" rx="6" fill="#1E293B"/>
|
|
86
|
-
<text x="44" y="266" font-size="11" fill="#CBD5E1">G1–G3 = Quality-Lite gate (typecheck · lint · test) + inter-wave session-reviewer · G4 = Full Gate (adds the full suite and a debug-artifact scan)</text>
|
|
87
|
-
|
|
88
|
-
<!-- wave 5 down into /close -->
|
|
89
|
-
<polyline points="969,228 969,296" fill="none" stroke="#94A3B8" stroke-width="2" marker-end="url(#wl-arrow)"/>
|
|
90
|
-
|
|
91
|
-
<!-- row C -->
|
|
92
|
-
<rect x="28" y="300" width="862" height="48" rx="8" fill="#334155"/>
|
|
93
|
-
<text x="44" y="321" font-size="12" font-weight="600" fill="#F8FAFC">After the last wave: every planned item is verified against evidence, the Full Gate runs once more,</text>
|
|
94
|
-
<text x="44" y="338" font-size="12" font-weight="600" fill="#F8FAFC">unfinished work becomes carryover issues, files are staged individually, the session record is written.</text>
|
|
95
|
-
|
|
96
|
-
<rect x="904" y="300" width="130" height="48" rx="8" fill="#238636"/>
|
|
97
|
-
<text x="969" y="330" font-size="14" font-weight="600" fill="#FFFFFF" text-anchor="middle">/close</text>
|
|
98
|
-
</svg>
|