@event4u/agent-config 15.0.0 → 16.1.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 (283) 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 +301 -347
  5. package/CONTRIBUTING.md +9 -0
  6. package/README.md +2 -2
  7. package/dist/agent-src/commands/analyze/inbox.md +26 -0
  8. package/dist/agent-src/commands/analyze/roadmap-repos.md +27 -0
  9. package/dist/agent-src/commands/challenge-me/closure.md +178 -0
  10. package/dist/agent-src/commands/challenge-me.md +9 -2
  11. package/dist/agent-src/commands/council/default.md +25 -5
  12. package/dist/agent-src/commands/feature/plan.md +28 -1
  13. package/dist/agent-src/commands/feature/roadmap.md +28 -1
  14. package/dist/agent-src/commands/implement-ticket.md +27 -0
  15. package/dist/agent-src/commands/jira-ticket.md +27 -0
  16. package/dist/agent-src/commands/pr/create.md +29 -0
  17. package/dist/agent-src/commands/pr/merge.md +60 -9
  18. package/dist/agent-src/commands/roadmap/ai-council.md +7 -1
  19. package/dist/agent-src/commands/roadmap/create.md +77 -29
  20. package/dist/agent-src/commands/roadmap/materialize.md +27 -0
  21. package/dist/agent-src/commands/roadmap/process-full.md +123 -18
  22. package/dist/agent-src/commands/team.md +22 -0
  23. package/dist/agent-src/contexts/authority/scope-growth-ownership.md +80 -0
  24. package/dist/agent-src/contexts/communication/rules-auto/user-interaction-mechanics.md +97 -0
  25. package/dist/agent-src/contexts/execution/auto-orchestration-activation.md +44 -3
  26. package/dist/agent-src/contexts/execution/autonomy-mechanics.md +15 -15
  27. package/dist/agent-src/contexts/execution/decision-closure-execution.md +93 -0
  28. package/dist/agent-src/contexts/execution/fix-loop-ladder.md +108 -0
  29. package/dist/agent-src/contexts/execution/plan-confidence-gate.md +4 -2
  30. package/dist/agent-src/contexts/execution/roadmap-process-loop.md +75 -6
  31. package/dist/agent-src/guidelines/agent-infra/active-remediation-mechanics.md +57 -0
  32. package/dist/agent-src/guidelines/agent-infra/evaluator-independence-mechanics.md +102 -0
  33. package/dist/agent-src/guidelines/design-fidelity-routing.md +29 -0
  34. package/dist/agent-src/rules/autonomous-execution.md +15 -8
  35. package/dist/agent-src/rules/delegation-policy.md +23 -45
  36. package/dist/agent-src/rules/design-fidelity.md +32 -37
  37. package/dist/agent-src/rules/evaluator-independence.md +43 -49
  38. package/dist/agent-src/rules/test-first.md +41 -0
  39. package/dist/agent-src/rules/user-interaction.md +31 -30
  40. package/dist/agent-src/skills/accessibility-auditor/data/manifest.json +3 -3
  41. package/dist/agent-src/skills/ai-council/SKILL.md +11 -3
  42. package/dist/agent-src/skills/api-design/data/manifest.json +3 -3
  43. package/dist/agent-src/skills/database/data/manifest.json +4 -4
  44. package/dist/agent-src/skills/database/data/query-tuning.csv +3 -3
  45. package/dist/agent-src/skills/design-review/SKILL.md +50 -0
  46. package/dist/agent-src/skills/error-handling-patterns/SKILL.md +9 -0
  47. package/dist/agent-src/skills/existing-ui-audit/SKILL.md +14 -4
  48. package/dist/agent-src/skills/experiment-loop/SKILL.md +31 -1
  49. package/dist/agent-src/skills/roadmap-writing/SKILL.md +1 -1
  50. package/dist/agent-src/skills/storybook-workshop/SKILL.md +42 -3
  51. package/dist/agent-src/skills/threat-modeling/SKILL.md +9 -4
  52. package/dist/agent-src/skills/threat-modeling/data/manifest.json +4 -4
  53. package/dist/agent-src/skills/verify-repair-loop/SKILL.md +37 -3
  54. package/dist/agent-src/templates/agent-settings.md +1 -1
  55. package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
  56. package/dist/agent-src/templates/roadmaps.md +188 -0
  57. package/dist/agent-src/templates/scripts/work_engine/directives/backend/_self_fix.ts +62 -2
  58. package/dist/agent-src/templates/scripts/work_engine/directives/backend/test.ts +1 -0
  59. package/dist/agent-src/templates/scripts/work_engine/directives/backend/verify.ts +1 -0
  60. package/dist/cli-delegate/check_update_banner.js +40 -2
  61. package/dist/cli-delegate/chunk-4E2W63DA.js +335 -0
  62. package/dist/cli-delegate/chunk-5R7HOLAF.js +99 -0
  63. package/dist/cli-delegate/{chunk-IVBPRCQO.js → chunk-6KFEC3G7.js} +1 -1
  64. package/dist/cli-delegate/{chunk-2TAQ2K4V.js → chunk-6KM6ORRY.js} +55 -386
  65. package/dist/cli-delegate/{chunk-2XY7ILBT.js → chunk-FC75PGJE.js} +42 -2
  66. package/dist/cli-delegate/{chunk-GYC3VRYN.js → chunk-FXYUYU4N.js} +57 -13
  67. package/dist/cli-delegate/{chunk-55ACLDUE.js → chunk-GJCBB2UG.js} +168 -10
  68. package/dist/cli-delegate/{chunk-AK6HODNQ.js → chunk-HDVVGPQW.js} +873 -539
  69. package/dist/cli-delegate/chunk-NUHLI2RX.js +80 -0
  70. package/dist/cli-delegate/{chunk-HZ52BRGG.js → chunk-P6ZHP5W2.js} +15 -90
  71. package/dist/cli-delegate/{chunk-TYSY33MD.js → chunk-QJWAUMJW.js} +12 -2
  72. package/dist/cli-delegate/{chunk-57PN4GYJ.js → chunk-RGNIWTEA.js} +1 -1
  73. package/dist/cli-delegate/chunk-ROYYTEZU.js +39 -0
  74. package/dist/cli-delegate/{chunk-2KKXJW25.js → chunk-S3VFXMTM.js} +1 -1
  75. package/dist/cli-delegate/{chunk-3FKR64R5.js → chunk-UNCRJBT3.js} +31 -7
  76. package/dist/cli-delegate/{chunk-2DK2VJWG.js → chunk-WGXTVKCQ.js} +20 -6
  77. package/dist/cli-delegate/chunk-YDPILCOL.js +152 -0
  78. package/dist/cli-delegate/cmd_analyze_session.js +1 -1
  79. package/dist/cli-delegate/cmd_brand_status.js +1 -1
  80. package/dist/cli-delegate/cmd_conformance.js +918 -325
  81. package/dist/cli-delegate/cmd_doctor.js +7 -5
  82. package/dist/cli-delegate/cmd_explain.js +5 -3
  83. package/dist/cli-delegate/cmd_export.js +9 -7
  84. package/dist/cli-delegate/cmd_fleet.js +4 -2
  85. package/dist/cli-delegate/cmd_handoff.js +6 -6
  86. package/dist/cli-delegate/cmd_mcp_available.js +1 -1
  87. package/dist/cli-delegate/cmd_migrate.js +2 -2
  88. package/dist/cli-delegate/cmd_packs_active.js +3 -2
  89. package/dist/cli-delegate/cmd_preflight.js +4 -2
  90. package/dist/cli-delegate/cmd_prune.js +10 -8
  91. package/dist/cli-delegate/cmd_route_audit.js +2 -2
  92. package/dist/cli-delegate/cmd_route_explain.js +1 -1
  93. package/dist/cli-delegate/cmd_session_recycle.js +4 -4
  94. package/dist/cli-delegate/cmd_settings_get.js +28 -2
  95. package/dist/cli-delegate/cmd_settings_migrate.js +9 -7
  96. package/dist/cli-delegate/cmd_settings_set.js +14 -1
  97. package/dist/cli-delegate/cmd_sync.js +10 -8
  98. package/dist/cli-delegate/cmd_uninstall.js +16 -13
  99. package/dist/cli-delegate/cmd_update.js +1 -1
  100. package/dist/cli-delegate/cmd_upgrade.js +1 -1
  101. package/dist/cli-delegate/cmd_validate.js +12 -10
  102. package/dist/cli-delegate/cmd_versions.js +1 -1
  103. package/dist/discovery/deprecation-report.md +1 -1
  104. package/dist/discovery/discovery-manifest.json +87 -40
  105. package/dist/discovery/discovery-manifest.json.sha256 +1 -1
  106. package/dist/discovery/discovery-manifest.summary.md +4 -4
  107. package/dist/discovery/dormancy-report.md +1 -1
  108. package/dist/discovery/orphan-report.md +1 -1
  109. package/dist/discovery/packs.json +10 -8
  110. package/dist/discovery/trust-report.md +3 -3
  111. package/dist/discovery/workspaces.json +8 -6
  112. package/dist/hooks/dispatch.js +2601 -1073
  113. package/dist/install/claudeRuleRewrite.js +14 -3
  114. package/dist/install/conflict.js +60 -10
  115. package/dist/install/conflictTracker.js +218 -0
  116. package/dist/install/fsPrimitives.js +54 -0
  117. package/dist/install/install.mjs +2008 -1481
  118. package/dist/install/manifestFiles.js +259 -0
  119. package/dist/install/preflight.js +14 -2
  120. package/dist/install/preserve.js +157 -0
  121. package/dist/install/recordedOwnership.js +132 -0
  122. package/dist/install/txlog.js +11 -2
  123. package/dist/mcp/registry-manifest.json +3 -3
  124. package/dist/mcp/server.json +2 -2
  125. package/dist/mcp/server.mjs +1612 -667
  126. package/dist/router.json +1 -1
  127. package/dist/scripts/_lib/agent_settings.js +3 -2
  128. package/dist/scripts/_lib/fs_atomic.js +167 -0
  129. package/dist/scripts/_lib/installed_lock.js +1 -1
  130. package/dist/scripts/_lib/installed_tools.js +460 -0
  131. package/dist/scripts/_lib/lean_projection_mode.js +3 -2
  132. package/dist/scripts/_lib/settings_renamed_keys.js +78 -0
  133. package/dist/server/io/yamlIO.js +50 -0
  134. package/dist/server/routes/install.js +8 -0
  135. package/dist/server/routes/wizard.js +9 -3
  136. package/dist/server/schemas/settings.js +17 -5
  137. package/dist/shared/missionExecution.js +97 -0
  138. package/dist/ui/assets/{index-D0jaCRPM.js → index-DH1xc62v.js} +17 -17
  139. package/dist/ui/index.html +1 -1
  140. package/docs/MIGRATION.md +103 -0
  141. package/docs/contracts/settings-classes.md +26 -7
  142. package/docs/guidelines/agent-infra/active-remediation-mechanics.md +57 -0
  143. package/docs/guidelines/agent-infra/evaluator-independence-mechanics.md +102 -0
  144. package/docs/guidelines/design-fidelity-routing.md +29 -0
  145. package/package.json +1 -1
  146. package/src/agent-src/templates/scripts/work_engine/directives/backend/_self_fix.ts +62 -2
  147. package/src/agent-src/templates/scripts/work_engine/directives/backend/test.ts +1 -0
  148. package/src/agent-src/templates/scripts/work_engine/directives/backend/verify.ts +1 -0
  149. package/src/config/agent-settings.template.yml +139 -27
  150. package/src/config/auto-rule-budget.json +16 -0
  151. package/src/config/ci-local-parity.yml +18 -5
  152. package/src/config/continuity-surface.json +28 -0
  153. package/src/config/gate-coverage.yml +146 -13
  154. package/src/config/gate-violation-baselines.json +6 -6
  155. package/src/config/hook-token-budget.json +2 -0
  156. package/src/config/host-injection-effect.json +1 -1
  157. package/src/config/loop-surfaces.yaml +215 -0
  158. package/src/config/platform-anchor.json +3 -2
  159. package/src/config/preamble-payload-budget.json +12 -7
  160. package/src/config/preamble-payload-exceptions.json +28 -0
  161. package/src/config/release-gate-locality.yml +11 -0
  162. package/src/config/routing-coverage-seed.json +18 -4
  163. package/src/config/rule-enforcement-baseline.json +0 -1
  164. package/src/install/claudeRuleRewrite.ts +16 -2
  165. package/src/install/conflict.ts +65 -9
  166. package/src/install/conflictTracker.ts +299 -0
  167. package/src/install/fsPrimitives.ts +54 -0
  168. package/src/install/manifestFiles.ts +312 -0
  169. package/src/install/preflight.ts +16 -2
  170. package/src/install/preserve.ts +205 -0
  171. package/src/install/recordedOwnership.ts +149 -0
  172. package/src/install/txlog.ts +11 -2
  173. package/src/install/types.ts +19 -4
  174. package/src/scripts/_cli/cmd_conformance.ts +88 -15
  175. package/src/scripts/_cli/cmd_doctor.ts +4 -3
  176. package/src/scripts/_cli/cmd_route_explain.ts +9 -2
  177. package/src/scripts/_cli/cmd_settings_get.ts +44 -0
  178. package/src/scripts/_cli/doctor_execution.ts +90 -0
  179. package/src/scripts/_dispatch.bash +3 -1
  180. package/src/scripts/_lib/activation_payload.ts +247 -0
  181. package/src/scripts/_lib/agent_settings.ts +4 -2
  182. package/src/scripts/_lib/authority_path.ts +102 -0
  183. package/src/scripts/_lib/base_ref_payload.ts +315 -0
  184. package/src/scripts/_lib/cascade_base.ts +94 -0
  185. package/src/scripts/_lib/changelog_eras.ts +7 -2
  186. package/src/scripts/_lib/continuation_ladder.ts +149 -2
  187. package/src/scripts/_lib/council_transport.ts +129 -0
  188. package/src/scripts/_lib/delivery_ready.ts +112 -0
  189. package/src/scripts/_lib/dropped_decision.ts +142 -0
  190. package/src/scripts/_lib/forge_protection.ts +267 -0
  191. package/src/scripts/_lib/hook_platform_keys.ts +27 -0
  192. package/src/scripts/_lib/installed_lock.ts +1 -1
  193. package/src/scripts/_lib/json_python_parity.ts +127 -0
  194. package/src/scripts/_lib/lean_projection_mode.ts +14 -3
  195. package/src/scripts/_lib/loop_surfaces.ts +382 -0
  196. package/src/scripts/_lib/measured_payload_ceiling.ts +424 -0
  197. package/src/scripts/_lib/mission_record.ts +190 -0
  198. package/src/scripts/_lib/obligation_frequency.ts +217 -0
  199. package/src/scripts/_lib/obligations.ts +402 -0
  200. package/src/scripts/_lib/orchestration_record.ts +11 -20
  201. package/src/scripts/_lib/payload_catalogue_completeness.ts +211 -0
  202. package/src/scripts/_lib/release_findings_ingest.ts +324 -0
  203. package/src/scripts/_lib/release_highlights.ts +199 -10
  204. package/src/scripts/_lib/release_holds.ts +493 -0
  205. package/src/scripts/_lib/release_migration_gate.ts +184 -0
  206. package/src/scripts/_lib/release_tag_dispatch.ts +63 -0
  207. package/src/scripts/_lib/review_baseline.ts +254 -0
  208. package/src/scripts/_lib/review_skipped_record.ts +61 -1
  209. package/src/scripts/_lib/rides_along.ts +102 -0
  210. package/src/scripts/_lib/settings_renamed_keys.ts +82 -0
  211. package/src/scripts/_lib/standing_bound_ratchet.ts +216 -103
  212. package/src/scripts/_lib/test_delta.ts +166 -0
  213. package/src/scripts/_lib/test_provenance.ts +171 -0
  214. package/src/scripts/_lib/turn_end_refusals.ts +51 -4
  215. package/src/scripts/_lib/typed_op_grant.ts +246 -0
  216. package/src/scripts/_lib/typed_op_watch.ts +185 -0
  217. package/src/scripts/ai_council/config.ts +8 -11
  218. package/src/scripts/ai_council/fallback_config.ts +5 -3
  219. package/src/scripts/ai_council/ownership_classes.ts +118 -0
  220. package/src/scripts/ask_block_census.ts +285 -15
  221. package/src/scripts/check_beta_review_markers.ts +35 -4
  222. package/src/scripts/check_enforcement_matrix.ts +624 -0
  223. package/src/scripts/check_finding_dispositions.ts +167 -16
  224. package/src/scripts/check_gate_reachability.ts +173 -10
  225. package/src/scripts/check_generator_sync.ts +954 -0
  226. package/src/scripts/check_held_object_arrivals.ts +381 -0
  227. package/src/scripts/check_preamble_payload_budget.ts +474 -124
  228. package/src/scripts/check_release_highlights.ts +33 -1
  229. package/src/scripts/check_release_holds.ts +567 -0
  230. package/src/scripts/check_release_published.ts +1 -1
  231. package/src/scripts/check_reply_consistency.ts +86 -17
  232. package/src/scripts/check_rule_projection_integrity.ts +22 -12
  233. package/src/scripts/check_test_delta.ts +294 -0
  234. package/src/scripts/check_test_weakening.ts +299 -0
  235. package/src/scripts/closure_scan.ts +333 -0
  236. package/src/scripts/compile_hook_manifest.ts +43 -3
  237. package/src/scripts/council_record_shape.ts +209 -0
  238. package/src/scripts/hook_manifest.json +1 -1
  239. package/src/scripts/hook_manifest.yaml +127 -12
  240. package/src/scripts/hooks/block_kernel_rule_writes.ts +12 -2
  241. package/src/scripts/hooks/concern_registry.ts +6 -0
  242. package/src/scripts/hooks/design_pass_hook.ts +128 -3
  243. package/src/scripts/hooks/end_review_nudge_hook.ts +101 -21
  244. package/src/scripts/hooks/evidence_independence.ts +96 -0
  245. package/src/scripts/hooks/memory_recall_hook.ts +540 -0
  246. package/src/scripts/hooks/obligation_settle_hook.ts +182 -0
  247. package/src/scripts/hooks/review_baseline_hook.ts +130 -0
  248. package/src/scripts/hooks/rule_inject_hook.ts +49 -1
  249. package/src/scripts/hooks/run_continuation_hook.ts +17 -20
  250. package/src/scripts/hooks/turn_end_gate_hook.ts +232 -14
  251. package/src/scripts/hooks_doctor.ts +89 -1
  252. package/src/scripts/hooks_status.ts +19 -0
  253. package/src/scripts/install.ts +107 -207
  254. package/src/scripts/interruption_report.ts +8 -2
  255. package/src/scripts/janitor.ts +15 -1
  256. package/src/scripts/lint_decision_classes.ts +464 -0
  257. package/src/scripts/lint_hook_manifest.ts +11 -0
  258. package/src/scripts/lint_major_migration_sections.ts +450 -0
  259. package/src/scripts/lint_plan_risk_register.ts +47 -3
  260. package/src/scripts/lint_roadmap_blockers.ts +61 -0
  261. package/src/scripts/lint_roadmap_producers.ts +202 -0
  262. package/src/scripts/measure_rule_budget.ts +101 -5
  263. package/src/scripts/measure_turn_end_gate.ts +302 -5
  264. package/src/scripts/new_roadmap.ts +37 -0
  265. package/src/scripts/orchestration_record.ts +0 -5
  266. package/src/scripts/release.ts +66 -162
  267. package/src/scripts/release_drill.ts +460 -15
  268. package/src/scripts/release_env.ts +1 -1
  269. package/src/scripts/release_publication.ts +52 -1
  270. package/src/scripts/report_enforcement_drift.ts +566 -0
  271. package/src/scripts/report_held_object_arrivals.ts +456 -0
  272. package/src/scripts/report_host_injection_effect.ts +3 -11
  273. package/src/scripts/report_standing_payload_by_host.ts +26 -152
  274. package/src/scripts/schemas/agent-settings.schema.json +23 -0
  275. package/src/scripts/schemas/command.schema.json +4 -0
  276. package/src/scripts/scope_growth.ts +219 -0
  277. package/src/scripts/sync_agent_settings.ts +140 -18
  278. package/src/scripts/trigger_eval_grandfather.json +0 -1
  279. package/src/scripts/ui_conformance_probe.ts +622 -0
  280. package/src/server/io/yamlIO.ts +48 -0
  281. package/src/server/schemas/settings.ts +31 -7
  282. package/src/shared/missionExecution.ts +126 -0
  283. package/dist/cli-delegate/chunk-523HCFYO.js +0 -90
@@ -0,0 +1,182 @@
1
+ #!/usr/bin/env tsx
2
+ /**
3
+ * obligation-settle — the reader the delivered-obligation record never had.
4
+ *
5
+ * The injector has always known which rules it delivered into a session. Until
6
+ * this concern, nothing asked whether the turn that received them discharged
7
+ * any. This is that question, asked at turn-end, and — for now — asked out
8
+ * loud and answered with nothing.
9
+ *
10
+ * SHADOW ONLY. IT REFUSES NOTHING, AND THAT IS NOT A PLACEHOLDER.
11
+ * It computes the verdict a future armed detector would reach, records it, and
12
+ * returns allow on every path. Arming is gated on a bar pre-registered in
13
+ * `docs/CLAIMS.md` BEFORE any code able to refuse exists, so that the bar
14
+ * cannot be chosen to fit the numbers the shadow window happened to produce.
15
+ * The estate already has one blocking turn-end concern and the first-ranked
16
+ * risk against this work is that a second one refuses a clean turn and an
17
+ * operator disables the carrier for good.
18
+ *
19
+ * THE TOUCHED SET COMES FROM THE DIFF, NOT FROM TOOL EVENTS.
20
+ * A tool event carries the path the tool was given. A shell heredoc writing
21
+ * three files reports one Bash call and no paths at all, so a detector reading
22
+ * tool events is blind to exactly the turns that wrote the most. `git diff
23
+ * --numstat HEAD` plus `git ls-files --others` sees the tree as it actually
24
+ * is, which is the only description that cannot be evaded by how the write was
25
+ * spelled. The helpers are the end-review concern's own, imported rather than
26
+ * re-derived.
27
+ *
28
+ * IT REFUSES ONLY ON CLASSES THAT COULD HAVE A DISCHARGE.
29
+ * `hook`, `validator` and `test` name carriers that run and leave traces.
30
+ * `observer`, `instruction-only` and `none` do not, so demanding a discharge
31
+ * for one would demand evidence nobody can produce — and 69 of 97 delivered
32
+ * rules on the frozen corpus are class `none`.
33
+ *
34
+ * IT IS SILENT WHILE A SUBAGENT DISPATCH IS OPEN.
35
+ * Three of the turn-end gate's six detectors are gated this way and three are
36
+ * not; the difference is whether the finding could be about work a dispatch is
37
+ * still doing. This one could — a subagent writing UI files is the canonical
38
+ * case — so it takes the gated side.
39
+ */
40
+
41
+ import path from 'node:path';
42
+ import process from 'node:process';
43
+
44
+ import { may_refuse_on } from '../_lib/obligation_frequency.js';
45
+ import { appendShadow, readDelivered, readDischarged, stamp } from '../_lib/obligations.js';
46
+ import { loadRouter, matchTierRules } from '../_lib/rule_injection.js';
47
+ import { gitNumstatRows, isDocPath, untrackedNonDocFiles } from './end_review_nudge_hook.js';
48
+ import { openRecordStats } from './subagent_ledger_hook.js';
49
+
50
+ const EXIT_ALLOW = 0;
51
+
52
+ /**
53
+ * Every non-doc path this turn wrote, tracked or not.
54
+ *
55
+ * Doc paths are excluded on the same grounds the end-review concern excludes
56
+ * them: a markdown edit is not the kind of change whose obligations this
57
+ * detector can speak to, and including them would make almost every turn a
58
+ * candidate.
59
+ */
60
+ export function touchedPaths(cwd: string): string[] {
61
+ const tracked = gitNumstatRows(cwd)
62
+ .map((r) => r.path)
63
+ .filter((p) => p !== '' && !isDocPath(p));
64
+ const untracked = untrackedNonDocFiles(cwd);
65
+ return [...new Set([...tracked, ...untracked])];
66
+ }
67
+
68
+ export interface SettleVerdict {
69
+ /** Non-doc paths this turn wrote. */
70
+ readonly touched: string[];
71
+ /** Delivered rules whose path triggers match something touched. */
72
+ readonly candidates: string[];
73
+ /** Candidates with no recorded discharge, and a class that could have one. */
74
+ readonly missing: string[];
75
+ /** True when a subagent dispatch is open — every other field is then advisory only. */
76
+ readonly dispatchOpen: boolean;
77
+ }
78
+
79
+ /**
80
+ * What an armed detector would conclude, computed without refusing anything.
81
+ *
82
+ * Exported whole rather than as a boolean so the shadow row, the tests and any
83
+ * later armed path all read the SAME computation. A detector whose shadow
84
+ * measurement and live behaviour come from different code measures nothing.
85
+ */
86
+ export function computeVerdict(root: string, session: string): SettleVerdict {
87
+ const dispatchOpen = (() => {
88
+ try {
89
+ return openRecordStats(root).open_count > 0;
90
+ } catch {
91
+ // An unreadable ledger is not a reason to change the verdict.
92
+ return false;
93
+ }
94
+ })();
95
+
96
+ const touched = touchedPaths(root);
97
+ if (touched.length === 0) {
98
+ return { touched, candidates: [], missing: [], dispatchOpen };
99
+ }
100
+
101
+ const delivered = readDelivered(root, session);
102
+ if (delivered.length === 0) {
103
+ return { touched, candidates: [], missing: [], dispatchOpen };
104
+ }
105
+
106
+ let pathMatched = new Set<string>();
107
+ try {
108
+ const router = loadRouter(root);
109
+ // Empty prompt, so only PATH triggers can fire. A turn's file writes
110
+ // are not a restatement of the user's request and must not re-fire
111
+ // keyword rules — the same reasoning the injector applies on its own
112
+ // tool slot.
113
+ pathMatched = new Set(matchTierRules(router, '', touched, null).map((m) => m.id));
114
+ } catch {
115
+ return { touched, candidates: [], missing: [], dispatchOpen };
116
+ }
117
+
118
+ const candidates = delivered
119
+ .filter((r) => pathMatched.has(r.rule) && may_refuse_on(r.cls))
120
+ .map((r) => r.rule);
121
+
122
+ const discharged = new Set(readDischarged(root, session).map((r) => r.rule));
123
+ const missing = candidates.filter((id) => !discharged.has(id));
124
+ return { touched, candidates: [...new Set(candidates)], missing: [...new Set(missing)] , dispatchOpen };
125
+ }
126
+
127
+ /**
128
+ * Whether this reading should force a continuation, were anything armed.
129
+ *
130
+ * An UNCHANGED missing set on a second reading means the previous nudge moved
131
+ * nothing, so nudging again would loop rather than progress. The obligations
132
+ * stay OPEN — that is the whole point, and it is the side of the source set's
133
+ * self-contradiction this ships. A budget running out is a fact about the
134
+ * budget; letting it write a policy verdict is how an unmet obligation becomes
135
+ * a satisfied one with nobody deciding, and an exhausted budget is exactly the
136
+ * moment the obligation is least likely to have been met.
137
+ *
138
+ * There is therefore no `waived` state anywhere in this file, and no code path
139
+ * that writes one.
140
+ */
141
+ export function shouldContinue(attempt: number): boolean {
142
+ return attempt <= 1;
143
+ }
144
+
145
+ export function main(): number {
146
+ const root = process.cwd();
147
+ const session = (process.env['CLAUDE_CODE_SESSION_ID'] ?? '').trim();
148
+ if (session === '') return EXIT_ALLOW;
149
+
150
+ let verdict: SettleVerdict;
151
+ try {
152
+ verdict = computeVerdict(root, session);
153
+ } catch {
154
+ // A detector that cannot compute has nothing to say. It never has a
155
+ // refusal to withhold, so there is no safe-vs-unsafe choice here.
156
+ return EXIT_ALLOW;
157
+ }
158
+
159
+ // Silent while a dispatch is open: the files may be a subagent's, still
160
+ // being written, and a shadow row claiming otherwise would poison the very
161
+ // corpus the arming decision is measured against.
162
+ if (verdict.dispatchOpen) return EXIT_ALLOW;
163
+ if (verdict.missing.length === 0) return EXIT_ALLOW;
164
+
165
+ const attempt = appendShadow(root, session, verdict.missing, stamp());
166
+ if (attempt === 0) return EXIT_ALLOW;
167
+
168
+ // ONE line for the whole missing set, never one per obligation — the same
169
+ // aggregation the row itself uses, for the same reason.
170
+ process.stderr.write(
171
+ `obligation-settle (shadow, refusing nothing): ${verdict.missing.length} `
172
+ + `obligation(s) with no recorded discharge after writing `
173
+ + `${verdict.touched.length} file(s): ${verdict.missing.join(', ')}`
174
+ + `${shouldContinue(attempt) ? '' : ' — unchanged since the last reading; still open'}\n`,
175
+ );
176
+ return EXIT_ALLOW;
177
+ }
178
+
179
+ // Bundle-safety: never auto-run when inlined into an esbuild bundle.
180
+ if (process.argv[1] !== undefined && path.basename(process.argv[1]).startsWith('obligation_settle')) {
181
+ process.exit(main());
182
+ }
@@ -0,0 +1,130 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * review-baseline — `session_start` concern
4
+ * (road-to-a-question-that-survives-the-turn Phase 2).
5
+ *
6
+ * WHAT IT DOES, and it is the whole of it: on session start, record how many
7
+ * non-doc lines were ALREADY dirty, and what HEAD was. `end-review-nudge`
8
+ * subtracts that at `stop` so a session is charged for its own mutation instead
9
+ * of for whatever the working tree happened to be carrying.
10
+ *
11
+ * THE DEFECT IT CLOSES. `end_review_nudge_hook`'s own header names it: a dirty
12
+ * tree that predates the session is counted as the session's, and the reason it
13
+ * was not fixed was "no such baseline exists anywhere a `stop` concern can read
14
+ * it". That was true of a phase with a one-concern scope, and stale by the time
15
+ * it mattered — `session_start` is bound on seven hosts carrying fourteen
16
+ * concerns. The measured consequence of leaving it: on a branch deliberately
17
+ * accumulating work, the nudge read 1,771 lines of two crashed sessions'
18
+ * uncommitted work and fired every turn. An advisory that always fires is noise,
19
+ * and noise is worse than silence because it trains the reader to skip the one
20
+ * that mattered.
21
+ *
22
+ * MEASURED WITH THE READER'S OWN INSTRUMENT. `totalNonDocMutatedLinesWithMeasure`
23
+ * is imported from the nudge rather than reimplemented, so the two sides cannot
24
+ * drift on what a "non-doc mutated line" is. A second counting implementation
25
+ * would produce a baseline in one unit and a measurement in another, and the
26
+ * subtraction would be silently wrong rather than visibly broken.
27
+ *
28
+ * CAPTURE ONLY. It never emits context, never warns, and always returns 0. A
29
+ * `session_start` concern that could refuse would turn an unreadable git
30
+ * directory into a session that cannot start, which is a far worse failure than
31
+ * the noise it is fixing.
32
+ *
33
+ * NO PATHS, NO FINGERPRINTS. The record is two numbers and a timestamp. A
34
+ * path-level change set was the shape the source draft proposed; it is a bigger
35
+ * object with a bigger privacy surface, and nothing in the nudge consumes one.
36
+ * The file cannot hold a path, a diff or a file body by construction, which is
37
+ * the same PII-exclusion-by-construction the telemetry record keeps.
38
+ */
39
+ import * as fs from 'node:fs';
40
+ import * as path from 'node:path';
41
+ import { fileURLToPath, pathToFileURL } from 'node:url';
42
+
43
+ import {
44
+ baselineStateFile,
45
+ currentHeadSha,
46
+ type ReviewBaseline,
47
+ } from '../_lib/review_baseline.js';
48
+ import { deriveSessionKey, totalNonDocMutatedLinesWithMeasure } from './end_review_nudge_hook.js';
49
+ import { unwrap, type JsonObject, type JsonValue } from './envelope.js';
50
+ import { readHookStdin } from './hook_stdin.js';
51
+ import { atomic_write_json } from './state_io.js';
52
+
53
+ const EXIT_OK = 0;
54
+
55
+ export function buildBaseline(workspaceRoot: string, now: Date = new Date()): ReviewBaseline {
56
+ const measured = totalNonDocMutatedLinesWithMeasure(workspaceRoot);
57
+ return {
58
+ head_sha: currentHeadSha(workspaceRoot),
59
+ baseline_lines: measured.lines,
60
+ // Carried, not discarded. Past `UNTRACKED_FILE_CAP` the count is a
61
+ // synthetic `THRESHOLD + 1 + tracked` chosen to be over the bar rather
62
+ // than to be true, and subtracting that from a later exact count is
63
+ // arithmetic over two different quantities. The consumer refuses the
64
+ // subtraction when the two measures disagree; it can only do that if the
65
+ // writer says which one it took.
66
+ measure: measured.measure,
67
+ written_at: now.toISOString(),
68
+ };
69
+ }
70
+
71
+ export function main(): number {
72
+ let envelope: JsonObject;
73
+ let payload: JsonObject;
74
+ try {
75
+ [envelope, payload] = unwrap(readHookStdin(), 'claude');
76
+ } catch {
77
+ return EXIT_OK;
78
+ }
79
+
80
+ const event = String(envelope['event'] ?? '');
81
+ if (event !== '' && event !== 'session_start') return EXIT_OK;
82
+
83
+ const workspaceRoot =
84
+ String((envelope['workspace_root'] as JsonValue | undefined) ?? '').trim() || process.cwd();
85
+
86
+ // A baseline is written ONCE per session. A host that fires session_start
87
+ // twice — Cline maps both TaskStart and TaskResume onto it — must not
88
+ // re-baseline mid-session: the second write would fold everything the
89
+ // session had already changed into its own baseline and the nudge would go
90
+ // permanently quiet for that session. Over-writing is the silent failure
91
+ // direction, so it is the one guarded.
92
+ const sessionKey = deriveSessionKey(envelope, payload);
93
+ const file = baselineStateFile(workspaceRoot, sessionKey);
94
+ try {
95
+ if (fs.existsSync(file)) return EXIT_OK;
96
+ } catch {
97
+ return EXIT_OK;
98
+ }
99
+
100
+ try {
101
+ // `atomic_write_json` is already `AGENT_CONFIG_REPLAY`-aware, so a
102
+ // fixture-replay run leaves no baseline behind.
103
+ atomic_write_json(file, buildBaseline(workspaceRoot) as unknown as JsonObject);
104
+ } catch {
105
+ // An unwritable state dir degrades to today's behaviour — the nudge
106
+ // reads `absent` and does not subtract. Never a failed session start.
107
+ }
108
+ return EXIT_OK;
109
+ }
110
+
111
+ // Bundle-safety: never auto-run when inlined into an esbuild bundle, where
112
+ // every module shares the bundle's `import.meta.url`.
113
+ declare const __AGENT_CONFIG_BUNDLE__: boolean | undefined;
114
+ function _isCliEntry(): boolean {
115
+ if (typeof __AGENT_CONFIG_BUNDLE__ !== 'undefined' && __AGENT_CONFIG_BUNDLE__) return false;
116
+ if (process.argv[1] === undefined) return false;
117
+ const argvUrl = pathToFileURL(path.resolve(process.argv[1])).href;
118
+ if (import.meta.url === argvUrl) return true;
119
+ try {
120
+ const here = fs.realpathSync(fileURLToPath(import.meta.url));
121
+ const argv = fs.realpathSync(path.resolve(process.argv[1]));
122
+ return here === argv;
123
+ } catch {
124
+ return false;
125
+ }
126
+ }
127
+
128
+ if (_isCliEntry()) {
129
+ process.exit(main());
130
+ }
@@ -11,7 +11,14 @@
11
11
  * the 36.2 %-against-48 % run at `docs/CLAIMS.md:188-189`. Whatever that
12
12
  * instrument's flaws — and ADR-202 closed it — it points one way.
13
13
  *
14
- * SHIPPED ON FOR CLAUDE CODE SINCE ADR-265 — corrected 2026-09-08 (R2 finding
14
+ * SHIPPED ON FOR CLAUDE CODE SINCE ADR-267 — the delivery-default record, and
15
+ * the citation is corrected here (2026-09-13) from ADR-265, which decides
16
+ * something else entirely: whether the iron-law reserve is activated, answered
17
+ * no, with the verifier kept inside the change. Nothing in it ships a
18
+ * projection mode. ADR-267 `delivery-default-for-claude-code` is the record
19
+ * that turns this concern on for this host, and it is the one whose
20
+ * `review_trigger` fires if a rule stops arriving. Previously corrected
21
+ * 2026-09-08 (R2 finding
15
22
  * 4), because this paragraph read "DEFAULT OFF, AND OFF MEANS ZERO BYTES …
16
23
  * under every shipped default it emits nothing … there is no measured emission
17
24
  * to register yet" and all four clauses became false in the same change that
@@ -94,6 +101,8 @@ import {
94
101
  normalizeLeanProjectionMode,
95
102
  resolveLeanProjectionHosts,
96
103
  } from '../_lib/lean_projection_mode.js';
104
+ import { enforcement_class_from_frontmatter } from '../_lib/obligation_frequency.js';
105
+ import { appendDelivered, stamp, type DeliveredRow } from '../_lib/obligations.js';
97
106
  import {
98
107
  loadRuleBody,
99
108
  loadRouter,
@@ -237,6 +246,44 @@ export interface Injection {
237
246
  body: string;
238
247
  }
239
248
 
249
+ /**
250
+ * Record what this fire delivered, so something can later ask whether the turn
251
+ * discharged it.
252
+ *
253
+ * The write happens HERE and not in a later slot because this is the only
254
+ * place that knows the answer. By the time a turn ends, the selection that
255
+ * produced these ids is gone — the router would have to be re-run against a
256
+ * prompt nobody kept, which is a different computation that could disagree.
257
+ *
258
+ * EMITTER RECORD, NEVER A COMPLIANCE ONE. A row says a body was emitted into
259
+ * this slot. It does not say the model read it, and the ledger's own header
260
+ * carries the council lock that forbids reading it that way.
261
+ *
262
+ * Best-effort by construction: every failure path inside `appendDelivered`
263
+ * returns rather than throws, and this wrapper adds one more guard so a
264
+ * ledger problem can never turn a successful injection into a failed hook.
265
+ * A missed reading is cheaper than a refused turn, and this concern has no
266
+ * refusal to offer anyway.
267
+ */
268
+ export function recordDelivered(root: string, session: string, ruleIds: string[]): number {
269
+ try {
270
+ const now = stamp();
271
+ const rows: DeliveredRow[] = ruleIds.map((id) => {
272
+ const body = loadRuleBody(root, id);
273
+ return {
274
+ rule: id,
275
+ // A rule with no projected body declares nothing this can read,
276
+ // which is `none` — the same answer as a body declaring none.
277
+ cls: body === null ? 'none' : enforcement_class_from_frontmatter(body),
278
+ at: now,
279
+ };
280
+ });
281
+ return appendDelivered(root, session, rows);
282
+ } catch {
283
+ return 0;
284
+ }
285
+ }
286
+
240
287
  /**
241
288
  * Build the injection for one event, or `null` for silence.
242
289
  *
@@ -371,6 +418,7 @@ export function main(argv: readonly string[] = process.argv.slice(2)): number {
371
418
 
372
419
  for (const id of injection.rules) seen.add(id);
373
420
  writeSeen(root, session, seen);
421
+ recordDelivered(root, session, injection.rules);
374
422
 
375
423
  process.stdout.write(
376
424
  `${JSON.stringify({
@@ -185,6 +185,7 @@ import { premiseMoved, readContextObservation } from '../_lib/context_observatio
185
185
  import { RUN_TERMINAL_VOCABULARY_VERSION } from '../_lib/outcome_vocabularies.js';
186
186
  import {
187
187
  HALT_ACTIONS,
188
+ buildLedgerEvent, parseDeliveryState, type DeliveryState,
188
189
  ladder,
189
190
  MAX_ITERATIONS,
190
191
  parseHaltStamp,
@@ -322,6 +323,8 @@ export interface RunState {
322
323
  * observed one, which is never a disagreement (`premiseMoved`).
323
324
  */
324
325
  context_fingerprint?: string;
326
+ /** DELIVERY position (8.1), written by the run; absent is no hold. */
327
+ delivery?: DeliveryState;
325
328
  }
326
329
 
327
330
  /**
@@ -651,6 +654,10 @@ function readState(file: string): RunState | null {
651
654
  if (o['inert_reported'] === true) {
652
655
  rec.inert_reported = true;
653
656
  }
657
+ // Round-tripped for the same reason `history_source` is: without this the
658
+ // 8.1 hold is dead code that reads as shipped.
659
+ const dlv = parseDeliveryState(o['delivery']);
660
+ if (dlv !== undefined) rec.delivery = dlv;
654
661
  // Round-tripped for the same reason `history_source` is: dropped, the
655
662
  // premise would be re-recorded every fire from the newest observation, so
656
663
  // the two sides could never differ and the rung would be dead code.
@@ -1384,14 +1391,14 @@ export function main(): number {
1384
1391
  const observed = readContextObservation(workspaceRoot)?.fingerprint ?? null;
1385
1392
  const premiseInvalidated = premiseMoved(state.context_fingerprint, observed);
1386
1393
 
1394
+ const delivery = parseDeliveryState(state.delivery) ?? null;
1387
1395
  const action = ladder(
1388
- state,
1389
- scan.open,
1390
- Date.now(),
1396
+ state, scan.open, Date.now(),
1391
1397
  scan.blocked,
1392
1398
  undefined,
1393
1399
  unavailable,
1394
1400
  premiseInvalidated,
1401
+ delivery,
1395
1402
  );
1396
1403
 
1397
1404
  if (action !== 'engage') {
@@ -1408,23 +1415,13 @@ export function main(): number {
1408
1415
  const alreadyBlocked =
1409
1416
  action === 'blocked' && eventAlreadyLogged(workspaceRoot, runId, slug, 'blocked');
1410
1417
  if (!alreadyStamped && !alreadyBlocked) {
1411
- appendEvent(workspaceRoot, {
1412
- event: action,
1413
- run_id: runId,
1414
- roadmap: slug,
1415
- turn: turnOrdinal,
1416
- iterations: state.iterations,
1417
- open: scan.open,
1418
- blocked: scan.blocked,
1419
- // A ledger line is a persisted shape carrying a `RunTerminalState`,
1420
- // so it names the vocabulary version it was written against: a
1421
- // reader meeting an unknown value can then tell a newer writer
1422
- // from a corrupt row.
1423
- terminal_state: terminalStateFor(action),
1424
- terminal_vocabulary_version: RUN_TERMINAL_VOCABULARY_VERSION,
1425
- at: new Date().toISOString(),
1426
- ...roots,
1427
- });
1418
+ const line = buildLedgerEvent(
1419
+ { action, runId, slug, turn: turnOrdinal, iterations: state.iterations,
1420
+ open: scan.open, blocked: scan.blocked, delivery },
1421
+ RUN_TERMINAL_VOCABULARY_VERSION,
1422
+ new Date(),
1423
+ );
1424
+ appendEvent(workspaceRoot, { ...line, ...roots } as JsonObject);
1428
1425
  }
1429
1426
  if (action === 'complete') {
1430
1427
  // Only a COMPLETION clears the state, so a later run on the same