@event4u/agent-config 16.1.0 → 16.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (467) hide show
  1. package/.augment-plugin/marketplace.json +3 -3
  2. package/.augment-plugin/plugin.json +1 -1
  3. package/.claude-plugin/marketplace.json +1 -1
  4. package/CHANGELOG.md +98 -257
  5. package/README.md +14 -12
  6. package/dist/agent-src/commands/analyze/inbox.md +1 -1
  7. package/dist/agent-src/commands/analyze/roadmap-repos.md +1 -1
  8. package/dist/agent-src/commands/bug/fix.md +17 -2
  9. package/dist/agent-src/commands/feature/plan.md +1 -1
  10. package/dist/agent-src/commands/feature/roadmap.md +1 -1
  11. package/dist/agent-src/commands/fix/ci.md +1 -1
  12. package/dist/agent-src/commands/implement-ticket.md +1 -1
  13. package/dist/agent-src/commands/jira-ticket.md +1 -1
  14. package/dist/agent-src/commands/roadmap/create.md +1 -1
  15. package/dist/agent-src/commands/roadmap/materialize.md +1 -1
  16. package/dist/agent-src/commands/roadmap/process-full.md +5 -5
  17. package/dist/agent-src/commands/roadmap/resolve-blockers.md +361 -0
  18. package/dist/agent-src/commands/roadmap/triage-parked.md +325 -0
  19. package/dist/agent-src/commands/roadmap.md +18 -4
  20. package/dist/agent-src/commands/tests/create.md +17 -3
  21. package/dist/agent-src/commands/tests/execute.md +22 -5
  22. package/dist/agent-src/commands/work.md +4 -0
  23. package/dist/agent-src/contexts/authority/decision-revisit-gate-enforcement.md +43 -0
  24. package/dist/agent-src/contexts/communication/rules-auto/design-fidelity-enforcement.md +24 -0
  25. package/dist/agent-src/contexts/execution/auto-dispatch-classification.md +6 -6
  26. package/dist/agent-src/contexts/execution/autonomy-mechanics.md +4 -8
  27. package/dist/agent-src/contexts/execution/roadmap-process-loop.md +32 -2
  28. package/dist/agent-src/contexts/execution/session-canary-enforcement-history.md +67 -0
  29. package/dist/agent-src/contexts/execution/toolchain-resolver.md +101 -7
  30. package/dist/agent-src/rules/active-remediation.md +1 -1
  31. package/dist/agent-src/rules/autonomous-execution.md +1 -0
  32. package/dist/agent-src/rules/broken-access-control.md +1 -0
  33. package/dist/agent-src/rules/command-suggestion-policy.md +1 -0
  34. package/dist/agent-src/rules/context-hygiene.md +1 -1
  35. package/dist/agent-src/rules/decision-revisit-gate.md +16 -33
  36. package/dist/agent-src/rules/design-fidelity.md +13 -16
  37. package/dist/agent-src/rules/doc-screenshot-hygiene.md +1 -0
  38. package/dist/agent-src/rules/domain-safety-disclaimer.md +1 -0
  39. package/dist/agent-src/rules/domain-safety-pii.md +1 -0
  40. package/dist/agent-src/rules/domain-safety-retention.md +1 -0
  41. package/dist/agent-src/rules/engineering-safety-floor.md +1 -0
  42. package/dist/agent-src/rules/evaluator-independence.md +3 -2
  43. package/dist/agent-src/rules/fast-path-marker-visibility.md +1 -0
  44. package/dist/agent-src/rules/finance-safety-floor.md +1 -0
  45. package/dist/agent-src/rules/git-history-discipline.md +1 -0
  46. package/dist/agent-src/rules/image-likeness-and-rights.md +1 -0
  47. package/dist/agent-src/rules/legal-safety-floor.md +3 -2
  48. package/dist/agent-src/rules/lethal-trifecta-guard.md +1 -0
  49. package/dist/agent-src/rules/low-impact-corpus-privacy-floor.md +1 -0
  50. package/dist/agent-src/rules/media-governance-routing.md +14 -17
  51. package/dist/agent-src/rules/persona-governance.md +2 -2
  52. package/dist/agent-src/rules/provider-lifecycle-discipline.md +1 -1
  53. package/dist/agent-src/rules/question-not-instruction.md +1 -0
  54. package/dist/agent-src/rules/roadmap-progress-sync.md +1 -1
  55. package/dist/agent-src/rules/runtime-safety.md +1 -0
  56. package/dist/agent-src/rules/secret-vcs-guard.md +1 -0
  57. package/dist/agent-src/rules/security-sensitive-stop.md +3 -2
  58. package/dist/agent-src/rules/session-canary.md +8 -62
  59. package/dist/agent-src/rules/settings-ask-protocol.md +1 -0
  60. package/dist/agent-src/rules/source-confidentiality.md +1 -0
  61. package/dist/agent-src/rules/strategy-safety-floor.md +1 -0
  62. package/dist/agent-src/rules/tool-safety.md +1 -0
  63. package/dist/agent-src/rules/ui-audit-gate.md +1 -2
  64. package/dist/agent-src/rules/untrusted-input-defense.md +1 -0
  65. package/dist/agent-src/rules/user-interrupt-priority.md +1 -0
  66. package/dist/agent-src/scripts/archive_completed_roadmaps.ts +115 -20
  67. package/dist/agent-src/scripts/deferral_carry.ts +339 -0
  68. package/dist/agent-src/scripts/update_roadmap_progress.ts +4 -1
  69. package/dist/agent-src/skills/accessibility-auditor/data/aria-patterns.csv +1 -1
  70. package/dist/agent-src/skills/accessibility-auditor/data/manifest.json +2 -2
  71. package/dist/agent-src/skills/api-design/data/manifest.json +2 -2
  72. package/dist/agent-src/skills/api-testing/SKILL.md +17 -0
  73. package/dist/agent-src/skills/code-intelligence/SKILL.md +14 -2
  74. package/dist/agent-src/skills/design-review/SKILL.md +30 -4
  75. package/dist/agent-src/skills/design-review/references/verification-automation.md +32 -0
  76. package/dist/agent-src/skills/design-system-capture/references/import-procedure.md +1 -1
  77. package/dist/agent-src/skills/existing-ui-audit/SKILL.md +18 -3
  78. package/dist/agent-src/skills/existing-ui-audit/references/component-taxonomy-detection.md +56 -0
  79. package/dist/agent-src/skills/fe-design/SKILL.md +1 -1
  80. package/dist/agent-src/skills/iconography/SKILL.md +1 -1
  81. package/dist/agent-src/skills/js-library-packaging/SKILL.md +2 -2
  82. package/dist/agent-src/skills/markitdown/SKILL.md +4 -0
  83. package/dist/agent-src/skills/monorepo-workspace/SKILL.md +1 -1
  84. package/dist/agent-src/skills/playwright-testing/SKILL.md +30 -6
  85. package/dist/agent-src/skills/quality-tools/SKILL.md +11 -1
  86. package/dist/agent-src/skills/quality-tools/references/go-tools.md +76 -0
  87. package/dist/agent-src/skills/quality-tools/references/python-tools.md +104 -0
  88. package/dist/agent-src/skills/review-routing/SKILL.md +2 -2
  89. package/dist/agent-src/skills/skill-improvement-pipeline/SKILL.md +1 -1
  90. package/dist/agent-src/skills/storybook-workshop/SKILL.md +3 -3
  91. package/dist/agent-src/skills/tailwind-engineer/SKILL.md +1 -1
  92. package/dist/agent-src/skills/test-case-discovery/SKILL.md +7 -0
  93. package/dist/agent-src/skills/test-driven-development/SKILL.md +17 -0
  94. package/dist/agent-src/skills/test-performance/SKILL.md +19 -1
  95. package/dist/agent-src/skills/ui-component-architect/SKILL.md +25 -2
  96. package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
  97. package/dist/agent-src/templates/roadmaps.md +30 -0
  98. package/dist/agent-src/templates/scripts/work_engine/directives/ui/app_spec.ts +10 -4
  99. package/dist/agent-src/templates/scripts/work_engine/directives/ui/apply.ts +302 -21
  100. package/dist/agent-src/templates/scripts/work_engine/directives/ui/audit.ts +22 -3
  101. package/dist/agent-src/templates/scripts/work_engine/directives/ui/scaffold.ts +6 -1
  102. package/dist/agent-src/templates/scripts/work_engine/stack/runner.ts +774 -109
  103. package/dist/agent-src/templates/scripts/work_engine/state.ts +57 -7
  104. package/dist/agent-src/templates/scripts/work_engine/taxonomy/detect.ts +500 -0
  105. package/dist/agent-src/templates/scripts/work_engine/taxonomy/place.ts +183 -0
  106. package/dist/cli/python/knowledge_ingest.js +1 -1
  107. package/dist/cli/python/workspace_drive.js +1 -1
  108. package/dist/cli/registry.js +1 -1
  109. package/dist/cli-delegate/archive_completed_roadmaps.js +92 -12
  110. package/dist/cli-delegate/check_update_banner.js +12 -1
  111. package/dist/cli-delegate/chunk-523HCFYO.js +90 -0
  112. package/dist/cli-delegate/{chunk-M2DPGFZ4.js → chunk-6V6TA25H.js} +22 -152
  113. package/dist/cli-delegate/chunk-7IQE3D5S.js +191 -0
  114. package/dist/cli-delegate/{chunk-6KFEC3G7.js → chunk-CQWX336L.js} +1 -1
  115. package/dist/cli-delegate/chunk-DEIV7XP7.js +108 -0
  116. package/dist/cli-delegate/chunk-EEHDXSYD.js +2040 -0
  117. package/dist/cli-delegate/{chunk-P6ZHP5W2.js → chunk-GKE7ZVFA.js} +4 -4
  118. package/dist/cli-delegate/chunk-JQOM7TTM.js +1201 -0
  119. package/dist/cli-delegate/{chunk-UNCRJBT3.js → chunk-JQVYKIZA.js} +41 -0
  120. package/dist/cli-delegate/{chunk-FC75PGJE.js → chunk-JY2I2DXV.js} +12 -1
  121. package/dist/cli-delegate/chunk-KB5FZ655.js +208 -0
  122. package/dist/cli-delegate/{chunk-YUM5KURG.js → chunk-KR6EM23A.js} +235 -1455
  123. package/dist/cli-delegate/{chunk-LTYVIY76.js → chunk-LIYTLH6O.js} +1 -1
  124. package/dist/cli-delegate/chunk-NQ7WT43P.js +2729 -0
  125. package/dist/cli-delegate/chunk-NXEILF2K.js +2160 -0
  126. package/dist/cli-delegate/{chunk-5R7HOLAF.js → chunk-NYX3L2PZ.js} +1 -1
  127. package/dist/cli-delegate/{chunk-4E2W63DA.js → chunk-OKJUIAPS.js} +62 -1
  128. package/dist/cli-delegate/{chunk-QAJS5YWY.js → chunk-PHD7OOLE.js} +2 -1
  129. package/dist/cli-delegate/{chunk-RGNIWTEA.js → chunk-QLDHIPWR.js} +1 -1
  130. package/dist/cli-delegate/{chunk-HDVVGPQW.js → chunk-RQNNW7IF.js} +1405 -567
  131. package/dist/cli-delegate/{chunk-S3VFXMTM.js → chunk-S4UMZNES.js} +1 -1
  132. package/dist/cli-delegate/{chunk-6KM6ORRY.js → chunk-S4YWNRKO.js} +24 -8
  133. package/dist/cli-delegate/{chunk-GJCBB2UG.js → chunk-TRI6DQZ6.js} +3054 -325
  134. package/dist/cli-delegate/chunk-ULQRUR23.js +208 -0
  135. package/dist/cli-delegate/{chunk-NUHLI2RX.js → chunk-YCU3TIXH.js} +2 -0
  136. package/dist/cli-delegate/chunk-ZBZV2G5R.js +210 -0
  137. package/dist/cli-delegate/{chunk-WGXTVKCQ.js → chunk-ZUZKZYCN.js} +5 -3
  138. package/dist/cli-delegate/cmd_analyze_session.js +1 -1
  139. package/dist/cli-delegate/cmd_brand_status.js +1 -1
  140. package/dist/cli-delegate/cmd_conformance.js +1862 -809
  141. package/dist/cli-delegate/cmd_doctor.js +13 -6
  142. package/dist/cli-delegate/cmd_doctor_neighbours.js +23 -0
  143. package/dist/cli-delegate/cmd_explain.js +5 -5
  144. package/dist/cli-delegate/cmd_export.js +11 -8
  145. package/dist/cli-delegate/cmd_fleet.js +3 -3
  146. package/dist/cli-delegate/cmd_handoff.js +10 -7
  147. package/dist/cli-delegate/cmd_mcp_available.js +1 -1
  148. package/dist/cli-delegate/cmd_memory_get.js +3 -1
  149. package/dist/cli-delegate/cmd_migrate.js +2 -2
  150. package/dist/cli-delegate/cmd_packs_active.js +3 -3
  151. package/dist/cli-delegate/cmd_preflight.js +6 -6
  152. package/dist/cli-delegate/cmd_prune.js +11 -8
  153. package/dist/cli-delegate/cmd_refresh.js +2 -1
  154. package/dist/cli-delegate/cmd_route_audit.js +8 -6
  155. package/dist/cli-delegate/cmd_route_explain.js +4 -4
  156. package/dist/cli-delegate/cmd_session_recycle.js +4 -3
  157. package/dist/cli-delegate/cmd_settings_get.js +1 -1
  158. package/dist/cli-delegate/cmd_settings_migrate.js +11 -8
  159. package/dist/cli-delegate/cmd_settings_set.js +4 -4
  160. package/dist/cli-delegate/cmd_sync.js +11 -8
  161. package/dist/cli-delegate/cmd_uninstall.js +11 -8
  162. package/dist/cli-delegate/cmd_update.js +1 -1
  163. package/dist/cli-delegate/cmd_upgrade.js +3 -2
  164. package/dist/cli-delegate/cmd_validate.js +11 -8
  165. package/dist/cli-delegate/cmd_versions.js +1 -1
  166. package/dist/cli-delegate/deferral_carry.js +16 -0
  167. package/dist/cli-delegate/gate_execute.js +2 -2
  168. package/dist/cli-delegate/roadmap_gates.js +5 -5
  169. package/dist/cli-delegate/update_roadmap_progress.js +1 -1
  170. package/dist/discovery/deprecation-report.md +1 -1
  171. package/dist/discovery/discovery-manifest.json +132 -80
  172. package/dist/discovery/discovery-manifest.json.sha256 +1 -1
  173. package/dist/discovery/discovery-manifest.summary.md +3 -3
  174. package/dist/discovery/dormancy-report.md +1 -1
  175. package/dist/discovery/orphan-report.md +1 -1
  176. package/dist/discovery/packs.json +7 -5
  177. package/dist/discovery/trust-report.md +2 -2
  178. package/dist/discovery/workspaces.json +6 -4
  179. package/dist/hooks/dispatch.js +7972 -5186
  180. package/dist/hooks/dispatch.sha256 +1 -0
  181. package/dist/install/claudePathsPlan.js +47 -6
  182. package/dist/install/claudeRuleRewrite.js +7 -0
  183. package/dist/install/emit_host_rules_cli.js +39 -36
  184. package/dist/install/globalRuleScope.js +87 -0
  185. package/dist/install/host_rule_ownership.js +160 -0
  186. package/dist/install/install.mjs +2226 -1055
  187. package/dist/install/installThinLayer.js +261 -0
  188. package/dist/install/installedRuleLinks.js +176 -0
  189. package/dist/install/wizard-plan.js +24 -0
  190. package/dist/mcp/registry-manifest.json +3 -3
  191. package/dist/mcp/server.json +2 -2
  192. package/dist/mcp/server.mjs +40913 -34865
  193. package/dist/scripts/_lib/agent_settings.js +11 -0
  194. package/dist/scripts/_lib/claude_settings_hooks.js +10 -8
  195. package/dist/scripts/_lib/installed_layer.js +378 -0
  196. package/dist/scripts/_lib/lean_projection_mode.js +218 -15
  197. package/dist/scripts/_lib/rule_consequence_class.js +45 -0
  198. package/dist/scripts/_lib/rule_injection.js +67 -10
  199. package/dist/scripts/_lib/rule_law_section.js +122 -0
  200. package/dist/scripts/_lib/thin_rules.js +458 -0
  201. package/dist/scripts/_lib/windsurf_render.js +60 -0
  202. package/dist/scripts/condense.js +14 -26
  203. package/dist/scripts/project_thin_rules.js +14 -294
  204. package/dist/server/schemas/settings.js +29 -0
  205. package/dist/shared/skillRanking.js +74 -13
  206. package/dist/ui/assets/index-Cwq4d0hX.js +23 -0
  207. package/dist/ui/assets/index-Cyjy8bWO.css +1 -0
  208. package/dist/ui/index.html +2 -2
  209. package/docs/contracts/settings-classes.md +10 -6
  210. package/package.json +4 -4
  211. package/src/agent-src/scripts/archive_completed_roadmaps.ts +115 -20
  212. package/src/agent-src/scripts/deferral_carry.ts +339 -0
  213. package/src/agent-src/scripts/update_roadmap_progress.ts +4 -1
  214. package/src/agent-src/templates/scripts/work_engine/directives/ui/app_spec.ts +10 -4
  215. package/src/agent-src/templates/scripts/work_engine/directives/ui/apply.ts +302 -21
  216. package/src/agent-src/templates/scripts/work_engine/directives/ui/audit.ts +22 -3
  217. package/src/agent-src/templates/scripts/work_engine/directives/ui/scaffold.ts +6 -1
  218. package/src/agent-src/templates/scripts/work_engine/stack/runner.ts +774 -109
  219. package/src/agent-src/templates/scripts/work_engine/state.ts +57 -7
  220. package/src/agent-src/templates/scripts/work_engine/taxonomy/detect.ts +500 -0
  221. package/src/agent-src/templates/scripts/work_engine/taxonomy/place.ts +183 -0
  222. package/src/config/agent-settings.template.yml +91 -22
  223. package/src/config/ci-local-parity.yml +18 -0
  224. package/src/config/continuity-surface.json +9 -2
  225. package/src/config/evaluator-budgets.json +4 -4
  226. package/src/config/gate-coverage.yml +327 -3
  227. package/src/config/gate-violation-baselines.json +21 -9
  228. package/src/config/hook-bundle-budget.json +25 -0
  229. package/src/config/hook-latency-budget.json +21 -0
  230. package/src/config/hook-token-budget.json +8 -3
  231. package/src/config/host-instruction-limits.json +33 -0
  232. package/src/config/metric-registry.yml +114 -0
  233. package/src/config/placeholder-drift-budget.json +10 -0
  234. package/src/config/routing-coverage-seed.json +38 -3
  235. package/src/config/rule-activation-census.json +7 -3
  236. package/src/config/rule-consequence-class.json +161 -0
  237. package/src/config/rule-law-ceilings.json +99 -0
  238. package/src/install/claudePathsPlan.ts +46 -4
  239. package/src/install/claudeRuleRewrite.ts +10 -1
  240. package/src/install/emit_host_rules_cli.ts +43 -34
  241. package/src/install/globalRuleScope.ts +109 -0
  242. package/src/install/host_rule_ownership.ts +170 -0
  243. package/src/install/installThinLayer.ts +312 -0
  244. package/src/install/installedRuleLinks.ts +220 -0
  245. package/src/install/wizard-plan.ts +24 -0
  246. package/src/scripts/_cli/cmd_doctor.ts +28 -90
  247. package/src/scripts/_cli/cmd_doctor_neighbours.ts +293 -0
  248. package/src/scripts/_cli/doctor_execution.ts +100 -17
  249. package/src/scripts/_cli/doctor_network_posture.ts +226 -0
  250. package/src/scripts/_dispatch.bash +16 -2
  251. package/src/scripts/_lib/agent_settings.ts +11 -0
  252. package/src/scripts/_lib/body_portable.ts +102 -0
  253. package/src/scripts/_lib/cc_transcript.ts +132 -1
  254. package/src/scripts/_lib/claude_settings_hooks.ts +10 -8
  255. package/src/scripts/_lib/concern_sla_window.ts +200 -0
  256. package/src/scripts/_lib/estate_offsets.ts +241 -0
  257. package/src/scripts/_lib/exemption_shape.ts +143 -0
  258. package/src/scripts/_lib/fnv.ts +23 -0
  259. package/src/scripts/_lib/foreign_scan.ts +106 -0
  260. package/src/scripts/_lib/forge_protection.ts +114 -0
  261. package/src/scripts/_lib/forge_reader.ts +558 -0
  262. package/src/scripts/_lib/gitignore_block.ts +314 -0
  263. package/src/scripts/_lib/global_deploy_inventory.ts +32 -0
  264. package/src/scripts/_lib/graph_feeder_record.ts +214 -0
  265. package/src/scripts/_lib/hook_settings.ts +73 -0
  266. package/src/scripts/_lib/host_capability.ts +40 -19
  267. package/src/scripts/_lib/host_env_write.ts +111 -0
  268. package/src/scripts/_lib/host_hook_merge.ts +165 -0
  269. package/src/scripts/_lib/installed_layer.ts +538 -0
  270. package/src/scripts/_lib/json_merge.ts +82 -0
  271. package/src/scripts/_lib/json_pointers.ts +93 -11
  272. package/src/scripts/_lib/lean_projection_mode.ts +275 -15
  273. package/src/scripts/_lib/llm_proposer_transport.ts +14 -4
  274. package/src/scripts/_lib/machine_wake.ts +16 -3
  275. package/src/scripts/_lib/neighbour_census.ts +760 -0
  276. package/src/scripts/_lib/neighbour_scan.ts +122 -0
  277. package/src/scripts/_lib/neighbour_tool_use.ts +189 -0
  278. package/src/scripts/_lib/obligations.ts +119 -8
  279. package/src/scripts/_lib/probe_inputs.ts +271 -0
  280. package/src/scripts/_lib/prompt_shape.ts +46 -0
  281. package/src/scripts/_lib/read_surface_scan.ts +322 -0
  282. package/src/scripts/_lib/release_highlights.ts +2 -1
  283. package/src/scripts/_lib/release_migration_gate.ts +19 -3
  284. package/src/scripts/_lib/reserved_name_sweep.ts +73 -0
  285. package/src/scripts/_lib/retrieval_sanitize.ts +77 -16
  286. package/src/scripts/_lib/review_coverage.ts +134 -0
  287. package/src/scripts/_lib/rule_consequence_class.ts +71 -0
  288. package/src/scripts/_lib/rule_injection.ts +130 -10
  289. package/src/scripts/_lib/rule_law_section.ts +147 -0
  290. package/src/scripts/_lib/rule_layer_overlap.ts +58 -0
  291. package/src/scripts/_lib/shell_write_shapes.ts +108 -0
  292. package/src/scripts/_lib/skill_origin.ts +254 -0
  293. package/src/scripts/_lib/structural_hiding.ts +622 -0
  294. package/src/scripts/_lib/structured_ask.ts +64 -21
  295. package/src/scripts/_lib/thin_rules.ts +540 -0
  296. package/src/scripts/_lib/touched_file_quality.ts +434 -0
  297. package/src/scripts/_lib/turn_end_refusals.ts +600 -4
  298. package/src/scripts/_lib/turn_end_transcript.ts +216 -0
  299. package/src/scripts/_lib/verification_command.ts +324 -0
  300. package/src/scripts/_lib/verification_evidence.ts +647 -0
  301. package/src/scripts/_lib/verify_clause.ts +280 -0
  302. package/src/scripts/_lib/windsurf_render.ts +63 -0
  303. package/src/scripts/ai_council/cli_call_budget.ts +13 -5
  304. package/src/scripts/ai_council/clients.ts +26 -57
  305. package/src/scripts/ai_council/config.ts +1 -1
  306. package/src/scripts/ai_council/vendor_defaults.ts +45 -0
  307. package/src/scripts/ai_team/team_dispatch.ts +4 -0
  308. package/src/scripts/audit_skill_overlap.ts +261 -0
  309. package/src/scripts/before_complete_hook.ts +573 -25
  310. package/src/scripts/bench_hook_latency.ts +412 -5
  311. package/src/scripts/bench_touched_file_quality.ts +238 -0
  312. package/src/scripts/cache_realization_report.ts +61 -1
  313. package/src/scripts/check_argument_hint.ts +287 -0
  314. package/src/scripts/check_claims.ts +125 -1
  315. package/src/scripts/check_enforcement_coverage.ts +38 -9
  316. package/src/scripts/check_enforcement_matrix.ts +621 -22
  317. package/src/scripts/check_estate_count.ts +130 -153
  318. package/src/scripts/check_finding_dispositions.ts +138 -7
  319. package/src/scripts/check_generator_sync.ts +71 -0
  320. package/src/scripts/check_hook_bundle_composition.ts +370 -0
  321. package/src/scripts/check_host_docs_digest.ts +786 -0
  322. package/src/scripts/check_host_format_column.ts +559 -0
  323. package/src/scripts/check_input_references.ts +228 -0
  324. package/src/scripts/check_inputs_parity.ts +235 -0
  325. package/src/scripts/check_kernel_edit_ratified.ts +89 -9
  326. package/src/scripts/check_kill_switch_table.ts +294 -0
  327. package/src/scripts/check_memory.ts +7 -1
  328. package/src/scripts/check_placeholder_drift.ts +221 -0
  329. package/src/scripts/check_read_surface_coverage.ts +515 -0
  330. package/src/scripts/check_release_highlights.ts +10 -0
  331. package/src/scripts/check_release_holds.ts +2 -1
  332. package/src/scripts/check_release_pr_shape.ts +38 -15
  333. package/src/scripts/check_routing_coverage.ts +378 -11
  334. package/src/scripts/check_standing_rule_delivery.ts +33 -13
  335. package/src/scripts/check_static_parity.ts +17 -0
  336. package/src/scripts/check_verify_expectation_delta.ts +521 -0
  337. package/src/scripts/ci_settle.ts +145 -19
  338. package/src/scripts/closure_scan.ts +115 -1
  339. package/src/scripts/code_graph/detect.ts +127 -7
  340. package/src/scripts/code_graph/foreign.ts +194 -0
  341. package/src/scripts/code_graph/query.ts +217 -4
  342. package/src/scripts/code_graph/types.ts +26 -2
  343. package/src/scripts/code_graph/validate.ts +3 -1
  344. package/src/scripts/code_graph/verbs.ts +103 -0
  345. package/src/scripts/compile_hook_manifest.ts +90 -14
  346. package/src/scripts/condense.ts +14 -27
  347. package/src/scripts/estate_exemption_shape.ts +202 -0
  348. package/src/scripts/external_sources_denylist.json +29 -4
  349. package/src/scripts/generate_host_cost_table.ts +1 -1
  350. package/src/scripts/hook_manifest.json +1 -1
  351. package/src/scripts/hook_manifest.yaml +192 -14
  352. package/src/scripts/hooks/block_config_weakening.ts +294 -4
  353. package/src/scripts/hooks/block_kernel_rule_writes.ts +9 -78
  354. package/src/scripts/hooks/block_plumbing_writes.ts +342 -0
  355. package/src/scripts/hooks/bundle_integrity.ts +291 -0
  356. package/src/scripts/hooks/chain_nudge_hook.ts +1 -2
  357. package/src/scripts/hooks/code_graph_context_hook.ts +253 -25
  358. package/src/scripts/hooks/comment_discipline_hook.ts +1 -3
  359. package/src/scripts/hooks/concern_failure_policy.ts +280 -0
  360. package/src/scripts/hooks/concern_registry.ts +2 -0
  361. package/src/scripts/hooks/concern_timings.ts +94 -0
  362. package/src/scripts/hooks/delegation_nudge_hook.ts +1 -2
  363. package/src/scripts/hooks/design_pass_hook.ts +100 -9
  364. package/src/scripts/hooks/design_slop_hook.ts +1 -2
  365. package/src/scripts/hooks/dispatch_hook.ts +77 -71
  366. package/src/scripts/hooks/edit_shape_hook.ts +1 -2
  367. package/src/scripts/hooks/end_review_nudge_hook.ts +1 -1
  368. package/src/scripts/hooks/evidence_independence.ts +1 -2
  369. package/src/scripts/hooks/exit_codes.ts +129 -0
  370. package/src/scripts/hooks/host_lowering.json +1 -0
  371. package/src/scripts/hooks/host_lowering.ts +146 -3
  372. package/src/scripts/hooks/host_lowering.yaml +262 -46
  373. package/src/scripts/hooks/host_semantics.ts +30 -2
  374. package/src/scripts/hooks/injection_budget.ts +9 -2
  375. package/src/scripts/hooks/interruption_ledger_hook.ts +1 -2
  376. package/src/scripts/hooks/journal_record_hook.ts +1 -2
  377. package/src/scripts/hooks/memory_recall_hook.ts +1 -2
  378. package/src/scripts/hooks/obligation_settle_hook.ts +151 -5
  379. package/src/scripts/hooks/one_question_per_ask_hook.ts +43 -6
  380. package/src/scripts/hooks/orchestration_record_hook.ts +1 -2
  381. package/src/scripts/hooks/payload_stub.ts +10 -1
  382. package/src/scripts/hooks/permission_gate.ts +1 -2
  383. package/src/scripts/hooks/push_settle_hook.ts +1 -3
  384. package/src/scripts/hooks/replay_hook.ts +2 -1
  385. package/src/scripts/hooks/reread_guard_hook.ts +1 -2
  386. package/src/scripts/hooks/review_baseline_hook.ts +1 -2
  387. package/src/scripts/hooks/rtk_wrap_hook.ts +1 -2
  388. package/src/scripts/hooks/rule_inject_hook.ts +765 -68
  389. package/src/scripts/hooks/run_continuation_hook.ts +13 -15
  390. package/src/scripts/hooks/skill_route_hook.ts +1 -2
  391. package/src/scripts/hooks/source_first_gate_hook.ts +1 -2
  392. package/src/scripts/hooks/spawn_guard_shadow_hook.ts +1 -2
  393. package/src/scripts/hooks/subagent_ledger_hook.ts +1 -2
  394. package/src/scripts/hooks/suggestion_capture_hook.ts +1 -1
  395. package/src/scripts/hooks/table_fingerprint.ts +70 -0
  396. package/src/scripts/hooks/telemetry_flush_hook.ts +1 -2
  397. package/src/scripts/hooks/telemetry_usage_hook.ts +65 -10
  398. package/src/scripts/hooks/tool_result_bytes_hook.ts +93 -9
  399. package/src/scripts/hooks/turn_end_gate_hook.ts +421 -324
  400. package/src/scripts/hooks/ui_route_nudge_hook.ts +1 -2
  401. package/src/scripts/hooks_status.ts +112 -0
  402. package/src/scripts/injection_scan_hook.ts +7 -0
  403. package/src/scripts/install.ts +75 -189
  404. package/src/scripts/installed_layer_report.ts +103 -0
  405. package/src/scripts/language_mirror_hook.ts +15 -6
  406. package/src/scripts/learning_sidecar.ts +119 -8
  407. package/src/scripts/lint_deny_text.ts +419 -0
  408. package/src/scripts/lint_exit_codes.ts +262 -0
  409. package/src/scripts/lint_hook_manifest.ts +108 -0
  410. package/src/scripts/lint_major_migration_sections.ts +156 -26
  411. package/src/scripts/lint_pack_boundaries.ts +63 -5
  412. package/src/scripts/lint_roadmap_blockers.ts +78 -12
  413. package/src/scripts/lint_rule_law_section.ts +517 -0
  414. package/src/scripts/lint_skill_descriptions.ts +1 -1
  415. package/src/scripts/lint_skill_scripts_readonly.ts +1 -1
  416. package/src/scripts/lint_workflow_security.ts +161 -5
  417. package/src/scripts/mcp_server/consumer_tool_catalog.json +53 -0
  418. package/src/scripts/mcp_server/graph_tools.ts +72 -3
  419. package/src/scripts/mcp_server/prompts.ts +142 -1
  420. package/src/scripts/measure_routing_signal.ts +54 -16
  421. package/src/scripts/measure_skill_ranker_baseline.ts +515 -53
  422. package/src/scripts/measure_turn_end_gate.ts +193 -0
  423. package/src/scripts/memory_learn_hook.ts +91 -6
  424. package/src/scripts/memory_lookup.ts +1 -1
  425. package/src/scripts/model_rule_injection.ts +52 -5
  426. package/src/scripts/prepack-check.mjs +53 -0
  427. package/src/scripts/project_thin_rules.ts +34 -327
  428. package/src/scripts/release_publication.ts +13 -1
  429. package/src/scripts/report_evidence_temperature.ts +657 -0
  430. package/src/scripts/report_installed_rule_links.ts +157 -0
  431. package/src/scripts/report_invocation_surface.ts +383 -0
  432. package/src/scripts/report_label_agreement.ts +233 -0
  433. package/src/scripts/report_obligation_carriers.ts +699 -0
  434. package/src/scripts/report_obligation_mechanism.ts +230 -0
  435. package/src/scripts/report_obligation_writer_split.ts +236 -0
  436. package/src/scripts/report_single_token_triggers.ts +277 -0
  437. package/src/scripts/report_skill_menu_census.ts +208 -0
  438. package/src/scripts/report_skill_ranker_confusion.ts +442 -0
  439. package/src/scripts/report_standing_payload_by_host.ts +188 -15
  440. package/src/scripts/report_verification_record_coverage.ts +306 -0
  441. package/src/scripts/roadmap_verify_share.ts +213 -0
  442. package/src/scripts/rule_activation_census.ts +5 -2
  443. package/src/scripts/rule_trigger_eval.ts +9 -9
  444. package/src/scripts/schemas/command.schema.json +44 -0
  445. package/src/scripts/schemas/review-findings.schema.json +7 -2
  446. package/src/scripts/schemas/rule.schema.json +5 -0
  447. package/src/scripts/schemas/skill.schema.json +44 -0
  448. package/src/scripts/self_review_gate.ts +421 -17
  449. package/src/scripts/semantic_parity_census.ts +314 -0
  450. package/src/scripts/session_eol_report.ts +54 -1
  451. package/src/scripts/skill_tools/score_skill_relevance.ts +290 -25
  452. package/src/scripts/sweep_dead_scan_roots.ts +1 -1
  453. package/src/scripts/sweep_skill_ranker_signals.ts +129 -0
  454. package/src/scripts/sync_gitignore.ts +29 -235
  455. package/src/scripts/trigger_eval_grandfather.json +0 -7
  456. package/src/scripts/trigger_eval_rotation.ts +122 -27
  457. package/src/scripts/ui_conformance_probe.ts +156 -4
  458. package/src/scripts/update_prices.ts +85 -3
  459. package/src/scripts/write_bundle_digest.mjs +74 -0
  460. package/src/server/schemas/settings.ts +41 -0
  461. package/src/shared/skillRanking.ts +117 -11
  462. package/src/templates/consumer-settings/ONBOARDING.md +1 -0
  463. package/dist/cli-delegate/chunk-FXYUYU4N.js +0 -4416
  464. package/dist/cli-delegate/chunk-QJWAUMJW.js +0 -1072
  465. package/dist/cli-delegate/chunk-YDPILCOL.js +0 -152
  466. package/dist/ui/assets/index-DH1xc62v.js +0 -29
  467. package/dist/ui/assets/index-jSfMW8_t.css +0 -1
@@ -0,0 +1,277 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Which one-token keyword triggers fire on prompts that belong to other rules.
4
+ *
5
+ * Step 1.3 of `road-to-rule-triggers-and-links-that-hold`. A single common word
6
+ * — `rename`, `delete` — is a legitimate trigger for the rule that owns the
7
+ * subject and a standing cost for every prompt that merely uses the word. This
8
+ * reports which words sit at which share of the corpus. **Report only.** A
9
+ * trigger change is a separate decision per rule, because a common word can be
10
+ * exactly right: `augment-edit-discipline` genuinely wants to fire on a rename.
11
+ *
12
+ * The corpus is `tests/eval/routing-matrix/*.yaml`, the frozen set this tree
13
+ * already evaluates routing against. Each `positives[].prompt` is labelled with
14
+ * its file's rule. For a one-token keyword `K` on rule `R`, the share is:
15
+ *
16
+ * |{ prompt : label(prompt) != R and K occurs as a word }|
17
+ * ---------------------------------------------------------
18
+ * |{ prompt : label(prompt) != R }|
19
+ *
20
+ * `near_misses` are deliberately NOT in the denominator. They carry a negative
21
+ * label against their own file's rule and no positive label against any other,
22
+ * so counting them would mix "belongs to another rule" with "belongs to no
23
+ * rule" — two different claims, and only the first is what a cross-firing
24
+ * trigger costs.
25
+ *
26
+ * The 5 % threshold is a stated default for a report, not a gate (D3). Nothing
27
+ * acts on the figure automatically.
28
+ */
29
+ import * as fs from 'node:fs';
30
+ import * as path from 'node:path';
31
+ import { fileURLToPath, pathToFileURL } from 'node:url';
32
+
33
+ import { parse as parseYaml } from 'yaml';
34
+
35
+ // ledger-exempt: a REPORTER, not a gate. Its only non-zero exit says the
36
+ // corpus or the rule directory could not be read.
37
+
38
+ const _HERE = fileURLToPath(import.meta.url);
39
+
40
+ export const DEFAULT_THRESHOLD = 0.05;
41
+
42
+ export interface LabelledPrompt {
43
+ rule: string;
44
+ prompt: string;
45
+ /** `positive` — belongs to `rule`. `near_miss` — declared NOT to be `rule`. */
46
+ kind: 'positive' | 'near_miss';
47
+ }
48
+
49
+ export interface TriggerRow {
50
+ rule: string;
51
+ keyword: string;
52
+ /** Foreign POSITIVE prompts the keyword occurs in. */
53
+ hits: number;
54
+ /** Prompts labelled positive against any other rule. */
55
+ foreign: number;
56
+ share: number;
57
+ /**
58
+ * Occurrences in a near-miss the rule ITSELF declares — the keyword fires
59
+ * on a prompt that rule's own fixture says it must not fire on. A declared
60
+ * false positive, and the sharpest row this report can produce.
61
+ */
62
+ own_near_miss_hits: number;
63
+ own_near_misses: number;
64
+ /** Occurrences in another rule's near-miss list. */
65
+ foreign_near_miss_hits: number;
66
+ /** Which rules those foreign positives belong to, highest first. */
67
+ top_labels: { rule: string; count: number }[];
68
+ }
69
+
70
+ /** Every prompt in the matrix, labelled with its file's rule and its kind. */
71
+ export function loadCorpus(matrixDir: string): LabelledPrompt[] {
72
+ const out: LabelledPrompt[] = [];
73
+ const files = fs
74
+ .readdirSync(matrixDir)
75
+ .filter((n) => n.endsWith('.yaml'))
76
+ .sort();
77
+ for (const file of files) {
78
+ const doc = parseYaml(fs.readFileSync(path.join(matrixDir, file), 'utf-8')) as {
79
+ rule?: unknown;
80
+ positives?: { prompt?: unknown }[];
81
+ near_misses?: { prompt?: unknown }[];
82
+ } | null;
83
+ const rule = typeof doc?.rule === 'string' ? doc.rule : '';
84
+ if (!rule) continue;
85
+ for (const [items, kind] of [
86
+ [doc?.positives, 'positive'],
87
+ [doc?.near_misses, 'near_miss'],
88
+ ] as const) {
89
+ if (!Array.isArray(items)) continue;
90
+ for (const item of items) {
91
+ if (typeof item?.prompt === 'string' && item.prompt.trim()) {
92
+ out.push({ rule, prompt: item.prompt, kind });
93
+ }
94
+ }
95
+ }
96
+ }
97
+ return out;
98
+ }
99
+
100
+ /** One-token keyword triggers per rule, from `src/rules/*.md` frontmatter. */
101
+ export function singleTokenKeywords(rulesDir: string): { rule: string; keyword: string }[] {
102
+ const out: { rule: string; keyword: string }[] = [];
103
+ const files = fs
104
+ .readdirSync(rulesDir)
105
+ .filter((n) => n.endsWith('.md'))
106
+ .sort();
107
+ for (const file of files) {
108
+ const content = fs.readFileSync(path.join(rulesDir, file), 'utf-8');
109
+ if (!content.startsWith('---')) continue;
110
+ const end = content.indexOf('\n---', 3);
111
+ const block = end === -1 ? content : content.slice(0, end);
112
+ const rule = file.replace(/\.md$/, '');
113
+ for (const m of block.matchAll(/^\s*-\s*keyword:\s*["']?([^"'\n]+?)["']?\s*$/gm)) {
114
+ const kw = (m[1] as string).trim();
115
+ if (kw === '' || /\s/.test(kw)) continue;
116
+ out.push({ rule, keyword: kw });
117
+ }
118
+ }
119
+ return out;
120
+ }
121
+
122
+ /** Word-boundary, case-insensitive, with the keyword's own characters escaped. */
123
+ function occursAsWord(prompt: string, keyword: string): boolean {
124
+ const esc = keyword.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
125
+ return new RegExp(`(?:^|[^\\p{L}\\p{N}_])${esc}(?:$|[^\\p{L}\\p{N}_])`, 'iu').test(prompt);
126
+ }
127
+
128
+ export function rows(
129
+ corpus: readonly LabelledPrompt[],
130
+ keywords: readonly { rule: string; keyword: string }[],
131
+ ): TriggerRow[] {
132
+ const out: TriggerRow[] = [];
133
+ for (const { rule, keyword } of keywords) {
134
+ const foreign = corpus.filter((c) => c.rule !== rule && c.kind === 'positive');
135
+ if (foreign.length === 0) continue;
136
+ const hitting = foreign.filter((c) => occursAsWord(c.prompt, keyword));
137
+ const ownNear = corpus.filter((c) => c.rule === rule && c.kind === 'near_miss');
138
+ const foreignNear = corpus.filter((c) => c.rule !== rule && c.kind === 'near_miss');
139
+ const labels = new Map<string, number>();
140
+ for (const h of hitting) labels.set(h.rule, (labels.get(h.rule) ?? 0) + 1);
141
+ out.push({
142
+ rule,
143
+ keyword,
144
+ hits: hitting.length,
145
+ foreign: foreign.length,
146
+ share: hitting.length / foreign.length,
147
+ own_near_miss_hits: ownNear.filter((c) => occursAsWord(c.prompt, keyword)).length,
148
+ own_near_misses: ownNear.length,
149
+ foreign_near_miss_hits: foreignNear.filter((c) => occursAsWord(c.prompt, keyword)).length,
150
+ top_labels: [...labels.entries()]
151
+ .map(([r, count]) => ({ rule: r, count }))
152
+ .sort((a, b) => b.count - a.count || a.rule.localeCompare(b.rule))
153
+ .slice(0, 5),
154
+ });
155
+ }
156
+ out.sort(
157
+ (a, b) =>
158
+ b.share - a.share ||
159
+ b.own_near_miss_hits - a.own_near_miss_hits ||
160
+ b.foreign_near_miss_hits - a.foreign_near_miss_hits ||
161
+ a.rule.localeCompare(b.rule) ||
162
+ a.keyword.localeCompare(b.keyword),
163
+ );
164
+ return out;
165
+ }
166
+
167
+ export function renderTable(data: readonly TriggerRow[]): string[] {
168
+ const L: string[] = [];
169
+ L.push(
170
+ '| Rule | Keyword | Foreign positives | Share | Own near-miss hits | Foreign near-miss hits | Mostly labelled against |',
171
+ );
172
+ L.push('|---|---|---:|---:|---:|---:|---|');
173
+ for (const r of data) {
174
+ const labels = r.top_labels.map((l) => `\`${l.rule}\` ${String(l.count)}`).join(', ') || '—';
175
+ L.push(
176
+ `| \`${r.rule}\` | \`${r.keyword}\` | ${String(r.hits)}/${String(r.foreign)} | ` +
177
+ `${(r.share * 100).toFixed(1)} % | ${String(r.own_near_miss_hits)}/${String(r.own_near_misses)} | ` +
178
+ `${String(r.foreign_near_miss_hits)} | ${labels} |`,
179
+ );
180
+ }
181
+ return L;
182
+ }
183
+
184
+ const USAGE =
185
+ 'usage: report_single_token_triggers [--root DIR] [--threshold F] [--json | --table] [--all]\n' +
186
+ ' --threshold share above which a row is reported (default 0.05)\n' +
187
+ ' --all report every one-token keyword, not just those over the threshold\n';
188
+
189
+ export function main(): number {
190
+ const argv = process.argv.slice(2);
191
+ if (argv.includes('--help') || argv.includes('-h')) {
192
+ process.stdout.write(USAGE);
193
+ return 0;
194
+ }
195
+ const KNOWN = ['--root', '--threshold', '--json', '--table', '--all'];
196
+ // A silently-ignored `--treshold` would report against a threshold the
197
+ // operator did not ask for, while still printing the default in the header.
198
+ for (const a of argv) {
199
+ if (a.startsWith('-') && !KNOWN.includes(a)) {
200
+ process.stderr.write(`unknown argument: ${a}\n${USAGE}`);
201
+ return 2;
202
+ }
203
+ }
204
+ const flagValue = (f: string): string | null => {
205
+ const i = argv.indexOf(f);
206
+ return i >= 0 && i + 1 < argv.length ? (argv[i + 1] as string) : null;
207
+ };
208
+ const root = flagValue('--root') ?? process.cwd();
209
+ const rawT = flagValue('--threshold');
210
+ const threshold = rawT === null ? DEFAULT_THRESHOLD : Number.parseFloat(rawT);
211
+ if (!Number.isFinite(threshold) || threshold < 0 || threshold > 1) {
212
+ process.stderr.write('--threshold must be a fraction between 0 and 1\n');
213
+ return 2;
214
+ }
215
+
216
+ const matrixDir = path.join(root, 'tests', 'eval', 'routing-matrix');
217
+ const rulesDir = path.join(root, 'src', 'rules');
218
+ if (!fs.existsSync(matrixDir) || !fs.existsSync(rulesDir)) {
219
+ process.stderr.write(`missing ${matrixDir} or ${rulesDir}\n`);
220
+ return 1;
221
+ }
222
+
223
+ const corpus = loadCorpus(matrixDir);
224
+ const keywords = singleTokenKeywords(rulesDir);
225
+ const all = rows(corpus, keywords);
226
+ // A keyword reaches the report by EITHER axis: the share the step names, or
227
+ // a hit on a near-miss. Filtering on the share alone would have hidden the
228
+ // only rows this corpus actually produces — see the report's own § on why
229
+ // the 5 % threshold finds nothing here.
230
+ const data = argv.includes('--all')
231
+ ? all
232
+ : all.filter((r) => r.share > threshold || r.own_near_miss_hits > 0 || r.foreign_near_miss_hits > 0);
233
+
234
+ if (argv.includes('--json')) {
235
+ process.stdout.write(
236
+ `${JSON.stringify({ threshold, corpus: corpus.length, keywords: keywords.length, rows: data }, null, 2)}\n`,
237
+ );
238
+ return 0;
239
+ }
240
+ if (argv.includes('--table')) {
241
+ process.stdout.write(`${renderTable(data).join('\n')}\n`);
242
+ return 0;
243
+ }
244
+
245
+ const positives = corpus.filter((c) => c.kind === 'positive').length;
246
+ process.stdout.write(
247
+ `corpus ${String(corpus.length)} prompt(s) — ${String(positives)} positive, ` +
248
+ `${String(corpus.length - positives)} near-miss · ` +
249
+ `${String(keywords.length)} one-token keyword(s) over ` +
250
+ `${String(new Set(keywords.map((k) => k.rule)).size)} rule(s)\n` +
251
+ `${String(all.filter((r) => r.share > threshold).length)} row(s) above ` +
252
+ `${(threshold * 100).toFixed(1)} % of foreign positives · ` +
253
+ `${String(data.length)} reportable on either axis\n\n`,
254
+ );
255
+ for (const r of data) {
256
+ process.stdout.write(
257
+ ` ${(r.share * 100).toFixed(1).padStart(5)} % ${r.keyword.padEnd(18)} ${r.rule.padEnd(32)} ` +
258
+ `pos ${String(r.hits)}/${String(r.foreign)} own-near ${String(r.own_near_miss_hits)}/${String(r.own_near_misses)} ` +
259
+ `foreign-near ${String(r.foreign_near_miss_hits)}\n`,
260
+ );
261
+ }
262
+ return 0;
263
+ }
264
+
265
+ function _isCliEntry(): boolean {
266
+ if (process.argv[1] === undefined) return false;
267
+ if (import.meta.url === pathToFileURL(path.resolve(process.argv[1])).href) return true;
268
+ try {
269
+ return fs.realpathSync(_HERE) === fs.realpathSync(path.resolve(process.argv[1]));
270
+ } catch {
271
+ return false;
272
+ }
273
+ }
274
+
275
+ if (_isCliEntry()) {
276
+ process.exit(main());
277
+ }
@@ -53,8 +53,21 @@
53
53
  * `command-only` and `flow-only` name a CANDIDATE for `user-invocable: false`,
54
54
  * never a decision — see the sole-entry limit above.
55
55
  *
56
+ * THE PER-PROFILE MENU, added by `road-to-a-menu-whose-precision-is-measured`
57
+ * 4.1. `--profile minimal|balanced|full|all` prints the menu byte count each
58
+ * shipped install profile delivers. Each profile's preset is read from its own
59
+ * file, and the answer is that all three are equal: no shipped preset declares
60
+ * a key that selects skills, so each one ships the whole menu.
61
+ *
62
+ * The equality note is WITHHELD when any profile carries a key listed in
63
+ * {@link SKILL_SELECTING_INI_KEYS}, and the key is named instead. That is the
64
+ * whole falsifiability claim, stated no wider than it holds: the byte count
65
+ * would not move — nothing here knows what such a key means — but the report
66
+ * stops asserting a conclusion whose premise has changed.
67
+ *
56
68
  * Usage:
57
69
  * ./scripts-run src/scripts/report_skill_menu_census # stdout summary
70
+ * ./scripts-run src/scripts/report_skill_menu_census --profile all
58
71
  * ./scripts-run src/scripts/report_skill_menu_census --emit # write the artifact
59
72
  * ./scripts-run src/scripts/report_skill_menu_census --emit --pin <sha>
60
73
  *
@@ -313,8 +326,203 @@ function headSha(root: string): string {
313
326
  return execFileSync('git', ['-C', root, 'rev-parse', 'HEAD'], { encoding: 'utf-8' }).trim();
314
327
  }
315
328
 
329
+ // ── per-profile menu bytes (road-to-a-menu-whose-precision-is-measured 4.1) ──
330
+
331
+ /** The shipped install presets, mirroring `install.ts` SUPPORTED_PROFILES. */
332
+ export const PROFILES = ['minimal', 'balanced', 'full'] as const;
333
+ export type Profile = (typeof PROFILES)[number];
334
+
335
+ /**
336
+ * Preset keys that would narrow the skill menu.
337
+ *
338
+ * EMPTY, and the emptiness is the finding rather than a stub. A preset selects
339
+ * `rule_loading_tier`, `discipline_profile` and `chat_history_frequency`; which
340
+ * SKILLS are written is decided by `packs:` frontmatter against the active pack
341
+ * set, which no preset names.
342
+ *
343
+ * It is a TRIPWIRE, not a selector, and the distinction is worth stating
344
+ * plainly because the first version of this comment overclaimed it. Nothing
345
+ * here knows what such a key would MEAN, so nothing here can apply one: the
346
+ * byte count would not move. What the list buys is that a preset carrying a
347
+ * listed key is NAMED in the output and the equality note is withheld — so the
348
+ * report stops asserting a conclusion whose premise has changed, instead of
349
+ * printing the same prose over a different tree.
350
+ */
351
+ export const SKILL_SELECTING_INI_KEYS: readonly string[] = [];
352
+
353
+ /** Flat `key=value` read of a profile preset; `;` comments and blanks dropped. */
354
+ export function readProfileIni(root: string, profile: Profile): Record<string, string> {
355
+ const p = path.join(root, 'src', 'config', 'profiles', `${profile}.ini`);
356
+ const out: Record<string, string> = {};
357
+ for (const raw of fs.readFileSync(p, 'utf-8').split('\n')) {
358
+ const line = raw.trim();
359
+ if (line === '' || line.startsWith(';') || line.startsWith('#') || line.startsWith('[')) continue;
360
+ const eq = line.indexOf('=');
361
+ if (eq === -1) continue;
362
+ out[line.slice(0, eq).trim()] = line.slice(eq + 1).trim();
363
+ }
364
+ return out;
365
+ }
366
+
367
+ export interface ProfileMenu {
368
+ readonly profile: Profile;
369
+ /** Skills this profile writes onto the model menu. */
370
+ readonly menuSkills: number;
371
+ /** Bytes of `- <name>: <description>\n`, the shape `censusSkillsCatalog` sums. */
372
+ readonly menuBytes: number;
373
+ /** chars/4, the estimator the payload budget uses. */
374
+ readonly tokensEstimate: number;
375
+ /** `declared_skill_catalogue_tokens` from the preset, or null when absent. */
376
+ readonly declaredCatalogueTokens: number | null;
377
+ /** Preset keys found that would narrow the menu. Empty today, by measurement. */
378
+ readonly selectingKeys: readonly string[];
379
+ }
380
+
381
+ /**
382
+ * One profile's menu, computed from its preset plus the skill tree.
383
+ *
384
+ * The catalogue LINE is the same shape `preamble_byte_census.censusSkillsCatalog`
385
+ * sums — `- <name>: <description>\n`. The POPULATION deliberately is not, and the
386
+ * difference is stated rather than smoothed over: that census sums every
387
+ * `SKILL.md`, because the payload bucket it feeds is about what the host lists;
388
+ * this one drops a skill carrying `user-invocable: false` or
389
+ * `disable-model-invocation: true`, because a menu is what the MODEL may pick
390
+ * from. Two skills carry such a flag today, so over the same tree and the same
391
+ * line shape this report reads 311 B below the full-population sum. Expect the
392
+ * two to differ; a reader who finds them equal has found a defect in one.
393
+ */
394
+ export function menuForProfile(root: string, profile: Profile): ProfileMenu {
395
+ const ini = readProfileIni(root, profile);
396
+ const selectingKeys = SKILL_SELECTING_INI_KEYS.filter((k) => k in ini);
397
+ const skillsDir = path.join(root, 'src', 'skills');
398
+ let menuSkills = 0;
399
+ let menuBytes = 0;
400
+ for (const entry of fs.readdirSync(skillsDir, { withFileTypes: true })) {
401
+ if (!entry.isDirectory()) continue;
402
+ const skillMd = path.join(skillsDir, entry.name, 'SKILL.md');
403
+ if (!fs.existsSync(skillMd)) continue;
404
+ const text = fs.readFileSync(skillMd, 'utf-8');
405
+ const flags = menuFlags(text);
406
+ if (flags.userInvocable === false || flags.disableModel === true) continue;
407
+ const name = /^name:\s*(\S.*?)\s*$/mu.exec(text.slice(0, 4000))?.[1] ?? entry.name;
408
+ const description = frontmatterDescription(text);
409
+ menuSkills += 1;
410
+ menuBytes += `- ${name}: ${description}\n`.length;
411
+ }
412
+ const declared = ini['declared_skill_catalogue_tokens'];
413
+ return {
414
+ profile,
415
+ menuSkills,
416
+ menuBytes,
417
+ tokensEstimate: Math.round(menuBytes / 4),
418
+ declaredCatalogueTokens: declared === undefined ? null : Number(declared),
419
+ selectingKeys,
420
+ };
421
+ }
422
+
423
+ /**
424
+ * The `description:` value, folded lines included.
425
+ *
426
+ * A `[^\n]+` capture was measured 17.1 % low elsewhere in this tree because it
427
+ * truncates every folded multi-line description; this reads to the next
428
+ * top-level key instead.
429
+ *
430
+ * A double-quoted value has its escapes DECODED. Leaving them encoded counted
431
+ * `\"` as two characters and over-reported the menu by 56 B across 13 skills —
432
+ * small, but the same class of defect as the 17.1 % one above, and a function
433
+ * whose docstring cites that fix should not carry a second instance of it.
434
+ */
435
+ export function frontmatterDescription(text: string): string {
436
+ if (!text.startsWith('---')) return '';
437
+ const end = text.indexOf('\n---', 3);
438
+ if (end === -1) return '';
439
+ const lines = text.slice(3, end).split('\n');
440
+ const at = lines.findIndex((l) => /^description:/u.test(l));
441
+ if (at === -1) return '';
442
+ const parts = [(lines[at] as string).replace(/^description:\s*/u, '')];
443
+ for (let i = at + 1; i < lines.length; i += 1) {
444
+ const l = lines[i] as string;
445
+ if (/^\S/u.test(l)) break;
446
+ parts.push(l.trim());
447
+ }
448
+ const joined = parts.join(' ').trim();
449
+ if (joined.length >= 2 && joined.startsWith('"') && joined.endsWith('"')) {
450
+ return joined.slice(1, -1).replace(/\\(["\\])/gu, '$1');
451
+ }
452
+ if (joined.length >= 2 && joined.startsWith("'") && joined.endsWith("'")) {
453
+ return joined.slice(1, -1).replace(/''/gu, "'");
454
+ }
455
+ return joined;
456
+ }
457
+
458
+ /** Render the per-profile menu table. Returns the lines, so tests read them. */
459
+ export function renderProfileMenus(menus: readonly ProfileMenu[]): string[] {
460
+ const L: string[] = [];
461
+ L.push('menu bytes per install profile (name + description, the catalogue shape):');
462
+ L.push(' profile skills menu_bytes ~tokens declared');
463
+ for (const m of menus) {
464
+ L.push(
465
+ ` ${m.profile.padEnd(10)} ${String(m.menuSkills).padStart(6)} ` +
466
+ `${String(m.menuBytes).padStart(12)} ${String(m.tokensEstimate).padStart(9)} ` +
467
+ `${(m.declaredCatalogueTokens === null ? '—' : String(m.declaredCatalogueTokens)).padStart(10)}`,
468
+ );
469
+ }
470
+ const flagged = menus.filter((m) => m.selectingKeys.length > 0);
471
+ if (flagged.length > 0) {
472
+ L.push('');
473
+ L.push(' A preset declares a key that could select skills, so the equality below is NOT');
474
+ L.push(' asserted — the premise it rested on has changed. Keys found:');
475
+ for (const m of flagged) L.push(` ${m.profile}: ${m.selectingKeys.join(', ')}`);
476
+ return L;
477
+ }
478
+ const distinct = new Set(menus.map((m) => m.menuBytes));
479
+ if (menus.length > 1 && distinct.size === 1) {
480
+ L.push('');
481
+ L.push(
482
+ ' All profiles report the SAME menu_bytes, and that is a measurement, not a',
483
+ );
484
+ L.push(
485
+ ' placeholder: no preset declares a key that selects skills, so each ships the',
486
+ );
487
+ L.push(
488
+ ' whole menu. The preset chooses which RULES activate; which SKILLS are written',
489
+ );
490
+ L.push(
491
+ ' is decided by `packs:` frontmatter against the active pack set. A menu-bytes',
492
+ );
493
+ L.push(
494
+ ' lever therefore has to move packs or the menu flags — changing profile does',
495
+ );
496
+ L.push(' nothing. The three presets already record this in their own comments.');
497
+ }
498
+ return L;
499
+ }
500
+
316
501
  export function main(argv: readonly string[]): number {
317
502
  const root = REPO_ROOT;
503
+ const profIdx = argv.indexOf('--profile');
504
+ if (profIdx !== -1) {
505
+ const want = argv[profIdx + 1] ?? 'all';
506
+ const chosen: Profile[] =
507
+ want === 'all' ? [...PROFILES] : PROFILES.includes(want as Profile) ? [want as Profile] : [];
508
+ if (chosen.length === 0) {
509
+ process.stderr.write(
510
+ `report_skill_menu_census: --profile expects ${PROFILES.join(' | ')} | all, got ${want}\n`,
511
+ );
512
+ return 2;
513
+ }
514
+ if (argv.includes('--emit')) {
515
+ // Refused rather than ignored: `--emit` writes the census artifact,
516
+ // `--profile` prints a table and writes nothing, and silently doing
517
+ // the second when the caller asked for both is how an artifact goes
518
+ // un-refreshed without anyone noticing.
519
+ process.stderr.write('report_skill_menu_census: --profile and --emit are separate runs\n');
520
+ return 2;
521
+ }
522
+ const menus = chosen.map((p) => menuForProfile(root, p));
523
+ process.stdout.write(`${renderProfileMenus(menus).join('\n')}\n`);
524
+ return 0;
525
+ }
318
526
  const emit = argv.includes('--emit');
319
527
  const pinIdx = argv.indexOf('--pin');
320
528
  const pin = pinIdx !== -1 && pinIdx + 1 < argv.length ? (argv[pinIdx + 1] as string) : headSha(root);