session-orchestrator 4.1.0 → 5.0.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/.agents/skills/session-start/SKILL.md +1 -1
- package/.agents/skills/ux-grill/SKILL.md +22 -0
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +3 -2
- package/.codex-plugin/plugin.json +1 -1
- package/.codex-plugin/skills/session-plan/SKILL.md +1 -1
- package/.codex-plugin/skills/session-start/SKILL.md +1 -1
- package/.codex-plugin/skills/ux-grill/SKILL.md +21 -0
- package/.codex-plugin/skills/ux-grill/agents/openai.yaml +5 -0
- package/.cursor/commands/ux-grill.md +14 -0
- package/.cursor/skills/session-plan/SKILL.md +1 -1
- package/.cursor/skills/session-start/SKILL.md +1 -1
- package/.cursor/skills/ux-grill/SKILL.md +13 -0
- package/.cursor-plugin/plugin.json +1 -1
- package/AGENTS.md +2 -1
- package/CHANGELOG.md +128 -1
- package/README.md +98 -86
- package/agents/dialectic-deriver.md +11 -0
- 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/session.md +3 -2
- package/commands/ux-grill.md +51 -0
- package/docs/README.md +4 -4
- package/docs/USER-GUIDE.md +117 -50
- 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 +9 -0
- package/docs/components.md +9 -9
- package/docs/cursor-setup.md +1 -0
- package/docs/events-schema.md +13 -6
- package/docs/github-mirror-protection.md +61 -20
- package/docs/instruction-delivery.md +1 -1
- package/docs/memory-proposal-flow.md +3 -3
- package/docs/migration-v4.md +2 -2
- package/docs/migration-v5.md +62 -0
- 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 +16 -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 +70 -3
- package/hooks/_lib/lock-bootstrap.mjs +84 -1
- package/hooks/_lib/vcs-create-matcher.mjs +401 -16
- package/hooks/enforce-scope.mjs +201 -0
- package/hooks/hooks-codex.json +1 -1
- package/hooks/hooks-cursor.json +5 -0
- package/hooks/hooks.json +7 -2
- package/hooks/on-session-start.mjs +171 -49
- package/hooks/post-bash-issue-budget-refund.mjs +375 -0
- package/hooks/pre-auq-clarity.mjs +70 -18
- package/hooks/pre-bash-issue-budget.mjs +170 -26
- package/hooks/subagent-telemetry.mjs +106 -20
- package/package.json +5 -4
- package/pi/prompts/ux-grill.md +12 -0
- package/scripts/baseline-archetypes.mjs +28 -0
- package/scripts/ci/assert-vitest-green.mjs +4 -2
- package/scripts/dialectic-deriver.mjs +32 -8
- package/scripts/emit-session.mjs +72 -1
- package/scripts/lib/agent-status.mjs +441 -9
- package/scripts/lib/auq/schema.mjs +10 -3
- 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/ci-status-banner.mjs +29 -6
- package/scripts/lib/claude-md-budget-lint.mjs +52 -2
- 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/config.mjs +12 -1
- package/scripts/lib/eval/engine.mjs +7 -1
- package/scripts/lib/file-lock.mjs +151 -8
- package/scripts/lib/git-porcelain.mjs +113 -0
- package/scripts/lib/instruction-budget-guard.mjs +415 -47
- package/scripts/lib/io.mjs +29 -4
- package/scripts/lib/issue-budget-reconcile.mjs +392 -0
- package/scripts/lib/issue-budget.mjs +412 -9
- package/scripts/lib/learnings/evolve-telemetry.mjs +1 -2
- package/scripts/lib/learnings/sizing-subject.mjs +44 -0
- package/scripts/lib/locks/staging-fence-lock.mjs +19 -38
- package/scripts/lib/locks/state-md-lock.mjs +19 -41
- package/scripts/lib/maintenance-due-banner.mjs +450 -0
- package/scripts/lib/owner-config.example.yaml +29 -46
- package/scripts/lib/owner-yaml.mjs +14 -13
- package/scripts/lib/peer-cards/merger.mjs +143 -0
- package/scripts/lib/pre-dispatch-check.mjs +20 -14
- package/scripts/lib/project-hygiene.mjs +81 -30
- package/scripts/lib/quality-gate.mjs +27 -71
- package/scripts/lib/reconcile/engine.mjs +19 -1
- package/scripts/lib/reconcile/writer.mjs +278 -11
- package/scripts/lib/resource-probe/evaluate.mjs +19 -21
- package/scripts/lib/rules-sync.mjs +34 -4
- package/scripts/lib/scope-echo.mjs +346 -0
- 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-lock.mjs +62 -2
- package/scripts/lib/session-record-repair.mjs +91 -0
- package/scripts/lib/session-schema/constants.mjs +6 -0
- package/scripts/lib/session-schema/filters.mjs +26 -1
- package/scripts/lib/session-schema/validator.mjs +20 -0
- package/scripts/lib/session-shape.mjs +558 -0
- package/scripts/lib/session-start-probes.mjs +429 -56
- 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/test-runner/artifact-paths.mjs +30 -5
- package/scripts/lib/test-runner/issue-reconcile.mjs +45 -8
- package/scripts/lib/tmux-layout/layouts.mjs +62 -4
- package/scripts/lib/ux-grill/collect.mjs +1163 -0
- package/scripts/lib/ux-grill/compare.mjs +285 -0
- package/scripts/lib/ux-grill/manifest.mjs +618 -0
- package/scripts/lib/ux-grill/measures.mjs +431 -0
- package/scripts/lib/ux-grill/paths.mjs +224 -0
- package/scripts/lib/ux-grill/pencil-coverage.mjs +284 -0
- package/scripts/lib/ux-grill/reconcile.mjs +344 -0
- package/scripts/lib/ux-grill/run-record.mjs +316 -0
- package/scripts/lib/ux-grill/schema.mjs +321 -0
- package/scripts/lib/validate/check-skill-script-paths.mjs +33 -10
- package/scripts/lib/validate/check-untracked-test-deps.mjs +33 -19
- package/scripts/lib/validate/check-unwired-features.mjs +56 -27
- package/scripts/lib/vault-mirror/process.mjs +2 -1
- package/scripts/lib/vault-status/board-lock.mjs +18 -0
- package/scripts/lib/vault-status/board-writer.mjs +8 -0
- 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/mcp-server.sh +16 -1
- package/scripts/print-applicable-rules.mjs +7 -6
- package/scripts/print-learnings-index.mjs +3 -2
- package/scripts/release.mjs +7 -2
- 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 +60 -209
- 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/references/bootstrap-ecosystem-health-flow.md +48 -0
- package/skills/bootstrap/references/bootstrap-refresh-lock-flow.md +37 -0
- package/skills/bootstrap/references/bootstrap-retroactive-flow.md +108 -0
- package/skills/bootstrap/references/bootstrap-rules-fetch-bridge.md +64 -0
- package/skills/bootstrap/standard-template.md +39 -24
- package/skills/claude-md-drift-check/SKILL.md +9 -2
- package/skills/claude-md-drift-check/checker.mjs +213 -21
- package/skills/discovery/SKILL.md +6 -173
- package/skills/discovery/probes/vault-staleness.mjs +35 -5
- package/skills/discovery/probes-docs.md +8 -4
- package/skills/discovery/probes-supply-chain.md +4 -2
- package/skills/discovery/probes-ui.md +8 -4
- package/skills/discovery/probes-vault.md +12 -4
- package/skills/discovery/references/discovery-interactive-triage.md +139 -0
- package/skills/discovery/references/discovery-triage-state.md +54 -0
- package/skills/docs-orchestrator/audience-mapping.md +1 -1
- package/skills/eval/rubric-v1.md +13 -0
- package/skills/evolve/SKILL.md +2 -458
- package/skills/evolve/references/evolve-analyze-mode.md +360 -0
- package/skills/evolve/references/evolve-dialectic-mode.md +139 -0
- 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/plan/mode-retro.md +4 -3
- package/skills/reconcile/SKILL.md +11 -1
- package/skills/session-end/SKILL.md +3 -2
- package/skills/session-end/drift-operations.md +20 -5
- package/skills/session-end/metrics-collection.md +1 -0
- package/skills/session-end/phase-3-2-docs-verification.md +1 -1
- package/skills/session-end/phase-3-6-tail.md +27 -67
- package/skills/session-end/phase-3-7a-recommendations.md +2 -2
- package/skills/session-end/references/phase-2-quality-gate.md +3 -3
- package/skills/session-end/references/phase-3-documentation-updates.md +8 -6
- package/skills/session-end/references/phase-5-issue-cleanup.md +32 -1
- package/skills/session-end/session-metrics-write.md +33 -12
- package/skills/session-plan/SKILL.md +46 -180
- package/skills/session-plan/references/session-plan-task-classification.md +152 -0
- package/skills/session-plan/wave-template.md +8 -15
- package/skills/session-start/SKILL.md +41 -7
- 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/operations-contract.md +114 -0
- package/skills/session-start/references/phase-1-5-session-continuity.md +2 -0
- package/skills/session-start/references/phase-4-ssot-environment-check.md +42 -24
- package/skills/session-start/references/phase-6-7-memory-banner-telemetry-consent.md +3 -1
- package/skills/session-start/soul.md +2 -2
- package/skills/test-runner/SKILL.md +1 -1
- package/skills/test-runner/rubric-v1.md +2 -2
- package/skills/tmux-layout/SKILL.md +3 -1
- package/skills/ux-grill/SKILL.md +211 -0
- package/skills/ux-grill/rubric-v2.md +201 -0
- package/skills/ux-grill/soul.md +76 -0
- package/skills/wave-executor/SKILL.md +32 -127
- package/skills/wave-executor/circuit-breaker.md +3 -1
- package/skills/wave-executor/references/wave-executor-quality-gate.md +61 -0
- package/skills/wave-executor/references/wave-executor-state-init.md +86 -0
- package/skills/wave-executor/references/wave-loop-dispatch.md +12 -2
- package/skills/wave-executor/references/wave-loop-review.md +19 -6
- package/skills/wave-executor/references/wave-loop-scope-manifest.md +6 -2
- package/templates/_shared/ux-manifest.template.md +149 -0
- 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
- package/templates/_shared/journey-manifest.md +0 -114
|
@@ -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)
|
|
@@ -119,70 +121,9 @@ Store this value for use throughout the session — it is needed by the simplifi
|
|
|
119
121
|
|
|
120
122
|
## Pre-Wave 1b: Initialize STATE.md
|
|
121
123
|
|
|
122
|
-
> Skip
|
|
123
|
-
|
|
124
|
-
Before dispatching Wave 1, write `<state-dir>/STATE.md` with YAML frontmatter and Markdown body:
|
|
125
|
-
|
|
126
|
-
```yaml
|
|
127
|
-
---
|
|
128
|
-
schema-version: 1
|
|
129
|
-
session-type: feature|deep|housekeeping
|
|
130
|
-
branch: <current branch>
|
|
131
|
-
issues: [<issue numbers from plan>]
|
|
132
|
-
started_at: <ISO 8601 timestamp with timezone>
|
|
133
|
-
status: active
|
|
134
|
-
current-wave: 0
|
|
135
|
-
total-waves: <from session plan>
|
|
136
|
-
---
|
|
137
|
-
```
|
|
138
|
-
|
|
139
|
-
```markdown
|
|
140
|
-
## Current Wave
|
|
141
|
-
|
|
142
|
-
Wave 0 — Initializing
|
|
143
|
-
|
|
144
|
-
## Wave History
|
|
145
|
-
|
|
146
|
-
(none yet)
|
|
147
|
-
|
|
148
|
-
## Deviations
|
|
149
|
-
|
|
150
|
-
(none yet)
|
|
151
|
-
```
|
|
152
|
-
|
|
153
|
-
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
|
-
|
|
155
|
-
#### Pre-Wave 1b Extension: Docs Tasks Persistence (A3 / #230)
|
|
156
|
-
|
|
157
|
-
After writing the base STATE.md frontmatter above, conditionally persist the docs tasks block emitted by session-plan:
|
|
158
|
-
|
|
159
|
-
**Condition:** BOTH of the following must be true:
|
|
160
|
-
1. The session plan contains a `### Docs Tasks (machine-readable)` section with a YAML code block.
|
|
161
|
-
2. `$CONFIG."docs-orchestrator".enabled` is `true`.
|
|
162
|
-
|
|
163
|
-
If either condition is false → omit the `docs-tasks` field entirely. Do NOT write an empty key (`docs-tasks: []`). Absence means "no docs tasks planned this session" — downstream consumers (session-end Phase 3.2) treat absence the same as an empty list.
|
|
164
|
-
|
|
165
|
-
When the condition is met, parse the YAML block from the session plan's `### Docs Tasks (machine-readable)` section and append the following field to the STATE.md YAML frontmatter (alongside the base fields above):
|
|
166
|
-
|
|
167
|
-
```yaml
|
|
168
|
-
docs-tasks:
|
|
169
|
-
- id: <task id from plan>
|
|
170
|
-
audience: <user|dev|vault>
|
|
171
|
-
target-pattern: <glob pattern from plan>
|
|
172
|
-
rationale: <rationale string from plan>
|
|
173
|
-
wave: <wave number the task is assigned to>
|
|
174
|
-
status: planned
|
|
175
|
-
```
|
|
176
|
-
|
|
177
|
-
Each entry's `status` is initialized to `planned`. session-end Phase 3.2 (Docs Verify) writes the terminal value per task: `ok` (diff is substantive), `partial` (diff region contains `<!-- REVIEW: source needed -->` markers), or `gap` (no matching diff). wave-executor does NOT perform intermediate status updates — `planned` remains until session-end runs.
|
|
124
|
+
> Skip entirely if `persistence: false`. Otherwise, before dispatching Wave 1, write `<state-dir>/STATE.md` (YAML frontmatter + Markdown body), then VALIDATE `total-waves` against the resolved shape — do not skip this step, a plan whose wave count the shape does not produce must never be dispatched silently. Full template, the shape-mismatch AUQ procedure, and the Docs Tasks Persistence extension (A3 / #230): [references/wave-executor-state-init.md](references/wave-executor-state-init.md).
|
|
178
125
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
> **Ownership clarification:** session-plan does NOT write STATE.md directly. The wave-executor owns ALL STATE.md writes — initialization here (Pre-Wave 1b) is the canonical write point for `docs-tasks`. session-plan only emits the source `### Docs Tasks (machine-readable)` block for the coordinator to consume. See `skills/_shared/state-ownership.md` for the full ownership matrix.
|
|
182
|
-
|
|
183
|
-
> **Consumer cross-reference:** session-end reads `STATE.md` frontmatter's `docs-tasks` field (if present) during Phase 3.2 Docs Verify — see `skills/session-end/SKILL.md`. The field is also readable by the docs-writer agent if it needs to know which tasks were planned for the current session.
|
|
184
|
-
|
|
185
|
-
> **Ownership:** STATE.md is owned by the wave-executor. Only the wave-executor writes to it (initialization + post-wave updates). session-end reads it for metrics extraction and sets `status: completed`. session-start reads it only for continuity checks (Phase 0.5). No other skill should write to STATE.md.
|
|
126
|
+
**Read WHEN:** before the first wave dispatches, every session with `persistence: true`; the VALIDATE step must not be skipped.
|
|
186
127
|
|
|
187
128
|
## Wave Execution Loop
|
|
188
129
|
|
|
@@ -227,7 +168,7 @@ Claude Code's `Agent` tool with `isolation: "worktree"` changes `process.cwd()`
|
|
|
227
168
|
|
|
228
169
|
**Rules for the coordinator (this is YOU during wave execution):**
|
|
229
170
|
|
|
230
|
-
1. **After every Agent() dispatch** (before reading its output), call `restoreCoordinatorCwd()` from `scripts/lib/
|
|
171
|
+
1. **After every Agent() dispatch** (before reading its output), call `restoreCoordinatorCwd()` from `scripts/lib/workspace.mjs`. `wave-loop.md § 2` makes this explicit.
|
|
231
172
|
2. **Prefer absolute file paths** for Read/Edit/Write tool calls. A drifted CWD turns relative paths into silent cross-tree writes.
|
|
232
173
|
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
174
|
4. **Verify at checkpoints** — when in doubt, run `git rev-parse --show-toplevel` to confirm which tree is currently active.
|
|
@@ -302,29 +243,44 @@ Cross-reference: PRD F2.1 / issue #501 / `docs/memory-proposal-flow.md` (coordin
|
|
|
302
243
|
|
|
303
244
|
## Session Type Behavior
|
|
304
245
|
|
|
305
|
-
### Housekeeping Sessions
|
|
246
|
+
### Housekeeping Sessions — the Maintenance Loop
|
|
247
|
+
|
|
248
|
+
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`).
|
|
249
|
+
|
|
250
|
+
**Ordered default scope — the maintenance loop.** Run it in this order, before the session's selected issues:
|
|
251
|
+
|
|
252
|
+
| # | Run | Gate | Artefact that proves it ran |
|
|
253
|
+
|---|---|---|---|
|
|
254
|
+
| 1 | `claude-md-drift-check` | unconditional | checker JSON (`errors`/`warnings` counts) |
|
|
255
|
+
| 2 | expired-learnings sweep | unconditional | `orchestrator.learnings.sweep_applied` |
|
|
256
|
+
| 3 | `/evolve analyze` | AUQ-gated (the operator approves the proposed learnings) | `orchestrator.evolve.completed` |
|
|
257
|
+
| 4 | `/reconcile` | AUQ-gated (rule proposals are never applied unasked) | `orchestrator.reconcile.completed` with `dry_run: false` |
|
|
258
|
+
| 5 | `/evolve dialectic` | AUQ-gated (the derived thesis is presented, not committed) | `orchestrator.dialectic.completed` |
|
|
259
|
+
| 6 | `/memory-cleanup` | AUQ-gated (deletions are operator-approved) | `orchestrator.memory.cleanup_completed` |
|
|
306
260
|
|
|
307
|
-
|
|
261
|
+
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`).
|
|
262
|
+
|
|
263
|
+
Then the mechanics:
|
|
308
264
|
|
|
309
265
|
1. Initialize STATE.md as normal (`session-type: housekeeping`, `total-waves: 1`)
|
|
310
|
-
2. Do NOT create `wave-scope.json` —
|
|
311
|
-
3.
|
|
266
|
+
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
|
|
267
|
+
3. Execute the maintenance loop above, then the session's selected issues, serially as coordinator actions
|
|
312
268
|
4. Run Baseline quality checks after all tasks complete (not between tasks)
|
|
313
269
|
5. Skip session-reviewer dispatch — housekeeping changes are low-risk
|
|
314
270
|
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
271
|
7. Proceed directly to session-end (`/close`)
|
|
316
272
|
|
|
317
|
-
|
|
273
|
+
Beyond the loop: git cleanup, SSOT refresh, CI fixes, branch merges, documentation.
|
|
318
274
|
End with a single commit summarizing all housekeeping work.
|
|
319
275
|
|
|
320
276
|
### Feature Sessions
|
|
321
|
-
-
|
|
322
|
-
-
|
|
277
|
+
- **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`
|
|
278
|
+
- 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
279
|
- Balance between implementation speed and quality
|
|
324
280
|
|
|
325
281
|
### Deep Sessions
|
|
326
|
-
-
|
|
327
|
-
-
|
|
282
|
+
- **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
|
|
283
|
+
- 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
284
|
- Extra emphasis on Discovery role and Quality role
|
|
329
285
|
- May include security audits, performance profiling, architecture refactoring
|
|
330
286
|
|
|
@@ -332,9 +288,9 @@ End with a single commit summarizing all housekeeping work.
|
|
|
332
288
|
|
|
333
289
|
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
290
|
|
|
335
|
-
- **
|
|
291
|
+
- **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
292
|
- **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,
|
|
293
|
+
- **`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
294
|
- **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
295
|
- **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
296
|
|
|
@@ -412,58 +368,7 @@ The diff JSON block (`{ new_errors, resolved_errors, baseline_count, current_cou
|
|
|
412
368
|
|
|
413
369
|
## Inter-Wave Quality-Gate (with Auto-Fix Loop — #521)
|
|
414
370
|
|
|
415
|
-
|
|
416
|
-
in Session Config, the gate uses `runQualityGateWithRetry()` from
|
|
417
|
-
`scripts/lib/quality-gate.mjs` which dispatches up to `max-retries` (default 2)
|
|
418
|
-
fixer-agent dispatches on failure.
|
|
419
|
-
|
|
420
|
-
**Quality-wave Full-Gate mandate (#724 C6):** the inter-wave gate following the **Quality wave** is ALWAYS the Full Gate (typecheck + test + lint) — never the cached Incremental short-circuit. The wave-executor threads the wave's `waveRole` into `shouldSkipIncremental` (see `wave-loop.md § Baseline cache check`); when `waveRole === 'Quality'` the cache is bypassed mechanically, so a valid cache or a narrow diff cannot downgrade the Quality-wave close-safety gate. See `skills/quality-gates/SKILL.md § Variant 3: Full Gate` — its dual consumers are session-end (Phase 2) and the Quality wave, and its Baseline-Cache invariant records that both are un-skippable.
|
|
421
|
-
|
|
422
|
-
### Invocation
|
|
423
|
-
|
|
424
|
-
```javascript
|
|
425
|
-
import { runQualityGateWithRetry } from '../../scripts/lib/quality-gate.mjs';
|
|
426
|
-
|
|
427
|
-
const result = await runQualityGateWithRetry({
|
|
428
|
-
maxRetries: config['verification-auto-fix']?.['max-retries'] ?? 2,
|
|
429
|
-
repoRoot: process.cwd(),
|
|
430
|
-
dispatchFixer: async ({ failures, correctiveContext, changedFiles }) => {
|
|
431
|
-
// Coordinator dispatches a code-implementer fixer subagent here with:
|
|
432
|
-
// - failures (gate + output)
|
|
433
|
-
// - correctiveContext (from .orchestrator/current-session.json)
|
|
434
|
-
// - changedFiles (since last green SHA)
|
|
435
|
-
// Subagent's task: fix the failing gate, never broaden scope.
|
|
436
|
-
await dispatchFixerSubagent({ failures, correctiveContext, changedFiles });
|
|
437
|
-
},
|
|
438
|
-
});
|
|
439
|
-
```
|
|
440
|
-
|
|
441
|
-
### Decision flow
|
|
442
|
-
|
|
443
|
-
- `result.ok === true` → Wave green, proceed to next wave or session-end.
|
|
444
|
-
- `result.ok === false` → Hard abort.
|
|
445
|
-
- quality-gate.mjs writes `.orchestrator/metrics/verification-failures/<ts>.json` (diagnostics bundle — automatic, redacted per `redactDiagnosticsBundle()`).
|
|
446
|
-
- **Coordinator** (not fixer-subagent) appends a deviation entry to STATE.md via `appendDeviationOnDisk()` — see `wave-loop.md` § STATE.md Deviation — Auto-Fix Result.
|
|
447
|
-
- Wave execution is blocked; operator must manually fix or disable auto-fix.
|
|
448
|
-
- `result.attempts > 1` → **Coordinator** logs a Deviation in STATE.md via `appendDeviationOnDisk()`: `auto-fix used N retries to clear Wave <wave>`.
|
|
449
|
-
|
|
450
|
-
### Skip Conditions
|
|
451
|
-
|
|
452
|
-
- `verification-auto-fix.enabled: false` (default) → fall back to single-shot
|
|
453
|
-
quality-gate, abort on first failure (current behavior preserved per PRD § 3
|
|
454
|
-
Gherkin negative path).
|
|
455
|
-
- `verification-auto-fix.max-retries: 0` → equivalent to disabled.
|
|
456
|
-
|
|
457
|
-
### Anti-pattern (BE-012 awareness)
|
|
458
|
-
|
|
459
|
-
The fixer-agent prompt MUST include a reminder of `.claude/rules/testing.md` § "Test Quality — False-Positive Prevention"
|
|
460
|
-
"test-the-mock" anti-pattern. A fix that makes tests green by mocking out the
|
|
461
|
-
real failure is a regression vector. The fixer prompt should explicitly say:
|
|
462
|
-
"Do NOT change test mocks to make tests pass. Fix the actual code defect."
|
|
463
|
-
|
|
464
|
-
### Heartbeat cadence at inter-wave checkpoints (#590-3)
|
|
465
|
-
|
|
466
|
-
After each quality-gate PASS, the coordinator refreshes the session-lock heartbeat via the post-wave STATE.md step. See `wave-loop.md § 3a. Post-Wave: Update STATE.md` — step 5 contains the `updateHeartbeat` instruction and best-effort framing. The `sessionId` passed to `updateHeartbeat` is the session identifier established by session-start Phase 1.2 `acquire()` and stored in `.orchestrator/session.lock` (its `session_id` field); it matches the STATE.md frontmatter `session:` field written during Pre-Wave 1b initialization.
|
|
371
|
+
> **Reference:** See [references/wave-executor-quality-gate.md](references/wave-executor-quality-gate.md) for the invocation, decision flow, skip conditions, the BE-012 test-the-mock anti-pattern reminder, the Quality-wave Full-Gate mandate (#724 C6), and the inter-wave heartbeat cadence (#590-3). Read after each wave completes, before proceeding to the next wave or session-end.
|
|
467
372
|
|
|
468
373
|
## Agent-Status Telemetry (#565)
|
|
469
374
|
|
|
@@ -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`.
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# Wave Executor — Inter-Wave Quality-Gate (Auto-Fix Loop, #521)
|
|
2
|
+
|
|
3
|
+
> Reference of the wave-executor skill, split out of `SKILL.md` (#1246). Body moved **byte-identical**; only this header is new.
|
|
4
|
+
> **Sibling-file paths inside this body are relative to the parent directory, not to `references/`**: `SKILL.md` → `../SKILL.md`, `wave-loop.md` → `../wave-loop.md`, `circuit-breaker.md` → `../circuit-breaker.md`. They were deliberately NOT rewritten, so the moved bytes stay verifiable against the pre-split file.
|
|
5
|
+
> Read after each wave completes, before proceeding to the next wave or session-end — see `../SKILL.md` § Inter-Wave Quality-Gate for the pointer.
|
|
6
|
+
|
|
7
|
+
## Inter-Wave Quality-Gate (with Auto-Fix Loop — #521)
|
|
8
|
+
|
|
9
|
+
After each wave, run the Quality-Gate. If `verification-auto-fix.enabled: true`
|
|
10
|
+
in Session Config, the gate uses `runQualityGateWithRetry()` from
|
|
11
|
+
`scripts/lib/quality-gate.mjs` which dispatches up to `max-retries` (default 2)
|
|
12
|
+
fixer-agent dispatches on failure.
|
|
13
|
+
|
|
14
|
+
**Quality-wave Full-Gate mandate (#724 C6):** the inter-wave gate following the **Quality wave** is ALWAYS the Full Gate (typecheck + test + lint) — never the cached Incremental short-circuit. The wave-executor threads the wave's `waveRole` into `shouldSkipIncremental` (see `wave-loop.md § Baseline cache check`); when `waveRole === 'Quality'` the cache is bypassed mechanically, so a valid cache or a narrow diff cannot downgrade the Quality-wave close-safety gate. See `skills/quality-gates/SKILL.md § Variant 3: Full Gate` — its dual consumers are session-end (Phase 2) and the Quality wave, and its Baseline-Cache invariant records that both are un-skippable.
|
|
15
|
+
|
|
16
|
+
### Invocation
|
|
17
|
+
|
|
18
|
+
```javascript
|
|
19
|
+
import { runQualityGateWithRetry } from '../../scripts/lib/quality-gate.mjs';
|
|
20
|
+
|
|
21
|
+
const result = await runQualityGateWithRetry({
|
|
22
|
+
maxRetries: config['verification-auto-fix']?.['max-retries'] ?? 2,
|
|
23
|
+
repoRoot: process.cwd(),
|
|
24
|
+
dispatchFixer: async ({ failures, correctiveContext, changedFiles }) => {
|
|
25
|
+
// Coordinator dispatches a code-implementer fixer subagent here with:
|
|
26
|
+
// - failures (gate + output)
|
|
27
|
+
// - correctiveContext (from .orchestrator/current-session.json)
|
|
28
|
+
// - changedFiles (since last green SHA)
|
|
29
|
+
// Subagent's task: fix the failing gate, never broaden scope.
|
|
30
|
+
await dispatchFixerSubagent({ failures, correctiveContext, changedFiles });
|
|
31
|
+
},
|
|
32
|
+
});
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### Decision flow
|
|
36
|
+
|
|
37
|
+
- `result.ok === true` → Wave green, proceed to next wave or session-end.
|
|
38
|
+
- `result.ok === false` → Hard abort.
|
|
39
|
+
- quality-gate.mjs writes `.orchestrator/metrics/verification-failures/<ts>.json` (diagnostics bundle — automatic, redacted per `redactDiagnosticsBundle()`).
|
|
40
|
+
- **Coordinator** (not fixer-subagent) appends a deviation entry to STATE.md via `appendDeviationOnDisk()` — see `wave-loop.md` § STATE.md Deviation — Auto-Fix Result.
|
|
41
|
+
- Wave execution is blocked; operator must manually fix or disable auto-fix.
|
|
42
|
+
- `result.attempts > 1` → **Coordinator** logs a Deviation in STATE.md via `appendDeviationOnDisk()`: `auto-fix used N retries to clear Wave <wave>`.
|
|
43
|
+
|
|
44
|
+
### Skip Conditions
|
|
45
|
+
|
|
46
|
+
- `verification-auto-fix.enabled: false` (default) → fall back to single-shot
|
|
47
|
+
quality-gate, abort on first failure (current behavior preserved per PRD § 3
|
|
48
|
+
Gherkin negative path).
|
|
49
|
+
- `verification-auto-fix.max-retries: 0` → equivalent to disabled.
|
|
50
|
+
|
|
51
|
+
### Anti-pattern (BE-012 awareness)
|
|
52
|
+
|
|
53
|
+
The fixer-agent prompt MUST include a reminder of `.claude/rules/testing.md` § "Test Quality — False-Positive Prevention"
|
|
54
|
+
"test-the-mock" anti-pattern. A fix that makes tests green by mocking out the
|
|
55
|
+
real failure is a regression vector. The fixer prompt should explicitly say:
|
|
56
|
+
"Do NOT change test mocks to make tests pass. Fix the actual code defect."
|
|
57
|
+
|
|
58
|
+
### Heartbeat cadence at inter-wave checkpoints (#590-3)
|
|
59
|
+
|
|
60
|
+
After each quality-gate PASS, the coordinator refreshes the session-lock heartbeat via the post-wave STATE.md step. See `wave-loop.md § 3a. Post-Wave: Update STATE.md` — step 5 contains the `updateHeartbeat` instruction and best-effort framing. The `sessionId` passed to `updateHeartbeat` is the session identifier established by session-start Phase 1.2 `acquire()` and stored in `.orchestrator/session.lock` (its `session_id` field); it matches the STATE.md frontmatter `session:` field written during Pre-Wave 1b initialization.
|
|
61
|
+
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# Wave Executor — STATE.md Initialization (Pre-Wave 1b)
|
|
2
|
+
|
|
3
|
+
> Reference of the wave-executor skill, split out of `SKILL.md` (#1246). Body moved **byte-identical**; only this header is new.
|
|
4
|
+
> **Sibling-file paths inside this body are relative to the parent directory, not to `references/`**: `SKILL.md` → `../SKILL.md`, `wave-loop.md` → `../wave-loop.md`, `circuit-breaker.md` → `../circuit-breaker.md`. They were deliberately NOT rewritten, so the moved bytes stay verifiable against the pre-split file.
|
|
5
|
+
> Read at Pre-Wave 1b, before dispatching Wave 1 — see `../SKILL.md` § Pre-Wave 1b for the pointer.
|
|
6
|
+
|
|
7
|
+
## Pre-Wave 1b: Initialize STATE.md
|
|
8
|
+
|
|
9
|
+
> Skip this section entirely if `persistence: false`.
|
|
10
|
+
|
|
11
|
+
Before dispatching Wave 1, write `<state-dir>/STATE.md` with YAML frontmatter and Markdown body:
|
|
12
|
+
|
|
13
|
+
```yaml
|
|
14
|
+
---
|
|
15
|
+
schema-version: 1
|
|
16
|
+
session-type: feature|deep|housekeeping
|
|
17
|
+
branch: <current branch>
|
|
18
|
+
issues: [<issue numbers from plan>]
|
|
19
|
+
started_at: <ISO 8601 timestamp with timezone>
|
|
20
|
+
status: active
|
|
21
|
+
current-wave: 0
|
|
22
|
+
total-waves: <from session plan>
|
|
23
|
+
---
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
```markdown
|
|
27
|
+
## Current Wave
|
|
28
|
+
|
|
29
|
+
Wave 0 — Initializing
|
|
30
|
+
|
|
31
|
+
## Wave History
|
|
32
|
+
|
|
33
|
+
(none yet)
|
|
34
|
+
|
|
35
|
+
## Deviations
|
|
36
|
+
|
|
37
|
+
(none yet)
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
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.
|
|
41
|
+
|
|
42
|
+
**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:
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
node scripts/session-shape.mjs --repo-root "$PWD" \
|
|
46
|
+
--session-type <session-type> [--profile <session-profile>] [--known-scope true|false] \
|
|
47
|
+
--no-event | jq .totalWaves
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
`--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`):
|
|
51
|
+
|
|
52
|
+
- **Equal** → continue to Wave 1.
|
|
53
|
+
- **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.
|
|
54
|
+
|
|
55
|
+
#### Pre-Wave 1b Extension: Docs Tasks Persistence (A3 / #230)
|
|
56
|
+
|
|
57
|
+
After writing the base STATE.md frontmatter above, conditionally persist the docs tasks block emitted by session-plan:
|
|
58
|
+
|
|
59
|
+
**Condition:** BOTH of the following must be true:
|
|
60
|
+
1. The session plan contains a `### Docs Tasks (machine-readable)` section with a YAML code block.
|
|
61
|
+
2. `$CONFIG."docs-orchestrator".enabled` is `true`.
|
|
62
|
+
|
|
63
|
+
If either condition is false → omit the `docs-tasks` field entirely. Do NOT write an empty key (`docs-tasks: []`). Absence means "no docs tasks planned this session" — downstream consumers (session-end Phase 3.2) treat absence the same as an empty list.
|
|
64
|
+
|
|
65
|
+
When the condition is met, parse the YAML block from the session plan's `### Docs Tasks (machine-readable)` section and append the following field to the STATE.md YAML frontmatter (alongside the base fields above):
|
|
66
|
+
|
|
67
|
+
```yaml
|
|
68
|
+
docs-tasks:
|
|
69
|
+
- id: <task id from plan>
|
|
70
|
+
audience: <user|dev|vault>
|
|
71
|
+
target-pattern: <glob pattern from plan>
|
|
72
|
+
rationale: <rationale string from plan>
|
|
73
|
+
wave: <wave number the task is assigned to>
|
|
74
|
+
status: planned
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Each entry's `status` is initialized to `planned`. session-end Phase 3.2 (Docs Verify) writes the terminal value per task: `ok` (diff is substantive), `partial` (diff region contains `<!-- REVIEW: source needed -->` markers), or `gap` (no matching diff). wave-executor does NOT perform intermediate status updates — `planned` remains until session-end runs.
|
|
78
|
+
|
|
79
|
+
> **Schema note:** `schema-version: 1` now includes the optional `docs-tasks` array. The field is backwards-compatible — its absence is a valid schema-version-1 STATE.md meaning "no docs tasks planned". Readers MUST treat a missing `docs-tasks` key identically to `docs-tasks: []`.
|
|
80
|
+
|
|
81
|
+
> **Ownership clarification:** session-plan does NOT write STATE.md directly. The wave-executor owns ALL STATE.md writes — initialization here (Pre-Wave 1b) is the canonical write point for `docs-tasks`. session-plan only emits the source `### Docs Tasks (machine-readable)` block for the coordinator to consume. See `skills/_shared/state-ownership.md` for the full ownership matrix.
|
|
82
|
+
|
|
83
|
+
> **Consumer cross-reference:** session-end reads `STATE.md` frontmatter's `docs-tasks` field (if present) during Phase 3.2 Docs Verify — see `skills/session-end/SKILL.md`. The field is also readable by the docs-writer agent if it needs to know which tasks were planned for the current session.
|
|
84
|
+
|
|
85
|
+
> **Ownership:** STATE.md is owned by the wave-executor. Only the wave-executor writes to it (initialization + post-wave updates). session-end reads it for metrics extraction and sets `status: completed`. session-start reads it only for continuity checks (Phase 0.5). No other skill should write to STATE.md.
|
|
86
|
+
|
|
@@ -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
|
|
|
@@ -551,6 +553,14 @@ Marker line plus fenced block, in that order: `hooks/pre-task-scope-disjoint.mjs
|
|
|
551
553
|
|
|
552
554
|
> **Registration note.** That hook was armed in `hooks/hooks.json` on 2026-08-14, after a green Full Gate. Its `PreToolUse` matcher is **`Agent`** — measured over 12 archived transcripts of this repo, `Agent` accounts for 147 of 147 dispatch `tool_use` blocks. A `Task` matcher would hit the unrelated todo family (`TaskCreate`/`TaskUpdate`/`TaskGet`/…) and never once fire on a dispatch: armed and inert, the failure mode that reads as done. It is deliberately absent from `hooks-codex.json` / `hooks-cursor.json` / `hooks-pi.json` — those platforms have no `Agent` dispatch tool, so the asymmetry is registered in `DOCUMENTED_ASYMMETRIES` rather than papered over with a matcher that can never fire.
|
|
553
555
|
|
|
556
|
+
**Scope-echo line (#1092).** Immediately AFTER the fenced `FILE-SCOPE` block, append one more line — the receive-side counterpart of the hook's send-side `scope_checked` record. Get it from the CLI, never hand-typed:
|
|
557
|
+
|
|
558
|
+
```bash
|
|
559
|
+
node scripts/lib/scope-echo.mjs --scope-file "$AGENT_FILESCOPE_JSON" --instruction
|
|
560
|
+
```
|
|
561
|
+
|
|
562
|
+
It prints exactly one line (`renderScopeEchoInstruction` in `scripts/lib/scope-echo.mjs`), naming the `scopeDigest` of that agent's own scope file: `End your final report with the line: SCOPE-DIGEST: <8-hex>`. Append it verbatim. It prints NOTHING when the scope file is empty, missing or unreadable — Discovery waves therefore inject nothing here, exactly as they inject no `FILE-SCOPE` block. The post-wave comparison is `wave-loop-review.md` step 3d-bis; what the echo does and does not prove is stated there.
|
|
563
|
+
|
|
554
564
|
#### Structured Reasoning (STATE:/PLAN:) — opt-in via `reasoning-output: true` (#79)
|
|
555
565
|
|
|
556
566
|
When `$CONFIG.reasoning-output` is `true`, append the following block to every agent prompt. The pattern is adapted from the BitGN PAC Agent's Soft-SGR: short structured transparency lines before tool invocations, without forcing structured output. Leave the block OUT when the flag is `false` (default) — this preserves exact legacy prompt behavior.
|
|
@@ -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) {
|
|
@@ -119,6 +119,19 @@ Log every non-`pass` result as an event to `.orchestrator/metrics/events.jsonl`
|
|
|
119
119
|
|
|
120
120
|
Cross-reference `.claude/rules/verification-before-completion.md` § VBC-004 Exception 2: a subagent's `STATUS: done` is a claim that needs its own verification — this step is that verification for the file-write side effect. `$WAVE_PREDISPATCH_HEAD` is the HEAD snapshot captured before this wave dispatched (same snapshot used by `### 3c. File-level grounding`). When `persistence: false` (no STATE.md), still perform the check and surface any violation in the wave progress update; only the deviation-write is skipped.
|
|
121
121
|
|
|
122
|
+
3d-bis. **Scope-echo check (#1092)** (per agent, INFORMATIONAL — never blocks): the send-side `orchestrator.wave_dispatch.scope_checked` record says what the guard saw in the prompt the coordinator handed to the dispatch tool; nothing observes the receive side. This step collects the one signal that is available — the agent's own echo of the digest injected in `wave-loop-dispatch.md` § Pre-Dispatch: File-Scope Injection.
|
|
123
|
+
|
|
124
|
+
For each agent that reported, write its final report text to a file and run:
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
node scripts/lib/scope-echo.mjs --scope-file "<state-dir>/filescopes/wave-<N>/<agent-id>.json" \
|
|
128
|
+
--report-file "<report.txt>" --wave <N> --agent-id "<agent-id>" --emit
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
`checkScopeEcho` prints the verdict as JSON and `--emit` appends one `orchestrator.wave_dispatch.scope_echo_checked` record (payload: `docs/events-schema.md`; counts, digests and closed enums only — no path, no prompt text). `match: false` or `echoed: false` is logged in the wave progress update as `scope-echo: <agent> missing|mismatch` and changes NOTHING else — no re-dispatch, no deviation write, no gate.
|
|
132
|
+
|
|
133
|
+
**What it proves:** the agent's final report carried the digest the coordinator handed it, i.e. the injected line survived the round trip into the agent's context and back. **What it does not prove:** that the model read the `FILE-SCOPE` block, understood it, or obeyed it — the digest is in the prompt, so it can be copied without ever looking at the paths (BV-004 ceiling, `docs/scope-collision-guard.md` § 4.2). Obedience is still measured at write time (`enforce-scope.mjs`) and by the W5 verification pass.
|
|
134
|
+
|
|
122
135
|
3e. **Collect Open Questions** (Close Handover-Alignment-Gate, PRD 2026-07-07): scan every completed agent's report from this wave for an optional `OPEN-QUESTIONS:` line (see the report-line convention in `wave-loop-dispatch.md` `#### Agent-Type Resolution` — an agent MAY emit `OPEN-QUESTIONS: <question> | context: <...> | candidates: <opt A / opt B>`; most agents emit none). For each such line found:
|
|
123
136
|
|
|
124
137
|
- Parse the question text (portion before the first ` | `).
|
|
@@ -317,9 +330,9 @@ If the commit itself fails (e.g., nothing to commit, pre-commit hook rejects), d
|
|
|
317
330
|
- **Discovery** and **Finalization** waves: skip session-reviewer dispatch — Discovery is read-only and Finalization is a final git status check only.
|
|
318
331
|
- This is complementary to the incremental verification in step 4 — the session-reviewer provides deeper analysis (security, silent failures, test depth, type design) that automated checks do not cover.
|
|
319
332
|
6. **Pencil design review** (after Impl-Core and Impl-Polish roles only, if `pencil` configured in Session Config):
|
|
320
|
-
a. Check Pencil
|
|
321
|
-
b. Get design structure: `
|
|
322
|
-
c.
|
|
333
|
+
a. Check Pencil app state: `mcp__pencil__get_app_state`. If the configured `.pen` file is not open, open it through the `mcp__pencil__execute` surface (an Open/activate call on `filePath` — `.pen` files are encrypted, so never `Read`/`Grep` them; follow the tool's own input schema, which the Pencil MCP server instructions describe as "be aware of the .pen schema and the instructions for the execute tool"). If that also fails → skip with note "Pencil review skipped — .pen file unavailable."
|
|
334
|
+
b. Get design structure: `mcp__pencil__execute` with a **Get visitor** scoped to `filePath: "<pencil-path>"` at depth 1 (top-level frames) or depth 2 (frame children) — find frames relevant to this wave's UI work. Read the execute tool's input schema for the exact visitor shape before calling; do not guess it.
|
|
335
|
+
c. Render relevant frames: there is no direct screenshot tool on the current surface — use `mcp__pencil__browser` to view/capture each frame matching the wave's UI tasks (`filePath` plus the frame's node id).
|
|
323
336
|
d. Read the actual UI files changed in this wave (from agent outputs).
|
|
324
337
|
e. **Compare**: layout structure, component hierarchy, visual elements (headings, buttons, inputs, cards), responsive behavior.
|
|
325
338
|
f. **Report** in wave progress:
|
|
@@ -334,7 +347,7 @@ If the commit itself fails (e.g., nothing to commit, pre-commit hook rejects), d
|
|
|
334
347
|
3. If "Revise" → re-run session-plan for remaining waves only
|
|
335
348
|
4. If "Abort" → mark remaining waves as DEFERRED, proceed to session-end
|
|
336
349
|
|
|
337
|
-
Always use the `filePath` parameter on Pencil MCP calls. Only review frames relevant to the current wave, not the entire file.
|
|
350
|
+
Always use the `filePath` parameter on Pencil MCP calls. Only review frames relevant to the current wave, not the entire file. The live tool names (`mcp__pencil__get_app_state`, `mcp__pencil__execute`, `mcp__pencil__browser`) have one definition in code — `PENCIL_TOOL_NAMES` in `scripts/lib/ux-grill/pencil-coverage.mjs`; the pre-2026 tool surface it replaced no longer exists (dead names enumerated in that module's JSDoc).
|
|
338
351
|
|
|
339
352
|
7. **Capture wave metrics**: If `persistence` is enabled in Session Config, record for this wave after all agents complete and quality checks run. If `persistence` is `false`, skip metrics capture entirely — do not accumulate in-memory metrics. Record:
|
|
340
353
|
- `wave_number`, `role`, `started_at` (when agents were dispatched), `completed_at` (when all finished)
|
|
@@ -462,7 +475,7 @@ After each wave completes and before the progress update, update `<state-dir>/ST
|
|
|
462
475
|
|
|
463
476
|
**Gate:** `persistence: true` in Session Config. When `persistence: false`, skip every push below — there is no runtime side-channel to feed.
|
|
464
477
|
|
|
465
|
-
The helper is `scripts/lib/agent-status.mjs`.
|
|
478
|
+
The helper is `scripts/lib/agent-status.mjs`. The writers (`setStatus`, `setProgress`) are no-throw and return `{ ok: true } | { ok: false, reason }`; the coordinator ignores the return value (best-effort). The reader `readCurrentStatus()` returns `{ entries, source: 'live-map'|'rebuilt-log'|'stale-cache', at, degraded? }` — the ledger `agent-status.jsonl` is the source of truth and the current-map is a rebuildable cache (#1342), so a consumer must SHOW `source`/`at`; `readCurrentStatusEntries()` is the bare-map accessor. Push at **three anchors** in the wave loop:
|
|
466
479
|
|
|
467
480
|
1. **dispatch** — in `### 1. Dispatch Agents`, as each agent is dispatched, push its status. Use `setProgress` when the wave's per-agent ordinal is meaningful, else `setStatus`:
|
|
468
481
|
|
|
@@ -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.
|