@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.
- package/.claude-plugin/plugin.json +3 -6
- package/CHANGELOG.md +27 -26
- package/README.md +35 -40
- package/agents/a2ui-engineer.md +26 -0
- package/agents/component-author.md +29 -0
- package/agents/framework-verifier.md +26 -0
- package/agents/release-engineer.md +25 -0
- package/agents/routing-corpus.json +318 -0
- package/bin/demo-postwrite-pattern-gate +94 -0
- package/bin/forge-lint +17 -2
- package/bin/sidecar-prewrite-guard +104 -0
- package/commands/deploy.md +9 -0
- package/commands/dogfood.md +10 -0
- package/commands/gen-review.md +9 -0
- package/commands/release.md +10 -0
- package/hooks/hooks.json +15 -0
- package/package.json +3 -5
- package/references/contracts/a2ui-mcp-surface.md +35 -0
- package/references/contracts/migration-guide-format.md +34 -0
- package/references/shared/content-trust.md +19 -74
- package/skills/adia-a2ui/SKILL.md +99 -0
- package/skills/{adia-ui-a2ui → adia-a2ui}/evals/routing-corpus.json +10 -10
- package/skills/adia-a2ui/references/anti-patterns.md +27 -0
- package/skills/adia-a2ui/references/chunk-authoring.md +71 -0
- package/skills/adia-a2ui/references/corpus-discipline.md +68 -0
- package/skills/adia-a2ui/references/eval-diagnostics.md +86 -0
- package/skills/adia-a2ui/references/format-extension-decisions.md +66 -0
- package/skills/adia-a2ui/references/leverage-rules.md +52 -0
- package/skills/adia-a2ui/references/mcp-pipeline-ops.md +83 -0
- package/skills/adia-a2ui/references/mcp-tool-reference.md +59 -0
- package/skills/adia-a2ui/references/pipeline-overview.md +115 -0
- package/skills/adia-a2ui/references/semantic-fail-lifting.md +74 -0
- package/skills/adia-a2ui/references/strategy-engines.md +109 -0
- package/skills/adia-a2ui/references/zettel-calibration.md +104 -0
- package/skills/adia-author/SKILL.md +112 -0
- package/skills/adia-author/evals/routing-corpus.json +222 -0
- package/skills/{adia-ui-authoring → adia-author}/references/anti-patterns.md +33 -4
- package/skills/{adia-ui-authoring → adia-author}/references/authoring-cycle.md +9 -11
- package/skills/adia-author/references/canonical-pattern-index.md +243 -0
- package/skills/{adia-ui-authoring → adia-author}/references/code-style.md +19 -21
- package/skills/adia-author/references/common-gotchas.md +129 -0
- package/skills/adia-author/references/composite-demo-protocol.md +271 -0
- package/skills/{adia-ui-authoring → adia-author}/references/css-patterns.md +19 -6
- package/skills/{adia-ui-authoring → adia-author}/references/lifecycle-patterns.md +1 -0
- package/skills/{adia-ui-authoring → adia-author}/references/llm-bridge.md +3 -5
- package/skills/{adia-ui-authoring → adia-author}/references/module-promotion.md +22 -40
- package/skills/{adia-ui-authoring → adia-author}/references/primitive-audit.md +2 -4
- package/skills/{adia-ui-authoring → adia-author}/references/shell-patterns.md +12 -18
- package/skills/{adia-ui-authoring → adia-author}/references/token-contract.md +5 -7
- package/skills/{adia-ui-authoring → adia-author}/references/worked-example.md +3 -3
- package/skills/{adia-ui-authoring → adia-author}/references/yaml-contract.md +3 -3
- package/skills/{adia-ui-authoring → adia-author}/scripts/build-canonical-pattern-index.mjs +3 -3
- package/skills/adia-deploy/SKILL.md +128 -0
- package/skills/adia-deploy/references/deploy-playbooks.md +276 -0
- package/skills/adia-dogfood/SKILL.md +124 -0
- package/skills/adia-dogfood/references/admin-shell-anatomy.md +77 -0
- package/skills/adia-dogfood/references/app-shell-pitfalls.md +66 -0
- package/skills/adia-dogfood/references/card-anatomy-sweep.md +66 -0
- package/skills/adia-dogfood/references/html-attr-sweep.md +63 -0
- package/skills/adia-dogfood/references/native-leak-annotations.md +73 -0
- package/skills/adia-dogfood/references/visual-probe-triage.md +80 -0
- package/skills/{adia-ui-dogfood → adia-dogfood}/scripts/analyze.mjs +30 -14
- package/skills/adia-gen-review/SKILL.md +127 -0
- package/skills/adia-gen-review/references/corpus-html-patterns.md +279 -0
- package/skills/adia-gen-review/references/loop-protocol.md +216 -0
- package/skills/adia-gen-review/references/rubric-cosmetic.md +100 -0
- package/skills/adia-gen-review/references/rubric-decompose.md +101 -0
- package/skills/adia-gen-review/references/rubric-score.md +214 -0
- package/skills/{adia-ui-gen-review → adia-gen-review}/references/scores.schema.json +2 -2
- package/skills/{adia-ui-gen-review → adia-gen-review}/scripts/gen-review-coverage-audit.mjs +4 -4
- package/skills/{adia-ui-gen-review → adia-gen-review}/scripts/gen-review-decompose.mjs +10 -6
- package/skills/{adia-ui-gen-review → adia-gen-review}/scripts/gen-review-status.mjs +5 -5
- package/skills/{adia-ui-gen-review → adia-gen-review}/scripts/validate-cycle-scores.mjs +5 -5
- package/skills/adia-llm-internals/SKILL.md +77 -0
- package/skills/{adia-ui-llm → adia-llm-internals}/references/adapter-contract.md +4 -2
- package/skills/{adia-ui-llm → adia-llm-internals}/references/add-a-provider.md +4 -5
- package/skills/{adia-ui-llm → adia-llm-internals}/references/bridge-facade.md +10 -3
- package/skills/{adia-ui-llm → adia-llm-internals}/references/browser-proxy-boundary.md +1 -1
- package/skills/{adia-ui-llm → adia-llm-internals}/references/model-registry.md +5 -3
- package/skills/{adia-ui-llm → adia-llm-internals}/references/streaming-sse.md +2 -2
- package/skills/adia-release/SKILL.md +72 -0
- package/skills/adia-release/references/changelog-discipline.md +119 -0
- package/skills/adia-release/references/cut-procedure.md +247 -0
- package/skills/adia-release/references/gates-catalog.md +222 -0
- package/skills/adia-release/references/independent-package-release.md +52 -0
- package/skills/adia-release/references/migration-guide-authoring.md +86 -0
- package/skills/adia-release/references/notes-authoring.md +90 -0
- package/skills/adia-release/references/recovery-paths.md +106 -0
- package/skills/{adia-ui-release → adia-release}/scripts/bump.mjs +27 -2
- package/skills/{adia-ui-release → adia-release}/scripts/dispatch-publish.mjs +72 -6
- package/skills/{adia-ui-release → adia-release}/scripts/insert-stub.mjs +6 -4
- package/skills/adia-release/scripts/package-paths.mjs +50 -0
- package/skills/{adia-ui-release → adia-release}/scripts/promote-unreleased.mjs +4 -3
- package/skills/{adia-ui-release → adia-release}/scripts/release-pack.mjs +48 -27
- package/skills/{adia-ui-release → adia-release}/scripts/tag-lockstep.mjs +15 -3
- package/skills/adia-site-docs/SKILL.md +68 -0
- package/skills/adia-site-docs/evals/audit-report.md +30 -0
- package/skills/adia-site-docs/evals/routing-corpus.json +176 -0
- package/skills/adia-site-docs/intent.md +72 -0
- package/bin/lib/audit-axes.mjs +0 -555
- package/bin/lib/dry-run-irreversible.mjs +0 -236
- package/bin/lib/run-skill-evals.mjs +0 -487
- package/bin/lib/teach-router.mjs +0 -250
- package/commands/adia-forge-a2ui.md +0 -10
- package/commands/adia-forge-author.md +0 -10
- package/commands/adia-forge-dogfood.md +0 -8
- package/commands/adia-forge-llm.md +0 -8
- package/commands/adia-forge-orient.md +0 -10
- package/commands/adia-forge-release.md +0 -8
- package/commands/adia-forge-review.md +0 -10
- package/references/shared/pev-rationale.md +0 -64
- package/references/shared/skill-conventions.md +0 -133
- package/skills/adia-ui-a2ui/CHANGELOG.md +0 -32
- package/skills/adia-ui-a2ui/SKILL.md +0 -243
- package/skills/adia-ui-a2ui/evals/adversarial-corpus.json +0 -75
- package/skills/adia-ui-a2ui/evals/teach-routing-cases.json +0 -100
- package/skills/adia-ui-a2ui/references/anti-patterns.md +0 -24
- package/skills/adia-ui-a2ui/references/chunk-authoring.md +0 -88
- package/skills/adia-ui-a2ui/references/corpus-discipline.md +0 -56
- package/skills/adia-ui-a2ui/references/eval-diagnostics.md +0 -127
- package/skills/adia-ui-a2ui/references/fragment-graph.md +0 -91
- package/skills/adia-ui-a2ui/references/mcp-pipeline-ops.md +0 -106
- package/skills/adia-ui-a2ui/references/mcp-tool-reference.md +0 -398
- package/skills/adia-ui-a2ui/references/pipeline-overview.md +0 -175
- package/skills/adia-ui-a2ui/references/semantic-fail-lifting.md +0 -120
- package/skills/adia-ui-a2ui/references/strategy-engines.md +0 -111
- package/skills/adia-ui-a2ui/references/teach-protocol.md +0 -220
- package/skills/adia-ui-a2ui/references/zettel-calibration.md +0 -93
- package/skills/adia-ui-a2ui/scripts/audit-a2ui-roster.mjs +0 -96
- package/skills/adia-ui-a2ui/scripts/teach-route.mjs +0 -157
- package/skills/adia-ui-a2ui/skill.json +0 -38
- package/skills/adia-ui-authoring/CHANGELOG.md +0 -32
- package/skills/adia-ui-authoring/SKILL.md +0 -256
- package/skills/adia-ui-authoring/assets/case-studies/admin-shell-decomposition.md +0 -101
- package/skills/adia-ui-authoring/assets/case-studies/maxtokens-32768-discovery.md +0 -109
- package/skills/adia-ui-authoring/assets/case-studies/theme-panel-promotion.md +0 -113
- package/skills/adia-ui-authoring/evals/adversarial-design-plan-gates.json +0 -138
- package/skills/adia-ui-authoring/evals/routing-corpus.json +0 -245
- package/skills/adia-ui-authoring/references/canonical-pattern-index.md +0 -179
- package/skills/adia-ui-authoring/references/common-gotchas.md +0 -93
- package/skills/adia-ui-authoring/references/composite-demo-protocol.md +0 -1084
- package/skills/adia-ui-authoring/references/teach-protocol.md +0 -428
- package/skills/adia-ui-authoring/scripts/audit-authoring-roster.mjs +0 -148
- package/skills/adia-ui-authoring/skill.json +0 -45
- package/skills/adia-ui-dogfood/CHANGELOG.md +0 -17
- package/skills/adia-ui-dogfood/README.md +0 -62
- package/skills/adia-ui-dogfood/SKILL.md +0 -866
- package/skills/adia-ui-dogfood/skill.json +0 -40
- package/skills/adia-ui-forge/SKILL.md +0 -88
- package/skills/adia-ui-forge/evals/routing-corpus.json +0 -30
- package/skills/adia-ui-gen-review/CHANGELOG.md +0 -108
- package/skills/adia-ui-gen-review/SKILL.md +0 -266
- package/skills/adia-ui-gen-review/references/loop-protocol.md +0 -712
- package/skills/adia-ui-gen-review/references/rubric-cosmetic.md +0 -144
- package/skills/adia-ui-gen-review/references/rubric-decompose.md +0 -117
- package/skills/adia-ui-gen-review/references/rubric-score.md +0 -249
- package/skills/adia-ui-gen-review/references/teach-protocol.md +0 -214
- package/skills/adia-ui-gen-review/skill.json +0 -22
- package/skills/adia-ui-llm/CHANGELOG.md +0 -25
- package/skills/adia-ui-llm/SKILL.md +0 -165
- package/skills/adia-ui-llm/evals/adversarial-corpus.json +0 -75
- package/skills/adia-ui-llm/evals/routing-corpus.json +0 -30
- package/skills/adia-ui-llm/evals/teach-routing-cases.json +0 -73
- package/skills/adia-ui-llm/references/teach-protocol.md +0 -78
- package/skills/adia-ui-llm/scripts/audit-llm-roster.mjs +0 -93
- package/skills/adia-ui-llm/scripts/teach-route.mjs +0 -119
- package/skills/adia-ui-llm/skill.json +0 -33
- package/skills/adia-ui-release/CHANGELOG.md +0 -23
- package/skills/adia-ui-release/SKILL.md +0 -295
- package/skills/adia-ui-release/assets/case-studies/2026-05-20-batch-push-v0.6.14-v0.6.15.md +0 -144
- package/skills/adia-ui-release/assets/case-studies/2026-05-20-corpus-drift-remediation-v0.6.15.md +0 -155
- package/skills/adia-ui-release/assets/case-studies/2026-05-20-version-skip-correction-v0.6.12.md +0 -114
- package/skills/adia-ui-release/assets/case-studies/2026-05-21-author-from-scratch-v0.6.18.md +0 -139
- package/skills/adia-ui-release/assets/case-studies/2026-05-21-feedback37-retraction-v0.6.21.md +0 -124
- package/skills/adia-ui-release/assets/case-studies/2026-05-21-fn1-enrichment-pass-v0.6.19.md +0 -125
- package/skills/adia-ui-release/assets/case-studies/2026-05-21-stale-test-detection-v0.6.20.md +0 -142
- package/skills/adia-ui-release/assets/case-studies/2026-05-23-freshness-gate-recovery-v0.6.32.md +0 -97
- package/skills/adia-ui-release/assets/case-studies/2026-05-26-catalog-drift-recurring-v0.6.40.md +0 -147
- package/skills/adia-ui-release/assets/templates/stub-changelog.template.md +0 -22
- package/skills/adia-ui-release/evals/evals.json +0 -164
- package/skills/adia-ui-release/references/changelog-discipline.md +0 -250
- package/skills/adia-ui-release/references/cycle-happy-path.md +0 -520
- package/skills/adia-ui-release/references/exe-deploy.md +0 -149
- package/skills/adia-ui-release/references/gates-catalog.md +0 -778
- package/skills/adia-ui-release/references/ledger-discipline.md +0 -232
- package/skills/adia-ui-release/references/migration-guide-authoring.md +0 -174
- package/skills/adia-ui-release/references/multi-agent-baseline.md +0 -207
- package/skills/adia-ui-release/references/notes-authoring.md +0 -212
- package/skills/adia-ui-release/references/recovery-paths.md +0 -215
- package/skills/adia-ui-release/references/rollup-notes.md +0 -208
- package/skills/adia-ui-release/references/teach-protocol.md +0 -468
- package/skills/adia-ui-release/scripts/audit-gate-roster.mjs +0 -196
- package/skills/adia-ui-release/scripts/make-ledger.mjs +0 -179
- package/skills/adia-ui-release/skill.json +0 -75
- /package/skills/{adia-ui-authoring → adia-author}/references/api-contract.md +0 -0
- /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
|
+
```
|