session-orchestrator 5.1.0 → 5.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 (293) hide show
  1. package/.agents/skills/autopilot/SKILL.md +1 -0
  2. package/.agents/skills/bootstrap/SKILL.md +2 -0
  3. package/.agents/skills/brainstorm/SKILL.md +3 -0
  4. package/.agents/skills/close/SKILL.md +17 -0
  5. package/.agents/skills/debug/SKILL.md +2 -0
  6. package/.agents/skills/discovery/SKILL.md +2 -1
  7. package/.agents/skills/dispatcher/SKILL.md +2 -0
  8. package/.agents/skills/eli5/SKILL.md +2 -0
  9. package/.agents/skills/eval/SKILL.md +1 -0
  10. package/.agents/skills/evolve/SKILL.md +2 -1
  11. package/.agents/skills/go/SKILL.md +18 -0
  12. package/.agents/skills/grill/SKILL.md +2 -0
  13. package/.agents/skills/harness-audit/SKILL.md +16 -0
  14. package/.agents/skills/memory-cleanup/SKILL.md +1 -0
  15. package/.agents/skills/persona-panel/SKILL.md +1 -0
  16. package/.agents/skills/plan/SKILL.md +3 -1
  17. package/.agents/skills/portfolio/SKILL.md +17 -0
  18. package/.agents/skills/reconcile/SKILL.md +1 -0
  19. package/.agents/skills/release/SKILL.md +18 -0
  20. package/.agents/skills/repo-audit/SKILL.md +1 -0
  21. package/.agents/skills/spinout/SKILL.md +1 -0
  22. package/.agents/skills/sunset-review/SKILL.md +2 -0
  23. package/.agents/skills/test/SKILL.md +17 -0
  24. package/.agents/skills/ux-grill/SKILL.md +2 -0
  25. package/.claude-plugin/marketplace.json +1 -1
  26. package/.claude-plugin/plugin.json +1 -1
  27. package/.codex-plugin/plugin.json +1 -1
  28. package/.codex-plugin/skills/autopilot/SKILL.md +5 -4
  29. package/.codex-plugin/skills/bootstrap/SKILL.md +8 -4
  30. package/.codex-plugin/skills/brainstorm/SKILL.md +11 -4
  31. package/.codex-plugin/skills/close/SKILL.md +3 -3
  32. package/.codex-plugin/skills/convergence-monitoring/SKILL.md +2 -0
  33. package/.codex-plugin/skills/convergence-monitoring/agents/openai.yaml +5 -0
  34. package/.codex-plugin/skills/debug/SKILL.md +11 -4
  35. package/.codex-plugin/skills/discovery/SKILL.md +8 -4
  36. package/.codex-plugin/skills/dispatcher/SKILL.md +4 -4
  37. package/.codex-plugin/skills/eli5/SKILL.md +9 -4
  38. package/.codex-plugin/skills/eval/SKILL.md +9 -4
  39. package/.codex-plugin/skills/evolve/SKILL.md +9 -4
  40. package/.codex-plugin/skills/go/SKILL.md +3 -3
  41. package/.codex-plugin/skills/grill/SKILL.md +11 -4
  42. package/.codex-plugin/skills/harness-audit/SKILL.md +4 -3
  43. package/.codex-plugin/skills/memory-cleanup/SKILL.md +9 -4
  44. package/.codex-plugin/skills/npm-publish/SKILL.md +2 -0
  45. package/.codex-plugin/skills/npm-publish/agents/openai.yaml +5 -0
  46. package/.codex-plugin/skills/persona-panel/SKILL.md +5 -5
  47. package/.codex-plugin/skills/plan/SKILL.md +8 -4
  48. package/.codex-plugin/skills/portfolio/SKILL.md +3 -3
  49. package/.codex-plugin/skills/reconcile/SKILL.md +9 -4
  50. package/.codex-plugin/skills/release/SKILL.md +3 -3
  51. package/.codex-plugin/skills/repo-audit/SKILL.md +6 -4
  52. package/.codex-plugin/skills/spinout/SKILL.md +4 -4
  53. package/.codex-plugin/skills/sunset-review/SKILL.md +5 -4
  54. package/.codex-plugin/skills/test/SKILL.md +3 -3
  55. package/.codex-plugin/skills/ux-grill/SKILL.md +11 -4
  56. package/.cursor/commands/autopilot.md +4 -4
  57. package/.cursor/commands/bootstrap.md +5 -4
  58. package/.cursor/commands/brainstorm.md +5 -4
  59. package/.cursor/commands/close.md +4 -3
  60. package/.cursor/commands/convergence-monitoring.md +13 -0
  61. package/.cursor/commands/debug.md +4 -4
  62. package/.cursor/commands/discovery.md +4 -4
  63. package/.cursor/commands/dispatcher.md +4 -4
  64. package/.cursor/commands/eli5.md +4 -4
  65. package/.cursor/commands/eval.md +4 -4
  66. package/.cursor/commands/evolve.md +4 -4
  67. package/.cursor/commands/go.md +4 -3
  68. package/.cursor/commands/grill.md +4 -4
  69. package/.cursor/commands/harness-audit.md +3 -3
  70. package/.cursor/commands/memory-cleanup.md +4 -4
  71. package/.cursor/commands/npm-publish.md +13 -0
  72. package/.cursor/commands/persona-panel.md +4 -4
  73. package/.cursor/commands/plan.md +5 -4
  74. package/.cursor/commands/portfolio.md +3 -3
  75. package/.cursor/commands/reconcile.md +4 -4
  76. package/.cursor/commands/release.md +4 -3
  77. package/.cursor/commands/repo-audit.md +4 -4
  78. package/.cursor/commands/spinout.md +4 -4
  79. package/.cursor/commands/sunset-review.md +4 -4
  80. package/.cursor/commands/test.md +3 -3
  81. package/.cursor/commands/ux-grill.md +4 -4
  82. package/.cursor/rules/010-session-workflow.mdc +2 -2
  83. package/.cursor/skills/bootstrap/SKILL.md +1 -0
  84. package/.cursor/skills/close/SKILL.md +13 -0
  85. package/.cursor/skills/debug/SKILL.md +0 -1
  86. package/.cursor/skills/discovery/SKILL.md +0 -1
  87. package/.cursor/skills/dispatcher/SKILL.md +0 -1
  88. package/.cursor/skills/eli5/SKILL.md +0 -1
  89. package/.cursor/skills/evolve/SKILL.md +0 -1
  90. package/.cursor/skills/go/SKILL.md +13 -0
  91. package/.cursor/skills/grill/SKILL.md +0 -1
  92. package/.cursor/skills/harness-audit/SKILL.md +12 -0
  93. package/.cursor/skills/portfolio/SKILL.md +12 -0
  94. package/.cursor/skills/release/SKILL.md +13 -0
  95. package/.cursor/skills/repo-audit/SKILL.md +0 -1
  96. package/.cursor/skills/sunset-review/SKILL.md +0 -1
  97. package/.cursor/skills/test/SKILL.md +12 -0
  98. package/.cursor/skills/ux-grill/SKILL.md +0 -1
  99. package/.cursor-plugin/plugin.json +1 -1
  100. package/.orchestrator/policy/blocked-commands.json +1 -1
  101. package/AGENTS.md +1 -1
  102. package/CHANGELOG.md +61 -0
  103. package/README.md +11 -9
  104. package/commands/session.md +10 -0
  105. package/docs/ci-setup.md +53 -0
  106. package/docs/codex-setup.md +1 -1
  107. package/docs/components.md +11 -6
  108. package/docs/events-schema.md +4 -1
  109. package/docs/install.md +16 -0
  110. package/docs/persona-panel.md +1 -1
  111. package/docs/pi-setup.md +1 -1
  112. package/docs/rule-authoring.md +83 -14
  113. package/docs/scope-collision-guard.md +2 -0
  114. package/docs/session-config-reference.md +6 -4
  115. package/hooks/_lib/hook-import-set.json +46 -6
  116. package/hooks/_lib/subagent-paths.mjs +15 -0
  117. package/hooks/_lib/vcs-create-matcher.mjs +217 -62
  118. package/hooks/hooks-codex.json +1 -1
  119. package/hooks/hooks.json +1 -1
  120. package/hooks/on-session-end.mjs +14 -2
  121. package/hooks/on-stop.mjs +43 -1
  122. package/hooks/post-bash-write-verify.mjs +3 -0
  123. package/hooks/pre-auq-clarity.mjs +3 -0
  124. package/hooks/pre-bash-issue-budget.mjs +103 -17
  125. package/hooks/pre-task-scope-disjoint.mjs +152 -3
  126. package/hooks/skill-invocation-telemetry.mjs +2 -1
  127. package/package.json +2 -1
  128. package/pi/prompts/autopilot.md +3 -3
  129. package/pi/prompts/bootstrap.md +3 -3
  130. package/pi/prompts/brainstorm.md +3 -3
  131. package/pi/prompts/close.md +2 -2
  132. package/pi/prompts/convergence-monitoring.md +11 -0
  133. package/pi/prompts/debug.md +3 -3
  134. package/pi/prompts/discovery.md +3 -3
  135. package/pi/prompts/dispatcher.md +3 -3
  136. package/pi/prompts/eli5.md +3 -3
  137. package/pi/prompts/eval.md +3 -3
  138. package/pi/prompts/evolve.md +3 -3
  139. package/pi/prompts/go.md +2 -2
  140. package/pi/prompts/grill.md +3 -3
  141. package/pi/prompts/harness-audit.md +2 -3
  142. package/pi/prompts/memory-cleanup.md +3 -3
  143. package/pi/prompts/npm-publish.md +11 -0
  144. package/pi/prompts/persona-panel.md +3 -3
  145. package/pi/prompts/plan.md +3 -3
  146. package/pi/prompts/portfolio.md +2 -2
  147. package/pi/prompts/reconcile.md +3 -3
  148. package/pi/prompts/release.md +3 -3
  149. package/pi/prompts/repo-audit.md +3 -4
  150. package/pi/prompts/session.md +1 -1
  151. package/pi/prompts/spinout.md +3 -3
  152. package/pi/prompts/sunset-review.md +3 -3
  153. package/pi/prompts/templates-ack.md +1 -1
  154. package/pi/prompts/test.md +3 -3
  155. package/pi/prompts/ux-grill.md +3 -3
  156. package/scripts/archive-closed-prds.mjs +2 -2
  157. package/scripts/auq-audit.mjs +2 -3
  158. package/scripts/backfill-abandoned-sessions.mjs +57 -3
  159. package/scripts/backfill-evidence-digest.mjs +2 -1
  160. package/scripts/backfill-learnings-from-vault.mjs +2 -2
  161. package/scripts/check-package-manager.mjs +2 -2
  162. package/scripts/ci/assert-vitest-green.mjs +2 -1
  163. package/scripts/emit-session.mjs +2 -3
  164. package/scripts/export-hw-learnings.mjs +2 -1
  165. package/scripts/express-path.mjs +1 -1
  166. package/scripts/gc-stale-worktrees.mjs +2 -1
  167. package/scripts/generate-codex-skills.mjs +48 -4
  168. package/scripts/generate-cursor-adapter.mjs +173 -9
  169. package/scripts/generate-hook-import-set.mjs +12 -27
  170. package/scripts/generate-pi-prompts.mjs +183 -13
  171. package/scripts/github-protection-audit.mjs +2 -3
  172. package/scripts/lib/agent-frontmatter.mjs +23 -1
  173. package/scripts/lib/claude-md-budget-lint.mjs +2 -5
  174. package/scripts/lib/command-blocker.mjs +133 -5
  175. package/scripts/lib/config/drift-check.mjs +19 -0
  176. package/scripts/lib/convergence-monitor.mjs +2 -2
  177. package/scripts/lib/cursor-hook-bridge.mjs +2 -2
  178. package/scripts/lib/description-surface.mjs +2 -5
  179. package/scripts/lib/dispatcher/cli.mjs +2 -1
  180. package/scripts/lib/ecosystem-wizard.mjs +2 -1
  181. package/scripts/lib/fetch-baseline.mjs +3 -8
  182. package/scripts/lib/gitlab-ops/stale-mr-sweep.mjs +2 -1
  183. package/scripts/lib/gitlab-portfolio/cli.mjs +2 -1
  184. package/scripts/lib/instruction-budget-guard.mjs +186 -46
  185. package/scripts/lib/is-main-module.mjs +82 -0
  186. package/scripts/lib/locks/index.mjs +32 -25
  187. package/scripts/lib/maintenance-due-banner.mjs +69 -3
  188. package/scripts/lib/peer-discovery.mjs +2 -5
  189. package/scripts/lib/playwright-driver/runner.mjs +2 -1
  190. package/scripts/lib/reconcile/rule-expiry-sweep.mjs +642 -0
  191. package/scripts/lib/rules-sync.mjs +2 -5
  192. package/scripts/lib/scope-echo.mjs +392 -7
  193. package/scripts/lib/session-close-backfill.mjs +58 -6
  194. package/scripts/lib/state-md.mjs +84 -3
  195. package/scripts/lib/sunset/walker.mjs +31 -4
  196. package/scripts/lib/tests-src-ratio.mjs +2 -6
  197. package/scripts/lib/tmux-layout/telemetry-stats.mjs +2 -1
  198. package/scripts/lib/user-invocable-skills.mjs +185 -0
  199. package/scripts/lib/validate/check-banner-parity.mjs +2 -2
  200. package/scripts/lib/validate/check-cursor-adapter.mjs +2 -2
  201. package/scripts/lib/validate/check-dead-bridge.mjs +2 -2
  202. package/scripts/lib/validate/check-doc-cli-commands.mjs +2 -2
  203. package/scripts/lib/validate/check-entry-guard.mjs +366 -0
  204. package/scripts/lib/validate/check-guard-requires-parity.mjs +2 -2
  205. package/scripts/lib/validate/check-hooks-emit-event-guard.mjs +2 -2
  206. package/scripts/lib/validate/check-learning-provenance.mjs +2 -2
  207. package/scripts/lib/validate/check-skill-links.mjs +27 -6
  208. package/scripts/lib/validate/check-skill-script-paths.mjs +2 -2
  209. package/scripts/lib/validate/check-test-git-config-target.mjs +2 -2
  210. package/scripts/lib/validate/check-unicode-safety.mjs +2 -2
  211. package/scripts/lib/validate/check-untracked-test-deps.mjs +2 -2
  212. package/scripts/lib/validate/check-unwired-features.mjs +91 -7
  213. package/scripts/lib/validate/check-validator-registration.mjs +2 -2
  214. package/scripts/lib/validate/check-vcs-repo-flag.mjs +2 -2
  215. package/scripts/lib/validate-vendored-rules.mjs +35 -9
  216. package/scripts/lib/wave-transcript-tail.mjs +2 -2
  217. package/scripts/lock-reaper.mjs +2 -1
  218. package/scripts/materialize-wave-scope.mjs +87 -4
  219. package/scripts/migrate-sessions-jsonl.mjs +2 -1
  220. package/scripts/migrate-vault-paths.mjs +2 -3
  221. package/scripts/release.mjs +80 -35
  222. package/scripts/relocate-vault-corpus.mjs +2 -3
  223. package/scripts/repair-invalid-sessions.mjs +2 -2
  224. package/scripts/session-shape.mjs +2 -2
  225. package/scripts/site-numbers.mjs +35 -11
  226. package/scripts/sweep-expired-rules.mjs +216 -0
  227. package/scripts/validate-plugin.mjs +9 -0
  228. package/scripts/vault-consolidate.mjs +2 -2
  229. package/scripts/vault-mirror.mjs +2 -3
  230. package/scripts/wave-scope-binding.mjs +2 -3
  231. package/skills/_shared/bootstrap-gate.md +1 -1
  232. package/skills/_shared/monitor-patterns.md +1 -1
  233. package/skills/_shared/research-evidence.md +53 -0
  234. package/skills/_shared/state-ownership.md +3 -0
  235. package/skills/autopilot/SKILL.md +58 -4
  236. package/skills/bootstrap/SKILL.md +51 -1
  237. package/skills/brainstorm/SKILL.md +16 -0
  238. package/skills/claude-md-drift-check/checker.mjs +49 -11
  239. package/{commands/close.md → skills/close/SKILL.md} +9 -3
  240. package/skills/debug/SKILL.md +10 -0
  241. package/skills/discovery/SKILL.md +24 -1
  242. package/skills/discovery/probes-session.md +2 -2
  243. package/skills/dispatcher/SKILL.md +38 -7
  244. package/skills/eli5/SKILL.md +11 -0
  245. package/skills/eval/SKILL.md +14 -0
  246. package/skills/evolve/SKILL.md +8 -1
  247. package/skills/evolve/references/evolve-dialectic-mode.md +6 -2
  248. package/{commands/go.md → skills/go/SKILL.md} +9 -1
  249. package/skills/grill/SKILL.md +19 -0
  250. package/{commands/harness-audit.md → skills/harness-audit/SKILL.md} +7 -2
  251. package/skills/hook-development/SKILL.md +46 -41
  252. package/skills/memory-cleanup/SKILL.md +7 -0
  253. package/skills/npm-publish/SKILL.md +1 -1
  254. package/skills/persona-panel/SKILL.md +56 -1
  255. package/skills/persona-panel/persona-format.md +1 -1
  256. package/skills/plan/SKILL.md +28 -1
  257. package/{commands/portfolio.md → skills/portfolio/SKILL.md} +8 -2
  258. package/skills/reconcile/SKILL.md +10 -0
  259. package/{commands/release.md → skills/release/SKILL.md} +16 -2
  260. package/skills/repo-audit/SKILL.md +7 -0
  261. package/skills/session-end/plan-verification.md +2 -2
  262. package/skills/session-plan/SKILL.md +1 -1
  263. package/skills/session-start/SKILL.md +5 -4
  264. package/skills/session-start/phase-8-5-express-path.md +6 -6
  265. package/skills/session-start/references/phase-1-5-session-continuity.md +1 -1
  266. package/skills/session-start/references/phase-2-7-portfolio-snapshot.md +1 -1
  267. package/skills/session-start/references/phase-4-ssot-environment-check.md +4 -3
  268. package/skills/spinout/SKILL.md +12 -1
  269. package/skills/sunset-review/SKILL.md +13 -0
  270. package/{commands/test.md → skills/test/SKILL.md} +10 -4
  271. package/skills/ux-grill/SKILL.md +19 -1
  272. package/skills/wave-executor/SKILL.md +7 -4
  273. package/skills/wave-executor/references/wave-executor-state-init.md +13 -1
  274. package/skills/wave-executor/references/wave-loop-dispatch.md +3 -1
  275. package/skills/wave-executor/references/wave-loop-review.md +17 -1
  276. package/commands/autopilot.md +0 -80
  277. package/commands/bootstrap.md +0 -56
  278. package/commands/brainstorm.md +0 -48
  279. package/commands/debug.md +0 -36
  280. package/commands/discovery.md +0 -32
  281. package/commands/dispatcher.md +0 -59
  282. package/commands/eli5.md +0 -33
  283. package/commands/eval.md +0 -28
  284. package/commands/evolve.md +0 -10
  285. package/commands/grill.md +0 -45
  286. package/commands/memory-cleanup.md +0 -26
  287. package/commands/persona-panel.md +0 -121
  288. package/commands/plan.md +0 -15
  289. package/commands/reconcile.md +0 -23
  290. package/commands/repo-audit.md +0 -24
  291. package/commands/spinout.md +0 -15
  292. package/commands/sunset-review.md +0 -27
  293. package/commands/ux-grill.md +0 -51
@@ -112,7 +112,7 @@ Before `syncRules()` writes a source file into a consumer repo's `.claude/rules/
112
112
 
113
113
  | Probe | Severity | Rejects / flags |
114
114
  |-------|----------|------------------|
115
- | `paths-frontmatter` | error | A top-level `paths:` frontmatter key `rule-loader.mjs` only recognises `globs:`; a `paths:` key is silently ignored and the rule loads always-on instead of the intended glob-scoped subset. |
115
+ | `paths-frontmatter` | error | A top-level `paths:` frontmatter key **in a `rules/` library source**. Since #795 `rule-loader.mjs` accepts `paths:` as an alias for `globs:`, so such a rule IS glob-scoped — this is a vendoring-CONVENTION gate (`globs:` is the canonical form for vendored rules, #742), not a loader-compatibility gate. Its population is what `syncRules()` reads, i.e. `<pluginRoot>/rules/**` as listed by `rules/_index.md`; the consolidated files under `.claude/rules/` are `paths:`-canonical (see § Consolidated rules point 3) and are never its input. |
116
116
  | `provenance-header` | error (opt-in via `requireProvenance`, default `true` in `syncRules()`) | Missing provenance header on a library source — without it, `rules-sync.mjs` mis-detects the file as a local override on the next re-sync and can never update it again. |
117
117
  | `placeholder` | error | Unfilled placeholder tokens: `{{PROJECT_NAME}}`-style handlebars, a `## TODO: Customize` heading, or a `<!-- TODO:` comment — skeleton content, not a finished rule. |
118
118
  | `zero-match-globs` | warn | A `globs:` pattern matching 0 files in the target repo's tracked file list (`git ls-files`, falling back to a directory walk). Legitimately possible in a freshly-scaffolded repo. |
@@ -285,19 +285,25 @@ them silently loses a learning or regenerates it:
285
285
  TTL past what its type registry granted it.) State the rule in the file
286
286
  itself, so the next editor does not "fix" it upward.
287
287
 
288
- 3. **Keep BOTH `globs:` and `paths:`, as byte-identical mirrors, each the
289
- UNION of the parts.** The merged file loads for any path any of its parts
290
- covered, so both lists are the union of theirs — never `globs:` alone.
291
- `rule-loader.mjs` resolves `globs:` for wave-time injection, while Claude
292
- Code's OWN native rule loader reads ONLY `paths:` and treats a rule
293
- lacking it as unconditional, always-on (`check-rules.mjs` check #1108,
294
- measured this session) — a `globs:`-only merged file is scoped everywhere
295
- it is inspected and loads everywhere it is used, exactly the
296
- instruction-budget failure consolidation exists to prevent. `rule-loader.mjs`
297
- still resolves `globs:` and `paths:` with `globs:` winning SILENTLY when
298
- both are present (issue #795, `parseGlobsFrontmatter`), but keeping the two
299
- lists identical makes that precedence moot there is no divergent value
300
- left for it to pick between.
288
+ 3. **`paths:` is the canonical scope key, and it carries the UNION of the
289
+ parts.** The merged file loads for any path any of its parts covered, so its
290
+ list is the union of theirs. `paths:` is the key Claude Code's OWN native
291
+ rule loader reads, and it treats a rule lacking it as unconditional,
292
+ always-on (`check-rules.mjs` check #1108) exactly the instruction-budget
293
+ failure consolidation exists to prevent. `globs:` is an accepted ALIAS, not a
294
+ second required mirror: `rule-loader.mjs` resolves either key
295
+ (`parseGlobsFrontmatter`, issue #795) and `instruction-budget-guard.mjs`
296
+ (`:960`) goes through that same parser, so a `paths:`-only file is
297
+ glob-scoped for every reader in this repo and still counts under
298
+ `bySurface.pathScoped`. Measured 2026-09-16 over the live tree
299
+ (`for f in .claude/rules/*.md; do awk '/^---$/{n++;next} n==1 && /^(paths|globs):/{print FILENAME": "$1}' "$f"; done`):
300
+ 10 path-scoped rule files 9 `paths:`-only, 1 (`cli-design.md`) carrying
301
+ both. `globs:` is canonical only for rules VENDORED OUT through the `rules/`
302
+ fleet library, where `validate-vendored-rules.mjs`'s `paths-frontmatter`
303
+ probe enforces it (issue #742); that probe judges `rules/` sources only and
304
+ never sees a consolidated file under `.claude/rules/`. Carrying both keys is
305
+ allowed, but NEVER with different values: `globs:` wins SILENTLY when both
306
+ are present (#795), and `check-rules.mjs` fails a divergent pair outright.
301
307
 
302
308
  4. **Substance in, boilerplate out.** Each absorbed learning becomes an `###`
303
309
  heading carrying its original rule sentence, plus its evidence line. What is
@@ -330,6 +336,69 @@ This writes `.orchestrator/runtime/reconcile-candidates.jsonl` (creating it if
330
336
  absent). Verify with a dry run: `alreadyMaterialized` must equal
331
337
  absorbed + dropped, not absorbed alone.
332
338
 
339
+ #### The expiry sweep (`scripts/sweep-expired-rules.mjs`, #1377)
340
+
341
+ `rule-loader.mjs` stops INJECTING a generated rule once its `expires-at` has
342
+ passed; nothing removed one from disk, so an expired consolidated file stayed
343
+ tracked, kept costing bytes against `generated-byte-ceiling`, and kept reading
344
+ as live corpus to every human and every grep. The sweep
345
+ (`scripts/lib/reconcile/rule-expiry-sweep.mjs`, CLI
346
+ `node scripts/sweep-expired-rules.mjs`, `--dry-run` default) closes that gap
347
+ under four contracts, all of them consequences of the four merge rules above:
348
+
349
+ 1. **Prose goes, the pair STAYS.** An expired entry's `###` block is deleted;
350
+ its provenance pair is converted to the `markers only` shape already present
351
+ in the corpus — a same-line HTML comment appended to the `- learning-id:`
352
+ bullet, leaving the backticked value regex-visible to
353
+ `BODY_LEARNING_ID_RE`. Per point 1, deleting the pair would make the
354
+ learning look unmaterialized and `/reconcile` would regenerate it. **A pair
355
+ is never deleted while its file survives.**
356
+
357
+ 2. **Two fail-open cases, both reported rather than guessed.** An entry carries
358
+ no date of its own; its date is recoverable only via `learning-id` →
359
+ `.orchestrator/metrics/learnings.jsonl` `expires_at` (measured 2026-09-17 @
360
+ `9e8146b4`: 87 of 92 unique ids resolve, 5 do not). An **unresolvable id**
361
+ keeps its entry and blocks the file delete. An **ambiguous file** — where the
362
+ `###` headings do not map 1:1 onto the non-`markers only` pairs, because
363
+ several learnings were merged into one prose entry — gets `action: 'keep'`
364
+ plus a `skipped` record with reason `no-1to1-mapping`. Measured the same day,
365
+ the 1:1 mapping held in 3 of the 7 live files
366
+ (`measurement-discipline` 12/12, `process-contracts` 6/6,
367
+ `toolchain-and-build` 10/10) and failed in the other 4. Malformed
368
+ `learnings.jsonl` lines are COUNTED (`malformedLines`), never skipped.
369
+
370
+ 3. **Deleting a whole file obeys the stamp-before-delete rule above.** A file is
371
+ deleted only when it has zero kept AND zero unresolved pairs, and EVERY pair
372
+ on it — markers-only ones included, since those are dedupe markers too — is
373
+ stamped via `markCandidateProcessed` BEFORE the `unlink`. Stamping afterwards
374
+ leaves a window in which neither the file nor a terminal verdict exists.
375
+
376
+ 4. **The header is recomputed only for a file the sweep actually rewrites.**
377
+ Point 2's earliest-date rule is re-applied over the pairs that remain and
378
+ are not expired, in the frontmatter and in the body sentence
379
+ ``**`expires-at` <D> = the EARLIEST of the <N> absorbed dates**``. A file
380
+ with nothing expired is left BYTE-IDENTICAL, so a recompute-on-every-run
381
+ cannot silently shorten a healthy file's TTL. The discrepancy is reported as
382
+ the plan's `advisory` field instead — computed over every RESOLVABLE pair on
383
+ the file and emitted BEFORE the `no-1to1-mapping` skip, so an ambiguous file
384
+ still gets one. Measured 2026-09-17
385
+ (`node scripts/sweep-expired-rules.mjs --json`, 7 files scanned, 0 expired),
386
+ **6 of the 7 files carry a discrepancy**: `identity-and-locks` 2026-10-01 vs
387
+ 2026-10-02, `measurement-discipline` 2026-10-04 vs 2026-10-02,
388
+ `process-contracts` 2026-10-04 vs 2026-10-27,
389
+ `review-and-adapter-contracts` 2026-10-04 vs 2026-10-02, `test-hygiene`
390
+ 2026-10-20 vs 2026-10-07, `toolchain-and-build` 2026-10-01 vs 2026-10-16 —
391
+ only `guard-design` agrees with its content. Three are the harmful direction,
392
+ a header OUTLIVING its content (`measurement-discipline`,
393
+ `review-and-adapter-contracts`, `test-hygiene`); the other three expire
394
+ earlier than they need to, which costs injection and loses nothing. `N`
395
+ counts the pairs remaining in the file, which is the total pair count — an
396
+ absorbed date stays absorbed after its prose is gone, and all 7 live
397
+ sentences carry that number.
398
+
399
+ `--apply` emits `orchestrator.rules.expiry_sweep_applied` after the writes
400
+ succeed (see `docs/events-schema.md`); a dry run emits nothing.
401
+
333
402
  ## Authoring Examples
334
403
 
335
404
  ### (a) Hand-authored always-on rule (no frontmatter)
@@ -157,6 +157,8 @@ Why it exists: the in-ledger `scopeSignals` counter (§ 4.1, rows 5/6) is a **wa
157
157
 
158
158
  A cryptographic proof still needs that platform boundary: no mechanism inside this repo can distinguish a copied digest from a read one.
159
159
 
160
+ **The digest now JOINS the two halves (#1092, 2026-09-16).** Everything above stays true — what changed is that the halves became joinable at all. `scope_checked` carries `scope_digest` (`scopeDigest()` over the paths extracted from the prompt, omitted for an empty scope rather than defaulted to the empty-string digest), `echo_instruction_present`, `instructed_digest` and `digest_consistent`; `scope_echo_checked` already carried `expected_digest` / `actual_digest`; and `materialize-wave-scope` writes one `scope_materialized` record per wave carrying `transport_observable`. `node scripts/lib/scope-echo.mjs --verify --wave <N> --state-dir <dir>` joins all three **on the digest, never on `agent_id`** — measured 2026-09-16 over this host's ledger, 609 send-side against 51 receive-side records whose agent-id sets overlap in ZERO elements (send records the dispatch `description` + `subagent_type`, receive the coordinator's short handle), so before the digest the two halves could not be paired even in principle. Its closed verdict enum — `matched` · `injected-not-echoed` · `echoed-not-injected` · `injection-missing` · `digest-unknown` · `duplicate-claim`, all degrading to `echo-only` when the transport is unobservable — is what makes the issue's AC-2 (an omitted injection turning an end-to-end probe red) a measurable state rather than an unfalsifiable absence. `digest_consistent: false` additionally catches agent A's fenced block beside agent B's echo line **at dispatch time**, from the prompt alone. None of this raises the ceiling above: a joined digest still proves transport of a LINE, never obedience.
161
+
160
162
  **Revisit-Trigger** (verbatim from issue #1092, for the transport half this section deliberately does NOT close):
161
163
 
162
164
  > Implement when the platform exposes a stable prompt-assembly hook or when a coordinator-owned digest event can be proven against the real dispatched transcript.
@@ -332,7 +332,7 @@ slopcheck:
332
332
  | `memory-cleanup-threshold` | integer | `5` | Recommend `/memory-cleanup` after N accumulated session memory files. |
333
333
  | `memory-cleanup-soft-limit` | integer | `180` | Hard ceiling on accumulated memory files before the cleanup nudge escalates from a soft suggestion to a strong recommendation. PRD F2.2 / issue #502. Used by `scripts/lib/auto-dream.mjs`. |
334
334
 
335
- **Nudge retirement (2026-09-09, #1246-adjacent).** The two session-end nudges that used to read these thresholds — Phase 3.6.5 Auto-Dream and Phase 3.6.7 Auto-Dialectic — are RETIRED as standalone close-time prompts: measured across consumer repos, the auto-dialectic nudge recorded `decided: true` while nobody ever ran it, and memory-cleanup had run in only 1 of 3 repos, because both fired at the moment the operator is closing down, not the moment they can act. `shouldDispatchAutoDream()` and `shouldDispatchAutoDialectic()` are unchanged as pure signal functions and still consult these two keys — but they are now called from the session-START `maintenance-due` probe (`scripts/lib/maintenance-due-banner.mjs`, see § Reconcile below), which reads them as two of its six signals and reports a single `⚠ maintenance due: N of 6 …` banner at the one moment the operator can actually run `/session housekeeping`.
335
+ **Nudge retirement (2026-09-09, #1246-adjacent).** The two session-end nudges that used to read these thresholds — Phase 3.6.5 Auto-Dream and Phase 3.6.7 Auto-Dialectic — are RETIRED as standalone close-time prompts: measured across consumer repos, the auto-dialectic nudge recorded `decided: true` while nobody ever ran it, and memory-cleanup had run in only 1 of 3 repos, because both fired at the moment the operator is closing down, not the moment they can act. `shouldDispatchAutoDream()` and `shouldDispatchAutoDialectic()` are unchanged as pure signal functions and still consult these two keys — but they are now called from the session-START `maintenance-due` probe (`scripts/lib/maintenance-due-banner.mjs`, see § Reconcile below), which reads them as two of its seven signals and reports a single `⚠ maintenance due: N of 7 …` banner at the one moment the operator can actually run `/session housekeeping`.
336
336
  | `learning-expiry-days` | integer | `30` | Legacy/default expiry window used by review/extend flows. New analyzer learnings preserve a candidate-supplied `expires_at` or derive expiry from `LEARNING_TTL_DAYS[type]` (for example, `autonomy-verdict` is 90 days). |
337
337
  | `learnings-surface-top-n` | integer | `15` | Cap on how many learnings the session-start Phase 5.6 and session-plan Step 0.5 sections surface, ranked by confidence descending. `0` = do not surface any learnings. Applies to Project Intelligence output. |
338
338
  | `learning-decay-rate` | float (0.0 ≤ x < 1.0) | `0.05` | Confidence decay applied to every untouched learning at session-end (after touched-set update, before prune). `0.0` = disable decay. A learning starting at `0.5` confidence survives ~10 untouched sessions with default decay. |
@@ -1008,7 +1008,7 @@ dialectic:
1008
1008
 
1009
1009
  | Field | Type | Default | Description |
1010
1010
  |-------|------|---------|-------------|
1011
- | `dialectic.cadence` | integer | `5` | Number of sessions between auto-dialectic dispatches. Set to `0` to disable all dispatches (kill-switch). Non-integer and negative values silently fall back to default. **The dispatch moment moved (2026-09-09):** `shouldDispatchAutoDialectic()` is still the decision function this key feeds, but session-end Phase 3.6.7's own auto-trigger nudge is gone — retired as a standalone close-time prompt, and its recording wrapper (with the `orchestrator.dialectic.nudge_decided` event) removed in #1288 — see § Persistence & Safety above "Nudge retirement". The session-start `maintenance-due` probe now calls it as one of six signals, so a due dialectic surfaces where the operator can act on it (session start), not where they are closing down. |
1011
+ | `dialectic.cadence` | integer | `5` | Number of sessions between auto-dialectic dispatches. Set to `0` to disable all dispatches (kill-switch). Non-integer and negative values silently fall back to default. **The dispatch moment moved (2026-09-09):** `shouldDispatchAutoDialectic()` is still the decision function this key feeds, but session-end Phase 3.6.7's own auto-trigger nudge is gone — retired as a standalone close-time prompt, and its recording wrapper (with the `orchestrator.dialectic.nudge_decided` event) removed in #1288 — see § Persistence & Safety above "Nudge retirement". The session-start `maintenance-due` probe now calls it as one of seven signals, so a due dialectic surfaces where the operator can act on it (session start), not where they are closing down. |
1012
1012
  | `dialectic.model` | string | `haiku` | Model tier for the critique call. Must be one of `haiku`, `sonnet`, `opus`. **Fail-fast**: unknown values cause parse-config.mjs to exit 1 at startup — NOT silently ignored. |
1013
1013
  | `dialectic.budget-tokens` | integer | `8000` | Input token budget per call. Output budget is fixed at 4000 (per #506). Non-integer and negative values fall back to default. |
1014
1014
 
@@ -1343,7 +1343,9 @@ Read by: `scripts/lib/config/evolve.mjs` (parser), `skills/evolve/SKILL.md` Step
1343
1343
 
1344
1344
  Opt-in configuration for the learning→conditional-rule reconciliation engine (Epic #693). When enabled, the reconciliation engine runs at session-end Phase 3.6.8 and proposes new `.claude/rules/` entries derived from accumulated learnings. The proposal is always operator-AUQ-gated — rules are **never** auto-applied. FA3 (#696) delivers proposals via `AskUserQuestion`; FA4 (#697) adds the guardrail config block documented here. When `enabled: false` (the default), Phase 3.6.8 is a silent no-op and the engine never runs.
1345
1345
 
1346
- **The former `reconcile-nudge` session-start probe is SUBSUMED (2026-09-09), not replaced 1:1.** `reconcile-nudge-banner.mjs`'s `computeReconcileNudge()` function is unchanged and still the reconcile-specific signal — but it is no longer surfaced as its own standalone banner. `scripts/lib/maintenance-due-banner.mjs` now calls it wholesale as one of six ANDed maintenance signals (`reconcile: computeReconcileNudge().nudge === true`) behind the single session-start `maintenance-due` probe, alongside `evolve`, `sweep`, `dialectic`, `memory-cleanup` and `pending-sidecar`. The reconcile signal is therefore not lost — it is reported as `⚠ maintenance due: N of 6 (…) — run /session housekeeping.` rather than as its own line, with a 7-day cooldown after any housekeeping session (`HOUSEKEEPING_COOLDOWN_DAYS`) so a repo that just ran the loop stays silent instead of re-nagging.
1346
+ **The former `reconcile-nudge` session-start probe is SUBSUMED (2026-09-09), not replaced 1:1.** `reconcile-nudge-banner.mjs`'s `computeReconcileNudge()` function is unchanged and still the reconcile-specific signal — but it is no longer surfaced as its own standalone banner. `scripts/lib/maintenance-due-banner.mjs` now calls it wholesale as one of seven ANDed maintenance signals (`reconcile: computeReconcileNudge().nudge === true`) behind the single session-start `maintenance-due` probe, alongside `evolve`, `sweep`, `dialectic`, `memory-cleanup`, `pending-sidecar` and `generated-rules-expiring`. The reconcile signal is therefore not lost — it is reported as `⚠ maintenance due: N of 7 (…) — run /session housekeeping.` rather than as its own line, with a 7-day cooldown after any housekeeping session (`HOUSEKEEPING_COOLDOWN_DAYS`) so a repo that just ran the loop stays silent instead of re-nagging.
1347
+
1348
+ **`generated-rules-expiring` (7th signal, 2026-09-16, #1372 follow-up).** Due when a machine-generated rule under `.claude/rules/` is expired or expires within `GENERATED_RULE_EXPIRY_HORIZON_DAYS` (7, a module constant — **no Session Config key**, same posture as every other threshold in this probe). Population and predicate come from `scripts/lib/instruction-budget-guard.mjs` (`listMachineGeneratedRules` / `daysUntilGeneratedRuleExpiry`), the same set its generated byte ceiling judges. The row names the files and their dates, because the repair is per-file: consolidate the content into a live thematic rule, move the `- learning-key:` / `- learning-id:` pairs with it, then delete. `rule-loader.mjs` filters an expired rule at READ time but nothing deletes it, so it keeps costing bytes and is still loaded verbatim by Claude Code (#1372). This alarm deliberately does NOT live in the test suite: an assertion against today's date turns `npm test` red on a calendar date with nothing committed, blocking unrelated work, and no test run can perform the consolidation.
1347
1349
 
1348
1350
  All fields live under a top-level `reconcile` object in your Session Config host file (`CLAUDE.md` or `AGENTS.md`), for example:
1349
1351
 
@@ -1770,7 +1772,7 @@ Leave disabled (default) when:
1770
1772
  - The wave's deliverable is server-side / backend-only and persona evaluation would be noise.
1771
1773
 
1772
1774
  **Related skills and files:**
1773
- - `commands/persona-panel.md` — standalone `/persona-panel` command for ad-hoc panel runs (not gated on `persona-gate-wave.enabled`).
1775
+ - `skills/persona-panel/SKILL.md` — standalone `/persona-panel` command for ad-hoc panel runs (not gated on `persona-gate-wave.enabled`).
1774
1776
  - `skills/persona-panel/SKILL.md` — full skill spec (catalog format, consolidation modes, sidecar shape).
1775
1777
  - `skills/wave-executor/wave-loop.md` § 3b — the wave-executor hook contract.
1776
1778
  - `agents/schemas/persona-panel-sidecar.schema.json` — sidecar JSON Schema enforced before write.
@@ -1,6 +1,6 @@
1
1
  {
2
- "generated_at": "2026-09-13T09:10:50.839Z",
3
- "head": "ff1ed19177795b743186eebfc387319de0e90c31",
2
+ "generated_at": "2026-09-16T17:00:47.173Z",
3
+ "head": "1ef26415ce80164bd7e27705a4e7d97c37c72126",
4
4
  "entries": [
5
5
  {
6
6
  "file": "hooks/_lib/atomic-json.mjs",
@@ -268,7 +268,8 @@
268
268
  "reachable_from": [
269
269
  "on-session-end.mjs",
270
270
  "on-session-start.mjs",
271
- "post-edit-validate.mjs"
271
+ "post-edit-validate.mjs",
272
+ "skill-invocation-telemetry.mjs"
272
273
  ]
273
274
  },
274
275
  {
@@ -358,6 +359,7 @@
358
359
  "pre-auq-clarity.mjs",
359
360
  "pre-bash-issue-budget.mjs",
360
361
  "pre-bash-memory-propose-audit.mjs",
362
+ "pre-task-scope-disjoint.mjs",
361
363
  "skill-invocation-telemetry.mjs"
362
364
  ]
363
365
  },
@@ -381,6 +383,7 @@
381
383
  "pre-auq-clarity.mjs",
382
384
  "pre-bash-issue-budget.mjs",
383
385
  "pre-bash-memory-propose-audit.mjs",
386
+ "pre-task-scope-disjoint.mjs",
384
387
  "skill-invocation-telemetry.mjs"
385
388
  ]
386
389
  },
@@ -660,6 +663,7 @@
660
663
  "pre-auq-clarity.mjs",
661
664
  "pre-bash-issue-budget.mjs",
662
665
  "pre-bash-memory-propose-audit.mjs",
666
+ "pre-task-scope-disjoint.mjs",
663
667
  "skill-invocation-telemetry.mjs"
664
668
  ]
665
669
  },
@@ -699,6 +703,7 @@
699
703
  "pre-auq-clarity.mjs",
700
704
  "pre-bash-issue-budget.mjs",
701
705
  "pre-bash-memory-propose-audit.mjs",
706
+ "pre-task-scope-disjoint.mjs",
702
707
  "skill-invocation-telemetry.mjs"
703
708
  ]
704
709
  },
@@ -838,7 +843,8 @@
838
843
  "post-tool-batch-wave-signal.mjs",
839
844
  "post-tooluse-frontend-slop.mjs",
840
845
  "pre-auq-clarity.mjs",
841
- "pre-bash-memory-propose-audit.mjs"
846
+ "pre-bash-memory-propose-audit.mjs",
847
+ "pre-task-scope-disjoint.mjs"
842
848
  ]
843
849
  },
844
850
  {
@@ -859,7 +865,8 @@
859
865
  "post-tool-batch-wave-signal.mjs",
860
866
  "post-tooluse-frontend-slop.mjs",
861
867
  "pre-auq-clarity.mjs",
862
- "pre-bash-memory-propose-audit.mjs"
868
+ "pre-bash-memory-propose-audit.mjs",
869
+ "pre-task-scope-disjoint.mjs"
863
870
  ]
864
871
  },
865
872
  {
@@ -882,6 +889,7 @@
882
889
  "pre-auq-clarity.mjs",
883
890
  "pre-bash-issue-budget.mjs",
884
891
  "pre-bash-memory-propose-audit.mjs",
892
+ "pre-task-scope-disjoint.mjs",
885
893
  "skill-invocation-telemetry.mjs"
886
894
  ]
887
895
  },
@@ -990,6 +998,16 @@
990
998
  "skill-invocation-telemetry.mjs"
991
999
  ]
992
1000
  },
1001
+ {
1002
+ "file": "scripts/lib/is-main-module.mjs",
1003
+ "reachable_from": [
1004
+ "enforce-scope.mjs",
1005
+ "on-session-end.mjs",
1006
+ "on-session-start.mjs",
1007
+ "pre-task-scope-disjoint.mjs",
1008
+ "skill-invocation-telemetry.mjs"
1009
+ ]
1010
+ },
993
1011
  {
994
1012
  "file": "scripts/lib/issue-budget.mjs",
995
1013
  "reachable_from": [
@@ -1024,6 +1042,7 @@
1024
1042
  "pre-auq-clarity.mjs",
1025
1043
  "pre-bash-issue-budget.mjs",
1026
1044
  "pre-bash-memory-propose-audit.mjs",
1045
+ "pre-task-scope-disjoint.mjs",
1027
1046
  "skill-invocation-telemetry.mjs"
1028
1047
  ]
1029
1048
  },
@@ -1047,6 +1066,7 @@
1047
1066
  "pre-auq-clarity.mjs",
1048
1067
  "pre-bash-issue-budget.mjs",
1049
1068
  "pre-bash-memory-propose-audit.mjs",
1069
+ "pre-task-scope-disjoint.mjs",
1050
1070
  "skill-invocation-telemetry.mjs"
1051
1071
  ]
1052
1072
  },
@@ -1070,6 +1090,7 @@
1070
1090
  "pre-auq-clarity.mjs",
1071
1091
  "pre-bash-issue-budget.mjs",
1072
1092
  "pre-bash-memory-propose-audit.mjs",
1093
+ "pre-task-scope-disjoint.mjs",
1073
1094
  "skill-invocation-telemetry.mjs"
1074
1095
  ]
1075
1096
  },
@@ -1196,6 +1217,12 @@
1196
1217
  "on-session-start.mjs"
1197
1218
  ]
1198
1219
  },
1220
+ {
1221
+ "file": "scripts/lib/scope-echo.mjs",
1222
+ "reachable_from": [
1223
+ "pre-task-scope-disjoint.mjs"
1224
+ ]
1225
+ },
1199
1226
  {
1200
1227
  "file": "scripts/lib/scope-gate.mjs",
1201
1228
  "reachable_from": [
@@ -1244,7 +1271,8 @@
1244
1271
  "post-tool-batch-wave-signal.mjs",
1245
1272
  "post-tooluse-frontend-slop.mjs",
1246
1273
  "pre-auq-clarity.mjs",
1247
- "pre-bash-memory-propose-audit.mjs"
1274
+ "pre-bash-memory-propose-audit.mjs",
1275
+ "pre-task-scope-disjoint.mjs"
1248
1276
  ]
1249
1277
  },
1250
1278
  {
@@ -1267,6 +1295,7 @@
1267
1295
  "pre-auq-clarity.mjs",
1268
1296
  "pre-bash-issue-budget.mjs",
1269
1297
  "pre-bash-memory-propose-audit.mjs",
1298
+ "pre-task-scope-disjoint.mjs",
1270
1299
  "skill-invocation-telemetry.mjs"
1271
1300
  ]
1272
1301
  },
@@ -1290,6 +1319,7 @@
1290
1319
  "pre-auq-clarity.mjs",
1291
1320
  "pre-bash-issue-budget.mjs",
1292
1321
  "pre-bash-memory-propose-audit.mjs",
1322
+ "pre-task-scope-disjoint.mjs",
1293
1323
  "skill-invocation-telemetry.mjs"
1294
1324
  ]
1295
1325
  },
@@ -1431,6 +1461,7 @@
1431
1461
  "pre-auq-clarity.mjs",
1432
1462
  "pre-bash-issue-budget.mjs",
1433
1463
  "pre-bash-memory-propose-audit.mjs",
1464
+ "pre-task-scope-disjoint.mjs",
1434
1465
  "skill-invocation-telemetry.mjs"
1435
1466
  ]
1436
1467
  },
@@ -1462,6 +1493,7 @@
1462
1493
  "pre-auq-clarity.mjs",
1463
1494
  "pre-bash-issue-budget.mjs",
1464
1495
  "pre-bash-memory-propose-audit.mjs",
1496
+ "pre-task-scope-disjoint.mjs",
1465
1497
  "skill-invocation-telemetry.mjs"
1466
1498
  ]
1467
1499
  },
@@ -1527,6 +1559,14 @@
1527
1559
  "skill-invocation-telemetry.mjs"
1528
1560
  ]
1529
1561
  },
1562
+ {
1563
+ "file": "scripts/lib/user-invocable-skills.mjs",
1564
+ "reachable_from": [
1565
+ "on-session-end.mjs",
1566
+ "on-session-start.mjs",
1567
+ "skill-invocation-telemetry.mjs"
1568
+ ]
1569
+ },
1530
1570
  {
1531
1571
  "file": "scripts/lib/wave-context.mjs",
1532
1572
  "reachable_from": [
@@ -21,6 +21,21 @@
21
21
  * override completely unvalidated. That containment check is a NEW rule this
22
22
  * consolidation introduces, not a preserved behaviour.
23
23
  *
24
+ * MEASURED NON-DEFECT (2026-09-16, #1289 Befund 2). The ~90 %
25
+ * `transcript_found:false` rate on `orchestrator.agent.stopped` is NOT a defect
26
+ * of this derivation. Over 15.457 records: 0 of 5077 distinct false `agent_id`s
27
+ * have a sidecar anywhere on the host (8626 sidecar ids found via
28
+ * `find ~/.claude/projects -path '*subagents/agent-*.jsonl'`), 0 have a
29
+ * `SubagentStart` record, and 0 records carrying a valid id got a null result
30
+ * from this function — the false mass is the #939/#949 phantom-stop class (the
31
+ * harness fires SubagentStop for an ephemeral class that never existed; the
32
+ * evidence is in the header of hooks/subagent-telemetry.mjs). Driving this
33
+ * resolver with the 30 newest real false-case payloads resolves 0/30 because the
34
+ * file does not exist, while the 30 newest true-case payloads resolve 26/30 (the
35
+ * other 4 are aged-out transcripts of 2026-09-13 sessions). Widening the
36
+ * derivation — a directory scan, a nested `subagents/` fallback, an unbounded id
37
+ * — would resolve onto a FOREIGN agent's sidecar without recovering one record.
38
+ *
24
39
  * @module hooks/_lib/subagent-paths
25
40
  */
26
41