@event4u/agent-config 16.2.0 → 16.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (332) hide show
  1. package/.augment-plugin/marketplace.json +3 -3
  2. package/.augment-plugin/plugin.json +1 -1
  3. package/.claude-plugin/marketplace.json +1 -1
  4. package/CHANGELOG.md +89 -237
  5. package/README.md +14 -12
  6. package/dist/agent-src/commands/analyze/inbox.md +1 -1
  7. package/dist/agent-src/commands/analyze/roadmap-repos.md +1 -1
  8. package/dist/agent-src/commands/bug/fix.md +17 -2
  9. package/dist/agent-src/commands/feature/plan.md +1 -1
  10. package/dist/agent-src/commands/feature/roadmap.md +1 -1
  11. package/dist/agent-src/commands/fix/ci.md +1 -1
  12. package/dist/agent-src/commands/implement-ticket.md +1 -1
  13. package/dist/agent-src/commands/jira-ticket.md +1 -1
  14. package/dist/agent-src/commands/roadmap/create.md +1 -1
  15. package/dist/agent-src/commands/roadmap/materialize.md +1 -1
  16. package/dist/agent-src/commands/roadmap/process-full.md +4 -4
  17. package/dist/agent-src/commands/roadmap/resolve-blockers.md +361 -0
  18. package/dist/agent-src/commands/roadmap/triage-parked.md +325 -0
  19. package/dist/agent-src/commands/roadmap.md +18 -4
  20. package/dist/agent-src/commands/tests/create.md +17 -3
  21. package/dist/agent-src/commands/tests/execute.md +22 -5
  22. package/dist/agent-src/contexts/authority/decision-revisit-gate-enforcement.md +43 -0
  23. package/dist/agent-src/contexts/communication/rules-auto/design-fidelity-enforcement.md +24 -0
  24. package/dist/agent-src/contexts/execution/auto-dispatch-classification.md +6 -6
  25. package/dist/agent-src/contexts/execution/roadmap-process-loop.md +32 -2
  26. package/dist/agent-src/contexts/execution/session-canary-enforcement-history.md +67 -0
  27. package/dist/agent-src/contexts/execution/toolchain-resolver.md +101 -7
  28. package/dist/agent-src/rules/active-remediation.md +1 -1
  29. package/dist/agent-src/rules/autonomous-execution.md +1 -0
  30. package/dist/agent-src/rules/broken-access-control.md +1 -0
  31. package/dist/agent-src/rules/command-suggestion-policy.md +1 -0
  32. package/dist/agent-src/rules/context-hygiene.md +1 -1
  33. package/dist/agent-src/rules/decision-revisit-gate.md +16 -33
  34. package/dist/agent-src/rules/design-fidelity.md +13 -16
  35. package/dist/agent-src/rules/doc-screenshot-hygiene.md +1 -0
  36. package/dist/agent-src/rules/domain-safety-disclaimer.md +1 -0
  37. package/dist/agent-src/rules/domain-safety-pii.md +1 -0
  38. package/dist/agent-src/rules/domain-safety-retention.md +1 -0
  39. package/dist/agent-src/rules/engineering-safety-floor.md +1 -0
  40. package/dist/agent-src/rules/evaluator-independence.md +3 -2
  41. package/dist/agent-src/rules/fast-path-marker-visibility.md +1 -0
  42. package/dist/agent-src/rules/finance-safety-floor.md +1 -0
  43. package/dist/agent-src/rules/git-history-discipline.md +1 -0
  44. package/dist/agent-src/rules/image-likeness-and-rights.md +1 -0
  45. package/dist/agent-src/rules/legal-safety-floor.md +3 -2
  46. package/dist/agent-src/rules/lethal-trifecta-guard.md +1 -0
  47. package/dist/agent-src/rules/low-impact-corpus-privacy-floor.md +1 -0
  48. package/dist/agent-src/rules/media-governance-routing.md +14 -17
  49. package/dist/agent-src/rules/persona-governance.md +2 -2
  50. package/dist/agent-src/rules/provider-lifecycle-discipline.md +1 -1
  51. package/dist/agent-src/rules/question-not-instruction.md +1 -0
  52. package/dist/agent-src/rules/roadmap-progress-sync.md +1 -1
  53. package/dist/agent-src/rules/runtime-safety.md +1 -0
  54. package/dist/agent-src/rules/secret-vcs-guard.md +1 -0
  55. package/dist/agent-src/rules/security-sensitive-stop.md +3 -2
  56. package/dist/agent-src/rules/session-canary.md +8 -62
  57. package/dist/agent-src/rules/settings-ask-protocol.md +1 -0
  58. package/dist/agent-src/rules/source-confidentiality.md +1 -0
  59. package/dist/agent-src/rules/strategy-safety-floor.md +1 -0
  60. package/dist/agent-src/rules/tool-safety.md +1 -0
  61. package/dist/agent-src/rules/untrusted-input-defense.md +1 -0
  62. package/dist/agent-src/rules/user-interrupt-priority.md +1 -0
  63. package/dist/agent-src/scripts/archive_completed_roadmaps.ts +115 -20
  64. package/dist/agent-src/scripts/deferral_carry.ts +339 -0
  65. package/dist/agent-src/scripts/update_roadmap_progress.ts +4 -1
  66. package/dist/agent-src/skills/api-design/data/manifest.json +2 -2
  67. package/dist/agent-src/skills/api-testing/SKILL.md +17 -0
  68. package/dist/agent-src/skills/code-intelligence/SKILL.md +14 -2
  69. package/dist/agent-src/skills/design-system-capture/references/import-procedure.md +1 -1
  70. package/dist/agent-src/skills/existing-ui-audit/SKILL.md +2 -2
  71. package/dist/agent-src/skills/fe-design/SKILL.md +1 -1
  72. package/dist/agent-src/skills/iconography/SKILL.md +1 -1
  73. package/dist/agent-src/skills/js-library-packaging/SKILL.md +2 -2
  74. package/dist/agent-src/skills/monorepo-workspace/SKILL.md +1 -1
  75. package/dist/agent-src/skills/quality-tools/SKILL.md +11 -1
  76. package/dist/agent-src/skills/quality-tools/references/go-tools.md +76 -0
  77. package/dist/agent-src/skills/quality-tools/references/python-tools.md +104 -0
  78. package/dist/agent-src/skills/review-routing/SKILL.md +2 -2
  79. package/dist/agent-src/skills/skill-improvement-pipeline/SKILL.md +1 -1
  80. package/dist/agent-src/skills/storybook-workshop/SKILL.md +3 -3
  81. package/dist/agent-src/skills/tailwind-engineer/SKILL.md +1 -1
  82. package/dist/agent-src/skills/test-case-discovery/SKILL.md +7 -0
  83. package/dist/agent-src/skills/test-driven-development/SKILL.md +17 -0
  84. package/dist/agent-src/skills/test-performance/SKILL.md +19 -1
  85. package/dist/agent-src/skills/ui-component-architect/SKILL.md +2 -2
  86. package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
  87. package/dist/agent-src/templates/scripts/work_engine/directives/ui/apply.ts +48 -6
  88. package/dist/agent-src/templates/scripts/work_engine/stack/runner.ts +774 -109
  89. package/dist/cli/registry.js +1 -1
  90. package/dist/cli-delegate/archive_completed_roadmaps.js +92 -12
  91. package/dist/cli-delegate/check_update_banner.js +12 -1
  92. package/dist/cli-delegate/chunk-523HCFYO.js +90 -0
  93. package/dist/cli-delegate/{chunk-M2DPGFZ4.js → chunk-6V6TA25H.js} +22 -152
  94. package/dist/cli-delegate/chunk-7IQE3D5S.js +191 -0
  95. package/dist/cli-delegate/{chunk-6KFEC3G7.js → chunk-CQWX336L.js} +1 -1
  96. package/dist/cli-delegate/chunk-DEIV7XP7.js +108 -0
  97. package/dist/cli-delegate/chunk-EEHDXSYD.js +2040 -0
  98. package/dist/cli-delegate/{chunk-P6ZHP5W2.js → chunk-GKE7ZVFA.js} +4 -4
  99. package/dist/cli-delegate/chunk-JQOM7TTM.js +1201 -0
  100. package/dist/cli-delegate/{chunk-IGD6ERX6.js → chunk-JQVYKIZA.js} +10 -0
  101. package/dist/cli-delegate/{chunk-FC75PGJE.js → chunk-JY2I2DXV.js} +12 -1
  102. package/dist/cli-delegate/chunk-KB5FZ655.js +208 -0
  103. package/dist/cli-delegate/{chunk-7RQP4UDH.js → chunk-KR6EM23A.js} +235 -1455
  104. package/dist/cli-delegate/{chunk-LTYVIY76.js → chunk-LIYTLH6O.js} +1 -1
  105. package/dist/cli-delegate/{chunk-YJ72UIAK.js → chunk-NQ7WT43P.js} +1186 -244
  106. package/dist/cli-delegate/chunk-NXEILF2K.js +2160 -0
  107. package/dist/cli-delegate/{chunk-5R7HOLAF.js → chunk-NYX3L2PZ.js} +1 -1
  108. package/dist/cli-delegate/{chunk-4E2W63DA.js → chunk-OKJUIAPS.js} +62 -1
  109. package/dist/cli-delegate/{chunk-QAJS5YWY.js → chunk-PHD7OOLE.js} +2 -1
  110. package/dist/cli-delegate/{chunk-RGNIWTEA.js → chunk-QLDHIPWR.js} +1 -1
  111. package/dist/cli-delegate/{chunk-7XGBX6ZH.js → chunk-RQNNW7IF.js} +1314 -560
  112. package/dist/cli-delegate/{chunk-4XFP2Y3E.js → chunk-S4UMZNES.js} +1 -1
  113. package/dist/cli-delegate/{chunk-6KM6ORRY.js → chunk-S4YWNRKO.js} +24 -8
  114. package/dist/cli-delegate/{chunk-FP36SWJD.js → chunk-TRI6DQZ6.js} +2781 -340
  115. package/dist/cli-delegate/chunk-ULQRUR23.js +208 -0
  116. package/dist/cli-delegate/{chunk-NUHLI2RX.js → chunk-YCU3TIXH.js} +2 -0
  117. package/dist/cli-delegate/chunk-ZBZV2G5R.js +210 -0
  118. package/dist/cli-delegate/{chunk-WGXTVKCQ.js → chunk-ZUZKZYCN.js} +5 -3
  119. package/dist/cli-delegate/cmd_analyze_session.js +1 -1
  120. package/dist/cli-delegate/cmd_brand_status.js +1 -1
  121. package/dist/cli-delegate/cmd_conformance.js +1167 -234
  122. package/dist/cli-delegate/cmd_doctor.js +13 -6
  123. package/dist/cli-delegate/cmd_doctor_neighbours.js +23 -0
  124. package/dist/cli-delegate/cmd_explain.js +5 -5
  125. package/dist/cli-delegate/cmd_export.js +11 -8
  126. package/dist/cli-delegate/cmd_fleet.js +3 -3
  127. package/dist/cli-delegate/cmd_handoff.js +10 -7
  128. package/dist/cli-delegate/cmd_mcp_available.js +1 -1
  129. package/dist/cli-delegate/cmd_memory_get.js +3 -1
  130. package/dist/cli-delegate/cmd_migrate.js +2 -2
  131. package/dist/cli-delegate/cmd_packs_active.js +3 -3
  132. package/dist/cli-delegate/cmd_preflight.js +6 -6
  133. package/dist/cli-delegate/cmd_prune.js +11 -8
  134. package/dist/cli-delegate/cmd_refresh.js +2 -1
  135. package/dist/cli-delegate/cmd_route_audit.js +8 -6
  136. package/dist/cli-delegate/cmd_route_explain.js +4 -4
  137. package/dist/cli-delegate/cmd_session_recycle.js +4 -3
  138. package/dist/cli-delegate/cmd_settings_get.js +1 -1
  139. package/dist/cli-delegate/cmd_settings_migrate.js +11 -8
  140. package/dist/cli-delegate/cmd_settings_set.js +4 -4
  141. package/dist/cli-delegate/cmd_sync.js +11 -8
  142. package/dist/cli-delegate/cmd_uninstall.js +11 -8
  143. package/dist/cli-delegate/cmd_update.js +1 -1
  144. package/dist/cli-delegate/cmd_upgrade.js +3 -2
  145. package/dist/cli-delegate/cmd_validate.js +11 -8
  146. package/dist/cli-delegate/cmd_versions.js +1 -1
  147. package/dist/cli-delegate/deferral_carry.js +16 -0
  148. package/dist/cli-delegate/gate_execute.js +2 -2
  149. package/dist/cli-delegate/roadmap_gates.js +5 -5
  150. package/dist/cli-delegate/update_roadmap_progress.js +1 -1
  151. package/dist/discovery/deprecation-report.md +1 -1
  152. package/dist/discovery/discovery-manifest.json +127 -75
  153. package/dist/discovery/discovery-manifest.json.sha256 +1 -1
  154. package/dist/discovery/discovery-manifest.summary.md +3 -3
  155. package/dist/discovery/dormancy-report.md +1 -1
  156. package/dist/discovery/orphan-report.md +1 -1
  157. package/dist/discovery/packs.json +7 -5
  158. package/dist/discovery/trust-report.md +2 -2
  159. package/dist/discovery/workspaces.json +6 -4
  160. package/dist/hooks/dispatch.js +5357 -6656
  161. package/dist/hooks/dispatch.sha256 +1 -1
  162. package/dist/install/claudePathsPlan.js +47 -6
  163. package/dist/install/claudeRuleRewrite.js +7 -0
  164. package/dist/install/emit_host_rules_cli.js +39 -36
  165. package/dist/install/globalRuleScope.js +87 -0
  166. package/dist/install/host_rule_ownership.js +160 -0
  167. package/dist/install/install.mjs +2080 -1024
  168. package/dist/install/installThinLayer.js +261 -0
  169. package/dist/install/installedRuleLinks.js +176 -0
  170. package/dist/install/wizard-plan.js +24 -0
  171. package/dist/mcp/registry-manifest.json +3 -3
  172. package/dist/mcp/server.json +2 -2
  173. package/dist/mcp/server.mjs +31965 -27154
  174. package/dist/scripts/_lib/agent_settings.js +11 -0
  175. package/dist/scripts/_lib/claude_settings_hooks.js +10 -8
  176. package/dist/scripts/_lib/installed_layer.js +378 -0
  177. package/dist/scripts/_lib/lean_projection_mode.js +218 -15
  178. package/dist/scripts/_lib/rule_consequence_class.js +45 -0
  179. package/dist/scripts/_lib/rule_injection.js +67 -10
  180. package/dist/scripts/_lib/rule_law_section.js +122 -0
  181. package/dist/scripts/_lib/thin_rules.js +458 -0
  182. package/dist/scripts/_lib/windsurf_render.js +60 -0
  183. package/dist/scripts/condense.js +14 -26
  184. package/dist/scripts/project_thin_rules.js +14 -294
  185. package/dist/server/schemas/settings.js +6 -0
  186. package/dist/shared/skillRanking.js +74 -13
  187. package/docs/contracts/settings-classes.md +7 -5
  188. package/package.json +1 -1
  189. package/src/agent-src/scripts/archive_completed_roadmaps.ts +115 -20
  190. package/src/agent-src/scripts/deferral_carry.ts +339 -0
  191. package/src/agent-src/scripts/update_roadmap_progress.ts +4 -1
  192. package/src/agent-src/templates/scripts/work_engine/directives/ui/apply.ts +48 -6
  193. package/src/agent-src/templates/scripts/work_engine/stack/runner.ts +774 -109
  194. package/src/config/agent-settings.template.yml +62 -22
  195. package/src/config/continuity-surface.json +7 -0
  196. package/src/config/evaluator-budgets.json +4 -4
  197. package/src/config/gate-coverage.yml +96 -3
  198. package/src/config/gate-violation-baselines.json +17 -9
  199. package/src/config/hook-bundle-budget.json +25 -0
  200. package/src/config/hook-token-budget.json +6 -1
  201. package/src/config/host-instruction-limits.json +33 -0
  202. package/src/config/routing-coverage-seed.json +24 -3
  203. package/src/config/rule-activation-census.json +7 -3
  204. package/src/config/rule-consequence-class.json +161 -0
  205. package/src/config/rule-law-ceilings.json +99 -0
  206. package/src/install/claudePathsPlan.ts +46 -4
  207. package/src/install/claudeRuleRewrite.ts +10 -1
  208. package/src/install/emit_host_rules_cli.ts +43 -34
  209. package/src/install/globalRuleScope.ts +109 -0
  210. package/src/install/host_rule_ownership.ts +170 -0
  211. package/src/install/installThinLayer.ts +312 -0
  212. package/src/install/installedRuleLinks.ts +220 -0
  213. package/src/install/wizard-plan.ts +24 -0
  214. package/src/scripts/_cli/cmd_doctor.ts +23 -71
  215. package/src/scripts/_cli/cmd_doctor_neighbours.ts +293 -0
  216. package/src/scripts/_dispatch.bash +16 -2
  217. package/src/scripts/_lib/agent_settings.ts +11 -0
  218. package/src/scripts/_lib/claude_settings_hooks.ts +10 -8
  219. package/src/scripts/_lib/estate_offsets.ts +241 -0
  220. package/src/scripts/_lib/exemption_shape.ts +143 -0
  221. package/src/scripts/_lib/fnv.ts +23 -0
  222. package/src/scripts/_lib/foreign_scan.ts +106 -0
  223. package/src/scripts/_lib/gitignore_block.ts +314 -0
  224. package/src/scripts/_lib/global_deploy_inventory.ts +32 -0
  225. package/src/scripts/_lib/graph_feeder_record.ts +214 -0
  226. package/src/scripts/_lib/hook_settings.ts +73 -0
  227. package/src/scripts/_lib/host_capability.ts +40 -19
  228. package/src/scripts/_lib/host_hook_merge.ts +165 -0
  229. package/src/scripts/_lib/installed_layer.ts +538 -0
  230. package/src/scripts/_lib/json_merge.ts +82 -0
  231. package/src/scripts/_lib/json_pointers.ts +93 -11
  232. package/src/scripts/_lib/lean_projection_mode.ts +275 -15
  233. package/src/scripts/_lib/machine_wake.ts +16 -3
  234. package/src/scripts/_lib/neighbour_census.ts +760 -0
  235. package/src/scripts/_lib/neighbour_scan.ts +122 -0
  236. package/src/scripts/_lib/neighbour_tool_use.ts +189 -0
  237. package/src/scripts/_lib/prompt_shape.ts +46 -0
  238. package/src/scripts/_lib/release_migration_gate.ts +19 -3
  239. package/src/scripts/_lib/reserved_name_sweep.ts +73 -0
  240. package/src/scripts/_lib/review_coverage.ts +134 -0
  241. package/src/scripts/_lib/rule_consequence_class.ts +71 -0
  242. package/src/scripts/_lib/rule_injection.ts +130 -10
  243. package/src/scripts/_lib/rule_law_section.ts +147 -0
  244. package/src/scripts/_lib/rule_layer_overlap.ts +58 -0
  245. package/src/scripts/_lib/skill_origin.ts +254 -0
  246. package/src/scripts/_lib/structured_ask.ts +64 -21
  247. package/src/scripts/_lib/thin_rules.ts +540 -0
  248. package/src/scripts/_lib/touched_file_quality.ts +434 -0
  249. package/src/scripts/_lib/turn_end_refusals.ts +77 -2
  250. package/src/scripts/_lib/windsurf_render.ts +63 -0
  251. package/src/scripts/ai_council/cli_call_budget.ts +13 -5
  252. package/src/scripts/ai_council/clients.ts +26 -57
  253. package/src/scripts/ai_council/config.ts +1 -1
  254. package/src/scripts/ai_council/vendor_defaults.ts +45 -0
  255. package/src/scripts/audit_skill_overlap.ts +261 -0
  256. package/src/scripts/before_complete_hook.ts +243 -12
  257. package/src/scripts/bench_touched_file_quality.ts +238 -0
  258. package/src/scripts/check_enforcement_matrix.ts +377 -20
  259. package/src/scripts/check_estate_count.ts +130 -153
  260. package/src/scripts/check_finding_dispositions.ts +116 -4
  261. package/src/scripts/check_hook_bundle_composition.ts +370 -0
  262. package/src/scripts/check_kernel_edit_ratified.ts +49 -11
  263. package/src/scripts/check_memory.ts +7 -1
  264. package/src/scripts/check_release_highlights.ts +10 -0
  265. package/src/scripts/check_release_pr_shape.ts +38 -15
  266. package/src/scripts/check_standing_rule_delivery.ts +33 -13
  267. package/src/scripts/check_verify_expectation_delta.ts +521 -0
  268. package/src/scripts/code_graph/detect.ts +127 -7
  269. package/src/scripts/code_graph/foreign.ts +194 -0
  270. package/src/scripts/code_graph/query.ts +217 -4
  271. package/src/scripts/code_graph/types.ts +26 -2
  272. package/src/scripts/code_graph/validate.ts +3 -1
  273. package/src/scripts/code_graph/verbs.ts +103 -0
  274. package/src/scripts/compile_hook_manifest.ts +33 -1
  275. package/src/scripts/condense.ts +14 -27
  276. package/src/scripts/estate_exemption_shape.ts +202 -0
  277. package/src/scripts/generate_host_cost_table.ts +1 -1
  278. package/src/scripts/hook_manifest.json +1 -1
  279. package/src/scripts/hook_manifest.yaml +150 -9
  280. package/src/scripts/hooks/block_config_weakening.ts +67 -2
  281. package/src/scripts/hooks/block_plumbing_writes.ts +29 -1
  282. package/src/scripts/hooks/code_graph_context_hook.ts +252 -23
  283. package/src/scripts/hooks/concern_failure_policy.ts +280 -0
  284. package/src/scripts/hooks/dispatch_hook.ts +52 -52
  285. package/src/scripts/hooks/exit_codes.ts +2 -1
  286. package/src/scripts/hooks/host_lowering.json +1 -1
  287. package/src/scripts/hooks/host_lowering.ts +19 -1
  288. package/src/scripts/hooks/one_question_per_ask_hook.ts +41 -25
  289. package/src/scripts/hooks/payload_stub.ts +10 -1
  290. package/src/scripts/hooks/rule_inject_hook.ts +757 -63
  291. package/src/scripts/hooks/table_fingerprint.ts +21 -0
  292. package/src/scripts/hooks/telemetry_usage_hook.ts +64 -8
  293. package/src/scripts/hooks/turn_end_gate_hook.ts +84 -0
  294. package/src/scripts/hooks_status.ts +112 -0
  295. package/src/scripts/injection_scan_hook.ts +7 -0
  296. package/src/scripts/install.ts +72 -185
  297. package/src/scripts/installed_layer_report.ts +103 -0
  298. package/src/scripts/language_mirror_hook.ts +15 -6
  299. package/src/scripts/learning_sidecar.ts +119 -8
  300. package/src/scripts/lint_hook_manifest.ts +53 -0
  301. package/src/scripts/lint_major_migration_sections.ts +156 -26
  302. package/src/scripts/lint_pack_boundaries.ts +63 -5
  303. package/src/scripts/lint_rule_law_section.ts +517 -0
  304. package/src/scripts/lint_workflow_security.ts +161 -5
  305. package/src/scripts/mcp_server/consumer_tool_catalog.json +53 -0
  306. package/src/scripts/mcp_server/graph_tools.ts +72 -3
  307. package/src/scripts/measure_routing_signal.ts +54 -16
  308. package/src/scripts/measure_skill_ranker_baseline.ts +216 -8
  309. package/src/scripts/memory_learn_hook.ts +91 -6
  310. package/src/scripts/model_rule_injection.ts +52 -5
  311. package/src/scripts/project_thin_rules.ts +34 -327
  312. package/src/scripts/report_evidence_temperature.ts +657 -0
  313. package/src/scripts/report_installed_rule_links.ts +157 -0
  314. package/src/scripts/report_label_agreement.ts +233 -0
  315. package/src/scripts/report_obligation_mechanism.ts +230 -0
  316. package/src/scripts/report_single_token_triggers.ts +277 -0
  317. package/src/scripts/report_skill_ranker_confusion.ts +442 -0
  318. package/src/scripts/report_standing_payload_by_host.ts +188 -15
  319. package/src/scripts/report_verification_record_coverage.ts +306 -0
  320. package/src/scripts/rule_activation_census.ts +5 -2
  321. package/src/scripts/rule_trigger_eval.ts +9 -9
  322. package/src/scripts/schemas/review-findings.schema.json +3 -2
  323. package/src/scripts/schemas/rule.schema.json +5 -0
  324. package/src/scripts/self_review_gate.ts +62 -7
  325. package/src/scripts/skill_tools/score_skill_relevance.ts +290 -25
  326. package/src/scripts/sweep_skill_ranker_signals.ts +129 -0
  327. package/src/scripts/sync_gitignore.ts +29 -235
  328. package/src/scripts/trigger_eval_grandfather.json +0 -5
  329. package/src/server/schemas/settings.ts +10 -0
  330. package/src/shared/skillRanking.ts +117 -11
  331. package/dist/cli-delegate/chunk-JNMVLWLR.js +0 -4457
  332. package/dist/cli-delegate/chunk-YDPILCOL.js +0 -152
@@ -0,0 +1,161 @@
1
+ {
2
+ "_comment": "The high-consequence rule class (road-to-rule-laws-that-can-stand Phase 2). The CRITERION lives in the `consequence_class` property of src/scripts/schemas/rule.schema.json and is the only thing that selects a member — this file is the list the criterion produced, not a list chosen first, which is why `excluded` carries a reason per row and is walked by id against the router. A rule earns a row by what its own law section obligates, never by its name or its pack. `members` is the whole class. `no_stub` is the subset whose law cannot stand in a stub today — missing, or over the 2,000-char ceiling — each with the reason; those project FULL-BODIED, which is strictly safer than a shortened copy, and the subset is what stops the class being quietly shrunk by not writing a law. A member in that state and NOT in `no_stub` fails the projection. Checked by tests/scripts/rule_consequence_class.test.ts.",
3
+ "criterion_ref": "src/scripts/schemas/rule.schema.json#/properties/consequence_class",
4
+ "members": {
5
+ "autonomous-execution": {
6
+ "clause": "authority-bypass",
7
+ "why": "The Hard Floor and the commit default are authority boundaries."
8
+ },
9
+ "broken-access-control": {
10
+ "clause": "security-boundary",
11
+ "why": "The law is the per-object ownership and tenant check before data is returned. Violating it hands one user another user's data."
12
+ },
13
+ "command-suggestion-policy": {
14
+ "clause": "authority-bypass",
15
+ "why": "The law is SUGGEST, NEVER INVOKE. Violating it starts a command flow the user never picked."
16
+ },
17
+ "decision-revisit-gate": {
18
+ "clause": "authority-bypass",
19
+ "why": "The law routes a reopened decision to the council by default and to the owner for the reserved set. Violating it executes an owner-reserved transition on the agent's own authority."
20
+ },
21
+ "doc-screenshot-hygiene": {
22
+ "clause": "irreversible-external",
23
+ "why": "The law calls a shipped screenshot with unredacted data an irreversible published egress, in those words."
24
+ },
25
+ "domain-safety-disclaimer": {
26
+ "clause": "authority-bypass",
27
+ "why": "The law refuses diagnosis and dosage outright and makes every advisory draft carry its disclaimer. Violating it issues a professional judgement the agent is not licensed to make."
28
+ },
29
+ "domain-safety-pii": {
30
+ "clause": "security-boundary",
31
+ "why": "The law keeps raw direct identifiers out of drafts, logs and exports. Violating it is the data exposure."
32
+ },
33
+ "domain-safety-retention": {
34
+ "clause": "irreversible-external",
35
+ "why": "The law forbids recommending deletion under audit, litigation or regulatory hold. Violating it destroys records that cannot be recovered."
36
+ },
37
+ "engineering-safety-floor": {
38
+ "clause": "irreversible-external",
39
+ "why": "The law restates the Hard Floor for deploys, production-trunk merges and production data. Every trigger it names is an action this agent cannot undo."
40
+ },
41
+ "evaluator-independence": {
42
+ "clause": "authority-bypass",
43
+ "why": "The law forbids authoring the verdict into an evaluator's prompt. Violating it manufactures the independent review a gate then relies on."
44
+ },
45
+ "fast-path-marker-visibility": {
46
+ "clause": "authority-bypass",
47
+ "why": "The marker is the only signal that a fast path resolved a decision instead of the user. Swallowing it hides the substitution."
48
+ },
49
+ "finance-safety-floor": {
50
+ "clause": "authority-bypass",
51
+ "why": "The law refuses a final invest or raise call. Violating it takes a decision the law assigns to the human."
52
+ },
53
+ "git-history-discipline": {
54
+ "clause": "irreversible-external",
55
+ "why": "The law forbids rewriting published history and dropping commits the session did not author. A force-push over someone else's work is not recoverable from here."
56
+ },
57
+ "image-likeness-and-rights": {
58
+ "clause": "irreversible-external",
59
+ "why": "The law gates a real person's likeness, a trademarked mark and a living artist's style behind explicit rights. The harm lands outside the repository and outlives the render."
60
+ },
61
+ "legal-safety-floor": {
62
+ "clause": "authority-bypass",
63
+ "why": "The law refuses a binding legal conclusion and stops outright at individual-case examination. Violating it performs regulated activity."
64
+ },
65
+ "lethal-trifecta-guard": {
66
+ "clause": "security-boundary",
67
+ "why": "The law forbids shipping private-data access, untrusted ingestion and external comms on one autonomous path. That combination is the exfiltration shape itself."
68
+ },
69
+ "low-impact-corpus-privacy-floor": {
70
+ "clause": "security-boundary",
71
+ "why": "The law is a non-bypassable redaction gate on entries that leave the project repo. It is the last check before an upstream push."
72
+ },
73
+ "question-not-instruction": {
74
+ "clause": "authority-bypass",
75
+ "why": "A question is not authorization to act — the purest authority-bypass clause in the tree."
76
+ },
77
+ "runtime-safety": {
78
+ "clause": "security-boundary",
79
+ "why": "The rule bounds what an `automated` skill may execute and which handlers are allowlisted."
80
+ },
81
+ "secret-vcs-guard": {
82
+ "clause": "security-boundary",
83
+ "why": "The law stops before a credential reaches version control. Once committed it is leaked in every clone, and only rotation undoes it."
84
+ },
85
+ "security-sensitive-stop": {
86
+ "clause": "security-boundary",
87
+ "why": "The law requires a threat pass before the first edit to auth, tenancy, billing, secrets, uploads and public endpoints. It is the gate the other security rules assume ran."
88
+ },
89
+ "session-canary": {
90
+ "clause": "authority-bypass",
91
+ "why": "The honesty clause forbids papering over a dropped canary. Faking continuity removes the user's only signal that the session is degrading, which is their decision to make."
92
+ },
93
+ "settings-ask-protocol": {
94
+ "clause": "authority-bypass",
95
+ "why": "The law calls a silent permissive default a decision taken in the user's name, and forbids it."
96
+ },
97
+ "source-confidentiality": {
98
+ "clause": "security-boundary",
99
+ "why": "The law keeps derivation attribution out of the tracked tree. A disclosure that lands in git history is not withdrawable."
100
+ },
101
+ "strategy-safety-floor": {
102
+ "clause": "authority-bypass",
103
+ "why": "The law refuses a final enter / build / buy / reorg call and names the human decision owner."
104
+ },
105
+ "tool-safety": {
106
+ "clause": "security-boundary",
107
+ "why": "The rule is the deny-by-default tool allowlist and the no-hidden-credentials floor."
108
+ },
109
+ "untrusted-input-defense": {
110
+ "clause": "security-boundary",
111
+ "why": "The law makes fetched, tool, file, RAG and MCP content data and never instructions. Violating it is how an attacker's text becomes the agent's plan."
112
+ },
113
+ "user-interrupt-priority": {
114
+ "clause": "authority-bypass",
115
+ "why": "The law stops the current task on a new instruction and asks before resuming. Silent resume continues work the user withdrew."
116
+ }
117
+ },
118
+ "no_stub": {
119
+ "autonomous-execution": {
120
+ "clause": "authority-bypass",
121
+ "why": "The Hard Floor and the commit default are authority boundaries.",
122
+ "reason": "No heading matching `Iron Law`: the rule states its floors under `## Hard Floor` and its bound in a fence under `## Validation-loop budget`. Promoting one of those to a law heading is authoring a law, which is a change to what the rule obligates and is out of scope for a projection change. Projects full-bodied until a law section is written deliberately."
123
+ },
124
+ "legal-safety-floor": {
125
+ "clause": "authority-bypass",
126
+ "why": "The law refuses a binding legal conclusion and stops outright at individual-case examination.",
127
+ "reason": "Law section is 2,023 characters, 23 over the hard ceiling, and carries a dated exception in rule-law-ceilings.json. It cannot stand in a stub until the RDG STOP block is reviewed out of the law and into the body beside it."
128
+ },
129
+ "question-not-instruction": {
130
+ "clause": "authority-bypass",
131
+ "why": "A question is not authorization to act — the purest authority-bypass clause in the tree.",
132
+ "reason": "No heading matching `Iron Law`: the rule opens with a bare fence under its title. The fence is the law and promoting it needs a heading, which is a rule edit rather than a projection one."
133
+ },
134
+ "runtime-safety": {
135
+ "clause": "security-boundary",
136
+ "why": "The rule bounds what an `automated` skill may execute and which handlers are allowlisted.",
137
+ "reason": "No heading matching `Iron Law`: the constraints are a bullet list under `## Constraints`. Named by the roadmap's own Context as one of the two sharpest cases in the 35."
138
+ },
139
+ "tool-safety": {
140
+ "clause": "security-boundary",
141
+ "why": "The rule is the deny-by-default tool allowlist and the no-hidden-credentials floor.",
142
+ "reason": "No heading matching `Iron Law`: the floor is stated under `## Constraints` and `## Scoped grants`. Named by the roadmap's own Context as one of the two sharpest cases in the 35."
143
+ }
144
+ },
145
+ "excluded": {
146
+ "active-remediation": "A spotted issue left unfixed costs quality and rework. Its one genuinely high-consequence branch — a live cross-user exposure — is already `broken-access-control`'s law, which is in the class.",
147
+ "delegation-policy": "Dispatch shape, not authority: it delegates nothing a Hard Floor would stop, and says so.",
148
+ "downstream-changes": "A missed caller is a broken build, which is loud and recoverable.",
149
+ "history-discipline": "Audit coverage is an auditability property. Its privacy interlock points at `domain-safety-pii`, which is in the class.",
150
+ "media-governance-routing": "A routing rule: it surfaces the project-local media policies. The rights obligation itself is `image-likeness-and-rights`, which is in the class.",
151
+ "media-sync-ground-truth": "The spend gate is real but the obligation its law states is a sourcing one — timing and singer come from the transcribed audio. The paid-render confirmation it builds on is `non-destructive-by-default`, a kernel rule that is never thinned.",
152
+ "no-attribution-footers": "An attribution footer is external but editable, so the action is not irreversible.",
153
+ "no-decorative-emojis-in-git-surfaces": "An emoji in a PR title or a commit subject is external but editable, and the fix is an edit rather than a rotation or a retraction. Same reason as no-attribution-footers, one surface over.",
154
+ "no-pr-progress-comments": "A noisy PR thread is a cost, not a boundary, and a comment can be deleted.",
155
+ "playbook-precedence": "Its propose-never-silently-run clause defers to the same floor `non-destructive-by-default` holds; on its own it decides which procedure to follow.",
156
+ "provider-lifecycle-discipline": "Tier declaration and a refuse-and-surface ask. The spend floor it defers to is a kernel rule.",
157
+ "scale-discipline": "An exhausted connection pool is an availability failure, not a security boundary.",
158
+ "senior-engineering-discipline": "The anchor that ROUTES to the invisible controls. The controls themselves are in the class; the anchor is not, or the criterion would admit every rule that mentions security.",
159
+ "think-before-action": "Analysis discipline. Violating it wastes work."
160
+ }
161
+ }
@@ -0,0 +1,99 @@
1
+ {
2
+ "_comment": "Per-rule law-section and body ceilings (road-to-rule-laws-that-can-stand Phase 1). SHRINK-ONLY on every axis: an id may LEAVE `missing`, never join it; a recorded exception is a ceiling the rule may fall below and never rise above. Raising one to clear a red is the config-weakening move this repo blocks by construction — `--write-baseline` refuses to raise, it only re-records the smaller of old and current. A rule at its exception is not thereby correct, only not worse. Regenerate with `lint_rule_law_section --write-baseline`; never hand-edit the counts, but DO hand-edit `owner`, `review` and `reason` — those are the review contract and the generator only seeds them.",
3
+ "measured_at_commit": "30f7d21f6",
4
+ "missing": [
5
+ "architecture",
6
+ "artifact-drafting-protocol",
7
+ "artifact-engagement-recording",
8
+ "augment-edit-discipline",
9
+ "autonomous-execution",
10
+ "cli-output-handling",
11
+ "commit-conventions",
12
+ "context-hygiene",
13
+ "copilot-routing",
14
+ "devcontainer-routing",
15
+ "docker-commands",
16
+ "external-code-graph-interop",
17
+ "improve-before-implement",
18
+ "laravel-routing",
19
+ "laravel-translations",
20
+ "linked-projects-onboarding-gate",
21
+ "markdown-safe-codeblocks",
22
+ "minimal-safe-diff",
23
+ "missing-tool-handling",
24
+ "model-recommendation",
25
+ "onboarding-gate",
26
+ "php-coding",
27
+ "question-not-instruction",
28
+ "reviewer-awareness",
29
+ "role-mode-adherence",
30
+ "rule-type-governance",
31
+ "runtime-safety",
32
+ "skill-improvement-trigger",
33
+ "skill-quality",
34
+ "slash-command-routing-policy",
35
+ "source-of-truth",
36
+ "symfony-routing",
37
+ "test-first",
38
+ "tool-safety",
39
+ "upstream-proposal"
40
+ ],
41
+ "law_exceptions": {
42
+ "legal-safety-floor": {
43
+ "chars": 2023,
44
+ "owner": "agent-config-maintainer",
45
+ "review": "2026-12-01",
46
+ "reason": "Three laws a regulator reads literally (no final legal call, consent gate, council gate) plus the RDG STOP block whose wording is quoted verbatim to the user. At 2023 characters it is 23 over the ceiling, so the review is about whether the STOP block belongs in the law or beside it."
47
+ },
48
+ "preservation-guard": {
49
+ "chars": 2418,
50
+ "owner": "agent-config-maintainer",
51
+ "review": "2026-12-01",
52
+ "reason": "The law IS a checklist: each bullet names a passage class that must survive a transform, and check_condensation reads the same list. Shortening the prose would shorten the contract the gate enforces. Review target: move the reject criteria out and leave the preservation checklist."
53
+ },
54
+ "roadmap-progress-sync": {
55
+ "chars": 5387,
56
+ "owner": "agent-config-maintainer",
57
+ "review": "2026-12-01",
58
+ "reason": "Three separate Iron Laws (dashboard sync, checkbox cadence, no silent archive) plus the preservation test that resolves a deferred item. Splitting them would create three rules with one trigger set; collapsing them would drop an obligation. Review target: move the resolution procedure under Iron Law 3 behind load_context and keep the three laws."
59
+ }
60
+ },
61
+ "body_exceptions": {
62
+ "code-provenance": {
63
+ "chars": 8106,
64
+ "owner": "agent-config-maintainer",
65
+ "review": "2026-12-01",
66
+ "reason": "Two layers (code borrows and the knowledge/harvest layer) each with its own law, plus the no-CI-detector honesty section recording a measured gate decision."
67
+ },
68
+ "context-hygiene": {
69
+ "chars": 8320,
70
+ "owner": "agent-config-maintainer",
71
+ "review": "2026-12-01",
72
+ "reason": "Carries the freshness ladder, the 3-failure rule, loop detection and the 15/25 read-loop rule, each with its own self-check. Already migrated once into context-hygiene-mechanics and re-grew."
73
+ },
74
+ "decision-revisit-gate": {
75
+ "chars": 8566,
76
+ "owner": "agent-config-maintainer",
77
+ "review": "2026-12-01",
78
+ "reason": "The largest rule in the tree. Carries the five-step gate, the two descriptive axes, the owner-reserved table and an honest-enforcement section. Phase 1.4 of road-to-rule-laws-that-can-stand names it as a first mover for the load_context split."
79
+ },
80
+ "design-fidelity": {
81
+ "chars": 8777,
82
+ "owner": "agent-config-maintainer",
83
+ "review": "2026-12-01",
84
+ "reason": "Carries the artefact-maturity axis, the fidelity-mode table and the routing matrix. Phase 1.4 names it as a first mover."
85
+ },
86
+ "roadmap-progress-sync": {
87
+ "chars": 9308,
88
+ "owner": "agent-config-maintainer",
89
+ "review": "2026-12-01",
90
+ "reason": "Three laws plus the deferred-item resolution procedure and the later/PR-gate dispositions. The dispositions are mechanism, not obligation, and are the load_context candidate."
91
+ },
92
+ "settings-ask-protocol": {
93
+ "chars": 8099,
94
+ "owner": "agent-config-maintainer",
95
+ "review": "2026-12-01",
96
+ "reason": "Carries the four slots, the seven keys, the non-interactive rule and the consent-verdict contract. The key inventory is reference material and is the load_context candidate."
97
+ }
98
+ }
99
+ }
@@ -188,6 +188,21 @@ export interface ClaudePathsPlan {
188
188
  dropped: { pattern: string; reason: 'unresolved-placeholder' | 'over-budget' | 'mixed-triggers' }[];
189
189
  }
190
190
 
191
+ /**
192
+ * The two trigger keys `derive_trigger_globs` turns into a host glob. Kept
193
+ * beside it deliberately: the day a third path-shaped key is added there, this
194
+ * set is the other half of the same edit, and a reader looking at either one
195
+ * sees the pair.
196
+ */
197
+ const PATH_SHAPED_TRIGGER_KEYS: ReadonlySet<string> = new Set(['file_pattern', 'path_prefix']);
198
+
199
+ /**
200
+ * Keys on a trigger object that match nothing. `reason:` documents why the
201
+ * trigger exists; counting it would make every annotated path trigger look
202
+ * mixed and scope nothing at all.
203
+ */
204
+ const NON_MATCHING_TRIGGER_KEYS: ReadonlySet<string> = new Set(['reason']);
205
+
191
206
  /**
192
207
  * Does this rule declare any trigger that is NOT path-shaped?
193
208
  *
@@ -199,6 +214,18 @@ export interface ClaudePathsPlan {
199
214
  * rule loads unconditionally. The emitter fed both semantics from one list, so
200
215
  * a rule that declared keywords *and* one path glob silently became
201
216
  * path-gated-only on Claude.
217
+ *
218
+ * **The test is a negation, not a list of non-path kinds**, and the difference
219
+ * is not stylistic. The predicate used to enumerate `keyword` and `phrase`,
220
+ * which left `command` — a prompt route like any other — uncounted:
221
+ * `roadmap-progress-sync` declares one `path_prefix` and three `command`
222
+ * triggers, so it was emitted with an exclusive `paths:` and its three command
223
+ * routes never fired on this host. A list can only ever be as current as its
224
+ * last edit; the schema's `additionalProperties: false` means a new match key
225
+ * arrives as a deliberate schema change, and until someone teaches
226
+ * `derive_trigger_globs` to make a glob from it, treating it as promptable is
227
+ * the fail-safe reading — the rule stays unconditional rather than silently
228
+ * narrowing to a path it does not have.
202
229
  */
203
230
  export function _has_non_path_trigger(meta: Record<string, unknown>): boolean {
204
231
  const triggers = meta['triggers'];
@@ -206,8 +233,21 @@ export function _has_non_path_trigger(meta: Record<string, unknown>): boolean {
206
233
  for (const t of triggers) {
207
234
  if (t === null || typeof t !== 'object' || Array.isArray(t)) continue;
208
235
  const obj = t as Record<string, unknown>;
209
- if (typeof obj['keyword'] === 'string' && obj['keyword']) return true;
210
- if (typeof obj['phrase'] === 'string' && obj['phrase']) return true;
236
+ for (const [key, value] of Object.entries(obj)) {
237
+ if (PATH_SHAPED_TRIGGER_KEYS.has(key) || NON_MATCHING_TRIGGER_KEYS.has(key)) continue;
238
+ // A string that is not blank, or ANY non-string value. The schema
239
+ // types all five match keys as strings today, so the second half is
240
+ // for the key that arrives later: a `keyword: ["a","b"]` read as
241
+ // "not a string, so not a trigger" would silently narrow the rule
242
+ // to a path it does not have, which is the exact failure the
243
+ // negation above exists to prevent. Undefined and null are the one
244
+ // shape that means "absent".
245
+ if (typeof value === 'string') {
246
+ if (value.trim() !== '') return true;
247
+ } else if (value !== undefined && value !== null) {
248
+ return true;
249
+ }
250
+ }
211
251
  }
212
252
  return false;
213
253
  }
@@ -230,8 +270,10 @@ export function _claude_paths_plan(meta: Record<string, unknown>): ClaudePathsPl
230
270
  const globs: string[] = [];
231
271
  const dropped: ClaudePathsPlan['dropped'] = [];
232
272
 
233
- // A rule that ALSO declares keyword / phrase triggers gets no `paths:` at
234
- // all, and therefore keeps loading unconditionally on Claude Code.
273
+ // A rule that ALSO declares a non-path trigger — keyword, phrase, command,
274
+ // or a match key added to the schema later — gets no `paths:` at all, and
275
+ // therefore keeps loading unconditionally on Claude Code. The predicate is
276
+ // a negation of the two path-shaped keys, deliberately: see its docblock.
235
277
  //
236
278
  // Emitting `paths:` here would narrow the rule to a path match and discard
237
279
  // every keyword the author wrote, because this host reads the list as the
@@ -132,7 +132,7 @@ export function rewriteAndReport(
132
132
  quiet: boolean,
133
133
  info: (m: string) => void,
134
134
  warn: (m: string) => void,
135
- wrappers?: { wrapped: readonly string[]; reserved: readonly string[] },
135
+ wrappers?: { wrapped: readonly string[]; reserved: readonly string[]; foreign?: readonly string[] },
136
136
  preserved: ReadonlySet<string> = new Set(),
137
137
  ): ClaudeRuleRewriteResult {
138
138
  const result = rewriteClaudeRules(rulesDir, preserved);
@@ -157,6 +157,15 @@ export function rewriteAndReport(
157
157
  'commands remain available',
158
158
  );
159
159
  }
160
+ if (wrappers.foreign !== undefined && wrappers.foreign.length > 0) {
161
+ // Reported, never removed. The name shadows a host built-in, but
162
+ // this package did not write the file, so deleting it would be
163
+ // taking a neighbour's command away without saying so.
164
+ info(
165
+ ` claude-code: kept ${wrappers.foreign.length} neighbour command file(s) whose ` +
166
+ `name is a Claude Code built-in (${wrappers.foreign.join(', ')}); not ours to remove`,
167
+ );
168
+ }
160
169
  }
161
170
  if (result.rewritten > 0 && !quiet) {
162
171
  info(
@@ -44,6 +44,14 @@ import {
44
44
  _emit_windsurf_rule,
45
45
  strip_frontmatter,
46
46
  } from '../scripts/condense.js';
47
+ import {
48
+ WINDSURFRULES_HEADER,
49
+ cleanOwnedOnly,
50
+ keptLine,
51
+ mayWriteRuleFile,
52
+ mayWriteWindsurfRules,
53
+ ownedPaths,
54
+ } from './host_rule_ownership.js';
47
55
 
48
56
  function _ruleFiles(rulesDir: string): string[] {
49
57
  return fs
@@ -60,63 +68,64 @@ function _ruleFiles(rulesDir: string): string[] {
60
68
  });
61
69
  }
62
70
 
63
- /** Remove entries not in `valid` (mirror of condense's `_clean_modern_dir`). */
64
- function _cleanDir(dir: string, valid: ReadonlySet<string>): void {
65
- let entries: string[];
66
- try {
67
- entries = fs.readdirSync(dir);
68
- } catch {
69
- return;
70
- }
71
- for (const name of entries) {
72
- if (name === 'README.md' || valid.has(name)) {
73
- continue;
74
- }
75
- const full = path.join(dir, name);
76
- try {
77
- if (fs.lstatSync(full).isDirectory()) {
78
- fs.rmSync(full, { recursive: true, force: true });
79
- } else {
80
- fs.unlinkSync(full);
81
- }
82
- } catch {
83
- /* best-effort clean */
84
- }
85
- }
86
- }
87
-
88
71
  export function emitCursor(rulesDir: string, projectRoot: string): number {
89
72
  const targetDir = path.join(projectRoot, '.cursor', 'rules');
90
73
  const files = _ruleFiles(rulesDir);
74
+ const owned = ownedPaths(projectRoot);
91
75
  const valid = new Set<string>();
76
+ let blocked = 0;
92
77
  for (const src of files) {
93
78
  const name = `${path.basename(src, '.md')}.mdc`;
94
- _emit_cursor_mdc(src, path.join(targetDir, name));
79
+ const target = path.join(targetDir, name);
80
+ // Ownership BEFORE the write: a neighbour's file under one of our
81
+ // names would otherwise be destroyed and then skipped by the cleanup
82
+ // pass, which only looks at names this run did not emit.
83
+ if (!mayWriteRuleFile(target, owned)) {
84
+ blocked += 1;
85
+ continue;
86
+ }
87
+ _emit_cursor_mdc(src, target);
95
88
  valid.add(name);
96
89
  }
97
- _cleanDir(targetDir, valid);
90
+ const { kept } = cleanOwnedOnly(targetDir, valid, owned);
91
+ const line = keptLine(kept + blocked);
92
+ if (line !== null) process.stdout.write(`cursor: ${line}\n`);
98
93
  return files.length;
99
94
  }
100
95
 
101
96
  export function emitWindsurf(rulesDir: string, projectRoot: string): number {
102
97
  const perRuleDir = path.join(projectRoot, '.windsurf', 'rules');
103
98
  const files = _ruleFiles(rulesDir);
99
+ const owned = ownedPaths(projectRoot);
104
100
  const valid = new Set<string>();
101
+ let blocked = 0;
105
102
  for (const src of files) {
106
103
  const name = path.basename(src);
107
- _emit_windsurf_rule(src, path.join(perRuleDir, name));
104
+ const target = path.join(perRuleDir, name);
105
+ if (!mayWriteRuleFile(target, owned)) {
106
+ blocked += 1;
107
+ continue;
108
+ }
109
+ _emit_windsurf_rule(src, target);
108
110
  valid.add(name);
109
111
  }
110
- _cleanDir(perRuleDir, valid);
112
+ const { kept } = cleanOwnedOnly(perRuleDir, valid, owned);
113
+ const line = keptLine(kept + blocked);
114
+ if (line !== null) process.stdout.write(`windsurf: ${line}\n`);
111
115
 
112
116
  // Concatenated legacy surface — same format as the projection-path
113
117
  // generator (condense.generate_windsurfrules), consumer-sourced header.
114
- const parts = ['# Auto-generated from .augment/rules/ — do not edit directly\n'];
115
- for (const src of files) {
116
- const content = strip_frontmatter(fs.readFileSync(src, 'utf-8'));
117
- parts.push(`---\n\n${content.trim()}\n`);
118
+ const target = path.join(projectRoot, '.windsurfrules');
119
+ if (mayWriteWindsurfRules(target, owned)) {
120
+ const parts = [`${WINDSURFRULES_HEADER}\n`];
121
+ for (const src of files) {
122
+ const content = strip_frontmatter(fs.readFileSync(src, 'utf-8'));
123
+ parts.push(`---\n\n${content.trim()}\n`);
124
+ }
125
+ fs.writeFileSync(target, parts.join('\n') + '\n', 'utf-8');
126
+ } else {
127
+ process.stdout.write('windsurf: kept: 1 neighbour file(s) (.windsurfrules)\n');
118
128
  }
119
- fs.writeFileSync(path.join(projectRoot, '.windsurfrules'), parts.join('\n') + '\n', 'utf-8');
120
129
  return files.length;
121
130
  }
122
131
 
@@ -0,0 +1,109 @@
1
+ /**
2
+ * The rule scope governing a GLOBAL deploy.
3
+ *
4
+ * Moved out of `src/scripts/install.ts` by
5
+ * `road-to-an-installed-layer-that-is-thinned` step 1.1, which adds the thinning
6
+ * call to the same deploy loop. `check_source_size_budget` is a one-way ratchet
7
+ * with no per-change headroom, and this repository's stated convention is that
8
+ * the total falls through EXTRACTION and never rises through a baseline edit —
9
+ * the same reason `claudeRuleRewrite` holds the claude-code post-copy reporting
10
+ * rather than the installer. This function is the nearest self-contained block
11
+ * to the change: it is the predicate the deploy loop resolves once per run, it
12
+ * reaches nothing else in the installer, and its four environment touches are
13
+ * already parameters in all but name.
14
+ *
15
+ * BEHAVIOUR IS UNCHANGED, DELIBERATELY. The body is the installer's, verbatim
16
+ * apart from the four injected seams below; `install.ts` keeps a wrapper of the
17
+ * same name and signature, so its re-export and every caller and test see what
18
+ * they saw before. A behaviour change smuggled into an extraction is the one
19
+ * thing that would make the line saving not worth having.
20
+ *
21
+ * The seams are injected rather than imported because each one is an
22
+ * install-time decision the installer owns: where the global settings doc lives,
23
+ * how this build reads and parses YAML, what the packaged template says, and
24
+ * where a warning goes.
25
+ */
26
+
27
+ import { LEGACY_ALL, ruleScopeFromSettings, type RuleScope } from './rule_scope.js';
28
+
29
+ export interface GlobalRuleScopeInputs {
30
+ /** The global settings file, or `null` when no artefact exists at all. */
31
+ readonly settingsPath: string | null;
32
+ /** The package being installed from. */
33
+ readonly packageRoot: string;
34
+ /** Read a file as text; throws like `fs.readFileSync` when it cannot. */
35
+ readonly readText: (p: string) => string;
36
+ /** Parse YAML permissively — anything unparseable comes back non-object. */
37
+ readonly parseYaml: (text: string) => unknown;
38
+ /** The packaged template settings, read only on a genuinely fresh machine. */
39
+ readonly loadDefaults: () => Record<string, unknown>;
40
+ /** Where a loud fallback goes. */
41
+ readonly warn: (message: string) => void;
42
+ }
43
+
44
+ function isPlainObject(v: unknown): boolean {
45
+ return typeof v === 'object' && v !== null && !Array.isArray(v);
46
+ }
47
+
48
+ /**
49
+ * Resolve the rule scope governing THIS global deploy — the same way the wizard
50
+ * does (`src/server/routes/install.ts::_resolveRuleScope`), so the two global
51
+ * paths cannot ship different rule sets for the same settings.
52
+ *
53
+ * Settings resolution mirrors `_resolve_scoped_projection` above: an existing
54
+ * global settings doc is authoritative, and only a genuinely fresh machine falls
55
+ * through to the packaged template. Any read or parse failure resolves to
56
+ * `LEGACY_ALL` — over-shipping is the safe direction, and the compat exclusion
57
+ * (`source-of-truth.md`) still applies even then.
58
+ */
59
+ export function resolveGlobalRuleScope(inputs: GlobalRuleScopeInputs): RuleScope {
60
+ const { packageRoot, warn } = inputs;
61
+ const settings_path = inputs.settingsPath;
62
+
63
+ // No global settings artefact at all — a genuinely fresh machine. Fall
64
+ // through to the packaged template silently: there is no user decision to
65
+ // contradict, and this is the documented upgrade-compat path.
66
+ if (settings_path === null) {
67
+ try {
68
+ return ruleScopeFromSettings(inputs.loadDefaults(), packageRoot);
69
+ } catch {
70
+ return LEGACY_ALL;
71
+ }
72
+ }
73
+
74
+ // A doc EXISTS, so the user has expressed a configuration. Failing to read
75
+ // it is not the same as not having one: falling back to legacy-all here
76
+ // ships the maintainer-only rules the user may have deliberately scoped out,
77
+ // and `_load_yaml_doc` would report that as an indistinguishable `{}`. So
78
+ // parse it explicitly and be LOUD when it does not parse — over-shipping
79
+ // stays the safe direction, but it must not be a silent one.
80
+ let text: string;
81
+ try {
82
+ text = inputs.readText(settings_path);
83
+ } catch (e) {
84
+ warn(
85
+ `could not read ${settings_path} (${String(e)}) — rule scoping falls back ` +
86
+ 'to legacy-all, so ALL rules including maintainer-only ones will be ' +
87
+ 'installed. Fix the file to restore scoping.',
88
+ );
89
+ return LEGACY_ALL;
90
+ }
91
+ const parsed = inputs.parseYaml(text);
92
+ if (!isPlainObject(parsed)) {
93
+ warn(
94
+ `${settings_path} is not a YAML mapping — rule scoping falls back to ` +
95
+ 'legacy-all, so ALL rules including maintainer-only ones will be ' +
96
+ 'installed. Fix the file to restore scoping.',
97
+ );
98
+ return LEGACY_ALL;
99
+ }
100
+ try {
101
+ return ruleScopeFromSettings(parsed as Record<string, unknown>, packageRoot);
102
+ } catch (e) {
103
+ warn(
104
+ `could not derive rule scope from ${settings_path} (${String(e)}) — ` +
105
+ 'falling back to legacy-all; ALL rules will be installed.',
106
+ );
107
+ return LEGACY_ALL;
108
+ }
109
+ }