Repository navigation
spec(AST-110): a swizzled component builds on its own and behaves like the package - #9190
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
AI review status for this pull request.
|
PR Analysis Report
Generated by PR Enrichment workflow | View current CI run |
|
/approve-spec 3268411 |
There was a problem hiding this comment.
The swizzle output contract is clear and an engineering owner has approved its direction, but it needs a few fixes before it lands. Please reconcile the records it conflicts with and close the gaps below; the review has the evidence for each.
- FR5 puts a copy's styles in the package component's cascade layer, while
spec:AST-080FR7 says product rules stay outsideastryx-base; amend AST-080 in this pull request or reword FR5. - FR4 cites
architecture:public-component-apiINV23, which is not onmain; define the private-attribute owner module here or hold FR4 until that claim lands. - Add
architecture:layer-runtimeto the impact for FR3, list the Toast case (a copieduseToastnever sees the packageLayerProvider), and say in FR2 what happens when a helper another copy already wrote exists. - Cite
spec:AST-035FR12 instead of restating theswizzle --listrule in FR7, move the 0.7.5 counts and CI estimates to the description, and record the approval in the front matter before merge.
[Automated review]
| `xstyle`, and product CSS reach it the same way. The copy's styles sit in the | ||
| same cascade layer as the package component's, so a theme's component | ||
| overrides apply to the copy as they do to the package. |
There was a problem hiding this comment.
This puts a copy's styles in astryx-base, while spec:AST-080 FR7 says "Product rules stay outside astryx-base" and its Non-goals leave how a product compiles an ejected component to the product. Please amend AST-080 in this pull request or reword FR5 so the two records agree.
| reads a store of a different shape. A module whose state only caches work may | ||
| be duplicated by a copy. | ||
| - **FR4 — Private attributes stay contained.** Each private-attribute owner | ||
| module the component reaches (`architecture:public-component-api` INV23) is |
There was a problem hiding this comment.
architecture:public-component-api has no INV23 on main (its invariants end at INV22), so FR4 depends on a claim that does not exist yet. Define the private-attribute owner module here, or hold FR4 until INV23 lands.
| template_version: 2 | ||
| kind: system-spec | ||
| id: spec:AST-110 | ||
| authority: draft |
There was a problem hiding this comment.
The spec content is approved at this head; please record it here (authority, approved_by, approved_at, phase) and in DEC-2's Decider before merge, so architecture:cli-surface never points at a draft.
| approved_at: null | ||
| phase: proposed | ||
| owners: [cixzhang, josephfarina] | ||
| affects_architecture: [architecture:cli-surface] |
There was a problem hiding this comment.
FR3 changes how Layer/layerStack.ts and Layer/modalOutlet.ts keep state, which architecture:layer-runtime owns; please list it here and in Current-state impact, and consider adding those modules to the path block.
| `./internal/*` path. | ||
| - **FR2 — The helpers it needs come with it.** Every module the component | ||
| reaches that is not reachable through a public export is copied into the | ||
| output, keeping its folder layout relative to the component, and copied files |
There was a problem hiding this comment.
With helpers kept beside the component, two copies that reach the same private helper (for example Layer/layerScopedContext, imported by Dialog, Drawer, Lightbox, MobileNav and many context modules) collide: the second swizzle hits ERR_FILE_EXISTS, or --overwrite replaces an edited helper. Please say what an existing helper does.
| - **FR7 — Every offered component is proven before merge.** `swizzle --list` | ||
| offers only component folders. Before a change to a component package's source |
There was a problem hiding this comment.
spec:AST-035 FR12 already says swizzle --list lists Core's components, and this record's Non-goals hand the list to it; please cite FR12 in Known gaps instead of restating the rule here.
| reported in the single pull-request report: standard runners, about three | ||
| job-minutes on a pull request that changes component source or the swizzle | ||
| copy (about two to three percent of pull-request runner time), no added |
There was a problem hiding this comment.
These are estimates from measurement, which architecture:knowledge-contracts INV10 keeps out of records; the R2 rubric line also says CI measurements stay out. Please move them to the description.
3268411 to
cb7b410
Compare
|
/approve-spec cb7b410 |
|
/approve-spec 0851db0 |
There was a problem hiding this comment.
The swizzle output contract has an engineering owner's approval, but the records need a few changes before they merge.
- Name the setup or swizzle output that puts a copy's rules in
astryx-base(AST-110 FR5 and the amended AST-080 FR7): the documented StyleX path and@astryxdesign/buildboth put product code aboveastryx-theme. List the styling overview, plus the build package if it changes, as affected. - Move AST-110 to
authority: currentwith the approval, or hold thecli-surfacepointer and the AST-080 exception until it is current, since a draft record does not govern. - List the
--overwrite, existing-file andswizzle.copyresponse changes from FR1 and FR2 in Current-state impact. - Add the time and context failure to the FR7 verification row, and say which proof covers the integration packages the FR1 row names. Cite AST-035 FR12 and public-component-api INV23 where the record restates them.
[Automated review]
| `xstyle`, and product CSS reach it the same way. The copy's component rules | ||
| sit in `astryx-base` like the package component's, below `astryx-theme`, so a | ||
| theme's component overrides apply to the copy as they do to the package | ||
| (`spec:AST-080` FR7). |
There was a problem hiding this comment.
Nothing in this record or AST-080 says what puts a copy's rules in astryx-base. The styling overview has products compile swizzled components with a stock StyleX plugin, and @astryxdesign/build puts product code in a product layer above astryx-theme with the x prefix. Please name the setup or swizzle output that places them (and with which class prefix), and list the styling overview, plus the build package if it changes, under Current-state impact and affects_consumer_docs.
| - `api/swizzle` — the component source copy; what a copy contains and promises | ||
| is `spec:AST-110`'s. |
There was a problem hiding this comment.
This hands the copy contract to spec:AST-110, which stays authority: draft, and a draft is "useful for review, but not a rule" (architecture:knowledge-contracts). Please move AST-110 to current with the approval (DEC-14 keeps it through that edit), or land this pointer and the AST-080 FR7 exception once it is current.
| their state and context objects under versioned keys (FR3, DEC-2). | ||
| - `spec:AST-080` FR7 places a swizzled copy's component rules in `astryx-base` | ||
| (FR5). | ||
| - The `swizzle` command doc states FR1 and FR6 for product teams. |
There was a problem hiding this comment.
FR2 changes what --overwrite and an existing file do. The command doc says --overwrite replaces existing files, and that without it they fail with ERR_FILE_EXISTS. FR1 also adds a dependency report that the swizzle.copy response type lacks. Please list those command-doc and response changes here.
| | FR4 | Scan each output for private data-attribute names and selectors; compare copied owner modules with the package's | Components that reach an owner module | A hand-written private attribute name or selector, or an edited owner module, in the output | | ||
| | FR5 | Render package and copy side by side under a theme with component overrides; compare | Default; each variant; a theme component override | A theme override, documented class name, data attribute, or theme target reaches one and not the other | | ||
| | FR6 | The component's own interaction and accessibility checks run against the copy | Keyboard, pointer, screen-reader tree | A check that passes on the package fails on the copy | | ||
| | FR7 | The required test lane's swizzle proof and the release-time proof | A change to component source; a change to the swizzle copy; an unrelated change | An offered component missing from the proof, or a failure that does not block | |
There was a problem hiding this comment.
FR7 also promises no added required context and no added wall-clock time, but this row has no failure for either. Please add one.
| writes no test, story, spec, or doc file, and lists only component folders | ||
| (FR7). The CLI owner implements and owns these changes. |
There was a problem hiding this comment.
FR7 no longer says the list holds only component folders; spec:AST-035 FR12 owns that, so cite FR12 here.
…collisions, layer contexts, inline FR4; record approval
…instance rejection
… changes wait for current; response and proof scope
0851db0 to
8294fdb
Compare
|
/approve-spec 8294fdb |
There was a problem hiding this comment.
The swizzle output record is clear about what a copy must do, but approval waits on an engineering owner's decision on this revision's content: FR1–FR7 and DEC-1–DEC-2 are new requirements in a draft record, and FR5 relies on the deferred exception to AST-080 FR7 ("Product rules stay outside astryx-base"). An engineering owner records that decision by commenting /approve-spec <full head SHA> on this pull request, and the author may do so as an engineering owner. Three record fixes are needed either way:
- FR5 asks products without the build package to add the copy to a "library pass" that the documented stock StyleX plugins do not have, so name that setting or limit FR5's cascade placement to the build package (and list AST-080 FR5 beside the deferred exception if the plain path stays).
- FR7 promises no added wall-clock time to the slowest required check, but its verification row only fails past the 15-minute job budget.
- Current-state impact credits FR7 with listing only component folders, which AST-035 FR12 owns, and the Non-goal hands StyleX compilation to AST-080, whose own Non-goals disclaim it.
[Automated review]
| apply to the copy as they do to the package component. A product that | ||
| compiles StyleX without the build package adds the output directory to its | ||
| library pass, as the styling overview states. The marker is a protocol |
There was a problem hiding this comment.
The styling overview's StyleX path uses stock plugins pointed at the app's own source; they compile in one pass with one class prefix and emit no astryx-base layer, so these products have no "library pass" to add the copy to. Please name the exact setting per documented plugin, or limit this placement to @astryxdesign/build and list the plain StyleX path under Platform support.
| | FR4 | Scan each output for private data-attribute names and selectors; compare copied owner modules with the package's | Components that reach an owner module | A hand-written private attribute name or selector, or an edited owner module, in the output | | ||
| | FR5 | Render package and copy side by side under a theme with component overrides; compare | Default; each variant; a theme component override; a copy built by the build package and by a plain StyleX setup | A theme override, documented class name, data attribute, or theme target reaches one and not the other | | ||
| | FR6 | The component's own interaction and accessibility checks run against the copy | Keyboard, pointer, screen-reader tree | A check that passes on the package fails on the copy | | ||
| | FR7 | The required test lane's swizzle proof and the release-time proof; both cover Core's list and every integration package's components through `--package` | A change to component source; a change to the swizzle copy; an unrelated change; Core, Lab, Charts, RichText, Vega | An offered component missing from the proof, a failure that does not block, a new required context, or the proof pushing its job past the pull-request time budget | |
There was a problem hiding this comment.
FR7 promises "no wall-clock time to a pull request's slowest required check", but this row only fails past the 15-minute job budget. Please fail when the proof lengthens the slowest required check, or reword FR7 to the budget.
| - The CLI's swizzle copy follows the component's module graph (FR1–FR2, FR4), | ||
| writes no test, story, spec, or doc file, and lists only component folders | ||
| (FR7). The CLI owner implements and owns these changes. |
There was a problem hiding this comment.
FR7 no longer says anything about which folders the list holds; spec:AST-035 FR12 owns swizzle --list, as this record's Non-goals say. Please cite FR12 here.
| - Which component a name copies and what `swizzle --list` reports: | ||
| `spec:AST-035` FR11–FR12 and `architecture:cli-surface` INV28. | ||
| - Where the command runs: `spec:AST-081` FR3. | ||
| - How a product compiles StyleX for its own code: `spec:AST-080`. |
There was a problem hiding this comment.
AST-080's Non-goals disclaim "How a product compiles its own code, including any StyleX settings it uses…", so this pointer names an owner that declines the topic. Please drop it or point at the owner that states it.
|
/approve-spec 285f61c |
There was a problem hiding this comment.
An engineering owner has approved this revision and the earlier gaps are fixed, but one change remains before merge. A copied Dialog copies hooks/useAnnounce.ts and its live regions, which breaks spec:AST-086 FR9's single announcement channel per document. FR3's Known gaps, Current-state impact and Verification row never name that channel, so the swizzle proof would pass a second live region. Please name it in those three places and re-approve the new head; when AST-110 moves to current, amend spec:AST-080 FR5 and FR7 in that same pull request, as Known gaps already say.
[Automated review]
| - The layer stack, the modal outlet, and the layer contexts keep module state, | ||
| so a copied Dialog, BottomSheet, or `useToast` starts its own stack or misses | ||
| the package's provider (FR3). |
There was a problem hiding this comment.
A copied Dialog also copies hooks/useAnnounce.ts, because ModalAnnounceProvider imports its private ModalAnnounceContext and modalAnnounce reaches announceMessage. The copy brings its own live-region singleton, so the document gets a second polite/assertive pair, against spec:AST-086 FR9. Please list the announcement channel here, in Current-state impact, and as a state in FR3's Verification row.
e91525b to
70b633b
Compare
|
/approve-spec 70b633b |
There was a problem hiding this comment.
The swizzle output record has an engineering owner's approval at this revision and the announcement-channel gap is fixed, but one change remains before merge. FR3's Verification row and Known gaps only test a copy under a package provider, while the usual composition is the reverse: package components inside a copied Dialog or overlay, where a copy today loses what the package gives them under spec:AST-086 FR14 (a package picker's messages go to the page channel the modal hides) and spec:AST-038 FR7 (package group context crosses the copy's layer boundary). Please add those states to the FR3 row, make its failure cover either direction, name that direction in Known gaps, and re-approve the new head.
[Automated review]
| | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | ||
| | FR1 | Eject every offered component into a clean project; type-check and bundle | Every component in `swizzle --list`; Core, Lab, Charts, RichText, Vega; a component that imports an npm dependency | A type or resolve error, or an import of a path the owner's `exports` lacks or of `./internal/*`, fails, or a dependency the copy imports goes unreported | | ||
| | FR2 | Import graph of each output | Helpers in `utils`, `hooks`, `Layer`; nested folders; colocated tests and docs; a helper an earlier swizzle wrote, identical and different | A copied file imports a module that is neither in the output nor a public export, or a test, story, spec, or doc is copied, or an existing helper is overwritten | | ||
| | FR3 | Render a copied and a package layer component together; inspect stores; open, stack, and Escape | Dialog over package Popover; BottomSheet over package Dialog; modality; a copied `useToast` under the package `LayerProvider`; a symbol-branded object passed between copy and package; a copied Dialog announcing beside a package component | Two stores of one protocol version exist in a document, or Escape and focus return differ from the package alone, or a copied consumer misses the package provider, or a brand check fails across them, or a copy adds a second live region instead of joining the package announcer (`spec:AST-086` FR9) | |
There was a problem hiding this comment.
This row only tests a copy under a package provider ("a copied consumer misses the package provider"), and the new state puts the package component beside the copied Dialog. The usual composition is a package component inside a copy: today a package Selector inside a copied Dialog reads the package ModalAnnounceContext, gets null, and announces on the page channel the modal hides, where a package Dialog delivers it inside (spec:AST-086 FR14); and the copy's LayerContentBoundary does not reset package group contexts, which a package overlay stops (spec:AST-038 FR7). Please add those states and make the failure cover either direction.
| - The layer stack, the modal outlet, the layer contexts, and `useAnnounce` | ||
| keep module state, so a copied Dialog, BottomSheet, or `useToast` starts its | ||
| own stack, misses the package's provider, or mounts a second pair of live | ||
| regions, breaking `spec:AST-086` FR9's one channel per document (FR3). |
There was a problem hiding this comment.
This gap names only a copy missing the package's provider. Please also name the reverse: package components inside a copied Dialog or overlay miss the copy's announcement route and layer boundary.
|
/approve-spec c984b8e |
There was a problem hiding this comment.
The swizzle output record has an engineering owner's approval at this revision, and FR3 now holds in both directions, so package components inside a copied Dialog or overlay keep in-modal announcements (spec:AST-086 FR14) and the layer boundary (spec:AST-038 FR7). Nothing else blocks merging. One optional cleanup: the Non-goal for where the command runs cites spec:AST-081 FR3, but swizzle's refusal outside a project is in FR4, so "FR3–FR4" would be exact.
[Automated review]
|
|
||
| - Which component a name copies and what `swizzle --list` reports: | ||
| `spec:AST-035` FR11–FR12 and `architecture:cli-surface` INV28. | ||
| - Where the command runs: `spec:AST-081` FR3. |
There was a problem hiding this comment.
Optional: spec:AST-081 FR3 says swizzle must not read the carried Core, but the refusal outside a project ("swizzle is refuse, even with a carried Core.") is in FR4, so FR3–FR4 would cite both.
Exact decision needed
What
astryx swizzlepromises about the copy it writes. Today a product team that swizzles a component to own its source gets a copy that often does not build: the copy imports Core helpers Core does not export. No current record says the copy must build, keep theme targets, or behave like the package component.This PR adds
spec:AST-110(Swizzle output) only. Core approved the record on 2026-10-31. It staysdraftuntil its co-owner, the CLI owner, agrees. A draft does not govern, so thespec:AST-080FR5 and FR7 swizzle cases and thearchitecture:cli-surfacepointer land in the same pull request that makes AST-110 current; the record's Known gaps say so.Existing authority searched
architecture:cli-surfaceINV28: whatswizzle --listreports. Says nothing about the copy's contents.spec:AST-035FR11–FR12: which component an unqualified or Core-scopedswizzle <Name>copies.spec:AST-081FR3: swizzle refuses to run outside a project.spec:AST-080FR7: product rules stay outsideastryx-base, which puts a swizzled copy above the theme layer. Its swizzle exception lands when AST-110 becomes current.@astryxdesign/build: it sorts modules into its library pass (astryx-base) or product pass by path (node_modules/@astryxdesign/), so a copy in the product tree lands in the product layer.spec:AST-017FR21: modules without a public contract stay internal;./internal/*is limited to Core and Lab.architecture:public-component-apiINV12 (anything reachable is public) and INV23 (each private data attribute has one owner module).architecture:layer-runtime: the layer stack, modal outlet, and layer contexts that FR3 keeps single.spec:AST-030FR11 and FR14: one test lane per surface; allowlisted triggers.docs/**and the CLI's command docs for swizzle, eject, and copy. The command doc only says imports that escape the component folder are rewritten.Decision
./internal/*. Swizzle reports the owner package's npm dependencies the copy imports (with their declared ranges); the copy builds once the product installs them.--overwrite): the new one is written alongside under a version-suffixed name, and only the new copy's imports point at it.spec:AST-086FR9) and the contexts that connect providers to consumers live under registered, versioned keys. A copy and the package on the same version share them, so stacking, Escape and focus return work as one, a copieduseToastreaches the package'sLayerProvider, and identity symbols (brands, context keys) agree between copy and package. It holds in both directions: package components inside a copied Dialog deliver announcements inside that modal (spec:AST-086FR14), and package group context stops at the copy's layer boundary (spec:AST-038FR7). A copy on another version keeps its own store and never reads a different shape.astryx-swizzle.jsonfile (owner package and version).@astryxdesign/buildcompiles modules under a marked directory in its library pass intoastryx-base, belowastryx-theme, so a theme's component overrides still apply. This holds for products on@astryxdesign/build. The stock StyleX plugins have no library-pass setting, so on them a copy's rules land aboveastryx-theme; the record lists that as a known gap.--listperspec:AST-035FR12, plus each integration package's components through--package) is ejected, type-checked, bundled with StyleX, and server-rendered, and a failure blocks the merge. Release checks add a real-browser render.CI impact of FR7, shaped with the CI and release owners: one step in the existing required test job, selected by the shared change-scope classification, with results in the single PR report. It uses standard runners and adds about +3 job-minutes on pull requests that change component source or swizzle code (≈ +2–3% of pull-request runner time). It adds no wall-clock time to green and no new runs or checks. These numbers are estimates until the first real run measures them.
Rejected alternatives
Claim scope and non-goals
The record governs only what a swizzled copy contains and promises, and the proof that keeps it true. It leaves out:
Evidence and implementation relationship
swizzle --listoffers: 12 build and render, 89 fail to build, 9 build but render wrong, and 2 build with nothing to render.../X/fileto the public@astryxdesign/core/Xentry, which lacks internal helpers (87 components).--listoffers 6 folders that are not components.main, a full ledger of 154 components (Core, Lab, Charts, RichText, Vega) finds:swizzle.copylists each helper as written, reused or versioned, and each npm dependency with its range.ERR_FILE_EXISTSunless--overwriteis passed.Writing rubric results
Scope
Validation
pnpm check:knowledge: Knowledge templates and records are aligned.git diff --check: clean.