@event4u/agent-config 16.0.0 → 16.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (382) hide show
  1. package/.augment-plugin/marketplace.json +3 -3
  2. package/.augment-plugin/plugin.json +1 -1
  3. package/.claude-plugin/marketplace.json +1 -1
  4. package/CHANGELOG.md +289 -140
  5. package/CONTRIBUTING.md +9 -0
  6. package/README.md +2 -2
  7. package/dist/agent-src/commands/analyze/inbox.md +26 -0
  8. package/dist/agent-src/commands/analyze/roadmap-repos.md +27 -0
  9. package/dist/agent-src/commands/challenge-me/closure.md +178 -0
  10. package/dist/agent-src/commands/challenge-me.md +9 -2
  11. package/dist/agent-src/commands/council/default.md +25 -5
  12. package/dist/agent-src/commands/feature/plan.md +28 -1
  13. package/dist/agent-src/commands/feature/roadmap.md +28 -1
  14. package/dist/agent-src/commands/implement-ticket.md +27 -0
  15. package/dist/agent-src/commands/jira-ticket.md +27 -0
  16. package/dist/agent-src/commands/pr/create.md +29 -0
  17. package/dist/agent-src/commands/pr/merge.md +60 -9
  18. package/dist/agent-src/commands/roadmap/ai-council.md +7 -1
  19. package/dist/agent-src/commands/roadmap/create.md +74 -27
  20. package/dist/agent-src/commands/roadmap/materialize.md +27 -0
  21. package/dist/agent-src/commands/roadmap/process-full.md +123 -18
  22. package/dist/agent-src/commands/team.md +22 -0
  23. package/dist/agent-src/commands/work.md +4 -0
  24. package/dist/agent-src/contexts/authority/scope-growth-ownership.md +80 -0
  25. package/dist/agent-src/contexts/communication/rules-auto/user-interaction-mechanics.md +47 -0
  26. package/dist/agent-src/contexts/execution/auto-orchestration-activation.md +44 -3
  27. package/dist/agent-src/contexts/execution/autonomy-mechanics.md +19 -23
  28. package/dist/agent-src/contexts/execution/decision-closure-execution.md +93 -0
  29. package/dist/agent-src/contexts/execution/fix-loop-ladder.md +108 -0
  30. package/dist/agent-src/contexts/execution/plan-confidence-gate.md +4 -2
  31. package/dist/agent-src/contexts/execution/roadmap-process-loop.md +75 -6
  32. package/dist/agent-src/guidelines/agent-infra/active-remediation-mechanics.md +57 -0
  33. package/dist/agent-src/guidelines/agent-infra/evaluator-independence-mechanics.md +102 -0
  34. package/dist/agent-src/guidelines/design-fidelity-routing.md +29 -0
  35. package/dist/agent-src/rules/autonomous-execution.md +15 -8
  36. package/dist/agent-src/rules/delegation-policy.md +23 -45
  37. package/dist/agent-src/rules/design-fidelity.md +32 -37
  38. package/dist/agent-src/rules/evaluator-independence.md +43 -49
  39. package/dist/agent-src/rules/test-first.md +41 -0
  40. package/dist/agent-src/rules/ui-audit-gate.md +1 -2
  41. package/dist/agent-src/skills/accessibility-auditor/data/aria-patterns.csv +1 -1
  42. package/dist/agent-src/skills/accessibility-auditor/data/manifest.json +3 -3
  43. package/dist/agent-src/skills/ai-council/SKILL.md +11 -3
  44. package/dist/agent-src/skills/api-design/data/manifest.json +3 -3
  45. package/dist/agent-src/skills/database/data/manifest.json +4 -4
  46. package/dist/agent-src/skills/database/data/query-tuning.csv +3 -3
  47. package/dist/agent-src/skills/design-review/SKILL.md +80 -4
  48. package/dist/agent-src/skills/design-review/references/verification-automation.md +32 -0
  49. package/dist/agent-src/skills/existing-ui-audit/SKILL.md +30 -5
  50. package/dist/agent-src/skills/existing-ui-audit/references/component-taxonomy-detection.md +56 -0
  51. package/dist/agent-src/skills/experiment-loop/SKILL.md +31 -1
  52. package/dist/agent-src/skills/markitdown/SKILL.md +4 -0
  53. package/dist/agent-src/skills/playwright-testing/SKILL.md +30 -6
  54. package/dist/agent-src/skills/roadmap-writing/SKILL.md +1 -1
  55. package/dist/agent-src/skills/storybook-workshop/SKILL.md +42 -3
  56. package/dist/agent-src/skills/threat-modeling/SKILL.md +9 -4
  57. package/dist/agent-src/skills/threat-modeling/data/manifest.json +4 -4
  58. package/dist/agent-src/skills/ui-component-architect/SKILL.md +23 -0
  59. package/dist/agent-src/skills/verify-repair-loop/SKILL.md +37 -3
  60. package/dist/agent-src/templates/agent-settings.md +1 -1
  61. package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
  62. package/dist/agent-src/templates/roadmaps.md +218 -0
  63. package/dist/agent-src/templates/scripts/work_engine/directives/backend/_self_fix.ts +62 -2
  64. package/dist/agent-src/templates/scripts/work_engine/directives/backend/test.ts +1 -0
  65. package/dist/agent-src/templates/scripts/work_engine/directives/backend/verify.ts +1 -0
  66. package/dist/agent-src/templates/scripts/work_engine/directives/ui/app_spec.ts +10 -4
  67. package/dist/agent-src/templates/scripts/work_engine/directives/ui/apply.ts +260 -21
  68. package/dist/agent-src/templates/scripts/work_engine/directives/ui/audit.ts +22 -3
  69. package/dist/agent-src/templates/scripts/work_engine/directives/ui/scaffold.ts +6 -1
  70. package/dist/agent-src/templates/scripts/work_engine/state.ts +57 -7
  71. package/dist/agent-src/templates/scripts/work_engine/taxonomy/detect.ts +500 -0
  72. package/dist/agent-src/templates/scripts/work_engine/taxonomy/place.ts +183 -0
  73. package/dist/cli/python/knowledge_ingest.js +1 -1
  74. package/dist/cli/python/workspace_drive.js +1 -1
  75. package/dist/cli-delegate/check_update_banner.js +40 -2
  76. package/dist/cli-delegate/chunk-4E2W63DA.js +335 -0
  77. package/dist/cli-delegate/{chunk-S3VFXMTM.js → chunk-4XFP2Y3E.js} +1 -1
  78. package/dist/cli-delegate/chunk-5R7HOLAF.js +99 -0
  79. package/dist/cli-delegate/{chunk-IVBPRCQO.js → chunk-6KFEC3G7.js} +1 -1
  80. package/dist/cli-delegate/{chunk-2TAQ2K4V.js → chunk-6KM6ORRY.js} +55 -386
  81. package/dist/cli-delegate/{chunk-AK6HODNQ.js → chunk-7XGBX6ZH.js} +973 -555
  82. package/dist/cli-delegate/{chunk-2XY7ILBT.js → chunk-FC75PGJE.js} +42 -2
  83. package/dist/cli-delegate/{chunk-55ACLDUE.js → chunk-FP36SWJD.js} +587 -141
  84. package/dist/cli-delegate/{chunk-3FKR64R5.js → chunk-IGD6ERX6.js} +62 -7
  85. package/dist/cli-delegate/{chunk-GYC3VRYN.js → chunk-JNMVLWLR.js} +99 -14
  86. package/dist/cli-delegate/{chunk-H32DGUJH.js → chunk-P6ZHP5W2.js} +1 -1
  87. package/dist/cli-delegate/{chunk-57PN4GYJ.js → chunk-RGNIWTEA.js} +1 -1
  88. package/dist/cli-delegate/chunk-ROYYTEZU.js +39 -0
  89. package/dist/cli-delegate/{chunk-2DK2VJWG.js → chunk-WGXTVKCQ.js} +20 -6
  90. package/dist/cli-delegate/chunk-YDPILCOL.js +152 -0
  91. package/dist/cli-delegate/{chunk-QJWAUMJW.js → chunk-YJ72UIAK.js} +730 -15
  92. package/dist/cli-delegate/cmd_analyze_session.js +1 -1
  93. package/dist/cli-delegate/cmd_brand_status.js +1 -1
  94. package/dist/cli-delegate/cmd_conformance.js +985 -831
  95. package/dist/cli-delegate/cmd_doctor.js +7 -5
  96. package/dist/cli-delegate/cmd_explain.js +2 -2
  97. package/dist/cli-delegate/cmd_export.js +9 -7
  98. package/dist/cli-delegate/cmd_fleet.js +4 -2
  99. package/dist/cli-delegate/cmd_handoff.js +7 -7
  100. package/dist/cli-delegate/cmd_mcp_available.js +1 -1
  101. package/dist/cli-delegate/cmd_memory_get.js +1 -1
  102. package/dist/cli-delegate/cmd_migrate.js +2 -2
  103. package/dist/cli-delegate/cmd_packs_active.js +2 -2
  104. package/dist/cli-delegate/cmd_preflight.js +4 -2
  105. package/dist/cli-delegate/cmd_prune.js +10 -8
  106. package/dist/cli-delegate/cmd_route_audit.js +3 -3
  107. package/dist/cli-delegate/cmd_route_explain.js +1 -1
  108. package/dist/cli-delegate/cmd_session_recycle.js +4 -4
  109. package/dist/cli-delegate/cmd_settings_get.js +28 -2
  110. package/dist/cli-delegate/cmd_settings_migrate.js +9 -7
  111. package/dist/cli-delegate/cmd_settings_set.js +1 -1
  112. package/dist/cli-delegate/cmd_sync.js +10 -8
  113. package/dist/cli-delegate/cmd_uninstall.js +16 -13
  114. package/dist/cli-delegate/cmd_update.js +1 -1
  115. package/dist/cli-delegate/cmd_upgrade.js +1 -1
  116. package/dist/cli-delegate/cmd_validate.js +12 -10
  117. package/dist/cli-delegate/cmd_versions.js +1 -1
  118. package/dist/discovery/deprecation-report.md +1 -1
  119. package/dist/discovery/discovery-manifest.json +90 -43
  120. package/dist/discovery/discovery-manifest.json.sha256 +1 -1
  121. package/dist/discovery/discovery-manifest.summary.md +4 -4
  122. package/dist/discovery/dormancy-report.md +1 -1
  123. package/dist/discovery/orphan-report.md +1 -1
  124. package/dist/discovery/packs.json +10 -8
  125. package/dist/discovery/trust-report.md +3 -3
  126. package/dist/discovery/workspaces.json +8 -6
  127. package/dist/hooks/dispatch.js +7682 -2772
  128. package/dist/hooks/dispatch.sha256 +1 -0
  129. package/dist/install/claudeRuleRewrite.js +14 -3
  130. package/dist/install/conflict.js +60 -10
  131. package/dist/install/conflictTracker.js +218 -0
  132. package/dist/install/fsPrimitives.js +54 -0
  133. package/dist/install/install.mjs +2200 -1559
  134. package/dist/install/manifestFiles.js +259 -0
  135. package/dist/install/preflight.js +14 -2
  136. package/dist/install/preserve.js +157 -0
  137. package/dist/install/recordedOwnership.js +132 -0
  138. package/dist/install/txlog.js +11 -2
  139. package/dist/mcp/registry-manifest.json +3 -3
  140. package/dist/mcp/server.json +2 -2
  141. package/dist/mcp/server.mjs +3066 -1470
  142. package/dist/router.json +1 -1
  143. package/dist/scripts/_lib/agent_settings.js +3 -2
  144. package/dist/scripts/_lib/fs_atomic.js +167 -0
  145. package/dist/scripts/_lib/installed_tools.js +460 -0
  146. package/dist/scripts/_lib/lean_projection_mode.js +3 -2
  147. package/dist/scripts/_lib/settings_renamed_keys.js +78 -0
  148. package/dist/server/routes/install.js +8 -0
  149. package/dist/server/routes/wizard.js +9 -3
  150. package/dist/server/schemas/settings.js +40 -5
  151. package/dist/shared/missionExecution.js +97 -0
  152. package/dist/ui/assets/index-Cwq4d0hX.js +23 -0
  153. package/dist/ui/assets/index-Cyjy8bWO.css +1 -0
  154. package/dist/ui/index.html +2 -2
  155. package/docs/MIGRATION.md +103 -0
  156. package/docs/contracts/settings-classes.md +28 -7
  157. package/docs/guidelines/agent-infra/active-remediation-mechanics.md +57 -0
  158. package/docs/guidelines/agent-infra/evaluator-independence-mechanics.md +102 -0
  159. package/docs/guidelines/design-fidelity-routing.md +29 -0
  160. package/package.json +4 -4
  161. package/src/agent-src/templates/scripts/work_engine/directives/backend/_self_fix.ts +62 -2
  162. package/src/agent-src/templates/scripts/work_engine/directives/backend/test.ts +1 -0
  163. package/src/agent-src/templates/scripts/work_engine/directives/backend/verify.ts +1 -0
  164. package/src/agent-src/templates/scripts/work_engine/directives/ui/app_spec.ts +10 -4
  165. package/src/agent-src/templates/scripts/work_engine/directives/ui/apply.ts +260 -21
  166. package/src/agent-src/templates/scripts/work_engine/directives/ui/audit.ts +22 -3
  167. package/src/agent-src/templates/scripts/work_engine/directives/ui/scaffold.ts +6 -1
  168. package/src/agent-src/templates/scripts/work_engine/state.ts +57 -7
  169. package/src/agent-src/templates/scripts/work_engine/taxonomy/detect.ts +500 -0
  170. package/src/agent-src/templates/scripts/work_engine/taxonomy/place.ts +183 -0
  171. package/src/config/agent-settings.template.yml +168 -27
  172. package/src/config/auto-rule-budget.json +16 -0
  173. package/src/config/ci-local-parity.yml +36 -5
  174. package/src/config/continuity-surface.json +23 -2
  175. package/src/config/gate-coverage.yml +377 -13
  176. package/src/config/gate-violation-baselines.json +10 -6
  177. package/src/config/hook-latency-budget.json +21 -0
  178. package/src/config/hook-token-budget.json +4 -2
  179. package/src/config/loop-surfaces.yaml +215 -0
  180. package/src/config/metric-registry.yml +114 -0
  181. package/src/config/placeholder-drift-budget.json +10 -0
  182. package/src/config/platform-anchor.json +3 -2
  183. package/src/config/release-gate-locality.yml +11 -0
  184. package/src/config/routing-coverage-seed.json +33 -5
  185. package/src/config/rule-enforcement-baseline.json +0 -1
  186. package/src/install/claudeRuleRewrite.ts +16 -2
  187. package/src/install/conflict.ts +65 -9
  188. package/src/install/conflictTracker.ts +299 -0
  189. package/src/install/fsPrimitives.ts +54 -0
  190. package/src/install/manifestFiles.ts +312 -0
  191. package/src/install/preflight.ts +16 -2
  192. package/src/install/preserve.ts +205 -0
  193. package/src/install/recordedOwnership.ts +149 -0
  194. package/src/install/txlog.ts +11 -2
  195. package/src/install/types.ts +19 -4
  196. package/src/scripts/_cli/cmd_conformance.ts +88 -15
  197. package/src/scripts/_cli/cmd_doctor.ts +7 -20
  198. package/src/scripts/_cli/cmd_route_explain.ts +9 -2
  199. package/src/scripts/_cli/cmd_settings_get.ts +44 -0
  200. package/src/scripts/_cli/doctor_execution.ts +173 -0
  201. package/src/scripts/_cli/doctor_network_posture.ts +226 -0
  202. package/src/scripts/_lib/activation_payload.ts +247 -0
  203. package/src/scripts/_lib/agent_settings.ts +4 -2
  204. package/src/scripts/_lib/authority_path.ts +102 -0
  205. package/src/scripts/_lib/body_portable.ts +102 -0
  206. package/src/scripts/_lib/cascade_base.ts +94 -0
  207. package/src/scripts/_lib/cc_transcript.ts +132 -1
  208. package/src/scripts/_lib/changelog_eras.ts +7 -2
  209. package/src/scripts/_lib/concern_sla_window.ts +200 -0
  210. package/src/scripts/_lib/continuation_ladder.ts +149 -2
  211. package/src/scripts/_lib/council_transport.ts +129 -0
  212. package/src/scripts/_lib/delivery_ready.ts +112 -0
  213. package/src/scripts/_lib/forge_protection.ts +381 -0
  214. package/src/scripts/_lib/forge_reader.ts +558 -0
  215. package/src/scripts/_lib/hook_platform_keys.ts +27 -0
  216. package/src/scripts/_lib/host_env_write.ts +111 -0
  217. package/src/scripts/_lib/json_python_parity.ts +127 -0
  218. package/src/scripts/_lib/lean_projection_mode.ts +14 -3
  219. package/src/scripts/_lib/llm_proposer_transport.ts +14 -4
  220. package/src/scripts/_lib/loop_surfaces.ts +382 -0
  221. package/src/scripts/_lib/mission_record.ts +190 -0
  222. package/src/scripts/_lib/obligation_frequency.ts +217 -0
  223. package/src/scripts/_lib/obligations.ts +513 -0
  224. package/src/scripts/_lib/orchestration_record.ts +11 -20
  225. package/src/scripts/_lib/probe_inputs.ts +271 -0
  226. package/src/scripts/_lib/read_surface_scan.ts +322 -0
  227. package/src/scripts/_lib/release_highlights.ts +201 -11
  228. package/src/scripts/_lib/release_holds.ts +493 -0
  229. package/src/scripts/_lib/release_migration_gate.ts +184 -0
  230. package/src/scripts/_lib/retrieval_sanitize.ts +77 -16
  231. package/src/scripts/_lib/rides_along.ts +102 -0
  232. package/src/scripts/_lib/settings_renamed_keys.ts +82 -0
  233. package/src/scripts/_lib/shell_write_shapes.ts +108 -0
  234. package/src/scripts/_lib/structural_hiding.ts +622 -0
  235. package/src/scripts/_lib/test_delta.ts +166 -0
  236. package/src/scripts/_lib/test_provenance.ts +171 -0
  237. package/src/scripts/_lib/turn_end_refusals.ts +525 -4
  238. package/src/scripts/_lib/turn_end_transcript.ts +216 -0
  239. package/src/scripts/_lib/typed_op_grant.ts +246 -0
  240. package/src/scripts/_lib/typed_op_watch.ts +185 -0
  241. package/src/scripts/_lib/verification_command.ts +324 -0
  242. package/src/scripts/_lib/verification_evidence.ts +647 -0
  243. package/src/scripts/_lib/verify_clause.ts +280 -0
  244. package/src/scripts/ai_council/config.ts +8 -11
  245. package/src/scripts/ai_council/fallback_config.ts +5 -3
  246. package/src/scripts/ai_council/ownership_classes.ts +118 -0
  247. package/src/scripts/ai_team/team_dispatch.ts +4 -0
  248. package/src/scripts/ask_block_census.ts +285 -15
  249. package/src/scripts/before_complete_hook.ts +340 -23
  250. package/src/scripts/bench_hook_latency.ts +412 -5
  251. package/src/scripts/cache_realization_report.ts +61 -1
  252. package/src/scripts/check_argument_hint.ts +287 -0
  253. package/src/scripts/check_claims.ts +125 -1
  254. package/src/scripts/check_enforcement_coverage.ts +38 -9
  255. package/src/scripts/check_enforcement_matrix.ts +866 -0
  256. package/src/scripts/check_finding_dispositions.ts +22 -3
  257. package/src/scripts/check_gate_reachability.ts +173 -10
  258. package/src/scripts/check_generator_sync.ts +1025 -0
  259. package/src/scripts/check_host_docs_digest.ts +786 -0
  260. package/src/scripts/check_host_format_column.ts +559 -0
  261. package/src/scripts/check_input_references.ts +228 -0
  262. package/src/scripts/check_inputs_parity.ts +235 -0
  263. package/src/scripts/check_kernel_edit_ratified.ts +47 -5
  264. package/src/scripts/check_kill_switch_table.ts +294 -0
  265. package/src/scripts/check_placeholder_drift.ts +221 -0
  266. package/src/scripts/check_preamble_payload_budget.ts +75 -2
  267. package/src/scripts/check_read_surface_coverage.ts +515 -0
  268. package/src/scripts/check_release_highlights.ts +33 -1
  269. package/src/scripts/check_release_holds.ts +568 -0
  270. package/src/scripts/check_routing_coverage.ts +378 -11
  271. package/src/scripts/check_rule_projection_integrity.ts +22 -12
  272. package/src/scripts/check_static_parity.ts +17 -0
  273. package/src/scripts/check_test_delta.ts +294 -0
  274. package/src/scripts/check_test_weakening.ts +299 -0
  275. package/src/scripts/ci_settle.ts +145 -19
  276. package/src/scripts/closure_scan.ts +447 -0
  277. package/src/scripts/compile_hook_manifest.ts +102 -18
  278. package/src/scripts/council_record_shape.ts +209 -0
  279. package/src/scripts/external_sources_denylist.json +29 -4
  280. package/src/scripts/hook_manifest.json +1 -1
  281. package/src/scripts/hook_manifest.yaml +127 -11
  282. package/src/scripts/hooks/block_config_weakening.ts +229 -4
  283. package/src/scripts/hooks/block_kernel_rule_writes.ts +9 -78
  284. package/src/scripts/hooks/block_plumbing_writes.ts +314 -0
  285. package/src/scripts/hooks/bundle_integrity.ts +291 -0
  286. package/src/scripts/hooks/chain_nudge_hook.ts +1 -2
  287. package/src/scripts/hooks/code_graph_context_hook.ts +1 -2
  288. package/src/scripts/hooks/comment_discipline_hook.ts +1 -3
  289. package/src/scripts/hooks/concern_registry.ts +6 -0
  290. package/src/scripts/hooks/concern_timings.ts +94 -0
  291. package/src/scripts/hooks/delegation_nudge_hook.ts +1 -2
  292. package/src/scripts/hooks/design_pass_hook.ts +221 -5
  293. package/src/scripts/hooks/design_slop_hook.ts +1 -2
  294. package/src/scripts/hooks/dispatch_hook.ts +25 -19
  295. package/src/scripts/hooks/edit_shape_hook.ts +1 -2
  296. package/src/scripts/hooks/end_review_nudge_hook.ts +1 -1
  297. package/src/scripts/hooks/evidence_independence.ts +97 -2
  298. package/src/scripts/hooks/exit_codes.ts +128 -0
  299. package/src/scripts/hooks/host_lowering.json +1 -0
  300. package/src/scripts/hooks/host_lowering.ts +128 -3
  301. package/src/scripts/hooks/host_lowering.yaml +262 -46
  302. package/src/scripts/hooks/host_semantics.ts +30 -2
  303. package/src/scripts/hooks/injection_budget.ts +9 -2
  304. package/src/scripts/hooks/interruption_ledger_hook.ts +1 -2
  305. package/src/scripts/hooks/journal_record_hook.ts +1 -2
  306. package/src/scripts/hooks/memory_recall_hook.ts +539 -0
  307. package/src/scripts/hooks/obligation_settle_hook.ts +328 -0
  308. package/src/scripts/hooks/one_question_per_ask_hook.ts +25 -4
  309. package/src/scripts/hooks/orchestration_record_hook.ts +1 -2
  310. package/src/scripts/hooks/permission_gate.ts +1 -2
  311. package/src/scripts/hooks/push_settle_hook.ts +1 -3
  312. package/src/scripts/hooks/replay_hook.ts +2 -1
  313. package/src/scripts/hooks/reread_guard_hook.ts +1 -2
  314. package/src/scripts/hooks/review_baseline_hook.ts +1 -2
  315. package/src/scripts/hooks/rtk_wrap_hook.ts +1 -2
  316. package/src/scripts/hooks/rule_inject_hook.ts +55 -4
  317. package/src/scripts/hooks/run_continuation_hook.ts +30 -35
  318. package/src/scripts/hooks/skill_route_hook.ts +1 -2
  319. package/src/scripts/hooks/source_first_gate_hook.ts +1 -2
  320. package/src/scripts/hooks/spawn_guard_shadow_hook.ts +1 -2
  321. package/src/scripts/hooks/subagent_ledger_hook.ts +1 -2
  322. package/src/scripts/hooks/suggestion_capture_hook.ts +1 -1
  323. package/src/scripts/hooks/table_fingerprint.ts +49 -0
  324. package/src/scripts/hooks/telemetry_flush_hook.ts +1 -2
  325. package/src/scripts/hooks/telemetry_usage_hook.ts +1 -2
  326. package/src/scripts/hooks/tool_result_bytes_hook.ts +93 -9
  327. package/src/scripts/hooks/turn_end_gate_hook.ts +337 -324
  328. package/src/scripts/hooks/ui_route_nudge_hook.ts +1 -2
  329. package/src/scripts/hooks_doctor.ts +85 -0
  330. package/src/scripts/hooks_status.ts +19 -0
  331. package/src/scripts/install.ts +109 -210
  332. package/src/scripts/janitor.ts +7 -0
  333. package/src/scripts/lint_decision_classes.ts +464 -0
  334. package/src/scripts/lint_deny_text.ts +419 -0
  335. package/src/scripts/lint_exit_codes.ts +262 -0
  336. package/src/scripts/lint_hook_manifest.ts +66 -0
  337. package/src/scripts/lint_major_migration_sections.ts +450 -0
  338. package/src/scripts/lint_plan_risk_register.ts +47 -3
  339. package/src/scripts/lint_roadmap_blockers.ts +139 -12
  340. package/src/scripts/lint_roadmap_producers.ts +202 -0
  341. package/src/scripts/lint_skill_descriptions.ts +1 -1
  342. package/src/scripts/lint_skill_scripts_readonly.ts +1 -1
  343. package/src/scripts/mcp_server/prompts.ts +142 -1
  344. package/src/scripts/measure_rule_budget.ts +101 -5
  345. package/src/scripts/measure_skill_ranker_baseline.ts +305 -51
  346. package/src/scripts/measure_turn_end_gate.ts +193 -0
  347. package/src/scripts/memory_lookup.ts +1 -1
  348. package/src/scripts/new_roadmap.ts +37 -0
  349. package/src/scripts/orchestration_record.ts +0 -5
  350. package/src/scripts/prepack-check.mjs +53 -0
  351. package/src/scripts/release.ts +14 -98
  352. package/src/scripts/release_drill.ts +267 -1
  353. package/src/scripts/release_publication.ts +64 -1
  354. package/src/scripts/report_enforcement_drift.ts +566 -0
  355. package/src/scripts/report_host_injection_effect.ts +3 -11
  356. package/src/scripts/report_invocation_surface.ts +383 -0
  357. package/src/scripts/report_obligation_carriers.ts +699 -0
  358. package/src/scripts/report_obligation_writer_split.ts +236 -0
  359. package/src/scripts/report_skill_menu_census.ts +208 -0
  360. package/src/scripts/report_standing_payload_by_host.ts +26 -152
  361. package/src/scripts/roadmap_verify_share.ts +213 -0
  362. package/src/scripts/schemas/agent-settings.schema.json +23 -0
  363. package/src/scripts/schemas/command.schema.json +48 -0
  364. package/src/scripts/schemas/review-findings.schema.json +4 -0
  365. package/src/scripts/schemas/skill.schema.json +44 -0
  366. package/src/scripts/scope_growth.ts +219 -0
  367. package/src/scripts/self_review_gate.ts +359 -10
  368. package/src/scripts/semantic_parity_census.ts +314 -0
  369. package/src/scripts/session_eol_report.ts +54 -1
  370. package/src/scripts/sweep_dead_scan_roots.ts +1 -1
  371. package/src/scripts/trigger_eval_grandfather.json +0 -3
  372. package/src/scripts/trigger_eval_rotation.ts +122 -27
  373. package/src/scripts/ui_conformance_probe.ts +774 -0
  374. package/src/scripts/update_prices.ts +85 -3
  375. package/src/scripts/write_bundle_digest.mjs +74 -0
  376. package/src/server/schemas/settings.ts +62 -7
  377. package/src/shared/missionExecution.ts +126 -0
  378. package/src/templates/consumer-settings/ONBOARDING.md +1 -0
  379. package/dist/cli-delegate/chunk-523HCFYO.js +0 -90
  380. package/dist/ui/assets/index-D0jaCRPM.js +0 -29
  381. package/dist/ui/assets/index-jSfMW8_t.css +0 -1
  382. /package/dist/cli-delegate/{chunk-YUM5KURG.js → chunk-7RQP4UDH.js} +0 -0
@@ -38,6 +38,7 @@ const REPO_ROOT = path.resolve(path.dirname(_HERE), '..', '..');
38
38
 
39
39
  const OVERRIDES_FILE = path.join(REPO_ROOT, 'docs', 'contracts', 'iron-law-overrides.txt');
40
40
  const TREND_FILE = path.join(REPO_ROOT, 'agents', 'runtime', '.rule-budget-history.jsonl');
41
+ const AUTO_BUDGET_FILE = path.join(REPO_ROOT, 'src', 'config', 'auto-rule-budget.json');
41
42
 
42
43
  // Council R2 amendments (2026-05-06) — see docs/contracts/kernel-membership.md § 5.1.
43
44
  // Per-rule cap raised 1.5k → 2.5k; warning band raised 1.2k → 2.0k.
@@ -59,6 +60,8 @@ export interface RuleMeasure {
59
60
  lines: number;
60
61
  tokens_gpt: number;
61
62
  tokens_claude: number;
63
+ /** Iron-Law headings in the body — the obligation count a char count hides. */
64
+ iron_laws: number;
62
65
  }
63
66
 
64
67
  export interface Aggregate {
@@ -80,6 +83,7 @@ export interface Aggregate {
80
83
  per_rule_hard: number;
81
84
  per_rule_target: number;
82
85
  per_rule_override_ceiling: number;
86
+ iron_law_total: number;
83
87
  oversize_rules: RuleMeasure[];
84
88
  top5_largest: RuleMeasure[];
85
89
  }
@@ -130,9 +134,32 @@ export function measure_rule(p: string): RuleMeasure {
130
134
  lines: _count(body, '\n'),
131
135
  tokens_gpt: token_count.gpt_tokens(body).tokens,
132
136
  tokens_claude: token_count.claude_tokens(body).tokens,
137
+ iron_laws: count_iron_laws(body),
133
138
  };
134
139
  }
135
140
 
141
+ /**
142
+ * Count Iron-Law HEADINGS in a rule body.
143
+ *
144
+ * road-to-design-fidelity-proof Phase 3.2. A rule is not an instruction:
145
+ * `token-efficiency` carries five Iron Laws in one file, so a file count and a
146
+ * char count both understate what a model is asked to hold at once. The
147
+ * instruction-following literature measures against the number of SIMULTANEOUS
148
+ * constraints, and an Iron-Law heading is the closest proxy this tree can count
149
+ * deterministically.
150
+ *
151
+ * Heading forms accepted, matching `preservation-guard`'s own definition: any
152
+ * level, optional `The`, singular or plural, optional trailing number or
153
+ * qualifier (`## Iron Law 2`, `## Iron Law — Gate`). A prose mention is NOT a
154
+ * heading, and a heading that merely contains the words later (`## Why the Iron
155
+ * Law exists`) is not one either — the phrase must open the heading text, or
156
+ * every retrospective paragraph would inflate the count.
157
+ */
158
+ export function count_iron_laws(body: string): number {
159
+ const re = /^#{1,6}[ \t]+(?:The[ \t]+)?Iron[ \t]+Laws?\b/gim;
160
+ return (body.match(re) ?? []).length;
161
+ }
162
+
136
163
  /**
137
164
  * Collect rule measurements from every source root (multi-root aware).
138
165
  * Deduplicates on logical rule id (stem) — first root wins. Mirrors
@@ -198,6 +225,7 @@ export function aggregate(rules: RuleMeasure[]): Aggregate {
198
225
  per_rule_hard: PER_RULE_HARD,
199
226
  per_rule_target: PER_RULE_TARGET,
200
227
  per_rule_override_ceiling: PER_RULE_OVERRIDE_CEILING,
228
+ iron_law_total: _sum(rules.map((r) => r.iron_laws)),
201
229
  oversize_rules: rules
202
230
  .filter((r) => r.chars > PER_RULE_HARD)
203
231
  .sort((a, b) => _cmp([-a.chars, a.id], [-b.chars, b.id])),
@@ -324,6 +352,59 @@ export function kernel_budget_check(
324
352
  return [0, out];
325
353
  }
326
354
 
355
+ export interface AutoBudgetConfig {
356
+ baseline_chars: number;
357
+ }
358
+
359
+ /** Read the auto-bucket baseline. Throws rather than defaulting: a ratchet that
360
+ * silently falls back to "no bound" is a permanently green gate. */
361
+ export function load_auto_budget(file: string = AUTO_BUDGET_FILE): AutoBudgetConfig {
362
+ const raw = JSON.parse(fs.readFileSync(file, 'utf-8')) as { baseline_chars?: unknown };
363
+ if (typeof raw.baseline_chars !== 'number') {
364
+ throw new Error(`${file}: baseline_chars must be a number`);
365
+ }
366
+ return { baseline_chars: raw.baseline_chars };
367
+ }
368
+
369
+ /**
370
+ * Enforce the `auto`-bucket growth ratchet. Returns `[exit_code, report_lines]`.
371
+ *
372
+ * road-to-design-fidelity-proof Phase 3.1. `kernel_budget_check` above bounds
373
+ * nine rules; this bounds the other 107, which carry ~93 % of the estate's rule
374
+ * prose and grew against nothing. Shrink-only: growth fails, a shrink passes and
375
+ * says how much slack it created, so the number walks down rather than becoming
376
+ * unused headroom.
377
+ *
378
+ * The Iron-Law total is REPORTED, never gated. No measurement in this tree says
379
+ * how many simultaneous obligations this layer can carry, and a bound nobody
380
+ * measured would publish a guess as a finding.
381
+ */
382
+ export function auto_budget_check(agg: Aggregate, cfg: AutoBudgetConfig): [number, string[]] {
383
+ const out: string[] = [];
384
+ const delta = agg.auto_chars - cfg.baseline_chars;
385
+ const signed = delta >= 0 ? `+${delta}` : String(delta);
386
+ out.push(
387
+ `auto-bucket: ${agg.auto_chars} / ${cfg.baseline_chars} chars ` +
388
+ `(${agg.auto_count} rules, ${signed} vs baseline)`,
389
+ );
390
+ out.push(`iron-law total: ${agg.iron_law_total} heading(s) — reported, not gated`);
391
+ out.push('');
392
+ if (delta > 0) {
393
+ out.push(`\u274c auto budget check: the auto bucket grew ${signed} chars`);
394
+ out.push(
395
+ ' The baseline may only move DOWN. Walk the bucket back, or state the ' +
396
+ 'compensating reduction — raising it to clear this is config weakening.',
397
+ );
398
+ return [1, out];
399
+ }
400
+ out.push(
401
+ delta === 0
402
+ ? '\u2705 auto budget check: pass (at the baseline)'
403
+ : `\u2705 auto budget check: pass (${signed} chars of slack — ratchet it down)`,
404
+ );
405
+ return [0, out];
406
+ }
407
+
327
408
  /**
328
409
  * Append a daily snapshot to agents/runtime/.rule-budget-history.jsonl.
329
410
  * Idempotent per UTC day. Mirrors `trend_append`. Returns `[exit, msg]`.
@@ -363,19 +444,22 @@ export function trend_append(agg: Aggregate): [number, string] {
363
444
  interface Args {
364
445
  json: boolean;
365
446
  kernelBudgetCheck: boolean;
447
+ autoBudgetCheck: boolean;
366
448
  trendAppend: boolean;
367
449
  }
368
450
 
369
451
  function parse_args(argv: string[]): Args {
370
452
  let json = false;
371
453
  let kernelBudgetCheck = false;
454
+ let autoBudgetCheck = false;
372
455
  let trendAppend = false;
373
456
  for (const a of argv) {
374
457
  if (a === '--json') json = true;
375
458
  else if (a === '--kernel-budget-check') kernelBudgetCheck = true;
459
+ else if (a === '--auto-budget-check') autoBudgetCheck = true;
376
460
  else if (a === '--trend-append') trendAppend = true;
377
461
  }
378
- return { json, kernelBudgetCheck, trendAppend };
462
+ return { json, kernelBudgetCheck, autoBudgetCheck, trendAppend };
379
463
  }
380
464
 
381
465
  export function main(argv: string[] = process.argv.slice(2)): number {
@@ -384,10 +468,22 @@ export function main(argv: string[] = process.argv.slice(2)): number {
384
468
  const rules = collect();
385
469
  const agg = aggregate(rules);
386
470
 
387
- if (args.kernelBudgetCheck) {
388
- const overrides = load_overrides();
389
- const [code, report] = kernel_budget_check(rules, agg, overrides);
390
- process.stdout.write(report.join('\n') + '\n');
471
+ // Both budget flags may be passed in one invocation, and the CI step does
472
+ // exactly that — a second task step would be a new gate `check_ci_local_parity`
473
+ // has to be told about, for two checks over one measurement.
474
+ if (args.kernelBudgetCheck || args.autoBudgetCheck) {
475
+ let code = 0;
476
+ if (args.kernelBudgetCheck) {
477
+ const overrides = load_overrides();
478
+ const [kCode, report] = kernel_budget_check(rules, agg, overrides);
479
+ process.stdout.write(report.join('\n') + '\n');
480
+ code = Math.max(code, kCode);
481
+ }
482
+ if (args.autoBudgetCheck) {
483
+ const [aCode, report] = auto_budget_check(agg, load_auto_budget());
484
+ process.stdout.write(report.join('\n') + '\n');
485
+ code = Math.max(code, aCode);
486
+ }
391
487
  return code;
392
488
  }
393
489
 
@@ -7,35 +7,49 @@
7
7
  * the corpus's expected skill per line". Building it surfaced two defects in
8
8
  * that premise, and this file is written around them rather than over them.
9
9
  *
10
- * DEFECT A — the 496-line corpus has no expected SKILL. `tests/eval/routing-matrix/`
11
- * is keyed by `rule:` and every prompt is labelled with the RULE it should
12
- * activate. Scoring a skill ranker against it would need a skill label that
13
- * does not exist there, so a "hit rate" over it would be invented.
10
+ * DEFECT A — the routing matrix had no expected SKILL. `tests/eval/routing-matrix/`
11
+ * is keyed by `rule:` and every prompt was labelled with the RULE it should
12
+ * activate. Scoring a skill ranker against it would need a skill label that did
13
+ * not exist there, so a "hit rate" over it would be invented.
14
14
  *
15
- * DEFECT B — 496 is stale. The matrix holds 499 prompts today
16
- * (`grep -h "^\s*- prompt:" tests/eval/routing-matrix/*.yaml | wc -l`), and 496
17
- * is still published in six places including `hook-token-budget.json`.
15
+ * **Defect A is CLOSED as of `road-to-a-menu-whose-precision-is-measured` 1.1.**
16
+ * Every case in the matrix now carries an `expected_skills` list, hand-written
17
+ * by seats that had not read this ranker's scoring (the protocol is in
18
+ * `tests/eval/routing-matrix/README.md`). An EMPTY list is a first-class value
19
+ * meaning "deliberately no skill expectation", and such a row is excluded from
20
+ * the accuracy denominator rather than counted as a miss. So the matrix arm now
21
+ * has ground truth; `--corpus routing-matrix` scores against it.
18
22
  *
19
- * So the baseline is measured against the corpus that DOES carry expected
20
- * skills — `tests/eval/corpus-{dev,non-dev}.yaml`, 26 labelled prompts — and
21
- * the routing matrix is reported alongside it as what it can honestly be: a
22
- * COVERAGE measurement (does the ranker return anything at all, and how
23
- * confident) with no ground truth and therefore no accuracy.
23
+ * DEFECT B — the published size is stale, repeatedly. 496 was published while
24
+ * the matrix held 499, and 499 is still published in several sites while the
25
+ * matrix holds more than that again. The size is therefore MEASURED on every run
26
+ * and printed, never asserted from a constant.
24
27
  *
25
- * Both halves are emitted in one JSON object so a later ranker revision can be
26
- * compared on the same two axes in one run:
28
+ * THE INTERVAL, AND THE REFUSAL TO PRINT A VERDICT UNDER n = 100.
29
+ * A hit rate over 26 prompts
30
+ * moves ~4 points per prompt; quoting it as "61.5 %" invites a comparison the
31
+ * sample cannot carry. Both arms therefore report a 95 % Wilson score interval
32
+ * (`_lib/capture_rate.ts` — Wilson rather than the normal approximation for the
33
+ * same reason stated there: near p = 1 the normal one produces bounds above 1).
27
34
  *
28
- * ./scripts-run src/scripts/measure_skill_ranker_baseline --json
29
- * ./scripts-run src/scripts/measure_skill_ranker_baseline --ranker keyword-v2 --json
35
+ * Below {@link MIN_POWERED_N} labelled rows the arm's `verdict` is
36
+ * `underpowered`. That is not a failure and not an exit code — the point
37
+ * estimate and the interval are still printed, because suppressing them would
38
+ * replace a wide measurement with no measurement. It is a label that travels
39
+ * with the number so a later reader cannot quote the point estimate alone.
30
40
  *
31
- * A 26-prompt denominator is small and is stated as such in the output. It is
32
- * the whole labelled ground truth this repository has.
41
+ * Usage:
42
+ * ./scripts-run src/scripts/measure_skill_ranker_baseline
43
+ * ./scripts-run src/scripts/measure_skill_ranker_baseline --corpus routing-matrix
44
+ * ./scripts-run src/scripts/measure_skill_ranker_baseline --corpus all
45
+ * ./scripts-run src/scripts/measure_skill_ranker_baseline --ranker keyword-v2
33
46
  */
34
47
 
35
48
  import * as fs from 'node:fs';
36
49
  import * as path from 'node:path';
37
50
  import { fileURLToPath } from 'node:url';
38
51
 
52
+ import { wilsonInterval } from './_lib/capture_rate.js';
39
53
  import { rank } from './skill_tools/score_skill_relevance.js';
40
54
  import type { RankOptions } from '../shared/skillRanking.js';
41
55
 
@@ -45,6 +59,20 @@ const MATRIX_DIR = 'tests/eval/routing-matrix';
45
59
  /** Where the projected skills live in this checkout. */
46
60
  export const SKILLS_DIR = path.join(REPO, 'src', 'skills');
47
61
 
62
+ /**
63
+ * The smallest labelled corpus this report will call powered.
64
+ *
65
+ * 100 is the roadmap's number, not a derived optimum, and it is stated as such:
66
+ * at n = 100 a point estimate near 0.7 carries a 95 % Wilson half-width of
67
+ * roughly ±9 points, which is wide but no longer moves on a single prompt the
68
+ * way a 26-row denominator does. Raise it with a dated reason if a decision
69
+ * ever needs a tighter bound than that.
70
+ */
71
+ export const MIN_POWERED_N = 100;
72
+
73
+ export type CorpusName = 'labelled' | 'routing-matrix' | 'all';
74
+ export const CORPUS_NAMES: readonly CorpusName[] = ['labelled', 'routing-matrix', 'all'];
75
+
48
76
  export interface LabelledPrompt {
49
77
  id: string;
50
78
  corpus: string;
@@ -94,38 +122,193 @@ export function readLabelledPrompts(repo = REPO): LabelledPrompt[] {
94
122
  return out;
95
123
  }
96
124
 
97
- /** Every `- prompt:` string in the rule routing matrix. No skill label exists. */
98
- export function readMatrixPrompts(repo = REPO): string[] {
125
+ export interface MatrixCase {
126
+ rule: string;
127
+ section: 'positives' | 'near_misses';
128
+ ordinal: number;
129
+ prompt: string;
130
+ /** `undefined` = the key is missing (a defect the lint catches); `[]` = deliberately unlabelled. */
131
+ expected?: string[];
132
+ }
133
+
134
+ /**
135
+ * Every case in the rule routing matrix, with its `expected_skills` label when
136
+ * one is present.
137
+ *
138
+ * Line-oriented for the same reason as the reader above, and because a YAML
139
+ * round-trip through this directory would reformat 100+ hand-kept fixtures.
140
+ * A case is `prompt:` plus the keys indented under it up to the next `- `.
141
+ */
142
+ export function readMatrixCases(repo = REPO): MatrixCase[] {
99
143
  const dir = path.join(repo, MATRIX_DIR);
100
144
  if (!fs.existsSync(dir)) return [];
101
- const out: string[] = [];
145
+ const out: MatrixCase[] = [];
102
146
  for (const name of fs.readdirSync(dir).sort()) {
103
147
  if (!name.endsWith('.yaml')) continue;
148
+ const rule = name.replace(/\.yaml$/, '');
149
+ let section: MatrixCase['section'] = 'positives';
150
+ let ordinal = 0;
151
+ let current: MatrixCase | undefined;
104
152
  for (const raw of fs.readFileSync(path.join(dir, name), 'utf8').split('\n')) {
105
- const m = /^\s*-?\s*prompt:\s*"(.*)"\s*$/.exec(raw.replace(/\r$/, ''));
106
- if (m && m[1]) out.push(m[1]);
153
+ const line = raw.replace(/\r$/, '');
154
+ if (/^positives:\s*$/.test(line)) {
155
+ section = 'positives';
156
+ ordinal = 0;
157
+ current = undefined;
158
+ continue;
159
+ }
160
+ if (/^near_misses:\s*$/.test(line)) {
161
+ section = 'near_misses';
162
+ ordinal = 0;
163
+ current = undefined;
164
+ continue;
165
+ }
166
+ const mPrompt = /^\s*-?\s*prompt:\s*"(.*)"\s*$/.exec(line);
167
+ if (mPrompt && mPrompt[1] !== undefined) {
168
+ current = { rule, section, ordinal: ordinal++, prompt: mPrompt[1] };
169
+ out.push(current);
170
+ continue;
171
+ }
172
+ const mExp = /^\s*expected_skills:\s*\[(.*)\]\s*$/.exec(line);
173
+ if (mExp && current) {
174
+ current.expected = mExp[1]!
175
+ .split(',')
176
+ .map((s) => s.trim())
177
+ .filter(Boolean);
178
+ }
107
179
  }
108
180
  }
109
181
  return out;
110
182
  }
111
183
 
184
+ /** Every `- prompt:` string in the rule routing matrix, label or no label. */
185
+ export function readMatrixPrompts(repo = REPO): string[] {
186
+ return readMatrixCases(repo).map((c) => c.prompt);
187
+ }
188
+
189
+ /** The matrix rows that carry a NON-EMPTY label — the accuracy denominator. */
190
+ export function readMatrixLabelledPrompts(repo = REPO): LabelledPrompt[] {
191
+ return readMatrixCases(repo)
192
+ .filter((c) => (c.expected?.length ?? 0) > 0)
193
+ .map((c) => ({
194
+ id: `${c.rule}#${c.section}[${c.ordinal}]`,
195
+ corpus: 'routing-matrix',
196
+ prompt: c.prompt,
197
+ expected: c.expected!,
198
+ }));
199
+ }
200
+
201
+ export function promptsForCorpus(corpus: CorpusName, repo = REPO): LabelledPrompt[] {
202
+ if (corpus === 'labelled') return readLabelledPrompts(repo);
203
+ if (corpus === 'routing-matrix') return readMatrixLabelledPrompts(repo);
204
+ return [...readLabelledPrompts(repo), ...readMatrixLabelledPrompts(repo)];
205
+ }
206
+
207
+ /**
208
+ * The packs a skill declares, read from its own `packs:` frontmatter block.
209
+ *
210
+ * Line-oriented, and deliberately NOT read from `src/packs/*\/pack.yaml`: those
211
+ * manifests are generated FROM this frontmatter, and four of them
212
+ * (`analytics`, `core`, `memory`, `product-reasoning`) carry no skill at all.
213
+ * A pack with no skill can never own a labelled prompt, so a census over the
214
+ * manifest set would state a floor nothing could ever meet.
215
+ */
216
+ export function packsForSkill(skillsDir: string, name: string): string[] {
217
+ const file = path.join(skillsDir, name, 'SKILL.md');
218
+ if (!fs.existsSync(file)) return [];
219
+ const text = fs.readFileSync(file, 'utf8');
220
+ if (!text.startsWith('---')) return [];
221
+ const end = text.indexOf('\n---', 3);
222
+ if (end === -1) return [];
223
+ const lines = text.slice(3, end).split('\n');
224
+ const at = lines.findIndex((l) => /^packs:/u.test(l));
225
+ if (at === -1) return [];
226
+ const out: string[] = [];
227
+ for (let i = at + 1; i < lines.length; i += 1) {
228
+ const m = /^\s+-\s+(\S+)\s*$/u.exec(lines[i] as string);
229
+ if (!m) break;
230
+ out.push(m[1] as string);
231
+ }
232
+ return out;
233
+ }
234
+
235
+ /** Every pack id that at least one shipped skill declares. The census domain. */
236
+ export function allPacks(skillsDir: string): string[] {
237
+ const packs = new Set<string>();
238
+ for (const entry of fs.readdirSync(skillsDir, { withFileTypes: true })) {
239
+ if (!entry.isDirectory()) continue;
240
+ for (const p of packsForSkill(skillsDir, entry.name)) packs.add(p);
241
+ }
242
+ return [...packs].sort();
243
+ }
244
+
245
+ /**
246
+ * How many labelled prompts each pack owns, via the packs of its expected skills.
247
+ *
248
+ * A prompt counts ONCE per pack however many of that pack's skills it expects —
249
+ * otherwise a single prompt naming three `laravel` skills would satisfy a
250
+ * three-prompt floor on its own, which is the shape of coverage this census
251
+ * exists to refuse.
252
+ */
253
+ export function packCensus(prompts: readonly LabelledPrompt[], skillsDir: string): Record<string, number> {
254
+ const out: Record<string, number> = {};
255
+ for (const p of allPacks(skillsDir)) out[p] = 0;
256
+ for (const prompt of prompts) {
257
+ const packs = new Set<string>();
258
+ for (const skill of prompt.expected) for (const pack of packsForSkill(skillsDir, skill)) packs.add(pack);
259
+ for (const pack of packs) out[pack] = (out[pack] ?? 0) + 1;
260
+ }
261
+ return out;
262
+ }
263
+
264
+ /** Packs owning fewer than `floor` labelled prompts, sorted by pack id. */
265
+ export function packsBelow(census: Record<string, number>, floor: number): string[] {
266
+ return Object.keys(census)
267
+ .filter((p) => (census[p] ?? 0) < floor)
268
+ .sort();
269
+ }
270
+
271
+ /** The per-pack floor `road-to-a-menu-whose-precision-is-measured` 1.1 states. */
272
+ export const MIN_PROMPTS_PER_PACK = 3;
273
+
274
+ export interface Interval {
275
+ lower: number;
276
+ upper: number;
277
+ }
278
+
112
279
  export interface AccuracyArm {
280
+ corpus: CorpusName;
113
281
  corpus_prompts: number;
114
282
  top1: number;
283
+ top1_ci95: Interval;
115
284
  top3: number;
285
+ top3_ci95: Interval;
286
+ /** `underpowered` below MIN_POWERED_N labelled rows; `measured` at or above. */
287
+ verdict: 'underpowered' | 'measured';
288
+ min_powered_n: number;
116
289
  misses: string[];
117
290
  denominator_note: string;
291
+ /** Pack ids at least one shipped skill declares — the census domain. */
292
+ packs_total: number;
293
+ min_prompts_per_pack: number;
294
+ /** Packs this corpus leaves under the floor. Empty is the passing shape. */
295
+ packs_below_floor: string[];
296
+ /** Full census, so a reader can see the distribution and not just the failures. */
297
+ pack_census: Record<string, number>;
118
298
  }
119
299
 
120
300
  export interface CoverageArm {
121
301
  corpus_prompts: number;
302
+ labelled_prompts: number;
303
+ unlabelled_prompts: number;
304
+ missing_label_key: number;
122
305
  prompts_with_any_result: number;
123
306
  mean_top_score: number;
124
307
  note: string;
125
308
  }
126
309
 
127
310
  export interface RankerBaseline {
128
- schema: 1;
311
+ schema: 2;
129
312
  ranker: string;
130
313
  commit: string;
131
314
  skills_dir: string;
@@ -139,28 +322,85 @@ function hitAt(rows: readonly (readonly [string, number, string[]])[], n: number
139
322
  return expected.some((e) => top.includes(e));
140
323
  }
141
324
 
142
- export function measure(opts: { ranker: string; commit: string; skillsDir?: string; repo?: string }): RankerBaseline {
143
- const repo = opts.repo ?? REPO;
144
- const skillsDir = opts.skillsDir ?? SKILLS_DIR;
145
- // `keyword-v2` is Phase 3.1: the same formula with `triggers:` prose folded
146
- // into each skill's term source. Any other label measures v1.
147
- const rankOpts: RankOptions = opts.ranker === 'keyword-v2' ? { includeTriggers: true } : {};
148
- const labelled = readLabelledPrompts(repo);
325
+ function round3(n: number): number {
326
+ return Math.round(n * 1000) / 1000;
327
+ }
328
+
329
+ function ci(successes: number, trials: number): Interval {
330
+ const raw = wilsonInterval(successes, trials);
331
+ return { lower: round3(raw.lower), upper: round3(raw.upper) };
332
+ }
333
+
334
+ const DENOMINATOR_NOTES: Record<CorpusName, string> = {
335
+ labelled:
336
+ 'tests/eval/corpus-dev.yaml + corpus-non-dev.yaml — the two hand-written eval corpora. ' +
337
+ 'Small; a single prompt moves each rate by ~4 points, which is why the interval is ' +
338
+ 'printed beside the point estimate and not instead of it.',
339
+ 'routing-matrix':
340
+ 'tests/eval/routing-matrix/ rows whose expected_skills list is NON-EMPTY. A row with an ' +
341
+ 'empty list is a deliberate "no skill expectation" and is excluded from the ' +
342
+ 'denominator — never counted as a miss.',
343
+ all:
344
+ 'Both labelled sources pooled. The two were written under different protocols, so a ' +
345
+ 'pooled rate is a convenience reading and the per-corpus arms are the citable ones.',
346
+ };
347
+
348
+ export function measureAccuracy(opts: {
349
+ corpus: CorpusName;
350
+ repo: string;
351
+ skillsDir: string;
352
+ rankOpts: RankOptions;
353
+ }): AccuracyArm {
354
+ const labelled = promptsForCorpus(opts.corpus, opts.repo);
149
355
  let top1 = 0;
150
356
  let top3 = 0;
151
357
  const misses: string[] = [];
152
358
  for (const p of labelled) {
153
- const rows = rank(p.prompt, skillsDir, rankOpts);
359
+ const rows = rank(p.prompt, opts.skillsDir, opts.rankOpts);
154
360
  if (hitAt(rows, 1, p.expected)) top1++;
155
361
  if (hitAt(rows, 3, p.expected)) top3++;
156
362
  else misses.push(p.id);
157
363
  }
364
+ const n = labelled.length;
365
+ const census = packCensus(labelled, opts.skillsDir);
366
+ return {
367
+ corpus: opts.corpus,
368
+ corpus_prompts: n,
369
+ top1: n ? round3(top1 / n) : 0,
370
+ top1_ci95: ci(top1, n),
371
+ top3: n ? round3(top3 / n) : 0,
372
+ top3_ci95: ci(top3, n),
373
+ verdict: n >= MIN_POWERED_N ? 'measured' : 'underpowered',
374
+ min_powered_n: MIN_POWERED_N,
375
+ misses,
376
+ denominator_note: DENOMINATOR_NOTES[opts.corpus],
377
+ packs_total: Object.keys(census).length,
378
+ min_prompts_per_pack: MIN_PROMPTS_PER_PACK,
379
+ packs_below_floor: packsBelow(census, MIN_PROMPTS_PER_PACK),
380
+ pack_census: census,
381
+ };
382
+ }
383
+
384
+ export function measure(opts: {
385
+ ranker: string;
386
+ commit: string;
387
+ corpus?: CorpusName;
388
+ skillsDir?: string;
389
+ repo?: string;
390
+ }): RankerBaseline {
391
+ const repo = opts.repo ?? REPO;
392
+ const skillsDir = opts.skillsDir ?? SKILLS_DIR;
393
+ // `keyword-v2` is Phase 3.1: the same formula with `triggers:` prose folded
394
+ // into each skill's term source. Any other label measures v1.
395
+ const rankOpts: RankOptions = opts.ranker === 'keyword-v2' ? { includeTriggers: true } : {};
396
+ const corpus = opts.corpus ?? 'labelled';
397
+ const accuracy = measureAccuracy({ corpus, repo, skillsDir, rankOpts });
158
398
 
159
- const matrix = readMatrixPrompts(repo);
399
+ const cases = readMatrixCases(repo);
160
400
  let withResult = 0;
161
401
  let scoreSum = 0;
162
- for (const prompt of matrix) {
163
- const rows = rank(prompt, skillsDir, rankOpts);
402
+ for (const c of cases) {
403
+ const rows = rank(c.prompt, skillsDir, rankOpts);
164
404
  if (rows.length > 0) {
165
405
  withResult++;
166
406
  scoreSum += rows[0]![1];
@@ -172,37 +412,51 @@ export function measure(opts: { ranker: string; commit: string; skillsDir?: stri
172
412
  : 0;
173
413
 
174
414
  return {
175
- schema: 1,
415
+ schema: 2,
176
416
  ranker: opts.ranker,
177
417
  commit: opts.commit,
178
418
  skills_dir: path.relative(repo, skillsDir),
179
419
  skills_indexed: skillsIndexed,
180
- accuracy: {
181
- corpus_prompts: labelled.length,
182
- top1: labelled.length ? Math.round((top1 / labelled.length) * 1000) / 1000 : 0,
183
- top3: labelled.length ? Math.round((top3 / labelled.length) * 1000) / 1000 : 0,
184
- misses,
185
- denominator_note:
186
- 'tests/eval/corpus-dev.yaml + corpus-non-dev.yaml — the only expected-skill ground ' +
187
- 'truth in this tree. Small; a single prompt moves each rate by ~4 points.',
188
- },
420
+ accuracy,
189
421
  matrix_coverage: {
190
- corpus_prompts: matrix.length,
422
+ corpus_prompts: cases.length,
423
+ labelled_prompts: cases.filter((c) => (c.expected?.length ?? 0) > 0).length,
424
+ unlabelled_prompts: cases.filter((c) => c.expected?.length === 0).length,
425
+ missing_label_key: cases.filter((c) => c.expected === undefined).length,
191
426
  prompts_with_any_result: withResult,
192
427
  mean_top_score: withResult ? Math.round((scoreSum / withResult) * 100) / 100 : 0,
193
428
  note:
194
- 'tests/eval/routing-matrix/ is labelled with the expected RULE, never a skill, so ' +
195
- 'this arm carries no accuracy — only whether the ranker answers at all and how ' +
196
- 'confident its top answer is. Its size is measured here, not assumed: the roadmap ' +
197
- 'and five other sites still publish 496.',
429
+ 'Coverage over EVERY matrix case, labelled or not: whether the ranker answers at ' +
430
+ 'all and how confident its top answer is. Accuracy over the labelled subset is ' +
431
+ 'the `accuracy` arm under `--corpus routing-matrix`. Size is measured here, never ' +
432
+ 'asserted: 496 and 499 are both published elsewhere and both stale.',
198
433
  },
199
434
  };
200
435
  }
201
436
 
437
+ function parseCorpus(argv: readonly string[]): CorpusName {
438
+ const i = argv.indexOf('--corpus');
439
+ if (i === -1) return 'labelled';
440
+ const raw = argv[i + 1];
441
+ if (!raw || !CORPUS_NAMES.includes(raw as CorpusName)) {
442
+ throw new Error(
443
+ `measure_skill_ranker_baseline: --corpus expects one of ${CORPUS_NAMES.join(' | ')}, got ${raw ?? '(nothing)'}`,
444
+ );
445
+ }
446
+ return raw as CorpusName;
447
+ }
448
+
202
449
  export function main(argv: readonly string[]): number {
203
450
  const ranker = argv.includes('--ranker') ? (argv[argv.indexOf('--ranker') + 1] ?? 'keyword-v1') : 'keyword-v1';
204
451
  const commit = argv.includes('--commit') ? (argv[argv.indexOf('--commit') + 1] ?? 'unknown') : 'unknown';
205
- const out = measure({ ranker, commit });
452
+ let corpus: CorpusName;
453
+ try {
454
+ corpus = parseCorpus(argv);
455
+ } catch (err) {
456
+ process.stderr.write(`${(err as Error).message}\n`);
457
+ return 2;
458
+ }
459
+ const out = measure({ ranker, commit, corpus });
206
460
  process.stdout.write(`${JSON.stringify(out, null, 2)}\n`);
207
461
  return 0;
208
462
  }