@adia-ai/adia-ui-forge 0.1.2

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 (136) hide show
  1. package/.claude-plugin/plugin.json +23 -0
  2. package/CHANGELOG.md +26 -0
  3. package/README.md +54 -0
  4. package/bin/forge-lint +263 -0
  5. package/bin/lib/audit-axes.mjs +555 -0
  6. package/bin/lib/dry-run-irreversible.mjs +236 -0
  7. package/bin/lib/run-skill-evals.mjs +487 -0
  8. package/bin/lib/teach-router.mjs +250 -0
  9. package/commands/adia-forge-a2ui.md +10 -0
  10. package/commands/adia-forge-author.md +10 -0
  11. package/commands/adia-forge-dogfood.md +8 -0
  12. package/commands/adia-forge-llm.md +8 -0
  13. package/commands/adia-forge-orient.md +10 -0
  14. package/commands/adia-forge-release.md +8 -0
  15. package/commands/adia-forge-review.md +10 -0
  16. package/hooks/hooks.json +15 -0
  17. package/package.json +41 -0
  18. package/references/shared/content-trust.md +76 -0
  19. package/references/shared/pev-rationale.md +64 -0
  20. package/references/shared/skill-conventions.md +133 -0
  21. package/skills/adia-ui-a2ui/CHANGELOG.md +32 -0
  22. package/skills/adia-ui-a2ui/SKILL.md +243 -0
  23. package/skills/adia-ui-a2ui/evals/adversarial-corpus.json +75 -0
  24. package/skills/adia-ui-a2ui/evals/routing-corpus.json +31 -0
  25. package/skills/adia-ui-a2ui/evals/teach-routing-cases.json +100 -0
  26. package/skills/adia-ui-a2ui/references/anti-patterns.md +24 -0
  27. package/skills/adia-ui-a2ui/references/chunk-authoring.md +88 -0
  28. package/skills/adia-ui-a2ui/references/corpus-discipline.md +56 -0
  29. package/skills/adia-ui-a2ui/references/eval-diagnostics.md +127 -0
  30. package/skills/adia-ui-a2ui/references/fragment-graph.md +91 -0
  31. package/skills/adia-ui-a2ui/references/mcp-pipeline-ops.md +106 -0
  32. package/skills/adia-ui-a2ui/references/mcp-tool-reference.md +398 -0
  33. package/skills/adia-ui-a2ui/references/pipeline-overview.md +175 -0
  34. package/skills/adia-ui-a2ui/references/semantic-fail-lifting.md +120 -0
  35. package/skills/adia-ui-a2ui/references/strategy-engines.md +111 -0
  36. package/skills/adia-ui-a2ui/references/teach-protocol.md +220 -0
  37. package/skills/adia-ui-a2ui/references/zettel-calibration.md +93 -0
  38. package/skills/adia-ui-a2ui/scripts/audit-a2ui-roster.mjs +96 -0
  39. package/skills/adia-ui-a2ui/scripts/teach-route.mjs +157 -0
  40. package/skills/adia-ui-a2ui/skill.json +38 -0
  41. package/skills/adia-ui-authoring/CHANGELOG.md +32 -0
  42. package/skills/adia-ui-authoring/SKILL.md +256 -0
  43. package/skills/adia-ui-authoring/assets/case-studies/admin-shell-decomposition.md +101 -0
  44. package/skills/adia-ui-authoring/assets/case-studies/maxtokens-32768-discovery.md +109 -0
  45. package/skills/adia-ui-authoring/assets/case-studies/theme-panel-promotion.md +113 -0
  46. package/skills/adia-ui-authoring/evals/adversarial-design-plan-gates.json +138 -0
  47. package/skills/adia-ui-authoring/evals/routing-corpus.json +245 -0
  48. package/skills/adia-ui-authoring/references/anti-patterns.md +606 -0
  49. package/skills/adia-ui-authoring/references/api-contract.md +205 -0
  50. package/skills/adia-ui-authoring/references/authoring-cycle.md +211 -0
  51. package/skills/adia-ui-authoring/references/canonical-pattern-index.md +179 -0
  52. package/skills/adia-ui-authoring/references/code-style.md +329 -0
  53. package/skills/adia-ui-authoring/references/common-gotchas.md +93 -0
  54. package/skills/adia-ui-authoring/references/composite-demo-protocol.md +1084 -0
  55. package/skills/adia-ui-authoring/references/css-patterns.md +364 -0
  56. package/skills/adia-ui-authoring/references/lifecycle-patterns.md +302 -0
  57. package/skills/adia-ui-authoring/references/llm-bridge.md +254 -0
  58. package/skills/adia-ui-authoring/references/module-promotion.md +289 -0
  59. package/skills/adia-ui-authoring/references/primitive-audit.md +123 -0
  60. package/skills/adia-ui-authoring/references/shell-patterns.md +561 -0
  61. package/skills/adia-ui-authoring/references/teach-protocol.md +428 -0
  62. package/skills/adia-ui-authoring/references/token-contract.md +120 -0
  63. package/skills/adia-ui-authoring/references/worked-example.md +351 -0
  64. package/skills/adia-ui-authoring/references/yaml-contract.md +224 -0
  65. package/skills/adia-ui-authoring/scripts/audit-authoring-roster.mjs +148 -0
  66. package/skills/adia-ui-authoring/scripts/build-canonical-pattern-index.mjs +199 -0
  67. package/skills/adia-ui-authoring/skill.json +45 -0
  68. package/skills/adia-ui-dogfood/CHANGELOG.md +17 -0
  69. package/skills/adia-ui-dogfood/README.md +62 -0
  70. package/skills/adia-ui-dogfood/SKILL.md +866 -0
  71. package/skills/adia-ui-dogfood/scripts/analyze.mjs +603 -0
  72. package/skills/adia-ui-dogfood/skill.json +40 -0
  73. package/skills/adia-ui-forge/SKILL.md +88 -0
  74. package/skills/adia-ui-forge/evals/routing-corpus.json +30 -0
  75. package/skills/adia-ui-gen-review/CHANGELOG.md +108 -0
  76. package/skills/adia-ui-gen-review/SKILL.md +266 -0
  77. package/skills/adia-ui-gen-review/references/loop-protocol.md +712 -0
  78. package/skills/adia-ui-gen-review/references/rubric-cosmetic.md +144 -0
  79. package/skills/adia-ui-gen-review/references/rubric-decompose.md +117 -0
  80. package/skills/adia-ui-gen-review/references/rubric-score.md +249 -0
  81. package/skills/adia-ui-gen-review/references/scores.schema.json +125 -0
  82. package/skills/adia-ui-gen-review/references/teach-protocol.md +214 -0
  83. package/skills/adia-ui-gen-review/scripts/gen-review-coverage-audit.mjs +127 -0
  84. package/skills/adia-ui-gen-review/scripts/gen-review-decompose.mjs +569 -0
  85. package/skills/adia-ui-gen-review/scripts/gen-review-status.mjs +176 -0
  86. package/skills/adia-ui-gen-review/scripts/validate-cycle-scores.mjs +198 -0
  87. package/skills/adia-ui-gen-review/skill.json +22 -0
  88. package/skills/adia-ui-llm/CHANGELOG.md +25 -0
  89. package/skills/adia-ui-llm/SKILL.md +165 -0
  90. package/skills/adia-ui-llm/evals/adversarial-corpus.json +75 -0
  91. package/skills/adia-ui-llm/evals/routing-corpus.json +30 -0
  92. package/skills/adia-ui-llm/evals/teach-routing-cases.json +73 -0
  93. package/skills/adia-ui-llm/references/adapter-contract.md +99 -0
  94. package/skills/adia-ui-llm/references/add-a-provider.md +90 -0
  95. package/skills/adia-ui-llm/references/bridge-facade.md +94 -0
  96. package/skills/adia-ui-llm/references/browser-proxy-boundary.md +89 -0
  97. package/skills/adia-ui-llm/references/model-registry.md +75 -0
  98. package/skills/adia-ui-llm/references/streaming-sse.md +95 -0
  99. package/skills/adia-ui-llm/references/teach-protocol.md +78 -0
  100. package/skills/adia-ui-llm/scripts/audit-llm-roster.mjs +93 -0
  101. package/skills/adia-ui-llm/scripts/teach-route.mjs +119 -0
  102. package/skills/adia-ui-llm/skill.json +33 -0
  103. package/skills/adia-ui-release/CHANGELOG.md +23 -0
  104. package/skills/adia-ui-release/SKILL.md +295 -0
  105. package/skills/adia-ui-release/assets/case-studies/2026-05-20-batch-push-v0.6.14-v0.6.15.md +144 -0
  106. package/skills/adia-ui-release/assets/case-studies/2026-05-20-corpus-drift-remediation-v0.6.15.md +155 -0
  107. package/skills/adia-ui-release/assets/case-studies/2026-05-20-version-skip-correction-v0.6.12.md +114 -0
  108. package/skills/adia-ui-release/assets/case-studies/2026-05-21-author-from-scratch-v0.6.18.md +139 -0
  109. package/skills/adia-ui-release/assets/case-studies/2026-05-21-feedback37-retraction-v0.6.21.md +124 -0
  110. package/skills/adia-ui-release/assets/case-studies/2026-05-21-fn1-enrichment-pass-v0.6.19.md +125 -0
  111. package/skills/adia-ui-release/assets/case-studies/2026-05-21-stale-test-detection-v0.6.20.md +142 -0
  112. package/skills/adia-ui-release/assets/case-studies/2026-05-23-freshness-gate-recovery-v0.6.32.md +97 -0
  113. package/skills/adia-ui-release/assets/case-studies/2026-05-26-catalog-drift-recurring-v0.6.40.md +147 -0
  114. package/skills/adia-ui-release/assets/templates/stub-changelog.template.md +22 -0
  115. package/skills/adia-ui-release/evals/evals.json +164 -0
  116. package/skills/adia-ui-release/references/changelog-discipline.md +250 -0
  117. package/skills/adia-ui-release/references/cycle-happy-path.md +520 -0
  118. package/skills/adia-ui-release/references/exe-deploy.md +149 -0
  119. package/skills/adia-ui-release/references/gates-catalog.md +778 -0
  120. package/skills/adia-ui-release/references/ledger-discipline.md +232 -0
  121. package/skills/adia-ui-release/references/migration-guide-authoring.md +174 -0
  122. package/skills/adia-ui-release/references/multi-agent-baseline.md +207 -0
  123. package/skills/adia-ui-release/references/notes-authoring.md +212 -0
  124. package/skills/adia-ui-release/references/recovery-paths.md +215 -0
  125. package/skills/adia-ui-release/references/rollup-notes.md +208 -0
  126. package/skills/adia-ui-release/references/teach-protocol.md +468 -0
  127. package/skills/adia-ui-release/scripts/assert-monorepo-root.mjs +49 -0
  128. package/skills/adia-ui-release/scripts/audit-gate-roster.mjs +196 -0
  129. package/skills/adia-ui-release/scripts/bump.mjs +118 -0
  130. package/skills/adia-ui-release/scripts/dispatch-publish.mjs +151 -0
  131. package/skills/adia-ui-release/scripts/insert-stub.mjs +127 -0
  132. package/skills/adia-ui-release/scripts/make-ledger.mjs +179 -0
  133. package/skills/adia-ui-release/scripts/promote-unreleased.mjs +88 -0
  134. package/skills/adia-ui-release/scripts/release-pack.mjs +350 -0
  135. package/skills/adia-ui-release/scripts/tag-lockstep.mjs +110 -0
  136. package/skills/adia-ui-release/skill.json +75 -0
@@ -0,0 +1,214 @@
1
+ ---
2
+ name: teach-protocol
3
+ description: >
4
+ §Teach invocation protocol for adia-ui-gen-review. 8-branch decision tree
5
+ routing new evidence to the correct file. Invoked via Mode 5 of
6
+ ColdStartTriage when the operator says "make sure gen-review knows about X"
7
+ or "train gen-review on Y".
8
+ ---
9
+
10
+ # §Teach Protocol — adia-ui-gen-review
11
+
12
+ **Invoked from**: SKILL.md §ColdStartTriage Mode 5 **Trigger phrases**: "make sure gen-review knows about X", "train gen-review on Y", "absorb this into gen-review", "update gen-review with the new primitive"
13
+
14
+ This is a standalone skill. §Teach here covers the 8 evidence types that arise from running review cycles, not a full extensibility harness.
15
+
16
+ ---
17
+
18
+ ## §WhenToInvoke
19
+
20
+ Invoke when evidence arrives from:
21
+
22
+ - A new AdiaUI primitive shipped to the monorepo's `packages/web-components/components/`
23
+ - A cycle reveals a new failure pattern not covered by the 9 existing cause codes
24
+ - Human QA consistently disagrees with mechanical scores (threshold drift)
25
+ - A native HTML element appears in canvas output that isn't yet tracked
26
+ - A new DOM attribute needs to pass the allowlist to the decomposed file
27
+ - A new cosmetic anti-pattern keeps appearing that the rubric doesn't name
28
+ - The loop protocol needs a new phase or conditional guard
29
+ - A cycle output field needs to be formally tracked in scores.schema.json
30
+
31
+ Do NOT invoke §Teach for:
32
+
33
+ - Fixing a bug in the loop protocol execution — that's an edit task, not a teach
34
+ - Re-running coverage audit after adding entries — that's verify, not teach
35
+ - Any change to the canvas or Gen UI pipeline itself — route to `adia-ui-a2ui`
36
+
37
+ ---
38
+
39
+ ## §DecisionTree
40
+
41
+ ### Branch 1 — New AdiaUI primitive shipped
42
+
43
+ Evidence: a new `-ui` tag appears in the monorepo's `packages/web-components/components/` that is missing from `TAG_TO_COMPONENT` in `gen-review-decompose.mjs`.
44
+
45
+ Detection (run from the monorepo root):
46
+
47
+ ```bash
48
+ node ${CLAUDE_PLUGIN_ROOT}/skills/adia-ui-gen-review/scripts/gen-review-coverage-audit.mjs
49
+ ```
50
+
51
+ Action:
52
+
53
+ 1. Find the component's display name in its `.yaml` `name:` field.
54
+ 2. Add to `TAG_TO_COMPONENT` in `gen-review-decompose.mjs`:
55
+
56
+ ```js
57
+ 'new-primitive-ui': 'NewPrimitive',
58
+ ```
59
+
60
+ 3. If it's a slotted container (card-like, drawer-like), also update `rubric-decompose.md` slot-vocabulary section.
61
+
62
+ Verify: `node ${CLAUDE_PLUGIN_ROOT}/skills/adia-ui-gen-review/scripts/gen-review-coverage-audit.mjs --strict` exits 0.
63
+
64
+ ---
65
+
66
+ ### Branch 2 — New native element appearing in canvas
67
+
68
+ Evidence: a cycle decomposition shows a native HTML element (e.g. `<details>`, `<dl>`, `<blockquote>`) in canvas output that should be tracked as a quality signal.
69
+
70
+ Action:
71
+
72
+ 1. Add to `TAG_TO_COMPONENT` in `gen-review-decompose.mjs`:
73
+
74
+ ```js
75
+ 'details': 'NativeDetails',
76
+ ```
77
+
78
+ Use the `Native` prefix to distinguish from AdiaUI primitives.
79
+
80
+ 2. Add a note to `rubric-decompose.md` in the native-element quality-signal section explaining what this element's presence indicates.
81
+
82
+ Verify: re-run decompose on a prompt that uses the element; confirm it appears in `decomposed.components[]` with the `Native*` name.
83
+
84
+ ---
85
+
86
+ ### Branch 3 — New allowlisted attribute
87
+
88
+ Evidence: a cycle reveals that a semantically useful attribute is being stripped from the decomposed file by the allowlist (e.g., a new `columns=` equivalent attr added to a grid primitive).
89
+
90
+ Action:
91
+
92
+ 1. Add the attribute name to `ATTR_ALLOWLIST` in `gen-review-decompose.mjs`:
93
+
94
+ ```js
95
+ const ATTR_ALLOWLIST = new Set([
96
+ ..., 'new-attr',
97
+ ]);
98
+ ```
99
+
100
+ 2. Confirm the attribute carries no user-controlled content that could be adversarial (strings typed by a user into the canvas). Static enum values (variant, size, gap) are safe; freeform text should stay blocked.
101
+
102
+ Verify: re-run decompose on the affected prompt; confirm the attribute appears in `decomposed.attrs{}`.
103
+
104
+ ---
105
+
106
+ ### Branch 4 — New root-cause code
107
+
108
+ Evidence: a recurring failure pattern across 2+ cycles that doesn't fit any of the 9 existing codes (WRONG_CHUNK, EMPTY_CHUNK, WRONG_COMPONENT, MISSING_PROPS, WRONG_NESTING, TRANSPILER_GAP, RETRIEVAL_SCORE, FREE_FORM_HALLUC, COSMETIC_ONLY).
109
+
110
+ Action:
111
+
112
+ 1. Add a new row to `rubric-score.md` §Root-Cause Classification.
113
+ 2. Add the new code to the `rootCauses[].code` enum in `scores.schema.json`.
114
+ 3. Bump `schemaVersion` in `scores.schema.json` (patch bump: `"2.0.1"` → `"2.0.2"`).
115
+ 4. Update CHANGELOG.md.
116
+
117
+ Verify: the schema file parses as JSON; confirm the new code appears in the enum array.
118
+
119
+ ---
120
+
121
+ ### Branch 5 — Threshold recalibration
122
+
123
+ Evidence: human QA gate results consistently contradict the mechanical score (e.g., QA passes 4/5 prompts but rubric-score marks them Failing), sustained across 2+ cycles.
124
+
125
+ Action:
126
+
127
+ 1. Edit `rubric-score.md` §Thresholds — adjust the Excellence/Acceptable/Failing boundary numbers.
128
+ 2. Update the `scores.schema.json` thresholds comment (if any) to match.
129
+ 3. Update SKILL.md §ExitCondition if the Excellence number changes (it is the exit gate).
130
+ 4. Note the calibration in CHANGELOG.md with the cycle numbers that motivated it.
131
+
132
+ Verify: re-score the 5 QA prompts from that cycle against the new thresholds; confirm they now agree with human QA judgement.
133
+
134
+ ---
135
+
136
+ ### Branch 6 — New rubric dimension
137
+
138
+ Evidence: a structural quality axis that cycles consistently expose but that D1–D6 don't capture (e.g., a new "Responsive breakpoint fidelity" dimension).
139
+
140
+ Action:
141
+
142
+ 1. Add a `D7+` section to `rubric-score.md` §Dimensions.
143
+ 2. Update the max-score note (e.g., D7 at 0–15 = max 120).
144
+ 3. Update `rubric-score.md` §Thresholds to reflect the new max.
145
+ 4. Add `d7` (or whatever the dimension key is) to the `rubricScore` object in `scores.schema.json`.
146
+ 5. Bump `schemaVersion` (minor bump: `"2.0.1"` → `"2.1.0"` for schema shape change).
147
+ 6. Update SKILL.md §ExitCondition if the Excellence threshold number changes.
148
+ 7. Update CHANGELOG.md.
149
+
150
+ Verify: the schema parses; loop-protocol.md §Phase 3 names the new dimension; rubric-decompose.md is updated if Phase 2 must produce new data the dimension consumes.
151
+
152
+ ---
153
+
154
+ ### Branch 7 — New cosmetic pattern
155
+
156
+ Evidence: a visual anti-pattern that recurs across prompts and isn't covered by COS-1 through COS-6 in `rubric-cosmetic.md`.
157
+
158
+ Action:
159
+
160
+ 1. If the pattern fits within an existing COS-N's scope, add it as a sub-item.
161
+ 2. If it's a distinct axis, add `COS-7+` as a new section.
162
+ 3. Assign severity (P1/P2/P3) using the existing rubric criteria:
163
+ - P1: blocks task completion or signals wrong primitive
164
+ - P2: visually broken but navigable
165
+ - P3: polish only
166
+
167
+ Verify: run Phase 4 on a prompt that exhibits the pattern; confirm the scoring rubric now names it and assigns the right severity.
168
+
169
+ ---
170
+
171
+ ### Branch 8 — New phase or phase-conditional logic
172
+
173
+ Evidence: the loop protocol needs a new phase (e.g., a "Phase 0 — prerequisite check" step), or an existing phase needs a new conditional guard.
174
+
175
+ Action:
176
+
177
+ 1. Edit `loop-protocol.md` — add or update the affected §Phase section.
178
+ 2. If the change adds new artifacts to the cycle output, update §DataModel in SKILL.md and the `files:` structure listed there.
179
+ 3. If Phase 5 routing changes (new peer skill invoked), update §SkillDelegation in SKILL.md.
180
+ 4. Update SKILL.md §SelfAudit if the check list needs updating.
181
+ 5. Update CHANGELOG.md.
182
+
183
+ Verify: trace through the full loop-protocol.md from §Setup through §Cycle Close to confirm the new logic is coherent with all phase transitions.
184
+
185
+ ---
186
+
187
+ ## §VerifyAfterAnyBranch
188
+
189
+ After ANY branch completes, run SelfAudit checks:
190
+
191
+ ```bash
192
+ # Check 1: skill.json files: list matches disk
193
+ node -e "
194
+ const root = process.env.CLAUDE_PLUGIN_ROOT + '/skills/adia-ui-gen-review';
195
+ const fs = require('fs');
196
+ const s = JSON.parse(fs.readFileSync(root + '/skill.json','utf8'));
197
+ const missing = s.files.filter(f => !fs.existsSync(root + '/' + f));
198
+ if (missing.length) { console.error('MISSING:', missing); process.exit(1); }
199
+ console.log('OK — all', s.files.length, 'files present');
200
+ "
201
+
202
+ # Check 5: TAG_TO_COMPONENT coverage (run from the monorepo root)
203
+ node ${CLAUDE_PLUGIN_ROOT}/skills/adia-ui-gen-review/scripts/gen-review-coverage-audit.mjs --strict
204
+ ```
205
+
206
+ ---
207
+
208
+ ## §AntiPatterns
209
+
210
+ - ✗ Adding new knowledge directly to SKILL.md prose — SKILL.md is cold-start weight only; it should cite file destinations, not duplicate content
211
+ - ✗ Updating a rubric threshold without also updating SKILL.md §ExitCondition — the exit gate and the threshold must agree
212
+ - ✗ Adding a TAG_TO_COMPONENT entry using a display name that doesn't match the component's `name:` field in its yaml
213
+ - ✗ Adding a new root-cause code without bumping `schemaVersion` — downstream tools validate against the schema
214
+ - ✗ Updating rubric-score.md §Dimensions without checking if rubric-decompose.md Phase 2 produces the data the new dimension consumes
@@ -0,0 +1,127 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * gen-review-coverage-audit.mjs — SelfAudit check 5 for adia-ui-gen-review.
4
+ *
5
+ * Verifies that every AdiaUI primitive tag (from component yamls) has
6
+ * an entry in gen-review-decompose.mjs TAG_TO_COMPONENT. Reports any
7
+ * gaps so the skill's primitive lookup table stays in sync with the
8
+ * substrate as new components ship.
9
+ *
10
+ * This script is skill-owned. It reads two inputs:
11
+ * - the sibling gen-review-decompose.mjs (resolved relative to this file)
12
+ * - the monorepo's packages/web-components/components/*.yaml tags
13
+ * (resolved from the working directory — run from the monorepo root)
14
+ *
15
+ * Requires `js-yaml` available on the module resolution path (the monorepo's
16
+ * node_modules). Imported as a bare specifier so it resolves wherever the
17
+ * script is run.
18
+ *
19
+ * Usage (from the monorepo root):
20
+ * node ${CLAUDE_PLUGIN_ROOT}/skills/adia-ui-gen-review/scripts/gen-review-coverage-audit.mjs # report gaps
21
+ * node ${CLAUDE_PLUGIN_ROOT}/skills/adia-ui-gen-review/scripts/gen-review-coverage-audit.mjs --strict # exit 1 if gaps exist
22
+ * node ${CLAUDE_PLUGIN_ROOT}/skills/adia-ui-gen-review/scripts/gen-review-coverage-audit.mjs --json # machine-readable output
23
+ */
24
+
25
+ import { readFileSync, readdirSync, statSync } from 'node:fs';
26
+ import { join, dirname } from 'node:path';
27
+ import { fileURLToPath } from 'node:url';
28
+ import { load as yamlLoad } from 'js-yaml';
29
+
30
+ const __dir = dirname(fileURLToPath(import.meta.url)); // the skill's scripts/ dir
31
+ const REPO_ROOT = process.cwd(); // the monorepo root
32
+
33
+ const strict = process.argv.includes('--strict');
34
+ const jsonMode = process.argv.includes('--json');
35
+
36
+ // ── 1. Extract all tag names from component yamls ────────────────────────────
37
+
38
+ const COMPONENTS_DIR = join(REPO_ROOT, 'packages/web-components/components');
39
+ const allTags = new Map(); // tag → { yamlPath, name }
40
+
41
+ function walkComponents(dir) {
42
+ for (const entry of readdirSync(dir)) {
43
+ const full = join(dir, entry);
44
+ if (statSync(full).isDirectory()) {
45
+ walkComponents(full);
46
+ } else if (entry.endsWith('.yaml') && !entry.includes('.a2ui.') && !entry.includes('schema')) {
47
+ try {
48
+ const raw = readFileSync(full, 'utf8');
49
+ const doc = yamlLoad(raw);
50
+ if (doc?.tag && typeof doc.tag === 'string' && doc.tag.endsWith('-ui')) {
51
+ allTags.set(doc.tag, { yamlPath: full.replace(REPO_ROOT + '/', ''), name: doc.name ?? doc.tag });
52
+ }
53
+ } catch { /* skip unparseable */ }
54
+ }
55
+ }
56
+ }
57
+
58
+ walkComponents(COMPONENTS_DIR);
59
+
60
+ // ── 2. Extract TAG_TO_COMPONENT from the sibling decompose script ────────────
61
+
62
+ const SCRIPT_PATH = join(__dir, 'gen-review-decompose.mjs');
63
+ const scriptSrc = readFileSync(SCRIPT_PATH, 'utf8');
64
+
65
+ // Parse the TAG_TO_COMPONENT object by extracting quoted key entries
66
+ const tagInScript = new Set();
67
+ const tagRegex = /['"]([a-z][a-z0-9-]+-ui)['"]:\s*(?:'[^']*'|null)/g;
68
+ let m;
69
+ while ((m = tagRegex.exec(scriptSrc)) !== null) {
70
+ tagInScript.add(m[1]);
71
+ }
72
+
73
+ // ── 3. Compute gaps ──────────────────────────────────────────────────────────
74
+
75
+ const missing = []; // in yaml, not in script
76
+ const extra = []; // in script, not in yaml (may be intentional like canvas-ui)
77
+
78
+ for (const [tag, meta] of allTags) {
79
+ if (!tagInScript.has(tag)) {
80
+ missing.push({ tag, ...meta });
81
+ }
82
+ }
83
+
84
+ // Script entries that have no yaml (legacy, intentional shells, or typos)
85
+ for (const scriptTag of tagInScript) {
86
+ if (!allTags.has(scriptTag)) {
87
+ extra.push(scriptTag);
88
+ }
89
+ }
90
+
91
+ // ── 4. Report ────────────────────────────────────────────────────────────────
92
+
93
+ const result = {
94
+ totalYamlTags: allTags.size,
95
+ coveredByScript: tagInScript.size,
96
+ missingCount: missing.length,
97
+ extraCount: extra.length,
98
+ missing,
99
+ extra,
100
+ status: missing.length === 0 ? 'CLEAN' : 'GAPS_FOUND',
101
+ };
102
+
103
+ if (jsonMode) {
104
+ console.log(JSON.stringify(result, null, 2));
105
+ } else {
106
+ console.log(`\n[coverage-audit] TAG_TO_COMPONENT coverage`);
107
+ console.log(` Yaml tags: ${allTags.size}`);
108
+ console.log(` In script: ${tagInScript.size}`);
109
+ console.log(` Missing: ${missing.length}`);
110
+ console.log(` Extra: ${extra.length} (intentional shells / aliases)`);
111
+
112
+ if (missing.length > 0) {
113
+ console.log(`\nMISSING from TAG_TO_COMPONENT (add to gen-review-decompose.mjs):`);
114
+ for (const { tag, name } of missing.sort((a,b) => a.tag.localeCompare(b.tag))) {
115
+ console.log(` '${tag}': '${name.replace(/^UI/, '')}',`);
116
+ }
117
+ } else {
118
+ console.log(`\n✓ All yaml tags covered in TAG_TO_COMPONENT`);
119
+ }
120
+
121
+ if (extra.length > 0) {
122
+ console.log(`\nIn script but no yaml (shells/aliases — verify intentional):`);
123
+ for (const tag of extra.sort()) console.log(` ${tag}`);
124
+ }
125
+ }
126
+
127
+ process.exit(strict && missing.length > 0 ? 1 : 0);