Skip to content

spec(AST-110): a swizzled component builds on its own and behaves like the package - #9190

Merged
cixzhang merged 8 commits into
mainfrom
spec/swizzle-output
Oct 31, 2026
Merged

cixzhang merged 8 commits into
mainfrom
spec/swizzle-output

Conversation

@cixzhang

@cixzhang cixzhang commented Oct 31, 2026 •

Copy link
Copy Markdown
Contributor

Exact decision needed

What astryx swizzle promises 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 stays draft until its co-owner, the CLI owner, agrees. A draft does not govern, so the spec:AST-080 FR5 and FR7 swizzle cases and the architecture:cli-surface pointer land in the same pull request that makes AST-110 current; the record's Known gaps say so.

Existing authority searched

  • architecture:cli-surface INV28: what swizzle --list reports. Says nothing about the copy's contents.
  • spec:AST-035 FR11–FR12: which component an unqualified or Core-scoped swizzle <Name> copies.
  • spec:AST-081 FR3: swizzle refuses to run outside a project.
  • spec:AST-080 FR7: product rules stay outside astryx-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-017 FR21: modules without a public contract stay internal; ./internal/* is limited to Core and Lab.
  • architecture:public-component-api INV12 (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-030 FR11 and FR14: one test lane per surface; allowlisted triggers.
  • Searched 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.
  • Overlapping open work: fix(cli): swizzle recursively copies nested component subdirectories #5101 (copy nested component subfolders) implements part of FR2; fix(cli): swizzle finds gh on PATH instead of running gh auth status #8626 (gh lookup) affects how fast the copy runs. Neither decides what the copy promises.

Decision

  • FR1, builds on its own: with the same Core version and a StyleX compiler, the copy type-checks and bundles with no edits. It imports only paths its owner package exports, never ./internal/*. Swizzle reports the owner package's npm dependencies the copy imports (with their declared ranges); the copy builds once the product installs them.
  • FR2, helpers come with it: non-public modules the component reaches are copied with it and imported by relative path. Nested folders come too. Tests, stories, specs and docs stay behind. If an earlier swizzle already wrote a helper, an identical file is reused. A different file is never overwritten (not even with --overwrite): the new one is written alongside under a version-suffixed name, and only the new copy's imports point at it.
  • FR3, shared runtime state stays single: coordinating state (layer stack, modal outlet, interaction modality, bottom-sheet stack, and the one announcement channel per document of spec:AST-086 FR9) 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 copied useToast reaches the package's LayerProvider, 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-086 FR14), and package group context stops at the copy's layer boundary (spec:AST-038 FR7). A copy on another version keeps its own store and never reads a different shape.
  • FR4: each private data attribute's owner module (INV23) that the component imports is copied whole, so callers, the copy included, still never write the attribute by hand.
  • FR5: theme targets, documented classes, data attributes and custom properties are unchanged. Swizzle marks its output directory with an astryx-swizzle.json file (owner package and version). @astryxdesign/build compiles modules under a marked directory in its library pass into astryx-base, below astryx-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 above astryx-theme; the record lists that as a known gap.
  • FR6: keyboard, focus, roles, names, states, announcements and events match the package until the product edits the copy.
  • FR7: before a change to component source or the swizzle copy merges, every component swizzle can copy (Core's --list per spec:AST-035 FR12, 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

  • Export the helpers copies need: anything reachable from a package path becomes public API (INV12), so internals would turn into permanent API to make a copy build.
  • Never copy stateful modules; import a contained package instance instead. That needs a public runtime entry with a compatibility promise only copies need, and it leaves a copy on another version without the state it needs. Core already keeps modality and the bottom-sheet stack in versioned per-document stores so duplicate bundles share state; FR3 makes that the rule.
  • Run the proof in the CLI smoke workflow: that workflow is not a required check, so a failure could not block, and it is already close to its time budget.

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:

  • which component a name copies;
  • where the command runs;
  • how a product compiles StyleX;
  • keeping an edited copy in step with later Core releases;
  • making internal helpers public.

Evidence and implementation relationship

  • Published 0.7.5 was built for real, covering all 112 names swizzle --list offers: 12 build and render, 89 fail to build, 9 build but render wrong, and 2 build with nothing to render.
  • Causes:
    • The import rewrite collapses ../X/file to the public @astryxdesign/core/X entry, which lacks internal helpers (87 components).
    • Deep StyleX modules are missing from the exports map (12).
    • Copied styles land in the app's cascade layer above the theme's, so theme component overrides stop applying (9 today, 38 once imports resolve). FR5 now states the cascade rule.
    • Spec records and tests get copied (104), and nested folders are left out.
    • --list offers 6 folders that are not components.
  • On main, a full ledger of 154 components (Core, Lab, Charts, RichText, Vega) finds:
    • 32 pass every check, 96 don't bundle, 17 lose theme component overrides, and 1 behaves differently (Escape doesn't close a copied Lab MobilePowerSearch).
    • Copying every helper Core doesn't export raises passes to 48. What remains:
      • copied shared-state modules (a second live region; focus misses the combobox in a copied CommandPalette);
      • the cascade layer;
      • Core's private type additions and a unique-symbol identity split;
      • 16 copies that need the owner package's npm dependencies (d3, vega-util).
    • FR1, FR3 and FR5 cover each of these.
  • Running 112 separate CLI processes took 654 s locally, almost all of it start-up, so the proof copies in-process.
  • Response and file changes:
    • swizzle.copy lists each helper as written, reused or versioned, and each npm dependency with its range.
    • An existing component file still fails with ERR_FILE_EXISTS unless --overwrite is passed.
    • An existing helper never fails the command and is never replaced.
  • No implementation is in this PR. The CLI owner implements the copy changes (FR1, FR2, FR4, FR7). Core implements FR3's stores and contexts. The build package reads the FR5 marker, and the styling overview documents where a copy's rules land with and without it.

Writing rubric results

  • R1 narration: none found. Intent, requirements, current-state impact and known gaps are in the present tense.
  • R2 research in the record: one finding, fixed. Ledger counts and CI estimates moved out of the record into this description; Known gaps keeps the causes only.
  • R3 evidence naming a change: none. Verification cells name checks and states.
  • R4 rejected alternatives over one line or outside a decision: two, in DEC-1 and DEC-2, one line each.
  • R5 pull request or issue references remaining: none in the record.
  • R6 ruled open questions still listing options: none. The record has no open questions; DEC-1 and DEC-2 carry Core's approval.

Scope

  • This PR contains specification records only.
  • It records observable behavior and preserves equivalent internal implementations.
  • Any named internal mechanism is intentionally public, names its dependent caller/system, and explains why an equivalent implementation would not satisfy the contract.
  • Verification states the evidence layer and failure signal without prescribing CI job/workflow topology.
  • It records one intentional durable decision, not a rescue for a separable tagalong.
  • The canonical knowledge owner, affected current claims, and overlapping open work are named; any ownership collision is resolved or called out for human decision.
  • Adjacent API, visual, accessibility, composition, and implementation facts remain outside scope unless this exact decision depends on them.
  • Public text and artifacts contain no internal Meta context.

Validation

  • pnpm check:knowledge: Knowledge templates and records are aligned.
  • Prettier: AST-110 clean.
  • git diff --check: clean.

@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Meta Open Source bot. label Oct 31, 2026
@vercel

vercel Bot commented Oct 31, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
astryx Error Error Oct 31, 2026 9:57pm UTC

Request Review

@astracat-bot

astracat-bot Bot commented Oct 31, 2026 •

Copy link
Copy Markdown

AI review status for this pull request.

Review status Updated
🟢 Reviewed (for maintainers only) Oct 31, 2026, 10:01 PM UTC

@github-actions github-actions Bot added the needs:spec-owner-review Current knowledge records await owner approval label Oct 31, 2026
@github-actions

github-actions Bot commented Oct 31, 2026 •

Copy link
Copy Markdown
Contributor

PR Analysis Report

Current run: The current change has no visual or preview-producing scope. Visual checks are not applicable; preview links are not shown.


Generated by PR Enrichment workflow | View current CI run

@cixzhang

Copy link
Copy Markdown
Contributor Author

/approve-spec 3268411

@cixzhang
cixzhang marked this pull request as ready for review October 31, 2026 19:17
@github-actions github-actions Bot removed the needs:spec-owner-review Current knowledge records await owner approval label Oct 31, 2026

@astracat-bot astracat-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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-080 FR7 says product rules stay outside astryx-base; amend AST-080 in this pull request or reword FR5.
  • FR4 cites architecture:public-component-api INV23, which is not on main; define the private-attribute owner module here or hold FR4 until that claim lands.
  • Add architecture:layer-runtime to the impact for FR3, list the Toast case (a copied useToast never sees the package LayerProvider), and say in FR2 what happens when a helper another copy already wrote exists.
  • Cite spec:AST-035 FR12 instead of restating the swizzle --list rule 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]

Comment on lines +91 to +93
`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.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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]

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment on lines +97 to +98
- **FR7 — Every offered component is proven before merge.** `swizzle --list`
offers only component folders. Before a change to a component package's source

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment on lines +127 to +129
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

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@cixzhang
cixzhang force-pushed the spec/swizzle-output branch from 3268411 to cb7b410 Compare October 31, 2026 19:48
@github-actions github-actions Bot added the needs:spec-owner-review Current knowledge records await owner approval label Oct 31, 2026
@cixzhang

Copy link
Copy Markdown
Contributor Author

/approve-spec cb7b410

@github-actions github-actions Bot removed the needs:spec-owner-review Current knowledge records await owner approval label Oct 31, 2026
@github-actions github-actions Bot added the needs:spec-owner-review Current knowledge records await owner approval label Oct 31, 2026
@cixzhang

Copy link
Copy Markdown
Contributor Author

/approve-spec 0851db0

@github-actions github-actions Bot removed the needs:spec-owner-review Current knowledge records await owner approval label Oct 31, 2026

@astracat-bot astracat-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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/build both put product code above astryx-theme. List the styling overview, plus the build package if it changes, as affected.
  • Move AST-110 to authority: current with the approval, or hold the cli-surface pointer and the AST-080 exception until it is current, since a draft record does not govern.
  • List the --overwrite, existing-file and swizzle.copy response 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]

Comment on lines +102 to +105
`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).

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread docs/architecture/cli-surface.md Outdated
Comment on lines +668 to +669
- `api/swizzle` — the component source copy; what a copy contains and promises
is `spec:AST-110`'s.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 |

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FR7 also promises no added required context and no added wall-clock time, but this row has no failure for either. Please add one.

Comment on lines +133 to +134
writes no test, story, spec, or doc file, and lists only component folders
(FR7). The CLI owner implements and owns these changes.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FR7 no longer says the list holds only component folders; spec:AST-035 FR12 owns that, so cite FR12 here.

@cixzhang
cixzhang force-pushed the spec/swizzle-output branch from 0851db0 to 8294fdb Compare October 31, 2026 20:38
@cixzhang

Copy link
Copy Markdown
Contributor Author

/approve-spec 8294fdb

@astracat-bot astracat-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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]

Comment on lines +107 to +109
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

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 |

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment on lines +140 to +142
- 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.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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`.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@cixzhang

Copy link
Copy Markdown
Contributor Author

/approve-spec 285f61c

@astracat-bot astracat-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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]

Comment on lines +174 to +176
- 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).

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@cixzhang
cixzhang force-pushed the spec/swizzle-output branch from e91525b to 70b633b Compare October 31, 2026 21:31
@cixzhang

Copy link
Copy Markdown
Contributor Author

/approve-spec 70b633b

@astracat-bot astracat-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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) |

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment on lines +178 to +181
- 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).

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@cixzhang

Copy link
Copy Markdown
Contributor Author

/approve-spec c984b8e

@astracat-bot astracat-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@cixzhang
cixzhang merged commit 7cf5979 into main Oct 31, 2026
51 of 52 checks passed

This branch had an error being deployed

1 failed deployment
Preview — c984b8eb Deployed Oct 31, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed This label is managed by the Meta Open Source bot.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant