@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
@@ -35,12 +35,14 @@
35
35
  */
36
36
 
37
37
  import * as fs from 'node:fs';
38
+ import * as os from 'node:os';
38
39
  import * as path from 'node:path';
39
40
  import { fileURLToPath, pathToFileURL } from 'node:url';
40
41
 
41
42
  import YAML from 'yaml';
42
43
 
43
- import { assertScanned, DeadScopeError } from './_lib/scan_scope.js';
44
+ import { runGateCli, runSelfTest } from './_lib/gate_self_test.js';
45
+ import { DeadScopeError, reportScanned } from './_lib/scan_scope.js';
44
46
  import { py_json_dumps_indent2 } from './_lib/security_lint.js';
45
47
 
46
48
  const _HERE = fileURLToPath(import.meta.url);
@@ -48,8 +50,16 @@ const _HERE = fileURLToPath(import.meta.url);
48
50
  const REPO_ROOT = path.resolve(path.dirname(_HERE), '..', '..');
49
51
  // Mutable bindings so tests can sandbox the scan target (mirrors the pytest
50
52
  // monkeypatch.setattr seam used by sibling lint twins).
51
- let WORKFLOWS_DIR = path.join(REPO_ROOT, '.github', 'workflows');
52
- let ALLOWLIST_PATH = path.join(path.dirname(_HERE), 'lint_workflow_security_allowlist.json');
53
+ //
54
+ // `--self-test` needs the same sandbox from a CHILD process, which cannot reach
55
+ // the in-process seam, so the two paths are also readable from the environment.
56
+ // Deliberately env and not argv: the CLI surface is pinned (argparse flags,
57
+ // byte-identical usage lines, unknown arg → exit 2), and a scan root is not
58
+ // something a caller should be able to redirect by typing a flag.
59
+ let WORKFLOWS_DIR = process.env['LINT_WORKFLOW_SECURITY_DIR'] ?? path.join(REPO_ROOT, '.github', 'workflows');
60
+ let ALLOWLIST_PATH =
61
+ process.env['LINT_WORKFLOW_SECURITY_ALLOWLIST'] ??
62
+ path.join(path.dirname(_HERE), 'lint_workflow_security_allowlist.json');
53
63
  const ALLOWLIST_CAP = 20;
54
64
 
55
65
  function _setWorkflowsDirForTest(p: string): void {
@@ -577,8 +587,147 @@ function _usageError(arg: string): string {
577
587
  );
578
588
  }
579
589
 
590
+ /**
591
+ * Prove, on demand, that the gate's rejections still fire.
592
+ *
593
+ * The CI invocation is warn-only, so the exit code `runSelfTest` reads cannot be
594
+ * moved by a finding there. The cases below therefore drive the two exits the
595
+ * gate really has — 2 for a dead scan scope or an over-cap allowlist, 1 for a
596
+ * HIGH under `--strict` — and pin the warn-only tier itself from the other side:
597
+ * a MEDIUM under `--strict` must still PASS, which is the direction a future
598
+ * re-tiering would silently break.
599
+ */
600
+ function selfTest(): number {
601
+ const plant = (name: string, files: Record<string, string>): string => {
602
+ const dir = fs.mkdtempSync(path.join(os.tmpdir(), `lws-selftest-${name}-`));
603
+ for (const [rel, body] of Object.entries(files)) {
604
+ fs.writeFileSync(path.join(dir, rel), body, 'utf-8');
605
+ }
606
+ return dir;
607
+ };
608
+ const allowlistFile = (entries: unknown[]): string => {
609
+ const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'lws-selftest-al-'));
610
+ const p = path.join(dir, 'allowlist.json');
611
+ fs.writeFileSync(p, JSON.stringify({ findings: entries }), 'utf-8');
612
+ return p;
613
+ };
614
+ const run = (wfDir: string, args: readonly string[], allowlist?: string): number => {
615
+ const prevDir = process.env['LINT_WORKFLOW_SECURITY_DIR'];
616
+ const prevAl = process.env['LINT_WORKFLOW_SECURITY_ALLOWLIST'];
617
+ process.env['LINT_WORKFLOW_SECURITY_DIR'] = wfDir;
618
+ if (allowlist !== undefined) {
619
+ process.env['LINT_WORKFLOW_SECURITY_ALLOWLIST'] = allowlist;
620
+ }
621
+ try {
622
+ return runGateCli(
623
+ REPO_ROOT,
624
+ path.join('src', 'scripts', 'lint_workflow_security.ts'),
625
+ args,
626
+ REPO_ROOT,
627
+ );
628
+ } finally {
629
+ if (prevDir === undefined) {
630
+ delete process.env['LINT_WORKFLOW_SECURITY_DIR'];
631
+ } else {
632
+ process.env['LINT_WORKFLOW_SECURITY_DIR'] = prevDir;
633
+ }
634
+ if (prevAl === undefined) {
635
+ delete process.env['LINT_WORKFLOW_SECURITY_ALLOWLIST'];
636
+ } else {
637
+ process.env['LINT_WORKFLOW_SECURITY_ALLOWLIST'] = prevAl;
638
+ }
639
+ }
640
+ };
641
+
642
+ const HIGH_WF = [
643
+ 'on:',
644
+ ' pull_request_target:',
645
+ 'jobs:',
646
+ ' build:',
647
+ ' steps:',
648
+ ' - uses: actions/checkout@0000000000000000000000000000000000000000',
649
+ ' with:',
650
+ ' persist-credentials: false',
651
+ ' ref: ${{ github.event.pull_request.head.sha }}',
652
+ '',
653
+ ].join('\n');
654
+ // Unpinned third-party action — MEDIUM, and MEDIUM alone.
655
+ const MEDIUM_WF = [
656
+ 'on:',
657
+ ' push:',
658
+ 'jobs:',
659
+ ' build:',
660
+ ' steps:',
661
+ ' - uses: somevendor/action@v1',
662
+ '',
663
+ ].join('\n');
664
+ const CLEAN_WF = [
665
+ 'on:',
666
+ ' push:',
667
+ 'jobs:',
668
+ ' build:',
669
+ ' steps:',
670
+ ' - uses: actions/checkout@0000000000000000000000000000000000000000',
671
+ ' with:',
672
+ ' persist-credentials: false',
673
+ ' - run: npm ci --ignore-scripts',
674
+ '',
675
+ ].join('\n');
676
+
677
+ return runSelfTest({
678
+ gate: 'lint_workflow_security',
679
+ minCases: 5,
680
+ minRejectCases: 3,
681
+ cases: [
682
+ {
683
+ name: 'an empty workflows directory is refused, not certified "0 HIGH, 0 MEDIUM"',
684
+ expect: 'reject',
685
+ run: () => run(plant('empty', {}), []),
686
+ },
687
+ {
688
+ name: 'a pull_request_target checkout of the untrusted ref fails under --strict',
689
+ expect: 'reject',
690
+ run: () => run(plant('high', { 'bad.yml': HIGH_WF }), ['--strict']),
691
+ },
692
+ {
693
+ name: 'an over-cap allowlist fails rather than suppressing silently',
694
+ expect: 'reject',
695
+ run: () =>
696
+ run(
697
+ plant('cap', { 'ok.yml': CLEAN_WF }),
698
+ [],
699
+ allowlistFile(
700
+ Array.from({ length: ALLOWLIST_CAP + 1 }, (_, n) => ({
701
+ workflow: `w${String(n)}.yml`,
702
+ rule: 'unpinned-action',
703
+ })),
704
+ ),
705
+ ),
706
+ },
707
+ {
708
+ name: 'a MEDIUM under --strict still PASSES — the tier split is real, not decorative',
709
+ expect: 'accept',
710
+ run: () => run(plant('medium', { 'med.yml': MEDIUM_WF }), ['--strict']),
711
+ },
712
+ {
713
+ name: 'a clean workflow passes under --strict',
714
+ expect: 'accept',
715
+ run: () => run(plant('clean', { 'ok.yml': CLEAN_WF }), ['--strict']),
716
+ },
717
+ ],
718
+ });
719
+ }
720
+
580
721
  export function main(argv?: string[]): number {
581
- const parsed = _parseArgs(argv ?? process.argv.slice(2));
722
+ const rawArgv = argv ?? process.argv.slice(2);
723
+ if (rawArgv.includes('--self-test')) {
724
+ if (process.env['GATE_SELF_TEST_CHILD'] === '1') {
725
+ process.stderr.write('lint_workflow_security: --self-test does not recurse\n');
726
+ return 2;
727
+ }
728
+ return selfTest();
729
+ }
730
+ const parsed = _parseArgs(rawArgv);
582
731
  if (parsed.exitCode !== undefined) {
583
732
  return parsed.exitCode;
584
733
  }
@@ -600,7 +749,14 @@ export function main(argv?: string[]): number {
600
749
  const yml = _sortedGlob('.yml');
601
750
  const yaml = _sortedGlob('.yaml');
602
751
  try {
603
- assertScanned({
752
+ // `reportScanned`, not `assertScanned`: the gate now carries a floor in
753
+ // src/config/gate-coverage.yml, and the guard there reads the
754
+ // machine-readable `scanned:` line. Asserting without publishing leaves
755
+ // the gate invisible to the coverage guard — the half-adoption
756
+ // `_lib/scan_scope.reportScanned` was written to close. The line goes to
757
+ // stdout unconditionally, `--quiet` included, because CI passes
758
+ // `--quiet` and a count only visible without it is not a count.
759
+ reportScanned({
604
760
  gate: 'lint_workflow_security',
605
761
  scanned: yml.length + yaml.length,
606
762
  units: 'workflow file(s)',
@@ -283,6 +283,59 @@
283
283
  "readOnlyHint": false
284
284
  }
285
285
  },
286
+ {
287
+ "name": "graph_node",
288
+ "description": "One symbol in full: where it is declared, what reaches it, what it reaches, and how connected it is. Resolves EXACTLY (node id, then label) and refuses anything it cannot — it never scores free text, so an answer is always about the node you named.",
289
+ "side_effect": "ro",
290
+ "implemented_on": [
291
+ "stdio"
292
+ ],
293
+ "input_schema": {
294
+ "type": "object",
295
+ "required": [
296
+ "id"
297
+ ],
298
+ "properties": {
299
+ "id": {
300
+ "type": "string",
301
+ "description": "Node id `<relpath>#<sym>` or an exact label."
302
+ },
303
+ "direction": {
304
+ "type": "string",
305
+ "enum": [
306
+ "in",
307
+ "out",
308
+ "both"
309
+ ],
310
+ "default": "both",
311
+ "description": "Which edges to walk. `in` = who reaches it, `out` = what it reaches."
312
+ },
313
+ "depth": {
314
+ "type": "integer",
315
+ "minimum": 1,
316
+ "maximum": 3,
317
+ "default": 1,
318
+ "description": "Hop limit."
319
+ },
320
+ "relation": {
321
+ "type": "string",
322
+ "enum": [
323
+ "calls",
324
+ "imports",
325
+ "uses",
326
+ "inherits",
327
+ "member",
328
+ "tests"
329
+ ],
330
+ "description": "Restrict to one relation. Omit for all of them."
331
+ }
332
+ },
333
+ "additionalProperties": false
334
+ },
335
+ "annotations": {
336
+ "readOnlyHint": true
337
+ }
338
+ },
286
339
  {
287
340
  "name": "graph_path",
288
341
  "description": "The shortest relation chain between two symbols, direction preserved. Answers \"how does A reach B\" without reading the files between. `(no path found)` when unconnected is an answer.",
@@ -1,7 +1,13 @@
1
1
  /**
2
- * The five code-graph MCP tools (`road-to-a-graph-that-is-shipped` 4.1).
2
+ * The code-graph MCP tools (`road-to-a-graph-that-is-shipped` 4.1).
3
+ *
4
+ * `graph_impact`, `graph_tests_for`, `graph_dead`, `graph_query`, `graph_path`
5
+ * — and `graph_node`, added by `road-to-a-graph-that-feeds-the-gate` 3.1. The
6
+ * first five all answer about a NEIGHBOURHOOD or a SET; none answered "where
7
+ * does this live, who reaches it, what does it reach, how connected is it",
8
+ * which is the question an agent asks first and had to assemble from two calls
9
+ * and a file read.
3
10
  *
4
- * `graph_impact`, `graph_tests_for`, `graph_dead`, `graph_query`, `graph_path`.
5
11
  * They are the agent-facing half of Phase 3: the verbs exist on the CLI and an
6
12
  * agent that has to shell out to reach them will not, so the engine's answers
7
13
  * were unreachable from the surface the consumer actually talks to. Risk-Register
@@ -39,7 +45,15 @@ import { changedFiles } from '../code_graph/cli.js';
39
45
  import { detectSources, graphState, NATIVE_CACHE_REL, pickSource } from '../code_graph/detect.js';
40
46
  import { resolvePath } from './path_util.js';
41
47
  import { isTestFile } from '../code_graph/build.js';
42
- import { loadGraph, type LoadedGraph, path as graphPathVerb, query as graphQueryVerb } from '../code_graph/query.js';
48
+ import {
49
+ loadGraph,
50
+ type LoadedGraph,
51
+ node as graphNodeVerb,
52
+ type NodeOptions,
53
+ path as graphPathVerb,
54
+ query as graphQueryVerb,
55
+ } from '../code_graph/query.js';
56
+ import type { Relation } from '../code_graph/types.js';
43
57
  import {
44
58
  dead,
45
59
  entryPointsFromFile,
@@ -97,6 +111,16 @@ function str(args: Record<string, unknown>, key: string): string {
97
111
  return typeof v === 'string' ? v.trim() : '';
98
112
  }
99
113
 
114
+ /**
115
+ * The closed relation vocabulary, as a schema enum.
116
+ *
117
+ * Spelled out rather than derived from the `Relation` union, which erases at
118
+ * compile time — and pinned by a test against that union so the two cannot
119
+ * drift. An enum that silently lost a value would turn a legal filter into a
120
+ * rejected argument.
121
+ */
122
+ const RELATIONS = ['calls', 'imports', 'uses', 'inherits', 'member', 'tests'] as const;
123
+
100
124
  function num(args: Record<string, unknown>, key: string, dflt: number): number {
101
125
  const v = args[key];
102
126
  return typeof v === 'number' && Number.isFinite(v) && v > 0 ? v : dflt;
@@ -263,6 +287,51 @@ export const GRAPH_TOOLS: Record<string, BuiltinTool> = {
263
287
  },
264
288
  },
265
289
 
290
+ graph_node: {
291
+ name: 'graph_node',
292
+ side_effect: 'ro',
293
+ description:
294
+ 'One symbol in full: where it is declared, what reaches it, what it ' +
295
+ 'reaches, and how connected it is. Resolves EXACTLY (node id, then ' +
296
+ 'label) and refuses anything it cannot — it never scores free text, ' +
297
+ 'so an answer is always about the node you named.',
298
+ input_schema: {
299
+ type: 'object',
300
+ required: ['id'],
301
+ properties: {
302
+ id: { type: 'string', description: 'Node id `<relpath>#<sym>` or an exact label.' },
303
+ direction: {
304
+ type: 'string',
305
+ enum: ['in', 'out', 'both'],
306
+ default: 'both',
307
+ description: 'Which edges to walk. `in` = who reaches it, `out` = what it reaches.',
308
+ },
309
+ depth: { type: 'integer', minimum: 1, maximum: 3, default: 1, description: 'Hop limit.' },
310
+ relation: {
311
+ type: 'string',
312
+ enum: RELATIONS,
313
+ description: 'Restrict to one relation. Omit for all of them.',
314
+ },
315
+ },
316
+ additionalProperties: false,
317
+ },
318
+ handler: async (args, root) => {
319
+ const id = str(args, 'id');
320
+ if (id === '') return { status: 'error', error: 'id must be a non-empty string' };
321
+ const staleness = graphState(root);
322
+ const opts: NodeOptions = {};
323
+ const dir = str(args, 'direction');
324
+ if (dir === 'in' || dir === 'out' || dir === 'both') opts.direction = dir;
325
+ // `num` clamps at >0 only; the 1-3 band is the verb's, so an
326
+ // out-of-band value is clamped there rather than rejected here —
327
+ // two places enforcing one band is how they drift apart.
328
+ opts.depth = num(args, 'depth', 1);
329
+ const rel = str(args, 'relation');
330
+ if ((RELATIONS as readonly string[]).includes(rel)) opts.relation = rel as Relation;
331
+ return withGraph(root, (g) => ({ staleness, ...graphNodeVerb(g, id, opts) }));
332
+ },
333
+ },
334
+
266
335
  graph_path: {
267
336
  name: 'graph_path',
268
337
  side_effect: 'ro',
@@ -69,6 +69,22 @@ export interface SkillPrompt {
69
69
  readonly kind: PromptKind;
70
70
  readonly recommended_for_user_types: readonly string[];
71
71
  readonly user_type_match: UserTypeMatch;
72
+ /**
73
+ * Declared parameters, from the frontmatter `inputs:` block.
74
+ *
75
+ * Empty for every artifact that declares none, which is the whole corpus
76
+ * today — so `arguments: []` on the wire stays the behaviour for an
77
+ * undeclared prompt rather than becoming a special case
78
+ * (road-to-an-invocation-contract-that-reaches-the-wire 3.2).
79
+ */
80
+ readonly inputs: readonly PromptInput[];
81
+ }
82
+
83
+ /** One declared parameter, reduced to what the MCP `arguments` shape carries. */
84
+ export interface PromptInput {
85
+ readonly name: string;
86
+ readonly description: string;
87
+ readonly required: boolean;
72
88
  }
73
89
 
74
90
  /** Walk up from this file to the repo root (parent of `scripts/`). */
@@ -92,6 +108,124 @@ export function _project_root(): string {
92
108
  * Avoids a YAML dependency for Phase 1; the frontmatter shape is
93
109
  * enforced by `task lint-skills` upstream.
94
110
  */
111
+ /** The raw frontmatter block of a document, or `''` when it has none. */
112
+ export function _frontmatter_text(text: string): string {
113
+ if (!text.startsWith('---\n')) return '';
114
+ const parts = _splitMax(text, '---\n', 2);
115
+ return parts.length < 3 ? '' : (parts[1] as string);
116
+ }
117
+
118
+ /**
119
+ * Parse the frontmatter `inputs:` block out of raw frontmatter text.
120
+ *
121
+ * WHY THIS EXISTS RATHER THAN A YAML CALL. `_strip_frontmatter` is a flat
122
+ * `key: value` line scanner and cannot represent a nested list — `inputs:`
123
+ * would land as an empty string and its `- name: x` lines as stray keys. The
124
+ * MCP server is deliberately stdlib-only and is bundled for the wire, so
125
+ * pulling a YAML parser in to read one optional block would pay a dependency
126
+ * on every install for a block almost nothing declares yet.
127
+ *
128
+ * IT READS ONE SHAPE AND REFUSES THE REST. Accepted:
129
+ *
130
+ * inputs:
131
+ * - name: target
132
+ * type: string
133
+ * required: true
134
+ * description: What to act on.
135
+ *
136
+ * Refused, by yielding NOTHING for the whole block: the flow form
137
+ * (`inputs: [{name: a}]`), a block scalar (`description: >`), and any nested
138
+ * mapping under an item key. Refusing is not silent — `check_inputs_parity`
139
+ * compares this reader against a real YAML parse for every declaring artifact
140
+ * and FAILS the build on any difference, so an unsupported form is a red gate
141
+ * with a named remedy rather than a wrong argument on the wire.
142
+ *
143
+ * That gate is the answer to the defect an independent review found here: the
144
+ * key scan used to run at any depth, so a `default:` MAPPING whose first child
145
+ * was `name:` overwrote the parameter's own name and the server offered a host
146
+ * an argument that did not exist. The header promised "no guessed input" while
147
+ * the code guessed. Depth is now tracked, and where this reader cannot be sure
148
+ * it declines and the parity gate makes the decline visible.
149
+ */
150
+ export function _parse_inputs(fm: string): PromptInput[] {
151
+ const lines = fm.split('\n');
152
+ let i = lines.findIndex((l) => /^inputs:/.test(l));
153
+ if (i === -1) return [];
154
+ // The flow form carries its payload on the `inputs:` line itself. This
155
+ // reader does not parse it; the parity gate turns that into a failure.
156
+ if (!/^inputs:\s*$/.test(lines[i] as string)) return [];
157
+
158
+ const out: PromptInput[] = [];
159
+ let current: { name?: string; description?: string; required?: boolean } | null = null;
160
+ /** Indent of the key lines belonging to the item being read. */
161
+ let keyIndent: number | null = null;
162
+ let refused = false;
163
+
164
+ const flush = (): void => {
165
+ if (current?.name !== undefined) {
166
+ out.push({
167
+ name: current.name,
168
+ description: current.description ?? '',
169
+ required: current.required === true,
170
+ });
171
+ }
172
+ current = null;
173
+ };
174
+
175
+ const clean = (raw: string): string => {
176
+ const v = raw.trim();
177
+ // An unquoted trailing comment is not part of the value — the same strip
178
+ // `_strip_frontmatter` applies, kept consistent deliberately.
179
+ const hashed = /^(?!["'])([^#]*?)\s+#.*$/.exec(v);
180
+ return _stripQuotes((hashed === null ? v : (hashed[1] ?? '')).trim());
181
+ };
182
+
183
+ for (i += 1; i < lines.length && !refused; i += 1) {
184
+ const line = lines[i] as string;
185
+ if (line.trim() === '') continue;
186
+ // A comment at any column is not block content and does not end the block.
187
+ if (/^\s*#/.test(line)) continue;
188
+ // A non-indented line ends the block.
189
+ if (!/^\s/.test(line)) break;
190
+
191
+ const indent = (/^(\s*)/.exec(line) as RegExpExecArray)[1]!.replace(/\t/g, ' ').length;
192
+
193
+ const item = /^\s*-\s+([A-Za-z_]\w*):\s*(.*)$/.exec(line);
194
+ if (item !== null) {
195
+ flush();
196
+ current = {};
197
+ keyIndent = line.indexOf('-') + 2;
198
+ if (item[1] === 'name') current.name = clean(item[2] ?? '');
199
+ continue;
200
+ }
201
+
202
+ const kv = /^\s+([A-Za-z_]\w*):\s*(.*)$/.exec(line);
203
+ if (kv === null || current === null || keyIndent === null) continue;
204
+ // Deeper than the item's own keys: a nested mapping or sequence this
205
+ // reader does not model. Decline the whole block rather than absorb it.
206
+ if (indent > keyIndent) {
207
+ refused = true;
208
+ break;
209
+ }
210
+ const key = kv[1] as string;
211
+ const rawValue = (kv[2] ?? '').trim();
212
+ // A block scalar or an empty value means the real content is on the
213
+ // following lines, which this reader does not join.
214
+ if (rawValue === '' || rawValue === '>' || rawValue === '|') {
215
+ refused = true;
216
+ break;
217
+ }
218
+ const value = clean(rawValue);
219
+ if (key === 'name') current.name = value;
220
+ else if (key === 'description') current.description = value;
221
+ else if (key === 'required') current.required = value.toLowerCase() === 'true';
222
+ }
223
+
224
+ if (refused) return [];
225
+ flush();
226
+ return out;
227
+ }
228
+
95
229
  export function _strip_frontmatter(text: string): [Record<string, string>, string] {
96
230
  if (!text.startsWith('---\n')) {
97
231
  return [{}, text];
@@ -365,6 +499,7 @@ function _load_file(p: string, kind: PromptKind, fallback_name: string): SkillPr
365
499
  kind,
366
500
  recommended_for_user_types: _parse_inline_array(meta.recommended_for_user_types ?? ''),
367
501
  user_type_match: '',
502
+ inputs: _parse_inputs(_frontmatter_text(text)),
368
503
  };
369
504
  }
370
505
 
@@ -548,7 +683,13 @@ export function to_mcp_prompt_meta(prompt: SkillPrompt): Record<string, unknown>
548
683
  name: wire,
549
684
  title: prompt.name,
550
685
  description: prompt.description,
551
- arguments: [],
686
+ // Derived from the declaration, never invented. An artifact that
687
+ // declares nothing still serves `[]` — see `_parse_inputs`.
688
+ arguments: prompt.inputs.map((i) => ({
689
+ name: i.name,
690
+ description: i.description,
691
+ required: i.required,
692
+ })),
552
693
  _meta: meta,
553
694
  };
554
695
  }
@@ -117,6 +117,59 @@ export function mcnemarExactP(gained: number, lost: number): number {
117
117
 
118
118
  const pp = (hits: number, total: number): number => (total === 0 ? 0 : (hits / total) * 100);
119
119
 
120
+ /** The inputs the pre-registered verdict function reads, and nothing else. */
121
+ export interface VerdictInputs {
122
+ readonly deltaRecall: number;
123
+ readonly deltaFalse: number;
124
+ readonly p: number;
125
+ readonly gained: number;
126
+ readonly lost: number;
127
+ }
128
+
129
+ /**
130
+ * The pre-registered verdict function, in its pre-registered ORDER.
131
+ *
132
+ * Extracted from `measure` on 2026-10-04 so the order can be tested against
133
+ * chosen inputs rather than against whatever the live corpus happens to
134
+ * produce. The behaviour is unchanged, deliberately: this is a move, not a
135
+ * rewrite, and the bars it reads are the same module-level constants.
136
+ *
137
+ * WHY IT HAD TO MOVE. The order is load-bearing — power floor, then guard,
138
+ * then primary — and the only thing pinning it was a test asserting that the
139
+ * LIVE run happened to be a case that discriminates: primary met AND guard
140
+ * breached, so a function checking the primary first would wrongly say
141
+ * `signal`. That witness is a property of the corpus, not of the code, and the
142
+ * corpus grows whenever a skill gains a trigger file. On 2026-10-04 one such
143
+ * growth moved delta-recall from 5.128 to 4.608 and the witness was gone,
144
+ * leaving the ordering guarded by nothing. Tested here against chosen inputs,
145
+ * the discriminating case is always available and no future corpus growth can
146
+ * take it away.
147
+ */
148
+ export function verdictOf(i: VerdictInputs): { verdict: Verdict; reason: string } {
149
+ if (i.gained + i.lost < POWER_FLOOR_DISCORDANT) {
150
+ return {
151
+ verdict: 'underpowered',
152
+ reason: `${i.gained + i.lost} discordant positive pairs < power floor ${POWER_FLOOR_DISCORDANT}`,
153
+ };
154
+ }
155
+ if (i.deltaFalse > FALSE_ACTIVATION_GUARD_PP) {
156
+ return {
157
+ verdict: 'harmful',
158
+ reason: `false activation +${i.deltaFalse.toFixed(2)} pp breaches the +${FALSE_ACTIVATION_GUARD_PP.toFixed(1)} pp guard`,
159
+ };
160
+ }
161
+ if (i.deltaRecall >= RECALL_GAIN_BAR_PP && i.p < ALPHA) {
162
+ return {
163
+ verdict: 'signal',
164
+ reason: `recall +${i.deltaRecall.toFixed(2)} pp at p=${i.p.toExponential(2)}`,
165
+ };
166
+ }
167
+ return {
168
+ verdict: 'null',
169
+ reason: `recall +${i.deltaRecall.toFixed(2)} pp at p=${i.p.toExponential(2)} does not clear +${RECALL_GAIN_BAR_PP.toFixed(1)} pp and p<${ALPHA}`,
170
+ };
171
+ }
172
+
120
173
  export function measure(repo = REPO, k = K): Measurement {
121
174
  const catalogue: CatalogueEntry[] = loadCatalogue(repo);
122
175
  const cases: CorpusCase[] = loadTrainCases(repo);
@@ -170,22 +223,7 @@ export function measure(repo = REPO, k = K): Measurement {
170
223
  (falsePp['description+body'] as number) - (falsePp['description'] as number);
171
224
  const p = mcnemarExactP(gained, lost);
172
225
 
173
- // The pre-registered verdict function, in its pre-registered order.
174
- let verdict: Verdict;
175
- let reason: string;
176
- if (gained + lost < POWER_FLOOR_DISCORDANT) {
177
- verdict = 'underpowered';
178
- reason = `${gained + lost} discordant positive pairs < power floor ${POWER_FLOOR_DISCORDANT}`;
179
- } else if (deltaFalse > FALSE_ACTIVATION_GUARD_PP) {
180
- verdict = 'harmful';
181
- reason = `false activation +${deltaFalse.toFixed(2)} pp breaches the +${FALSE_ACTIVATION_GUARD_PP.toFixed(1)} pp guard`;
182
- } else if (deltaRecall >= RECALL_GAIN_BAR_PP && p < ALPHA) {
183
- verdict = 'signal';
184
- reason = `recall +${deltaRecall.toFixed(2)} pp at p=${p.toExponential(2)}`;
185
- } else {
186
- verdict = 'null';
187
- reason = `recall +${deltaRecall.toFixed(2)} pp at p=${p.toExponential(2)} does not clear +${RECALL_GAIN_BAR_PP.toFixed(1)} pp and p<${ALPHA}`;
188
- }
226
+ const { verdict, reason } = verdictOf({ deltaRecall, deltaFalse, p, gained, lost });
189
227
 
190
228
  return {
191
229
  k,