@event4u/agent-config 16.4.0 → 17.0.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 (1052) hide show
  1. package/.augment-plugin/marketplace.json +3 -3
  2. package/.augment-plugin/plugin.json +1 -1
  3. package/.claude-plugin/marketplace.json +2 -2
  4. package/.claude-plugin/skills/install-agent-config/SKILL.md +4 -4
  5. package/CHANGELOG.md +483 -669
  6. package/CONTRIBUTING.md +47 -0
  7. package/README.md +25 -25
  8. package/dist/agent-src/commands/{design.md → agent-design.md} +11 -7
  9. package/dist/agent-src/commands/agents/init.md +66 -3
  10. package/dist/agent-src/commands/agents/user/init.md +3 -0
  11. package/dist/agent-src/commands/{optimize/deep.md → analyze/and-optimize.md} +72 -33
  12. package/dist/agent-src/commands/analyze/inbox.md +168 -37
  13. package/dist/agent-src/commands/analyze/pr.md +144 -0
  14. package/dist/agent-src/commands/analyze/{repo.md → reference.md} +85 -14
  15. package/dist/agent-src/commands/analyze/roadmap-repos.md +19 -10
  16. package/dist/agent-src/commands/analyze.md +28 -12
  17. package/dist/agent-src/commands/audit.md +115 -0
  18. package/dist/agent-src/commands/brand/review.md +13 -2
  19. package/dist/agent-src/commands/brand/tokens.md +3 -3
  20. package/dist/agent-src/commands/brand/voice.md +8 -5
  21. package/dist/agent-src/commands/brand.md +2 -2
  22. package/dist/agent-src/commands/challenge-me/closure.md +25 -0
  23. package/dist/agent-src/commands/design-system/import.md +8 -3
  24. package/dist/agent-src/commands/design-system/sync.md +98 -0
  25. package/dist/agent-src/commands/design-system.md +6 -1
  26. package/dist/agent-src/commands/evals/analyze.json +44 -0
  27. package/dist/agent-src/commands/evals/audit.json +12 -0
  28. package/dist/agent-src/commands/evals/retro.json +12 -0
  29. package/dist/agent-src/commands/fix/ci.md +10 -8
  30. package/dist/agent-src/commands/ghostwriter/write.md +22 -0
  31. package/dist/agent-src/commands/memory/learn-low-impact.md +70 -83
  32. package/dist/agent-src/commands/memory/propose.md +12 -0
  33. package/dist/agent-src/commands/memory.md +1 -1
  34. package/dist/agent-src/commands/optimize.md +4 -3
  35. package/dist/agent-src/commands/post-as/me.md +18 -0
  36. package/dist/agent-src/commands/pr/create.md +17 -342
  37. package/dist/agent-src/commands/pr/merge.md +129 -368
  38. package/dist/agent-src/commands/retro.md +121 -0
  39. package/dist/agent-src/commands/review/routing.md +1 -1
  40. package/dist/agent-src/commands/roadmap/next.md +4 -1
  41. package/dist/agent-src/commands/roadmap/process-full.md +46 -333
  42. package/dist/agent-src/commands/roadmap/triage-parked.md +6 -2
  43. package/dist/agent-src/commands/team-knowledge/consolidate.md +2 -2
  44. package/dist/agent-src/commands/team-knowledge.md +1 -1
  45. package/dist/agent-src/commands/threat-model.md +1 -1
  46. package/dist/agent-src/commands/upstream-contribute.md +91 -101
  47. package/dist/agent-src/commands/worktree/cleanup.md +18 -11
  48. package/dist/agent-src/contexts/contracts/consumer-agents-md-guide.md +54 -0
  49. package/dist/agent-src/contexts/execution/analysis-loop.md +137 -0
  50. package/dist/agent-src/contexts/execution/auto-dispatch-classification.md +3 -3
  51. package/dist/agent-src/contexts/execution/decision-closure-execution.md +1 -1
  52. package/dist/agent-src/contexts/execution/host-capability-manifest.md +6 -0
  53. package/dist/agent-src/contexts/execution/orchestration-telemetry.md +11 -4
  54. package/dist/agent-src/contexts/execution/pr-create-branches-content.md +250 -0
  55. package/dist/agent-src/contexts/execution/pr-create-branches-freshness.md +150 -0
  56. package/dist/agent-src/contexts/execution/pr-merge-queue.md +287 -0
  57. package/dist/agent-src/contexts/execution/pr-merge-sync.md +276 -0
  58. package/dist/agent-src/contexts/execution/pr-merge-trust-boundary.md +69 -0
  59. package/dist/agent-src/contexts/execution/process-full-all.md +233 -0
  60. package/dist/agent-src/contexts/execution/process-full-delivery.md +231 -0
  61. package/dist/agent-src/contexts/execution/roadmap-execution-contract-branches.md +42 -0
  62. package/dist/agent-src/contexts/execution/roadmap-execution-contract.md +1 -35
  63. package/dist/agent-src/contexts/execution/roadmap-process-loop-close.md +235 -0
  64. package/dist/agent-src/contexts/execution/roadmap-process-loop-prescan.md +216 -0
  65. package/dist/agent-src/contexts/execution/roadmap-process-loop-run.md +253 -0
  66. package/dist/agent-src/contexts/execution/roadmap-process-loop-set.md +212 -0
  67. package/dist/agent-src/contexts/execution/roadmap-process-loop.md +25 -864
  68. package/dist/agent-src/contexts/execution/subagent-spawn-contract.md +14 -2
  69. package/dist/agent-src/contexts/execution/subagent-steering.md +65 -8
  70. package/dist/agent-src/contexts/model-recommendations.md +5 -5
  71. package/dist/agent-src/contexts/subagent-configuration.md +1 -1
  72. package/dist/agent-src/guidelines/agent-infra/api-cost-levers.md +4 -3
  73. package/dist/agent-src/guidelines/agent-infra/model-recommendation.md +11 -5
  74. package/dist/agent-src/guidelines/agent-infra/rule-body-migration-inventory.md +1 -1
  75. package/dist/agent-src/guidelines/agent-infra/self-improvement-pipeline.md +47 -24
  76. package/dist/agent-src/guidelines/design-antipatterns.md +6 -1
  77. package/dist/agent-src/guidelines/design-asset-discipline.md +17 -0
  78. package/dist/agent-src/guidelines/design-fidelity-routing.md +1 -0
  79. package/dist/agent-src/guidelines/php/api-design.md +10 -1
  80. package/dist/agent-src/rules/brand-source-of-truth.md +7 -6
  81. package/dist/agent-src/rules/code-provenance.md +1 -1
  82. package/dist/agent-src/rules/context-hygiene.md +0 -1
  83. package/dist/agent-src/rules/design-fidelity.md +12 -13
  84. package/dist/agent-src/rules/external-reference-deep-dive.md +2 -2
  85. package/dist/agent-src/rules/fix-what-you-see.md +1 -1
  86. package/dist/agent-src/rules/low-impact-corpus-privacy-floor.md +14 -15
  87. package/dist/agent-src/rules/missing-skill-recovery.md +12 -11
  88. package/dist/agent-src/rules/neighbour-precedence.md +1 -1
  89. package/dist/agent-src/rules/recurring-criticism.md +1 -1
  90. package/dist/agent-src/rules/roadmap-ci-steps-policy.md +0 -2
  91. package/dist/agent-src/rules/self-repair-loop.md +2 -2
  92. package/dist/agent-src/rules/token-budget-discipline.md +2 -2
  93. package/dist/agent-src/scripts/dashboard_mode.ts +4 -1
  94. package/dist/agent-src/scripts/roadmap_age.ts +175 -0
  95. package/dist/agent-src/scripts/update_roadmap_progress.ts +10 -2
  96. package/dist/agent-src/skills/accessibility-auditor/SKILL.md +3 -3
  97. package/dist/agent-src/skills/accessibility-auditor/data/aria-patterns.csv +2 -0
  98. package/dist/agent-src/skills/accessibility-auditor/data/manifest.json +3 -3
  99. package/dist/agent-src/skills/agent-security-review/SKILL.md +2 -2
  100. package/dist/agent-src/skills/agents-md-thin-root/SKILL.md +12 -4
  101. package/dist/agent-src/skills/ai-code-blindspots/SKILL.md +7 -0
  102. package/dist/agent-src/skills/ai-council/references/advanced-modes.md +11 -1
  103. package/dist/agent-src/skills/ai-council/references/cost-and-redaction.md +7 -0
  104. package/dist/agent-src/skills/ai-council/references/execution-modes.md +7 -0
  105. package/dist/agent-src/skills/ai-council/references/output-and-synthesis.md +19 -0
  106. package/dist/agent-src/skills/ai-council/references/procedure.md +7 -0
  107. package/dist/agent-src/skills/api-design/SKILL.md +2 -2
  108. package/dist/agent-src/skills/api-endpoint/SKILL.md +2 -2
  109. package/dist/agent-src/skills/authz-review/SKILL.md +5 -3
  110. package/dist/agent-src/skills/blade-ui/SKILL.md +2 -2
  111. package/dist/agent-src/skills/brand/SKILL.md +7 -3
  112. package/dist/agent-src/skills/brand-asset-generation/SKILL.md +13 -6
  113. package/dist/agent-src/skills/brand-audit/SKILL.md +2 -1
  114. package/dist/agent-src/skills/brand-identity/SKILL.md +5 -4
  115. package/dist/agent-src/skills/brand-strategy/SKILL.md +5 -4
  116. package/dist/agent-src/skills/brand-to-tokens/SKILL.md +18 -39
  117. package/dist/agent-src/skills/brand-to-tokens/templates/marp-brand-deck.md.example +2 -2
  118. package/dist/agent-src/skills/brand-to-tokens/templates/reveal-brand-deck.yaml +2 -2
  119. package/dist/agent-src/skills/canvas-design/SKILL.md +16 -4
  120. package/dist/agent-src/skills/competitive-positioning/SKILL.md +2 -2
  121. package/dist/agent-src/skills/composer-packages/SKILL.md +18 -0
  122. package/dist/agent-src/skills/condense-memory/SKILL.md +1 -1
  123. package/dist/agent-src/skills/content-funnel-design/SKILL.md +8 -1
  124. package/dist/agent-src/skills/context-authoring/SKILL.md +1 -1
  125. package/dist/agent-src/skills/context-document/SKILL.md +2 -2
  126. package/dist/agent-src/skills/corpus-grounding/SKILL.md +6 -6
  127. package/dist/agent-src/skills/corpus-grounding/scripts/decision_engine.ts +13 -11
  128. package/dist/agent-src/skills/corpus-grounding/scripts/ground.mjs +1706 -0
  129. package/dist/agent-src/skills/corpus-grounding/scripts/schema_validator.ts +7 -1
  130. package/dist/agent-src/skills/dashboard-design/SKILL.md +1 -1
  131. package/dist/agent-src/skills/data-flow-mapper/SKILL.md +5 -2
  132. package/dist/agent-src/skills/database/SKILL.md +5 -4
  133. package/dist/agent-src/skills/decision-record/references/weighted-matrix.md +12 -0
  134. package/dist/agent-src/skills/deep-reading-analyst/SKILL.md +1 -1
  135. package/dist/agent-src/skills/dependency-upgrade/SKILL.md +61 -11
  136. package/dist/agent-src/skills/description-assist/SKILL.md +1 -1
  137. package/dist/agent-src/skills/design-intelligence/SKILL.md +1 -1
  138. package/dist/agent-src/skills/design-intelligence/data/landing.csv +1 -1
  139. package/dist/agent-src/skills/design-intelligence/references/context-and-registers.md +10 -2
  140. package/dist/agent-src/skills/design-intelligence/references/design-languages.md +1 -1
  141. package/dist/agent-src/skills/design-intelligence/references/design-rules-checklist.md +4 -3
  142. package/dist/agent-src/skills/design-intelligence/references/integration-mapping.md +25 -22
  143. package/dist/agent-src/skills/{wireframe → design-review}/references/story-beat-ledger.schema.json +1 -1
  144. package/dist/agent-src/skills/design-review/references/verification-automation.md +11 -3
  145. package/dist/agent-src/skills/design-system-capture/SKILL.md +15 -10
  146. package/dist/agent-src/skills/design-system-capture/references/design-system-json.md +20 -5
  147. package/dist/agent-src/skills/design-system-capture/references/import-procedure.md +2 -2
  148. package/dist/agent-src/skills/design-tokens/SKILL.md +23 -23
  149. package/dist/agent-src/skills/design-tokens/scripts/tokens.mjs +1037 -0
  150. package/dist/agent-src/skills/design-tokens/scripts/tokens.ts +336 -13
  151. package/dist/agent-src/skills/design-tokens/templates/design-tokens-starter.json +52 -53
  152. package/dist/agent-src/skills/docker/SKILL.md +1 -1
  153. package/dist/agent-src/skills/estimate-ticket/SKILL.md +1 -1
  154. package/dist/agent-src/skills/existing-ui-audit/SKILL.md +14 -276
  155. package/dist/agent-src/skills/existing-ui-audit/procedure.yaml +44 -0
  156. package/dist/agent-src/skills/existing-ui-audit/references/anti-slop-cross-reference.md +1 -1
  157. package/dist/agent-src/skills/existing-ui-audit/references/steps/00-inspect-stack-and-input.md +4 -0
  158. package/dist/agent-src/skills/existing-ui-audit/references/steps/01-enumerate-components.md +26 -0
  159. package/dist/agent-src/skills/existing-ui-audit/references/steps/01b-detect-component-taxonomy.md +7 -0
  160. package/dist/agent-src/skills/existing-ui-audit/references/steps/02-identify-design-system.md +12 -0
  161. package/dist/agent-src/skills/existing-ui-audit/references/steps/03-detect-design-tokens.md +35 -0
  162. package/dist/agent-src/skills/existing-ui-audit/references/steps/04-detect-shadcn-inventory.md +13 -0
  163. package/dist/agent-src/skills/existing-ui-audit/references/steps/04b-live-storybook-read.md +26 -0
  164. package/dist/agent-src/skills/existing-ui-audit/references/steps/05-list-reusable-patterns.md +14 -0
  165. package/dist/agent-src/skills/existing-ui-audit/references/steps/06-match-candidates.md +52 -0
  166. package/dist/agent-src/skills/existing-ui-audit/references/steps/07-greenfield-branch.md +29 -0
  167. package/dist/agent-src/skills/existing-ui-audit/references/steps/08-a11y-baseline.md +40 -0
  168. package/dist/agent-src/skills/existing-ui-audit/references/steps/09-validate-and-write.md +5 -0
  169. package/dist/agent-src/skills/experiment-loop/SKILL.md +1 -1
  170. package/dist/agent-src/skills/fe-design/references/design-patterns.md +65 -4
  171. package/dist/agent-src/skills/fe-design/references/design-read-and-memory.md +1 -1
  172. package/dist/agent-src/skills/fe-design/references/source-led-port.md +1 -1
  173. package/dist/agent-src/skills/finishing-a-development-branch/SKILL.md +6 -6
  174. package/dist/agent-src/skills/flux/SKILL.md +1 -1
  175. package/dist/agent-src/skills/form-handler/SKILL.md +15 -2
  176. package/dist/agent-src/skills/frontend-render-security/SKILL.md +9 -0
  177. package/dist/agent-src/skills/git-workflow/SKILL.md +2 -2
  178. package/dist/agent-src/skills/git-workflow/references/branch-update.md +7 -0
  179. package/dist/agent-src/skills/git-workflow/references/commit-subject.md +12 -0
  180. package/dist/agent-src/skills/git-workflow/references/push-closes-its-loop.md +10 -0
  181. package/dist/agent-src/skills/gtm-launch/SKILL.md +8 -1
  182. package/dist/agent-src/skills/html-deck/SKILL.md +7 -2
  183. package/dist/agent-src/skills/humanizer/SKILL.md +16 -3
  184. package/dist/agent-src/skills/humanizer/references/anti-aiisms.md +13 -1
  185. package/dist/agent-src/skills/humanizer/references/fixtures.md +8 -0
  186. package/dist/agent-src/skills/iconography/SKILL.md +4 -2
  187. package/dist/agent-src/skills/image-provider-routing/SKILL.md +16 -5
  188. package/dist/agent-src/skills/js-library-packaging/SKILL.md +5 -4
  189. package/dist/agent-src/skills/js-library-packaging/scripts/check_package_surface.mjs +197 -0
  190. package/dist/agent-src/skills/judge-artifact-completeness/SKILL.md +1 -1
  191. package/dist/agent-src/skills/judge-security-auditor/SKILL.md +2 -2
  192. package/dist/agent-src/skills/launch-readiness/SKILL.md +18 -22
  193. package/dist/agent-src/skills/learning-to-rule-or-skill/SKILL.md +5 -5
  194. package/dist/agent-src/skills/livewire/SKILL.md +1 -1
  195. package/dist/agent-src/skills/llm-provider-knowledge/SKILL.md +18 -0
  196. package/dist/agent-src/skills/logo-generation/SKILL.md +21 -11
  197. package/dist/agent-src/skills/market-entry-analysis/SKILL.md +6 -1
  198. package/dist/agent-src/skills/markitdown/SKILL.md +1 -1
  199. package/dist/agent-src/skills/memory-consolidation/SKILL.md +2 -2
  200. package/dist/agent-src/skills/messaging-architecture/SKILL.md +8 -1
  201. package/dist/agent-src/skills/motion-choreographer/SKILL.md +2 -2
  202. package/dist/agent-src/skills/playwright-testing/SKILL.md +2 -0
  203. package/dist/agent-src/skills/positioning-strategy/SKILL.md +10 -2
  204. package/dist/agent-src/skills/prediction-pool-optimizer/references/ev-fixtures.md +13 -0
  205. package/dist/agent-src/skills/prediction-pool-optimizer/references/odds-and-bonus.md +7 -0
  206. package/dist/agent-src/skills/project-analysis-core/SKILL.md +1 -1
  207. package/dist/agent-src/skills/project-analysis-hypothesis-driven/SKILL.md +1 -1
  208. package/dist/agent-src/skills/project-analyzer/SKILL.md +14 -75
  209. package/dist/agent-src/skills/project-analyzer/procedure.yaml +39 -0
  210. package/dist/agent-src/skills/project-analyzer/references/steps/01-project-overview.md +9 -0
  211. package/dist/agent-src/skills/project-analyzer/references/steps/02-architecture.md +7 -0
  212. package/dist/agent-src/skills/project-analyzer/references/steps/03-data-layer.md +6 -0
  213. package/dist/agent-src/skills/project-analyzer/references/steps/04-business-domains.md +7 -0
  214. package/dist/agent-src/skills/project-analyzer/references/steps/05-api-surface.md +6 -0
  215. package/dist/agent-src/skills/project-analyzer/references/steps/06-service-map.md +6 -0
  216. package/dist/agent-src/skills/project-analyzer/references/steps/07-module-inventory.md +6 -0
  217. package/dist/agent-src/skills/project-analyzer/references/steps/08-infrastructure-and-testing.md +5 -0
  218. package/dist/agent-src/skills/project-analyzer/references/steps/09-agent-docs-audit.md +6 -0
  219. package/dist/agent-src/skills/project-analyzer/references/steps/10-gap-analysis.md +6 -0
  220. package/dist/agent-src/skills/quality-tools/references/js-ts-tools.md +10 -0
  221. package/dist/agent-src/skills/quality-tools/references/php-tools.md +13 -0
  222. package/dist/agent-src/skills/quality-tools/references/python-tools.md +11 -0
  223. package/dist/agent-src/skills/react-native-setup/SKILL.md +7 -2
  224. package/dist/agent-src/skills/react-shadcn-ui/SKILL.md +5 -1
  225. package/dist/agent-src/skills/react-shadcn-ui/references/registry.md +13 -0
  226. package/dist/agent-src/skills/react-shadcn-ui/scripts/shadcn_add.mjs +315 -0
  227. package/dist/agent-src/skills/react-shadcn-ui/scripts/shadcn_add.ts +6 -6
  228. package/dist/agent-src/skills/receiving-code-review/SKILL.md +1 -1
  229. package/dist/agent-src/skills/refine-ticket/SKILL.md +2 -2
  230. package/dist/agent-src/skills/release-comms/SKILL.md +2 -1
  231. package/dist/agent-src/skills/repomix-packer/SKILL.md +2 -2
  232. package/dist/agent-src/skills/requesting-code-review/SKILL.md +3 -3
  233. package/dist/agent-src/skills/roadmap-management/references/archival.md +3 -3
  234. package/dist/agent-src/skills/roadmap-management/references/authoring.md +9 -2
  235. package/dist/agent-src/skills/roadmap-management/references/dashboard.md +3 -3
  236. package/dist/agent-src/skills/roadmap-management/references/lifecycle.md +3 -3
  237. package/dist/agent-src/skills/roadmap-writing/SKILL.md +4 -4
  238. package/dist/agent-src/skills/rtk-output-filtering/SKILL.md +1 -1
  239. package/dist/agent-src/skills/rule-writing/SKILL.md +1 -2
  240. package/dist/agent-src/skills/security/SKILL.md +41 -5
  241. package/dist/agent-src/skills/skill-improvement-pipeline/SKILL.md +5 -3
  242. package/dist/agent-src/skills/skill-reviewer/SKILL.md +1 -1
  243. package/dist/agent-src/skills/skill-writing/SKILL.md +23 -8
  244. package/dist/agent-src/skills/skill-writing/references/section-patterns.md +19 -1
  245. package/dist/agent-src/skills/source-discovery/SKILL.md +3 -3
  246. package/dist/agent-src/skills/sql-writing/SKILL.md +2 -2
  247. package/dist/agent-src/skills/standards-from-config/SKILL.md +2 -2
  248. package/dist/agent-src/skills/storybook-workshop/SKILL.md +5 -0
  249. package/dist/agent-src/skills/storybook-workshop/scripts/story_contrast_floor.mjs +99 -0
  250. package/dist/agent-src/skills/subagent-orchestration/SKILL.md +13 -13
  251. package/dist/agent-src/skills/subagent-orchestration/prompts/README.md +4 -3
  252. package/dist/agent-src/skills/svg-authoring/SKILL.md +324 -0
  253. package/dist/agent-src/skills/systematic-debugging/SKILL.md +2 -2
  254. package/dist/agent-src/skills/tailwind-engineer/SKILL.md +8 -3
  255. package/dist/agent-src/skills/tailwind-engineer/scripts/tailwind_config_gen.mjs +482 -0
  256. package/dist/agent-src/skills/test-case-discovery/SKILL.md +1 -1
  257. package/dist/agent-src/skills/threat-modeling/SKILL.md +8 -1
  258. package/dist/agent-src/skills/threat-modeling/data/manifest.json +5 -2
  259. package/dist/agent-src/skills/threat-modeling/data/reasoning.csv +10 -10
  260. package/dist/agent-src/skills/threat-modeling/data/surfaces.csv +11 -10
  261. package/dist/agent-src/skills/threat-modeling/data/threats.csv +34 -20
  262. package/dist/agent-src/skills/type-design/SKILL.md +196 -0
  263. package/dist/agent-src/skills/type-design/references/font-spec.md +90 -0
  264. package/dist/agent-src/skills/type-design/references/gates.md +113 -0
  265. package/dist/agent-src/skills/type-design/references/toolchain.md +66 -0
  266. package/dist/agent-src/skills/typography-system/SKILL.md +21 -11
  267. package/dist/agent-src/skills/typography-system/references/worked-scales.md +85 -0
  268. package/dist/agent-src/skills/ui-apply-generic/SKILL.md +1 -1
  269. package/dist/agent-src/skills/universal-project-analysis/SKILL.md +1 -1
  270. package/dist/agent-src/skills/upstream-contribute/SKILL.md +120 -125
  271. package/dist/agent-src/skills/using-git-worktrees/SKILL.md +1 -0
  272. package/dist/agent-src/skills/verify-repair-loop/SKILL.md +1 -1
  273. package/dist/agent-src/skills/voice-and-tone-design/SKILL.md +71 -2
  274. package/dist/agent-src/skills/wireframe/SKILL.md +5 -2
  275. package/dist/agent-src/skills/worktree-lifecycle/SKILL.md +16 -14
  276. package/dist/agent-src/templates/AGENTS.md +1 -1
  277. package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
  278. package/dist/agent-src/templates/agents/proposal.example.md +1 -1
  279. package/dist/agent-src/templates/github-workflows/advisories-daily.yml +85 -0
  280. package/dist/agent-src/templates/roadmaps.md +39 -1
  281. package/dist/agent-src/templates/scripts/check_memory.ts +2 -2
  282. package/dist/agent-src/templates/scripts/historical-bug-patterns.example.yml +13 -1
  283. package/dist/agent-src/templates/scripts/work_engine/cli.ts +17 -0
  284. package/dist/agent-src/templates/scripts/work_engine/cli_args.ts +10 -0
  285. package/dist/agent-src/templates/scripts/work_engine/directives/ui/scaffold.ts +22 -13
  286. package/dist/agent-src/templates/scripts/work_engine/dispatcher.ts +18 -4
  287. package/dist/agent-src/templates/scripts/work_engine/procedure/checks.ts +283 -0
  288. package/dist/agent-src/templates/scripts/work_engine/procedure/run.ts +432 -0
  289. package/dist/agent-src/templates/scripts/work_engine/procedure/sidecar.ts +185 -0
  290. package/dist/agent-src/templates/scripts/work_engine/state.ts +3 -0
  291. package/dist/cli/commands/uiRender.js +265 -13
  292. package/dist/cli/commands/uiRenderAsset.js +1316 -0
  293. package/dist/cli/commands/uiRenderAssetCompare.js +752 -0
  294. package/dist/cli/commands/uiRenderAssetFonts.js +158 -0
  295. package/dist/cli/commands/uiRenderAssetGrid.js +265 -0
  296. package/dist/cli/commands/uiRenderAssetGrounds.js +323 -0
  297. package/dist/cli/commands/uiRenderAssetIsolation.js +85 -0
  298. package/dist/cli/commands/uiRenderAssetTypes.js +133 -0
  299. package/dist/cli/initRouting.js +1 -1
  300. package/dist/cli/main.js +47 -1
  301. package/dist/cli/mcp/dispatch.js +31 -9
  302. package/dist/cli/python/knowledge_ingest.js +7 -39
  303. package/dist/cli/python/workspace_secrets.js +4 -177
  304. package/dist/cli/registry.js +5 -2
  305. package/dist/cli-delegate/archive_completed_roadmaps.js +3 -2
  306. package/dist/cli-delegate/check_update_banner.js +635 -266
  307. package/dist/cli-delegate/{chunk-AED2VQGM.js → chunk-2CX7NWRB.js} +233 -437
  308. package/dist/cli-delegate/{chunk-F3QDG2GT.js → chunk-2SFOOFJJ.js} +259 -159
  309. package/dist/cli-delegate/chunk-2Y5AMLMY.js +136 -0
  310. package/dist/cli-delegate/{chunk-OKJUIAPS.js → chunk-4E2W63DA.js} +1 -62
  311. package/dist/cli-delegate/{chunk-6SRJTYJS.js → chunk-4SHNERBL.js} +37 -16
  312. package/dist/cli-delegate/chunk-5VO3RTMV.js +208 -0
  313. package/dist/cli-delegate/chunk-6E2LDY4F.js +27 -0
  314. package/dist/cli-delegate/{chunk-XQTPXKFA.js → chunk-6ZD2QZMN.js} +8 -17
  315. package/dist/cli-delegate/{chunk-6V5FMA3P.js → chunk-7CEI2YPS.js} +1 -1
  316. package/dist/cli-delegate/{chunk-DMSZHXZS.js → chunk-7DRWULEQ.js} +80 -7
  317. package/dist/cli-delegate/chunk-7NXCZSDL.js +54 -0
  318. package/dist/cli-delegate/chunk-AX3U3UWJ.js +328 -0
  319. package/dist/cli-delegate/{chunk-WOKWX5PL.js → chunk-BKALSTBZ.js} +18 -3
  320. package/dist/cli-delegate/chunk-BMGUGSY6.js +103 -0
  321. package/dist/cli-delegate/chunk-BYV46VRM.js +3309 -0
  322. package/dist/cli-delegate/{chunk-5GPULQBE.js → chunk-CAL76ADI.js} +4215 -682
  323. package/dist/cli-delegate/chunk-DSY6LLCQ.js +127 -0
  324. package/dist/cli-delegate/chunk-E6XNBVE4.js +581 -0
  325. package/dist/cli-delegate/{chunk-ZUZKZYCN.js → chunk-ESUNQ5XR.js} +46 -10
  326. package/dist/cli-delegate/{chunk-G5VVIZRC.js → chunk-F7HLSW6P.js} +1 -1
  327. package/dist/cli-delegate/chunk-FIEEOH5C.js +133 -0
  328. package/dist/cli-delegate/chunk-FKYZOWVD.js +719 -0
  329. package/dist/cli-delegate/chunk-GOMILC3O.js +918 -0
  330. package/dist/cli-delegate/chunk-I4KJYVPB.js +69 -0
  331. package/dist/cli-delegate/{chunk-JQOM7TTM.js → chunk-IDAOTDPE.js} +1 -100
  332. package/dist/cli-delegate/chunk-IROAQ77A.js +137 -0
  333. package/dist/cli-delegate/chunk-J2SJZICO.js +698 -0
  334. package/dist/cli-delegate/{chunk-6YQC2HLF.js → chunk-JHMMWNJ2.js} +54 -40
  335. package/dist/cli-delegate/{chunk-WQWXGIWB.js → chunk-M6CRG5O6.js} +165 -15
  336. package/dist/cli-delegate/{chunk-PHD7OOLE.js → chunk-MQ2UDEGV.js} +14 -3
  337. package/dist/cli-delegate/chunk-NTM7G2V4.js +220 -0
  338. package/dist/cli-delegate/chunk-PV7S5DPJ.js +175 -0
  339. package/dist/cli-delegate/{chunk-F7DQWYR5.js → chunk-RAXQOETZ.js} +6 -2
  340. package/dist/cli-delegate/{chunk-T7XSJSUQ.js → chunk-SIOMDAKL.js} +980 -784
  341. package/dist/cli-delegate/{chunk-KR6EM23A.js → chunk-TULVDQ4L.js} +3 -48
  342. package/dist/cli-delegate/chunk-UUYEKXKR.js +20 -0
  343. package/dist/cli-delegate/{chunk-7XGD2OEU.js → chunk-UXWHY6CQ.js} +2 -1
  344. package/dist/cli-delegate/{chunk-PWX7CKMD.js → chunk-VC3ECUEK.js} +1 -1
  345. package/dist/cli-delegate/{chunk-KXXWJTDO.js → chunk-WMNYVIYX.js} +1 -1
  346. package/dist/cli-delegate/chunk-XCY5KKUC.js +181 -0
  347. package/dist/cli-delegate/{chunk-H76PHZBI.js → chunk-XLY5G3CM.js} +14 -19
  348. package/dist/cli-delegate/cmd_adr_effective.js +41 -2
  349. package/dist/cli-delegate/cmd_analyze_session.js +7 -7
  350. package/dist/cli-delegate/cmd_brand_status.js +1287 -10
  351. package/dist/cli-delegate/cmd_ci_settle.js +330 -0
  352. package/dist/cli-delegate/cmd_conformance.js +669 -822
  353. package/dist/cli-delegate/cmd_doctor.js +26 -16
  354. package/dist/cli-delegate/cmd_doctor_neighbours.js +10 -7
  355. package/dist/cli-delegate/cmd_explain.js +9 -24
  356. package/dist/cli-delegate/cmd_export.js +14 -11
  357. package/dist/cli-delegate/cmd_fleet.js +5 -3
  358. package/dist/cli-delegate/cmd_git_convention.js +8 -4
  359. package/dist/cli-delegate/cmd_handoff.js +79 -242
  360. package/dist/cli-delegate/cmd_mcp_available.js +1 -1
  361. package/dist/cli-delegate/cmd_memory_get.js +4 -2
  362. package/dist/cli-delegate/cmd_migrate.js +2 -2
  363. package/dist/cli-delegate/cmd_packs_active.js +2 -2
  364. package/dist/cli-delegate/cmd_preflight.js +7 -5
  365. package/dist/cli-delegate/cmd_prune.js +16 -13
  366. package/dist/cli-delegate/cmd_route_audit.js +11 -8
  367. package/dist/cli-delegate/cmd_route_explain.js +649 -16
  368. package/dist/cli-delegate/cmd_session_recycle.js +15 -6
  369. package/dist/cli-delegate/cmd_settings_check.js +2 -2
  370. package/dist/cli-delegate/cmd_settings_get.js +1 -1
  371. package/dist/cli-delegate/cmd_settings_migrate.js +14 -11
  372. package/dist/cli-delegate/cmd_settings_set.js +1 -1
  373. package/dist/cli-delegate/cmd_sync.js +16 -13
  374. package/dist/cli-delegate/cmd_uninstall.js +185 -31
  375. package/dist/cli-delegate/cmd_update.js +67 -3
  376. package/dist/cli-delegate/cmd_upgrade.js +38 -7
  377. package/dist/cli-delegate/cmd_validate.js +16 -13
  378. package/dist/cli-delegate/cmd_versions.js +1 -1
  379. package/dist/cli-delegate/dashboard_mode.js +1 -1
  380. package/dist/cli-delegate/gate_execute.js +4 -3
  381. package/dist/cli-delegate/roadmap_age.js +22 -0
  382. package/dist/cli-delegate/roadmap_gates.js +4 -3
  383. package/dist/cli-delegate/update_roadmap_progress.js +3 -2
  384. package/dist/cli-delegate/upgrade_unattended-VVL6JLSX.js +541 -0
  385. package/dist/discovery/deprecation-report.md +1 -1
  386. package/dist/discovery/discovery-manifest.json +409 -201
  387. package/dist/discovery/discovery-manifest.json.sha256 +1 -1
  388. package/dist/discovery/discovery-manifest.summary.md +8 -8
  389. package/dist/discovery/dormancy-report.md +1 -1
  390. package/dist/discovery/orphan-report.md +1 -1
  391. package/dist/discovery/packs.json +36 -27
  392. package/dist/discovery/trust-report.md +3 -3
  393. package/dist/discovery/workspaces.json +25 -16
  394. package/dist/hooks/chat_history.chunk.cjs +6852 -0
  395. package/dist/hooks/chat_history.chunk.cjs.sha256 +1 -0
  396. package/dist/hooks/dispatch.js +17706 -18627
  397. package/dist/hooks/dispatch.sha256 +1 -1
  398. package/dist/hooks/egress_engine.chunk.cjs +915 -0
  399. package/dist/hooks/egress_engine.chunk.cjs.sha256 +1 -0
  400. package/dist/hooks/svg_rules.chunk.cjs +1858 -0
  401. package/dist/hooks/svg_rules.chunk.cjs.sha256 +1 -0
  402. package/dist/install/conflict.js +36 -6
  403. package/dist/install/conflictTracker.js +81 -7
  404. package/dist/install/detect.js +3 -3
  405. package/dist/install/hostPermissionsAdapter.js +106 -0
  406. package/dist/install/install.mjs +3117 -1949
  407. package/dist/install/installThinLayer.js +1 -1
  408. package/dist/install/manifestFiles.js +21 -0
  409. package/dist/install/noGoDenyRows.js +300 -0
  410. package/dist/install/noGoRules.js +368 -0
  411. package/dist/install/ownershipLedger.js +133 -0
  412. package/dist/install/plan.js +1 -1
  413. package/dist/install/preflight.js +39 -8
  414. package/dist/install/preserve.js +74 -12
  415. package/dist/install/recordedOwnership.js +88 -5
  416. package/dist/install/rule_scope.js +20 -3
  417. package/dist/install/stagedRuntime.js +455 -0
  418. package/dist/install/toolsArg.js +19 -0
  419. package/dist/install/userPin.js +47 -0
  420. package/dist/install/wizard-plan.js +26 -4
  421. package/dist/mcp/awesome-mcp-servers.row.md +1 -1
  422. package/dist/mcp/mcp-cloudflare-catalogue.json +3 -2
  423. package/dist/mcp/registry-manifest.json +6 -5
  424. package/dist/mcp/server.json +3 -3
  425. package/dist/mcp/server.mjs +9814 -5333
  426. package/dist/router.json +1 -1
  427. package/dist/scripts/_lib/agent_settings.js +7 -12
  428. package/dist/scripts/_lib/asset_render_record.js +219 -0
  429. package/dist/scripts/_lib/claude_builtin_names.js +9 -4
  430. package/dist/scripts/_lib/claude_settings_hooks.js +102 -34
  431. package/dist/scripts/_lib/egress_rules.js +766 -0
  432. package/dist/scripts/_lib/environment_detector.js +24 -33
  433. package/dist/scripts/_lib/global_deploy_inventory.js +146 -9
  434. package/dist/scripts/_lib/guidelines_lane.js +10 -1
  435. package/dist/scripts/_lib/identifier_redaction.js +24 -0
  436. package/dist/scripts/_lib/installed_layer.js +17 -0
  437. package/dist/scripts/_lib/installed_layer_normalise.js +72 -0
  438. package/dist/scripts/_lib/lean_projection_mode.js +34 -152
  439. package/dist/scripts/_lib/project_tree_form.js +118 -0
  440. package/dist/scripts/_lib/scan_scope.js +105 -0
  441. package/dist/scripts/_lib/scoped_projection.js +133 -3
  442. package/dist/scripts/_lib/settings_removed_keys.js +29 -0
  443. package/dist/scripts/_lib/settings_renamed_keys.js +17 -5
  444. package/dist/scripts/_lib/subagent_native_fields.js +35 -0
  445. package/dist/scripts/_lib/svg_dom.js +284 -0
  446. package/dist/scripts/_lib/svg_filled_counter.js +285 -0
  447. package/dist/scripts/_lib/svg_geometry.js +477 -0
  448. package/dist/scripts/_lib/svg_motion.js +179 -0
  449. package/dist/scripts/_lib/svg_rules.js +1339 -0
  450. package/dist/scripts/_lib/svg_urls.js +67 -0
  451. package/dist/scripts/_lib/ui_surface.js +19 -0
  452. package/dist/scripts/_lib/verification_command.js +364 -0
  453. package/dist/scripts/_lib/workspace_secrets.js +229 -0
  454. package/dist/scripts/ai_council/seat_policy.js +27 -0
  455. package/dist/scripts/ai_council/transport_resolver.js +9 -10
  456. package/dist/scripts/condense.js +25 -29
  457. package/dist/scripts/lint_svg_asset.js +324 -0
  458. package/dist/server/io/substituteTemplate.js +2 -2
  459. package/dist/server/io/yamlIO.js +2 -2
  460. package/dist/server/routes/install.js +5 -1
  461. package/dist/server/routes/settings.js +2 -2
  462. package/dist/server/routes/wizard.js +1 -1
  463. package/dist/server/schemas/settings.js +22 -16
  464. package/dist/server/serverInfo.js +1 -1
  465. package/dist/shared/brandTokenDocument.js +47 -0
  466. package/dist/shared/brandTokenPaths.js +43 -0
  467. package/dist/shared/settingsCarveOut.js +0 -7
  468. package/dist/shared/skillRanking.js +103 -5
  469. package/dist/shared/userMd/formAdapter.js +16 -0
  470. package/dist/shared/userMd/schema.js +17 -0
  471. package/dist/ui/assets/index-w70Ado09.js +23 -0
  472. package/dist/ui/index.html +1 -1
  473. package/docs/MIGRATION.md +89 -2
  474. package/docs/THIRD-PARTY-NOTICES.md +27 -0
  475. package/docs/contracts/settings-classes.md +27 -9
  476. package/docs/guidelines/agent-infra/api-cost-levers.md +4 -3
  477. package/docs/guidelines/agent-infra/model-recommendation.md +11 -5
  478. package/docs/guidelines/agent-infra/rule-body-migration-inventory.md +1 -1
  479. package/docs/guidelines/agent-infra/self-improvement-pipeline.md +47 -24
  480. package/docs/guidelines/design-antipatterns.md +6 -1
  481. package/docs/guidelines/design-asset-discipline.md +17 -0
  482. package/docs/guidelines/design-fidelity-routing.md +1 -0
  483. package/docs/guidelines/php/api-design.md +10 -1
  484. package/llms.txt +12 -10
  485. package/package.json +15 -5
  486. package/src/agent-src/scripts/dashboard_mode.ts +4 -1
  487. package/src/agent-src/scripts/roadmap_age.ts +175 -0
  488. package/src/agent-src/scripts/update_roadmap_progress.ts +10 -2
  489. package/src/agent-src/templates/AGENTS.md +1 -1
  490. package/src/agent-src/templates/scripts/check_memory.ts +2 -2
  491. package/src/agent-src/templates/scripts/historical-bug-patterns.example.yml +13 -1
  492. package/src/agent-src/templates/scripts/work_engine/cli.ts +17 -0
  493. package/src/agent-src/templates/scripts/work_engine/cli_args.ts +10 -0
  494. package/src/agent-src/templates/scripts/work_engine/directives/ui/scaffold.ts +22 -13
  495. package/src/agent-src/templates/scripts/work_engine/dispatcher.ts +18 -4
  496. package/src/agent-src/templates/scripts/work_engine/procedure/checks.ts +283 -0
  497. package/src/agent-src/templates/scripts/work_engine/procedure/run.ts +432 -0
  498. package/src/agent-src/templates/scripts/work_engine/procedure/sidecar.ts +185 -0
  499. package/src/agent-src/templates/scripts/work_engine/state.ts +3 -0
  500. package/src/config/agent-settings.template.yml +120 -57
  501. package/src/config/agents-paths.yml +13 -7
  502. package/src/config/ci-local-parity.yml +44 -0
  503. package/src/config/continuity-surface.json +51 -2
  504. package/src/config/depth-audit-samples.json +5 -0
  505. package/src/config/discovery/command-verbs.yml +1 -0
  506. package/src/config/dispatch-economy-metrics.json +1 -0
  507. package/src/config/egress-mutations/class-precedence-swapped.json +7 -0
  508. package/src/config/egress-mutations/credential-token-restores.json +7 -0
  509. package/src/config/egress-mutations/iban-checksum-disabled.json +7 -0
  510. package/src/config/egress-mutations/raw-value-in-the-count-record.json +7 -0
  511. package/src/config/evaluator-budgets.json +4 -4
  512. package/src/config/gate-coverage.yml +149 -3
  513. package/src/config/gate-reachability-exemptions.json +3 -5
  514. package/src/config/gate-violation-baselines.json +29 -25
  515. package/src/config/gitignore-block.txt +6 -3
  516. package/src/config/hook-bundle-budget.json +34 -3
  517. package/src/config/hook-carrier-probes.json +30 -0
  518. package/src/config/hook-latency-budget.json +4 -2
  519. package/src/config/host-injection-effect.json +8 -1
  520. package/src/config/host-skill-frontmatter-keys.json +56 -0
  521. package/src/config/layer-form-brakes.json +4 -0
  522. package/src/config/layer-form-comparison-corpus.json +42 -0
  523. package/src/config/loop-surfaces.yaml +28 -47
  524. package/src/config/memory-twin-verdicts.yml +9 -1
  525. package/src/config/metric-registry.yml +40 -3
  526. package/src/config/pack-size-budget.json +15 -0
  527. package/src/config/procedure-core-budget.json +21 -0
  528. package/src/config/profiles/minimal.ini +1 -1
  529. package/src/config/project-tree-form.json +24 -0
  530. package/src/config/reach-channels.yml +3 -3
  531. package/src/config/reach-prescriptions-intake.yml +1 -1
  532. package/src/config/roadmap-step-size-baseline.json +118 -0
  533. package/src/config/routing-coverage-seed.json +100 -2
  534. package/src/config/skill-body-budget.json +686 -0
  535. package/src/config/skill-fork-pilot.json +17 -0
  536. package/src/config/skill-lifecycle-probes-codex.json +57 -0
  537. package/src/config/skill-lifecycle-probes.json +57 -0
  538. package/src/config/skill-output-shape.json +316 -0
  539. package/src/config/step-depth-table.json +58 -0
  540. package/src/config/turnaround-budget.json +90 -0
  541. package/src/install/conflict.ts +38 -6
  542. package/src/install/conflictTracker.ts +126 -8
  543. package/src/install/detect.ts +3 -3
  544. package/src/install/hostPermissionsAdapter.ts +136 -0
  545. package/src/install/installThinLayer.ts +6 -1
  546. package/src/install/manifestFiles.ts +24 -0
  547. package/src/install/noGoDenyRows.ts +368 -0
  548. package/src/install/noGoRules.ts +438 -0
  549. package/src/install/ownershipLedger.ts +180 -0
  550. package/src/install/plan.ts +1 -1
  551. package/src/install/preflight.ts +42 -10
  552. package/src/install/preserve.ts +94 -12
  553. package/src/install/recordedOwnership.ts +121 -3
  554. package/src/install/rule_scope.ts +20 -3
  555. package/src/install/stagedRuntime.ts +528 -0
  556. package/src/install/toolsArg.ts +16 -0
  557. package/src/install/types.ts +23 -2
  558. package/src/install/userPin.ts +54 -0
  559. package/src/install/wizard-plan.ts +25 -4
  560. package/src/scripts/_cli/_doctor_emit.ts +24 -0
  561. package/src/scripts/_cli/cmd_brand_status.ts +223 -12
  562. package/src/scripts/_cli/cmd_ci_settle.ts +32 -0
  563. package/src/scripts/_cli/cmd_doctor.ts +23 -44
  564. package/src/scripts/_cli/cmd_route_explain.ts +176 -2
  565. package/src/scripts/_cli/cmd_uninstall.ts +270 -26
  566. package/src/scripts/_cli/cmd_update.ts +34 -1
  567. package/src/scripts/_cli/cmd_upgrade.ts +38 -2
  568. package/src/scripts/_cli/detection_report.ts +7 -0
  569. package/src/scripts/_cli/doctor_execution.ts +1 -2
  570. package/src/scripts/_cli/doctor_install_mode.ts +39 -0
  571. package/src/scripts/_cli/doctor_network_posture.ts +3 -3
  572. package/src/scripts/_cli/doctor_ownership_ledger.ts +126 -0
  573. package/src/scripts/_cli/doctor_update_verdict.ts +104 -0
  574. package/src/scripts/_cli/explain_last/scrubber.ts +9 -21
  575. package/src/scripts/_cli/upgrade_unattended.ts +548 -0
  576. package/src/scripts/_dispatch.bash +42 -9
  577. package/src/scripts/_lib/activation_ladder.ts +1 -1
  578. package/src/scripts/_lib/advisory_inventory.ts +405 -0
  579. package/src/scripts/_lib/advisory_notice.ts +270 -0
  580. package/src/scripts/_lib/advisory_sources.ts +752 -0
  581. package/src/scripts/_lib/advisory_verdicts.ts +363 -0
  582. package/src/scripts/_lib/agent_artifact_location.ts +177 -0
  583. package/src/scripts/_lib/agent_settings.ts +7 -12
  584. package/src/scripts/_lib/arrival_record.ts +217 -0
  585. package/src/scripts/_lib/asset_render_record.ts +247 -0
  586. package/src/scripts/_lib/billing_grant.ts +4 -2
  587. package/src/scripts/_lib/brand_voice_slot.ts +258 -0
  588. package/src/scripts/_lib/canary_status.ts +24 -0
  589. package/src/scripts/_lib/cc_transcript.ts +276 -0
  590. package/src/scripts/_lib/changelog_eras.ts +83 -0
  591. package/src/scripts/_lib/claim_phrases.ts +71 -0
  592. package/src/scripts/_lib/classify_update.ts +171 -0
  593. package/src/scripts/_lib/claude_builtin_names.ts +9 -4
  594. package/src/scripts/_lib/claude_hook_scope.ts +195 -0
  595. package/src/scripts/_lib/claude_settings_hooks.ts +118 -34
  596. package/src/scripts/_lib/codex_resident_set.ts +460 -0
  597. package/src/scripts/_lib/codex_skill_finder.ts +225 -0
  598. package/src/scripts/_lib/confusables.ts +19 -0
  599. package/src/scripts/_lib/consumer_impact.ts +199 -0
  600. package/src/scripts/_lib/content_class.ts +127 -0
  601. package/src/scripts/_lib/counted_probe.ts +68 -1
  602. package/src/scripts/_lib/credential_deny_rows.ts +151 -0
  603. package/src/scripts/_lib/credential_files.ts +367 -0
  604. package/src/scripts/_lib/delivery_arm_experiment.ts +2 -2
  605. package/src/scripts/_lib/depth_flip.ts +141 -0
  606. package/src/scripts/_lib/design_lock.ts +117 -0
  607. package/src/scripts/_lib/design_sync_ledger.ts +142 -0
  608. package/src/scripts/_lib/design_sync_plan.ts +177 -0
  609. package/src/scripts/_lib/design_sync_reconcile.ts +159 -0
  610. package/src/scripts/_lib/design_sync_verify.ts +99 -0
  611. package/src/scripts/_lib/design_system_claude_design.ts +323 -0
  612. package/src/scripts/_lib/design_system_import.ts +84 -6
  613. package/src/scripts/_lib/dispatch_import_closure.ts +42 -11
  614. package/src/scripts/_lib/egress_credential_env.ts +35 -0
  615. package/src/scripts/_lib/egress_engine.ts +22 -0
  616. package/src/scripts/_lib/egress_known_values.ts +223 -0
  617. package/src/scripts/_lib/egress_normalize.ts +67 -0
  618. package/src/scripts/_lib/egress_prefixes.ts +42 -0
  619. package/src/scripts/_lib/egress_rules.ts +896 -0
  620. package/src/scripts/_lib/egress_rules_linearity.ts +56 -0
  621. package/src/scripts/_lib/egress_sender_scan.ts +290 -0
  622. package/src/scripts/_lib/egress_session_table.ts +510 -0
  623. package/src/scripts/_lib/egress_text.ts +63 -0
  624. package/src/scripts/_lib/egress_token.ts +252 -0
  625. package/src/scripts/_lib/empty_cycles.ts +1 -1
  626. package/src/scripts/_lib/environment_detector.ts +45 -30
  627. package/src/scripts/_lib/exemption_shape.ts +75 -1
  628. package/src/scripts/_lib/floor_posture.ts +185 -0
  629. package/src/scripts/_lib/forge_protection.ts +140 -2
  630. package/src/scripts/_lib/global_deploy_inventory.ts +159 -6
  631. package/src/scripts/_lib/guidelines_lane.ts +12 -1
  632. package/src/scripts/_lib/hand_kinds.ts +65 -0
  633. package/src/scripts/_lib/headless_invocation.ts +3 -3
  634. package/src/scripts/_lib/host_capability.ts +34 -2
  635. package/src/scripts/_lib/host_env_write.ts +39 -18
  636. package/src/scripts/_lib/host_listing_model.ts +14 -7
  637. package/src/scripts/_lib/identifier_redaction.ts +26 -0
  638. package/src/scripts/_lib/impact_bump.ts +70 -0
  639. package/src/scripts/_lib/install_reach_checks.ts +53 -1
  640. package/src/scripts/_lib/installed_layer.ts +31 -0
  641. package/src/scripts/_lib/installed_layer_normalise.ts +75 -0
  642. package/src/scripts/_lib/instructions_loaded_classify.ts +75 -0
  643. package/src/scripts/_lib/instructions_loaded_event.ts +53 -0
  644. package/src/scripts/_lib/knowledge_global_redaction.ts +2 -2
  645. package/src/scripts/_lib/layer_form_corpus.ts +165 -0
  646. package/src/scripts/_lib/layer_form_decision.ts +248 -0
  647. package/src/scripts/_lib/layer_form_evidence.ts +112 -0
  648. package/src/scripts/_lib/lean_projection_mode.ts +32 -161
  649. package/src/scripts/_lib/llm_proposer_transport.ts +1 -0
  650. package/src/scripts/_lib/loop_guards.ts +8 -120
  651. package/src/scripts/_lib/major_surface.ts +207 -0
  652. package/src/scripts/_lib/mcp_bridge.ts +30 -69
  653. package/src/scripts/_lib/merge_clearance_record.ts +384 -0
  654. package/src/scripts/_lib/neighbour_census.ts +18 -1
  655. package/src/scripts/_lib/orchestration_record.ts +61 -0
  656. package/src/scripts/_lib/outcome_vocabularies.ts +15 -23
  657. package/src/scripts/_lib/pin_advice.ts +49 -0
  658. package/src/scripts/_lib/platform_anchor.ts +222 -0
  659. package/src/scripts/_lib/procedure_checks.ts +5 -0
  660. package/src/scripts/_lib/project_tree_form.ts +134 -0
  661. package/src/scripts/_lib/projection_key_migration.ts +122 -0
  662. package/src/scripts/_lib/prompt_shape.ts +13 -2
  663. package/src/scripts/_lib/provenance_verify.ts +272 -0
  664. package/src/scripts/_lib/quota_parked.ts +27 -1
  665. package/src/scripts/_lib/ratification_artifact.ts +64 -1
  666. package/src/scripts/_lib/ratification_subject.ts +508 -46
  667. package/src/scripts/_lib/release_findings_ingest.ts +52 -5
  668. package/src/scripts/_lib/release_highlights.ts +72 -2
  669. package/src/scripts/_lib/release_settings_surface.ts +392 -0
  670. package/src/scripts/_lib/review_round.ts +134 -0
  671. package/src/scripts/_lib/roadmap_checkboxes.ts +1 -1
  672. package/src/scripts/_lib/roadmap_slug_lock.ts +254 -0
  673. package/src/scripts/_lib/roadmap_step_size.ts +75 -0
  674. package/src/scripts/_lib/run_checkpoint.ts +60 -3
  675. package/src/scripts/_lib/runtime_wiring_checks.ts +15 -1
  676. package/src/scripts/_lib/scope_guard.sh +64 -5
  677. package/src/scripts/_lib/scoped_projection.ts +136 -3
  678. package/src/scripts/_lib/secret_detector.ts +24 -103
  679. package/src/scripts/_lib/security_fix.ts +429 -0
  680. package/src/scripts/_lib/security_fix_context.ts +133 -0
  681. package/src/scripts/_lib/self_repair.ts +69 -39
  682. package/src/scripts/_lib/self_repair_outgoing.ts +209 -0
  683. package/src/scripts/_lib/self_repair_payload.ts +86 -0
  684. package/src/scripts/_lib/session_eol.ts +3 -0
  685. package/src/scripts/_lib/session_pr_watch.ts +207 -0
  686. package/src/scripts/_lib/session_register.ts +51 -1
  687. package/src/scripts/_lib/session_tag.ts +51 -0
  688. package/src/scripts/_lib/settings_removed_keys.ts +30 -0
  689. package/src/scripts/_lib/settings_renamed_keys.ts +19 -5
  690. package/src/scripts/_lib/skill_catalogue.ts +66 -25
  691. package/src/scripts/_lib/skill_fork_pilot.ts +121 -0
  692. package/src/scripts/_lib/skill_leases.ts +203 -0
  693. package/src/scripts/_lib/skill_listing_overrides.ts +294 -0
  694. package/src/scripts/_lib/skill_materialisation.ts +388 -0
  695. package/src/scripts/_lib/skill_materialisation_gate.ts +210 -0
  696. package/src/scripts/_lib/skill_materialisation_lock.ts +149 -0
  697. package/src/scripts/_lib/skill_materialisation_triggers.ts +96 -0
  698. package/src/scripts/_lib/skill_origin.ts +21 -1
  699. package/src/scripts/_lib/skill_reach.ts +342 -0
  700. package/src/scripts/_lib/source_shape.ts +7 -2
  701. package/src/scripts/_lib/sqlite_guard.ts +1 -1
  702. package/src/scripts/_lib/step_anatomy.ts +330 -0
  703. package/src/scripts/_lib/subagent_capsule.ts +8 -2
  704. package/src/scripts/_lib/subagent_native_fields.ts +30 -0
  705. package/src/scripts/_lib/subagent_spawn.ts +36 -0
  706. package/src/scripts/_lib/subagent_steering.ts +367 -0
  707. package/src/scripts/_lib/svg_dom.ts +318 -0
  708. package/src/scripts/_lib/svg_filled_counter.ts +287 -0
  709. package/src/scripts/_lib/svg_geometry.ts +480 -0
  710. package/src/scripts/_lib/svg_motion.ts +208 -0
  711. package/src/scripts/_lib/svg_rules.ts +1455 -0
  712. package/src/scripts/_lib/svg_urls.ts +72 -0
  713. package/src/scripts/_lib/test_delta.ts +89 -6
  714. package/src/scripts/_lib/tier_downshift_verdict.ts +222 -0
  715. package/src/scripts/_lib/tool_result_census.ts +62 -0
  716. package/src/scripts/_lib/trigger_routers.ts +1 -0
  717. package/src/scripts/_lib/turnaround_throughput.ts +378 -0
  718. package/src/scripts/_lib/ui_surface.ts +22 -0
  719. package/src/scripts/_lib/unattended_gates.ts +100 -0
  720. package/src/scripts/_lib/undeployed_fixes.ts +34 -0
  721. package/src/scripts/_lib/update_check.ts +143 -12
  722. package/src/scripts/_lib/update_invite.ts +116 -0
  723. package/src/scripts/_lib/update_notice.ts +259 -0
  724. package/src/scripts/_lib/verification_command.ts +59 -0
  725. package/src/scripts/_lib/verify_clause.ts +31 -19
  726. package/src/scripts/_lib/version_order.ts +146 -0
  727. package/src/scripts/_lib/worker_reuse.ts +191 -0
  728. package/src/scripts/_lib/workspace_secrets.ts +273 -0
  729. package/src/scripts/_lib/worktree_git.ts +67 -0
  730. package/src/scripts/_lib/worktree_ignored.ts +116 -0
  731. package/src/scripts/adoption_status.ts +330 -3
  732. package/src/scripts/adr_cite_check.ts +93 -0
  733. package/src/scripts/advisories.ts +803 -0
  734. package/src/scripts/ai-image/adapters/flux.sh +1 -0
  735. package/src/scripts/ai-image/adapters/gemini-image.sh +1 -0
  736. package/src/scripts/ai-image/adapters/ideogram.sh +1 -0
  737. package/src/scripts/ai-image/adapters/recraft.sh +1 -0
  738. package/src/scripts/ai-video/adapters/comfyui.sh +1 -0
  739. package/src/scripts/ai-video/adapters/fal.sh +1 -0
  740. package/src/scripts/ai-video/adapters/gemini-veo.sh +1 -0
  741. package/src/scripts/ai-video/adapters/higgsfield.sh +1 -0
  742. package/src/scripts/ai-video/adapters/kling.sh +1 -0
  743. package/src/scripts/ai-video/adapters/musetalk.sh +1 -0
  744. package/src/scripts/ai-video/adapters/openai-images.sh +1 -0
  745. package/src/scripts/ai-video/adapters/replicate.sh +1 -0
  746. package/src/scripts/ai-video/adapters/sora.sh +1 -0
  747. package/src/scripts/ai-video/adapters/syncso.sh +1 -0
  748. package/src/scripts/ai-video/stitch.sh +115 -2
  749. package/src/scripts/ai_council/airgap.ts +1 -0
  750. package/src/scripts/ai_council/api_transport.ts +27 -4
  751. package/src/scripts/ai_council/budget_guard.ts +300 -11
  752. package/src/scripts/ai_council/bundler.ts +20 -29
  753. package/src/scripts/ai_council/chairman.ts +17 -2
  754. package/src/scripts/ai_council/cli_agency_bounds.ts +158 -0
  755. package/src/scripts/ai_council/cli_launch_env.ts +54 -0
  756. package/src/scripts/ai_council/cli_least_agency_canary.ts +18 -2
  757. package/src/scripts/ai_council/clients.ts +55 -73
  758. package/src/scripts/ai_council/config.ts +16 -22
  759. package/src/scripts/ai_council/council_egress.ts +167 -0
  760. package/src/scripts/ai_council/exposure_ledger.ts +117 -0
  761. package/src/scripts/ai_council/free_hand_shadow.ts +276 -0
  762. package/src/scripts/ai_council/free_route.ts +216 -0
  763. package/src/scripts/ai_council/free_route_catalog.ts +173 -0
  764. package/src/scripts/ai_council/free_route_client.ts +95 -0
  765. package/src/scripts/ai_council/free_rung.ts +61 -0
  766. package/src/scripts/ai_council/hand_payload.ts +230 -0
  767. package/src/scripts/ai_council/hand_schemas.ts +123 -0
  768. package/src/scripts/ai_council/jury_aggregate.ts +155 -1
  769. package/src/scripts/ai_council/kind_flip.ts +221 -0
  770. package/src/scripts/ai_council/kind_flip_state.ts +19 -0
  771. package/src/scripts/ai_council/learn_low_impact_preview.ts +50 -22
  772. package/src/scripts/ai_council/modification_review.ts +98 -5
  773. package/src/scripts/ai_council/necessity.ts +7 -0
  774. package/src/scripts/ai_council/orchestrator.ts +16 -16
  775. package/src/scripts/ai_council/pricing.ts +10 -0
  776. package/src/scripts/ai_council/redact_low_impact_entry.ts +5 -2
  777. package/src/scripts/ai_council/response_render.ts +27 -4
  778. package/src/scripts/ai_council/round_reservations.ts +119 -0
  779. package/src/scripts/ai_council/route_chooser.ts +161 -0
  780. package/src/scripts/ai_council/route_liveness.ts +152 -0
  781. package/src/scripts/ai_council/route_member.ts +52 -0
  782. package/src/scripts/ai_council/seat_policy.ts +40 -0
  783. package/src/scripts/ai_council/seat_resume.ts +40 -0
  784. package/src/scripts/ai_council/shadow_dispatch.ts +13 -3
  785. package/src/scripts/ai_council/spend_gate.ts +18 -2
  786. package/src/scripts/ai_council/status_surface.ts +37 -0
  787. package/src/scripts/ai_council/transport_resolver.ts +15 -18
  788. package/src/scripts/analysis_analysts.ts +226 -0
  789. package/src/scripts/analysis_anchors.ts +140 -0
  790. package/src/scripts/analysis_and_optimize.ts +44 -0
  791. package/src/scripts/analysis_emit.ts +150 -0
  792. package/src/scripts/analysis_exchange.ts +186 -0
  793. package/src/scripts/analysis_gate.ts +158 -0
  794. package/src/scripts/analysis_inbox_final.ts +173 -0
  795. package/src/scripts/analysis_ledger.ts +119 -0
  796. package/src/scripts/analysis_pr_optimize.ts +219 -0
  797. package/src/scripts/analysis_pr_review.ts +240 -0
  798. package/src/scripts/analysis_pr_target.ts +121 -0
  799. package/src/scripts/analysis_prompts.ts +130 -0
  800. package/src/scripts/analysis_publish.ts +358 -0
  801. package/src/scripts/analysis_questions.ts +78 -0
  802. package/src/scripts/analysis_run.ts +248 -0
  803. package/src/scripts/analysis_run_record.ts +210 -0
  804. package/src/scripts/analysis_target.ts +343 -0
  805. package/src/scripts/analysis_validate.ts +161 -0
  806. package/src/scripts/anchor_eval_falsify.ts +1 -1
  807. package/src/scripts/arrival_report.ts +115 -0
  808. package/src/scripts/ask_transport.ts +284 -35
  809. package/src/scripts/backfill_model_tier.ts +4 -5
  810. package/src/scripts/before_complete_hook.ts +48 -2
  811. package/src/scripts/bench_hook_latency.ts +3 -3
  812. package/src/scripts/bench_procedure_cursor.ts +440 -0
  813. package/src/scripts/bootstrap.sh +3 -3
  814. package/src/scripts/build_exit_code_register.ts +440 -0
  815. package/src/scripts/build_proof.ts +23 -1
  816. package/src/scripts/cache_realization_report.ts +155 -14
  817. package/src/scripts/canary_liveness.ts +117 -0
  818. package/src/scripts/chat_history.ts +12 -15
  819. package/src/scripts/check_account_surface.ts +318 -0
  820. package/src/scripts/check_agent_artifact_location.ts +17 -160
  821. package/src/scripts/check_bundle_path_leakage.ts +6 -0
  822. package/src/scripts/check_ci_local_parity.ts +21 -13
  823. package/src/scripts/check_claims.ts +77 -2
  824. package/src/scripts/check_completion_review.ts +34 -1
  825. package/src/scripts/check_concern_admissions.ts +41 -5
  826. package/src/scripts/check_consumer_scope_flip.ts +1 -1
  827. package/src/scripts/check_context_paths.ts +2 -0
  828. package/src/scripts/check_council_layout.ts +9 -0
  829. package/src/scripts/check_credential_spelling.ts +267 -0
  830. package/src/scripts/check_egress_mutations.ts +239 -0
  831. package/src/scripts/check_enforcement_coverage.ts +16 -3
  832. package/src/scripts/check_estate_count.ts +16 -1
  833. package/src/scripts/check_finding_dispositions.ts +147 -0
  834. package/src/scripts/check_gate_coverage.ts +123 -21
  835. package/src/scripts/check_gate_reachability.ts +16 -2
  836. package/src/scripts/check_generator_sync.ts +46 -0
  837. package/src/scripts/check_hook_bundle_composition.ts +139 -1
  838. package/src/scripts/check_host_docs_digest.ts +142 -5
  839. package/src/scripts/check_kernel_edit_ratified.ts +301 -76
  840. package/src/scripts/check_kernel_rule_bundle.ts +14 -10
  841. package/src/scripts/check_knowledge_cards.ts +83 -3
  842. package/src/scripts/check_memory.ts +13 -3
  843. package/src/scripts/check_pack_size.ts +1 -1
  844. package/src/scripts/check_permission_deny_syntax.ts +247 -0
  845. package/src/scripts/check_platform_anchor.ts +243 -0
  846. package/src/scripts/check_proposal.ts +183 -1
  847. package/src/scripts/check_reach_channels.ts +5 -3
  848. package/src/scripts/check_references.ts +5 -2
  849. package/src/scripts/check_release_manifest_content.ts +33 -0
  850. package/src/scripts/check_release_pr_shape.ts +5 -0
  851. package/src/scripts/check_rule_invariants.ts +9 -1
  852. package/src/scripts/check_skill_body_budget.ts +343 -0
  853. package/src/scripts/check_skill_requires.ts +137 -1
  854. package/src/scripts/check_step_receipts.ts +246 -0
  855. package/src/scripts/check_structural_breaking.ts +7 -1
  856. package/src/scripts/check_suppression_hygiene.ts +8 -0
  857. package/src/scripts/check_test_weakening.ts +14 -0
  858. package/src/scripts/check_token_quality_golden.ts +5 -1
  859. package/src/scripts/check_update_banner.ts +50 -3
  860. package/src/scripts/check_verify_expectation_delta.ts +2 -1
  861. package/src/scripts/check_voice_lexicon.ts +531 -0
  862. package/src/scripts/ci_os_attribution.ts +327 -0
  863. package/src/scripts/ci_platform_scope.ts +69 -0
  864. package/src/scripts/ci_settle.ts +4 -1
  865. package/src/scripts/ci_status.ts +0 -1
  866. package/src/scripts/ci_time_to_green.ts +748 -0
  867. package/src/scripts/classify_merge_risk.ts +407 -19
  868. package/src/scripts/code_graph/sqlite_store.ts +1 -1
  869. package/src/scripts/compile_trigger_evals.ts +353 -0
  870. package/src/scripts/condense.ts +25 -31
  871. package/src/scripts/consumer_matrix.ts +61 -3
  872. package/src/scripts/council_availability_hook.ts +12 -4
  873. package/src/scripts/council_cli.ts +48 -38
  874. package/src/scripts/cross_repo_retrieve.ts +9 -72
  875. package/src/scripts/design_lock.ts +106 -0
  876. package/src/scripts/design_slop_rules.ts +30 -0
  877. package/src/scripts/design_sync.ts +264 -0
  878. package/src/scripts/design_system_import.ts +30 -13
  879. package/src/scripts/dispatch_economy_report.ts +46 -1
  880. package/src/scripts/dispatch_r2_reviewer.ts +41 -5
  881. package/src/scripts/egress_canary.ts +166 -0
  882. package/src/scripts/egress_sender_census.ts +101 -0
  883. package/src/scripts/external_sources_denylist.json +94 -12
  884. package/src/scripts/generate_catalog.ts +6 -2
  885. package/src/scripts/generate_owned_digest.ts +163 -0
  886. package/src/scripts/generate_reference_toc.ts +143 -0
  887. package/src/scripts/generate_subagent_floor.ts +11 -1
  888. package/src/scripts/handoff_context_hook.ts +34 -0
  889. package/src/scripts/hook_carrier_census.ts +263 -0
  890. package/src/scripts/hook_chunks.d.mts +12 -0
  891. package/src/scripts/hook_chunks.mjs +114 -0
  892. package/src/scripts/hook_manifest.json +1 -1
  893. package/src/scripts/hook_manifest.yaml +65 -42
  894. package/src/scripts/hooks/block_config_weakening.ts +127 -3
  895. package/src/scripts/hooks/block_credential_reads.ts +165 -0
  896. package/src/scripts/hooks/block_no_verify.ts +47 -23
  897. package/src/scripts/hooks/block_speaking_inbox_dir.ts +54 -8
  898. package/src/scripts/hooks/category_a.ts +202 -25
  899. package/src/scripts/hooks/concern_failure_policy.ts +6 -6
  900. package/src/scripts/hooks/concern_registry.ts +31 -6
  901. package/src/scripts/hooks/concern_registry_types.ts +11 -0
  902. package/src/scripts/hooks/delegation_nudge_hook.ts +105 -15
  903. package/src/scripts/hooks/design_pass_hook.ts +199 -13
  904. package/src/scripts/hooks/dispatch_hook.ts +13 -12
  905. package/src/scripts/hooks/egress_engine_loader.ts +125 -0
  906. package/src/scripts/hooks/egress_stage.ts +440 -0
  907. package/src/scripts/hooks/hook_stdin.ts +29 -4
  908. package/src/scripts/hooks/host_docs_pages.ts +194 -0
  909. package/src/scripts/hooks/host_docs_pages.yaml +75 -0
  910. package/src/scripts/hooks/host_lowering.json +1 -1
  911. package/src/scripts/hooks/host_lowering.yaml +22 -21
  912. package/src/scripts/hooks/host_rate_marker_hook.ts +73 -0
  913. package/src/scripts/hooks/instructions_observer_hook.ts +170 -0
  914. package/src/scripts/hooks/interruption_ledger_hook.ts +1 -1
  915. package/src/scripts/hooks/lazy_concern.ts +148 -0
  916. package/src/scripts/hooks/orchestration_record_hook.ts +80 -2
  917. package/src/scripts/hooks/payload_stub.ts +16 -6
  918. package/src/scripts/hooks/permission_gate.ts +7 -7
  919. package/src/scripts/hooks/push_settle_hook.ts +32 -3
  920. package/src/scripts/hooks/replay_hook.ts +43 -0
  921. package/src/scripts/hooks/reread_guard_hook.ts +87 -10
  922. package/src/scripts/hooks/rtk_wrap_hook.ts +4 -3
  923. package/src/scripts/hooks/rule_inject_hook.ts +8 -1
  924. package/src/scripts/hooks/session_eol_hook.ts +48 -1
  925. package/src/scripts/hooks/source_first_gate_hook.ts +6 -8
  926. package/src/scripts/hooks/spawn_guard_shadow_hook.ts +203 -10
  927. package/src/scripts/hooks/state_io.ts +2 -2
  928. package/src/scripts/hooks/svg_obligation.ts +553 -0
  929. package/src/scripts/hooks/svg_rules_loader.ts +133 -0
  930. package/src/scripts/hooks/tool_result_bytes_hook.ts +14 -5
  931. package/src/scripts/hooks/turn_end_gate_hook.ts +5 -3
  932. package/src/scripts/hooks/ui_route_nudge_hook.ts +2 -12
  933. package/src/scripts/hooks_doctor.ts +31 -0
  934. package/src/scripts/hooks_status.ts +58 -3
  935. package/src/scripts/inbox_source_census.ts +74 -13
  936. package/src/scripts/install +25 -3
  937. package/src/scripts/install.ts +60 -91
  938. package/src/scripts/interruption_report.ts +11 -1
  939. package/src/scripts/layer_baseline_report.ts +267 -0
  940. package/src/scripts/learning_sidecar.ts +1 -1
  941. package/src/scripts/lint_agents_layout.ts +3 -0
  942. package/src/scripts/lint_blocker_citations.ts +104 -0
  943. package/src/scripts/lint_breaking_changes_index.ts +40 -7
  944. package/src/scripts/lint_consolidation_lineage.ts +150 -19
  945. package/src/scripts/lint_consumer_internal_refs.ts +90 -42
  946. package/src/scripts/lint_context_spine_usage.ts +21 -0
  947. package/src/scripts/lint_design_antipattern_parity.ts +39 -7
  948. package/src/scripts/lint_design_quality.ts +60 -12
  949. package/src/scripts/lint_evidence_artifacts.ts +31 -0
  950. package/src/scripts/lint_example_fences.ts +11 -2
  951. package/src/scripts/lint_ghostwriter_source.ts +42 -10
  952. package/src/scripts/lint_hook_manifest.ts +94 -0
  953. package/src/scripts/lint_marketplace_install_completeness.ts +4 -0
  954. package/src/scripts/lint_mcp_config_security.ts +9 -11
  955. package/src/scripts/lint_motion_authority_drift.ts +27 -0
  956. package/src/scripts/lint_plan_risk_register.ts +28 -1
  957. package/src/scripts/lint_positioning.ts +4 -4
  958. package/src/scripts/lint_provenance.ts +1 -0
  959. package/src/scripts/lint_roadmap_blockers.ts +65 -4
  960. package/src/scripts/lint_roadmap_complexity.ts +98 -0
  961. package/src/scripts/lint_skill_link_reach.ts +251 -9
  962. package/src/scripts/lint_skill_procedures.ts +252 -0
  963. package/src/scripts/lint_skill_scripts_readonly.ts +12 -1
  964. package/src/scripts/lint_subagent_determinism.ts +14 -1
  965. package/src/scripts/lint_svg_asset.ts +365 -0
  966. package/src/scripts/lint_ticked_unmet_criteria.ts +300 -21
  967. package/src/scripts/mcp_registry_submit.sh +2 -2
  968. package/src/scripts/mcp_server/consumer_tool_catalog.json +4 -4
  969. package/src/scripts/mcp_server/suggest_skill.ts +89 -4
  970. package/src/scripts/mcp_server/tools.ts +2 -33
  971. package/src/scripts/measure_skill_ranker_baseline.ts +203 -5
  972. package/src/scripts/measure_turn_end_gate.ts +61 -1
  973. package/src/scripts/merge_clearance.ts +225 -0
  974. package/src/scripts/migrate_command_suggestions.ts +1 -1
  975. package/src/scripts/network_sources.ts +236 -0
  976. package/src/scripts/no_go_deny_rows.ts +225 -0
  977. package/src/scripts/orchestration_record.ts +15 -2
  978. package/src/scripts/plan_test_shards.ts +103 -0
  979. package/src/scripts/pr_url_reminder_hook.ts +10 -0
  980. package/src/scripts/print_required_checks.ts +5 -5
  981. package/src/scripts/probe_depth_shadow.ts +406 -0
  982. package/src/scripts/probe_turnaround.ts +342 -10
  983. package/src/scripts/ratification_subject.ts +176 -0
  984. package/src/scripts/rdp_gate_classify.ts +1 -0
  985. package/src/scripts/rdp_quality_eval.ts +1 -0
  986. package/src/scripts/release.ts +26 -55
  987. package/src/scripts/release_drill.ts +52 -23
  988. package/src/scripts/release_env.ts +37 -0
  989. package/src/scripts/release_publication.ts +11 -1
  990. package/src/scripts/report_consumer_gate_reach.ts +233 -0
  991. package/src/scripts/report_merge_gate_load.ts +295 -0
  992. package/src/scripts/report_project_contract.ts +123 -0
  993. package/src/scripts/report_reducible_classes.ts +342 -0
  994. package/src/scripts/report_required_checks_drift.ts +5 -3
  995. package/src/scripts/report_skill_host_census.ts +199 -0
  996. package/src/scripts/report_standing_payload_by_host.ts +7 -7
  997. package/src/scripts/retro.ts +758 -0
  998. package/src/scripts/retro_facts.ts +575 -0
  999. package/src/scripts/retro_queue.ts +739 -0
  1000. package/src/scripts/retro_session_facts.ts +218 -0
  1001. package/src/scripts/retro_sweep.ts +493 -0
  1002. package/src/scripts/retro_triage.ts +126 -0
  1003. package/src/scripts/roadmap_context.ts +2 -2
  1004. package/src/scripts/roadmap_drain_queue.ts +601 -0
  1005. package/src/scripts/run_supervise.ts +24 -2
  1006. package/src/scripts/schemas/agent-settings.schema.json +0 -16
  1007. package/src/scripts/schemas/command.schema.json +3 -3
  1008. package/src/scripts/schemas/review-findings.schema.json +8 -0
  1009. package/src/scripts/schemas/skill.schema.json +7 -6
  1010. package/src/scripts/schemas/subagent.schema.json +33 -2
  1011. package/src/scripts/score_skill_selection.ts +5 -2
  1012. package/src/scripts/second_brain_retrieval.ts +1 -0
  1013. package/src/scripts/second_brain_run.ts +1 -0
  1014. package/src/scripts/self_repair_cli.ts +563 -87
  1015. package/src/scripts/self_review_gate.ts +59 -4
  1016. package/src/scripts/session_register_hook.ts +91 -4
  1017. package/src/scripts/sessions_cli.ts +100 -5
  1018. package/src/scripts/skill_collision_clusters.ts +1 -1
  1019. package/src/scripts/skill_tools/score_skill_relevance.ts +31 -2
  1020. package/src/scripts/skill_trigger_eval.ts +4 -2
  1021. package/src/scripts/source_adapters.ts +183 -0
  1022. package/src/scripts/step_receipt.ts +100 -0
  1023. package/src/scripts/sync_pr_branch.ts +4 -0
  1024. package/src/scripts/test_council_qualification.ts +1 -1
  1025. package/src/scripts/trigger_eval_grandfather.json +3 -49
  1026. package/src/scripts/trigger_eval_rotation.ts +31 -3
  1027. package/src/scripts/ui_conformance_probe.ts +405 -1
  1028. package/src/scripts/update_counts.ts +230 -11
  1029. package/src/scripts/update_invite_hook.ts +245 -0
  1030. package/src/scripts/update_notice_hook.ts +298 -0
  1031. package/src/scripts/validate_frontmatter.ts +85 -0
  1032. package/src/scripts/worker_reuse_shadow_report.ts +121 -0
  1033. package/src/scripts/worktree_cleanup_check.ts +915 -121
  1034. package/src/server/io/yamlIO.ts +2 -2
  1035. package/src/server/schemas/settings.ts +37 -23
  1036. package/src/shared/brandTokenDocument.ts +46 -0
  1037. package/src/shared/brandTokenPaths.ts +43 -0
  1038. package/src/shared/settingsCarveOut.ts +0 -8
  1039. package/src/shared/skillRanking.ts +106 -5
  1040. package/src/shared/userMd/formAdapter.ts +15 -1
  1041. package/src/shared/userMd/schema.ts +22 -0
  1042. package/src/templates/agent-user.md +4 -0
  1043. package/src/templates/agent-user.yml +4 -0
  1044. package/dist/cli-delegate/chunk-523HCFYO.js +0 -90
  1045. package/dist/cli-delegate/chunk-AL3RZ22D.js +0 -74
  1046. package/dist/cli-delegate/chunk-KB5FZ655.js +0 -208
  1047. package/dist/cli-delegate/chunk-MDPPKQCQ.js +0 -875
  1048. package/dist/cli-delegate/chunk-MSUL5NYP.js +0 -1123
  1049. package/dist/cli-delegate/chunk-OAFSJBHH.js +0 -272
  1050. package/dist/ui/assets/index-C3djEJah.js +0 -23
  1051. package/src/scripts/_lib/continuation_ladder.ts +0 -335
  1052. package/src/scripts/hooks/run_continuation_hook.ts +0 -1524
@@ -0,0 +1,1455 @@
1
+ /**
2
+ * svg_rules — the SVG rule set, with no command-line surface attached.
3
+ *
4
+ * Split out of `lint_svg_asset.ts` for a measured reason, not a tidy one, and
5
+ * the routing it was split FOR IS NOW LIVE.
6
+ *
7
+ * THIS PARAGRAPH USED TO SAY THE OPPOSITE, and it was false the moment the
8
+ * routing landed — finding 10 of that change's own completion review, which is
9
+ * the cheapest kind of defect to leave behind and the most expensive to read: a
10
+ * reader opening this file to decide whether it sits on a hot path was told it
11
+ * does not.
12
+ *
13
+ * It does. `hooks/design_pass_hook.ts` routes every `.svg` write here through
14
+ * `hooks/svg_rules_loader.ts`, and `package.json`'s `build:hook-chunks` emits
15
+ * this module as `dist/hooks/svg_rules.chunk.cjs` for exactly that. The chunk
16
+ * is why it is live without being expensive: step 1.4 of
17
+ * `road-to-svg-with-a-witness` imported these rules STATICALLY into the one
18
+ * bundle every hook event loads — 30,924 bytes, 12,931 past the ceiling — and
19
+ * an independent council refused it 2/2. Loaded as a chunk the same rules cost
20
+ * 2,128 bytes in that bundle and are `require`d only for a path ending in
21
+ * `.svg`.
22
+ *
23
+ * So: this module is on the `.svg` write path and on no other. Anything added
24
+ * here is paid by an SVG write and by nothing else — a budget, not a licence.
25
+ * Its other consumers are `lint_svg_asset` and the fixture report.
26
+ *
27
+ * `lint_svg_asset.ts` is now the CLI over this module and re-exports it, so
28
+ * every existing import keeps resolving.
29
+ */
30
+
31
+ import { attrsOf, parseDocument, tagEnd, type XmlDocument, type XmlNode } from './svg_dom.js';
32
+ import { ruleFilledCounter } from './svg_filled_counter.js';
33
+ import { cssUnescape, isActiveUrl, localName, rawAttrs, resolvedUrl, svgPrefixes, URL_ATTRS } from './svg_urls.js';
34
+ import {
35
+ type Box,
36
+ elementExtent,
37
+ pathDataError,
38
+ pathExtent,
39
+ pointsExtent,
40
+ staticBox,
41
+ tokenisePath,
42
+ } from './svg_geometry.js';
43
+
44
+ export { elementExtent, pathDataError, pathExtent, pointsExtent, staticBox, tokenisePath };
45
+ export type { Box };
46
+ import {
47
+ SMIL_TAGS,
48
+ declarationsOf,
49
+ firstSmilLine,
50
+ hasCssAnimation,
51
+ hasReducedMotionQuery,
52
+ hasSmilMarkup,
53
+ readCssRules,
54
+ smilVerdict,
55
+ } from './svg_motion.js';
56
+
57
+ // ---------------------------------------------------------------------------
58
+ // Types
59
+ // ---------------------------------------------------------------------------
60
+
61
+ /**
62
+ * Where the file is embedded. It decides what can EXECUTE, which is why the
63
+ * safety family reads it (step 1.5). An undeclared mode is graded as `inline`,
64
+ * the strictest — never as the mode that happens to be convenient.
65
+ */
66
+ export type DeliveryMode = 'inline' | 'document' | 'img' | 'css-bg';
67
+
68
+ export const DELIVERY_MODES: readonly DeliveryMode[] = ['inline', 'document', 'img', 'css-bg'];
69
+
70
+ export type Severity = 'error' | 'warning' | 'inert';
71
+
72
+ export interface Finding {
73
+ rule: string;
74
+ file: string;
75
+ line: number;
76
+ snippet: string;
77
+ severity: Severity;
78
+ }
79
+
80
+ export interface ProjectConfig {
81
+ ignoreRules?: string[];
82
+ ignoreFiles?: string[];
83
+ }
84
+
85
+ /** One parsed element. Attribute values are raw (entity-decoded for `&` only). */
86
+ export interface SvgElement {
87
+ name: string;
88
+ attrs: Record<string, string>;
89
+ line: number;
90
+ /** Character offset of the opening `<`. */
91
+ start: number;
92
+ selfClosing: boolean;
93
+ closing: boolean;
94
+ raw: string;
95
+ }
96
+
97
+ // ---------------------------------------------------------------------------
98
+ // A deliberately small scanner
99
+ // ---------------------------------------------------------------------------
100
+
101
+ /**
102
+ * Elements in document order.
103
+ *
104
+ * Not a conformant XML parser and not trying to be: it skips comments, CDATA
105
+ * and the prolog, and reads tag names plus attributes. Everything the rules
106
+ * below need is attribute-level or text-level, and pulling in a real parser
107
+ * would make a dependency-free linter depend on one.
108
+ */
109
+ export function parseElements(content: string): SvgElement[] {
110
+ const out: SvgElement[] = [];
111
+ const lineOf = lineIndexer(content);
112
+ let i = 0;
113
+ while (i < content.length) {
114
+ const lt = content.indexOf('<', i);
115
+ if (lt === -1) break;
116
+ // Comments, CDATA and the prolog carry no elements.
117
+ if (content.startsWith('<!--', lt)) {
118
+ const end = content.indexOf('-->', lt + 4);
119
+ i = end === -1 ? content.length : end + 3;
120
+ continue;
121
+ }
122
+ if (content.startsWith('<![CDATA[', lt)) {
123
+ const end = content.indexOf(']]>', lt + 9);
124
+ i = end === -1 ? content.length : end + 3;
125
+ continue;
126
+ }
127
+ if (content.startsWith('<?', lt)) {
128
+ // A PROCESSING INSTRUCTION ENDS AT `?>`, NEVER AT THE FIRST `>`.
129
+ //
130
+ // Finding 3 of the third completion review, reproduced by running
131
+ // `scanSvg`: this branch shared the DOCTYPE's first-`>` search, so
132
+ // `<?xml-stylesheet href="a.css?x=1><script/>"?>` — a legal PI
133
+ // whose quoted pseudo-attribute contains a `>` — ended early and
134
+ // the remainder was parsed as markup. `<script/>` then became a
135
+ // real element and the file was reported
136
+ // `svg-safety-active-content` at severity `error`, on a document
137
+ // that contains no script. Under finding 4's static-only path that
138
+ // false error is a BLOCKING refusal, so the two compound.
139
+ //
140
+ // `svg_dom.parseDocument` always did this correctly; the two
141
+ // scanners disagreeing about where the prolog ends is what the
142
+ // module header below now says plainly instead of claiming they
143
+ // are one scan.
144
+ const end = content.indexOf('?>', lt + 2);
145
+ i = end === -1 ? content.length : end + 2;
146
+ continue;
147
+ }
148
+ if (content.startsWith('<!', lt)) {
149
+ // A DOCTYPE may carry a bracketed internal subset; skip past it.
150
+ const bracket = content.indexOf('[', lt);
151
+ const close = content.indexOf('>', lt);
152
+ if (bracket !== -1 && close !== -1 && bracket < close) {
153
+ const endBracket = content.indexOf(']', bracket);
154
+ const after = endBracket === -1 ? close : content.indexOf('>', endBracket);
155
+ i = after === -1 ? content.length : after + 1;
156
+ } else {
157
+ i = close === -1 ? content.length : close + 1;
158
+ }
159
+ continue;
160
+ }
161
+ const gt = tagEnd(content, lt);
162
+ if (gt === -1) break;
163
+ const raw = content.slice(lt, gt + 1);
164
+ const closing = raw.startsWith('</');
165
+ const selfClosing = raw.endsWith('/>');
166
+ const nameMatch = /^<\/?\s*([A-Za-z_][\w.:-]*)/.exec(raw);
167
+ if (!nameMatch) {
168
+ i = gt + 1;
169
+ continue;
170
+ }
171
+ out.push({
172
+ name: (nameMatch[1] ?? '').toLowerCase(),
173
+ attrs: closing ? {} : attrsOf(raw),
174
+ line: lineOf(lt),
175
+ start: lt,
176
+ selfClosing,
177
+ closing,
178
+ raw,
179
+ });
180
+ i = gt + 1;
181
+ }
182
+ return out;
183
+ }
184
+
185
+ /** End of the tag, honouring quoted attribute values that may contain `>`. */
186
+
187
+
188
+ /**
189
+ * Blank out every XML comment, preserving offsets and line breaks.
190
+ *
191
+ * Finding 2.3 of `road-to-svg-rules-in-the-dispatch-path`, from the R2
192
+ * completion review of the parent change: `parseElements` skips comments and
193
+ * the content-level regex rules did not, so the two disagreed about what the
194
+ * file contains. A commented `url(#x)` marked a `<defs>` entry used, and a
195
+ * commented `@media (prefers-reduced-motion)` suppressed the
196
+ * no-reduced-motion rule — in both directions a finding was lost to markup
197
+ * that does not render.
198
+ *
199
+ * Every character inside `<!--` … `-->` becomes a space, the delimiters
200
+ * included, and newlines are kept. That keeps every offset and every line
201
+ * number identical to the raw text, so a rule can report a position in the
202
+ * masked string and have it mean the same thing in the file. An unterminated
203
+ * comment masks to end of input, which is what a browser does with it too.
204
+ *
205
+ * NOT applied to `parseIgnores`, deliberately: the inline
206
+ * `svg-asset-disable` markers LIVE in comments, so masking them would make every one of them silently stop
207
+ * working. The raw text is the right input there and the masked text is the
208
+ * right input everywhere else.
209
+ */
210
+ export function maskComments(content: string): string {
211
+ if (!content.includes('<!--')) return content;
212
+ const out = content.split('');
213
+ let i = 0;
214
+ while (i < out.length) {
215
+ const start = content.indexOf('<!--', i);
216
+ if (start === -1) break;
217
+ const close = content.indexOf('-->', start + 4);
218
+ const end = close === -1 ? content.length : close + 3;
219
+ for (let j = start; j < end; j += 1) {
220
+ if (out[j] !== '\n' && out[j] !== '\r') out[j] = ' ';
221
+ }
222
+ i = end;
223
+ }
224
+ return out.join('');
225
+ }
226
+
227
+ function lineIndexer(content: string): (offset: number) => number {
228
+ const starts: number[] = [0];
229
+ for (let i = 0; i < content.length; i++) if (content[i] === '\n') starts.push(i + 1);
230
+ return (offset: number): number => {
231
+ let lo = 0;
232
+ let hi = starts.length - 1;
233
+ while (lo < hi) {
234
+ const mid = (lo + hi + 1) >> 1;
235
+ if ((starts[mid] as number) <= offset) lo = mid;
236
+ else hi = mid - 1;
237
+ }
238
+ return lo + 1;
239
+ };
240
+ }
241
+
242
+ /** The text between an opening and its matching closing tag, for simple leaves. */
243
+ function textOf(content: string, elements: SvgElement[], index: number): string {
244
+ const el = elements[index];
245
+ if (!el || el.selfClosing) return '';
246
+ const openEnd = content.indexOf('>', el.start);
247
+ const closeTag = `</${el.name}`;
248
+ const closeAt = content.toLowerCase().indexOf(closeTag, openEnd);
249
+ if (openEnd === -1 || closeAt === -1) return '';
250
+ return content.slice(openEnd + 1, closeAt);
251
+ }
252
+
253
+ // ---------------------------------------------------------------------------
254
+ // Shared readings
255
+ // ---------------------------------------------------------------------------
256
+
257
+ export interface ViewBox {
258
+ x: number;
259
+ y: number;
260
+ w: number;
261
+ h: number;
262
+ }
263
+
264
+ export function readViewBox(el: SvgElement | undefined): ViewBox | null {
265
+ const raw = el?.attrs['viewbox'];
266
+ if (!raw) return null;
267
+ const parts = raw.trim().split(/[\s,]+/).map(Number);
268
+ if (parts.length !== 4 || parts.some((n) => !Number.isFinite(n))) return null;
269
+ const [x, y, w, h] = parts as [number, number, number, number];
270
+ if (w <= 0 || h <= 0) return null;
271
+ return { x, y, w, h };
272
+ }
273
+
274
+ /** Every `#id` this file points at, from `href`/`xlink:href` and from `url(#…)`. */
275
+ export function referencedIds(content: string, elements: SvgElement[]): Set<string> {
276
+ const ids = new Set<string>();
277
+ for (const el of elements) {
278
+ for (const key of ['href', 'xlink:href']) {
279
+ const v = el.attrs[key];
280
+ if (v && v.startsWith('#')) ids.add(v.slice(1));
281
+ }
282
+ }
283
+ const urlRe = /url\(\s*['"]?#([^)'"\s]+)/g;
284
+ let m: RegExpExecArray | null;
285
+ while ((m = urlRe.exec(content)) !== null) ids.add(m[1] as string);
286
+ // `begin="other.end"` and `<set attributeName=…>` name elements too.
287
+ const beginRe = /\b(?:begin|end)\s*=\s*"([A-Za-z_][\w.:-]*)\./g;
288
+ while ((m = beginRe.exec(content)) !== null) ids.add(m[1] as string);
289
+ return ids;
290
+ }
291
+
292
+
293
+ // ---------------------------------------------------------------------------
294
+ // Rule family — structure
295
+ // ---------------------------------------------------------------------------
296
+
297
+ function ruleMissingXmlns(elements: SvgElement[], file: string): Finding[] {
298
+ const root = elements.find((el) => el.name === 'svg' && !el.closing);
299
+ if (!root) return [];
300
+ if (root.attrs['xmlns']) return [];
301
+ return [
302
+ {
303
+ rule: 'svg-structure-missing-xmlns',
304
+ file,
305
+ line: root.line,
306
+ snippet: 'root <svg> has no xmlns — the file does not render as a standalone document or through <img>',
307
+ severity: 'error',
308
+ },
309
+ ];
310
+ }
311
+
312
+ /** Is this element inside a `<defs>`, `<symbol>`, `<clipPath>`, `<mask>` or `<marker>`? */
313
+ function buildContainerDepth(elements: SvgElement[]): Map<number, string[]> {
314
+ const stack: string[] = [];
315
+ const out = new Map<number, string[]>();
316
+ elements.forEach((el, idx) => {
317
+ if (el.closing) {
318
+ if (stack[stack.length - 1] === el.name) stack.pop();
319
+ out.set(idx, [...stack]);
320
+ return;
321
+ }
322
+ out.set(idx, [...stack]);
323
+ if (!el.selfClosing) stack.push(el.name);
324
+ });
325
+ return out;
326
+ }
327
+
328
+ const NON_RENDERED_CONTAINERS = new Set(['defs', 'symbol', 'clippath', 'mask', 'marker', 'pattern']);
329
+
330
+ function ruleOutsideViewBox(elements: SvgElement[], file: string): Finding[] {
331
+ const root = elements.find((el) => el.name === 'svg' && !el.closing);
332
+ const vb = readViewBox(root);
333
+ if (!vb) return [];
334
+ const ancestry = buildContainerDepth(elements);
335
+ const findings: Finding[] = [];
336
+ elements.forEach((el, idx) => {
337
+ if (el.closing) return;
338
+ const chain = ancestry.get(idx) ?? [];
339
+ if (chain.some((c) => NON_RENDERED_CONTAINERS.has(c))) return;
340
+ // A transform anywhere up the chain moves the box; staticBox already
341
+ // refuses an element with its own, and this refuses an inherited one.
342
+ if (chain.length && hasTransformedAncestor(elements, idx)) return;
343
+ const box = staticBox(el);
344
+ if (!box) return;
345
+ const outside =
346
+ box.maxX <= vb.x || box.minX >= vb.x + vb.w || box.maxY <= vb.y || box.minY >= vb.y + vb.h;
347
+ if (!outside) return;
348
+ findings.push({
349
+ rule: 'svg-structure-outside-viewbox',
350
+ file,
351
+ line: el.line,
352
+ snippet:
353
+ `<${el.name}> box (${box.minX},${box.minY})-(${box.maxX},${box.maxY}) lies entirely outside ` +
354
+ `viewBox ${vb.x} ${vb.y} ${vb.w} ${vb.h} — it will never paint`,
355
+ severity: 'error',
356
+ });
357
+ });
358
+ return findings;
359
+ }
360
+
361
+ function hasTransformedAncestor(elements: SvgElement[], index: number): boolean {
362
+ const stack: SvgElement[] = [];
363
+ for (let i = 0; i < index; i++) {
364
+ const el = elements[i] as SvgElement;
365
+ if (el.closing) {
366
+ if (stack[stack.length - 1]?.name === el.name) stack.pop();
367
+ continue;
368
+ }
369
+ if (!el.selfClosing) stack.push(el);
370
+ }
371
+ return stack.some((el) => Boolean(el.attrs['transform']));
372
+ }
373
+
374
+ /**
375
+ * Path-data grammar check.
376
+ *
377
+ * Reports only data that no conformant parser could accept — a command letter
378
+ * followed by the wrong number of coordinates, or a stray token. A path whose
379
+ * numbers are odd but well-formed is a design question, not a parse error.
380
+ */
381
+ function ruleUnparseablePath(elements: SvgElement[], file: string): Finding[] {
382
+ const findings: Finding[] = [];
383
+ for (const el of elements) {
384
+ if (el.closing) continue;
385
+ const d = el.attrs['d'];
386
+ if (d === undefined) continue;
387
+ const err = pathDataError(d);
388
+ if (err) {
389
+ findings.push({
390
+ rule: 'svg-structure-unparseable-path',
391
+ file,
392
+ line: el.line,
393
+ snippet: `<${el.name} d="…"> ${err}`,
394
+ severity: 'error',
395
+ });
396
+ }
397
+ }
398
+ return findings;
399
+ }
400
+
401
+ function ruleUnusedDefs(content: string, elements: SvgElement[], file: string): Finding[] {
402
+ const used = referencedIds(content, elements);
403
+ const ancestry = buildContainerDepth(elements);
404
+ const findings: Finding[] = [];
405
+ elements.forEach((el, idx) => {
406
+ if (el.closing) return;
407
+ const id = el.attrs['id'];
408
+ if (!id) return;
409
+ const chain = ancestry.get(idx) ?? [];
410
+ if (!chain.includes('defs')) return;
411
+ // A `<symbol>` is a sprite-sheet entry addressed from a HOST page, so
412
+ // the file cannot see its own reference. Exempt by construction —
413
+ // flagging it would red every sprite sheet in existence.
414
+ if (el.name === 'symbol') return;
415
+ if (used.has(id)) return;
416
+ findings.push({
417
+ rule: 'svg-structure-unused-defs',
418
+ file,
419
+ line: el.line,
420
+ snippet: `<${el.name} id="${id}"> sits in <defs> and nothing in this file references it`,
421
+ severity: 'warning',
422
+ });
423
+ });
424
+ return findings;
425
+ }
426
+
427
+ function ruleDanglingReference(content: string, elements: SvgElement[], file: string): Finding[] {
428
+ const defined = new Set<string>();
429
+ for (const el of elements) if (!el.closing && el.attrs['id']) defined.add(el.attrs['id'] as string);
430
+ const findings: Finding[] = [];
431
+ const lineOf = lineIndexer(content);
432
+
433
+ for (const el of elements) {
434
+ if (el.closing) continue;
435
+ for (const key of ['href', 'xlink:href']) {
436
+ const v = el.attrs[key];
437
+ if (!v || !v.startsWith('#')) continue;
438
+ const id = v.slice(1);
439
+ if (defined.has(id)) continue;
440
+ findings.push({
441
+ rule: 'svg-structure-dangling-reference',
442
+ file,
443
+ line: el.line,
444
+ snippet: `<${el.name} ${key}="${v}"> points at an id this file does not define`,
445
+ severity: 'error',
446
+ });
447
+ }
448
+ }
449
+ const urlRe = /url\(\s*['"]?#([^)'"\s]+)/g;
450
+ let m: RegExpExecArray | null;
451
+ while ((m = urlRe.exec(content)) !== null) {
452
+ const id = m[1] as string;
453
+ if (defined.has(id)) continue;
454
+ findings.push({
455
+ rule: 'svg-structure-dangling-reference',
456
+ file,
457
+ line: lineOf(m.index),
458
+ snippet: `url(#${id}) points at an id this file does not define`,
459
+ severity: 'error',
460
+ });
461
+ }
462
+ return dedupe(findings);
463
+ }
464
+
465
+ // ---------------------------------------------------------------------------
466
+ // Rule family — safety, graded per delivery mode (step 1.5)
467
+ // ---------------------------------------------------------------------------
468
+
469
+ /**
470
+ * What a delivery mode can execute.
471
+ *
472
+ * `inline` and `document` run script in an origin that matters, so active
473
+ * content is an ERROR there. `img` and `css-bg` run nothing and load nothing,
474
+ * so the same file is `inert` — reported, never called clean, because the same
475
+ * bytes can be inlined tomorrow with no edit to the file (decision E12).
476
+ */
477
+ export function safetySeverity(mode: DeliveryMode): Severity {
478
+ return mode === 'img' || mode === 'css-bg' ? 'inert' : 'error';
479
+ }
480
+
481
+ // An UNQUOTED value counts. Finding 1 of the fifth completion review: this
482
+ // fallback exists because the attribute parser only sees quoted values, and it
483
+ // required a quote itself — so `onload=alert(1)`, which every browser runs,
484
+ // produced zero findings while `onload="alert(1)"` produced an error. The
485
+ // safety family missing the unquoted spelling is the one miss that matters,
486
+ // because an attacker writes whichever spelling is not checked.
487
+ const ACTIVE_ATTR_RE = /\son[a-z]+\s*=\s*(["']|[^\s>"'])/i;
488
+
489
+
490
+ /** Elements that run or embed foreign content when the graphic is inlined. */
491
+ const ACTIVE_ELEMENTS = new Set(['script', 'foreignobject', 'iframe', 'embed', 'object']);
492
+
493
+ function ruleActiveContent(
494
+ content: string,
495
+ elements: SvgElement[],
496
+ file: string,
497
+ mode: DeliveryMode,
498
+ ): Finding[] {
499
+ const severity = safetySeverity(mode);
500
+ const findings: Finding[] = [];
501
+ const lineOf = lineIndexer(content);
502
+ const prefixes = svgPrefixes(content);
503
+
504
+ for (const el of elements) {
505
+ if (el.closing) continue;
506
+ const local = localName(el.name, prefixes);
507
+ // SMIL that writes an active URL into an href is the same href, later.
508
+ const smil = local === 'set' || local === 'animate' ? rawAttrs(el.raw) : null;
509
+ const animatesHref = smil !== null && /^(xlink:)?href$/i.test(resolvedUrl(smil['attributename'] ?? ''));
510
+ if (
511
+ animatesHref &&
512
+ ['to', 'from', 'by', 'values'].some((k) => (smil[k] ?? '').split(';').some(isActiveUrl))
513
+ ) {
514
+ findings.push({
515
+ rule: 'svg-safety-active-content',
516
+ file,
517
+ line: el.line,
518
+ snippet: `<${el.name} attributeName="href"> animates in an active URL; delivery mode "${mode}" grades it ${severity}`,
519
+ severity,
520
+ });
521
+ }
522
+ if (ACTIVE_ELEMENTS.has(local)) {
523
+ findings.push({
524
+ rule: 'svg-safety-active-content',
525
+ file,
526
+ line: el.line,
527
+ snippet: `<${el.name}> is active content; delivery mode "${mode}" grades it ${severity}`,
528
+ severity,
529
+ });
530
+ }
531
+ for (const [key, value] of Object.entries(el.attrs)) {
532
+ if (/^on[a-z]+$/.test(key)) {
533
+ findings.push({
534
+ rule: 'svg-safety-active-content',
535
+ file,
536
+ line: el.line,
537
+ snippet: `<${el.name} ${key}="…"> is an event handler; delivery mode "${mode}" grades it ${severity}`,
538
+ severity,
539
+ });
540
+ }
541
+ if (/^javascript:/i.test(resolvedUrl(value)) || (URL_ATTRS.has(key) && isActiveUrl(value))) {
542
+ findings.push({
543
+ rule: 'svg-safety-active-content',
544
+ file,
545
+ line: el.line,
546
+ snippet: `<${el.name} ${key}="${resolvedUrl(value).slice(0, 16)}…"> is an active URL; delivery mode "${mode}" grades it ${severity}`,
547
+ severity,
548
+ });
549
+ }
550
+ }
551
+ }
552
+ // A handler written on an element the scanner did not turn into attributes
553
+ // (an unquoted value, say) still has to be seen.
554
+ if (!findings.length && ACTIVE_ATTR_RE.test(content)) {
555
+ const idx = content.search(ACTIVE_ATTR_RE);
556
+ findings.push({
557
+ rule: 'svg-safety-active-content',
558
+ file,
559
+ line: lineOf(idx),
560
+ snippet: `an on* event handler attribute is present; delivery mode "${mode}" grades it ${severity}`,
561
+ severity,
562
+ });
563
+ }
564
+ return dedupe(findings);
565
+ }
566
+
567
+ /**
568
+ * An internal DTD subset or an entity declaration.
569
+ *
570
+ * A BARE public DOCTYPE is clean and deliberately so — exported assets carry
571
+ * one routinely and it is harmless. What is not harmless is the bracketed
572
+ * subset, which is where billion-laughs and external-entity payloads live.
573
+ */
574
+ function ruleInternalSubset(content: string, file: string): Finding[] {
575
+ const lineOf = lineIndexer(content);
576
+ const findings: Finding[] = [];
577
+ const entity = /<!ENTITY\b/i.exec(content);
578
+ if (entity) {
579
+ findings.push({
580
+ rule: 'svg-safety-internal-subset',
581
+ file,
582
+ line: lineOf(entity.index),
583
+ snippet: '<!ENTITY declaration — an entity-expansion surface; a bare public DOCTYPE is clean, this is not',
584
+ severity: 'error',
585
+ });
586
+ }
587
+ const doctype = /<!DOCTYPE[^>[]*\[/i.exec(content);
588
+ if (doctype) {
589
+ findings.push({
590
+ rule: 'svg-safety-internal-subset',
591
+ file,
592
+ line: lineOf(doctype.index),
593
+ snippet: 'DOCTYPE carries an internal subset "[…]" — strip it; a bare public DOCTYPE is clean',
594
+ severity: 'error',
595
+ });
596
+ }
597
+ return dedupe(findings);
598
+ }
599
+
600
+ const EXTERNAL_URL_RE = /^(?:https?:|ftp:|\/\/)/i;
601
+
602
+
603
+ function ruleExternalReference(
604
+ content: string,
605
+ elements: SvgElement[],
606
+ file: string,
607
+ mode: DeliveryMode,
608
+ ): Finding[] {
609
+ const severity = safetySeverity(mode);
610
+ const findings: Finding[] = [];
611
+ const lineOf = lineIndexer(content);
612
+
613
+ for (const el of elements) {
614
+ if (el.closing) continue;
615
+ for (const key of ['href', 'xlink:href', 'src']) {
616
+ const v = el.attrs[key];
617
+ if (v && EXTERNAL_URL_RE.test(resolvedUrl(v))) {
618
+ findings.push({
619
+ rule: 'svg-safety-external-reference',
620
+ file,
621
+ line: el.line,
622
+ snippet: `<${el.name} ${key}="${v.slice(0, 60)}"> loads from outside the file; mode "${mode}" grades it ${severity}`,
623
+ severity,
624
+ });
625
+ }
626
+ }
627
+ }
628
+ const urlRe = /url\(\s*['"]?((?:https?:|ftp:|\/\/)[^)'"\s]+)/gi;
629
+ let m: RegExpExecArray | null;
630
+ while ((m = urlRe.exec(content)) !== null) {
631
+ findings.push({
632
+ rule: 'svg-safety-external-reference',
633
+ file,
634
+ line: lineOf(m.index),
635
+ snippet: `url(${(m[1] ?? '').slice(0, 60)}) loads from outside the file; mode "${mode}" grades it ${severity}`,
636
+ severity,
637
+ });
638
+ }
639
+ // `@import "https://…"` names its target without `url()`. The target is
640
+ // resolved before the scheme test — XML references and CSS escapes both
641
+ // reach the stylesheet loader decoded.
642
+ const importRe = /@import\s+(?:url\(\s*)?(['"])([^'"]*)\1/gi;
643
+ while ((m = importRe.exec(content)) !== null) {
644
+ const target = resolvedUrl(cssUnescape(resolvedUrl(m[2] ?? '')));
645
+ if (!EXTERNAL_URL_RE.test(target)) continue;
646
+ findings.push({
647
+ rule: 'svg-safety-external-reference',
648
+ file,
649
+ line: lineOf(m.index),
650
+ snippet: `@import "${target.slice(0, 60)}" loads from outside the file; mode "${mode}" grades it ${severity}`,
651
+ severity,
652
+ });
653
+ }
654
+ return dedupe(findings);
655
+ }
656
+
657
+ // ---------------------------------------------------------------------------
658
+ // Rule family — vector honesty (defect D3)
659
+ // ---------------------------------------------------------------------------
660
+
661
+
662
+ /** Geometry elements that ARE vector drawing. `rect`/`circle` count (D3's false red). */
663
+ export const VECTOR_GEOMETRY = new Set([
664
+ 'path', 'rect', 'circle', 'ellipse', 'line', 'polyline', 'polygon', 'text', 'textpath', 'use',
665
+ ]);
666
+
667
+ export interface VectorVerdict {
668
+ /** `true` only when the file paints exclusively with vector geometry. */
669
+ trueVector: boolean;
670
+ rasterElements: number;
671
+ vectorElements: number;
672
+ reason: string;
673
+ }
674
+
675
+ /**
676
+ * Replaces the presence-of-`<path>` check (`logo-generation/SKILL.md:53-55`),
677
+ * which was false-green and false-red at once: a raster beside one dummy path
678
+ * passed, and a mark built from `rect`/`circle` — the very thing that file
679
+ * recommends — failed.
680
+ */
681
+ export function vectorVerdict(elements: SvgElement[]): VectorVerdict {
682
+ let raster = 0;
683
+ let vector = 0;
684
+ for (const el of elements) {
685
+ if (el.closing) continue;
686
+ if (el.name === 'image') {
687
+ // EVERY `<image>` counts, whatever it points at. Finding 7 of the
688
+ // fifth completion review: both arms of the `if` added 1, so the
689
+ // test and the regex behind it decided nothing — a dead constant
690
+ // and a dead branch dressed as a discrimination, which is the
691
+ // class this change has now found five times under other names.
692
+ // The comment on the else arm was the correct rule all along: an
693
+ // `<image>` of unknown type is still not vector geometry, and an
694
+ // href this scanner cannot classify is exactly the case where
695
+ // guessing "vector" would be the unsafe answer.
696
+ raster += 1;
697
+ continue;
698
+ }
699
+ if (el.name === 'foreignobject') {
700
+ raster += 1;
701
+ continue;
702
+ }
703
+ if (VECTOR_GEOMETRY.has(el.name)) vector += 1;
704
+ }
705
+ if (raster > 0) {
706
+ return {
707
+ trueVector: false,
708
+ rasterElements: raster,
709
+ vectorElements: vector,
710
+ reason: `carries ${raster} raster/foreignObject element(s) — not a true vector whatever else it contains`,
711
+ };
712
+ }
713
+ return {
714
+ trueVector: vector > 0,
715
+ rasterElements: 0,
716
+ vectorElements: vector,
717
+ reason:
718
+ vector > 0
719
+ ? `${vector} vector geometry element(s), no embedded raster`
720
+ : 'no painted geometry at all',
721
+ };
722
+ }
723
+
724
+ function ruleEmbeddedRaster(elements: SvgElement[], file: string): Finding[] {
725
+ const verdict = vectorVerdict(elements);
726
+ if (verdict.rasterElements === 0) return [];
727
+ const first = elements.find((el) => !el.closing && (el.name === 'image' || el.name === 'foreignobject'));
728
+ return [
729
+ {
730
+ rule: 'svg-vector-embedded-raster',
731
+ file,
732
+ line: first?.line ?? 1,
733
+ snippet: `${verdict.reason}; ${verdict.vectorElements} vector element(s) beside it does not make it one`,
734
+ severity: 'error',
735
+ },
736
+ ];
737
+ }
738
+
739
+ // ---------------------------------------------------------------------------
740
+ // Rule family — accessibility
741
+ // ---------------------------------------------------------------------------
742
+
743
+ /**
744
+ * Ids that collide the moment two graphics are inlined on one page (R7: two
745
+ * inline graphics both using `id="title"` made the accessibility tree name
746
+ * both after the first).
747
+ */
748
+ export const GENERIC_IDS = new Set([
749
+ 'title', 'desc', 'a', 'b', 'c', 'g', 'gradient', 'grad', 'mask', 'clip', 'clippath',
750
+ 'shadow', 'icon', 'path', 'path1', 'layer1', 'layer_1', 'svg', 'defs', 'filter', 'glow',
751
+ ]);
752
+
753
+ function ruleGenericId(elements: SvgElement[], file: string): Finding[] {
754
+ const findings: Finding[] = [];
755
+ for (const el of elements) {
756
+ if (el.closing) continue;
757
+ const id = el.attrs['id'];
758
+ if (!id) continue;
759
+ if (!GENERIC_IDS.has(id.toLowerCase())) continue;
760
+ findings.push({
761
+ rule: 'svg-a11y-generic-id',
762
+ file,
763
+ line: el.line,
764
+ snippet: `id="${id}" collides with any second graphic inlined on the same page — namespace it`,
765
+ severity: 'warning',
766
+ });
767
+ }
768
+ return findings;
769
+ }
770
+
771
+ interface NameState {
772
+ root: SvgElement | undefined;
773
+ hidden: boolean;
774
+ hasTitle: boolean;
775
+ hasLabel: boolean;
776
+ hasLabelledBy: boolean;
777
+ /** `aria-labelledby` resolving to this graphic's own `<title>` — one name, not two. */
778
+ labelledByOwnTitle: boolean;
779
+ hasRoleImg: boolean;
780
+ }
781
+
782
+ /** Is `elements[index]` a direct child of the root `<svg>`? */
783
+ /**
784
+ * Is the element at `index` a direct child of the root graphic?
785
+ *
786
+ * Counts open/close depth over the flat list. Step 3.1 of
787
+ * `road-to-svg-review-followups` moved the AUTHORITY for this question onto
788
+ * the parse tree ({@link directChildrenOfRoot}); this stays as the flat
789
+ * reading it is cross-checked against, because a depth counter and a tree
790
+ * agreeing on every fixture is evidence the tree is right, while replacing one
791
+ * with the other is evidence of nothing.
792
+ */
793
+ function isDirectChildOfRoot(elements: SvgElement[], index: number): boolean {
794
+ let depth = 0;
795
+ for (let i = 0; i < index; i++) {
796
+ const el = elements[i] as SvgElement;
797
+ if (el.closing) depth -= 1;
798
+ else if (!el.selfClosing) depth += 1;
799
+ }
800
+ return depth === 1;
801
+ }
802
+
803
+ /**
804
+ * The root graphic's direct element children, from the parse tree.
805
+ *
806
+ * Parentage decided by ancestry rather than by a depth count: the two agree on
807
+ * a well-formed file and the tree is the one that stays right on a file that
808
+ * is not, because a stray closing tag moves a depth counter and does not move
809
+ * an ancestor chain.
810
+ */
811
+ export function directChildrenOfRoot(doc: XmlDocument): XmlNode[] {
812
+ const root = doc.elements.find((el) => el.name === 'svg');
813
+ if (root === undefined) return [];
814
+ return root.children.filter((n) => n.kind === 'element');
815
+ }
816
+
817
+ /**
818
+ * Well-formedness, reported rather than assumed.
819
+ *
820
+ * Step 3.1. `<g><path></g></path>` carries both tags, both spelled correctly,
821
+ * and is not a tree — which is precisely what a flat element list cannot see
822
+ * and what a pattern over the raw text cannot decide. A file that does not
823
+ * parse is a FINDING, never a skipped file: a skip reads as clean in every
824
+ * count that matters.
825
+ *
826
+ * Capped at the first five errors. One mistake inside a deep document can
827
+ * produce an error per remaining tag, and a wall of them buries the first,
828
+ * which is the only one a reader can act on.
829
+ */
830
+ /**
831
+ * A gradient whose stops are all the same colour is a flat fill in costume.
832
+ *
833
+ * Step 3.1 of `road-to-svg-from-a-live-session`. A rejected 195 KB trace
834
+ * carried 355 gradient definitions, nine of them with identical stops, and the
835
+ * static witness reported one finding: a missing accessible name. Two external
836
+ * reviews counted the gradients by hand. This is cheap to decide from the
837
+ * bytes and nothing was deciding it.
838
+ *
839
+ * WARNING, not an error. A degenerate gradient renders correctly; it costs
840
+ * bytes and makes a file harder to edit, which is a reading rather than a
841
+ * defect — and the cost family above it is deliberately ungated for the same
842
+ * reason.
843
+ *
844
+ * Colour comparison is textual after whitespace and case normalisation, so
845
+ * `#FFF` and `#ffffff` are NOT reported as identical. That is the conservative
846
+ * direction: a false negative costs a reading, a false positive costs trust in
847
+ * every other finding beside it.
848
+ */
849
+ function ruleDegenerateGradient(elements: SvgElement[], file: string): Finding[] {
850
+ const findings: Finding[] = [];
851
+ const open: Array<{ el: SvgElement; stops: Array<{ color: string; opacity: string }> }> = [];
852
+ for (const el of elements) {
853
+ if (el.name === 'lineargradient' || el.name === 'radialgradient') {
854
+ if (el.closing) {
855
+ const g = open.pop();
856
+ if (!g || g.stops.length < 2) continue;
857
+ const first = g.stops[0] as { color: string; opacity: string };
858
+ const same = g.stops.every((s) => s.color === first.color && s.opacity === first.opacity);
859
+ if (!same) continue;
860
+ findings.push({
861
+ rule: 'svg-structure-degenerate-gradient',
862
+ file,
863
+ line: g.el.line,
864
+ snippet:
865
+ `<${g.el.name} id="${g.el.attrs['id'] ?? ''}"> has ${String(g.stops.length)} stops, all ` +
866
+ `${first.color || '(inherited)'} at opacity ${first.opacity} — a flat fill in a gradient's ` +
867
+ 'clothes, which costs bytes and makes the file harder to edit',
868
+ severity: 'warning',
869
+ });
870
+ } else if (!el.selfClosing) {
871
+ open.push({ el, stops: [] });
872
+ }
873
+ continue;
874
+ }
875
+ if (el.name === 'stop' && !el.closing) {
876
+ const host = open[open.length - 1];
877
+ if (!host) continue;
878
+ const style = el.attrs['style'] ?? '';
879
+ const fromStyle = /stop-color\s*:\s*([^;]+)/i.exec(style)?.[1];
880
+ const opacityFromStyle = /stop-opacity\s*:\s*([^;]+)/i.exec(style)?.[1];
881
+ host.stops.push({
882
+ color: (fromStyle ?? el.attrs['stop-color'] ?? '').trim().toLowerCase(),
883
+ opacity: (opacityFromStyle ?? el.attrs['stop-opacity'] ?? '1').trim(),
884
+ });
885
+ }
886
+ }
887
+ return findings;
888
+ }
889
+
890
+ function ruleWellFormed(doc: XmlDocument, file: string): Finding[] {
891
+ if (doc.errors.length === 0) return [];
892
+ const shown = doc.errors.slice(0, 5);
893
+ const more = doc.errors.length - shown.length;
894
+ return shown.map((e, i) => ({
895
+ rule: 'svg-structure-not-well-formed',
896
+ file,
897
+ line: e.line,
898
+ snippet:
899
+ `${e.message} (column ${String(e.column)})` +
900
+ (i === shown.length - 1 && more > 0 ? ` — and ${String(more)} further error(s)` : ''),
901
+ severity: 'error' as Severity,
902
+ }));
903
+ }
904
+
905
+ function rootNameState(content: string, doc: XmlDocument, elements: SvgElement[]): NameState {
906
+ const rootIdx = elements.findIndex((el) => el.name === 'svg' && !el.closing);
907
+ const root = rootIdx === -1 ? undefined : elements[rootIdx];
908
+ // A DIRECT child of the root only. The first `<title>` ANYWHERE was wrong
909
+ // in both directions: a chart's nested series title read as the graphic's
910
+ // name, producing a false `svg-a11y-double-name` beside an `aria-label`,
911
+ // and hiding a genuinely unnamed graphic behind a descendant's title.
912
+ const titleIdx = elements.findIndex(
913
+ (el, i) => el.name === 'title' && !el.closing && rootIdx !== -1 && isDirectChildOfRoot(elements, i),
914
+ );
915
+ // Step 3.1: the PARSE TREE decides parentage, and the depth count above is
916
+ // kept as the reading it is checked against. The two agree on every
917
+ // well-formed file; where they disagree the file is not well-formed, which
918
+ // `svg-structure-not-well-formed` already reports, and the tree is the one
919
+ // that is still right — a stray closing tag moves a depth counter and does
920
+ // not move an ancestor chain.
921
+ const titleIsDirectChild = directChildrenOfRoot(doc).some((n) => n.name === 'title');
922
+ const hasTitle =
923
+ titleIsDirectChild && titleIdx !== -1 && textOf(content, elements, titleIdx).trim().length > 0;
924
+
925
+ // `aria-labelledby="x"` + `<title id="x">` is the CANONICAL pattern, not a
926
+ // collision: the attribute POINTS AT the title rather than competing with
927
+ // it. Counting the pair as two names reds the one shape the accessibility
928
+ // guidance actually asks for, which is the false red that gets a linter
929
+ // switched off (Risk 1).
930
+ const titleIds = new Set(
931
+ elements
932
+ .filter((el) => el.name === 'title' && !el.closing && el.attrs['id'])
933
+ .map((el) => el.attrs['id'] as string),
934
+ );
935
+ const labelledBy = (root?.attrs['aria-labelledby'] ?? '').trim();
936
+ const labelledByTargets = labelledBy.split(/\s+/).filter(Boolean);
937
+ const labelledByOwnTitle =
938
+ labelledByTargets.length > 0 && labelledByTargets.every((id) => titleIds.has(id));
939
+
940
+ return {
941
+ root,
942
+ hidden: (root?.attrs['aria-hidden'] ?? '').toLowerCase() === 'true',
943
+ hasTitle,
944
+ hasLabel: Boolean(root?.attrs['aria-label']?.trim()),
945
+ hasLabelledBy: labelledBy.length > 0,
946
+ labelledByOwnTitle,
947
+ hasRoleImg: (root?.attrs['role'] ?? '').toLowerCase() === 'img',
948
+ };
949
+ }
950
+
951
+ function ruleAccessibleName(s: NameState, file: string): Finding[] {
952
+ if (!s.root) return [];
953
+ // A graphic declared decorative is correct with no name — that is the
954
+ // other half of the pattern, not a gap in it.
955
+ if (s.hidden) return [];
956
+ if (s.hasTitle || s.hasLabel || s.hasLabelledBy) return [];
957
+ return [
958
+ {
959
+ rule: 'svg-a11y-no-accessible-name',
960
+ file,
961
+ line: s.root.line,
962
+ snippet:
963
+ 'graphic has no accessible name and is not aria-hidden="true" — add role="img" with a <title>, ' +
964
+ 'or declare it decorative',
965
+ severity: 'error',
966
+ },
967
+ ];
968
+ }
969
+
970
+ function ruleDoubleName(s: NameState, file: string): Finding[] {
971
+ if (!s.root) return [];
972
+ // A labelledby that resolves to this graphic's own <title> is the SAME
973
+ // name reached through a reference, so it is not counted twice.
974
+ const labelledByIsSeparate = s.hasLabelledBy && !s.labelledByOwnTitle;
975
+ const names = [s.hasLabel, labelledByIsSeparate, s.hasTitle].filter(Boolean).length;
976
+ if (names < 2) return [];
977
+ return [
978
+ {
979
+ rule: 'svg-a11y-double-name',
980
+ file,
981
+ line: s.root.line,
982
+ snippet:
983
+ 'graphic declares more than one accessible name (aria-label / aria-labelledby / <title>) — ' +
984
+ 'only one wins and which one is renderer-dependent',
985
+ severity: 'error',
986
+ },
987
+ ];
988
+ }
989
+
990
+ // ---------------------------------------------------------------------------
991
+ // Rule family — motion. Every rule binds per element or per selector (R9).
992
+ // ---------------------------------------------------------------------------
993
+
994
+ function ruleKeySplinesRange(elements: SvgElement[], file: string): Finding[] {
995
+ const findings: Finding[] = [];
996
+ for (const el of elements) {
997
+ if (el.closing) continue;
998
+ const raw = el.attrs['keysplines'];
999
+ if (!raw) continue;
1000
+ const nums = raw.split(/[;\s,]+/).filter(Boolean).map(Number);
1001
+ const bad = nums.filter((n) => Number.isFinite(n) && (n < 0 || n > 1));
1002
+ if (!bad.length) continue;
1003
+ findings.push({
1004
+ rule: 'svg-motion-keysplines-range',
1005
+ file,
1006
+ line: el.line,
1007
+ snippet:
1008
+ `<${el.name} keySplines="${raw}"> holds ${bad.join(', ')} outside [0,1] — SMIL rejects the ` +
1009
+ 'whole animation rather than clamping it, so the element never moves',
1010
+ severity: 'error',
1011
+ });
1012
+ }
1013
+ return findings;
1014
+ }
1015
+
1016
+ /**
1017
+ * `transform-origin` without `transform-box`.
1018
+ *
1019
+ * PER ELEMENT and PER SELECTOR, which is the R9 correction: a file-scoped
1020
+ * "does `transform-box` appear anywhere" test passes a file whose wrong
1021
+ * element sits beside a correct twin, and the fixture plants exactly that.
1022
+ */
1023
+ function ruleTransformBox(content: string, elements: SvgElement[], file: string): Finding[] {
1024
+ const findings: Finding[] = [];
1025
+
1026
+ for (const el of elements) {
1027
+ if (el.closing) continue;
1028
+ const style = el.attrs['style'] ?? '';
1029
+ const decls = declarationsOf(style);
1030
+ const origin = decls['transform-origin'] ?? el.attrs['transform-origin'];
1031
+ if (!origin) continue;
1032
+ if (isOriginDefault(origin)) continue;
1033
+ const box = decls['transform-box'] ?? el.attrs['transform-box'];
1034
+ if (box) continue;
1035
+ findings.push({
1036
+ rule: 'svg-motion-transform-box',
1037
+ file,
1038
+ line: el.line,
1039
+ snippet:
1040
+ `<${el.name}> sets transform-origin: ${origin} with no transform-box — in SVG the origin ` +
1041
+ 'resolves against the view box, not the element, so the pivot lands somewhere else',
1042
+ severity: 'error',
1043
+ });
1044
+ }
1045
+
1046
+ for (const rule of readCssRules(content)) {
1047
+ const decls = declarationsOf(rule.body);
1048
+ const origin = decls['transform-origin'];
1049
+ if (!origin || isOriginDefault(origin)) continue;
1050
+ if (decls['transform-box']) continue;
1051
+ findings.push({
1052
+ rule: 'svg-motion-transform-box',
1053
+ file,
1054
+ line: rule.line,
1055
+ snippet:
1056
+ `selector "${rule.selector}" sets transform-origin: ${origin} with no transform-box — ` +
1057
+ 'the pivot resolves against the view box',
1058
+ severity: 'error',
1059
+ });
1060
+ }
1061
+ return dedupe(findings);
1062
+ }
1063
+
1064
+ function isOriginDefault(value: string): boolean {
1065
+ const v = value.trim().toLowerCase();
1066
+ return v === '0 0' || v === '0px 0px' || v === '0' || v === 'left top' || v === '0% 0%';
1067
+ }
1068
+
1069
+ function ruleNoReducedMotion(content: string, elements: SvgElement[], file: string): Finding[] {
1070
+ if (!hasSmilMarkup(content) && !hasCssAnimation(content)) return [];
1071
+ if (hasReducedMotionQuery(content)) return [];
1072
+ const line = hasSmilMarkup(content)
1073
+ ? firstSmilLine(content)
1074
+ : (elements.find((el) => !el.closing)?.line ?? 1);
1075
+ return [
1076
+ {
1077
+ rule: 'svg-motion-no-reduced-motion',
1078
+ file,
1079
+ line,
1080
+ snippet:
1081
+ 'the file animates and carries no prefers-reduced-motion route at all — a viewer who asked ' +
1082
+ 'the OS to stop motion still gets it',
1083
+ severity: 'error',
1084
+ },
1085
+ ];
1086
+ }
1087
+
1088
+ /**
1089
+ * SMIL whose only reduced-motion route is CSS (R4).
1090
+ *
1091
+ * Measured: under emulated `reduce`, a page-level `svg * { animation: none
1092
+ * !important }` left an `<animate>` moving `cx` from 20.2 to 76.8 in 700 ms.
1093
+ * CSS `animation` does not reach SMIL, so a file that believes it is covered
1094
+ * is covered by nothing. The reading itself lives in `_lib/svg_motion.ts`, so
1095
+ * DQ4's inline half (step 1.3) cannot drift away from this one.
1096
+ */
1097
+ function ruleSmilCssOnlyBranch(content: string, file: string): Finding[] {
1098
+ if (smilVerdict(content) !== 'css-only') return [];
1099
+ return [
1100
+ {
1101
+ rule: 'svg-motion-smil-css-only',
1102
+ file,
1103
+ line: firstSmilLine(content),
1104
+ snippet:
1105
+ 'this file animates with SMIL and its only reduced-motion route is a CSS declaration — ' +
1106
+ 'CSS animation/transition properties do not reach SMIL, so the animation keeps running',
1107
+ severity: 'error',
1108
+ },
1109
+ ];
1110
+ }
1111
+
1112
+ const PATH_COMMAND_RE = /[a-zA-Z]/g;
1113
+
1114
+ /**
1115
+ * An `<animate attributeName="d">` whose `values` entries are not the same
1116
+ * shape. SVG morphs a path by interpolating coordinate lists, so two values
1117
+ * with a different command count or a different command sequence do not
1118
+ * interpolate — the browser snaps instead of morphing, or refuses outright.
1119
+ */
1120
+ function ruleDMorphMismatch(elements: SvgElement[], file: string): Finding[] {
1121
+ const findings: Finding[] = [];
1122
+ for (const el of elements) {
1123
+ if (el.closing) continue;
1124
+ if (!SMIL_TAGS.has(el.name)) continue;
1125
+ if ((el.attrs['attributename'] ?? '').toLowerCase() !== 'd') continue;
1126
+ const raw = el.attrs['values'];
1127
+ if (!raw) continue;
1128
+ const entries = raw.split(';').map((s) => s.trim()).filter(Boolean);
1129
+ if (entries.length < 2) continue;
1130
+ const shapes = entries.map((e) => (e.match(PATH_COMMAND_RE) ?? []).join('').toUpperCase());
1131
+ const first = shapes[0] as string;
1132
+ const differing = shapes.findIndex((s) => s !== first);
1133
+ if (differing === -1) continue;
1134
+ findings.push({
1135
+ rule: 'svg-motion-d-morph-mismatch',
1136
+ file,
1137
+ line: el.line,
1138
+ snippet:
1139
+ `<${el.name} attributeName="d"> value ${differing + 1} has command sequence "${shapes[differing]}" ` +
1140
+ `against "${first}" in value 1 — a path morph interpolates coordinates, not commands`,
1141
+ severity: 'error',
1142
+ });
1143
+ }
1144
+ return findings;
1145
+ }
1146
+
1147
+ // ---------------------------------------------------------------------------
1148
+ // Composition
1149
+ // ---------------------------------------------------------------------------
1150
+
1151
+ function dedupe(findings: Finding[]): Finding[] {
1152
+ const seen = new Set<string>();
1153
+ const out: Finding[] = [];
1154
+ for (const f of findings) {
1155
+ const key = `${f.rule}::${f.file}::${f.line}::${f.snippet}`;
1156
+ if (seen.has(key)) continue;
1157
+ seen.add(key);
1158
+ out.push(f);
1159
+ }
1160
+ return out;
1161
+ }
1162
+
1163
+ export interface ScanOptions {
1164
+ /** Undeclared is graded as `inline`, the strictest (step 1.5). */
1165
+ delivery?: DeliveryMode | undefined;
1166
+ }
1167
+
1168
+ /** Every static finding for one file's bytes. */
1169
+ export function scanSvg(content: string, relPath: string, opts: ScanOptions = {}): Finding[] {
1170
+ const mode: DeliveryMode = opts.delivery ?? 'inline';
1171
+ const elements = parseElements(content);
1172
+ // ONE parse, and ONE name state. Finding 10 of the third completion
1173
+ // review, re-measured because the previous round's disposition was taken
1174
+ // on the wrong regime: over the seven small real fixtures the duplicate
1175
+ // parse is 0.03% of the dispatch, which is why it was accepted then — but
1176
+ // on a 760 KB file `parseDocument` is 8.2 ms against a 163 ms `scanSvg`,
1177
+ // so two redundant parses plus a recomputed name state are about 10% of a
1178
+ // pass that now runs on every `.svg` write. The document and the state are
1179
+ // passed rather than cached: a module-level cache in a hook is shared
1180
+ // mutable state, and the only thing that needed fixing was recomputation
1181
+ // inside one call.
1182
+ const doc = parseDocument(content);
1183
+ const nameState = rootNameState(content, doc, elements);
1184
+ // Finding 2.3: the content-level rules read the SAME file the element
1185
+ // parser reads. `maskComments` keeps every offset, so a line number taken
1186
+ // here still names the right line in the raw text.
1187
+ const text = maskComments(content);
1188
+ const raw: Finding[] = [
1189
+ ...ruleWellFormed(doc, relPath),
1190
+ // No `text` argument: the rule reads elements only, and comment
1191
+ // skipping already happened inside `parseElements`. Passing the masked
1192
+ // text told a reader the parameter was load-bearing (finding 12);
1193
+ // `no-unused-vars` could not catch it because two later parameters were
1194
+ // used.
1195
+ ...ruleDegenerateGradient(elements, relPath),
1196
+ ...ruleMissingXmlns(elements, relPath),
1197
+ ...ruleOutsideViewBox(elements, relPath),
1198
+ ...ruleUnparseablePath(elements, relPath),
1199
+ ...ruleFilledCounter(elements, relPath),
1200
+ ...ruleUnusedDefs(text, elements, relPath),
1201
+ ...ruleDanglingReference(text, elements, relPath),
1202
+ ...ruleActiveContent(text, elements, relPath, mode),
1203
+ ...ruleInternalSubset(text, relPath),
1204
+ ...ruleExternalReference(text, elements, relPath, mode),
1205
+ ...ruleEmbeddedRaster(elements, relPath),
1206
+ ...ruleGenericId(elements, relPath),
1207
+ ...ruleAccessibleName(nameState, relPath),
1208
+ ...ruleDoubleName(nameState, relPath),
1209
+ ...ruleKeySplinesRange(elements, relPath),
1210
+ ...ruleTransformBox(text, elements, relPath),
1211
+ ...ruleNoReducedMotion(text, elements, relPath),
1212
+ ...ruleSmilCssOnlyBranch(text, relPath),
1213
+ ...ruleDMorphMismatch(elements, relPath),
1214
+ ];
1215
+ // RAW, not masked — the inline `svg-asset-disable` markers live in comments.
1216
+ const ignores = parseIgnores(content.split('\n'));
1217
+ return dedupe(raw).filter((f) => !isIgnored(ignores, f));
1218
+ }
1219
+
1220
+ /**
1221
+ * Inline SVG inside a host HTML page.
1222
+ *
1223
+ * Each `<svg>…</svg>` is scanned on its own so a per-element rule keeps its
1224
+ * own line numbers, and the id rules then run once ACROSS the graphics —
1225
+ * which is the whole of R7: two graphics are each individually fine and
1226
+ * collide only together.
1227
+ */
1228
+ /**
1229
+ * The TOP-LEVEL `<svg>` elements of a host document, by offset.
1230
+ *
1231
+ * Depth-counted, not matched. Finding 3 of the fifth completion review: the
1232
+ * non-greedy `/<svg\b[\s\S]*?<\/svg\s*>/` ended at the FIRST `</svg>`, which
1233
+ * on a graphic containing a nested `<svg>` — legal, and how a sprite or a
1234
+ * symbol is embedded — is the inner one. The slice was then a truncated
1235
+ * fragment, so the host reported a FALSE `svg-structure-not-well-formed` for
1236
+ * an unclosed root AND missed everything after the inner close, including a
1237
+ * real `<script>` that the same bytes report correctly when scanned flat. A
1238
+ * false structure error and a missed active-content error, from one regex.
1239
+ *
1240
+ * A nested graphic is part of its parent and is not returned separately: the
1241
+ * rules read the whole thing, exactly as a browser does.
1242
+ */
1243
+ /**
1244
+ * Blank the BODY of every `<script>` and `<style>`, offsets and newlines kept.
1245
+ *
1246
+ * Finding 1 of the review of this branch, and it is a regression this branch
1247
+ * introduced: emitting an unterminated graphic made `topLevelGraphics` count
1248
+ * `<svg …>` wherever it appears — including inside a JavaScript string, where
1249
+ * it is text rather than markup. A host containing NO SVG element at all, but
1250
+ * mentioning one in a script, went from zero findings to five, four of them in
1251
+ * the blocking families the static witness counts. Fail-CLOSED rather than
1252
+ * fail-open, so it refuses a valid document instead of passing a bad one, but
1253
+ * a gate that refuses a correct turn is the failure this change has spent six
1254
+ * rounds removing.
1255
+ *
1256
+ * The tags survive; only what is between them is blanked. `ruleActiveContent`
1257
+ * keys on the element NAME, so a real `<script>` inside a graphic is still
1258
+ * found — masking its body hides nothing the rules read.
1259
+ */
1260
+ function maskScriptBodies(content: string): string {
1261
+ const out = content.split('');
1262
+ const re = /<(script|style)\b[^>]*>/gi;
1263
+ let m: RegExpExecArray | null;
1264
+ while ((m = re.exec(content)) !== null) {
1265
+ const bodyStart = m.index + m[0].length;
1266
+ const close = content.toLowerCase().indexOf(`</${(m[1] as string).toLowerCase()}`, bodyStart);
1267
+ const bodyEnd = close === -1 ? content.length : close;
1268
+ for (let i = bodyStart; i < bodyEnd; i += 1) {
1269
+ if (out[i] !== '\n' && out[i] !== '\r') out[i] = ' ';
1270
+ }
1271
+ re.lastIndex = bodyEnd;
1272
+ }
1273
+ return out.join('');
1274
+ }
1275
+
1276
+ function topLevelGraphics(text: string): Array<{ start: number; end: number }> {
1277
+ const out: Array<{ start: number; end: number }> = [];
1278
+ const tagRe = /<(\/?)svg\b[^>]*?(\/?)>/gi;
1279
+ let depth = 0;
1280
+ let start = -1;
1281
+ let t: RegExpExecArray | null;
1282
+ while ((t = tagRe.exec(text)) !== null) {
1283
+ const closing = t[1] === '/';
1284
+ const selfClosing = t[2] === '/';
1285
+ if (closing) {
1286
+ if (depth === 0) continue;
1287
+ depth -= 1;
1288
+ if (depth === 0 && start !== -1) {
1289
+ out.push({ start, end: t.index + t[0].length });
1290
+ start = -1;
1291
+ }
1292
+ continue;
1293
+ }
1294
+ if (selfClosing) {
1295
+ // An empty graphic at top level is still a graphic.
1296
+ if (depth === 0) out.push({ start: t.index, end: t.index + t[0].length });
1297
+ continue;
1298
+ }
1299
+ if (depth === 0) start = t.index;
1300
+ depth += 1;
1301
+ }
1302
+ // AN UNTERMINATED GRAPHIC IS STILL A GRAPHIC, to end of input.
1303
+ //
1304
+ // Finding 1 of the sixth completion review, and it FAILS OPEN, which is
1305
+ // why it is the one that matters: a host whose inline `<svg>` is never
1306
+ // closed was dropped whole, so `scanHostHtml` returned zero findings — a
1307
+ // planted `<script>` included — `staticWitness` then reported `errors: 0`,
1308
+ // and that is exactly the condition `dischargeFor` uses to discharge the
1309
+ // BLOCKING turn-end obligation on the static-only path. A malformed file
1310
+ // discharged a gate that a well-formed one with the same script does not.
1311
+ //
1312
+ // NOT a regression of the depth counter that replaced the regex: the old
1313
+ // `/<svg\b[\s\S]*?<\/svg\s*>/` required a closing tag and matched nothing
1314
+ // here either. The rewrite preserved the hole rather than opening it, and
1315
+ // the honest version of that sentence is worth more than the flattering
1316
+ // one. Emitting the fragment hands `scanSvg` a document whose root is
1317
+ // never closed — it reports that AND everything inside it, which is both
1318
+ // answers the reader needs.
1319
+ if (depth > 0 && start !== -1) out.push({ start, end: text.length });
1320
+ return out;
1321
+ }
1322
+
1323
+ export function scanHostHtml(content: string, relPath: string, opts: ScanOptions = {}): Finding[] {
1324
+ const mode: DeliveryMode = opts.delivery ?? 'inline';
1325
+ const findings: Finding[] = [];
1326
+ const lineOf = lineIndexer(content);
1327
+ const graphics: Array<{ body: string; offset: number }> = [];
1328
+ // Finding 2.3 on the host side: a graphic inside an HTML comment is not on
1329
+ // the page, so it is not scanned and its ids do not collide with anything.
1330
+ // Masking preserves offsets, so `lineOf` — built from the raw text — still
1331
+ // answers correctly for the graphics that survive.
1332
+ const hostText = maskScriptBodies(maskComments(content));
1333
+ for (const m of topLevelGraphics(hostText)) {
1334
+ // The MATCH is found in the masked text — a graphic inside an HTML
1335
+ // comment is not on the page — but the BODY is sliced from the raw
1336
+ // text at the same offsets.
1337
+ //
1338
+ // Finding 16 of this change's completion review: handing the masked
1339
+ // slice to `scanSvg` meant its own `parseIgnores` saw every
1340
+ // `svg-asset-disable` marker blanked, which is precisely the failure
1341
+ // `maskComments`' doc warns about. Suppression survived only because
1342
+ // this function re-filters on absolute lines at the end — the inner
1343
+ // pass was inert, and silently so, which made the outer re-filter
1344
+ // load-bearing without anything saying it was.
1345
+ graphics.push({ body: content.slice(m.start, m.end), offset: m.start });
1346
+ }
1347
+
1348
+ for (const g of graphics) {
1349
+ const baseLine = lineOf(g.offset) - 1;
1350
+ // Two families are dropped for an inline graphic, each for a reason
1351
+ // the slice itself cannot see:
1352
+ // · the host supplies the namespace, so a missing xmlns is correct;
1353
+ // · the reduced-motion ROUTE may live in the host's own <style> or
1354
+ // in a <picture> wrapper, and this slice ends at </svg>. Judging
1355
+ // the route from inside the slice would red every correctly
1356
+ // covered inline animation. DQ4 owns the inline case (step 1.3)
1357
+ // and reads the whole document, which is where that answer is.
1358
+ for (const f of scanSvg(g.body, relPath, { delivery: mode })) {
1359
+ if (f.rule === 'svg-structure-missing-xmlns') continue;
1360
+ if (f.rule === 'svg-motion-no-reduced-motion') continue;
1361
+ if (f.rule === 'svg-motion-smil-css-only') continue;
1362
+ findings.push({ ...f, line: f.line + baseLine });
1363
+ }
1364
+ }
1365
+
1366
+ // Cross-graphic id collisions — the finding R7 named.
1367
+ const seen = new Map<string, number>();
1368
+ for (const g of graphics) {
1369
+ const baseLine = lineOf(g.offset) - 1;
1370
+ for (const el of parseElements(g.body)) {
1371
+ if (el.closing) continue;
1372
+ const id = el.attrs['id'];
1373
+ if (!id) continue;
1374
+ const key = id.toLowerCase();
1375
+ if (seen.has(key)) {
1376
+ findings.push({
1377
+ // ITS OWN RULE ID. Finding 4 of the fifth completion
1378
+ // review: this error shared `svg-a11y-generic-id` with the
1379
+ // single-graphic WARNING about a generic name, so one
1380
+ // `svg-asset-disable svg-a11y-generic-id` — a reasonable
1381
+ // thing to write when you have deliberately named
1382
+ // something `icon` — silenced a real collision between two
1383
+ // graphics as well. Two findings, two severities, two
1384
+ // remedies; one id was one too few.
1385
+ rule: 'svg-a11y-duplicate-id',
1386
+ file: relPath,
1387
+ line: el.line + baseLine,
1388
+ snippet:
1389
+ `id="${id}" is already used by another inline graphic on this page (line ${seen.get(key)}) — ` +
1390
+ 'the accessibility tree names both after the first',
1391
+ severity: 'error',
1392
+ });
1393
+ } else {
1394
+ seen.set(key, el.line + baseLine);
1395
+ }
1396
+ }
1397
+ }
1398
+
1399
+ const ignores = parseIgnores(content.split('\n'));
1400
+ return dedupe(findings).filter((f) => !isIgnored(ignores, f));
1401
+ }
1402
+
1403
+ // ---------------------------------------------------------------------------
1404
+
1405
+ // ---------------------------------------------------------------------------
1406
+ // Inline-ignore + config
1407
+ // ---------------------------------------------------------------------------
1408
+
1409
+ /**
1410
+ * Which lines a rule is suppressed on, and whether the whole file is.
1411
+ *
1412
+ * Finding 6 of the fifth completion review: "whole file" used to be spelled as
1413
+ * the numbers 1..10_000 in a Set — so a directive cost ten thousand entries on
1414
+ * a path that now runs on every `.svg` write, AND a file longer than ten
1415
+ * thousand lines silently stopped being suppressed past that line. A flag says
1416
+ * the same thing in one bit and has no horizon.
1417
+ */
1418
+ export interface RuleIgnore {
1419
+ lines: Set<number>;
1420
+ wholeFile: boolean;
1421
+ }
1422
+
1423
+ export function parseIgnores(lines: string[]): Map<string, RuleIgnore> {
1424
+ const ignores = new Map<string, RuleIgnore>();
1425
+ const slot = (rule: string): RuleIgnore => {
1426
+ const found = ignores.get(rule) ?? { lines: new Set<number>(), wholeFile: false };
1427
+ ignores.set(rule, found);
1428
+ return found;
1429
+ };
1430
+ const add = (rule: string, line: number): void => {
1431
+ slot(rule).lines.add(line);
1432
+ };
1433
+ for (let i = 0; i < lines.length; i++) {
1434
+ const line = lines[i] ?? '';
1435
+ const next = /svg-asset-disable-next-line\s+([\w-]+)/i.exec(line);
1436
+ if (next?.[1]) add(next[1].toLowerCase(), i + 2);
1437
+ const same = /svg-asset-disable-line\s+([\w-]+)/i.exec(line);
1438
+ if (same?.[1]) add(same[1].toLowerCase(), i + 1);
1439
+ const whole = /svg-asset-disable\s+([\w-]+)/i.exec(line);
1440
+ if (whole?.[1] && !/-next-line|-line\s/.test(line)) {
1441
+ slot(whole[1].toLowerCase()).wholeFile = true;
1442
+ }
1443
+ }
1444
+ return ignores;
1445
+ }
1446
+
1447
+ function isIgnored(ignores: Map<string, RuleIgnore>, finding: Finding): boolean {
1448
+ // The safety family is not suppressible from inside the file it judges: the
1449
+ // markers are the scanned file's own bytes, and a file that silences its
1450
+ // own script zeroes the error count a blocking gate discharges on.
1451
+ if (finding.rule.startsWith('svg-safety-')) return false;
1452
+ const hit = ignores.get(finding.rule.toLowerCase());
1453
+ if (hit === undefined) return false;
1454
+ return hit.wholeFile || hit.lines.has(finding.line);
1455
+ }