session-orchestrator 4.1.0 → 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.
Files changed (137) hide show
  1. package/.agents/skills/session-plan/SKILL.md +1 -1
  2. package/.claude-plugin/marketplace.json +1 -1
  3. package/.claude-plugin/plugin.json +1 -1
  4. package/.codex-plugin/plugin.json +1 -1
  5. package/.codex-plugin/skills/session-plan/SKILL.md +1 -1
  6. package/.cursor/skills/session-plan/SKILL.md +1 -1
  7. package/.cursor-plugin/plugin.json +1 -1
  8. package/CHANGELOG.md +34 -0
  9. package/README.md +8 -8
  10. package/agents/ux-evaluator.md +1 -1
  11. package/commands/close.md +3 -3
  12. package/commands/go.md +2 -0
  13. package/commands/memory-cleanup.md +4 -3
  14. package/commands/persona-panel.md +1 -1
  15. package/commands/session.md +3 -2
  16. package/docs/README.md +4 -4
  17. package/docs/USER-GUIDE.md +115 -48
  18. package/docs/agent-authoring.md +2 -2
  19. package/docs/baseline.md +55 -1
  20. package/docs/ci-setup.md +1 -1
  21. package/docs/codex-setup.md +1 -0
  22. package/docs/components.md +2 -2
  23. package/docs/cursor-setup.md +1 -0
  24. package/docs/events-schema.md +4 -1
  25. package/docs/instruction-delivery.md +1 -1
  26. package/docs/memory-proposal-flow.md +3 -3
  27. package/docs/migration-v4.md +2 -2
  28. package/docs/owner-config-schema.md +74 -90
  29. package/docs/persona-panel.md +4 -4
  30. package/docs/pi-setup.md +1 -0
  31. package/docs/rule-authoring.md +13 -6
  32. package/docs/scope-collision-guard.md +2 -0
  33. package/docs/session-config-reference.md +55 -22
  34. package/docs/session-config-template.md +9 -5
  35. package/docs/vault-docs-architecture.md +4 -2
  36. package/hooks/_lib/hook-import-set.json +28 -3
  37. package/hooks/_lib/vcs-create-matcher.mjs +214 -16
  38. package/hooks/hooks-codex.json +1 -1
  39. package/hooks/hooks.json +1 -1
  40. package/hooks/pre-bash-issue-budget.mjs +123 -26
  41. package/hooks/subagent-telemetry.mjs +106 -20
  42. package/package.json +4 -4
  43. package/scripts/baseline-archetypes.mjs +28 -0
  44. package/scripts/lib/auto-dialectic.mjs +0 -68
  45. package/scripts/lib/baseline-archetypes.mjs +439 -0
  46. package/scripts/lib/build-live-signals.mjs +5 -6
  47. package/scripts/lib/config/issue-budget.mjs +68 -8
  48. package/scripts/lib/config/private-config-dir.mjs +3 -2
  49. package/scripts/lib/config/remote-hosts.mjs +2 -2
  50. package/scripts/lib/config-schema.mjs +79 -0
  51. package/scripts/lib/file-lock.mjs +47 -5
  52. package/scripts/lib/issue-budget-reconcile.mjs +392 -0
  53. package/scripts/lib/issue-budget.mjs +76 -3
  54. package/scripts/lib/learnings/evolve-telemetry.mjs +1 -2
  55. package/scripts/lib/maintenance-due-banner.mjs +440 -0
  56. package/scripts/lib/owner-config.example.yaml +29 -46
  57. package/scripts/lib/owner-yaml.mjs +14 -13
  58. package/scripts/lib/quality-gate.mjs +13 -6
  59. package/scripts/lib/resource-probe/evaluate.mjs +19 -21
  60. package/scripts/lib/rules-sync.mjs +34 -4
  61. package/scripts/lib/session-close-backfill.mjs +182 -40
  62. package/scripts/lib/session-end/phase-skip.mjs +85 -86
  63. package/scripts/lib/session-end/tail-runner.mjs +178 -0
  64. package/scripts/lib/session-schema/constants.mjs +6 -0
  65. package/scripts/lib/session-schema/validator.mjs +20 -0
  66. package/scripts/lib/session-shape.mjs +558 -0
  67. package/scripts/lib/session-start-probes.mjs +10 -3
  68. package/scripts/lib/session-token-rollup.mjs +95 -10
  69. package/scripts/lib/state-md/frontmatter-mutators.mjs +22 -34
  70. package/scripts/lib/state-md.mjs +1 -0
  71. package/scripts/lib/subagents-schema.mjs +77 -9
  72. package/scripts/lib/telemetry/pricing.mjs +197 -0
  73. package/scripts/lib/telemetry/sync.mjs +50 -1
  74. package/scripts/lib/validate/check-skill-script-paths.mjs +33 -10
  75. package/scripts/lib/validate/check-unwired-features.mjs +8 -7
  76. package/scripts/lib/vault-mirror/process.mjs +2 -1
  77. package/scripts/lib/vault-status/narrative-mirror.mjs +4 -4
  78. package/scripts/lib/wave-resource-gate.mjs +23 -27
  79. package/scripts/lib/wave-sizing.mjs +10 -3
  80. package/scripts/materialize-wave-scope.mjs +68 -14
  81. package/scripts/print-applicable-rules.mjs +7 -6
  82. package/scripts/print-learnings-index.mjs +3 -2
  83. package/scripts/session-shape.mjs +266 -0
  84. package/skills/_shared/config-reading.md +15 -9
  85. package/skills/_shared/private-capability-context.md +89 -0
  86. package/skills/bootstrap/SKILL.md +61 -13
  87. package/skills/bootstrap/_shared-template.md +99 -14
  88. package/skills/bootstrap/deep-template.md +36 -26
  89. package/skills/bootstrap/fast-template.md +44 -8
  90. package/skills/bootstrap/intensity-heuristic.md +10 -4
  91. package/skills/bootstrap/private-contract.md +119 -0
  92. package/skills/bootstrap/public-fallback.md +30 -18
  93. package/skills/bootstrap/standard-template.md +39 -24
  94. package/skills/discovery/probes-ui.md +1 -1
  95. package/skills/docs-orchestrator/audience-mapping.md +1 -1
  96. package/skills/evolve/SKILL.md +2 -2
  97. package/skills/gitlab-ops/SKILL.md +3 -3
  98. package/skills/grill/SKILL.md +1 -1
  99. package/skills/memory-cleanup/SKILL.md +2 -2
  100. package/skills/plan/mode-new.md +9 -0
  101. package/skills/reconcile/SKILL.md +1 -1
  102. package/skills/session-end/SKILL.md +3 -2
  103. package/skills/session-end/phase-3-2-docs-verification.md +1 -1
  104. package/skills/session-end/phase-3-6-tail.md +23 -65
  105. package/skills/session-end/phase-3-7a-recommendations.md +2 -2
  106. package/skills/session-end/references/phase-3-documentation-updates.md +8 -6
  107. package/skills/session-end/references/phase-5-issue-cleanup.md +26 -0
  108. package/skills/session-end/session-metrics-write.md +31 -12
  109. package/skills/session-plan/SKILL.md +56 -48
  110. package/skills/session-plan/wave-template.md +8 -15
  111. package/skills/session-start/SKILL.md +18 -2
  112. package/skills/session-start/phase-2-5-docs-planning.md +1 -1
  113. package/skills/session-start/phase-8-5-express-path.md +12 -9
  114. package/skills/session-start/references/phase-1-5-session-continuity.md +2 -0
  115. package/skills/session-start/references/phase-4-ssot-environment-check.md +21 -5
  116. package/skills/session-start/references/phase-6-7-memory-banner-telemetry-consent.md +3 -1
  117. package/skills/test-runner/rubric-v1.md +2 -2
  118. package/skills/wave-executor/SKILL.md +42 -12
  119. package/skills/wave-executor/circuit-breaker.md +3 -1
  120. package/skills/wave-executor/references/wave-loop-dispatch.md +4 -2
  121. package/skills/wave-executor/references/wave-loop-review.md +1 -1
  122. package/skills/wave-executor/references/wave-loop-scope-manifest.md +6 -2
  123. package/templates/nextjs-minimal/package.json +1 -1
  124. package/templates/node-minimal/package.json +1 -1
  125. package/scripts/lib/multi-provider-build/providers.mjs +0 -64
  126. package/scripts/lib/multi-provider-build/templating.mjs +0 -130
  127. package/scripts/lib/owner-config/coerce.mjs +0 -29
  128. package/scripts/lib/owner-config/constants.mjs +0 -21
  129. package/scripts/lib/owner-config/defaults.mjs +0 -50
  130. package/scripts/lib/owner-config/error.mjs +0 -19
  131. package/scripts/lib/owner-config/index.mjs +0 -13
  132. package/scripts/lib/owner-config/merge.mjs +0 -52
  133. package/scripts/lib/owner-config/validate.mjs +0 -259
  134. package/scripts/lib/owner-config-loader.mjs +0 -170
  135. package/scripts/lib/owner-config.mjs +0 -28
  136. package/scripts/lib/soul-resolve.mjs +0 -130
  137. package/scripts/lib/vault-mirror/render.mjs +0 -8
@@ -96,7 +96,7 @@ enforcement: warn
96
96
  vcs: github
97
97
  ```
98
98
 
99
- If you skip this step, the plugin uses sensible defaults: `feature` type, 6 agents per wave, 5 waves, and auto-detected VCS. See [`docs/session-config-template.md`](session-config-template.md) for the full field walkthrough.
99
+ If you skip this step, the plugin uses sensible defaults: `feature` type (a fixed 3-wave shape — see [`docs/session-config-reference.md`](session-config-reference.md#session-shapes) § Session Shapes for the full per-type wave/agent-cap table), 6 agents per wave, and auto-detected VCS. See [`docs/session-config-template.md`](session-config-template.md) for the full field walkthrough.
100
100
 
101
101
  ### Run your first session
102
102
 
@@ -198,6 +198,22 @@ You can also run the bootstrap flow explicitly, outside of any session:
198
198
 
199
199
  `--retroactive` is the recommended path for existing repos that predate the Bootstrap Gate — it adds the missing `CLAUDE.md` structure and lock file without touching your existing code or configuration.
200
200
 
201
+ ### Use a configured baseline
202
+
203
+ Standard and Deep bootstrap can read the validated archetype contract from your
204
+ configured local baseline. Existing project markers select a matching archetype;
205
+ an empty or unrecognized project presents the baseline's available choices.
206
+ The selected contract supplies templates, runtime and package-manager versions,
207
+ commands, CI expectations, and required rules. Existing project files are
208
+ preserved and reported for review.
209
+
210
+ Configuration resolves from `SO_BASELINE_PATH`, a matching named baseline,
211
+ `owner.yaml` paths, then the project's `plan-baseline-path`. Lookup is offline
212
+ and does not install packages. An absent baseline uses the bundled public
213
+ templates; an invalid configured contract stops before writing files. Missing
214
+ quality gates are reported as unavailable. See [Baseline integration](baseline.md)
215
+ for the contract and configuration details.
216
+
201
217
  ### Anti-bureaucracy promise
202
218
 
203
219
  - **Normal flow:** exactly 1 question (tier confirmation)
@@ -285,29 +301,47 @@ Structured requirement gathering, PRD generation, and issue creation. Accepts on
285
301
 
286
302
  **Optional:** `plan-baseline-path` in Session Config (for `/plan new` repo scaffolding from your own baseline). When absent, `/bootstrap` falls back to plugin-bundled minimal templates. Not required for `/plan feature` or `/plan retro`.
287
303
 
304
+ **Optional private capability context:** For new-project or session planning, you can
305
+ supply a small catalog excerpt or authorize a particular offline, read-only local
306
+ catalog lookup and state that its planning destination is private/internal. The
307
+ planner uses at most five matches to explain reuse alternatives and remaining
308
+ contract checks. Existing authorization carries forward. Missing or unsuitable
309
+ context leaves ordinary planning available; a match does not authorize adoption.
310
+ Private findings stay out of public output and generated repositories. This needs
311
+ no new Session Config key; see the [shared procedure](../skills/_shared/private-capability-context.md).
312
+
288
313
  ---
289
314
 
290
315
  ## 4. Session Types
291
316
 
292
- ### Housekeeping
317
+ Wave count, roles, and per-wave agent caps are resolved by one module — `scripts/lib/session-shape.mjs` — not derived by hand from `waves`/`agents-per-wave`. Run the CLI yourself to see exactly what a given mode resolves to before starting a session:
318
+
319
+ ```
320
+ node scripts/session-shape.mjs --repo-root "$PWD" --session-type <housekeeping|feature|deep> \
321
+ [--profile ultradeep] [--known-scope true|false] --no-event
322
+ ```
323
+
324
+ It prints one JSON line (`totalWaves`, `waves[]` with each wave's `role`/`agentCap`/`maxTurns`/`verification`, `discovery`, `wavesConfigHonored`, `notes`) and, without `--no-event`, records `orchestrator.session.shape_resolved` to `.orchestrator/metrics/events.jsonl`.
325
+
326
+ ### Housekeeping — the maintenance loop
293
327
 
294
328
  Best for: git cleanup, SSOT refresh, CI fixes, branch merges, documentation updates.
295
329
 
296
- - **Execution model:** Serial (no wave structure)
297
- - **Agents:** 1-2 per task
330
+ - **Execution model:** **1 coordinator-direct wave.** Housekeeping *is* the maintenance loop: drift-check → expired-learnings sweep → `/evolve analyze` → `/reconcile` → `/evolve dialectic` → `/memory-cleanup`, then any operator-selected housekeeping issues appended in the order picked. No wave-executor dispatch for the loop itself — most of those steps are `AskUserQuestion`-gated, and AUQ does not exist inside a dispatched subagent.
331
+ - **Agents:** 0 dispatched for the loop (the coordinator runs it directly); the dialectic step dispatches the read-only `dialectic-deriver` subagent.
298
332
  - **Typical duration:** Short
299
- - **Use when:** Your repo needs maintenance, not new features
333
+ - **Use when:** Your repo needs maintenance, not new features — or when the session-start `maintenance-due` banner tells you it's overdue.
300
334
 
301
335
  ```
302
336
  /session housekeeping
303
337
  ```
304
338
 
305
- ### Feature
339
+ ### Feature — 3 waves
306
340
 
307
341
  Best for: frontend/backend feature work, implementing issues, standard development.
308
342
 
309
- - **Execution model:** 5 waves with parallel agents
310
- - **Agents:** 4-6 per wave (configurable)
343
+ - **Execution model:** 3 waves Impl-Core → Impl-Polish+Quality → Finalization.
344
+ - **Agents:** capped at 4 / 4 / 2 respectively (subject to `agents-per-wave`).
311
345
  - **Typical duration:** Medium
312
346
  - **Use when:** You have feature issues to implement
313
347
 
@@ -315,12 +349,12 @@ Best for: frontend/backend feature work, implementing issues, standard developme
315
349
  /session feature
316
350
  ```
317
351
 
318
- ### Deep
352
+ ### Deep — 5 waves (4 when scope is already known)
319
353
 
320
354
  Best for: complex backend work, security audits, database refactoring, architecture changes.
321
355
 
322
- - **Execution model:** 5 waves with parallel agents
323
- - **Agents:** Up to 10-18 per wave (configurable)
356
+ - **Execution model:** 5 waves Discovery → Impl-Core → Impl-Polish → Quality → Finalization. When the agreed scope is already fully known (`--known-scope true`), Discovery is dropped and the rest renumbered to 4 waves.
357
+ - **Agents:** capped at 8 / 10 / 8 / 6 / 4 per wave respectively (subject to `agents-per-wave`).
324
358
  - **Typical duration:** Longer
325
359
  - **Use when:** The work requires extensive discovery, testing, or touches critical systems
326
360
 
@@ -328,6 +362,19 @@ Best for: complex backend work, security audits, database refactoring, architect
328
362
  /session deep
329
363
  ```
330
364
 
365
+ ### Ultradeep — 7 fixed waves (a profile over `deep`)
366
+
367
+ Best for: sessions that outgrow 5 waves — large audits, work needing web research before implementation, or a release that benefits from an independent review panel.
368
+
369
+ - **Execution model:** a **fixed 7-wave shape** — Research+Code-Discovery → Synthesis-Gate (coordinator-direct, 0 agents, blocking `AskUserQuestion`) → Impl-Core → Impl-Polish → Review-Panel (read-only) → Quality → Release/Finalization. It IGNORES the `waves` Session Config value outright (`wavesConfigHonored: false`) — there is no "waves < 7 is an error" check; the profile just reports that it ignored the configured number.
370
+ - **Agents:** capped at 18 / 0 / 8 / 8 / 3 / 6 / 4 per wave respectively; `max-turns` is set PER WAVE (40 / — / 25 / 25 / 25 / 25 / 15), not one flat number.
371
+ - **Typical duration:** Longest
372
+ - **Use when:** `/session deep` would work but the scope needs research first, or you want a dedicated review panel before Quality.
373
+
374
+ ```
375
+ /session deep --profile ultradeep # or the /session ultradeep alias, per commands/session.md
376
+ ```
377
+
331
378
  ---
332
379
 
333
380
  ## 5. Session Config Reference
@@ -372,8 +419,8 @@ Add a `## Session Config` section to your project's Session Config host file to
372
419
 
373
420
  | Field | Type | Default | Description |
374
421
  |-------|------|---------|-------------|
375
- | `agents-per-wave` | integer | `6` | Maximum number of parallel subagents per wave. Higher values increase parallelism but use more resources. |
376
- | `waves` | integer | `5` | Number of execution waves for feature and deep sessions. |
422
+ | `agents-per-wave` | integer | `6` | Maximum number of parallel subagents per wave. Higher values increase parallelism but use more resources. Supports the per-type override syntax `6 (deep: 18)` — see [§ 4 Session Types](#4-session-types). |
423
+ | `waves` | integer | `5` | Base wave count. The wave count actually used is resolved per session type by `scripts/session-shape.mjs` (see [§ 4 Session Types](#4-session-types)) — `feature` and `deep` each have one natural shape and ignore a disagreeing `waves` value; the `ultradeep` profile ignores it outright. |
377
424
  | `pencil` | string | none | Path to a `.pen` design file (relative to project root). Enables design-code alignment reviews after Impl-Core and Impl-Polish waves. |
378
425
  | `cross-repos` | list | none | Related repositories under `~/Projects/`. The orchestrator checks their git state and critical issues during session start. |
379
426
  | `ssot-files` | list | none | Single Source of Truth files to track for freshness (e.g., `STATUS.md`, `STATE.md`). Flagged if older than 5 days. |
@@ -414,7 +461,7 @@ Add a `## Session Config` section to your project's Session Config host file to
414
461
  | `cold-start.silence-after-sessions` | integer | `1` | Consecutive silent sessions (no commits, no learnings) before the cold-start detector fires a nudge. PRD F1.3 / issue #500. |
415
462
  | `enforcement` | string | `warn` | Hook enforcement level for scope and command restrictions: `strict`, `warn`, or `off`. |
416
463
  | `isolation` | string | `auto` | Agent isolation mode: `worktree`, `none`, or `auto`. `auto` resolves per-wave via the graduated default (#194): ≤2 agents → `none`, 3–4 agents on feature/deep → `worktree`, ≥5 agents → `worktree`, housekeeping 3–4 → `none`. See Section 15 "Isolation Graduation" below. |
417
- | `max-turns` | integer or string | `auto` | Max agent turns before PARTIAL. Auto: housekeeping=8, feature=15, deep=25. |
464
+ | `max-turns` | integer or string | `auto` | Max agent turns before PARTIAL. Auto-resolves per session shape (`scripts/lib/session-shape.mjs`): 8 for housekeeping, 15 for feature, 25 for deep — applied to every wave. The `ultradeep` profile sets it PER WAVE instead (40 for Research+Code-Discovery, 25 for Impl-Core/Impl-Polish/Quality, 15 for Release/Finalization). See [§ 4 Session Types](#4-session-types). |
418
465
 
419
466
  > **Security:** Do not embed credentials, API keys, or auth tokens in Session Config fields — especially `health-endpoints` URLs. These values are stored in your config host file (`CLAUDE.md` / `AGENTS.md`) which may be committed to version control. Use header-based auth or separate secret management instead.
420
467
 
@@ -434,7 +481,7 @@ See [examples](examples/) for project-specific configurations (Next.js, Express
434
481
 
435
482
  ## 6. The Wave Pattern
436
483
 
437
- Feature and deep sessions execute work in structured waves, each assigned one of 5 roles. Each wave has a specific purpose, and agents within a wave run in parallel.
484
+ Feature and deep sessions execute work in structured waves drawn from the same 5 named roles (some combined into one wave, depending on session type — see [§ 4 Session Types](#4-session-types)). Each wave has a specific purpose, and agents within a wave run in parallel.
438
485
 
439
486
  ### Wave Structure
440
487
 
@@ -448,14 +495,15 @@ Feature and deep sessions execute work in structured waves, each assigned one of
448
495
 
449
496
  ### Role-to-Wave Mapping
450
497
 
451
- Roles map dynamically to the configured wave count (default: 5):
498
+ **As of 2026-09-09, this mapping is resolved by `scripts/session-shape.mjs` per session type — it is no longer a function of the `waves` config value.** The former table (`waves: 3/4/5/6+` → a re-combined role mapping) is retired; a `waves` value that disagrees with a type's natural shape is now IGNORED and reported in the shape's `notes`, never used to re-combine roles. See [§ 4 Session Types](#4-session-types) for the CLI, and the per-type wave lists there:
452
499
 
453
- | `waves` | Mapping |
454
- |---------|---------|
455
- | 3 | W1=Discovery+Impl-Core, W2=Impl-Polish+Quality, W3=Finalization |
456
- | 4 | W1=Discovery, W2=Impl-Core+Impl-Polish, W3=Quality, W4=Finalization |
457
- | 5 | W1=Discovery, W2=Impl-Core, W3=Impl-Polish, W4=Quality, W5=Finalization |
458
- | 6+ | W1=Discovery, W2-W3=Impl-Core (split), W4-W5=Impl-Polish (split), W6=Quality+Finalization |
500
+ | Session type | Waves | Roles |
501
+ |---|---|---|
502
+ | `housekeeping` | 1 | Housekeeping (coordinator-direct maintenance loop) |
503
+ | `feature` | 3 | Impl-CoreImpl-Polish+Quality Finalization |
504
+ | `deep` (scope not yet known) | 5 | Discovery Impl-Core Impl-Polish Quality Finalization |
505
+ | `deep` (scope known) | 4 | Impl-Core Impl-Polish QualityFinalization |
506
+ | `deep` + `ultradeep` profile | 7 (fixed, ignores `waves`) | Research+Code-Discovery → Synthesis-Gate → Impl-Core → Impl-Polish → Review-Panel → Quality → Release/Finalization |
459
507
 
460
508
  ### Wave Details
461
509
 
@@ -476,13 +524,15 @@ One or two agents update SSOT files, close or update issues, write session hando
476
524
 
477
525
  ### Agent Counts by Session Type
478
526
 
527
+ These are the shape table's RAW per-wave ceilings (`agentCapRaw` in the `scripts/session-shape.mjs` JSON) — not a range the orchestrator picks within by feel:
528
+
479
529
  | Session Type | Discovery | Impl-Core | Impl-Polish | Quality | Finalization |
480
530
  |-------------|-----------|-----------|-------------|---------|-------------|
481
- | housekeeping | 2 | 2 | 1 | 1 | 1 |
482
- | feature | 4-6 | 6 | 4-6 | 4 | 2 |
483
- | deep | 6-8 | 6-10 | 6-8 | 6 | 2-4 |
531
+ | housekeeping | | (0, coordinator-direct) | | | |
532
+ | feature | | 4 | 4 (combined w/ Quality) | *(combined)* | 2 |
533
+ | deep | 8 | 10 | 8 | 6 | 4 |
484
534
 
485
- The `agents-per-wave` config value caps the maximum. These counts are guidelines — the orchestrator adjusts based on task complexity.
535
+ **The number actually used (`agentCap`) is `min(raw, agents-per-wave)`.** With the documented default `agents-per-wave: 6`, a `deep` session's Discovery/Impl-Core/Impl-Polish waves are clipped DOWN to 6 — the 8/10/8 above only apply once you raise the cap for that type, e.g. `agents-per-wave: 6 (deep: 18)` (this plugin's own committed config). `feature`'s 4/4/2 already sit under the default 6 and are unaffected by it. These are still ceilings, not targets — the orchestrator adjusts DOWN based on task complexity, per the tier guidance below.
486
536
 
487
537
  The **Quality column is a cap, not a target**: since this version, test-writing capacity is need-gated on measured demand — roughly one test-writer per three HIGH/MED gaps the review panel actually found, capped by the number above. If no gaps were measured, the Quality wave writes no tests and is skipped (the read-only review panel still runs); with no measurement signal at all, the orchestrator allocates a conservative 1-2 rather than the full cap.
488
538
 
@@ -546,18 +596,21 @@ After you choose a direction, the orchestrator decomposes the work into a role-b
546
596
  ```
547
597
  ## Wave Plan (Session: feature)
548
598
 
549
- ### Wave 1: Discovery (4 agents)
550
- - Agent 1: Audit API endpoint structure → src/api/ → map current routes
551
- - Agent 2: Verify database schema → prisma/schema.prisma → check relations
552
- ...
599
+ A `feature` session resolves to 3 waves — no Discovery wave; see [§ 4 Session Types](#4-session-types).
553
600
 
554
- ### Wave 2: Impl-Core (6 agents)
601
+ ### Wave 1: Impl-Core (4 agents)
555
602
  - Agent 1: Implement new API route → src/api/users.ts → endpoint returns 200
603
+ - Agent 2: Add database migration → prisma/schema.prisma → check relations
604
+ ...
605
+
606
+ ### Wave 2: Impl-Polish+Quality (4 agents)
607
+ - Agent 1: Build frontend form component → src/components/ → wired to the new route
608
+ - Agent 2: Write and run tests → tests/api/users.test.mjs → passing
556
609
  ...
557
610
 
558
611
  ### Inter-Wave Checkpoints
559
612
  - After Impl-Core: Design review (Pencil configured)
560
- - After Quality: Full quality gate
613
+ - After Impl-Polish+Quality: Full quality gate
561
614
 
562
615
  Ready to execute? Use /go to begin.
563
616
  ```
@@ -573,13 +626,12 @@ You can request changes to the plan. When satisfied:
573
626
  Waves execute automatically. Agents within each wave run in parallel. Between waves, the orchestrator reviews results, runs checks, and adapts the plan if needed. You see progress updates after each wave:
574
627
 
575
628
  ```
576
- ## Wave 2 (Impl-Core) Complete ✓
629
+ ## Wave 1 (Impl-Core) Complete ✓
577
630
  - Agent 1: done — API route implemented, returns correct schema
578
631
  - Agent 2: done — Database migration created
579
- - Agent 3: done — Frontend form component built
580
632
  - Tests: 3 new passing | TypeScript: 0 errors
581
633
  - Design: ALIGNED
582
- - Adaptations for Impl-Polish: none
634
+ - Adaptations for Impl-Polish+Quality: none
583
635
  ```
584
636
 
585
637
  ### Step 5: Close the session
@@ -954,7 +1006,7 @@ Session Orchestrator persists session state so you can resume after crashes, pau
954
1006
 
955
1007
  ### STATE.md
956
1008
 
957
- Lives at `.claude/STATE.md` in your project. Contains YAML frontmatter (`session-type`, `branch`, `issues`, `started`, `status`, `current-wave`, `total-waves`) and a Markdown body tracking the Current Wave, Wave History, and any Deviations from the plan. Written by the wave-executor after each wave; read by session-start on the next `/session` invocation.
1009
+ Lives at `.claude/STATE.md` in your project. Contains YAML frontmatter (`session-type`, `branch`, `issues`, `started`, `status`, `current-wave`, `total-waves`) and a Markdown body tracking the Current Wave, Wave History, and any Deviations from the plan. Written by the wave-executor after each wave; read by session-start on the next `/session` invocation. <!-- path-check: example -->
958
1010
 
959
1011
  ### Session Continuity
960
1012
 
@@ -1163,7 +1215,7 @@ Read-only display of all active learnings with confidence scores and expiry date
1163
1215
 
1164
1216
  ## 18. Adaptive Wave Sizing
1165
1217
 
1166
- Instead of fixed agent counts, the orchestrator scores session complexity and adjusts agent allocation dynamically.
1218
+ Instead of always dispatching a wave's full agent-cap ceiling, the orchestrator scores session complexity and relaxes agent allocation downward when the briefed work does not need the full cap.
1167
1219
 
1168
1220
  ### Complexity Scoring
1169
1221
  Three factors are scored (0-2 points each):
@@ -1177,18 +1229,11 @@ Three factors are scored (0-2 points each):
1177
1229
  ### Complexity Tiers
1178
1230
  - **Simple** (0-1 points): fewer agents per wave
1179
1231
  - **Moderate** (2-3 points): standard allocation
1180
- - **Complex** (4-6 points): maximum agents per wave
1232
+ - **Complex** (4-6 points): up to the wave's cap
1181
1233
 
1182
- ### Dynamic Scaling Between Waves
1183
- After each wave, agent count is adjusted based on performance:
1184
- - All agents fast + no issues → reduce next wave
1185
- - Failures or broken code → add fix agents
1186
- - Scope expansion → scale up
1187
- - Quality regressions → targeted fix agents
1234
+ The tier score relaxes agent count **downward only** — a simple-tier session may plan fewer agents than the wave's `agentCap` where the briefed work does not fill it. It never raises the count above that cap; a moderate or complex tier does not scale it up. The cap itself comes from the resolved session shape (`waves[].agentCap`, see [`docs/session-config-reference.md`](session-config-reference.md#session-shapes) § Session Shapes) — it is not derived from the tier, and the `agents-per-wave` config value is the ceiling that cap was already built against.
1188
1235
 
1189
- The `agents-per-wave` config value always caps the maximum.
1190
-
1191
- > **Note:** Housekeeping sessions skip complexity scoring and use fixed counts.
1236
+ > **Note:** Housekeeping has no tier at all — it is a single coordinator-direct wave (0 dispatched agents) running the fixed maintenance loop, not a scored/scaled wave.
1192
1237
 
1193
1238
  ---
1194
1239
 
@@ -1281,7 +1326,7 @@ Yes. Between each wave, the orchestrator reviews results and can adapt the plan.
1281
1326
 
1282
1327
  ### How many agents run in parallel?
1283
1328
 
1284
- This is controlled by the `agents-per-wave` setting in your Session Config. The default is 6. For deep sessions, you can increase this to 10-18. All agents within a single wave run in parallel; the orchestrator waits for all of them to complete before starting the next wave.
1329
+ This is controlled by the `agents-per-wave` setting in your Session Config, using the override form `6 (deep: 18)` to raise the ceiling for deep sessions specifically see [`docs/session-config-reference.md`](session-config-reference.md#session-shapes) § Session Shapes for the full per-wave cap table. Note that a plain `deep` session's own raw wave caps top out at 10 (the Impl-Core wave) regardless of the override value configured; an override of 18 only actually binds under the `ultradeep` profile (`/session ultradeep`), whose Research+Code-Discovery wave is the one wave sized at 18. All agents within a single wave run in parallel; the orchestrator waits for all of them to complete before starting the next wave.
1285
1330
 
1286
1331
  ### Do I need Pencil?
1287
1332
 
@@ -1377,6 +1422,28 @@ If agents consistently time out during wave execution:
1377
1422
  - Switch from `deep` to `feature` session type if you do not need the extra agent count
1378
1423
  - Check that your machine has sufficient resources for parallel agent execution
1379
1424
 
1425
+ ### Import-probe warnings and missing ESLint
1426
+
1427
+ The post-edit import probe checks edited `.mjs`, `.js`, and `.cjs` files only when the path is listed in the project's `hooks/_lib/hook-import-set.json` and the hook is enabled. It reports likely hook breakage as a warning and always exits 0. Its two checks have different coverage:
1428
+
1429
+ - **C1 — ESLint:** reports `no-undef` and fatal/parse errors. Install ESLint with the project's package manager and enable `no-undef` in the **project's ESLint configuration**; the probe does not enable that rule itself. Other lint rules remain the full lint command's responsibility.
1430
+ - **C2 — import:** loads allowlisted files under `scripts/lib/**` in a child process to detect errors during module loading. It does not import hook entrypoints or call exported functions. An undefined identifier reached only when a function runs can therefore escape C2 even when importing the module succeeds.
1431
+
1432
+ ESLint lookup tries these paths in order: the project's `node_modules/.bin/eslint`, the project's `node_modules/eslint/bin/eslint.js`, then the same two paths under the plugin root. A consumer installation may have no plugin development dependencies, so the project's ESLint installation matters. `SO_IMPORT_PROBE_ESLINT` overrides this search with an explicit script path; use an absolute path to the ESLint entry script. An empty or nonexistent override disables C1 **without falling back** to either installation.
1433
+
1434
+ When no ESLint is available, C1 is normally silent and only eligible C2 checks remain. For an edit that reaches the checks, `SO_IMPORT_PROBE_TRACE=1` writes `probe:eslint-unavailable` to stderr when ESLint cannot be located. This trace diagnoses unavailable ESLint; it does not diagnose every timeout or configuration failure. A timed-out ESLint or an unreadable/non-JSON report is skipped, so silence does not prove that C1 ran successfully.
1435
+
1436
+ The shipped event wiring is:
1437
+
1438
+ | Harness | Import-probe event |
1439
+ |---------|--------------------|
1440
+ | Claude Code | `PostToolUse` for edit/write tools |
1441
+ | Cursor | `postToolUse` and `afterFileEdit` |
1442
+ | Pi | `tool_result` for edit/write tools |
1443
+ | Codex | **Unwired:** the current Codex hook manifest has no import-probe handler |
1444
+
1445
+ Codex does not currently run this probe automatically, and reinstalling the same bundle does not add that missing handler. Use the project's normal lint and tests for verification there. Implementation: [`hooks/post-edit-import-probe.mjs`](../hooks/post-edit-import-probe.mjs); event manifests: [Claude Code](../hooks/hooks.json), [Cursor](../hooks/hooks-cursor.json), [Pi](../hooks/hooks-pi.json), [Codex](../hooks/hooks-codex.json).
1446
+
1380
1447
  ### Design review skipped unexpectedly
1381
1448
 
1382
1449
  If you configured `pencil` but design reviews are not running:
@@ -1,4 +1,4 @@
1
- <!-- Moved in v4.0.0 from `agents/AGENTS.md` (audit 2026-09-06 § 5A). It never was an agent: Claude Code's plugin loader registers every `agents/*.md` by directory convention and the manifest's `agents` key is additive-only, so no manifest entry could exclude it — only pseudo-frontmatter (`name: agents-authoring-spec`, `tools: Read`) kept the false registration bounded. Living under `docs/` removes the registration instead of bounding it. -->
1
+ <!-- Moved in v4.0.0 from `agents/AGENTS.md` (audit 2026-09-06 § 5A). It never was an agent: Claude Code's plugin loader registers every `agents/*.md` by directory convention and the manifest's `agents` key is additive-only, so no manifest entry could exclude it — only pseudo-frontmatter (`name: agents-authoring-spec`, `tools: Read`) kept the false registration bounded. Living under `docs/` removes the registration instead of bounding it. --> <!-- path-check: historical -->
2
2
 
3
3
  # Sub-Agent Authoring Conventions (`agents/**`)
4
4
 
@@ -11,7 +11,7 @@
11
11
  > plugin loader registers every `agents/*.md` as a dispatchable agent by
12
12
  > directory convention, and the manifest's `agents` key is documented as
13
13
  > *additive* ("in addition to those in the `agents/` directory"), so it cannot
14
- > exclude a path. As `agents/AGENTS.md` this file was therefore a registered
14
+ > exclude a path. As `agents/AGENTS.md` this file was therefore a registered <!-- path-check: historical -->
15
15
  > agent — first an unnamed one with **full tool access**, later a contained one
16
16
  > whose pseudo-frontmatter capped `tools` at `Read`. Moving it out of the
17
17
  > directory removes the registration rather than bounding it. The same applies
package/docs/baseline.md CHANGED
@@ -19,7 +19,61 @@ A separate git repository (not vendored, not a submodule, not on npm) carrying:
19
19
  rules are read **from the baseline**, not from this plugin, so the plugin had to
20
20
  learn the baseline's frontmatter convention rather than the other way round.
21
21
 
22
- ## How the plugin finds it
22
+ ## How bootstrap finds it
23
+
24
+ Bootstrap resolves only explicit local configuration, in this order:
25
+
26
+ 1. `SO_BASELINE_PATH`
27
+ 2. A matching entry in the host-local `owner.yaml` `baselines` list
28
+ 3. `owner.yaml` `paths.baseline-path`
29
+ 4. `plan-baseline-path` in the repository's Session Config
30
+
31
+ `scripts/baseline-archetypes.mjs --repo <repo>` reuses the existing configuration
32
+ resolvers. Their diagnostics are contained in a bounded local process so paths
33
+ and private match names cannot leak through the bootstrap CLI. An absent or
34
+ missing directory keeps the public fallback. An existing directory with a
35
+ missing producer, unsupported schema, invalid metadata or unsafe source is an
36
+ explicit error; bootstrap does not silently switch to a public default.
37
+
38
+ The local producer is the baseline's `archetype-manifest.mjs export` CLI. Its reduced v1
39
+ JSON owns IDs, ordering, declarative detection signals, runtimes, package
40
+ managers, UI/API/deploy metadata, command documentation, quality gates, CI,
41
+ browser automation, and rule targets. No private package inventory or source
42
+ evidence is imported into the plugin. Marker inference uses exported priorities;
43
+ an unknown result is `insufficient-evidence`, requiring a selection from the
44
+ returned catalog for Standard/Deep. `--archetype <id>` validates an explicit ID.
45
+
46
+ The CLI is read-only, offline and dependency-free without a baseline. Commands
47
+ in its JSON are data and are never evaluated. Only the explicit scaffold action
48
+ in [`private-contract.md`](../skills/bootstrap/private-contract.md) invokes the
49
+ configured baseline's local renderer, into temporary staging. It preserves
50
+ existing destination files and excludes staged rules. Deep retains baseline CI
51
+ and its exemption/requirement instead of generating a public Node pipeline.
52
+ New instruction-file command slots come from exactly matching declared gate
53
+ IDs. Missing test/typecheck/lint gates are reported as unavailable and use
54
+ `false` placeholders, never inferred npm commands. Private verification reports
55
+ declared gates separately from unavailable slots; the generic quality runner
56
+ is unchanged and an unavailable slot must not be reported as a passed check.
57
+
58
+ S99 recomputes the complete exported rule union after rendering, including
59
+ dependency-conditional targets, and validates the baseline's local `rules`
60
+ projection against it. Sources must stay under `.claude/rules/` or
61
+ `templates/shared/.claude/rules/`, with no symlinks. All basenames owned by
62
+ `rules/_index.md` are excluded, including scoped entries: `rules-sync.mjs` remains
63
+ their sole writer. `syncBootstrapRules` passes the contract's required plugin
64
+ basenames to that writer after validating the local export, so a public scope
65
+ tag cannot silently omit a private requirement. Private S99 adds missing local rules and preserves existing
66
+ ones; the public opt-in fetch retains its existing remote behavior with the
67
+ same plugin-ownership exclusion. No baseline is downloaded or located by
68
+ guessing sibling/private host paths.
69
+
70
+ New private quality policies use the same exact `test`, `typecheck`, and `lint`
71
+ gate IDs as new Session Config blocks. Missing slots use `false` with an
72
+ unavailable explanation; existing owner policies remain unchanged. Bootstrap
73
+ accumulates actual created relative paths through inherited tiers and stages
74
+ those files individually.
75
+
76
+ ## Legacy vault and maintenance resolution
23
77
 
24
78
  Never by a hardcoded path. Resolution is host-local, most specific first:
25
79
 
package/docs/ci-setup.md CHANGED
@@ -123,7 +123,7 @@ jobs, so a half-revert or a template refresh flipping one site back to
123
123
  check diffs real content rather than passing on a broken comparison.
124
124
 
125
125
  Per `.claude/rules/security.md` § SEC-005, this token's lifecycle belongs in
126
- `.claude/docs/SECRETS-INVENTORY.md` once one exists — that file is not present
126
+ `.claude/docs/SECRETS-INVENTORY.md` once one exists — that file is not present <!-- path-check: example -->
127
127
  in this repo (measured 2026-09-02: no `.claude/docs/` directory tracked), so
128
128
  the inventory is not adopted here and this section remains the sole record.
129
129
 
@@ -255,6 +255,7 @@ codex plugin list --available --json
255
255
  - **The `kanevry` marketplace points at another source:** confirm the conflict with `codex plugin marketplace list --json`, run `codex plugin marketplace remove kanevry`, then rerun the installer from the intended clone so it performs the public marketplace add and plugin add lifecycle.
256
256
  - **`go` or `close` is missing from the skill picker:** follow [the refresh steps](#refresh-and-explicit-cache-invalidation), verify the installed version, and restart Codex if reopening the picker does not load the new entries. Use the namespaced skill form from [Usage](#usage), rather than selecting the unrelated native Goal command.
257
257
  - **Plugin is installed and enabled but hooks do not fire:** start a fresh task or fully restart Codex, run `/hooks`, and review the trust state. Installation does not imply hook approval.
258
+ - **No import-probe warning after an edit:** the post-edit import probe is currently **unwired in Codex**. Reinstalling the same bundle does not add it. Use normal project lint and tests; see [the probe's harness wiring and ESLint requirements](USER-GUIDE.md#import-probe-warnings-and-missing-eslint).
258
259
  - **Other pre-public plugin/config/cache/hook-state residue is suspected:** this state is unsupported. Do not modify private Codex files. File an issue with `codex --version`, `codex plugin list --available --json`, and `codex plugin marketplace list --json` output so the public recovery path can be diagnosed.
259
260
  - **Agent dispatch fails:** verify Codex multi-agent support and inspect the bundled or project-level role TOMLs.
260
261
  - **Hooks report that Node is unavailable:** expose Node 24+ on the Codex hook PATH or set `SO_NODE_BIN` to the absolute Node executable.
@@ -56,7 +56,7 @@ Codex uses the curated six-event project subset `SessionStart`, `PreToolUse`, `P
56
56
  - **Cursor:** `.cursor-plugin/plugin.json` registers canonical skills and `.mcp.json` using Cursor's native manifest format. Additional native component discovery is explicitly disabled; `scripts/cursor-install.mjs` supplies the existing command and hook adapters. The former standard root manifest was moved to prevent [Codex manifest interception](codex-setup.md#manifest-compatibility).
57
57
  - **Pi:** `package.json` `pi` manifest, `pi/extensions/session-orchestrator.ts` bridge, `hooks/hooks-pi.json`, `scripts/pi-install.mjs`.
58
58
  - **Portable cross-harness surface (generated, never hand-edited):** root `AGENTS.md` (byte-identical copy of `CLAUDE.md`) and `.agents/skills/<name>/SKILL.md` — mirrors carrying only spec-legal frontmatter plus a pointer body. These two surfaces are written by `scripts/generate-agents-skills.mjs` and drift-checked via its `--check` form inside `scripts/validate-plugin.mjs`. Native plugin manifests are maintained separately, with versions updated by `scripts/release.mjs`.
59
- - **Scripts:** deterministic CLI tools (parse-config, run-quality-gate, validate-wave-scope, validate-plugin, token-audit, autopilot) plus shared lib under `scripts/lib/*.mjs`, all covered by the vitest suite.
59
+ - **Scripts:** deterministic CLI tools (parse-config, run-quality-gate, validate-wave-scope, validate-plugin, token-audit, autopilot, session-shape) plus shared lib under `scripts/lib/*.mjs` — e.g. `session-shape.mjs` (the one wave-shape resolver for `/session` mode + `--profile`), `maintenance-due-banner.mjs` (the single session-start probe for the whole maintenance loop), `session-end/tail-runner.mjs` (the mechanical apply-half of Phase 3.6.4's Expired-Learnings Sweep), `issue-budget-reconcile.mjs` (close-time recorded-vs-charged cross-check), and `telemetry/pricing.mjs` (per-model USD-per-token rates for cost estimation) — all covered by the vitest suite.
60
60
 
61
61
  ## `/harness-audit` — Anthropic large-codebase rubric
62
62
 
@@ -88,7 +88,7 @@ Both [`maestro-orchestrate`](https://github.com/josstei/maestro-orchestrate) and
88
88
 
89
89
  | Axis | session-orchestrator | maestro-orchestrate |
90
90
  |---|---|---|
91
- | Execution model | 5 typed waves (Discovery Impl-Core Impl-Polish Quality Finalization) with inter-wave quality gates and confidence-scored session-reviewer | 4-phase sequential model with parallel subagents |
91
+ | Execution model | typed waves resolved from the session mode by `scripts/session-shape.mjs` (housekeeping 1 · feature 3 · deep 5 · ultradeep 7) with inter-wave quality gates and confidence-scored session-reviewer | 4-phase sequential model with parallel subagents |
92
92
  | Runtime coverage | Claude Code + Codex CLI + Cursor IDE + Pi (4) | Gemini CLI + Claude Code + Codex + Qwen Code (4) |
93
93
  | VCS integration | GitLab + GitHub (auto-detected); hook events + commands wire to both | Runtime-agnostic; VCS work delegated to user |
94
94
  | Cross-session learning | Confidence-scored entries surfaced at session-start; opt-in `/evolve` review | Session archival without explicit learning extraction |
@@ -96,5 +96,6 @@ Switch freely between Claude Code, Codex, and Cursor on the same project — all
96
96
  - **`/session` missing**: Confirm `.cursor/commands/session.md` exists. Reload Cursor. If this is another project, re-run `node scripts/cursor-install.mjs .`
97
97
  - **Rules not loading**: Ensure `.cursor/rules/` exists with `.mdc` files; `000-session-orchestrator.mdc` has `alwaysApply: true`
98
98
  - **Hooks not firing**: Confirm `.cursor/hooks.json` exists. Open Cursor Settings → Hooks. Restart Cursor if it was open during install.
99
+ - **No import-probe warning after an edit**: Cursor wires the probe through `postToolUse` and `afterFileEdit`; it still needs an eligible file and ESLint with the project's `no-undef` rule enabled for C1. See [import-probe coverage and ESLint troubleshooting](USER-GUIDE.md#import-probe-warnings-and-missing-eslint).
99
100
  - **`'node' not found` in hook PATH**: See README troubleshooting. `hooks/run-node.sh` resolves Homebrew/nvm Node.
100
101
  - **State files not created**: Check `.cursor/` is writable
@@ -121,7 +121,10 @@ namespace we own. The validator + regex live in `scripts/lib/events-schema.mjs`
121
121
  | `orchestrator.reconcile.completed` | `scripts/lib/reconcile/engine.mjs` (`emitReconcileCompleted`, called from the thin `runReconcile` wrapper; name const `RECONCILE_EVENT`) | one record per `runReconcile` call (#1192) — from the WRAPPER, so all three return points are covered: the empty short-circuit, the normal tail, and the never-throws catch. An inline emit would have missed two of them, including the empty corpus and the error path — the two runs an operator most needs recorded (`.claude/rules/host-resources.md` § HR-105). Same shape as `narrative_mirrored`'s wrapper, and the emit is try/catch-wrapped because `emitEvent` THROWS `EventValidationError`, which would otherwise break `runReconcile`'s never-throws contract. **Payload, all ALWAYS present including as `0`** (each was measured over the whole run, like `mirror_run_completed`'s counters): `trigger` (`skill` | `session-end` | `phase-skip` | `unknown` — written always, so the per-trigger denominator is complete; the two markdown callers depend on a coordinator passing it, hence the honest `unknown` default), `dry_run`, `learnings_total`, `eligible`, `proposals`, `rejected`, `capped`, `already_materialized` (`summary.alreadyMaterialized` — the REAL idempotent-skip count, #484), `written`, `duration_ms`. **Optional, absent-is-not-zero:** `targets` (the caller's effective target list; absent ⇒ none asserted), `store_records_dropped` (`summary.skipped` — absent ⇒ the candidate store was never INSPECTED: under `dryRun`, on the empty short-circuit, on the error path; a `0` there would be a false all-clear), and `aborted: 'engine-error'` + `reason` (clamped to 300 chars) — present ONLY when the never-throws guard fired; their absence means "ran to the end", never "unknown". **`dry_run` is the discriminator, not the event's absence:** the `phase-skip` caller runs dry on EVERY close and is the highest-volume trigger, so consumers filter `dry_run: false` for real runs. **A missing `repoRoot` SKIPS the emit with a stderr WARN** rather than falling back to `SO_PROJECT_DIR` — most engine tests pass none, and the fallback would append synthetic records to the real fleet ledger on every `npm test` (#1119, same contract as `express_path.evaluated` and `narrative_mirrored`); a regression test pins it. **Engine identities — the counters are NOT a flat partition, and a live payload reads as inconsistent without them** (source: `scripts/lib/reconcile/engine.mjs:64-83`): `learnings_total === proposals + rejected`, and `capped` + `already_materialized` are DIAGNOSTIC SUB-COUNTS *inside* `rejected`, not siblings of it — each capped or already-materialized learning is also counted as rejected. Within the eligible set: `eligible − proposals − capped === already_materialized`. Worked against a live record: `learnings_total 164 = proposals 10 + rejected 154`, and `eligible 102 − proposals 10 − capped 72 = already_materialized 20` — with `capped 72` and `already_materialized 20` both sitting inside those 154. `already_materialized` is computed BEFORE the volume brake, so a terminal learning never consumes a new learning's quota. `session_id` / `semantic_session_id` / `wave` / `schema_version` are stamped by `emitEvent()` |
122
122
  | `orchestrator.evolve.completed` | `scripts/lib/learnings/evolve-telemetry.mjs` (`emitEvolveCompleted`), called from `scripts/sweep-expired-learnings.mjs`'s `--prune --apply` exit path (#1206) for the success form, and from `skills/evolve/SKILL.md` Phase 1 (persistence/no-session-data aborts, skill-prose via `scripts/emit-event.mjs` — no mechanical pipeline call site precedes either gate) for the two abort forms | once per `/evolve analyze` run (default mode; #1200, mechanized #1206) — the success form is now the SAME command that performs the Step 3.5(5) store write, so the event can no longer be forgotten independently of the write it reports on (previously a separate `emit-event.mjs` call in skill prose, one edit away from drifting out of sync). Until #1200 `/evolve` reported completion in prose only — the whole class of `orchestrator.evolve.*` / `orchestrator.dialectic.*` events was **0 records across 164k fleet events** despite every run reporting success. **Payload (success), all FOUR counters ALWAYS present including as `0`** (same contract as `mirror_run_completed`'s counters): `appended` (new learnings written, Step 3.5(4)), `boosted` (existing learnings reinforced, Step 3.5(2)), `pruned` (`$PRUNE.archived` — this SAME call's own returned `archived` total, across every `_archive_reason`), `promoted` (always `0` from THIS call site — promotion to `public` scope is a separate CLI, `npm run share:hw-learnings -- --promote`, never invoked by `/evolve analyze` itself), `duration_ms`. **Optional, absent-is-not-zero:** `skipped` (HR-105 — an array of optional-step slugs, e.g. `skill-evolution-off` \| `vault-mirror-off`, that RAN but were themselves skipped this run; present only when non-empty, and distinct from the `aborted` form below — "ran, a step inside it skipped" is not "did not run at all"). **Payload (abort):** `aborted` (`persistence-disabled` \| `no-session-data`), `reason` (the abort message shown to the user, clamped to 300 chars), `duration_ms`. A `--prune --dry-run` preview run emits NOTHING — a preview never wrote anything, so it must not report a completed run either; `emitEvolveCompleted()` also refuses to emit (stderr WARN, never a throw) without an explicit `repoRoot`, same #1119 fail-closed contract as `emitReconcileCompleted` |
123
123
  | `orchestrator.dialectic.completed` | `scripts/lib/learnings/evolve-telemetry.mjs` (`recordDialecticRun`), called from `scripts/dialectic-deriver.mjs`'s `runDialecticDeriver()` for its FOUR return-based outcomes (`empty-input`, `budget-exceeded`, `would-empty-card`, and the dry-run `ok` success form — all four are values the function itself returns, so it records each one mechanically at the exact return point), and from `skills/evolve/SKILL.md` Step 6.4's apply branch / Step 6.5's two THROWN outcomes (`unknown-model` from `validateModel()`, `subagent-crash` from a failed `dispatchAgent`/`Agent()` call — neither is a `runDialecticDeriver()` return value, so only the skill-prose catcher one layer up can record them; apply-mode success similarly needs the post-merge `mergePeerCard()` stats `runDialecticDeriver()` does not have) | once per `/evolve --dialectic` run (#1200, mechanized #1206). Same fleet-zero gap as `evolve.completed` above, now closed the same way: the pipeline function records itself for every outcome it alone can determine, and only the two throw-based aborts (of Step 6.5's five-slug abort enum) plus apply-mode's merge-dependent success remain a skill-prose call site. **Payload (success):** `mode` (`dry-run` \| `apply`), `user_deltas`, `agent_deltas` (dry-run: `countManagedSections()` on the proposed diff text — a pure helper counting `<!-- BEGIN MANAGED: … -->` sentinels, or 1 for a bare full-body replacement with none; apply: `mergePeerCard()`'s own `stats.replaced + stats.appended` per target), `tokens_in`, `tokens_out`, `duration_ms`. **Payload (abort):** `aborted` (`unknown-model` \| `budget-exceeded` \| `would-empty-card` \| `empty-input` \| `subagent-crash`), `duration_ms`. Same #1119 refusal as `emitEvolveCompleted` — `recordDialecticRun()` skips the emit (stderr WARN) without an explicit `repoRoot` |
124
- | `orchestrator.dialectic.nudge_decided` | `scripts/lib/auto-dialectic.mjs` (`decideAndRecordAutoDialectic`, wrapping `shouldDispatchAutoDialectic` with an additive `emitEvent` call) | once per `decideAndRecordAutoDialectic()` call made with `record !== false` (#1200 part c) the MECHANICAL sibling of the two skill-prose events above: those two cover a manual `/evolve` run, this one covers the session-end Phase 3.6.7 auto-dialectic NUDGE DECISION itself, which previously computed a trigger every close and recorded nothing. Emitted on ALL FOUR `shouldDispatchAutoDialectic()` return paths (kill-switch, no-new-input, cadence-threshold-met, under-threshold) — try/catch-wrapped, because `emitEvent()` throws `EventValidationError` on a malformed record and a telemetry failure must never change the decision the caller already has in hand. **Payload:** `decided` (boolean, mirrors `trigger` verbatim), `reason`, `cadence`, `sessions_since`, `learnings_since`, plus `session_id`/`semantic_session_id` via `sessionAttribution` (omitted, never fabricated, without a readable `session.lock`). **Deliberately never emitted** when the caller passes `record: false` — the read-only PROBE call site (`scripts/lib/session-end/phase-skip.mjs`'s Phase-3.6.x tail-skip aggregator) keeps calling the un-wrapped `shouldDispatchAutoDialectic()` directly instead, so its own documented side-effect-free contract ("reads only … writes NOTHING") is not broken by this event's write |
124
+ | `orchestrator.dialectic.nudge_decided` | **HISTORICAL no emission since 2026-09-09.** The producer wrapper (`decideAndRecordAutoDialectic` in `scripts/lib/auto-dialectic.mjs`, #1200 part c) was REMOVED in #1288 after its only caller session-end Phase 3.6.7 was retired; the session-start `maintenance-due` probe (`scripts/lib/maintenance-due-banner.mjs`) reads the side-effect-free `shouldDispatchAutoDialectic()` instead. | never no emitter exists. Row kept so records written before 2026-09-09 stay readable. **Payload (historical):** `decided` (boolean, mirrored `trigger`), `reason`, `cadence`, `sessions_since`, `learnings_since`, plus `session_id`/`semantic_session_id` via `sessionAttribution`. |
125
+ | `orchestrator.learnings.sweep_applied` | `scripts/lib/session-end/tail-runner.mjs` (`runExpiredSweep`, emit in `emitSweepApplied`; name const `SWEEP_EVENT`) | once per APPLIED session-end Phase 3.6.4 Expired-Learnings Sweep — emitted only on the write path, never on a plan-skip, a no-plan call, or the never-throws error branch, so a record's presence is proof the active store was actually rewritten. This event exists because the apply path did not: until #723-B4 was wired here, `sweepExpiredLearnings` had no session-end caller at all (census 2026-09-09 — definition, the `dryRun: true` probe in `phase-skip.mjs`, the standalone CLI, tests), so 0 sweeps were ever applied across three consumer repos while 628 learnings stayed resident. The sweep CLI (`scripts/sweep-expired-learnings.mjs:210`) deliberately emits nothing, and `orchestrator.evolve.completed` covers only the `--prune --apply` sibling — this is the ONLY record of a time-driven sweep. **Payload:** `scanned` (entries read from `learnings.jsonl`), `archived` (entries moved to `learnings-archive.jsonl`; a measured `0` is emitted, since the planner can legitimately RUN on a fail-open probe-error), `source` (always `session-end-3.6.4`, separating this producer from any future one), plus `session_id` / `semantic_session_id` via `sessionAttribution(repoRoot)` — the SAME root the record is pinned to via `emitEvent(..., {repoRoot})`, both keys OMITTED rather than fabricated when no `session.lock` is readable. Emission is best-effort and wrapped in its own catch: a telemetry failure never changes the sweep's return value, and — like the sweep itself — can never block a session close. |
126
+ | `orchestrator.session.shape_resolved` | `scripts/lib/session-shape.mjs` (`resolveAndRecordSessionShape`, emit in `_emitShapeResolved`; name const `SESSION_SHAPE_EVENT`), reachable as an entrypoint via `scripts/session-shape.mjs` | once per session-shape resolution, at the moment the coordinator turns the confirmed mode into an execution plan. Until this event existed the shape lived in PROSE at 27 sites contradicting each other in 8 answers (measured 2026-09-09), so "how many waves did this session actually run" was unanswerable from the ledger — which is why 6 consumer-repo `housekeeping` sessions ran the full 5-wave deep shape unnoticed. **Payload:** `session_type` (always), `total_waves`, `waves_config_honored` (false exactly when the ultradeep profile ignored the Session Config `waves` value — the fixed 7-wave shape of `skills/session-plan/SKILL.md` § Role-to-Wave Mapping; PRD AC-9's `waves < 7` rejection was dropped 2026-09-09), `discovery`, `agent_caps` (one clamped cap per wave, in wave order — `min(tier raw, agents-per-wave)`, `0` on a coordinator-direct wave), `coordinator_direct_waves` (the 1-based `n` of every coordinator-direct wave; `[]` is a MEASURED empty list, not an omission — a housekeeping shape must read `[1]` and a plain deep shape `[]`), `shape_version` (the `SESSION_SHAPE_VERSION` contract the record was produced under). **Optional, absent-is-not-zero:** `session_profile` (OMITTED, never `null`/`''`, when the session has no profile — a written null would read as "measured, no profile"; value set is the closed `VALID_SESSION_PROFILES`), `task_count`, plus `session_id`/`semantic_session_id` via `sessionAttribution(repoRoot)` — the SAME root the record is pinned to, omitted rather than fabricated without a readable `session.lock`. **A missing `repoRoot` SKIPS the emit with a stderr WARN** rather than falling through to `SO_PROJECT_DIR` (#941), and `--no-event` skips it entirely so a planning dry-run cannot record a session that never ran. `events.mjs` is imported lazily so a pure-resolver consumer does not gain `platform.mjs` and its module-load filesystem walk-ups |
127
+ | `orchestrator.issue_budget.reconciled` | `scripts/lib/issue-budget-reconcile.mjs` (`reconcileIssueBudget`, emit in `emitIssueBudgetReconciled`; name const `ISSUE_BUDGET_RECONCILED_EVENT`) | once per session close — the cross-check between what the session RECORDED as created (`record.issues_created.length`) and what the issue-budget ledger CHARGED. It exists because `readBudgetState` returns a ZEROED state for a MISSING counter file, so "the hook never ran for a single create" and "the session created nothing" are byte-identical in its return value; measured 2026-09-09 on a real session record with **26** recorded creations, **0** charged and no counter file under either accounting key. **Payload:** `verdict` (always — `match` \| `escaped` \| `no-ledger` \| `stale-record`; `no-ledger` is the absent-ledger case above, `stale-record` the inverse), `recorded`, `charged`, `exempt`, `overflow`, `escaped` (= `max(0, recorded − charged − exempt)`), and `ledgers` — one record per accounting key looked up (`key`: `semantic`\|`raw`, `path` — **repo-RELATIVE** `.orchestrator/runtime/issue-budget/<hash>.json`, never absolute: this payload also travels over the optional Clank webhook with no redaction, and an absolute ledger path names the operator's home directory and the private repo slug; the absolute form stays in the local WARN text only, `found`, `charged`, `exempt`); a fifth verdict `corrupt-ledger` marks a file that exists but has a non-integer `count`. **BOTH keys are read and SUMMED**, because the accounting key is semantic only when `current-session.json` verified the raw id (`resolveIssueBudgetSessionId`) — measured in one consumer repo: 25 of 36 counter files keyed semantic, 11 keyed raw, so reading one key reports a phantom escape for every session that used the other. `found` is measured with `existsSync` BEFORE the read and is the only thing that separates `no-ledger` from a real zero — read an absent `found` as "not measured", never as "no spend". Plus `session_id`/`semantic_session_id` via `sessionAttribution`. **A missing `repoRoot` SKIPS the emit with a stderr WARN** rather than falling through to `SO_PROJECT_DIR` (#941); `events.mjs` is imported lazily so no consumer of the reconcile module gains `platform.mjs`'s module-load filesystem walk-ups |
125
128
 
126
129
  Non-orchestrator names still present in the stream: `tmux-layout.{invoked,completed,degraded}`
127
130
  (tmux-layout skill) and `stagnation_detected`. The latter keeps its legacy bare name
@@ -105,7 +105,7 @@ to remove.
105
105
  Note the shape of the mistake, because this repo already has a rule for it: a census
106
106
  keyed on the payload (`loadApplicableRules` in `*.mjs`) misses every consumer that
107
107
  pins only the channel (`print-applicable-rules.mjs` invoked from prose). See
108
- `.claude/rules/anti-pattern-a-protocol-migration-census-keyed-on-the-payload-misses-every-consumer-that-pins-only-the-channel-18f3d0a.md`.
108
+ `.claude/rules/measurement-discipline.md`.
109
109
 
110
110
  ### 1.3 A second delivery source outside this repo's control
111
111
 
@@ -1,4 +1,4 @@
1
- <!-- Moved in v4.0.0 from `agents/memory-proposal-collector.md` (audit 2026-09-06 § 5A). It never was a dispatchable agent — it documents a coordinator-direct flow — but Claude Code's plugin loader registers every `agents/*.md` by directory convention and the manifest's `agents` key is additive-only, so no manifest entry could exclude it. Living under `docs/` removes the registration instead of bounding it. -->
1
+ <!-- Moved in v4.0.0 from `agents/memory-proposal-collector.md` (audit 2026-09-06 § 5A). It never was a dispatchable agent — it documents a coordinator-direct flow — but Claude Code's plugin loader registers every `agents/*.md` by directory convention and the manifest's `agents` key is additive-only, so no manifest entry could exclude it. Living under `docs/` removes the registration instead of bounding it. --> <!-- path-check: historical -->
2
2
 
3
3
  # Memory Proposal Collector (Reference Documentation)
4
4
 
@@ -270,13 +270,13 @@ resolver, which is the correct failure mode.
270
270
  | `scripts/lib/memory-proposals/sink.mjs` | `promoteAndClear()` (composes `writeApproved()` + `clearProposalsJsonl()` behind the write-before-clear guard, #797/#828), `archiveRejected()` |
271
271
  | `agents/dialectic-deriver.md` | Similar coordinator-invoked pattern (compare: deriver dispatches as a subagent because it only reads files; this flow does not dispatch because it calls AUQ) |
272
272
  | `.claude/rules/ask-via-tool.md` §AUQ-004 | Authoritative rule prohibiting AUQ inside subagents |
273
- | `.claude/STATE.md` Wave History — D3 | Locked decisions: pagination=4, FIFO order, label format, decision tree |
273
+ | `.claude/STATE.md` Wave History — D3 | Locked decisions: pagination=4, FIFO order, label format, decision tree <!-- path-check: example --> |
274
274
 
275
275
  ---
276
276
 
277
277
  ## Design Decisions (Locked — D3, Wave 1)
278
278
 
279
- These decisions are recorded in `.claude/STATE.md` Wave History line D3 and are not open
279
+ These decisions are recorded in `.claude/STATE.md` Wave History line D3 and are not open <!-- path-check: example -->
280
280
  for revision within this session:
281
281
 
282
282
  - **Batch size**: 4 proposals per AUQ call (not 3, not 5).
@@ -45,7 +45,7 @@ change list: [CHANGELOG.md](../CHANGELOG.md).
45
45
  | `node scripts/fleet-instruction-scan.mjs`, `lifecycle-sim-v6.mjs`, `upload-social-preview.mjs` | **removed** | No replacement. | <!-- path-check: historical -->
46
46
  | dispatching `AGENTS.md` as an agent | **removed as a dispatch target** | It was never an agent — it is the authoring spec, now [`docs/agent-authoring.md`](./agent-authoring.md). Read it; do not dispatch it. |
47
47
  | dispatching `memory-proposal-collector` as an agent | **removed as a dispatch target** | Now [`docs/memory-proposal-flow.md`](./memory-proposal-flow.md). |
48
- | citing `skills/_shared/model-selection.md` | **removed** | No replacement; it had zero consumers. |
48
+ | citing `skills/_shared/model-selection.md` | **removed** | No replacement; it had zero consumers. <!-- path-check: historical --> |
49
49
  | Session Config `autopilot.bg-isolation` | **removed** | No replacement — delete the `autopilot:` block from your `CLAUDE.md`/`AGENTS.md`, it was the block's only field and is now inert (`scripts/parse-config.mjs` never parsed it). |
50
50
  | Session Config `webhooks.<kind>.url` | **removed** (pre-4.0.0, `ce6a28aa`) | No replacement — `scripts/lib/webhook-url.mjs` had zero callers. The still-live Clank Event Bus webhook (`CLANK_EVENT_SECRET`/`CLANK_EVENT_URL`) is unrelated and unaffected. | <!-- path-check: historical -->
51
51
 
@@ -221,7 +221,7 @@ node scripts/validate-plugin.mjs
221
221
 
222
222
  **Nothing breaks at runtime.** A citation in a consumer repo's `CLAUDE.md` (or `AGENTS.md`) to
223
223
  `daily`, `skill-creator`, `ubiquitous-language`, `contract-version-bump`, `journey-audit`,
224
- `domain-model` or `skills/_shared/model-selection.md` is prose. No loader resolves it, no hook
224
+ `domain-model` or `skills/_shared/model-selection.md` is prose. No loader resolves it, no hook <!-- path-check: historical -->
225
225
  reads it, and the session starts normally.
226
226
 
227
227
  **But `claude-md-drift-check` may flag it** as a dangling citation, and if you run it with