session-orchestrator 4.0.1 → 4.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agents/skills/session-plan/SKILL.md +1 -1
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/.codex-plugin/skills/session-plan/SKILL.md +1 -1
- package/.cursor/skills/session-plan/SKILL.md +1 -1
- package/.cursor-plugin/plugin.json +1 -1
- package/CHANGELOG.md +57 -0
- package/README.md +55 -51
- package/agents/ux-evaluator.md +1 -1
- package/commands/close.md +3 -3
- package/commands/go.md +2 -0
- package/commands/memory-cleanup.md +4 -3
- package/commands/persona-panel.md +1 -1
- package/commands/release.md +4 -4
- package/commands/session.md +3 -2
- package/docs/README.md +4 -4
- package/docs/USER-GUIDE.md +115 -48
- package/docs/agent-authoring.md +2 -2
- package/docs/baseline.md +55 -1
- package/docs/ci-setup.md +1 -1
- package/docs/codex-setup.md +1 -0
- package/docs/components.md +2 -2
- package/docs/cursor-setup.md +1 -0
- package/docs/events-schema.md +4 -1
- package/docs/instruction-delivery.md +1 -1
- package/docs/memory-proposal-flow.md +3 -3
- package/docs/migration-v4.md +2 -2
- package/docs/owner-config-schema.md +74 -90
- package/docs/persona-panel.md +4 -4
- package/docs/pi-setup.md +1 -0
- package/docs/rule-authoring.md +13 -6
- package/docs/scope-collision-guard.md +2 -0
- package/docs/session-config-reference.md +55 -22
- package/docs/session-config-template.md +9 -5
- package/docs/vault-docs-architecture.md +4 -2
- package/hooks/_lib/hook-import-set.json +28 -3
- package/hooks/_lib/vcs-create-matcher.mjs +214 -16
- package/hooks/hooks-codex.json +1 -1
- package/hooks/hooks.json +1 -1
- package/hooks/pre-bash-issue-budget.mjs +123 -26
- package/hooks/subagent-telemetry.mjs +106 -20
- package/package.json +4 -4
- package/scripts/baseline-archetypes.mjs +28 -0
- package/scripts/ci/assert-coverage-green.mjs +100 -0
- package/scripts/lib/auto-dialectic.mjs +0 -68
- package/scripts/lib/baseline-archetypes.mjs +439 -0
- package/scripts/lib/build-live-signals.mjs +5 -6
- package/scripts/lib/config/issue-budget.mjs +68 -8
- package/scripts/lib/config/private-config-dir.mjs +3 -2
- package/scripts/lib/config/remote-hosts.mjs +2 -2
- package/scripts/lib/config-schema.mjs +79 -0
- package/scripts/lib/events.mjs +3 -3
- package/scripts/lib/file-lock.mjs +47 -5
- package/scripts/lib/issue-budget-reconcile.mjs +392 -0
- package/scripts/lib/issue-budget.mjs +76 -3
- package/scripts/lib/learnings/evolve-telemetry.mjs +1 -2
- package/scripts/lib/maintenance-due-banner.mjs +440 -0
- package/scripts/lib/owner-config.example.yaml +29 -46
- package/scripts/lib/owner-yaml.mjs +14 -13
- package/scripts/lib/project-hygiene.mjs +182 -6
- package/scripts/lib/quality-gate.mjs +13 -6
- package/scripts/lib/resource-probe/evaluate.mjs +19 -21
- package/scripts/lib/rules-sync.mjs +34 -4
- package/scripts/lib/session-close-backfill.mjs +182 -40
- package/scripts/lib/session-end/phase-skip.mjs +85 -86
- package/scripts/lib/session-end/tail-runner.mjs +178 -0
- package/scripts/lib/session-identity/own-session.mjs +24 -13
- package/scripts/lib/session-schema/constants.mjs +6 -0
- package/scripts/lib/session-schema/validator.mjs +20 -0
- package/scripts/lib/session-shape.mjs +558 -0
- package/scripts/lib/session-start-probes.mjs +10 -3
- package/scripts/lib/session-token-rollup.mjs +95 -10
- package/scripts/lib/state-md/frontmatter-mutators.mjs +22 -34
- package/scripts/lib/state-md.mjs +1 -0
- package/scripts/lib/subagents-schema.mjs +77 -9
- package/scripts/lib/telemetry/pricing.mjs +197 -0
- package/scripts/lib/telemetry/sync.mjs +50 -1
- package/scripts/lib/validate/check-owner-leakage.mjs +17 -8
- package/scripts/lib/validate/check-skill-script-paths.mjs +33 -10
- package/scripts/lib/validate/check-unwired-features.mjs +8 -7
- package/scripts/lib/vault-mirror/process.mjs +2 -1
- package/scripts/lib/vault-mirror/render-sessions.mjs +8 -1
- package/scripts/lib/vault-status/narrative-mirror.mjs +4 -4
- package/scripts/lib/wave-resource-gate.mjs +23 -27
- package/scripts/lib/wave-sizing.mjs +10 -3
- package/scripts/materialize-wave-scope.mjs +68 -14
- package/scripts/print-applicable-rules.mjs +7 -6
- package/scripts/print-learnings-index.mjs +3 -2
- package/scripts/release.mjs +32 -11
- package/scripts/session-shape.mjs +266 -0
- package/skills/_shared/config-reading.md +15 -9
- package/skills/_shared/private-capability-context.md +89 -0
- package/skills/bootstrap/SKILL.md +61 -13
- package/skills/bootstrap/_shared-template.md +99 -14
- package/skills/bootstrap/deep-template.md +36 -26
- package/skills/bootstrap/fast-template.md +44 -8
- package/skills/bootstrap/intensity-heuristic.md +10 -4
- package/skills/bootstrap/private-contract.md +119 -0
- package/skills/bootstrap/public-fallback.md +30 -18
- package/skills/bootstrap/standard-template.md +39 -24
- package/skills/discovery/probes-ui.md +1 -1
- package/skills/docs-orchestrator/audience-mapping.md +1 -1
- package/skills/evolve/SKILL.md +2 -2
- package/skills/gitlab-ops/SKILL.md +3 -3
- package/skills/grill/SKILL.md +1 -1
- package/skills/memory-cleanup/SKILL.md +2 -2
- package/skills/plan/mode-new.md +9 -0
- package/skills/reconcile/SKILL.md +1 -1
- package/skills/session-end/SKILL.md +3 -2
- package/skills/session-end/phase-3-2-docs-verification.md +1 -1
- package/skills/session-end/phase-3-6-tail.md +23 -65
- package/skills/session-end/phase-3-7a-recommendations.md +2 -2
- package/skills/session-end/references/phase-3-documentation-updates.md +8 -6
- package/skills/session-end/references/phase-5-issue-cleanup.md +26 -0
- package/skills/session-end/session-metrics-write.md +31 -12
- package/skills/session-plan/SKILL.md +56 -48
- package/skills/session-plan/wave-template.md +8 -15
- package/skills/session-start/SKILL.md +18 -2
- package/skills/session-start/phase-2-5-docs-planning.md +1 -1
- package/skills/session-start/phase-8-5-express-path.md +12 -9
- package/skills/session-start/references/phase-1-5-session-continuity.md +2 -0
- package/skills/session-start/references/phase-4-ssot-environment-check.md +21 -5
- package/skills/session-start/references/phase-6-7-memory-banner-telemetry-consent.md +3 -1
- package/skills/test-runner/rubric-v1.md +2 -2
- package/skills/wave-executor/SKILL.md +42 -12
- package/skills/wave-executor/circuit-breaker.md +3 -1
- package/skills/wave-executor/references/wave-loop-dispatch.md +4 -2
- package/skills/wave-executor/references/wave-loop-review.md +1 -1
- package/skills/wave-executor/references/wave-loop-scope-manifest.md +6 -2
- package/templates/nextjs-minimal/package.json +1 -1
- package/templates/node-minimal/package.json +1 -1
- package/scripts/lib/multi-provider-build/providers.mjs +0 -64
- package/scripts/lib/multi-provider-build/templating.mjs +0 -130
- package/scripts/lib/owner-config/coerce.mjs +0 -29
- package/scripts/lib/owner-config/constants.mjs +0 -21
- package/scripts/lib/owner-config/defaults.mjs +0 -50
- package/scripts/lib/owner-config/error.mjs +0 -19
- package/scripts/lib/owner-config/index.mjs +0 -13
- package/scripts/lib/owner-config/merge.mjs +0 -52
- package/scripts/lib/owner-config/validate.mjs +0 -259
- package/scripts/lib/owner-config-loader.mjs +0 -170
- package/scripts/lib/owner-config.mjs +0 -28
- package/scripts/lib/soul-resolve.mjs +0 -130
- package/scripts/lib/vault-mirror/render.mjs +0 -8
|
@@ -11,30 +11,23 @@ For each wave, define agents with:
|
|
|
11
11
|
Acceptance: [what "done" looks like — measurable]
|
|
12
12
|
Tools needed: [Read, Write, Edit, Bash, Grep, Glob, etc.]
|
|
13
13
|
Dependencies: [output from which prior role/agent]
|
|
14
|
-
Isolation: [worktree|none —
|
|
15
|
-
MaxTurns: [
|
|
14
|
+
Isolation: [worktree|none — copied verbatim from this wave's `waves[].isolation` entry in the shape output (`scripts/session-shape.mjs`); do not compute by hand]
|
|
15
|
+
MaxTurns: [this wave's `maxTurns` from `scripts/session-shape.mjs`; do not compute by hand]
|
|
16
16
|
status: brainstormed
|
|
17
17
|
```
|
|
18
18
|
|
|
19
|
-
- `Isolation: worktree` means the wave-executor will pass `isolation: "worktree"` to the Agent tool, giving each agent its own git worktree copy
|
|
19
|
+
- `Isolation: worktree` means the wave-executor will pass `isolation: "worktree"` to the Agent tool, giving each agent its own git worktree copy. Each wave's `isolation` (`worktree`/`none`) AND its paired `enforcement` (`strict`/`warn`/`off`) are computed per-wave from that wave's `agentCap` and the session type — the graduated rule (`resolveIsolation`/`resolveEnforcement`, issue #194: ≤2 agents → `none`, ≥5 agents → `worktree`, 3-4 agents → `none` for housekeeping else `worktree`, an explicit Session Config `isolation` value always wins) lives in `scripts/lib/wave-sizing.mjs`, not in `session-shape.mjs` itself — `session-shape.mjs` only calls it once per wave row and copies the two results onto that wave's record.
|
|
20
20
|
- `MaxTurns` is enforced via the agent prompt — wave-executor includes a turn limit instruction in each agent's prompt
|
|
21
21
|
- `status` is the mission-status enum value for this wave-plan item (#340). Always `brainstormed` in the initial plan. Wave-executor updates it at gate transitions (validated → in-dev → testing → completed). Rollback to `brainstormed` is allowed from any state. The five values are listed in `SKILL.md` § Mission-Status Enum; nothing validates them mechanically — `setMissionStatus` writes the string it is given to both STATE.md surfaces on purpose, so keeping the value in-enum is the coordinator's job.
|
|
22
22
|
- The wave-plan item's `id` becomes the `taskId` every `setMissionStatus` call for this item uses, and `setMissionStatus` REFUSES ids outside `[a-z][a-z0-9]*(?:-[a-z0-9]+)*-\d+` (lowercase segments, single hyphens, trailing bare digits) with `refused: 'id-grammar'` and a stderr WARN — nothing is written. Mint ids matching it: `m-1`, `docs-2`, `w2-1`, `w2-a-10` all accepted; `w2-a10`, `w3-p2`, `W3-I1`, `Docs_2` all refused.
|
|
23
23
|
|
|
24
24
|
> **Deconfliction rule:** Before finalizing agent specs for a wave, verify that no two agents in the same wave list overlapping `Files:` paths. If overlap is found, either merge the agents into one or move one task to a later wave. Two agents editing the same file in parallel causes merge conflicts that require manual resolution.
|
|
25
25
|
|
|
26
|
-
## Agent Count
|
|
26
|
+
## Agent Count and Turn Budget per Wave
|
|
27
27
|
|
|
28
|
-
|
|
29
|
-
|-------------|-----------|-----------|-------------|---------|-------------|
|
|
30
|
-
| housekeeping | — | 1-6* | — | — | — |
|
|
31
|
-
| feature | 4-6 | 6 | 4-6 | 4 | 2 |
|
|
32
|
-
| deep | 6-8 | 6-10 | 6-8 | 6 | 2-4 |
|
|
28
|
+
There is no count table here. Both numbers come from the session shape resolved once at plan time (`SKILL.md` § Role-to-Wave Mapping):
|
|
33
29
|
|
|
34
|
-
|
|
30
|
+
- **Agent ceiling** — that wave's `agentCap` (already capped by the Session Config `agents-per-wave` value; `agentCapRaw` is the value before that ceiling).
|
|
31
|
+
- **Turn budget** — that wave's `maxTurns` (`null` on a `coordinatorDirect: true` wave, which dispatches no agents).
|
|
35
32
|
|
|
36
|
-
> **The Quality
|
|
37
|
-
|
|
38
|
-
> **Note:** For feature and deep sessions, prefer the complexity-based agent counts from Step 3. This table provides defaults when complexity scoring is skipped (housekeeping) or as a fallback.
|
|
39
|
-
|
|
40
|
-
> \* Housekeeping sessions use single-wave serial execution (see wave-executor). Agent counts are for the single consolidated wave, not per-role.
|
|
33
|
+
> **The Quality wave's cap is a CAP, not a target.** Quality capacity is need-gated: the effective count is `min(<the wave's agentCap>, ceil((HIGH + MED gaps from the most recent qa-strategist run) / 3))`. 0 gaps → 0 test-writing tasks and the wave is skipped (the read-only review panel is unaffected); no qa-strategist signal at all → a conservative 1-2, never the blind cap. The shape marks this wave `qualityEarned: true`. Full rule: `SKILL.md` § Agent Count by Tier footnote.
|
|
@@ -205,7 +205,7 @@ Group issues by:
|
|
|
205
205
|
|
|
206
206
|
## Phase 4: SSOT & Environment Check
|
|
207
207
|
|
|
208
|
-
> Always runs; every finding is a NON-BLOCKING banner in the Session Overview (never a gate — the Full Gate is the Quality wave's job). Covers SSOT freshness, the Baseline quality commands (resolved `.orchestrator/policy/quality-gates.json` → Session Config → defaults, each availability-checked with `command -v`), Pencil design status, plugin + `bootstrap.lock` freshness, and the banner-probe family registered in `scripts/lib/session-start-probes.mjs` (vault-staleness, telemetry-flush-health, ci-status, qg-command-drift, peer-cards, loop-readiness, instruction-budget,
|
|
208
|
+
> Always runs; every finding is a NON-BLOCKING banner in the Session Overview (never a gate — the Full Gate is the Quality wave's job). Covers SSOT freshness, the Baseline quality commands (resolved `.orchestrator/policy/quality-gates.json` → Session Config → defaults, each availability-checked with `command -v`), Pencil design status, plugin + `bootstrap.lock` freshness, and the banner-probe family registered in `scripts/lib/session-start-probes.mjs` (vault-staleness, telemetry-flush-health, ci-status, qg-command-drift, peer-cards, loop-readiness, instruction-budget, maintenance-due, sessions-staleness, sessions-integrity, owner-config, moc-staleness, context-coverage, claude-md-budget-lint, tests:src-ratio, project-hygiene, mirror-issues, git-config-drift). Per-probe module path, return contract and exact banner wording: [`references/phase-4-ssot-environment-check.md`](references/phase-4-ssot-environment-check.md).
|
|
209
209
|
|
|
210
210
|
## Phase 4.5: Resource Health (v3.1.0)
|
|
211
211
|
|
|
@@ -279,6 +279,20 @@ Surface context from previous sessions:
|
|
|
279
279
|
- ALWAYS verify current state in actual code — never assume based on memory or SSOT alone
|
|
280
280
|
|
|
281
281
|
**For `housekeeping` sessions:**
|
|
282
|
+
|
|
283
|
+
A housekeeping session IS the **maintenance loop** — the one place the repo's own upkeep runs, which is why the session-start `maintenance-due` probe (`checkMaintenanceDue`, `scripts/lib/maintenance-due-banner.mjs`) recommends exactly this session type, and why the retired session-end nudges (Phase 3.6.5 auto-dream, 3.6.7 auto-dialectic) no longer exist: a nudge fired at close asks the operator to do here what he can only do here.
|
|
284
|
+
|
|
285
|
+
Propose the **ordered default scope** below in the Phase 8 Q&A. Every step is AUQ-gated — the operator selects which of them run, none of them runs unasked:
|
|
286
|
+
|
|
287
|
+
1. **Drift-check as a work-list** — `checker.mjs --mode warn` (procedure below); its `errors[]`/`warnings[]` become candidate scope.
|
|
288
|
+
2. **Expired-learnings sweep** — the same sweep session-end 3.6.4 applies mechanically (`runTailPhases` / `runExpiredSweep`), run here when the `sweep` signal is due.
|
|
289
|
+
3. **`/evolve analyze`** — extract this period's session patterns into learnings.
|
|
290
|
+
4. **`/reconcile`** — turn high-confidence learnings into operator-approved `.claude/rules/` proposals.
|
|
291
|
+
5. **`/evolve dialectic`** — dry-run first, review `.orchestrator/dialectic-pending.md`, then apply. This step dispatches the read-only `dialectic-deriver` agent, so **"coordinator-direct" means no wave-executor, not zero subagents**. <!-- path-check: example -->
|
|
292
|
+
6. **`/memory-cleanup`** — `--dry-run` writes the MEMORY.md proposal to `.orchestrator/pending-dream.md`; `--apply-pending` applies it. <!-- path-check: example -->
|
|
293
|
+
|
|
294
|
+
Operator-selected issues (from Phase 6) are appended AFTER this loop, not interleaved with it — the loop's outputs (new learnings, new rules) are inputs the issue work should already see.
|
|
295
|
+
|
|
282
296
|
- Focus on git cleanup, documentation currency, CI health
|
|
283
297
|
- Skip deep research — prioritize operational tasks
|
|
284
298
|
- Run token efficiency check: `bash "${CLAUDE_PLUGIN_ROOT:-${CODEX_PLUGIN_ROOT:-$PLUGIN_ROOT}}/scripts/token-audit.sh"` and include findings in Session Overview. Flag any HIGH/WARN items as recommended housekeeping tasks.
|
|
@@ -335,12 +349,14 @@ When all conditions are met, the CLI emits the banner on stderr:
|
|
|
335
349
|
```
|
|
336
350
|
Express path activated — <N> tasks, coordinator-direct, no inter-wave checks.
|
|
337
351
|
```
|
|
338
|
-
Carry that banner into Phase 9 and hand off to session-plan as usual — session-plan short-circuits to a 1-wave `coordinator-direct` plan, which is the artifact `/go` detects. Tasks are then executed coordinator-direct (bypassing wave-executor, subagent dispatch and inter-wave checkpoints) and a Deviations entry is logged in STATE.md. Silent no-op when any condition fails — proceeds normally to Phase 9.
|
|
352
|
+
Carry that banner into Phase 9 and hand off to session-plan as usual — session-plan short-circuits to a 1-wave `coordinator-direct` plan, which is the artifact `/go` detects. That 1-wave plan is not a special case: it EQUALS the housekeeping shape `scripts/session-shape.mjs` resolves (`totalWaves: 1`, `coordinatorDirect: true`, one `Housekeeping` wave with `agentCap: 0`), so the express path and the ordinary housekeeping path emit the same shape and differ only in scope size. Tasks are then executed coordinator-direct (bypassing wave-executor, subagent dispatch and inter-wave checkpoints) and a Deviations entry is logged in STATE.md. Silent no-op when any condition fails — proceeds normally to Phase 9.
|
|
339
353
|
|
|
340
354
|
**See `phase-8-5-express-path.md` for full details.**
|
|
341
355
|
|
|
342
356
|
## Phase 9: Handoff to Session Plan
|
|
343
357
|
|
|
358
|
+
> **Record the wave shape ONCE, here, at plan time.** Run `node scripts/session-shape.mjs --repo-root "$PWD" --session-type <housekeeping|feature|deep> [--profile ultradeep] [--known-scope true|false] --task-count <N>` — the CLI wraps `resolveAndRecordSessionShape` / `resolveSessionShape` from `scripts/lib/session-shape.mjs`, prints ONE JSON line (`totalWaves`, `discovery`, `coordinatorDirect`, `waves[]`, `notes[]`) and records the event `orchestrator.session.shape_resolved`. session-plan CONSUMES that JSON instead of re-deriving the wave count; never hand-write a wave count that the shape already answers. Add `--no-event` only for a planning dry-run.
|
|
359
|
+
|
|
344
360
|
After user alignment:
|
|
345
361
|
1. Invoke the **session-plan** skill with the agreed scope
|
|
346
362
|
2. The session-plan skill will decompose tasks into waves and present the execution plan
|
|
@@ -23,7 +23,7 @@ If `DOCS_ENABLED` is not `true`, skip all remaining steps in this phase and proc
|
|
|
23
23
|
Using signals already gathered in Phases 2–5 (git analysis, VCS issues, branch state, SSOT checks), apply the following heuristic to determine which audiences are likely affected. Record each match with its triggering signal for inclusion in the output block.
|
|
24
24
|
|
|
25
25
|
**User audience** — flag as likely when any of the following are true:
|
|
26
|
-
- Affected files include `README.md`, `docs/user/**/*.md`, `docs/getting-started.md`, or `examples/**/*.md`
|
|
26
|
+
- Affected files include `README.md`, `docs/user/**/*.md`, `docs/getting-started.md`, or `examples/**/*.md` <!-- path-check: example -->
|
|
27
27
|
- Open or recently closed issues reference CLI UX changes, new user-facing commands, or a breaking API change
|
|
28
28
|
- New public commands are introduced (e.g. changes to `commands/` directory)
|
|
29
29
|
- Install flow or setup instructions are modified
|
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
|
|
5
5
|
After the user confirms the session type and scope via the Q&A above, evaluate whether the **Express Path** applies before handing off to session-plan. The express path collapses the full 5-wave plan into a single coordinator-direct phase for lightweight sessions.
|
|
6
6
|
|
|
7
|
+
> **The 1-wave plan is not a special artifact — it EQUALS the housekeeping shape.** `resolveSessionShape` in `scripts/lib/session-shape.mjs` (CLI: `node scripts/session-shape.mjs --repo-root "$PWD" --session-type housekeeping --task-count <N>`) already resolves `housekeeping` to `totalWaves: 1`, `coordinatorDirect: true`, one `Housekeeping` wave with `agentCap: 0` and `verification: 'full'`. Express path and ordinary housekeeping therefore emit the SAME shape and differ only in scope size; the shape is recorded once at Phase 9 as `orchestrator.session.shape_resolved`.
|
|
8
|
+
|
|
7
9
|
**Do not evaluate these conditions by hand — run the CLI (#1119, #1146).**
|
|
8
10
|
|
|
9
11
|
```bash
|
|
@@ -87,19 +89,20 @@ Express path activated — <N> tasks, coordinator-direct, no inter-wave checks.
|
|
|
87
89
|
|
|
88
90
|
Hand off to Phase 9 as usual. The coordinator then executes the 1-wave plan session-plan emits directly, without dispatching subagents:
|
|
89
91
|
|
|
90
|
-
1. Proceed to Phase 9 (session-plan handoff) carrying the banner. session-plan short-circuits to the 1-wave `coordinator-direct` plan
|
|
91
|
-
2.
|
|
92
|
-
3.
|
|
93
|
-
4.
|
|
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
|
+
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
|
+
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.
|
|
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.
|
|
94
97
|
|
|
95
98
|
**Persistence contract:**
|
|
96
99
|
|
|
97
|
-
Step 1 is the Phase 9 handoff and ends the session-start turn — the operator types `/go` next, exactly as on the normal path. Steps 2–
|
|
100
|
+
Step 1 is the Phase 9 handoff and ends the session-start turn — the operator types `/go` next, exactly as on the normal path. Steps 2–5 then MUST all happen within a SINGLE coordinator turn, the one `/go` opens. Specifically:
|
|
98
101
|
|
|
99
|
-
- Step 2 (execute tasks)
|
|
100
|
-
- Step
|
|
101
|
-
- Step
|
|
102
|
-
- Step
|
|
102
|
+
- Step 2 (maintenance loop) and step 3 (execute tasks) happen first in that turn's main flow, in that order.
|
|
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
|
+
- 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
|
+
- 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:`.
|
|
103
106
|
|
|
104
107
|
When `/go` is invoked and session-plan emitted a 1-wave Express Path plan (per `skills/session-plan/SKILL.md` § "Express Path Short-Circuit"), the `/go` command MUST detect this and route to coord-direct execution + session-end auto-invocation, NOT to wave-executor. See `commands/go.md` for the detection branch — that plan is the artifact `/go` keys on, which is why Phase 8.5 hands off to session-plan rather than skipping it.
|
|
105
108
|
|
|
@@ -251,4 +251,6 @@ import { setSessionProfile } from '${PLUGIN_ROOT}/scripts/lib/state-md.mjs';
|
|
|
251
251
|
contents = setSessionProfile(contents, 'ultradeep');
|
|
252
252
|
```
|
|
253
253
|
|
|
254
|
+
**The profile owns its wave count; the STATE.md write does not compute one.** `total-waves` comes from the resolved shape — `node scripts/session-shape.mjs --repo-root "$PWD" --session-type deep --profile ultradeep …` (`resolveSessionShape` / `resolveAndRecordSessionShape` in `scripts/lib/session-shape.mjs`), whose `totalWaves` field is authoritative and is recorded once as `orchestrator.session.shape_resolved`. The former AC-9 rule (the profile must honour the Session Config `waves:` value) is DROPPED as of 2026-09-09: a profile that cannot set its own wave count is not a wave-shape variant. Write `total-waves` from `shape.totalWaves`, never from a hand-derived number, and never from `waves:` when a profile is present — the shape's `wavesConfigHonored` flag and `notes[]` already say which of the two won.
|
|
255
|
+
|
|
254
256
|
For every other argument, write **nothing** — absence is the contract, never `''`, `none` or `null` as a value. `setSessionProfile(contents, null)` deletes a stale key inherited from a previous session's record; it throws on an empty-string profile, so never pass one.
|
|
@@ -69,11 +69,27 @@
|
|
|
69
69
|
|
|
70
70
|
Additionally, invoke the instruction-budget probe (`scripts/lib/instruction-budget-guard.mjs`) via `checkInstructionBudget({ repoRoot })`. The helper returns `null` (silent no-op) when the always-on directive count is at or under the configured ceiling, or on any read failure. When a non-null result is returned (`{ severity: 'warn', message }`), render `result.message` alongside the other banners. Non-blocking. Cross-reference: "Instruction Budget Audit" (#687; archived in the private Meta-Vault).
|
|
71
71
|
|
|
72
|
-
Additionally, invoke the
|
|
73
|
-
- **Nudge fires**: `"⚠ reconcile-nudge: <N> active learnings, <E> rule-eligible, last reconcile run: <never|YYYY-MM-DD> — run /reconcile to convert learnings into rules."` plus, when `reconcile.enabled: false`, an additional line: `"(reconcile.enabled: false — banner is advisory only; /reconcile still runs on-demand.)"`
|
|
74
|
-
- **No nudge**: silent (no banner).
|
|
72
|
+
Additionally, invoke the maintenance-due probe (`scripts/lib/maintenance-due-banner.mjs`) via `await checkMaintenanceDue({ repoRoot, config: $CONFIG })`. This probe REPLACED the former `reconcile-nudge` entry in `scripts/lib/session-start-probes.mjs` (it reuses `computeReconcileNudge` wholesale as one of its six signals) AND the two retired session-end nudges — Phase 3.6.5 auto-dream and Phase 3.6.7 auto-dialectic — which asked the operator at close for work only a housekeeping session can do. Return contract: `null` (silent no-op) or `{ severity: 'warn', message }`; never throws. `computeMaintenanceDue` exposes the same computation without the banner suppression.
|
|
75
73
|
|
|
76
|
-
|
|
74
|
+
The six signals (`MAINTENANCE_TOTAL_SIGNALS = 6`), each side-effect-free:
|
|
75
|
+
|
|
76
|
+
| id | due when | source |
|
|
77
|
+
|---|---|---|
|
|
78
|
+
| `evolve` | no `orchestrator.evolve.completed` on record AND ≥ `MAINTENANCE_MIN_LEARNINGS` (20) active learnings | `events.jsonl` + `computeReconcileNudge` |
|
|
79
|
+
| `sweep` | the dry-run expiry sweep would archive ≥ 1 entry | `scripts/lib/learnings/expiry-sweep.mjs` |
|
|
80
|
+
| `reconcile` | `computeReconcileNudge().nudge === true` | `scripts/lib/reconcile-nudge-banner.mjs` (reused whole) |
|
|
81
|
+
| `dialectic` | `shouldDispatchAutoDialectic().trigger === true` | `scripts/lib/auto-dialectic.mjs` — the side-effect-free decision function; a variant that advanced the last-run stamp would consume the signal it reports (the former recording wrapper was removed in #1288) |
|
|
82
|
+
| `memory-cleanup` | `shouldDispatchAutoDream().trigger === true` | `scripts/lib/auto-dream.mjs` |
|
|
83
|
+
| `pending-sidecar` | an unapplied pending dream/dialectic proposal younger than `SIDECAR_MAX_AGE_DAYS` (14) | `.orchestrator/*-pending*.md` |
|
|
84
|
+
|
|
85
|
+
Introduces NO new Session Config key — every threshold is an existing key or an existing module constant.
|
|
86
|
+
|
|
87
|
+
- **Nothing due and everything readable**: silent (no banner).
|
|
88
|
+
- **A housekeeping session completed within `HOUSEKEEPING_COOLDOWN_DAYS` (7) days**: silent, even when signals are due — the operator already did the thing the banner would ask for. This cooldown is what keeps the instrument rare (HR-101, `.claude/rules/host-resources.md`); the housekeeping session's own record IS the last-maintenance stamp.
|
|
89
|
+
- **Otherwise**: render `result.message`, shaped as `⚠ maintenance due: <N> of 6 (<id>: <detail> · <id>: <detail>) — run /session housekeeping.` Every due signal carries the number or date that made it due (HR-106) — no aggregate stands alone.
|
|
90
|
+
- **Unreadable inputs** append an ` · undeterminable: <id>, <id>` suffix before the `— run /session housekeeping.` tail. A signal whose inputs cannot be read is three-state: never folded into "clean" (the `never` vs `undeterminable` discipline inherited from `reconcile-nudge-banner.mjs`).
|
|
91
|
+
|
|
92
|
+
Non-blocking. Cross-reference: `scripts/lib/reconcile-nudge-banner.mjs` (`computeReconcileNudge`), `scripts/lib/reconcile/engine.mjs` (`runReconcile`), `skills/reconcile/SKILL.md`, `skills/session-start/SKILL.md` Phase 7 (the housekeeping maintenance loop the banner routes to), and issues #723 / #1200.
|
|
77
93
|
|
|
78
94
|
Additionally, invoke the sessions-staleness probe (`scripts/lib/sessions-staleness-banner.mjs`) via `checkSessionsStaleness({ repoRoot })` (synchronous — no await). This detects the "close-through" gap: sessions that end without ever writing a `.orchestrator/metrics/sessions.jsonl` ledger record. It returns `null` (silent no-op) when `.orchestrator/metrics/sessions.jsonl` or `.orchestrator/metrics/events.jsonl` are absent or all-malformed, when no foreign (pre-session) event exists, or when the gap between the last ledger entry and the newest foreign event is at or under the warn threshold. When a non-null result is returned (`{ severity, message }`), render `result.message` alongside the other banners:
|
|
79
95
|
- **warn** (gap > 8h): `"⚠ sessions-staleness: last sessions.jsonl entry <ISO> is <N>h behind pre-session events.jsonl activity <ISO> — possible close-through gap (sessions ended without a ledger record; run node scripts/backfill-abandoned-sessions.mjs --dry-run)."`
|
|
@@ -156,5 +172,5 @@
|
|
|
156
172
|
|
|
157
173
|
Non-blocking. Cross-reference: `scripts/lib/vcs-repo-spec.mjs` (`isQueryFailure` — the same absence-vs-query-failure split this probe's `degraded` state implements).
|
|
158
174
|
|
|
159
|
-
All banners are non-blocking — display in the Session Overview, do not halt the session. If `bootstrap-lock-freshness.mjs` is absent (pre-#186 plugin install) or `peer-cards/staleness-banner.mjs` is absent (pre-#503 plugin install) or `loop-readiness-banner.mjs` is absent (pre-#633 plugin install) or `instruction-budget-guard.mjs` is absent (pre-#687 plugin install) or `reconcile-nudge-banner.mjs`
|
|
175
|
+
All banners are non-blocking — display in the Session Overview, do not halt the session. If `bootstrap-lock-freshness.mjs` is absent (pre-#186 plugin install) or `peer-cards/staleness-banner.mjs` is absent (pre-#503 plugin install) or `loop-readiness-banner.mjs` is absent (pre-#633 plugin install) or `instruction-budget-guard.mjs` is absent (pre-#687 plugin install) or `maintenance-due-banner.mjs` / `reconcile-nudge-banner.mjs` are absent (pre-#723 plugin install) or `sessions-staleness-banner.mjs` is absent (pre-#724 plugin install) or `sessions-integrity-banner.mjs` is absent (pre-#958 plugin install) or `owner-config-banner.mjs` is absent (pre-#820 plugin install) or `moc-staleness-banner.mjs` / `context-coverage-banner.mjs` are absent (pre-#831 plugin install) or `claude-md-budget-lint.mjs` is absent (pre-#878 plugin install) or `mirror-issues-banner.mjs` is absent (pre-#1022 plugin install), skip silently.
|
|
160
176
|
|
|
@@ -99,5 +99,7 @@ Setting `telemetry:\n enabled: true` in the host-local `~/.config/session-orche
|
|
|
99
99
|
|
|
100
100
|
The decision persists host-locally in `~/.config/session-orchestrator/telemetry.json`; once `consent` is non-`null` (granted or denied), `resolveConsent().prompt` stays `false` and this phase never fires again on that host — no repeat prompting across repos or sessions.
|
|
101
101
|
|
|
102
|
-
Cross-reference: GitLab #845 (Epic #841); `docs/
|
|
102
|
+
Cross-reference: GitLab #845 (Epic #841); `docs/telemetry.md`; consent API in `scripts/lib/telemetry/consent.mjs` (`resolveConsent`, `grantConsent`, `denyConsent`, `isHeadless`, `readTelemetryState`).
|
|
103
|
+
|
|
104
|
+
Historical design: archived PRD `docs/prd/2026-07-20-anonymous-usage-telemetry.md` §3 FA1/FA5. <!-- path-check: historical -->
|
|
103
105
|
|
|
@@ -316,14 +316,14 @@ indicates a translucent/blurred background that uses:
|
|
|
316
316
|
- `.glassEffect()` — correct Liquid Glass API
|
|
317
317
|
- `.glassEffect(.regular)` / `.glassEffect(.prominent)` — parametrised Liquid Glass
|
|
318
318
|
- Solid color backgrounds (no blur) — these are not Liquid Glass candidates
|
|
319
|
-
- Explicitly documented exceptions in `docs/apple-hig-exceptions.md`
|
|
319
|
+
- Explicitly documented exceptions in `docs/apple-hig-exceptions.md` <!-- path-check: example -->
|
|
320
320
|
|
|
321
321
|
**Severity mapping:**
|
|
322
322
|
|
|
323
323
|
| Condition | Severity |
|
|
324
324
|
|---|---|
|
|
325
325
|
| Non-compliant surface, no documented exception | MEDIUM |
|
|
326
|
-
| Non-compliant surface, documented exception in `docs/apple-hig-exceptions.md` | LOW |
|
|
326
|
+
| Non-compliant surface, documented exception in `docs/apple-hig-exceptions.md` | LOW <!-- path-check: example --> |
|
|
327
327
|
|
|
328
328
|
**Evidence required:**
|
|
329
329
|
- Peekaboo screenshot of the non-compliant surface (referenced in `evidence_path`).
|
|
@@ -87,6 +87,8 @@ Before starting the first wave (Discovery role):
|
|
|
87
87
|
- `persistence` (default: true), `enforcement` (default: warn), `isolation` (default: auto)
|
|
88
88
|
- `agents-per-wave` (default: 6), `max-turns` (default: auto), `pencil` (default: null)
|
|
89
89
|
|
|
90
|
+
**Neither `agents-per-wave` nor `max-turns` carries its own default here.** The per-wave `agentCap` and `maxTurns` come from the RESOLVED SHAPE (`node scripts/session-shape.mjs --repo-root "$PWD" --session-type <session-type> [--profile <session-profile>] [--known-scope true|false]`, module `scripts/lib/session-shape.mjs`) — that is the one place a session mode becomes an execution shape. Session Config's `agents-per-wave` (with its per-type override, e.g. `6 (deep: 18)`) CLAMPS the shape's `agentCap`; `max-turns: auto` is expanded per type inside the shape, not in this file.
|
|
91
|
+
|
|
90
92
|
**Execution Config shortcut:** If the session-plan output contains an `### Execution Config` section, its execution-level fields (waves, agents-per-wave, isolation, enforcement, max-turns) take precedence over `$CONFIG`. Session-level fields (persistence, pencil) always come from `$CONFIG`. If the Execution Config section is missing, use `$CONFIG` alone.
|
|
91
93
|
6. **Initialize session metrics** (if `persistence` enabled): Prepare a metrics tracking object for this session:
|
|
92
94
|
- `session_id`: `<branch>-<YYYY-MM-DD>-<HHmm>` (HHmm from `started_at` — ensures uniqueness across multiple sessions per day)
|
|
@@ -152,6 +154,19 @@ Wave 0 — Initializing
|
|
|
152
154
|
|
|
153
155
|
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.
|
|
154
156
|
|
|
157
|
+
**Then VALIDATE `total-waves` against the resolved shape — do not skip this.** A plan whose wave count the shape does not produce must never be dispatched silently:
|
|
158
|
+
|
|
159
|
+
```bash
|
|
160
|
+
node scripts/session-shape.mjs --repo-root "$PWD" \
|
|
161
|
+
--session-type <session-type> [--profile <session-profile>] [--known-scope true|false] \
|
|
162
|
+
--no-event | jq .totalWaves
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
`--no-event` is used HERE because the plan-time run already recorded `orchestrator.session.shape_resolved` — this is a re-read, not a second resolution. Compare the printed number with the plan's wave count (the value just written to `total-waves`):
|
|
166
|
+
|
|
167
|
+
- **Equal** → continue to Wave 1.
|
|
168
|
+
- **Mismatch** → STOP. Surface it via `AskUserQuestion` per `.claude/rules/ask-via-tool.md`, with the shape's number and the plan's number both in the option descriptions: **re-plan to the shape (Recommended)** — rebuild the wave plan at the shape's wave count, the only outcome that keeps STATE.md, the ledger and the dispatch loop describing the same session — versus **proceed with a logged Deviation**, which requires appending the divergence to STATE.md `## Deviations` (`appendDeviationOnDisk()` from `scripts/lib/state-md.mjs`) before the first dispatch.
|
|
169
|
+
|
|
155
170
|
#### Pre-Wave 1b Extension: Docs Tasks Persistence (A3 / #230)
|
|
156
171
|
|
|
157
172
|
After writing the base STATE.md frontmatter above, conditionally persist the docs tasks block emitted by session-plan:
|
|
@@ -227,7 +242,7 @@ Claude Code's `Agent` tool with `isolation: "worktree"` changes `process.cwd()`
|
|
|
227
242
|
|
|
228
243
|
**Rules for the coordinator (this is YOU during wave execution):**
|
|
229
244
|
|
|
230
|
-
1. **After every Agent() dispatch** (before reading its output), call `restoreCoordinatorCwd()` from `scripts/lib/
|
|
245
|
+
1. **After every Agent() dispatch** (before reading its output), call `restoreCoordinatorCwd()` from `scripts/lib/workspace.mjs`. `wave-loop.md § 2` makes this explicit.
|
|
231
246
|
2. **Prefer absolute file paths** for Read/Edit/Write tool calls. A drifted CWD turns relative paths into silent cross-tree writes.
|
|
232
247
|
3. **Before any Bash git command**, either `cd` inside a subshell (`cd /path && cmd`) or rely on `git -C /path <cmd>`. Do not assume CWD.
|
|
233
248
|
4. **Verify at checkpoints** — when in doubt, run `git rev-parse --show-toplevel` to confirm which tree is currently active.
|
|
@@ -302,29 +317,44 @@ Cross-reference: PRD F2.1 / issue #501 / `docs/memory-proposal-flow.md` (coordin
|
|
|
302
317
|
|
|
303
318
|
## Session Type Behavior
|
|
304
319
|
|
|
305
|
-
### Housekeeping Sessions
|
|
320
|
+
### Housekeeping Sessions — the Maintenance Loop
|
|
321
|
+
|
|
322
|
+
A housekeeping session is **ONE coordinator-direct wave**, not a shrunken multi-wave run: `node scripts/session-shape.mjs --repo-root "$PWD" --session-type housekeeping --no-event` resolves to `totalWaves: 1` with that wave's `coordinatorDirect: true` and `writes: true`. "Coordinator-direct" means **no wave-executor dispatch loop** — it does not mean zero subagents (`/evolve dialectic` dispatches the read-only `dialectic-deriver`).
|
|
323
|
+
|
|
324
|
+
**Ordered default scope — the maintenance loop.** Run it in this order, before the session's selected issues:
|
|
325
|
+
|
|
326
|
+
| # | Run | Gate | Artefact that proves it ran |
|
|
327
|
+
|---|---|---|---|
|
|
328
|
+
| 1 | `claude-md-drift-check` | unconditional | checker JSON (`errors`/`warnings` counts) |
|
|
329
|
+
| 2 | expired-learnings sweep | unconditional | `orchestrator.learnings.sweep_applied` |
|
|
330
|
+
| 3 | `/evolve analyze` | AUQ-gated (the operator approves the proposed learnings) | `orchestrator.evolve.completed` |
|
|
331
|
+
| 4 | `/reconcile` | AUQ-gated (rule proposals are never applied unasked) | `orchestrator.reconcile.completed` with `dry_run: false` |
|
|
332
|
+
| 5 | `/evolve dialectic` | AUQ-gated (the derived thesis is presented, not committed) | `orchestrator.dialectic.completed` |
|
|
333
|
+
| 6 | `/memory-cleanup` | AUQ-gated (deletions are operator-approved) | `orchestrator.memory.cleanup_completed` |
|
|
334
|
+
|
|
335
|
+
The session-start probe `maintenance-due` (`scripts/lib/maintenance-due-banner.mjs`) says which of these are DUE for this repo; a run that is not due may be skipped, and the skip is reported. An AUQ-gated run the operator declines is reported as declined — never as done. **Absence of the artefact event is the only evidence that counts**: a run claimed in prose without its event is not a run (`.claude/rules/verification-before-completion.md`).
|
|
306
336
|
|
|
307
|
-
|
|
337
|
+
Then the mechanics:
|
|
308
338
|
|
|
309
339
|
1. Initialize STATE.md as normal (`session-type: housekeeping`, `total-waves: 1`)
|
|
310
|
-
2. Do NOT create `wave-scope.json` —
|
|
311
|
-
3.
|
|
340
|
+
2. Do NOT create `wave-scope.json` — there is no agent fan-out to constrain; the coordinator's own edits stay governed by its `coordinator.json` record
|
|
341
|
+
3. Execute the maintenance loop above, then the session's selected issues, serially as coordinator actions
|
|
312
342
|
4. Run Baseline quality checks after all tasks complete (not between tasks)
|
|
313
343
|
5. Skip session-reviewer dispatch — housekeeping changes are low-risk
|
|
314
344
|
6. Do NOT update STATE.md to `status: completed` — that write is reserved for session-end per state-ownership contract (`skills/_shared/state-ownership.md`). Leave `status: active`.
|
|
315
345
|
7. Proceed directly to session-end (`/close`)
|
|
316
346
|
|
|
317
|
-
|
|
347
|
+
Beyond the loop: git cleanup, SSOT refresh, CI fixes, branch merges, documentation.
|
|
318
348
|
End with a single commit summarizing all housekeeping work.
|
|
319
349
|
|
|
320
350
|
### Feature Sessions
|
|
321
|
-
-
|
|
322
|
-
-
|
|
351
|
+
- **3 waves** (Impl-Core → Impl-Polish+Quality → Finalization) with **no Discovery wave** — read them from the shape, not from this file: `node scripts/session-shape.mjs --repo-root "$PWD" --session-type feature --no-event`
|
|
352
|
+
- Per-wave agent caps come from the shape's `agentCap` (the shape caps a feature wave at 4), clamped by Session Config `agents-per-wave`
|
|
323
353
|
- Balance between implementation speed and quality
|
|
324
354
|
|
|
325
355
|
### Deep Sessions
|
|
326
|
-
-
|
|
327
|
-
-
|
|
356
|
+
- **5 waves** from the shape (`--session-type deep`); the **Discovery wave is conditional** — pass `--known-scope true` when the scope is already established and the shape drops Discovery, leaving 4 waves
|
|
357
|
+
- Per-wave agent caps come from the shape's `agentCap`, clamped by Session Config `agents-per-wave` with its per-type override (this repo: `agents-per-wave: 6 (deep: 18)`)
|
|
328
358
|
- Extra emphasis on Discovery role and Quality role
|
|
329
359
|
- May include security audits, performance profiling, architecture refactoring
|
|
330
360
|
|
|
@@ -332,9 +362,9 @@ End with a single commit summarizing all housekeeping work.
|
|
|
332
362
|
|
|
333
363
|
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: `docs/prd/2026-09-06-ultradeep-session-profile.md`.
|
|
334
364
|
|
|
335
|
-
- **
|
|
365
|
+
- **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.
|
|
336
366
|
- **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).
|
|
337
|
-
- **`max-turns` per ROLE,
|
|
367
|
+
- **`max-turns` is per ROLE, and the numbers live in the shape:** take each wave's value from `waves[].maxTurns` in the shape output above (the Research, implementing and Release/Finalization figures are produced there, not restated here). Set it on the dispatch; a wave whose `maxTurns` is `null` is coordinator-direct and dispatches nothing.
|
|
338
368
|
- **Web tools are role-bound.** Research agents in wave 1 receive `WebSearch` and `WebFetch`. **No write-capable agent may receive them** — not in wave 1's Code-Discovery half, and not in any later wave. The grant follows the READ-ONLY property, so the pairing "has Write/Edit" + "has WebSearch/WebFetch" must never occur in a single dispatch. Research findings carry URL + retrieval date, the web analogue of the PSA-006 evidence rule (`.claude/rules/parallel-sessions.md`).
|
|
339
369
|
- **Budgets are not implemented.** The PRD's `ultradeep.max-*` block (§ 7) is deferred until three runs have been measured (HR-105: no threshold without a firing rate). Nothing reads such a key today — do not invent one, and do not gate a wave on it.
|
|
340
370
|
|
|
@@ -4,7 +4,7 @@ Sub-reference for the wave-executor skill. Defines safety mechanisms for agent e
|
|
|
4
4
|
|
|
5
5
|
## Circuit Breaker
|
|
6
6
|
|
|
7
|
-
1. **MaxTurns enforcement**: Read `
|
|
7
|
+
1. **MaxTurns enforcement**: Read the wave's `maxTurns` from the resolved shape (`scripts/session-shape.mjs` / `scripts/lib/session-shape.mjs`; a Session Config `max-turns: auto` is expanded per session type THERE, not here). Include this instruction in EVERY agent prompt:
|
|
8
8
|
```
|
|
9
9
|
TURN LIMIT: You have a maximum of [N] turns. If you cannot complete within [N] turns, report PARTIAL with what you accomplished and what remains.
|
|
10
10
|
```
|
|
@@ -93,6 +93,8 @@ The function never throws — it always returns a result object. Treat `skipped:
|
|
|
93
93
|
| 3–4 | feature / deep | `worktree` |
|
|
94
94
|
| ≥ 5 | any | `worktree` |
|
|
95
95
|
|
|
96
|
+
`sessionType` for `resolveIsolation` comes from the resolved shape (the same `session-type` the shape was resolved for), not from a value re-derived at dispatch time.
|
|
97
|
+
|
|
96
98
|
Rationale: the verified learning `coordinator-over-worktree-on-shared-files` (confidence 0.75) shows that small waves on partitioned scopes merge cleaner when run in-place. Two consecutive deep-session regressions (2026-04-20 07:30, 09:00) were worktree base-ref staleness on ≤2-agent waves editing the same SKILL.md. Graduated default makes worktree the tool for parallelism, not the default tax on every wave.
|
|
97
99
|
|
|
98
100
|
2. **Enforcement auto-promote (#194)**: Call `resolveEnforcement({ isolation, configEnforcement })` from the same module. When isolation resolves to `none` and the user has not explicitly set `configEnforcement: 'off'`, enforcement auto-promotes from `warn` → `strict`. Worktrees provide filesystem-level isolation; in-place dispatch relies on the scope hook as the only barrier — it must be hard, not informational. Write the resolved value into `wave-scope.json` `enforcement`.
|
|
@@ -113,7 +113,7 @@ When `worker-pool.enabled: true` in Session Config, dispatch via `runWavePool()`
|
|
|
113
113
|
|
|
114
114
|
Use the **Agent tool** to dispatch this wave's agents. **`run_in_background: true` is the PLATFORM DEFAULT for wave dispatch, not a deviation of ours** — since Claude Code **2.1.232**, non-teammate agent spawns default to background in interactive sessions. Our own measurement 2026-08-22 (v2.1.239) is why that default is the right one for a wave: under blocking dispatch the coordinator was 143 s incapable of acting between an agent's mid-run escalation and its own next turn — escalation latency equals the batch's remaining runtime. Background dispatch returns turns to the coordinator between agent completions; a running agent received a queued message mid-run and answered ~9 min before its final report.
|
|
115
115
|
|
|
116
|
-
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.
|
|
116
|
+
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 -->
|
|
117
117
|
|
|
118
118
|
**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: `docs/audits/2026-09-06-360-audit/w1/d9-claude-code-fable.md` (FA-6, 2026-09-06).
|
|
119
119
|
|
|
@@ -123,6 +123,8 @@ After resolving `isolation`, compute the wave's enforcement via `resolveEnforcem
|
|
|
123
123
|
|
|
124
124
|
Before dispatching, verify the wave's agent count does not exceed `$CONFIG.agents-per-wave` — if it does, warn the user and request plan revision.
|
|
125
125
|
|
|
126
|
+
`sessionType` for `resolveIsolation` is the session type the shape was resolved for, and the wave's `coordinatorDirect` and `writes` flags are READ FROM the shape's wave entry (`scripts/session-shape.mjs` → `waves[]`) rather than inferred from the role name. The dispatch-side marker is unchanged: the coordinator still keys the table below on `coordinator-direct: true` in the wave-plan item — the shape says which waves are expected to carry it, the plan item is what the coordinator acts on.
|
|
127
|
+
|
|
126
128
|
**Coordinator-direct waves (`coordinator-direct: true`) dispatch NOTHING — and that is not a silent drop.** Keyed on the marker, never on a profile name (`skills/session-plan/SKILL.md` keys its matching empty-role exception the same way), so any future coordinator-direct wave inherits this:
|
|
127
129
|
|
|
128
130
|
| Step | Behaviour when the wave-plan item carries `coordinator-direct: true` |
|
|
@@ -480,7 +482,7 @@ After `wave-scope.json` is written for this wave and before assembling the `Agen
|
|
|
480
482
|
|
|
481
483
|
**Gate:** runs when `.claude/rules/` exists. When it does not, the CLI prints nothing and exits 0 — zero behaviour change. This step never blocks dispatch: any non-zero exit or empty output means "inject nothing, continue" (same best-effort framing as Pre-Dispatch Grounding Injection above).
|
|
482
484
|
|
|
483
|
-
**Per-wave scoping (not per-agent):** the rule set is computed ONCE per wave from the wave's `allowedPaths` union (the same `wave-scope.json` source used elsewhere), not per agent. The CLI resolves `scopePaths` from `allowedPaths`, `mode` from the `session-type:` frontmatter in
|
|
485
|
+
**Per-wave scoping (not per-agent):** the rule set is computed ONCE per wave from the wave's `allowedPaths` union (the same `wave-scope.json` source used elsewhere), not per agent. The CLI resolves `scopePaths` from `allowedPaths`, `mode` from the `session-type:` frontmatter in the active harness's `STATE.md`, and `hostClass` from `.orchestrator/host.json` — all overridable, all degrading to "no gating" when unreadable. Default state and scope paths resolve independently: state-directory override first, then active-harness and legacy fallback.
|
|
484
486
|
|
|
485
487
|
**Invocation:** once per wave, run from the repo root and capture stdout as `$RULES_BLOCK`:
|
|
486
488
|
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
**Step 2.0 — Restore coordinator CWD (#219):** BEFORE reading any agent output or running any quality check, restore the coordinator's working directory. Claude Code's `Agent` tool with `isolation: "worktree"` `chdir()`s into each worktree internally and does NOT restore it on agent return. Subsequent Edit/Write/Bash calls would silently route to whichever worktree's tree CWD last drifted into.
|
|
10
10
|
|
|
11
11
|
```js
|
|
12
|
-
import { restoreCoordinatorCwd } from '$PLUGIN_ROOT/scripts/lib/
|
|
12
|
+
import { restoreCoordinatorCwd } from '$PLUGIN_ROOT/scripts/lib/workspace.mjs';
|
|
13
13
|
|
|
14
14
|
const cwd = await restoreCoordinatorCwd();
|
|
15
15
|
if (cwd.restored) {
|
|
@@ -61,13 +61,13 @@ Before each wave dispatch:
|
|
|
61
61
|
2. Validate by piping through `node "$PLUGIN_ROOT/scripts/validate-wave-scope.mjs"` (`$PLUGIN_ROOT` — see "Shell variables used in this section" above). If validation fails (exit 1), fix the JSON based on stderr errors and retry.
|
|
62
62
|
3. **`allowedPaths` is COMPUTED from one canonical declaration array — never hand-transcribed (#1020/#1083).** Transcribing either declaration shape or the union by hand produced scope divergences. Globs stay verbatim (`scripts/*.sh`) — the enforcement hook resolves them at check time.
|
|
63
63
|
|
|
64
|
-
**3.1 — materialize both declaration shapes once.** Build one JSON array from the session plan, one `{id, files}` record for every agent plus exactly one `coordinator` record for the coordinator's planned direct edits. `files` arrays, their entries and their order are the plan's verbatim declarations.
|
|
64
|
+
**3.1 — materialize both declaration shapes once.** Build one JSON array from the session plan, one `{id, files}` record for every agent plus exactly one `coordinator` record for the coordinator's planned direct edits. `files` arrays, their entries and their order are the plan's verbatim declarations. Run from the **project root**, materialize it ONCE and capture the aggregate-sidecar path:
|
|
65
65
|
|
|
66
66
|
```bash
|
|
67
67
|
WAVE_SCOPE_RECORDS='[{"id":"W3-I1","files":["scripts/example.mjs"]},{"id":"coordinator","files":["skills/wave-executor/wave-loop.md"]}]'
|
|
68
68
|
WAVE_SCOPES_SIDECAR="$(
|
|
69
69
|
printf '%s' "$WAVE_SCOPE_RECORDS" | node "$PLUGIN_ROOT/scripts/materialize-wave-scope.mjs" \
|
|
70
|
-
--state-dir "$STATE_DIR" --wave "$WAVE"
|
|
70
|
+
--state-dir "$STATE_DIR" --wave "$WAVE" --warn-missing
|
|
71
71
|
)"
|
|
72
72
|
[ -n "$WAVE_SCOPES_SIDECAR" ] || { echo "materialize-wave-scope produced no sidecar path" >&2; exit 1; }
|
|
73
73
|
```
|
|
@@ -81,6 +81,10 @@ Before each wave dispatch:
|
|
|
81
81
|
|
|
82
82
|
`materialize-wave-scope.mjs` validates the COMPLETE input before writing; it writes `<state-dir>/filescopes/wave-<N>/<agent-id>.json` as each bare `files` array first, then writes `<state-dir>/filescopes/wave-<N>.scopes.json` as the unchanged aggregate record array last. Its human stdout is only that final sidecar path, so the command substitution above is the canonical `$WAVE_SCOPES_SIDECAR`. On error, do not continue with a partial declaration set; correct the plan and run the one command again.
|
|
83
83
|
|
|
84
|
+
**Check missing-path warnings before dispatch (#1235).** The recipe opts into `--warn-missing`: each absent concrete path is named with its agent on stderr. Relative paths resolve from the current project working directory, never from `$STATE_DIR`. A warning does not fail materialization or alter stdout, declaration bytes, ordering, or session binding. Grants containing `*` or ending in `/` retain their existing glob/prefix meaning and are skipped; `?` and braces remain literal path characters.
|
|
85
|
+
|
|
86
|
+
For each file the plan intentionally creates, append **`--new-file PATH`**, repeating the option for multiple files. Each exception must exactly match a path already declared in a `files` array and pass the same path validation. Invalid or undeclared exceptions fail before any scope file is written, even without `--warn-missing`. This explicit declaration suppresses only that path's warning; the materializer never guesses a filename or rewrites a grant. Without `--warn-missing`, existing callers keep their quiet missing-path behavior.
|
|
87
|
+
|
|
84
88
|
The per-agent path IS `$AGENT_FILESCOPE_JSON` — the same file `--assert-subset` (#796 below), Grounding Injection (#85), the Learnings-Index (#1014) and File-Scope Injection (#1020) consume. Never write a `$TMPDIR` copy: it degrades to a signal-free allow when an injector cannot find the addressable wave-keyed file. The coordinator's record is materialized as `coordinator.json` and included in the aggregate, so its direct edits are covered by the two checks below.
|
|
85
89
|
|
|
86
90
|
> **`<state-dir>/filescopes/` is control state, like `wave-scope.json` itself — never a wave territory.** Step 3.1 necessarily runs before the union exists, so writing these files reports `bash-write-verify: N file(s) changed by a Bash call OUTSIDE the wave's allowedPaths` naming `filescopes/wave-<N>/*.json`. Expected once per wave rollover at this step; it is information, not a scope violation. Never widen `allowedPaths` to silence it — that would grant agents write access to the deconfliction record itself.
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* scripts/lib/multi-provider-build/providers.mjs
|
|
3
|
-
*
|
|
4
|
-
* Provider configuration for the single-source → many-provider build PoC.
|
|
5
|
-
*
|
|
6
|
-
* Scope: the THREE harnesses session-orchestrator actually targets
|
|
7
|
-
* (Claude Code primary, Codex CLI, Cursor IDE). Inspired by pbakaus/impeccable's
|
|
8
|
-
* 12-provider build (Apache-2.0) — we deliberately keep the templating mechanic
|
|
9
|
-
* and drop the 12-provider breadth (maintenance ≫ value for providers with ~0
|
|
10
|
-
* users; see the linked backlog issue's overengineering verdict).
|
|
11
|
-
*
|
|
12
|
-
* Each provider entry:
|
|
13
|
-
* {
|
|
14
|
-
* key, // canonical id used in build output paths
|
|
15
|
-
* tags, // <tag> block markers this provider keeps (rest are stripped)
|
|
16
|
-
* configDir, // on-disk harness dir
|
|
17
|
-
* placeholders: { model, configFile, commandPrefix, askInstruction }
|
|
18
|
-
* }
|
|
19
|
-
*/
|
|
20
|
-
|
|
21
|
-
/** @typedef {{key:string, tags:string[], configDir:string, placeholders:{model:string, configFile:string, commandPrefix:string, askInstruction:string}}} ProviderConfig */
|
|
22
|
-
|
|
23
|
-
/** @type {Record<string, ProviderConfig>} */
|
|
24
|
-
export const PROVIDERS = {
|
|
25
|
-
'claude-code': {
|
|
26
|
-
key: 'claude-code',
|
|
27
|
-
tags: ['claude-code', 'claude'],
|
|
28
|
-
configDir: '.claude',
|
|
29
|
-
placeholders: {
|
|
30
|
-
model: 'Claude',
|
|
31
|
-
configFile: 'CLAUDE.md',
|
|
32
|
-
commandPrefix: '/',
|
|
33
|
-
askInstruction: 'STOP and call the AskUserQuestion tool to clarify.',
|
|
34
|
-
},
|
|
35
|
-
},
|
|
36
|
-
codex: {
|
|
37
|
-
key: 'codex',
|
|
38
|
-
tags: ['codex'],
|
|
39
|
-
configDir: '.codex',
|
|
40
|
-
placeholders: {
|
|
41
|
-
model: 'GPT',
|
|
42
|
-
configFile: 'AGENTS.md',
|
|
43
|
-
commandPrefix: '$',
|
|
44
|
-
askInstruction: "STOP and use Codex's structured user-input tool to clarify.",
|
|
45
|
-
},
|
|
46
|
-
},
|
|
47
|
-
cursor: {
|
|
48
|
-
key: 'cursor',
|
|
49
|
-
tags: ['cursor'],
|
|
50
|
-
configDir: '.cursor',
|
|
51
|
-
placeholders: {
|
|
52
|
-
model: 'the model',
|
|
53
|
-
configFile: '.cursorrules',
|
|
54
|
-
commandPrefix: '/',
|
|
55
|
-
askInstruction: 'ask the user directly to clarify before proceeding.',
|
|
56
|
-
},
|
|
57
|
-
},
|
|
58
|
-
};
|
|
59
|
-
|
|
60
|
-
/** The full set of recognized provider block tags across ALL providers. */
|
|
61
|
-
export const ALL_BLOCK_TAGS = new Set(Object.values(PROVIDERS).flatMap((p) => p.tags));
|
|
62
|
-
|
|
63
|
-
/** Provider keys in a stable order. */
|
|
64
|
-
export const PROVIDER_KEYS = Object.keys(PROVIDERS);
|