Skip to content

Components: Spec patterns: ingestion from the canonical catalog and repeatable generation #4252

Description

@JasonVMo

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

  1. 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.
  2. Pin the upstream revision in a committed file, alongside the upstream plugin version.
  3. Define the transform from the upstream shape to the local shape, precisely:
    SKILL.md -> SPEC.md, web/* -> flat companions, front-matter rewriting,
    platform retargeting from web to react-native (Windows, macOS), and what happens
    to mobile/**.
  4. Implement a repeatable "pull spec" operation that takes a component name and
    produces a staged spec folder under specs/<name> in the local shape.
  5. Implement or specify the "promote spec" operation that moves a staged spec into a
    component directory in the generated shape, with front matter rewritten.
  6. Record provenance per spec: upstream path, upstream SHA, upstream plugin version,
    and ingestion date.
  7. 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.
  8. Formalize blockers.md as a structured record with a status, a reason, and the
    evidence, so a blocker can be programmatically re-evaluated rather than re-read.
  9. 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 button divergence itself; this task only has to detect and report it.

Deliverables

  1. A written decision on the ingestion boundary, with the licensing,
    disclosure, and public-repository implications addressed.
  2. A committed pin file naming the upstream repository, SHA, plugin version, and
    ingestion date.
  3. A documented, implemented transform from the upstream spec shape to the local one.
  4. A repeatable pull operation for a named component, runnable through a declared
    workspace script.
  5. A documented promote operation from specs/<name> to
    src/components/<name>/spec/, with front-matter rewriting.
  6. Per-spec provenance metadata in the local SPEC.md front matter.
  7. A drift check and an initial drift report covering all 36 local specs.
  8. A structured blockers.md schema and a migration of the 18 existing blocker records
    into it.
  9. 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 whose SPEC.md front
    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 the button divergence.
  • blockers.md records 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

Risks and open decisions

  1. 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.
  2. 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.
  3. 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
    resize and native <label> association, which are exactly the blockers recorded
    for textarea and
    label. Open decision: whether the transform
    is mechanical with a manual retargeting pass, or an agent-assisted translation with
    a review gate.
  4. Drift policy. Once drift is visible, a policy is needed: block the build, open an
    issue, or record an accepted deviation. button will be the first case.
  5. 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

Sources retrieved 2026-08-20.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions