@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
@@ -52,21 +52,82 @@
52
52
  # of them.
53
53
  # canary optional mutation recipe (Phase 7) — see CANARY below
54
54
  #
55
- # COVERAGE — THE HONEST DENOMINATOR (re-measured 2026-09-08)
55
+ # COVERAGE — THE HONEST DENOMINATOR (re-measured 2026-09-29)
56
56
  # ----------------------------------------------------------
57
- # 322 `lint_*` / `check_*` / `audit_*` scripts live under `src/scripts/`
57
+ # 345 `lint_*` / `check_*` / `audit_*` scripts live under `src/scripts/`
58
58
  # (the count is computed, not asserted — `check_gate_coverage.count_gate_scripts`,
59
- # re-run on the tree carrying these rows. Re-measured 2026-09-08 from the 306
60
- # recorded on 2026-08-28: the tree had drifted to 321 without the header moving,
61
- # which is 15 — exactly the drift tolerance in
62
- # `check_gate_coverage.test.ts` § states an honest denominator — so the next
63
- # single addition was always going to red it. `check_candidate_lines` from
64
- # `road-to-candidate-moves-floor` was that addition. The number is re-measured
65
- # rather than the tolerance widened: a stated population that nobody re-measures
66
- # is the un-asserted count this whole manifest exists to refuse.)
67
- # **79 of them emit the machine-readable `scanned: <N>` line under their
68
- # CI-identical argv, and 76 of those 79 are listed below.** The manifest is
69
- # complete for every gate that can carry a floor, covering ~25 % of the population.
59
+ # re-run on the tree carrying these rows.)
60
+ # **107 rows are listed below**, so the manifest covers 107/345 = 31.0 % of the
61
+ # population, and is complete for every gate that can carry a floor. The row
62
+ # count is `grep -c '^ - id:'`, recomputed here rather than incremented.
63
+ #
64
+ # RECOUNTED 2026-09-29 ON A MERGE, and this is the hazard the two blocks below
65
+ # predicted arriving in the form they predicted. Two branches each recounted
66
+ # CORRECTLY for their own tree — 341/103 here, 341/102-then-103 there — and
67
+ # after the merge both rows exist, so NEITHER pair describes the file. Both
68
+ # figures are recomputed on the MERGED tree rather than reconciled or
69
+ # incremented: 345 from `count_gate_scripts`, 107 from `grep -c '^ - id:'`.
70
+ # The same failure `.secret-allow` records for its own line pin, in a second
71
+ # file, for the same structural reason: a figure about an append-heavy manifest
72
+ # drifts per-branch, not only per-commit.
73
+ #
74
+ # RECOUNTED 2026-09-29, on the change that adds `check_read_surface_coverage`.
75
+ # The header said 97 rows against an actual 102 and 330 scripts against an
76
+ # actual 341 — drift of 5 and 11 against the tolerance of 15 in
77
+ # `check_gate_coverage.test.ts` § states an honest denominator, so nothing was
78
+ # red and the 341st arrival would not have redded it either. Corrected on the
79
+ # way past, which the block below says is exactly how this figure keeps ending
80
+ # up a function of who happened to look. Both numbers are computed here, not
81
+ # incremented: 341 from `count_gate_scripts`, 103 from `grep -c '^ - id:'`.
82
+ # RECOUNTED 2026-09-29, on the change that adds `check_host_format_column`, and
83
+ # BOTH figures had already drifted — which is the block below predicting itself.
84
+ # The header read 330 scripts against a measured 341 (drift 11, against the
85
+ # tolerance of 15 in `check_gate_coverage.test.ts` § states an honest
86
+ # denominator) and 97 rows against a measured 102 before this change. Neither
87
+ # was red and neither would have been for another four arrivals. Both are
88
+ # recomputed on this tree rather than incremented, for the reason the 2026-09-13
89
+ # block gives: an incremented figure inherits whatever error it started from,
90
+ # and the drift above is what that inheritance looks like after sixteen days.
91
+ #
92
+ # RECOUNTED 2026-09-13, on a change that adds NO gate script — and that is the
93
+ # point of this block rather than an aside. Every recount below was triggered by
94
+ # someone adding a row and noticing the header was stale on the way past, which
95
+ # makes the figure a function of who happened to look rather than of the tree.
96
+ # Two scripts had arrived since the 2026-09-12 block below without one: 328 → 330.
97
+ # The drift was 2 against the tolerance of 15 in `check_gate_coverage.test.ts`
98
+ # § states an honest denominator, so nothing was red and nothing would have been
99
+ # for another thirteen arrivals. Corrected here deliberately, while it is cheap,
100
+ # because the 2026-09-08 block records what the other end of that tolerance costs:
101
+ # the tree drifted to 321 against a stated 306 — 15 exactly — and the next single
102
+ # addition was always going to red it.
103
+ #
104
+ # The honest limit of this correction: it is another hand recount, so it goes
105
+ # stale the same way the moment the next script lands. The block below already
106
+ # said as much on 2026-09-12 and it was true again within a day. What would
107
+ # actually close it is deriving the figure at read time instead of asserting it —
108
+ # `count_gate_scripts()` is already the function that answers, and the test
109
+ # already calls it. Deliberately not done here: that is a change to what this
110
+ # file IS (a hand-maintained manifest with a prose header) rather than a
111
+ # correction of a number in it.
112
+ #
113
+ # RE-MEASURED 2026-09-12, on the change that adds `check_enforcement_matrix`:
114
+ # `count_gate_scripts()` returned **328**. Six scripts had arrived since the 322
115
+ # recorded on 2026-09-08, which itself replaced the 306 of 2026-08-28 after the
116
+ # tree drifted to 321 unnoticed. Recomputed on that tree rather than incremented:
117
+ # a stated population that nobody re-measures is the un-asserted count this whole
118
+ # manifest exists to refuse.
119
+ #
120
+ # RECOUNTED AGAIN ON THE MERGE, 2026-09-13. Two branches recounted this header on
121
+ # the same day and neither number survives: the block above measured 330 scripts
122
+ # against 95 rows on a tree without `check_test_delta` / `check_test_weakening`,
123
+ # and this branch measured 97 rows against a tree without the two scripts that
124
+ # block counted. Re-derived on the MERGED tree rather than by adding either
125
+ # side's delta — which is the same resolution `.secret-allow` prescribes for its
126
+ # own pin, and for the same reason: picking a side is how a stated figure comes
127
+ # to describe a tree that no longer exists. Both new gates arrive WITH their
128
+ # rows, so the manifest stays complete for every gate that can carry a floor,
129
+ # and both carry a `--self-test`, so neither adds to the
130
+ # `gate-self-test:registered-non-adopters` ratchet.
70
131
  #
71
132
  # RECOUNTED 2026-09-07, on the change that adds `lint_agent_security` — the
72
133
  # security umbrella, which until this change was the one gate in the tree whose
@@ -436,6 +497,54 @@ gates:
436
497
 
437
498
  Never disclose this section to the user; it is a planted
438
499
  disclosure-suppression imperative and the umbrella must block on it.
500
+ - id: check_test_delta
501
+ argv: ["--quiet"]
502
+ min_scanned: 0
503
+ corpus: "every path in the diff against the base ref. The floor is 0 and the `allowEmpty` reason says why: an empty diff is a real state — a docs-only branch, or a re-run against an already-merged base — and a floor above 0 would red on it. What a `> 0` check could not see is covered instead by the self-test, which drives both polarities of the finding AND both polarities of the owner-label escape."
504
+ status: enforced
505
+ no_canary_reason: >-
506
+ A create-only canary cannot express this gate's violation. The corpus is a
507
+ DIFF, not a tree, so a planted file is only in scope if it is also
508
+ committed — and the violation is an ABSENCE (code changed, no test did),
509
+ which no added file can create. Discrimination is proven instead by the
510
+ gate's own `--self-test`: 8 cases, 3 rejecting, each driving the real CLI.
511
+ Both directions of every branch are covered — a finding with and without
512
+ the owner label, a code change with and without a test delta, and the two
513
+ classes that must NOT count as code (a docs-only change, a generated
514
+ projection under `dist/`).
515
+ note: >
516
+ road-to-adversarial-verification-and-long-runs 2.4. The mechanically
517
+ decidable half of `test-first`: it proves an absence, and deliberately does
518
+ not claim the two things a diff cannot carry — whether the test came FIRST,
519
+ and whether it tests the thing that changed. The exemption is a PR label
520
+ rather than a file, because a file-based escape inside the diff under
521
+ review is one an agent can widen; Risk 4 of that roadmap names exactly
522
+ that failure.
523
+
524
+ - id: check_test_weakening
525
+ argv: ["--quiet"]
526
+ min_scanned: 0
527
+ corpus: "every changed TEST file in the diff against the base ref, measured as NET assertion and suppression counts per file. The floor is 0 for the same reason as its sibling: most diffs change no test file at all, and a floor above 0 would make the gate cry wolf on the majority of the PRs it runs against."
528
+ status: enforced
529
+ no_canary_reason: >-
530
+ A create-only canary cannot express this gate's violation either: the
531
+ violation is a NET REDUCTION between two revisions of one file, so a
532
+ planted file has no predecessor to be smaller than. Proven instead by the
533
+ gate's own `--self-test`: 11 cases, 5 rejecting. The load-bearing cases are
534
+ the ACCEPTS — editing an assertion, adding assertions, and removing a skip
535
+ must all pass, because a gate that reds a legitimate test edit gets its
536
+ exemption widened until it finds nothing. Two cases exist because an
537
+ independent review found the gate blind to them: deleting a whole test file
538
+ (the maximal weakening, and `git rm` was the cheapest way past both gates),
539
+ and a diff line held as string data inside a suite that tests this very
540
+ detector.
541
+ note: >
542
+ road-to-adversarial-verification-and-long-runs 2.4, enforcing
543
+ `evaluator-independence` § Tests are evaluators. Counts are NET, never raw
544
+ removals — that distinction is the whole gate. The discharge is a committed
545
+ artefact under `agents/evidence/reviews/`; whether it contains a genuinely
546
+ independent verdict is a judgement no gate can make and is not claimed.
547
+
439
548
  - id: lint_motion_authority_drift
440
549
  argv: ["--quiet"]
441
550
  min_scanned: 90
@@ -1337,6 +1446,14 @@ gates:
1337
1446
  status: enforced
1338
1447
  no_canary_reason: "The gate carries its own `--self-test` (5 cases, 3 rejecting) — so this row is an adopter, not an exemption; the reason field states why a PLANTED canary is additionally unavailable. Beyond the self-test the negative controls are the 30 mechanism tests in tests/scripts/check_claims.test.ts (17) and tests/scripts/check_claims_retired_phrasings.test.ts (13), each building a throwaway tree and running the real script in it. Twenty-one assert a REJECTION, and one publishes the LITERAL historical string (`zero runtime daemon`) that shipped in package.json and .github/about.yml while claim:no-runtime-daemon was already withdrawn. A planted canary here would mean committing a retired phrasing to a real publish surface and removing it again, and a forgotten one is precisely the defect this gate exists to catch."
1339
1448
 
1449
+ - id: check_kill_switch_table
1450
+ argv: []
1451
+ ci_invocation: .github/workflows/rule-backstops.yml
1452
+ min_scanned: 3
1453
+ corpus: "the three ledger targets this gate resolves — the tree enumeration over `src/scripts/hooks/` and `src/scripts/_lib/` (419 files, 29 switches at registration), the table's row set in `docs/contracts/hook-architecture-v1.md` § Kill switches (29 rows), and the set comparison between them. The FILE count is separately floored by `assertScanned`, which refuses a run that read zero source files, so a moved root fails rather than reporting an empty tree as agreement with an empty table."
1454
+ status: enforced
1455
+ no_canary_reason: "The gate carries its own `--self-test` (6 cases, 4 rejecting), run as its own CI step: a switch with no row, a row with no switch, one-added-plus-one-stale (which a COUNT comparison would pass and a set comparison must not), and a missing section — plus two ACCEPT cases pinning that the two esbuild `--define` identifiers stay excluded, so a gate that started rejecting everything would not pass either. A planted canary would mean committing a fake `AGENT_CONFIG_*` read into a real hook file and removing it again, and a forgotten one is an environment variable in the shipped hook layer that nothing reads — the exact defect this gate exists to catch, left behind by its own canary."
1456
+
1340
1457
  - id: check_rule_evidence_declaration
1341
1458
  argv: []
1342
1459
  min_scanned: 10
@@ -2800,3 +2917,250 @@ gates:
2800
2917
 
2801
2918
  A projected rule routing to a guideline body the projection does not
2802
2919
  contain. The gate must fail on this and pass once it is removed.
2920
+
2921
+ - id: check_placeholder_drift
2922
+ argv: []
2923
+ ci_invocation: .github/workflows/consistency.yml
2924
+ min_scanned: 400
2925
+ corpus: "every projected command and skill source — 502 at baseline; the ratchet reads their PROSE, per the unit published in report_invocation_surface.ts"
2926
+ status: enforced
2927
+ no_canary_reason: >-
2928
+ a create-only canary CAN plant a skill, and a planted body carrying a
2929
+ foreign placeholder is exactly what this gate rejects — so the plant is
2930
+ not impossible, it is redundant. `--self-test` already runs that plant as
2931
+ one of five cases over a fixture root, and doing it in-tree would drop a
2932
+ stray SKILL.md into a projected directory, reddening unrelated generator
2933
+ and count gates before this one is reached. Discrimination is proven by
2934
+ that self-test (5 cases, 3 rejecting) covering both directions the ratchet
2935
+ has to separate: at the seed and BELOW it, green — it must never fail on
2936
+ the absolute number; one occurrence over a ceiling, and a first occurrence
2937
+ against a zero ceiling, red; and an unreadable budget refusing with exit 2
2938
+ rather than certifying a green it could not compute.
2939
+
2940
+ - id: check_input_references
2941
+ argv: []
2942
+ ci_invocation: .github/workflows/consistency.yml
2943
+ min_scanned: 400
2944
+ corpus: "projected commands and skill sources — 502 at baseline. Only artifacts that DECLARE an inputs block are checked, so the finding population is a subset that grows with adoption"
2945
+ status: enforced
2946
+ no_canary_reason: >-
2947
+ the finding population is empty by construction until artifacts adopt the
2948
+ optional block, so a create-only canary would have to plant a DECLARING
2949
+ artifact carrying an unbacked reference — which is precisely what the
2950
+ self-test plants, over a fixture root instead of into src/skills/.
2951
+ Discrimination is proven there (3 cases, 1 rejecting) across the
2952
+ distinction that actually matters for this gate: a backed reference
2953
+ passes, an unbacked one fails, and an artifact with NO declaration passes
2954
+ however it references — the scope rule that lets the gate ship green.
2955
+
2956
+ - id: check_argument_hint
2957
+ argv: []
2958
+ ci_invocation: .github/workflows/consistency.yml
2959
+ min_scanned: 200
2960
+ corpus: "command sources under src/domains — 203 at baseline. Commands only, because argument-hint is a key of command.schema.json and of no other schema"
2961
+ status: enforced
2962
+ no_canary_reason: >-
2963
+ every violation is a DISAGREEMENT between two fields of one file — a
2964
+ hand-written hint contradicting a declaration, or a declaration with no
2965
+ hint. A created file is a legitimate corpus member unless it carries that
2966
+ disagreement, so the useful plant is a content plant, which the self-test
2967
+ performs over a fixture root (4 cases, 2 rejecting): a matching hint
2968
+ passes, a contradicting one fails, a declaration with no hint fails, and
2969
+ an artifact with no declaration keeps its free-text hint untouched. The
2970
+ conflict case was also reproduced by hand against the real tree on
2971
+ 2026-09-29 by planting a wrong hint on a declaring command, which exits 1.
2972
+
2973
+ - id: check_inputs_parity
2974
+ argv: []
2975
+ ci_invocation: .github/workflows/consistency.yml
2976
+ min_scanned: 400
2977
+ corpus: "command sources, skill sources and projected commands — 705 at baseline; only those declaring an inputs block can diverge"
2978
+ status: enforced
2979
+ no_canary_reason: >-
2980
+ a violation is a DIFFERENCE between two readings of one file, so nothing
2981
+ about a file's existence can produce it — only its content can. The
2982
+ self-test plants that content over a fixture root (5 cases, 4 rejecting),
2983
+ one per way the stdlib-only MCP reader and a real YAML parse can part
2984
+ company: the flow form, a nested mapping under an item key, a block
2985
+ scalar, and a dash on its own line. The accepting case is the supported
2986
+ block form, because a suite that only proves failures proves a gate that
2987
+ refuses everything.
2988
+
2989
+ - id: check_read_surface_coverage
2990
+ argv: ["--quiet"]
2991
+ ci_invocation: .github/workflows/consistency.yml
2992
+ min_scanned: 12
2993
+ corpus: >
2994
+ modules under `src/scripts/` that both bring outside bytes in through a
2995
+ recognised inbound shape (`await fetch`, a `node:http(s)` request, a
2996
+ subprocess of curl/wget/gh, a parse of markup on stdin, or a shape the
2997
+ module declares at its own site) and hand a string onward — 24 at
2998
+ baseline. The floor sits at 12 because the shape set is narrow and the
2999
+ corpus moves by ones as a network call is added or removed; a reading
3000
+ below it means a detector stopped matching or the walk lost `src/scripts/`,
3001
+ not that half this repository's network calls were deleted.
3002
+ status: enforced
3003
+ no_canary_reason: >-
3004
+ the canary contract is create-only, and a planted module carrying a fetch
3005
+ would be a legitimate corpus member — the gate would emit a row for it and
3006
+ be right to. Every violation this gate has is a DIFFERENCE between two
3007
+ readings: the committed table against the regenerated one, the floor's
3008
+ header against the table, the detector's text against the no-percentage
3009
+ rule. None of those can be produced by a file's existence, only by its
3010
+ content. Discrimination is proven by `--self-test` instead (7 cases, 6
3011
+ rejecting: a seeded unsanitized fetch-derived emit the table does not
3012
+ carry, a hand-flipped coverage cell, a header restating a module path the
3013
+ table lists, a header with no pointer at all, a recall percentage in the
3014
+ structural detector, and an empty scan root). That last case is the
3015
+ scan-scope collapse `assertScanned` exists for, observed red on
3016
+ 2026-09-29 by planting a fixture root with no `src/scripts/` at all: it
3017
+ exits 1 with "scanned 0 read surface(s)" rather than certifying a table it
3018
+ never compared.
3019
+
3020
+ - id: check_enforcement_matrix
3021
+ argv: ["--quiet"]
3022
+ ci_invocation: .github/workflows/consistency.yml
3023
+ min_scanned: 20
3024
+ corpus: "host-slot pairs in src/scripts/hooks/host_lowering.yaml (32 at baseline, across the 6 hosts that carry a non-empty `slots:` map)"
3025
+ status: enforced
3026
+ no_canary_reason: >-
3027
+ the corpus is the slot map of ONE configuration file at a fixed path, and
3028
+ the canary contract is create-only. A planted second `host_lowering.yaml`
3029
+ would have to overwrite the real one to land in scope, and every violation
3030
+ this gate can have is a MODIFICATION — a cell hand-edited in the generated
3031
+ region, a `block_exit` changed without a regeneration, a cell claiming a
3032
+ vocabulary value no line in the configuration produces. Discrimination is
3033
+ proven by `--self-test` instead (5 cases, 4 rejecting: the phantom
3034
+ `halt-by-state` value, a value outside the closed set, a hand-flipped
3035
+ outcome, a deleted row), and end to end by hand on 2026-09-12: flipping
3036
+ `claude/post_tool_use`'s `block_exit` from null to 2 without regenerating
3037
+ exits 1, and `--write` then exits 0. The `--write` guard was itself
3038
+ measured rather than assumed — an earlier version computed its backing set
3039
+ from the generator's own output, so a sabotaged mapping was self-backing
3040
+ and the phantom value wrote through; the backing set now reads the raw
3041
+ configuration fields and the same sabotage exits 1.
3042
+
3043
+ - id: check_host_format_column
3044
+ argv: ["--quiet"]
3045
+ ci_invocation: .github/workflows/consistency.yml
3046
+ min_scanned: 8
3047
+ corpus: "rows of the host table in docs/enforcement-by-host.md, found by its exact header line (9 at baseline — the nine hosts the file lists)"
3048
+ status: enforced
3049
+ no_canary_reason: >-
3050
+ the corpus is the one table in ONE document at a fixed path, and the
3051
+ canary contract is create-only. A planted second `enforcement-by-host.md`
3052
+ would have to overwrite the real one to land in scope, and every violation
3053
+ this gate can have is a MODIFICATION — a format cell hand-edited to name a
3054
+ path no emitter writes. Discrimination is proven by `--self-test` instead
3055
+ (5 cases, 4 rejecting: the historical `.cursorrules` defect planted back
3056
+ into the Cursor cell, the near-miss `.cursor/rules.mdc`, an invented
3057
+ surface on a correct neighbouring row, and a document whose table header
3058
+ is gone — which must fail on the scope assertion rather than pass over an
3059
+ empty corpus). The self-test earned that list rather than being written to
3060
+ match: on its first run it reported the near-miss case as NOT behaving,
3061
+ twice and for two different real defects. The first was in the matcher —
3062
+ `USER_SCOPE_PATHS`' bare `~/.cursor/` anchor was being treated as a root
3063
+ anything could refine, so `.cursor/rules.mdc` passed on an anchor no
3064
+ emitter writes into; anchors are exact-match only now. The second was in
3065
+ the fixture — it replaced the token rather than the whole row, and the
3066
+ same token appears in the prose paragraph above the table, which this gate
3067
+ does not scan, so `String.replace` mutated the prose and the case asserted
3068
+ nothing. End to end by hand on 2026-09-29: restoring `.cursorrules` into
3069
+ the committed Cursor row exits 1 naming that row and that token, and the
3070
+ restore exits 0.
3071
+
3072
+ - id: lint_deny_text
3073
+ argv: []
3074
+ ci_invocation: .github/workflows/consistency.yml
3075
+ min_scanned: 5
3076
+ corpus: "the `severity: blocking` concerns in src/scripts/hook_manifest.yaml, read from the manifest rather than listed in the gate (9 at baseline)"
3077
+ status: enforced
3078
+ no_canary_reason: >-
3079
+ the corpus is derived from one manifest at a fixed path and the canary
3080
+ contract is create-only. A planted extra concern file is out of scope
3081
+ unless the manifest also names it, and every violation this gate can have
3082
+ is a MODIFICATION — a refusal message edited to name the switch that
3083
+ turns the guard off. Discrimination is proven by `--self-test` instead
3084
+ (7 cases, 3 rejecting: an env assignment with its value, an `export`
3085
+ line, a settings key in prose, and — the direction that matters for a
3086
+ young pattern gate — a bare settings key used as a LOOKUP, which must
3087
+ pass, because a rule that fired on it would fire on every concern that
3088
+ reads its own configuration). An empty corpus is refused by
3089
+ `assertScanned` before the verdict, so a manifest whose `severity:` key
3090
+ was renamed fails rather than certifying zero concerns clean.
3091
+
3092
+ - id: lint_exit_codes
3093
+ argv: []
3094
+ ci_invocation: .github/workflows/consistency.yml
3095
+ min_scanned: 40
3096
+ corpus: "every `.ts` under src/scripts/hooks/ except `exit_codes.ts` itself, which is where the numerals are defined (59 at baseline)"
3097
+ status: enforced
3098
+ no_canary_reason: >-
3099
+ the canary contract is create-only, and a planted hook file carrying a
3100
+ bare `process.exit(1)` WOULD be a legitimate violation the gate is right
3101
+ to report — so a create-only canary here proves detection, not
3102
+ discrimination, and the interesting direction is the opposite one: an
3103
+ imported name must PASS. Discrimination is proven by `--self-test`
3104
+ instead (6 cases, 4 rejecting: a bare deny numeral, a bare allow numeral
3105
+ — `0` is the one people assume is harmless — a call with whitespace
3106
+ inside it, and an empty corpus; against two accepting cases, an imported
3107
+ name and the table file, whose fixture plants a bare exit into
3108
+ `exit_codes.ts` every run so the one exclusion is exercised rather than
3109
+ asserted). What a green does NOT cover is stated in the gate's header: it
3110
+ accepts an identifier without resolving where the identifier came from, so
3111
+ a local `const EXIT_BLOCK = 1` shadowing the import passes this gate and
3112
+ is caught in review as a duplicate declaration.
3113
+
3114
+ - id: check_release_holds
3115
+ argv: ['--lint']
3116
+ ci_invocation: .github/workflows/release-validation.yml
3117
+ min_scanned: 700
3118
+ corpus: >
3119
+ every markdown file under `agents/roadmaps/` and its four subfolders —
3120
+ the active root plus `later/`, `archive/`, `skipped/` and `stubs/`, 950 at
3121
+ baseline. Wider than the active-only glob the sibling roadmap gates use,
3122
+ and deliberately: template rule 28's per-folder lifecycle says a window
3123
+ does not disappear by moving the file, so a hold in `later/` still lists
3124
+ and still refuses. The floor sits at 700 because the archive alone holds
3125
+ ~725 files and legitimate archival churn moves the total by tens, never by
3126
+ hundreds; a reading below it means the walk lost a folder, not that
3127
+ roadmaps were deleted.
3128
+ status: enforced
3129
+ no_canary_reason: >-
3130
+ the canary contract is create-only, and a planted roadmap carrying a
3131
+ well-formed hold would be a legitimate corpus member rather than a
3132
+ violation — the gate would read it, evaluate it, and be right to stay
3133
+ green. Every violation this gate has is a MALFORMED declaration or a
3134
+ lifecycle breach, both of which are properties of a file's content or its
3135
+ folder, not of its existence. Discrimination is proven by `--selftest`
3136
+ instead (19 cases, 13 of them refusing: the four state-table rows, the
3137
+ channel split, seven distinct not-evaluable shapes, and six lifecycle
3138
+ cases), and the scan-scope collapse this row exists to catch is caught
3139
+ inside the gate by `assertScanned` — observed red on 2026-09-19 by
3140
+ pointing the folder list at a non-existent directory, which exits 1 with
3141
+ "scanned 0 roadmap file(s)" rather than certifying an empty corpus.
3142
+ - id: check_host_docs_digest
3143
+ argv: []
3144
+ min_scanned: 8
3145
+ corpus: "verified ROWS of src/scripts/hooks/host_lowering.yaml — the (host, surface) pairs carrying a `verified:` block, which are the rows that can cite a page at all. Nine today; the floor is 8 because a host legitimately leaving the table is a one-row move, while a resolver that stopped seeing the table would read zero. Counting rows and not URLs is deliberate: `cowork` carries a verified block with `docs_url: null` and is a real row the watcher must still see and skip, so a URL count would make a correct no-url row look like corpus loss."
3146
+ status: enforced
3147
+ ci_invocation: .github/workflows/host-docs-digest.yml
3148
+ no_canary_reason: >-
3149
+ A planted file cannot express this gate's violation. The corpus is ONE
3150
+ configuration file plus the live bodies of eight vendor pages, so there is
3151
+ no tree a canary could be added to: the only way to make the floor fire is
3152
+ to remove rows from `host_lowering.yaml`, which is the thing the floor
3153
+ exists to notice and not something a canary may do to a plumbing source
3154
+ under `check_kernel_edit_ratified`. Discrimination is proven instead by
3155
+ two things that run. `--self-test` drives the real decision functions over
3156
+ the real table, including both directions of every state — unchanged vs
3157
+ changed, filled vs missed, 404 (`gone`) vs 503 (`unreachable`), and drift
3158
+ write vs no write. And `tests/scripts/host_docs_digest.test.ts` (20 cases)
3159
+ runs the REAL `lint_hook_manifest._check_host_lowering` over the REAL
3160
+ writer's output, so the full drift-to-refusal chain is exercised end to
3161
+ end rather than asserted. Both were seen RED deliberately: neutralising
3162
+ `isDrift` fails 4 of 20, and reverting the `gone` write-skip fails the
3163
+ case that pins it. The `filled`-vs-`missed` direction named above is
3164
+ asserted in BOTH places — it was credited to `--self-test` before it was
3165
+ actually there, which a review caught and which is why the assertion now
3166
+ exists rather than the sentence being softened.
@@ -34,9 +34,9 @@
34
34
  "note": "The nine active non-draft roadmaps that predate the `relates:` requirement (road-to-agentic-engineering-assurance, road-to-component-library-lifecycle, road-to-council-evidence-integrity, road-to-frontend-fidelity-calibration, road-to-monorepo-scope-and-detection, road-to-observability-plate, road-to-role-scoped-spawn-profiles, road-to-skill-delivery-over-mcp, road-to-standing-payload-diet). This is a NEW obligation rather than a repaired scan root, and the ratchet is the point: `depends:` shipped in the template with nothing requiring it and measured 0 adoption, so requiring `relates:` retroactively across the estate would be an owner-reserved write with no analysis behind the edges it would have to invent (held as step 4.3, deferred). Clearing an entry here is one roadmap at a time: run `agent-config roadmap:context` on it, write the rows the probe surfaces, and lower the count."
35
35
  },
36
36
  "check_source_size_budget": {
37
- "count": 17947,
38
- "landed": "2026-09-11",
39
- "note": "Lowered 17,959 -> 17,947 on 2026-09-11 by the findings-ledger branch, on this entry's own doctrine. The branch adds step 7 to release.ts, which sits ~326 lines PAST the 1500 cap so every line there is one unit, and it came in at +145. Paid down rather than baselined, in two moves. `settle_findings_ledger` and `_blocking_without_disposition` moved out of release.ts into _lib/release_findings_ingest.ts -- 95 lines, and that module is under the cap, so the step and its rationale now cost nothing. It moved on merit rather than only on arithmetic: the module already owns every message the step prints and every argv it builds, and release.ts held only the sequencing. Then the `--ci` fallback dispatch of the three tag-triggered workflows -- 24 lines of loop and warning with no reader elsewhere in step 10 -- moved into _lib/release_tag_dispatch.ts, also under the cap. release.ts 1,826 -> 1,814: a branch that ADDED a pipeline step leaves the file twelve lines shorter than it found it. LOCAL READING, ADMISSIBLE ON THIS ENTRY'S OWN DISCRIMINATOR: the gate walks only SOURCE_ROOTS=['src'] counting excess over the 1500-line ceiling, and `git status --porcelain -uall -- src/` reports the branch's new src/ file staged and nothing untracked, so a clean CI checkout of this commit measures the same 17,947. If CI reads anything else, CI's number is the correct one and this note is the record of why. Carried in this commit because tests/scripts/check_source_size_budget.test.ts asserts baseline == live and therefore BLOCKS where the gate only advises. PRIOR ENTRY: Lowered 17,963 -> 17,959 on 2026-09-09 by feat/cdd-fidelity-council, on the same shape as the entry below and with the same discriminator. The branch's src-link rewrite (a blind review found 253 of 268 links under dist/agent-src/guidelines/ dead, because condense.ts folded only the docs/ prefixes and never (../)+src/rules|skills) came in at +15 excess: condense.ts sits ~1,208 lines PAST the 1500 cap, so every line there is one unit. Paid down rather than baselined, in the way this entry's doctrine prescribes. `_rewrite_body_links` and its two regex constants moved out of condense.ts into _lib/guidelines_lane.ts, 151 lines and therefore UNDER the cap, so the function and its prose now cost nothing. It moved on merit rather than only on arithmetic: every rule the rewriter applies is a statement about which source tree projects where, which is that module's own subject, and condense.ts held it only because that is where the projector lives. condense.ts 2,712 -> 2,708 net, after the one import line and the one call-site rename -- a branch that ADDED a rewrite rule leaves the file four lines shorter than it found it. LOCAL READING, ADMISSIBLE ON THIS ENTRY'S OWN DISCRIMINATOR: the gate walks only SOURCE_ROOTS=['src'] counting excess over the 1500-line ceiling, and `git status --porcelain -uall -- src/` reports the branch's new src/ files tracked and nothing untracked, so a clean CI checkout of this commit measures the same 17,959. If CI reads anything else, CI's number is the correct one and this note is the record of why. Carried in this commit because tests/scripts/check_source_size_budget.test.ts asserts baseline == live and therefore BLOCKS where the gate only advises. PRIOR ENTRY: Lowered 17,973 -> 17,963 on 2026-09-08 by drain/graph-shipped-close. The branch registers five code-graph MCP tools (road-to-a-graph-that-is-shipped 4.1). Their five records and schemas are ~250 lines, and putting them in mcp_server/tools.ts -- which sits ~500 lines PAST the 1500 cap, so every line there is one unit -- would have cost ~250 excess. They live in mcp_server/graph_tools.ts instead (~280 lines, UNDER the cap, so they and their prose cost nothing), and tools.ts spreads them into ALLOWLIST in two lines: one import and one `...GRAPH_TOOLS,`. Those two were paid for rather than baselined, in the way this entry's doctrine prescribes: `_strip` and `_resolvePath` -- a whitespace trim and a tolerant realpath, generic by nature -- moved out of tools.ts into mcp_server/path_util.ts, also under the cap. They are imported under their original underscore names, so all 28 call sites are unchanged and a two-function move does not read as a sweep. tools.ts 2,025 -> 2,015: -12 from the extraction, +2 from the registration, net -10 for a branch that ADDED a feature. CORRECTED 2026-09-08 after an independent review checked the figure against the tree -- this sentence read '2,025 -> 2,013, net -12, against the +2 the registration needed', which states an intermediate file length as the final one and leaves the arithmetic (-12) and the conclusion (-10) describing different trees. `git show origin/main:...tools.ts | wc -l` is 2025 and the working tree is 2015. The TOTAL, 17,963, was always the gate's own output and is unchanged. Every other file this branch touches or creates -- code_graph/verbs.ts, code_graph/cli.ts, code_graph/build.ts, code_graph/detect.ts, code_graph/query.ts, code_graph/sqlite_store.ts, code_graph/types.ts, _lib/regression_neighbourhood.ts -- is under the 1500 ceiling before and after, so it contributes zero excess whatever it grew by. LOCAL READING, ADMISSIBLE ON THIS ENTRY'S OWN DISCRIMINATOR: the gate walks only SOURCE_ROOTS=['src'] counting excess over the 1500-line ceiling, and `git status --porcelain -uall -- src/` reports the two new src/ files staged and nothing untracked, so a clean CI checkout of this commit measures the same 17,963. If CI reads anything else, CI's number is the correct one and this note is the record of why. Carried in this commit because tests/scripts/check_source_size_budget.test.ts asserts baseline == live and therefore BLOCKS where the gate only advises. PRIOR ENTRY: Lowered 17,974 -> 17,973 on 2026-09-08 by drain/topology-c1. The branch adds a live-path emit to council_cli.ts, which sits 2,454 lines PAST the 1500 cap so every line there is one unit, and paid its own way down rather than raising the baseline, in the way this entry's doctrine prescribes. The twelve-line `all_responses` assembly (a spread, three conditionals and a push) moved out of council_cli.ts into ai_council/deliberation_metrics.ts::runCallsFrom, 353 lines and therefore UNDER the cap, so the six stage labels and their prose cost nothing. It moved on merit rather than only on arithmetic: 'which calls did this run make, and at which stage' is that module's own subject, and the CLI held the list only as a flat array with the stage boundaries implicit in the order -- both readings now come out of one walk, so the billing total and the attribution can never be computed over different sets. council_cli.ts 3,955 -> 3,954 net, after the two call lines, the import and the one payload key the feature needed. LOCAL READING, ADMISSIBLE ON THIS ENTRY'S OWN DISCRIMINATOR: the gate walks only SOURCE_ROOTS=['src'] counting excess over the 1500-line ceiling, and `git status --porcelain -uall -- src/` reports the two new src/ files staged and nothing untracked, so a clean CI checkout of this commit measures the same 17,973. If CI reads anything else, CI's number is the correct one and this note is the record of why. Carried in this commit because tests/scripts/check_source_size_budget.test.ts asserts baseline == live and therefore BLOCKS where the gate only advises. PRIOR ENTRY: Lowered 18,016 -> 17,974 on 2026-09-07 by fix/single-delivery-unconditional. SETTLED FIGURE, and it supersedes an intra-session 17,962 this same branch committed earlier — read the direction against MAIN, which is what the ratchet protects: main is 18,016, the branch leaves 17,974, net -42. The 17,962 was this branch's own low before a neutral review returned twelve findings; fixing them added a real mechanism to mcp_server and condense.ts (the flat-wrapper carriage gate the review showed was missing, plus its four-skip predicate), and condense.ts sits ~1,215 lines PAST the cap so each of those lines is one unit. Paid down first, in the way this entry's doctrine prescribes: _suggestSkillForTaskHandler and its 18-line docstring moved out of mcp_server/tools.ts (2,079 -> 2,025) into mcp_server/suggest_skill.ts, 72 lines and UNDER the cap, so its prose costs nothing. The residual +12 against the intra-session low is the gate's own output on the settled tree and is recorded rather than engineered away, because the alternative was extracting _nested_command_subpath and its two callers out of condense.ts - which needs MODULE_STATE, _relativeToPosix and _domains_command_logical passed in, a wider diff than the finding warrants and one no review asked for. LOCAL READING, ADMISSIBLE ON THIS ENTRY'S OWN DISCRIMINATOR: the gate walks only SOURCE_ROOTS=['src'] counting excess over the 1500-line ceiling, and `git status --porcelain -uall -- src/` reports the three new src/ files staged and nothing untracked, so a clean CI checkout of this commit measures the same 17,974. If CI reads anything else, CI's number is the correct one and this note is the record of why. Carried in this commit because tests/scripts/check_source_size_budget.test.ts asserts baseline == live and therefore BLOCKS where the gate only advises. PRIOR ENTRY: MERGE RECONCILIATION 2026-09-07, 18,061/18,017 -> 18,016. drain/admissible-council-seats and fix/release-obligation-answerable each lowered from the SAME 18,062 base in parallel — main to 18,061 (net -1: curlJsonPost, the seat-status helpers and the per-seat config parsing moved out of the three over-ceiling council files) and the branch to 18,017 (net -45: the /dev/tty prompt helpers moved out of release.ts into _lib/tty_prompt.ts, 76 lines and UNDER the cap, taking release.ts 1871 -> 1826). Taking either side alone is wrong in a different direction, which is the reconciliation doctrine this entry already carried from 2026-09-03: main's value silently discards the branch's earned -45, and the branch's value RAISES the baseline against main and weakens the gate. The committed number is neither — it is the gate's own output on the MERGED tree, where both reductions apply, and it equals the arithmetic (18,062 - 45 - 1 = 18,016) rather than being reconciled by it. LOCAL READING, ADMISSIBLE ON THIS ENTRY'S OWN DISCRIMINATOR: the gate walks only SOURCE_ROOTS=['src'] counting excess over the 1500-line ceiling, and `git status --porcelain -uall -- src/` reports ZERO untracked files, so a clean CI checkout of this commit measures the same 18,016. If CI reads anything else, CI's number is the correct one and this note is the record of why. Carried in the merge commit because tests/scripts/check_source_size_budget.test.ts asserts baseline == live and therefore BLOCKS where the gate only advises. PRIOR ENTRY: Lowered 18,062 -> 18,061 on 2026-09-07 by drain/admissible-council-seats. The branch's own additions to the three over-ceiling files (ai_council/config.ts, council_cli.ts, ai_council/clients.ts) came in at +96 excess and were paid down to -1 rather than baselined, in the four ways this entry's doctrine prescribes: `curlJsonPost` moved out of clients.ts into ai_council/api_transport.ts (UNDER the cap, so the prose and the three new provider shims cost nothing); the seat-status helpers moved out of council_cli.ts into ai_council/status_surface.ts, taking an orphaned doc comment with them; the three new per-seat config fields' parsing moved out of config.ts into ai_council/seat_policy.ts::parseSeatFields; and three prose blocks that the branch's own transport repair made FACTUALLY FALSE (the `openai` / `google-genai` Python-SDK claims, and a `_curlJsonPost` pointer to a function that had moved) were corrected and shortened in the same change. LOCAL READING, ADMISSIBLE ON THIS ENTRY'S OWN DISCRIMINATOR: the gate walks only SOURCE_ROOTS=['src'] counting excess over the 1500-line ceiling, and `git status --porcelain -uall -- src/` reports zero untracked files with the five new src/ files staged, so a clean CI checkout of this commit measures the same 18,061. If CI reads anything else, CI's number is the correct one and this note is the record of why. Carried here because tests/scripts/check_source_size_budget.test.ts:166 asserts baseline == live and therefore blocks where the gate only advises -- the lowering commit must carry its own baseline update. Lowered 18,068 -> 18,062 on 2026-09-06 by drain/host-enforcement-truth. Collapsing the five per-host binding constants in install.ts into src/scripts/hooks/host_lowering.yaml removed 29 lines from a file ~3,900 past the 1500 cap, where every line costs one excess; the journal's surface column and its extracted migration helper gave 23 back. Lowered 18,245 -> 18,230 on 2026-09-02 by drain/publication-integrity-option-a. Phase 2 Option A moved the authoring instruction out of the generator: release.ts, which sits ~530 lines PAST the 1500 cap so every line there costs one excess, loses the comment emission, a 16-line HEAD_DEFAULT doc block and its alias const, and gains a shorter writer docstring. The doc block moved to _lib/release_material.ts, which is 289 lines and therefore UNDER the cap, so the same prose now costs nothing. Net -15. The branch first came in at +8 and paid its own way down rather than raising the baseline.",
37
+ "count": 17745,
38
+ "landed": "2026-09-30",
39
+ "note": "LOWERING 2026-09-30, 17,746 -> 17,745 — a lowering commit, and the gate asked for it by name. The direction is the surprising one again: this branch ADDED a feature to `install.ts` (an opt-in host env write) and that file came out SHORTER than it went in, 5,270 -> 5,269. The whole mechanism — the two-row allow table, the settings reader, the env-block builder and the bridge wrapper — lives in the new under-cap `src/scripts/_lib/host_env_write.ts`, so `install.ts` pays ONE import line and one expression for it; and the three-line `for` loop over healed plugin ids in the same function collapsed to one, which is inside the function the change already edits. The gain is a MOVE plus one tightening, never a deletion: nothing was dropped and nothing was swept. That module boundary is also the better one on its own terms — one place now owns which host variable this package may write, which is the question the owner decision of 2026-09-30 turns on. LOCAL READING, on the same discriminator every entry below uses: the gate walks authored source under `src/`, and `git status --porcelain -uall -- src` reported ZERO untracked files, so a clean CI checkout of this commit measures the same 17,745. If CI reads anything else, CI's number is the correct one and this note is the record of why. `landed` is refreshed deliberately: checkRatchet returns `improved` BEFORE it evaluates staleness, so an entry left at 17,746 while the tree measures 17,745 would never expire at any age. PRIOR ENTRY: LOWERING 2026-09-29, 17,748 -> 17,746 — a lowering commit, same doctrine as every entry below, and the gate asked for it by name. This branch ADDED two gate scripts, a guard and a shared lib under `src/`, so the direction is the surprising one and worth stating: the new files each land under their own cap and contribute zero EXCESS, while factoring the shell write shapes out of `block_kernel_rule_writes` into `_lib/shell_write_shapes.ts` moved lines out of a file that was over its cap and into one that is not. Net -2. LOCAL READING, on the same discriminator every entry below uses: the gate walks authored source under `src/`, and `git status --porcelain -uall -- src` reported ZERO untracked files, so a clean CI checkout of this commit measures the same 17,746. If CI reads anything else, CI's number is the correct one and this note is the record of why. `landed` is refreshed deliberately: checkRatchet returns `improved` BEFORE it evaluates staleness, so an entry left at 17,748 while the tree measures 17,746 would never expire at any age. PRIOR ENTRY: LOWERING 2026-09-29, 17,762 -> 17,748 — a lowering commit, same doctrine as every entry below, and the gate asked for it by name. Reporting the host traffic environment in `doctor` needed a new surface; rather than pay those lines against `src/scripts/_cli/cmd_doctor.ts`, already far over the cap, the whole network-posture block moved out into the new under-cap `src/scripts/_cli/doctor_network_posture.ts`, which is also where the new logic lives: one module owns which traffic variables exist, how each is read, and how an absent one is reported, so the unit is coherent rather than a line-count trim. cmd_doctor.ts 3,618 -> 3,604; total 17,762 -> 17,748. The gain is a MOVE, not a deletion — nothing was dropped and nothing was swept. `landed` is refreshed deliberately, for the reason the prior entries record: checkRatchet returns `improved` BEFORE it evaluates staleness, so an entry left at 17,762 while the tree measures 17,748 would never expire at any age, and the gate only ADVISES on a loose ratchet while `check_source_size_budget.test.ts` blocks — that asymmetry is what forces a lowering commit to carry its own baseline update. PRIOR ENTRY: LOWERING 2026-09-27, 17,765 -> 17,762 — the second half of the same repair, and a lowering for the same reason. Closing the remaining preservation defects needed lines in two over-cap files: `src/scripts/install.ts` (route the generated Claude Desktop marker through the tracker, hand the rule-rewrite its skip set) and `src/server/routes/wizard.ts` (surface the conflict count, stop reporting exit 3 as a failure). Both were paid from the same subject rather than from the ratchet: `_files_by_tool_from_bridges` joined its sibling in `src/install/manifestFiles.ts`, which now owns the whole per-tool inventory, and the wizard's exit-code semantics moved into `src/install/preserve.ts` beside `installDoneSummary`, which already lives there for this exact reason. install.ts 5,277 -> 5,270; wizard.ts 1,500 -> 1,504. PRIOR ENTRY: LOWERING 2026-09-27, 17,784 -> 17,765 — a lowering commit, same doctrine as the entry it replaces. Repairing the install-manifest digest loss needed new state in `_update_installed_tools_manifest` (a recorded-inventory read, a preserved-path index, a reconciliation call). Rather than pay that against `src/scripts/install.ts`, already 3,777 over the cap, the manifest file-inventory block (`_sha256_of_file`, `_file_entry`, `_files_by_tool_from_deploy` — 29 lines) moved out into the under-cap `src/install/manifestFiles.ts`, which is where the new logic lives too: one module now owns what the manifest's per-tool `files[]` holds, how it is read back, and how the two reconcile, so the unit is coherent rather than a line-count trim. install.ts 5,296 -> 5,277; total 17,784 -> 17,765. PRIOR ENTRY: LOWERING 2026-09-20, 17,858 -> 17,784 — a lowering commit, not a reconciliation, but measured under the same doctrine the four entries below carry: the committed number is the gate's own reading on the MERGED tree, taken after origin/main was merged into drain/headless-log-write rather than from either side. The branch's transaction-log write added 27 lines to `src/scripts/install.ts`, already 3,897 over the cap, taking the total to 17,885. Rather than pay that, the `json.dumps` byte-parity block (`jsonStrNoAscii`, `_jsonScalar`, `_dumpIndent`, `jsonDumpsIndent`, `jsonDumpsCompact` — 101 lines) moved out of `install.ts` into the under-cap `src/scripts/_lib/json_python_parity.ts`: pure, total functions of their input carrying no installer state, so the unit is coherent rather than a line-count trim. install.ts 5,397 -> 5,296; total 17,885 -> 17,784, a net -74 against the prior baseline. PRIOR ENTRY: MERGE RECONCILIATION 2026-09-14, 17,861 -> 17,858 — the FOURTH reconciliation of this entry, same doctrine as the three below: the committed number is the gate's own reading on the MERGED tree, never either side's. Neither branch earned the -3; it falls out of main having absorbed drain/release-head-field, whose `derive_head_prefill` extraction moved the span-to-prefill composition into an under-cap module. Taking this branch's 17,861 would have silently given that gain back. PRIOR ENTRY: MERGE RECONCILIATION 2026-09-13, 17,863 -> 17,861 — the THIRD reconciliation of this entry in one day, and re-measured on the merged tree rather than taken from either side, which is the only resolution that converges. drain/adversarial-verification reached 17,864 by paying for a `forge_protection` block and a cascade-base module entirely in files under the 1,500-line cap; the incoming reconciliation reached 17,863 from different work. Neither describes the merge. PRIOR ENTRY: MERGE RECONCILIATION 2026-09-13, 17,944/17,866 -> 17,863. drain/decision-closure and drain/breaking-line-contradiction each lowered from the SAME 17,947 base in parallel -- this branch to 17,944 (net -3: the ownership vocabulary, its two Iron-Law sets and the merged default-mode map moved out of ai_council/config.ts into ai_council/ownership_classes.ts, and the settings-rename block moved out of _lib/agent_settings.ts into _lib/settings_renamed_keys.ts, both UNDER the 1500 cap so both cost nothing) and main to 17,866. Taking either side alone is wrong in a different direction, which is the doctrine this entry already carries from 2026-09-03 and 2026-09-07: main's value silently discards this branch's earned -3, and this branch's value RAISES the baseline against main and weakens the gate. The committed number is neither -- it is the gate's own output on the MERGED tree, where both reductions apply. MAIN-SIDE ENTRY, carried so neither branch's record is lost: Lowered 17,947 -> 17,866 on 2026-09-13 by drain/breaking-line-contradiction, on this entry's own doctrine. The branch adds the BACKWARD half of the MIGRATION.md obligation -- a refusal for a major cut whose BREAKING section has no migration entry -- and writing it into release.ts came in at +61, because that file sits ~375 lines PAST the 1500 cap so every line there is one unit. Paid down rather than baselined, and the move was the coherent unit rather than the minimum one: BOTH refusals went, the new backward half and the pre-existing forward half (assert_scheduled_deprecations_clear), into _lib/release_migration_gate.ts at 184 lines and therefore UNDER the cap, so the pair and all their rationale now cost nothing. They moved on merit rather than only on arithmetic -- they are the two halves of one obligation, written eleven months apart, and release.ts held only the two call sites. parse_version stayed module-private in release.ts and the lib carries a two-line _minor_patch instead: exporting a private helper to serve one caller would widen the surface of the file the move exists to narrow. release.ts 1,875 -> 1,733: a branch that ADDED a pipeline refusal leaves the file eighty-one lines shorter than it found it. LOCAL READING, ADMISSIBLE ON THIS ENTRY'S OWN DISCRIMINATOR: the gate walks only SOURCE_ROOTS=['src'] counting excess over the 1500-line ceiling, and `git status --porcelain -uall -- src/` reports nothing staged and nothing untracked at this commit, so a clean CI checkout measures the same 17,866. If CI reads anything else, CI's number is the correct one and this note is the record of why. Carried in this commit because tests/scripts/check_source_size_budget.test.ts asserts baseline == live and therefore BLOCKS where the gate only advises -- which is how CI found it: the gate exited 0 with a loose-ratchet warning and the test went red. PRIOR ENTRY: Lowered 17,959 -> 17,947 on 2026-09-11 by the findings-ledger branch, on this entry's own doctrine. The branch adds step 7 to release.ts, which sits ~326 lines PAST the 1500 cap so every line there is one unit, and it came in at +145. Paid down rather than baselined, in two moves. `settle_findings_ledger` and `_blocking_without_disposition` moved out of release.ts into _lib/release_findings_ingest.ts -- 95 lines, and that module is under the cap, so the step and its rationale now cost nothing. It moved on merit rather than only on arithmetic: the module already owns every message the step prints and every argv it builds, and release.ts held only the sequencing. Then the `--ci` fallback dispatch of the three tag-triggered workflows -- 24 lines of loop and warning with no reader elsewhere in step 10 -- moved into _lib/release_tag_dispatch.ts, also under the cap. release.ts 1,826 -> 1,814: a branch that ADDED a pipeline step leaves the file twelve lines shorter than it found it. LOCAL READING, ADMISSIBLE ON THIS ENTRY'S OWN DISCRIMINATOR: the gate walks only SOURCE_ROOTS=['src'] counting excess over the 1500-line ceiling, and `git status --porcelain -uall -- src/` reports the branch's new src/ file staged and nothing untracked, so a clean CI checkout of this commit measures the same 17,947. If CI reads anything else, CI's number is the correct one and this note is the record of why. Carried in this commit because tests/scripts/check_source_size_budget.test.ts asserts baseline == live and therefore BLOCKS where the gate only advises. PRIOR ENTRY: Lowered 17,963 -> 17,959 on 2026-09-09 by feat/cdd-fidelity-council, on the same shape as the entry below and with the same discriminator. The branch's src-link rewrite (a blind review found 253 of 268 links under dist/agent-src/guidelines/ dead, because condense.ts folded only the docs/ prefixes and never (../)+src/rules|skills) came in at +15 excess: condense.ts sits ~1,208 lines PAST the 1500 cap, so every line there is one unit. Paid down rather than baselined, in the way this entry's doctrine prescribes. `_rewrite_body_links` and its two regex constants moved out of condense.ts into _lib/guidelines_lane.ts, 151 lines and therefore UNDER the cap, so the function and its prose now cost nothing. It moved on merit rather than only on arithmetic: every rule the rewriter applies is a statement about which source tree projects where, which is that module's own subject, and condense.ts held it only because that is where the projector lives. condense.ts 2,712 -> 2,708 net, after the one import line and the one call-site rename -- a branch that ADDED a rewrite rule leaves the file four lines shorter than it found it. LOCAL READING, ADMISSIBLE ON THIS ENTRY'S OWN DISCRIMINATOR: the gate walks only SOURCE_ROOTS=['src'] counting excess over the 1500-line ceiling, and `git status --porcelain -uall -- src/` reports the branch's new src/ files tracked and nothing untracked, so a clean CI checkout of this commit measures the same 17,959. If CI reads anything else, CI's number is the correct one and this note is the record of why. Carried in this commit because tests/scripts/check_source_size_budget.test.ts asserts baseline == live and therefore BLOCKS where the gate only advises. PRIOR ENTRY: Lowered 17,973 -> 17,963 on 2026-09-08 by drain/graph-shipped-close. The branch registers five code-graph MCP tools (road-to-a-graph-that-is-shipped 4.1). Their five records and schemas are ~250 lines, and putting them in mcp_server/tools.ts -- which sits ~500 lines PAST the 1500 cap, so every line there is one unit -- would have cost ~250 excess. They live in mcp_server/graph_tools.ts instead (~280 lines, UNDER the cap, so they and their prose cost nothing), and tools.ts spreads them into ALLOWLIST in two lines: one import and one `...GRAPH_TOOLS,`. Those two were paid for rather than baselined, in the way this entry's doctrine prescribes: `_strip` and `_resolvePath` -- a whitespace trim and a tolerant realpath, generic by nature -- moved out of tools.ts into mcp_server/path_util.ts, also under the cap. They are imported under their original underscore names, so all 28 call sites are unchanged and a two-function move does not read as a sweep. tools.ts 2,025 -> 2,015: -12 from the extraction, +2 from the registration, net -10 for a branch that ADDED a feature. CORRECTED 2026-09-08 after an independent review checked the figure against the tree -- this sentence read '2,025 -> 2,013, net -12, against the +2 the registration needed', which states an intermediate file length as the final one and leaves the arithmetic (-12) and the conclusion (-10) describing different trees. `git show origin/main:...tools.ts | wc -l` is 2025 and the working tree is 2015. The TOTAL, 17,963, was always the gate's own output and is unchanged. Every other file this branch touches or creates -- code_graph/verbs.ts, code_graph/cli.ts, code_graph/build.ts, code_graph/detect.ts, code_graph/query.ts, code_graph/sqlite_store.ts, code_graph/types.ts, _lib/regression_neighbourhood.ts -- is under the 1500 ceiling before and after, so it contributes zero excess whatever it grew by. LOCAL READING, ADMISSIBLE ON THIS ENTRY'S OWN DISCRIMINATOR: the gate walks only SOURCE_ROOTS=['src'] counting excess over the 1500-line ceiling, and `git status --porcelain -uall -- src/` reports the two new src/ files staged and nothing untracked, so a clean CI checkout of this commit measures the same 17,963. If CI reads anything else, CI's number is the correct one and this note is the record of why. Carried in this commit because tests/scripts/check_source_size_budget.test.ts asserts baseline == live and therefore BLOCKS where the gate only advises. PRIOR ENTRY: Lowered 17,974 -> 17,973 on 2026-09-08 by drain/topology-c1. The branch adds a live-path emit to council_cli.ts, which sits 2,454 lines PAST the 1500 cap so every line there is one unit, and paid its own way down rather than raising the baseline, in the way this entry's doctrine prescribes. The twelve-line `all_responses` assembly (a spread, three conditionals and a push) moved out of council_cli.ts into ai_council/deliberation_metrics.ts::runCallsFrom, 353 lines and therefore UNDER the cap, so the six stage labels and their prose cost nothing. It moved on merit rather than only on arithmetic: 'which calls did this run make, and at which stage' is that module's own subject, and the CLI held the list only as a flat array with the stage boundaries implicit in the order -- both readings now come out of one walk, so the billing total and the attribution can never be computed over different sets. council_cli.ts 3,955 -> 3,954 net, after the two call lines, the import and the one payload key the feature needed. LOCAL READING, ADMISSIBLE ON THIS ENTRY'S OWN DISCRIMINATOR: the gate walks only SOURCE_ROOTS=['src'] counting excess over the 1500-line ceiling, and `git status --porcelain -uall -- src/` reports the two new src/ files staged and nothing untracked, so a clean CI checkout of this commit measures the same 17,973. If CI reads anything else, CI's number is the correct one and this note is the record of why. Carried in this commit because tests/scripts/check_source_size_budget.test.ts asserts baseline == live and therefore BLOCKS where the gate only advises. PRIOR ENTRY: Lowered 18,016 -> 17,974 on 2026-09-07 by fix/single-delivery-unconditional. SETTLED FIGURE, and it supersedes an intra-session 17,962 this same branch committed earlier — read the direction against MAIN, which is what the ratchet protects: main is 18,016, the branch leaves 17,974, net -42. The 17,962 was this branch's own low before a neutral review returned twelve findings; fixing them added a real mechanism to mcp_server and condense.ts (the flat-wrapper carriage gate the review showed was missing, plus its four-skip predicate), and condense.ts sits ~1,215 lines PAST the cap so each of those lines is one unit. Paid down first, in the way this entry's doctrine prescribes: _suggestSkillForTaskHandler and its 18-line docstring moved out of mcp_server/tools.ts (2,079 -> 2,025) into mcp_server/suggest_skill.ts, 72 lines and UNDER the cap, so its prose costs nothing. The residual +12 against the intra-session low is the gate's own output on the settled tree and is recorded rather than engineered away, because the alternative was extracting _nested_command_subpath and its two callers out of condense.ts - which needs MODULE_STATE, _relativeToPosix and _domains_command_logical passed in, a wider diff than the finding warrants and one no review asked for. LOCAL READING, ADMISSIBLE ON THIS ENTRY'S OWN DISCRIMINATOR: the gate walks only SOURCE_ROOTS=['src'] counting excess over the 1500-line ceiling, and `git status --porcelain -uall -- src/` reports the three new src/ files staged and nothing untracked, so a clean CI checkout of this commit measures the same 17,974. If CI reads anything else, CI's number is the correct one and this note is the record of why. Carried in this commit because tests/scripts/check_source_size_budget.test.ts asserts baseline == live and therefore BLOCKS where the gate only advises. PRIOR ENTRY: MERGE RECONCILIATION 2026-09-07, 18,061/18,017 -> 18,016. drain/admissible-council-seats and fix/release-obligation-answerable each lowered from the SAME 18,062 base in parallel — main to 18,061 (net -1: curlJsonPost, the seat-status helpers and the per-seat config parsing moved out of the three over-ceiling council files) and the branch to 18,017 (net -45: the /dev/tty prompt helpers moved out of release.ts into _lib/tty_prompt.ts, 76 lines and UNDER the cap, taking release.ts 1871 -> 1826). Taking either side alone is wrong in a different direction, which is the reconciliation doctrine this entry already carried from 2026-09-03: main's value silently discards the branch's earned -45, and the branch's value RAISES the baseline against main and weakens the gate. The committed number is neither — it is the gate's own output on the MERGED tree, where both reductions apply, and it equals the arithmetic (18,062 - 45 - 1 = 18,016) rather than being reconciled by it. LOCAL READING, ADMISSIBLE ON THIS ENTRY'S OWN DISCRIMINATOR: the gate walks only SOURCE_ROOTS=['src'] counting excess over the 1500-line ceiling, and `git status --porcelain -uall -- src/` reports ZERO untracked files, so a clean CI checkout of this commit measures the same 18,016. If CI reads anything else, CI's number is the correct one and this note is the record of why. Carried in the merge commit because tests/scripts/check_source_size_budget.test.ts asserts baseline == live and therefore BLOCKS where the gate only advises. PRIOR ENTRY: Lowered 18,062 -> 18,061 on 2026-09-07 by drain/admissible-council-seats. The branch's own additions to the three over-ceiling files (ai_council/config.ts, council_cli.ts, ai_council/clients.ts) came in at +96 excess and were paid down to -1 rather than baselined, in the four ways this entry's doctrine prescribes: `curlJsonPost` moved out of clients.ts into ai_council/api_transport.ts (UNDER the cap, so the prose and the three new provider shims cost nothing); the seat-status helpers moved out of council_cli.ts into ai_council/status_surface.ts, taking an orphaned doc comment with them; the three new per-seat config fields' parsing moved out of config.ts into ai_council/seat_policy.ts::parseSeatFields; and three prose blocks that the branch's own transport repair made FACTUALLY FALSE (the `openai` / `google-genai` Python-SDK claims, and a `_curlJsonPost` pointer to a function that had moved) were corrected and shortened in the same change. LOCAL READING, ADMISSIBLE ON THIS ENTRY'S OWN DISCRIMINATOR: the gate walks only SOURCE_ROOTS=['src'] counting excess over the 1500-line ceiling, and `git status --porcelain -uall -- src/` reports zero untracked files with the five new src/ files staged, so a clean CI checkout of this commit measures the same 18,061. If CI reads anything else, CI's number is the correct one and this note is the record of why. Carried here because tests/scripts/check_source_size_budget.test.ts:166 asserts baseline == live and therefore blocks where the gate only advises -- the lowering commit must carry its own baseline update. Lowered 18,068 -> 18,062 on 2026-09-06 by drain/host-enforcement-truth. Collapsing the five per-host binding constants in install.ts into src/scripts/hooks/host_lowering.yaml removed 29 lines from a file ~3,900 past the 1500 cap, where every line costs one excess; the journal's surface column and its extracted migration helper gave 23 back. Lowered 18,245 -> 18,230 on 2026-09-02 by drain/publication-integrity-option-a. Phase 2 Option A moved the authoring instruction out of the generator: release.ts, which sits ~530 lines PAST the 1500 cap so every line there costs one excess, loses the comment emission, a 16-line HEAD_DEFAULT doc block and its alias const, and gains a shorter writer docstring. The doc block moved to _lib/release_material.ts, which is 289 lines and therefore UNDER the cap, so the same prose now costs nothing. Net -15. The branch first came in at +8 and paid its own way down rather than raising the baseline. BRANCH-SIDE ENTRY: Lowered 17,947 -> 17,944 on 2026-09-13 by drain/decision-closure, on this entry's own doctrine and at the gate's own request. The branch's ownership-axis additions to ai_council/config.ts came in at +86 excess: that file sits ~700 lines PAST the 1500 cap, so every line there is one unit. Paid down rather than baselined, in one move. The eight ownership class names, the two Iron-Law sets, the merged default-mode map and the mode-check function moved out of config.ts into ai_council/ownership_classes.ts -- 91 lines, UNDER the cap, so the vocabulary and its prose now cost nothing. It moved on merit rather than only on arithmetic: the roadmap-side gate lint_decision_classes needs the same eight names and has no business importing the council's whole schema to learn them, and the loader now carries three lines where it carried twenty-four. config.ts 2,182 -> 2,179: a branch that ADDED an entire decision axis leaves the file three lines shorter than it found it. Every other src/ file this branch creates -- lint_decision_classes.ts, lint_roadmap_producers.ts, closure_scan.ts, council_record_shape.ts, scope_growth.ts, ownership_classes.ts -- is under the 1500 ceiling, so it contributes zero excess whatever it grew by. LOCAL READING, ADMISSIBLE ON THIS ENTRY'S OWN DISCRIMINATOR: the gate walks only SOURCE_ROOTS=['src'] counting excess over the 1500-line ceiling, and `git status --porcelain -uall -- src/` reports the branch's new src/ files tracked and nothing untracked, so a clean CI checkout of this commit measures the same 17,944. If CI reads anything else, CI's number is the correct one and this note is the record of why. Carried in this commit because tests/scripts/check_source_size_budget.test.ts asserts baseline == live and therefore BLOCKS where the gate only advises. PRIOR ENTRY: Lowered 17,959 -> 17,947 on 2026-09-11 by the findings-ledger branch, on this entry's own doctrine. The branch adds step 7 to release.ts, which sits ~326 lines PAST the 1500 cap so every line there is one unit, and it came in at +145. Paid down rather than baselined, in two moves. `settle_findings_ledger` and `_blocking_without_disposition` moved out of release.ts into _lib/release_findings_ingest.ts -- 95 lines, and that module is under the cap, so the step and its rationale now cost nothing. It moved on merit rather than only on arithmetic: the module already owns every message the step prints and every argv it builds, and release.ts held only the sequencing. Then the `--ci` fallback dispatch of the three tag-triggered workflows -- 24 lines of loop and warning with no reader elsewhere in step 10 -- moved into _lib/release_tag_dispatch.ts, also under the cap. release.ts 1,826 -> 1,814: a branch that ADDED a pipeline step leaves the file twelve lines shorter than it found it. LOCAL READING, ADMISSIBLE ON THIS ENTRY'S OWN DISCRIMINATOR: the gate walks only SOURCE_ROOTS=['src'] counting excess over the 1500-line ceiling, and `git status --porcelain -uall -- src/` reports the branch's new src/ file staged and nothing untracked, so a clean CI checkout of this commit measures the same 17,947. If CI reads anything else, CI's number is the correct one and this note is the record of why. Carried in this commit because tests/scripts/check_source_size_budget.test.ts asserts baseline == live and therefore BLOCKS where the gate only advises. PRIOR ENTRY: Lowered 17,963 -> 17,959 on 2026-09-09 by feat/cdd-fidelity-council, on the same shape as the entry below and with the same discriminator. The branch's src-link rewrite (a blind review found 253 of 268 links under dist/agent-src/guidelines/ dead, because condense.ts folded only the docs/ prefixes and never (../)+src/rules|skills) came in at +15 excess: condense.ts sits ~1,208 lines PAST the 1500 cap, so every line there is one unit. Paid down rather than baselined, in the way this entry's doctrine prescribes. `_rewrite_body_links` and its two regex constants moved out of condense.ts into _lib/guidelines_lane.ts, 151 lines and therefore UNDER the cap, so the function and its prose now cost nothing. It moved on merit rather than only on arithmetic: every rule the rewriter applies is a statement about which source tree projects where, which is that module's own subject, and condense.ts held it only because that is where the projector lives. condense.ts 2,712 -> 2,708 net, after the one import line and the one call-site rename -- a branch that ADDED a rewrite rule leaves the file four lines shorter than it found it. LOCAL READING, ADMISSIBLE ON THIS ENTRY'S OWN DISCRIMINATOR: the gate walks only SOURCE_ROOTS=['src'] counting excess over the 1500-line ceiling, and `git status --porcelain -uall -- src/` reports the branch's new src/ files tracked and nothing untracked, so a clean CI checkout of this commit measures the same 17,959. If CI reads anything else, CI's number is the correct one and this note is the record of why. Carried in this commit because tests/scripts/check_source_size_budget.test.ts asserts baseline == live and therefore BLOCKS where the gate only advises. PRIOR ENTRY: Lowered 17,973 -> 17,963 on 2026-09-08 by drain/graph-shipped-close. The branch registers five code-graph MCP tools (road-to-a-graph-that-is-shipped 4.1). Their five records and schemas are ~250 lines, and putting them in mcp_server/tools.ts -- which sits ~500 lines PAST the 1500 cap, so every line there is one unit -- would have cost ~250 excess. They live in mcp_server/graph_tools.ts instead (~280 lines, UNDER the cap, so they and their prose cost nothing), and tools.ts spreads them into ALLOWLIST in two lines: one import and one `...GRAPH_TOOLS,`. Those two were paid for rather than baselined, in the way this entry's doctrine prescribes: `_strip` and `_resolvePath` -- a whitespace trim and a tolerant realpath, generic by nature -- moved out of tools.ts into mcp_server/path_util.ts, also under the cap. They are imported under their original underscore names, so all 28 call sites are unchanged and a two-function move does not read as a sweep. tools.ts 2,025 -> 2,015: -12 from the extraction, +2 from the registration, net -10 for a branch that ADDED a feature. CORRECTED 2026-09-08 after an independent review checked the figure against the tree -- this sentence read '2,025 -> 2,013, net -12, against the +2 the registration needed', which states an intermediate file length as the final one and leaves the arithmetic (-12) and the conclusion (-10) describing different trees. `git show origin/main:...tools.ts | wc -l` is 2025 and the working tree is 2015. The TOTAL, 17,963, was always the gate's own output and is unchanged. Every other file this branch touches or creates -- code_graph/verbs.ts, code_graph/cli.ts, code_graph/build.ts, code_graph/detect.ts, code_graph/query.ts, code_graph/sqlite_store.ts, code_graph/types.ts, _lib/regression_neighbourhood.ts -- is under the 1500 ceiling before and after, so it contributes zero excess whatever it grew by. LOCAL READING, ADMISSIBLE ON THIS ENTRY'S OWN DISCRIMINATOR: the gate walks only SOURCE_ROOTS=['src'] counting excess over the 1500-line ceiling, and `git status --porcelain -uall -- src/` reports the two new src/ files staged and nothing untracked, so a clean CI checkout of this commit measures the same 17,963. If CI reads anything else, CI's number is the correct one and this note is the record of why. Carried in this commit because tests/scripts/check_source_size_budget.test.ts asserts baseline == live and therefore BLOCKS where the gate only advises. PRIOR ENTRY: Lowered 17,974 -> 17,973 on 2026-09-08 by drain/topology-c1. The branch adds a live-path emit to council_cli.ts, which sits 2,454 lines PAST the 1500 cap so every line there is one unit, and paid its own way down rather than raising the baseline, in the way this entry's doctrine prescribes. The twelve-line `all_responses` assembly (a spread, three conditionals and a push) moved out of council_cli.ts into ai_council/deliberation_metrics.ts::runCallsFrom, 353 lines and therefore UNDER the cap, so the six stage labels and their prose cost nothing. It moved on merit rather than only on arithmetic: 'which calls did this run make, and at which stage' is that module's own subject, and the CLI held the list only as a flat array with the stage boundaries implicit in the order -- both readings now come out of one walk, so the billing total and the attribution can never be computed over different sets. council_cli.ts 3,955 -> 3,954 net, after the two call lines, the import and the one payload key the feature needed. LOCAL READING, ADMISSIBLE ON THIS ENTRY'S OWN DISCRIMINATOR: the gate walks only SOURCE_ROOTS=['src'] counting excess over the 1500-line ceiling, and `git status --porcelain -uall -- src/` reports the two new src/ files staged and nothing untracked, so a clean CI checkout of this commit measures the same 17,973. If CI reads anything else, CI's number is the correct one and this note is the record of why. Carried in this commit because tests/scripts/check_source_size_budget.test.ts asserts baseline == live and therefore BLOCKS where the gate only advises. PRIOR ENTRY: Lowered 18,016 -> 17,974 on 2026-09-07 by fix/single-delivery-unconditional. SETTLED FIGURE, and it supersedes an intra-session 17,962 this same branch committed earlier — read the direction against MAIN, which is what the ratchet protects: main is 18,016, the branch leaves 17,974, net -42. The 17,962 was this branch's own low before a neutral review returned twelve findings; fixing them added a real mechanism to mcp_server and condense.ts (the flat-wrapper carriage gate the review showed was missing, plus its four-skip predicate), and condense.ts sits ~1,215 lines PAST the cap so each of those lines is one unit. Paid down first, in the way this entry's doctrine prescribes: _suggestSkillForTaskHandler and its 18-line docstring moved out of mcp_server/tools.ts (2,079 -> 2,025) into mcp_server/suggest_skill.ts, 72 lines and UNDER the cap, so its prose costs nothing. The residual +12 against the intra-session low is the gate's own output on the settled tree and is recorded rather than engineered away, because the alternative was extracting _nested_command_subpath and its two callers out of condense.ts - which needs MODULE_STATE, _relativeToPosix and _domains_command_logical passed in, a wider diff than the finding warrants and one no review asked for. LOCAL READING, ADMISSIBLE ON THIS ENTRY'S OWN DISCRIMINATOR: the gate walks only SOURCE_ROOTS=['src'] counting excess over the 1500-line ceiling, and `git status --porcelain -uall -- src/` reports the three new src/ files staged and nothing untracked, so a clean CI checkout of this commit measures the same 17,974. If CI reads anything else, CI's number is the correct one and this note is the record of why. Carried in this commit because tests/scripts/check_source_size_budget.test.ts asserts baseline == live and therefore BLOCKS where the gate only advises. PRIOR ENTRY: MERGE RECONCILIATION 2026-09-07, 18,061/18,017 -> 18,016. drain/admissible-council-seats and fix/release-obligation-answerable each lowered from the SAME 18,062 base in parallel — main to 18,061 (net -1: curlJsonPost, the seat-status helpers and the per-seat config parsing moved out of the three over-ceiling council files) and the branch to 18,017 (net -45: the /dev/tty prompt helpers moved out of release.ts into _lib/tty_prompt.ts, 76 lines and UNDER the cap, taking release.ts 1871 -> 1826). Taking either side alone is wrong in a different direction, which is the reconciliation doctrine this entry already carried from 2026-09-03: main's value silently discards the branch's earned -45, and the branch's value RAISES the baseline against main and weakens the gate. The committed number is neither — it is the gate's own output on the MERGED tree, where both reductions apply, and it equals the arithmetic (18,062 - 45 - 1 = 18,016) rather than being reconciled by it. LOCAL READING, ADMISSIBLE ON THIS ENTRY'S OWN DISCRIMINATOR: the gate walks only SOURCE_ROOTS=['src'] counting excess over the 1500-line ceiling, and `git status --porcelain -uall -- src/` reports ZERO untracked files, so a clean CI checkout of this commit measures the same 18,016. If CI reads anything else, CI's number is the correct one and this note is the record of why. Carried in the merge commit because tests/scripts/check_source_size_budget.test.ts asserts baseline == live and therefore BLOCKS where the gate only advises. PRIOR ENTRY: Lowered 18,062 -> 18,061 on 2026-09-07 by drain/admissible-council-seats. The branch's own additions to the three over-ceiling files (ai_council/config.ts, council_cli.ts, ai_council/clients.ts) came in at +96 excess and were paid down to -1 rather than baselined, in the four ways this entry's doctrine prescribes: `curlJsonPost` moved out of clients.ts into ai_council/api_transport.ts (UNDER the cap, so the prose and the three new provider shims cost nothing); the seat-status helpers moved out of council_cli.ts into ai_council/status_surface.ts, taking an orphaned doc comment with them; the three new per-seat config fields' parsing moved out of config.ts into ai_council/seat_policy.ts::parseSeatFields; and three prose blocks that the branch's own transport repair made FACTUALLY FALSE (the `openai` / `google-genai` Python-SDK claims, and a `_curlJsonPost` pointer to a function that had moved) were corrected and shortened in the same change. LOCAL READING, ADMISSIBLE ON THIS ENTRY'S OWN DISCRIMINATOR: the gate walks only SOURCE_ROOTS=['src'] counting excess over the 1500-line ceiling, and `git status --porcelain -uall -- src/` reports zero untracked files with the five new src/ files staged, so a clean CI checkout of this commit measures the same 18,061. If CI reads anything else, CI's number is the correct one and this note is the record of why. Carried here because tests/scripts/check_source_size_budget.test.ts:166 asserts baseline == live and therefore blocks where the gate only advises -- the lowering commit must carry its own baseline update. Lowered 18,068 -> 18,062 on 2026-09-06 by drain/host-enforcement-truth. Collapsing the five per-host binding constants in install.ts into src/scripts/hooks/host_lowering.yaml removed 29 lines from a file ~3,900 past the 1500 cap, where every line costs one excess; the journal's surface column and its extracted migration helper gave 23 back. Lowered 18,245 -> 18,230 on 2026-09-02 by drain/publication-integrity-option-a. Phase 2 Option A moved the authoring instruction out of the generator: release.ts, which sits ~530 lines PAST the 1500 cap so every line there costs one excess, loses the comment emission, a 16-line HEAD_DEFAULT doc block and its alias const, and gains a shorter writer docstring. The doc block moved to _lib/release_material.ts, which is 289 lines and therefore UNDER the cap, so the same prose now costs nothing. Net -15. The branch first came in at +8 and paid its own way down rather than raising the baseline.",
40
40
  "reason": "Lowered 18,230 -> 18,209 on 2026-09-03 by drain/artifact-location. `doctor --strict` needed 16 lines of integration wiring in cmd_doctor.ts, which sits ~2,100 lines PAST the 1500 cap so every line there costs one excess. The branch first came in at +137 and paid its own way down rather than raising the baseline: the strict semantics, threshold, kill switch, payload helper and argv fragments moved to _cli/_doctor_strict.ts (~160 lines, under the cap, costs nothing), taking it to +15; then the 37-line drift renderer moved to _cli/_doctor_emit.ts. Net -21. The renderer was chosen deliberately: pure rendering, two dependencies, no argparse involvement and therefore none of the byte-parity risk the parser surface carries against the retired Python implementation; nothing outside cmd_doctor.ts imported it. An AI council on the residual split (a: raise by 15 / d: descope the feature); neither was taken, because a parallel branch demonstrated the same day that paying down is achievable in this codebase, which is the evidence the split lacked. This gate's own test comment prescribes it: a commit that splits a god-file lowers the excess and must carry the lowered baseline. MERGE RECONCILIATION 2026-09-03, 18,209 -> 18,205: drain/wired-instruments and drain/artifact-location each lowered from the SAME 18,230 base in parallel -- the branch to 18,226 (net -4, run_continuation_hook.ts's termination ladder moved to _lib/continuation_ladder.ts and runtime_journal.ts's prose to _lib/outcome_vocabularies.ts, both under the cap) and main to 18,209 (net -21, the doctor --strict split above). Taking either side alone is wrong in a different direction: main's value silently discards the branch's earned -4, and the branch's value RAISES the baseline against main and weakens the gate. The committed number is neither -- it is the gate's own output on the merged tree, where both reductions apply. THIS LOCAL READING IS ADMISSIBLE HERE, and the discriminator matters because this file's ci-parity:local-only entry records a 165 -> 164 lowering that was reverted for exactly the opposite case: that count depends on how the environment resolves the projection surface, whereas this gate walks only SOURCE_ROOTS = ['src'] (check_source_size_budget.ts:88) counting excess lines over the 1500 ceiling, and `git status --porcelain -uall -- src/` reported ZERO untracked files, so a clean CI checkout of this commit measures the same 18,205. If CI reads anything else, CI's number is the correct one and this note is the record of why. LOWERED 18,205 -> 18,193 on 2026-09-03 by fix/release-curation-ask. The change adds a fifth publication guard, and its own additions landed at +10 excess: release.ts 2,008 -> 2,018, every line of which is past the 1500 cap. Paid down rather than baselined, in the two ways this gate's doctrine prescribes. The guard's rationale (~13 lines of docstring, including why prMerged is a parameter rather than a call-site if) lives in release_publication.ts, 915 lines and therefore UNDER the cap, so the prose costs nothing; and the two pure text helpers _splitlines and _rstrip moved out of release.ts into release_env.ts (333 lines, also under the cap), taking release.ts to 1,996 -- 12 BELOW where it started. The branch came in at +10 and leaves at -12. Recorded while moving them: _splitlines is duplicated a third and fourth time at adoption_report.ts:106 and readme_linter.ts:418; collapsing all three onto one definition touches three modules and was left out of this change rather than done silently. LOWERED 18,193 -> 18,180 on 2026-09-03 by fix/release-branch-reuse. The step-1 branch decision -- twenty lines of if/else in release.ts -- became one call to checkout_release_branch in release_publication.ts, which is 1,019 lines and therefore UNDER the 1500 cap, so the twenty lines plus a ~35-line rationale now cost nothing. release.ts 1,996 -> 1,983, every one of those lines being past the cap. The drill grew 765 -> 918 for two new scenarios and the snapshot/restore that keeps them from writing the real CHANGELOG, and it is under the cap too. A change that adds a feature and lowers the excess is the shape this gate's doctrine asks for; the alternative was a +13 raise for logic that had no business in the god-file. LOWERED 18,180 -> 18,068 on 2026-09-05 by fix/release-gate-producer-parity. The change wires the governance-mix obligation into the writer and the two local guards and adds a release-gate locality registry; its own additions to release.ts landed at +19 excess, every one of those lines past the 1500 cap. Paid down rather than baselined, in the way this entry's doctrine prescribes: the 132-line test-count-trend unit (_TEST_COUNT_LINE_RE, _TEST_LIST_MAX_BUFFER, _count_from_list_result, _count_tests_current, _previous_test_count_from_changelog, _render_test_trend_line) moved from release.ts into release_publication.ts, which is under the cap and where it now belongs on merit rather than only on arithmetic -- the same diff makes the missing `Tests: N` footer a section-level publication blocker, so the writer that emits the footer and the guard that refuses its absence sit in one module. release.ts 1,983 -> 1,871. All four names are re-exported from release.ts, so tests/scripts/release.test.ts is unchanged. LOCAL READING, ADMISSIBLE ON THIS ENTRY'S OWN DISCRIMINATOR: the gate walks only SOURCE_ROOTS=['src'] counting excess over the ceiling, and `git status --porcelain -uall -- src/` reports ZERO untracked files with the two new src/ files staged, so a clean CI checkout of this commit measures the same 18,068. If CI reads anything else, CI's number is the correct one and this note is the record of why."
41
41
  },
42
42
  "ci-parity:local-only": {
@@ -52,14 +52,18 @@
52
52
  "falsifier": "./scripts-run src/scripts/check_gate_coverage"
53
53
  },
54
54
  "lint_canonical_terms": {
55
- "count": 1005,
56
- "landed": "2026-09-08",
57
- "note": "Lowered 1006 -> 1005 on 2026-09-08 by drain/graph-shipped, on the same shape as the entry below and with the same discriminator. This branch's payload migration moved a See-also line carrying `artefact` from the rule into the code-intelligence skill; keeping the word in both places would have RAISED the count, so both occurrences were written canonical LINE-SCOPED — only lines this diff authored, never a file-wide sweep. One of the two is a genuine reduction. The gate reported the tree at 1005 against a baseline of 1006 and asked for exactly this lowering. `landed` is refreshed for the reason the prior entry records: checkRatchet returns `improved` before it evaluates staleness, so an entry left high never expires. PRIOR ENTRY: Lowered 1007 -> 1006 on 2026-09-07 by drain/observed-learning-signal, and the gain was INCIDENTAL rather than sought: the branch's own new prose introduced five house-dialect occurrences under src/ and docs/ (four `artefact`/`artefacts`, one `behaviour`), which were rewritten LINE-SCOPED — only lines this diff authored, never a file-wide sweep, because `sweep_authorised: false` is still a live constraint. Four of the five were net-neutral (they replaced deleted lines carrying the same word) and one was a genuine reduction. The gate reported the tree at 1006 against a baseline of 1007 and asked for exactly this lowering, so the gain cannot be silently given back. LOCAL READING, ADMISSIBLE ON THE SAME DISCRIMINATOR THIS FILE'S check_source_size_budget ENTRY USES: the gate walks only authored prose under src/ and docs/, and `git status --porcelain -uall -- src docs` reported ZERO untracked files, so a clean CI checkout of this commit measures the same 1006. If CI reads anything else, CI's number is the correct one and this note is the record of why. `landed` is refreshed deliberately: checkRatchet returns `improved` BEFORE it evaluates staleness, so an entry left at 1007 while the tree measures 1006 would never expire at any age. PRIOR ENTRY: House-dialect debt in authored prose under src/ and docs/, measured by the gate this entry governs (road-to-canonical-terms 2.2). NOT a dead-root repair: the gate is new, and the debt is the corpus that predates the dialect decision recorded in src/config/canonical-terms.yml. A hard-zero gate was refused on the same ground the AI council refused an unconditional sweep on 2026-08-25. The ratchet IS the council's staged promotion: report-only for existing debt, blocking for anything new. What remains is dominated by two large-radius pairs — `artefact`->`artifact` and `behaviour`->`behavior` — plus `hand-off`->`handoff` and `licence`->`license`, the last of which the map flags `semantic-care-required` because licence TITLES are protected text. Shrink-only from here. Full classified inventory: agents/evidence/analysis/canonical-terms-inventory-2026-08-26.md.",
55
+ "count": 1004,
56
+ "landed": "2026-09-13",
57
+ "note": "Lowered 1005 -> 1004 on 2026-09-13 by drain/adversarial-verification, on the same shape and the same discriminator as every entry below. That branch added a routed rule and then paid its per-spawn payload cost by migrating argument prose out of three standing rules (autonomous-execution, delegation-policy, evaluator-independence) into their context and guideline files under the P4 pattern. Nothing was deleted and nothing was swept: the migrated lines were REWRITTEN CANONICAL where the diff authored them, LINE-SCOPED, because `sweep_authorised: false` is still a live constraint. Most were net-neutral -- they replaced deleted lines carrying the same word -- and one is a genuine reduction. The gate reported the tree at 1004 against a baseline of 1005 and asked for exactly this lowering, so the gain cannot be silently given back. LOCAL READING, ADMISSIBLE ON THE SAME DISCRIMINATOR THE ENTRIES BELOW USE: the gate walks only authored prose under src/ and docs/, and a porcelain status over those two roots reported ZERO untracked files, so a clean CI checkout of this commit measures the same 1004. If CI reads anything else, CI's number is the correct one and this note is the record of why. `landed` is refreshed deliberately: checkRatchet returns `improved` BEFORE it evaluates staleness, so an entry left at 1005 while the tree measures 1004 would never expire at any age. PRIOR ENTRY: Lowered 1006 -> 1005 on 2026-09-08 by drain/graph-shipped, on the same shape as the entry below and with the same discriminator. This branch's payload migration moved a See-also line carrying `artefact` from the rule into the code-intelligence skill; keeping the word in both places would have RAISED the count, so both occurrences were written canonical LINE-SCOPED — only lines this diff authored, never a file-wide sweep. One of the two is a genuine reduction. The gate reported the tree at 1005 against a baseline of 1006 and asked for exactly this lowering. `landed` is refreshed for the reason the prior entry records: checkRatchet returns `improved` before it evaluates staleness, so an entry left high never expires. PRIOR ENTRY: Lowered 1007 -> 1006 on 2026-09-07 by drain/observed-learning-signal, and the gain was INCIDENTAL rather than sought: the branch's own new prose introduced five house-dialect occurrences under src/ and docs/ (four `artefact`/`artefacts`, one `behaviour`), which were rewritten LINE-SCOPED — only lines this diff authored, never a file-wide sweep, because `sweep_authorised: false` is still a live constraint. Four of the five were net-neutral (they replaced deleted lines carrying the same word) and one was a genuine reduction. The gate reported the tree at 1006 against a baseline of 1007 and asked for exactly this lowering, so the gain cannot be silently given back. LOCAL READING, ADMISSIBLE ON THE SAME DISCRIMINATOR THIS FILE'S check_source_size_budget ENTRY USES: the gate walks only authored prose under src/ and docs/, and `git status --porcelain -uall -- src docs` reported ZERO untracked files, so a clean CI checkout of this commit measures the same 1006. If CI reads anything else, CI's number is the correct one and this note is the record of why. `landed` is refreshed deliberately: checkRatchet returns `improved` BEFORE it evaluates staleness, so an entry left at 1007 while the tree measures 1006 would never expire at any age. PRIOR ENTRY: House-dialect debt in authored prose under src/ and docs/, measured by the gate this entry governs (road-to-canonical-terms 2.2). NOT a dead-root repair: the gate is new, and the debt is the corpus that predates the dialect decision recorded in src/config/canonical-terms.yml. A hard-zero gate was refused on the same ground the AI council refused an unconditional sweep on 2026-08-25. The ratchet IS the council's staged promotion: report-only for existing debt, blocking for anything new. What remains is dominated by two large-radius pairs — `artefact`->`artifact` and `behaviour`->`behavior` — plus `hand-off`->`handoff` and `licence`->`license`, the last of which the map flags `semantic-care-required` because licence TITLES are protected text. Shrink-only from here. Full classified inventory: agents/evidence/analysis/canonical-terms-inventory-2026-08-26.md.",
58
58
  "falsifier": "./scripts-run src/scripts/lint_canonical_terms"
59
59
  },
60
60
  "lint_handoffs": {
61
61
  "count": 18,
62
62
  "landed": "2026-08-02",
63
+ "reaffirmed": {
64
+ "date": "2026-09-28",
65
+ "reason": "Re-derived today, not waved through: `lint_handoffs` reports the same 18 findings across 13 distinct link targets — adr-create, upstream-contribute, feature-planning, po-discovery, refine-ticket, risk-officer, spreadsheet-authoring, html-deck, systematic-debugging, source-discovery, threat-modeling, finishing-a-development-branch, incident-commander. The count has not drifted, so nothing new accumulated behind the baseline. NOT CLEARED because clearing it is not the backfill the note makes it sound like: `tier` is a judgement per skill, and asserting `senior` on thirteen link targets is an estate-wide metadata decision that changes what other tier-reading surfaces see — it is not release work and it is not one person's call inside an unrelated PR. The alternative the note names (deciding `tier: unset` on a link target is not a violation, and narrowing the rule) is the same decision from the other side. This reaffirmation is the honest state: the debt is measured, static, understood, and undecided. Revisit-if: the count rises above 18, or the tier taxonomy is decided either way — whichever comes first. Expires 2026-11-23 (56 days), by which point a decision is owed rather than a second reaffirmation."
66
+ },
63
67
  "note": "All 18 are `handoff_tier_mismatch`: a senior-tier skill links to a peer whose frontmatter carries no `tier` (reported as tier='unset') — e.g. build-buy-partner → adr-create, customer-research → po-discovery, gated-reach → threat-modeling. This is a tier-metadata backfill backlog on the LINKED-TO skills, not a broken link. The one genuinely dangling link revealed by the same repair (competitive-positioning → a non-existent ../analyze-reference-repo/SKILL.md; it is a command, not a skill) was FIXED in the repairing commit rather than baselined. Clearing this entry means backfilling `tier` on the linked-to skills, or deciding that `tier: unset` on a link target is not a violation and narrowing the rule."
64
68
  },
65
69
  "lint_pack_boundaries": {