@adia-ai/adia-ui-forge 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (136) hide show
  1. package/.claude-plugin/plugin.json +23 -0
  2. package/CHANGELOG.md +26 -0
  3. package/README.md +54 -0
  4. package/bin/forge-lint +263 -0
  5. package/bin/lib/audit-axes.mjs +555 -0
  6. package/bin/lib/dry-run-irreversible.mjs +236 -0
  7. package/bin/lib/run-skill-evals.mjs +487 -0
  8. package/bin/lib/teach-router.mjs +250 -0
  9. package/commands/adia-forge-a2ui.md +10 -0
  10. package/commands/adia-forge-author.md +10 -0
  11. package/commands/adia-forge-dogfood.md +8 -0
  12. package/commands/adia-forge-llm.md +8 -0
  13. package/commands/adia-forge-orient.md +10 -0
  14. package/commands/adia-forge-release.md +8 -0
  15. package/commands/adia-forge-review.md +10 -0
  16. package/hooks/hooks.json +15 -0
  17. package/package.json +41 -0
  18. package/references/shared/content-trust.md +76 -0
  19. package/references/shared/pev-rationale.md +64 -0
  20. package/references/shared/skill-conventions.md +133 -0
  21. package/skills/adia-ui-a2ui/CHANGELOG.md +32 -0
  22. package/skills/adia-ui-a2ui/SKILL.md +243 -0
  23. package/skills/adia-ui-a2ui/evals/adversarial-corpus.json +75 -0
  24. package/skills/adia-ui-a2ui/evals/routing-corpus.json +31 -0
  25. package/skills/adia-ui-a2ui/evals/teach-routing-cases.json +100 -0
  26. package/skills/adia-ui-a2ui/references/anti-patterns.md +24 -0
  27. package/skills/adia-ui-a2ui/references/chunk-authoring.md +88 -0
  28. package/skills/adia-ui-a2ui/references/corpus-discipline.md +56 -0
  29. package/skills/adia-ui-a2ui/references/eval-diagnostics.md +127 -0
  30. package/skills/adia-ui-a2ui/references/fragment-graph.md +91 -0
  31. package/skills/adia-ui-a2ui/references/mcp-pipeline-ops.md +106 -0
  32. package/skills/adia-ui-a2ui/references/mcp-tool-reference.md +398 -0
  33. package/skills/adia-ui-a2ui/references/pipeline-overview.md +175 -0
  34. package/skills/adia-ui-a2ui/references/semantic-fail-lifting.md +120 -0
  35. package/skills/adia-ui-a2ui/references/strategy-engines.md +111 -0
  36. package/skills/adia-ui-a2ui/references/teach-protocol.md +220 -0
  37. package/skills/adia-ui-a2ui/references/zettel-calibration.md +93 -0
  38. package/skills/adia-ui-a2ui/scripts/audit-a2ui-roster.mjs +96 -0
  39. package/skills/adia-ui-a2ui/scripts/teach-route.mjs +157 -0
  40. package/skills/adia-ui-a2ui/skill.json +38 -0
  41. package/skills/adia-ui-authoring/CHANGELOG.md +32 -0
  42. package/skills/adia-ui-authoring/SKILL.md +256 -0
  43. package/skills/adia-ui-authoring/assets/case-studies/admin-shell-decomposition.md +101 -0
  44. package/skills/adia-ui-authoring/assets/case-studies/maxtokens-32768-discovery.md +109 -0
  45. package/skills/adia-ui-authoring/assets/case-studies/theme-panel-promotion.md +113 -0
  46. package/skills/adia-ui-authoring/evals/adversarial-design-plan-gates.json +138 -0
  47. package/skills/adia-ui-authoring/evals/routing-corpus.json +245 -0
  48. package/skills/adia-ui-authoring/references/anti-patterns.md +606 -0
  49. package/skills/adia-ui-authoring/references/api-contract.md +205 -0
  50. package/skills/adia-ui-authoring/references/authoring-cycle.md +211 -0
  51. package/skills/adia-ui-authoring/references/canonical-pattern-index.md +179 -0
  52. package/skills/adia-ui-authoring/references/code-style.md +329 -0
  53. package/skills/adia-ui-authoring/references/common-gotchas.md +93 -0
  54. package/skills/adia-ui-authoring/references/composite-demo-protocol.md +1084 -0
  55. package/skills/adia-ui-authoring/references/css-patterns.md +364 -0
  56. package/skills/adia-ui-authoring/references/lifecycle-patterns.md +302 -0
  57. package/skills/adia-ui-authoring/references/llm-bridge.md +254 -0
  58. package/skills/adia-ui-authoring/references/module-promotion.md +289 -0
  59. package/skills/adia-ui-authoring/references/primitive-audit.md +123 -0
  60. package/skills/adia-ui-authoring/references/shell-patterns.md +561 -0
  61. package/skills/adia-ui-authoring/references/teach-protocol.md +428 -0
  62. package/skills/adia-ui-authoring/references/token-contract.md +120 -0
  63. package/skills/adia-ui-authoring/references/worked-example.md +351 -0
  64. package/skills/adia-ui-authoring/references/yaml-contract.md +224 -0
  65. package/skills/adia-ui-authoring/scripts/audit-authoring-roster.mjs +148 -0
  66. package/skills/adia-ui-authoring/scripts/build-canonical-pattern-index.mjs +199 -0
  67. package/skills/adia-ui-authoring/skill.json +45 -0
  68. package/skills/adia-ui-dogfood/CHANGELOG.md +17 -0
  69. package/skills/adia-ui-dogfood/README.md +62 -0
  70. package/skills/adia-ui-dogfood/SKILL.md +866 -0
  71. package/skills/adia-ui-dogfood/scripts/analyze.mjs +603 -0
  72. package/skills/adia-ui-dogfood/skill.json +40 -0
  73. package/skills/adia-ui-forge/SKILL.md +88 -0
  74. package/skills/adia-ui-forge/evals/routing-corpus.json +30 -0
  75. package/skills/adia-ui-gen-review/CHANGELOG.md +108 -0
  76. package/skills/adia-ui-gen-review/SKILL.md +266 -0
  77. package/skills/adia-ui-gen-review/references/loop-protocol.md +712 -0
  78. package/skills/adia-ui-gen-review/references/rubric-cosmetic.md +144 -0
  79. package/skills/adia-ui-gen-review/references/rubric-decompose.md +117 -0
  80. package/skills/adia-ui-gen-review/references/rubric-score.md +249 -0
  81. package/skills/adia-ui-gen-review/references/scores.schema.json +125 -0
  82. package/skills/adia-ui-gen-review/references/teach-protocol.md +214 -0
  83. package/skills/adia-ui-gen-review/scripts/gen-review-coverage-audit.mjs +127 -0
  84. package/skills/adia-ui-gen-review/scripts/gen-review-decompose.mjs +569 -0
  85. package/skills/adia-ui-gen-review/scripts/gen-review-status.mjs +176 -0
  86. package/skills/adia-ui-gen-review/scripts/validate-cycle-scores.mjs +198 -0
  87. package/skills/adia-ui-gen-review/skill.json +22 -0
  88. package/skills/adia-ui-llm/CHANGELOG.md +25 -0
  89. package/skills/adia-ui-llm/SKILL.md +165 -0
  90. package/skills/adia-ui-llm/evals/adversarial-corpus.json +75 -0
  91. package/skills/adia-ui-llm/evals/routing-corpus.json +30 -0
  92. package/skills/adia-ui-llm/evals/teach-routing-cases.json +73 -0
  93. package/skills/adia-ui-llm/references/adapter-contract.md +99 -0
  94. package/skills/adia-ui-llm/references/add-a-provider.md +90 -0
  95. package/skills/adia-ui-llm/references/bridge-facade.md +94 -0
  96. package/skills/adia-ui-llm/references/browser-proxy-boundary.md +89 -0
  97. package/skills/adia-ui-llm/references/model-registry.md +75 -0
  98. package/skills/adia-ui-llm/references/streaming-sse.md +95 -0
  99. package/skills/adia-ui-llm/references/teach-protocol.md +78 -0
  100. package/skills/adia-ui-llm/scripts/audit-llm-roster.mjs +93 -0
  101. package/skills/adia-ui-llm/scripts/teach-route.mjs +119 -0
  102. package/skills/adia-ui-llm/skill.json +33 -0
  103. package/skills/adia-ui-release/CHANGELOG.md +23 -0
  104. package/skills/adia-ui-release/SKILL.md +295 -0
  105. package/skills/adia-ui-release/assets/case-studies/2026-05-20-batch-push-v0.6.14-v0.6.15.md +144 -0
  106. package/skills/adia-ui-release/assets/case-studies/2026-05-20-corpus-drift-remediation-v0.6.15.md +155 -0
  107. package/skills/adia-ui-release/assets/case-studies/2026-05-20-version-skip-correction-v0.6.12.md +114 -0
  108. package/skills/adia-ui-release/assets/case-studies/2026-05-21-author-from-scratch-v0.6.18.md +139 -0
  109. package/skills/adia-ui-release/assets/case-studies/2026-05-21-feedback37-retraction-v0.6.21.md +124 -0
  110. package/skills/adia-ui-release/assets/case-studies/2026-05-21-fn1-enrichment-pass-v0.6.19.md +125 -0
  111. package/skills/adia-ui-release/assets/case-studies/2026-05-21-stale-test-detection-v0.6.20.md +142 -0
  112. package/skills/adia-ui-release/assets/case-studies/2026-05-23-freshness-gate-recovery-v0.6.32.md +97 -0
  113. package/skills/adia-ui-release/assets/case-studies/2026-05-26-catalog-drift-recurring-v0.6.40.md +147 -0
  114. package/skills/adia-ui-release/assets/templates/stub-changelog.template.md +22 -0
  115. package/skills/adia-ui-release/evals/evals.json +164 -0
  116. package/skills/adia-ui-release/references/changelog-discipline.md +250 -0
  117. package/skills/adia-ui-release/references/cycle-happy-path.md +520 -0
  118. package/skills/adia-ui-release/references/exe-deploy.md +149 -0
  119. package/skills/adia-ui-release/references/gates-catalog.md +778 -0
  120. package/skills/adia-ui-release/references/ledger-discipline.md +232 -0
  121. package/skills/adia-ui-release/references/migration-guide-authoring.md +174 -0
  122. package/skills/adia-ui-release/references/multi-agent-baseline.md +207 -0
  123. package/skills/adia-ui-release/references/notes-authoring.md +212 -0
  124. package/skills/adia-ui-release/references/recovery-paths.md +215 -0
  125. package/skills/adia-ui-release/references/rollup-notes.md +208 -0
  126. package/skills/adia-ui-release/references/teach-protocol.md +468 -0
  127. package/skills/adia-ui-release/scripts/assert-monorepo-root.mjs +49 -0
  128. package/skills/adia-ui-release/scripts/audit-gate-roster.mjs +196 -0
  129. package/skills/adia-ui-release/scripts/bump.mjs +118 -0
  130. package/skills/adia-ui-release/scripts/dispatch-publish.mjs +151 -0
  131. package/skills/adia-ui-release/scripts/insert-stub.mjs +127 -0
  132. package/skills/adia-ui-release/scripts/make-ledger.mjs +179 -0
  133. package/skills/adia-ui-release/scripts/promote-unreleased.mjs +88 -0
  134. package/skills/adia-ui-release/scripts/release-pack.mjs +350 -0
  135. package/skills/adia-ui-release/scripts/tag-lockstep.mjs +110 -0
  136. package/skills/adia-ui-release/skill.json +75 -0
@@ -0,0 +1,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.