@event4u/agent-config 14.21.0 → 14.23.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 (235) hide show
  1. package/.augment-plugin/marketplace.json +3 -3
  2. package/.augment-plugin/plugin.json +1 -1
  3. package/.claude-plugin/marketplace.json +1 -1
  4. package/CHANGELOG.md +164 -187
  5. package/CREDITS.md +37 -0
  6. package/README.md +1 -1
  7. package/dist/agent-src/commands/analyze/inbox.md +380 -5
  8. package/dist/agent-src/commands/analyze/repo.md +4 -2
  9. package/dist/agent-src/commands/pr/merge.md +47 -38
  10. package/dist/agent-src/commands/roadmap/next.md +8 -0
  11. package/dist/agent-src/contexts/communication/rules-auto/token-efficiency-mechanics.md +69 -0
  12. package/dist/agent-src/contexts/execution/auto-dispatch-classification.md +5 -5
  13. package/dist/agent-src/contexts/execution/autonomy-mechanics.md +12 -0
  14. package/dist/agent-src/rules/external-code-graph-interop.md +19 -14
  15. package/dist/agent-src/rules/notes-first-reasoning.md +36 -25
  16. package/dist/agent-src/rules/token-efficiency.md +26 -3
  17. package/dist/agent-src/scripts/_cli_entry.ts +51 -0
  18. package/dist/agent-src/scripts/archive_completed_roadmaps.ts +120 -27
  19. package/dist/agent-src/scripts/roadmap_gates.ts +4 -20
  20. package/dist/agent-src/scripts/roadmap_set_step.ts +5 -2
  21. package/dist/agent-src/scripts/stubs_due.ts +4 -18
  22. package/dist/agent-src/scripts/update_roadmap_progress.ts +7 -24
  23. package/dist/agent-src/skills/code-intelligence/SKILL.md +82 -1
  24. package/dist/agent-src/skills/git-workflow/references/push-closes-its-loop.md +9 -0
  25. package/dist/agent-src/skills/roadmap-writing/SKILL.md +17 -0
  26. package/dist/agent-src/skills/tailwind-engineer/SKILL.md +52 -2
  27. package/dist/agent-src/templates/agent-settings.md +0 -1
  28. package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
  29. package/dist/cli/registry.js +1 -1
  30. package/dist/cli-delegate/_cli_entry.js +8 -0
  31. package/dist/cli-delegate/_lib/pin_resolver.js +170 -0
  32. package/dist/cli-delegate/archival_sweep.js +8 -0
  33. package/dist/cli-delegate/archive_completed_roadmaps.js +542 -0
  34. package/dist/cli-delegate/check_update_banner.js +923 -0
  35. package/dist/cli-delegate/{chunk-ZVBOHSVF.js → chunk-2KKXJW25.js} +23 -9
  36. package/dist/cli-delegate/chunk-5EK5WQN6.js +28 -0
  37. package/dist/cli-delegate/chunk-7XGD2OEU.js +96 -0
  38. package/dist/cli-delegate/chunk-DBH4AHDK.js +155 -0
  39. package/dist/cli-delegate/{chunk-4FS4WRXP.js → chunk-IAI3XL43.js} +15 -3
  40. package/dist/cli-delegate/chunk-JO25G5H7.js +20 -0
  41. package/dist/cli-delegate/{chunk-643Z7FIC.js → chunk-KCNTUSPI.js} +7 -1
  42. package/dist/cli-delegate/chunk-LTYVIY76.js +7961 -0
  43. package/dist/cli-delegate/chunk-QAJS5YWY.js +999 -0
  44. package/dist/cli-delegate/{chunk-YS6XTLEK.js → chunk-TYSY33MD.js} +56 -6
  45. package/dist/cli-delegate/chunk-ULET4BYS.js +328 -0
  46. package/dist/cli-delegate/chunk-YDQSHTD7.js +187 -0
  47. package/dist/cli-delegate/chunk-YWVXLR44.js +48 -0
  48. package/dist/cli-delegate/chunk-ZZQBUHFK.js +273 -0
  49. package/dist/cli-delegate/cmd_conformance.js +2 -2
  50. package/dist/cli-delegate/cmd_export.js +2 -2
  51. package/dist/cli-delegate/cmd_handoff.js +45 -13
  52. package/dist/cli-delegate/cmd_prune.js +2 -2
  53. package/dist/cli-delegate/cmd_session_recycle.js +4 -14
  54. package/dist/cli-delegate/cmd_settings_migrate.js +2 -2
  55. package/dist/cli-delegate/cmd_settings_set.js +1 -1
  56. package/dist/cli-delegate/cmd_sync.js +2 -2
  57. package/dist/cli-delegate/cmd_uninstall.js +2 -2
  58. package/dist/cli-delegate/cmd_validate.js +2 -2
  59. package/dist/cli-delegate/dashboard_mode.js +16 -0
  60. package/dist/cli-delegate/gate_budget.js +24 -0
  61. package/dist/cli-delegate/gate_execute.js +25 -0
  62. package/dist/cli-delegate/guarded_baseline.js +18 -0
  63. package/dist/cli-delegate/resume_probe.js +22 -0
  64. package/dist/cli-delegate/roadmap_gates.js +638 -0
  65. package/dist/cli-delegate/roadmap_set_step.js +173 -0
  66. package/dist/cli-delegate/stub_queue.js +47 -0
  67. package/dist/cli-delegate/stubs_due.js +38 -0
  68. package/dist/cli-delegate/ticket_bundles.js +59 -0
  69. package/dist/cli-delegate/update_roadmap_progress.js +60 -0
  70. package/dist/discovery/deprecation-report.md +1 -1
  71. package/dist/discovery/discovery-manifest.json +13 -13
  72. package/dist/discovery/discovery-manifest.json.sha256 +1 -1
  73. package/dist/discovery/discovery-manifest.summary.md +1 -1
  74. package/dist/discovery/dormancy-report.md +1 -1
  75. package/dist/discovery/orphan-report.md +1 -1
  76. package/dist/discovery/packs.json +2 -2
  77. package/dist/discovery/trust-report.md +1 -1
  78. package/dist/discovery/workspaces.json +2 -2
  79. package/dist/hooks/dispatch.js +1813 -946
  80. package/dist/install/claudeLayerCarriage.js +237 -0
  81. package/dist/install/claudePathsPlan.js +79 -0
  82. package/dist/install/hostLayerFingerprint.js +1 -1
  83. package/dist/install/install.mjs +21 -3
  84. package/dist/install/partitionEligibility.js +231 -117
  85. package/dist/install/ruleLayerPartition.js +10 -7
  86. package/dist/mcp/registry-manifest.json +2 -2
  87. package/dist/mcp/server.json +2 -2
  88. package/dist/mcp/server.mjs +10870 -7641
  89. package/dist/scripts/_lib/lean_projection_mode.js +108 -0
  90. package/dist/scripts/_lib/router_match.js +243 -0
  91. package/dist/scripts/_lib/rule_injection.js +216 -0
  92. package/dist/scripts/condense.js +52 -45
  93. package/dist/scripts/project_thin_rules.js +139 -19
  94. package/dist/server/schemas/settings.js +16 -3
  95. package/docs/MIGRATION.md +105 -1
  96. package/docs/contracts/settings-classes.md +40 -8
  97. package/docs/guidelines/agent-infra/minimal-safe-diff-mechanics.md +73 -0
  98. package/docs/guidelines/agent-infra/notes-horizon-mechanics.md +90 -0
  99. package/llms.txt +1 -1
  100. package/package.json +7 -4
  101. package/src/agent-src/scripts/_cli_entry.ts +51 -0
  102. package/src/agent-src/scripts/archive_completed_roadmaps.ts +120 -27
  103. package/src/agent-src/scripts/roadmap_gates.ts +4 -20
  104. package/src/agent-src/scripts/roadmap_set_step.ts +5 -2
  105. package/src/agent-src/scripts/stubs_due.ts +4 -18
  106. package/src/agent-src/scripts/update_roadmap_progress.ts +7 -24
  107. package/src/config/agent-settings.template.yml +47 -1
  108. package/src/config/continuity-surface.json +516 -0
  109. package/src/config/estate-count-budget.json +1 -1
  110. package/src/config/evaluator-budgets.json +4 -4
  111. package/src/config/gate-coverage.yml +29 -8
  112. package/src/config/gate-violation-baselines.json +8 -8
  113. package/src/config/gitignore-block.txt +19 -0
  114. package/src/config/hook-token-budget.json +7 -6
  115. package/src/config/host-injection-effect.json +73 -0
  116. package/src/config/packed-binary-manifest.json +33 -0
  117. package/src/config/preamble-payload-budget.json +112 -105
  118. package/src/config/publish-surface.json +4 -2
  119. package/src/config/routing-coverage-seed.json +9 -2
  120. package/src/install/claudeLayerCarriage.ts +273 -0
  121. package/src/install/claudePathsPlan.ts +76 -0
  122. package/src/install/hostLayerFingerprint.ts +1 -1
  123. package/src/install/partitionEligibility.ts +261 -127
  124. package/src/install/ruleLayerPartition.ts +19 -23
  125. package/src/scripts/_cli/cmd_session_recycle.ts +2 -25
  126. package/src/scripts/_cli/handoff_sessions.ts +14 -44
  127. package/src/scripts/_dispatch.bash +77 -27
  128. package/src/scripts/_lib/auto_dispatch.ts +2 -2
  129. package/src/scripts/_lib/catalogue_layer_parity.ts +146 -0
  130. package/src/scripts/_lib/continuity_slot.ts +295 -0
  131. package/src/scripts/_lib/continuity_surface.ts +296 -0
  132. package/src/scripts/_lib/continuity_writer.ts +253 -0
  133. package/src/scripts/_lib/corpus_manifest.ts +26 -9
  134. package/src/scripts/_lib/hook_settings.ts +86 -1
  135. package/src/scripts/_lib/injection_effect.ts +151 -0
  136. package/src/scripts/_lib/layer_overlap_notice.ts +50 -6
  137. package/src/scripts/_lib/lean_projection_mode.ts +127 -0
  138. package/src/scripts/_lib/packed_binary_predicate.ts +262 -0
  139. package/src/scripts/_lib/pin_resolver.ts +13 -0
  140. package/src/scripts/_lib/recycle_envelope_paths.ts +109 -21
  141. package/src/scripts/_lib/regression_neighbourhood.ts +159 -22
  142. package/src/scripts/_lib/retired_status.ts +74 -0
  143. package/src/scripts/_lib/session_eol.ts +44 -0
  144. package/src/scripts/_lib/session_index_trust.ts +346 -0
  145. package/src/scripts/_lib/skill_catalogue.ts +156 -21
  146. package/src/scripts/_lib/standing_bound_ratchet.ts +154 -0
  147. package/src/scripts/_lib/subagent_capsule.ts +117 -8
  148. package/src/scripts/_lib/vendored_grammar_upstream.ts +280 -0
  149. package/src/scripts/ai_council/api_transport.ts +5 -0
  150. package/src/scripts/ai_council/deliberation_metrics.ts +353 -0
  151. package/src/scripts/ai_council/prompts.ts +9 -0
  152. package/src/scripts/ai_council/synthesis_disagreement.ts +206 -0
  153. package/src/scripts/audit_mcp_tools.ts +69 -25
  154. package/src/scripts/build_mcp_catalog.ts +24 -1
  155. package/src/scripts/capture_skill_catalogue.ts +24 -11
  156. package/src/scripts/check_candidate_lines.ts +499 -0
  157. package/src/scripts/check_continuity_surface.ts +475 -0
  158. package/src/scripts/check_dependency_floors.ts +13 -6
  159. package/src/scripts/check_estate_count.ts +40 -79
  160. package/src/scripts/check_host_tree_parity.ts +291 -0
  161. package/src/scripts/check_pack_size.ts +262 -23
  162. package/src/scripts/check_preamble_payload_budget.ts +136 -7
  163. package/src/scripts/check_roadmap_trackable.ts +19 -16
  164. package/src/scripts/check_rule_layer_partition.ts +54 -34
  165. package/src/scripts/check_rule_projection_integrity.ts +102 -2
  166. package/src/scripts/check_single_delivery.ts +97 -31
  167. package/src/scripts/check_update_banner.ts +13 -0
  168. package/src/scripts/ci_settle.ts +205 -5
  169. package/src/scripts/code_graph/build.ts +217 -15
  170. package/src/scripts/code_graph/cli.ts +334 -15
  171. package/src/scripts/code_graph/detect.ts +38 -0
  172. package/src/scripts/code_graph/extract.ts +11 -0
  173. package/src/scripts/code_graph/grammar_probe.ts +43 -0
  174. package/src/scripts/code_graph/loader.ts +88 -14
  175. package/src/scripts/code_graph/query.ts +144 -11
  176. package/src/scripts/code_graph/resolution_tiers.ts +238 -0
  177. package/src/scripts/code_graph/sqlite_store.ts +262 -6
  178. package/src/scripts/code_graph/types.ts +117 -3
  179. package/src/scripts/code_graph/validate.ts +26 -1
  180. package/src/scripts/code_graph/verbs.ts +590 -0
  181. package/src/scripts/condense.ts +54 -42
  182. package/src/scripts/corpus_manifest.ts +5 -5
  183. package/src/scripts/council_cli.ts +11 -12
  184. package/src/scripts/count_intent_disagreement.ts +426 -0
  185. package/src/scripts/handoff_context_hook.ts +118 -1
  186. package/src/scripts/hook_effect_doctor.ts +82 -1
  187. package/src/scripts/hook_manifest.json +1 -1
  188. package/src/scripts/hook_manifest.yaml +87 -12
  189. package/src/scripts/hooks/block_speaking_inbox_dir.ts +182 -16
  190. package/src/scripts/hooks/chain_nudge_hook.ts +282 -0
  191. package/src/scripts/hooks/code_graph_context_hook.ts +201 -0
  192. package/src/scripts/hooks/concern_registry.ts +4 -2
  193. package/src/scripts/hooks/host_semantics.ts +1 -1
  194. package/src/scripts/hooks/orchestration_record_hook.ts +2 -2
  195. package/src/scripts/hooks/push_settle_hook.ts +3 -2
  196. package/src/scripts/hooks/rule_inject_hook.ts +108 -21
  197. package/src/scripts/hooks/session_eol_hook.ts +120 -2
  198. package/src/scripts/hooks/skill_route_hook.ts +11 -4
  199. package/src/scripts/hot_context_hook.ts +62 -7
  200. package/src/scripts/inbox_source_census.ts +322 -0
  201. package/src/scripts/install-hooks.sh +82 -0
  202. package/src/scripts/{lint_carrier_integrity.ts → lint_deferral_integrity.ts} +60 -214
  203. package/src/scripts/lint_plan_risk_register.ts +13 -17
  204. package/src/scripts/lint_roadmap_blockers.ts +164 -1
  205. package/src/scripts/mcp_server/consumer_tool_catalog.json +152 -2
  206. package/src/scripts/mcp_server/graph_tools.ts +294 -0
  207. package/src/scripts/mcp_server/path_util.ts +31 -0
  208. package/src/scripts/mcp_server/suggest_skill.ts +72 -0
  209. package/src/scripts/mcp_server/tool_catalog_source.ts +7 -1
  210. package/src/scripts/mcp_server/tools.ts +5 -69
  211. package/src/scripts/media/lib/adapter-contract.md +7 -2
  212. package/src/scripts/media/lib/fixtures/flux/asset-0001.png +0 -0
  213. package/src/scripts/media/lib/fixtures/gemini-image/asset-0001.png +0 -0
  214. package/src/scripts/media/lib/fixtures/ideogram/asset-0001.png +0 -0
  215. package/src/scripts/model_rule_injection.ts +61 -4
  216. package/src/scripts/project_thin_rules.ts +149 -16
  217. package/src/scripts/rdp_candidates_delta.ts +276 -0
  218. package/src/scripts/rdp_quality_eval.ts +217 -19
  219. package/src/scripts/release_mix_taxonomy.json +3 -2
  220. package/src/scripts/report_host_injection_effect.ts +189 -0
  221. package/src/scripts/report_layer_overlap.ts +41 -0
  222. package/src/scripts/report_skill_menu_census.ts +341 -0
  223. package/src/scripts/report_standing_payload_by_host.ts +650 -0
  224. package/src/scripts/schemas/agent-settings.schema.json +7 -1
  225. package/src/scripts/session_memory_index.ts +81 -8
  226. package/src/scripts/skill_tools/score_skill_relevance.ts +70 -6
  227. package/src/scripts/skill_tools/suggest_skill_for_task.ts +37 -3
  228. package/src/server/schemas/settings.ts +23 -4
  229. package/src/vendor/grammars/README.md +86 -0
  230. package/src/vendor/grammars/tree-sitter-javascript.wasm +0 -0
  231. package/src/vendor/grammars/tree-sitter-php.wasm +0 -0
  232. package/src/vendor/grammars/tree-sitter-typescript.wasm +0 -0
  233. package/src/scripts/_lib/carrier_status.ts +0 -118
  234. package/src/scripts/_lib/estate_carrier_cases.ts +0 -72
  235. package/src/scripts/hooks/code_graph_nudge_hook.ts +0 -191
@@ -12,30 +12,33 @@
12
12
  * > neighbourhood names, and a fixture proves a neighbour regression is
13
13
  * > caught.**
14
14
  *
15
- * ## Which graph, and why it is not the one the step names
15
+ * TWO GRAPHS, BOTH DELIBERATE — the substitution is retired.
16
16
  *
17
- * The step says "the code graph". This tree has two graph surfaces and only one
18
- * of them resolves here, so the substitution is STATED rather than hidden.
17
+ * The step says "the code graph". Until 2026-09-08 this module could not read
18
+ * it: `agent-config code-graph detect` answered `no code-graph source detected`
19
+ * in a fresh checkout, so it selected over the artefact graph instead and said
20
+ * so. `road-to-a-graph-that-is-shipped` removed that premise — Phase 0.1 ships
21
+ * the parsers, Phase 1 builds the graph on install, and 3.1 added
22
+ * `impact --diff`, which is the reverse-reachability query this selector needs.
19
23
  *
20
- * · `agent-config code-graph` — the native code-graph engine (ADR-124). It
21
- * ships `hooks.code_graph.enabled: false`
22
- * (`src/config/agent-settings.template.yml:1373-1374`), and
23
- * `agent-config code-graph detect` in this checkout answers
24
- * `no code-graph source detected`. There is no index to select against.
25
- * · `src/scripts/discovery_graph.ts` — this suite's OWN artefact relation
26
- * graph, five typed edges extracted from the discovery manifest's
27
- * structured fields, with an `affected` BFS already implemented and
28
- * answering. That is the surface this module builds on.
24
+ * So both surfaces are now first-class, and which one a candidate uses is a
25
+ * property OF THE CANDIDATE rather than of what happened to resolve:
29
26
  *
30
- * The substitution changes what a "neighbourhood" means, and the difference is
31
- * worth naming: the native engine would neighbour a candidate by SYMBOL
32
- * relations, while `discovery_graph` neighbours it by ARTEFACT relations
33
- * (`supersedes`, `routes_to`, `references_adr`, pack and workspace membership).
34
- * For this roadmap's candidates — rules, skills, guidelines — the artefact
35
- * graph is the one that carries the coupling a rewrite can break. For a
36
- * candidate that edits a `.ts` symbol, it is the weaker surface, and this
37
- * module says so through {@link selectionVerdict}: a touched surface absent
38
- * from the graph is REFUSED, never silently neighboured with the empty set.
27
+ * · **Code candidates** — a diff that edits `.ts` / `.php` symbols. Use
28
+ * {@link selectRegressionsFromCode}, which neighbours by SYMBOL relations
29
+ * over the native graph, filtered to accepted mechanisms
30
+ * (`resolved_via` not in `{name-lookup, dynamic}`) so a same-name guess
31
+ * never selects a regression.
32
+ * · **Artefact candidates** — a diff that edits rules, skills, guidelines.
33
+ * Use {@link selectRegressions}, which neighbours by ARTEFACT relations
34
+ * over `src/scripts/discovery_graph.ts` (`supersedes`, `routes_to`,
35
+ * `references_adr`, pack and workspace membership). For a rule rewrite that
36
+ * is the graph carrying the coupling a change can break; the code graph
37
+ * does not model it at all.
38
+ *
39
+ * Neither is a fallback for the other, and neither is silent: both paths REFUSE
40
+ * through {@link selectionVerdict} when a touched surface is absent from the
41
+ * graph they read, because an unknown neighbourhood is not an empty one.
39
42
  *
40
43
  * ## Why an unresolved surface refuses instead of selecting nothing
41
44
  *
@@ -55,6 +58,9 @@
55
58
  * A test asserts that identity rather than an id match, because an id match
56
59
  * would also be satisfied by a synthesized spec carrying a copied id.
57
60
  */
61
+ import type { GraphState } from '../code_graph/detect.js';
62
+ import type { LoadedGraph } from '../code_graph/query.js';
63
+ import { impact } from '../code_graph/verbs.js';
58
64
  import { affected, isSyntheticNode, type Graph } from '../discovery_graph.js';
59
65
 
60
66
  /**
@@ -106,6 +112,48 @@ export interface NeighbourhoodReport {
106
112
  selected: readonly Selection[];
107
113
  /** Registry entries whose guards intersect nothing in the neighbourhood. */
108
114
  skipped: readonly string[];
115
+ /**
116
+ * Which graph answered. Present so a verdict record can never be read as a
117
+ * claim about the other surface — the failure the retired substitution
118
+ * section describes, arriving through the report instead of through the
119
+ * docstring.
120
+ */
121
+ graph: 'artefact' | 'code';
122
+ /**
123
+ * The graph's freshness at selection time: `fresh` / `behind:N` / `absent`
124
+ * on the code path, `null` on the artefact path (which is derived from the
125
+ * committed manifest and has no staleness axis).
126
+ *
127
+ * Added after an independent review found the code path hardcoding
128
+ * `'absent'` and dropping the value: the verbs are required to report
129
+ * staleness, `verbs.ts` argues the whole point is telling "dead" from "dead
130
+ * as of a graph 40 commits behind" — and the one in-repo CONSUMER of those
131
+ * verbs discarded it, so no selection could refuse on a stale graph.
132
+ */
133
+ graph_state: GraphState | null;
134
+ /**
135
+ * Touched files in a language the code graph does not index. NOT a refusal
136
+ * reason: an unindexed `.md` is a file with no symbols, which is a fact
137
+ * about the language set rather than an unknown neighbourhood.
138
+ */
139
+ not_indexed: readonly string[];
140
+ /**
141
+ * The edges that produced the neighbourhood, rendered, sorted.
142
+ *
143
+ * Required by step 3.4: *"the selected regressions and the producing edges
144
+ * enter the verdict record"*. Empty on the artefact path, whose `affected`
145
+ * BFS returns a relation name per hop rather than an edge — stated rather
146
+ * than faked, because an empty list a reader can attribute is better than a
147
+ * synthesised one they cannot.
148
+ */
149
+ producing_edges: readonly string[];
150
+ /**
151
+ * The `resolved_via` histogram of the edges the walk REFUSED, on the code
152
+ * path. A selection that looks narrow because half the graph was a guess
153
+ * reads very differently from one that looks narrow because the code is
154
+ * decoupled, and this is the only field that distinguishes them.
155
+ */
156
+ rejected_via: string;
109
157
  /**
110
158
  * Literal 0. This module selects from a registry; a non-zero value here
111
159
  * would mean it authored a task, which is K9.
@@ -196,6 +244,80 @@ export function selectRegressions(
196
244
  unresolved: unresolvedSurfaces(graph, candidate),
197
245
  selected: selected.sort((a, b) => a.spec.id.localeCompare(b.spec.id)),
198
246
  skipped: skipped.sort((a, b) => a.localeCompare(b)),
247
+ graph: 'artefact',
248
+ graph_state: null,
249
+ not_indexed: [],
250
+ producing_edges: [],
251
+ rejected_via: '(not applicable — the artefact graph has no mechanism axis)',
252
+ authored: 0,
253
+ };
254
+ }
255
+
256
+ /**
257
+ * The code-graph path (step 3.4).
258
+ *
259
+ * Neighbours a candidate by the symbols its diff touched, over the native code
260
+ * graph, using the same `impact --diff` reverse walk the CLI verb exposes. The
261
+ * neighbourhood's nodes are the seeds at depth 0 (`touched`) plus every
262
+ * accepted reverse-reachable node at its shallowest depth (`neighbour`), which
263
+ * is the identical shape {@link neighbourhood} produces for the artefact graph
264
+ * — so `selectRegressions`' matching logic is reused rather than forked.
265
+ *
266
+ * `touches` is read as FILE paths here, not artefact ids: a code candidate is a
267
+ * diff, and a diff names files. A touched file with no node in the graph lands
268
+ * in `unresolved` and therefore refuses through {@link selectionVerdict}, on
269
+ * exactly the terms the artefact path already uses.
270
+ */
271
+ export function selectRegressionsFromCode(
272
+ g: LoadedGraph,
273
+ candidate: Candidate,
274
+ registry: readonly RegressionSpec[],
275
+ depth: number = DEFAULT_NEIGHBOURHOOD_DEPTH,
276
+ isTest: (relPath: string) => boolean = () => false,
277
+ graphState: GraphState = 'absent',
278
+ ): NeighbourhoodReport {
279
+ const res = impact(g, candidate.touches, graphState, depth, isTest);
280
+ const best = new Map<string, NeighbourhoodNode>();
281
+ for (const seed of res.seeds) {
282
+ best.set(seed, { node: seed, reason: 'touched', depth: 0, via: '', synthetic: false });
283
+ }
284
+ for (const r of res.reached) {
285
+ if (best.has(r.node)) continue;
286
+ best.set(r.node, {
287
+ node: r.node,
288
+ reason: 'neighbour',
289
+ depth: r.depth,
290
+ via: `${r.via}/${r.resolved_via}`,
291
+ synthetic: false,
292
+ });
293
+ }
294
+ const hood = [...best.values()].sort((a, b) => a.depth - b.depth || a.node.localeCompare(b.node));
295
+ const reasonOf = new Map<string, NeighbourReason>(hood.map((n) => [n.node, n.reason]));
296
+ const selected: Selection[] = [];
297
+ const skipped: string[] = [];
298
+ for (const spec of registry) {
299
+ const matched = spec.guards.filter((x) => reasonOf.has(x)).sort((a, b) => a.localeCompare(b));
300
+ if (matched.length === 0) {
301
+ skipped.push(spec.id);
302
+ continue;
303
+ }
304
+ selected.push({
305
+ spec,
306
+ matched,
307
+ reason: matched.some((m) => reasonOf.get(m) === 'touched') ? 'touched' : 'neighbour',
308
+ });
309
+ }
310
+ return {
311
+ candidate_id: candidate.id,
312
+ neighbourhood: hood,
313
+ unresolved: [...res.unresolved_files].sort((a, b) => a.localeCompare(b)),
314
+ selected: selected.sort((a, b) => a.spec.id.localeCompare(b.spec.id)),
315
+ skipped: skipped.sort((a, b) => a.localeCompare(b)),
316
+ graph: 'code',
317
+ graph_state: graphState,
318
+ not_indexed: [...res.not_indexed_files].sort((a, b) => a.localeCompare(b)),
319
+ producing_edges: [...res.producing_edges].sort(),
320
+ rejected_via: res.rejected_via,
199
321
  authored: 0,
200
322
  };
201
323
  }
@@ -209,9 +331,24 @@ export function selectRegressions(
209
331
  */
210
332
  export function selectionVerdict(report: NeighbourhoodReport): readonly string[] | null {
211
333
  const reasons: string[] = [];
334
+ // An ABSENT graph is the case the artefact path's own docstring warns
335
+ // about in the other direction: a lookup that cannot resolve and then
336
+ // selects nothing reports a clean sheet. `behind:N` is deliberately NOT a
337
+ // refusal — a stale graph still answers, and blocking on staleness would
338
+ // be the blocking-gate-on-a-stale-graph that K7 forbids. It is surfaced
339
+ // through `graph_state` so a caller that wants to refuse can.
340
+ if (report.graph === 'code' && report.graph_state === 'absent') {
341
+ reasons.push(
342
+ 'the code graph is absent — a neighbourhood computed over no graph is empty for a ' +
343
+ 'reason that has nothing to do with the candidate. Run `agent-config code-graph build`.',
344
+ );
345
+ }
212
346
  if (report.unresolved.length > 0) {
347
+ // Named per graph: a code-path refusal that said "relation graph" would
348
+ // point a reader at the wrong surface to go fix.
349
+ const which = report.graph === 'code' ? 'code graph' : 'artefact relation graph';
213
350
  reasons.push(
214
- `touched surfaces absent from the relation graph: ${report.unresolved.join(', ')} — ` +
351
+ `touched surfaces absent from the ${which}: ${report.unresolved.join(', ')} — ` +
215
352
  'their neighbourhood is unknown, and an unknown neighbourhood is not an empty one',
216
353
  );
217
354
  }
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Roadmap `status:` values this repository has RETIRED, and the diagnostic a
3
+ * reader gets when one survives in a file.
4
+ *
5
+ * `status: carrier` was deleted by ADR-262. It had two jobs welded together: a
6
+ * structural one (this roadmap is the live destination of an archived parent's
7
+ * `[~]` carries) and an authority one (an autonomous run may not act on it
8
+ * until a human flips it to `ready`). The second was authored by the repository
9
+ * about its own maintainer, was enforced by nothing, and contradicted the drain
10
+ * command's own FORBIDDEN NON-HALT REASONS. The first is a property of the
11
+ * deferral EDGES, which `lint_deferral_integrity` already verifies from both
12
+ * ends, so nothing was left for a node-level status to carry.
13
+ *
14
+ * Deleting a vocabulary term silently is the failure this module exists to
15
+ * prevent: an old file would keep a value nothing reads, and the next author
16
+ * would infer a meaning from its presence. So a retired value is REJECTED with
17
+ * a migration diagnostic rather than ignored.
18
+ *
19
+ * The read is frontmatter-scoped on purpose, and that is inherited rather than
20
+ * newly decided: a fenced `status: carrier` inside a roadmap documenting the
21
+ * syntax is not a declaration, and a body-wide match turned exactly that into a
22
+ * hard failure before a review caught it.
23
+ */
24
+
25
+ /** Retired `status:` values, mapped to the record that retired each one. */
26
+ export const RETIRED_STATUS_VALUES: ReadonlyMap<string, string> = new Map([
27
+ ['carrier', 'ADR-262'],
28
+ ]);
29
+
30
+ /** The leading `---\n…\n---` block, or null when the text carries none. */
31
+ export function frontmatterBlock(text: string): string | null {
32
+ const m = /^---\n([\s\S]*?)\n---/.exec(text);
33
+ return m === null ? null : (m[1] ?? '');
34
+ }
35
+
36
+ /** The frontmatter `status:` value, lower-cased, or null when absent. */
37
+ export function frontmatterStatus(text: string | null | undefined): string | null {
38
+ if (text === null || text === undefined) {
39
+ return null;
40
+ }
41
+ const fm = frontmatterBlock(text);
42
+ if (fm === null) {
43
+ return null;
44
+ }
45
+ const m = /^status:[ \t]*([A-Za-z-]+)[ \t]*$/m.exec(fm);
46
+ return m === null ? null : (m[1] ?? '').toLowerCase();
47
+ }
48
+
49
+ /**
50
+ * The retired value this text declares, or null when it declares none.
51
+ *
52
+ * Returns the value rather than a boolean so the caller can name it in the
53
+ * diagnostic — "status: carrier is retired" is actionable, "invalid status" is
54
+ * not.
55
+ */
56
+ export function declaresRetiredStatus(text: string | null | undefined): string | null {
57
+ const status = frontmatterStatus(text);
58
+ if (status === null) {
59
+ return null;
60
+ }
61
+ return RETIRED_STATUS_VALUES.has(status) ? status : null;
62
+ }
63
+
64
+ /** The migration diagnostic for a retired value. Names the record and the fix. */
65
+ export function retiredStatusDiagnostic(value: string): string {
66
+ const record = RETIRED_STATUS_VALUES.get(value) ?? 'a decision record';
67
+ return (
68
+ `declares the retired \`status: ${value}\`, deleted by ${record}. ` +
69
+ 'A `status:` value no longer decides whether an autonomous run may act on ' +
70
+ 'a roadmap. Use `status: ready` and put any genuine human-ACTION gate in a ' +
71
+ '`## Blockers` entry, or park the file under `agents/roadmaps/later/` with ' +
72
+ 'an `entry_condition`. Both are read by a gate; a status is not.'
73
+ );
74
+ }
@@ -269,6 +269,50 @@ export function readEolCounters(
269
269
  }
270
270
  }
271
271
 
272
+ /**
273
+ * Committed substantive-content floor, in parsed transcript tokens (billable
274
+ * input of the last main-chain assistant record, per `cc_transcript.ts`) — the
275
+ * OR-arm for a session that produced real discussion without ever calling a
276
+ * tool.
277
+ *
278
+ * Derivation (`agents/evidence/analysis/handoff-substantive-threshold.md`, code-comment-allow provenance-comment -- unchanged relocation of a docblock whose whole point is that this threshold may only move on cited evidence
279
+ * 217 sessions of the local Claude store, 2026-08-10): 206 of the 207 sessions
280
+ * carrying an assistant record also carry a `tool_use` block, so this arm
281
+ * serves the ~0.5 % tail and the hosts whose transcripts log no tool blocks at
282
+ * all. 10 000 sits **25× below** the p10 of real working sessions (254 939),
283
+ * so it cannot hide one, and above a single trivial exchange. Changing it is a
284
+ * PR citing evidence, never a drive-by edit.
285
+ */
286
+ export const SUBSTANTIVE_TOKEN_FLOOR = 10_000;
287
+
288
+ /**
289
+ * Does this session hold anything worth resuming?
290
+ *
291
+ * `≥ 1 assistant turn AND (≥ 1 tool call OR parsed tokens ≥ the committed
292
+ * floor)`, read from the counts-only session-eol state.
293
+ *
294
+ * **Fail-open, deliberately** (Phase 1.2): absent, unreadable or mis-shaped
295
+ * state LISTS the candidate rather than filtering it, and a `tool_calls` key
296
+ * missing from a state file written before that counter existed reads as
297
+ * *unknown*, never as zero. A wrongly listed candidate is noise the user
298
+ * scrolls past; a wrongly hidden one is data loss they cannot even see.
299
+ */
300
+ export function is_substantive(counters: StoredEolCounters | null): boolean {
301
+ if (counters === null) return true;
302
+ // Every "unknown" below is a NUMBER test, not an `=== undefined` test.
303
+ // Absent, null and NaN are all unknown, and each one reaches this code by
304
+ // a real path: a state file written before the counter existed is absent,
305
+ // `JSON.stringify` turns NaN into null on the way to disk, and a
306
+ // half-written file yields a parseable object with missing keys. Reading
307
+ // any of them as "counted zero" hides a session that did real work.
308
+ if (!Number.isFinite(counters.assistant_records)) return true;
309
+ if ((counters.assistant_records as number) < 1) return false;
310
+ if (!Number.isFinite(counters.tool_calls)) return true;
311
+ if ((counters.tool_calls as number) >= 1) return true;
312
+ if (!Number.isFinite(counters.final_context_tokens)) return true;
313
+ return (counters.final_context_tokens as number) >= SUBSTANTIVE_TOKEN_FLOOR;
314
+ }
315
+
272
316
  export interface NewLinesRead {
273
317
  /** The complete lines appended since `fromByte` (possibly empty). */
274
318
  text: string;