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
|
@@ -231,7 +231,7 @@ It prints one JSON line carrying:
|
|
|
231
231
|
- `wavesConfigHonored` — whether the Session Config `waves` value was used
|
|
232
232
|
- `notes` — human-readable reasons for any of the above
|
|
233
233
|
|
|
234
|
-
**By default, the plan's wave list IS that output.** The coordinator fills tasks into the returned waves without adding, removing, or renumbering them. Exceptions are the empty-role rule below (and its coordinator-direct carve-out) and the user-authorized housekeeping execution deviation above. `--known-scope true` is what drops the Discovery wave on a deep session; `--profile ultradeep` is what selects the ultradeep shape, and it applies ONLY when STATE.md frontmatter carries `session-profile: ultradeep` (written by the `/session ultradeep` argument alias — see `commands/session.md`). `session-type` stays `deep`; the profile changes the wave SHAPE, nothing else, and it ignores the Session Config `waves` value (the shape says so in `wavesConfigHonored` / `notes`). Spec:
|
|
234
|
+
**By default, the plan's wave list IS that output.** The coordinator fills tasks into the returned waves without adding, removing, or renumbering them. Exceptions are the empty-role rule below (and its coordinator-direct carve-out) and the user-authorized housekeeping execution deviation above. `--known-scope true` is what drops the Discovery wave on a deep session; `--profile ultradeep` is what selects the ultradeep shape, and it applies ONLY when STATE.md frontmatter carries `session-profile: ultradeep` (written by the `/session ultradeep` argument alias — see `commands/session.md`). `session-type` stays `deep`; the profile changes the wave SHAPE, nothing else, and it ignores the Session Config `waves` value (the shape says so in `wavesConfigHonored` / `notes`). Spec: the internal ultradeep session-profile PRD (2026-09-06, not in the public mirror) § 5.
|
|
235
235
|
|
|
236
236
|
**Ultradeep agent counts per wave:** take each wave's cap from that wave's `agentCap` in the shape — there is no second table here to disagree with it. The caps are ceilings, not targets, and the Quality wave's cap is still EARNED per the Step 3 rule (the shape marks it `qualityEarned: true`); Research and Code-Discovery share wave 1's cap across their two separately-scoped groups; the Synthesis-Gate wave carries `agentCap: 0` with `coordinatorDirect: true` and writes only the coordinator's own artifacts (audit report, STATE.md, plan).
|
|
237
237
|
|
|
@@ -268,7 +268,7 @@ When `docs-orchestrator.enabled: true`, apply the following concrete dispatch ru
|
|
|
268
268
|
|
|
269
269
|
- `agents: 0` on such an item is a DECLARATION, never a defect — do not "fix" it upward, and do not let the Step 3.5 constraint check or the Step 3 tier table raise it.
|
|
270
270
|
- The wave still counts toward `total-waves` and still occupies its wave number; the renumbering above skips over it, it does not absorb it.
|
|
271
|
-
- The ultradeep Synthesis-Gate (wave 2) is the only such wave today. Without this exception the empty-role rule deletes it — and it is the one wave whose entire job is to stop and ask before any code is written (
|
|
271
|
+
- The ultradeep Synthesis-Gate (wave 2) is the only such wave today. Without this exception the empty-role rule deletes it — and it is the one wave whose entire job is to stop and ask before any code is written (the internal ultradeep session-profile PRD (2026-09-06, not in the public mirror) AC-4).
|
|
272
272
|
- The exception is scoped to the MARKER, not to the profile: any future coordinator-direct wave inherits it without another edit here.
|
|
273
273
|
|
|
274
274
|
### Role Details
|
|
@@ -446,7 +446,9 @@ The wait between presenting this plan and the operator's answer is the LONGEST u
|
|
|
446
446
|
// #1229 — the plan-approval wait is the longest unheartbeated stretch in the flow.
|
|
447
447
|
// Best-effort: a failure must NOT block the handoff to wave-executor.
|
|
448
448
|
import { updateHeartbeat } from 'scripts/lib/session-lock.mjs';
|
|
449
|
-
|
|
449
|
+
import { resolveOwnLockRootFor } from 'scripts/lib/own-lock-root.mjs';
|
|
450
|
+
// The checkout holding this session's lock, not the cwd's (#1730) — at the wrong root nothing is written.
|
|
451
|
+
updateHeartbeat({ sessionId, repoRoot: resolveOwnLockRootFor({ sessionId }).root });
|
|
450
452
|
```
|
|
451
453
|
|
|
452
454
|
- **Ownership-guarded — this cannot steal a peer's lock.** `updateHeartbeat` (`scripts/lib/session-lock.mjs`) reads the lock first and returns `false` WITHOUT writing when `session_id` differs from the one passed, when no lock exists, or when `sessionId` is empty. The write is reached only for a lock this session already owns.
|
|
@@ -77,10 +77,14 @@ Read and parse Session Config per `skills/_shared/config-reading.md`. Store resu
|
|
|
77
77
|
|
|
78
78
|
```javascript
|
|
79
79
|
try {
|
|
80
|
+
const { pathToFileURL } = await import('node:url');
|
|
80
81
|
const { appendSkillInvocation, DEFAULT_SKILL_INVOCATIONS_PATH } =
|
|
81
|
-
await import('${PLUGIN_ROOT}/scripts/lib/skill-invocations-schema.mjs');
|
|
82
|
-
const
|
|
83
|
-
|
|
82
|
+
await import(pathToFileURL('${PLUGIN_ROOT}/scripts/lib/skill-invocations-schema.mjs').href);
|
|
83
|
+
const { resolveLedgerPath } =
|
|
84
|
+
await import(pathToFileURL('${PLUGIN_ROOT}/scripts/lib/ledger-path.mjs').href);
|
|
85
|
+
// In a linked worktree the main checkout's ledger when it is gitignored there,
|
|
86
|
+
// so the record survives the worktree (#1722).
|
|
87
|
+
await appendSkillInvocation(resolveLedgerPath(DEFAULT_SKILL_INVOCATIONS_PATH, process.cwd()), {
|
|
84
88
|
timestamp: new Date().toISOString(),
|
|
85
89
|
event: 'selected',
|
|
86
90
|
skill: 'session-orchestrator:session-start',
|
|
@@ -350,9 +354,11 @@ Invokes `buildLiveSignals` (single SSOT for the signals shape) then `selectMode(
|
|
|
350
354
|
|
|
351
355
|
**See `phase-7-5-mode-selector.md` for full details.**
|
|
352
356
|
|
|
357
|
+
If session-start aborts without work scope (no candidates, nothing to do), send the one report required by `skills/_shared/fleet-protocol.md` § Rücklauf an den Navigator (Pflicht) in this turn (wait case `keine-arbeit`) before returning, instead of waiting silently, and end the answer with its wait sentence.
|
|
358
|
+
|
|
353
359
|
## Phase 7.6: Fleet Check-in (#1462)
|
|
354
360
|
|
|
355
|
-
> Skip silently when `persistence: false` (no ledger for the event). Runs BEFORE the first writing action; everything not stated here is in `skills/_shared/fleet-protocol.md`.
|
|
361
|
+
> Skip silently when `persistence: false` (no ledger for the event). Runs BEFORE the first writing action; everything not stated here is in `skills/_shared/fleet-protocol.md`. Without fleet integration the CLI itself skips (stdout `skipped: "no-fleet"`, no file, no event, #1559): then this phase ends there — no hint, no `auflagen/` read, and no Standard-Auflagen, except for a headless run (`plattform: kopflos`), which applies them at once.
|
|
356
362
|
|
|
357
363
|
Build the check-in JSON (fields per the reference; `session` from `.orchestrator/current-session.json`, `modus` = session type, `kandidaten` = the issue numbers you intend to claim) and pipe it into `node "${CLAUDE_PLUGIN_ROOT:-${CODEX_PLUGIN_ROOT:-$PLUGIN_ROOT}}/scripts/fleet-checkin.mjs"` — it validates, stores `checkin/<session_id>.json` (0600) under `~/.config/navigator/` and emits `orchestrator.fleet.checkin`. Its stdout carries `navigator_state` and `navigator_adresse` (the lease's validated `adresse`, else `null`): on `active`, and only when `navigator_adresse` is non-null and reachable via `ListAgents`, send ONE hint to exactly that name, naming the file path — never expect or wait for a reply (CSM-004). Then read `auflagen/<session_id>.json` via `node "${CLAUDE_PLUGIN_ROOT:-${CODEX_PLUGIN_ROOT:-$PLUGIN_ROOT}}/scripts/fleet-auflagen.mjs" <session_id>` (only a file whose `repo` resolves to this repo's git common dir is `accepted`; a `rejected` one counts as absent); if it is still absent at the stdout's `fallback_due_at`, the reference's Standard-Auflagen apply (the CLI computes that deadline: `zeit` plus `CHECKIN_FALLBACK_MIN` while a navigator is active, `zeit` itself — immediately — otherwise). Reading work and edits in the own worktree continue meanwhile.
|
|
358
364
|
|
|
@@ -51,8 +51,8 @@ on it there fired the *critical* threshold on 84.0% of starts.
|
|
|
51
51
|
| Signal | Threshold | Class |
|
|
52
52
|
|---|---|---|
|
|
53
53
|
| CPU, judged on **min(1m, 5m)** (#943) | above `cpu-load-max-pct` (default 90) | soft |
|
|
54
|
-
| Live peer **sessions** from the registry | ≥ `concurrent-sessions-warn` (default 5) |
|
|
55
|
-
| Claude **processes** (fallback only, registry unreadable) | ≥ threshold × 6 |
|
|
54
|
+
| Live peer **sessions** from the registry | ≥ `concurrent-sessions-warn` (default 5) | **info** (reported, never counted — standing condition, #1654) |
|
|
55
|
+
| Claude **processes** (fallback only, registry unreadable) | ≥ threshold × 6 | **info** (same state, same rule — #1654) |
|
|
56
56
|
| Swap, **only while memory is unhealthy** | `> 3072 MB` hard / `> 1024 MB` soft | both |
|
|
57
57
|
| Zombie processes with a live peer/process context | ≥ 1 | **info** (reported, never counted — see HR-104) |
|
|
58
58
|
| SSH detected AND `ssh-no-docker: true` | — | info note |
|
|
@@ -70,10 +70,16 @@ caller can log which axes fired rather than re-deriving them from prose.
|
|
|
70
70
|
**Unit note (#1089):** `concurrent-sessions-warn` is denominated in SESSIONS. It
|
|
71
71
|
was compared against `claude_processes_count` until this rebuild — a measured 6x
|
|
72
72
|
unit error (median processes:sessions = 6.0 over 1461 paired samples) that made
|
|
73
|
-
the threshold fire on 93.6% of starts instead of 4.2%.
|
|
73
|
+
the threshold fire on 93.6% of starts instead of 4.2%. The 4.2% did not hold
|
|
74
|
+
(re-measured 2026-10-17, #1650: 67.4% M4 / 25.7% M5 since 2026-08-24). `probe()` now supplies
|
|
74
75
|
`peer_sessions_count` from the session registry (`detectPeers()`, self excluded,
|
|
75
76
|
heartbeat-fresh); the rescaled process count is a fallback for hosts where the
|
|
76
|
-
registry is unreadable.
|
|
77
|
+
registry is unreadable. **Report-only since #1654:** a state present on two thirds
|
|
78
|
+
of M4 starts is a standing condition, and HR-104 forbids counting one as a second
|
|
79
|
+
opinion — so the peer count (and its process fallback) appears as an `info:`
|
|
80
|
+
reason but never enters `signals.soft`, neither here nor in the inter-wave gate
|
|
81
|
+
(`evaluateWaveResourceGate()` delegates to `evaluate()`). The rare concurrency
|
|
82
|
+
signal is the session-start banner's start wave (`recent_starts_count`, #1650).
|
|
77
83
|
|
|
78
84
|
**CPU methodology (#943):** the gate/probe runs right after the coordinator's own
|
|
79
85
|
CPU-saturating quality-gate run by construction, so the 1-minute load average
|
|
@@ -90,7 +96,7 @@ to `cpu_load_pct` alone.
|
|
|
90
96
|
Print a one-line Resource Health verdict immediately after Phase 4's output:
|
|
91
97
|
|
|
92
98
|
```
|
|
93
|
-
Resource Health: ⚠ warn — two signals agree (
|
|
99
|
+
Resource Health: ⚠ warn — two signals agree (memory + cpu); capping agents-per-wave at 2.
|
|
94
100
|
```
|
|
95
101
|
|
|
96
102
|
On `green` with a reported signal, print the line but say plainly that nothing
|
|
@@ -7,7 +7,7 @@ Run immediately before Phase 8 so the Mode-Selector recommendation can influence
|
|
|
7
7
|
|
|
8
8
|
## Distinction from Phase 1.5 Recommendations Banner
|
|
9
9
|
|
|
10
|
-
Phase 1.5 renders a `📋` banner that reads the **previous** session's
|
|
10
|
+
Phase 1.5 renders a `📋` banner that reads the **previous** session's Recommendation fields from the STATE.md frontmatter (`recommended-mode`, `rationale`, `completion-rate`, etc., written by session-end Phase 3.7a). It is a backward-looking handoff signal.
|
|
11
11
|
|
|
12
12
|
Phase 7.5 runs `selectMode(signals)` — a **forward-looking** computation over live signals gathered this session (current carryover ratios, recent sessions trend, learnings, bootstrap tier). It emits a `📊` banner.
|
|
13
13
|
|
|
@@ -45,6 +45,8 @@ truncated window (fixed in `c20d4d2`).
|
|
|
45
45
|
|
|
46
46
|
**Key source bindings (Phase → field):**
|
|
47
47
|
|
|
48
|
+
The five Recommendation fields are read from the STATE.md frontmatter that session-end Phase 3.7a wrote at the previous `/close`. The Phase 1.5 Idle Reset keeps them there (#1560; before, it deleted them and this step saw `recommendedMode: null`, confidence 0); the "Phase" column names the phase that surfaces them first, not a writer.
|
|
49
|
+
|
|
48
50
|
| `signals` field | Populated from | Phase |
|
|
49
51
|
|---|---|---|
|
|
50
52
|
| `recommendedMode` | `parseRecommendations(frontmatter).mode` | Phase 1.5 |
|
|
@@ -92,7 +92,7 @@ Hand off to Phase 9 as usual. The coordinator then executes the 1-wave plan sess
|
|
|
92
92
|
1. Proceed to Phase 9 (session-plan handoff) carrying the banner. session-plan short-circuits to the 1-wave `coordinator-direct` plan — the same shape `scripts/session-shape.mjs` resolves for `housekeeping`; `/go` detects it and does NOT invoke wave-executor.
|
|
93
93
|
2. **Run the maintenance loop FIRST** (`skills/session-start/SKILL.md` Phase 7, "For housekeeping sessions"): drift-check work-list, expired sweep, `/evolve analyze`, `/reconcile`, `/evolve dialectic` (dry-run then apply), `/memory-cleanup` — each AUQ-gated. `coordinator-direct` means no wave-executor, NOT zero subagents: the dialectic step dispatches the read-only `dialectic-deriver`. The operator-selected tasks run after the loop, because the loop's outputs (new learnings, new rules) are inputs the task work should already see.
|
|
94
94
|
3. For each agreed task (in dependency order): execute as a direct coordinator action — read files, make changes, run quality checks inline. No subagents, no inter-wave checkpoints.
|
|
95
|
-
4. Log the express-path activation in STATE.md `## Deviations` section: `Express path: N tasks executed coord-direct (express-path.enabled: true, session-type: housekeeping, scope: N issues)` — written BEFORE session-end is invoked. Then invoke `skills/session-end/SKILL.md` directly.
|
|
95
|
+
4. Log the express-path activation in STATE.md `## Deviations` section: `Express path: N tasks executed coord-direct (express-path.enabled: true, session-type: housekeeping, scope: N issues)` — written BEFORE session-end is invoked. Then invoke `skills/session-end/SKILL.md` directly — **unless the session is lead-guided** (fleet integration, `flotte: true` per `skills/_shared/fleet-protocol.md` § Rücklauf an den Navigator): then it ends with summary, report, hand-over and `Bereit zum Schließen.` as the last sentence instead, and the coordinator triggers `/close` (fleet-protocol § Übergabe Punkt 4, #1620). Step 5 does not run on this path: the regular session-end flow of that `/close` finalizes STATE.md.
|
|
96
96
|
5. After session-end completes successfully: verify STATE.md `status` is `completed` and `## Deviations` contains the express-path entry from step 4. If either is missing, warn the user with a one-line note and instructions to re-run `/close` manually. Then return the final session summary to the user.
|
|
97
97
|
|
|
98
98
|
**Persistence contract:**
|
|
@@ -101,6 +101,7 @@ Step 1 is the Phase 9 handoff and ends the session-start turn — the operator t
|
|
|
101
101
|
|
|
102
102
|
- Step 2 (maintenance loop) and step 3 (execute tasks) happen first in that turn's main flow, in that order.
|
|
103
103
|
- Step 4a (deviations log) is written BEFORE session-end is invoked. The coordinator calls `appendDeviation()` from `scripts/lib/state-md.mjs` to append the `Express path:` bullet to the `## Deviations` section while STATE.md is still `status: active`.
|
|
104
|
+
- Lead-guided exception (#1620): with fleet integration, steps 4b and 5 do not run — the turn ends with the hand-over after step 4a, and session-end runs when the coordinator triggers `/close`.
|
|
104
105
|
- Step 4b (invoke session-end) flips `status` to `completed`, writes the metrics record to `.orchestrator/metrics/sessions.jsonl`, and runs the standard close flow. Session-end has no Express Path-specific logic — it treats this run identically to any other completed session.
|
|
105
106
|
- Step 5 (verification) is the coordinator's final action before returning control. The verification check uses `parseStateMd()` from `scripts/lib/state-md.mjs` to read the file and check `frontmatter.status === 'completed'` and that the body contains the literal string `Express path:`.
|
|
106
107
|
|
|
@@ -125,5 +126,5 @@ Proceed normally to Phase 9 (session-plan handoff). The express-path evaluation
|
|
|
125
126
|
- `scripts/express-path.mjs` — the CLI this phase runs; `scripts/lib/express-path.mjs` holds the decision + its `orchestrator.express_path.evaluated` record
|
|
126
127
|
- `skills/session-plan/SKILL.md` § "Express Path Short-Circuit (#214)" — the 1-wave plan Phase 9 emits when the banner is present
|
|
127
128
|
- `skills/go/SKILL.md` — Express Path detection and auto-invocation of session-end after coord-direct tasks
|
|
128
|
-
- `skills/session-end/SKILL.md` —
|
|
129
|
-
- `skills/close/SKILL.md` —
|
|
129
|
+
- `skills/session-end/SKILL.md` — the session-end workflow express-path auto-invokes directly, bypassing the `/close` pre-check
|
|
130
|
+
- `skills/close/SKILL.md` — its pre-check (`scripts/close-precheck.mjs`) stops `/close` with verdict `already-finalized` when STATE.md is `completed` and recorded; that wording is what the user sees if express-path persistence breaks
|
|
@@ -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'`).
|
|
@@ -98,14 +98,22 @@ When (and only when) the prior `status` is `completed`, rewrite STATE.md to a cl
|
|
|
98
98
|
|
|
99
99
|
Reset rules — applies ONLY on the `completed` branch. Do NOT perform this reset on `active` or `paused`; those paths stay user-interactive via AskUserQuestion.
|
|
100
100
|
|
|
101
|
+
Run it as code, after the Recommendations Banner — never rewrite STATE.md by hand (#1560):
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
node "${PLUGIN_ROOT}/scripts/state-md-idle-reset.mjs" --json
|
|
105
|
+
```
|
|
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) `"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
|
+
|
|
101
109
|
1. Set frontmatter `status: idle`.
|
|
102
110
|
2. Clear `current-wave` (set to `0`).
|
|
103
|
-
3. Move the existing `## Wave History` body into a new `## Previous Session` archive section (retain the record, but demote it below the new session's live state). Remove the original `## Wave History` section — wave-executor will recreate it on the next wave.
|
|
111
|
+
3. Move the existing `## Wave History` body into a new `## Previous Session` archive section under `### Wave History (<session>)`, replacing any earlier `## Previous Session` section (retain the record, but demote it below the new session's live state). Remove the original `## Wave History` section — wave-executor will recreate it on the next wave.
|
|
104
112
|
4. Clear `## Deviations` (leave the heading with an empty body so the schema is preserved).
|
|
105
113
|
- **PRESERVE `## What Not To Retry` (#623):** do NOT clear, demote, or drop this section during the Idle Reset. Unlike `## Deviations` (per-session, emptied above) and `## Wave History` (demoted into `## Previous Session`), `## What Not To Retry` is a **cross-session continuity slot** — its entries must survive into the next session so session-start Phase 6.5.1 can surface them. Leave the section, its heading, and all entries byte-for-byte intact.
|
|
106
114
|
- **PRESERVE `## Open Questions` (#772):** do NOT clear, demote, or drop this section during the Idle Reset. Unlike `## Deviations` (per-session, emptied above) and `## Wave History` (demoted into `## Previous Session`), `## Open Questions` is a **cross-session continuity slot** — unanswered entries must survive into the next session so session-start Phase 6.5.2 can surface them as a forced-read. Leave the section, its heading, and all entries (answered and unanswered) byte-for-byte intact.
|
|
107
115
|
5. Leave other frontmatter fields (`schema-version`, `session-type`, `branch`, `issues`, `started_at`, `total-waves`) intact until Phase 1b overwrites them with the new session's values.
|
|
108
|
-
6. **v1.1 Recommendation
|
|
116
|
+
6. **v1.1 Recommendation fields STAY in the frontmatter (Epic #271 Phase A, AC2; changed by #1560):** do NOT remove `recommended-mode`, `top-priorities`, `carryover-ratio`, `completion-rate`, `rationale`. They are the handoff Phase 7.5 reads; session-end Phase 3.7a, their only writer, overwrites them at the next `/close`. If ANY of them is present, also prepend a readable copy (NOT YAML) to the `## Previous Session` body:
|
|
109
117
|
|
|
110
118
|
```markdown
|
|
111
119
|
### Recommendations (archived from v1.1 frontmatter)
|
|
@@ -117,9 +125,9 @@ Reset rules — applies ONLY on the `completed` branch. Do NOT perform this rese
|
|
|
117
125
|
```
|
|
118
126
|
|
|
119
127
|
Omit individual bullets for null-valued fields. If all 5 are null (i.e., `parseRecommendations` returned non-null but every field is null after type-coercion), skip the archival block entirely.
|
|
120
|
-
7. **Scope-baseline key deletion (Epic #894 S5, #898):** If ANY of the 5 `scope-baseline-*` frontmatter keys (`scope-baseline-intent`, `scope-baseline-owner-boundary`, `scope-baseline-planned-files`, `scope-baseline-session`, `scope-baseline-frozen-at`) is present, remove them via
|
|
128
|
+
7. **Scope-baseline key deletion (Epic #894 S5, #898):** If ANY of the 5 `scope-baseline-*` frontmatter keys (`scope-baseline-intent`, `scope-baseline-owner-boundary`, `scope-baseline-planned-files`, `scope-baseline-session`, `scope-baseline-frozen-at`) is present, remove them via `updateFrontmatterFields(contents, {field: null, ...})` (null value deletes the key). Rule 5 leaves unknown frontmatter fields intact and no other rule removes these five — without this step they survive into session N+1 and silently corrupt the next session's drift-baseline denominator. This is a hygiene layer only: the primary defense is mechanical — `scripts/lib/scope-baseline.mjs` compares `scope-baseline-session` against the canonical `session` field, so a stale baseline self-invalidates (`readBaseline()` returns `{stale: true, …}`) even if this rule were skipped. Delete exactly these five keys; do not remove any other unknown key.
|
|
121
129
|
|
|
122
|
-
Rationale: `/close` intentionally keeps STATE.md as a record so the next session-start can read it. This reset completes that contract by demoting the record before new session state is written, so a fresh session never appears "already completed".
|
|
130
|
+
Rationale: `/close` intentionally keeps STATE.md as a record so the next session-start can read it. This reset completes that contract by demoting the record before new session state is written, so a fresh session never appears "already completed". Rule 6 keeps the Recommendation fields because the Phase 7.5 Mode-Selector (`buildLiveSignals()`) reads them from this same frontmatter later in session-start, and the current session writes its own only at session-end Phase 3.7a. Deleting them (the rule until #1560) left the selector with no recommendation after every clean `/close`: `{"mode":"feature","rationale":"missing/invalid recommendedMode → default","confidence":0}`. The archived block is a human-readable copy for browsing STATE.md history.
|
|
123
131
|
|
|
124
132
|
### Snapshot Recovery (#196)
|
|
125
133
|
|
|
@@ -239,7 +247,7 @@ Also read `<state-dir>/STATUS.md` if it exists for additional project-level cont
|
|
|
239
247
|
|
|
240
248
|
### Session-Profile Persistence (`ultradeep` alias)
|
|
241
249
|
|
|
242
|
-
> **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.
|
|
243
251
|
|
|
244
252
|
When the `/session` argument was the `ultradeep` ALIAS, persist the profile alongside the type:
|
|
245
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:-}"
|