@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,238 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * bench_touched_file_quality — what the shadow quality pass costs at stop.
4
+ *
5
+ * `road-to-touched-files-that-pass-their-own-tools` 1.4: record the `shadow`
6
+ * variant's p95 over a five-file TypeScript fixture BEFORE any default moves.
7
+ * Risk 1 of that roadmap is that a quality pass makes stop the slowest hook in
8
+ * the chain, and 2.3 defers the `shadow → warn` flip to the owner. Neither
9
+ * decision should be taken on an estimate.
10
+ *
11
+ * CORRECTED FROM REPRODUCTION — the step's premise was wrong, and the
12
+ * correction is why this is a file rather than a patch.
13
+ *
14
+ * The step reads "the per-concern bench already measures `verify-before-complete`
15
+ * at default". It does not. `bench_hook_latency.perConcernRows` is fed by
16
+ * `blockingConcerns()`, which filters `hook_manifest.yaml` to
17
+ * `severity: blocking`; `verify-before-complete` is declared `severity: advisory`
18
+ * (manifest line ~122), so it has never had a per-concern row and there was no
19
+ * `default` reading to add a `shadow` variant beside. Adding the concern to that
20
+ * filter would have changed what "blocking" means in a report whose consumer
21
+ * (step 3.3 of the kernel-plumbing roadmap) derives per-concern TIMEOUTS from it
22
+ * — a timeout for an advisory concern is a different decision, and smuggling it
23
+ * in through a measurement would have been the wrong place to take it.
24
+ *
25
+ * So this measures the pass directly, which is also the number the two open
26
+ * decisions actually need: the slot p95 in `docs/hook-latency.json` is dominated
27
+ * by spawn + bundle load, and what 1.4 asks about is the DELTA the pass adds.
28
+ *
29
+ * METHOD, pinned so the number reproduces:
30
+ * - A throwaway consumer-shaped project: `package.json` declaring `typescript`
31
+ * and `eslint`, five TypeScript files, a flat ESLint config, `git init`, and
32
+ * a `minimal-safe-diff.json` naming all five as this turn's touched files.
33
+ * - `node_modules` is SYMLINKED to this repository's, so `npx eslint` resolves
34
+ * a real, installed linter and runs offline. The measurement is therefore of
35
+ * a real tool over real files, not of a stub.
36
+ * - N iterations (default 20, `--runs N`) of `collectTouchedFileQuality` — the
37
+ * exact function the stop path calls — measured with `performance.now()`.
38
+ * That covers the recorder read, `git status`, `resolve_toolchain`, and every
39
+ * spawn the plan produced.
40
+ * - The `off` baseline is measured too, by the same loop with the pass skipped,
41
+ * so the row reports a DELTA rather than an absolute whose floor is unknown.
42
+ *
43
+ * WHAT THE NUMBER IS NOT. It is one machine's reading, and the dominant term is
44
+ * whichever tools the fixture's resolver happens to emit — a consumer whose
45
+ * toolchain resolves `phpstan` over a large tree will measure something else
46
+ * entirely. It bounds the decision for THIS shape; it is not a budget.
47
+ *
48
+ * Exit codes: 0 measured · 2 the fixture could not be built.
49
+ */
50
+ import { spawnSync } from 'node:child_process';
51
+ import * as fs from 'node:fs';
52
+ import * as os from 'node:os';
53
+ import * as path from 'node:path';
54
+ import { fileURLToPath } from 'node:url';
55
+
56
+ import { collectTouchedFileQuality } from './before_complete_hook.js';
57
+
58
+ const REPO_ROOT = path.resolve(path.dirname(path.resolve(fileURLToPath(import.meta.url))), '..', '..');
59
+
60
+ /** Files in the fixture. Five, per the step. */
61
+ export const FIXTURE_FILE_COUNT = 5;
62
+
63
+ const SESSION = 'bench-touched-file-quality';
64
+
65
+ export function percentile(samples: readonly number[], p: number): number {
66
+ if (samples.length === 0) return 0;
67
+ const sorted = [...samples].sort((a, b) => a - b);
68
+ const idx = Math.min(sorted.length - 1, Math.ceil((p / 100) * sorted.length) - 1);
69
+ return sorted[Math.max(0, idx)] as number;
70
+ }
71
+
72
+ /**
73
+ * Build the throwaway consumer project and return its root.
74
+ *
75
+ * Deliberately NOT inside the repository: a fixture under `src/` or `agents/`
76
+ * would be picked up by the tree's own gates, and one that `git init`s inside an
77
+ * existing worktree would report the repository's status rather than its own.
78
+ */
79
+ export function buildFixture(): string {
80
+ const root = fs.mkdtempSync(path.join(os.tmpdir(), 'tfq-bench-'));
81
+
82
+ fs.writeFileSync(
83
+ path.join(root, 'package.json'),
84
+ JSON.stringify(
85
+ { name: 'tfq-bench-fixture', private: true, devDependencies: { typescript: '*', eslint: '*' } },
86
+ null,
87
+ 2,
88
+ ),
89
+ 'utf-8',
90
+ );
91
+ // A flat config with no rules: eslint still parses every file, which is the
92
+ // cost being measured, without a rule set that would make the number a
93
+ // statement about this repository's lint configuration.
94
+ fs.writeFileSync(path.join(root, 'eslint.config.mjs'), 'export default [];\n', 'utf-8');
95
+
96
+ for (let i = 0; i < FIXTURE_FILE_COUNT; i += 1) {
97
+ fs.writeFileSync(
98
+ path.join(root, `mod${String(i)}.ts`),
99
+ `export interface Row${String(i)} { id: number; name: string }\n` +
100
+ `export function build${String(i)}(n: number): Row${String(i)}[] {\n` +
101
+ ` return Array.from({ length: n }, (_, j) => ({ id: j, name: 'r' + String(j) }));\n` +
102
+ `}\n`,
103
+ 'utf-8',
104
+ );
105
+ }
106
+
107
+ try {
108
+ fs.symlinkSync(path.join(REPO_ROOT, 'node_modules'), path.join(root, 'node_modules'), 'dir');
109
+ } catch {
110
+ // Left absent on a platform that refuses the link: `npx eslint` then
111
+ // resolves to nothing and the pass records `skipped: absent`, which the
112
+ // report states rather than hides.
113
+ }
114
+
115
+ spawnSync('git', ['init', '-q'], { cwd: root });
116
+
117
+ fs.mkdirSync(path.join(root, 'agents', 'state'), { recursive: true });
118
+ fs.writeFileSync(
119
+ path.join(root, 'agents', 'state', 'minimal-safe-diff.json'),
120
+ JSON.stringify({
121
+ session_id: SESSION,
122
+ files_touched_this_turn: Array.from(
123
+ { length: FIXTURE_FILE_COUNT },
124
+ (_, i) => `mod${String(i)}.ts`,
125
+ ),
126
+ }),
127
+ 'utf-8',
128
+ );
129
+
130
+ return root;
131
+ }
132
+
133
+ export interface BenchRow {
134
+ readonly concern: string;
135
+ readonly mode: 'off' | 'shadow';
136
+ readonly runs: number;
137
+ readonly p50_ms: number;
138
+ readonly p95_ms: number;
139
+ readonly max_ms: number;
140
+ /** What the pass actually did, so a row of zeros is never mistaken for speed. */
141
+ readonly commands: readonly string[];
142
+ readonly skipped: readonly string[];
143
+ }
144
+
145
+ export function measure(root: string, iterations: number): { off: BenchRow; shadow: BenchRow } {
146
+ const offSamples: number[] = [];
147
+ const shadowSamples: number[] = [];
148
+ let commands: string[] = [];
149
+ let skipped: string[] = [];
150
+
151
+ // One warm-up outside the samples: the first `npx` resolution pays a cost no
152
+ // subsequent stop pays, and reporting it inside the p95 would describe a
153
+ // session's first stop as if it were every stop.
154
+ collectTouchedFileQuality(root, SESSION);
155
+
156
+ for (let i = 0; i < iterations; i += 1) {
157
+ const t0 = performance.now();
158
+ const result = collectTouchedFileQuality(root, SESSION);
159
+ shadowSamples.push(performance.now() - t0);
160
+ if (i === 0 && result !== null) {
161
+ commands = result.runs.filter((r) => r.skipped === null).map((r) => r.command);
162
+ skipped = result.runs
163
+ .filter((r) => r.skipped !== null)
164
+ .map((r) => `${r.source_command} → ${String(r.skipped)}`);
165
+ }
166
+
167
+ // `off` is not "the same loop with a flag" — on the off path the stop
168
+ // handler never reaches this function at all, so the baseline is the
169
+ // empty interval, measured to keep the harness's own overhead visible.
170
+ const t1 = performance.now();
171
+ offSamples.push(performance.now() - t1);
172
+ }
173
+
174
+ const row = (mode: 'off' | 'shadow', xs: number[]): BenchRow => ({
175
+ concern: 'verify-before-complete',
176
+ mode,
177
+ runs: xs.length,
178
+ p50_ms: Math.round(percentile(xs, 50) * 1000) / 1000,
179
+ p95_ms: Math.round(percentile(xs, 95) * 1000) / 1000,
180
+ max_ms: Math.round(Math.max(...xs, 0) * 1000) / 1000,
181
+ commands: mode === 'shadow' ? commands : [],
182
+ skipped: mode === 'shadow' ? skipped : [],
183
+ });
184
+
185
+ return { off: row('off', offSamples), shadow: row('shadow', shadowSamples) };
186
+ }
187
+
188
+ export function render(rows: { off: BenchRow; shadow: BenchRow }): string {
189
+ const line = (r: BenchRow): string =>
190
+ `| ${r.concern} | ${r.mode} | ${String(r.runs)} | ${r.p50_ms.toFixed(3)} | ` +
191
+ `${r.p95_ms.toFixed(3)} | ${r.max_ms.toFixed(3)} |`;
192
+ const out: string[] = [
193
+ '',
194
+ `fixture: ${String(FIXTURE_FILE_COUNT)} TypeScript files, consumer-shaped project`,
195
+ '',
196
+ '| concern | mode | runs | p50_ms | p95_ms | max_ms |',
197
+ '|---|---|---|---|---|---|',
198
+ line(rows.off),
199
+ line(rows.shadow),
200
+ '',
201
+ `delta p95 (shadow − off): ${(rows.shadow.p95_ms - rows.off.p95_ms).toFixed(3)} ms`,
202
+ '',
203
+ `commands executed: ${rows.shadow.commands.length === 0 ? '(none)' : rows.shadow.commands.join(' · ')}`,
204
+ `skipped: ${rows.shadow.skipped.length === 0 ? '(none)' : rows.shadow.skipped.join(' · ')}`,
205
+ '',
206
+ ];
207
+ return out.join('\n');
208
+ }
209
+
210
+ export function main(argv: readonly string[]): number {
211
+ const runsIdx = argv.indexOf('--runs');
212
+ const parsed = runsIdx === -1 ? NaN : Number(argv[runsIdx + 1]);
213
+ const iterations = Number.isFinite(parsed) && parsed > 0 ? Math.trunc(parsed) : 20;
214
+ const asJson = argv.includes('--json');
215
+
216
+ let root: string;
217
+ try {
218
+ root = buildFixture();
219
+ } catch (err) {
220
+ process.stderr.write(`bench_touched_file_quality: could not build the fixture — ${String(err)}\n`);
221
+ return 2;
222
+ }
223
+
224
+ const rows = measure(root, iterations);
225
+ process.stdout.write(asJson ? `${JSON.stringify(rows, null, 2)}\n` : render(rows));
226
+
227
+ try {
228
+ fs.rmSync(root, { recursive: true, force: true });
229
+ } catch {
230
+ // A leftover temp directory is not a failed measurement.
231
+ }
232
+ return 0;
233
+ }
234
+
235
+ const _entry = process.argv[1];
236
+ if (_entry !== undefined && path.resolve(_entry) === path.resolve(fileURLToPath(import.meta.url))) {
237
+ process.exit(main(process.argv.slice(2)));
238
+ }
@@ -44,6 +44,10 @@ import {
44
44
  DEFAULT_PROJECTS_ROOT,
45
45
  projectStoreSlug,
46
46
  scanTranscripts,
47
+ measureBytesPerToken,
48
+ derivedBytes,
49
+ type BytesPerToken,
50
+ type DerivedBytes,
47
51
  type BucketAggregate,
48
52
  type TranscriptBucket,
49
53
  type TranscriptRecord,
@@ -764,11 +768,38 @@ export interface Report {
764
768
  council_mispricing: CouncilMispricingResult;
765
769
  worktree_fragmentation: WorktreeFragmentationResult;
766
770
  prefix_stability: PrefixStabilityResult;
771
+ /**
772
+ * Provider bytes per bucket — DERIVED, and labelled so on every value
773
+ * (`road-to-a-bytes-row-that-exists` Phase 2, metric
774
+ * `provider-bytes-per-call`).
775
+ *
776
+ * `null` when this corpus supplies no bytes-per-token factor. That is the
777
+ * honest outcome and it is emitted as `null` rather than as a figure from
778
+ * some other corpus: the factor is a property of the content it was
779
+ * measured on, and no constant lives in this file or in
780
+ * `_lib/cc_transcript.ts` for it to fall back to.
781
+ *
782
+ * LIMIT, stated where the number is rather than only in the registry: the
783
+ * factor is measured over ASSISTANT OUTPUT content, which is the only text
784
+ * a transcript carries. The request's system prompt, tool definitions and
785
+ * prior turns are not in the transcript at all (the same limitation
786
+ * `preamble_byte_census` records), so the factor is an output-side ratio
787
+ * applied to an input-side token count. That is why the value is `derived`
788
+ * and not `measured`, and why it must never be quoted as a wire figure.
789
+ */
790
+ provider_bytes: {
791
+ factor: BytesPerToken | null;
792
+ main: DerivedBytes | null;
793
+ subagent: DerivedBytes | null;
794
+ };
767
795
  claims: Claim[];
768
796
  }
769
797
 
770
798
  export function buildReport(opts: Options): Report {
771
- const scan = scanTranscripts({ root: opts.root, maxAgeDays: opts.maxAgeDays });
799
+ // `measureBytes` is the only change to this scan: the token columns below
800
+ // are computed from the same `usage` blocks as before and are unaffected.
801
+ const scan = scanTranscripts({ root: opts.root, maxAgeDays: opts.maxAgeDays, measureBytes: true });
802
+ const bytesFactor = measureBytesPerToken(scan.records);
772
803
  const buckets = aggregateByBucket(scan.records);
773
804
  const subagentColdStart = computeColdStarts(scan.records);
774
805
  const byAgent = aggregateByAgent(scan.records);
@@ -822,6 +853,11 @@ export function buildReport(opts: Options): Report {
822
853
  council_mispricing: councilMispricing,
823
854
  worktree_fragmentation: worktreeFragmentation,
824
855
  prefix_stability: prefixStability,
856
+ provider_bytes: {
857
+ factor: bytesFactor,
858
+ main: derivedBytes(billableInputTokens(buckets.main), bytesFactor),
859
+ subagent: derivedBytes(billableInputTokens(buckets.subagent), bytesFactor),
860
+ },
825
861
  claims,
826
862
  };
827
863
  }
@@ -851,6 +887,30 @@ export function renderText(r: Report): string {
851
887
  out.push(` ${ps.reason}`);
852
888
  out.push('');
853
889
 
890
+ const pb = r.provider_bytes;
891
+ out.push('Provider bytes (DERIVED - never a counted wire figure):');
892
+ if (pb.factor === null) {
893
+ out.push(' basis: unavailable - this corpus supplied no bytes-per-token factor');
894
+ out.push(' no figure is emitted; a ratio from another corpus would not be this one');
895
+ } else {
896
+ out.push(
897
+ ` factor: ${pb.factor.bytes_per_token.toFixed(4)} bytes/token (basis: ${pb.factor.basis},`
898
+ + ` ${pb.factor.content_bytes} content bytes / ${pb.factor.output_tokens} output tokens`
899
+ + ` over ${pb.factor.records} records)`,
900
+ );
901
+ for (const bucket of ['main', 'subagent'] as const) {
902
+ const d = pb[bucket];
903
+ out.push(
904
+ d === null
905
+ ? ` ${bucket}: basis: unavailable`
906
+ : ` ${bucket}: ${d.bytes} bytes (basis: ${d.basis}, from ${d.tokens} billable input tokens)`,
907
+ );
908
+ }
909
+ out.push(' the factor is measured over assistant OUTPUT content; the request payload is');
910
+ out.push(' not in any transcript, so this is an estimate of volume, not a wire measurement');
911
+ }
912
+ out.push('');
913
+
854
914
  out.push('By bucket:');
855
915
  for (const bucket of ['main', 'subagent'] as const) {
856
916
  const b = r.buckets[bucket];
@@ -0,0 +1,287 @@
1
+ #!/usr/bin/env tsx
2
+ /**
3
+ * Where an artifact declares `inputs:`, its `argument-hint` is DERIVED from
4
+ * that declaration — and a hand-written hint on the same artifact is a
5
+ * conflict, not a merge.
6
+ *
7
+ * road-to-an-invocation-contract-that-reaches-the-wire 3.1.
8
+ *
9
+ * WHY A CONFLICT RATHER THAN A MERGE
10
+ *
11
+ * The tree already answers "what does this take?" in prose, through
12
+ * `argument-hint`, which nothing validates. Adding a structured declaration
13
+ * beside it creates a SECOND authority for the same question, and two surfaces
14
+ * that can answer one question drift — which is the failure this whole roadmap
15
+ * exists to make unrepresentable (Risk 1). Making the derived direction
16
+ * one-way, and a disagreement a build failure, is what keeps the second
17
+ * authority from forming. Merging the two would BE the second authority.
18
+ *
19
+ * An artifact with no declaration is untouched: its hand-written hint stays
20
+ * exactly as it is, because there is nothing to derive from and no conflict to
21
+ * have. That is why the 43 hintless commands are a generation gap that closes
22
+ * as declarations land, rather than 43 prose edits nobody reviewed.
23
+ *
24
+ * THE FORM, AND WHY IT ROUND-TRIPS
25
+ *
26
+ * `<name>` required · `[name]` optional · `:a|b` appended for an enum. The
27
+ * corpus already writes `[path]` and `[--force]` in exactly this shape, so the
28
+ * generated hint is not a new dialect. {@link parseHint} recovers the name,
29
+ * requiredness and enum from the rendered string, and the round-trip is
30
+ * asserted rather than assumed — a generator whose output its own parser cannot
31
+ * read would make the conflict check unfalsifiable.
32
+ *
33
+ * Exit codes: 0 in sync · 1 a conflict or a missing derived hint · 2 unreadable.
34
+ */
35
+
36
+ import fs from 'node:fs';
37
+ import os from 'node:os';
38
+ import path from 'node:path';
39
+ import { fileURLToPath, pathToFileURL } from 'node:url';
40
+
41
+ import * as yaml from 'js-yaml';
42
+
43
+ import { GateLedger } from './_lib/gate_ledger.js';
44
+ import { runGateCli, runSelfTest } from './_lib/gate_self_test.js';
45
+ import { assertScanned, reportScanned } from './_lib/scan_scope.js';
46
+
47
+ const _HERE = fileURLToPath(import.meta.url);
48
+ const ROOT = path.resolve(path.dirname(_HERE), '..', '..');
49
+
50
+ export const GATE = 'check_argument_hint';
51
+
52
+ export interface InputDecl {
53
+ readonly name: string;
54
+ readonly type: string;
55
+ readonly required?: boolean;
56
+ readonly enum?: readonly string[];
57
+ }
58
+
59
+ /** Render the canonical hint for a declaration. */
60
+ export function hintFor(inputs: readonly InputDecl[]): string {
61
+ return inputs
62
+ .map((i) => {
63
+ const body = i.enum && i.enum.length > 0 ? `${i.name}:${i.enum.join('|')}` : i.name;
64
+ return i.required === true ? `<${body}>` : `[${body}]`;
65
+ })
66
+ .join(' ');
67
+ }
68
+
69
+ export interface ParsedHint {
70
+ readonly name: string;
71
+ readonly required: boolean;
72
+ readonly enum?: readonly string[];
73
+ }
74
+
75
+ /** Recover declarations from a rendered hint. The inverse of {@link hintFor}. */
76
+ export function parseHint(hint: string): ParsedHint[] {
77
+ const out: ParsedHint[] = [];
78
+ for (const m of hint.matchAll(/([<[])([a-z][a-z0-9_]*)(?::([^\]>]+))?([\]>])/g)) {
79
+ const open = m[1]!;
80
+ const close = m[4]!;
81
+ if ((open === '<') !== (close === '>')) continue;
82
+ const parsed: ParsedHint = {
83
+ name: m[2]!,
84
+ required: open === '<',
85
+ ...(m[3] === undefined ? {} : { enum: m[3].split('|') }),
86
+ };
87
+ out.push(parsed);
88
+ }
89
+ return out;
90
+ }
91
+
92
+ /** Frontmatter of one artifact, or `null` when it has none or it is malformed. */
93
+ function frontmatter(text: string): Record<string, unknown> | null {
94
+ const m = /^---\n([\s\S]*?)\n---/.exec(text);
95
+ if (m === null) return null;
96
+ try {
97
+ const fm = yaml.load(m[1]!);
98
+ return fm !== null && typeof fm === 'object' ? (fm as Record<string, unknown>) : null;
99
+ } catch {
100
+ return null;
101
+ }
102
+ }
103
+
104
+ export function declaredInputs(fm: Record<string, unknown> | null): InputDecl[] | null {
105
+ if (fm === null) return null;
106
+ const raw = fm['inputs'];
107
+ if (!Array.isArray(raw)) return null;
108
+ const out: InputDecl[] = [];
109
+ for (const item of raw) {
110
+ if (item === null || typeof item !== 'object') continue;
111
+ const r = item as Record<string, unknown>;
112
+ if (typeof r['name'] !== 'string' || typeof r['type'] !== 'string') continue;
113
+ out.push({
114
+ name: r['name'],
115
+ type: r['type'],
116
+ ...(typeof r['required'] === 'boolean' ? { required: r['required'] } : {}),
117
+ ...(Array.isArray(r['enum'])
118
+ ? { enum: r['enum'].filter((x): x is string => typeof x === 'string') }
119
+ : {}),
120
+ });
121
+ }
122
+ return out;
123
+ }
124
+
125
+ export type FindingKind = 'conflict' | 'missing';
126
+
127
+ export interface Finding {
128
+ readonly file: string;
129
+ readonly kind: FindingKind;
130
+ readonly derived: string;
131
+ /** The hand-written hint, for a conflict. */
132
+ readonly written?: string;
133
+ }
134
+
135
+ /** Compare one artifact's declaration against its hint. */
136
+ export function checkArtifact(text: string, file: string): Finding | null {
137
+ const fm = frontmatter(text);
138
+ const inputs = declaredInputs(fm);
139
+ if (inputs === null || inputs.length === 0) return null;
140
+ const derived = hintFor(inputs);
141
+ const written = fm?.['argument-hint'];
142
+ if (typeof written !== 'string') return { file, kind: 'missing', derived };
143
+ if (written.trim() !== derived) return { file, kind: 'conflict', derived, written };
144
+ return null;
145
+ }
146
+
147
+ function walk(dir: string, match: (n: string) => boolean, out: string[] = []): string[] {
148
+ if (!fs.existsSync(dir)) return out;
149
+ for (const e of fs.readdirSync(dir, { withFileTypes: true })) {
150
+ const p = path.join(dir, e.name);
151
+ if (e.isDirectory()) walk(p, match, out);
152
+ else if (match(e.name)) out.push(p);
153
+ }
154
+ return out;
155
+ }
156
+
157
+ /**
158
+ * COMMANDS ONLY, and the reason is structural rather than a scoping preference:
159
+ * `argument-hint` is a key of `command.schema.json` and of no other schema. A
160
+ * skill cannot carry one, so reporting a skill that declares `inputs:` as
161
+ * "missing its hint" would demand a field the schema forbids. Skills still get
162
+ * their declaration onto the wire — that is `to_mcp_prompt_meta`'s job (3.2),
163
+ * which reads the declaration directly and needs no hint at all.
164
+ */
165
+ export function scan(root: string): { findings: Finding[]; scanned: number; declaring: number } {
166
+ const files = walk(path.join(root, 'src', 'domains'), (n) => n === 'command.md').sort();
167
+
168
+ const findings: Finding[] = [];
169
+ let declaring = 0;
170
+ for (const f of files) {
171
+ const text = fs.readFileSync(f, 'utf8');
172
+ const inputs = declaredInputs(frontmatter(text));
173
+ if (inputs !== null && inputs.length > 0) declaring += 1;
174
+ const finding = checkArtifact(text, path.relative(root, f));
175
+ if (finding !== null) findings.push(finding);
176
+ }
177
+ return { findings, scanned: files.length, declaring };
178
+ }
179
+
180
+ export function main(argv: readonly string[]): number {
181
+ if (argv.includes('--self-test')) return selfTest();
182
+ const rootIdx = argv.indexOf('--root');
183
+ const root = rootIdx === -1 ? ROOT : (argv[rootIdx + 1] ?? ROOT);
184
+
185
+ const { findings, scanned, declaring } = scan(root);
186
+ const ledger = new GateLedger(GATE);
187
+ ledger.plan(findings.map((f) => f.file));
188
+ for (const f of findings) ledger.fail(f.file, f.kind);
189
+
190
+ // A gate that read nothing has not passed. This fires BEFORE the verdict,
191
+ // because certifying against an empty corpus is the false green the coverage
192
+ // manifest exists to refuse — named by the provider-diverse ratification
193
+ // review as one condition that would have flipped its verdict.
194
+ assertScanned({ gate: GATE, scanned: scanned, units: 'artifact(s)', roots: ['src/domains'] });
195
+
196
+ reportScanned({
197
+ gate: GATE,
198
+ scanned,
199
+ units: 'artifact(s)',
200
+ roots: ['src/domains'],
201
+ });
202
+ ledger.report();
203
+
204
+ if (findings.length > 0) {
205
+ for (const f of findings) {
206
+ if (f.kind === 'conflict') {
207
+ process.stdout.write(
208
+ `❌ ${GATE}: ${f.file} has a hand-written \`argument-hint\` that contradicts ` +
209
+ `its declaration.\n written: ${f.written ?? ''}\n derived: ${f.derived}\n`,
210
+ );
211
+ } else {
212
+ process.stdout.write(
213
+ `❌ ${GATE}: ${f.file} declares \`inputs:\` but carries no \`argument-hint\`.\n` +
214
+ ` derived: ${f.derived}\n`,
215
+ );
216
+ }
217
+ }
218
+ process.stdout.write(
219
+ '\nWhere a declaration exists the hint is derived from it, one way. Fix the ' +
220
+ 'declaration, or delete the hand-written hint — never reconcile the two by ' +
221
+ 'hand, which is the second authority this check exists to prevent.\n',
222
+ );
223
+ return 1;
224
+ }
225
+
226
+ process.stdout.write(
227
+ `✅ ${GATE}: every derived \`argument-hint\` matches its declaration ` +
228
+ `(${String(declaring)} of ${String(scanned)} artifact(s) declare \`inputs:\`).\n`,
229
+ );
230
+ return 0;
231
+ }
232
+
233
+ /**
234
+ * A self-test is what proves a gate DISCRIMINATES. An enforced scan floor only
235
+ * proves it read something; the verdict has to move.
236
+ */
237
+ function selfTest(): number {
238
+ const plant = (name: string, fm: string): string => {
239
+ const dir = fs.mkdtempSync(path.join(os.tmpdir(), `arg-hint-${name}-`));
240
+ const cmd = path.join(dir, 'src', 'domains', 'probe-pack', 'probe');
241
+ fs.mkdirSync(cmd, { recursive: true });
242
+ fs.writeFileSync(path.join(cmd, 'command.md'), `---\nname: probe\n${fm}---\n\nbody\n`, 'utf-8');
243
+ return dir;
244
+ };
245
+
246
+ const run = (root: string): number =>
247
+ runGateCli(ROOT, path.join('src', 'scripts', 'check_argument_hint.ts'), ['--root', root], ROOT);
248
+
249
+ return runSelfTest({
250
+ gate: GATE,
251
+ minCases: 5,
252
+ minRejectCases: 3,
253
+ cases: [
254
+ {
255
+ name: 'an empty corpus is refused, not certified green',
256
+ expect: 'reject',
257
+ run: () => run(fs.mkdtempSync(path.join(os.tmpdir(), 'arg-hint-empty-'))),
258
+ },
259
+ {
260
+ name: 'a hint matching its declaration passes',
261
+ expect: 'accept',
262
+ run: () =>
263
+ run(plant('match', 'argument-hint: "<a>"\ninputs:\n - name: a\n type: string\n required: true\n')),
264
+ },
265
+ {
266
+ name: 'a hand-written hint contradicting the declaration fails',
267
+ expect: 'reject',
268
+ run: () =>
269
+ run(plant('conflict', 'argument-hint: "[other]"\ninputs:\n - name: a\n type: string\n required: true\n')),
270
+ },
271
+ {
272
+ name: 'a declaration with no hint at all fails',
273
+ expect: 'reject',
274
+ run: () => run(plant('missing', 'inputs:\n - name: a\n type: string\n')),
275
+ },
276
+ {
277
+ name: 'an artifact with no declaration keeps its free-text hint',
278
+ expect: 'accept',
279
+ run: () => run(plant('nodecl', 'argument-hint: "[free text]"\n')),
280
+ },
281
+ ],
282
+ });
283
+ }
284
+
285
+ if (process.argv[1] && import.meta.url === pathToFileURL(path.resolve(process.argv[1])).href) {
286
+ process.exit(main(process.argv.slice(2)));
287
+ }