@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
@@ -18,11 +18,10 @@
18
18
  * target dir of your choosing — it never overwrites the live `.claude/` /
19
19
  * `.augment/` projections.
20
20
  *
21
- * NOTE (faithful literal): the thin-entry pointer keeps the verbatim
22
- * `Body: [`{rule_id}`](../../dist/agent-src/rules/{rule_id}.md)`
23
- * Markdown link from the retired Python implementation. The same-basename `.py` twin
24
- * carries that literal, so the ADR-051 legacy-path guard exempts this `.ts`
25
- * file by twin-parity.
21
+ * NOTE: the thin-entry pointer literal moved with the mechanism — it is
22
+ * `BODY_LINK_PREFIX` in `_lib/thin_rules.ts` now, so the twin-parity argument
23
+ * that exempted it from the ADR-051 legacy-path guard belongs to THAT file and
24
+ * no longer describes this one, which holds the string nowhere.
26
25
  *
27
26
  * Historical quirks are preserved deliberately — tests and downstream consumers pin the exact behaviour.
28
27
  *
@@ -34,299 +33,20 @@ import * as fs from 'node:fs';
34
33
  import * as path from 'node:path';
35
34
  import process from 'node:process';
36
35
  import { fileURLToPath, pathToFileURL } from 'node:url';
37
- import { pathOnlyRuleIds, triggerlessRuleIds, } from './_lib/rule_injection.js';
38
36
  import * as token_count from './_lib/token_count.js';
39
- const _HERE = fileURLToPath(import.meta.url);
40
- // REPO_ROOT = Path(__file__).resolve().parent.parent.parent — three dirs up
41
- // from src/scripts/<file>.
42
- export const REPO_ROOT = path.resolve(path.dirname(_HERE), '..', '..');
43
- export const RULES_SOURCE = path.join(REPO_ROOT, 'dist/agent-src', 'rules');
44
- export const ROUTER = path.join(REPO_ROOT, 'dist', 'router.json');
45
- /** `sorted(dir.glob("*.md"))` — non-recursive, lexically sorted abs paths. */
46
- function _globSortedMd(dir) {
47
- let names;
48
- try {
49
- names = fs.readdirSync(dir);
50
- }
51
- catch {
52
- return [];
53
- }
54
- const out = names.filter((n) => n.endsWith('.md')).map((n) => path.join(dir, n));
55
- out.sort();
56
- return out;
57
- }
58
- /** The always-full-bodied set — authoritative kernel list from the router. */
59
- export function kernel_ids() {
60
- const data = JSON.parse(fs.readFileSync(ROUTER, 'utf-8'));
61
- const kernel = data.kernel;
62
- if (Array.isArray(kernel)) {
63
- return new Set(kernel.map((x) => String(x)));
64
- }
65
- return new Set();
66
- }
67
- /**
68
- * Tier rules the router gives NO trigger at all.
69
- *
70
- * These cannot be delivered on a match, so thinning them would remove a body
71
- * that nothing can ever put back — a silent hole rather than a saving. They
72
- * project full-bodied, and `measure` reports them by name so the residue is
73
- * visible instead of being absorbed into the pointer count
74
- * (road-to-trigger-delivered-rule-bodies 1.3).
75
- */
76
- /**
77
- * Rules whose ONLY triggers are path-shaped — reachable from `pre_tool_use`
78
- * and from no other slot (road-to-delivery-for-every-host 3.2).
79
- *
80
- * They must never be thinned while the delivery concern is NOT bound on
81
- * `pre_tool_use`, because a stub plus a slot nobody listens on is a rule that
82
- * reaches the model at no scope at all. This is the same failure the
83
- * `no_trigger_ids` exemption prevents, one step out: there the rule has nothing
84
- * to match on, here it has nothing to match ON.
85
- *
86
- * Stated as a PROPERTY rather than a list of ids. If the concern is ever bound
87
- * on `pre_tool_use` again, this exemption is what should be reconsidered — not
88
- * three names someone has to remember.
89
- *
90
- * WHAT THIS EXEMPTION DOES **NOT** COVER — recorded 2026-09-08, R2 finding 1,
91
- * because the silence read as coverage. The predicate is `every` trigger
92
- * path-shaped. A rule with BOTH path and non-path triggers is therefore THINNED,
93
- * and its path-shaped half then has no carrier at all: the stub carries no
94
- * frontmatter (`condense.ts` writes it with `_writeText`, bypassing
95
- * `_emit_claude_rule`'s host-native `paths:` key), `pre_tool_use` lost the
96
- * binding under owner ruling E2, and `user_prompt_submit` never populates
97
- * `openFiles`. Measured over the current `dist/router.json`: **18 such rules**,
98
- * all non-kernel and all thinned — augment-edit-discipline, design-fidelity,
99
- * doc-screenshot-hygiene, domain-adoption-policy,
100
- * framework-neutrality-in-generic-skills, image-likeness-and-rights,
101
- * laravel-translations, lethal-trifecta-guard, linked-projects-onboarding-gate,
102
- * low-impact-corpus-privacy-floor, markdown-safe-codeblocks, onboarding-gate,
103
- * persona-governance, php-coding, provider-lifecycle-discipline,
104
- * roadmap-ci-steps-policy, roadmap-progress-sync, settings-ask-protocol.
105
- *
106
- * They do NOT reach the model at no scope: every non-path route
107
- * (keyword / phrase / command) still delivers the body through the concern, and
108
- * on `claude-code` they never had a separate host-native path route to lose —
109
- * `_claude_paths_plan` emits no `paths:` for a mixed-trigger rule on purpose, so
110
- * under `eager-all` they loaded UNCONDITIONALLY. What is lost is the difference
111
- * between unconditional and prompt-triggered: a session that touches a matching
112
- * file and says nothing that matches gets nothing.
113
- *
114
- * The labelled corpus cannot see this and the endpoint says so rather than
115
- * implying otherwise — `model_rule_injection --endpoints` (b) now publishes the
116
- * shipped `user_prompt_submit` reach beside the pre-registered reading, and
117
- * every one of these 18 has at least one prompt-matching positive, so none
118
- * appears in its "reachable only via a path trigger" column.
119
- *
120
- * WHY THIS IS NOT WIDENED TO `some` HERE. Measured: exempting all 18 moves the
121
- * thin rule layer 23,592 -> 39,921 GPT tok (+16,329, +69 %), which is a budget
122
- * move of a magnitude no agent may take, and it would erase most of the saving
123
- * the flip is licensed on. Restoring a path route needs either that exemption
124
- * under a re-anchored baseline or a `pre_tool_use` binding under a raised slot
125
- * cap — both owner-reserved, so the closure is tracked rather than taken here.
126
- * ADR-267's Consequences section names the receiver.
127
- */
128
- /**
129
- * ONE spelling of the router-derived sets, shared with the injector (R2
130
- * finding 11).
131
- *
132
- * `path_only_ids` and `no_trigger_ids` were each a second, hand-rolled
133
- * implementation of `_lib/rule_injection.ts::pathOnlyRuleIds` /
134
- * `triggerlessRuleIds` — verified identical over the current router at the time
135
- * (symmetric difference empty), so drift risk rather than a live defect. It is
136
- * exactly the hazard `THIN_ENTRY_MARKER`'s own comment argues against one
137
- * screen below ("a gate that re-spelled this string would drift from the writer
138
- * silently"), and the two consumers here are the PROJECTOR's exemption set and
139
- * the INJECTOR's reachability set: they must not be able to disagree about which
140
- * rules the delivery path can reach.
141
- *
142
- * The finding named `path_only_ids`. The defect-pattern sweep found the SECOND
143
- * instance one function down — `no_trigger_ids` against `triggerlessRuleIds` —
144
- * and it is delegated in the same change. Count: 2 of 2 duplicated
145
- * router-derived sets in this file, 0 remaining.
146
- */
147
- function _router() {
148
- return JSON.parse(fs.readFileSync(ROUTER, 'utf-8'));
149
- }
150
- export function path_only_ids() {
151
- return pathOnlyRuleIds(_router());
152
- }
153
- export function no_trigger_ids() {
154
- return new Set(triggerlessRuleIds(_router()));
155
- }
156
- /**
157
- * id → workspaces for every non-kernel router entry (router.json schema v2,
158
- * road-to-request-scoped-rule-load Phase 1). Rules absent from the map (or
159
- * with an empty list) fail safe: they stay in scope.
160
- */
161
- export function rule_workspaces_map() {
162
- const data = JSON.parse(fs.readFileSync(ROUTER, 'utf-8'));
163
- const map = new Map();
164
- for (const tier of ['tier_1', 'tier_2']) {
165
- const entries = data[tier];
166
- if (!Array.isArray(entries))
167
- continue;
168
- for (const e of entries) {
169
- const obj = e;
170
- const ws = Array.isArray(obj.workspaces) ? obj.workspaces.map((w) => String(w)) : [];
171
- map.set(String(obj.id), ws);
172
- }
173
- }
174
- return map;
175
- }
176
- /**
177
- * Whether a rule id survives a workspace scope. Kernel always survives;
178
- * `scope === null` = legacy-all. An out-of-scope non-kernel rule is dropped
179
- * entirely — neither body nor pointer line (the pointer floor shrinks with
180
- * consumer scoping).
181
- */
182
- export function id_in_scope(rule_id, scope, kernel, wsMap, fallback_ws = []) {
183
- if (scope === null || kernel.has(rule_id)) {
184
- return true;
185
- }
186
- // Router map first (schema v2); `type: manual` rules are reference-only
187
- // and never emitted to the router — their frontmatter is the fallback.
188
- const ws = wsMap.get(rule_id) ?? fallback_ws;
189
- if (ws.length === 0) {
190
- return true; // untagged / unknown → fail safe
191
- }
192
- return ws.some((w) => scope.includes(w));
193
- }
194
- /** `workspaces:` list parsed straight from a rule file's frontmatter. */
195
- export function fm_workspaces(text) {
196
- const [fm] = split_frontmatter(text);
197
- // Flow style: `workspaces: [a, b, c]`
198
- const flow = /^workspaces:[ \t]*\[([^\]]*)\]/m.exec(fm);
199
- if (flow) {
200
- return flow[1]
201
- .split(',')
202
- .map((s) => s.trim().replace(/^["']|["']$/g, ''))
203
- .filter((s) => s.length > 0);
204
- }
205
- // Block style; tolerate the list being the LAST frontmatter key.
206
- const m = /^workspaces:[ \t]*\n((?:[ \t]+-[ \t]+.*(?:\n|$))+)/m.exec(fm);
207
- if (!m) {
208
- return [];
209
- }
210
- return [...m[1].matchAll(/-[ \t]+(\S+)/g)].map((x) => x[1]);
211
- }
212
- /** Return [frontmatter_including_fences, body]. Empty fm if none. */
213
- export function split_frontmatter(text) {
214
- if (text.startsWith('---\n')) {
215
- const end = text.indexOf('\n---\n', 4);
216
- if (end !== -1) {
217
- return [text.slice(0, end + 5), text.slice(end + 5)];
218
- }
219
- }
220
- return ['', text];
221
- }
222
- function _description(fm) {
223
- // re.search(r'^description:\s*"?(.+?)"?\s*$', fm, re.MULTILINE)
224
- const m = /^description:\s*"?(.+?)"?\s*$/m.exec(fm);
225
- return m ? m[1].trim() : '';
226
- }
227
- // How many trigger keywords/phrases to surface as the always-on match hint.
228
- const _TRIGGER_HINT_LIMIT = 6;
229
- /** A short, comma-joined sample of the rule's trigger keywords/phrases. */
230
- function _trigger_hint(fm) {
231
- const hits = [];
232
- // re.finditer(r'^\s*-\s*(?:keyword|phrase):\s*"?(.+?)"?\s*$', fm, re.MULTILINE)
233
- const re = /^\s*-\s*(?:keyword|phrase):\s*"?(.+?)"?\s*$/gm;
234
- let m;
235
- while ((m = re.exec(fm)) !== null) {
236
- hits.push(m[1].trim());
237
- if (hits.length >= _TRIGGER_HINT_LIMIT) {
238
- break;
239
- }
240
- }
241
- return hits.join(', ');
242
- }
243
- /** Python `str.title()` — uppercase first alpha of each run, rest lowercase. */
244
- function _title(s) {
245
- return s.replace(/[A-Za-z]+/g, (w) => w.charAt(0).toUpperCase() + w.slice(1).toLowerCase());
246
- }
247
- /**
248
- * The marker that makes a projected entry recognisable as a stub.
249
- *
250
- * Exported so the WRITER and every DETECTOR share one definition. A gate that
251
- * re-spelled this string would drift from the writer silently, and the failure
252
- * would be invisible in exactly the direction that matters: a stub the gate
253
- * fails to recognise reads as a complete rule body.
254
- */
255
- export const THIN_ENTRY_MARKER = '> Routed rule — load the body on trigger-match.';
256
- /** Is this projected entry a pointer stub rather than a rule body? */
257
- export function is_thin_entry(text) {
258
- return text.includes(THIN_ENTRY_MARKER);
259
- }
260
37
  /**
261
- * Where a stub tells a reader the body actually is.
262
- *
263
- * FIXED 2026-09-07: this pointed into the uncondensed source tree ADR-051
264
- * retired — the literal path is deliberately NOT reproduced here, because
265
- * `check_no_uncondensed_refs` matches the string and cannot tell a repair
266
- * record from a live reference; `git log -S` on this file finds the old value.
267
- * It exists in no checkout, so EVERY stub's fallback pointer
268
- * resolved to nothing — invisible under `delivery`, where the hook loads the
269
- * body from `dist/agent-src/rules` and never follows this link, and total under
270
- * `thin`, where the pointer is the only path to the body there is.
38
+ * The mechanism lives in `_lib/thin_rules.ts`; this file is its CLI.
271
39
  *
272
- * KNOWN LIMIT, stated rather than discovered later: one relative prefix cannot
273
- * be right for every tree, because they sit at different depths — `.claude/rules`
274
- * and `.cursor/rules` are two levels below the repo root and `.clinerules` is
275
- * one. `../../` is correct for the first two, which are the only trees the
276
- * shipped `lean_projection.hosts` default can thin. A stub in `.clinerules`
277
- * needs an operator to have added `cline` to that list, and its pointer is off
278
- * by one level. That is strictly better than the previous state, where the
279
- * pointer was dead in all three, and it is recorded here so the remaining case
280
- * is a known limit rather than a surprise.
40
+ * Re-exported wholesale so every existing importer of
41
+ * `project_thin_rules.{build_thin,thin_entry,is_thin_entry,RULES_SOURCE,…}`
42
+ * keeps working unchanged. The split exists because `src/scripts/install.ts` is
43
+ * a bundle entry and `check_installer_import_purity` forbids it reaching a
44
+ * module-level `process.exit()` — which this file has, at its foot, by design.
281
45
  */
282
- const BODY_LINK_PREFIX = '../../dist/agent-src/rules/';
283
- /** Build the minimal progressive-disclosure pointer for a non-kernel rule. */
284
- export function thin_entry(rule_id, text) {
285
- const [fm] = split_frontmatter(text);
286
- const desc = _description(fm);
287
- const hint = _trigger_hint(fm);
288
- const title = _title(rule_id.replace(/-/g, ' '));
289
- const fires = hint ? ` Fires on: ${hint}.` : '';
290
- return (`## ${title}\n` +
291
- `${THIN_ENTRY_MARKER}${fires} ${desc} ` +
292
- `Body: [\`${rule_id}\`](${BODY_LINK_PREFIX}${rule_id}.md)\n`);
293
- }
294
- /** Map {filename: thin_or_full_text} for every rule. Kernel stays full. */
295
- export function build_thin(rules_dir = RULES_SOURCE, scope = null,
296
- /**
297
- * Optional sink for the D3 diagnostic
298
- * (road-to-delivery-for-every-host 2.2).
299
- *
300
- * An `auto` rule the router gives NO trigger is already kept full-bodied by
301
- * `noTrigger` below, which is the substantive protection and predates this
302
- * roadmap. What did not exist is any way to SEE it happen: the rule is
303
- * silently exempted, so an author who removes a rule's last trigger gets a
304
- * silently eager rule and no signal. The line makes the exemption audible.
305
- */
306
- announce = null) {
307
- const kernel = kernel_ids();
308
- const noTrigger = no_trigger_ids();
309
- const pathOnly = path_only_ids();
310
- const wsMap = scope !== null ? rule_workspaces_map() : new Map();
311
- const out = new Map();
312
- for (const p of _globSortedMd(rules_dir)) {
313
- const text = fs.readFileSync(p, 'utf-8');
314
- const stem = path.basename(p).replace(/\.md$/, '');
315
- if (!id_in_scope(stem, scope, kernel, wsMap, fm_workspaces(text))) {
316
- continue; // out of workspace scope — no body, no pointer line
317
- }
318
- const full = kernel.has(stem) || noTrigger.has(stem) || pathOnly.has(stem);
319
- if (announce !== null && noTrigger.has(stem) && !kernel.has(stem)) {
320
- announce(`D3: trigger-less auto rule ${path.basename(p)} — kept full-bodied, never thinned`);
321
- }
322
- if (announce !== null && pathOnly.has(stem) && !kernel.has(stem) && !noTrigger.has(stem)) {
323
- announce(`E2: path-only auto rule ${path.basename(p)} — kept full-bodied; its only triggers ` +
324
- `are path-shaped and the delivery concern is not bound on pre_tool_use`);
325
- }
326
- out.set(path.basename(p), full ? text : thin_entry(stem, text));
327
- }
328
- return out;
329
- }
46
+ export * from './_lib/thin_rules.js';
47
+ import { _globSortedMd, build_thin, fm_workspaces, id_in_scope, kernel_ids, no_trigger_ids, REPO_ROOT, RULES_SOURCE, rule_workspaces_map, } from './_lib/thin_rules.js';
48
+ void REPO_ROOT;
49
+ const _HERE = fileURLToPath(import.meta.url);
330
50
  /** Python `round(x, 1)` — round-half-to-even at one decimal. */
331
51
  function _python_round1(x) {
332
52
  const scaled = x * 10;
@@ -190,6 +190,9 @@ export const settingsSchema = z.object({
190
190
  fidelity_mode: 'strict',
191
191
  approximation: { enabled: false, tolerance: { color: null, length: null } },
192
192
  }),
193
+ code_graph: z.object({
194
+ consumer_index_paths: z.array(z.string()).default([]).describe("Extra project-relative paths where THIS project keeps a code-graph index another tool wrote. The built-in list is deliberately vendor-neutral (graph.json, code-graph.json, .code-graph/graph.json) and names no tool, so a tool that writes its index elsewhere is named here rather than waited for. A graph found this way LOADS and answers query and explain with every edge tagged read-from-elsewhere; the gate verbs (impact, untested, dead) refuse a graph made only of such edges and say so, because an empty answer from a gate must not read as 'nothing found' when it means 'nothing trusted'. Absolute paths and paths escaping the project root are ignored."),
195
+ }).default({}),
193
196
  consistency: z.object({
194
197
  cross_source: crossSourceMode.default('on').describe('Consumed by the cross-source-consistency rule. When the agent works from multiple sources (ticket text, an attached image/mockup, the spec, the codebase) it checks them against each other and asks before proceeding on a discrepancy — instead of silently guessing. on (default) = surface every real cross-source contradiction / silent-scope-expansion as one question; auto = surface only high-confidence contradictions, state low-confidence as an assumption; off = no cross-source checking.'),
195
198
  }).default({ cross_source: 'on' }),
@@ -319,6 +322,9 @@ export const settingsSchema = z.object({
319
322
  tier1_concerns: z.array(z.string()).default([]).describe('Concern IDs (one per line) that are allowed to block the run on failure rather than warn. Reserved for high-confidence guards — leave empty unless you maintain custom hooks.'),
320
323
  hard_fail: z.boolean().default(false).describe('When a hook exceeds hooks.concern_budget.max_per_event, fail the run (true) instead of warning and continuing (false, default). Turn on in CI when you want hook quality to gate merges.'),
321
324
  }),
325
+ verify_before_complete: z.object({
326
+ touched_file_quality: z.enum(['off', 'shadow', 'warn']).default('off').describe("Stop-slot pass that runs the project's OWN quality commands — the ones resolve_toolchain() already reports — over the files this turn edited. Default off. `shadow` records a per-run result and emits nothing; `warn` adds ONE advisory line of at most 200 bytes. Never blocks and never counts as verification: a command recorded here never reaches _lib/verification_command.ts, so a clean eslint run cannot let a turn claim it verified anything. A writing command runs only in its check form (pint --test) or not at all, and a command with no per-file form is skipped rather than run project-wide at every stop."),
327
+ }).default({}),
322
328
  injection_scan: z.object({
323
329
  enabled: z.boolean().default(false).describe('PostToolUse prompt-injection scanner (road-to-security-pillar.md P3.2). Default off. When on, scans tool output (file reads, web fetches, MCP responses) for injection signatures and WARNS in context (never blocks). Runtime backstop on top of the always-on untrusted-input-defense rule; detection is probabilistic.'),
324
330
  }).default({}),
@@ -24,10 +24,13 @@
24
24
  * `score_skill_relevance` CLI's argparse-parity suite is the check that the move
25
25
  * changed nothing.
26
26
  *
27
- * THE ONE ADDITION is `triggerTerms`, off by default. Phase 3.1 of the same
28
- * roadmap indexes `triggers.keyword` / `triggers.phrase` text; it is an opt-in
29
- * parameter rather than a second function so the two rankers can be measured
30
- * against each other on one corpus.
27
+ * THE ADDITIONS ARE ALL OFF BY DEFAULT and live in one {@link RankOptions}
28
+ * object: three that widen the indexed TERM SOURCE (trigger prose, the skill's
29
+ * own `## When to use` section, the body's headings) and one that changes the
30
+ * WEIGHTING (inverse document frequency over the catalogue). They are opt-in
31
+ * parameters rather than separate functions so two configurations can be
32
+ * measured against each other on one corpus without becoming two rankers. With
33
+ * every flag off the arithmetic is the ported one, unchanged.
31
34
  *
32
35
  * Pure — no imports, no I/O, no clock.
33
36
  */
@@ -113,23 +116,78 @@ export function skillTerms(skill, opts = {}) {
113
116
  const parts = [skill.name, skill.description];
114
117
  if (opts.includeTriggers && skill.triggerText)
115
118
  parts.push(...skill.triggerText);
119
+ if (opts.includeWhenToUse && skill.whenToUseText)
120
+ parts.push(skill.whenToUseText);
121
+ if (opts.includeHeadings && skill.headingText)
122
+ parts.push(...skill.headingText);
116
123
  return tokenize(parts.join(' '));
117
124
  }
125
+ /** True when `opts` turns on at least one term source beyond name + description. */
126
+ export function widensTermSource(opts) {
127
+ return Boolean(opts.includeTriggers || opts.includeWhenToUse || opts.includeHeadings);
128
+ }
129
+ /**
130
+ * @param termSets the per-skill term sets, in the same order as `skills`.
131
+ * Required rather than derived, so the catalogue is tokenized ONCE per call
132
+ * instead of once here and again at the scoring loop. Tokenizing ~300 skills
133
+ * twice per prompt is invisible next to re-reading them from disk, which is
134
+ * exactly why it would have stayed invisible.
135
+ */
136
+ export function buildTermStats(skills, termSets) {
137
+ const df = new Map();
138
+ for (const terms of termSets) {
139
+ for (const t of terms)
140
+ df.set(t, (df.get(t) ?? 0) + 1);
141
+ }
142
+ return { df, skills: skills.length };
143
+ }
144
+ /**
145
+ * The weight one matched term carries.
146
+ *
147
+ * `ln(1 + skills / (1 + df))`, the smoothed textbook form: a term on every
148
+ * skill tends to zero, a term on one skill carries the most. Unknown terms are
149
+ * treated as df 0 — a task term no skill indexes cannot be matched anyway, so
150
+ * its weight only affects the denominator, where counting it fully is right:
151
+ * failing to cover a rare term should cost more than failing to cover a common
152
+ * one.
153
+ */
154
+ export function idf(term, stats) {
155
+ return Math.log(1 + stats.skills / (1 + (stats.df.get(term) ?? 0)));
156
+ }
118
157
  /**
119
158
  * The score. `taskTerms` is `tokenize(task)`; `terms` is `skillTerms(skill)`.
120
159
  *
121
160
  * Both are passed in rather than derived here so a caller ranking N skills
122
161
  * tokenizes the task once and each skill once, which is what makes this usable
123
- * on a per-prompt hook path.
162
+ * on a per-prompt hook path. `opts` and `stats` are optional and, with them
163
+ * absent or every flag off, the arithmetic is exactly the Python-parity one.
124
164
  */
125
- export function scoreSkill(taskTerms, skill, terms) {
165
+ export function scoreSkill(taskTerms, skill, terms, opts = {}, stats) {
166
+ if (opts.idfWeighting && !stats) {
167
+ throw new Error('scoreSkill: idfWeighting is set but no TermStats was passed — scoring unweighted here ' +
168
+ 'would publish a baseline number under the idf label. Build stats with buildTermStats.');
169
+ }
126
170
  if (taskTerms.size === 0)
127
171
  return 0;
128
- let inter = 0;
129
- for (const t of taskTerms)
130
- if (terms.has(t))
131
- inter++;
132
- const overlap = inter / Math.max(taskTerms.size, 1);
172
+ let overlap;
173
+ if (opts.idfWeighting && stats) {
174
+ let hit = 0;
175
+ let total = 0;
176
+ for (const t of taskTerms) {
177
+ const w = idf(t, stats);
178
+ total += w;
179
+ if (terms.has(t))
180
+ hit += w;
181
+ }
182
+ overlap = total > 0 ? hit / total : 0;
183
+ }
184
+ else {
185
+ let inter = 0;
186
+ for (const t of taskTerms)
187
+ if (terms.has(t))
188
+ inter++;
189
+ overlap = inter / Math.max(taskTerms.size, 1);
190
+ }
133
191
  let personaHit = 0;
134
192
  const taskLower = [...taskTerms].join(' ');
135
193
  for (const persona of skill.personas ?? []) {
@@ -147,9 +205,12 @@ export function scoreSkill(taskTerms, skill, terms) {
147
205
  */
148
206
  export function rankSkills(task, skills, opts = {}) {
149
207
  const taskTerms = tokenize(task);
208
+ const termSets = skills.map((s) => skillTerms(s, opts));
209
+ const stats = opts.idfWeighting ? buildTermStats(skills, termSets) : undefined;
150
210
  const rows = [];
151
- for (const skill of skills) {
152
- const score = scoreSkill(taskTerms, skill, skillTerms(skill, opts));
211
+ for (let i = 0; i < skills.length; i += 1) {
212
+ const skill = skills[i];
213
+ const score = scoreSkill(taskTerms, skill, termSets[i], opts, stats);
153
214
  if (score > 0)
154
215
  rows.push({ name: skill.name, score, personas: [...(skill.personas ?? [])] });
155
216
  }
@@ -247,8 +247,8 @@ dispatch runs, not WHETHER the layer exists, so they keep their own C rows.
247
247
  |---|---|
248
248
  | A — preference | 26 |
249
249
  | B — consent | 3 |
250
- | C — guarded | 124 |
251
- | **Total** | **153** |
250
+ | C — guarded | 126 |
251
+ | **Total** | **155** |
252
252
 
253
253
  It rose again on 2026-09-13 when `road-to-adversarial-verification-and-long-runs`
254
254
  Phase 0 added three C keys: `quality.local_auto_run_in_mission` (`consent` — it
@@ -381,10 +381,10 @@ the template, which is the drift this contract exists to prevent.
381
381
  | Disposition | Keys |
382
382
  |---|---|
383
383
  | derivable | 83 |
384
- | un-inferrable | 9 |
385
- | consent | 47 |
384
+ | un-inferrable | 10 |
385
+ | consent | 48 |
386
386
  | policy | 14 |
387
- | **Total** | **153** |
387
+ | **Total** | **155** |
388
388
 
389
389
  First measured 2026-08-12 at 140 leaves (derivable 88 · consent 38 ·
390
390
  un-inferrable 9 · policy 5), from the table below rather than predicted — the
@@ -562,6 +562,7 @@ Rows follow template order, so a diff against the template reads straight down.
562
562
  | `design.approximation.tolerance.color` | C | `null` | decides which colour deviations may be taken silently | policy |
563
563
  | `design.approximation.tolerance.length` | C | `null` | decides which length deviations may be taken silently | policy |
564
564
  | `consistency.cross_source` | C | `"on"` | disables the cross-source discrepancy gate | derivable — the rule's own trigger condition; a discrepancy exists only when two present sources contradict |
565
+ | `code_graph.consumer_index_paths` | C | `[]` | names where this project's OTHER tool writes a code-graph index, so it widens what the graph reader will load and trust | un-inferrable — the path belongs to a tool this package does not ship and cannot probe for; the built-in list is deliberately vendor-neutral, so only the operator knows where their index lands |
565
566
  | `screenshots.identity_allowlist` | C | `[]` | allowlist of identities that ship unredacted | consent |
566
567
  | `screenshots.forbid_terminal_capture` | C | `true` | kill-switch over the highest-leak capture path | consent |
567
568
  | `screenshots.data_bearing_gate` | C | `"on"` | the human-confirmation gate over a published egress | consent |
@@ -622,6 +623,7 @@ Rows follow template order, so a diff against the template reads straight down.
622
623
  | `hooks.design_pass.enabled` | C | `false` | C-test 1 — it configures code that runs on every tool call and at every turn end, and unlike its two `hooks.*` neighbours its stop pass can BLOCK rather than warn | policy — whether an operator accepts a gate that can refuse a turn is a risk preference, not a fact the tree can compute. The two neighbours are `derivable` because they only ever warn; the block is the discriminator. `road-to-frontend-power` transfers the default flip to the owner for the same reason |
623
624
  | `hooks.code_graph.enabled` | C | `false` | INERT since 2026-09-07 — the nudge it gated was replaced by `code-graph-context`, which reads no flag. The KEY stays registered because `docs/MIGRATION.md` commits to exactly that ("the surfaces stay registered and disabled"): removing it is a breaking change for any consumer who set it, and the row was briefly marked REMOVED, which left the template's own leaf unclassified. Classified, not deleted. | derivable — the index-detection probe the replacement hook already runs; no index means silence |
624
625
  | `hooks.suggestion_capture.enabled` | C | `false` | configures code that runs at every turn end and every prompt | consent |
626
+ | `hooks.verify_before_complete.touched_file_quality` | C | `"off"` | C-test 1 — it decides whether the stop hook SPAWNS the project's own quality commands over the turn's edited files, so the cost it authorises is other people's processes on the operator's machine | consent — running a consumer's toolchain against their working tree is a thing to be permitted, not a fact the tree can derive. The three values are not a severity ladder the agent may climb: `shadow` records, `warn` adds one advisory line, and the flip from one to the other is the owner decision `road-to-touched-files-that-pass-their-own-tools` 2.3 defers |
625
627
  | `decision_engine.surface_traces` | C | `false` | the decision engine’s own black box; the agent must not be able to close it | derivable — the engine's own active-gate state; there is nothing to surface when no gate fired |
626
628
  | `decision_engine.min_confidence` | C | `"off"` | the confidence gate | derivable — the confidence band the scoring engine already computes at the plan phase |
627
629
  | `decision_engine.block_on_risk` | C | `"off"` | the risk-class gate | derivable — the risk class the engine already computes at the implement phase; the Hard Floor covers the irreversible end unconditionally |
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@event4u/agent-config",
3
3
  "mcpName": "io.github.event4u-app/agent-config",
4
- "version": "16.2.0",
4
+ "version": "16.3.0",
5
5
  "description": "Governed skills, rules and commands for AI coding tools (Claude Code, Cursor, Windsurf, Copilot) — every claim machine-checked, including the counts in these badges.",
6
6
  "license": "MIT",
7
7
  "private": false,