Repository navigation
Components: Spec patterns: ingestion from the canonical catalog and repeatable generation #4252
Description
Summary
Build a reproducible path from the canonical Fluent Flex catalog to local React Native component specs, including revision pinning, transformation, promotion, and drift detection. This work unblocks the 14 catalog components with no local spec; its key boundaries are the licensing and disclosure policy for vendored material and an explicit, reviewable transform from web-authored contracts to Windows and macOS React Native behavior.
Observed. The mechanism today is entirely manual and entirely undocumented in this
repository. Specs exist locally in two shapes, both different from the upstream shape,
with no recorded source revision. Nothing in the repository even names the upstream
repository.
Goal
Make the path from the canonical Fluent Flex design catalog to a generated FURN
component reproducible: pin the upstream revision, define and implement the transform
into this repository's spec shape, provide a repeatable way to pull a new or updated
spec, and detect drift between a generated component and the spec revision it came
from.
Stage
Stage 1 (beta foundations). It gates the 14 missing components that have no local spec
at all, and it supplies the spec-fidelity evidence the readiness model needs.
Why it matters
Observed. A repository-wide search for x3-design, fluent-design, and
component-map across Markdown, JSON, TypeScript, and YAML returns no matches
outside research/. There is no ingestion script, no pinned revision, no manifest, and
no drift check.
Observed. The only trace of provenance is prose: spec files reference
flex-components:<name> and flex-system:<name> identifiers, which are upstream skill
references. They identify the concept but not the revision.
Observed. Local button has already drifted from upstream button. Upstream
removed the Selected axis, renamed the Radius axis to Shape, and split the icon slot
into independent leading and trailing slots. Local
button.types.ts
still has selected, one icon slot with iconPosition, and
shape: 'rounded' | 'square' | 'circle'. Inferred: the local specs were ingested
from a pre-v1.4.0 upstream state and no mechanism noticed.
Inferred. Without ingestion, the 14 catalog components with no local spec cannot be
generated at all, and every future upstream change lands as a surprise rather than a
reviewable diff.
Observed current state
Upstream
Observed. x3-design/fluent-design, default branch main, HEAD
d334acf
at retrieval.
| Aspect | Observed |
|---|---|
| Machine-readable index | component-map.yaml at the repository root: 50 component keys, each with skill, figma.web, figma.mobile, and an empty code field |
| Human-readable index | plugins/components/skills/components-index/SKILL.md: the skill map with tier (atom / molecule), platforms, and a description per component |
| Per-component layout | plugins/components/skills/<name>/ with SKILL.md, usage.md, web/tokens.yaml, web/accessibility.md, web/interaction.md, and optional mobile/overview.md, mobile/tokens.yaml, mobile/ios/**, mobile/android/** |
| Versioning | plugins/components is versioned as @fluent-design/plugin-components (1.5.0 at HEAD) with a beachball changelog. No git tags exist, so a commit SHA is the only stable pin |
Observed. Upstream explicitly states that no platform falls back to another
platform's files, so ingesting the web/ companions is a deliberate platform choice,
not a default.
Local
Observed. Two local shapes, both flat, both different from upstream:
| Location | Files | Front matter companion paths |
|---|---|---|
Generated component, e.g. src/components/button |
SPEC.md + spec/{tokens.yaml,accessibility.md,interaction.md,usage.md} |
./spec/tokens.yaml, ./spec/accessibility.md, ... |
Staged spec, e.g. specs/tablist |
SPEC.md, tokens.yaml, accessibility.md, interaction.md, usage.md, blockers.md |
tokens.yaml, accessibility.md, ... |
Observed. Local SPEC.md front matter carries name, platform, description,
argument-hint, and the companion paths. platform is
react-native (Windows, macOS), so the ingestion has already been platform-retargeted
from upstream's web.
Observed. 18 staged specs exist under
specs/; the remaining 14 catalog gaps
have nothing locally. See
missing-components/README.md for the full mapping.
Observed. Migration from staged to generated is a real, repeated operation: the
spec folder moves into the component directory as spec/, SPEC.md moves up one
level, front matter paths are rewritten, and blockers.md is dropped. Nothing
automates or validates it.
Observed. apps/component-generator is the
legacy gulp-based scaffolder for the old packages/components layout and has no
relationship to the agentic pipeline. Agentic generation is driven by the
agentic-component-authoring
skill, which is prose for an agent, not a program.
Scope
- Decide the ingestion boundary. Observed viable options: vendor a snapshot of the
needed upstream files into the repository with a recorded SHA; add a git submodule;
or fetch on demand with a script and commit only the transformed output. The choice
must account for upstream being private and for the repository being public. - Pin the upstream revision in a committed file, alongside the upstream plugin version.
- Define the transform from the upstream shape to the local shape, precisely:
SKILL.md->SPEC.md,web/*-> flat companions, front-matter rewriting,
platform retargeting fromwebtoreact-native (Windows, macOS), and what happens
tomobile/**. - Implement a repeatable "pull spec" operation that takes a component name and
produces a staged spec folder underspecs/<name>in the local shape. - Implement or specify the "promote spec" operation that moves a staged spec into a
component directory in the generated shape, with front matter rewritten. - Record provenance per spec: upstream path, upstream SHA, upstream plugin version,
and ingestion date. - Implement drift detection: compare each local spec against the upstream file at the
pinned SHA and at upstream HEAD, and report the difference. Produce the initial
drift report across all 18 generated and 18 staged specs. - Formalize
blockers.mdas a structured record with a status, a reason, and the
evidence, so a blocker can be programmatically re-evaluated rather than re-read. - Decide whether the primitives, which have no upstream counterpart, get local-only
specs so the whole package is spec-covered.
Out of scope
- Automating component implementation. Generation stays an agent workflow governed by
the authoring skill; this task supplies its inputs and verifies its inputs stay
current. - Ingesting upstream design tokens. Flex token mapping is the Theming workstream's
(packages/agentic/design/src/tokens/mappings/flex-token-map.yaml). - Ingesting
mobile/**companions, unless the decision in scope item 3 says otherwise. - Resolving the
buttondivergence itself; this task only has to detect and report it.
Deliverables
- A written decision on the ingestion boundary, with the licensing,
disclosure, and public-repository implications addressed. - A committed pin file naming the upstream repository, SHA, plugin version, and
ingestion date. - A documented, implemented transform from the upstream spec shape to the local one.
- A repeatable pull operation for a named component, runnable through a declared
workspace script. - A documented promote operation from
specs/<name>to
src/components/<name>/spec/, with front-matter rewriting. - Per-spec provenance metadata in the local
SPEC.mdfront matter. - A drift check and an initial drift report covering all 36 local specs.
- A structured
blockers.mdschema and a migration of the 18 existing blocker records
into it. - Updated authoring instructions describing when to pull, when to promote, and how to
respond to drift.
Acceptance criteria
- A committed file pins the upstream repository, SHA, and plugin version.
- Running the pull operation for a component with no local spec produces a
specs/<name>folder in the local shape, with provenance metadata. - Running the pull operation twice on an unchanged upstream produces no diff.
- The promote operation produces a component
spec/folder whoseSPEC.mdfront
matter resolves to the moved companion paths. - The drift check reports, per local spec, whether it matches the pinned upstream
revision and whether upstream HEAD has moved since. - The initial drift report covers all 18 generated and 18 staged specs and
explicitly records thebuttondivergence. -
blockers.mdrecords are structured, with at least a status, reason, evidence,
and last-evaluated date. - The 14 catalog components with no local spec can be staged with the pull
operation, unblocking their tasks. - Authoring instructions reference the operations and are linked from
packages/agentic/components/AGENTS.md.
Dependencies and ordering
- Blocks the 14 missing-component tasks with no local spec:
avatar-group,
combobox,
destructive-button,
dialog,field,
nav,nav-item,
search-box,
select,
simple-nav,
split-button,
teaching-popover,
toast,
toggle-button. - Blocks the spec-fidelity axis of readiness-model.md.
- Informs component-test-strategy.md, whose
coverage contract derives required axes fromSPEC.md. - Independent of focus-zone.md.
Risks and open decisions
- Licensing and disclosure boundary. Vendoring spec text into this public
repository requires an explicit policy. Open decision: what may be
vendored, what must be summarized, and what must remain a link to the
upstream reference. - No upstream tags. Only commit SHAs are available to pin against, so upgrades are
SHA bumps and the diff has no semantic boundary other than the plugin changelog. - Shape divergence is a lossy transform. Upstream
web/companions are
web-authored; the local specs are already retargeted to React Native for Windows and
macOS. A mechanical transform will reintroduce web-only concepts such as CSS
resizeand native<label>association, which are exactly the blockers recorded
fortextareaand
label. Open decision: whether the transform
is mechanical with a manual retargeting pass, or an agent-assisted translation with
a review gate. - Drift policy. Once drift is visible, a policy is needed: block the build, open an
issue, or record an accepted deviation.buttonwill be the first case. - Upstream churn. The plugin moved 1.0.2 -> 1.5.0 with substantial component
additions and contract changes in that span. Inferred: drift will be continuous,
so the report needs to be cheap to run and easy to triage.
Evidence and references
x3-design/fluent-design@
d334acf:
component-map.yaml,
plugins/components/skills/components-index/SKILL.md,
plugins/components/CHANGELOG.md.packages/agentic/components/specs-
the 18 staged specs and theirblockers.mdrecords.packages/agentic/components/src/components/button/SPEC.md
andspec/- the
generated spec shape.packages/agentic/components/src/components/button/button.types.ts- the drifted local contract.
.github/skills/agentic-component-authoring/SKILL.md- the generation workflow this task feeds.
apps/component-generator/README.md-
the legacy scaffolder, for contrast.
Sources retrieved 2026-08-20.
Activity
[-]Spec patterns: ingestion from the canonical catalog and repeatable generation[/-][+]Components: Spec patterns: ingestion from the canonical catalog and repeatable generation[/+]