@event4u/agent-config 16.0.0 → 16.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 (382) 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 +289 -140
  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 +74 -27
  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/commands/work.md +4 -0
  24. package/dist/agent-src/contexts/authority/scope-growth-ownership.md +80 -0
  25. package/dist/agent-src/contexts/communication/rules-auto/user-interaction-mechanics.md +47 -0
  26. package/dist/agent-src/contexts/execution/auto-orchestration-activation.md +44 -3
  27. package/dist/agent-src/contexts/execution/autonomy-mechanics.md +19 -23
  28. package/dist/agent-src/contexts/execution/decision-closure-execution.md +93 -0
  29. package/dist/agent-src/contexts/execution/fix-loop-ladder.md +108 -0
  30. package/dist/agent-src/contexts/execution/plan-confidence-gate.md +4 -2
  31. package/dist/agent-src/contexts/execution/roadmap-process-loop.md +75 -6
  32. package/dist/agent-src/guidelines/agent-infra/active-remediation-mechanics.md +57 -0
  33. package/dist/agent-src/guidelines/agent-infra/evaluator-independence-mechanics.md +102 -0
  34. package/dist/agent-src/guidelines/design-fidelity-routing.md +29 -0
  35. package/dist/agent-src/rules/autonomous-execution.md +15 -8
  36. package/dist/agent-src/rules/delegation-policy.md +23 -45
  37. package/dist/agent-src/rules/design-fidelity.md +32 -37
  38. package/dist/agent-src/rules/evaluator-independence.md +43 -49
  39. package/dist/agent-src/rules/test-first.md +41 -0
  40. package/dist/agent-src/rules/ui-audit-gate.md +1 -2
  41. package/dist/agent-src/skills/accessibility-auditor/data/aria-patterns.csv +1 -1
  42. package/dist/agent-src/skills/accessibility-auditor/data/manifest.json +3 -3
  43. package/dist/agent-src/skills/ai-council/SKILL.md +11 -3
  44. package/dist/agent-src/skills/api-design/data/manifest.json +3 -3
  45. package/dist/agent-src/skills/database/data/manifest.json +4 -4
  46. package/dist/agent-src/skills/database/data/query-tuning.csv +3 -3
  47. package/dist/agent-src/skills/design-review/SKILL.md +80 -4
  48. package/dist/agent-src/skills/design-review/references/verification-automation.md +32 -0
  49. package/dist/agent-src/skills/existing-ui-audit/SKILL.md +30 -5
  50. package/dist/agent-src/skills/existing-ui-audit/references/component-taxonomy-detection.md +56 -0
  51. package/dist/agent-src/skills/experiment-loop/SKILL.md +31 -1
  52. package/dist/agent-src/skills/markitdown/SKILL.md +4 -0
  53. package/dist/agent-src/skills/playwright-testing/SKILL.md +30 -6
  54. package/dist/agent-src/skills/roadmap-writing/SKILL.md +1 -1
  55. package/dist/agent-src/skills/storybook-workshop/SKILL.md +42 -3
  56. package/dist/agent-src/skills/threat-modeling/SKILL.md +9 -4
  57. package/dist/agent-src/skills/threat-modeling/data/manifest.json +4 -4
  58. package/dist/agent-src/skills/ui-component-architect/SKILL.md +23 -0
  59. package/dist/agent-src/skills/verify-repair-loop/SKILL.md +37 -3
  60. package/dist/agent-src/templates/agent-settings.md +1 -1
  61. package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
  62. package/dist/agent-src/templates/roadmaps.md +218 -0
  63. package/dist/agent-src/templates/scripts/work_engine/directives/backend/_self_fix.ts +62 -2
  64. package/dist/agent-src/templates/scripts/work_engine/directives/backend/test.ts +1 -0
  65. package/dist/agent-src/templates/scripts/work_engine/directives/backend/verify.ts +1 -0
  66. package/dist/agent-src/templates/scripts/work_engine/directives/ui/app_spec.ts +10 -4
  67. package/dist/agent-src/templates/scripts/work_engine/directives/ui/apply.ts +260 -21
  68. package/dist/agent-src/templates/scripts/work_engine/directives/ui/audit.ts +22 -3
  69. package/dist/agent-src/templates/scripts/work_engine/directives/ui/scaffold.ts +6 -1
  70. package/dist/agent-src/templates/scripts/work_engine/state.ts +57 -7
  71. package/dist/agent-src/templates/scripts/work_engine/taxonomy/detect.ts +500 -0
  72. package/dist/agent-src/templates/scripts/work_engine/taxonomy/place.ts +183 -0
  73. package/dist/cli/python/knowledge_ingest.js +1 -1
  74. package/dist/cli/python/workspace_drive.js +1 -1
  75. package/dist/cli-delegate/check_update_banner.js +40 -2
  76. package/dist/cli-delegate/chunk-4E2W63DA.js +335 -0
  77. package/dist/cli-delegate/{chunk-S3VFXMTM.js → chunk-4XFP2Y3E.js} +1 -1
  78. package/dist/cli-delegate/chunk-5R7HOLAF.js +99 -0
  79. package/dist/cli-delegate/{chunk-IVBPRCQO.js → chunk-6KFEC3G7.js} +1 -1
  80. package/dist/cli-delegate/{chunk-2TAQ2K4V.js → chunk-6KM6ORRY.js} +55 -386
  81. package/dist/cli-delegate/{chunk-AK6HODNQ.js → chunk-7XGBX6ZH.js} +973 -555
  82. package/dist/cli-delegate/{chunk-2XY7ILBT.js → chunk-FC75PGJE.js} +42 -2
  83. package/dist/cli-delegate/{chunk-55ACLDUE.js → chunk-FP36SWJD.js} +587 -141
  84. package/dist/cli-delegate/{chunk-3FKR64R5.js → chunk-IGD6ERX6.js} +62 -7
  85. package/dist/cli-delegate/{chunk-GYC3VRYN.js → chunk-JNMVLWLR.js} +99 -14
  86. package/dist/cli-delegate/{chunk-H32DGUJH.js → chunk-P6ZHP5W2.js} +1 -1
  87. package/dist/cli-delegate/{chunk-57PN4GYJ.js → chunk-RGNIWTEA.js} +1 -1
  88. package/dist/cli-delegate/chunk-ROYYTEZU.js +39 -0
  89. package/dist/cli-delegate/{chunk-2DK2VJWG.js → chunk-WGXTVKCQ.js} +20 -6
  90. package/dist/cli-delegate/chunk-YDPILCOL.js +152 -0
  91. package/dist/cli-delegate/{chunk-QJWAUMJW.js → chunk-YJ72UIAK.js} +730 -15
  92. package/dist/cli-delegate/cmd_analyze_session.js +1 -1
  93. package/dist/cli-delegate/cmd_brand_status.js +1 -1
  94. package/dist/cli-delegate/cmd_conformance.js +985 -831
  95. package/dist/cli-delegate/cmd_doctor.js +7 -5
  96. package/dist/cli-delegate/cmd_explain.js +2 -2
  97. package/dist/cli-delegate/cmd_export.js +9 -7
  98. package/dist/cli-delegate/cmd_fleet.js +4 -2
  99. package/dist/cli-delegate/cmd_handoff.js +7 -7
  100. package/dist/cli-delegate/cmd_mcp_available.js +1 -1
  101. package/dist/cli-delegate/cmd_memory_get.js +1 -1
  102. package/dist/cli-delegate/cmd_migrate.js +2 -2
  103. package/dist/cli-delegate/cmd_packs_active.js +2 -2
  104. package/dist/cli-delegate/cmd_preflight.js +4 -2
  105. package/dist/cli-delegate/cmd_prune.js +10 -8
  106. package/dist/cli-delegate/cmd_route_audit.js +3 -3
  107. package/dist/cli-delegate/cmd_route_explain.js +1 -1
  108. package/dist/cli-delegate/cmd_session_recycle.js +4 -4
  109. package/dist/cli-delegate/cmd_settings_get.js +28 -2
  110. package/dist/cli-delegate/cmd_settings_migrate.js +9 -7
  111. package/dist/cli-delegate/cmd_settings_set.js +1 -1
  112. package/dist/cli-delegate/cmd_sync.js +10 -8
  113. package/dist/cli-delegate/cmd_uninstall.js +16 -13
  114. package/dist/cli-delegate/cmd_update.js +1 -1
  115. package/dist/cli-delegate/cmd_upgrade.js +1 -1
  116. package/dist/cli-delegate/cmd_validate.js +12 -10
  117. package/dist/cli-delegate/cmd_versions.js +1 -1
  118. package/dist/discovery/deprecation-report.md +1 -1
  119. package/dist/discovery/discovery-manifest.json +90 -43
  120. package/dist/discovery/discovery-manifest.json.sha256 +1 -1
  121. package/dist/discovery/discovery-manifest.summary.md +4 -4
  122. package/dist/discovery/dormancy-report.md +1 -1
  123. package/dist/discovery/orphan-report.md +1 -1
  124. package/dist/discovery/packs.json +10 -8
  125. package/dist/discovery/trust-report.md +3 -3
  126. package/dist/discovery/workspaces.json +8 -6
  127. package/dist/hooks/dispatch.js +7682 -2772
  128. package/dist/hooks/dispatch.sha256 +1 -0
  129. package/dist/install/claudeRuleRewrite.js +14 -3
  130. package/dist/install/conflict.js +60 -10
  131. package/dist/install/conflictTracker.js +218 -0
  132. package/dist/install/fsPrimitives.js +54 -0
  133. package/dist/install/install.mjs +2200 -1559
  134. package/dist/install/manifestFiles.js +259 -0
  135. package/dist/install/preflight.js +14 -2
  136. package/dist/install/preserve.js +157 -0
  137. package/dist/install/recordedOwnership.js +132 -0
  138. package/dist/install/txlog.js +11 -2
  139. package/dist/mcp/registry-manifest.json +3 -3
  140. package/dist/mcp/server.json +2 -2
  141. package/dist/mcp/server.mjs +3066 -1470
  142. package/dist/router.json +1 -1
  143. package/dist/scripts/_lib/agent_settings.js +3 -2
  144. package/dist/scripts/_lib/fs_atomic.js +167 -0
  145. package/dist/scripts/_lib/installed_tools.js +460 -0
  146. package/dist/scripts/_lib/lean_projection_mode.js +3 -2
  147. package/dist/scripts/_lib/settings_renamed_keys.js +78 -0
  148. package/dist/server/routes/install.js +8 -0
  149. package/dist/server/routes/wizard.js +9 -3
  150. package/dist/server/schemas/settings.js +40 -5
  151. package/dist/shared/missionExecution.js +97 -0
  152. package/dist/ui/assets/index-Cwq4d0hX.js +23 -0
  153. package/dist/ui/assets/index-Cyjy8bWO.css +1 -0
  154. package/dist/ui/index.html +2 -2
  155. package/docs/MIGRATION.md +103 -0
  156. package/docs/contracts/settings-classes.md +28 -7
  157. package/docs/guidelines/agent-infra/active-remediation-mechanics.md +57 -0
  158. package/docs/guidelines/agent-infra/evaluator-independence-mechanics.md +102 -0
  159. package/docs/guidelines/design-fidelity-routing.md +29 -0
  160. package/package.json +4 -4
  161. package/src/agent-src/templates/scripts/work_engine/directives/backend/_self_fix.ts +62 -2
  162. package/src/agent-src/templates/scripts/work_engine/directives/backend/test.ts +1 -0
  163. package/src/agent-src/templates/scripts/work_engine/directives/backend/verify.ts +1 -0
  164. package/src/agent-src/templates/scripts/work_engine/directives/ui/app_spec.ts +10 -4
  165. package/src/agent-src/templates/scripts/work_engine/directives/ui/apply.ts +260 -21
  166. package/src/agent-src/templates/scripts/work_engine/directives/ui/audit.ts +22 -3
  167. package/src/agent-src/templates/scripts/work_engine/directives/ui/scaffold.ts +6 -1
  168. package/src/agent-src/templates/scripts/work_engine/state.ts +57 -7
  169. package/src/agent-src/templates/scripts/work_engine/taxonomy/detect.ts +500 -0
  170. package/src/agent-src/templates/scripts/work_engine/taxonomy/place.ts +183 -0
  171. package/src/config/agent-settings.template.yml +168 -27
  172. package/src/config/auto-rule-budget.json +16 -0
  173. package/src/config/ci-local-parity.yml +36 -5
  174. package/src/config/continuity-surface.json +23 -2
  175. package/src/config/gate-coverage.yml +377 -13
  176. package/src/config/gate-violation-baselines.json +10 -6
  177. package/src/config/hook-latency-budget.json +21 -0
  178. package/src/config/hook-token-budget.json +4 -2
  179. package/src/config/loop-surfaces.yaml +215 -0
  180. package/src/config/metric-registry.yml +114 -0
  181. package/src/config/placeholder-drift-budget.json +10 -0
  182. package/src/config/platform-anchor.json +3 -2
  183. package/src/config/release-gate-locality.yml +11 -0
  184. package/src/config/routing-coverage-seed.json +33 -5
  185. package/src/config/rule-enforcement-baseline.json +0 -1
  186. package/src/install/claudeRuleRewrite.ts +16 -2
  187. package/src/install/conflict.ts +65 -9
  188. package/src/install/conflictTracker.ts +299 -0
  189. package/src/install/fsPrimitives.ts +54 -0
  190. package/src/install/manifestFiles.ts +312 -0
  191. package/src/install/preflight.ts +16 -2
  192. package/src/install/preserve.ts +205 -0
  193. package/src/install/recordedOwnership.ts +149 -0
  194. package/src/install/txlog.ts +11 -2
  195. package/src/install/types.ts +19 -4
  196. package/src/scripts/_cli/cmd_conformance.ts +88 -15
  197. package/src/scripts/_cli/cmd_doctor.ts +7 -20
  198. package/src/scripts/_cli/cmd_route_explain.ts +9 -2
  199. package/src/scripts/_cli/cmd_settings_get.ts +44 -0
  200. package/src/scripts/_cli/doctor_execution.ts +173 -0
  201. package/src/scripts/_cli/doctor_network_posture.ts +226 -0
  202. package/src/scripts/_lib/activation_payload.ts +247 -0
  203. package/src/scripts/_lib/agent_settings.ts +4 -2
  204. package/src/scripts/_lib/authority_path.ts +102 -0
  205. package/src/scripts/_lib/body_portable.ts +102 -0
  206. package/src/scripts/_lib/cascade_base.ts +94 -0
  207. package/src/scripts/_lib/cc_transcript.ts +132 -1
  208. package/src/scripts/_lib/changelog_eras.ts +7 -2
  209. package/src/scripts/_lib/concern_sla_window.ts +200 -0
  210. package/src/scripts/_lib/continuation_ladder.ts +149 -2
  211. package/src/scripts/_lib/council_transport.ts +129 -0
  212. package/src/scripts/_lib/delivery_ready.ts +112 -0
  213. package/src/scripts/_lib/forge_protection.ts +381 -0
  214. package/src/scripts/_lib/forge_reader.ts +558 -0
  215. package/src/scripts/_lib/hook_platform_keys.ts +27 -0
  216. package/src/scripts/_lib/host_env_write.ts +111 -0
  217. package/src/scripts/_lib/json_python_parity.ts +127 -0
  218. package/src/scripts/_lib/lean_projection_mode.ts +14 -3
  219. package/src/scripts/_lib/llm_proposer_transport.ts +14 -4
  220. package/src/scripts/_lib/loop_surfaces.ts +382 -0
  221. package/src/scripts/_lib/mission_record.ts +190 -0
  222. package/src/scripts/_lib/obligation_frequency.ts +217 -0
  223. package/src/scripts/_lib/obligations.ts +513 -0
  224. package/src/scripts/_lib/orchestration_record.ts +11 -20
  225. package/src/scripts/_lib/probe_inputs.ts +271 -0
  226. package/src/scripts/_lib/read_surface_scan.ts +322 -0
  227. package/src/scripts/_lib/release_highlights.ts +201 -11
  228. package/src/scripts/_lib/release_holds.ts +493 -0
  229. package/src/scripts/_lib/release_migration_gate.ts +184 -0
  230. package/src/scripts/_lib/retrieval_sanitize.ts +77 -16
  231. package/src/scripts/_lib/rides_along.ts +102 -0
  232. package/src/scripts/_lib/settings_renamed_keys.ts +82 -0
  233. package/src/scripts/_lib/shell_write_shapes.ts +108 -0
  234. package/src/scripts/_lib/structural_hiding.ts +622 -0
  235. package/src/scripts/_lib/test_delta.ts +166 -0
  236. package/src/scripts/_lib/test_provenance.ts +171 -0
  237. package/src/scripts/_lib/turn_end_refusals.ts +525 -4
  238. package/src/scripts/_lib/turn_end_transcript.ts +216 -0
  239. package/src/scripts/_lib/typed_op_grant.ts +246 -0
  240. package/src/scripts/_lib/typed_op_watch.ts +185 -0
  241. package/src/scripts/_lib/verification_command.ts +324 -0
  242. package/src/scripts/_lib/verification_evidence.ts +647 -0
  243. package/src/scripts/_lib/verify_clause.ts +280 -0
  244. package/src/scripts/ai_council/config.ts +8 -11
  245. package/src/scripts/ai_council/fallback_config.ts +5 -3
  246. package/src/scripts/ai_council/ownership_classes.ts +118 -0
  247. package/src/scripts/ai_team/team_dispatch.ts +4 -0
  248. package/src/scripts/ask_block_census.ts +285 -15
  249. package/src/scripts/before_complete_hook.ts +340 -23
  250. package/src/scripts/bench_hook_latency.ts +412 -5
  251. package/src/scripts/cache_realization_report.ts +61 -1
  252. package/src/scripts/check_argument_hint.ts +287 -0
  253. package/src/scripts/check_claims.ts +125 -1
  254. package/src/scripts/check_enforcement_coverage.ts +38 -9
  255. package/src/scripts/check_enforcement_matrix.ts +866 -0
  256. package/src/scripts/check_finding_dispositions.ts +22 -3
  257. package/src/scripts/check_gate_reachability.ts +173 -10
  258. package/src/scripts/check_generator_sync.ts +1025 -0
  259. package/src/scripts/check_host_docs_digest.ts +786 -0
  260. package/src/scripts/check_host_format_column.ts +559 -0
  261. package/src/scripts/check_input_references.ts +228 -0
  262. package/src/scripts/check_inputs_parity.ts +235 -0
  263. package/src/scripts/check_kernel_edit_ratified.ts +47 -5
  264. package/src/scripts/check_kill_switch_table.ts +294 -0
  265. package/src/scripts/check_placeholder_drift.ts +221 -0
  266. package/src/scripts/check_preamble_payload_budget.ts +75 -2
  267. package/src/scripts/check_read_surface_coverage.ts +515 -0
  268. package/src/scripts/check_release_highlights.ts +33 -1
  269. package/src/scripts/check_release_holds.ts +568 -0
  270. package/src/scripts/check_routing_coverage.ts +378 -11
  271. package/src/scripts/check_rule_projection_integrity.ts +22 -12
  272. package/src/scripts/check_static_parity.ts +17 -0
  273. package/src/scripts/check_test_delta.ts +294 -0
  274. package/src/scripts/check_test_weakening.ts +299 -0
  275. package/src/scripts/ci_settle.ts +145 -19
  276. package/src/scripts/closure_scan.ts +447 -0
  277. package/src/scripts/compile_hook_manifest.ts +102 -18
  278. package/src/scripts/council_record_shape.ts +209 -0
  279. package/src/scripts/external_sources_denylist.json +29 -4
  280. package/src/scripts/hook_manifest.json +1 -1
  281. package/src/scripts/hook_manifest.yaml +127 -11
  282. package/src/scripts/hooks/block_config_weakening.ts +229 -4
  283. package/src/scripts/hooks/block_kernel_rule_writes.ts +9 -78
  284. package/src/scripts/hooks/block_plumbing_writes.ts +314 -0
  285. package/src/scripts/hooks/bundle_integrity.ts +291 -0
  286. package/src/scripts/hooks/chain_nudge_hook.ts +1 -2
  287. package/src/scripts/hooks/code_graph_context_hook.ts +1 -2
  288. package/src/scripts/hooks/comment_discipline_hook.ts +1 -3
  289. package/src/scripts/hooks/concern_registry.ts +6 -0
  290. package/src/scripts/hooks/concern_timings.ts +94 -0
  291. package/src/scripts/hooks/delegation_nudge_hook.ts +1 -2
  292. package/src/scripts/hooks/design_pass_hook.ts +221 -5
  293. package/src/scripts/hooks/design_slop_hook.ts +1 -2
  294. package/src/scripts/hooks/dispatch_hook.ts +25 -19
  295. package/src/scripts/hooks/edit_shape_hook.ts +1 -2
  296. package/src/scripts/hooks/end_review_nudge_hook.ts +1 -1
  297. package/src/scripts/hooks/evidence_independence.ts +97 -2
  298. package/src/scripts/hooks/exit_codes.ts +128 -0
  299. package/src/scripts/hooks/host_lowering.json +1 -0
  300. package/src/scripts/hooks/host_lowering.ts +128 -3
  301. package/src/scripts/hooks/host_lowering.yaml +262 -46
  302. package/src/scripts/hooks/host_semantics.ts +30 -2
  303. package/src/scripts/hooks/injection_budget.ts +9 -2
  304. package/src/scripts/hooks/interruption_ledger_hook.ts +1 -2
  305. package/src/scripts/hooks/journal_record_hook.ts +1 -2
  306. package/src/scripts/hooks/memory_recall_hook.ts +539 -0
  307. package/src/scripts/hooks/obligation_settle_hook.ts +328 -0
  308. package/src/scripts/hooks/one_question_per_ask_hook.ts +25 -4
  309. package/src/scripts/hooks/orchestration_record_hook.ts +1 -2
  310. package/src/scripts/hooks/permission_gate.ts +1 -2
  311. package/src/scripts/hooks/push_settle_hook.ts +1 -3
  312. package/src/scripts/hooks/replay_hook.ts +2 -1
  313. package/src/scripts/hooks/reread_guard_hook.ts +1 -2
  314. package/src/scripts/hooks/review_baseline_hook.ts +1 -2
  315. package/src/scripts/hooks/rtk_wrap_hook.ts +1 -2
  316. package/src/scripts/hooks/rule_inject_hook.ts +55 -4
  317. package/src/scripts/hooks/run_continuation_hook.ts +30 -35
  318. package/src/scripts/hooks/skill_route_hook.ts +1 -2
  319. package/src/scripts/hooks/source_first_gate_hook.ts +1 -2
  320. package/src/scripts/hooks/spawn_guard_shadow_hook.ts +1 -2
  321. package/src/scripts/hooks/subagent_ledger_hook.ts +1 -2
  322. package/src/scripts/hooks/suggestion_capture_hook.ts +1 -1
  323. package/src/scripts/hooks/table_fingerprint.ts +49 -0
  324. package/src/scripts/hooks/telemetry_flush_hook.ts +1 -2
  325. package/src/scripts/hooks/telemetry_usage_hook.ts +1 -2
  326. package/src/scripts/hooks/tool_result_bytes_hook.ts +93 -9
  327. package/src/scripts/hooks/turn_end_gate_hook.ts +337 -324
  328. package/src/scripts/hooks/ui_route_nudge_hook.ts +1 -2
  329. package/src/scripts/hooks_doctor.ts +85 -0
  330. package/src/scripts/hooks_status.ts +19 -0
  331. package/src/scripts/install.ts +109 -210
  332. package/src/scripts/janitor.ts +7 -0
  333. package/src/scripts/lint_decision_classes.ts +464 -0
  334. package/src/scripts/lint_deny_text.ts +419 -0
  335. package/src/scripts/lint_exit_codes.ts +262 -0
  336. package/src/scripts/lint_hook_manifest.ts +66 -0
  337. package/src/scripts/lint_major_migration_sections.ts +450 -0
  338. package/src/scripts/lint_plan_risk_register.ts +47 -3
  339. package/src/scripts/lint_roadmap_blockers.ts +139 -12
  340. package/src/scripts/lint_roadmap_producers.ts +202 -0
  341. package/src/scripts/lint_skill_descriptions.ts +1 -1
  342. package/src/scripts/lint_skill_scripts_readonly.ts +1 -1
  343. package/src/scripts/mcp_server/prompts.ts +142 -1
  344. package/src/scripts/measure_rule_budget.ts +101 -5
  345. package/src/scripts/measure_skill_ranker_baseline.ts +305 -51
  346. package/src/scripts/measure_turn_end_gate.ts +193 -0
  347. package/src/scripts/memory_lookup.ts +1 -1
  348. package/src/scripts/new_roadmap.ts +37 -0
  349. package/src/scripts/orchestration_record.ts +0 -5
  350. package/src/scripts/prepack-check.mjs +53 -0
  351. package/src/scripts/release.ts +14 -98
  352. package/src/scripts/release_drill.ts +267 -1
  353. package/src/scripts/release_publication.ts +64 -1
  354. package/src/scripts/report_enforcement_drift.ts +566 -0
  355. package/src/scripts/report_host_injection_effect.ts +3 -11
  356. package/src/scripts/report_invocation_surface.ts +383 -0
  357. package/src/scripts/report_obligation_carriers.ts +699 -0
  358. package/src/scripts/report_obligation_writer_split.ts +236 -0
  359. package/src/scripts/report_skill_menu_census.ts +208 -0
  360. package/src/scripts/report_standing_payload_by_host.ts +26 -152
  361. package/src/scripts/roadmap_verify_share.ts +213 -0
  362. package/src/scripts/schemas/agent-settings.schema.json +23 -0
  363. package/src/scripts/schemas/command.schema.json +48 -0
  364. package/src/scripts/schemas/review-findings.schema.json +4 -0
  365. package/src/scripts/schemas/skill.schema.json +44 -0
  366. package/src/scripts/scope_growth.ts +219 -0
  367. package/src/scripts/self_review_gate.ts +359 -10
  368. package/src/scripts/semantic_parity_census.ts +314 -0
  369. package/src/scripts/session_eol_report.ts +54 -1
  370. package/src/scripts/sweep_dead_scan_roots.ts +1 -1
  371. package/src/scripts/trigger_eval_grandfather.json +0 -3
  372. package/src/scripts/trigger_eval_rotation.ts +122 -27
  373. package/src/scripts/ui_conformance_probe.ts +774 -0
  374. package/src/scripts/update_prices.ts +85 -3
  375. package/src/scripts/write_bundle_digest.mjs +74 -0
  376. package/src/server/schemas/settings.ts +62 -7
  377. package/src/shared/missionExecution.ts +126 -0
  378. package/src/templates/consumer-settings/ONBOARDING.md +1 -0
  379. package/dist/cli-delegate/chunk-523HCFYO.js +0 -90
  380. package/dist/ui/assets/index-D0jaCRPM.js +0 -29
  381. package/dist/ui/assets/index-jSfMW8_t.css +0 -1
  382. /package/dist/cli-delegate/{chunk-YUM5KURG.js → chunk-7RQP4UDH.js} +0 -0
@@ -0,0 +1,513 @@
1
+ /**
2
+ * The obligation ledger — write side.
3
+ *
4
+ * This repository already knows which obligations it delivered into a turn.
5
+ * `hooks/rule_inject_hook.ts` writes one record per session naming every rule
6
+ * whose body it injected, and until now the only reads of that path were the
7
+ * writer itself and its own test. The tree could say what it delivered and
8
+ * nothing could ask whether the turn discharged any of it.
9
+ *
10
+ * This module is the state that closes that gap, and ONLY the state. It adds
11
+ * no reader, no detector and no refusal — those arrive in later phases of
12
+ * `road-to-a-ledger-that-closes-the-loop`, deliberately after the rows exist,
13
+ * so that a detector is written against real recorded shapes instead of
14
+ * against an imagined one.
15
+ *
16
+ * WHAT A `delivered` ROW MEANS, AND WHAT IT MAY NEVER BE CITED FOR.
17
+ * A row records that an EMITTER RAN. It does not record that a rule reached
18
+ * the model, and it may not be cited as evidence that one did. That question
19
+ * is council-locked as blocked-by-architecture (2026-08-31, two convergent
20
+ * seats) on a reproducible probe: an obligation present in the shipped tree
21
+ * was absent from the operator's installed copy, so installation proves
22
+ * availability and not exposure. The lock tested a different mechanism than
23
+ * this one, which is why the write side is allowed at all — but a reader who
24
+ * turns these rows into a compliance claim has made exactly the assertion the
25
+ * council refused.
26
+ *
27
+ * WHY THE PATH COMES FROM A HELPER AND NOT A LITERAL.
28
+ * `statePathFor` below is the same shape as `before_complete_hook.ts:128` —
29
+ * `session_state_file(<dir>, session_id)` — and shares that function rather
30
+ * than reimplementing it. The digest keying matters: it removes the
31
+ * filename-length failure mode for long ids, and it is the property an
32
+ * in-file `session_id` integrity check is written against. A second
33
+ * hand-rolled sanitiser here would be a second shape, and two shapes for one
34
+ * concept is how a session's state becomes unfindable by the tool meant to
35
+ * read it.
36
+ */
37
+
38
+ import fs from 'node:fs';
39
+ import path from 'node:path';
40
+
41
+ import {
42
+ atomic_write_json,
43
+ is_replay_mode,
44
+ session_state_file,
45
+ update_json_under_lock,
46
+ } from '../hooks/state_io.js';
47
+ import { manifest_path } from './installed_tools.js';
48
+ import { type EnforcementClass, is_enforcement_class } from './obligation_frequency.js';
49
+ import { hasSentinel } from './repo_root.js';
50
+
51
+ /**
52
+ * Directory holding one ledger file per session.
53
+ *
54
+ * Under `agents/runtime/state/`, which is gitignored and janitor-pruned, and
55
+ * which carries a `disposition: excluded` row in
56
+ * `src/config/continuity-surface.json` — this is per-session instrumentation,
57
+ * not continuity state a later session must be able to resume from.
58
+ */
59
+ export const OBLIGATION_STATE_DIR = path.join('agents', 'runtime', 'state', 'obligations');
60
+
61
+ /** Path of one session's ledger. Same shape as the existing consumer. */
62
+ export function statePathFor(session_id: string): string {
63
+ return session_state_file(OBLIGATION_STATE_DIR, session_id);
64
+ }
65
+
66
+ /**
67
+ * WHICH KIND OF TREE WROTE A ROW — the whole vocabulary, and nothing else.
68
+ *
69
+ * `obligation-settle-shadow-bar` is read off `agents/runtime/state/obligations/`,
70
+ * which is one machine's gitignored runtime state. That scope bound lives in the
71
+ * claim's prose and in a roadmap paragraph, and in nothing a reader of the rows
72
+ * can compute. A reading that clears the pre-registered floor would therefore be
73
+ * quotable as evidence about a population nobody separated. This field makes the
74
+ * separation a property of the DATA, so the split is computable from the ledger
75
+ * alone rather than asserted beside it.
76
+ *
77
+ * THREE VALUES, CLOSED, AND A UNION OF LITERALS ON PURPOSE. The record gains one
78
+ * enumerated field and no `payload`, `notes` or `extra` slot, so the privacy
79
+ * property is the SHAPE and not a scrubber somebody has to keep correct — the
80
+ * same by-construction posture `artifact-engagement-recording` states for the
81
+ * telemetry event. A type that cannot hold a path cannot leak one.
82
+ *
83
+ * WHAT IT DELIBERATELY IS NOT. Not a path, not a username, not a hostname, not a
84
+ * machine id, not a git remote. The obvious implementation of "which tree wrote
85
+ * this" is the resolved root's filesystem path, which on a developer machine
86
+ * carries an account name — and because the bar is read by QUOTING rows, one such
87
+ * field would make every row already written unquotable rather than scrubbable.
88
+ * So the field carries a ROLE: what kind of tree this is, never which one.
89
+ */
90
+ export type WriterRole =
91
+ /** The tree carries this package's own `package.json` sentinel — a maintainer checkout. */
92
+ | 'package'
93
+ /** Not the package, but an install manifest is present — a consumer project. */
94
+ | 'consumer'
95
+ /** Neither marker resolved. An honest absence, never a default. */
96
+ | 'unknown';
97
+
98
+ /** The closed set, in one place, so a reader and a writer cannot disagree. */
99
+ export const WRITER_ROLES: readonly WriterRole[] = ['package', 'consumer', 'unknown'];
100
+
101
+ /** Narrowing guard for a value read back off disk. */
102
+ export function isWriterRole(value: unknown): value is WriterRole {
103
+ return typeof value === 'string' && (WRITER_ROLES as readonly string[]).includes(value);
104
+ }
105
+
106
+ /**
107
+ * Classify the root this ledger lives under.
108
+ *
109
+ * ORDER IS THE CONTRACT. `package` is tested FIRST, because a maintainer checkout
110
+ * routinely also carries the install artifacts a consumer has — this repository
111
+ * installs itself — and a consumer-first test would label the maintainer tree
112
+ * `consumer` on exactly the machine the bar is being read from. The sentinel is
113
+ * `hasSentinel` from `repo_root.ts` rather than a second `package.json` reader:
114
+ * the sentinel name lives in one place or it drifts.
115
+ *
116
+ * `consumer` keys on the INSTALL MANIFEST (`agents/installed-tools.lock`, via the
117
+ * canonical `manifest_path` so the `AGENT_CONFIG_INSTALLED_TOOLS` override is
118
+ * honoured) and not on `.agent-settings.yml`. The settings file is something a
119
+ * human writes by hand; the lock is written by the installer and by nothing else.
120
+ * A weaker marker would buy coverage by guessing, which is the one thing this
121
+ * field may not do.
122
+ *
123
+ * NEVER THROWS. A probe that cannot stat returns `unknown`, which is a real
124
+ * answer — the ledger's contract is that an instrument missing a reading is
125
+ * cheaper than a refused turn, and a classifier is no exception.
126
+ */
127
+ export function resolveWriterRole(root: string): WriterRole {
128
+ try {
129
+ if (hasSentinel(root)) return 'package';
130
+ if (fs.existsSync(manifest_path(root))) return 'consumer';
131
+ } catch {
132
+ return 'unknown';
133
+ }
134
+ return 'unknown';
135
+ }
136
+
137
+ /**
138
+ * One rule delivered into this session, with the class its frontmatter declares.
139
+ *
140
+ * `cls` rather than `class` because `class` is a reserved word, and a field
141
+ * named around a keyword is worse than a field named honestly. The serialised
142
+ * key is `class`, which is what a reader of the JSON expects to see.
143
+ */
144
+ export interface DeliveredRow {
145
+ /** Rule id, as the injector names it. */
146
+ readonly rule: string;
147
+ /** Declared enforcement class, or `none` when the rule declares none. */
148
+ readonly cls: EnforcementClass;
149
+ /** ISO-8601, second precision. */
150
+ readonly at: string;
151
+ /** Which kind of tree wrote this. Stamped here, never supplied — see {@link WriterInput}. */
152
+ readonly writer: WriterRole;
153
+ }
154
+
155
+ /**
156
+ * What a CALLER offers, for every row type: the stored shape minus its writer.
157
+ *
158
+ * The writer is stamped by the append functions from the `root` they were
159
+ * already given, and is deliberately not part of any caller's input. A caller
160
+ * that could supply it could supply the wrong one — and a provenance field a
161
+ * caller can set is a provenance field, not provenance. Making it unsettable is
162
+ * cheaper than any check that it was set correctly.
163
+ */
164
+ export type WriterInput<T> = Omit<T, 'writer'>;
165
+
166
+ /**
167
+ * One obligation discharged this session.
168
+ *
169
+ * Written by the concern that ALREADY made the decision — the design-pass
170
+ * concern knows the UI audit is fresh, so it is the only thing that can say so
171
+ * without re-deriving a verdict it does not own. A discharge nobody observed
172
+ * is never inferred: absence of a row means nothing was recorded, not that
173
+ * nothing happened, and the shadow rows below are labelled accordingly.
174
+ */
175
+ export interface DischargeRow {
176
+ /** Rule id whose obligation this discharges. */
177
+ readonly rule: string;
178
+ /** What observed it — a concern name, so a reader can go and check. */
179
+ readonly by: string;
180
+ readonly at: string;
181
+ /** Which kind of tree wrote this. Stamped, never supplied. */
182
+ readonly writer: WriterRole;
183
+ }
184
+
185
+ /**
186
+ * One turn-end reading that WOULD have refused, had anything been armed.
187
+ *
188
+ * The whole point of the shadow phase: the detector runs, the verdict is
189
+ * recorded, and nothing is refused. A row here is evidence about the
190
+ * detector's behaviour, never about the turn's compliance.
191
+ */
192
+ export interface ShadowRow {
193
+ readonly at: string;
194
+ /** The missing SET, as one row. Never one row per obligation — see `appendShadow`. */
195
+ readonly missing: readonly string[];
196
+ /** 1 for the first reading of a given missing set, incrementing after. */
197
+ readonly attempt: number;
198
+ /** Always true today: a row is only written when the detector would refuse. */
199
+ readonly would_refuse: boolean;
200
+ /**
201
+ * Which kind of tree wrote this. Stamped, never supplied.
202
+ *
203
+ * The shadow array is the one the pre-registered bar actually counts, so
204
+ * this is the field that turns "the corpus is one machine" from a sentence
205
+ * in a claim into something a reader can compute off the rows.
206
+ */
207
+ readonly writer: WriterRole;
208
+ }
209
+
210
+ /** The on-disk shape of one session's ledger. */
211
+ export interface ObligationLedger {
212
+ /** Cheap integrity check against a copied or hand-edited file. */
213
+ readonly session_id: string;
214
+ readonly delivered: readonly DeliveredRow[];
215
+ readonly discharged: readonly DischargeRow[];
216
+ readonly shadow: readonly ShadowRow[];
217
+ }
218
+
219
+ /** The JSON spelling of a row — `class` on disk, `cls` in TypeScript. */
220
+ interface SerialisedRow {
221
+ rule: string;
222
+ class: string;
223
+ at: string;
224
+ writer: WriterRole;
225
+ }
226
+
227
+ function serialise(row: DeliveredRow): SerialisedRow {
228
+ return { rule: row.rule, class: row.cls, at: row.at, writer: row.writer };
229
+ }
230
+
231
+ /**
232
+ * Read rows back out of whatever is on disk, discarding anything malformed.
233
+ *
234
+ * Deliberately lenient about the FILE and strict about the ROW. A truncated or
235
+ * hand-edited ledger must not cost the rows that are still intact, and a row
236
+ * whose class is outside the closed set must not enter the ledger wearing a
237
+ * value the vocabulary denies — `is_enforcement_class` is the gate, so a
238
+ * future widening of the schema cannot leak in through the state file.
239
+ *
240
+ * THE WRITER IS TREATED THE OTHER WAY ROUND, and the asymmetry is deliberate. A
241
+ * row whose CLASS is outside the vocabulary is dropped, because the class is the
242
+ * row's subject matter and a row wearing a denied class is a false statement
243
+ * about what was delivered. A row whose WRITER is absent or unrecognised is KEPT
244
+ * and reads `unknown`, because the writer is provenance ABOUT a delivery that
245
+ * genuinely happened — dropping the row would destroy a real record to punish a
246
+ * missing label, and every row written before this field existed is exactly that
247
+ * case. `unknown` is the honest reading of both, which is why one value covers
248
+ * absent and unresolvable alike.
249
+ */
250
+ export function parseRows(loaded: unknown): DeliveredRow[] {
251
+ if (typeof loaded !== 'object' || loaded === null) return [];
252
+ const raw = (loaded as { delivered?: unknown }).delivered;
253
+ if (!Array.isArray(raw)) return [];
254
+ const out: DeliveredRow[] = [];
255
+ for (const entry of raw) {
256
+ if (typeof entry !== 'object' || entry === null) continue;
257
+ const e = entry as Record<string, unknown>;
258
+ const rule = e['rule'];
259
+ const cls = e['class'];
260
+ const at = e['at'];
261
+ if (typeof rule !== 'string' || rule === '') continue;
262
+ if (typeof at !== 'string' || at === '') continue;
263
+ if (!is_enforcement_class(cls)) continue;
264
+ const writer = e['writer'];
265
+ out.push({ rule, cls, at, writer: isWriterRole(writer) ? writer : 'unknown' });
266
+ }
267
+ return out;
268
+ }
269
+
270
+ /** ISO-8601 to second precision — the ledger has no use for milliseconds. */
271
+ export function stamp(now: Date = new Date()): string {
272
+ return `${now.toISOString().slice(0, 19)}Z`;
273
+ }
274
+
275
+ /**
276
+ * Append delivered rows for one session, idempotently per rule.
277
+ *
278
+ * Idempotent because the injector's own contract is once-per-session-per-rule
279
+ * re-armed on compaction: after a compaction a rule is legitimately delivered
280
+ * again, and a second row would say the same thing twice while a reader
281
+ * counting distinct obligations wants it once. First delivery wins, so the
282
+ * timestamp is the one that answers "when did this session first come under
283
+ * this rule".
284
+ *
285
+ * Returns the number of rows actually added — 0 is a real answer (every rule
286
+ * already recorded) and is not a failure.
287
+ *
288
+ * NEVER THROWS, and never fails a turn. A ledger that cannot be written is an
289
+ * instrument that missed a reading; a turn refused because an instrument
290
+ * missed a reading is a worse outcome than the missing reading, and this
291
+ * module has no refusal to offer in the first place.
292
+ */
293
+ export function appendDelivered(
294
+ root: string,
295
+ session_id: string,
296
+ rows: readonly WriterInput<DeliveredRow>[],
297
+ ): number {
298
+ if (rows.length === 0) return 0;
299
+ if (is_replay_mode()) return 0;
300
+ if (session_id.trim() === '') return 0;
301
+
302
+ const target = path.join(root, statePathFor(session_id));
303
+ const writer = resolveWriterRole(root);
304
+ let added = 0;
305
+ try {
306
+ update_json_under_lock<LedgerFile>(target, (loaded) => {
307
+ const existing = parseRows(loaded);
308
+ const seen = new Set(existing.map((r) => r.rule));
309
+ const merged = existing.map(serialise);
310
+ for (const row of rows) {
311
+ if (seen.has(row.rule)) continue;
312
+ seen.add(row.rule);
313
+ merged.push(serialise({ ...row, writer }));
314
+ added += 1;
315
+ }
316
+ if (added === 0) return null; // deliberate no-write
317
+ return { ...carryOver(loaded), session_id, delivered: merged };
318
+ });
319
+ } catch {
320
+ return 0;
321
+ }
322
+ return added;
323
+ }
324
+
325
+ /** The on-disk file, as the lock's mutator sees it. */
326
+ interface LedgerFile {
327
+ session_id: string;
328
+ delivered: SerialisedRow[];
329
+ discharged: DischargeRow[];
330
+ shadow: ShadowRow[];
331
+ }
332
+
333
+ /**
334
+ * The arrays this write is not touching, preserved verbatim.
335
+ *
336
+ * Three writers share one file and each owns one array. Without this, the
337
+ * last writer of a turn silently truncates the other two — the kind of loss
338
+ * that shows up as a detector reporting a clean turn because the discharges
339
+ * it was meant to read were dropped by the delivery write that followed them.
340
+ */
341
+ function carryOver(loaded: Partial<LedgerFile>): Pick<LedgerFile, 'discharged' | 'shadow'> {
342
+ return {
343
+ discharged: Array.isArray(loaded.discharged) ? loaded.discharged : [],
344
+ shadow: Array.isArray(loaded.shadow) ? loaded.shadow : [],
345
+ };
346
+ }
347
+
348
+ /**
349
+ * Record that one concern observed an obligation discharged.
350
+ *
351
+ * Idempotent per (rule, by): the design-pass concern may fire many times in a
352
+ * turn and the ledger wants the fact, not the frequency.
353
+ */
354
+ export function appendDischarge(
355
+ root: string,
356
+ session_id: string,
357
+ rows: readonly WriterInput<DischargeRow>[],
358
+ ): number {
359
+ if (rows.length === 0) return 0;
360
+ if (is_replay_mode()) return 0;
361
+ if (session_id.trim() === '') return 0;
362
+
363
+ const target = path.join(root, statePathFor(session_id));
364
+ const writer = resolveWriterRole(root);
365
+ let added = 0;
366
+ try {
367
+ update_json_under_lock<LedgerFile>(target, (loaded) => {
368
+ const existing = Array.isArray(loaded.discharged) ? loaded.discharged : [];
369
+ const key = (r: WriterInput<DischargeRow>): string => `${r.rule}::${r.by}`;
370
+ const seen = new Set(existing.map(key));
371
+ const merged = [...existing];
372
+ for (const row of rows) {
373
+ if (seen.has(key(row))) continue;
374
+ seen.add(key(row));
375
+ merged.push({ ...row, writer });
376
+ added += 1;
377
+ }
378
+ if (added === 0) return null;
379
+ return {
380
+ session_id,
381
+ delivered: parseRows(loaded).map(serialise),
382
+ discharged: merged,
383
+ shadow: Array.isArray(loaded.shadow) ? loaded.shadow : [],
384
+ };
385
+ });
386
+ } catch {
387
+ return 0;
388
+ }
389
+ return added;
390
+ }
391
+
392
+ /** Rules discharged this session, by rule id. */
393
+ export function readDischarged(root: string, session_id: string): DischargeRow[] {
394
+ const file = readLedgerFile(root, session_id);
395
+ if (file === null) return [];
396
+ return Array.isArray(file.discharged) ? file.discharged : [];
397
+ }
398
+
399
+ /** Shadow readings recorded this session. */
400
+ export function readShadow(root: string, session_id: string): ShadowRow[] {
401
+ const file = readLedgerFile(root, session_id);
402
+ if (file === null) return [];
403
+ return Array.isArray(file.shadow) ? file.shadow : [];
404
+ }
405
+
406
+ /**
407
+ * Record one would-refuse reading — ONE ROW PER MISSING SET, never per obligation.
408
+ *
409
+ * The aggregation is the contract, not an optimisation. Five missing
410
+ * obligations are one reading of one turn, and emitting five rows would let a
411
+ * later count of "refusals" read five times the truth while every one of them
412
+ * described the same moment.
413
+ *
414
+ * `attempt` increments when the SAME missing set is seen again. An unchanged
415
+ * set on a second reading is the signal that nothing moved — the caller uses
416
+ * it to stop forcing continuation, and it is recorded here so that decision is
417
+ * auditable rather than re-derived.
418
+ *
419
+ * Returns the attempt number written, or 0 when nothing was written.
420
+ */
421
+ export function appendShadow(
422
+ root: string,
423
+ session_id: string,
424
+ missing: readonly string[],
425
+ now: string = stamp(),
426
+ ): number {
427
+ if (missing.length === 0) return 0;
428
+ if (is_replay_mode()) return 0;
429
+ if (session_id.trim() === '') return 0;
430
+
431
+ const fingerprint = [...missing].sort().join('::');
432
+ const target = path.join(root, statePathFor(session_id));
433
+ const writer = resolveWriterRole(root);
434
+ let attempt = 0;
435
+ try {
436
+ update_json_under_lock<LedgerFile>(target, (loaded) => {
437
+ const existing = Array.isArray(loaded.shadow) ? loaded.shadow : [];
438
+ const prior = existing.filter(
439
+ (r) => [...(r.missing ?? [])].sort().join('::') === fingerprint,
440
+ );
441
+ attempt = prior.length + 1;
442
+ return {
443
+ session_id,
444
+ delivered: parseRows(loaded).map(serialise),
445
+ discharged: Array.isArray(loaded.discharged) ? loaded.discharged : [],
446
+ shadow: [
447
+ ...existing,
448
+ { at: now, missing: [...missing], attempt, would_refuse: true, writer },
449
+ ],
450
+ };
451
+ });
452
+ } catch {
453
+ return 0;
454
+ }
455
+ return attempt;
456
+ }
457
+
458
+ /** The whole file for one session, or `null` when it is absent or not ours. */
459
+ function readLedgerFile(root: string, session_id: string): Partial<LedgerFile> | null {
460
+ if (session_id.trim() === '') return null;
461
+ const target = path.join(root, statePathFor(session_id));
462
+ try {
463
+ const decoded: unknown = JSON.parse(fs.readFileSync(target, 'utf-8'));
464
+ if (typeof decoded !== 'object' || decoded === null) return null;
465
+ if ((decoded as { session_id?: unknown }).session_id !== session_id) return null;
466
+ return decoded as Partial<LedgerFile>;
467
+ } catch {
468
+ return null;
469
+ }
470
+ }
471
+
472
+ /**
473
+ * Rows recorded for one session, or `[]` when there are none to read.
474
+ *
475
+ * A file carrying somebody else's `session_id` reads as empty rather than as
476
+ * that session's rows — a copy, a restore or a hand-edit must not be able to
477
+ * attribute one session's deliveries to another. The producer side repairs
478
+ * the same case by rewriting the id; the consumer side refuses it. Both halves
479
+ * are needed, as `hooks/state_io.ts` § Per-session concern state states for
480
+ * the two concerns that got here first.
481
+ */
482
+ export function readDelivered(root: string, session_id: string): DeliveredRow[] {
483
+ if (session_id.trim() === '') return [];
484
+ const target = path.join(root, statePathFor(session_id));
485
+ try {
486
+ const decoded: unknown = JSON.parse(fs.readFileSync(target, 'utf-8'));
487
+ if (typeof decoded !== 'object' || decoded === null) return [];
488
+ if ((decoded as { session_id?: unknown }).session_id !== session_id) return [];
489
+ return parseRows(decoded);
490
+ } catch {
491
+ return [];
492
+ }
493
+ }
494
+
495
+ /**
496
+ * Whether the ledger directory can be written — the doctor's readiness line.
497
+ *
498
+ * Probes by writing and removing a sentinel rather than by checking mode bits,
499
+ * because the modes that matter here (a read-only mount, a directory owned by
500
+ * another user, a full disk) are not all visible in a `stat`. Returns `false`
501
+ * rather than throwing: the doctor reports, it does not fail.
502
+ */
503
+ export function ledgerWritable(root: string): boolean {
504
+ if (is_replay_mode()) return false;
505
+ const probe = path.join(root, OBLIGATION_STATE_DIR, '.writable-probe.json');
506
+ try {
507
+ atomic_write_json(probe, { probe: true });
508
+ fs.rmSync(probe, { force: true });
509
+ return true;
510
+ } catch {
511
+ return false;
512
+ }
513
+ }
@@ -59,8 +59,6 @@ export type LinePhase = 'refine' | 'memory' | 'analyze' | 'plan' | 'implement' |
59
59
  * table can be checked against it (`road-to-experience-loop-broadening` 1.3).
60
60
  */
61
61
  export type LineOutcome = PhaseOutcome;
62
- /** Which capsule-emission trigger arm fired first (Phase 1 shadow comparison). */
63
- export type TriggerArm = 'watermark' | 'saturation' | 'tie';
64
62
  /** The orchestration form the form-gate selected (road-to-opt-subagent-harvest P2). */
65
63
  export type DispatchModeId =
66
64
  | 'do-and-judge'
@@ -225,12 +223,16 @@ export interface RecordInput {
225
223
  capsule_emitted?: boolean | undefined;
226
224
  /** Total entries across the capsule's arrays — a size proxy, never content. */
227
225
  capsule_entries?: number | null | undefined;
228
- /** 1-based step at which the token-watermark arm fired (null = never). */
229
- watermark_step?: number | null | undefined;
230
- /** 1-based step at which the novelty-saturation arm fired (null = never). */
231
- saturation_step?: number | null | undefined;
232
- /** Which arm fired first on this dispatch — the paired-comparison datum. */
233
- trigger_arm_earlier?: TriggerArm | null | undefined;
226
+ // The three trigger-comparison fields that used to sit here —
227
+ // `watermark_step`, `saturation_step`, `trigger_arm_earlier` — were removed
228
+ // on 2026-09-13. They were accepted by the CLI and no caller could ever
229
+ // compute them: `capsule_trigger.compareTriggers` needs per-step token
230
+ // counts AND per-step surfaced terms, and no payload this tree has observed
231
+ // carries either. `subagent_ledger_hook.ts` names that gap and refuses to
232
+ // invent a proxy, citing this exact module. A flag nothing can fill is the
233
+ // same built-and-unreachable class the rest of this change detects, so it
234
+ // goes rather than waiting behind a label. Re-adding them is one commit
235
+ // once an observation source exists.
234
236
  // Audit-log envelope (sensible defaults for a dispatch record)
235
237
  phase?: LinePhase | undefined;
236
238
  outcome?: LineOutcome | undefined;
@@ -258,7 +260,6 @@ const BANDS: readonly Band[] = ['low', 'medium', 'high'];
258
260
  const PHASES: readonly LinePhase[] = ['refine', 'memory', 'analyze', 'plan', 'implement', 'test', 'verify', 'report'];
259
261
  const LOOKUP_CLASSES: readonly LookupClass[] = ['definition', 'references', 'string-existence', 'report-run'];
260
262
  const ROUTES_TAKEN: readonly RouteTaken[] = ['primitive', 'subagent', 'ask'];
261
- const TRIGGER_ARMS: readonly TriggerArm[] = ['watermark', 'saturation', 'tie'];
262
263
  /** Hex-only payload hash — a hash can never smuggle content (privacy by construction). */
263
264
  const PAYLOAD_HASH_RE = /^[a-f0-9]{8,64}$/i;
264
265
  /** Id-shaped origin tag — enum-ish, never free-form prose. */
@@ -549,16 +550,9 @@ export function buildOrchestrationLine(input: RecordInput): BuiltLine {
549
550
  if (input.capsule_emitted !== undefined && typeof input.capsule_emitted !== 'boolean') {
550
551
  errors.push('capsule_emitted must be a boolean or omitted');
551
552
  }
552
- for (const [key, v] of [
553
- ['capsule_entries', input.capsule_entries],
554
- ['watermark_step', input.watermark_step],
555
- ['saturation_step', input.saturation_step],
556
- ] as const) {
553
+ for (const [key, v] of [['capsule_entries', input.capsule_entries]] as const) {
557
554
  if (v != null && (!isInt(v) || v < 0)) errors.push(`${key} must be a non-negative integer count`);
558
555
  }
559
- if (input.trigger_arm_earlier != null && !TRIGGER_ARMS.includes(input.trigger_arm_earlier)) {
560
- errors.push(`trigger_arm_earlier must be one of ${TRIGGER_ARMS.join(' | ')} (or omitted / null when neither arm fired)`);
561
- }
562
556
 
563
557
  for (const [key, v] of [
564
558
  ['model_requested', input.model_requested],
@@ -619,9 +613,6 @@ export function buildOrchestrationLine(input: RecordInput): BuiltLine {
619
613
  // capsule shadow-measurement — readers ignore unknowns per audit-log-v1
620
614
  capsule_emitted: input.capsule_emitted ?? null,
621
615
  capsule_entries: input.capsule_entries ?? null,
622
- watermark_step: input.watermark_step ?? null,
623
- saturation_step: input.saturation_step ?? null,
624
- trigger_arm_earlier: input.trigger_arm_earlier ?? null,
625
616
  };
626
617
 
627
618
  const line: Record<string, unknown> = {