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
@@ -5,32 +5,38 @@
5
5
 
6
6
  ## Step 1: Detect PATH_TYPE (Silent — No User Interaction)
7
7
 
8
- Read `plan-baseline-path` from Session Config in `CLAUDE.md` (or `AGENTS.md` on Codex):
8
+ Run the dependency-free local contract reader. It uses the existing configuration
9
+ resolvers, with precedence `SO_BASELINE_PATH` → matching named baseline →
10
+ `owner.yaml` `paths.baseline-path` → committed Session Config. Paths resolve
11
+ host-locally and are never written into the contract result.
9
12
 
10
13
  ```bash
11
- BASELINE_PATH=$(grep -m1 "^plan-baseline-path:" "$REPO_ROOT/CLAUDE.md" 2>/dev/null | awk '{print $2}')
12
- # Expand leading ~ to $HOME so paths like ~/Projects/projects-baseline work correctly
13
- BASELINE_PATH="${BASELINE_PATH/#\~/$HOME}"
14
+ BOOTSTRAP_CONTRACT=$(node "$PLUGIN_ROOT/scripts/baseline-archetypes.mjs" --repo "$REPO_ROOT") || exit 2
15
+ PATH_TYPE=$(printf '%s' "$BOOTSTRAP_CONTRACT" | node --input-type=module -e '
16
+ let input = ""; for await (const chunk of process.stdin) input += chunk;
17
+ process.stdout.write(JSON.parse(input).status);
18
+ ')
19
+ export BOOTSTRAP_CONTRACT PATH_TYPE
14
20
  ```
15
21
 
16
- Decision logic (evaluated in order first match wins):
22
+ | Reader outcome | Bootstrap behavior |
23
+ |----------------|--------------------|
24
+ | No configured baseline, empty value, or missing directory | `public`; use bundled templates |
25
+ | Existing baseline with valid reduced schema v1 export | `private`; retain `archetypes` and `selected` |
26
+ | Existing baseline with missing CLI, invalid export, unsafe source, or producer failure | Abort; report the sanitized error reason |
27
+ | Valid private contract but no matching repository markers | `private`, `selected: null`; require selection for Standard/Deep |
17
28
 
18
- | Condition | PATH_TYPE |
19
- |-----------|-----------|
20
- | `plan-baseline-path` key is absent in Session Config | `public` |
21
- | `plan-baseline-path` key is present but value is empty | `public` |
22
- | Key is present, value is non-empty, AND `test -d "$BASELINE_PATH"` succeeds | `private` |
23
- | Key is present, value is non-empty, BUT path does not exist on disk | `public` |
24
-
25
- Set `PATH_TYPE = private | public`. Do not report this detection to the user — it is silent.
26
-
27
- > **Note on rules-fetch (Phase 3.5 of SKILL.md):** The optional rules-fetch step runs regardless of `PATH_TYPE`. Both private (with `plan-baseline-path` set) and public (without it) repos can opt into the fetch by setting `baseline-ref` in Session Config. The fetch is independent of how the initial scaffold was generated.
28
-
29
- ---
29
+ Lookup is offline and read-only. A broken configured contract never silently
30
+ switches to a public archetype. Missing directories retain the established
31
+ public fallback. Never guess a baseline path or fetch one automatically.
30
32
 
31
33
  ## Private Path
32
34
 
33
- When `PATH_TYPE = private`, the baseline templates are used directly. No new logic is needed here — the existing tier-template flow (`fast-template.md`, `standard-template.md`, `deep-template.md`) already calls `$BASELINE_PATH` scripts for CLAUDE.md generation and archetype file sourcing. Continue with the calling template file's steps unchanged.
35
+ Read and execute [`private-contract.md`](private-contract.md): use its selection,
36
+ staged scaffold, command/CI expectations, and S99 rule projection. Keep
37
+ `CONFIRMED_ARCHETYPE` as the validated contract ID throughout the tier flow.
38
+ The optional remote rules fetch is only a public-path opt-in; a configured
39
+ private baseline supplies selected rules locally.
34
40
 
35
41
  ---
36
42
 
@@ -38,6 +44,12 @@ When `PATH_TYPE = private`, the baseline templates are used directly. No new log
38
44
 
39
45
  When `PATH_TYPE = public`, no `projects-baseline` is available. Use the plugin-bundled templates and platform-appropriate CLAUDE.md generation described below.
40
46
 
47
+ For each file newly created by these steps, append its relative filename to the
48
+ existing `BOOTSTRAP_FILES` array. This includes instruction files, minimal
49
+ README/gitignore files and each copied template file. Preserve the accumulated
50
+ array through inherited tiers; never append a directory or an existing owner
51
+ file just because it is present.
52
+
41
53
  ### Detect Platform
42
54
 
43
55
  Read the current platform using the patterns from `skills/_shared/platform-tools.md`:
@@ -5,6 +5,20 @@
5
5
 
6
6
  Standard tier is a strict superset of Fast tier. Execute all Fast-tier steps first, then the Standard-specific steps below.
7
7
 
8
+ Track actual newly created relative files in one `BOOTSTRAP_FILES` array across
9
+ all inherited steps. Append each file when created; preserve the array across
10
+ repeated steps. Never append an existing owner file or a directory.
11
+
12
+ ## Private Contract Preparation
13
+
14
+ When `PATH_TYPE = private`, resolve `CONFIRMED_ARCHETYPE` with
15
+ `private-contract.md` and execute its Scaffold section **before** the inherited
16
+ Fast steps. Preserve its rendered metadata, manifests, source, README and CI.
17
+ Add its reported created files to `BOOTSTRAP_FILES`. Skip the four public
18
+ archetype sections below, then continue at Step 3a, S99 and the lock/quality-policy
19
+ steps. Populate command expectations from the selected contract as described
20
+ in `private-contract.md`.
21
+
8
22
  ## Step 1–7: Execute Fast Tier
9
23
 
10
24
  Read and execute `skills/bootstrap/fast-template.md` Steps 1–7 in full. Do not skip any step. The Fast commit (`chore: bootstrap (fast)`) is NOT made — Fast steps produce files only; the single commit happens at Standard Step 7 below.
@@ -21,9 +35,9 @@ Before executing the stack-specific steps, resolve the final archetype:
21
35
  ARCHETYPE = CONFIRMED_ARCHETYPE # set by SKILL.md from intensity-heuristic or user selection
22
36
  ```
23
37
 
24
- Valid values: `static-html` | `node-minimal` | `nextjs-minimal` | `python-uv`
38
+ For `PATH_TYPE = public`, valid values are `static-html` | `node-minimal` | `nextjs-minimal` | `python-uv`. Private IDs come only from the validated contract.
25
39
 
26
- If `ARCHETYPE` is `null` or unset at this point, default to `node-minimal`.
40
+ On the public path only, if `ARCHETYPE` is `null` or unset, default to `node-minimal`. A private missing/invalid selection aborts before scaffolding; it never reaches the public sections.
27
41
 
28
42
  The sections below are conditional on `ARCHETYPE`. Execute only the section that matches.
29
43
 
@@ -592,12 +606,11 @@ indent_size = 2
592
606
 
593
607
  Canonical implementation in [`_shared-template.md#parallel-sessions-rule`](_shared-template.md).
594
608
 
595
- Run `node "$PLUGIN_ROOT/scripts/lib/rules-sync.mjs" --repo-root "$REPO_ROOT"` it vendors every
596
- rule registered in `rules/_index.md` (idempotent: missing→create, identical→skip, differs→overwrite),
597
- and it is the only writer that applies the pre-write validator, the basename-collision guard and the
598
- copy-on-write branch that preserves repo-private rules. The shared partial also runs
599
- `cp "$PLUGIN_ROOT/templates/_shared/loop.md" "$REPO_ROOT/.claude/loop.md"` so bare `/loop` gets a
600
- repo-aware maintenance prompt. See shared partial for full shell command. Issues #155, #633, #1060.
609
+ Execute the shared partial's `syncBootstrapRules` action. It supplies private
610
+ required basenames to `scripts/lib/rules-sync.mjs`, preserving its manifest,
611
+ pre-write validation and local-override checks. The partial appends actual newly
612
+ created rule paths, and creates `.claude/loop.md` only when missing. See the
613
+ shared partial for the executable shell command. Issues #155, #633, #1060.
601
614
 
602
615
  Note: Runs before S99. S99 no longer fetches `parallel-sessions.md` from the baseline — that entry
603
616
  was removed from the S99 manifest in #1060. The baseline copy carries no provenance header, so
@@ -611,8 +624,10 @@ Create the metrics directory and an empty learnings file so the `/evolve` skill
611
624
 
612
625
  ```bash
613
626
  mkdir -p "$REPO_ROOT/.orchestrator/metrics"
614
- [[ -f "$REPO_ROOT/.orchestrator/metrics/learnings.jsonl" ]] || \
627
+ if [[ ! -e "$REPO_ROOT/.orchestrator/metrics/learnings.jsonl" && ! -L "$REPO_ROOT/.orchestrator/metrics/learnings.jsonl" ]]; then
615
628
  : > "$REPO_ROOT/.orchestrator/metrics/learnings.jsonl"
629
+ BOOTSTRAP_FILES+=(.orchestrator/metrics/learnings.jsonl)
630
+ fi
616
631
  ```
617
632
 
618
633
  **Idempotent.** Re-running bootstrap does not overwrite an existing file.
@@ -624,11 +639,13 @@ mkdir -p "$REPO_ROOT/.orchestrator/metrics"
624
639
 
625
640
  Canonical implementation in [`_shared-template.md#baseline-fetch`](_shared-template.md).
626
641
 
627
- OPT-IN: only fires when `baseline-ref` is in Session Config, `GITLAB_TOKEN` is set, and
642
+ PRIVATE: applies the selected local contract rule union without network access, preserving existing files and excluding every plugin-owned basename. An invalid contract aborts.
643
+
644
+ PUBLIC OPT-IN: only fires when `baseline-ref` is in Session Config, `GITLAB_TOKEN` is set, and
628
645
  `scripts/lib/fetch-baseline.mjs` exists. Fetches `.claude/rules/*.md` from the baseline GitLab
629
646
  project (default project 52) and writes `.claude/.baseline-fetch.lock`. Does NOT abort on failure.
630
- The rule manifest includes `owner-persona.md` (alongside `parallel-sessions.md`, `development.md`,
631
- and the rest of the always-on rules). See shared partial for full implementation.
647
+ The private rule manifest comes from the selected contract. Every plugin-owned
648
+ basename is excluded from either delivery path. See the shared partial.
632
649
 
633
650
  ---
634
651
 
@@ -649,6 +666,8 @@ Write `.orchestrator/bootstrap.lock` **atomically** (mktemp + mv prevents a corr
649
666
  process is interrupted mid-write):
650
667
 
651
668
  ```bash
669
+ _LOCK_CREATED=false
670
+ [[ -e "$REPO_ROOT/.orchestrator/bootstrap.lock" || -L "$REPO_ROOT/.orchestrator/bootstrap.lock" ]] || _LOCK_CREATED=true
652
671
  _LOCK_TMP=$(mktemp "$REPO_ROOT/.orchestrator/bootstrap.lock.XXXXXX")
653
672
  cat > "$_LOCK_TMP" << LOCK
654
673
  # .orchestrator/bootstrap.lock
@@ -661,6 +680,7 @@ plugin-version: <session-orchestrator plugin version — read from $PLUGIN_ROOT/
661
680
  bootstrapped-at: <current ISO 8601 UTC — same value as timestamp; distinct field for age-validation probe>
662
681
  LOCK
663
682
  mv "$_LOCK_TMP" "$REPO_ROOT/.orchestrator/bootstrap.lock"
683
+ if [[ "$_LOCK_CREATED" = true ]]; then BOOTSTRAP_FILES+=(.orchestrator/bootstrap.lock); fi
664
684
  ```
665
685
 
666
686
  Set `source` using the same logic as fast-template Step 5:
@@ -673,8 +693,8 @@ Set `source` using the same logic as fast-template Step 5:
673
693
 
674
694
  Canonical implementation in [`_shared-template.md#quality-gate-policy`](_shared-template.md).
675
695
 
676
- Write `.orchestrator/policy/quality-gates.json` with package-manager-detected defaults (idempotent:
677
- skip if file already exists). Uses `scripts/lib/package-manager.mjs`; falls back to npm defaults.
696
+ Write `.orchestrator/policy/quality-gates.json` from exact private contract gates,
697
+ or package-manager defaults on the public path. Skip existing owner policy.
678
698
  See shared partial for full shell command. Issue #183.
679
699
 
680
700
  <!-- @include _shared-template.md#state-md-scaffold -->
@@ -682,8 +702,9 @@ See shared partial for full shell command. Issue #183.
682
702
 
683
703
  Canonical implementation in [`_shared-template.md#state-md-scaffold`](_shared-template.md).
684
704
 
685
- Scaffold `.claude/STATE.md` from `skills/bootstrap/STATE.md.template` (idempotent: skip if
686
- already exists). On Codex CLI / Cursor IDE, substitute `.codex/` or `.cursor/` for `.claude/`.
705
+ Scaffold `.claude/STATE.md` (idempotent: skip if it already exists). <!-- path-check: example -->
706
+ Use `skills/bootstrap/STATE.md.template`.
707
+ On Codex CLI / Cursor IDE, substitute `.codex/` or `.cursor/` for `.claude/`.
687
708
  See shared partial for full shell command. Issue #184.
688
709
 
689
710
  <!-- @include _shared-template.md#agents-scaffold -->
@@ -701,15 +722,9 @@ Stage all created files and commit:
701
722
 
702
723
  ```bash
703
724
  cd "$REPO_ROOT"
704
- BOOTSTRAP_FILES=(
705
- CLAUDE.md AGENTS.md .gitignore README.md .orchestrator/bootstrap.lock
706
- .orchestrator/policy/quality-gates.json
707
- package.json pyproject.toml tsconfig.json eslint.config.mjs .prettierrc
708
- .editorconfig src/ tests/ .claude/
709
- )
710
725
  # Add only the files bootstrap created — no sweeping -u/-A to avoid catching pre-existing files
711
- for _f in "${BOOTSTRAP_FILES[@]}"; do
712
- [[ -e "$_f" ]] && git add -- "$_f"
726
+ for _f in ${BOOTSTRAP_FILES[@]+"${BOOTSTRAP_FILES[@]}"}; do
727
+ [[ -f "$_f" && ! -L "$_f" ]] && git add -- "$_f"
713
728
  done
714
729
  git commit -m "chore: bootstrap (standard)"
715
730
  ```
@@ -142,6 +142,6 @@ fpRisk: low | medium | high
142
142
 
143
143
  **Default Severity:** Per-rule (high for the absolute-ban tells, low for advisory quality nits). `fpRisk` is reported so triage can weight high-FP rules (`ai-purple-gradient`) more skeptically.
144
144
 
145
- > **Precision boundary (honest):** this regex tier cannot resolve CSS cascade, so `side-stripe-border` flags any ≥2px side accent even when the element is unrounded — `.claude/rules/frontend.md` bans those (>1px accent), but a cascade-aware detector would narrow further. The heavier static-HTML + browser tiers are deliberately omitted (cost ≫ value for a probe/hook). See `scripts/lib/frontend-detect/rules.mjs`.
145
+ > **Precision boundary (honest):** this regex tier cannot resolve CSS cascade, so `side-stripe-border` flags any ≥2px side accent even when the element is unrounded — `rules/opt-in-stack/frontend.md` bans those (>1px accent), but a cascade-aware detector would narrow further. The heavier static-HTML + browser tiers are deliberately omitted (cost ≫ value for a probe/hook). See `scripts/lib/frontend-detect/rules.mjs`.
146
146
 
147
147
  ---
@@ -8,7 +8,7 @@ Rules for mapping session scope to target audiences, content sources, and docume
8
8
 
9
9
  | Audience | Target files (globs) | Typical update triggers |
10
10
  |----------|----------------------|-------------------------|
11
- | User | `README.md`, `docs/user/**/*.md`, `docs/getting-started.md`, `examples/**/*.md` | new CLI command, breaking API change, install flow change, new user-facing feature, changed example output |
11
+ | User | `README.md`, `docs/user/**/*.md`, `docs/getting-started.md`, `examples/**/*.md` | new CLI command, breaking API change, install flow change, new user-facing feature, changed example output <!-- path-check: example --> |
12
12
  | Dev | `CLAUDE.md` (or `AGENTS.md` on Codex CLI), `docs/dev/**/*.md`, `docs/adr/**/*.md` | architecture decision, major refactor, new module/subsystem, test coverage change, dependency upgrade, ADR-worthy choice |
13
13
  | Vault/Ops | `<vault>/01-projects/<slug>/context.md`, `<vault>/01-projects/<slug>/decisions.md`, `<vault>/01-projects/<slug>/people.md` | project status change, ownership transition, stack/infra decision, cross-project dependency, migration, archival event |
14
14
 
@@ -634,7 +634,7 @@ const result = await runDialecticDeriver({
634
634
  ```
635
635
 
636
636
  ### Step 6.4: Diff Output & Apply Gate
637
- - If dry-run (default): present diff inline; write to `.orchestrator/dialectic-pending.md` (atomic tmp+rename); EXIT. Suggestion: "Re-run with `/evolve --dialectic --apply` to apply."
637
+ - If dry-run (default): present diff inline; write to `.orchestrator/dialectic-pending.md` (atomic tmp+rename); EXIT. Suggestion: "Re-run with `/evolve --dialectic --apply` to apply." <!-- path-check: example -->
638
638
  - If `--apply`: call `mergePeerCard(existingBody, managedUpdates)` from `scripts/lib/peer-cards/merger.mjs` for each card target, then `writePeerCard(repoRoot, 'user', mergedUserCard)` and `writePeerCard(repoRoot, 'agent', mergedAgentCard)` from `scripts/lib/peer-cards/writer.mjs`. Update the `updated:` frontmatter.
639
639
  - Report: `Dialectic-derived: M deltas to USER.md, N deltas to AGENT.md. Dry-run | Applied. Tokens: in=<X> out=<Y>.`
640
640
 
@@ -666,7 +666,7 @@ await recordDialecticRun({
666
666
  - `status: 'budget-exceeded'` → emit `{status:'budget-exceeded', used:N, budget:M}`, do NOT truncate
667
667
  - `status: 'would-empty-card'` → warn + require `--allow-emptying` flag
668
668
  - `status: 'empty-input'` → exit clean with message "dialectic: skipped (no input)"
669
- - subagent crash → log ⚠, exit cleanly (do NOT write to `.orchestrator/dialectic-pending.md`)
669
+ - subagent crash → log ⚠, exit cleanly (do NOT write to `.orchestrator/dialectic-pending.md`) <!-- path-check: example -->
670
670
 
671
671
  **Telemetry (#1200, #1206) — emitted by `scripts/dialectic-deriver.mjs` for THREE of the five
672
672
  outcomes.** `budget-exceeded`, `would-empty-card`, and `empty-input` are `runDialecticDeriver()`
@@ -111,7 +111,7 @@ done
111
111
  **Taxonomy convention — `priority` REVERSED to scoped `::` (supersedes #727 for this one axis).**
112
112
 
113
113
  - **`priority::<level>` is canonical.** #727's stated rationale was that "this repo mirrors to GitHub, which has no scoped-label semantics … while a migration would break every existing label reference and issue." Both halves were checked on 2026-07-25 and neither holds:
114
- - **Issues are not mirrored at all.** `aiat-poc-infra/docs/github-mirror-runbook.md:1,5` describes a git **push-mirror** with GitHub as "read-only downstream"; `docs/gitlab-team-org-2026-06-21.md:45` confirms there is no two-way GitLab issue sync. Nothing crosses the boundary that a label rename could break.
114
+ - **Issues are not mirrored at all.** `aiat-poc-infra/docs/github-mirror-runbook.md:1,5` describes a git **push-mirror** with GitHub as "read-only downstream"; the external team-organization audit `docs/gitlab-team-org-2026-06-21.md:45` confirms there is no two-way GitLab issue sync. Nothing crosses the boundary that a label rename could break. <!-- path-check: example -->
115
115
  - **GitHub already uses the scoped form.** `gh api "repos/AIAT-AIandBusinessgrowth/aiat-barrierefrei-engine/labels"` returns `priority::high`, `priority::low`, `priority::med`, `priority::medium` across 77 open issues, and **zero** `priority:high`. Same pattern on `aiat-doc-vlm`. GitHub treats `::` as an ordinary string; it merely does not enforce mutual exclusion.
116
116
  - Volume agrees independently: **416 `priority::` against 249 `priority:` and 7 bare** at the time of the decision. Chasing the minority spelling would mean re-labelling the majority.
117
117
  Producers were migrated FIRST (this change); the label-data migration follows separately, because migrating data before producers means the divergence returns within a day.
@@ -340,8 +340,8 @@ Bash calls when the current session contains no prior `Read` on a matching templ
340
340
  **When this matters:** before you or a subagent opens an MR, PR, or issue via CLI, a
341
341
  matching template must have been read in the current session:
342
342
 
343
- - GitHub: `.github/PULL_REQUEST_TEMPLATE.md` / `.github/ISSUE_TEMPLATE*`
344
- - GitLab: `.gitlab/merge_request_templates/Default.md` / `.gitlab/issue_templates/*`
343
+ - GitHub: `.github/pull_request_template.md` / `.github/ISSUE_TEMPLATE*`
344
+ - GitLab: `.gitlab/merge_request_templates/Default.md` / `.gitlab/issue_templates/*` <!-- path-check: example -->
345
345
 
346
346
  Accepted template paths are configured in `.orchestrator/policy/templates-policy.json`
347
347
  (versioned, operator-editable). Default behaviour:
@@ -33,7 +33,7 @@ Read `soul.md` in this skill directory before anything else. It defines WHO you
33
33
  Establish *what* you are grilling and ground yourself in the *code* before asking the user anything.
34
34
 
35
35
  1. **Resolve the target.** Parse `$ARGUMENTS`:
36
- - A file path (e.g. `docs/prd/2026-06-09-export.md`, `STATE.md`, a spec) → read it in full.
36
+ - A file path (e.g. `docs/prd/2026-06-09-export.md`, `STATE.md`, a spec) → read it in full. <!-- path-check: example -->
37
37
  - A topic/slug or empty → grill the plan or idea already present in the current conversation. If there is no plan in context, ask the user — via AUQ — to state the plan in one or two sentences before continuing.
38
38
  2. **Ground in the codebase.** Read the project's domain language if present (`CONTEXT.md`, `.orchestrator/steering/*.md`, relevant `docs/adr/*`), then Grep/Glob the areas the plan touches. Build a short mental model of what the code *actually* does today. This is what lets you run the code-contradiction tactic.
39
39
  3. **State the target back.** In 1–2 plain-text sentences, summarize what you understand the plan to be and what you've grounded it against. This catches a wrong target before you waste a grill on it.
@@ -35,8 +35,8 @@ This skill accepts two optional flags. Default (no flag) runs the interactive 4-
35
35
 
36
36
  | Flag | Behavior |
37
37
  |---|---|
38
- | `--dry-run` | Run Phases 1-3 read-only; instead of mutating MEMORY.md / topic files, write a complete-body MEMORY.md proposal (single fenced block — never a unified-diff) to `.orchestrator/pending-dream.md` (atomic). Exit 0. |
39
- | `--apply-pending` | Read `.orchestrator/pending-dream.md`; refuse if older than 14 days (`stale`) or if MEMORY.md changed since the producing --dry-run (`stale-index`, #788); apply diff; delete pending file; print `auto-dream applied: -<X> lines, +<Y> entries`. Exit 0. |
38
+ | `--dry-run` | Run Phases 1-3 read-only; instead of mutating MEMORY.md / topic files, write a complete-body MEMORY.md proposal (single fenced block — never a unified-diff) to `.orchestrator/pending-dream.md` (atomic). Exit 0. <!-- path-check: example --> |
39
+ | `--apply-pending` | Read `.orchestrator/pending-dream.md`; refuse if older than 14 days (`stale`) or if MEMORY.md changed since the producing --dry-run (`stale-index`, #788); apply diff; delete pending file; print `auto-dream applied: -<X> lines, +<Y> entries`. Exit 0. <!-- path-check: example --> |
40
40
 
41
41
  Flags are mutually exclusive — passing both is an error. Absence of both = legacy interactive mode (Phases 1-4 below).
42
42
 
@@ -40,6 +40,15 @@ Agent({ subagent_type: "Explore", description: "Check ecosystem for conflicts",
40
40
  5. **Core problem being solved** — Open-ended. Claude suggests structure if answer is vague.
41
41
  6. **GitLab group** — Select the GitLab host explicitly, then discover available groups dynamically. Run `ls $BASELINE_PATH/templates/` for project types, and check for a groups config in `$BASELINE_PATH/config/` or run `glab api --hostname "$GITLAB_HOST" "groups?per_page=100&min_access_level=10"` to discover GitLab groups — read each entry's `full_path` field. (`glab` has no `group` subcommand at all — invoking one exits 1 with `Unknown command "group"`.) Present findings via AskUserQuestion.
42
42
 
43
+ ### Optional private capability context — after Wave 1
44
+
45
+ Before Wave 2 research, apply [Private capability context](../_shared/private-capability-context.md)
46
+ only when the owner has explicitly supplied or authorized a local catalog lookup
47
+ for an explicitly private/internal planning audience. Use eligible findings to
48
+ inform the existing shared-patterns research and reuse alternatives; keep the
49
+ archetype research and questions below. With no authorized context, or with a
50
+ public/unknown audience, skip this optional step without a prompt or a lookup.
51
+
43
52
  ### Wave 2 — Technical Details (5 questions, dynamic per archetype)
44
53
 
45
54
  **Pre-wave agents:**
@@ -238,7 +238,7 @@ Exit after printing. Do not proceed to Phase 4.
238
238
 
239
239
  ## Phase 4: Write Pending Sidecar (Normal Mode Only)
240
240
 
241
- Write the proposals to `.orchestrator/metrics/reconcile-pending.md` as a human-readable
241
+ Create the runtime proposal sidecar `.orchestrator/metrics/reconcile-pending.md` as a human-readable <!-- path-check: example -->
242
242
  record before presenting the AUQ. This sidecar is informational only — it lets the operator
243
243
  see the full proposal set in an editor alongside the AUQ prompt.
244
244
 
@@ -280,8 +280,8 @@ Present to the user the **Session Summary**: Completed / Carried Over / Dropped
280
280
  | `references/phase-3-documentation-updates.md` | Phase 3 full procedural body — final heartbeat (#590-3), 3.0 Defensive Cleanup, 3.1 SSOT files, 3.2/3.2a docs + handover, 3.3 rules freshness, 3.4/3.4a STATE.md write + snapshot cleanup, 3.45 Telemetry Flush, 3.5/3.5a/3.6.x memory + learnings + tail dispatcher, 3.7/3.7a/3.7b/3.7c/3.7d metrics, recommendations, durable commit, vault board, session-eval |
281
281
  | `phase-3-2-docs-verification.md` | Phase 3.2 full procedural body — docs-tasks load, SESSION_START_REF, per-task loop, mode-gated report, Documentation Coverage block |
282
282
  | `learning-patterns.md` | Phases 3.5a + 3.6 extraction heuristics, confidence updates, passive decay, and JSONL write procedure |
283
- | `phase-3-6-tail.md` | Phase 3.6.x tail — full unabridged detail procedures for all six tail phases: 3.6.3 Memory-Proposals Collection (`collectProposals` + AUQ multiSelect + `promoteAndClear`, composing `writeApproved` + `clearProposalsJsonl` behind a mechanical write-before-clear guard, #828), 3.6.4 Expired-Learnings Sweep (Epic #723 B4), 3.6.5 Auto-Dream nudge (`shouldDispatchAutoDream`, #614), 3.6.6 Skill-Applied Judge (#645 L3 — `runSkillJudge`, coordinator-writes), 3.6.7 Auto-Dialectic nudge (`shouldDispatchAutoDialectic`, #614), 3.6.8 Reconciliation Rule Proposals (#696 FA3 — `runReconcile` + AUQ + `writeApprovedRules`). Loaded on demand by the SKILL.md skip-plan dispatcher (#724) — only phases with `run: true` in the `planTailPhases()` plan execute |
284
- | `scripts/lib/session-end/phase-skip.mjs` | Phase 3.6.x tail skip-plan aggregator (#724) — `planTailPhases({repoRoot, config, sessionId, platform})` → `{plan, skippedReport}`; side-effect-free (reconcile/sweep via dry-run — no writes), never-throws (per-phase probe error fail-opens to `run: true`); wraps the six existing signal helpers with config gates first, then input detection |
283
+ | `phase-3-6-tail.md` | Phase 3.6.x tail — detail procedures for the tail phases: 3.6.3 Memory-Proposals Collection (`collectProposals` + AUQ multiSelect + `promoteAndClear`, composing `writeApproved` + `clearProposalsJsonl` behind a mechanical write-before-clear guard, #828), **3.6.4 Expired-Learnings Sweep — MECHANICAL since 2026-09-09**: after `planTailPhases()`, call `runTailPhases({ repoRoot, plan })` from `scripts/lib/session-end/tail-runner.mjs` (delegating to `runExpiredSweep`) and report `result['3.6.4']` (`ran`, `scanned`, `archived`); the event `orchestrator.learnings.sweep_applied` is the proof it ran (Epic #723 B4), **3.6.5 Auto-Dream RETIRED** and **3.6.7 Auto-Dialectic — RETIRED** (both replaced by the session-start `maintenance-due` probe, `checkMaintenanceDue` in `scripts/lib/maintenance-due-banner.mjs`; headings kept as two-line stubs because other docs cite them), 3.6.6 Skill-Applied Judge (#645 L3 — `runSkillJudge`, coordinator-writes), 3.6.8 Reconciliation Rule Proposals (#696 FA3 — `runReconcile` + AUQ + `writeApprovedRules`). Loaded on demand by the SKILL.md skip-plan dispatcher (#724) — only phases with `run: true` in the `planTailPhases()` plan execute |
284
+ | `scripts/lib/session-end/phase-skip.mjs` | Phase 3.6.x tail skip-plan aggregator (#724) — `planTailPhases({repoRoot, config, sessionId, platform})` → `{plan, skippedReport}`; side-effect-free (reconcile/sweep via dry-run — no writes), never-throws (per-phase probe error fail-opens to `run: true`). Since 2026-09-09 it plans FOUR phases, not six: the 3.6.5 (auto-dream) and 3.6.7 (auto-dialectic) deciders were removed with those phases' retirement. Its APPLY half for 3.6.4 is `scripts/lib/session-end/tail-runner.mjs` (`runTailPhases`, `runExpiredSweep`) — the planner fails OPEN, the runner fails CLOSED |
285
285
  | `references/phase-3-documentation-updates.md` § 3.45 | Telemetry Flush (advisory, #844; MECHANICAL since #1138 — `hooks/on-session-end.mjs` calls `flush()` itself at the end of every teardown and emits an `orchestrator.telemetry.flush` breadcrumb, so this phase is the DESCRIPTION and the fallback, never the trigger; a coordinator that skips it changes nothing) — `flush()` from `scripts/lib/telemetry/sync.mjs` drains the host-local send-queue fire-and-forget; no config key (send-gate is `resolveConsent()` inside the module, fail-closed); skip when `persistence: false`; never-throw + ~3s-bounded, offline → bounded oldest-dropped queue, optional `Telemetry: sent/queued/gated` close-summary line, NEVER an error banner; runs late in the close after Phase 3.7 |
286
286
  | `session-metrics-write.md` | Phase 3.7 JSONL append, vault-mirror invocation, durable narrative mirror (`mirrorNarrative`, #675), and behavior matrix |
287
287
  | `phase-3-7a-recommendations.md` | Phase 3.7a full procedural body — computeV0Recommendation call, STATE.md field write, data source guarantee, error mode |
@@ -293,6 +293,7 @@ Present to the user the **Session Summary**: Completed / Carried Over / Dropped
293
293
  | `references/phase-4a-worktree-cleanup.md` | Phase 4a full procedural body — auto-promoted-worktree detection (`detectAutoPromotedWorktree`, marker-keyed since #1069), clean-check, clean auto-remove path, dirty 3-option AUQ (`Behalten`/`Löschen`/`Manuell`), PSA-003 + #490 ordering rationale |
294
294
  | `references/phase-4b-worktree-orphan-sweep.md` | Phase 4b full procedural body — `checkWorktreeOrphans()` read-only proposal set, the coordinator-rendered AUQ, opt-in `worktree-orphans.enabled` gate |
295
295
  | `references/phase-5-issue-cleanup.md` | Phase 5 full procedural body — close resolved issues (`stripStatusLabels`, #308), Step 3 filing of the Phase 1.65 carry-list incl. the deferred `createSpiralCarryoverIssue` and `markOpenQuestionAnsweredOnDisk`, Step 3b `[Backlog-Sammel]` overflow, discovery-issue creation |
296
+ | `references/phase-5-issue-cleanup.md` § Step 3b.2 (issue-budget reconcile) | Phase 5 issue-budget cross-check — `reconcileIssueBudget({ repoRoot, record, sessionId, rawSessionId, config })` from `scripts/lib/issue-budget-reconcile.mjs` runs on the in-memory session record BEFORE it is appended to `sessions.jsonl`, `emitIssueBudgetReconciled` records `orchestrator.issue_budget.reconciled`, and `formatIssueBudgetReconcileWarn(result)` prints the verdict (`match` / `no-ledger` / `escaped` / `stale-record`) in the Final Report. Ordered AFTER the overflow drain (which resets `overflow[]`) and BEFORE `reapStaleBudgetFiles` (which must not remove the file being read) |
296
297
  | `references/session-summary-template.md` | Phase 6 Final Report — the full Session Summary template (Completed / Carried Over / Dropped at Handover Gate / New Issues / Unresolved Review Findings / Metrics incl. Docs Health + Custom Phases / Next Session Recommendations) plus the Test-delta and Documentation-Coverage anchors |
297
298
 
298
299
  ## Anti-Patterns
@@ -57,7 +57,7 @@ The following mini-table mirrors `skills/docs-orchestrator/audience-mapping.md`
57
57
 
58
58
  | Audience | Target file patterns |
59
59
  |----------|----------------------|
60
- | `user` | `README.md`, `docs/user/**/*.md`, `docs/getting-started.md`, `examples/**/*.md` |
60
+ | `user` | `README.md`, `docs/user/**/*.md`, `docs/getting-started.md`, `examples/**/*.md` <!-- path-check: example --> |
61
61
  | `dev` | `CLAUDE.md` (or `AGENTS.md` on Codex CLI), `docs/dev/**/*.md`, `docs/adr/**/*.md` |
62
62
  | `vault` | `<vault>/01-projects/<slug>/context.md`, `<vault>/01-projects/<slug>/decisions.md`, `<vault>/01-projects/<slug>/people.md` |
63
63
 
@@ -11,7 +11,7 @@
11
11
  > - `memory.proposals.enabled` is `false` (default: `true`)
12
12
  > - `.orchestrator/metrics/proposals.jsonl` does not exist OR contains zero entries
13
13
 
14
- After learnings are written (Phase 3.6) and BEFORE auto-dream dispatch (Phase 3.6.5), collect agent-proposed memory entries written during this session and present them to the operator via `AskUserQuestion` multiSelect. Approved entries flow to `learnings.jsonl` with `_provenance: agent-proposed@<wave-id>`. Rejected entries are archived to `.orchestrator/proposals.rejected.log`.
14
+ After learnings are written (Phase 3.6) and BEFORE the Skill-Applied Judge (Phase 3.6.6 — Phase 3.6.5 is retired), collect agent-proposed memory entries written during this session and present them to the operator via `AskUserQuestion` multiSelect. Approved entries flow to `learnings.jsonl` with `_provenance: agent-proposed@<wave-id>`. Rejected entries are archived to `.orchestrator/proposals.rejected.log`.
15
15
 
16
16
  The proposals queue is populated mid-session by wave-executor agents calling `node scripts/memory-propose.mjs --type ... --subject ... --insight ... --evidence ... --confidence ...`. The CLI enforces:
17
17
  - Quota per wave (default 5, configurable via `memory.proposals.quota-per-wave`)
@@ -138,40 +138,29 @@ The proposals queue is populated mid-session by wave-executor agents calling `no
138
138
 
139
139
  > Best-effort, non-blocking. Skip silently if the sweep script errors or `.orchestrator/metrics/learnings.jsonl` is absent.
140
140
 
141
- After learnings are written (Phase 3.6), run `node scripts/sweep-expired-learnings.mjs --json` (dry-run) against the learnings store. If the summary reports `archived > 0`, follow with `node scripts/sweep-expired-learnings.mjs --apply --json` to move the stale-past-grace entries into `.orchestrator/metrics/learnings-archive.jsonl` (append-only, never deleted). Note the resulting counts for the Phase 6 Final Report; any error surfaces on stderr with a non-zero exit (`1` usage error, `2` sweep failure) and never blocks close — the CLI does not write to `.orchestrator/metrics/sweep.log` (that path is the session-registry's own sweep log, unrelated to this CLI).
141
+ **MECHANICAL since 2026-09-09.** This phase is no longer a two-command prose recipe ("run `--json`, then `--apply --json` when `archived > 0`") that recipe was the reason the apply path had ZERO session-end callers: measured across three consumer repos, 0 sweeps had ever been applied and 628 learnings were resident in the active stores. The dry-run decision already lives in `planTailPhases()`; the APPLY half now lives in `scripts/lib/session-end/tail-runner.mjs`.
142
142
 
143
- ### 3.6.5 Auto-Dream Dispatch (#502, F2.2)
143
+ After learnings are written (Phase 3.6) and `planTailPhases()` has produced its `plan` (see § "Phase 3.6.x Tail — Mechanical Skip-Plan" in `references/phase-3-documentation-updates.md`), call `runTailPhases` ONCE and read the `3.6.4` slot of its keyed result:
144
144
 
145
- > Skip this phase if `memory-cleanup-threshold: 0` (kill-switch per PRD F2.2). Also skip on non-Claude-Code platforms (memory dir at `~/.claude/projects/` is Claude Code-only, mirrors Phase 3.5 gate).
145
+ ```javascript
146
+ import { runTailPhases } from '${PLUGIN_ROOT}/scripts/lib/session-end/tail-runner.mjs';
146
147
 
147
- After learnings are written (Phase 3.6), determine whether to emit a **manual-cadence nudge** to run `/memory-cleanup --dry-run` in the next session. The decision uses MEMORY.md line count and a sessions-since-last-cleanup signal. There is no `memory-cleanup` agent in the registry, so the historical auto-dream subagent dispatch never fired (see #614) — the nudge replaces it. A manually-run `/memory-cleanup --dry-run` writes a complete-replacement MEMORY.md proposal (single fenced ` ```markdown ` block — never git-style diff hunks, see #717) to `.orchestrator/pending-dream.md` for the session after that to apply via `/memory-cleanup --apply-pending`.
148
+ const tail = await runTailPhases({ repoRoot: process.cwd(), plan });
149
+ const sweep = tail['3.6.4'];
150
+ // { ran: true, scanned, archived, archivePath } | { ran: false, reason: 'plan-skip' | 'no-plan' | 'error', error? }
151
+ ```
148
152
 
149
- 1. Read `memory-cleanup-threshold` (default 5) and `memory-cleanup-soft-limit` (default 180) from `$CONFIG`.
150
- 2. Invoke `shouldDispatchAutoDream` from `scripts/lib/auto-dream.mjs`:
153
+ - `runTailPhases` delegates to `runExpiredSweep({ repoRoot, plan, now })` — the same module's single-phase entry point — and returns a KEYED shape so a caller keeps working when a second phase becomes mechanical. Today exactly one phase is: 3.6.3, 3.6.5–3.6.8 stay coordinator-executed because they are AUQ-gated or need a subagent dispatch a library function cannot make.
154
+ - **Never throws, fails CLOSED.** Any error yields `{ ran: false, reason: 'error' }` and the close proceeds. Stale-past-grace entries move into `.orchestrator/metrics/learnings-archive.jsonl` (append-only, never deleted).
155
+ - **Report** `sweep.ran`, `sweep.scanned` and `sweep.archived` in the Phase 6 Final Report, e.g. `expired-sweep: 12 archived of 640 scanned`. When `ran: false`, report the `reason` instead — a skipped sweep is a stated outcome, never silence.
156
+ - **The proof it ran is the event `orchestrator.learnings.sweep_applied`** in `.orchestrator/metrics/events.jsonl` (payload source `session-end-3.6.4`, which separates it from the standalone CLI). A close claiming a sweep with no such event did not sweep.
151
157
 
152
- ```javascript
153
- import { shouldDispatchAutoDream } from '${PLUGIN_ROOT}/scripts/lib/auto-dream.mjs';
154
- import { resolveMemoryDir } from '${PLUGIN_ROOT}/scripts/lib/memory-paths.mjs';
155
- const repoRoot = process.cwd();
156
- const memoryDir = resolveMemoryDir(repoRoot);
157
- const decision = await shouldDispatchAutoDream({
158
- repoRoot,
159
- memoryDir,
160
- threshold: config['memory-cleanup-threshold'] ?? 5,
161
- softLimit: config['memory-cleanup-soft-limit'] ?? 180,
162
- });
163
- ```
164
- 3. If `decision.trigger === false`: log `auto-dream: not triggered (${decision.reason})` and continue. Emit no nudge.
165
- 4. If `decision.trigger === true`: **do not dispatch a subagent** — there is no `memory-cleanup` agent in `agents/`, so the historical `Agent({…})` dispatch pointed at the agent name `memory-cleanup` (a subagent type that was never built) and never fired (see #614). Instead, emit a manual-cadence nudge and continue:
158
+ The standalone `node scripts/sweep-expired-learnings.mjs --apply --json` CLI remains available for manual/out-of-session use; it is no longer the session-end path.
166
159
 
167
- `auto-dream: cadence reached (${decision.reason}) — run /memory-cleanup --dry-run manually in the next session, then apply the proposal with /memory-cleanup --apply-pending.`
160
+ ### 3.6.5 Auto-Dream Dispatch (#502, F2.2) RETIRED
168
161
 
169
- The `shouldDispatchAutoDream` decision helper and `scripts/lib/auto-dream.mjs` lib stay in use: they compute the signal that drives this nudge and back the manual `/memory-cleanup` path (`writePendingDream` / `readPendingDream` / `applyPendingDream`).
170
- 5. Record the outcome (skipped / nudge-emitted) so Phase 6 Final Report can surface a line: `auto-dream: manual /memory-cleanup --dry-run recommended (cadence reached) apply with /memory-cleanup --apply-pending next session`.
171
-
172
- The pending-dream sidecar at `.orchestrator/pending-dream.md` is intentionally outside the vault tree — vault-mirror (Phase 3.7) must exclude it from its scope so the proposal survives the session close without being mirrored into 50-sessions/.
173
-
174
- Cross-reference: PRD F2.2 acceptance criteria; `scripts/lib/auto-dream.mjs` API (`shouldDispatchAutoDream`, `readDreamSignals`, `writePendingDream`, `readPendingDream`, `applyPendingDream`).
162
+ > **RETIRED 2026-09-09.** The nudge is replaced by the session-start `maintenance-due` probe (`checkMaintenanceDue`, `scripts/lib/maintenance-due-banner.mjs`), whose `memory-cleanup` signal reuses the very same `shouldDispatchAutoDream` decision a nudge emitted while the operator is closing down was read by nobody. Its decider is also gone from `planTailPhases()` in `scripts/lib/session-end/phase-skip.mjs`; the heading stays because other docs cite it.
163
+ > The housekeeping session runs `/memory-cleanup` itself (see `skills/session-start/SKILL.md` Phase 7 the maintenance loop). `scripts/lib/auto-dream.mjs` (`shouldDispatchAutoDream`, `readDreamSignals`, `writePendingDream`, `readPendingDream`, `applyPendingDream`) stays in use: the probe reads it, and `/memory-cleanup --dry-run` / `--apply-pending` still write and consume `.orchestrator/pending-dream.md`. <!-- path-check: example -->
175
164
 
176
165
  ### 3.6.6 Skill-Applied Judge (#645, L3)
177
166
 
@@ -181,7 +170,7 @@ Cross-reference: PRD F2.2 acceptance criteria; `scripts/lib/auto-dream.mjs` API
181
170
  >
182
171
  > When skipped, log `skill-judge: disabled (skill-evolution.judge=false)` (or `persistence=false`) and return. **This is the disabled-path guarantee:** with the judge off, only L1 (`skill-invocations.jsonl`, written by the PreToolUse hook) and L2 (`scripts/lib/skill-health/join.mjs`) records exist — no judgment, no error, zero L3 code executes. Do NOT import `scripts/lib/skill-judge.mjs` on the disabled path.
183
172
 
184
- After learnings are written (Phase 3.6) and the auto-dream decision is made (Phase 3.6.5), and when the judge is enabled, run a **bounded, read-only LLM-judge** over this session's selected skills to emit ADVISORY per-skill applied/completed judgments to `.orchestrator/metrics/skill-judgments.jsonl`.
173
+ After learnings are written (Phase 3.6), and when the judge is enabled, run a **bounded, read-only LLM-judge** over this session's selected skills to emit ADVISORY per-skill applied/completed judgments to `.orchestrator/metrics/skill-judgments.jsonl`.
185
174
 
186
175
  **The #614 distinction (the whole point of L3's Design A):** unlike the 3.6.5 / 3.6.7 nudge-only paths — which cannot dispatch a live subagent because the target read-only agents (`memory-cleanup`, `dialectic-deriver`) cannot write their own sidecars — L3 performs a **LIVE read-only dispatch**. This is #614-safe because the read-only `skill-applied-judge` agent **RETURNS JSON** and the **COORDINATOR writes the sidecar**, not the agent. A read-only agent that returns judgments is allowed; a read-only agent that must write a file is the #614 trap.
187
176
 
@@ -238,41 +227,10 @@ After learnings are written (Phase 3.6) and the auto-dream decision is made (Pha
238
227
 
239
228
  Cross-reference: PRD §A L3 acceptance criteria (#645, epic #643); `scripts/lib/skill-judge.mjs` API (`runSkillJudge`, `validateModel`, `estimateInputTokens`, `checkBudget`, `buildJudgePrompt`, `parseJudgeResponse`); `scripts/lib/skill-judgments-schema.mjs` (`appendSkillJudgment`, `readSkillJudgments`, `validateSkillJudgment`); agent `agents/skill-applied-judge.md`.
240
229
 
241
- ### 3.6.7 Auto-Dialectic Dispatch (#506, F2.5)
242
-
243
- > Skip this phase if `dialectic.cadence: 0` (kill-switch per PRD F2.5 AC3). Also skip if `persistence` is `false` in Session Config.
244
-
245
- After learnings are written (Phase 3.6) and the auto-dream decision is made (Phase 3.6.5), determine whether to emit a **manual-cadence nudge** to run `/evolve --dialectic` in the next session. The decision uses sessions-since-last-dialectic counted against `.orchestrator/dialectic-last-run`. There is no `evolve` agent in the registry, and the nearest one (`dialectic-deriver`) is `sandbox-tier: read-only` and cannot write the sidecar — so the historical auto-dialectic subagent dispatch never fired (see #614). On trigger, emit the nudge and advance `.orchestrator/dialectic-last-run`; the timestamp is updated only when the nudge is emitted (not on skip), so the reminder surfaces once per cadence window rather than every session. A manually-run `/evolve --dialectic --dry-run` writes the proposed diff to `.orchestrator/dialectic-pending.md`.
246
-
247
- 1. Read `dialectic.cadence` (default 5), `dialectic.model` (default haiku), `dialectic.budget-tokens` (default 8000) from `$CONFIG`.
248
-
249
- 2. Invoke `decideAndRecordAutoDialectic` from `scripts/lib/auto-dialectic.mjs`:
250
- ```javascript
251
- import { decideAndRecordAutoDialectic } from '${PLUGIN_ROOT}/scripts/lib/auto-dialectic.mjs';
252
- const decision = await decideAndRecordAutoDialectic({
253
- repoRoot: process.cwd(),
254
- cadence: config.dialectic?.cadence ?? 5,
255
- });
256
- ```
257
- Same return shape as `shouldDispatchAutoDialectic` (`{trigger, reason, signals}`) — `decideAndRecordAutoDialectic` calls it internally and additionally emits the mechanical `orchestrator.dialectic.nudge_decided` telemetry record on all four return paths (#1200 part c), so the nudge decision is observable without depending on this prose actually reaching step 5/7.
258
-
259
- 3. If `decision.trigger === false`: log `auto-dialectic: not triggered (${decision.reason})` and continue. Emit no nudge. Do NOT update `.orchestrator/dialectic-last-run`.
260
-
261
- 4. **AC4 precondition guard:** Even if cadence met, if `signals.sessionsSinceLast === 0 && signals.learningsSinceLast === 0`, skip with reason `no-new-input-since-last-run`. The Final Report (Phase 6) MUST include the literal string `dialectic: skipped (no new input since last run)`.
262
-
263
- 5. If `decision.trigger === true`: **do not dispatch a subagent** (see #614 — no `evolve` agent exists; `dialectic-deriver` is read-only and cannot write the sidecar). Instead, emit a manual-cadence nudge and continue:
264
-
265
- `auto-dialectic: cadence reached (${decision.reason}) — run /evolve --dialectic --dry-run manually in the next session, review .orchestrator/dialectic-pending.md, then apply with /evolve --dialectic --apply.`
266
-
267
- The `shouldDispatchAutoDialectic` decision helper and `scripts/lib/auto-dialectic.mjs` lib stay in use: they compute the cadence signal that drives this nudge.
268
-
269
- 6. When the nudge is emitted (cadence reached), update `.orchestrator/dialectic-last-run` via `writeDialecticLastRun({ repoRoot, isoTimestamp: new Date().toISOString() })` so the cadence counter advances and the nudge does not repeat every session. Atomic; failures non-fatal.
270
-
271
- 7. Record outcome (skipped / nudge-emitted) for Phase 6 Final Report: `auto-dialectic: manual /evolve --dialectic --dry-run recommended (cadence reached) — apply with /evolve --dialectic --apply next session`.
272
-
273
- The `.orchestrator/dialectic-pending.md` sidecar is intentionally outside the vault tree — vault-mirror (Phase 3.7) MUST exclude it from its scope.
230
+ ### 3.6.7 Auto-Dialectic Dispatch (#506, F2.5) — RETIRED
274
231
 
275
- Cross-reference: PRD F2.5 acceptance criteria (#506); `scripts/lib/auto-dialectic.mjs` API.
232
+ > **RETIRED 2026-09-09.** The nudge is replaced by the session-start `maintenance-due` probe (`checkMaintenanceDue`, `scripts/lib/maintenance-due-banner.mjs`), whose `dialectic` signal reads the side-effect-free `shouldDispatchAutoDialectic` — never a variant that advances the last-run stamp, which would consume the very signal it reports. Its decider is also gone from `planTailPhases()` in `scripts/lib/session-end/phase-skip.mjs`; the heading stays because other docs cite it.
233
+ > The housekeeping session runs `/evolve dialectic` itself (see `skills/session-start/SKILL.md` Phase 7 — the maintenance loop): dry-run first, review `.orchestrator/dialectic-pending.md`, then apply. `scripts/lib/auto-dialectic.mjs` (`shouldDispatchAutoDialectic`, `writeDialecticLastRun`) and the read-only `dialectic-deriver` agent stay in use on that manual path. The recording wrapper around that signal, and its `orchestrator.dialectic.nudge_decided` event, were removed in #1288 — nothing emits that event any more. <!-- path-check: example -->
276
234
 
277
235
  > **Dialectic chain rationale** — design choices in the manual `/evolve --dialectic` chain (`/evolve → runDialecticDeriver → dispatchAgent → Agent`). Session-end no longer auto-dispatches this chain (see #614 — the `evolve` agent never existed); the rationale below applies when you run `/evolve --dialectic` manually:
278
236
  > - **/evolve → subagent (not direct invoke):** the manual `/evolve --dialectic` skill spawns a subagent so the dialectic pass runs in a fresh context window — keeping the deriver's input-heavy payload (top-50 learnings + last-10 sessions + 2 peer cards + steering) out of the invoking coordinator's context, and letting the deriver run as Haiku while the coordinator stays Opus.
@@ -286,7 +244,7 @@ Cross-reference: PRD F2.5 acceptance criteria (#506); `scripts/lib/auto-dialecti
286
244
  > - `reconcile.enabled` is `false` (default: `false` — opt-in; this is the silent no-op path for all repos that have not opted in)
287
245
  > - `.orchestrator/metrics/learnings.jsonl` does not exist OR contains zero entries
288
246
 
289
- After the auto-dialectic nudge decision is made (Phase 3.6.7), and when the reconcile engine is enabled, run the **reconciliation engine** to turn high-confidence learnings into conditional-rule proposals and present them to the operator via `AskUserQuestion` multiSelect. Approved proposals flow to `.claude/rules/` via `writeApprovedRules`. Rejected proposals are archived to `.orchestrator/reconcile.rejected.log`. The engine NEVER writes `.claude/rules/` itself — every write is operator-AUQ-gated (#693 FA2/FA3 brandmauer).
247
+ After the Skill-Applied Judge (Phase 3.6.6 Phase 3.6.7 is retired), and when the reconcile engine is enabled, run the **reconciliation engine** to turn high-confidence learnings into conditional-rule proposals and present them to the operator via `AskUserQuestion` multiSelect. Approved proposals flow to `.claude/rules/` via `writeApprovedRules`. Rejected proposals are archived to `.orchestrator/reconcile.rejected.log`. The engine NEVER writes `.claude/rules/` itself — every write is operator-AUQ-gated (#693 FA2/FA3 brandmauer).
290
248
 
291
249
  #### Coordinator-direct procedure
292
250
 
@@ -323,7 +281,7 @@ After the auto-dialectic nudge decision is made (Phase 3.6.7), and when the reco
323
281
 
324
282
  3. If `surfaced.length === 0`: log `reconcile: 0 proposals above confidence floor (eligible=${summary.eligible}, rejected=${summary.rejected}, floor=${floor})` and continue. No AUQ, no sidecar write.
325
283
 
326
- 4. **Write the human-readable proposal sidecar** `.orchestrator/metrics/reconcile-pending.md` so the operator can review raw content outside the AUQ:
284
+ 4. **Write the human-readable proposal sidecar** `.orchestrator/metrics/reconcile-pending.md` so the operator can review raw content outside the AUQ: <!-- path-check: example -->
327
285
 
328
286
  ```
329
287
  # Reconciliation Rule Proposals — <ISO timestamp>
@@ -76,7 +76,7 @@ try {
76
76
 
77
77
  ## Phase 3.7b: Durable-Commit Session Telemetry (#490 AC2)
78
78
 
79
- > **Ordering:** Runs AFTER Phase 3.7a (Recommendation fields just-written to STATE.md) and BEFORE Phase 3.4 (`status: completed`). The canonical runtime order is `… → 3.6.7 → 3.6.8 → 3.7 → 3.7a → 3.7b → 3.7c → 3.7d → 3.4`. Both session-end-owned files (`sessions.jsonl` from Phase 3.7, `STATE.md` from Phase 3.7a) have already been written to disk; this step only declares them as the durable-commit set.
79
+ > **Ordering:** Runs AFTER Phase 3.7a (Recommendation fields just-written to STATE.md) and BEFORE Phase 3.4 (`status: completed`). The canonical runtime order is `… → 3.6.6 → 3.6.8 → 3.7 → 3.7a → 3.7b → 3.7c → 3.7d → 3.4` (3.6.5 Auto-Dream and 3.6.7 Auto-Dialectic were RETIRED 2026-09-09 — see `phase-3-6-tail.md`). Both session-end-owned files (`sessions.jsonl` from Phase 3.7, `STATE.md` from Phase 3.7a) have already been written to disk; this step only declares them as the durable-commit set.
80
80
 
81
81
  > **Ownership:** session-end commits ONLY the two files it owns — `.orchestrator/metrics/sessions.jsonl` and `<state-dir>/STATE.md`. `.orchestrator/metrics/autopilot.jsonl` is NOT session-end's responsibility: `scripts/lib/autopilot/loop.mjs` commits that file in the autopilot loop (the core `loop.mjs` wiring shipped in #490 Wave-2). Do not add autopilot.jsonl to the files array here.
82
82
 
@@ -95,6 +95,6 @@ await withDurableCommit(
95
95
  );
96
96
  ```
97
97
 
98
- - Use the platform-resolved `<state-dir>/STATE.md` path (e.g. `~/.claude/STATE.md` on Claude Code) — NOT a hardcoded `.claude/STATE.md`.
98
+ - Use the platform-resolved `<state-dir>/STATE.md` path (e.g. `~/.claude/STATE.md` on Claude Code) — NOT a hardcoded `.claude/STATE.md`. <!-- path-check: example -->
99
99
  - The `files` array is staged individually by `durableCommit` (PSA-004: never `git add .`/`-A`); the existing `SAFE_BRANCH_RE` branch-name allowlist + cwd-confinement guards in `durable-telemetry.mjs` apply unchanged.
100
100
  - `enabled: false` short-circuits before any git command runs, so the local-execution path performs zero VCS mutation — Phase 4 (`git add` + commit) remains the single staging point for local closes.