@adia-ai/adia-ui-forge 0.1.2 → 0.8.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 (196) hide show
  1. package/.claude-plugin/plugin.json +3 -6
  2. package/CHANGELOG.md +27 -26
  3. package/README.md +35 -40
  4. package/agents/a2ui-engineer.md +26 -0
  5. package/agents/component-author.md +29 -0
  6. package/agents/framework-verifier.md +26 -0
  7. package/agents/release-engineer.md +25 -0
  8. package/agents/routing-corpus.json +318 -0
  9. package/bin/demo-postwrite-pattern-gate +94 -0
  10. package/bin/forge-lint +17 -2
  11. package/bin/sidecar-prewrite-guard +104 -0
  12. package/commands/deploy.md +9 -0
  13. package/commands/dogfood.md +10 -0
  14. package/commands/gen-review.md +9 -0
  15. package/commands/release.md +10 -0
  16. package/hooks/hooks.json +15 -0
  17. package/package.json +3 -5
  18. package/references/contracts/a2ui-mcp-surface.md +35 -0
  19. package/references/contracts/migration-guide-format.md +34 -0
  20. package/references/shared/content-trust.md +19 -74
  21. package/skills/adia-a2ui/SKILL.md +99 -0
  22. package/skills/{adia-ui-a2ui → adia-a2ui}/evals/routing-corpus.json +10 -10
  23. package/skills/adia-a2ui/references/anti-patterns.md +27 -0
  24. package/skills/adia-a2ui/references/chunk-authoring.md +71 -0
  25. package/skills/adia-a2ui/references/corpus-discipline.md +68 -0
  26. package/skills/adia-a2ui/references/eval-diagnostics.md +86 -0
  27. package/skills/adia-a2ui/references/format-extension-decisions.md +66 -0
  28. package/skills/adia-a2ui/references/leverage-rules.md +52 -0
  29. package/skills/adia-a2ui/references/mcp-pipeline-ops.md +83 -0
  30. package/skills/adia-a2ui/references/mcp-tool-reference.md +59 -0
  31. package/skills/adia-a2ui/references/pipeline-overview.md +115 -0
  32. package/skills/adia-a2ui/references/semantic-fail-lifting.md +74 -0
  33. package/skills/adia-a2ui/references/strategy-engines.md +109 -0
  34. package/skills/adia-a2ui/references/zettel-calibration.md +104 -0
  35. package/skills/adia-author/SKILL.md +112 -0
  36. package/skills/adia-author/evals/routing-corpus.json +222 -0
  37. package/skills/{adia-ui-authoring → adia-author}/references/anti-patterns.md +33 -4
  38. package/skills/{adia-ui-authoring → adia-author}/references/authoring-cycle.md +9 -11
  39. package/skills/adia-author/references/canonical-pattern-index.md +243 -0
  40. package/skills/{adia-ui-authoring → adia-author}/references/code-style.md +19 -21
  41. package/skills/adia-author/references/common-gotchas.md +129 -0
  42. package/skills/adia-author/references/composite-demo-protocol.md +271 -0
  43. package/skills/{adia-ui-authoring → adia-author}/references/css-patterns.md +19 -6
  44. package/skills/{adia-ui-authoring → adia-author}/references/lifecycle-patterns.md +1 -0
  45. package/skills/{adia-ui-authoring → adia-author}/references/llm-bridge.md +3 -5
  46. package/skills/{adia-ui-authoring → adia-author}/references/module-promotion.md +22 -40
  47. package/skills/{adia-ui-authoring → adia-author}/references/primitive-audit.md +2 -4
  48. package/skills/{adia-ui-authoring → adia-author}/references/shell-patterns.md +12 -18
  49. package/skills/{adia-ui-authoring → adia-author}/references/token-contract.md +5 -7
  50. package/skills/{adia-ui-authoring → adia-author}/references/worked-example.md +3 -3
  51. package/skills/{adia-ui-authoring → adia-author}/references/yaml-contract.md +3 -3
  52. package/skills/{adia-ui-authoring → adia-author}/scripts/build-canonical-pattern-index.mjs +3 -3
  53. package/skills/adia-deploy/SKILL.md +128 -0
  54. package/skills/adia-deploy/references/deploy-playbooks.md +276 -0
  55. package/skills/adia-dogfood/SKILL.md +124 -0
  56. package/skills/adia-dogfood/references/admin-shell-anatomy.md +77 -0
  57. package/skills/adia-dogfood/references/app-shell-pitfalls.md +66 -0
  58. package/skills/adia-dogfood/references/card-anatomy-sweep.md +66 -0
  59. package/skills/adia-dogfood/references/html-attr-sweep.md +63 -0
  60. package/skills/adia-dogfood/references/native-leak-annotations.md +73 -0
  61. package/skills/adia-dogfood/references/visual-probe-triage.md +80 -0
  62. package/skills/{adia-ui-dogfood → adia-dogfood}/scripts/analyze.mjs +30 -14
  63. package/skills/adia-gen-review/SKILL.md +127 -0
  64. package/skills/adia-gen-review/references/corpus-html-patterns.md +279 -0
  65. package/skills/adia-gen-review/references/loop-protocol.md +216 -0
  66. package/skills/adia-gen-review/references/rubric-cosmetic.md +100 -0
  67. package/skills/adia-gen-review/references/rubric-decompose.md +101 -0
  68. package/skills/adia-gen-review/references/rubric-score.md +214 -0
  69. package/skills/{adia-ui-gen-review → adia-gen-review}/references/scores.schema.json +2 -2
  70. package/skills/{adia-ui-gen-review → adia-gen-review}/scripts/gen-review-coverage-audit.mjs +4 -4
  71. package/skills/{adia-ui-gen-review → adia-gen-review}/scripts/gen-review-decompose.mjs +10 -6
  72. package/skills/{adia-ui-gen-review → adia-gen-review}/scripts/gen-review-status.mjs +5 -5
  73. package/skills/{adia-ui-gen-review → adia-gen-review}/scripts/validate-cycle-scores.mjs +5 -5
  74. package/skills/adia-llm-internals/SKILL.md +77 -0
  75. package/skills/{adia-ui-llm → adia-llm-internals}/references/adapter-contract.md +4 -2
  76. package/skills/{adia-ui-llm → adia-llm-internals}/references/add-a-provider.md +4 -5
  77. package/skills/{adia-ui-llm → adia-llm-internals}/references/bridge-facade.md +10 -3
  78. package/skills/{adia-ui-llm → adia-llm-internals}/references/browser-proxy-boundary.md +1 -1
  79. package/skills/{adia-ui-llm → adia-llm-internals}/references/model-registry.md +5 -3
  80. package/skills/{adia-ui-llm → adia-llm-internals}/references/streaming-sse.md +2 -2
  81. package/skills/adia-release/SKILL.md +72 -0
  82. package/skills/adia-release/references/changelog-discipline.md +119 -0
  83. package/skills/adia-release/references/cut-procedure.md +247 -0
  84. package/skills/adia-release/references/gates-catalog.md +222 -0
  85. package/skills/adia-release/references/independent-package-release.md +52 -0
  86. package/skills/adia-release/references/migration-guide-authoring.md +86 -0
  87. package/skills/adia-release/references/notes-authoring.md +90 -0
  88. package/skills/adia-release/references/recovery-paths.md +106 -0
  89. package/skills/{adia-ui-release → adia-release}/scripts/bump.mjs +27 -2
  90. package/skills/{adia-ui-release → adia-release}/scripts/dispatch-publish.mjs +72 -6
  91. package/skills/{adia-ui-release → adia-release}/scripts/insert-stub.mjs +6 -4
  92. package/skills/adia-release/scripts/package-paths.mjs +50 -0
  93. package/skills/{adia-ui-release → adia-release}/scripts/promote-unreleased.mjs +4 -3
  94. package/skills/{adia-ui-release → adia-release}/scripts/release-pack.mjs +48 -27
  95. package/skills/{adia-ui-release → adia-release}/scripts/tag-lockstep.mjs +15 -3
  96. package/skills/adia-site-docs/SKILL.md +68 -0
  97. package/skills/adia-site-docs/evals/audit-report.md +30 -0
  98. package/skills/adia-site-docs/evals/routing-corpus.json +176 -0
  99. package/skills/adia-site-docs/intent.md +72 -0
  100. package/bin/lib/audit-axes.mjs +0 -555
  101. package/bin/lib/dry-run-irreversible.mjs +0 -236
  102. package/bin/lib/run-skill-evals.mjs +0 -487
  103. package/bin/lib/teach-router.mjs +0 -250
  104. package/commands/adia-forge-a2ui.md +0 -10
  105. package/commands/adia-forge-author.md +0 -10
  106. package/commands/adia-forge-dogfood.md +0 -8
  107. package/commands/adia-forge-llm.md +0 -8
  108. package/commands/adia-forge-orient.md +0 -10
  109. package/commands/adia-forge-release.md +0 -8
  110. package/commands/adia-forge-review.md +0 -10
  111. package/references/shared/pev-rationale.md +0 -64
  112. package/references/shared/skill-conventions.md +0 -133
  113. package/skills/adia-ui-a2ui/CHANGELOG.md +0 -32
  114. package/skills/adia-ui-a2ui/SKILL.md +0 -243
  115. package/skills/adia-ui-a2ui/evals/adversarial-corpus.json +0 -75
  116. package/skills/adia-ui-a2ui/evals/teach-routing-cases.json +0 -100
  117. package/skills/adia-ui-a2ui/references/anti-patterns.md +0 -24
  118. package/skills/adia-ui-a2ui/references/chunk-authoring.md +0 -88
  119. package/skills/adia-ui-a2ui/references/corpus-discipline.md +0 -56
  120. package/skills/adia-ui-a2ui/references/eval-diagnostics.md +0 -127
  121. package/skills/adia-ui-a2ui/references/fragment-graph.md +0 -91
  122. package/skills/adia-ui-a2ui/references/mcp-pipeline-ops.md +0 -106
  123. package/skills/adia-ui-a2ui/references/mcp-tool-reference.md +0 -398
  124. package/skills/adia-ui-a2ui/references/pipeline-overview.md +0 -175
  125. package/skills/adia-ui-a2ui/references/semantic-fail-lifting.md +0 -120
  126. package/skills/adia-ui-a2ui/references/strategy-engines.md +0 -111
  127. package/skills/adia-ui-a2ui/references/teach-protocol.md +0 -220
  128. package/skills/adia-ui-a2ui/references/zettel-calibration.md +0 -93
  129. package/skills/adia-ui-a2ui/scripts/audit-a2ui-roster.mjs +0 -96
  130. package/skills/adia-ui-a2ui/scripts/teach-route.mjs +0 -157
  131. package/skills/adia-ui-a2ui/skill.json +0 -38
  132. package/skills/adia-ui-authoring/CHANGELOG.md +0 -32
  133. package/skills/adia-ui-authoring/SKILL.md +0 -256
  134. package/skills/adia-ui-authoring/assets/case-studies/admin-shell-decomposition.md +0 -101
  135. package/skills/adia-ui-authoring/assets/case-studies/maxtokens-32768-discovery.md +0 -109
  136. package/skills/adia-ui-authoring/assets/case-studies/theme-panel-promotion.md +0 -113
  137. package/skills/adia-ui-authoring/evals/adversarial-design-plan-gates.json +0 -138
  138. package/skills/adia-ui-authoring/evals/routing-corpus.json +0 -245
  139. package/skills/adia-ui-authoring/references/canonical-pattern-index.md +0 -179
  140. package/skills/adia-ui-authoring/references/common-gotchas.md +0 -93
  141. package/skills/adia-ui-authoring/references/composite-demo-protocol.md +0 -1084
  142. package/skills/adia-ui-authoring/references/teach-protocol.md +0 -428
  143. package/skills/adia-ui-authoring/scripts/audit-authoring-roster.mjs +0 -148
  144. package/skills/adia-ui-authoring/skill.json +0 -45
  145. package/skills/adia-ui-dogfood/CHANGELOG.md +0 -17
  146. package/skills/adia-ui-dogfood/README.md +0 -62
  147. package/skills/adia-ui-dogfood/SKILL.md +0 -866
  148. package/skills/adia-ui-dogfood/skill.json +0 -40
  149. package/skills/adia-ui-forge/SKILL.md +0 -88
  150. package/skills/adia-ui-forge/evals/routing-corpus.json +0 -30
  151. package/skills/adia-ui-gen-review/CHANGELOG.md +0 -108
  152. package/skills/adia-ui-gen-review/SKILL.md +0 -266
  153. package/skills/adia-ui-gen-review/references/loop-protocol.md +0 -712
  154. package/skills/adia-ui-gen-review/references/rubric-cosmetic.md +0 -144
  155. package/skills/adia-ui-gen-review/references/rubric-decompose.md +0 -117
  156. package/skills/adia-ui-gen-review/references/rubric-score.md +0 -249
  157. package/skills/adia-ui-gen-review/references/teach-protocol.md +0 -214
  158. package/skills/adia-ui-gen-review/skill.json +0 -22
  159. package/skills/adia-ui-llm/CHANGELOG.md +0 -25
  160. package/skills/adia-ui-llm/SKILL.md +0 -165
  161. package/skills/adia-ui-llm/evals/adversarial-corpus.json +0 -75
  162. package/skills/adia-ui-llm/evals/routing-corpus.json +0 -30
  163. package/skills/adia-ui-llm/evals/teach-routing-cases.json +0 -73
  164. package/skills/adia-ui-llm/references/teach-protocol.md +0 -78
  165. package/skills/adia-ui-llm/scripts/audit-llm-roster.mjs +0 -93
  166. package/skills/adia-ui-llm/scripts/teach-route.mjs +0 -119
  167. package/skills/adia-ui-llm/skill.json +0 -33
  168. package/skills/adia-ui-release/CHANGELOG.md +0 -23
  169. package/skills/adia-ui-release/SKILL.md +0 -295
  170. package/skills/adia-ui-release/assets/case-studies/2026-05-20-batch-push-v0.6.14-v0.6.15.md +0 -144
  171. package/skills/adia-ui-release/assets/case-studies/2026-05-20-corpus-drift-remediation-v0.6.15.md +0 -155
  172. package/skills/adia-ui-release/assets/case-studies/2026-05-20-version-skip-correction-v0.6.12.md +0 -114
  173. package/skills/adia-ui-release/assets/case-studies/2026-05-21-author-from-scratch-v0.6.18.md +0 -139
  174. package/skills/adia-ui-release/assets/case-studies/2026-05-21-feedback37-retraction-v0.6.21.md +0 -124
  175. package/skills/adia-ui-release/assets/case-studies/2026-05-21-fn1-enrichment-pass-v0.6.19.md +0 -125
  176. package/skills/adia-ui-release/assets/case-studies/2026-05-21-stale-test-detection-v0.6.20.md +0 -142
  177. package/skills/adia-ui-release/assets/case-studies/2026-05-23-freshness-gate-recovery-v0.6.32.md +0 -97
  178. package/skills/adia-ui-release/assets/case-studies/2026-05-26-catalog-drift-recurring-v0.6.40.md +0 -147
  179. package/skills/adia-ui-release/assets/templates/stub-changelog.template.md +0 -22
  180. package/skills/adia-ui-release/evals/evals.json +0 -164
  181. package/skills/adia-ui-release/references/changelog-discipline.md +0 -250
  182. package/skills/adia-ui-release/references/cycle-happy-path.md +0 -520
  183. package/skills/adia-ui-release/references/exe-deploy.md +0 -149
  184. package/skills/adia-ui-release/references/gates-catalog.md +0 -778
  185. package/skills/adia-ui-release/references/ledger-discipline.md +0 -232
  186. package/skills/adia-ui-release/references/migration-guide-authoring.md +0 -174
  187. package/skills/adia-ui-release/references/multi-agent-baseline.md +0 -207
  188. package/skills/adia-ui-release/references/notes-authoring.md +0 -212
  189. package/skills/adia-ui-release/references/recovery-paths.md +0 -215
  190. package/skills/adia-ui-release/references/rollup-notes.md +0 -208
  191. package/skills/adia-ui-release/references/teach-protocol.md +0 -468
  192. package/skills/adia-ui-release/scripts/audit-gate-roster.mjs +0 -196
  193. package/skills/adia-ui-release/scripts/make-ledger.mjs +0 -179
  194. package/skills/adia-ui-release/skill.json +0 -75
  195. /package/skills/{adia-ui-authoring → adia-author}/references/api-contract.md +0 -0
  196. /package/skills/{adia-ui-release → adia-release}/scripts/assert-monorepo-root.mjs +0 -0
@@ -0,0 +1,27 @@
1
+ # Anti-pattern catalogue — the 8 checks + tuning discipline
2
+
3
+ `check_anti_patterns` (rule source: `packages/a2ui/retrieval/anti-patterns.js`)
4
+ runs eight checks against rendered HTML:
5
+
6
+ - `noBareDivs` — use a layout component (`col-ui`, `row-ui`, `grid-ui`, `stack-ui`).
7
+ - `noBareInputs` — use `input-ui` / `select-ui` / `check-ui`.
8
+ - `cardStructure` — flags incorrect card nesting (section > header, etc.).
9
+ - `columnWrap` — section content must be wrapped in `col-ui`.
10
+ - `noHardcodedColors` — no hex/rgb in inline `style` attributes.
11
+ - `noInlineLayout` — no `display: flex` / `grid` in inline styles.
12
+ - `noInventedComponents` — every `*-ui` tag must exist in the catalog.
13
+ - `slotOnContainer` — `slot` attributes belong on content elements, not containers.
14
+
15
+ ## Tuning discipline
16
+
17
+ - **Don't bypass `validate_schema`.** A low structural score usually explains
18
+ anti-pattern findings; fix the A2UI JSON before chasing HTML-level issues.
19
+ - **Don't act on stub outputs.** Without the API key loaded, `thinking` mode
20
+ silently falls back to `StubLLMAdapter`'s canned 6-component card — any
21
+ finding or feedback against it is noise. Check the output shape first.
22
+ - **Don't commit intermediate JSON.** Scratch files go under the session
23
+ scratchpad or `.gitignore`d paths; the corpus is harvester-generated into
24
+ `packages/a2ui/corpus/chunks/` via `npm run harvest:chunks`.
25
+ - **One rule at a time**, and after any rule/threshold change re-run
26
+ `npm run mcp:smoke` plus a spot-check over known-clean corpus HTML to
27
+ measure the false-positive delta before landing.
@@ -0,0 +1,71 @@
1
+ # Chunk authoring — HTML-first synthesis + harvest
2
+
3
+ The corpus is **one-format and harvester-driven**. Hand-authored pattern /
4
+ composition JSON dirs (`compose/patterns/`, `compose/{fragments,compositions}/`,
5
+ `corpus/patterns/`) were retired; the only retrieval substrate is
6
+ `packages/a2ui/corpus/chunks/` (394 chunks + `_index.json`), produced by
7
+ `npm run harvest:chunks`. In-tree SoT for this workflow:
8
+ `packages/a2ui/corpus/data-flow.md`.
9
+
10
+ ## The authoring loop
11
+
12
+ 1. **Author a live demo** in a harvest root: `apps/<name>/app/<demo>/`,
13
+ `playgrounds/<name>/`, `catalog/<lib>/app/<demo>/`, or `site/pages/<route>/`.
14
+ Demo pages are live-rendering and human-verified — they are the canonical
15
+ source; chunk JSON is derived output.
16
+ 2. **Tag retrievable regions** on the bounding element:
17
+ `data-chunk="<slug>"` + `data-chunk-kind="block|page|panel|field"` +
18
+ `data-chunk-domain` + `data-chunk-description` + `data-chunk-keywords`.
19
+ Spec: `.claude/docs/specs/genui-chunk-marker.md`; dev tooling:
20
+ `site/dev-chunks.{js,css}` (the `?chunks` overlay).
21
+ 3. **Harvest** — `npm run harvest:chunks` (dry-run: `harvest:chunks:dry`)
22
+ walks the source roots, writes `chunks/<slug>.json` + `_index.json`, and
23
+ runs the transpile pass (`compose/transpiler/`) to produce the A2UI
24
+ `template` for annotated chunks.
25
+ 4. **Embed** (optional but expected for retrieval parity) —
26
+ `npm run build:embeddings:chunks` regenerates `chunk-embeddings.json`.
27
+ Freshness gates: `npm run check:chunks-fresh` + `check:embeddings-fresh`.
28
+ 5. **Verify** — `npm run smoke:chunks` (stub-LLM, offline) and a rendered
29
+ check of the demo page; then the eval floor:
30
+ `npm run eval:diff -- --engine zettel`.
31
+
32
+ Never hand-author or hand-edit `corpus/chunks/*.json` — they're build outputs;
33
+ the harvester wins on the next run.
34
+
35
+ ## Metadata is the search index
36
+
37
+ `data-chunk-description` + `data-chunk-keywords` + the slug are what
38
+ `keywordScore()` and `searchAll()` match (name-token hits dominate — see
39
+ [zettel-calibration](zettel-calibration.md)). A chunk whose name lacks its
40
+ entity words is invisible to short queries. Write keyword-rich descriptions
41
+ derived from what's IN the chunk (headings, labels, button text), not
42
+ generic ("content card") prose.
43
+
44
+ ## Component-catalog examples (the other training signal)
45
+
46
+ Per-component variant coverage lives in the component **yaml** (SoT) at
47
+ `packages/web-components/components/<name>/<name>.yaml`; `<name>.a2ui.json`
48
+ sidecars are **generated** by `node scripts/build/components.mjs` and
49
+ hook-guarded — edit the yaml and rebuild, never the sidecar. Templates use
50
+ PascalCase component names (`Chat`, not `chat-ui`); the registry maps class →
51
+ tag. Demo variants in `<name>.examples.html` are the canonical variant list;
52
+ yaml examples mirror them 1:1. Primitive/demo authoring itself belongs to the
53
+ authoring sibling skill.
54
+
55
+ ## Pitfalls specific to authoring time
56
+
57
+ - **`?chunks` dev overlay prepends a `<span data-chunk-marker>`** into every
58
+ `[data-chunk]` element — `:first-child` / `:nth-child` rules on those
59
+ children break during dev only; don't chase it as a corpus bug.
60
+ - **Page-kind chunks declare slot regions via `data-chunk-slot="X"`** — slot
61
+ regions are NOT themselves chunks. A new page-kind chunk needs a matching
62
+ entry in the slot-validation map (`chunk-composer.js`) or compose-time
63
+ validation rejects its plans.
64
+ - **Chunk-kind matters for retrieval budget** — pages and panels ride
65
+ unconditionally into the LLM prompt; blocks compete for the remaining
66
+ `PRE_SEARCH_LIMIT` budget. Mis-kinding a block as a panel inflates every
67
+ prompt.
68
+
69
+ After harvest, continue with [corpus-discipline](corpus-discipline.md) for the
70
+ cross-cutting corpus pitfalls, and [leverage-rules](leverage-rules.md) before
71
+ splitting a repeated subtree into its own chunk.
@@ -0,0 +1,68 @@
1
+ # Corpus discipline — retrieval quality, harvest hygiene, drift traps
2
+
3
+ ## Pipeline-side pitfalls (each one has burned a session)
4
+
5
+ - **"Thinking mode returns garbage"** → API keys must be in `.env` AND
6
+ `scripts/load-env.mjs` imported; otherwise the stub adapter answers with a
7
+ canned 6-component card.
8
+ - **"Search misses obvious patterns"** → inspect chunk metadata first
9
+ (`npm run audit:corpus-stats`); bad descriptions/keywords = invisible chunks.
10
+ - **"Gate rejects valid intents"** → intent words are filtered by the
11
+ `GATE_STOPS` set + 3-char minimum
12
+ (`compose/strategies/monolithic/generate-instant.js`). Check whether the key
13
+ word is a stop word before touching thresholds.
14
+ - **`npm run smoke:chunks` re-harvests chunks as a side effect**, touching
15
+ `corpus/chunks/*.json` mtimes. After smokes, stage only files you actually
16
+ changed — never a blanket `git add -A`.
17
+ - **The chunk-synthesizer fast-path threshold is 8** on the blended
18
+ keyword+cosine score. Below it, synthesis fires. Tune in
19
+ `chunk-synthesizer.js` (`STRONG_RETRIEVAL_SCORE`), not by inflating
20
+ `keywordScore()` weights in `chunk-library.js`.
21
+ - **`state_id` is opaque to callers** — engine-generated on compose. Never
22
+ construct or parse it; refinements pass back the prior `state_id` so the
23
+ cache chains via `parent_state_id`.
24
+ - **After any `@bp` / layout-attribute change to `data-chunk`-annotated HTML,
25
+ run `npm run harvest:chunks` in the same session** — otherwise training
26
+ chunks silently hold stale values.
27
+ - **The corpus's inline `style=` is almost entirely structural page-frame
28
+ layout** (no primitive exists for it) — don't propose a mass
29
+ "convert to primitives" campaign; it's a known, accepted shape.
30
+ - **Harvest roots are a hard-coded list** in `scripts/build/harvest-chunks.mjs`.
31
+ A directory rename that adds new siblings silently drops chunks while the
32
+ harvest "runs clean" (a rename once dropped 26 chunks for ~24h). After any
33
+ root-level rename: check the roots list AND diff chunk counts against the
34
+ previous `_index.json` tally.
35
+ - **Subagent-authored demo pages misuse component APIs** (nested content
36
+ instead of label/description attrs, native `<table>` inside `table-ui`
37
+ causing a "No data" overlay). Visual QA every subagent-authored page before
38
+ harvesting it into the corpus.
39
+
40
+ ## Transpile-pass parser lessons (HTML → A2UI)
41
+
42
+ The transpile pass (`compose/transpiler/`) inherited these hard-won rules —
43
+ preserve them in any rewrite:
44
+
45
+ 1. **Regex `([\s\S]*?)` can't handle nested same-name tags** — depth-tracking
46
+ tag counting is required (`<card-ui><card-ui>…` matches the inner close).
47
+ 2. **Only DIRECT children belong in `comp.children`** — not the flattened
48
+ subtree; identify direct children as components not claimed by any other
49
+ component.
50
+ 3. **Re-ID by array index, not original ID** — multiple components can share
51
+ an original ID; index-mapping guarantees uniqueness.
52
+ 4. **Subtree walks need a visited set** — shared children reached via multiple
53
+ parents otherwise duplicate.
54
+ 5. **Boolean-attribute trap**: `text=""` parses as boolean `true`; any code
55
+ doing `c.text.toLowerCase()` needs a `typeof c.text === 'string'` guard.
56
+
57
+ ## Improving search quality without new infrastructure
58
+
59
+ Three interventions compose multiplicatively; apply in order:
60
+
61
+ 1. **Enrich metadata** — derive descriptions from structural signals
62
+ (Input("Email") + Input("Password") + Button("Sign In") → "Login form with
63
+ email, password fields"). Historically 40% → 95% meaningful descriptions.
64
+ 2. **Trace which search path each mode actually invokes** — semantic search
65
+ has been built-but-unwired before; one-line wiring changes beat new infra.
66
+ 3. **Expand synonym/keyword surfaces** — bridge user vocabulary to chunk
67
+ vocabulary ("inbox" → notification) via `data-chunk-keywords`, remembering
68
+ name-token hits outrank keyword hits.
@@ -0,0 +1,86 @@
1
+ # Eval diagnostics — gap diagnosis + regression triage
2
+
3
+ **Diagnostics run before code changes — always.** The #1 anti-pattern is
4
+ "tweak and hope": changing a prompt or adding a chunk without knowing why the
5
+ score is low burns real-LLM eval runs and teaches nothing.
6
+
7
+ ## Phase 0 — author a stub-mode diagnostic (zero LLM cost)
8
+
9
+ For each failing intent, capture four signals:
10
+
11
+ 1. **Search ranking** — what `searchChunks()` returns for the intent query.
12
+ 2. **Composition output** — what the pipeline emitted (HTML or plan).
13
+ 3. **Tag inventory** — every custom-element-like tag in the emitted HTML.
14
+ 4. **Coverage delta** — expected_components vs found_components.
15
+
16
+ Template (runnable with `llmAdapter: null`):
17
+
18
+ ```js
19
+ async function diagnose(intent) {
20
+ const search = searchChunks(intent.intent, { limit: 10 });
21
+ const comp = await composeFromIntent({ intent: intent.intent, llmAdapter: null });
22
+ const tags = [...comp.html.matchAll(/<([a-z]+-[a-z-]+)[\s>]/gi)]
23
+ .map(m => m[1]).filter((v, i, a) => a.indexOf(v) === i);
24
+ const found = intent.expected_components.filter(tag =>
25
+ new RegExp(`<${kebab(tag)}-ui[\\s>]`).test(comp.html));
26
+ return { search, tags, found,
27
+ missing: intent.expected_components.filter(t => !found.includes(t)) };
28
+ }
29
+ ```
30
+
31
+ ## Phase 1 — classify failures
32
+
33
+ | Bucket | Symptom | Root cause | Cost |
34
+ | --- | --- | --- | --- |
35
+ | A. Holdout misalignment | Top-1 retrieved ≠ expected_chunk | Holdout drifted from corpus | minutes |
36
+ | B. Coverage gap | Chunk retrieved but HTML lacks expected tags | Chunk HTML doesn't contain those components | hours (new chunks) |
37
+ | C. Wrong shell | Retrieval OK, LLM picks wrong page shell | Prompt ambiguity / missing domain→shell mapping | hours (prompt tuning) |
38
+ | D. Broken render | HTML emitted but console errors / blank | Missing registrations or bad markup | hours (harvester bug) |
39
+ | E. Measurement bug | Output looks right, score low | Scorer regex/casing/substring bug | minutes |
40
+ | F. Embedding drift | Async top-1 ≠ sync top-1 | Cosine boosts flip rankings non-deterministically | minutes |
41
+
42
+ **Fix order: A → E → F → C → B → D.** Fix measurement before content (else
43
+ scores can't be trusted); fix determinism before adding content (else evals
44
+ fluctuate).
45
+
46
+ ## Phase 2 — fixes per bucket
47
+
48
+ - **A. Holdout alignment** — map each intent's `expected_chunk` to the actual
49
+ top-1; update `packages/a2ui/corpus/evals/holdout-compose-from-chunks.jsonl`.
50
+ - **E. Measurement traps** — PascalCase→kebab (`AgentTrace` → `agent-trace`,
51
+ not `agenttrace`); substring false positives (`pane` vs `panel`,
52
+ `textarea-ui` contains `text-ui` — word boundaries); case sensitivity (`/i`).
53
+ - **F. Embedding drift** — prefer sync keyword search for deterministic
54
+ fast-path tiers; keep async cosine for the synthesis tier only (embeddings
55
+ are a tie-breaker by design — see zettel-calibration).
56
+ - **C. Wrong shell** — check the `SYSTEM_PROMPT` domain→shell mapping in
57
+ `chunk-synthesizer.js`; add explicit examples for the failing domain and a
58
+ negative constraint ("NEVER default to dashboard-admin-page for
59
+ non-dashboard intents").
60
+ - **B. Coverage gap** — author a block chunk the HTML-first way
61
+ ([chunk-authoring](chunk-authoring.md)): demo page in a harvest root (e.g.
62
+ `catalog/ui-patterns/app/<name>/`), `data-chunk` + `data-chunk-kind="block"`
63
+ markers, real component tags so coverage scoring matches, then
64
+ `npm run harvest:chunks`.
65
+ - **D. Broken render** — `packages/a2ui/mcp/scripts/render-fidelity.mjs`
66
+ output (console errors, blank viewport, undefined elements); verify
67
+ registrations in `packages/web-components/index.js`; check the harvester
68
+ didn't strip `data-chunk-slot` from page shells.
69
+
70
+ ## Verification
71
+
72
+ ```bash
73
+ npm run eval:compose-from-chunks # stub first: fast, free
74
+ npm run eval:compose-from-chunks -- --real-llm --report-file # then real LLM
75
+ ```
76
+
77
+ Stop only when all intents pass and the average is stable across 3 runs, and
78
+ the SKILL.md floors hold.
79
+
80
+ ## The eval suite's dimensions
81
+
82
+ `packages/a2ui/mcp/scripts/test-evals.mjs` scores 5 weighted dimensions:
83
+ structural_validity 30% · intent_alignment 25% · component_coverage 20% ·
84
+ card_model_compliance 15% · anti_pattern_count 10%. `--save-baseline`
85
+ (`npm run test:evals:baseline`) stores scores; later runs flag any dimension
86
+ dropping >5 points or aggregate >3 (exit code 2 = regression).
@@ -0,0 +1,66 @@
1
+ # Format-extension decisions — when a component's contract can't express something
2
+
3
+ A component's A2UI JSON contract sometimes can't carry a content shape the
4
+ authored HTML has — a table cell with mixed prose and inline `<code>`, a
5
+ label needing an icon, a slot needing arbitrary nested markup. Read this
6
+ before extending any component's declarative contract to close a gap like
7
+ that; it doesn't cover corpus/chunk decisions (see
8
+ [leverage-rules](leverage-rules.md)) — it's about the component's own
9
+ prop/construct surface.
10
+
11
+ ## The three-tier ladder, in order of preference
12
+
13
+ 1. **Extend an existing declarative registry the component already has** —
14
+ e.g. `table-ui`'s `components/table/cell-types.js` (`badge`, `progress`,
15
+ `link`, …). Add a new named type; the component dispatches rendering
16
+ through the registry it already owns. **Smallest blast radius**: no new
17
+ A2UI construct, no runtime/validator changes, no registry entry in
18
+ `packages/a2ui/runtime/registry.js`. **Ceiling**: only covers formats the
19
+ registry's render contract can express (inline-safe content, not
20
+ arbitrary nested block markup). Building the new registry entry is a
21
+ web-components primitive edit — hand off to `adia-author` for the
22
+ implementation half; this reference only decides the tier.
23
+ 2. **New A2UI primitive(s)**, each slot/cell a normal construct subtree (any
24
+ registered component nestable inside it) — matches how `Column`/`Row`
25
+ already compose. **Most expressive** — no ceiling on what can nest.
26
+ **Largest blast radius**: new constructs need transpiler synthesis,
27
+ runtime rendering support, and validator rules; changes what the
28
+ containing component MEANS in the A2UI graph model. Reserve for cases
29
+ tier 1 genuinely can't reach.
30
+ 3. **Raw-HTML/markup passthrough**, wired through an existing visual-only
31
+ escape hatch (e.g. `table-ui[raw]`) registered as an A2UI construct
32
+ carrying sanitized markup as a string prop. **Fastest to ship** — reuses
33
+ infrastructure verbatim. **Weakest fit for a generative pipeline**: an
34
+ LLM composing pages would need to emit raw HTML strings instead of
35
+ constructs, undermining the JSON-graph value the A2UI format exists for.
36
+ Acceptable ONLY as a narrowly-scoped escape hatch (e.g. a docs-migration
37
+ pipeline), never as a pattern taught to the generation engines.
38
+
39
+ **Default to tier 1.** Climb to tier 2 or 3 only when a concrete case proves
40
+ tier 1's ceiling — don't pre-emptively build the more expressive, more
41
+ invasive tier for a gap tier 1 can still close.
42
+
43
+ ## Worked precedent — TKT-0008 (table-ui rich cells, 2026-07-13)
44
+
45
+ `table-ui`'s `{columns, data}` contract only carried plain-string cells;
46
+ docs reference tables with inline `<code>` in cells (e.g. `` `variant` `` in
47
+ a props table) fell back to a preserved-HTML `Code` block instead of a
48
+ `Table` component — the table never reached the A2UI graph. All three tiers
49
+ were scoped (rich-cell vocabulary / new `TableRow`/`TableCell` primitives /
50
+ raw-HTML passthrough via the existing `table-ui[raw]` prop); tier 1 was
51
+ ratified and built: a new `markdown` cell type in `cell-types.js`, rendered
52
+ via `core/markdown.js`'s `inline()` (exported for this reuse — the exact
53
+ client-side counterpart to the docs-transpiler's own `convertInline()`).
54
+ Measured result: the targeted failure class (`structural:tables` in the
55
+ strict re-verification sweep) dropped 39→7 pages out of 98 measured; the
56
+ residual 7 carry cells genuinely outside tier 1's reach (block content,
57
+ component tags) and are an open tier-2/3 follow-up
58
+ (`docs/tickets/TKT-0008-table-ui-cannot-express-rich-cells.md`).
59
+
60
+ ## Why this generalizes
61
+
62
+ The same ladder applies to any AdiaUI primitive with a per-item/per-cell
63
+ render dispatch (table columns, list-item variants, chart series formats,
64
+ …): check for an existing registry FIRST, size the ceiling honestly against
65
+ the concrete case at hand, and only pay the blast-radius cost of a new
66
+ construct or a raw-passthrough when the registry provably can't reach it.
@@ -0,0 +1,52 @@
1
+ # Leverage rules — when a repeated subtree earns its own corpus entry
2
+
3
+ Historical note first: the zettel **fragment mechanism is retired**
4
+ (`$fragment` refs now render as visible drift placeholders — see
5
+ `compose/strategies/zettel/composer.js`). The leverage discipline below
6
+ outlived the mechanism: it now governs whether a repeated subtree gets split
7
+ into its own `data-chunk` block vs staying inline in its parent chunk, and it
8
+ is the calibration for any future extraction tier.
9
+
10
+ ## The leverage rule
11
+
12
+ **Do not extract a reusable unit unless it has leverage ≥ 3** — at least 3
13
+ consumers would use it. Two exceptions:
14
+
15
+ 1. **Singleton closing a semantic gap** — a distinct domain primitive (e.g. a
16
+ keyboard-shortcut row) justifies extraction at leverage 1 because the
17
+ concept itself needs to be retrievable.
18
+ 2. **Intra-composition multi-use** — one composition instantiating the same
19
+ subtree N ≥ ~10 times justifies extraction; reuse is measured per instance,
20
+ not per referencing composition. Historical evidence: `calendar-day-cell`
21
+ used 35× inside one calendar composition lifted corpus reuse 26.4% → 33.5%
22
+ on its own.
23
+
24
+ Sub-leverage extraction bloats the corpus, slows retrieval, and adds
25
+ maintenance without improving reuse. Common candidates that DO clear the bar:
26
+ card headers, key-value rows, icon+text rows, labeled progress bars, stat
27
+ tiles, notification rows.
28
+
29
+ ## Keyword preservation (the extraction-drift lesson)
30
+
31
+ Extraction SHRINKS the parent's keyword surface: before, the parent contained
32
+ the subtree's text; after, it only references the child. Historically,
33
+ extracting a card-header from the login-form pattern dropped its retrieval
34
+ score 28 → 24 because "login / sign in / email password" tokens left with the
35
+ extract; explicitly re-adding those keywords to the parent restored 28.
36
+
37
+ **Rule: every parent that loses a subtree must be re-enriched with the
38
+ semantic tokens that left.** In today's corpus this means the parent chunk's
39
+ `data-chunk-keywords` (and re-harvest). Without it, retrieval degrades
40
+ silently.
41
+
42
+ ## Threshold discipline after extraction
43
+
44
+ If a known-good intent starts scoring below a retrieval threshold after a
45
+ corpus split, DO NOT lower the threshold. In order:
46
+
47
+ 1. Verify the parent's keywords were preserved (above).
48
+ 2. Check the new entry's name/description doesn't cannibalize the parent's
49
+ semantic space (retrieval collision — see
50
+ [semantic-fail-lifting](semantic-fail-lifting.md) Strategy B).
51
+ 3. Only then consider threshold work, with
52
+ [zettel-calibration](zettel-calibration.md) history read first.
@@ -0,0 +1,83 @@
1
+ # MCP pipeline operations — generate → validate → render → feedback
2
+
3
+ Operator workflows against the a2ui MCP server. Tool schemas + wrappers:
4
+ [mcp-tool-reference](mcp-tool-reference.md).
5
+
6
+ ## Full pipeline — one command
7
+
8
+ ```bash
9
+ node scripts/mcp-pipeline.cjs "dashboard with 4 stat cards and a revenue chart"
10
+ ```
11
+
12
+ Pipes `intent → generate_ui → validate_schema → a2ui-to-html →
13
+ check_anti_patterns` and prints the scores. Fastest whole-stack confirmation
14
+ after a change.
15
+
16
+ ## Step-by-step (inspect intermediates)
17
+
18
+ ```bash
19
+ node scripts/mcp-call.cjs generate_ui '{"intent":"dashboard with 4 stat cards","mode":"instant"}'
20
+ node scripts/mcp-call.cjs validate_schema '{"messages":"<paste-messages-json>"}'
21
+ echo '<paste-messages-json>' | node scripts/a2ui-to-html.cjs
22
+ node scripts/mcp-call.cjs check_anti_patterns '{"html":"<paste-rendered-html>"}'
23
+ ```
24
+
25
+ `validate_schema` is fast and deterministic — batch it over
26
+ `packages/a2ui/corpus/chunks/*.json` templates to surface corpus drift without
27
+ re-running the generator.
28
+
29
+ ## Compose from the chunk corpus
30
+
31
+ When the intent matches a known page-shape (auth flow, dashboard layout, error
32
+ shell), prefer `compose_from_chunks` over `generate_ui`:
33
+
34
+ ```bash
35
+ node scripts/mcp-call.cjs compose_from_chunks '{"intent":"sign-in card with email + password + OAuth"}'
36
+ ```
37
+
38
+ Tier 1 returns a matched chunk's HTML when the retrieval score ≥ 8; otherwise
39
+ the LLM picks a `{page, slot_bindings}` plan from a pre-filtered ~30-entry
40
+ catalog and the chunk composer materializes it, validating slot-name +
41
+ chunk-kind contracts. Returns `{state_id, html, plan, candidates}`.
42
+
43
+ ## Multi-turn refinement (state_id chain)
44
+
45
+ ```bash
46
+ # Turn 1 → returns state_id "abc123"
47
+ node scripts/mcp-call.cjs compose_from_chunks '{"intent":"sign-in card with email + password"}'
48
+ # Turn 2 → updateComponents messages + new state_id
49
+ node scripts/mcp-call.cjs refine_composition '{"state_id":"abc123","intent":"add OAuth row with Google + GitHub"}'
50
+ ```
51
+
52
+ Two-pass synthesis (locator → modifier), validator-driven retry
53
+ (`maxAttempts=2`). Each refinement chains through `parent_state_id`; walk
54
+ history with `get_state`. The state cache is in-memory and bounded — after an
55
+ MCP server restart, re-run `compose_from_chunks` for a fresh `state_id`.
56
+
57
+ ## Reporting issues
58
+
59
+ When the engine breaks expectations, fire `report_issue` with the most recent
60
+ `state_id` (reporter: `llm` for agent self-fire, `user` for a human request).
61
+ The record lands in the engine-internal telemetry store — scratch data for
62
+ diagnosis. Anything worth durable tracking (a recurring pattern, a fix
63
+ proposal) belongs in a GitHub issue or the PR description of the fixing
64
+ change.
65
+
66
+ ## Validation checks
67
+
68
+ `validate_schema` runs a weighted checklist (target aggregate ≥ 80). Common
69
+ failures when the corpus drifts: `hasRootComponent` (missing `id: "root"`),
70
+ `cardContentModel` (section without Column wrapper, or heading inside section
71
+ instead of header), `headingHierarchy` (skipped levels), `flatAdjacency`
72
+ (nested components instead of sibling id references). Full list + weights:
73
+ `packages/a2ui/mcp/TOOLS.md`.
74
+
75
+ ## Feedback loop
76
+
77
+ Score runs with `submit_feedback` keyed on the `executionId` from
78
+ `generate_ui`. The analyzer
79
+ (`packages/a2ui/retrieval/feedback/feedback-analyzer.js`) aggregates
80
+ `corpus/feedback/*.jsonl` into per-intent trends, promotion candidates
81
+ (≥95 score + ≥4 rating across 3+ runs → `npm run feedback:promote`), and the
82
+ gap registry (`packages/a2ui/corpus/gaps/registry.json`).
83
+ `npm run feedback:report` surfaces the current state.
@@ -0,0 +1,59 @@
1
+ # MCP tool reference — the a2ui server's tool surface
2
+
3
+ **Schema SoT is `packages/a2ui/mcp/TOOLS.md` (30 tools), generated alongside
4
+ `server.js` — read it for input/output shapes; never restate schemas here
5
+ (they drift).** Tools register in `packages/a2ui/mcp/server.js` +
6
+ `packages/a2ui/mcp/tools/{corpus,discovery,feedback,refine,synthesis,
7
+ validation,zettel}.js`. Any tool change updates TOOLS.md in the same commit.
8
+
9
+ ## Stability rule (load-bearing)
10
+
11
+ Changing an existing tool's input/output contract is a breaking change for
12
+ every external MCP client. The full producer-side rule — dry-run schema diff,
13
+ explicit operator proceed, `@adia-ai/a2ui-mcp` version bump, factory pin
14
+ update, and the consumer-load-bearing subset whose contracts are
15
+ frozen-unless-versioned — lives in
16
+ [../../../references/contracts/a2ui-mcp-surface.md](../../../references/contracts/a2ui-mcp-surface.md).
17
+ Adding tools is additive and safe; removing or renaming is breaking.
18
+
19
+ ## Tool map by job
20
+
21
+ | Group | Tools | Registered in |
22
+ | --- | --- | --- |
23
+ | Generation | `generate_ui`, `refine_ui` (monolithic repair) | `synthesis.js`, `refine.js` |
24
+ | Chunk synthesis + multi-turn | `compose_from_chunks`, `refine_composition`, `get_state`, `report_issue` | `synthesis.js` |
25
+ | Chunk retrieval | `search_chunks`, `get_chunk`, `lookup_chunk` | `corpus.js` |
26
+ | Catalog inspection | `lookup_component`, `get_component_map`, `get_traits`, `get_wiring_catalog`, `server_status` | `discovery.js` |
27
+ | Composition/zettel inspection | `search_patterns`, `get_fragment`, `get_composition`, `get_graph`, `resolve_composition`, `zettel_stats` | `zettel.js` |
28
+ | Intent + context | `classify_intent`, `assemble_context` | `discovery.js` |
29
+ | Validation + conversion | `validate_schema`, `check_anti_patterns`, `convert_html` | `validation.js` |
30
+ | Feedback + evaluation | `submit_feedback`, `get_quality_metrics`, `get_training_gaps`, `run_eval` | `feedback.js` |
31
+ | Authoring (write path) | `import_pattern` | `zettel.js` |
32
+
33
+ Selection heuristics:
34
+
35
+ - **Fresh creation from a known page-shape** → `compose_from_chunks`
36
+ (retrieval-first); generic/novel intent → `generate_ui`.
37
+ - **Modifying an existing surface** ("change", "add to", "remove") →
38
+ `refine_composition` with the prior `state_id` — never re-generate.
39
+ - **Catalog literacy** → `lookup_component` / `get_component_map`; the MCP is
40
+ the live catalog — don't memorize component names.
41
+ - **Trust gate on any LLM-emitted A2UI** → `validate_schema` then
42
+ `check_anti_patterns` on the rendered HTML.
43
+
44
+ ## Local wrappers (all verified in `scripts/`)
45
+
46
+ - `scripts/mcp-pipeline.cjs "<intent>"` — full pipeline one-shot with a
47
+ combined report.
48
+ - `scripts/mcp-call.cjs <tool> '<json-args>'` — single-tool call for stepping
49
+ through / inspecting intermediates.
50
+ - `scripts/a2ui-to-html.cjs [file|stdin]` — renders A2UI message arrays to
51
+ HTML between `validate_schema` and `check_anti_patterns`.
52
+
53
+ ## When adding a tool
54
+
55
+ 1. Register in the matching `mcp/tools/<group>.js` file (zod input schema +
56
+ description that names its trigger phrases and its non-goals).
57
+ 2. Update `TOOLS.md` in the same commit (generated with the server build —
58
+ `npm run build:mcp-server`).
59
+ 3. Verify: `npm run mcp:smoke`, then a real-client round-trip.
@@ -0,0 +1,115 @@
1
+ # Pipeline overview — generator + retrieval + engines (mode: modify pipeline internals)
2
+
3
+ All paths repo-relative. Specs worth reading before structural changes:
4
+ `.claude/docs/specs/a2ui-v0.9-catalog-guide.md` (protocol + catalog format),
5
+ `.claude/docs/specs/genui-multiturn-architecture.md` (state cache, refiner,
6
+ op format), `.claude/docs/specs/genui-chunk-marker.md` (chunk attributes),
7
+ `.claude/docs/conventions/gen-ui-pipeline.md` (harvester wiring + embedding
8
+ lifecycle), `.claude/docs/specs/package-architecture.md` (package relations).
9
+
10
+ ## Key files (verified 2026-07)
11
+
12
+ ### Engine orchestration
13
+
14
+ | File | Role |
15
+ | --- | --- |
16
+ | `packages/a2ui/compose/core/generator.js` | `generate_ui` orchestrator — instant / pro / thinking / stream modes; multi-turn via `executionId` |
17
+ | `packages/a2ui/compose/strategies/registry.js` | Engine registry — `registerEngine(name, factory)`. Reserved names: `monolithic`, `zettel`, `mcp`, `monolithic-*` |
18
+ | `packages/a2ui/compose/strategies/zettel/` | Zettel + chunk-zettel engines — see [strategy-engines](strategy-engines.md) for the per-file map |
19
+ | `packages/a2ui/compose/strategies/free-form-composer/` | Free-form engine (`index.js`, `system-prompt.js`, `transpile.js`) |
20
+ | `packages/a2ui/compose/strategies/_shared/chunk-loader.js` | Shared chunk loading for engines |
21
+ | `packages/a2ui/compose/shared/validate-and-repair.js` | Shared closed-loop validate→repair, adopted by every LLM-calling engine (thinking/pro fully; free-form validate-only) — see [strategy-engines](strategy-engines.md) §Closed-loop validation |
22
+ | `packages/a2ui/compose/transpiler/transpiler.js` | HTML → A2UI transpile pass (used by harvester + convert_html) |
23
+
24
+ ### Corpus + retrieval
25
+
26
+ | File | Role |
27
+ | --- | --- |
28
+ | `packages/a2ui/corpus/scripts/chunk-library.js` | Chunk catalog API — `getChunk()`, `searchChunks()` (keyword), `searchChunksAsync()` (keyword + cosine), `listChunksByKind()`, `lookupChunksByPrimary()`. Reads `corpus/chunks/` + `_index.json` |
29
+ | `packages/a2ui/compose/strategies/zettel/composition-library.js` | Composition loader + `searchAll()` scoring (normalizes harvested chunks to composition shape) |
30
+ | `scripts/build/harvest-chunks.mjs` | `[data-chunk]` boundary walker over `site/pages/`, `apps/`, `playgrounds/`, `catalog/` — writes `corpus/chunks/<name>.json` + `_index.json`. Run via `npm run harvest:chunks` |
31
+ | `packages/a2ui/retrieval/intent/intent-categorizer.js` | Free-text intent → UI-category taxonomy |
32
+ | `packages/a2ui/retrieval/feedback/feedback-analyzer.js` | Aggregates JSONL feedback (`corpus/feedback/*.jsonl`); promotion + gap candidates |
33
+ | `packages/a2ui/retrieval/feedback/gap-registry.js` | Persistent gap tracking → `packages/a2ui/corpus/gaps/registry.json` |
34
+ | `packages/a2ui/retrieval/anti-patterns.js` | The `check_anti_patterns` rule source |
35
+ | `packages/a2ui/compose/core/reference.js` | Thin wrappers over retrieval exports (`searchBlocks`, `searchBlocksSemantic`, …) |
36
+
37
+ ### LLM bridge + MCP
38
+
39
+ | File | Role |
40
+ | --- | --- |
41
+ | `packages/llm/llm-bridge.js` | `createAdapter()` — real LLM or stub fallback |
42
+ | `scripts/load-env.mjs` | Shared .env loader for Node scripts |
43
+ | `packages/a2ui/mcp/server.js` + `mcp/tools/*.js` | MCP stdio server + tool registrations — see [mcp-tool-reference](mcp-tool-reference.md) |
44
+
45
+ ## Critical rules
46
+
47
+ 1. **Relative imports in `packages/llm/*.js`** — never `@llm/` Vite aliases;
48
+ they don't resolve in Node and break published consumers.
49
+ 2. **`load-env.mjs` before any a2ui import in Node** — without it,
50
+ `createAdapter()` silently returns `StubLLMAdapter` (canned 6-component
51
+ card). Feedback or diagnosis on stub output is noise.
52
+ 3. **Metadata IS the search index.** Descriptions + keywords are what retrieval
53
+ matches. Enriching descriptions from structure (headings, labels, button
54
+ text) took meaningful-description rate 40% → 95%. When search degrades,
55
+ inspect chunk metadata before touching thresholds.
56
+ 4. **Instant-mode gate lives in `monolithic/generate-instant.js`** — words ≥3
57
+ chars pass; `GATE_STOPS` filters boilerplate. Grep the set before concluding
58
+ "the gate rejects valid intents".
59
+ 5. **A2UI describes LAYOUT, not behavior.** Generation emits component trees +
60
+ props + slot bindings; never JS or per-canvas CSS. Behavior delegates to
61
+ traits or pre-built apps. "Make the generator emit JS/CSS" is a won't-fix.
62
+ 6. **Renderer guards `textContent` against container wipe** —
63
+ `packages/a2ui/runtime/renderer.js` whitelists pure-text leaves
64
+ (`TEXT_TAG_OK`); everything else routes through the `text=` attribute so
65
+ slotted children survive. Preserve this when touching the renderer.
66
+ 7. **Registry ↔ catalog parity.** A component in the runtime registry but
67
+ missing from catalog schemas silently drops from generated compositions —
68
+ `npm run check:registry-catalog-coherence` guards it; run it after catalog
69
+ changes.
70
+ 8. **Multi-turn emits A2UI `updateComponents` messages, not new compositions.**
71
+ The chunk-refiner mutates the binding plan via four ops (`rebindSlot` /
72
+ `appendToSlot` / `removeFromSlot` / `replacePage`); state chains via
73
+ `parent_state_id`. Spec: `genui-multiturn-architecture.md`.
74
+ 9. **Issue telemetry goes through `ctx.issueAccumulator`** (suppressed when
75
+ `ctx.evalMode` is true so evals stay clean). New engine failure paths plumb
76
+ the accumulator; never write records to disk directly. Durable issue
77
+ tracking belongs in GitHub issues / PR descriptions, not the engine store.
78
+ 10. **A new LLM-calling engine adopts `validate-and-repair.js` as its final
79
+ stage** — full schema/catalog/anti-pattern conformance, orthogonal to
80
+ whatever narrower plan/grounding validation the engine already runs
81
+ (chunk-zettel's slot bindings, free-form's ingredient grounding). It
82
+ is NOT automatic: a raw-HTML-wrapper engine (chunk-zettel today, see
83
+ TKT-0009) gets zero value from it — check the engine's actual output
84
+ shape is a real component graph before wiring it in.
85
+ 11. **Escalation across engine tiers is the dispatcher's call, never the
86
+ engine's own** — `registry.js`'s adapter wrappers (not
87
+ `generate-instant.js`/`generate-pro.js` themselves) decide whether a
88
+ hard-fail escalates to a stronger tier, capped at one hop. See
89
+ `generateInstantAdapter`'s comment for the worked example.
90
+ 12. **`harvest-chunks.mjs` validates every templated chunk at admission
91
+ time** (validate-only, via the same shared module) — report-only by
92
+ default; `--strict` refuses to write on any invalid chunk but is NOT
93
+ the default for `npm run harvest:chunks` (TKT-0010: 27% of the corpus
94
+ currently fails, mostly a schema-generation gap around `data-*`/`span`
95
+ attributes, not corpus-content defects — see the ticket before
96
+ assuming a chunk is actually broken).
97
+
98
+ ## Test + run commands (all verified in root package.json)
99
+
100
+ ```bash
101
+ npm run test:a2ui # smoke checks, no LLM (22/22, +1 skipped OK)
102
+ npm run test:a2ui:full # + thinking mode (calls API)
103
+ npm run test:evals # 5-dimension quality evals (mcp/evals/evals.json); --save-baseline via test:evals:baseline
104
+ npm run generate "login form" # instant-mode CLI (mcp/scripts/generate.mjs)
105
+ npm run smoke:engines # all registered engines
106
+ npm run smoke:register-engine # in-process registration (11/11)
107
+ npm run eval:diff -- --engine zettel # floors in SKILL.md
108
+ npm run harvest:chunks # re-harvest corpus (dry: harvest:chunks:dry)
109
+ npm run smoke:chunks # stub-LLM chunk smoke (re-harvests as side effect!)
110
+ npm run eval:chunk-synthesis # hold-out intents against real LLM
111
+ npm run build:embeddings:chunks
112
+ npm run smoke:refine && npm run smoke:state-cache && npm run smoke:issues
113
+ npm run eval:refine-synthesis # multi-turn refinement quality
114
+ npm run feedback:report && npm run feedback:promote
115
+ ```