@event4u/agent-config 16.1.0 → 16.3.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 (467) 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 +98 -257
  5. package/README.md +14 -12
  6. package/dist/agent-src/commands/analyze/inbox.md +1 -1
  7. package/dist/agent-src/commands/analyze/roadmap-repos.md +1 -1
  8. package/dist/agent-src/commands/bug/fix.md +17 -2
  9. package/dist/agent-src/commands/feature/plan.md +1 -1
  10. package/dist/agent-src/commands/feature/roadmap.md +1 -1
  11. package/dist/agent-src/commands/fix/ci.md +1 -1
  12. package/dist/agent-src/commands/implement-ticket.md +1 -1
  13. package/dist/agent-src/commands/jira-ticket.md +1 -1
  14. package/dist/agent-src/commands/roadmap/create.md +1 -1
  15. package/dist/agent-src/commands/roadmap/materialize.md +1 -1
  16. package/dist/agent-src/commands/roadmap/process-full.md +5 -5
  17. package/dist/agent-src/commands/roadmap/resolve-blockers.md +361 -0
  18. package/dist/agent-src/commands/roadmap/triage-parked.md +325 -0
  19. package/dist/agent-src/commands/roadmap.md +18 -4
  20. package/dist/agent-src/commands/tests/create.md +17 -3
  21. package/dist/agent-src/commands/tests/execute.md +22 -5
  22. package/dist/agent-src/commands/work.md +4 -0
  23. package/dist/agent-src/contexts/authority/decision-revisit-gate-enforcement.md +43 -0
  24. package/dist/agent-src/contexts/communication/rules-auto/design-fidelity-enforcement.md +24 -0
  25. package/dist/agent-src/contexts/execution/auto-dispatch-classification.md +6 -6
  26. package/dist/agent-src/contexts/execution/autonomy-mechanics.md +4 -8
  27. package/dist/agent-src/contexts/execution/roadmap-process-loop.md +32 -2
  28. package/dist/agent-src/contexts/execution/session-canary-enforcement-history.md +67 -0
  29. package/dist/agent-src/contexts/execution/toolchain-resolver.md +101 -7
  30. package/dist/agent-src/rules/active-remediation.md +1 -1
  31. package/dist/agent-src/rules/autonomous-execution.md +1 -0
  32. package/dist/agent-src/rules/broken-access-control.md +1 -0
  33. package/dist/agent-src/rules/command-suggestion-policy.md +1 -0
  34. package/dist/agent-src/rules/context-hygiene.md +1 -1
  35. package/dist/agent-src/rules/decision-revisit-gate.md +16 -33
  36. package/dist/agent-src/rules/design-fidelity.md +13 -16
  37. package/dist/agent-src/rules/doc-screenshot-hygiene.md +1 -0
  38. package/dist/agent-src/rules/domain-safety-disclaimer.md +1 -0
  39. package/dist/agent-src/rules/domain-safety-pii.md +1 -0
  40. package/dist/agent-src/rules/domain-safety-retention.md +1 -0
  41. package/dist/agent-src/rules/engineering-safety-floor.md +1 -0
  42. package/dist/agent-src/rules/evaluator-independence.md +3 -2
  43. package/dist/agent-src/rules/fast-path-marker-visibility.md +1 -0
  44. package/dist/agent-src/rules/finance-safety-floor.md +1 -0
  45. package/dist/agent-src/rules/git-history-discipline.md +1 -0
  46. package/dist/agent-src/rules/image-likeness-and-rights.md +1 -0
  47. package/dist/agent-src/rules/legal-safety-floor.md +3 -2
  48. package/dist/agent-src/rules/lethal-trifecta-guard.md +1 -0
  49. package/dist/agent-src/rules/low-impact-corpus-privacy-floor.md +1 -0
  50. package/dist/agent-src/rules/media-governance-routing.md +14 -17
  51. package/dist/agent-src/rules/persona-governance.md +2 -2
  52. package/dist/agent-src/rules/provider-lifecycle-discipline.md +1 -1
  53. package/dist/agent-src/rules/question-not-instruction.md +1 -0
  54. package/dist/agent-src/rules/roadmap-progress-sync.md +1 -1
  55. package/dist/agent-src/rules/runtime-safety.md +1 -0
  56. package/dist/agent-src/rules/secret-vcs-guard.md +1 -0
  57. package/dist/agent-src/rules/security-sensitive-stop.md +3 -2
  58. package/dist/agent-src/rules/session-canary.md +8 -62
  59. package/dist/agent-src/rules/settings-ask-protocol.md +1 -0
  60. package/dist/agent-src/rules/source-confidentiality.md +1 -0
  61. package/dist/agent-src/rules/strategy-safety-floor.md +1 -0
  62. package/dist/agent-src/rules/tool-safety.md +1 -0
  63. package/dist/agent-src/rules/ui-audit-gate.md +1 -2
  64. package/dist/agent-src/rules/untrusted-input-defense.md +1 -0
  65. package/dist/agent-src/rules/user-interrupt-priority.md +1 -0
  66. package/dist/agent-src/scripts/archive_completed_roadmaps.ts +115 -20
  67. package/dist/agent-src/scripts/deferral_carry.ts +339 -0
  68. package/dist/agent-src/scripts/update_roadmap_progress.ts +4 -1
  69. package/dist/agent-src/skills/accessibility-auditor/data/aria-patterns.csv +1 -1
  70. package/dist/agent-src/skills/accessibility-auditor/data/manifest.json +2 -2
  71. package/dist/agent-src/skills/api-design/data/manifest.json +2 -2
  72. package/dist/agent-src/skills/api-testing/SKILL.md +17 -0
  73. package/dist/agent-src/skills/code-intelligence/SKILL.md +14 -2
  74. package/dist/agent-src/skills/design-review/SKILL.md +30 -4
  75. package/dist/agent-src/skills/design-review/references/verification-automation.md +32 -0
  76. package/dist/agent-src/skills/design-system-capture/references/import-procedure.md +1 -1
  77. package/dist/agent-src/skills/existing-ui-audit/SKILL.md +18 -3
  78. package/dist/agent-src/skills/existing-ui-audit/references/component-taxonomy-detection.md +56 -0
  79. package/dist/agent-src/skills/fe-design/SKILL.md +1 -1
  80. package/dist/agent-src/skills/iconography/SKILL.md +1 -1
  81. package/dist/agent-src/skills/js-library-packaging/SKILL.md +2 -2
  82. package/dist/agent-src/skills/markitdown/SKILL.md +4 -0
  83. package/dist/agent-src/skills/monorepo-workspace/SKILL.md +1 -1
  84. package/dist/agent-src/skills/playwright-testing/SKILL.md +30 -6
  85. package/dist/agent-src/skills/quality-tools/SKILL.md +11 -1
  86. package/dist/agent-src/skills/quality-tools/references/go-tools.md +76 -0
  87. package/dist/agent-src/skills/quality-tools/references/python-tools.md +104 -0
  88. package/dist/agent-src/skills/review-routing/SKILL.md +2 -2
  89. package/dist/agent-src/skills/skill-improvement-pipeline/SKILL.md +1 -1
  90. package/dist/agent-src/skills/storybook-workshop/SKILL.md +3 -3
  91. package/dist/agent-src/skills/tailwind-engineer/SKILL.md +1 -1
  92. package/dist/agent-src/skills/test-case-discovery/SKILL.md +7 -0
  93. package/dist/agent-src/skills/test-driven-development/SKILL.md +17 -0
  94. package/dist/agent-src/skills/test-performance/SKILL.md +19 -1
  95. package/dist/agent-src/skills/ui-component-architect/SKILL.md +25 -2
  96. package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
  97. package/dist/agent-src/templates/roadmaps.md +30 -0
  98. package/dist/agent-src/templates/scripts/work_engine/directives/ui/app_spec.ts +10 -4
  99. package/dist/agent-src/templates/scripts/work_engine/directives/ui/apply.ts +302 -21
  100. package/dist/agent-src/templates/scripts/work_engine/directives/ui/audit.ts +22 -3
  101. package/dist/agent-src/templates/scripts/work_engine/directives/ui/scaffold.ts +6 -1
  102. package/dist/agent-src/templates/scripts/work_engine/stack/runner.ts +774 -109
  103. package/dist/agent-src/templates/scripts/work_engine/state.ts +57 -7
  104. package/dist/agent-src/templates/scripts/work_engine/taxonomy/detect.ts +500 -0
  105. package/dist/agent-src/templates/scripts/work_engine/taxonomy/place.ts +183 -0
  106. package/dist/cli/python/knowledge_ingest.js +1 -1
  107. package/dist/cli/python/workspace_drive.js +1 -1
  108. package/dist/cli/registry.js +1 -1
  109. package/dist/cli-delegate/archive_completed_roadmaps.js +92 -12
  110. package/dist/cli-delegate/check_update_banner.js +12 -1
  111. package/dist/cli-delegate/chunk-523HCFYO.js +90 -0
  112. package/dist/cli-delegate/{chunk-M2DPGFZ4.js → chunk-6V6TA25H.js} +22 -152
  113. package/dist/cli-delegate/chunk-7IQE3D5S.js +191 -0
  114. package/dist/cli-delegate/{chunk-6KFEC3G7.js → chunk-CQWX336L.js} +1 -1
  115. package/dist/cli-delegate/chunk-DEIV7XP7.js +108 -0
  116. package/dist/cli-delegate/chunk-EEHDXSYD.js +2040 -0
  117. package/dist/cli-delegate/{chunk-P6ZHP5W2.js → chunk-GKE7ZVFA.js} +4 -4
  118. package/dist/cli-delegate/chunk-JQOM7TTM.js +1201 -0
  119. package/dist/cli-delegate/{chunk-UNCRJBT3.js → chunk-JQVYKIZA.js} +41 -0
  120. package/dist/cli-delegate/{chunk-FC75PGJE.js → chunk-JY2I2DXV.js} +12 -1
  121. package/dist/cli-delegate/chunk-KB5FZ655.js +208 -0
  122. package/dist/cli-delegate/{chunk-YUM5KURG.js → chunk-KR6EM23A.js} +235 -1455
  123. package/dist/cli-delegate/{chunk-LTYVIY76.js → chunk-LIYTLH6O.js} +1 -1
  124. package/dist/cli-delegate/chunk-NQ7WT43P.js +2729 -0
  125. package/dist/cli-delegate/chunk-NXEILF2K.js +2160 -0
  126. package/dist/cli-delegate/{chunk-5R7HOLAF.js → chunk-NYX3L2PZ.js} +1 -1
  127. package/dist/cli-delegate/{chunk-4E2W63DA.js → chunk-OKJUIAPS.js} +62 -1
  128. package/dist/cli-delegate/{chunk-QAJS5YWY.js → chunk-PHD7OOLE.js} +2 -1
  129. package/dist/cli-delegate/{chunk-RGNIWTEA.js → chunk-QLDHIPWR.js} +1 -1
  130. package/dist/cli-delegate/{chunk-HDVVGPQW.js → chunk-RQNNW7IF.js} +1405 -567
  131. package/dist/cli-delegate/{chunk-S3VFXMTM.js → chunk-S4UMZNES.js} +1 -1
  132. package/dist/cli-delegate/{chunk-6KM6ORRY.js → chunk-S4YWNRKO.js} +24 -8
  133. package/dist/cli-delegate/{chunk-GJCBB2UG.js → chunk-TRI6DQZ6.js} +3054 -325
  134. package/dist/cli-delegate/chunk-ULQRUR23.js +208 -0
  135. package/dist/cli-delegate/{chunk-NUHLI2RX.js → chunk-YCU3TIXH.js} +2 -0
  136. package/dist/cli-delegate/chunk-ZBZV2G5R.js +210 -0
  137. package/dist/cli-delegate/{chunk-WGXTVKCQ.js → chunk-ZUZKZYCN.js} +5 -3
  138. package/dist/cli-delegate/cmd_analyze_session.js +1 -1
  139. package/dist/cli-delegate/cmd_brand_status.js +1 -1
  140. package/dist/cli-delegate/cmd_conformance.js +1862 -809
  141. package/dist/cli-delegate/cmd_doctor.js +13 -6
  142. package/dist/cli-delegate/cmd_doctor_neighbours.js +23 -0
  143. package/dist/cli-delegate/cmd_explain.js +5 -5
  144. package/dist/cli-delegate/cmd_export.js +11 -8
  145. package/dist/cli-delegate/cmd_fleet.js +3 -3
  146. package/dist/cli-delegate/cmd_handoff.js +10 -7
  147. package/dist/cli-delegate/cmd_mcp_available.js +1 -1
  148. package/dist/cli-delegate/cmd_memory_get.js +3 -1
  149. package/dist/cli-delegate/cmd_migrate.js +2 -2
  150. package/dist/cli-delegate/cmd_packs_active.js +3 -3
  151. package/dist/cli-delegate/cmd_preflight.js +6 -6
  152. package/dist/cli-delegate/cmd_prune.js +11 -8
  153. package/dist/cli-delegate/cmd_refresh.js +2 -1
  154. package/dist/cli-delegate/cmd_route_audit.js +8 -6
  155. package/dist/cli-delegate/cmd_route_explain.js +4 -4
  156. package/dist/cli-delegate/cmd_session_recycle.js +4 -3
  157. package/dist/cli-delegate/cmd_settings_get.js +1 -1
  158. package/dist/cli-delegate/cmd_settings_migrate.js +11 -8
  159. package/dist/cli-delegate/cmd_settings_set.js +4 -4
  160. package/dist/cli-delegate/cmd_sync.js +11 -8
  161. package/dist/cli-delegate/cmd_uninstall.js +11 -8
  162. package/dist/cli-delegate/cmd_update.js +1 -1
  163. package/dist/cli-delegate/cmd_upgrade.js +3 -2
  164. package/dist/cli-delegate/cmd_validate.js +11 -8
  165. package/dist/cli-delegate/cmd_versions.js +1 -1
  166. package/dist/cli-delegate/deferral_carry.js +16 -0
  167. package/dist/cli-delegate/gate_execute.js +2 -2
  168. package/dist/cli-delegate/roadmap_gates.js +5 -5
  169. package/dist/cli-delegate/update_roadmap_progress.js +1 -1
  170. package/dist/discovery/deprecation-report.md +1 -1
  171. package/dist/discovery/discovery-manifest.json +132 -80
  172. package/dist/discovery/discovery-manifest.json.sha256 +1 -1
  173. package/dist/discovery/discovery-manifest.summary.md +3 -3
  174. package/dist/discovery/dormancy-report.md +1 -1
  175. package/dist/discovery/orphan-report.md +1 -1
  176. package/dist/discovery/packs.json +7 -5
  177. package/dist/discovery/trust-report.md +2 -2
  178. package/dist/discovery/workspaces.json +6 -4
  179. package/dist/hooks/dispatch.js +7972 -5186
  180. package/dist/hooks/dispatch.sha256 +1 -0
  181. package/dist/install/claudePathsPlan.js +47 -6
  182. package/dist/install/claudeRuleRewrite.js +7 -0
  183. package/dist/install/emit_host_rules_cli.js +39 -36
  184. package/dist/install/globalRuleScope.js +87 -0
  185. package/dist/install/host_rule_ownership.js +160 -0
  186. package/dist/install/install.mjs +2226 -1055
  187. package/dist/install/installThinLayer.js +261 -0
  188. package/dist/install/installedRuleLinks.js +176 -0
  189. package/dist/install/wizard-plan.js +24 -0
  190. package/dist/mcp/registry-manifest.json +3 -3
  191. package/dist/mcp/server.json +2 -2
  192. package/dist/mcp/server.mjs +40913 -34865
  193. package/dist/scripts/_lib/agent_settings.js +11 -0
  194. package/dist/scripts/_lib/claude_settings_hooks.js +10 -8
  195. package/dist/scripts/_lib/installed_layer.js +378 -0
  196. package/dist/scripts/_lib/lean_projection_mode.js +218 -15
  197. package/dist/scripts/_lib/rule_consequence_class.js +45 -0
  198. package/dist/scripts/_lib/rule_injection.js +67 -10
  199. package/dist/scripts/_lib/rule_law_section.js +122 -0
  200. package/dist/scripts/_lib/thin_rules.js +458 -0
  201. package/dist/scripts/_lib/windsurf_render.js +60 -0
  202. package/dist/scripts/condense.js +14 -26
  203. package/dist/scripts/project_thin_rules.js +14 -294
  204. package/dist/server/schemas/settings.js +29 -0
  205. package/dist/shared/skillRanking.js +74 -13
  206. package/dist/ui/assets/index-Cwq4d0hX.js +23 -0
  207. package/dist/ui/assets/index-Cyjy8bWO.css +1 -0
  208. package/dist/ui/index.html +2 -2
  209. package/docs/contracts/settings-classes.md +10 -6
  210. package/package.json +4 -4
  211. package/src/agent-src/scripts/archive_completed_roadmaps.ts +115 -20
  212. package/src/agent-src/scripts/deferral_carry.ts +339 -0
  213. package/src/agent-src/scripts/update_roadmap_progress.ts +4 -1
  214. package/src/agent-src/templates/scripts/work_engine/directives/ui/app_spec.ts +10 -4
  215. package/src/agent-src/templates/scripts/work_engine/directives/ui/apply.ts +302 -21
  216. package/src/agent-src/templates/scripts/work_engine/directives/ui/audit.ts +22 -3
  217. package/src/agent-src/templates/scripts/work_engine/directives/ui/scaffold.ts +6 -1
  218. package/src/agent-src/templates/scripts/work_engine/stack/runner.ts +774 -109
  219. package/src/agent-src/templates/scripts/work_engine/state.ts +57 -7
  220. package/src/agent-src/templates/scripts/work_engine/taxonomy/detect.ts +500 -0
  221. package/src/agent-src/templates/scripts/work_engine/taxonomy/place.ts +183 -0
  222. package/src/config/agent-settings.template.yml +91 -22
  223. package/src/config/ci-local-parity.yml +18 -0
  224. package/src/config/continuity-surface.json +9 -2
  225. package/src/config/evaluator-budgets.json +4 -4
  226. package/src/config/gate-coverage.yml +327 -3
  227. package/src/config/gate-violation-baselines.json +21 -9
  228. package/src/config/hook-bundle-budget.json +25 -0
  229. package/src/config/hook-latency-budget.json +21 -0
  230. package/src/config/hook-token-budget.json +8 -3
  231. package/src/config/host-instruction-limits.json +33 -0
  232. package/src/config/metric-registry.yml +114 -0
  233. package/src/config/placeholder-drift-budget.json +10 -0
  234. package/src/config/routing-coverage-seed.json +38 -3
  235. package/src/config/rule-activation-census.json +7 -3
  236. package/src/config/rule-consequence-class.json +161 -0
  237. package/src/config/rule-law-ceilings.json +99 -0
  238. package/src/install/claudePathsPlan.ts +46 -4
  239. package/src/install/claudeRuleRewrite.ts +10 -1
  240. package/src/install/emit_host_rules_cli.ts +43 -34
  241. package/src/install/globalRuleScope.ts +109 -0
  242. package/src/install/host_rule_ownership.ts +170 -0
  243. package/src/install/installThinLayer.ts +312 -0
  244. package/src/install/installedRuleLinks.ts +220 -0
  245. package/src/install/wizard-plan.ts +24 -0
  246. package/src/scripts/_cli/cmd_doctor.ts +28 -90
  247. package/src/scripts/_cli/cmd_doctor_neighbours.ts +293 -0
  248. package/src/scripts/_cli/doctor_execution.ts +100 -17
  249. package/src/scripts/_cli/doctor_network_posture.ts +226 -0
  250. package/src/scripts/_dispatch.bash +16 -2
  251. package/src/scripts/_lib/agent_settings.ts +11 -0
  252. package/src/scripts/_lib/body_portable.ts +102 -0
  253. package/src/scripts/_lib/cc_transcript.ts +132 -1
  254. package/src/scripts/_lib/claude_settings_hooks.ts +10 -8
  255. package/src/scripts/_lib/concern_sla_window.ts +200 -0
  256. package/src/scripts/_lib/estate_offsets.ts +241 -0
  257. package/src/scripts/_lib/exemption_shape.ts +143 -0
  258. package/src/scripts/_lib/fnv.ts +23 -0
  259. package/src/scripts/_lib/foreign_scan.ts +106 -0
  260. package/src/scripts/_lib/forge_protection.ts +114 -0
  261. package/src/scripts/_lib/forge_reader.ts +558 -0
  262. package/src/scripts/_lib/gitignore_block.ts +314 -0
  263. package/src/scripts/_lib/global_deploy_inventory.ts +32 -0
  264. package/src/scripts/_lib/graph_feeder_record.ts +214 -0
  265. package/src/scripts/_lib/hook_settings.ts +73 -0
  266. package/src/scripts/_lib/host_capability.ts +40 -19
  267. package/src/scripts/_lib/host_env_write.ts +111 -0
  268. package/src/scripts/_lib/host_hook_merge.ts +165 -0
  269. package/src/scripts/_lib/installed_layer.ts +538 -0
  270. package/src/scripts/_lib/json_merge.ts +82 -0
  271. package/src/scripts/_lib/json_pointers.ts +93 -11
  272. package/src/scripts/_lib/lean_projection_mode.ts +275 -15
  273. package/src/scripts/_lib/llm_proposer_transport.ts +14 -4
  274. package/src/scripts/_lib/machine_wake.ts +16 -3
  275. package/src/scripts/_lib/neighbour_census.ts +760 -0
  276. package/src/scripts/_lib/neighbour_scan.ts +122 -0
  277. package/src/scripts/_lib/neighbour_tool_use.ts +189 -0
  278. package/src/scripts/_lib/obligations.ts +119 -8
  279. package/src/scripts/_lib/probe_inputs.ts +271 -0
  280. package/src/scripts/_lib/prompt_shape.ts +46 -0
  281. package/src/scripts/_lib/read_surface_scan.ts +322 -0
  282. package/src/scripts/_lib/release_highlights.ts +2 -1
  283. package/src/scripts/_lib/release_migration_gate.ts +19 -3
  284. package/src/scripts/_lib/reserved_name_sweep.ts +73 -0
  285. package/src/scripts/_lib/retrieval_sanitize.ts +77 -16
  286. package/src/scripts/_lib/review_coverage.ts +134 -0
  287. package/src/scripts/_lib/rule_consequence_class.ts +71 -0
  288. package/src/scripts/_lib/rule_injection.ts +130 -10
  289. package/src/scripts/_lib/rule_law_section.ts +147 -0
  290. package/src/scripts/_lib/rule_layer_overlap.ts +58 -0
  291. package/src/scripts/_lib/shell_write_shapes.ts +108 -0
  292. package/src/scripts/_lib/skill_origin.ts +254 -0
  293. package/src/scripts/_lib/structural_hiding.ts +622 -0
  294. package/src/scripts/_lib/structured_ask.ts +64 -21
  295. package/src/scripts/_lib/thin_rules.ts +540 -0
  296. package/src/scripts/_lib/touched_file_quality.ts +434 -0
  297. package/src/scripts/_lib/turn_end_refusals.ts +600 -4
  298. package/src/scripts/_lib/turn_end_transcript.ts +216 -0
  299. package/src/scripts/_lib/verification_command.ts +324 -0
  300. package/src/scripts/_lib/verification_evidence.ts +647 -0
  301. package/src/scripts/_lib/verify_clause.ts +280 -0
  302. package/src/scripts/_lib/windsurf_render.ts +63 -0
  303. package/src/scripts/ai_council/cli_call_budget.ts +13 -5
  304. package/src/scripts/ai_council/clients.ts +26 -57
  305. package/src/scripts/ai_council/config.ts +1 -1
  306. package/src/scripts/ai_council/vendor_defaults.ts +45 -0
  307. package/src/scripts/ai_team/team_dispatch.ts +4 -0
  308. package/src/scripts/audit_skill_overlap.ts +261 -0
  309. package/src/scripts/before_complete_hook.ts +573 -25
  310. package/src/scripts/bench_hook_latency.ts +412 -5
  311. package/src/scripts/bench_touched_file_quality.ts +238 -0
  312. package/src/scripts/cache_realization_report.ts +61 -1
  313. package/src/scripts/check_argument_hint.ts +287 -0
  314. package/src/scripts/check_claims.ts +125 -1
  315. package/src/scripts/check_enforcement_coverage.ts +38 -9
  316. package/src/scripts/check_enforcement_matrix.ts +621 -22
  317. package/src/scripts/check_estate_count.ts +130 -153
  318. package/src/scripts/check_finding_dispositions.ts +138 -7
  319. package/src/scripts/check_generator_sync.ts +71 -0
  320. package/src/scripts/check_hook_bundle_composition.ts +370 -0
  321. package/src/scripts/check_host_docs_digest.ts +786 -0
  322. package/src/scripts/check_host_format_column.ts +559 -0
  323. package/src/scripts/check_input_references.ts +228 -0
  324. package/src/scripts/check_inputs_parity.ts +235 -0
  325. package/src/scripts/check_kernel_edit_ratified.ts +89 -9
  326. package/src/scripts/check_kill_switch_table.ts +294 -0
  327. package/src/scripts/check_memory.ts +7 -1
  328. package/src/scripts/check_placeholder_drift.ts +221 -0
  329. package/src/scripts/check_read_surface_coverage.ts +515 -0
  330. package/src/scripts/check_release_highlights.ts +10 -0
  331. package/src/scripts/check_release_holds.ts +2 -1
  332. package/src/scripts/check_release_pr_shape.ts +38 -15
  333. package/src/scripts/check_routing_coverage.ts +378 -11
  334. package/src/scripts/check_standing_rule_delivery.ts +33 -13
  335. package/src/scripts/check_static_parity.ts +17 -0
  336. package/src/scripts/check_verify_expectation_delta.ts +521 -0
  337. package/src/scripts/ci_settle.ts +145 -19
  338. package/src/scripts/closure_scan.ts +115 -1
  339. package/src/scripts/code_graph/detect.ts +127 -7
  340. package/src/scripts/code_graph/foreign.ts +194 -0
  341. package/src/scripts/code_graph/query.ts +217 -4
  342. package/src/scripts/code_graph/types.ts +26 -2
  343. package/src/scripts/code_graph/validate.ts +3 -1
  344. package/src/scripts/code_graph/verbs.ts +103 -0
  345. package/src/scripts/compile_hook_manifest.ts +90 -14
  346. package/src/scripts/condense.ts +14 -27
  347. package/src/scripts/estate_exemption_shape.ts +202 -0
  348. package/src/scripts/external_sources_denylist.json +29 -4
  349. package/src/scripts/generate_host_cost_table.ts +1 -1
  350. package/src/scripts/hook_manifest.json +1 -1
  351. package/src/scripts/hook_manifest.yaml +192 -14
  352. package/src/scripts/hooks/block_config_weakening.ts +294 -4
  353. package/src/scripts/hooks/block_kernel_rule_writes.ts +9 -78
  354. package/src/scripts/hooks/block_plumbing_writes.ts +342 -0
  355. package/src/scripts/hooks/bundle_integrity.ts +291 -0
  356. package/src/scripts/hooks/chain_nudge_hook.ts +1 -2
  357. package/src/scripts/hooks/code_graph_context_hook.ts +253 -25
  358. package/src/scripts/hooks/comment_discipline_hook.ts +1 -3
  359. package/src/scripts/hooks/concern_failure_policy.ts +280 -0
  360. package/src/scripts/hooks/concern_registry.ts +2 -0
  361. package/src/scripts/hooks/concern_timings.ts +94 -0
  362. package/src/scripts/hooks/delegation_nudge_hook.ts +1 -2
  363. package/src/scripts/hooks/design_pass_hook.ts +100 -9
  364. package/src/scripts/hooks/design_slop_hook.ts +1 -2
  365. package/src/scripts/hooks/dispatch_hook.ts +77 -71
  366. package/src/scripts/hooks/edit_shape_hook.ts +1 -2
  367. package/src/scripts/hooks/end_review_nudge_hook.ts +1 -1
  368. package/src/scripts/hooks/evidence_independence.ts +1 -2
  369. package/src/scripts/hooks/exit_codes.ts +129 -0
  370. package/src/scripts/hooks/host_lowering.json +1 -0
  371. package/src/scripts/hooks/host_lowering.ts +146 -3
  372. package/src/scripts/hooks/host_lowering.yaml +262 -46
  373. package/src/scripts/hooks/host_semantics.ts +30 -2
  374. package/src/scripts/hooks/injection_budget.ts +9 -2
  375. package/src/scripts/hooks/interruption_ledger_hook.ts +1 -2
  376. package/src/scripts/hooks/journal_record_hook.ts +1 -2
  377. package/src/scripts/hooks/memory_recall_hook.ts +1 -2
  378. package/src/scripts/hooks/obligation_settle_hook.ts +151 -5
  379. package/src/scripts/hooks/one_question_per_ask_hook.ts +43 -6
  380. package/src/scripts/hooks/orchestration_record_hook.ts +1 -2
  381. package/src/scripts/hooks/payload_stub.ts +10 -1
  382. package/src/scripts/hooks/permission_gate.ts +1 -2
  383. package/src/scripts/hooks/push_settle_hook.ts +1 -3
  384. package/src/scripts/hooks/replay_hook.ts +2 -1
  385. package/src/scripts/hooks/reread_guard_hook.ts +1 -2
  386. package/src/scripts/hooks/review_baseline_hook.ts +1 -2
  387. package/src/scripts/hooks/rtk_wrap_hook.ts +1 -2
  388. package/src/scripts/hooks/rule_inject_hook.ts +765 -68
  389. package/src/scripts/hooks/run_continuation_hook.ts +13 -15
  390. package/src/scripts/hooks/skill_route_hook.ts +1 -2
  391. package/src/scripts/hooks/source_first_gate_hook.ts +1 -2
  392. package/src/scripts/hooks/spawn_guard_shadow_hook.ts +1 -2
  393. package/src/scripts/hooks/subagent_ledger_hook.ts +1 -2
  394. package/src/scripts/hooks/suggestion_capture_hook.ts +1 -1
  395. package/src/scripts/hooks/table_fingerprint.ts +70 -0
  396. package/src/scripts/hooks/telemetry_flush_hook.ts +1 -2
  397. package/src/scripts/hooks/telemetry_usage_hook.ts +65 -10
  398. package/src/scripts/hooks/tool_result_bytes_hook.ts +93 -9
  399. package/src/scripts/hooks/turn_end_gate_hook.ts +421 -324
  400. package/src/scripts/hooks/ui_route_nudge_hook.ts +1 -2
  401. package/src/scripts/hooks_status.ts +112 -0
  402. package/src/scripts/injection_scan_hook.ts +7 -0
  403. package/src/scripts/install.ts +75 -189
  404. package/src/scripts/installed_layer_report.ts +103 -0
  405. package/src/scripts/language_mirror_hook.ts +15 -6
  406. package/src/scripts/learning_sidecar.ts +119 -8
  407. package/src/scripts/lint_deny_text.ts +419 -0
  408. package/src/scripts/lint_exit_codes.ts +262 -0
  409. package/src/scripts/lint_hook_manifest.ts +108 -0
  410. package/src/scripts/lint_major_migration_sections.ts +156 -26
  411. package/src/scripts/lint_pack_boundaries.ts +63 -5
  412. package/src/scripts/lint_roadmap_blockers.ts +78 -12
  413. package/src/scripts/lint_rule_law_section.ts +517 -0
  414. package/src/scripts/lint_skill_descriptions.ts +1 -1
  415. package/src/scripts/lint_skill_scripts_readonly.ts +1 -1
  416. package/src/scripts/lint_workflow_security.ts +161 -5
  417. package/src/scripts/mcp_server/consumer_tool_catalog.json +53 -0
  418. package/src/scripts/mcp_server/graph_tools.ts +72 -3
  419. package/src/scripts/mcp_server/prompts.ts +142 -1
  420. package/src/scripts/measure_routing_signal.ts +54 -16
  421. package/src/scripts/measure_skill_ranker_baseline.ts +515 -53
  422. package/src/scripts/measure_turn_end_gate.ts +193 -0
  423. package/src/scripts/memory_learn_hook.ts +91 -6
  424. package/src/scripts/memory_lookup.ts +1 -1
  425. package/src/scripts/model_rule_injection.ts +52 -5
  426. package/src/scripts/prepack-check.mjs +53 -0
  427. package/src/scripts/project_thin_rules.ts +34 -327
  428. package/src/scripts/release_publication.ts +13 -1
  429. package/src/scripts/report_evidence_temperature.ts +657 -0
  430. package/src/scripts/report_installed_rule_links.ts +157 -0
  431. package/src/scripts/report_invocation_surface.ts +383 -0
  432. package/src/scripts/report_label_agreement.ts +233 -0
  433. package/src/scripts/report_obligation_carriers.ts +699 -0
  434. package/src/scripts/report_obligation_mechanism.ts +230 -0
  435. package/src/scripts/report_obligation_writer_split.ts +236 -0
  436. package/src/scripts/report_single_token_triggers.ts +277 -0
  437. package/src/scripts/report_skill_menu_census.ts +208 -0
  438. package/src/scripts/report_skill_ranker_confusion.ts +442 -0
  439. package/src/scripts/report_standing_payload_by_host.ts +188 -15
  440. package/src/scripts/report_verification_record_coverage.ts +306 -0
  441. package/src/scripts/roadmap_verify_share.ts +213 -0
  442. package/src/scripts/rule_activation_census.ts +5 -2
  443. package/src/scripts/rule_trigger_eval.ts +9 -9
  444. package/src/scripts/schemas/command.schema.json +44 -0
  445. package/src/scripts/schemas/review-findings.schema.json +7 -2
  446. package/src/scripts/schemas/rule.schema.json +5 -0
  447. package/src/scripts/schemas/skill.schema.json +44 -0
  448. package/src/scripts/self_review_gate.ts +421 -17
  449. package/src/scripts/semantic_parity_census.ts +314 -0
  450. package/src/scripts/session_eol_report.ts +54 -1
  451. package/src/scripts/skill_tools/score_skill_relevance.ts +290 -25
  452. package/src/scripts/sweep_dead_scan_roots.ts +1 -1
  453. package/src/scripts/sweep_skill_ranker_signals.ts +129 -0
  454. package/src/scripts/sync_gitignore.ts +29 -235
  455. package/src/scripts/trigger_eval_grandfather.json +0 -7
  456. package/src/scripts/trigger_eval_rotation.ts +122 -27
  457. package/src/scripts/ui_conformance_probe.ts +156 -4
  458. package/src/scripts/update_prices.ts +85 -3
  459. package/src/scripts/write_bundle_digest.mjs +74 -0
  460. package/src/server/schemas/settings.ts +41 -0
  461. package/src/shared/skillRanking.ts +117 -11
  462. package/src/templates/consumer-settings/ONBOARDING.md +1 -0
  463. package/dist/cli-delegate/chunk-FXYUYU4N.js +0 -4416
  464. package/dist/cli-delegate/chunk-QJWAUMJW.js +0 -1072
  465. package/dist/cli-delegate/chunk-YDPILCOL.js +0 -152
  466. package/dist/ui/assets/index-DH1xc62v.js +0 -29
  467. package/dist/ui/assets/index-jSfMW8_t.css +0 -1
@@ -22,8 +22,22 @@
22
22
  * adding corpus cases lowers coverage while every count rises, and a count
23
23
  * ratchet would call that progress.
24
24
  *
25
- * Exit codes: 0 = at or above seed · 1 = a scope fell · 2 = dead scope.
25
+ * THE THIRD SCOPE, ADDED BY `road-to-a-menu-whose-precision-is-measured` 2.1.
26
+ * The two ratios above are estate-wide and therefore patient: a contributor can
27
+ * edit a corpus-less skill for a year without either ratio moving, because the
28
+ * numerator and the denominator both stay put. `touched` closes that on the one
29
+ * occasion where the cost of writing a corpus is lowest — the author already has
30
+ * the skill open. A skill CHANGED by the diff must carry `evals/triggers.json`.
31
+ *
32
+ * It is deliberately not a fourth ratio. A ratio over a diff is meaningless when
33
+ * the diff touches one skill, and the obligation here is per skill rather than
34
+ * per estate: one uncovered touched skill is one failure, whatever the estate
35
+ * average says.
36
+ *
37
+ * Exit codes: 0 = at or above seed and no uncovered touched skill · 1 = a scope
38
+ * fell or a touched skill has no corpus · 2 = dead scope.
26
39
  */
40
+ import { spawnSync } from 'node:child_process';
27
41
  import * as fs from 'node:fs';
28
42
  import * as os from 'node:os';
29
43
  import * as path from 'node:path';
@@ -32,6 +46,7 @@ import { fileURLToPath } from 'node:url';
32
46
 
33
47
  import { GateLedger } from './_lib/gate_ledger.js';
34
48
  import { type SelfTestCase, runGateCli, runSelfTest } from './_lib/gate_self_test.js';
49
+ import { gitEnv } from './_lib/git_env.js';
35
50
  import { DeadScopeError, reportScanned } from './_lib/scan_scope.js';
36
51
 
37
52
  const HERE = fileURLToPath(import.meta.url);
@@ -114,6 +129,73 @@ export function measureSkills(root: string): ScopeReading {
114
129
  return { scope: 'skills', cases, units, ratio: units === 0 ? 0 : cases / units };
115
130
  }
116
131
 
132
+ /**
133
+ * The skills carrying no `evals/triggers.json`, grouped by the packs they declare.
134
+ *
135
+ * `road-to-a-menu-whose-precision-is-measured` 2.1's second half. The ratio above
136
+ * says HOW MANY are uncovered; this says WHICH, in the grouping a contributor
137
+ * can act on — a pack owner can take their own column without reading the other
138
+ * twenty-six. A skill declaring several packs appears under each, so the column
139
+ * sums CAN exceed the total. Measured 2026-09-29 they do not: no skill in this
140
+ * tree declares more than one pack, so the columns sum to exactly the total.
141
+ * Stated as a property of the renderer rather than of today's tree, because a
142
+ * reader checking the arithmetic against the table needs to know which it is.
143
+ *
144
+ * A skill declaring no pack lands under `(no pack declared)` rather than being
145
+ * dropped, because a silently omitted skill is exactly the coverage hole this
146
+ * census exists to make visible.
147
+ */
148
+ export function uncoveredByPack(root: string): Record<string, string[]> {
149
+ const base = path.join(root, 'src', 'skills');
150
+ const out: Record<string, string[]> = {};
151
+ for (const e of fs.readdirSync(base, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
152
+ if (!e.isDirectory()) continue;
153
+ const skillMd = path.join(base, e.name, 'SKILL.md');
154
+ if (!fs.existsSync(skillMd)) continue;
155
+ if (fs.existsSync(path.join(base, e.name, 'evals', 'triggers.json'))) continue;
156
+ const packs = readPacks(skillMd);
157
+ for (const p of packs.length > 0 ? packs : ['(no pack declared)']) {
158
+ (out[p] ??= []).push(e.name);
159
+ }
160
+ }
161
+ return out;
162
+ }
163
+
164
+ /** The `packs:` list from a SKILL.md frontmatter block. Line-oriented on purpose. */
165
+ function readPacks(skillMd: string): string[] {
166
+ const text = fs.readFileSync(skillMd, 'utf-8');
167
+ if (!text.startsWith('---')) return [];
168
+ const end = text.indexOf('\n---', 3);
169
+ if (end === -1) return [];
170
+ const lines = text.slice(3, end).split('\n');
171
+ const at = lines.findIndex((l) => /^packs:/u.test(l));
172
+ if (at === -1) return [];
173
+ const out: string[] = [];
174
+ for (let i = at + 1; i < lines.length; i += 1) {
175
+ const m = /^\s+-\s+(\S+)\s*$/u.exec(lines[i] as string);
176
+ if (!m) break;
177
+ out.push(m[1] as string);
178
+ }
179
+ return out;
180
+ }
181
+
182
+ /** Render the census as the markdown section `docs/SKILL_CENSUS.md` carries. */
183
+ export function renderUncoveredCensus(root: string): string[] {
184
+ const byPack = uncoveredByPack(root);
185
+ const reading = measureSkills(root);
186
+ const total = reading.units - reading.cases;
187
+ const L: string[] = [];
188
+ L.push(`Skills with no \`evals/triggers.json\`: **${String(total)} of ${String(reading.units)}**.`);
189
+ L.push('');
190
+ L.push('| Pack | Uncovered | Skills |');
191
+ L.push('|---|---:|---|');
192
+ for (const pack of Object.keys(byPack).sort()) {
193
+ const names = byPack[pack] as string[];
194
+ L.push(`| \`${pack}\` | ${String(names.length)} | ${names.map((n) => `\`${n}\``).join(', ')} |`);
195
+ }
196
+ return L;
197
+ }
198
+
117
199
  export function readSeed(root: string): Seed {
118
200
  const p = path.join(root, SEED_REL);
119
201
  let raw: string;
@@ -136,10 +218,125 @@ export function readSeed(root: string): Seed {
136
218
  return { rules: s.rules, skills: s.skills };
137
219
  }
138
220
 
221
+ /** The ref the touched-skill scope diffs against when `--base` is not given. */
222
+ export const TOUCHED_BASE_DEFAULT = 'origin/main';
223
+
224
+ /** One git invocation: the lines it printed, and whether it actually ran. */
225
+ interface GitRead {
226
+ lines: string[];
227
+ ok: boolean;
228
+ }
229
+
230
+ function gitLines(root: string, args: string[]): GitRead {
231
+ const r = spawnSync('git', args, { cwd: root, encoding: 'utf-8', env: gitEnv() });
232
+ return { lines: r.status === 0 ? r.stdout.split('\n') : [], ok: r.status === 0 };
233
+ }
234
+
235
+ /**
236
+ * Paths the BRANCH added or changed against `base`.
237
+ *
238
+ * `ok` is separate from the path list and that separation is the whole point: a
239
+ * failed `git diff` and a diff that changed nothing both print zero lines, and
240
+ * reading the first as the second is how a gate reports green over a corpus it
241
+ * never saw. `ok: false` means the branch arm was not measured, never that it
242
+ * was measured and found empty.
243
+ *
244
+ * `ACMR` and not `D`: a DELETED skill cannot be asked for a corpus, and reading
245
+ * a deletion as an uncovered touch would make removing a skill impossible.
246
+ */
247
+ export function branchPaths(root: string, base: string): GitRead {
248
+ return gitLines(root, ['diff', '--name-only', '--diff-filter=ACMR', `${base}...HEAD`]);
249
+ }
250
+
251
+ /**
252
+ * Paths changed in this checkout with no base ref involved — dirty, staged and
253
+ * untracked.
254
+ *
255
+ * Measured even when the branch arm cannot be: these three need no merge base,
256
+ * so discarding them because a base ref is unresolvable throws away the
257
+ * coverage that WAS available. Untracked is included so a brand-new skill
258
+ * counts as touched before its first commit.
259
+ */
260
+ export function localPaths(root: string): string[] {
261
+ return [
262
+ ...new Set([
263
+ ...gitLines(root, ['diff', '--name-only', '--diff-filter=ACMR']).lines,
264
+ ...gitLines(root, ['diff', '--name-only', '--diff-filter=ACMR', '--cached']).lines,
265
+ ...gitLines(root, ['ls-files', '--others', '--exclude-standard']).lines,
266
+ ]),
267
+ ].filter((f) => f.trim().length > 0);
268
+ }
269
+
270
+ /** Every path the diff touched, branch arm included when it could be read. */
271
+ export function changedPaths(root: string, base: string): string[] {
272
+ const branch = baseUsable(root, base) ? branchPaths(root, base).lines : [];
273
+ return [...new Set([...branch, ...localPaths(root)])].filter((f) => f.trim().length > 0);
274
+ }
275
+
276
+ /**
277
+ * True when `base` can actually be diffed against, not merely named.
278
+ *
279
+ * Ref existence is NOT the question, and assuming it was is the defect this
280
+ * function is named after. In a shallow clone `git rev-parse origin/main` exits
281
+ * 0 while `git diff origin/main...HEAD` exits 128 with `no merge base`, because
282
+ * the common ancestor was never fetched. The only honest probe is the diff
283
+ * itself, so that is what this runs.
284
+ */
285
+ export function baseUsable(root: string, base: string): boolean {
286
+ if (!gitLines(root, ['rev-parse', '--verify', '--quiet', `${base}^{commit}`]).ok) return false;
287
+ return branchPaths(root, base).ok;
288
+ }
289
+
290
+ /** Retained name for the ref-existence half alone. Prefer {@link baseUsable}. */
291
+ export function baseResolvable(root: string, base: string): boolean {
292
+ return gitLines(root, ['rev-parse', '--verify', '--quiet', `${base}^{commit}`]).ok;
293
+ }
294
+
295
+ export interface TouchedReading {
296
+ /** Skills the diff changed, sorted. */
297
+ touched: string[];
298
+ /** Of those, the ones with no `evals/triggers.json`, sorted. */
299
+ uncovered: string[];
300
+ /**
301
+ * False when the BRANCH arm could not be read — an unresolvable base, or a
302
+ * shallow clone with no merge base. The local arms below are still measured
303
+ * and can still fail the run; `measured: false` narrows what the run may
304
+ * CLAIM, it does not switch the scope off.
305
+ */
306
+ measured: boolean;
307
+ base: string;
308
+ }
309
+
310
+ /**
311
+ * Which skills the diff touched, and which of those still have no corpus.
312
+ *
313
+ * An unresolvable base is NOT an empty touch set. A shallow clone, a detached
314
+ * build or a fresh worktree with no remote would otherwise report "nothing
315
+ * touched" and pass, which is the silent-green shape this repository's gates are
316
+ * written against. It reports `measured: false` and the caller says so out loud.
317
+ */
318
+ export function measureTouchedSkills(root: string, base: string): TouchedReading {
319
+ const measured = baseUsable(root, base);
320
+ const rels = measured ? changedPaths(root, base) : localPaths(root);
321
+ const names = new Set<string>();
322
+ for (const rel of rels) {
323
+ const m = /^src\/skills\/([^/]+)\//.exec(rel.replace(/\\/g, '/'));
324
+ if (m?.[1] !== undefined) names.add(m[1]);
325
+ }
326
+ const touched = [...names].sort();
327
+ const uncovered = touched.filter(
328
+ (n) =>
329
+ fs.existsSync(path.join(root, 'src', 'skills', n, 'SKILL.md')) &&
330
+ !fs.existsSync(path.join(root, 'src', 'skills', n, 'evals', 'triggers.json')),
331
+ );
332
+ return { touched, uncovered, measured, base };
333
+ }
334
+
139
335
  export interface Verdict {
140
336
  readings: ScopeReading[];
141
337
  seed: Seed;
142
338
  fallen: Scope[];
339
+ touched: TouchedReading;
143
340
  ledger: GateLedger;
144
341
  }
145
342
 
@@ -163,14 +360,14 @@ export function r4(n: number): number {
163
360
  return Math.round(n * 10_000) / 10_000;
164
361
  }
165
362
 
166
- export function evaluate(root = REPO_ROOT): Verdict {
363
+ export function evaluate(root = REPO_ROOT, base = TOUCHED_BASE_DEFAULT): Verdict {
167
364
  const seed = readSeed(root);
168
365
  const readings = [measureRules(root), measureSkills(root)];
169
366
  // The ledger's target is the SCOPE, not the individual unit: this gate's
170
367
  // verdict is per scope, so a ledger over 404 units would report 404 rows
171
368
  // no one can act on while hiding which of the two actually fell.
172
369
  const ledger = new GateLedger('check_routing_coverage');
173
- ledger.plan(readings.map((r) => r.scope));
370
+ ledger.plan([...readings.map((r) => r.scope), 'touched']);
174
371
  const fallen: Scope[] = [];
175
372
  for (const r of readings) {
176
373
  if (r4(r.ratio) < r4(seed[r.scope])) {
@@ -184,7 +381,18 @@ export function evaluate(root = REPO_ROOT): Verdict {
184
381
  ledger.complete(r.scope);
185
382
  }
186
383
  }
187
- return { readings, seed, fallen, ledger };
384
+ const touched = measureTouchedSkills(root, base);
385
+ if (touched.uncovered.length > 0) {
386
+ ledger.fail('touched', `${String(touched.uncovered.length)} touched skill(s) with no corpus`);
387
+ } else if (!touched.measured) {
388
+ // A finding outranks the precondition: the local arms need no base ref,
389
+ // so an uncovered skill they found is a real failure even though the
390
+ // branch arm did not run. Only a CLEAN unmeasured run is a skip.
391
+ ledger.skip('touched', 'precondition_unmet');
392
+ } else {
393
+ ledger.complete('touched');
394
+ }
395
+ return { readings, seed, fallen, touched, ledger };
188
396
  }
189
397
 
190
398
  /**
@@ -231,12 +439,64 @@ function selfTestRoot(
231
439
  return root;
232
440
  }
233
441
 
442
+ /**
443
+ * Turn a self-test root into a git repository with a `base` commit, then apply
444
+ * `edit` as the working-tree change the gate will read as "this diff".
445
+ *
446
+ * A real repository rather than a stubbed diff: the touched scope's whole risk
447
+ * is that its git invocation resolves against the wrong tree (an inherited
448
+ * `GIT_DIR` does exactly that), and a stub would never see it.
449
+ */
450
+ function gitBaseline(root: string, edit: (root: string) => void): string {
451
+ const env = gitEnv();
452
+ const git = (...args: string[]): void => {
453
+ const r = spawnSync('git', args, { cwd: root, encoding: 'utf-8', env });
454
+ if (r.status !== 0) throw new Error(`git ${args.join(' ')} failed: ${r.stderr}`);
455
+ };
456
+ git('init', '--quiet', '--initial-branch=base');
457
+ git('config', 'user.email', 'selftest@example.invalid');
458
+ git('config', 'user.name', 'selftest');
459
+ git('config', 'commit.gpgsign', 'false');
460
+ git('add', '--all');
461
+ git('commit', '--quiet', '-m', 'baseline');
462
+ edit(root);
463
+ return root;
464
+ }
465
+
466
+ /**
467
+ * Like {@link gitBaseline}, but the edit is COMMITTED on a feature branch.
468
+ *
469
+ * The arm CI actually uses is `git diff <base>...HEAD`, and every case that
470
+ * leaves its edit in the working tree exercises the other three arms instead.
471
+ * A committed fixture is the only one that can fail when the branch arm breaks.
472
+ */
473
+ function gitBranchCommit(root: string, edit: (root: string) => void): string {
474
+ gitBaseline(root, () => undefined);
475
+ const env = gitEnv();
476
+ const git = (...args: string[]): void => {
477
+ const r = spawnSync('git', args, { cwd: root, encoding: 'utf-8', env });
478
+ if (r.status !== 0) throw new Error(`git ${args.join(' ')} failed: ${r.stderr}`);
479
+ };
480
+ git('checkout', '--quiet', '-b', 'feature');
481
+ edit(root);
482
+ git('add', '--all');
483
+ git('commit', '--quiet', '-m', 'the change');
484
+ return root;
485
+ }
486
+
487
+ /** Write `evals/triggers.json` for a self-test skill. */
488
+ function plantCorpus(root: string, skill: string): void {
489
+ const d = path.join(root, 'src', 'skills', skill, 'evals');
490
+ fs.mkdirSync(d, { recursive: true });
491
+ fs.writeFileSync(path.join(d, 'triggers.json'), '[]\n');
492
+ }
493
+
234
494
  export function selfTest(): number {
235
495
  const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'crc-selftest-'));
236
- const run = (root: string): number => {
496
+ const run = (root: string, args: readonly string[] = []): number => {
237
497
  process.env['CHECK_ROUTING_COVERAGE_ROOT'] = root;
238
498
  try {
239
- return runGateCli(REAL_REPO_ROOT, 'src/scripts/check_routing_coverage.ts', [], root);
499
+ return runGateCli(REAL_REPO_ROOT, 'src/scripts/check_routing_coverage.ts', args, root);
240
500
  } finally {
241
501
  delete process.env['CHECK_ROUTING_COVERAGE_ROOT'];
242
502
  }
@@ -285,13 +545,82 @@ export function selfTest(): number {
285
545
  return run(root);
286
546
  },
287
547
  },
548
+ {
549
+ name: 'TOUCHING a skill that has no evals/triggers.json is rejected',
550
+ expect: 'reject',
551
+ run: () => {
552
+ // st-skill-9 is outside `sc`, so the baseline leaves it corpus-less.
553
+ const root = gitBaseline(selfTestRoot(tmp, { r: 10, rc: 8, s: 10, sc: 5 }), (r) => {
554
+ fs.appendFileSync(path.join(r, 'src', 'skills', 'st-skill-9', 'SKILL.md'), '\nedited\n');
555
+ });
556
+ return run(root, ['--base', 'base']);
557
+ },
558
+ },
559
+ {
560
+ name: 'touching a skill that DOES carry a corpus is accepted',
561
+ expect: 'accept',
562
+ run: () => {
563
+ const root = gitBaseline(selfTestRoot(tmp, { r: 10, rc: 8, s: 10, sc: 5 }), (r) => {
564
+ fs.appendFileSync(path.join(r, 'src', 'skills', 'st-skill-0', 'SKILL.md'), '\nedited\n');
565
+ });
566
+ return run(root, ['--base', 'base']);
567
+ },
568
+ },
569
+ {
570
+ name: 'a corpus-less skill the diff did NOT touch is accepted — the scope is the diff',
571
+ expect: 'accept',
572
+ run: () => {
573
+ const root = gitBaseline(selfTestRoot(tmp, { r: 10, rc: 8, s: 10, sc: 5 }), (r) => {
574
+ fs.writeFileSync(path.join(r, 'unrelated.txt'), 'x\n');
575
+ });
576
+ return run(root, ['--base', 'base']);
577
+ },
578
+ },
579
+ {
580
+ name: 'a NEW corpus-less skill added by the diff is rejected before its first commit',
581
+ expect: 'reject',
582
+ run: () => {
583
+ const root = gitBaseline(selfTestRoot(tmp, { r: 10, rc: 8, s: 10, sc: 5 }), (r) => {
584
+ const d = path.join(r, 'src', 'skills', 'st-skill-new');
585
+ fs.mkdirSync(d, { recursive: true });
586
+ fs.writeFileSync(path.join(d, 'SKILL.md'), '---\nname: st\n---\n');
587
+ // A corpus-less sibling gains one, so the SKILLS RATIO stays above
588
+ // seed (6/11 > 0.5) and the only thing left to reject on is the new
589
+ // skill's own missing corpus. Without this the case rejected for the
590
+ // ratio instead — green for the wrong reason, which the sabotage
591
+ // probe caught.
592
+ plantCorpus(r, 'st-skill-6');
593
+ });
594
+ return run(root, ['--base', 'base']);
595
+ },
596
+ },
597
+ {
598
+ name: 'a skill edited in a COMMIT, not the working tree, is rejected — the arm CI uses',
599
+ expect: 'reject',
600
+ run: () => {
601
+ const root = gitBranchCommit(selfTestRoot(tmp, { r: 10, rc: 8, s: 10, sc: 5 }), (r) => {
602
+ fs.appendFileSync(path.join(r, 'src', 'skills', 'st-skill-9', 'SKILL.md'), '\nedited\n');
603
+ });
604
+ return run(root, ['--base', 'base']);
605
+ },
606
+ },
607
+ {
608
+ name: 'an UNDIFFABLE base still rejects a dirty uncovered skill — the local arms need no base',
609
+ expect: 'reject',
610
+ run: () => {
611
+ const root = gitBaseline(selfTestRoot(tmp, { r: 10, rc: 8, s: 10, sc: 5 }), (r) => {
612
+ fs.appendFileSync(path.join(r, 'src', 'skills', 'st-skill-9', 'SKILL.md'), '\nedited\n');
613
+ });
614
+ return run(root, ['--base', 'no-such-ref-at-all']);
615
+ },
616
+ },
288
617
  ];
289
618
  try {
290
619
  return runSelfTest({
291
620
  gate: 'check_routing_coverage',
292
621
  cases,
293
- minCases: 7,
294
- minRejectCases: 5,
622
+ minCases: 13,
623
+ minRejectCases: 9,
295
624
  });
296
625
  } finally {
297
626
  fs.rmSync(tmp, { recursive: true, force: true });
@@ -300,9 +629,15 @@ export function selfTest(): number {
300
629
 
301
630
  export function main(argv: string[] = process.argv.slice(2), root = REPO_ROOT): number {
302
631
  if (argv.includes('--self-test')) return selfTest();
632
+ if (argv.includes('--census')) {
633
+ process.stdout.write(`${renderUncoveredCensus(root).join('\n')}\n`);
634
+ return 0;
635
+ }
636
+ const baseIdx = argv.indexOf('--base');
637
+ const base = baseIdx !== -1 ? (argv[baseIdx + 1] ?? TOUCHED_BASE_DEFAULT) : TOUCHED_BASE_DEFAULT;
303
638
  let v: Verdict;
304
639
  try {
305
- v = evaluate(root);
640
+ v = evaluate(root, base);
306
641
  reportScanned({
307
642
  gate: 'check_routing_coverage',
308
643
  scanned: v.readings.reduce((n, r) => n + r.units, 0),
@@ -330,10 +665,42 @@ export function main(argv: string[] = process.argv.slice(2), root = REPO_ROOT):
330
665
  `${r.ratio.toFixed(4)} (seed ${seed.toFixed(4)})\n`,
331
666
  );
332
667
  }
333
- if (v.fallen.length === 0) {
334
- process.stdout.write('✅ routing coverage at or above seed in both scopes.\n');
668
+ if (!v.touched.measured) {
669
+ process.stdout.write(
670
+ ` · touched ${String(v.touched.touched.length).padStart(4)} skill(s) from the working ` +
671
+ `tree only — base ${v.touched.base} is not diffable here (missing ref, or a shallow ` +
672
+ `clone with no merge base), so the branch arm did NOT run. ` +
673
+ `${String(v.touched.uncovered.length)} without evals/triggers.json.\n`,
674
+ );
675
+ } else {
676
+ process.stdout.write(
677
+ ` ${v.touched.uncovered.length === 0 ? '=' : '❌'} touched ${String(v.touched.touched.length).padStart(4)} skill(s) ` +
678
+ `vs ${v.touched.base}, ${String(v.touched.uncovered.length)} without evals/triggers.json\n`,
679
+ );
680
+ }
681
+ if (v.fallen.length === 0 && v.touched.uncovered.length === 0) {
682
+ // The claim is narrowed to what actually ran. A single affirmative line
683
+ // covering an unmeasured scope is the silent green this scope exists to
684
+ // refuse, and it would be this gate committing it.
685
+ process.stdout.write(
686
+ v.touched.measured
687
+ ? '✅ routing coverage at or above seed, and every touched skill carries a corpus.\n'
688
+ : '✅ routing coverage at or above seed. ⚠️ The branch arm of the touched-skill ' +
689
+ `scope did NOT run (base ${v.touched.base} is not diffable here), so nothing ` +
690
+ 'is claimed about skills this branch changed in a commit.\n',
691
+ );
335
692
  return 0;
336
693
  }
694
+ if (v.touched.uncovered.length > 0) {
695
+ process.stderr.write(
696
+ `❌ ${String(v.touched.uncovered.length)} skill(s) changed by this diff carry no ` +
697
+ 'evals/triggers.json:\n' +
698
+ v.touched.uncovered.map((n) => ` · src/skills/${n}/evals/triggers.json\n`).join('') +
699
+ ' The corpus is cheapest to write while the skill is open. Write the trigger ' +
700
+ 'cases for each; the discipline each file must meet is enforced by ' +
701
+ 'lint_skill_trigger_corpus.\n',
702
+ );
703
+ }
337
704
  for (const s of v.fallen) {
338
705
  const r = v.readings.find((x) => x.scope === s)!;
339
706
  process.stderr.write(
@@ -31,24 +31,44 @@
31
31
  * Exit codes: `0` under cap · `1` over cap or an unstated cap raise · `2` usage
32
32
  * error · `3` dead scan scope.
33
33
  *
34
- * ## This is deliberately NOT a CI gate
34
+ * THIS IS DELIBERATELY NOT A CI GATE, AND THE REASON IS NARROWER THAN IT WAS.
35
35
  *
36
- * Both inputs are machine-local by construction. `.claude/rules/` is gitignored
37
- * (`.gitignore:123` — it is generated by `task sync` / `task generate-tools`),
38
- * and no CI workflow performs a user-scope install, so on a fresh runner
39
- * **neither** layer exists. Wired into `ci` it would either red on a dead scope
40
- * or, with the empty case allowed, sit permanently green while certifying a
41
- * property it cannot observe — the exact failure `src/config/gate-coverage.yml`
42
- * was written to end. It is therefore registered as a local / doctor-surface
43
- * check and is absent from `gate-coverage.yml`, whose contract is CI-identical
44
- * invocation.
36
+ * **Corrected 2026-10-05 (step 0.1 of
37
+ * `road-to-a-rule-carrier-that-works-outside-the-repo`).** This section used to
38
+ * say "no CI workflow performs a user-scope install, so on a fresh runner
39
+ * **neither** layer exists", and then concluded that making the measurement
40
+ * CI-observable "needs the `InstructionsLoaded` record committed from a real
41
+ * session, which is a different piece of work". The first clause describes a
42
+ * workflow inventory; the second turns it into a capability claim, and that
43
+ * step does not hold. A runner CAN stage a user-scope install: the installer
44
+ * reads `HOME` from the environment, and
45
+ * `tests/install/global_install_hooks_smoke.test.ts` already drives
46
+ * `src/scripts/install --global --tools=claude-code` against a `mkdtemp` HOME
47
+ * and asserts the tree it produced. Nothing about the `InstructionsLoaded`
48
+ * event is required to observe the FILESYSTEM half, which is what this script's
49
+ * `filesystem` input reads.
45
50
  *
46
- * What that costs, stated plainly: the duplicate-delivery defect is not
51
+ * What stays true, and is the real reason this is not in `gate-coverage.yml`:
52
+ * **this gate measures THIS machine**, by design. `main()` resolves
53
+ * `os.homedir()` and `REPO_ROOT`, and the question it answers — "how much is
54
+ * the developer in front of me actually loading" — is about a machine, not
55
+ * about a commit. A CI run of it would measure the runner, which is a tree
56
+ * nobody works in, so the number would be real and uninteresting. The
57
+ * `measureStandingDelivery` function underneath takes both directories as
58
+ * PARAMETERS and is therefore already temp-HOME-capable; what is missing is a
59
+ * reason to point it at a runner, not a way to.
60
+ *
61
+ * The honest cost is unchanged: the duplicate-delivery defect is not
47
62
  * mechanically caught for anyone who never runs this locally. The enforced layer
48
63
  * is the installer gate (`install.ts::_gate_rule_layer_overlap`), which refuses
49
64
  * to CREATE the doubled state; this script measures a state that already exists.
50
- * Making the measurement CI-observable needs the `InstructionsLoaded` record
51
- * committed from a real session, which is a different piece of work.
65
+ *
66
+ * `_lib/installed_layer.ts` is the step-0.1 library that takes the same reading
67
+ * per host rule directory with every input as a parameter, and
68
+ * `installed_layer_report.ts` is its report. The two measure different things —
69
+ * that one counts characters after the frontmatter and block-comment strip,
70
+ * this one counts tokens over whole files against a cap — and neither number
71
+ * converts into the other.
52
72
  */
53
73
 
54
74
  import * as fs from 'node:fs';
@@ -266,6 +266,23 @@ export function main(argv: readonly string[] = process.argv.slice(2)): number {
266
266
  }
267
267
  if (present.exitCode !== 0 || absent.exitCode !== 0) {
268
268
  process.stderr.write('❌ one of the two runs is red; parity over a red suite proves nothing\n');
269
+ // Name them. This branch used to print the sentence above and nothing
270
+ // else, while holding every per-test verdict in memory — so a red that
271
+ // reproduced only on the CI runner was undiagnosable from its log, and
272
+ // the only move left was to re-run and hope. Found 2026-09-30 by being
273
+ // on the receiving end of exactly that.
274
+ for (const [label, result] of [['present-but-off', present], ['absent', absent]] as const) {
275
+ const quiescent = new Set(['passed', 'pending', 'skipped', 'todo', 'disabled']);
276
+ const failed = result.verdicts.filter((v) => !quiescent.has(v.status));
277
+ if (failed.length === 0) continue;
278
+ process.stderr.write(` ${label} — ${failed.length} not passing:\n`);
279
+ for (const v of failed.slice(0, 20)) {
280
+ process.stderr.write(` · ${v.status} ${v.name}\n`);
281
+ }
282
+ if (failed.length > 20) {
283
+ process.stderr.write(` … and ${failed.length - 20} more\n`);
284
+ }
285
+ }
269
286
  return 1;
270
287
  }
271
288
  if (differences.length > 0) {