@adia-ai/adia-ui-forge 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/plugin.json +23 -0
- package/CHANGELOG.md +26 -0
- package/README.md +54 -0
- package/bin/forge-lint +263 -0
- package/bin/lib/audit-axes.mjs +555 -0
- package/bin/lib/dry-run-irreversible.mjs +236 -0
- package/bin/lib/run-skill-evals.mjs +487 -0
- package/bin/lib/teach-router.mjs +250 -0
- package/commands/adia-forge-a2ui.md +10 -0
- package/commands/adia-forge-author.md +10 -0
- package/commands/adia-forge-dogfood.md +8 -0
- package/commands/adia-forge-llm.md +8 -0
- package/commands/adia-forge-orient.md +10 -0
- package/commands/adia-forge-release.md +8 -0
- package/commands/adia-forge-review.md +10 -0
- package/hooks/hooks.json +15 -0
- package/package.json +41 -0
- package/references/shared/content-trust.md +76 -0
- package/references/shared/pev-rationale.md +64 -0
- package/references/shared/skill-conventions.md +133 -0
- package/skills/adia-ui-a2ui/CHANGELOG.md +32 -0
- package/skills/adia-ui-a2ui/SKILL.md +243 -0
- package/skills/adia-ui-a2ui/evals/adversarial-corpus.json +75 -0
- package/skills/adia-ui-a2ui/evals/routing-corpus.json +31 -0
- package/skills/adia-ui-a2ui/evals/teach-routing-cases.json +100 -0
- package/skills/adia-ui-a2ui/references/anti-patterns.md +24 -0
- package/skills/adia-ui-a2ui/references/chunk-authoring.md +88 -0
- package/skills/adia-ui-a2ui/references/corpus-discipline.md +56 -0
- package/skills/adia-ui-a2ui/references/eval-diagnostics.md +127 -0
- package/skills/adia-ui-a2ui/references/fragment-graph.md +91 -0
- package/skills/adia-ui-a2ui/references/mcp-pipeline-ops.md +106 -0
- package/skills/adia-ui-a2ui/references/mcp-tool-reference.md +398 -0
- package/skills/adia-ui-a2ui/references/pipeline-overview.md +175 -0
- package/skills/adia-ui-a2ui/references/semantic-fail-lifting.md +120 -0
- package/skills/adia-ui-a2ui/references/strategy-engines.md +111 -0
- package/skills/adia-ui-a2ui/references/teach-protocol.md +220 -0
- package/skills/adia-ui-a2ui/references/zettel-calibration.md +93 -0
- package/skills/adia-ui-a2ui/scripts/audit-a2ui-roster.mjs +96 -0
- package/skills/adia-ui-a2ui/scripts/teach-route.mjs +157 -0
- package/skills/adia-ui-a2ui/skill.json +38 -0
- package/skills/adia-ui-authoring/CHANGELOG.md +32 -0
- package/skills/adia-ui-authoring/SKILL.md +256 -0
- package/skills/adia-ui-authoring/assets/case-studies/admin-shell-decomposition.md +101 -0
- package/skills/adia-ui-authoring/assets/case-studies/maxtokens-32768-discovery.md +109 -0
- package/skills/adia-ui-authoring/assets/case-studies/theme-panel-promotion.md +113 -0
- package/skills/adia-ui-authoring/evals/adversarial-design-plan-gates.json +138 -0
- package/skills/adia-ui-authoring/evals/routing-corpus.json +245 -0
- package/skills/adia-ui-authoring/references/anti-patterns.md +606 -0
- package/skills/adia-ui-authoring/references/api-contract.md +205 -0
- package/skills/adia-ui-authoring/references/authoring-cycle.md +211 -0
- package/skills/adia-ui-authoring/references/canonical-pattern-index.md +179 -0
- package/skills/adia-ui-authoring/references/code-style.md +329 -0
- package/skills/adia-ui-authoring/references/common-gotchas.md +93 -0
- package/skills/adia-ui-authoring/references/composite-demo-protocol.md +1084 -0
- package/skills/adia-ui-authoring/references/css-patterns.md +364 -0
- package/skills/adia-ui-authoring/references/lifecycle-patterns.md +302 -0
- package/skills/adia-ui-authoring/references/llm-bridge.md +254 -0
- package/skills/adia-ui-authoring/references/module-promotion.md +289 -0
- package/skills/adia-ui-authoring/references/primitive-audit.md +123 -0
- package/skills/adia-ui-authoring/references/shell-patterns.md +561 -0
- package/skills/adia-ui-authoring/references/teach-protocol.md +428 -0
- package/skills/adia-ui-authoring/references/token-contract.md +120 -0
- package/skills/adia-ui-authoring/references/worked-example.md +351 -0
- package/skills/adia-ui-authoring/references/yaml-contract.md +224 -0
- package/skills/adia-ui-authoring/scripts/audit-authoring-roster.mjs +148 -0
- package/skills/adia-ui-authoring/scripts/build-canonical-pattern-index.mjs +199 -0
- package/skills/adia-ui-authoring/skill.json +45 -0
- package/skills/adia-ui-dogfood/CHANGELOG.md +17 -0
- package/skills/adia-ui-dogfood/README.md +62 -0
- package/skills/adia-ui-dogfood/SKILL.md +866 -0
- package/skills/adia-ui-dogfood/scripts/analyze.mjs +603 -0
- package/skills/adia-ui-dogfood/skill.json +40 -0
- package/skills/adia-ui-forge/SKILL.md +88 -0
- package/skills/adia-ui-forge/evals/routing-corpus.json +30 -0
- package/skills/adia-ui-gen-review/CHANGELOG.md +108 -0
- package/skills/adia-ui-gen-review/SKILL.md +266 -0
- package/skills/adia-ui-gen-review/references/loop-protocol.md +712 -0
- package/skills/adia-ui-gen-review/references/rubric-cosmetic.md +144 -0
- package/skills/adia-ui-gen-review/references/rubric-decompose.md +117 -0
- package/skills/adia-ui-gen-review/references/rubric-score.md +249 -0
- package/skills/adia-ui-gen-review/references/scores.schema.json +125 -0
- package/skills/adia-ui-gen-review/references/teach-protocol.md +214 -0
- package/skills/adia-ui-gen-review/scripts/gen-review-coverage-audit.mjs +127 -0
- package/skills/adia-ui-gen-review/scripts/gen-review-decompose.mjs +569 -0
- package/skills/adia-ui-gen-review/scripts/gen-review-status.mjs +176 -0
- package/skills/adia-ui-gen-review/scripts/validate-cycle-scores.mjs +198 -0
- package/skills/adia-ui-gen-review/skill.json +22 -0
- package/skills/adia-ui-llm/CHANGELOG.md +25 -0
- package/skills/adia-ui-llm/SKILL.md +165 -0
- package/skills/adia-ui-llm/evals/adversarial-corpus.json +75 -0
- package/skills/adia-ui-llm/evals/routing-corpus.json +30 -0
- package/skills/adia-ui-llm/evals/teach-routing-cases.json +73 -0
- package/skills/adia-ui-llm/references/adapter-contract.md +99 -0
- package/skills/adia-ui-llm/references/add-a-provider.md +90 -0
- package/skills/adia-ui-llm/references/bridge-facade.md +94 -0
- package/skills/adia-ui-llm/references/browser-proxy-boundary.md +89 -0
- package/skills/adia-ui-llm/references/model-registry.md +75 -0
- package/skills/adia-ui-llm/references/streaming-sse.md +95 -0
- package/skills/adia-ui-llm/references/teach-protocol.md +78 -0
- package/skills/adia-ui-llm/scripts/audit-llm-roster.mjs +93 -0
- package/skills/adia-ui-llm/scripts/teach-route.mjs +119 -0
- package/skills/adia-ui-llm/skill.json +33 -0
- package/skills/adia-ui-release/CHANGELOG.md +23 -0
- package/skills/adia-ui-release/SKILL.md +295 -0
- package/skills/adia-ui-release/assets/case-studies/2026-05-20-batch-push-v0.6.14-v0.6.15.md +144 -0
- package/skills/adia-ui-release/assets/case-studies/2026-05-20-corpus-drift-remediation-v0.6.15.md +155 -0
- package/skills/adia-ui-release/assets/case-studies/2026-05-20-version-skip-correction-v0.6.12.md +114 -0
- package/skills/adia-ui-release/assets/case-studies/2026-05-21-author-from-scratch-v0.6.18.md +139 -0
- package/skills/adia-ui-release/assets/case-studies/2026-05-21-feedback37-retraction-v0.6.21.md +124 -0
- package/skills/adia-ui-release/assets/case-studies/2026-05-21-fn1-enrichment-pass-v0.6.19.md +125 -0
- package/skills/adia-ui-release/assets/case-studies/2026-05-21-stale-test-detection-v0.6.20.md +142 -0
- package/skills/adia-ui-release/assets/case-studies/2026-05-23-freshness-gate-recovery-v0.6.32.md +97 -0
- package/skills/adia-ui-release/assets/case-studies/2026-05-26-catalog-drift-recurring-v0.6.40.md +147 -0
- package/skills/adia-ui-release/assets/templates/stub-changelog.template.md +22 -0
- package/skills/adia-ui-release/evals/evals.json +164 -0
- package/skills/adia-ui-release/references/changelog-discipline.md +250 -0
- package/skills/adia-ui-release/references/cycle-happy-path.md +520 -0
- package/skills/adia-ui-release/references/exe-deploy.md +149 -0
- package/skills/adia-ui-release/references/gates-catalog.md +778 -0
- package/skills/adia-ui-release/references/ledger-discipline.md +232 -0
- package/skills/adia-ui-release/references/migration-guide-authoring.md +174 -0
- package/skills/adia-ui-release/references/multi-agent-baseline.md +207 -0
- package/skills/adia-ui-release/references/notes-authoring.md +212 -0
- package/skills/adia-ui-release/references/recovery-paths.md +215 -0
- package/skills/adia-ui-release/references/rollup-notes.md +208 -0
- package/skills/adia-ui-release/references/teach-protocol.md +468 -0
- package/skills/adia-ui-release/scripts/assert-monorepo-root.mjs +49 -0
- package/skills/adia-ui-release/scripts/audit-gate-roster.mjs +196 -0
- package/skills/adia-ui-release/scripts/bump.mjs +118 -0
- package/skills/adia-ui-release/scripts/dispatch-publish.mjs +151 -0
- package/skills/adia-ui-release/scripts/insert-stub.mjs +127 -0
- package/skills/adia-ui-release/scripts/make-ledger.mjs +179 -0
- package/skills/adia-ui-release/scripts/promote-unreleased.mjs +88 -0
- package/skills/adia-ui-release/scripts/release-pack.mjs +350 -0
- package/skills/adia-ui-release/scripts/tag-lockstep.mjs +110 -0
- package/skills/adia-ui-release/skill.json +75 -0
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# Reference: Corpus discipline — keyword coverage, metadata, retrieval
|
|
2
|
+
|
|
3
|
+
**Source:** Absorbed from the former `a2ui-pipeline` skill (§Common Pitfalls + §Improving Search Quality + §Extract.js Parser Lessons) and `adia-ui-training` (§Pitfalls) — Phase 3 rollup. **Used by:** mode 3 of `adia-ui-a2ui` (after harvest; before promotion). **Companion:** `chunk-authoring.md`.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Common Pitfalls (pipeline-side)
|
|
8
|
+
|
|
9
|
+
- **"Thinking mode returns garbage"** → check API keys are in .env AND load-env.mjs is imported
|
|
10
|
+
- **"Search misses obvious patterns"** → check metadata: run `npm run pipeline:stats`, inspect descriptions with `node -e "...listPatterns()..."`
|
|
11
|
+
- **"Gate rejects valid intents"** → the intent words are filtered by GATE_STOPS set and 3-char minimum. Check if the key word is in the stop list
|
|
12
|
+
- **"extract.js produces 'content card' descriptions"** → the HTML pattern lacks headings, labels, or icons. Improve the training HTML source
|
|
13
|
+
- **"extract.js crashes on c.text.toLowerCase"** → a button has `text=""` parsed as boolean `true`. Guard with `typeof c.text === 'string'`
|
|
14
|
+
- **Synonym map** lives in `pattern-library.js` (grep `_synonyms` / `synonyms` — populated from `_taxonomy.json` at ingest time).
|
|
15
|
+
- **Subagent-authored training pages** frequently misuse component APIs (timeline-ui with nested content instead of label/description attrs, table-ui wrapping native `<table>` causing "No data" overlay). Always visual QA after subagent creates training pages.
|
|
16
|
+
- **extract.js cleans output dirs** on each run (rm + mkdir). Stale chunk files from old parser runs are removed automatically.
|
|
17
|
+
- **Visual QA is mandatory** for training pages. User expects screenshots of every new page before it's considered done.
|
|
18
|
+
- **`npm run smoke:chunks` re-harvests chunks as a side effect** — touching `packages/a2ui/corpus/chunks/*.json` mtimes. After running smokes, do NOT `git add -A` blindly; stage only the files you actually changed (`git add <path>`).
|
|
19
|
+
- **chunk-synthesizer retrieval threshold is 8** (not the keyword score; it's the blended keyword + 5×cosine when embeddings are available). Below the threshold the synthesis fallback fires. Tune the threshold in `chunk-synthesizer.js`, not the keyword score in `chunk-library.js`.
|
|
20
|
+
- **Page-kind chunks must declare slot regions via `data-chunk-slot="X"`** — these are NOT themselves chunks. Slot names are validated against the page-kind chunk's slot list at compose time. Adding a new page-kind chunk requires adding a corresponding entry in the slot-validation map (see `chunk-composer.js`).
|
|
21
|
+
- **state_id is opaque to callers** — it's generated by the engine on createSurface. Don't construct or parse it. Refinements MUST pass back the prior `state_id` so the cache can chain through `parent_state_id`.
|
|
22
|
+
|
|
23
|
+
## Improving Search Quality Without New Infrastructure
|
|
24
|
+
|
|
25
|
+
When search quality is poor but you can't add embeddings/vector DB, three high-leverage interventions compose multiplicatively:
|
|
26
|
+
|
|
27
|
+
1. **Enrich extracted metadata** — Derive descriptions from structural signals (headings, labels, icons, button text, component composition). A card with Input("Email") + Input("Password") + Button("Sign In") = "Login form with email, password fields" not "Card from auth: card-42". Took search from 40% → 95% meaningful descriptions.
|
|
28
|
+
|
|
29
|
+
2. **Wire up existing semantic search** — Check if LLM-enhanced search exists but isn't called. `semanticSearchPatterns()` was built but `searchBlocks()` (keyword-only) was the only path used. One-line wiring change, massive impact. Always trace which search path each mode actually invokes.
|
|
30
|
+
|
|
31
|
+
3. **Expand synonym maps** — Hand-curated conceptual mappings (115 entries, 12 categories) bridge user vocabulary to pattern vocabulary. "inbox" → notification, "checkout" → form+cart+payment. Order: metadata first → synonyms → semantic wiring.
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## Extract.js Parser Lessons
|
|
36
|
+
|
|
37
|
+
The HTML parser in extract.js has been rewritten twice. Key pitfalls:
|
|
38
|
+
|
|
39
|
+
1. **Regex `([\s\S]*?)` can't handle nested same-name tags** — `<card-ui><card-ui>inner</card-ui></card-ui>` matches the inner close tag. The fix is a depth-tracking parser that counts open/close tags.
|
|
40
|
+
|
|
41
|
+
2. **`parseElement` must only put DIRECT children in `comp.children`** — not the full flattened subtree. Identify direct children as components NOT listed as a child of any other component in the recursive result.
|
|
42
|
+
|
|
43
|
+
3. **`reIdTree` must use array-index-based mapping** — not original-ID-based. Multiple components can share the same original ID (from the page-level flat list). Map by index to guarantee uniqueness.
|
|
44
|
+
|
|
45
|
+
4. **`getSubtree` needs a visited set** — without it, shared children get visited multiple times through different parent paths, producing duplicates.
|
|
46
|
+
|
|
47
|
+
5. **Always clean output dirs before extraction** — `rm chunks/*.json pages/*.json` then extract. Old files with different naming from previous parser versions persist and pollute ingestion.
|
|
48
|
+
|
|
49
|
+
## Pitfalls (training-flow side)
|
|
50
|
+
|
|
51
|
+
- **Forgetting the examples step** — a new component with only source files won't show up in retrieval for its variants. Training coverage drops silently.
|
|
52
|
+
- **A2UI component naming** — use `component: "Chat"` (PascalCase class name from `.a2ui.json`), not `"chat-ui"` (tag). The registry maps class → tag.
|
|
53
|
+
- **Child-of relationships** — `Segment` lives inside `SegmentedControl`; `ChatInput` is a sibling of `Chat`; `TimelineItem` inside `Timeline`. Examples must include the parent wrapper when the child can't stand alone.
|
|
54
|
+
- **Keywords shrink on extraction** — when a monolithic pattern becomes a fragment + composition, keywords on the composition must be enriched to compensate (see `fragment-graph.md` — keyword preservation).
|
|
55
|
+
- **Stale training artifacts** — `training/pages/*.json` and `chunks/*.json` are outputs, not sources. Regenerate with `node packages/a2ui/corpus/scripts/extract.js` (or `npm run harvest:chunks` for the chunk corpus path). Never hand-edit them.
|
|
56
|
+
- **`test:a2ui` "post-ingest count"** can mislead — it combines live catalog registrations with training-extracted patterns. If the extract.js path is wrong, the number can still look reasonable thanks to component examples.
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
# Reference: Eval diagnostics — gap diagnosis + regression triage
|
|
2
|
+
|
|
3
|
+
**Source:** Absorbed from the former `a2ui-pipeline` skill (§Eval Gap Diagnosis + §Eval Suite + §Verification) — Phase 3 rollup. **Used by:** mode 6 of `adia-ui-a2ui` (diagnose eval gap or regression). **Companion:** `strategy-engines.md`, `zettel-calibration.md`.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Eval Gap Diagnosis
|
|
8
|
+
|
|
9
|
+
**Mandatory first step — run diagnostics before touching code.**
|
|
10
|
+
|
|
11
|
+
The #1 anti-pattern: "tweak and hope" — changing a prompt or adding a chunk without knowing why the current score is low. This wastes eval runs (real LLM cost per run).
|
|
12
|
+
|
|
13
|
+
## Phase 0: Author a diagnostic script
|
|
14
|
+
|
|
15
|
+
For each failing intent, capture:
|
|
16
|
+
|
|
17
|
+
1. **Search ranking** — what `searchChunks()` returns for the intent query
|
|
18
|
+
2. **Composition output** — what the pipeline actually emitted (HTML or plan)
|
|
19
|
+
3. **Tag inventory** — every custom-element-like tag in emitted HTML
|
|
20
|
+
4. **Coverage delta** — expected_components vs. found_components
|
|
21
|
+
|
|
22
|
+
Diagnostic must be runnable in **stub mode** (no LLM cost).
|
|
23
|
+
|
|
24
|
+
Template:
|
|
25
|
+
|
|
26
|
+
```js
|
|
27
|
+
async function diagnose(intent) {
|
|
28
|
+
const search = searchChunks(intent.intent, { limit: 10 });
|
|
29
|
+
const comp = await composeFromIntent({ intent: intent.intent, llmAdapter: null });
|
|
30
|
+
const tags = [...comp.html.matchAll(/<([a-z]+-[a-z-]+)[\s>]/gi)]
|
|
31
|
+
.map(m => m[1]).filter((v,i,a) => a.indexOf(v)===i);
|
|
32
|
+
const found = intent.expected_components.filter(tag =>
|
|
33
|
+
new RegExp(`<${kebab(tag)}-ui[\\s>]`).test(comp.html));
|
|
34
|
+
return { search, tags, found, missing: intent.expected_components.filter(t => !found.includes(t)) };
|
|
35
|
+
}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Phase 1: Classify failures
|
|
39
|
+
|
|
40
|
+
| Bucket | Symptom | Root cause | Speed |
|
|
41
|
+
| --- | --- | --- | --- |
|
|
42
|
+
| A. Holdout misalignment | Top-1 retrieved ≠ expected_chunk | Holdout expected_chunk drifted from corpus | Minutes |
|
|
43
|
+
| B. Coverage gap | Retrieved chunk exists but HTML lacks expected tags | Chunk HTML doesn't contain those components | Hours (new chunks) |
|
|
44
|
+
| C. Wrong shell | Retrieval OK but LLM picks wrong page shell | Prompt ambiguity or missing domain→shell mapping | Hours (prompt tuning) |
|
|
45
|
+
| D. Broken render | HTML emitted but console errors / blank | Missing component registrations or bad markup | Hours (harvester bug) |
|
|
46
|
+
| E. Measurement bug | Composition looks correct but score low | Scoring code has regex/casing/substring bug | Minutes (fix scorer) |
|
|
47
|
+
| F. Embedding drift | Async search returns different top-1 than sync | Embeddings non-deterministically boost wrong chunks | Minutes (use sync for fast path) |
|
|
48
|
+
|
|
49
|
+
**Fix order:** A → E → F → C → B → D
|
|
50
|
+
|
|
51
|
+
- Fix measurement before fixing content (otherwise you can't trust scores)
|
|
52
|
+
- Fix determinism before adding content (otherwise evals fluctuate)
|
|
53
|
+
|
|
54
|
+
## Phase 2: Specific fixes
|
|
55
|
+
|
|
56
|
+
### A. Holdout alignment
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
npm run eval:diagnose
|
|
60
|
+
# Or directly: node packages/a2ui/mcp/scripts/eval-fix.mjs --verbose
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Map each intent's `expected_chunk` to the actual top-1 retrieved chunk. Update `holdout-compose-from-chunks.jsonl`.
|
|
64
|
+
|
|
65
|
+
### E. Measurement bugs (common traps)
|
|
66
|
+
|
|
67
|
+
1. **PascalCase → kebab-case** — `AgentTrace` must become `agent-trace` (not `agenttrace`)
|
|
68
|
+
2. **Substring match** — `pane` matching `panel` gives false positives
|
|
69
|
+
3. **Regex escaping** — `textarea-ui` contains `text-ui` as substring; need word boundaries
|
|
70
|
+
4. **Case sensitivity** — HTML may be uppercase; regex needs `/i` flag
|
|
71
|
+
|
|
72
|
+
### F. Embedding drift
|
|
73
|
+
|
|
74
|
+
Symptom: `searchChunksAsync` returns different top-1 than `searchChunks`. Root cause: small cosine boosts (0.1–0.3) flip rankings unpredictably. Fix: prefer sync keyword search for deterministic paths (fast-retrieval tier). Keep async for synthesis-tier only (where creativity is desired).
|
|
75
|
+
|
|
76
|
+
### B. Coverage gap (adding block chunks)
|
|
77
|
+
|
|
78
|
+
When the LLM needs components not in corpus:
|
|
79
|
+
|
|
80
|
+
1. Create `catalog/ui-patterns/app/<name>/<name>.contents.html`
|
|
81
|
+
2. Use `data-chunk="<name>"` + `data-chunk-kind="block"` markers
|
|
82
|
+
3. Include actual component tags (`<input-ui>`, `<button-ui>`, etc.) so coverage scoring matches
|
|
83
|
+
4. Re-harvest: `npm run harvest:chunks`
|
|
84
|
+
|
|
85
|
+
### C. Wrong shell (prompt tuning)
|
|
86
|
+
|
|
87
|
+
When the LLM consistently picks the wrong page shell:
|
|
88
|
+
|
|
89
|
+
1. Check `SYSTEM_PROMPT` domain→shell mapping table
|
|
90
|
+
2. Add explicit examples for the failing domain
|
|
91
|
+
3. Add negative constraint: "NEVER default to dashboard-admin-page for non-dashboard intents"
|
|
92
|
+
|
|
93
|
+
### D. Broken render
|
|
94
|
+
|
|
95
|
+
When HTML is structurally valid but render score is low:
|
|
96
|
+
|
|
97
|
+
1. Check `render-fidelity.mjs` output: console errors, blank viewport, undefined custom elements
|
|
98
|
+
2. Verify component registrations in `packages/web-components/index.js`
|
|
99
|
+
3. Check harvester didn't strip `data-chunk-slot` from page shells
|
|
100
|
+
|
|
101
|
+
## Verification
|
|
102
|
+
|
|
103
|
+
After all fixes, run:
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
# Stub first (fast, free)
|
|
107
|
+
npm run eval:compose-from-chunks
|
|
108
|
+
|
|
109
|
+
# Then real-LLM if stub improved
|
|
110
|
+
npm run eval:compose-from-chunks -- --real-llm --report-file
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Stop only when all intents pass and avg is stable across 3 runs.
|
|
114
|
+
|
|
115
|
+
## Eval Suite
|
|
116
|
+
|
|
117
|
+
`packages/a2ui/mcp/scripts/test-evals.mjs` scores generated output on 5 weighted dimensions:
|
|
118
|
+
|
|
119
|
+
- structural_validity (30%): schema validation score
|
|
120
|
+
- intent_alignment (25%): F1 of required vs present components
|
|
121
|
+
- component_coverage (20%): absence of forbidden patterns
|
|
122
|
+
- card_model_compliance (15%): header/section/footer structure
|
|
123
|
+
- anti_pattern_count (10%): text without variant, header children without slot
|
|
124
|
+
|
|
125
|
+
Baseline regression: `--save-baseline` stores scores, subsequent runs flag any dimension dropping
|
|
126
|
+
|
|
127
|
+
> 5 points or aggregate dropping >3 points. Exit code 2 = regression.
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# Reference: Fragment graph — leverage rule, extraction, $fragment refs
|
|
2
|
+
|
|
3
|
+
**Source:** Absorbed from the former `a2ui-pipeline` skill (§Fragment Extraction) and `zettel-internals` (§How fragment-graph composer resolves `$fragment` refs) — Phase 3 rollup. **Used by:** mode 4 of `adia-ui-a2ui` (extract a fragment). **Companion:** `strategy-engines.md`, `chunk-authoring.md`.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Fragment Extraction
|
|
8
|
+
|
|
9
|
+
Use when converting a monolithic pattern into a composition that references one or more zettel fragments, OR when authoring a new fragment from shared substructure across multiple patterns.
|
|
10
|
+
|
|
11
|
+
## The leverage rule
|
|
12
|
+
|
|
13
|
+
**Do not extract a fragment unless it has leverage ≥ 3** — i.e., at least 3 compositions would use it. Two exceptions:
|
|
14
|
+
|
|
15
|
+
1. Singleton fragment that closes a well-defined semantic gap (e.g., `kbd-shortcut-row` even at leverage 1, because "a keyboard shortcut row" is a distinct domain primitive).
|
|
16
|
+
|
|
17
|
+
2. **Intra-composition multi-use.** A single composition that instantiates the same fragment N times (N ≥ ~10) also justifies extraction — reuse ratio is `fragment_refs / composition_nodes`, not `fragments / comps`. Example: `calendar-day-cell` is used 35× inside one composition (`calendar-month-view`), and this alone lifted corpus reuse from 26.4% → 33.5%. Treat a repeating subtree of 10+ instances as a fragment even if no other composition references it yet.
|
|
18
|
+
|
|
19
|
+
Sub-leverage fragments bloat the library, slow retrieval, and make maintenance harder without improving reuse.
|
|
20
|
+
|
|
21
|
+
## Steps
|
|
22
|
+
|
|
23
|
+
1. **Candidate identification**
|
|
24
|
+
- Look for repeated subtrees across ≥ 3 patterns
|
|
25
|
+
- Common candidates: card headers, key-value rows, icon+text rows, labeled progress bars, stat displays, notification rows
|
|
26
|
+
- Check for existing fragment first: `ls packages/a2ui/compose/fragments/` or call the `zettel_stats` MCP tool to see the current corpus
|
|
27
|
+
|
|
28
|
+
2. **Fragment authoring**
|
|
29
|
+
- File: `packages/a2ui/compose/fragments/<name>.json`
|
|
30
|
+
- Schema: `{ name, description, keywords, slots, template }`
|
|
31
|
+
- Keywords should be rich — retrieval depends on them
|
|
32
|
+
- Slots use `{ $slot: 'name' }` placeholders in the template
|
|
33
|
+
|
|
34
|
+
3. **Composition refactor**
|
|
35
|
+
- Replace the inlined subtree with `{ $fragment: 'name', bindings: {...} }`
|
|
36
|
+
- Composer at `packages/a2ui/compose/strategies/zettel/composer.js` handles namespaced id rewriting and slot expansion
|
|
37
|
+
|
|
38
|
+
4. **KEYWORD PRESERVATION (critical)**
|
|
39
|
+
- Extraction SHRINKS the composition's own keyword surface
|
|
40
|
+
- Before: the composition contained all the subtree's text
|
|
41
|
+
- After: the composition only references the fragment
|
|
42
|
+
- **Fix:** enrich the composition's `keywords` field with the semantic tokens that left with the fragment
|
|
43
|
+
- Without this, retrieval degrades silently
|
|
44
|
+
|
|
45
|
+
5. **Rebuild + verify**
|
|
46
|
+
- `node scripts/build/components.mjs`
|
|
47
|
+
- Run the full structural-gate sweep (see SKILL.md §Plan-Execute-Verify)
|
|
48
|
+
- Specifically check `eval:diff --engine zettel` for coverage + avgScore regressions
|
|
49
|
+
|
|
50
|
+
## The extraction drift lesson
|
|
51
|
+
|
|
52
|
+
During the zettel migration, extracting `card-header-with-description` from the `login-form` pattern dropped its retrieval score from 28 → 24. The cause: the composition lost all the "login", "sign in", "email password" tokens because those moved into the fragment slot bindings.
|
|
53
|
+
|
|
54
|
+
Fix applied: added `keywords: ["login", "sign in", "email", "password", "authenticate"]` explicitly to the composition. Retrieval rebounded to 28.
|
|
55
|
+
|
|
56
|
+
**Lesson:** every fragment-using composition must have richer keywords than its inlined predecessor.
|
|
57
|
+
|
|
58
|
+
## Threshold calibration
|
|
59
|
+
|
|
60
|
+
If a known-good intent starts scoring below the retrieval threshold after extraction, DO NOT lower the threshold first. First:
|
|
61
|
+
|
|
62
|
+
1. Verify the composition's keywords were preserved
|
|
63
|
+
2. Check the fragment's description doesn't cannibalize the composition's semantic space
|
|
64
|
+
3. Only then consider threshold adjustment (current: 22; lowering below 20 starts producing false matches)
|
|
65
|
+
|
|
66
|
+
## Current corpus state
|
|
67
|
+
|
|
68
|
+
For the live fragment count, reuse ratio, and top-leverage fragments, run the `zettel_stats` MCP tool or `npm run pipeline:stats` — these are runtime-derived, not pinned in this reference (the numbers drift as the corpus grows).
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
## How fragment-graph composer resolves `$fragment` refs
|
|
73
|
+
|
|
74
|
+
`composer.js` algorithm (memorize before modifying):
|
|
75
|
+
|
|
76
|
+
```text
|
|
77
|
+
For each node in the composition template:
|
|
78
|
+
If node.$fragment:
|
|
79
|
+
1. Clone the fragment's template
|
|
80
|
+
2. Prefix every internal id with the composition-node id (avoid collisions)
|
|
81
|
+
e.g. fragment node "fi-in" under composition node "email" → "email--fi-in"
|
|
82
|
+
3. Apply slot bindings (set the attribute on the slot's targetId)
|
|
83
|
+
slots: [{ name: "label", targetId: "fi-field", attribute: "label", required: true }]
|
|
84
|
+
bindings: { label: "Email" } → cloned[0].label = "Email"
|
|
85
|
+
4. If node.children was provided, append those ids to the fragment root's children
|
|
86
|
+
5. Emit the fragment root under node.id, then the rest of the fragment nodes
|
|
87
|
+
Else:
|
|
88
|
+
Emit the node as-is
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
**Don't change ID prefixing without a corpus-wide search** — fragments that compose nest IDs predictably; consumers may pattern-match on the `{compNode}--{fragNode}` shape.
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# Reference: MCP pipeline operations — generate → validate → render → feedback
|
|
2
|
+
|
|
3
|
+
**Source:** Absorbed from the former `adia-ui-training` skill (§Workflows + §Validation checks + §Feedback loop) — Phase 3 rollup. **Used by:** mode 1 of `adia-ui-a2ui` (operator-side MCP runs). **Companion:** `mcp-tool-reference.md`, `anti-patterns.md`, `eval-diagnostics.md`.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Workflows
|
|
8
|
+
|
|
9
|
+
### Full pipeline — one command
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
node scripts/mcp-pipeline.cjs "dashboard with 4 stat cards and a revenue chart"
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Pipes `intent → generate_ui → validate_schema → a2ui-to-html → check_anti_patterns` and prints the scores. Fastest way to confirm the whole stack is working after a change.
|
|
16
|
+
|
|
17
|
+
### Step-by-step (when you need to inspect intermediates)
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
# 1. Generate
|
|
21
|
+
node scripts/mcp-call.cjs generate_ui \
|
|
22
|
+
'{"intent":"dashboard with 4 stat cards","mode":"instant"}'
|
|
23
|
+
|
|
24
|
+
# 2. Validate — paste the messages array from step 1
|
|
25
|
+
node scripts/mcp-call.cjs validate_schema \
|
|
26
|
+
'{"messages":"<paste-messages-json>"}'
|
|
27
|
+
|
|
28
|
+
# 3. Render — inspect the HTML before anti-pattern scanning
|
|
29
|
+
echo '<paste-messages-json>' | node scripts/a2ui-to-html.cjs
|
|
30
|
+
|
|
31
|
+
# 4. Anti-pattern check
|
|
32
|
+
node scripts/mcp-call.cjs check_anti_patterns \
|
|
33
|
+
'{"html":"<paste-rendered-html>"}'
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### Validation-only (existing A2UI doc)
|
|
37
|
+
|
|
38
|
+
Useful for auditing training corpus entries. `validate_schema` is fast and deterministic — batch it over `packages/a2ui/corpus/patterns/**/*.json` to surface drift without re-running the generator.
|
|
39
|
+
|
|
40
|
+
### Multi-turn generation (legacy executionId chain)
|
|
41
|
+
|
|
42
|
+
Every `generate_ui` response includes an `executionId`. Pass it back on subsequent calls to keep the pattern-library context and the feedback record coherent — so a score submitted against that id attributes correctly to the original run.
|
|
43
|
+
|
|
44
|
+
### Compose from the chunk corpus
|
|
45
|
+
|
|
46
|
+
When the intent matches a known page-shape (auth flow, dashboard layout, error shell), prefer `compose_from_chunks` over `generate_ui`:
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
node scripts/mcp-call.cjs compose_from_chunks \
|
|
50
|
+
'{"intent":"sign-in card with email + password + OAuth"}'
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
The synthesizer first checks for a strong retrieval match (score ≥ 8); if no chunk dominates, it picks a `{page, slot_bindings}` plan from a pre-filtered ~30 catalog and materializes via the chunk composer. Validator enforces slot-name + chunk-kind contracts before HTML emission. Returns `{state_id, html, plan, candidates}` — pass `state_id` to `refine_composition` to mutate this surface in subsequent turns.
|
|
54
|
+
|
|
55
|
+
### Multi-turn refinement (state_id chain)
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
# Turn 1: create the surface
|
|
59
|
+
node scripts/mcp-call.cjs compose_from_chunks \
|
|
60
|
+
'{"intent":"sign-in card with email + password"}'
|
|
61
|
+
# → returns state_id "abc123"
|
|
62
|
+
|
|
63
|
+
# Turn 2: mutate by intent
|
|
64
|
+
node scripts/mcp-call.cjs refine_composition \
|
|
65
|
+
'{"state_id":"abc123","intent":"add OAuth row with Google + GitHub"}'
|
|
66
|
+
# → returns updateComponents A2UI messages + new state_id "def456"
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
The refiner runs two-pass synthesis:
|
|
70
|
+
|
|
71
|
+
1. **Locator** — identifies which slots are affected by the intent
|
|
72
|
+
2. **Modifier** — picks the op (`rebindSlot`/`appendToSlot`/`removeFromSlot`/`replacePage`) and target chunk
|
|
73
|
+
|
|
74
|
+
Validator-driven retry loop (default `maxAttempts=2`). Each refinement chains through `parent_state_id` so you can `get_state` and walk back through the conversation history.
|
|
75
|
+
|
|
76
|
+
### Reporting issues from the LLM side
|
|
77
|
+
|
|
78
|
+
If you (the agent) determine that the engine produced something that breaks expectations, fire `report_issue`:
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
node scripts/mcp-call.cjs report_issue \
|
|
82
|
+
'{"state_id":"abc123","reporter":"llm","reason":"slot-binding produced empty section","trace":{...}}'
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Lands as immutable JSON under `.brain/audit-history/issues/`. These become weekly-triage candidates for promotion to curated tickets when patterns emerge.
|
|
86
|
+
|
|
87
|
+
## Validation checks
|
|
88
|
+
|
|
89
|
+
`validate_schema` runs a weighted checklist (target aggregate ≥ 80). Common failures when training data drifts:
|
|
90
|
+
|
|
91
|
+
- `hasRootComponent` — missing `id: "root"` on the surface root.
|
|
92
|
+
- `cardContentModel` — section without `col-ui` wrapper, or heading inside section instead of header.
|
|
93
|
+
- `headingHierarchy` — skipped levels (h1 → h3).
|
|
94
|
+
- `flatAdjacency` — nested components instead of sibling id references.
|
|
95
|
+
|
|
96
|
+
Full list + weights: `mcp-tool-reference.md`.
|
|
97
|
+
|
|
98
|
+
## Feedback loop
|
|
99
|
+
|
|
100
|
+
After a run, score the output with `submit_feedback` keyed on the `executionId`. The feedback analyzer (`packages/a2ui/retrieval/feedback/feedback-analyzer.js`) aggregates these into:
|
|
101
|
+
|
|
102
|
+
- Per-intent quality trends.
|
|
103
|
+
- Promotion candidates (runs scoring ≥ 95 with ≥ 4 rating across 3+ runs become named patterns via `npm run feedback:promote --apply`).
|
|
104
|
+
- Gap registry — intents with no pattern match AND low scores land in `packages/a2ui/corpus/gaps/registry.json`.
|
|
105
|
+
|
|
106
|
+
Running `npm run feedback:report` surfaces the current state.
|