@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
@@ -27,7 +27,10 @@
27
27
  * `existing-ui-audit` skill (R3 Phase 2). `null` while the audit
28
28
  * has not run; populated dict once the skill returns. `greenfield`
29
29
  * flag plus `greenfield_decision` carry the user's scaffolding
30
- * pick. The audit gate (`work_engine.directives.ui.audit`)
30
+ * pick; `component_taxonomy` and `component_root` carry the
31
+ * granularity taxonomy the project itself evidences, or `'none'`.
32
+ * `'none'` is the value that leaves every downstream step behaving
33
+ * exactly as it did before detection existed. The audit gate (`work_engine.directives.ui.audit`)
31
34
  * refuses to advance to design/apply while the slot is empty or
32
35
  * while `greenfield` is set without a recorded decision.
33
36
  * - `app_spec` — optional greenfield grounding artifact written by
@@ -40,7 +43,12 @@
40
43
  * - `ui_design` — optional design brief produced by
41
44
  * `work_engine.directives.ui.design` (R3 Phase 3 Step 1). Locks
42
45
  * layout / components / states / microcopy / a11y; `design_confirmed`
43
- * carries the user's sign-off.
46
+ * carries the user's sign-off. A `components[]` entry MAY carry a
47
+ * `tier` naming one of `ui_audit.component_taxonomy`'s tiers; the
48
+ * apply step reads it to name the target directory, and it is the
49
+ * only producer of that field. Omit it and the component is
50
+ * reported as a conformance gap rather than placed, which is the
51
+ * honest answer when nothing said where it belongs.
44
52
  * - `ui_scaffold` — optional greenfield scaffold plan written by
45
53
  * `work_engine.directives.ui.scaffold` (greenfield-scaffold
46
54
  * Phase 3). Plan-only and stack-agnostic
@@ -172,6 +180,37 @@ export const KNOWN_DIRECTIVE_SETS: ReadonlySet<string> = new Set([
172
180
  'mixed',
173
181
  ]);
174
182
 
183
+ /**
184
+ * Every value `state.ui_audit.greenfield_decision` may carry.
185
+ *
186
+ * One list, read by the schema here and by the halt that offers the options
187
+ * (`directives/ui/audit.ts`), so an option the user can pick and a value the
188
+ * schema accepts cannot drift apart.
189
+ *
190
+ * `granularity_convention` is option 4: scaffold, and organise what is
191
+ * scaffolded under a named granularity convention. It is a fourth value of the
192
+ * same single-number question rather than a second axis — one number still
193
+ * answers the block, which is what the reply-shape rule requires.
194
+ */
195
+ export const GREENFIELD_DECISIONS: ReadonlyArray<string> = [
196
+ 'scaffold',
197
+ 'bare',
198
+ 'external_reference',
199
+ 'granularity_convention',
200
+ ];
201
+
202
+ /**
203
+ * The greenfield pick that adopts a component-granularity convention.
204
+ *
205
+ * Exported HERE, beside the list it belongs to, and imported by every gate
206
+ * that tests for it — the audit halt that offers it, and the scaffold and
207
+ * app-spec gates that must let it through. It was a bare string literal in
208
+ * four places; a rename would have left those gates silently not matching,
209
+ * and an inert scaffold gate returns SUCCESS, so picking option 4 would have
210
+ * scaffolded nothing with no error anywhere.
211
+ */
212
+ export const GRANULARITY_CONVENTION = 'granularity_convention';
213
+
175
214
  /** Raised when a state payload violates the v1 contract. */
176
215
  export class SchemaError extends Error {
177
216
  constructor(message: string) {
@@ -535,7 +574,7 @@ function _validate_stack(stack: JsonValue): void {
535
574
  * skill returns but before findings land; the gate treats it the
536
575
  * same as `null`. Once populated, `greenfield` (when present)
537
576
  * must be a bool, and `greenfield_decision` (when present) must
538
- * be one of the three documented choices. Other keys (`components`,
577
+ * be one of `GREENFIELD_DECISIONS`. Other keys (`components`,
539
578
  * `patterns`, ...) are validated by the audit handler against the
540
579
  * skill contract — the schema only enforces shape, not content.
541
580
  */
@@ -559,16 +598,27 @@ function _validate_ui_audit(ui_audit: JsonValue): void {
559
598
  : null;
560
599
  if (
561
600
  decision !== null &&
562
- decision !== 'scaffold' &&
563
- decision !== 'bare' &&
564
- decision !== 'external_reference'
601
+ !(typeof decision === 'string' && GREENFIELD_DECISIONS.includes(decision))
565
602
  ) {
566
603
  throw new SchemaError(
567
604
  `state.ui_audit.greenfield_decision must be one of ` +
568
- `'scaffold', 'bare', 'external_reference', or null; ` +
605
+ `${GREENFIELD_DECISIONS.map((d) => `'${d}'`).join(', ')}, or null; ` +
569
606
  `got ${pyRepr(decision)}`,
570
607
  );
571
608
  }
609
+ const taxonomy = 'component_taxonomy' in ui_audit ? ui_audit['component_taxonomy'] : null;
610
+ if (taxonomy !== null && typeof taxonomy !== 'string') {
611
+ throw new SchemaError(
612
+ 'state.ui_audit.component_taxonomy must be a string when present ' +
613
+ "— the project's own tiers joined with '/', or 'none'",
614
+ );
615
+ }
616
+ const root = 'component_root' in ui_audit ? ui_audit['component_root'] : null;
617
+ if (root !== null && typeof root !== 'string') {
618
+ throw new SchemaError(
619
+ 'state.ui_audit.component_root must be a string when present',
620
+ );
621
+ }
572
622
  if (
573
623
  'a11y_baseline' in ui_audit &&
574
624
  !Array.isArray(ui_audit['a11y_baseline'])
@@ -0,0 +1,500 @@
1
+ /**
2
+ * Component-granularity taxonomy detection from a project's own layout.
3
+ *
4
+ * Leaf module — stdlib only, NO intra-`work_engine` imports — so it can be
5
+ * called from a directive, a skill runner, or a test without pulling the
6
+ * engine in. Public API names stay snake_case to mirror the sibling detectors
7
+ * (`stack/detect.ts`, `stack/runner.ts`) 1:1, per ADR-200.
8
+ *
9
+ * What this answers, and what it deliberately does not:
10
+ *
11
+ * The question is *"has this project already chosen a way to organise its
12
+ * components by granularity, and if so what does it call the tiers?"* — never
13
+ * *"which taxonomy should this project use?"*. The recorded answer is the
14
+ * project's OWN tier directory names in the project's own order, joined with
15
+ * `/`, or {@link NO_TAXONOMY}. Nothing canonical is ever written into the
16
+ * record: a project with three tiers records three tiers, and a project whose
17
+ * tiers are called `primitives / patterns / features` records exactly that.
18
+ *
19
+ * That is the whole point of the module. The suite follows a taxonomy a
20
+ * project has chosen; it never forces one onto a project that has not.
21
+ *
22
+ * Why there is a lexicon at all, and what bounds it:
23
+ *
24
+ * Deciding *"is `components/checkout/` a granularity tier or a domain
25
+ * folder?"* cannot be done from structure alone — both are directories full of
26
+ * components. So {@link GRANULARITY_LEXICON} exists as a **recognition aid**:
27
+ * an unordered set of granularity words drawn from several vocabularies, with
28
+ * no ordering, no completeness claim, no level semantics, and no per-tier
29
+ * rules of any kind. It is read to answer "does this bucket name look like a
30
+ * granularity word", and for nothing else.
31
+ *
32
+ * In particular it is NEVER read when a component is placed: placement reads
33
+ * the recorded tier list by name (see `./place.ts`). A project whose tiers are
34
+ * in no lexicon still gets conformance, because a declaration in its own docs
35
+ * outranks inference — which is the path the `declared` fixture exercises.
36
+ *
37
+ * Evidence rules:
38
+ *
39
+ * 1. **Declared beats inferred.** A `## Component taxonomy` section in the
40
+ * project's `DESIGN.md` naming backticked directory names wins outright,
41
+ * provided at least {@link MIN_TIERS} of those names exist on disk under
42
+ * the component root. Grounding the declaration in the filesystem keeps a
43
+ * stale doc from declaring a taxonomy the tree does not have.
44
+ * 2. **Inference needs a majority, never a single hit.** At least
45
+ * {@link MIN_TIERS} immediate buckets under the component root must carry
46
+ * lexicon names, AND they must be at least {@link TIER_MAJORITY} of all
47
+ * buckets. One granularity-sounding folder among domain folders is a
48
+ * collision, not a convention — that is the false-positive direction the
49
+ * `collision` fixture pins.
50
+ * 3. **Anything else is {@link NO_TAXONOMY}.** A flat component folder, a
51
+ * missing component folder, an unreadable tree, a non-existent root: all
52
+ * record `none`, and the authoring step then behaves exactly as it does
53
+ * without this module.
54
+ *
55
+ * Detection never throws. A malformed doc, a permission error, or a missing
56
+ * directory degrades to `none` — the same recoverable-error contract as
57
+ * `stack/detect.ts`, and for the same reason: a wrong label is recoverable
58
+ * (the user can say so), a crash mid-dispatch is not. Degrading to `none` is
59
+ * also the *safe* direction here, because `none` is precisely the value that
60
+ * leaves the project's own structures untouched.
61
+ */
62
+ import * as fs from 'node:fs';
63
+ import * as path from 'node:path';
64
+
65
+ /** Recorded value for a project that evidences no granularity taxonomy. */
66
+ export const NO_TAXONOMY = 'none';
67
+
68
+ /**
69
+ * Granularity words used to RECOGNISE a tier name — never to prescribe one.
70
+ *
71
+ * Drawn from several vocabularies rather than one, and read at exactly one
72
+ * place: {@link _infer_tiers}, which calls `.has()` and nothing else. It
73
+ * carries no level, no cap and no placement rule, and it is not consulted when
74
+ * a component is placed. A project is free to use names that appear nowhere
75
+ * here — it declares them, and the declaration wins.
76
+ *
77
+ * **Held ALPHABETICALLY, and that is load-bearing rather than tidy.** A
78
+ * JavaScript `Set` iterates in INSERTION order, so "it is a Set" proves
79
+ * nothing about ordering — an earlier draft of this file claimed exactly that
80
+ * while the first five entries were one vocabulary's five levels in their own
81
+ * sequence. Alphabetical order cannot be a level sequence, it is checkable,
82
+ * and a test asserts it. Iteration order is still never read for a decision.
83
+ */
84
+ const _LEXICON_WORDS: ReadonlyArray<string> = [
85
+ 'atom',
86
+ 'block',
87
+ 'cell',
88
+ 'composite',
89
+ 'element',
90
+ 'layout',
91
+ 'molecule',
92
+ 'organism',
93
+ 'page',
94
+ 'part',
95
+ 'particle',
96
+ 'pattern',
97
+ 'primitive',
98
+ 'template',
99
+ 'widget',
100
+ ];
101
+
102
+ export const GRANULARITY_LEXICON: ReadonlySet<string> = new Set(_LEXICON_WORDS);
103
+
104
+ /**
105
+ * Sibling folders that hold no components of their own tier.
106
+ *
107
+ * Excluded from the majority DENOMINATOR, never from the matches. A project
108
+ * that keeps `hooks/`, `utils/` and `types/` next to its tiers has not stopped
109
+ * using its tiers, but {@link COMPONENT_SUFFIXES} includes `.ts`, so those
110
+ * folders read as buckets and drag the share down: `atoms / molecules /
111
+ * organisms / hooks / utils / types` measured 3 of 6 and recorded `none`.
112
+ * Three ordinary sibling folders were enough to suppress a real taxonomy.
113
+ */
114
+ export const SUPPORT_BUCKETS: ReadonlySet<string> = new Set([
115
+ 'constant',
116
+ 'context',
117
+ 'helper',
118
+ 'hook',
119
+ 'icon',
120
+ 'lib',
121
+ 'provider',
122
+ 'store',
123
+ 'style',
124
+ 'test',
125
+ 'type',
126
+ 'util',
127
+ ]);
128
+
129
+ /** Fewest tiers that can evidence a taxonomy. One name is a coincidence. */
130
+ export const MIN_TIERS = 2;
131
+
132
+ /**
133
+ * Share of a component root's buckets that must be tier-shaped to infer one.
134
+ *
135
+ * **Above two thirds, which is a measurement rather than a preference.** It
136
+ * sat at 0.6, and at 0.6 the ordinary kind-split `components/{layout, pages,
137
+ * forms}` measured 2 of 3 = 0.67 and was recorded as the taxonomy
138
+ * `layout/pages` — every `forms` component then a conformance gap, on a
139
+ * project that never chose a taxonomy at all. That is this roadmap's
140
+ * highest-ranked risk happening, so the floor is set above the 2-of-3 ratio
141
+ * that produced it. `layout`, `page`, `block`, `element`, `part` and `cell`
142
+ * are all ordinary folder names as well as granularity words, which is why a
143
+ * bare share is not enough on its own and {@link SUPPORT_BUCKETS} exists.
144
+ *
145
+ * It is NOT set higher: `atoms / molecules / organisms` beside one support
146
+ * folder is 3 of 4 = 0.75 and must still be found.
147
+ */
148
+ export const TIER_MAJORITY = 0.7;
149
+
150
+ /** Directory names searched, in order, for the project's component root. */
151
+ export const COMPONENT_ROOT_CANDIDATES: ReadonlyArray<string> = [
152
+ 'src/components',
153
+ 'app/components',
154
+ 'src/lib/components',
155
+ 'src/app/components',
156
+ 'resources/js/components',
157
+ 'resources/views/components',
158
+ 'app/javascript/components',
159
+ 'components',
160
+ ];
161
+
162
+ /**
163
+ * Docs read for a declared convention, in order; the first QUALIFYING one wins.
164
+ *
165
+ * A doc with no `## Component taxonomy` section, or one naming fewer than
166
+ * {@link MIN_TIERS} tiers that exist on disk, falls through to the next.
167
+ */
168
+ export const DECLARATION_DOCS: ReadonlyArray<string> = [
169
+ 'DESIGN.md',
170
+ 'docs/DESIGN.md',
171
+ ];
172
+
173
+ /** Heading that opens a declared-taxonomy section, lower-cased. */
174
+ const DECLARATION_HEADING = 'component taxonomy';
175
+
176
+ /** File suffixes that make a file look like a component. */
177
+ const COMPONENT_SUFFIXES: ReadonlyArray<string> = [
178
+ '.tsx',
179
+ '.jsx',
180
+ '.vue',
181
+ '.svelte',
182
+ '.blade.php',
183
+ '.ts',
184
+ '.js',
185
+ ];
186
+
187
+ /** Where the recorded taxonomy came from. */
188
+ export type TaxonomySource = 'declared' | 'inferred' | 'none';
189
+
190
+ /** What {@link detect_component_taxonomy} returns. */
191
+ export interface TaxonomyResult {
192
+ /**
193
+ * The single value the audit records in `state.ui_audit.component_taxonomy`
194
+ * — the project's own tiers joined with `/`, or {@link NO_TAXONOMY}.
195
+ */
196
+ taxonomy: string;
197
+ /**
198
+ * The same tiers as a list.
199
+ *
200
+ * `declared` carries the DOCUMENT's order, which is the project's own.
201
+ * `inferred` carries alphabetical order, because bucket names arrive
202
+ * sorted and no filesystem fact states a level sequence. It reads as one
203
+ * for `atoms/molecules/organisms` by coincidence and would not for
204
+ * `pages/templates/organisms`. Nothing downstream reads the order.
205
+ */
206
+ tiers: string[];
207
+ /** Component root relative to the project root, or `null` when none. */
208
+ component_root: string | null;
209
+ /** Which rule produced the answer. */
210
+ source: TaxonomySource;
211
+ /** Human-readable trace of what was read, for the audit's output. */
212
+ evidence: string[];
213
+ }
214
+
215
+ function _none(evidence: string[], component_root: string | null = null): TaxonomyResult {
216
+ return {
217
+ taxonomy: NO_TAXONOMY,
218
+ tiers: [],
219
+ component_root,
220
+ source: 'none',
221
+ evidence,
222
+ };
223
+ }
224
+
225
+ /**
226
+ * Detect the component-granularity taxonomy a project evidences.
227
+ *
228
+ * @param project_root Absolute or relative path to the project root.
229
+ * @returns A {@link TaxonomyResult}; never throws.
230
+ */
231
+ export function detect_component_taxonomy(project_root: string): TaxonomyResult {
232
+ const evidence: string[] = [];
233
+ let component_root: string | null;
234
+ try {
235
+ component_root = _find_component_root(project_root);
236
+ } catch {
237
+ return _none(['component root unreadable']);
238
+ }
239
+ if (component_root === null) {
240
+ evidence.push(
241
+ `no component root under ${_display(project_root)} ` +
242
+ `(searched ${COMPONENT_ROOT_CANDIDATES.length} conventional paths)`,
243
+ );
244
+ return _none(evidence);
245
+ }
246
+ evidence.push(`component root: ${component_root}`);
247
+
248
+ let buckets: string[];
249
+ try {
250
+ buckets = _component_buckets(path.join(project_root, component_root));
251
+ } catch {
252
+ return _none([...evidence, 'component root unreadable'], component_root);
253
+ }
254
+
255
+ // `_declared_tiers` already applies the MIN_TIERS floor and returns `[]`
256
+ // below it, so re-testing the floor here would be a second copy of one
257
+ // invariant — the shape this branch removed from `apply.ts` for masking a
258
+ // regression in its own sibling. One owner; this asks only "was there one".
259
+ const declared = _declared_tiers(project_root, buckets, evidence);
260
+ if (declared.length > 0) {
261
+ return {
262
+ taxonomy: declared.join('/'),
263
+ tiers: declared,
264
+ component_root,
265
+ source: 'declared',
266
+ evidence,
267
+ };
268
+ }
269
+
270
+ const inferred = _infer_tiers(buckets, evidence);
271
+ if (inferred.length === 0) {
272
+ return _none(evidence, component_root);
273
+ }
274
+ return {
275
+ taxonomy: inferred.join('/'),
276
+ tiers: inferred,
277
+ component_root,
278
+ source: 'inferred',
279
+ evidence,
280
+ };
281
+ }
282
+
283
+ /** First conventional component directory that exists, or `null`. */
284
+ function _find_component_root(project_root: string): string | null {
285
+ for (const candidate of COMPONENT_ROOT_CANDIDATES) {
286
+ const full = path.join(project_root, ...candidate.split('/'));
287
+ let st: fs.Stats;
288
+ try {
289
+ st = fs.statSync(full);
290
+ } catch {
291
+ continue;
292
+ }
293
+ if (st.isDirectory()) {
294
+ return candidate;
295
+ }
296
+ }
297
+ return null;
298
+ }
299
+
300
+ /**
301
+ * Immediate subdirectories of the component root that actually hold components.
302
+ *
303
+ * A directory with no component-shaped file anywhere beneath it is not a
304
+ * bucket — it is a stray folder, and counting it would drag the majority test
305
+ * around for reasons that have nothing to do with the project's convention.
306
+ */
307
+ function _component_buckets(root: string): string[] {
308
+ const out: string[] = [];
309
+ for (const entry of fs.readdirSync(root, { withFileTypes: true })) {
310
+ if (!entry.isDirectory()) continue;
311
+ if (entry.name.startsWith('.')) continue;
312
+ if (_holds_component(path.join(root, entry.name), 0)) {
313
+ out.push(entry.name);
314
+ }
315
+ }
316
+ out.sort();
317
+ return out;
318
+ }
319
+
320
+ /** True when a component-shaped file sits at or under `dir` (depth-capped). */
321
+ function _holds_component(dir: string, depth: number): boolean {
322
+ if (depth > 3) return false;
323
+ let entries: fs.Dirent[];
324
+ try {
325
+ entries = fs.readdirSync(dir, { withFileTypes: true });
326
+ } catch {
327
+ return false;
328
+ }
329
+ for (const entry of entries) {
330
+ if (entry.isFile()) {
331
+ if (COMPONENT_SUFFIXES.some((s) => entry.name.endsWith(s))) {
332
+ return true;
333
+ }
334
+ } else if (entry.isDirectory() && !entry.name.startsWith('.')) {
335
+ if (_holds_component(path.join(dir, entry.name), depth + 1)) {
336
+ return true;
337
+ }
338
+ }
339
+ }
340
+ return false;
341
+ }
342
+
343
+ /**
344
+ * Tiers declared in the project's own docs, grounded against the tree.
345
+ *
346
+ * Reads the `## Component taxonomy` section of the first
347
+ * {@link DECLARATION_DOCS} hit and keeps the backticked names that exist as
348
+ * buckets. Order is the document's, because that is the project's own order.
349
+ */
350
+ function _declared_tiers(
351
+ project_root: string,
352
+ buckets: string[],
353
+ evidence: string[],
354
+ ): string[] {
355
+ const known = new Set(buckets);
356
+ for (const doc of DECLARATION_DOCS) {
357
+ let body: string;
358
+ try {
359
+ body = fs.readFileSync(path.join(project_root, ...doc.split('/')), 'utf8');
360
+ } catch {
361
+ continue;
362
+ }
363
+ const section = _taxonomy_section(body);
364
+ if (section === null) {
365
+ evidence.push(`${doc}: no \`## Component taxonomy\` section`);
366
+ continue;
367
+ }
368
+ const named: string[] = [];
369
+ for (const line of section.split(/\r?\n/u)) {
370
+ // Only a list item declares a tier. A backticked name inside a
371
+ // prose sentence is a mention — the `legacy` fixture says in prose
372
+ // that it is NOT a tier, and a parser that scanned the whole
373
+ // section would enrol it on the strength of the backticks alone.
374
+ // `**` / `__` may lead the name: `- **\`alpha\`** — ...` is a
375
+ // completely ordinary way to write this list, and a regex that
376
+ // demanded the backtick lead dropped it silently.
377
+ const item = /^\s*(?:[-*+]|\d+[.)])\s+(?:\*\*|__)?`([^`\n]+)`/u.exec(line);
378
+ if (item === null) continue;
379
+ const name = (item[1] ?? '').trim();
380
+ // Grounded against the tree: a declaration cannot conjure a tier
381
+ // the project has not created.
382
+ if (name !== '' && known.has(name) && !named.includes(name)) {
383
+ named.push(name);
384
+ }
385
+ }
386
+ if (named.length >= MIN_TIERS) {
387
+ evidence.push(`${doc}: declares ${named.length} tiers, all present on disk`);
388
+ return named;
389
+ }
390
+ evidence.push(
391
+ `${doc}: \`## Component taxonomy\` names ${named.length} tier(s) ` +
392
+ `present on disk — below the ${MIN_TIERS}-tier floor, so not a declaration`,
393
+ );
394
+ }
395
+ return [];
396
+ }
397
+
398
+ /** Body of the `## Component taxonomy` section, or `null` when absent. */
399
+ function _taxonomy_section(body: string): string | null {
400
+ const lines = body.split(/\r?\n/u);
401
+ let start = -1;
402
+ let level = 0;
403
+ for (let i = 0; i < lines.length; i += 1) {
404
+ const m = /^(#{1,6})\s+(.*)$/u.exec(lines[i] as string);
405
+ if (m === null) continue;
406
+ const heading = (m[2] ?? '').trim().toLowerCase().replace(/[:.]+$/u, '');
407
+ if (start === -1) {
408
+ if (heading === DECLARATION_HEADING) {
409
+ start = i + 1;
410
+ level = (m[1] as string).length;
411
+ }
412
+ continue;
413
+ }
414
+ if ((m[1] as string).length <= level) {
415
+ return lines.slice(start, i).join('\n');
416
+ }
417
+ }
418
+ return start === -1 ? null : lines.slice(start).join('\n');
419
+ }
420
+
421
+ /**
422
+ * Tiers inferred from bucket names, or `[]` when the evidence is too thin.
423
+ *
424
+ * The only place {@link GRANULARITY_LEXICON} is consulted.
425
+ */
426
+ function _infer_tiers(buckets: string[], evidence: string[]): string[] {
427
+ if (buckets.length === 0) {
428
+ evidence.push('component root has no sub-directories — flat layout');
429
+ return [];
430
+ }
431
+ const matched = buckets.filter((b) => GRANULARITY_LEXICON.has(_singular(b)));
432
+ const counted = buckets.filter((b) => !SUPPORT_BUCKETS.has(_singular(b)));
433
+ if (counted.length === 0) {
434
+ evidence.push('every bucket is a support folder — no tiers to weigh');
435
+ return [];
436
+ }
437
+ const share = matched.length / counted.length;
438
+ if (matched.length < MIN_TIERS || share < TIER_MAJORITY) {
439
+ evidence.push(
440
+ `${matched.length} of ${counted.length} weighed buckets carry granularity ` +
441
+ `names (need >= ${MIN_TIERS} and >= ${Math.round(TIER_MAJORITY * 100)}%; ` +
442
+ `${buckets.length - counted.length} support folder(s) not weighed) — ` +
443
+ 'reads as domain folders, not a taxonomy',
444
+ );
445
+ return [];
446
+ }
447
+ evidence.push(
448
+ `${matched.length} of ${counted.length} weighed buckets carry granularity ` +
449
+ `names (${buckets.length - counted.length} support folder(s) not weighed) — ` +
450
+ 'inferred from layout',
451
+ );
452
+ return matched;
453
+ }
454
+
455
+ /**
456
+ * Lower-case a bucket name and drop a trailing plural `s`.
457
+ *
458
+ * Deliberately naive: it exists so `atoms` matches `atom`, not to be a
459
+ * morphology engine. A name it mis-normalises simply fails to match, which
460
+ * lands on `none` — the safe direction.
461
+ */
462
+ function _singular(name: string): string {
463
+ const lower = name.toLowerCase();
464
+ return lower.endsWith('s') && lower.length > 2 ? lower.slice(0, -1) : lower;
465
+ }
466
+
467
+ /** Trim a path for an evidence line so the trace stays readable. */
468
+ function _display(p: string): string {
469
+ const base = path.basename(p);
470
+ return base === '' ? p : base;
471
+ }
472
+
473
+ /**
474
+ * CLI entry: print the detection result for a project root as JSON.
475
+ *
476
+ * `existing-ui-audit` § 1b tells the agent to RUN this file, so it has to be
477
+ * runnable. Without this block `npx tsx .../detect.ts` exited 0 and printed
478
+ * nothing, and an agent following the instruction literally got silence and
479
+ * then had to invent the two state keys the audit is supposed to record.
480
+ *
481
+ * ```bash
482
+ * npx tsx scripts/work_engine/taxonomy/detect.ts [--root <dir>]
483
+ * ```
484
+ *
485
+ * Defaults to the process CWD. Exits 0 whichever answer it reaches: `none` is
486
+ * a real answer here, not a failure, and an exit code that said otherwise
487
+ * would push a caller toward treating a no-taxonomy project as an error.
488
+ */
489
+ export function main(argv: ReadonlyArray<string>): number {
490
+ const at = argv.indexOf('--root');
491
+ const root = at !== -1 && at + 1 < argv.length ? (argv[at + 1] as string) : process.cwd();
492
+ process.stdout.write(`${JSON.stringify(detect_component_taxonomy(root), null, 2)}\n`);
493
+ return 0;
494
+ }
495
+
496
+ // Only when this module is the process entry, never on import. tsx sets
497
+ // `import.meta.url` to the entry file URL.
498
+ if (import.meta.url === `file://${process.argv[1]}`) {
499
+ process.exitCode = main(process.argv.slice(2));
500
+ }