Summary
Decide whether the five FURN-only primitives are public API, package-private, or a
separate package, then apply the resulting export, spec-policy, and test-guard changes
without altering primitive behavior.
Observed. packages/agentic/components exports five primitives -- Icon,
CheckboxIndicator, CompoundItemLayout, FocusVisual, and LayoutStableText -- from
its only public entry point. None of them has a SPEC.md, a recorded upstream catalog
entry, or any upstream contract. The package's own
authoring guidance
treats primitives as "unstyled building blocks" extracted for repeated behavioral or
structural contracts. Every consumer of every primitive is inside the same package; no
external package or application imports them directly today.
Inferred. A public export without a contract, a spec, or an explicit scope decision
is a forward compatibility hazard. Until the decision is made, primitives cannot be
assigned a readiness level, and consumers cannot know whether they are adopting stable
API or an implementation detail that may change.
Goal
Record the owner-approved scope decision -- public API, package-private, or
separate package -- and apply the resulting mechanical changes to exports, the export
guard test, the readiness model, and the component test strategy without changing
primitive behavior.
Stage
Stages 0 and 1 (scope decision through beta foundations). Stage 0 records the
owner-approved public, private, or separate-package disposition. Stage 1 applies
the corresponding export, contract, test, and readiness changes. The readiness
model cannot assign a level to primitives until their scope is settled; every
new component generated while the decision is open inherits the same ambiguity.
Why it matters
Observed. The provisional readiness assessment in
readiness-model.md places all five primitives at
experimental with the ceiling note: "no SPEC.md and no upstream catalog entry, so
the spec-fidelity axis cannot be evaluated at all." That ceiling is structural, not
coverage-driven. It does not lift until the scope is decided.
Observed. The component test strategy notes that
primitives have no SPEC.md and leaves open whether the coverage contract derives from
their types file or from authored specs. The contract cannot be written until the scope
decision resolves what a primitive is obligated to document.
Observed. Four out of five primitives are in
src/index.test.ts's
"exports shared primitives" block. Icon is exported in
src/index.ts
but is not asserted in that block, creating a silent gap in the export guard for the
most broadly consumed primitive.
Inferred. The _unstable suffix on the component pipeline exports signals "the
composition API may change," not "the component is immature." Primitives carry no
equivalent signal. A consumer who imports Icon or FocusVisual today has no
indication whether those names are stable API or subject to silent removal.
Observed current state
The five primitives
Observed. All five reside under
src/primitives
and are exported explicitly from
src/index.ts.
| Primitive |
Exports from src/index.ts |
Purpose |
icon |
Icon, FontIconSource, IconElementProps, IconProps, SvgIconSource |
Multi-source icon renderer: font glyph, SVG component, or image. Discriminated union on source type. |
checkbox-indicator |
CheckboxIndicator, CheckboxIndicatorProps, CheckboxIndicatorStatus |
Renders checked/unchecked/indeterminate state with configurable icon sources and colors. |
compound-item-layout |
CompoundItemLayout, CompoundItemLayoutProps |
Flex layout with leading, primary, secondary, and trailing slots. Secondary placement (right or under) is controllable. |
focus-visual |
FocusVisual, createFocusVisualProps, FocusVisualOptions, FocusVisualProps, FocusVisualRingProps, FocusVisualStyles |
Single or dual-ring focus visual that mounts eagerly and changes only opacity. createFocusVisualProps converts token values into ring ViewStyle arrays. |
layout-stable-text |
LayoutStableText, LayoutStableTextProps |
Reserve-and-visible text pair: a hidden element reserves metrics for the widest state; the visible element renders over it. |
Coverage
Observed. Each primitive has a colocated runtime test, a type test, and a
Storybook story. None has a __snapshots__ directory and none has a SPEC.md. The
Storybook story titles use the Primitives/ prefix (e.g., 'Primitives/Icon'),
placing them in the same sidebar tier as 'Primitives/Callout' from
packages/native/Callout.
Observed. primitives-icon--default is one of only three story IDs in
apps/storybook/scripts/smoke-stories.json,
alongside components-button--default and primitives-callout--default. Icon is
therefore the only one of the five primitives with any on-device render coverage.
Observed. The export guard in
src/index.test.ts
asserts CheckboxIndicator, CompoundItemLayout, FocusVisual,
createFocusVisualProps, and LayoutStableText in an "exports shared primitives"
block. Icon is exported from src/index.ts but is not asserted in that block.
Internal consumer inventory
Observed. All consumers are inside packages/agentic/components/src/components.
No external package and no application imports a primitive directly from
@fluentui-react-native/components.
| Primitive |
Consuming components (internal only) |
Icon |
accordion, avatar, badge, button, divider, input, list-item, listbox-item, menu-item, progress-bar, tab, tag (12 of 18) |
FocusVisual |
accordion, button, card, checkbox, list-item, listbox-item, menu-item, radio, switch, tab, tag (11 of 18) |
LayoutStableText |
button, list-item, listbox-item, menu-item, tab (5 of 18) |
CompoundItemLayout |
list-item, listbox-item, menu-item (3 of 18) |
CheckboxIndicator |
checkbox, listbox-item, menu-item (3 of 18) |
Observed. FocusVisual is the most cross-cutting primitive (11 consumers, 5
exported types plus a helper function, and named explicitly in
src/components/AGENTS.md
as the mandated focus-feedback path). Icon is the most broadly consumed (12
consumers) and the only primitive with on-device smoke coverage. CheckboxIndicator
and CompoundItemLayout are narrower (3 consumers each) and more directly
substitutable.
Relationship to src/common
Observed.
src/common
contains item.types.ts, which exports ItemSecondaryContentPosition -- a type used
by CompoundItemLayoutProps. src/common is not exported from src/index.ts and is
documented in
src/AGENTS.md
as "component-library-specific non-public types, constants, and helpers." A functioning
package-private tier therefore already exists.
Upstream catalog position
Observed. The canonical catalog is x3-design/fluent-design at
d334acf,
indexed by
component-map.yaml.
Its 50 entries have no "primitives" tier; components are atom or molecule. None of
the five FURN primitives maps to a catalog entry. Icon is a concept that appears in
many upstream component slot definitions but is not itself a catalog component. The
other four have no upstream analogue at any tier.
Inferred. The primitives are a FURN implementation artifact, extracted because
multiple higher-order components share the same behavioral or structural contract.
That origin is consistent with package-private status but does not preclude making them
public; it means the contract obligation would be entirely self-imposed.
Scope
1. Owner decision (resolved)
The five primitives are public API, with these approved boundaries:
- Export primitive components and their public types only from
@fluentui-react-native/components/primitives; do not retain compatibility exports
at the package root.
- Keep primitive component names stable, matching the naming convention for normal
components (CheckboxIndicator, CompoundItemLayout, FocusVisual, Icon, and
LayoutStableText).
- Apply the normal
_unstable convention to composition and helper APIs. Export
createFocusVisualProps_unstable from the primitive subpath.
- Give each public primitive a lightweight local
CONTRACT.md covering behavior,
accepted sources and props, accessibility, platform branches, and stability rather
than inventing an upstream SPEC.md.
- Apply the normal component readiness ladder to primitives, substituting fidelity to
the local contract, public types, and renderer branches for upstream-spec fidelity.
Implementation: #4288.
2. Export guard repair
Guard the package root and primitive subpath as separate exact runtime surfaces. The
root guard must contain only higher-order APIs; the primitive guard must contain all
five primitives, including Icon, plus createFocusVisualProps_unstable.
3. Contract and spec policy for public primitives
Each public primitive requires a lightweight CONTRACT.md stating behavioral
invariants, accepted source types and props, accessibility behavior, renderer or
platform branches, and stability expectations, without a design-system spec tie.
The coverage contract in
component-test-strategy.md
derives primitive coverage from that contract, the public types, and renderer branches.
4. Readiness model implications
Apply the same readiness ladder used for normal components. For primitives, local
contract fidelity replaces upstream-spec fidelity, so the absence of an upstream catalog
entry does not impose a readiness ceiling.
5. Authoring guidance update
Update
src/primitives/AGENTS.md
and
src/AGENTS.md
to state the approved scope, the contract obligation for any public primitive, and the
process for deciding when a new primitive warrants extraction.
Out of scope
- Changing the behavior of any primitive.
- Adding or removing primitives; this task settles the boundary for the existing five.
- Deciding the
_unstable suffix policy for higher-order component pipeline exports;
that is readiness-model.md.
- Extracting
src/common into its own package; the package-private tier already
works and is not at issue.
- Replacing
FocusVisual with a new focus-feedback mechanism; that is
focus-zone.md.
Deliverables
- An owner decision record stating the approved scope for each primitive (public,
package-private, or separate package), with the rationale.
src/index.ts updated to match the decision: exports removed, renamed, or
unchanged.
src/index.test.ts updated so every symbol exported from src/index.ts is
asserted, including Icon if it remains public.
- For any primitive that becomes package-private: public slot types in higher-order
components updated to remove constructor references, using structural types or
internal aliases instead.
- For any primitive that remains public: a contract document at the agreed depth
stating behavioral invariants and stability expectations.
src/primitives/AGENTS.md and src/AGENTS.md updated to state the approved
scope and the process for adding a new primitive.
- readiness-model.md updated to reflect the decision in the
primitives row and the "ceiling" note.
- component-test-strategy.md updated to close the
open decision on whether primitives derive their coverage contract from authored
specs or from types files.
Acceptance criteria
Dependencies and ordering
- Soft-blocks readiness-model.md: primitives cannot be
assigned a meaningful readiness level until their scope is decided; the readiness
model can proceed for the 18 higher-order components independently.
- Soft-blocks component-test-strategy.md: the
coverage contract for primitives cannot be written until this task resolves what a
primitive is obligated to document.
- Independent of focus-zone.md: the FocusZone work does not
depend on whether FocusVisual is public; it depends only on the primitive's
behavioral contract, which does not change here.
- Independent of spec-ingestion-and-generation.md:
spec ingestion is a higher-order component concern; primitives have no upstream entry
to ingest regardless of scope.
- Independent of the missing-component wave tasks: wave 1 and wave 2 components
will use the existing primitives as internal building blocks; the scope decision does
not change that usage.
Risks and open decisions
-
Slot type leakage under Option B. Resolved by retaining
CheckboxIndicator and the other primitive constructors as public exports from the
primitive subpath, so higher-order public slot declarations continue to reference
public constructors.
-
Icon source types as implicit public API. Resolved by exporting
FontIconSource, IconElementProps, IconProps, and SvgIconSource with Icon
from the primitive subpath.
-
Separate package overhead. Resolved by using a package subpath rather than
creating a separate published package.
-
createFocusVisualProps is a function, not a component. It converts
FocusVisualOptions into a pair of ring ViewStyle arrays. It is the most
"internal-feeling" export among the five -- no component slot references it and it
exists purely to share ring-geometry computation. Resolved by retaining it as the
public primitive composition helper createFocusVisualProps_unstable.
Evidence and references
Sources retrieved 2026-08-21.
Summary
Decide whether the five FURN-only primitives are public API, package-private, or a
separate package, then apply the resulting export, spec-policy, and test-guard changes
without altering primitive behavior.
Observed.
packages/agentic/componentsexports five primitives --Icon,CheckboxIndicator,CompoundItemLayout,FocusVisual, andLayoutStableText-- fromits only public entry point. None of them has a
SPEC.md, a recorded upstream catalogentry, or any upstream contract. The package's own
authoring guidance
treats primitives as "unstyled building blocks" extracted for repeated behavioral or
structural contracts. Every consumer of every primitive is inside the same package; no
external package or application imports them directly today.
Inferred. A public export without a contract, a spec, or an explicit scope decision
is a forward compatibility hazard. Until the decision is made, primitives cannot be
assigned a readiness level, and consumers cannot know whether they are adopting stable
API or an implementation detail that may change.
Goal
Record the owner-approved scope decision -- public API, package-private, or
separate package -- and apply the resulting mechanical changes to exports, the export
guard test, the readiness model, and the component test strategy without changing
primitive behavior.
Stage
Stages 0 and 1 (scope decision through beta foundations). Stage 0 records the
owner-approved public, private, or separate-package disposition. Stage 1 applies
the corresponding export, contract, test, and readiness changes. The readiness
model cannot assign a level to primitives until their scope is settled; every
new component generated while the decision is open inherits the same ambiguity.
Why it matters
Observed. The provisional readiness assessment in
readiness-model.md places all five primitives at
experimentalwith the ceiling note: "noSPEC.mdand no upstream catalog entry, sothe spec-fidelity axis cannot be evaluated at all." That ceiling is structural, not
coverage-driven. It does not lift until the scope is decided.
Observed. The component test strategy notes that
primitives have no
SPEC.mdand leaves open whether the coverage contract derives fromtheir types file or from authored specs. The contract cannot be written until the scope
decision resolves what a primitive is obligated to document.
Observed. Four out of five primitives are in
src/index.test.ts's"exports shared primitives" block.
Iconis exported insrc/index.tsbut is not asserted in that block, creating a silent gap in the export guard for the
most broadly consumed primitive.
Inferred. The
_unstablesuffix on the component pipeline exports signals "thecomposition API may change," not "the component is immature." Primitives carry no
equivalent signal. A consumer who imports
IconorFocusVisualtoday has noindication whether those names are stable API or subject to silent removal.
Observed current state
The five primitives
Observed. All five reside under
src/primitivesand are exported explicitly from
src/index.ts.src/index.tsiconIcon,FontIconSource,IconElementProps,IconProps,SvgIconSourcecheckbox-indicatorCheckboxIndicator,CheckboxIndicatorProps,CheckboxIndicatorStatuscompound-item-layoutCompoundItemLayout,CompoundItemLayoutPropsfocus-visualFocusVisual,createFocusVisualProps,FocusVisualOptions,FocusVisualProps,FocusVisualRingProps,FocusVisualStylescreateFocusVisualPropsconverts token values into ring ViewStyle arrays.layout-stable-textLayoutStableText,LayoutStableTextPropsCoverage
Observed. Each primitive has a colocated runtime test, a type test, and a
Storybook story. None has a
__snapshots__directory and none has aSPEC.md. TheStorybook story titles use the
Primitives/prefix (e.g.,'Primitives/Icon'),placing them in the same sidebar tier as
'Primitives/Callout'frompackages/native/Callout.Observed.
primitives-icon--defaultis one of only three story IDs inapps/storybook/scripts/smoke-stories.json,alongside
components-button--defaultandprimitives-callout--default.Iconistherefore the only one of the five primitives with any on-device render coverage.
Observed. The export guard in
src/index.test.tsasserts
CheckboxIndicator,CompoundItemLayout,FocusVisual,createFocusVisualProps, andLayoutStableTextin an "exports shared primitives"block.
Iconis exported fromsrc/index.tsbut is not asserted in that block.Internal consumer inventory
Observed. All consumers are inside
packages/agentic/components/src/components.No external package and no application imports a primitive directly from
@fluentui-react-native/components.Iconaccordion,avatar,badge,button,divider,input,list-item,listbox-item,menu-item,progress-bar,tab,tag(12 of 18)FocusVisualaccordion,button,card,checkbox,list-item,listbox-item,menu-item,radio,switch,tab,tag(11 of 18)LayoutStableTextbutton,list-item,listbox-item,menu-item,tab(5 of 18)CompoundItemLayoutlist-item,listbox-item,menu-item(3 of 18)CheckboxIndicatorcheckbox,listbox-item,menu-item(3 of 18)Observed.
FocusVisualis the most cross-cutting primitive (11 consumers, 5exported types plus a helper function, and named explicitly in
src/components/AGENTS.mdas the mandated focus-feedback path).
Iconis the most broadly consumed (12consumers) and the only primitive with on-device smoke coverage.
CheckboxIndicatorand
CompoundItemLayoutare narrower (3 consumers each) and more directlysubstitutable.
Relationship to src/common
Observed.
src/commoncontains
item.types.ts, which exportsItemSecondaryContentPosition-- a type usedby
CompoundItemLayoutProps.src/commonis not exported fromsrc/index.tsand isdocumented in
src/AGENTS.mdas "component-library-specific non-public types, constants, and helpers." A functioning
package-private tier therefore already exists.
Upstream catalog position
Observed. The canonical catalog is
x3-design/fluent-designatd334acf,indexed by
component-map.yaml.Its 50 entries have no "primitives" tier; components are
atomormolecule. None ofthe five FURN primitives maps to a catalog entry.
Iconis a concept that appears inmany upstream component slot definitions but is not itself a catalog component. The
other four have no upstream analogue at any tier.
Inferred. The primitives are a FURN implementation artifact, extracted because
multiple higher-order components share the same behavioral or structural contract.
That origin is consistent with package-private status but does not preclude making them
public; it means the contract obligation would be entirely self-imposed.
Scope
1. Owner decision (resolved)
The five primitives are public API, with these approved boundaries:
@fluentui-react-native/components/primitives; do not retain compatibility exportsat the package root.
components (
CheckboxIndicator,CompoundItemLayout,FocusVisual,Icon, andLayoutStableText)._unstableconvention to composition and helper APIs. ExportcreateFocusVisualProps_unstablefrom the primitive subpath.CONTRACT.mdcovering behavior,accepted sources and props, accessibility, platform branches, and stability rather
than inventing an upstream
SPEC.md.the local contract, public types, and renderer branches for upstream-spec fidelity.
Implementation: #4288.
2. Export guard repair
Guard the package root and primitive subpath as separate exact runtime surfaces. The
root guard must contain only higher-order APIs; the primitive guard must contain all
five primitives, including
Icon, pluscreateFocusVisualProps_unstable.3. Contract and spec policy for public primitives
Each public primitive requires a lightweight
CONTRACT.mdstating behavioralinvariants, accepted source types and props, accessibility behavior, renderer or
platform branches, and stability expectations, without a design-system spec tie.
The coverage contract in
component-test-strategy.md
derives primitive coverage from that contract, the public types, and renderer branches.
4. Readiness model implications
Apply the same readiness ladder used for normal components. For primitives, local
contract fidelity replaces upstream-spec fidelity, so the absence of an upstream catalog
entry does not impose a readiness ceiling.
5. Authoring guidance update
Update
src/primitives/AGENTS.mdand
src/AGENTS.mdto state the approved scope, the contract obligation for any public primitive, and the
process for deciding when a new primitive warrants extraction.
Out of scope
_unstablesuffix policy for higher-order component pipeline exports;that is readiness-model.md.
src/commoninto its own package; the package-private tier alreadyworks and is not at issue.
FocusVisualwith a new focus-feedback mechanism; that isfocus-zone.md.
Deliverables
package-private, or separate package), with the rationale.
src/index.tsupdated to match the decision: exports removed, renamed, orunchanged.
src/index.test.tsupdated so every symbol exported fromsrc/index.tsisasserted, including
Iconif it remains public.components updated to remove constructor references, using structural types or
internal aliases instead.
stating behavioral invariants and stability expectations.
src/primitives/AGENTS.mdandsrc/AGENTS.mdupdated to state the approvedscope and the process for adding a new primitive.
primitives row and the "ceiling" note.
open decision on whether primitives derive their coverage contract from authored
specs or from types files.
Acceptance criteria
src/index.tscontains exactly the exports approved by the decision.src/index.tsis asserted insrc/index.test.ts.constructor;
typeof CheckboxIndicator,typeof CompoundItemLayout, etc. inslot types are either from a public export or replaced by a structural type.
src/primitives/AGENTS.mdstates the approved scope and the addition process.the decision.
component-test-strategy.md is closed.
yarn workspace @fluentui-react-native/components format,lint,build, andtestpass after the changes.Dependencies and ordering
assigned a meaningful readiness level until their scope is decided; the readiness
model can proceed for the 18 higher-order components independently.
coverage contract for primitives cannot be written until this task resolves what a
primitive is obligated to document.
depend on whether
FocusVisualis public; it depends only on the primitive'sbehavioral contract, which does not change here.
spec ingestion is a higher-order component concern; primitives have no upstream entry
to ingest regardless of scope.
will use the existing primitives as internal building blocks; the scope decision does
not change that usage.
Risks and open decisions
Slot type leakage under Option B. Resolved by retaining
CheckboxIndicatorand the other primitive constructors as public exports from theprimitive subpath, so higher-order public slot declarations continue to reference
public constructors.
Iconsource types as implicit public API. Resolved by exportingFontIconSource,IconElementProps,IconProps, andSvgIconSourcewithIconfrom the primitive subpath.
Separate package overhead. Resolved by using a package subpath rather than
creating a separate published package.
createFocusVisualPropsis a function, not a component. It convertsFocusVisualOptionsinto a pair of ring ViewStyle arrays. It is the most"internal-feeling" export among the five -- no component slot references it and it
exists purely to share ring-geometry computation. Resolved by retaining it as the
public primitive composition helper
createFocusVisualProps_unstable.Evidence and references
packages/agentic/components/src/primitivespackages/agentic/components/src/index.tspackages/agentic/components/src/index.test.tspackages/agentic/components/src/primitives/AGENTS.mdpackages/agentic/components/src/AGENTS.mdpackages/agentic/components/src/components/AGENTS.mdx3-design/fluent-designcomponent-map.yamlapps/storybook/scripts/smoke-stories.jsonpackages/agentic/components/package.jsonSources retrieved 2026-08-21.