@event4u/agent-config 16.2.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 (332) 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 +89 -237
  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 +4 -4
  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/contexts/authority/decision-revisit-gate-enforcement.md +43 -0
  23. package/dist/agent-src/contexts/communication/rules-auto/design-fidelity-enforcement.md +24 -0
  24. package/dist/agent-src/contexts/execution/auto-dispatch-classification.md +6 -6
  25. package/dist/agent-src/contexts/execution/roadmap-process-loop.md +32 -2
  26. package/dist/agent-src/contexts/execution/session-canary-enforcement-history.md +67 -0
  27. package/dist/agent-src/contexts/execution/toolchain-resolver.md +101 -7
  28. package/dist/agent-src/rules/active-remediation.md +1 -1
  29. package/dist/agent-src/rules/autonomous-execution.md +1 -0
  30. package/dist/agent-src/rules/broken-access-control.md +1 -0
  31. package/dist/agent-src/rules/command-suggestion-policy.md +1 -0
  32. package/dist/agent-src/rules/context-hygiene.md +1 -1
  33. package/dist/agent-src/rules/decision-revisit-gate.md +16 -33
  34. package/dist/agent-src/rules/design-fidelity.md +13 -16
  35. package/dist/agent-src/rules/doc-screenshot-hygiene.md +1 -0
  36. package/dist/agent-src/rules/domain-safety-disclaimer.md +1 -0
  37. package/dist/agent-src/rules/domain-safety-pii.md +1 -0
  38. package/dist/agent-src/rules/domain-safety-retention.md +1 -0
  39. package/dist/agent-src/rules/engineering-safety-floor.md +1 -0
  40. package/dist/agent-src/rules/evaluator-independence.md +3 -2
  41. package/dist/agent-src/rules/fast-path-marker-visibility.md +1 -0
  42. package/dist/agent-src/rules/finance-safety-floor.md +1 -0
  43. package/dist/agent-src/rules/git-history-discipline.md +1 -0
  44. package/dist/agent-src/rules/image-likeness-and-rights.md +1 -0
  45. package/dist/agent-src/rules/legal-safety-floor.md +3 -2
  46. package/dist/agent-src/rules/lethal-trifecta-guard.md +1 -0
  47. package/dist/agent-src/rules/low-impact-corpus-privacy-floor.md +1 -0
  48. package/dist/agent-src/rules/media-governance-routing.md +14 -17
  49. package/dist/agent-src/rules/persona-governance.md +2 -2
  50. package/dist/agent-src/rules/provider-lifecycle-discipline.md +1 -1
  51. package/dist/agent-src/rules/question-not-instruction.md +1 -0
  52. package/dist/agent-src/rules/roadmap-progress-sync.md +1 -1
  53. package/dist/agent-src/rules/runtime-safety.md +1 -0
  54. package/dist/agent-src/rules/secret-vcs-guard.md +1 -0
  55. package/dist/agent-src/rules/security-sensitive-stop.md +3 -2
  56. package/dist/agent-src/rules/session-canary.md +8 -62
  57. package/dist/agent-src/rules/settings-ask-protocol.md +1 -0
  58. package/dist/agent-src/rules/source-confidentiality.md +1 -0
  59. package/dist/agent-src/rules/strategy-safety-floor.md +1 -0
  60. package/dist/agent-src/rules/tool-safety.md +1 -0
  61. package/dist/agent-src/rules/untrusted-input-defense.md +1 -0
  62. package/dist/agent-src/rules/user-interrupt-priority.md +1 -0
  63. package/dist/agent-src/scripts/archive_completed_roadmaps.ts +115 -20
  64. package/dist/agent-src/scripts/deferral_carry.ts +339 -0
  65. package/dist/agent-src/scripts/update_roadmap_progress.ts +4 -1
  66. package/dist/agent-src/skills/api-design/data/manifest.json +2 -2
  67. package/dist/agent-src/skills/api-testing/SKILL.md +17 -0
  68. package/dist/agent-src/skills/code-intelligence/SKILL.md +14 -2
  69. package/dist/agent-src/skills/design-system-capture/references/import-procedure.md +1 -1
  70. package/dist/agent-src/skills/existing-ui-audit/SKILL.md +2 -2
  71. package/dist/agent-src/skills/fe-design/SKILL.md +1 -1
  72. package/dist/agent-src/skills/iconography/SKILL.md +1 -1
  73. package/dist/agent-src/skills/js-library-packaging/SKILL.md +2 -2
  74. package/dist/agent-src/skills/monorepo-workspace/SKILL.md +1 -1
  75. package/dist/agent-src/skills/quality-tools/SKILL.md +11 -1
  76. package/dist/agent-src/skills/quality-tools/references/go-tools.md +76 -0
  77. package/dist/agent-src/skills/quality-tools/references/python-tools.md +104 -0
  78. package/dist/agent-src/skills/review-routing/SKILL.md +2 -2
  79. package/dist/agent-src/skills/skill-improvement-pipeline/SKILL.md +1 -1
  80. package/dist/agent-src/skills/storybook-workshop/SKILL.md +3 -3
  81. package/dist/agent-src/skills/tailwind-engineer/SKILL.md +1 -1
  82. package/dist/agent-src/skills/test-case-discovery/SKILL.md +7 -0
  83. package/dist/agent-src/skills/test-driven-development/SKILL.md +17 -0
  84. package/dist/agent-src/skills/test-performance/SKILL.md +19 -1
  85. package/dist/agent-src/skills/ui-component-architect/SKILL.md +2 -2
  86. package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
  87. package/dist/agent-src/templates/scripts/work_engine/directives/ui/apply.ts +48 -6
  88. package/dist/agent-src/templates/scripts/work_engine/stack/runner.ts +774 -109
  89. package/dist/cli/registry.js +1 -1
  90. package/dist/cli-delegate/archive_completed_roadmaps.js +92 -12
  91. package/dist/cli-delegate/check_update_banner.js +12 -1
  92. package/dist/cli-delegate/chunk-523HCFYO.js +90 -0
  93. package/dist/cli-delegate/{chunk-M2DPGFZ4.js → chunk-6V6TA25H.js} +22 -152
  94. package/dist/cli-delegate/chunk-7IQE3D5S.js +191 -0
  95. package/dist/cli-delegate/{chunk-6KFEC3G7.js → chunk-CQWX336L.js} +1 -1
  96. package/dist/cli-delegate/chunk-DEIV7XP7.js +108 -0
  97. package/dist/cli-delegate/chunk-EEHDXSYD.js +2040 -0
  98. package/dist/cli-delegate/{chunk-P6ZHP5W2.js → chunk-GKE7ZVFA.js} +4 -4
  99. package/dist/cli-delegate/chunk-JQOM7TTM.js +1201 -0
  100. package/dist/cli-delegate/{chunk-IGD6ERX6.js → chunk-JQVYKIZA.js} +10 -0
  101. package/dist/cli-delegate/{chunk-FC75PGJE.js → chunk-JY2I2DXV.js} +12 -1
  102. package/dist/cli-delegate/chunk-KB5FZ655.js +208 -0
  103. package/dist/cli-delegate/{chunk-7RQP4UDH.js → chunk-KR6EM23A.js} +235 -1455
  104. package/dist/cli-delegate/{chunk-LTYVIY76.js → chunk-LIYTLH6O.js} +1 -1
  105. package/dist/cli-delegate/{chunk-YJ72UIAK.js → chunk-NQ7WT43P.js} +1186 -244
  106. package/dist/cli-delegate/chunk-NXEILF2K.js +2160 -0
  107. package/dist/cli-delegate/{chunk-5R7HOLAF.js → chunk-NYX3L2PZ.js} +1 -1
  108. package/dist/cli-delegate/{chunk-4E2W63DA.js → chunk-OKJUIAPS.js} +62 -1
  109. package/dist/cli-delegate/{chunk-QAJS5YWY.js → chunk-PHD7OOLE.js} +2 -1
  110. package/dist/cli-delegate/{chunk-RGNIWTEA.js → chunk-QLDHIPWR.js} +1 -1
  111. package/dist/cli-delegate/{chunk-7XGBX6ZH.js → chunk-RQNNW7IF.js} +1314 -560
  112. package/dist/cli-delegate/{chunk-4XFP2Y3E.js → chunk-S4UMZNES.js} +1 -1
  113. package/dist/cli-delegate/{chunk-6KM6ORRY.js → chunk-S4YWNRKO.js} +24 -8
  114. package/dist/cli-delegate/{chunk-FP36SWJD.js → chunk-TRI6DQZ6.js} +2781 -340
  115. package/dist/cli-delegate/chunk-ULQRUR23.js +208 -0
  116. package/dist/cli-delegate/{chunk-NUHLI2RX.js → chunk-YCU3TIXH.js} +2 -0
  117. package/dist/cli-delegate/chunk-ZBZV2G5R.js +210 -0
  118. package/dist/cli-delegate/{chunk-WGXTVKCQ.js → chunk-ZUZKZYCN.js} +5 -3
  119. package/dist/cli-delegate/cmd_analyze_session.js +1 -1
  120. package/dist/cli-delegate/cmd_brand_status.js +1 -1
  121. package/dist/cli-delegate/cmd_conformance.js +1167 -234
  122. package/dist/cli-delegate/cmd_doctor.js +13 -6
  123. package/dist/cli-delegate/cmd_doctor_neighbours.js +23 -0
  124. package/dist/cli-delegate/cmd_explain.js +5 -5
  125. package/dist/cli-delegate/cmd_export.js +11 -8
  126. package/dist/cli-delegate/cmd_fleet.js +3 -3
  127. package/dist/cli-delegate/cmd_handoff.js +10 -7
  128. package/dist/cli-delegate/cmd_mcp_available.js +1 -1
  129. package/dist/cli-delegate/cmd_memory_get.js +3 -1
  130. package/dist/cli-delegate/cmd_migrate.js +2 -2
  131. package/dist/cli-delegate/cmd_packs_active.js +3 -3
  132. package/dist/cli-delegate/cmd_preflight.js +6 -6
  133. package/dist/cli-delegate/cmd_prune.js +11 -8
  134. package/dist/cli-delegate/cmd_refresh.js +2 -1
  135. package/dist/cli-delegate/cmd_route_audit.js +8 -6
  136. package/dist/cli-delegate/cmd_route_explain.js +4 -4
  137. package/dist/cli-delegate/cmd_session_recycle.js +4 -3
  138. package/dist/cli-delegate/cmd_settings_get.js +1 -1
  139. package/dist/cli-delegate/cmd_settings_migrate.js +11 -8
  140. package/dist/cli-delegate/cmd_settings_set.js +4 -4
  141. package/dist/cli-delegate/cmd_sync.js +11 -8
  142. package/dist/cli-delegate/cmd_uninstall.js +11 -8
  143. package/dist/cli-delegate/cmd_update.js +1 -1
  144. package/dist/cli-delegate/cmd_upgrade.js +3 -2
  145. package/dist/cli-delegate/cmd_validate.js +11 -8
  146. package/dist/cli-delegate/cmd_versions.js +1 -1
  147. package/dist/cli-delegate/deferral_carry.js +16 -0
  148. package/dist/cli-delegate/gate_execute.js +2 -2
  149. package/dist/cli-delegate/roadmap_gates.js +5 -5
  150. package/dist/cli-delegate/update_roadmap_progress.js +1 -1
  151. package/dist/discovery/deprecation-report.md +1 -1
  152. package/dist/discovery/discovery-manifest.json +127 -75
  153. package/dist/discovery/discovery-manifest.json.sha256 +1 -1
  154. package/dist/discovery/discovery-manifest.summary.md +3 -3
  155. package/dist/discovery/dormancy-report.md +1 -1
  156. package/dist/discovery/orphan-report.md +1 -1
  157. package/dist/discovery/packs.json +7 -5
  158. package/dist/discovery/trust-report.md +2 -2
  159. package/dist/discovery/workspaces.json +6 -4
  160. package/dist/hooks/dispatch.js +5357 -6656
  161. package/dist/hooks/dispatch.sha256 +1 -1
  162. package/dist/install/claudePathsPlan.js +47 -6
  163. package/dist/install/claudeRuleRewrite.js +7 -0
  164. package/dist/install/emit_host_rules_cli.js +39 -36
  165. package/dist/install/globalRuleScope.js +87 -0
  166. package/dist/install/host_rule_ownership.js +160 -0
  167. package/dist/install/install.mjs +2080 -1024
  168. package/dist/install/installThinLayer.js +261 -0
  169. package/dist/install/installedRuleLinks.js +176 -0
  170. package/dist/install/wizard-plan.js +24 -0
  171. package/dist/mcp/registry-manifest.json +3 -3
  172. package/dist/mcp/server.json +2 -2
  173. package/dist/mcp/server.mjs +31965 -27154
  174. package/dist/scripts/_lib/agent_settings.js +11 -0
  175. package/dist/scripts/_lib/claude_settings_hooks.js +10 -8
  176. package/dist/scripts/_lib/installed_layer.js +378 -0
  177. package/dist/scripts/_lib/lean_projection_mode.js +218 -15
  178. package/dist/scripts/_lib/rule_consequence_class.js +45 -0
  179. package/dist/scripts/_lib/rule_injection.js +67 -10
  180. package/dist/scripts/_lib/rule_law_section.js +122 -0
  181. package/dist/scripts/_lib/thin_rules.js +458 -0
  182. package/dist/scripts/_lib/windsurf_render.js +60 -0
  183. package/dist/scripts/condense.js +14 -26
  184. package/dist/scripts/project_thin_rules.js +14 -294
  185. package/dist/server/schemas/settings.js +6 -0
  186. package/dist/shared/skillRanking.js +74 -13
  187. package/docs/contracts/settings-classes.md +7 -5
  188. package/package.json +1 -1
  189. package/src/agent-src/scripts/archive_completed_roadmaps.ts +115 -20
  190. package/src/agent-src/scripts/deferral_carry.ts +339 -0
  191. package/src/agent-src/scripts/update_roadmap_progress.ts +4 -1
  192. package/src/agent-src/templates/scripts/work_engine/directives/ui/apply.ts +48 -6
  193. package/src/agent-src/templates/scripts/work_engine/stack/runner.ts +774 -109
  194. package/src/config/agent-settings.template.yml +62 -22
  195. package/src/config/continuity-surface.json +7 -0
  196. package/src/config/evaluator-budgets.json +4 -4
  197. package/src/config/gate-coverage.yml +96 -3
  198. package/src/config/gate-violation-baselines.json +17 -9
  199. package/src/config/hook-bundle-budget.json +25 -0
  200. package/src/config/hook-token-budget.json +6 -1
  201. package/src/config/host-instruction-limits.json +33 -0
  202. package/src/config/routing-coverage-seed.json +24 -3
  203. package/src/config/rule-activation-census.json +7 -3
  204. package/src/config/rule-consequence-class.json +161 -0
  205. package/src/config/rule-law-ceilings.json +99 -0
  206. package/src/install/claudePathsPlan.ts +46 -4
  207. package/src/install/claudeRuleRewrite.ts +10 -1
  208. package/src/install/emit_host_rules_cli.ts +43 -34
  209. package/src/install/globalRuleScope.ts +109 -0
  210. package/src/install/host_rule_ownership.ts +170 -0
  211. package/src/install/installThinLayer.ts +312 -0
  212. package/src/install/installedRuleLinks.ts +220 -0
  213. package/src/install/wizard-plan.ts +24 -0
  214. package/src/scripts/_cli/cmd_doctor.ts +23 -71
  215. package/src/scripts/_cli/cmd_doctor_neighbours.ts +293 -0
  216. package/src/scripts/_dispatch.bash +16 -2
  217. package/src/scripts/_lib/agent_settings.ts +11 -0
  218. package/src/scripts/_lib/claude_settings_hooks.ts +10 -8
  219. package/src/scripts/_lib/estate_offsets.ts +241 -0
  220. package/src/scripts/_lib/exemption_shape.ts +143 -0
  221. package/src/scripts/_lib/fnv.ts +23 -0
  222. package/src/scripts/_lib/foreign_scan.ts +106 -0
  223. package/src/scripts/_lib/gitignore_block.ts +314 -0
  224. package/src/scripts/_lib/global_deploy_inventory.ts +32 -0
  225. package/src/scripts/_lib/graph_feeder_record.ts +214 -0
  226. package/src/scripts/_lib/hook_settings.ts +73 -0
  227. package/src/scripts/_lib/host_capability.ts +40 -19
  228. package/src/scripts/_lib/host_hook_merge.ts +165 -0
  229. package/src/scripts/_lib/installed_layer.ts +538 -0
  230. package/src/scripts/_lib/json_merge.ts +82 -0
  231. package/src/scripts/_lib/json_pointers.ts +93 -11
  232. package/src/scripts/_lib/lean_projection_mode.ts +275 -15
  233. package/src/scripts/_lib/machine_wake.ts +16 -3
  234. package/src/scripts/_lib/neighbour_census.ts +760 -0
  235. package/src/scripts/_lib/neighbour_scan.ts +122 -0
  236. package/src/scripts/_lib/neighbour_tool_use.ts +189 -0
  237. package/src/scripts/_lib/prompt_shape.ts +46 -0
  238. package/src/scripts/_lib/release_migration_gate.ts +19 -3
  239. package/src/scripts/_lib/reserved_name_sweep.ts +73 -0
  240. package/src/scripts/_lib/review_coverage.ts +134 -0
  241. package/src/scripts/_lib/rule_consequence_class.ts +71 -0
  242. package/src/scripts/_lib/rule_injection.ts +130 -10
  243. package/src/scripts/_lib/rule_law_section.ts +147 -0
  244. package/src/scripts/_lib/rule_layer_overlap.ts +58 -0
  245. package/src/scripts/_lib/skill_origin.ts +254 -0
  246. package/src/scripts/_lib/structured_ask.ts +64 -21
  247. package/src/scripts/_lib/thin_rules.ts +540 -0
  248. package/src/scripts/_lib/touched_file_quality.ts +434 -0
  249. package/src/scripts/_lib/turn_end_refusals.ts +77 -2
  250. package/src/scripts/_lib/windsurf_render.ts +63 -0
  251. package/src/scripts/ai_council/cli_call_budget.ts +13 -5
  252. package/src/scripts/ai_council/clients.ts +26 -57
  253. package/src/scripts/ai_council/config.ts +1 -1
  254. package/src/scripts/ai_council/vendor_defaults.ts +45 -0
  255. package/src/scripts/audit_skill_overlap.ts +261 -0
  256. package/src/scripts/before_complete_hook.ts +243 -12
  257. package/src/scripts/bench_touched_file_quality.ts +238 -0
  258. package/src/scripts/check_enforcement_matrix.ts +377 -20
  259. package/src/scripts/check_estate_count.ts +130 -153
  260. package/src/scripts/check_finding_dispositions.ts +116 -4
  261. package/src/scripts/check_hook_bundle_composition.ts +370 -0
  262. package/src/scripts/check_kernel_edit_ratified.ts +49 -11
  263. package/src/scripts/check_memory.ts +7 -1
  264. package/src/scripts/check_release_highlights.ts +10 -0
  265. package/src/scripts/check_release_pr_shape.ts +38 -15
  266. package/src/scripts/check_standing_rule_delivery.ts +33 -13
  267. package/src/scripts/check_verify_expectation_delta.ts +521 -0
  268. package/src/scripts/code_graph/detect.ts +127 -7
  269. package/src/scripts/code_graph/foreign.ts +194 -0
  270. package/src/scripts/code_graph/query.ts +217 -4
  271. package/src/scripts/code_graph/types.ts +26 -2
  272. package/src/scripts/code_graph/validate.ts +3 -1
  273. package/src/scripts/code_graph/verbs.ts +103 -0
  274. package/src/scripts/compile_hook_manifest.ts +33 -1
  275. package/src/scripts/condense.ts +14 -27
  276. package/src/scripts/estate_exemption_shape.ts +202 -0
  277. package/src/scripts/generate_host_cost_table.ts +1 -1
  278. package/src/scripts/hook_manifest.json +1 -1
  279. package/src/scripts/hook_manifest.yaml +150 -9
  280. package/src/scripts/hooks/block_config_weakening.ts +67 -2
  281. package/src/scripts/hooks/block_plumbing_writes.ts +29 -1
  282. package/src/scripts/hooks/code_graph_context_hook.ts +252 -23
  283. package/src/scripts/hooks/concern_failure_policy.ts +280 -0
  284. package/src/scripts/hooks/dispatch_hook.ts +52 -52
  285. package/src/scripts/hooks/exit_codes.ts +2 -1
  286. package/src/scripts/hooks/host_lowering.json +1 -1
  287. package/src/scripts/hooks/host_lowering.ts +19 -1
  288. package/src/scripts/hooks/one_question_per_ask_hook.ts +41 -25
  289. package/src/scripts/hooks/payload_stub.ts +10 -1
  290. package/src/scripts/hooks/rule_inject_hook.ts +757 -63
  291. package/src/scripts/hooks/table_fingerprint.ts +21 -0
  292. package/src/scripts/hooks/telemetry_usage_hook.ts +64 -8
  293. package/src/scripts/hooks/turn_end_gate_hook.ts +84 -0
  294. package/src/scripts/hooks_status.ts +112 -0
  295. package/src/scripts/injection_scan_hook.ts +7 -0
  296. package/src/scripts/install.ts +72 -185
  297. package/src/scripts/installed_layer_report.ts +103 -0
  298. package/src/scripts/language_mirror_hook.ts +15 -6
  299. package/src/scripts/learning_sidecar.ts +119 -8
  300. package/src/scripts/lint_hook_manifest.ts +53 -0
  301. package/src/scripts/lint_major_migration_sections.ts +156 -26
  302. package/src/scripts/lint_pack_boundaries.ts +63 -5
  303. package/src/scripts/lint_rule_law_section.ts +517 -0
  304. package/src/scripts/lint_workflow_security.ts +161 -5
  305. package/src/scripts/mcp_server/consumer_tool_catalog.json +53 -0
  306. package/src/scripts/mcp_server/graph_tools.ts +72 -3
  307. package/src/scripts/measure_routing_signal.ts +54 -16
  308. package/src/scripts/measure_skill_ranker_baseline.ts +216 -8
  309. package/src/scripts/memory_learn_hook.ts +91 -6
  310. package/src/scripts/model_rule_injection.ts +52 -5
  311. package/src/scripts/project_thin_rules.ts +34 -327
  312. package/src/scripts/report_evidence_temperature.ts +657 -0
  313. package/src/scripts/report_installed_rule_links.ts +157 -0
  314. package/src/scripts/report_label_agreement.ts +233 -0
  315. package/src/scripts/report_obligation_mechanism.ts +230 -0
  316. package/src/scripts/report_single_token_triggers.ts +277 -0
  317. package/src/scripts/report_skill_ranker_confusion.ts +442 -0
  318. package/src/scripts/report_standing_payload_by_host.ts +188 -15
  319. package/src/scripts/report_verification_record_coverage.ts +306 -0
  320. package/src/scripts/rule_activation_census.ts +5 -2
  321. package/src/scripts/rule_trigger_eval.ts +9 -9
  322. package/src/scripts/schemas/review-findings.schema.json +3 -2
  323. package/src/scripts/schemas/rule.schema.json +5 -0
  324. package/src/scripts/self_review_gate.ts +62 -7
  325. package/src/scripts/skill_tools/score_skill_relevance.ts +290 -25
  326. package/src/scripts/sweep_skill_ranker_signals.ts +129 -0
  327. package/src/scripts/sync_gitignore.ts +29 -235
  328. package/src/scripts/trigger_eval_grandfather.json +0 -5
  329. package/src/server/schemas/settings.ts +10 -0
  330. package/src/shared/skillRanking.ts +117 -11
  331. package/dist/cli-delegate/chunk-JNMVLWLR.js +0 -4457
  332. package/dist/cli-delegate/chunk-YDPILCOL.js +0 -152
@@ -0,0 +1,517 @@
1
+ #!/usr/bin/env tsx
2
+ /**
3
+ * lint_rule_law_section — every routed rule states its law in one section short
4
+ * enough to stand on its own.
5
+ *
6
+ * THE DEFECT. Measured 2026-10-01 over `dist/router.json`'s 106 tier rules:
7
+ * 71 carry a heading matching `Iron Law`, 35 do not — among them
8
+ * `runtime-safety` and `tool-safety`, which govern tool grants and execution
9
+ * handlers. Several of the 35 state a law in bold text or a bare fence instead,
10
+ * so 35 is an upper bound on what is genuinely missing, not a count of
11
+ * lawless rules. Nothing asserted either way: law sections were an authoring
12
+ * habit, never a state.
13
+ *
14
+ * That matters now because `project_thin_rules` ships a class of rules as a
15
+ * stub plus their own law section. A rule with no law section cannot be in that
16
+ * class, and a rule whose law section is 5,387 characters defeats the point of
17
+ * a stub. This gate holds three lines so the projector can rely on them.
18
+ *
19
+ * WHAT IT ASSERTS, three axes, each with its own ratchet:
20
+ *
21
+ * 1. PRESENCE (`--axis presence`). Every routed tier rule has a law section, or
22
+ * is named in the `missing` baseline. SHRINK-ONLY: an id may leave that list
23
+ * freely, and a routed rule that is not on it and has no law section fails.
24
+ * A NEW routed rule therefore cannot be born lawless.
25
+ *
26
+ * 2. LAW CEILING (`--axis ceiling`). Target 1,200 characters, REPORTED and
27
+ * never failing; hard 2,000, failing. A rule over the hard ceiling today is
28
+ * named in `law_exceptions` with an owner and a review date, and may only
29
+ * shrink — its recorded size is a ceiling, not a licence.
30
+ *
31
+ * 3. BODY CEILING (`--axis body-ceiling`). 8,000 characters of
32
+ * comment-stripped body, the unit a full runtime delivery carries, so it is
33
+ * priced in the same unit the installed-layer report uses. Today's larger
34
+ * rules are named in `body_exceptions`, shrink-only on the same terms.
35
+ *
36
+ * WHY THE TARGET IS REPORTED AND NOT ENFORCED. A rule shortened to fit a number
37
+ * is the failure this roadmap says it will not cause. 1,200 is where the
38
+ * distribution sits (median 511, p90 1,787) and a rule between target and hard
39
+ * ceiling is a rule to look at, not a rule to cut. Only 2,000 — above p90, so
40
+ * no rule is truncated by it — refuses.
41
+ *
42
+ * WHY SCOPED TO TIER RULES. "Routed" means present in `router.json`'s `tier_1`
43
+ * or `tier_2`. The nine kernel rules are excluded and the exclusion is a fact
44
+ * about this repository, not a judgement: kernel rule files are agent-write-
45
+ * denied, and two of them (`agent-authority`, `scope-control`) carry no Iron-Law
46
+ * heading today, so a gate that included them would be a gate no agent could
47
+ * ever make green. They are counted and printed under `kernel (reported)` so
48
+ * the exclusion is visible rather than silent, and `project_thin_rules` keeps
49
+ * the kernel full-bodied anyway — it never ships a kernel stub, so the stub-law
50
+ * question does not arise there.
51
+ *
52
+ * WHAT IT DOES NOT ASSERT. Not that the extracted section is the rule's real
53
+ * obligation — a heading match is structural and checkable, "this sentence is
54
+ * the obligation" is not; the `# obligation: line N` marker and
55
+ * `report_obligation_carriers` own that. Not that a rule at its recorded
56
+ * exception is correct, only that it is not worse. And not a cross-commit
57
+ * ratchet: this gate reads one tree, so "shrink-only" means the tree sits at or
58
+ * under what is committed, and a silently LOWERED baseline is a question the
59
+ * diff answers, not this gate. Saying so is the point.
60
+ *
61
+ * Exit codes: 0 clean, 1 violations, 2 usage error, 3 internal error.
62
+ */
63
+ import * as fs from 'node:fs';
64
+ import * as os from 'node:os';
65
+ import * as path from 'node:path';
66
+ import process from 'node:process';
67
+ import { fileURLToPath, pathToFileURL } from 'node:url';
68
+
69
+ import { asOf } from './_lib/as_of.js';
70
+ import { measureRuleFile, type RuleLawMeasure } from './_lib/rule_law_section.js';
71
+ import { DeadScopeError, reportScanned } from './_lib/scan_scope.js';
72
+
73
+ const _HERE = fileURLToPath(import.meta.url);
74
+ export const REPO_ROOT = path.resolve(path.dirname(_HERE), '..', '..');
75
+
76
+ export const CONFIG_REL = 'src/config/rule-law-ceilings.json';
77
+ export const RULES_REL = 'src/rules';
78
+ export const ROUTER_REL = 'dist/router.json';
79
+
80
+ /** Reported, never failing — a rule above it is a rule to look at. */
81
+ export const LAW_TARGET_CHARS = 1_200;
82
+ /** Refused. Above p90, so no rule in the distribution is truncated by it. */
83
+ export const LAW_HARD_CHARS = 2_000;
84
+ /** Refused. The unit a full runtime delivery carries. */
85
+ export const BODY_HARD_CHARS = 8_000;
86
+
87
+ export interface SizeException {
88
+ /** Recorded size at adoption. The rule may sit at or under it, never above. */
89
+ chars: number;
90
+ /** Who answers for this rule staying oversized. */
91
+ owner: string;
92
+ /** `YYYY-MM-DD` the exception is due for review. */
93
+ review: string;
94
+ /** Why this rule cannot state its law under the ceiling today. */
95
+ reason: string;
96
+ }
97
+
98
+ export interface LawConfig {
99
+ _comment?: string;
100
+ measured_at_commit?: string;
101
+ /** Routed rules with no law section. Shrink-only: ids may leave, never join. */
102
+ missing: string[];
103
+ /** Rules whose law exceeds {@link LAW_HARD_CHARS}. */
104
+ law_exceptions: Record<string, SizeException>;
105
+ /** Rules whose body exceeds {@link BODY_HARD_CHARS}. */
106
+ body_exceptions: Record<string, SizeException>;
107
+ }
108
+
109
+ export type Axis = 'presence' | 'ceiling' | 'body-ceiling' | 'all';
110
+
111
+ export interface Finding {
112
+ id: string;
113
+ axis: Exclude<Axis, 'all'>;
114
+ kind: string;
115
+ detail: string;
116
+ }
117
+
118
+ export interface LintResult {
119
+ findings: Finding[];
120
+ /** Rules at or above the soft target but under the hard ceiling. */
121
+ overTarget: Array<{ id: string; chars: number }>;
122
+ measures: RuleLawMeasure[];
123
+ kernelReported: Array<{ id: string; sections: number }>;
124
+ scanned: number;
125
+ }
126
+
127
+ /** Routed tier rule ids, sorted — `tier_1` + `tier_2`, kernel excluded. */
128
+ export function routedRuleIds(root = REPO_ROOT): string[] {
129
+ const data = JSON.parse(fs.readFileSync(path.join(root, ROUTER_REL), 'utf-8')) as Record<
130
+ string,
131
+ unknown
132
+ >;
133
+ const ids: string[] = [];
134
+ for (const tier of ['tier_1', 'tier_2']) {
135
+ const entries = data[tier];
136
+ if (!Array.isArray(entries)) continue;
137
+ for (const e of entries) ids.push(String((e as Record<string, unknown>).id));
138
+ }
139
+ return [...new Set(ids)].sort();
140
+ }
141
+
142
+ export function kernelRuleIds(root = REPO_ROOT): string[] {
143
+ const data = JSON.parse(fs.readFileSync(path.join(root, ROUTER_REL), 'utf-8')) as Record<
144
+ string,
145
+ unknown
146
+ >;
147
+ const kernel = data.kernel;
148
+ return Array.isArray(kernel) ? kernel.map((x) => String(x)).sort() : [];
149
+ }
150
+
151
+ export function readConfig(root = REPO_ROOT): LawConfig {
152
+ const p = path.join(root, CONFIG_REL);
153
+ const raw = JSON.parse(fs.readFileSync(p, 'utf-8')) as Partial<LawConfig>;
154
+ return {
155
+ missing: Array.isArray(raw.missing) ? raw.missing : [],
156
+ law_exceptions: raw.law_exceptions ?? {},
157
+ body_exceptions: raw.body_exceptions ?? {},
158
+ measured_at_commit: raw.measured_at_commit ?? '',
159
+ };
160
+ }
161
+
162
+ /** Measure every routed rule that has a file. */
163
+ export function measureRouted(root = REPO_ROOT): RuleLawMeasure[] {
164
+ const out: RuleLawMeasure[] = [];
165
+ for (const id of routedRuleIds(root)) {
166
+ const p = path.join(root, RULES_REL, `${id}.md`);
167
+ if (!fs.existsSync(p)) continue;
168
+ out.push(measureRuleFile(id, p));
169
+ }
170
+ return out;
171
+ }
172
+
173
+ export function lint(root = REPO_ROOT, axis: Axis = 'all'): LintResult {
174
+ const cfg = readConfig(root);
175
+ const measures = measureRouted(root);
176
+ const findings: Finding[] = [];
177
+ const overTarget: Array<{ id: string; chars: number }> = [];
178
+ const baselined = new Set(cfg.missing);
179
+ const want = (a: Exclude<Axis, 'all'>): boolean => axis === 'all' || axis === a;
180
+
181
+ for (const m of measures) {
182
+ if (want('presence') && m.law === null && !baselined.has(m.id)) {
183
+ findings.push({
184
+ id: m.id,
185
+ axis: 'presence',
186
+ kind: 'no-law-section',
187
+ detail:
188
+ 'routed rule has no heading matching `Iron Law` — state the obligation under ' +
189
+ `one, or add the id to \`missing\` in ${CONFIG_REL} (that list is shrink-only, ` +
190
+ 'so a new routed rule may not join it)',
191
+ });
192
+ }
193
+ if (want('ceiling') && m.law !== null) {
194
+ const exc = cfg.law_exceptions[m.id];
195
+ if (exc === undefined) {
196
+ if (m.lawChars > LAW_HARD_CHARS) {
197
+ findings.push({
198
+ id: m.id,
199
+ axis: 'ceiling',
200
+ kind: 'law-over-hard-ceiling',
201
+ detail:
202
+ `law section is ${m.lawChars} chars, over the ${LAW_HARD_CHARS} hard ceiling — ` +
203
+ 'move the discussion under it behind `load_context`, or record a dated ' +
204
+ `exception with an owner in ${CONFIG_REL}`,
205
+ });
206
+ } else if (m.lawChars > LAW_TARGET_CHARS) {
207
+ overTarget.push({ id: m.id, chars: m.lawChars });
208
+ }
209
+ } else if (m.lawChars > exc.chars) {
210
+ findings.push({
211
+ id: m.id,
212
+ axis: 'ceiling',
213
+ kind: 'law-exception-grew',
214
+ detail:
215
+ `law section is ${m.lawChars} chars, above its recorded exception of ` +
216
+ `${exc.chars} (owner ${exc.owner}, review ${exc.review}) — an exception is ` +
217
+ 'a ceiling, not a licence; it may only shrink',
218
+ });
219
+ }
220
+ }
221
+ if (want('body-ceiling')) {
222
+ const exc = cfg.body_exceptions[m.id];
223
+ if (exc === undefined) {
224
+ if (m.bodyChars > BODY_HARD_CHARS) {
225
+ findings.push({
226
+ id: m.id,
227
+ axis: 'body-ceiling',
228
+ kind: 'body-over-hard-ceiling',
229
+ detail:
230
+ `body is ${m.bodyChars} chars, over the ${BODY_HARD_CHARS} hard ceiling — ` +
231
+ 'move history and mechanism discussion into a context file the rule names ' +
232
+ `in \`load_context\`, or record a dated exception in ${CONFIG_REL}`,
233
+ });
234
+ }
235
+ } else if (m.bodyChars > exc.chars) {
236
+ findings.push({
237
+ id: m.id,
238
+ axis: 'body-ceiling',
239
+ kind: 'body-exception-grew',
240
+ detail:
241
+ `body is ${m.bodyChars} chars, above its recorded exception of ${exc.chars} ` +
242
+ `(owner ${exc.owner}, review ${exc.review}) — it may only shrink`,
243
+ });
244
+ }
245
+ }
246
+ }
247
+
248
+ // A baseline entry for a rule that now HAS a law section is debt that was
249
+ // paid and never collected. Reported as a finding, because a stale
250
+ // suppression is how a ratchet hardens into configuration.
251
+ if (want('presence')) {
252
+ const haveLaw = new Set(measures.filter((m) => m.law !== null).map((m) => m.id));
253
+ const routed = new Set(measures.map((m) => m.id));
254
+ for (const id of cfg.missing) {
255
+ if (haveLaw.has(id)) {
256
+ findings.push({
257
+ id,
258
+ axis: 'presence',
259
+ kind: 'stale-baseline-entry',
260
+ detail: `now has a law section — remove it from \`missing\` in ${CONFIG_REL}`,
261
+ });
262
+ } else if (!routed.has(id)) {
263
+ findings.push({
264
+ id,
265
+ axis: 'presence',
266
+ kind: 'stale-baseline-entry',
267
+ detail: `is not a routed rule any more — remove it from \`missing\` in ${CONFIG_REL}`,
268
+ });
269
+ }
270
+ }
271
+ }
272
+
273
+ const kernelReported = kernelRuleIds(root).map((id) => {
274
+ const p = path.join(root, RULES_REL, `${id}.md`);
275
+ return { id, sections: fs.existsSync(p) ? measureRuleFile(id, p).sections : 0 };
276
+ });
277
+
278
+ findings.sort((a, b) => (a.id < b.id ? -1 : a.id > b.id ? 1 : a.kind < b.kind ? -1 : 1));
279
+ overTarget.sort((a, b) => b.chars - a.chars);
280
+ return { findings, overTarget, measures, kernelReported, scanned: measures.length };
281
+ }
282
+
283
+ /** Rebuild the config from the current tree. Never raises an existing ceiling. */
284
+ export function writeBaseline(root = REPO_ROOT, today: string): string {
285
+ const prev = fs.existsSync(path.join(root, CONFIG_REL))
286
+ ? readConfig(root)
287
+ : { missing: [], law_exceptions: {}, body_exceptions: {} };
288
+ const measures = measureRouted(root);
289
+ const missing = measures.filter((m) => m.law === null).map((m) => m.id).sort();
290
+ const law_exceptions: Record<string, SizeException> = {};
291
+ const body_exceptions: Record<string, SizeException> = {};
292
+ for (const m of measures) {
293
+ if (m.law !== null && m.lawChars > LAW_HARD_CHARS) {
294
+ const old = prev.law_exceptions[m.id];
295
+ law_exceptions[m.id] = {
296
+ chars: old === undefined ? m.lawChars : Math.min(old.chars, m.lawChars),
297
+ owner: old?.owner ?? 'agent-config-maintainer',
298
+ review: old?.review ?? today,
299
+ reason: old?.reason ?? 'recorded at adoption; not yet reviewed',
300
+ };
301
+ }
302
+ if (m.bodyChars > BODY_HARD_CHARS) {
303
+ const old = prev.body_exceptions[m.id];
304
+ body_exceptions[m.id] = {
305
+ chars: old === undefined ? m.bodyChars : Math.min(old.chars, m.bodyChars),
306
+ owner: old?.owner ?? 'agent-config-maintainer',
307
+ review: old?.review ?? today,
308
+ reason: old?.reason ?? 'recorded at adoption; not yet reviewed',
309
+ };
310
+ }
311
+ }
312
+ const cfg = {
313
+ _comment:
314
+ 'Per-rule law-section and body ceilings (road-to-rule-laws-that-can-stand Phase 1). ' +
315
+ 'SHRINK-ONLY on every axis: an id may LEAVE `missing`, never join it; a recorded ' +
316
+ 'exception is a ceiling the rule may fall below and never rise above. Raising one to ' +
317
+ 'clear a red is the config-weakening move this repo blocks by construction — ' +
318
+ '`--write-baseline` refuses to raise, it only re-records the smaller of old and ' +
319
+ 'current. A rule at its exception is not thereby correct, only not worse. Regenerate ' +
320
+ 'with `lint_rule_law_section --write-baseline`; never hand-edit the counts, but DO ' +
321
+ 'hand-edit `owner`, `review` and `reason` — those are the review contract and the ' +
322
+ 'generator only seeds them.',
323
+ measured_at_commit: prev.measured_at_commit ?? '',
324
+ missing,
325
+ law_exceptions,
326
+ body_exceptions,
327
+ };
328
+ const text = JSON.stringify(cfg, null, 4) + '\n';
329
+ fs.writeFileSync(path.join(root, CONFIG_REL), text, 'utf-8');
330
+ return text;
331
+ }
332
+
333
+ interface Args {
334
+ axis: Axis;
335
+ quiet: boolean;
336
+ writeBaseline: boolean;
337
+ root: string;
338
+ selfTest: boolean;
339
+ }
340
+
341
+ const USAGE =
342
+ 'usage: lint_rule_law_section [--axis presence|ceiling|body-ceiling|all] [--quiet] ' +
343
+ '[--write-baseline] [--root DIR] [--self-test]\n';
344
+
345
+ function parseArgs(argv: string[]): Args {
346
+ const out: Args = { axis: 'all', quiet: false, writeBaseline: false, root: REPO_ROOT, selfTest: false };
347
+ for (let i = 0; i < argv.length; i += 1) {
348
+ const a = argv[i] as string;
349
+ if (a === '-h' || a === '--help') {
350
+ process.stdout.write(USAGE);
351
+ process.exit(0);
352
+ } else if (a === '--quiet') {
353
+ out.quiet = true;
354
+ } else if (a === '--write-baseline') {
355
+ out.writeBaseline = true;
356
+ } else if (a === '--self-test') {
357
+ out.selfTest = true;
358
+ } else if (a === '--axis') {
359
+ const v = argv[i + 1];
360
+ if (v === undefined) {
361
+ process.stderr.write(USAGE);
362
+ process.exit(2);
363
+ }
364
+ out.axis = v as Axis;
365
+ i += 1;
366
+ } else if (a.startsWith('--axis=')) {
367
+ out.axis = a.slice('--axis='.length) as Axis;
368
+ } else if (a === '--root') {
369
+ const v = argv[i + 1];
370
+ if (v === undefined) {
371
+ process.stderr.write(USAGE);
372
+ process.exit(2);
373
+ }
374
+ out.root = path.resolve(v);
375
+ i += 1;
376
+ } else if (a.startsWith('--root=')) {
377
+ out.root = path.resolve(a.slice('--root='.length));
378
+ } else {
379
+ process.stderr.write(USAGE);
380
+ process.stderr.write(`lint_rule_law_section: error: unrecognized argument: ${a}\n`);
381
+ process.exit(2);
382
+ }
383
+ }
384
+ if (!['presence', 'ceiling', 'body-ceiling', 'all'].includes(out.axis)) {
385
+ process.stderr.write(USAGE);
386
+ process.stderr.write(`lint_rule_law_section: error: unknown axis: ${out.axis}\n`);
387
+ process.exit(2);
388
+ }
389
+ return out;
390
+ }
391
+
392
+ /**
393
+ * Prove the gate's reading changes its verdict, not just that it read
394
+ * something. A `scanned:` floor says a corpus was inspected; only a rejecting
395
+ * case says the inspection decides anything.
396
+ */
397
+ export function selfTest(write: (s: string) => void = (s) => process.stdout.write(s)): number {
398
+ const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'law-selftest-'));
399
+ try {
400
+ fs.mkdirSync(path.join(tmp, 'src', 'rules'), { recursive: true });
401
+ fs.mkdirSync(path.join(tmp, 'src', 'config'), { recursive: true });
402
+ fs.mkdirSync(path.join(tmp, 'dist'), { recursive: true });
403
+ fs.writeFileSync(
404
+ path.join(tmp, ROUTER_REL),
405
+ JSON.stringify({ kernel: [], tier_1: [{ id: 'lawless' }, { id: 'lawful' }], tier_2: [] }),
406
+ );
407
+ fs.writeFileSync(
408
+ path.join(tmp, CONFIG_REL),
409
+ JSON.stringify({ missing: [], law_exceptions: {}, body_exceptions: {} }),
410
+ );
411
+ fs.writeFileSync(path.join(tmp, 'src/rules/lawless.md'), '---\nx: 1\n---\n\n# Lawless\n\nProse.\n');
412
+ fs.writeFileSync(
413
+ path.join(tmp, 'src/rules/lawful.md'),
414
+ '---\nx: 1\n---\n\n# Lawful\n\n## The Iron Law\n\nNEVER DO THE THING.\n',
415
+ );
416
+
417
+ const rejecting = lint(tmp, 'presence');
418
+ const accepting = (() => {
419
+ fs.writeFileSync(
420
+ path.join(tmp, CONFIG_REL),
421
+ JSON.stringify({ missing: ['lawless'], law_exceptions: {}, body_exceptions: {} }),
422
+ );
423
+ return lint(tmp, 'presence');
424
+ })();
425
+ const ok =
426
+ rejecting.findings.length === 1 &&
427
+ rejecting.findings[0]?.kind === 'no-law-section' &&
428
+ accepting.findings.length === 0;
429
+ write(
430
+ ok
431
+ ? '✅ self-test: 1 rejecting case, 1 accepting case — the reading decides the verdict\n'
432
+ : `❌ self-test FAILED: rejecting=${rejecting.findings.length} accepting=${accepting.findings.length}\n`,
433
+ );
434
+ return ok ? 0 : 1;
435
+ } finally {
436
+ fs.rmSync(tmp, { recursive: true, force: true });
437
+ }
438
+ }
439
+
440
+ export function main(argv: string[] | null = null): number {
441
+ const args = parseArgs(argv ?? process.argv.slice(2));
442
+ if (args.selfTest) {
443
+ return selfTest();
444
+ }
445
+ if (args.writeBaseline) {
446
+ const today = asOf().toISOString().slice(0, 10);
447
+ writeBaseline(args.root, today);
448
+ process.stdout.write(`wrote ${CONFIG_REL}\n`);
449
+ return 0;
450
+ }
451
+
452
+ let res: LintResult;
453
+ try {
454
+ res = lint(args.root, args.axis);
455
+ } catch (exc) {
456
+ process.stderr.write(`❌ lint_rule_law_section: ${(exc as Error).message}\n`);
457
+ return 3;
458
+ }
459
+
460
+ // Outside the `--quiet` guard on purpose: CI passes `--quiet`, and a gate
461
+ // whose count disappears under the flag reads as silent to the coverage
462
+ // census.
463
+ try {
464
+ reportScanned({
465
+ gate: 'lint_rule_law_section',
466
+ scanned: res.scanned,
467
+ units: 'routed rule(s)',
468
+ roots: [RULES_REL, ROUTER_REL],
469
+ });
470
+ } catch (exc) {
471
+ if (exc instanceof DeadScopeError) {
472
+ process.stderr.write(`❌ ${exc.message}\n`);
473
+ return 1;
474
+ }
475
+ throw exc;
476
+ }
477
+
478
+ if (!args.quiet) {
479
+ const withLaw = res.measures.filter((m) => m.law !== null).length;
480
+ process.stdout.write(
481
+ `law sections: ${withLaw}/${res.measures.length} routed rules; ` +
482
+ `kernel reported separately (${res.kernelReported.filter((k) => k.sections > 0).length}/` +
483
+ `${res.kernelReported.length} carry one, write-denied so never failed here)\n`,
484
+ );
485
+ for (const t of res.overTarget) {
486
+ process.stdout.write(
487
+ ` over target (${LAW_TARGET_CHARS}), not a failure: ${t.id} — ${t.chars} chars\n`,
488
+ );
489
+ }
490
+ }
491
+
492
+ if (res.findings.length > 0) {
493
+ for (const f of res.findings) {
494
+ process.stderr.write(`❌ ${f.id} [${f.axis}/${f.kind}] ${f.detail}\n`);
495
+ }
496
+ process.stderr.write(`❌ lint_rule_law_section: ${res.findings.length} finding(s)\n`);
497
+ return 1;
498
+ }
499
+ if (!args.quiet) {
500
+ process.stdout.write('✅ lint_rule_law_section clean\n');
501
+ }
502
+ return 0;
503
+ }
504
+
505
+ function isCliEntry(): boolean {
506
+ if (process.argv[1] === undefined) return false;
507
+ if (import.meta.url === pathToFileURL(path.resolve(process.argv[1])).href) return true;
508
+ try {
509
+ return fs.realpathSync(fileURLToPath(import.meta.url)) === fs.realpathSync(path.resolve(process.argv[1]));
510
+ } catch {
511
+ return false;
512
+ }
513
+ }
514
+
515
+ if (isCliEntry()) {
516
+ process.exit(main());
517
+ }