@adia-ai/adia-ui-forge 0.8.62 → 0.8.64

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 (143) hide show
  1. package/.claude-plugin/plugin.json +2 -2
  2. package/.codex-plugin/plugin.json +3 -3
  3. package/CHANGELOG.md +317 -275
  4. package/README.md +54 -18
  5. package/__init__.py +8 -8
  6. package/agents/a2ui-maintenance-agent.corpus.json +7 -7
  7. package/agents/a2ui-maintenance-agent.md +6 -6
  8. package/agents/a2ui-planner-agent.md +11 -11
  9. package/agents/demo-audit-agent.corpus.json +4 -4
  10. package/agents/demo-audit-agent.md +8 -9
  11. package/agents/framework-checker.md +10 -11
  12. package/agents/framework-planner-agent.md +9 -10
  13. package/agents/gen-ui-review-agent.corpus.json +4 -4
  14. package/agents/gen-ui-review-agent.md +8 -8
  15. package/agents/package-release-agent.corpus.json +4 -4
  16. package/agents/package-release-agent.md +9 -10
  17. package/agents/primitive-authoring-agent.corpus.json +1 -1
  18. package/agents/primitive-authoring-agent.md +5 -5
  19. package/agents/routing-corpus.json +46 -46
  20. package/codex.interface.json +1 -1
  21. package/commands/gen-ui-review.md +1 -1
  22. package/commands/package-release.md +2 -2
  23. package/package.json +1 -1
  24. package/plugin.yaml +2 -2
  25. package/prompts/gen-ui-review.md +1 -1
  26. package/prompts/package-release.md +2 -2
  27. package/references/contracts/a2ui-mcp-surface.md +7 -7
  28. package/references/contracts/migration-guide-format.md +6 -7
  29. package/references/shared/content-trust.md +3 -5
  30. package/skills/a2ui-maintenance/SKILL.md +13 -16
  31. package/skills/a2ui-maintenance/evals/evals.json +2 -2
  32. package/skills/a2ui-maintenance/evals/routing-corpus.json +1 -1
  33. package/skills/a2ui-maintenance/references/anti-patterns.md +10 -10
  34. package/skills/a2ui-maintenance/references/chunk-authoring.md +11 -12
  35. package/skills/a2ui-maintenance/references/corpus-discipline.md +13 -14
  36. package/skills/a2ui-maintenance/references/data-model-reactivity.md +22 -25
  37. package/skills/a2ui-maintenance/references/eval-diagnostics.md +21 -21
  38. package/skills/a2ui-maintenance/references/format-extension-decisions.md +12 -13
  39. package/skills/a2ui-maintenance/references/leverage-rules.md +6 -6
  40. package/skills/a2ui-maintenance/references/mcp-pipeline-ops.md +9 -10
  41. package/skills/a2ui-maintenance/references/mcp-tool-reference.md +11 -12
  42. package/skills/a2ui-maintenance/references/pipeline-overview.md +33 -38
  43. package/skills/a2ui-maintenance/references/semantic-fail-lifting.md +13 -14
  44. package/skills/a2ui-maintenance/references/strategy-engines.md +14 -16
  45. package/skills/a2ui-maintenance/references/surface-lifecycle.md +10 -11
  46. package/skills/a2ui-maintenance/references/zettel-calibration.md +14 -16
  47. package/skills/component-md-authoring/SKILL.md +22 -22
  48. package/skills/component-md-authoring/agents/openai.yaml +1 -1
  49. package/skills/cross-harness-authoring-standards/SKILL.md +19 -21
  50. package/skills/cross-harness-authoring-standards/agents/openai.yaml +1 -1
  51. package/skills/cross-harness-compatibility-standards/SKILL.md +35 -34
  52. package/skills/cross-harness-compatibility-standards/agents/openai.yaml +1 -1
  53. package/skills/demo-audit/SKILL.md +38 -38
  54. package/skills/demo-audit/evals/routing-corpus.json +1 -1
  55. package/skills/demo-audit/references/admin-shell-anatomy.md +21 -25
  56. package/skills/demo-audit/references/app-shell-pitfalls.md +11 -13
  57. package/skills/demo-audit/references/auto-fix-allowlist.md +3 -3
  58. package/skills/demo-audit/references/card-anatomy-sweep.md +10 -11
  59. package/skills/demo-audit/references/chat-shell-anatomy.md +25 -26
  60. package/skills/demo-audit/references/editor-shell-anatomy.md +25 -28
  61. package/skills/demo-audit/references/html-attr-sweep.md +12 -13
  62. package/skills/demo-audit/references/mode7-status-battery.md +4 -4
  63. package/skills/demo-audit/references/native-leak-annotations.md +8 -8
  64. package/skills/demo-audit/references/output-contract-worked-example.md +8 -8
  65. package/skills/demo-audit/references/probe-discipline-and-escalation.md +2 -2
  66. package/skills/demo-audit/references/visual-probe-triage.md +21 -23
  67. package/skills/gen-ui-review/SKILL.md +28 -28
  68. package/skills/gen-ui-review/agents/openai.yaml +1 -1
  69. package/skills/gen-ui-review/evals/evals.json +1 -1
  70. package/skills/gen-ui-review/evals/routing-corpus.json +1 -1
  71. package/skills/gen-ui-review/references/corpus-html-patterns.md +28 -29
  72. package/skills/gen-ui-review/references/exit-gate-mechanics.md +5 -5
  73. package/skills/gen-ui-review/references/lookup-maintenance.md +2 -2
  74. package/skills/gen-ui-review/references/loop-protocol.md +25 -25
  75. package/skills/gen-ui-review/references/rubric-cosmetic.md +10 -10
  76. package/skills/gen-ui-review/references/rubric-decompose.md +15 -16
  77. package/skills/gen-ui-review/references/rubric-score.md +23 -24
  78. package/skills/gen-ui-review/references/scorecard-worked-examples.md +12 -12
  79. package/skills/gen-ui-review/references/scores.schema.json +2 -2
  80. package/skills/llm-client-maintenance/SKILL.md +12 -12
  81. package/skills/llm-client-maintenance/evals/routing-corpus.json +1 -1
  82. package/skills/llm-client-maintenance/references/adapter-contract.md +27 -27
  83. package/skills/llm-client-maintenance/references/add-a-provider.md +25 -25
  84. package/skills/llm-client-maintenance/references/bridge-facade.md +31 -31
  85. package/skills/llm-client-maintenance/references/browser-proxy-boundary.md +17 -17
  86. package/skills/llm-client-maintenance/references/model-registry.md +14 -14
  87. package/skills/llm-client-maintenance/references/streaming-sse.md +21 -21
  88. package/skills/package-release/SKILL.md +21 -23
  89. package/skills/package-release/evals/evals.json +2 -2
  90. package/skills/package-release/evals/routing-corpus.json +2 -2
  91. package/skills/package-release/references/authorization-model.md +6 -8
  92. package/skills/package-release/references/changelog-discipline.md +25 -27
  93. package/skills/package-release/references/cut-procedure.md +129 -136
  94. package/skills/package-release/references/gates-catalog.md +42 -43
  95. package/skills/package-release/references/independent-package-release.md +14 -15
  96. package/skills/package-release/references/invariants-detail.md +9 -9
  97. package/skills/package-release/references/mechanization.md +11 -12
  98. package/skills/package-release/references/migration-guide-authoring.md +20 -20
  99. package/skills/package-release/references/notes-authoring.md +20 -20
  100. package/skills/package-release/references/recovery-paths.md +28 -28
  101. package/skills/primitive-authoring/SKILL.md +19 -19
  102. package/skills/primitive-authoring/agents/openai.yaml +1 -1
  103. package/skills/primitive-authoring/evals/evals.json +2 -2
  104. package/skills/primitive-authoring/evals/routing-corpus.json +2 -2
  105. package/skills/primitive-authoring/references/INDEX.md +22 -22
  106. package/skills/primitive-authoring/references/anti-patterns.md +76 -77
  107. package/skills/primitive-authoring/references/api-contract.md +63 -65
  108. package/skills/primitive-authoring/references/authoring-cycle.md +47 -50
  109. package/skills/primitive-authoring/references/canonical-pattern-index.md +118 -118
  110. package/skills/primitive-authoring/references/code-style.md +50 -52
  111. package/skills/primitive-authoring/references/common-gotchas.md +23 -23
  112. package/skills/primitive-authoring/references/composite-demo-protocol.md +42 -45
  113. package/skills/primitive-authoring/references/css-patterns.md +97 -97
  114. package/skills/primitive-authoring/references/for-attribute-event-contract.md +12 -12
  115. package/skills/primitive-authoring/references/form-control-sizing.md +16 -18
  116. package/skills/primitive-authoring/references/lifecycle-patterns.md +42 -42
  117. package/skills/primitive-authoring/references/llm-bridge.md +21 -21
  118. package/skills/primitive-authoring/references/module-promotion.md +51 -51
  119. package/skills/primitive-authoring/references/primitive-audit.md +16 -16
  120. package/skills/primitive-authoring/references/shell-patterns.md +81 -81
  121. package/skills/primitive-authoring/references/svg-authoring.md +62 -66
  122. package/skills/primitive-authoring/references/token-contract.md +45 -49
  123. package/skills/primitive-authoring/references/trait-pages.md +8 -8
  124. package/skills/primitive-authoring/references/worked-example.md +39 -39
  125. package/skills/primitive-authoring/references/yaml-contract.md +139 -143
  126. package/skills/site-deployment/SKILL.md +25 -26
  127. package/skills/site-deployment/agents/openai.yaml +1 -1
  128. package/skills/site-deployment/evals/routing-corpus.json +1 -1
  129. package/skills/site-deployment/references/deploy-playbooks.md +51 -53
  130. package/skills/site-docs-authoring/SKILL.md +14 -14
  131. package/skills/site-docs-authoring/agents/openai.yaml +1 -1
  132. package/skills/site-docs-authoring/evals/evals.json +1 -1
  133. package/skills/site-docs-authoring/evals/routing-corpus.json +1 -1
  134. package/skills/site-docs-authoring/intent.md +18 -18
  135. package/skills/ssr-compatibility/SKILL.md +24 -25
  136. package/skills/ssr-compatibility/agents/openai.yaml +1 -1
  137. package/skills/ssr-compatibility/evals/evals.json +1 -1
  138. package/skills/ssr-compatibility/evals/routing-corpus.json +1 -1
  139. package/skills/ssr-compatibility/references/consumer-workarounds.md +22 -23
  140. package/skills/ssr-compatibility/references/failure-shapes.md +84 -91
  141. package/skills/ssr-compatibility/references/guard-patterns.md +54 -58
  142. package/skills/ssr-compatibility/references/status-ledger.md +17 -17
  143. package/skills/ssr-compatibility/references/test-without-linkedom.md +26 -27
@@ -1,21 +1,21 @@
1
- # Form-control sizing posture fill by default, `[inline]` hugs
1
+ # Form-control sizing posture, fill by default, `[inline]` hugs
2
2
 
3
3
  Source of truth: [ADR-0077](../../../../../../docs/ops/adr/adr-0077-form-control-sizing-posture.md)
4
4
  (ratified 2026-08-19; amends [ADR-0037](../../../../../../docs/ops/adr/adr-0037-inline-display-mode-convention.md)
5
- partial `[inline]` semantics widen to own the sizing axis, the display-default
5
+ partial, `[inline]` semantics widen to own the sizing axis, the display-default
6
6
  convention itself is unchanged). Companion SPEC:
7
7
  `docs/ops/spec/spec-form-control-sizing-posture.md`. Read the ADR before
8
- touching a form-control host's width/min-width rules this file is the
8
+ touching a form-control host's width/min-width rules: this file is the
9
9
  routing pointer + the fix-now checklist, not a restatement of the ruling.
10
10
 
11
11
  ## The one-line rule
12
12
 
13
13
  A form-control host, absent `[inline]`, is block-level (ADR-0037, unchanged)
14
- **and fills its container's available inline space** in block flow via
14
+ **and fills its container's available inline space**, in block flow via
15
15
  `width: auto` (already true), in flex/grid parents via `width: 100%` on the
16
16
  host (the mechanism `field-ui`/`otp-input-ui` already shipped). `[inline]`
17
17
  flips BOTH axes at once: inline-level display **and** shrink-wrap/hug sizing.
18
- No separate `hug` attribute exists or should be minted one token, one
18
+ No separate `hug` attribute exists or should be minted, one token, one
19
19
  posture flip.
20
20
 
21
21
  ## Where legibility floors live
@@ -23,7 +23,7 @@ posture flip.
23
23
  **[retired 2026-08-28, ADR-0095]** The `--*-min-width: 20ch`-class floor
24
24
  described below is no longer permitted anywhere, including on
25
25
  `:scope[inline]`. A primitive never floors its own inline-size independent
26
- of its own geometry the only permitted floor is
26
+ of its own geometry: the only permitted floor is
27
27
  `min-inline-size: var(--<component>-height)` (a square minimum). ADR-0095
28
28
  Decision Class 1 removes the 20ch-class field-width floor for
29
29
  `input-ui`/`select-ui`/`combobox-ui` (plus `--input-label-min-width`,
@@ -32,21 +32,20 @@ floor, generalizing the `button-ui` keep-square pattern already named below
32
32
  to the field-width components.
33
33
 
34
34
  **[amended 2026-08-31, gh#2534, ADR-0095]** The square-minimum rule governs
35
- a primitive's own OUTER inline-size floor against its own block-size only
36
- it does not reach a component's INTERNAL grid-column sizing. A token like
35
+ a primitive's own OUTER inline-size floor against its own block-size only, it does not reach a component's INTERNAL grid-column sizing. A token like
37
36
  `field-ui[inline]`'s `--field-label-inline-min: 10ch` floors the label
38
37
  *column* inside the component's internal grid, to keep sibling rows
39
- aligned not to prevent the host itself from collapsing so it is not
38
+ aligned, not to prevent the host itself from collapsing, so it is not
40
39
  "a `--*-min-width: 20ch`-class floor" in ADR-0095's sense and is not
41
40
  retired by the Decision above. Don't apply the square-minimum rule to an
42
41
  internal grid-column token by analogy to the field-width class.
43
42
 
44
- *(Historical, pre-ADR-0095 rule superseded by the retirement above, kept
43
+ *(Historical, pre-ADR-0095 rule, superseded by the retirement above, kept
45
44
  only for context on what the old convention was.)* A
46
45
  `--*-min-width: 20ch`-class floor (the gh#781/gh#1633 pattern on
47
46
  `input-ui`/`select-ui`/`combobox-ui`) used to belong on `:scope[inline]`
48
47
  only, never on the plain `:scope`. In the fill state the container owns the
49
- width, all the way down no exception, no re-added floor. Two floor
48
+ width, all the way down, no exception, no re-added floor. Two floor
50
49
  classes are
51
50
  explicitly NOT sizing-posture floors and stay in both states: inner
52
51
  `min-width: 0` flex unclampers (the ellipsis-reset machinery) and
@@ -60,18 +59,17 @@ posture, and also stay in both states.
60
59
  - Plain `:scope` (no `[inline]`): block-level, `width: auto` in block flow,
61
60
  `width: 100%` in flex/grid parents. No `min-width` legibility floor here.
62
61
  - `:scope[inline]`: inline-level display, shrink-wrap sizing. Per ADR-0095,
63
- no `--*-min-width: 20ch`-class floor moves here any more a floor-carrier
62
+ no `--*-min-width: 20ch`-class floor moves here any more, a floor-carrier
64
63
  component uses the square minimum (`min-inline-size:
65
64
  var(--<component>-height)`) instead, in both states.
66
65
  - A component that wants to own its line but still hug has no attribute for
67
- that state (ADR-0077 Decision item 6, accepted consequence) tell the
66
+ that state (ADR-0077 Decision item 6, accepted consequence), tell the
68
67
  consumer to use explicit sizing (`width: fit-content` or an explicit
69
68
  width), don't invent a third posture token.
70
69
  - `field-ui`/`fields-ui`'s `[inline]` carve-out (single-row layout meaning,
71
70
  ADR-0037 §5) is retained and stays behaviorally convergent with this
72
- decision don't "fix" it to match the family's token meaning.
73
- - The trip-wire gate is `scripts/release/check-display-default-convention.mjs`
74
- — it fails a 20ch-class host floor on the plain `:scope` and fails a
71
+ decision, don't "fix" it to match the family's token meaning.
72
+ - The trip-wire gate is `scripts/release/check-display-default-convention.mjs`, it fails a 20ch-class host floor on the plain `:scope` and fails a
75
73
  missing fill mechanism in the default state, for roster components. One
76
74
  gate, no second check to satisfy.
77
75
 
@@ -79,6 +77,6 @@ posture, and also stay in both states.
79
77
 
80
78
  The exact component roster and the breaking-wave/golden-regeneration rollout
81
79
  (one coordinated wave, MIGRATION GUIDE entry same cycle, deliberately
82
- regenerated `evals/visual/` goldens never additive-first, never shimmed)
80
+ regenerated `evals/visual/` goldens, never additive-first, never shimmed)
83
81
  are the companion PLAN's job (`docs/ops/plan/plan-form-control-sizing.md`),
84
- not this reference cite the ADR/PLAN rather than re-deriving scope here.
82
+ not this reference, cite the ADR/PLAN rather than re-deriving scope here.
@@ -1,10 +1,10 @@
1
- # Lifecycle Patterns Listeners, Timers, Observers, Popovers
1
+ # Lifecycle Patterns, Listeners, Timers, Observers, Popovers
2
2
 
3
3
  Deep dive on symmetric `connected()` / `disconnected()` in AdiaUI components. Read when adding any side effect (listener, timer, observer, popover) to a component, or when inheriting from `UIFormElement`.
4
4
 
5
5
  ## The symmetry rule
6
6
 
7
- Every side effect set up in `connected()` has a matching teardown in `disconnected()`. The teardown is authored at the same time as the setup, not later if you leave `disconnected()` for tomorrow, tomorrow won't happen.
7
+ Every side effect set up in `connected()` has a matching teardown in `disconnected()`. The teardown is authored at the same time as the setup, not later, if you leave `disconnected()` for tomorrow, tomorrow won't happen.
8
8
 
9
9
  ```javascript
10
10
  class UIWidget extends UIElement {
@@ -36,16 +36,16 @@ Four things happen in `disconnected()`:
36
36
  3. [Not shown] Clear any timers.
37
37
  4. [Not shown] Disconnect any observers.
38
38
 
39
- ## Stable handlers `#field` arrows, never inline arrows
39
+ ## Stable handlers, `#field` arrows, never inline arrows
40
40
 
41
41
  `removeEventListener(type, handler)` requires reference equality on `handler`. Inline arrows create a fresh function every time:
42
42
 
43
43
  ```javascript
44
- // WRONG the inline arrow is a new function every render
44
+ // WRONG: the inline arrow is a new function every render
45
45
  this.#buttonEl.addEventListener('click', (e) => this.doThing(e));
46
- // Later: removeEventListener can't match no reference equality.
46
+ // Later: removeEventListener can't match, no reference equality.
47
47
 
48
- // RIGHT stable reference via private field
48
+ // RIGHT, stable reference via private field
49
49
  #onButtonClick = (e) => this.doThing(e);
50
50
  // connected:
51
51
  this.#buttonEl.addEventListener('click', this.#onButtonClick);
@@ -57,10 +57,10 @@ The `#` prefix keeps the handler private to the class (can't be accidentally use
57
57
 
58
58
  **Historical bugs this pattern prevents:**
59
59
 
60
- - `cot.js:116` summary click handler as inline arrow. Couldn't be removed; fixed by promoting to `#onSummaryClick`.
61
- - Generic pattern across ~35 components in the audit cycle all promoted to stable fields.
60
+ - `cot.js:116`, summary click handler as inline arrow. Couldn't be removed; fixed by promoting to `#onSummaryClick`.
61
+ - Generic pattern across ~35 components in the audit cycle, all promoted to stable fields.
62
62
 
63
- ## Ephemeral DOM with inline arrows the tolerance rule
63
+ ## Ephemeral DOM with inline arrows, the tolerance rule
64
64
 
65
65
  There's ONE exception: inline arrows on DOM elements that are guaranteed to be fully detached + GC'd before the next re-render.
66
66
 
@@ -78,12 +78,12 @@ There's ONE exception: inline arrows on DOM elements that are guaranteed to be f
78
78
  }
79
79
  ```
80
80
 
81
- **This is tolerated but not preferred.** If anything about the lifetime changes if someone adds a `row.remove()` that leaves a cached reference, or converts to incremental DOM updates the listeners stop getting GC'd and become leaks.
81
+ **This is tolerated but not preferred.** If anything about the lifetime changes, if someone adds a `row.remove()` that leaves a cached reference, or converts to incremental DOM updates, the listeners stop getting GC'd and become leaks.
82
82
 
83
83
  The safer pattern is event delegation on the stable parent:
84
84
 
85
85
  ```javascript
86
- // Preferred one listener on the parent, reads data-attributes
86
+ // Preferred, one listener on the parent, reads data-attributes
87
87
  connected() {
88
88
  this.#container.addEventListener('click', this.#onContainerClick);
89
89
  }
@@ -102,7 +102,7 @@ disconnected() {
102
102
 
103
103
  One listener, always removable, survives DOM churn.
104
104
 
105
- ## Timers `setInterval`, `setTimeout`
105
+ ## Timers, `setInterval`, `setTimeout`
106
106
 
107
107
  Store the handle, clear it in `disconnected()`:
108
108
 
@@ -128,7 +128,7 @@ disconnected() {
128
128
 
129
129
  The null-guard is defensive; `clearInterval(null)` is actually a no-op but the guard makes the "was it created?" intent explicit.
130
130
 
131
- ## Observers ResizeObserver, MutationObserver, IntersectionObserver
131
+ ## Observers, ResizeObserver, MutationObserver, IntersectionObserver
132
132
 
133
133
  Every observer has `.disconnect()`:
134
134
 
@@ -146,28 +146,28 @@ disconnected() {
146
146
  }
147
147
  ```
148
148
 
149
- **Historical bug:** `chart.js` had TWO `disconnected()` methods the second one silently overrode the first. The first had the `ResizeObserver.disconnect()` call; the second didn't. Chart listeners leaked until a production repro surfaced it.
149
+ **Historical bug:** `chart.js` had TWO `disconnected()` methods, the second one silently overrode the first. The first had the `ResizeObserver.disconnect()` call; the second didn't. Chart listeners leaked until a production repro surfaced it.
150
150
 
151
- **Lesson:** a class must declare `disconnected()` exactly once. If you need to add teardown, edit the existing method. Duplicate method names silently shadow no error, no warning.
151
+ **Lesson:** a class must declare `disconnected()` exactly once. If you need to add teardown, edit the existing method. Duplicate method names silently shadow, no error, no warning.
152
152
 
153
- ## Reflection non-finite Number-typed props don't stringify
153
+ ## Reflection, non-finite Number-typed props don't stringify
154
154
 
155
- `reflect()` in `core/element.js` writes a `Number`-typed reflected property's value back onto the DOM attribute by stringifying it. That's correct for finite numbers, but a non-numeric sentinel assigned to a `Number`-typed reflected prop (e.g. `range-total="?"` table-ui's open/unproven-total marker for cursor/hasMore server paging) used to get silently coerced and re-stringified as the literal attribute `range-total="NaN"`, before any render ran (gh#1895, PR #1907, ADR-0082 amendment).
155
+ `reflect()` in `core/element.js` writes a `Number`-typed reflected property's value back onto the DOM attribute by stringifying it. That's correct for finite numbers, but a non-numeric sentinel assigned to a `Number`-typed reflected prop (e.g. `range-total="?"`, table-ui's open/unproven-total marker for cursor/hasMore server paging) used to get silently coerced and re-stringified as the literal attribute `range-total="NaN"`, before any render ran (gh#1895, PR #1907, ADR-0082 amendment).
156
156
 
157
- **Fix, framework-wide:** a non-finite `Number`-typed value is left unreflected instead of stringified gated narrowly on `Number` type + `Number.isFinite()` failing, so no other reflected prop's behavior changes.
157
+ **Fix, framework-wide:** a non-finite `Number`-typed value is left unreflected instead of stringified, gated narrowly on `Number` type + `Number.isFinite()` failing, so no other reflected prop's behavior changes.
158
158
 
159
159
  ```javascript
160
- // core/element.js reflect() the narrow gate
160
+ // core/element.js reflect(), the narrow gate
161
161
  if (type === Number && !Number.isFinite(value)) {
162
- // leave unreflected do NOT write "NaN" to the attribute
162
+ // leave unreflected, do NOT write "NaN" to the attribute
163
163
  } else {
164
164
  this.setAttribute(attr, String(value));
165
165
  }
166
166
  ```
167
167
 
168
- **Recognition:** any future non-numeric sentinel authored onto a `Number`-typed reflected prop (a `'?'`, an `'auto'`, an open-ended marker) hits the same wart unless it too fails `Number.isFinite()` before the stringify step. If a prop's contract legitimately mixes numbers with a sentinel string, declare it non-reflecting or widen its declared type don't rely on the reflect() gate alone as documentation of the contract.
168
+ **Recognition:** any future non-numeric sentinel authored onto a `Number`-typed reflected prop (a `'?'`, an `'auto'`, an open-ended marker) hits the same wart unless it too fails `Number.isFinite()` before the stringify step. If a prop's contract legitimately mixes numbers with a sentinel string, declare it non-reflecting or widen its declared type, don't rely on the reflect() gate alone as documentation of the contract.
169
169
 
170
- ## `UIFormElement` `super` discipline
170
+ ## `UIFormElement`, `super` discipline
171
171
 
172
172
  Form-participating components inherit `ElementInternals` wiring from `UIFormElement`. That wiring only works if `super.connected()` and `super.disconnected()` are called:
173
173
 
@@ -185,20 +185,20 @@ class UIInput extends UIFormElement {
185
185
  #onInput = () => this.syncValue(this.#inputEl?.value ?? '');
186
186
 
187
187
  connected() {
188
- super.connected(); // MUST registers ElementInternals, form association
188
+ super.connected(); // MUST, registers ElementInternals, form association
189
189
  this.#inputEl = this.querySelector('input');
190
190
  this.#inputEl?.addEventListener('input', this.#onInput);
191
191
  }
192
192
 
193
193
  disconnected() {
194
- super.disconnected(); // MUST unregisters form participation
194
+ super.disconnected(); // MUST, unregisters form participation
195
195
  this.#inputEl?.removeEventListener('input', this.#onInput);
196
196
  this.#inputEl = null;
197
197
  }
198
198
  }
199
199
  ```
200
200
 
201
- Omit `super.connected()` and the component won't participate in form submission the field value is never collected, no validation fires, no form-reset handler runs. It's a silent failure; you only find out when form submission misses the field.
201
+ Omit `super.connected()` and the component won't participate in form submission: the field value is never collected, no validation fires, no form-reset handler runs. It's a silent failure; you only find out when form submission misses the field.
202
202
 
203
203
  ## Popovers, tooltips, document-level overlays
204
204
 
@@ -290,7 +290,7 @@ disconnected() {
290
290
  }
291
291
  ```
292
292
 
293
- ## DOM-reference nulling why it matters
293
+ ## DOM-reference nulling, why it matters
294
294
 
295
295
  A cached DOM reference (`this.#fooEl`) held by the component class keeps the referenced element alive for GC purposes, along with its own subtree. When the component is removed from the DOM, the old tree hangs around in memory until the component itself is garbage-collected.
296
296
 
@@ -306,36 +306,36 @@ disconnected() {
306
306
  }
307
307
  ```
308
308
 
309
- Especially important for components that are attached/detached frequently (modals, toasts, drawers) each incarnation leaks its old subtree into memory if refs aren't released.
309
+ Especially important for components that are attached/detached frequently (modals, toasts, drawers), each incarnation leaks its old subtree into memory if refs aren't released.
310
310
 
311
- ## Adopt-or-stamp slot content is the source of truth
311
+ ## Adopt-or-stamp, slot content is the source of truth
312
312
 
313
313
  Every slot-bearing primitive that exposes both an attribute and a slot for the same logical content (`text=` + `<span slot="text">`) follows the `#stampMark` / `#wasStamped` policy (working reference: `list-item-ui`, `packages/web-components/components/list/list.js`):
314
314
 
315
315
  1. `connected()` either **adopts** the consumer-authored slotted child or **stamps** a new one, tagging it `data-{component}-stamped`.
316
- 2. `render()` may only mutate or remove elements carrying the stamp. An unstamped slotted child is consumer-owned leave it alone, even when the matching attribute is empty.
316
+ 2. `render()` may only mutate or remove elements carrying the stamp. An unstamped slotted child is consumer-owned, leave it alone, even when the matching attribute is empty.
317
317
  3. The attribute stays a convenience shorthand that produces a stamped element; both forms render identically and are interchangeable.
318
318
 
319
- The failure this prevents: a `render()` that overwrites `textContent` from the attribute, or hides/removes the slot element when the attribute is empty, silently destroys consumer-authored rich content (links, `<strong>`, inline icons). A 13-page audit found 73 slot uses where the slotted form is the only way to author the intended content (the destructive render() confirmed in 3 primitives) before the policy landed from the page author's seat it looks like "the docs are broken", from the component author's seat like "the consumer didn't pass the attribute"; both are wrong-blame. Source: ADR-0010.
319
+ The failure this prevents: a `render()` that overwrites `textContent` from the attribute, or hides/removes the slot element when the attribute is empty, silently destroys consumer-authored rich content (links, `<strong>`, inline icons). A 13-page audit found 73 slot uses where the slotted form is the only way to author the intended content (the destructive render() confirmed in 3 primitives) before the policy landed, from the page author's seat it looks like "the docs are broken", from the component author's seat like "the consumer didn't pass the attribute"; both are wrong-blame. Source: ADR-0010.
320
320
 
321
- ### `slot="caret"` the one disclosure-indicator slot
321
+ ### `slot="caret"`, the one disclosure-indicator slot
322
322
 
323
- The expand/collapse (and dropdown) directional-indicator slot is named `caret` on every primitive that exposes composition slots (`tree-item-ui`, `pane-ui`, `accordion-item-ui`, `nav-group-ui`, `select-ui`, `drilldown-ui`). `chevron` is retired a hard rename with no compat alias, and `--*-chevron-*` tokens became `--*-caret-*` (Phosphor names the glyphs `caret-right` / `caret-down`; there is no `chevron-*` icon).
323
+ The expand/collapse (and dropdown) directional-indicator slot is named `caret` on every primitive that exposes composition slots (`tree-item-ui`, `pane-ui`, `accordion-item-ui`, `nav-group-ui`, `select-ui`, `drilldown-ui`). `chevron` is retired, a hard rename with no compat alias, and `--*-chevron-*` tokens became `--*-caret-*` (Phosphor names the glyphs `caret-right` / `caret-down`; there is no `chevron-*` icon).
324
324
 
325
325
  - The mechanism is adopt-or-stamp (above): honor a declarative `<… slot="caret">` child if present, else stamp `<icon-ui slot="caret" name="caret-right">`. The same adopt-or-stamp rule applies to **every** composition slot the stamp builds (e.g. `slot="actions"`).
326
- - Rotation is driven by host open-state CSS (`:scope[open]` / `[collapsed]` / `[expanded]`) never by swapping the icon name.
327
- - Internal-render components with no composition slots (`agent-trace`, `agent-reasoning`) keep `[data-*-caret]` styling hooks the convention governs slots, not internal hooks.
326
+ - Rotation is driven by host open-state CSS (`:scope[open]` / `[collapsed]` / `[expanded]`), never by swapping the icon name.
327
+ - Internal-render components with no composition slots (`agent-trace`, `agent-reasoning`) keep `[data-*-caret]` styling hooks, the convention governs slots, not internal hooks.
328
328
 
329
329
  Source: ADR-0036, ADR-0092 (`drilldown-ui`'s row-level caret migrated from `slot="chevron"`,
330
330
  retained under ADR-0085, to `slot="caret"`, reversing that exemption).
331
331
 
332
332
  ## Anti-patterns to watch for
333
333
 
334
- - **Inline arrow + `removeEventListener`** the listener isn't really being removed. Silent leak.
335
- - **Forgot to create `disconnected()`** if `connected()` adds side effects, `disconnected()` must exist. No exceptions.
336
- - **Two `disconnected()` methods in the same class** the second silently wins; the first's cleanup is lost.
337
- - **`super.connected()` missing in `UIFormElement` subclass** form participation silently fails.
338
- - **Popover / tooltip never cleaned up** stays in `document.body` forever.
339
- - **Observer recreated on every render** re-observing without disconnecting first leaks observer refs.
340
- - **Timer fired after component removed** not cleared in `disconnected()`, fires on a dead component. Can cause null-deref crashes or phantom re-renders.
341
- - **Multi-`await` method that can be re-invoked before the previous call finishes** (a route change, search-as-you-type, tab switch) a guard checked only at ONE checkpoint doesn't protect the awaits after it; a slow-but-eventually-resuming call can pass every checkpoint it reaches and clobber a newer call's result. See [common-gotchas.md §6](common-gotchas.md#6-async-loadrender-function-completing-out-of-order--a-guard-at-the-checkpoint-isnt-enough) (the sequence-token pattern: claim before the first `await`, re-check after every subsequent one).
334
+ - **Inline arrow + `removeEventListener`**, the listener isn't really being removed. Silent leak.
335
+ - **Forgot to create `disconnected()`**, if `connected()` adds side effects, `disconnected()` must exist. No exceptions.
336
+ - **Two `disconnected()` methods in the same class**, the second silently wins; the first's cleanup is lost.
337
+ - **`super.connected()` missing in `UIFormElement` subclass**, form participation silently fails.
338
+ - **Popover / tooltip never cleaned up**, stays in `document.body` forever.
339
+ - **Observer recreated on every render**, re-observing without disconnecting first leaks observer refs.
340
+ - **Timer fired after component removed**, not cleared in `disconnected()`, fires on a dead component. Can cause null-deref crashes or phantom re-renders.
341
+ - **Multi-`await` method that can be re-invoked before the previous call finishes** (a route change, search-as-you-type, tab switch), a guard checked only at ONE checkpoint doesn't protect the awaits after it; a slow-but-eventually-resuming call can pass every checkpoint it reaches and clobber a newer call's result. See [common-gotchas.md §6](common-gotchas.md#6-async-loadrender-function-completing-out-of-order--a-guard-at-the-checkpoint-isnt-enough) (the sequence-token pattern: claim before the first `await`, re-check after every subsequent one).
@@ -1,9 +1,9 @@
1
- # LLM Bridge extension @adia-ai/llm
1
+ # LLM Bridge extension, @adia-ai/llm
2
2
 
3
- **Superseded 2026-07-18 `@adia-ai/llm` internals are `llm-client-maintenance`'s charter, not
3
+ **Superseded 2026-07-18, `@adia-ai/llm` internals are `llm-client-maintenance`'s charter, not
4
4
  `primitive-authoring`'s.** That skill's references (`adapter-contract.md`, `streaming-sse.md`,
5
5
  `model-registry.md`, `bridge-facade.md`, `browser-proxy-boundary.md`, `add-a-provider.md`)
6
- are the current, maintained source this file is kept only as a historical pointer and is
6
+ are the current, maintained source: this file is kept only as a historical pointer and is
7
7
  no longer linked from `primitive-authoring`'s task-shape table or `code-style.md`. Route provider/
8
8
  streaming/bridge work to `llm-client-maintenance`.
9
9
 
@@ -47,13 +47,13 @@ This reference is about **modifying** the package. For day-to-day consumption (i
47
47
  └── server.js ← Local proxy + static server (NOT shipped to npm)
48
48
  ```
49
49
 
50
- ## Hard rules defaults that bit us
50
+ ## Hard rules, defaults that bit us
51
51
 
52
52
  ### Rule 1: `maxTokens: 32768` is intentional
53
53
 
54
54
  The bridge hardcodes `maxTokens: 32768` in `complete()` and `stream()`. **Don't lower this.**
55
55
 
56
- A2UI JSON for moderately complex UIs (kanban, dashboard, pricing tier table) routinely exceeds 8k. An 8k truncation was discovered producing silent fallbacks the validator rubber-stamped at ~89/100 the generator looked healthy but was emitting incomplete component trees.
56
+ A2UI JSON for moderately complex UIs (kanban, dashboard, pricing tier table) routinely exceeds 8k. An 8k truncation was discovered producing silent fallbacks the validator rubber-stamped at ~89/100: the generator looked healthy but was emitting incomplete component trees.
57
57
 
58
58
  If you're adding a new provider that has a lower maxTokens ceiling (e.g., a model with 4k output limit), surface it as a model-level constraint in `MODELS`, **don't bypass the 32k bridge default**.
59
59
 
@@ -66,18 +66,18 @@ The bridge passes `cache: this.#provider === 'anthropic'`. This marks the ~23KB
66
66
  - Below provider's minimum cacheable size (1024 tok Sonnet/Opus, 2048 Haiku): no-op
67
67
  - Other providers silently ignore the unknown opt
68
68
 
69
- When adding a new provider, leave `cache` falsy unless that provider has its own caching mechanism then add a parallel `case` in the adapter's `buildRequest`.
69
+ When adding a new provider, leave `cache` falsy unless that provider has its own caching mechanism, then add a parallel `case` in the adapter's `buildRequest`.
70
70
 
71
71
  ### Rule 3: stopReason must propagate
72
72
 
73
73
  The bridge surfaces `stopReason` from the upstream response in both `complete()` and `stream()`'s terminal `done` chunk. Values:
74
74
 
75
- - `'end'` / `'stop'` / `'STOP'` clean completion
76
- - `'max_tokens'` / `'length'` / `'MAX_TOKENS'` (Gemini) **truncation**
77
- - `'tool_use'` / `'tool_calls'` tool-call paused output
78
- - `'error'` provider-side error
75
+ - `'end'` / `'stop'` / `'STOP'`, clean completion
76
+ - `'max_tokens'` / `'length'` / `'MAX_TOKENS'` (Gemini), **truncation**
77
+ - `'tool_use'` / `'tool_calls'`, tool-call paused output
78
+ - `'error'`, provider-side error
79
79
 
80
- **Downstream parser refuses silent fallback rendering when stopReason indicates truncation.** Don't normalize all values to `'end'` the generator's truncation detector reads the raw value.
80
+ **Downstream parser refuses silent fallback rendering when stopReason indicates truncation.** Don't normalize all values to `'end'`, the generator's truncation detector reads the raw value.
81
81
 
82
82
  ### Rule 4: lazy-load adapters in browser-facing modules
83
83
 
@@ -164,7 +164,7 @@ Edit `packages/llm/core/package.json`:
164
164
  }
165
165
  ```
166
166
 
167
- The `./adapters/*` glob already covers new files no edit needed unless you want a named subpath.
167
+ The `./adapters/*` glob already covers new files, no edit needed unless you want a named subpath.
168
168
 
169
169
  ### Step 4: Update `models.js` catalog
170
170
 
@@ -227,16 +227,16 @@ LLM_PROVIDER=myprovider node packages/gen-ui/mcp/gen-ui/scripts/eval-diff.mjs --
227
227
 
228
228
  ## Pitfalls
229
229
 
230
- - **Two proxy shapes `proxyUrl` is overloaded.** The bridge supports two architecturally different proxies and the dispatcher in `packages/llm/core/adapters/index.js` chooses between them by URL shape. Get this wrong and you get silent 401s in the browser even though `node packages/llm/core/server.js` works fine.
230
+ - **Two proxy shapes, `proxyUrl` is overloaded.** The bridge supports two architecturally different proxies and the dispatcher in `packages/llm/core/adapters/index.js` chooses between them by URL shape. Get this wrong and you get silent 401s in the browser even though `node packages/llm/core/server.js` works fine.
231
231
  - **Smart proxy** (`packages/llm/core/server.js`, route `POST /api/chat`): accepts a **provider-neutral body** `{ provider, model, messages, ... }`, holds the API key server-side, and dispatches internally to the right adapter. Headers are just `content-type: application/json`.
232
- - **Passthrough proxy** (Vite dev `/api/llm/<provider>/<rest>` → real upstream URL `https://api.<provider>.com/<rest>`): expects the **real upstream body shape** (Anthropic-shaped for `/anthropic/`, OpenAI-shaped for `/openai/`, etc.) plus the **adapter's own auth headers** (`x-api-key` + `anthropic-version`, or `Authorization: Bearer …`). The proxy is dumb it just rewrites the URL and forwards bytes.
232
+ - **Passthrough proxy** (Vite dev `/api/llm/<provider>/<rest>` → real upstream URL `https://api.<provider>.com/<rest>`): expects the **real upstream body shape** (Anthropic-shaped for `/anthropic/`, OpenAI-shaped for `/openai/`, etc.) plus the **adapter's own auth headers** (`x-api-key` + `anthropic-version`, or `Authorization: Bearer …`). The proxy is dumb, it just rewrites the URL and forwards bytes.
233
233
  - Dispatcher: `chat()` and `streamChat()` in `adapters/index.js` call `isPassthroughProxy(proxyUrl)` (regex `/\/api\/llm\/[a-z]+(\/|$)/`). If true, they call `passthroughRequest()` which builds the real upstream body via `adapter.buildRequest()` then swaps in `proxyUrl`. If false, they call `proxyRequest()` with the provider-neutral body.
234
- - **When adding a new provider**, make sure: (1) `buildRequest()` omits the API key header when `proxyUrl` is the smart proxy (already handled), (2) `buildRequest()` _includes_ the API key header when called from `passthroughRequest()` (also already handled passthroughRequest forwards adapter headers). The adapter's `buildRequest()` is the single source of truth for upstream-shape body + headers. Don't fork it for proxy mode.
234
+ - **When adding a new provider**, make sure: (1) `buildRequest()` omits the API key header when `proxyUrl` is the smart proxy (already handled), (2) `buildRequest()` _includes_ the API key header when called from `passthroughRequest()` (also already handled, passthroughRequest forwards adapter headers). The adapter's `buildRequest()` is the single source of truth for upstream-shape body + headers. Don't fork it for proxy mode.
235
235
  - **Don't add `apiKey` to the proxy body**. The proxy holds the key server-side. Adapter's `buildRequest` should set the API-key header only when `proxyUrl` is unset (i.e., direct mode).
236
236
  - **Don't normalize `stopReason`**. Each provider uses different terminal values (`max_tokens` vs `length` vs `MAX_TOKENS`); the truncation detector reads them raw.
237
237
  - **Don't import the package from itself.** `index.js` re-exports from `./adapters/index.js`; consumers inside the package reference relative paths to dodge the package-name resolution loop.
238
- - **`MODELS` is consumed by `<chat-input-ui>`** its shape is `[{ label, options: [{ value, label }] }]` matching `<select-ui>` with `<optgroup>`s. Don't deviate from this shape or 3 apps break.
239
- - **`DEFAULT_MODEL` is the cheapest option** resist the urge to default to a flagship model most consumers want fast/cheap by default.
238
+ - **`MODELS` is consumed by `<chat-input-ui>`**, its shape is `[{ label, options: [{ value, label }] }]` matching `<select-ui>` with `<optgroup>`s. Don't deviate from this shape or 3 apps break.
239
+ - **`DEFAULT_MODEL` is the cheapest option**, resist the urge to default to a flagship model, most consumers want fast/cheap by default.
240
240
 
241
241
  ## Verification gates
242
242
 
@@ -253,7 +253,7 @@ If you added a new SSE chunk type to the streaming protocol, verify all 7 consum
253
253
 
254
254
  ## Cross-references
255
255
 
256
- - [code-style.md](code-style.md) general AdiaUI conventions (this is a specialty path; start there if you need broader context)
257
- - **a2ui-maintenance** (sibling skill) generator / MCP / zettel synthesis (consumes the bridge)
258
- - Spec: `.claude/docs/specs/package-architecture.md` § 11 (Phase 5 engine registry)
259
- - README: `packages/llm/core/README.md` consumer-facing docs
256
+ - [code-style.md](code-style.md), general AdiaUI conventions (this is a specialty path; start there if you need broader context)
257
+ - **a2ui-maintenance** (sibling skill), generator / MCP / zettel synthesis (consumes the bridge)
258
+ - Spec: `.claude/docs/specs/package-architecture.md` § 11 (Phase 5, engine registry)
259
+ - README: `packages/llm/core/README.md`, consumer-facing docs