@event4u/agent-config 16.0.0 → 16.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (382) hide show
  1. package/.augment-plugin/marketplace.json +3 -3
  2. package/.augment-plugin/plugin.json +1 -1
  3. package/.claude-plugin/marketplace.json +1 -1
  4. package/CHANGELOG.md +289 -140
  5. package/CONTRIBUTING.md +9 -0
  6. package/README.md +2 -2
  7. package/dist/agent-src/commands/analyze/inbox.md +26 -0
  8. package/dist/agent-src/commands/analyze/roadmap-repos.md +27 -0
  9. package/dist/agent-src/commands/challenge-me/closure.md +178 -0
  10. package/dist/agent-src/commands/challenge-me.md +9 -2
  11. package/dist/agent-src/commands/council/default.md +25 -5
  12. package/dist/agent-src/commands/feature/plan.md +28 -1
  13. package/dist/agent-src/commands/feature/roadmap.md +28 -1
  14. package/dist/agent-src/commands/implement-ticket.md +27 -0
  15. package/dist/agent-src/commands/jira-ticket.md +27 -0
  16. package/dist/agent-src/commands/pr/create.md +29 -0
  17. package/dist/agent-src/commands/pr/merge.md +60 -9
  18. package/dist/agent-src/commands/roadmap/ai-council.md +7 -1
  19. package/dist/agent-src/commands/roadmap/create.md +74 -27
  20. package/dist/agent-src/commands/roadmap/materialize.md +27 -0
  21. package/dist/agent-src/commands/roadmap/process-full.md +123 -18
  22. package/dist/agent-src/commands/team.md +22 -0
  23. package/dist/agent-src/commands/work.md +4 -0
  24. package/dist/agent-src/contexts/authority/scope-growth-ownership.md +80 -0
  25. package/dist/agent-src/contexts/communication/rules-auto/user-interaction-mechanics.md +47 -0
  26. package/dist/agent-src/contexts/execution/auto-orchestration-activation.md +44 -3
  27. package/dist/agent-src/contexts/execution/autonomy-mechanics.md +19 -23
  28. package/dist/agent-src/contexts/execution/decision-closure-execution.md +93 -0
  29. package/dist/agent-src/contexts/execution/fix-loop-ladder.md +108 -0
  30. package/dist/agent-src/contexts/execution/plan-confidence-gate.md +4 -2
  31. package/dist/agent-src/contexts/execution/roadmap-process-loop.md +75 -6
  32. package/dist/agent-src/guidelines/agent-infra/active-remediation-mechanics.md +57 -0
  33. package/dist/agent-src/guidelines/agent-infra/evaluator-independence-mechanics.md +102 -0
  34. package/dist/agent-src/guidelines/design-fidelity-routing.md +29 -0
  35. package/dist/agent-src/rules/autonomous-execution.md +15 -8
  36. package/dist/agent-src/rules/delegation-policy.md +23 -45
  37. package/dist/agent-src/rules/design-fidelity.md +32 -37
  38. package/dist/agent-src/rules/evaluator-independence.md +43 -49
  39. package/dist/agent-src/rules/test-first.md +41 -0
  40. package/dist/agent-src/rules/ui-audit-gate.md +1 -2
  41. package/dist/agent-src/skills/accessibility-auditor/data/aria-patterns.csv +1 -1
  42. package/dist/agent-src/skills/accessibility-auditor/data/manifest.json +3 -3
  43. package/dist/agent-src/skills/ai-council/SKILL.md +11 -3
  44. package/dist/agent-src/skills/api-design/data/manifest.json +3 -3
  45. package/dist/agent-src/skills/database/data/manifest.json +4 -4
  46. package/dist/agent-src/skills/database/data/query-tuning.csv +3 -3
  47. package/dist/agent-src/skills/design-review/SKILL.md +80 -4
  48. package/dist/agent-src/skills/design-review/references/verification-automation.md +32 -0
  49. package/dist/agent-src/skills/existing-ui-audit/SKILL.md +30 -5
  50. package/dist/agent-src/skills/existing-ui-audit/references/component-taxonomy-detection.md +56 -0
  51. package/dist/agent-src/skills/experiment-loop/SKILL.md +31 -1
  52. package/dist/agent-src/skills/markitdown/SKILL.md +4 -0
  53. package/dist/agent-src/skills/playwright-testing/SKILL.md +30 -6
  54. package/dist/agent-src/skills/roadmap-writing/SKILL.md +1 -1
  55. package/dist/agent-src/skills/storybook-workshop/SKILL.md +42 -3
  56. package/dist/agent-src/skills/threat-modeling/SKILL.md +9 -4
  57. package/dist/agent-src/skills/threat-modeling/data/manifest.json +4 -4
  58. package/dist/agent-src/skills/ui-component-architect/SKILL.md +23 -0
  59. package/dist/agent-src/skills/verify-repair-loop/SKILL.md +37 -3
  60. package/dist/agent-src/templates/agent-settings.md +1 -1
  61. package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
  62. package/dist/agent-src/templates/roadmaps.md +218 -0
  63. package/dist/agent-src/templates/scripts/work_engine/directives/backend/_self_fix.ts +62 -2
  64. package/dist/agent-src/templates/scripts/work_engine/directives/backend/test.ts +1 -0
  65. package/dist/agent-src/templates/scripts/work_engine/directives/backend/verify.ts +1 -0
  66. package/dist/agent-src/templates/scripts/work_engine/directives/ui/app_spec.ts +10 -4
  67. package/dist/agent-src/templates/scripts/work_engine/directives/ui/apply.ts +260 -21
  68. package/dist/agent-src/templates/scripts/work_engine/directives/ui/audit.ts +22 -3
  69. package/dist/agent-src/templates/scripts/work_engine/directives/ui/scaffold.ts +6 -1
  70. package/dist/agent-src/templates/scripts/work_engine/state.ts +57 -7
  71. package/dist/agent-src/templates/scripts/work_engine/taxonomy/detect.ts +500 -0
  72. package/dist/agent-src/templates/scripts/work_engine/taxonomy/place.ts +183 -0
  73. package/dist/cli/python/knowledge_ingest.js +1 -1
  74. package/dist/cli/python/workspace_drive.js +1 -1
  75. package/dist/cli-delegate/check_update_banner.js +40 -2
  76. package/dist/cli-delegate/chunk-4E2W63DA.js +335 -0
  77. package/dist/cli-delegate/{chunk-S3VFXMTM.js → chunk-4XFP2Y3E.js} +1 -1
  78. package/dist/cli-delegate/chunk-5R7HOLAF.js +99 -0
  79. package/dist/cli-delegate/{chunk-IVBPRCQO.js → chunk-6KFEC3G7.js} +1 -1
  80. package/dist/cli-delegate/{chunk-2TAQ2K4V.js → chunk-6KM6ORRY.js} +55 -386
  81. package/dist/cli-delegate/{chunk-AK6HODNQ.js → chunk-7XGBX6ZH.js} +973 -555
  82. package/dist/cli-delegate/{chunk-2XY7ILBT.js → chunk-FC75PGJE.js} +42 -2
  83. package/dist/cli-delegate/{chunk-55ACLDUE.js → chunk-FP36SWJD.js} +587 -141
  84. package/dist/cli-delegate/{chunk-3FKR64R5.js → chunk-IGD6ERX6.js} +62 -7
  85. package/dist/cli-delegate/{chunk-GYC3VRYN.js → chunk-JNMVLWLR.js} +99 -14
  86. package/dist/cli-delegate/{chunk-H32DGUJH.js → chunk-P6ZHP5W2.js} +1 -1
  87. package/dist/cli-delegate/{chunk-57PN4GYJ.js → chunk-RGNIWTEA.js} +1 -1
  88. package/dist/cli-delegate/chunk-ROYYTEZU.js +39 -0
  89. package/dist/cli-delegate/{chunk-2DK2VJWG.js → chunk-WGXTVKCQ.js} +20 -6
  90. package/dist/cli-delegate/chunk-YDPILCOL.js +152 -0
  91. package/dist/cli-delegate/{chunk-QJWAUMJW.js → chunk-YJ72UIAK.js} +730 -15
  92. package/dist/cli-delegate/cmd_analyze_session.js +1 -1
  93. package/dist/cli-delegate/cmd_brand_status.js +1 -1
  94. package/dist/cli-delegate/cmd_conformance.js +985 -831
  95. package/dist/cli-delegate/cmd_doctor.js +7 -5
  96. package/dist/cli-delegate/cmd_explain.js +2 -2
  97. package/dist/cli-delegate/cmd_export.js +9 -7
  98. package/dist/cli-delegate/cmd_fleet.js +4 -2
  99. package/dist/cli-delegate/cmd_handoff.js +7 -7
  100. package/dist/cli-delegate/cmd_mcp_available.js +1 -1
  101. package/dist/cli-delegate/cmd_memory_get.js +1 -1
  102. package/dist/cli-delegate/cmd_migrate.js +2 -2
  103. package/dist/cli-delegate/cmd_packs_active.js +2 -2
  104. package/dist/cli-delegate/cmd_preflight.js +4 -2
  105. package/dist/cli-delegate/cmd_prune.js +10 -8
  106. package/dist/cli-delegate/cmd_route_audit.js +3 -3
  107. package/dist/cli-delegate/cmd_route_explain.js +1 -1
  108. package/dist/cli-delegate/cmd_session_recycle.js +4 -4
  109. package/dist/cli-delegate/cmd_settings_get.js +28 -2
  110. package/dist/cli-delegate/cmd_settings_migrate.js +9 -7
  111. package/dist/cli-delegate/cmd_settings_set.js +1 -1
  112. package/dist/cli-delegate/cmd_sync.js +10 -8
  113. package/dist/cli-delegate/cmd_uninstall.js +16 -13
  114. package/dist/cli-delegate/cmd_update.js +1 -1
  115. package/dist/cli-delegate/cmd_upgrade.js +1 -1
  116. package/dist/cli-delegate/cmd_validate.js +12 -10
  117. package/dist/cli-delegate/cmd_versions.js +1 -1
  118. package/dist/discovery/deprecation-report.md +1 -1
  119. package/dist/discovery/discovery-manifest.json +90 -43
  120. package/dist/discovery/discovery-manifest.json.sha256 +1 -1
  121. package/dist/discovery/discovery-manifest.summary.md +4 -4
  122. package/dist/discovery/dormancy-report.md +1 -1
  123. package/dist/discovery/orphan-report.md +1 -1
  124. package/dist/discovery/packs.json +10 -8
  125. package/dist/discovery/trust-report.md +3 -3
  126. package/dist/discovery/workspaces.json +8 -6
  127. package/dist/hooks/dispatch.js +7682 -2772
  128. package/dist/hooks/dispatch.sha256 +1 -0
  129. package/dist/install/claudeRuleRewrite.js +14 -3
  130. package/dist/install/conflict.js +60 -10
  131. package/dist/install/conflictTracker.js +218 -0
  132. package/dist/install/fsPrimitives.js +54 -0
  133. package/dist/install/install.mjs +2200 -1559
  134. package/dist/install/manifestFiles.js +259 -0
  135. package/dist/install/preflight.js +14 -2
  136. package/dist/install/preserve.js +157 -0
  137. package/dist/install/recordedOwnership.js +132 -0
  138. package/dist/install/txlog.js +11 -2
  139. package/dist/mcp/registry-manifest.json +3 -3
  140. package/dist/mcp/server.json +2 -2
  141. package/dist/mcp/server.mjs +3066 -1470
  142. package/dist/router.json +1 -1
  143. package/dist/scripts/_lib/agent_settings.js +3 -2
  144. package/dist/scripts/_lib/fs_atomic.js +167 -0
  145. package/dist/scripts/_lib/installed_tools.js +460 -0
  146. package/dist/scripts/_lib/lean_projection_mode.js +3 -2
  147. package/dist/scripts/_lib/settings_renamed_keys.js +78 -0
  148. package/dist/server/routes/install.js +8 -0
  149. package/dist/server/routes/wizard.js +9 -3
  150. package/dist/server/schemas/settings.js +40 -5
  151. package/dist/shared/missionExecution.js +97 -0
  152. package/dist/ui/assets/index-Cwq4d0hX.js +23 -0
  153. package/dist/ui/assets/index-Cyjy8bWO.css +1 -0
  154. package/dist/ui/index.html +2 -2
  155. package/docs/MIGRATION.md +103 -0
  156. package/docs/contracts/settings-classes.md +28 -7
  157. package/docs/guidelines/agent-infra/active-remediation-mechanics.md +57 -0
  158. package/docs/guidelines/agent-infra/evaluator-independence-mechanics.md +102 -0
  159. package/docs/guidelines/design-fidelity-routing.md +29 -0
  160. package/package.json +4 -4
  161. package/src/agent-src/templates/scripts/work_engine/directives/backend/_self_fix.ts +62 -2
  162. package/src/agent-src/templates/scripts/work_engine/directives/backend/test.ts +1 -0
  163. package/src/agent-src/templates/scripts/work_engine/directives/backend/verify.ts +1 -0
  164. package/src/agent-src/templates/scripts/work_engine/directives/ui/app_spec.ts +10 -4
  165. package/src/agent-src/templates/scripts/work_engine/directives/ui/apply.ts +260 -21
  166. package/src/agent-src/templates/scripts/work_engine/directives/ui/audit.ts +22 -3
  167. package/src/agent-src/templates/scripts/work_engine/directives/ui/scaffold.ts +6 -1
  168. package/src/agent-src/templates/scripts/work_engine/state.ts +57 -7
  169. package/src/agent-src/templates/scripts/work_engine/taxonomy/detect.ts +500 -0
  170. package/src/agent-src/templates/scripts/work_engine/taxonomy/place.ts +183 -0
  171. package/src/config/agent-settings.template.yml +168 -27
  172. package/src/config/auto-rule-budget.json +16 -0
  173. package/src/config/ci-local-parity.yml +36 -5
  174. package/src/config/continuity-surface.json +23 -2
  175. package/src/config/gate-coverage.yml +377 -13
  176. package/src/config/gate-violation-baselines.json +10 -6
  177. package/src/config/hook-latency-budget.json +21 -0
  178. package/src/config/hook-token-budget.json +4 -2
  179. package/src/config/loop-surfaces.yaml +215 -0
  180. package/src/config/metric-registry.yml +114 -0
  181. package/src/config/placeholder-drift-budget.json +10 -0
  182. package/src/config/platform-anchor.json +3 -2
  183. package/src/config/release-gate-locality.yml +11 -0
  184. package/src/config/routing-coverage-seed.json +33 -5
  185. package/src/config/rule-enforcement-baseline.json +0 -1
  186. package/src/install/claudeRuleRewrite.ts +16 -2
  187. package/src/install/conflict.ts +65 -9
  188. package/src/install/conflictTracker.ts +299 -0
  189. package/src/install/fsPrimitives.ts +54 -0
  190. package/src/install/manifestFiles.ts +312 -0
  191. package/src/install/preflight.ts +16 -2
  192. package/src/install/preserve.ts +205 -0
  193. package/src/install/recordedOwnership.ts +149 -0
  194. package/src/install/txlog.ts +11 -2
  195. package/src/install/types.ts +19 -4
  196. package/src/scripts/_cli/cmd_conformance.ts +88 -15
  197. package/src/scripts/_cli/cmd_doctor.ts +7 -20
  198. package/src/scripts/_cli/cmd_route_explain.ts +9 -2
  199. package/src/scripts/_cli/cmd_settings_get.ts +44 -0
  200. package/src/scripts/_cli/doctor_execution.ts +173 -0
  201. package/src/scripts/_cli/doctor_network_posture.ts +226 -0
  202. package/src/scripts/_lib/activation_payload.ts +247 -0
  203. package/src/scripts/_lib/agent_settings.ts +4 -2
  204. package/src/scripts/_lib/authority_path.ts +102 -0
  205. package/src/scripts/_lib/body_portable.ts +102 -0
  206. package/src/scripts/_lib/cascade_base.ts +94 -0
  207. package/src/scripts/_lib/cc_transcript.ts +132 -1
  208. package/src/scripts/_lib/changelog_eras.ts +7 -2
  209. package/src/scripts/_lib/concern_sla_window.ts +200 -0
  210. package/src/scripts/_lib/continuation_ladder.ts +149 -2
  211. package/src/scripts/_lib/council_transport.ts +129 -0
  212. package/src/scripts/_lib/delivery_ready.ts +112 -0
  213. package/src/scripts/_lib/forge_protection.ts +381 -0
  214. package/src/scripts/_lib/forge_reader.ts +558 -0
  215. package/src/scripts/_lib/hook_platform_keys.ts +27 -0
  216. package/src/scripts/_lib/host_env_write.ts +111 -0
  217. package/src/scripts/_lib/json_python_parity.ts +127 -0
  218. package/src/scripts/_lib/lean_projection_mode.ts +14 -3
  219. package/src/scripts/_lib/llm_proposer_transport.ts +14 -4
  220. package/src/scripts/_lib/loop_surfaces.ts +382 -0
  221. package/src/scripts/_lib/mission_record.ts +190 -0
  222. package/src/scripts/_lib/obligation_frequency.ts +217 -0
  223. package/src/scripts/_lib/obligations.ts +513 -0
  224. package/src/scripts/_lib/orchestration_record.ts +11 -20
  225. package/src/scripts/_lib/probe_inputs.ts +271 -0
  226. package/src/scripts/_lib/read_surface_scan.ts +322 -0
  227. package/src/scripts/_lib/release_highlights.ts +201 -11
  228. package/src/scripts/_lib/release_holds.ts +493 -0
  229. package/src/scripts/_lib/release_migration_gate.ts +184 -0
  230. package/src/scripts/_lib/retrieval_sanitize.ts +77 -16
  231. package/src/scripts/_lib/rides_along.ts +102 -0
  232. package/src/scripts/_lib/settings_renamed_keys.ts +82 -0
  233. package/src/scripts/_lib/shell_write_shapes.ts +108 -0
  234. package/src/scripts/_lib/structural_hiding.ts +622 -0
  235. package/src/scripts/_lib/test_delta.ts +166 -0
  236. package/src/scripts/_lib/test_provenance.ts +171 -0
  237. package/src/scripts/_lib/turn_end_refusals.ts +525 -4
  238. package/src/scripts/_lib/turn_end_transcript.ts +216 -0
  239. package/src/scripts/_lib/typed_op_grant.ts +246 -0
  240. package/src/scripts/_lib/typed_op_watch.ts +185 -0
  241. package/src/scripts/_lib/verification_command.ts +324 -0
  242. package/src/scripts/_lib/verification_evidence.ts +647 -0
  243. package/src/scripts/_lib/verify_clause.ts +280 -0
  244. package/src/scripts/ai_council/config.ts +8 -11
  245. package/src/scripts/ai_council/fallback_config.ts +5 -3
  246. package/src/scripts/ai_council/ownership_classes.ts +118 -0
  247. package/src/scripts/ai_team/team_dispatch.ts +4 -0
  248. package/src/scripts/ask_block_census.ts +285 -15
  249. package/src/scripts/before_complete_hook.ts +340 -23
  250. package/src/scripts/bench_hook_latency.ts +412 -5
  251. package/src/scripts/cache_realization_report.ts +61 -1
  252. package/src/scripts/check_argument_hint.ts +287 -0
  253. package/src/scripts/check_claims.ts +125 -1
  254. package/src/scripts/check_enforcement_coverage.ts +38 -9
  255. package/src/scripts/check_enforcement_matrix.ts +866 -0
  256. package/src/scripts/check_finding_dispositions.ts +22 -3
  257. package/src/scripts/check_gate_reachability.ts +173 -10
  258. package/src/scripts/check_generator_sync.ts +1025 -0
  259. package/src/scripts/check_host_docs_digest.ts +786 -0
  260. package/src/scripts/check_host_format_column.ts +559 -0
  261. package/src/scripts/check_input_references.ts +228 -0
  262. package/src/scripts/check_inputs_parity.ts +235 -0
  263. package/src/scripts/check_kernel_edit_ratified.ts +47 -5
  264. package/src/scripts/check_kill_switch_table.ts +294 -0
  265. package/src/scripts/check_placeholder_drift.ts +221 -0
  266. package/src/scripts/check_preamble_payload_budget.ts +75 -2
  267. package/src/scripts/check_read_surface_coverage.ts +515 -0
  268. package/src/scripts/check_release_highlights.ts +33 -1
  269. package/src/scripts/check_release_holds.ts +568 -0
  270. package/src/scripts/check_routing_coverage.ts +378 -11
  271. package/src/scripts/check_rule_projection_integrity.ts +22 -12
  272. package/src/scripts/check_static_parity.ts +17 -0
  273. package/src/scripts/check_test_delta.ts +294 -0
  274. package/src/scripts/check_test_weakening.ts +299 -0
  275. package/src/scripts/ci_settle.ts +145 -19
  276. package/src/scripts/closure_scan.ts +447 -0
  277. package/src/scripts/compile_hook_manifest.ts +102 -18
  278. package/src/scripts/council_record_shape.ts +209 -0
  279. package/src/scripts/external_sources_denylist.json +29 -4
  280. package/src/scripts/hook_manifest.json +1 -1
  281. package/src/scripts/hook_manifest.yaml +127 -11
  282. package/src/scripts/hooks/block_config_weakening.ts +229 -4
  283. package/src/scripts/hooks/block_kernel_rule_writes.ts +9 -78
  284. package/src/scripts/hooks/block_plumbing_writes.ts +314 -0
  285. package/src/scripts/hooks/bundle_integrity.ts +291 -0
  286. package/src/scripts/hooks/chain_nudge_hook.ts +1 -2
  287. package/src/scripts/hooks/code_graph_context_hook.ts +1 -2
  288. package/src/scripts/hooks/comment_discipline_hook.ts +1 -3
  289. package/src/scripts/hooks/concern_registry.ts +6 -0
  290. package/src/scripts/hooks/concern_timings.ts +94 -0
  291. package/src/scripts/hooks/delegation_nudge_hook.ts +1 -2
  292. package/src/scripts/hooks/design_pass_hook.ts +221 -5
  293. package/src/scripts/hooks/design_slop_hook.ts +1 -2
  294. package/src/scripts/hooks/dispatch_hook.ts +25 -19
  295. package/src/scripts/hooks/edit_shape_hook.ts +1 -2
  296. package/src/scripts/hooks/end_review_nudge_hook.ts +1 -1
  297. package/src/scripts/hooks/evidence_independence.ts +97 -2
  298. package/src/scripts/hooks/exit_codes.ts +128 -0
  299. package/src/scripts/hooks/host_lowering.json +1 -0
  300. package/src/scripts/hooks/host_lowering.ts +128 -3
  301. package/src/scripts/hooks/host_lowering.yaml +262 -46
  302. package/src/scripts/hooks/host_semantics.ts +30 -2
  303. package/src/scripts/hooks/injection_budget.ts +9 -2
  304. package/src/scripts/hooks/interruption_ledger_hook.ts +1 -2
  305. package/src/scripts/hooks/journal_record_hook.ts +1 -2
  306. package/src/scripts/hooks/memory_recall_hook.ts +539 -0
  307. package/src/scripts/hooks/obligation_settle_hook.ts +328 -0
  308. package/src/scripts/hooks/one_question_per_ask_hook.ts +25 -4
  309. package/src/scripts/hooks/orchestration_record_hook.ts +1 -2
  310. package/src/scripts/hooks/permission_gate.ts +1 -2
  311. package/src/scripts/hooks/push_settle_hook.ts +1 -3
  312. package/src/scripts/hooks/replay_hook.ts +2 -1
  313. package/src/scripts/hooks/reread_guard_hook.ts +1 -2
  314. package/src/scripts/hooks/review_baseline_hook.ts +1 -2
  315. package/src/scripts/hooks/rtk_wrap_hook.ts +1 -2
  316. package/src/scripts/hooks/rule_inject_hook.ts +55 -4
  317. package/src/scripts/hooks/run_continuation_hook.ts +30 -35
  318. package/src/scripts/hooks/skill_route_hook.ts +1 -2
  319. package/src/scripts/hooks/source_first_gate_hook.ts +1 -2
  320. package/src/scripts/hooks/spawn_guard_shadow_hook.ts +1 -2
  321. package/src/scripts/hooks/subagent_ledger_hook.ts +1 -2
  322. package/src/scripts/hooks/suggestion_capture_hook.ts +1 -1
  323. package/src/scripts/hooks/table_fingerprint.ts +49 -0
  324. package/src/scripts/hooks/telemetry_flush_hook.ts +1 -2
  325. package/src/scripts/hooks/telemetry_usage_hook.ts +1 -2
  326. package/src/scripts/hooks/tool_result_bytes_hook.ts +93 -9
  327. package/src/scripts/hooks/turn_end_gate_hook.ts +337 -324
  328. package/src/scripts/hooks/ui_route_nudge_hook.ts +1 -2
  329. package/src/scripts/hooks_doctor.ts +85 -0
  330. package/src/scripts/hooks_status.ts +19 -0
  331. package/src/scripts/install.ts +109 -210
  332. package/src/scripts/janitor.ts +7 -0
  333. package/src/scripts/lint_decision_classes.ts +464 -0
  334. package/src/scripts/lint_deny_text.ts +419 -0
  335. package/src/scripts/lint_exit_codes.ts +262 -0
  336. package/src/scripts/lint_hook_manifest.ts +66 -0
  337. package/src/scripts/lint_major_migration_sections.ts +450 -0
  338. package/src/scripts/lint_plan_risk_register.ts +47 -3
  339. package/src/scripts/lint_roadmap_blockers.ts +139 -12
  340. package/src/scripts/lint_roadmap_producers.ts +202 -0
  341. package/src/scripts/lint_skill_descriptions.ts +1 -1
  342. package/src/scripts/lint_skill_scripts_readonly.ts +1 -1
  343. package/src/scripts/mcp_server/prompts.ts +142 -1
  344. package/src/scripts/measure_rule_budget.ts +101 -5
  345. package/src/scripts/measure_skill_ranker_baseline.ts +305 -51
  346. package/src/scripts/measure_turn_end_gate.ts +193 -0
  347. package/src/scripts/memory_lookup.ts +1 -1
  348. package/src/scripts/new_roadmap.ts +37 -0
  349. package/src/scripts/orchestration_record.ts +0 -5
  350. package/src/scripts/prepack-check.mjs +53 -0
  351. package/src/scripts/release.ts +14 -98
  352. package/src/scripts/release_drill.ts +267 -1
  353. package/src/scripts/release_publication.ts +64 -1
  354. package/src/scripts/report_enforcement_drift.ts +566 -0
  355. package/src/scripts/report_host_injection_effect.ts +3 -11
  356. package/src/scripts/report_invocation_surface.ts +383 -0
  357. package/src/scripts/report_obligation_carriers.ts +699 -0
  358. package/src/scripts/report_obligation_writer_split.ts +236 -0
  359. package/src/scripts/report_skill_menu_census.ts +208 -0
  360. package/src/scripts/report_standing_payload_by_host.ts +26 -152
  361. package/src/scripts/roadmap_verify_share.ts +213 -0
  362. package/src/scripts/schemas/agent-settings.schema.json +23 -0
  363. package/src/scripts/schemas/command.schema.json +48 -0
  364. package/src/scripts/schemas/review-findings.schema.json +4 -0
  365. package/src/scripts/schemas/skill.schema.json +44 -0
  366. package/src/scripts/scope_growth.ts +219 -0
  367. package/src/scripts/self_review_gate.ts +359 -10
  368. package/src/scripts/semantic_parity_census.ts +314 -0
  369. package/src/scripts/session_eol_report.ts +54 -1
  370. package/src/scripts/sweep_dead_scan_roots.ts +1 -1
  371. package/src/scripts/trigger_eval_grandfather.json +0 -3
  372. package/src/scripts/trigger_eval_rotation.ts +122 -27
  373. package/src/scripts/ui_conformance_probe.ts +774 -0
  374. package/src/scripts/update_prices.ts +85 -3
  375. package/src/scripts/write_bundle_digest.mjs +74 -0
  376. package/src/server/schemas/settings.ts +62 -7
  377. package/src/shared/missionExecution.ts +126 -0
  378. package/src/templates/consumer-settings/ONBOARDING.md +1 -0
  379. package/dist/cli-delegate/chunk-523HCFYO.js +0 -90
  380. package/dist/ui/assets/index-D0jaCRPM.js +0 -29
  381. package/dist/ui/assets/index-jSfMW8_t.css +0 -1
  382. /package/dist/cli-delegate/{chunk-YUM5KURG.js → chunk-7RQP4UDH.js} +0 -0
@@ -0,0 +1,294 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * The kill-switch table says what it counts — this checks that it still does.
4
+ *
5
+ * `docs/contracts/hook-architecture-v1.md` § Kill switches enumerates every
6
+ * `AGENT_CONFIG_*` identifier a file under `src/scripts/hooks/` or
7
+ * `src/scripts/_lib/` carries, and publishes the command that reproduces the
8
+ * count. An equality between a hand-written table and a grep over a growing
9
+ * tree is exactly the claim that decays without anybody touching the document:
10
+ * the table shipped at 28 == 28 and was stale within a day, because a merge
11
+ * brought in one hook carrying one new switch. Found by an independent review,
12
+ * which is a worse way to find it than a gate.
13
+ *
14
+ * WHAT IS CHECKED, and it is the equality itself rather than a proxy for it:
15
+ * the gate runs the published enumeration and compares the SET against the
16
+ * table's rows. A set comparison rather than a count, because two errors that
17
+ * cancel — one switch added, one row for a deleted switch left behind — is the
18
+ * failure a count cannot see, and it is the one that leaves a reader chasing a
19
+ * variable nothing reads.
20
+ *
21
+ * WHAT IS DELIBERATELY NOT CHECKED: the owner class, and the prose of each row.
22
+ * Whether a switch is `maintainer` or `harness` is a judgement about who is
23
+ * expected to set it, and a gate that guessed at it would either be wrong or
24
+ * would have to encode the judgement somewhere else — which is the table.
25
+ *
26
+ * THE TWO EXCLUSIONS ARE PART OF THE CONTRACT, not a convenience.
27
+ * `__AGENT_CONFIG_BUNDLE__` and `__AGENT_CONFIG_CLI_DELEGATE__` are esbuild
28
+ * `--define` identifiers (see `package.json`'s `build:*` scripts): nothing can
29
+ * set one at runtime, so neither is a switch. They are excluded on the emitted
30
+ * token, because `grep -o` strips the leading underscores and the naive
31
+ * `grep -v __AGENT_CONFIG_BUNDLE__` therefore matches nothing.
32
+ */
33
+ import * as fs from 'node:fs';
34
+ import * as path from 'node:path';
35
+ import process from 'node:process';
36
+ import { fileURLToPath, pathToFileURL } from 'node:url';
37
+
38
+ import { GateLedger } from './_lib/gate_ledger.js';
39
+ import { runGateCli, runSelfTest, type SelfTestCase } from './_lib/gate_self_test.js';
40
+ import { assertScanned, reportScanned } from './_lib/scan_scope.js';
41
+
42
+ const GATE = 'check_kill_switch_table';
43
+ const CONTRACT = path.join('docs', 'contracts', 'hook-architecture-v1.md');
44
+ const SECTION = '## Kill switches';
45
+ const ROOTS = [path.join('src', 'scripts', 'hooks'), path.join('src', 'scripts', '_lib')];
46
+
47
+ /** The esbuild defines. Not environment variables, so not switches. */
48
+ const BUILD_DEFINES: ReadonlySet<string> = new Set([
49
+ 'AGENT_CONFIG_BUNDLE__',
50
+ 'AGENT_CONFIG_CLI_DELEGATE__',
51
+ ]);
52
+
53
+ const TOKEN = /AGENT_CONFIG_[A-Z_]+/g;
54
+ /** A table row: `| \`AGENT_CONFIG_X\` | owner | … |`. */
55
+ const ROW = /^\|\s*`(AGENT_CONFIG_[A-Z_]+)`\s*\|/gm;
56
+
57
+ function walk(dir: string, out: string[]): void {
58
+ let entries: fs.Dirent[];
59
+ try {
60
+ entries = fs.readdirSync(dir, { withFileTypes: true });
61
+ } catch {
62
+ return;
63
+ }
64
+ for (const e of entries) {
65
+ const full = path.join(dir, e.name);
66
+ if (e.isDirectory()) {
67
+ walk(full, out);
68
+ } else if (e.isFile()) {
69
+ out.push(full);
70
+ }
71
+ }
72
+ }
73
+
74
+ /** Every switch name the declared roots carry, minus the build defines. */
75
+ export function switchesInTree(root: string): { names: Set<string>; files: number } {
76
+ const files: string[] = [];
77
+ for (const rel of ROOTS) {
78
+ walk(path.join(root, rel), files);
79
+ }
80
+ const names = new Set<string>();
81
+ for (const f of files) {
82
+ let text: string;
83
+ try {
84
+ text = fs.readFileSync(f, 'utf-8');
85
+ } catch {
86
+ continue;
87
+ }
88
+ for (const m of text.matchAll(TOKEN)) {
89
+ const name = m[0];
90
+ if (!BUILD_DEFINES.has(name)) names.add(name);
91
+ }
92
+ }
93
+ return { names, files: files.length };
94
+ }
95
+
96
+ /** Every switch the contract's table has a row for. */
97
+ export function switchesInTable(root: string): { names: Set<string>; present: boolean } {
98
+ let text: string;
99
+ try {
100
+ text = fs.readFileSync(path.join(root, CONTRACT), 'utf-8');
101
+ } catch {
102
+ return { names: new Set(), present: false };
103
+ }
104
+ const at = text.indexOf(SECTION);
105
+ if (at === -1) return { names: new Set(), present: false };
106
+ const rest = text.slice(at + SECTION.length);
107
+ const end = rest.indexOf('\n## ');
108
+ const section = end === -1 ? rest : rest.slice(0, end);
109
+ const names = new Set<string>();
110
+ for (const m of section.matchAll(ROW)) {
111
+ const n = m[1];
112
+ if (n !== undefined) names.add(n);
113
+ }
114
+ return { names, present: true };
115
+ }
116
+
117
+ export function main(argv: readonly string[] = process.argv.slice(2)): number {
118
+ if (argv.includes('--self-test')) return selfTest();
119
+
120
+ const root = process.cwd();
121
+ const ledger = new GateLedger(GATE);
122
+ ledger.plan(['tree-enumeration', 'table-rows', 'set-equality']);
123
+
124
+ const tree = switchesInTree(root);
125
+ const scope = {
126
+ gate: GATE,
127
+ scanned: tree.files,
128
+ units: 'source file(s)',
129
+ roots: ROOTS,
130
+ };
131
+ // BEFORE the verdict, per the empty-corpus contract: a run that read no
132
+ // source file must refuse rather than report an empty tree as agreement
133
+ // with an empty table.
134
+ assertScanned(scope);
135
+ reportScanned(scope);
136
+ ledger.complete('tree-enumeration');
137
+
138
+ const table = switchesInTable(root);
139
+ if (!table.present) {
140
+ ledger.fail('table-rows', `no "${SECTION}" section in ${CONTRACT}`);
141
+ ledger.fail('set-equality', 'nothing to compare against');
142
+ ledger.report();
143
+ process.stderr.write(
144
+ `❌ ${GATE}: ${CONTRACT} carries no "${SECTION}" section. The section is the\n` +
145
+ ' contract; a tree enumeration with nothing to compare it against is not a pass.\n',
146
+ );
147
+ return 1;
148
+ }
149
+ ledger.complete('table-rows');
150
+
151
+ const missing = [...tree.names].filter((n) => !table.names.has(n)).sort();
152
+ const extra = [...table.names].filter((n) => !tree.names.has(n)).sort();
153
+
154
+ if (missing.length === 0 && extra.length === 0) {
155
+ ledger.complete('set-equality');
156
+ ledger.report();
157
+ process.stdout.write(
158
+ `✅ ${GATE}: ${String(tree.names.size)} switch(es) in ` +
159
+ `${String(tree.files)} file(s), ${String(table.names.size)} table row(s), sets equal\n`,
160
+ );
161
+ return 0;
162
+ }
163
+
164
+ ledger.fail('set-equality', `${String(missing.length + extra.length)} mismatch(es)`);
165
+ ledger.report();
166
+ process.stderr.write(`❌ ${GATE}: the table and the tree disagree.\n\n`);
167
+ for (const n of missing) {
168
+ process.stderr.write(` MISSING ROW ${n} — read by the tree, absent from the table\n`);
169
+ }
170
+ for (const n of extra) {
171
+ process.stderr.write(
172
+ ` STALE ROW ${n} — a table row for a name the tree no longer carries\n`,
173
+ );
174
+ }
175
+ process.stderr.write(
176
+ `\nAdd or remove the row in ${CONTRACT} § Kill switches. A switch with no row is\n` +
177
+ 'one an operator cannot find; a row with no switch is one they will set expecting\n' +
178
+ 'an effect. If a name is genuinely read outside the two declared roots, the row\n' +
179
+ 'stays and says so — the row still has to exist.\n',
180
+ );
181
+ return 1;
182
+ }
183
+
184
+ // ---------------------------------------------------------------------------
185
+ // Self-test — the gate proving its own rejections still fire
186
+ // ---------------------------------------------------------------------------
187
+
188
+ function fixture(body: { tree: string; table: string }): string {
189
+ const dir = fs.mkdtempSync(path.join(process.cwd(), '.kst-'));
190
+ fs.mkdirSync(path.join(dir, 'src', 'scripts', 'hooks'), { recursive: true });
191
+ fs.mkdirSync(path.join(dir, 'src', 'scripts', '_lib'), { recursive: true });
192
+ fs.mkdirSync(path.join(dir, 'docs', 'contracts'), { recursive: true });
193
+ fs.writeFileSync(path.join(dir, 'src', 'scripts', 'hooks', 'a.ts'), body.tree, 'utf-8');
194
+ fs.writeFileSync(path.join(dir, CONTRACT), body.table, 'utf-8');
195
+ return dir;
196
+ }
197
+
198
+ function table(...names: string[]): string {
199
+ const rows = names.map((n) => `| \`${n}\` | maintainer | does a thing | a.ts:1 |`).join('\n');
200
+ return `# Contract\n\n${SECTION}\n\n| Switch | Owner | What | Read at |\n|---|---|---|---|\n${rows}\n\n## Stability\n\nBeta.\n`;
201
+ }
202
+
203
+ function selfTest(): number {
204
+ const repoRoot = process.cwd();
205
+ const script = path.join('src', 'scripts', 'check_kill_switch_table.ts');
206
+ const dirs: string[] = [];
207
+ const run = (body: { tree: string; table: string }): number => {
208
+ const dir = fixture(body);
209
+ dirs.push(dir);
210
+ return runGateCli(repoRoot, script, [], dir);
211
+ };
212
+
213
+ const cases: SelfTestCase[] = [
214
+ {
215
+ name: 'equal sets pass',
216
+ expect: 'accept',
217
+ run: () =>
218
+ run({
219
+ tree: "const A = process.env['AGENT_CONFIG_ALPHA'];\n",
220
+ table: table('AGENT_CONFIG_ALPHA'),
221
+ }),
222
+ },
223
+ {
224
+ name: 'a switch with no row is rejected',
225
+ expect: 'reject',
226
+ run: () =>
227
+ run({
228
+ tree: "const A = process.env['AGENT_CONFIG_ALPHA'];\nconst B = process.env['AGENT_CONFIG_BETA'];\n",
229
+ table: table('AGENT_CONFIG_ALPHA'),
230
+ }),
231
+ },
232
+ {
233
+ name: 'a row with no switch is rejected',
234
+ expect: 'reject',
235
+ run: () =>
236
+ run({
237
+ tree: "const A = process.env['AGENT_CONFIG_ALPHA'];\n",
238
+ table: table('AGENT_CONFIG_ALPHA', 'AGENT_CONFIG_GONE'),
239
+ }),
240
+ },
241
+ {
242
+ name: 'one added and one stale do NOT cancel — the count would hide it',
243
+ expect: 'reject',
244
+ run: () =>
245
+ run({
246
+ tree: "const B = process.env['AGENT_CONFIG_BETA'];\n",
247
+ table: table('AGENT_CONFIG_ALPHA'),
248
+ }),
249
+ },
250
+ {
251
+ name: 'the esbuild defines are excluded, not counted',
252
+ expect: 'accept',
253
+ run: () =>
254
+ run({
255
+ tree:
256
+ 'declare const __AGENT_CONFIG_BUNDLE__: boolean;\n'
257
+ + 'declare const __AGENT_CONFIG_CLI_DELEGATE__: boolean;\n'
258
+ + "const A = process.env['AGENT_CONFIG_ALPHA'];\n",
259
+ table: table('AGENT_CONFIG_ALPHA'),
260
+ }),
261
+ },
262
+ {
263
+ name: 'a missing section is rejected, never read as an empty table',
264
+ expect: 'reject',
265
+ run: () =>
266
+ run({
267
+ tree: "const A = process.env['AGENT_CONFIG_ALPHA'];\n",
268
+ table: '# Contract\n\n## Stability\n\nBeta.\n',
269
+ }),
270
+ },
271
+ ];
272
+
273
+ try {
274
+ return runSelfTest({ gate: GATE, cases, minCases: 6, minRejectCases: 4 });
275
+ } finally {
276
+ for (const d of dirs) fs.rmSync(d, { recursive: true, force: true });
277
+ }
278
+ }
279
+
280
+ function isCliEntry(): boolean {
281
+ const argv1 = process.argv[1];
282
+ if (argv1 === undefined) return false;
283
+ try {
284
+ return (
285
+ fs.realpathSync(fileURLToPath(import.meta.url)) === fs.realpathSync(path.resolve(argv1))
286
+ );
287
+ } catch {
288
+ return pathToFileURL(argv1).href === import.meta.url;
289
+ }
290
+ }
291
+
292
+ if (isCliEntry()) {
293
+ process.exit(main());
294
+ }
@@ -0,0 +1,221 @@
1
+ #!/usr/bin/env tsx
2
+ /**
3
+ * The placeholder-drift ratchet — foreign placeholder syntaxes in prose may
4
+ * shrink, never grow.
5
+ *
6
+ * road-to-an-invocation-contract-that-reaches-the-wire 1.2.
7
+ *
8
+ * WHAT IT GATES, AND WHAT IT DELIBERATELY DOES NOT
9
+ *
10
+ * It fails on GROWTH of the foreign forms only. It never fails on the absolute
11
+ * number, and it never fails on the plurality form — so holding at the seeded
12
+ * counts is permanently green, and nothing here asks for a rewrite of a working
13
+ * body. That is the whole of Risk 2's mitigation: a committed number naming two
14
+ * syntaxes as foreign reads like a canon ruling whose obvious next move is
15
+ * rewriting those bodies, and a gate that is green at the seed removes the
16
+ * pressure to do it.
17
+ *
18
+ * THE SEED IS MEASURED, NOT COPIED
19
+ *
20
+ * The roadmap's step text says "seeded at the measured 4 and 2". The operative
21
+ * word is MEASURED: under the census's published unit — a token in PROSE,
22
+ * outside fenced blocks, indented blocks and inline code spans — the corpus
23
+ * carries different numbers, because the source figures counted tokens inside
24
+ * demonstrated code (JS template literals in `playwright-testing`, HCL in
25
+ * `terragrunt`, a table-cell marker the prose is talking ABOUT in
26
+ * `livewire-architect`). The seed below is this tree's own reading. The
27
+ * reasoning lives in `report_invocation_surface.ts`'s module header, and the
28
+ * per-file breakdown is in the census artifact, so the definition is arguable
29
+ * rather than merely asserted.
30
+ *
31
+ * A NOTE ON WHAT A ZERO SEED MEANS. `{{…}}` seeds at zero. That is not "the
32
+ * check has nothing to do" — a zero-seeded ratchet is the strictest form of
33
+ * this gate, because the first occurrence is growth. It is also the cheapest
34
+ * possible moment to hold a line: no existing body has to change for it.
35
+ *
36
+ * Exit codes: 0 within the ratchet · 1 growth · 2 the corpus could not be read.
37
+ */
38
+
39
+ import fs from 'node:fs';
40
+ import os from 'node:os';
41
+ import path from 'node:path';
42
+ import { fileURLToPath, pathToFileURL } from 'node:url';
43
+
44
+ import { GateLedger } from './_lib/gate_ledger.js';
45
+ import { runGateCli, runSelfTest } from './_lib/gate_self_test.js';
46
+ import { assertScanned, reportScanned } from './_lib/scan_scope.js';
47
+ import { SYNTAXES, type SyntaxId, census } from './report_invocation_surface.js';
48
+
49
+ const _HERE = fileURLToPath(import.meta.url);
50
+ const ROOT = path.resolve(path.dirname(_HERE), '..', '..');
51
+
52
+ export const GATE = 'check_placeholder_drift';
53
+
54
+ /** Where the seed lives, so a deliberate change is a reviewable diff. */
55
+ export const BUDGET_REL = path.join('src', 'config', 'placeholder-drift-budget.json');
56
+
57
+ export interface Budget {
58
+ readonly seeded_at: string;
59
+ readonly note: string;
60
+ readonly ceilings: Readonly<Partial<Record<SyntaxId, number>>>;
61
+ }
62
+
63
+ export function readBudget(root: string): Budget | null {
64
+ const p = path.join(root, BUDGET_REL);
65
+ if (!fs.existsSync(p)) return null;
66
+ try {
67
+ return JSON.parse(fs.readFileSync(p, 'utf8')) as Budget;
68
+ } catch {
69
+ return null;
70
+ }
71
+ }
72
+
73
+ export interface Finding {
74
+ readonly syntax: SyntaxId;
75
+ readonly ceiling: number;
76
+ readonly measured: number;
77
+ }
78
+
79
+ /** Which foreign syntaxes exceed their ceiling. Plurality forms are skipped. */
80
+ export function findings(
81
+ occurrences: Readonly<Record<SyntaxId, number>>,
82
+ budget: Budget,
83
+ ): Finding[] {
84
+ const out: Finding[] = [];
85
+ for (const s of SYNTAXES) {
86
+ if (s.canonical) continue;
87
+ const ceiling = budget.ceilings[s.id];
88
+ if (ceiling === undefined) continue;
89
+ const measured = occurrences[s.id] ?? 0;
90
+ if (measured > ceiling) out.push({ syntax: s.id, ceiling, measured });
91
+ }
92
+ return out;
93
+ }
94
+
95
+ export function main(argv: readonly string[]): number {
96
+ if (argv.includes('--self-test')) return selfTest();
97
+ const rootIdx = argv.indexOf('--root');
98
+ const root = rootIdx === -1 ? ROOT : (argv[rootIdx + 1] ?? ROOT);
99
+
100
+ const budget = readBudget(root);
101
+ if (budget === null) {
102
+ process.stderr.write(
103
+ `❌ ${GATE}: ${BUDGET_REL} is missing or unreadable. A ratchet that cannot read ` +
104
+ 'its own seed must not pass — it would be a green nobody earned.\n',
105
+ );
106
+ return 2;
107
+ }
108
+
109
+ const c = census(root);
110
+ const foreign = SYNTAXES.filter((s) => !s.canonical);
111
+ const ledger = new GateLedger(GATE);
112
+ ledger.plan(foreign.map((s) => s.id));
113
+
114
+ const found = findings(c.occurrences, budget);
115
+ for (const s of foreign) {
116
+ const f = found.find((x) => x.syntax === s.id);
117
+ if (f === undefined) ledger.complete(s.id);
118
+ else ledger.fail(s.id, `${String(f.measured)} occurrence(s) against a ceiling of ${String(f.ceiling)}`);
119
+ }
120
+
121
+ // A gate that read nothing has not passed. This fires BEFORE the verdict,
122
+ // because certifying against an empty corpus is the false green the coverage
123
+ // manifest exists to refuse — named by the provider-diverse ratification
124
+ // review as one condition that would have flipped its verdict.
125
+ assertScanned({ gate: GATE, scanned: c.rows.length, units: 'artifact(s)', roots: [BUDGET_REL, 'dist/agent-src/commands', 'src/skills'] });
126
+
127
+ reportScanned({
128
+ gate: GATE,
129
+ scanned: c.rows.length,
130
+ units: 'artifact(s)',
131
+ roots: [BUDGET_REL],
132
+ });
133
+ ledger.report();
134
+
135
+ if (found.length > 0) {
136
+ for (const f of found) {
137
+ const label = SYNTAXES.find((s) => s.id === f.syntax)?.label ?? f.syntax;
138
+ process.stdout.write(
139
+ `❌ ${GATE}: \`${label}\` grew to ${String(f.measured)} prose occurrence(s), ceiling ${String(f.ceiling)}.\n`,
140
+ );
141
+ }
142
+ process.stdout.write(
143
+ '\nThe ratchet walks DOWN only. Use the plurality form instead, or — if the new ' +
144
+ `occurrence is genuinely right — lower nothing and raise the ceiling in ` +
145
+ `\`${BUDGET_REL}\` in the same change, with the reason in the diff.\n` +
146
+ 'Counting unit and the per-file breakdown: ' +
147
+ '`./scripts-run src/scripts/report_invocation_surface --write`.\n',
148
+ );
149
+ return 1;
150
+ }
151
+
152
+ process.stdout.write(
153
+ `✅ ${GATE}: foreign placeholder syntaxes within their ratchet ` +
154
+ `(${foreign.map((s) => `${s.label} ${String(c.occurrences[s.id])}/${String(budget.ceilings[s.id] ?? 0)}`).join(' · ')}).\n`,
155
+ );
156
+ return 0;
157
+ }
158
+
159
+ /**
160
+ * A ratchet that cannot be shown to move is a number, not a gate. Each rejecting
161
+ * case is one occurrence over a ceiling; the accepting cases are the seed itself
162
+ * and a shrink, because a suite that only proves failures proves a gate that is
163
+ * merely loud.
164
+ */
165
+ function selfTest(): number {
166
+ const plant = (name: string, bodies: readonly string[], ceilings: Record<string, number>): string => {
167
+ const dir = fs.mkdtempSync(path.join(os.tmpdir(), `drift-${name}-`));
168
+ fs.mkdirSync(path.join(dir, 'src', 'config'), { recursive: true });
169
+ fs.writeFileSync(
170
+ path.join(dir, 'src', 'config', 'placeholder-drift-budget.json'),
171
+ JSON.stringify({ seeded_at: '2026-09-29', note: 'fixture', ceilings }, null, 2),
172
+ 'utf-8',
173
+ );
174
+ bodies.forEach((body, i) => {
175
+ const d = path.join(dir, 'src', 'skills', `probe-${String(i)}`);
176
+ fs.mkdirSync(d, { recursive: true });
177
+ fs.writeFileSync(path.join(d, 'SKILL.md'), `---\nname: probe-${String(i)}\n---\n\n${body}\n`, 'utf-8');
178
+ });
179
+ return dir;
180
+ };
181
+ const run = (root: string): number =>
182
+ runGateCli(ROOT, path.join('src', 'scripts', 'check_placeholder_drift.ts'), ['--root', root], ROOT);
183
+
184
+ return runSelfTest({
185
+ gate: GATE,
186
+ minCases: 5,
187
+ minRejectCases: 3,
188
+ cases: [
189
+ {
190
+ name: 'at the seed it is green',
191
+ expect: 'accept',
192
+ run: () => run(plant('seed', ['Uses {{a}} once.'], { 'double-brace': 1, 'angle-upper': 0 })),
193
+ },
194
+ {
195
+ name: 'below the seed it stays green — it never fails on the absolute number',
196
+ expect: 'accept',
197
+ run: () => run(plant('shrink', ['no placeholders'], { 'double-brace': 1, 'angle-upper': 0 })),
198
+ },
199
+ {
200
+ name: 'one occurrence over the ceiling fails',
201
+ expect: 'reject',
202
+ run: () => run(plant('grow', ['Uses {{a}} and {{b}}.'], { 'double-brace': 1, 'angle-upper': 0 })),
203
+ },
204
+ {
205
+ name: 'a zero ceiling makes the first occurrence growth',
206
+ expect: 'reject',
207
+ run: () => run(plant('zero', ['Uses <NAME> here.'], { 'double-brace': 0, 'angle-upper': 0 })),
208
+ },
209
+ {
210
+ name: 'an unreadable budget refuses rather than certifying a green',
211
+ expect: 'reject',
212
+ run: () => runGateCli(ROOT, path.join('src', 'scripts', 'check_placeholder_drift.ts'),
213
+ ['--root', fs.mkdtempSync(path.join(os.tmpdir(), 'drift-nobudget-'))], ROOT),
214
+ },
215
+ ],
216
+ });
217
+ }
218
+
219
+ if (process.argv[1] && import.meta.url === pathToFileURL(path.resolve(process.argv[1])).href) {
220
+ process.exit(main(process.argv.slice(2)));
221
+ }
@@ -64,8 +64,10 @@
64
64
  import * as fs from 'node:fs';
65
65
  import * as path from 'node:path';
66
66
 
67
+ import { type ActivationPayload, measureActivationPayload } from './_lib/activation_payload.js';
67
68
  import { asOf } from './_lib/as_of.js';
68
69
  import { assertScanned, DeadScopeError } from './_lib/scan_scope.js';
70
+ import { CAP_BYTES } from './hooks/rule_inject_hook.js';
69
71
  import { censusClaudeMdHierarchy, censusRuleDir, censusSkillsCatalog } from './preamble_byte_census.js';
70
72
  import { PREFIX_STABLE_SURFACES, prefixStableRoots } from './_lib/prefix_stable_surfaces.js';
71
73
  import { HOST_SURFACES } from './_lib/host_projection_reach.js';
@@ -624,6 +626,49 @@ export function renderBounds(b: BoundsRatchetVerdict): string {
624
626
  );
625
627
  }
626
628
 
629
+ /**
630
+ * The activation reading, rendered with a label nothing else in this gate uses.
631
+ *
632
+ * Third of the three quantities this report keeps structurally apart
633
+ * (`road-to-the-delivery-flip-that-tells-the-truth` 3.1). The first two are
634
+ * ADR-270's `source_corpus` and `host_payload`; this one is per FIRE and in
635
+ * BYTES, while both of those are per SESSION and in chars/4 tokens. Two units
636
+ * and two denominators is precisely why it carries its own label rather than
637
+ * being folded into either total — and why no line below adds it to one.
638
+ *
639
+ * INFORMATIONAL, like the host reading beside it. It never reaches the exit
640
+ * code: this phase adds reporting and registers no second gate. The activation
641
+ * charge already HAS a registered ceiling in
642
+ * `src/config/hook-token-budget.json` (`per_slot_sum_caps_bytes.user_prompt_submit`
643
+ * and the `rule-inject` concern row); enforcing it a second time here would put
644
+ * one obligation behind two gates that can disagree.
645
+ */
646
+ export function renderActivation(a: ActivationPayload): string {
647
+ const out = ['\n activation payload — per trigger-match fire, BYTES (informational, not gated)\n'];
648
+ if (!a.measured) {
649
+ out.push(
650
+ ` ${'· UNMEASURED'.padEnd(38)} ${a.unmeasured_reason ?? 'reason not recorded'}\n` +
651
+ ' Reported as unmeasured rather than estimated: a fabricated distribution\n' +
652
+ ' published beside a standing saving is the defect this number exists to stop.\n',
653
+ );
654
+ return out.join('');
655
+ }
656
+ for (const s of a.slots) {
657
+ const label = `· ${s.slot}`;
658
+ out.push(
659
+ ` ${label.padEnd(38)} ${String(s.fires).padStart(5)} fires ` +
660
+ `p50 ${String(s.p50).padStart(6)} B p90 ${String(s.p90).padStart(6)} B ` +
661
+ `max ${String(s.max).padStart(6)} B\n`,
662
+ );
663
+ }
664
+ out.push(
665
+ ` ${'· sampled at cap'.padEnd(38)} ${String(a.cap_bytes).padStart(5)} B over ${a.corpus}\n` +
666
+ ' Not summed into either total above: this is bytes per fire, those are chars/4\n' +
667
+ ' tokens per session. `pre_compact` is 0 by construction, not by measurement.\n',
668
+ );
669
+ return out.join('');
670
+ }
671
+
627
672
  export function main(argv: string[] = process.argv.slice(2)): number {
628
673
  const json = argv.includes('--format=json') || argv.includes('--json');
629
674
 
@@ -737,6 +782,13 @@ export function main(argv: string[] = process.argv.slice(2)): number {
737
782
  }
738
783
  }
739
784
 
785
+ // The third labelled number. Taken unconditionally and behind no flag: a
786
+ // cost that has to be asked for is a cost that stays unpublished, which is
787
+ // the failure mode `road-to-the-delivery-flip-that-tells-the-truth` names.
788
+ // `measureActivationPayload` never throws and never estimates, so this
789
+ // cannot change any verdict below — only what the report says.
790
+ const activation = measureActivationPayload(repoRoot, CAP_BYTES);
791
+
740
792
  let decision: Decision;
741
793
  try {
742
794
  decision = decide({ repoRoot, requireBase, verifiedApprovals });
@@ -775,7 +827,15 @@ export function main(argv: string[] = process.argv.slice(2)): number {
775
827
  // for exactly that: until the two are named apart, every
776
828
  // ceiling discussion risks comparing unlike quantities. The
777
829
  // legacy top-level fields stay so no existing reader breaks.
778
- measurement_scope: host === null ? 'source-corpus' : 'source-corpus+host-payload',
830
+ // Three parts now, joined from what was actually taken
831
+ // rather than from a fixed pair: the activation reading is
832
+ // the third quantity and it is absent on a tree with no
833
+ // frozen corpus, so a hardcoded label would over-claim.
834
+ measurement_scope: [
835
+ 'source-corpus',
836
+ ...(host === null ? [] : ['host-payload']),
837
+ ...(activation.measured ? ['activation-payload'] : []),
838
+ ].join('+'),
779
839
  source_corpus: {
780
840
  measured: verdict.measured,
781
841
  baseline: verdict.baseline,
@@ -783,6 +843,9 @@ export function main(argv: string[] = process.argv.slice(2)): number {
783
843
  buckets: verdict.buckets,
784
844
  },
785
845
  host_payload: host,
846
+ // Per FIRE and in BYTES — never added to either total
847
+ // above, which are per session and in chars/4 tokens.
848
+ activation_payload: activation,
786
849
  ok: decision.ok,
787
850
  measured_ceiling: {
788
851
  ceiling: decision.ceiling.ceiling,
@@ -817,6 +880,11 @@ export function main(argv: string[] = process.argv.slice(2)): number {
817
880
  return decision.ok ? 0 : 1;
818
881
  }
819
882
 
883
+ // The label goes above the buckets so the gated number cannot be read as
884
+ // either of the two beneath it. This is the source tree's own size — what
885
+ // it COULD deliver — and after a per-host projection flip that is no longer
886
+ // what any one host receives.
887
+ process.stdout.write(' source corpus — what the source tree could deliver (GATED)\n');
820
888
  for (const b of verdict.buckets) {
821
889
  process.stdout.write(` ${b.name.padEnd(38)} ${String(b.tokens).padStart(8)} tok\n`);
822
890
  }
@@ -841,7 +909,10 @@ export function main(argv: string[] = process.argv.slice(2)): number {
841
909
  // sharing one name.
842
910
  if (host !== null) {
843
911
  const label = host.host === null ? host.rules_path : host.host;
844
- process.stdout.write(`\n host payload — ${label} (informational, not gated)\n`);
912
+ process.stdout.write(
913
+ `\n host payload — ${label}, what this host actually receives at rest ` +
914
+ '(informational, not gated)\n',
915
+ );
845
916
  for (const b of host.buckets) {
846
917
  process.stdout.write(` ${('· ' + b.name).padEnd(38)} ${String(b.tokens).padStart(8)} tok\n`);
847
918
  }
@@ -860,6 +931,8 @@ export function main(argv: string[] = process.argv.slice(2)): number {
860
931
  }
861
932
  }
862
933
 
934
+ process.stdout.write(renderActivation(activation));
935
+
863
936
  // THE CEILING IS ESTABLISHED BEFORE THE SIZE QUESTION. A run that could not
864
937
  // measure its own ceiling has no business reporting a tree as within one,
865
938
  // and an expired grant is a refusal whatever the payload does.