From 910da3fbec3aa86bd795afca59ae15b8c7ed1fad Mon Sep 17 00:00:00 2001 From: Jason Morse Date: Wed, 14 Oct 2026 16:57:27 -0700 Subject: [PATCH 01/14] initial agent improvements research --- agent-improvements-plan.md | 589 +++++++++++++++++++++++++++++++++++++ 1 file changed, 589 insertions(+) create mode 100644 agent-improvements-plan.md diff --git a/agent-improvements-plan.md b/agent-improvements-plan.md new file mode 100644 index 0000000000..aad18ac8a4 --- /dev/null +++ b/agent-improvements-plan.md @@ -0,0 +1,589 @@ +# Agent instruction and skill improvements plan + +**Status:** Proposed; awaiting user decisions. This document authorizes no +instruction migration, skill changes, policy changes, or evaluation runs. + +**Basis:** Repository inspection at `246d336682c628e6ff65d11c6f5997d9135be5b6` +on 2026-10-14, independent plans from Claude Opus 5.5 and GPT 6 Astra, and +reciprocal cross-review in the original reviewer threads. + +## Recommended outcome + +Create **`references/agents/`** as the shared, tool-neutral home for reusable +agent guidance. Keep root and scoped `AGENTS.md` files as discovery entrypoints +and concise routers; keep discoverable workflows in `.github/skills/`. Leave +product contracts, protocols, token mappings, package documentation, and +historical plans beside their owning code. + +Recommend **separate generation-specific recipes, not eight immediately +separate skills**: one classification-first v0/v1 workflow, the existing +agentic authoring workflow, one read-only component-review workflow, and a +native-authoring entrypoint that also acts as an overlay. Native is a platform +and integration dimension, not an exclusive fourth component generation. +Retain the existing skill names initially; explicit `implement-*` names remain +an owner choice. + +Begin with a small routing and non-safety extraction pilot. Defer Apple/Office, +permissions, and security-policy extraction until a separately approved parity +review. Establish portable rule IDs and evidence records without choosing or +building an evaluation framework. + +## 1. Goals, boundaries, and success criteria + +| Goal | Success criterion | +| --------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Centralized shared guidance | Reused rules have one authoritative home and explicit owners; skills and root/scoped routers link to that home. | +| Future evaluation reuse | A case can identify the applicable instructions, rule assertions, inspected repository revision, and actual evidence without depending on an agent vendor. | +| Clear component workflows | Tasks distinguish v0, v1, agentic higher-order, agentic primitive, and native obligations without using directory names alone. | +| Progressive loading | Agents load the applicable workflow and conditional references, not the complete instruction collection. Actual loading and duplication are measured. | +| Safe continuous improvement | Evidence produces a human-reviewable proposal; no autonomous policy weakening, external publication, or raw-transcript collection. | + +This is an organizational change, not a component migration. Preserve public +APIs, framework behavior, native registration, supported hosts, contract +lifecycle, qualification requirements, permission behavior, and publishing +policy. Correct stale instructions only after reconciling them with their +owning implementation; do not use the reorganization to expand unrelated work. + +## 2. Observed starting point + +The inspected checkout contains **12 `AGENTS.md` files including the root, +two root/tool adapters, five repository skills, ten authoring references, and +six contract-source adapters**. The root is 418 lines and approximately 30 KB. +These are file measurements, not token measurements. + +| Finding | Evidence and implication | +| ------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Root guidance conflates component models | [Root component guidance](AGENTS.md) recommends composition and new `stagedComponent` usage at line 257; [framework-base guidance](packages/framework-base/AGENTS.md), line 15, prohibits new staged usage. These need explicit domain routing, not a global winner. | +| v0/v1 guidance is outside consumer ancestry | [v0 guide](packages/deprecated/foundation-compose/AGENTS.md), lines 3-6, and [v1 guide](packages/framework/framework/AGENTS.md), lines 3-6, apply to consuming components outside their directories. Ancestor discovery alone cannot be assumed to supply them. | +| A package can contain both generations | [Deprecated Button](packages/components/Button/src/deprecated/Button.tsx), lines 17-19, uses foundation-compose; [current Button](packages/components/Button/src/Button.tsx), lines 6-7, uses framework composition. | +| Helper names do not determine generation | [v1 Text](packages/components/Text/src/Text.tsx), lines 5-17 and 133, uses `compressible` and a `directComponent` continuation. [Agentic Button](packages/agentic/components/src/components/button/button.ts), lines 9-13, is a normal React function assembling its stages. | +| Legacy API and implementation model are different axes | [Native-core Callout facade](packages/agentic/native-core/src/legacy/callout/Callout.tsx), lines 2-10, uses modern helpers while retaining legacy compatibility. | +| Native cuts across generations | [Experimental Checkbox](packages/experimental/Checkbox/src/Checkbox.macos.tsx), lines 10 and 27-39, combines v1 composition with a native root. Native modules may have no component generation at all. | +| Shared references are owned by one skill directory | [Framework-base guidance](packages/framework-base/AGENTS.md), line 54, consumes authoring focus guidance; [native-core README](packages/agentic/native-core/README.md), line 177, consumes its Windows Fabric guidance. | +| Existing loading instructions are too broad | [Authoring skill](.github/skills/agentic-component-authoring/SKILL.md), lines 37-46, includes a conditional Windows reference in an "every reference" recommendation and needs a primitive-specific `CONTRACT.md` branch instead of unconditional `SPEC.md`. | +| Commands need source reconciliation | [Root manifest](package.json), lines 18-19 and 31, defines `build` as `lage root-build`, `build:clean` separately, and `root-build` as `tsc -b`. Do not copy the root's `yarn build --clean` advice into the new reference unchecked. | +| Instruction anchors are contract dependencies | [Button specification](packages/agentic/components/src/components/button/SPEC.md), lines 70 and 99, cites `src/components/AGENTS.md#focus-visual-policy`. Reviewers found 45 such links in 34 files and three root Apple/Office-anchor links. Preserve their meaning and compatibility. | +| Contract infrastructure already has its own schema | [SPEC-SOURCE.md](packages/agentic/components/SPEC-SOURCE.md), lines 24-57 and 185-216, defines provenance and lifecycle. [Contract checker](packages/agentic/components/scripts/spec-contracts/check-spec-contracts.cjs), lines 45-75 and 504-546, enforces profile/layout constraints. Do not repurpose it as an instruction registry. | + +**Observed, session-specific:** the review context included the root guidance +both directly and through the `CLAUDE.md` import. This warrants a duplication +probe; it does not prove identical behavior in all clients. Copilot CLI help +confirms root/cwd instruction locations and `/instructions`, `/env`, `/skills`, +`/context`, and `/usage`. Linked-file transclusion, nested inheritance, and +other clients' discovery semantics remain unqualified. + +**Additional migration candidates:** Claude identified stale tester paths, +E2E naming, `depcheck`/format advice, changeset-script references, and the docs +command. Record these in the migration ledger and verify against current +manifests/source before changing them. They were inspected, not all executed. + +## 3. Target architecture and authority + +```text +AGENTS.md # Repository safeguards and task router +CLAUDE.md # Existing adapter; qualify before changing +.github/copilot-instructions.md # Existing adapter +.github/skills/ + agentic-component-authoring/ + agentic-component-contract-authoring/ + agentic-component-optimization/ + agentic-storybook-development/ + fix-security-issues/ + maintain-v0-v1-component/ # Proposed; separate conditional recipes + review-component/ # Proposed; read-only, all generations + implement-native-component/ # Proposed; also modules/services overlay + assess-agent-efficacy/ # Optional; proposal-only +references/agents/ + README.md # Short index, ownership, document roles + repository-development.md # Verified commands and validation routing + components/ + classify.md + v0.md + v1.md + agentic/ # Only recipes proven shared by workflows + focus/ + common.md + windows-win32.md + macos.md + native/ + windows-fabric.md + apple-office.md # Later, separately approved extraction + qualification.md # Only if genuinely shared detail warrants it +packages/**/AGENTS.md # Retained scoped entrypoints +apps/storybook/AGENTS.md # Retained app entrypoint +``` + +This is a destination map, not a requirement to create every listed file. +Keep the primary routing table in root `AGENTS.md`; do not add another mandatory +`routing.md` hop. Detailed classification is loaded when the task requires it. +Review sequencing belongs in the review skill, not a second duplicated protocol. + +### Ownership and precedence + +- Root owns repository-wide safeguards and routes. Scoped entrypoints own + local invariants and explicitly identify their applicable domain. +- Skills own task sequencing, entry conditions, required inputs, outputs, + conditional reads, and stop conditions. +- Shared references own reusable behavioral rules. Their central location + does not transfer technical stewardship away from the owning package. +- Product contracts, transport protocols, token definitions, and source locks + stay authoritative in their existing homes. A README can contain an + authoritative protocol; a filename alone does not establish its role. +- Respect actual runtime instruction priority and user authorization. Do not + invent a repository precedence ladder above either. Scope determines which + rule applies; path depth is not a universal conflict-resolution mechanism. +- Validator failures are evidence of unmet enforced contracts, not permission + to override reviewed requirements silently. Escalate unresolved same-domain + conflicts. Plans and historical observations do not become implementation + guarantees through relocation. + +Keep one full authoritative definition per rule. Concise entrypoint warnings +and links are intentional safety redundancy, not another complete copy. +The permission hook provides approvals; unmatched commands are not blanket +denials. This migration must preserve that distinction. + +## 4. Classification and proposed skill workflows + +Classify the actual changed surfaces before selecting recipes: + +1. **Task mode:** implement/maintain, contract-only, plan-only, review, + cross-component optimization, Storybook/toolchain, or dependency security. +2. **Implementation model:** v0 foundation-compose, v1 compose/compressible, + agentic higher-order, agentic primitive, another framework-base pattern, + or no component model. +3. **Public compatibility:** legacy aliases/`componentRef`, V1 exports, + modern `ref` contracts, primitive entrypoints, or native-core legacy facade. +4. **Native obligations:** platform, renderer, host ownership, schema, + registration, Apple module/bridging integration, and qualification status. + +Paths, imports, exports, and component structure provide evidence together. +Neither `legacy/`, a JSX pragma, nor a single helper import is sufficient. +For mixed diffs, classify each surface; never force the whole change into one +generation. + +| Surface | Implementation workflow | Conditional recipe and review obligations | +| ------------------------------- | ------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| v0 | `maintain-v0-v1-component`, v0 branch | Settings layering, filters, prepare/render boundary, token dependencies, overrides, aliases, and existing public refs. Preserve the current maintenance-first v0 policy. | +| v1 | Same entrypoint, v1 branch | Compose versus compressible behavior, continuations, token precedence, customization, platform variants, and existing ref/API compatibility. New-v1 policy requires D04. | +| Agentic higher-order | Existing `agentic-component-authoring` | Reviewed contract gate where applicable; stage structure, slots, state ownership, cached styles, rendering, tests, and stories. Contract-only work routes to the contract skill. | +| Agentic primitive | Explicit branch of the same authoring skill | Local `CONTRACT.md`, unstyled acceptance API, renderer branches, exports, slot type tests, and documented ref exceptions. Do not impose Flex provenance or a uniform imperative ref. | +| Native component/module/service | `implement-native-component`, directly or as an overlay | Codegen, registration, UI affinity, lifetimes, cancellation, renderer/host admission, packaging, and native evidence. Use native-core for new shared modern native work; do not force unrelated old wrappers to migrate. | +| Any component review | `review-component` | Read-only classification, applicable generation checklist, relevant native/focus overlays, high-confidence findings with path/line evidence, and truthful validation status. | + +The native skill name would have to describe modules and shared host services +as well as components. `native-authoring` is a clearer alternative if D03 +favors broader names. + +**Why not eight skills initially?** Skill selection happens before an agent +has necessarily inspected APIs; v0/v1 can share a package and a review can span +both. Separate recipe branches preserve incompatible conventions while one +entry workflow avoids overlapping metadata. A single review route shares +read-only rules and evidence standards without duplicating four workflows. +Split entrypoints if measured routing errors justify it, not merely to achieve +symmetrical names. + +**Eight-skill alternative:** `implement-v0-component`, +`implement-v1-component`, `implement-agentic-component`, +`implement-native-component`, and corresponding `review-*` skills. If chosen, +each must delegate to shared recipes rather than copy them, mixed-generation +review must have an explicit composition route, and native must remain an +overlay. Retire replaced active skills atomically; never leave discoverable +alias skills competing for automatic selection. + +## 5. Progressive loading contract + +1. Start with the root router and applicable scoped instructions. Verify + actual discovery; explicitly read required scopes when it is unqualified. +2. Select one task workflow. Read its positive scope, exclusions, and output + contract before invoking adjacent workflows. +3. Load only the chosen generation recipe, relevant product contract, and + references triggered by the work. Loading both v0 and v1 is appropriate + only for a genuinely mixed change. +4. Load native/focus overlays for semantic changes as well as file suffixes: + schemas, refs, platform wrappers, registration, Pod/build configuration, + accessibility actions, and focus behavior can cross native boundaries. +5. Choose source adapters by evidence type; do not load all six by default. + Look up relevant token entries rather than reading the entire mapping or + source inventory. +6. Links are navigation, not assumed automatic transclusion. Avoid recursive + "read everything" instructions, circular dependencies, and redirect chains. +7. Never trade required contract or safety evidence for fewer tokens. Missing + validation is reported explicitly, not converted to a successful fallback. + +Suggested **review budgets**, subject to D08: root eventually around 8 KB or +1,000 words, skill bodies around 600 words, descriptions around 50 words with +clear exclusions, and mandatory root-to-rule routes within two explicit reads. +Keep safety triggers directly in always-on entrypoints. Scoped files and +coherent references have advisory budgets, not hard truncation limits. + +Measure unique file bytes/words separately from actual injected tokens, +repeated loading, tool calls, and elapsed time. Shortening files does not prove +reduced runtime context. A 30% median loading reduction may be a later reporting +target, **not an empirically established prediction or pilot gate**. + +## 6. Minimal evaluation groundwork + +Do not introduce an instruction DSL, retrieval service, vendor-specific runner, +or a generated instruction system. + +Recommended first convention, subject to D07: three metadata fields on shared +references only: + +```yaml +--- +id: native.apple-office +kind: instruction-reference +owner_paths: + - packages/agentic/native-core +--- +``` + +Use `instruction-reference`, `evidence-reference`, or `compatibility-reference` +to distinguish document roles. These are repository conventions, not assumed +runtime-recognized frontmatter. Keep load conditions in routers, not duplicated +in a registry, frontmatter, and skill description. + +Assign stable IDs only to consequential, testable obligations, for example +`R-APPLE-001`, and separate version-bound observations as `C-FOCUS-001` with +source, inspected revision/version, date, and limits. Do not reuse component +requirement IDs or modify their checker schema. Distinguish an authoritative +rule definition from references to its ID. + +Git identifies document versions. Compute file hashes when assessing a case; +do not maintain per-file revision counters or self-referential hashes. +An optional generated inventory can follow once an integrity checker has a +real need; do not start with both a rich catalog and duplicate frontmatter. + +A portable case/result boundary can use: + +```yaml +case_id: classify-v1-compressible +task_mode: review +repository_commit: '' +instructions: + - path: references/agents/components/v1.md + sha256: '' +assertions: + - id: preserves-v1-public-ref + rule_ids: [R-V1-001] + source_refs: ['packages/components/Text/src/Text.tsx:5-17'] + result: not-run + evidence: 'No execution recorded.' +``` + +Case definitions describe inputs and expected assertions; execution results +add runtime/model versions, actual loaded files, command outcomes, skips, +measurement method, and sanitized evidence. Separate expected reads from +observed reads. A future evaluation framework can consume these artifacts +without changing the underlying instructions. + +Small sanitized case fixtures may be checked in after D11 approval; otherwise +keep them local. Framework selection is not a prerequisite for portable cases. +The agentic contract checker discovers that package's higher-order components, +not native-core specs +([checker common module](packages/agentic/components/scripts/spec-contracts/common.cjs), +lines 5-9 and 58-60). Do not claim native-contract coverage from it. + +## 7. Migration map + +### Existing skills and references + +| Existing surface | Proposed treatment | +| -------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `agentic-component-authoring` | Retain name initially. Narrow implementation scope, distinguish primitive/higher-order contracts, and replace blanket reference loads with explicit conditions. | +| Its types/state/styles/render/tests references | Retain initially. Move an individual recipe to `references/agents/components/agentic/` when implementation and review actually share it, updating both consumers in the same change. | +| Its three focus references | Centralize under `references/agents/focus/` after the pilot; preserve cross-generation intent, platform-specific recipes, version limits, and historical evidence labels. | +| Its Windows Fabric reference | Centralize under `references/agents/native/windows-fabric.md` after the pilot; preserve native-core's narrower Fabric-only boundary. | +| Its `spec-source-adaptation.md` redirect | Retain as a non-skill compatibility reference until explicit retirement. No tracked inbound links does not establish absence of external consumers. | +| `agentic-component-contract-authoring` and six source adapters | Retain lifecycle and skill-local adapters. Move only if demonstrated reuse warrants it; keep `SPEC-SOURCE.md`, source locks, `agency.toml`, and checker locations intact. | +| `agentic-component-optimization` | Retain cross-component scope. Link to dependency-direction authority instead of copying it; do not broaden to repository-wide optimization. | +| `agentic-storybook-development` | Retain. Route operations through app-owned instructions/map and current declared scripts; keep the live story catalog out of shared instructions. | +| `fix-security-issues` | Retain safeguards, age policy, remediation ordering, and fail-closed behavior. No policy changes through extraction. | + +### Every instruction entrypoint + +| Entrypoint | Proposed treatment | +| ------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Root `AGENTS.md` | Compact router and safeguards. Extract reconciled architecture/commands/framework recipes incrementally. Preserve permissions and Apple/Office text in the first pilot. | +| `CLAUDE.md` and `.github/copilot-instructions.md` | Retain discovery filenames and paths; change adapter/import semantics only after actual supported-client probes. | +| `packages/agentic/components/AGENTS.md` | Retain package boundaries, modern API invariants, and component/primitive routes. Link to shared recipes without repeating them. | +| `packages/agentic/components/src/AGENTS.md` | Retain dependency direction, public primitive boundaries, and extraction ownership. | +| `packages/agentic/components/src/components/AGENTS.md` | Retain higher-order rules and the `#focus-visual-policy` anchor. Detailed reusable recipes may move; contract meaning does not. | +| `packages/agentic/components/src/primitives/AGENTS.md` | Retain local-contract, unstyled behavior, renderer, export, and ref exceptions. | +| `packages/agentic/native-core/AGENTS.md` | Retain host/export/lifetime boundaries; link to shared native guidance when approved. | +| `packages/agentic/desktop-driver/AGENTS.md` | Retain ownership/security constraints and local documentation routes. Do not move its package reference tree. | +| `packages/framework-base/AGENTS.md` | Retain modern-pattern/React compatibility boundaries; update shared focus links. | +| `packages/framework-base/src/accessibility/AGENTS.md` | Retain abstraction/import constraints and qualification requirements; it is cited by product specs. Separate mixed technical evidence only with owner review. | +| `packages/framework/framework/AGENTS.md` | Keep framework-maintainer entrypoint; extract reusable v1 consumer guidance to `components/v1.md`. | +| `packages/deprecated/foundation-compose/AGENTS.md` | Keep deprecated-framework entrypoint; extract reusable v0 guidance to `components/v0.md`. | +| `apps/storybook/AGENTS.md` | Retain app invariants and `agent-map.yaml` route. Keep operational diagnostics app-owned and deduplicate only after reconciling scripts. | + +Update tracked inbound Markdown links, anchors, backtick paths, map paths, and +tooling/profile dependencies atomically. Keep the root +`#apple-native-authoring-and-office-integration` anchor as a short route if +Apple extraction is later approved. Preserve the component focus anchor. +Compatibility redirects are ordinary Markdown, not additional active skills. +Check packed-package contents if changing package-local documentation paths; +do not assume such files never ship. + +## 8. Phased execution and gates + +All phases require owner approval of their blocking decisions. The coordinator +owns the ledger, routing, and synthesis; affected package maintainers own +technical parity; the user owns architecture/policy choices and acceptance. + +| Phase | Work and dependencies | Exit gate | +| ------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | +| P0: baseline | Resolve D01-D08 as relevant. Inventory paths, rule definitions, consumers, anchors, stale claims, supported runtimes, and observed loading. Capture a rule-preservation ledger before edits. | Approved pilot scope and cases; discovery assumptions recorded, not guessed. | +| P1: bounded pilot | After P0, add classification route, one v1 consumer extraction, a `review-component` prototype, and limited non-safety root slimming associated with those routes. Resolve stale statements in the touched scope before extraction. No native/safety relocation or source-adapter moves. | Static integrity, actual discovery, correct routing, and preserved rules; efficiency reported without a mandatory percentage gain. | +| P2: workflow rollout | After successful P1 and D02-D04, add merged v0/v1 and native entrypoints, separate recipes, and small agreed integrity tooling under D09. | Mixed-generation and native-overlay cases select the correct recipes; review remains read-only. | +| P3: shared-reference consolidation | After P2 and D10, move proven shared focus/Windows guidance and genuinely shared agentic recipes. Keep source adapters and domain docs local. | Inbound links/anchors updated; no lost rule, evidence limit, owner, or contract meaning. | +| P4: safety and compatibility qualification | Only after explicit D06 approval and parity review, consider Apple/permissions extraction. Requalify adapters, packed-file effects, and supported-client loading; wire approved integrity checks into CI. | Safety parity, truthful native evidence, and compatibility retained. Removal requires a separate explicit retirement decision. | +| P5: optional efficacy loop | After stable earlier phases and D11-D12, introduce proposal-only assessment using approved evidence and untouched holdouts. | Human-reviewed improvement with regression evidence; no autonomous canonical policy edits. | + +No unrelated native rebuild is required merely to move Markdown. If instructions +change a native recipe, validate the actual affected boundary and record +unavailable host/Office/runtime checks. A source build is not evidence of +physical input, UIA/AX action dispatch, or Office qualification. + +### Pilot cases and measurement + +Use six development cases: v0 Button maintenance, v1 Button review, compressible +Text review, agentic Button plan-only work, Icon primitive review, and a +native-backed experimental Checkbox review. Reserve holdouts covering a +modern implementation behind a legacy native-core facade, macOS extern +registration, Storybook operations, and dependency remediation. + +Compare baseline and candidate instructions against the same frozen product +source, task inputs, tools, model configuration, and permissions. Record the +distinct instruction revisions/hashes. Begin with one paired run per approved +case; repeat noisy/ambiguous cases only within D08's resource budget. Qualify a +second supported configuration before broad promotion. + +Hard gates: correct domain routing, no critical-rule loss, no unauthorized +writes or review-to-implementation switch, and no unsupported success claims. +Report per-case loading, duplicate injection, actual tokens when exposed, +tool calls, elapsed time, and human verdict. Small samples and synthetic +routing tasks do not prove end-to-end component-authoring efficacy. + +## 9. Integrity and validation + +Use existing tooling first. Add a small deterministic integrity check only +for gaps that existing tools do not cover; do not disguise agent evaluation as +static linting. + +| Check | Method | +| --------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Preservation | Every previous consequential rule maps to a retained/new authoritative location or an explicitly approved retirement; observations retain versions and evidence limits. | +| Links and fragments | `yarn lage test-links`; additionally verify cross-file heading fragments, compatibility anchors, case-sensitive paths, and registered backtick/map references. Current fragment coverage is unqualified. | +| Routing integrity | Check missing targets, redirect chains, cycles, conflicting mandatory loads, metadata validity, and duplicate rule definitions rather than repeated ID references. | +| Formatting/repository conventions | `yarn format:check` and `yarn lint-repo` for migration changes; use targeted formatting during iteration. | +| Permission parity | `yarn test:agent-permissions` when touching permission-related surfaces; hook behavior must remain unchanged unless separately authorized. | +| Contract-linked changes | `yarn workspace @fluentui-react-native/components check:spec-contracts` when changing linked instruction locations or contract-sensitive guidance. | +| Packaging changes | `yarn check-publishing` and owning-package archive inspection when package documentation paths or published manifests change. | +| Runtime discovery | Probe root and nested cwd using actual supported client versions; inspect loaded instructions, active skills, and duplication. Static links do not establish runtime discovery. | +| Native validation reporting | Record executed, failed, skipped, not-run, and unsupported checks separately, with reasons. All-skipped or JS-only evidence is not native qualification. | + +For implementation/tooling additions, run the owning declared tests/builds and +root graph only when their scope warrants it. Inspect manifests before using +commands; do not assume generic `check`, `depcheck`, or direct runner scripts. +The commands above are migration validation requirements, not claims that this +planning task executed them all. + +## 10. Optional agent-efficacy improvement workflow + +`assess-agent-efficacy` should accept an explicit assessment request and +approved sanitized evidence. A description requesting explicit invocation is +not an enforced opt-out; verify runtime selection controls and retain an entry +gate that stops without user intent. + +Capture case ID, instruction/product revisions, model/client/tool versions, +loaded-file evidence, rule failures, actual command outcomes and skip reasons, +numeric usage where available, and human assessment. Exclude credentials, +private source bodies, personal identifiers, raw transcripts, and unrestricted +machine-log discovery. Do not upload evidence or publish a PR without separate +authorization. + +The output is a local proposal: observed failure, inferred root cause, smallest +instruction/skill change, expected benefit, tradeoffs, and regression results. +Apply it only through human review. Permission hooks/text, security-age policy, +native safety rules, and contract admission are protected review surfaces; +automated assessment cannot weaken or rewrite them. + +Prefer repeated evidence across task families and more than one supported +configuration. Keep acceptance holdouts unseen while tuning; a reused holdout +becomes a development case. Control for model upgrades, tool availability, +task difficulty, caching, compaction, human steering, network/toolchain +failures, native permissions, skipped tests, active plugins, and judge bias. +Cheaper output is not an improvement if correctness or safety regresses. + +## 11. User decisions required before execution + +Recommendations are not approvals. Edit **Answer** and **Status** below. +Use `Answered` or `Deferred`; a deferral should name its reconsideration event. + +| ID | Decision | Recommendation | Blocks | +| --- | -------------------------------- | ----------------------------------------------------------------------------------------------------------------------- | -------------------------------- | +| D01 | Shared home | `references/agents/`, not all package references centralized | P1 extraction | +| D02 | Workflow granularity | Merged v0/v1 entrypoint, separate recipes, one review skill, native overlay | P2 skills | +| D03 | Skill names and compatibility | Retain existing names; add `maintain-v0-v1-component`, `review-component`, and `implement-native-component` | P2 discovery | +| D04 | New v1 component policy | Preserve current supported behavior; explicitly decide whether new v1 work remains allowed | v1 workflow scope | +| D05 | Supported agent/client matrix | Qualify Copilot CLI and Claude Code if required; inventory other actual consumers before promising support | Discovery claims/adapter changes | +| D06 | Safety extraction | Keep Apple/permissions text in first pilot; later extraction only with parity review and retained safety routes/anchors | P4 safety migration | +| D07 | Metadata and rule IDs | Minimal shared-reference frontmatter, selective `R-`/`C-` IDs, Git/hash result identity; no rich registry initially | Eval-ready metadata/tooling | +| D08 | Pilot budget and acceptance | Approved paired cases, correctness/safety hard gates, efficiency measured; 30% only an optional later target | Pilot runs/rollout | +| D09 | Integrity tooling | Small link/anchor/rule checker; wire into existing CI after pilot | Tooling/CI changes | +| D10 | Recipe relocation and retirement | Move only demonstrated shared recipes; preserve adapters/anchors/redirects until explicitly retired | P3 moves/removal | +| D11 | Evidence and fixture storage | Sanitized portable fixtures may be committed; raw logs stay out; no external collection without approval | Eval fixtures/assessment inputs | +| D12 | Improvement governance | Defer proposal-only efficacy skill until pilot evidence; require human approval for promotion | P5 | + +### D01: Shared home + +**Status:** Unanswered +**Answer:** + +Choose `references/agents/` (recommended), root `references/`, or +`.github/references/`. The first preserves a root reference namespace for +future non-agent material. Product-owned documentation remains local under +every option. + +### D02: Workflow granularity + +**Status:** Unanswered +**Answer:** + +Choose the hybrid above (recommended), separate v0/v1 implementation skills +with one review router, or all eight `implement-*`/`review-*` skills. Separate +generation recipes and native-overlay composition are required in all cases. + +### D03: Names and discoverable compatibility + +**Status:** Unanswered +**Answer:** + +Keep existing names initially (recommended), or adopt `implement-*` consistently, +including renaming agentic authoring. Choose whether the native name should be +`implement-native-component` or broader `native-authoring`. No duplicate active +aliases; define any supported transition period explicitly. + +### D04: v1 admission policy + +**Status:** Unanswered +**Answer:** + +Are new v1 components supported, maintenance-only, or allowed only for specific +compatibility requirements? The reviewers did not establish a current +maintenance-only v1 prohibition. Do not invent one during reorganization. + +### D05: Required clients and adapter behavior + +**Status:** Unanswered +**Answer:** + +Name required clients/versions: Copilot CLI, Claude Code, VS Code, cloud coding +agent, code review, Agency, or others. Claude/GPT models inside Copilot are not +evidence of Claude Code discovery. Keep current adapters until the required +matrix verifies imports, nested scopes, skill discovery, and duplication. + +### D06: Safety material + +**Status:** Unanswered +**Answer:** + +Approve deferred extraction with parity review (recommended), retain full +safety guidance always-on permanently, or specify another boundary. Short +native/permission safeguards and explicit conditional routes remain always-on; +no actual policy change is included. + +### D07: Metadata convention + +**Status:** Unanswered +**Answer:** + +Choose minimal frontmatter plus selective IDs (recommended), a single minimal +catalog instead, or plain Markdown initially. Do not duplicate routing and +ownership metadata across multiple inventories. Future evals record revision +and computed hashes, not manually maintained per-file versions. + +### D08: Pilot resource budget and thresholds + +**Status:** Unanswered +**Answer:** + +Approve/adjust the six development and four holdout cases, configurations, +run/retry ceiling, and evidence method before runs. Choose whether efficiency +is reporting-only (recommended) or has a measured acceptance threshold. +The first pilot does not promise a 30% reduction or an 8 KB root. + +### D09: Checker and CI scope + +**Status:** Unanswered +**Answer:** + +Approve a small deterministic integrity checker and later CI wiring +(recommended), or manual checks initially. Decide whether it is invoked by +existing lint/link infrastructure or a declared dedicated script. It checks +instruction integrity, not model behavior. + +### D10: Shared recipes and compatibility retirement + +**Status:** Unanswered +**Answer:** + +Approve consumer-driven recipe moves (recommended), centralize all agent +recipes, or retain skill-local recipes. Keep contract source adapters local +unless actual reuse changes. Set the event required to retire old reference +paths; preserve product-linked anchors irrespective of routine cleanup. + +### D11: Evidence storage + +**Status:** Unanswered +**Answer:** + +Choose checked-in sanitized case fixtures with local execution evidence +(recommended), entirely local cases, or a separately approved external store. +Define public/private boundaries and any retention requirements. Framework +selection can remain deferred. + +### D12: Improvement loop + +**Status:** Unanswered +**Answer:** + +Defer `assess-agent-efficacy` until the pilot establishes useful measurements +(recommended), add a proposal-only skill earlier, or omit it. Define reviewer +ownership and promotion evidence; no autonomous safety edits or external +publication under any option. + +## 12. Cross-review reconciliation and provenance + +| Topic | Reconciled recommendation | +| --------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Root namespace | Both reviewers accepted `references/agents/` after cross-review. | +| v0/v1 skill count | Astra revised its initial separate-skill recommendation to a merged classifier entrypoint with separate recipes; retain splitting as a decision/pilot fallback. | +| Review/native split | Both favored one review workflow and native as an orthogonal entry/overlay. | +| Recipe centralization | Reject wholesale source-adapter migration. Move agentic recipes only with demonstrated implementation/review reuse; move focus/Windows guidance because existing consumers already cross boundaries. | +| Pilot scope | Reject Claude's initial safety-heavy root rewrite. Use limited non-safety slimming so the pilot can measure something; do not require the final root-size target immediately. | +| Authority | Reject a repository-defined global precedence ladder. Apply actual runtime authority, domain scopes, and explicit conflict resolution. | +| Metadata | Both rejected rich duplicated inventories. This synthesis recommends minimal frontmatter; the remaining format choice is D07. | +| Compatibility | Preserve Apple/focus anchors and ordinary Markdown redirects; avoid active alias skills. | +| Efficiency | Treat byte/token/latency savings as measured outcomes, not unsupported forecasts; safety and correct routing are hard gates. | +| Eval fixtures | Reviewers differed on local-only versus checked-in fixtures. Portable sanitized cases need no chosen framework; owner approval under D11 resolves storage. | + +Research used `claude-opus-5.5` and `gpt-6-astra`, each with `xhigh` reasoning +and `long_context`, first independently and then in reciprocal peer review. +The invocation interface exposes no exact token-window setting, so **an exact +1M-token context was requested but could not be verified**. + +The four complete reports are retained as session artifacts: +`claude-independent-report.txt`, `astra-independent-report.txt`, +`claude-cross-review.txt`, and `astra-cross-review.txt`. They are provenance, +not repository instruction entrypoints. Reviewer findings remain distinguishable +from runtime-qualified behavior and owner-approved decisions. + +**Known gaps:** supported-client discovery, exact context capacity, +cross-file fragment checking, external reference consumers, packed +instruction-file contents, quantitative efficacy, and unavailable native +qualification remain open until the relevant phase. No component code, +instruction files, skills, contracts, or permission hooks were changed to +produce this plan. From a1fd5c65d3f4d5069d32e2efd60cb5c1ec2a5f1f Mon Sep 17 00:00:00 2001 From: Jason Morse Date: Wed, 14 Oct 2026 19:52:39 -0700 Subject: [PATCH 02/14] answered questions --- AGENTS.md | 3 +++ agent-improvements-plan.md | 48 +++++++++++++++++++------------------- 2 files changed, 27 insertions(+), 24 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index fe2e40686a..01420c345b 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -86,6 +86,8 @@ This is the **FluentUI React Native** repository, a monorepo containing React Na /win32/ - Win32-specific test app /component-generator/ - Tool to generate new components /packages/ - Core library packages + /agentic/components/ - Modern agentic component implementation package + /agentic/design/ - Common design ancestor package /agentic/native-core/ - Shared modern native components and TurboModules /components/ - UI component implementations (Button, Checkbox, Avatar, etc.) /shim/ - Compatibility entrypoints (Callout, FocusZone, theming, etc.) @@ -94,6 +96,7 @@ This is the **FluentUI React Native** repository, a monorepo containing React Na /theme/ - Theme system /use-tokens/ - Token-based styling hooks /use-slots/ - Slot-based component composition + /framework-base/ - Base package used by all component packages /theming/ - Theme definitions for different platforms /android-theme/ /apple-theme/ diff --git a/agent-improvements-plan.md b/agent-improvements-plan.md index aad18ac8a4..a11a3baa29 100644 --- a/agent-improvements-plan.md +++ b/agent-improvements-plan.md @@ -439,8 +439,8 @@ Use `Answered` or `Deferred`; a deferral should name its reconsideration event. ### D01: Shared home -**Status:** Unanswered -**Answer:** +**Status:** Answered +**Answer:** `references/agents` Choose `references/agents/` (recommended), root `references/`, or `.github/references/`. The first preserves a root reference namespace for @@ -449,8 +449,8 @@ every option. ### D02: Workflow granularity -**Status:** Unanswered -**Answer:** +**Status:** Answered +**Answer:** the recommended hybrid model Choose the hybrid above (recommended), separate v0/v1 implementation skills with one review router, or all eight `implement-*`/`review-*` skills. Separate @@ -458,8 +458,8 @@ generation recipes and native-overlay composition are required in all cases. ### D03: Names and discoverable compatibility -**Status:** Unanswered -**Answer:** +**Status:** Answered +**Answer:** keep existing names where applicable. Use `implement-native-component` Keep existing names initially (recommended), or adopt `implement-*` consistently, including renaming agentic authoring. Choose whether the native name should be @@ -468,8 +468,8 @@ aliases; define any supported transition period explicitly. ### D04: v1 admission policy -**Status:** Unanswered -**Answer:** +**Status:** Answered +**Answer:** maintenance-only Are new v1 components supported, maintenance-only, or allowed only for specific compatibility requirements? The reviewers did not establish a current @@ -477,8 +477,8 @@ maintenance-only v1 prohibition. Do not invent one during reorganization. ### D05: Required clients and adapter behavior -**Status:** Unanswered -**Answer:** +**Status:** Answered +**Answer:** GitHub Copilot CLI is the main entrypoint, use AGENTS.md files where appropriate, keep the repository root CLAUDE.md with the AGENTS.md import Name required clients/versions: Copilot CLI, Claude Code, VS Code, cloud coding agent, code review, Agency, or others. Claude/GPT models inside Copilot are not @@ -487,8 +487,8 @@ matrix verifies imports, nested scopes, skill discovery, and duplication. ### D06: Safety material -**Status:** Unanswered -**Answer:** +**Status:** Answered +**Answer:** Approve deferred extraction with parity review Approve deferred extraction with parity review (recommended), retain full safety guidance always-on permanently, or specify another boundary. Short @@ -497,8 +497,8 @@ no actual policy change is included. ### D07: Metadata convention -**Status:** Unanswered -**Answer:** +**Status:** Answered +**Answer:** Use the recommended approach for frontmatter but make it standardized Choose minimal frontmatter plus selective IDs (recommended), a single minimal catalog instead, or plain Markdown initially. Do not duplicate routing and @@ -507,8 +507,8 @@ and computed hashes, not manually maintained per-file versions. ### D08: Pilot resource budget and thresholds -**Status:** Unanswered -**Answer:** +**Status:** Answered +**Answer:** make efficiency reporting only Approve/adjust the six development and four holdout cases, configurations, run/retry ceiling, and evidence method before runs. Choose whether efficiency @@ -517,8 +517,8 @@ The first pilot does not promise a 30% reduction or an 8 KB root. ### D09: Checker and CI scope -**Status:** Unanswered -**Answer:** +**Status:** Answered +**Answer:** use a small deterministic integrity checker, it should be executable from the repo root and run when 'yarn lage buildci' is executed (which will make it happen in CI) Approve a small deterministic integrity checker and later CI wiring (recommended), or manual checks initially. Decide whether it is invoked by @@ -527,8 +527,8 @@ instruction integrity, not model behavior. ### D10: Shared recipes and compatibility retirement -**Status:** Unanswered -**Answer:** +**Status:** Answered +**Answer:** Err on the side of centralized recipes with an clear taxonomy for organization and extension for things which are likely to be used by multiple skills, reviewers, evals, or should be part of the general knowledge base. Keep things skill-local if they are really skill specific. Approve consumer-driven recipe moves (recommended), centralize all agent recipes, or retain skill-local recipes. Keep contract source adapters local @@ -537,8 +537,8 @@ paths; preserve product-linked anchors irrespective of routine cleanup. ### D11: Evidence storage -**Status:** Unanswered -**Answer:** +**Status:** Answered +**Answer:** use the recommended approach Choose checked-in sanitized case fixtures with local execution evidence (recommended), entirely local cases, or a separately approved external store. @@ -547,8 +547,8 @@ selection can remain deferred. ### D12: Improvement loop -**Status:** Unanswered -**Answer:** +**Status:** Answered +**Answer:** defer the assess-agent-efficiency skill Defer `assess-agent-efficacy` until the pilot establishes useful measurements (recommended), add a proposal-only skill earlier, or omit it. Define reviewer From 3d1076b82afca7efcfa5b199fbcc1538ff3743b9 Mon Sep 17 00:00:00 2001 From: Jason Morse Date: Wed, 14 Oct 2026 20:23:00 -0700 Subject: [PATCH 03/14] Reorganize agent instructions into shared references Move reusable agent guidance into references/agents with a documented taxonomy, standardized frontmatter, rule IDs, and compatibility redirects. Add maintain-v0-v1-component, review-component, and implement-native-component skills; slim root AGENTS.md to safeguards and routing; extract permissions with sentence-level parity; and add sanitized evaluation case fixtures. Add `yarn check-agent-instructions`, a deterministic integrity checker with node tests, and run it in `yarn lage buildci` and `repo-checks`. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .changeset/agent-instruction-references.md | 4 + .../agentic-component-authoring/SKILL.md | 86 +-- .../references/focus-macos.md | 186 +----- .../references/focus-windows.md | 169 +----- .../references/focus.md | 192 +----- .../references/rendering.md | 84 +-- .../references/spec-source-adaptation.md | 18 +- .../references/state-and-accessibility.md | 196 +------ .../references/styles-and-tokens.md | 217 +------ .../references/tests-and-stories.md | 280 +-------- .../references/types-and-slots.md | 113 +--- .../windows-fabric-native-components.md | 272 +-------- .../SKILL.md | 18 +- .../references/sources/composite-evidence.md | 42 +- .../references/sources/flex-x3.md | 52 +- .../references/sources/fluent-v9.md | 36 +- .../references/sources/html-css.md | 41 +- .../references/sources/local-foundation.md | 28 +- .../references/sources/visual-reference.md | 41 +- .../agentic-component-optimization/SKILL.md | 17 +- .../agentic-storybook-development/SKILL.md | 2 +- .github/skills/fix-security-issues/SKILL.md | 5 +- .../implement-native-component/SKILL.md | 63 ++ .../skills/maintain-v0-v1-component/SKILL.md | 37 ++ .github/skills/review-component/SKILL.md | 46 ++ AGENTS.md | 464 +++------------ agent-improvements-plan.md | 108 +++- apps/storybook/AGENTS.md | 20 + lage.config.mjs | 7 +- package.json | 1 + packages/agentic/components/AGENTS.md | 62 +- packages/agentic/components/src/AGENTS.md | 2 +- .../components/src/components/AGENTS.md | 16 +- .../components/src/primitives/AGENTS.md | 17 +- packages/agentic/native-core/README.md | 2 +- .../deprecated/foundation-compose/AGENTS.md | 198 +------ packages/framework-base/AGENTS.md | 2 +- .../src/component-patterns/README.md | 2 +- packages/framework/framework/AGENTS.md | 248 +------- references/agents/README.md | 106 ++++ .../contract-sources/composite-evidence.md | 43 ++ .../agentic/contract-sources/flex-x3.md | 53 ++ .../agentic/contract-sources/fluent-v9.md | 37 ++ .../agentic/contract-sources/html-css.md | 42 ++ .../contract-sources/local-foundation.md | 29 + .../contract-sources/visual-reference.md | 42 ++ .../agents/components/agentic/rendering.md | 85 +++ .../agentic/state-and-accessibility.md | 197 +++++++ .../components/agentic/styles-and-tokens.md | 219 +++++++ .../components/agentic/tests-and-stories.md | 282 +++++++++ .../components/agentic/types-and-slots.md | 114 ++++ .../agents/components/classification.md | 80 +++ .../agents/components/legacy-test-apps.md | 38 ++ references/agents/components/v0.md | 190 ++++++ references/agents/components/v1.md | 216 +++++++ .../agents/design/tokens-and-theming.md | 43 ++ references/agents/evals/README.md | 53 ++ .../cases/agentic-button-appearance-plan.json | 37 ++ .../cases/icon-primitive-ref-review.json | 37 ++ .../cases/macos-paper-manager-command.json | 34 ++ .../cases/native-checkbox-macos-review.json | 35 ++ .../native-core-legacy-callout-prop.json | 37 ++ .../cases/storybook-macos-bundle-failure.json | 25 + .../transitive-advisory-remediation.json | 31 + .../evals/cases/v0-button-settings-fix.json | 37 ++ .../agents/evals/cases/v1-button-review.json | 37 ++ .../cases/v1-text-compressible-review.json | 31 + .../cases/windows-fabric-callout-prop.json | 37 ++ references/agents/focus/common.md | 194 ++++++ references/agents/focus/macos.md | 188 ++++++ references/agents/focus/windows-win32.md | 171 ++++++ .../agents/native/components-and-modules.md | 52 ++ references/agents/native/windows-fabric.md | 273 +++++++++ references/agents/repository/architecture.md | 65 +++ references/agents/repository/development.md | 93 +++ references/agents/repository/permissions.md | 80 +++ references/agents/repository/release.md | 19 + references/agents/repository/typescript.md | 43 ++ scripts/package.json | 3 +- scripts/src/cli.ts | 2 + scripts/src/tasks/agentInstructions.test.ts | 169 ++++++ scripts/src/tasks/agentInstructions.ts | 551 ++++++++++++++++++ .../src/tasks/checkAgentInstructionsTask.ts | 36 ++ 83 files changed, 4836 insertions(+), 2744 deletions(-) create mode 100644 .changeset/agent-instruction-references.md create mode 100644 .github/skills/implement-native-component/SKILL.md create mode 100644 .github/skills/maintain-v0-v1-component/SKILL.md create mode 100644 .github/skills/review-component/SKILL.md create mode 100644 references/agents/README.md create mode 100644 references/agents/components/agentic/contract-sources/composite-evidence.md create mode 100644 references/agents/components/agentic/contract-sources/flex-x3.md create mode 100644 references/agents/components/agentic/contract-sources/fluent-v9.md create mode 100644 references/agents/components/agentic/contract-sources/html-css.md create mode 100644 references/agents/components/agentic/contract-sources/local-foundation.md create mode 100644 references/agents/components/agentic/contract-sources/visual-reference.md create mode 100644 references/agents/components/agentic/rendering.md create mode 100644 references/agents/components/agentic/state-and-accessibility.md create mode 100644 references/agents/components/agentic/styles-and-tokens.md create mode 100644 references/agents/components/agentic/tests-and-stories.md create mode 100644 references/agents/components/agentic/types-and-slots.md create mode 100644 references/agents/components/classification.md create mode 100644 references/agents/components/legacy-test-apps.md create mode 100644 references/agents/components/v0.md create mode 100644 references/agents/components/v1.md create mode 100644 references/agents/design/tokens-and-theming.md create mode 100644 references/agents/evals/README.md create mode 100644 references/agents/evals/cases/agentic-button-appearance-plan.json create mode 100644 references/agents/evals/cases/icon-primitive-ref-review.json create mode 100644 references/agents/evals/cases/macos-paper-manager-command.json create mode 100644 references/agents/evals/cases/native-checkbox-macos-review.json create mode 100644 references/agents/evals/cases/native-core-legacy-callout-prop.json create mode 100644 references/agents/evals/cases/storybook-macos-bundle-failure.json create mode 100644 references/agents/evals/cases/transitive-advisory-remediation.json create mode 100644 references/agents/evals/cases/v0-button-settings-fix.json create mode 100644 references/agents/evals/cases/v1-button-review.json create mode 100644 references/agents/evals/cases/v1-text-compressible-review.json create mode 100644 references/agents/evals/cases/windows-fabric-callout-prop.json create mode 100644 references/agents/focus/common.md create mode 100644 references/agents/focus/macos.md create mode 100644 references/agents/focus/windows-win32.md create mode 100644 references/agents/native/components-and-modules.md create mode 100644 references/agents/native/windows-fabric.md create mode 100644 references/agents/repository/architecture.md create mode 100644 references/agents/repository/development.md create mode 100644 references/agents/repository/permissions.md create mode 100644 references/agents/repository/release.md create mode 100644 references/agents/repository/typescript.md create mode 100644 scripts/src/tasks/agentInstructions.test.ts create mode 100644 scripts/src/tasks/agentInstructions.ts create mode 100644 scripts/src/tasks/checkAgentInstructionsTask.ts diff --git a/.changeset/agent-instruction-references.md b/.changeset/agent-instruction-references.md new file mode 100644 index 0000000000..99646bb8bf --- /dev/null +++ b/.changeset/agent-instruction-references.md @@ -0,0 +1,4 @@ +--- +--- + +Reorganize agent instructions into shared `references/agents` documents; documentation only. diff --git a/.github/skills/agentic-component-authoring/SKILL.md b/.github/skills/agentic-component-authoring/SKILL.md index d83ef87e6c..13e937e6b6 100644 --- a/.github/skills/agentic-component-authoring/SKILL.md +++ b/.github/skills/agentic-component-authoring/SKILL.md @@ -1,69 +1,71 @@ --- name: agentic-component-authoring -description: Generate or update Fluent UI React Native components in packages/agentic/components. Use for component APIs, slots, state hooks, token styling, render functions, tests, stories, and spec-driven component work. +description: Implement or change components and primitives in packages/agentic/components, including every new component. Covers public APIs, slots, state, token styling, rendering, tests, and stories. Not for contract-only work, read-only reviews, v0/v1 maintenance, or the Storybook app toolchain. license: MIT --- # Agentic component authoring Build components in `packages/agentic/components` as React Native adaptations of the Fluent UI v9 component pattern. -This skill is the workflow router. Load only the references needed for the current change instead of placing every -authoring rule in one always-loaded instruction file. +New components always use this workflow; v0 and v1 are maintenance-only. This skill routes the work. Load only the +references the current change needs. ## Choose the component kind -- Higher-order components live in `src/components`. Read the - [higher-order component instructions](../../../packages/agentic/components/src/components/AGENTS.md). -- Primitive components live in `src/primitives`. Read the - [primitive instructions](../../../packages/agentic/components/src/primitives/AGENTS.md). -- If the change spans multiple components, repeated helpers, or shared dependency boundaries, read the - [package-wide optimization guidance](../agentic-component-optimization/SKILL.md) and - [`packages/agentic/components/src/AGENTS.md`](../../../packages/agentic/components/src/AGENTS.md). Do not run a - whole-package audit for a tiny single-component edit. -- Work on the Storybook application, native projects, bundling, or CocoaPods belongs to the - [agentic Storybook development skill](../agentic-storybook-development/SKILL.md), not this component workflow. +- **Higher-order components** live in `src/components`. Read the + [package instructions](../../../packages/agentic/components/AGENTS.md) and the + [higher-order component instructions](../../../packages/agentic/components/src/components/AGENTS.md). The + component's `SPEC.md` and `spec/` companions are its contract. +- **Primitives** live in `src/primitives`. Read the package instructions and the + [primitive instructions](../../../packages/agentic/components/src/primitives/AGENTS.md). A primitive's colocated + `CONTRACT.md` is its contract; primitives have no upstream-backed `SPEC.md`. +- Changes spanning components, repeated helpers, or dependency boundaries also use the + [optimization skill](../agentic-component-optimization/SKILL.md). Do not audit the whole package for a small edit. +- Read-only reviews use [review-component](../review-component/SKILL.md). Storybook application, native project, + bundling, or CocoaPods work uses the [Storybook skill](../agentic-storybook-development/SKILL.md). +- Native views, codegen, or registration also use the + [native component skill](../implement-native-component/SKILL.md) as an overlay. ## Load focused references -| Work | Reference | -| ---------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- | -| Source interpretation, provenance, contract drafting, review, or divergences | [Contract authoring](../agentic-component-contract-authoring/SKILL.md) | -| Public props, slots, state types, native prop exposure, or exports | [Types and slots](references/types-and-slots.md) | -| Defaults, derived state, interaction hooks, accessibility, or slots | [State and accessibility](references/state-and-accessibility.md) | -| Focus, focus visuals, keyboard activation, or native focus refs | [Common focus authoring](references/focus.md), then [Windows/Win32](references/focus-windows.md) or [macOS](references/focus-macos.md) | -| Tokens, theme caching, state precedence, or slot style application | [Styles and tokens](references/styles-and-tokens.md) | -| Pure slot rendering, component assembly, or display names | [Rendering and assembly](references/rendering.md) | -| Runtime tests, type tests, snapshots, Storybook stories, or validation | [Tests and stories](references/tests-and-stories.md) | -| Native React Native Windows Fabric components, codegen, registration, or UIA | [Windows Fabric native components](references/windows-fabric-native-components.md) | -| Cross-component duplication, shared extraction, or dependency hygiene | [Package optimization](../agentic-component-optimization/SKILL.md) | +| Work | Reference | +| ---------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Source interpretation, provenance, contract drafting, or divergences | [Contract authoring](../agentic-component-contract-authoring/SKILL.md) | +| Public props, slots, state types, native prop exposure, or exports | [Types and slots](../../../references/agents/components/agentic/types-and-slots.md) | +| Defaults, derived state, interaction hooks, accessibility, or slots | [State and accessibility](../../../references/agents/components/agentic/state-and-accessibility.md) | +| Focus, focus visuals, keyboard activation, or native focus refs | [Common focus authoring](../../../references/agents/focus/common.md), then [Windows/Win32](../../../references/agents/focus/windows-win32.md) or [macOS](../../../references/agents/focus/macos.md) | +| Tokens, theme caching, state precedence, or slot style application | [Styles and tokens](../../../references/agents/components/agentic/styles-and-tokens.md) and [tokens and theming](../../../references/agents/design/tokens-and-theming.md) | +| Pure slot rendering, component assembly, or display names | [Rendering and assembly](../../../references/agents/components/agentic/rendering.md) | +| Runtime tests, type tests, snapshots, Storybook stories, or validation | [Tests and stories](../../../references/agents/components/agentic/tests-and-stories.md) | +| Native React Native Windows Fabric components, codegen, registration, or UIA | [Windows Fabric native components](../../../references/agents/native/windows-fabric.md) | -A new higher-order component normally needs every reference. A focused fix should load only the affected reference and -its immediate neighbors. Keep the component's colocated `SPEC.md` and companion files authoritative for its contract. +A new higher-order component normally needs the types, state, styles, rendering, and tests references, plus focus +guidance when it is focusable. Load the Windows Fabric reference only for native Windows views. A focused fix loads only +the affected reference and its immediate neighbors. ## Workflow -1. Read the repository and package instructions, the component `SPEC.md`, - `spec/source.json`, and every React Native companion referenced by the spec. -2. Use an existing reviewed contract directly when its source evidence and - requested behavior are unchanged. If the contract is absent, stale, still a - draft, or accompanied by new source evidence, run the - [contract authoring workflow](../agentic-component-contract-authoring/SKILL.md) - and complete its pre-code review gate first. -3. Inspect the closest canonical implementation. Use - [`components/button`](../../../packages/agentic/components/src/components/button) for a styled higher-order component - and [`primitives/icon`](../../../packages/agentic/components/src/primitives/icon) for a direct primitive. +1. Read the package instructions and the component's contract: `SPEC.md`, `spec/source.json`, and every React Native + companion it references for a higher-order component, or `CONTRACT.md` for a primitive. +2. Higher-order components: use an existing reviewed contract directly when its source evidence and requested behavior + are unchanged. If the contract is absent, stale, still a draft, or accompanied by new source evidence, run the + [contract authoring workflow](../agentic-component-contract-authoring/SKILL.md) and complete its pre-code review + gate first (R-AGENTIC-001 in the [package instructions](../../../packages/agentic/components/AGENTS.md)). +3. Inspect the closest canonical implementation: + [`components/button`](../../../packages/agentic/components/src/components/button) for a styled higher-order + component, or [`primitives/icon`](../../../packages/agentic/components/src/primitives/icon) for a direct primitive. 4. Establish the public contract before implementation: variants, slots, native props, accessibility, interaction states, and platform behavior. 5. Implement in dependency order: types and slots, state and accessibility, styles and slot props, pure rendering, component assembly, and explicit exports. -6. Preserve the specification. Record a genuine token or platform gap rather than substituting an unrelated value or +6. Preserve the contract. Record a genuine token or platform gap rather than substituting an unrelated value or web-only behavior. 7. Add focused tests and stories that exercise the public API and the resolved native output. -8. Reconcile and ratify the local contract against the realized public types, - tests, stories, and platform evidence. Upstream changes require explicit - re-review and never overwrite the local contract. -9. Run the smallest declared validation command while iterating. Finish with package format, lint, build, and tests; run - the Storybook bundle for story changes and the root build when public types, manifests, or project references change. +8. Reconcile and ratify the local contract against the realized public types, tests, stories, and platform evidence. + Upstream changes require explicit re-review and never overwrite the local contract. +9. Run the smallest declared validation command while iterating. Finish with package format, lint, build, and tests; + run the Storybook bundle for story changes and the root build when public types, manifests, or project references + change. Report results truthfully (R-VALID-002). Do not divide one component implementation into separate sub-agent or sub-skill phases. Its types, state, styling, and rendering form one contract and should remain in one implementation context. diff --git a/.github/skills/agentic-component-authoring/references/focus-macos.md b/.github/skills/agentic-component-authoring/references/focus-macos.md index 990010bb1a..28d31af83b 100644 --- a/.github/skills/agentic-component-authoring/references/focus-macos.md +++ b/.github/skills/agentic-component-authoring/references/focus-macos.md @@ -1,180 +1,12 @@ -# macOS focus authoring +--- +id: compat.agentic-component-authoring.focus-macos +kind: compatibility-reference +owner_paths: + - packages/agentic/components +--- -Read [common focus authoring](focus.md) first. Match shared accessibility goals, -but do not transplant Windows click-focus, modifier, or key-up policies into -AppKit. +# Moved: macOS focus authoring -**Reviewed implementation:** local `react-native-macos` 0.81.9, including Fabric -View, repository FocusZone, and Callout implementations, September 14, 2026. -Verify the renderer branch and resolved component before use. A `.macos.tsx` -file can be a stub: V1 Checkbox's macOS entrypoint warns and renders null, so it -is not a valid macOS focus reference. +This reference now lives at [references/agents/focus/macos.md](../../../../references/agents/focus/macos.md). -## First responder, key window, and actual target - -AppKit routes keyboard input through the key window and its first responder. -Keep window activation separate from control focus. A window can retain a first -responder while inactive; React `focused` alone does not describe whether its -focus visual should be presented as active. - -- Prefer a supported native host ref/command. In native adapters request focus - with the owning window's first-responder mechanism on the main thread. -- Do not treat `makeFirstResponder` returning true as conclusive proof that the - requested view owns focus. The current responder can refuse to resign, and - AppKit documents a case where the window becomes responder when the requested - object refuses. Confirm the actual target through focus events/observation. -- Resolve the actual editable control. An NSTextField may use the window's - shared field editor; the first responder can therefore differ from the visible - control/ref. Native focus-within mapping must recognize this relationship. -- Keep native view identity scoped to the mounted window/surface. Clear it on - detach/recycle and use common React 19 ref composition, not legacy componentRef - exposure or global numeric-tag lookup. - -Local references: -`packages\agentic\native-core\macos\components\FocusZone\shared\RCTFocusZone.m:24-35,122-129`; -`packages\agentic\native-core\macos\components\FocusZone\fabric\RCTFocusZoneComponentView.mm:118-179`. -Installed RNmacOS `React\Fabric\Mounting\ComponentViews\View\RCTViewComponentView.mm:1719-1804` -implements focus/blur commands and responder event emission. - -## Pointer focus and click-through - -Do not force Windows-style focus-before-press on ordinary macOS controls. -V1 `useOnPressWithFocus` recognizes macOS as focus-capable but only requests -click focus on Windows/Win32. The installed Fabric View also overrides -`needsPanelToBecomeKey` with a comment preserving mouse-versus-keyboard focus. - -`acceptsFirstMouse` controls click-through in an inactive window; it is not an -alias for `focusable`, first-responder acceptance, or keyboard ring visibility. -Test first click to activate a window separately from an actual control action. -Menus can intentionally focus on hover, so document that owner-specific -exception rather than forcing all click/hover paths into one rule. - -Evidence: `packages\utils\interactive-hooks\src\useOnPressWithFocus.ts:14-29`; -installed RNmacOS `RCTViewComponentView.mm:1769-1779`; -`packages\components\Menu\src\MenuItem\useMenuItem.ts:68-120`. - -## Keyboard navigation, shortcuts, and editing - -- V1 key helpers prefer key-down on macOS, versus key-up on Windows/Win32. - The macOS branches use `validKeysDown`/`validKeysUp`; current RNmacOS native - handling also has `keyDownEvents`/`keyUpEvents`. Resolve and validate the - compatibility path instead of configuring both indiscriminately. -- Matching native handled-key entries suppress AppKit's `super` handling. - Register only keys owned by the control/scope so key equivalents, native - navigation, and text editing are not swallowed. -- Preserve Command shortcuts, Option text movement, Control/VoiceOver chords, - IME composition, and the distinction between Return and Space/default-button - activation. Do not copy Win32's modifier allowance or TabList Ctrl+Tab behavior. -- Confirm exactly-once keyboard/accessibility activation; an AX action is not - equivalent evidence to a physical key sequence. -- RNmacOS Pressability activates Return/Space on keydown; keyup supplies - `onPressOut`. Keep native press cleanup separate from Windows keyup-activation - pairing. Intervening keys, caller cancellation, blur, disable, and target - replacement must not strand pressed feedback or duplicate release callbacks. - Retain a pooled event only with `persist()` and release that reference when - the keyboard press ends. -- Use AppKit's key-view loop where applicable. Respect keyboard-navigation/Full - Keyboard Access settings and test enabled/disabled settings explicitly. - `NSApplication.isFullKeyboardAccessEnabled` is not KVO-observable according to - Apple; do not invent a KVO subscription or assume one cached startup value - remains current. -- The shared native focus stories enter through an editable control and Tab, - rather than forcing ordinary pointer clicks to focus. The all-controls lane - requires Keyboard navigation enabled; record that setting, obtain permission - before changing a developer's setting, and restore changes authorized as - temporary. Keep it enabled on development/CI Macs unless the owner chooses - otherwise; locking the desktop gates physical input, not this preference. - A Windows-only pointer - assertion may be a no-op on macOS, but the shared focus/activation assertions - must still execute. -- Do not equate VoiceOver navigation focus with the application's keyboard first - responder. Validate role/name/value and native action behavior in both modes. - -Evidence: `packages\utils\interactive-hooks\src\useKeyProps.ts:8-95,114-126`; -installed RNmacOS `RCTViewComponentView.mm:1807-1861`. - -## FocusZone and Fabric wrapper lifetime - -The local macOS FocusZone translates key events, recalculates the key-view loop, -and falls back to mounted descendant order when AppKit collapses a Fabric-backed -zone to one key-loop entry. The Fabric wrapper deliberately does not participate -as another first-responder stop; it forwards focus to its content view. - -Preserve that single-owner behavior. Do not add a second JS roving loop to the -same keys. Recompute eligibility when children mount/unmount and when props -change, and clear the default responder on recycle. Retain direction, wrap/stop, -RTL, nested zones, scrolling, and editable-child semantics. - -Evidence: `packages\agentic\native-core\macos\components\FocusZone\shared\RCTFocusZone.m:584-675`; -`packages\agentic\native-core\macos\components\FocusZone\fabric\RCTFocusZoneComponentView.mm:118-195`. - -## Popup windows and lifecycle - -Callout's AppKit window can become key but not main. Its `setInitialFocus` and -`focusWindow` make the popup window key; that alone does not establish a particular -child as first responder. Its separate Fabric touch attachment also demonstrates -why React ancestry is not sufficient evidence for popup input capture. - -Define and test initial child focus after content attachment, Escape, nested -popup dismissal, outside click, window/application deactivation, and guarded -return to the parent control. Do not reactivate the application or steal the -outside-click target's focus as an unconditional cleanup action. - -The legacy ContextualMenu uses a macOS timer after a layout effect. Treat it as -evidence of a native readiness problem, not a reusable delay constant. Prefer a -mount/window-ready handshake with cancellation and focus confirmation. Keep a -bounded, documented workaround only if the supported renderer has no such path. - -Reuse lifecycle-safe local event-monitor/observer ownership: install once for -the owning popup/window, remove exactly once, and capture weak native owners. -Do not install OS-global keyboard hooks or require desktop-driver permissions -for ordinary production focus behavior. - -Evidence: -`packages\agentic\native-core\macos\components\Callout\shared\CalloutWindow.swift:9-38`; -`packages\agentic\native-core\macos\components\Callout\shared\CalloutView.swift:33-45,142-216`; -`packages\agentic\native-core\macos\components\Callout\fabric\RCTCalloutComponentView.mm:96-132`; -`packages\agentic\native-core\macos\components\Callout\shared\GuardedEventMonitor.swift:1-29`; -`packages\components\ContextualMenu\src\ContextualMenu.tsx:39-55`. - -## Native versus custom ring drawing - -Keep native macOS rings enabled by default unless the reviewed control requires -a custom path. The installed Fabric View draws a rounded AppKit focus mask only -when `enableFocusRing` is true. A focusable view or an enum property by itself is -not proof that the ring mask/drawing is implemented correctly. - -When drawing a custom ring, disable the competing native path and preserve the -decorative/hit-testing contract. Revalidate mask bounds after layout/radius -changes, key-window transitions, clipping, appearance, Increase Contrast, and -backing-scale changes. Use AppKit points and conversions; do not copy Windows -Composition physical-pixel calculations into NSView layout. - -Evidence: installed RNmacOS `RCTViewComponentView.mm:1729-1753` and Apple's -focus-mask documentation below. Ring colors remain a native/theme concern; -do not resolve opaque system colors through a guessed web-color fallback. - -## Native service candidates and qualification - -An opt-in native projection can help with window activity, keyboard-navigation -preferences, renderer-scoped target observation, or popup readiness if public -React Native APIs do not supply them. A TurboModule is suitable for a non-view -capability/snapshot service; view focus belongs to refs or typed native commands. -Keep generated Apple glue in Objective-C++ as required by the pinned codegen; -use AppKit/Swift/C++ behind that boundary without introducing UIKit assumptions. - -Run physical keyboard/pointer scenarios, VoiceOver, active/inactive windows, -first-click behavior, keyboard-navigation settings, editable field editors, and -native/custom rings. Include Paper only where it remains supported and Fabric -as an independently qualified endpoint. This Windows-based documentation -investigation did not execute a macOS native run. - -## Public references - -- [AppKit makeFirstResponder]() -- [AppKit first responder and key-view loop](https://developer.apple.com/documentation/appkit/nswindow) -- [Click-through and acceptsFirstMouse]() -- [Full Keyboard Access query](https://developer.apple.com/documentation/appkit/nsapplication/isfullkeyboardaccessenabled) -- [AppKit field editor](https://developer.apple.com/library/archive/documentation/TextFonts/Conceptual/CocoaTextArchitecture/TextEditing/TextEditing.html) -- [Focus ring mask bounds](https://developer.apple.com/documentation/appkit/nsview/focusringmaskbounds) -- [Focus ring mask drawing]() +This compatibility path remains so existing links continue to resolve. Do not add new links to it. diff --git a/.github/skills/agentic-component-authoring/references/focus-windows.md b/.github/skills/agentic-component-authoring/references/focus-windows.md index bd677f65cc..1d03315b5f 100644 --- a/.github/skills/agentic-component-authoring/references/focus-windows.md +++ b/.github/skills/agentic-component-authoring/references/focus-windows.md @@ -1,163 +1,12 @@ -# Windows and Win32 focus authoring +--- +id: compat.agentic-component-authoring.focus-windows +kind: compatibility-reference +owner_paths: + - packages/agentic/components +--- -Read [common focus authoring](focus.md) first. This file applies the shipped V1 -Win32 behavioral model to both Windows endpoints, while keeping their native -implementations separate. +# Moved: Windows and Win32 focus authoring -## Identify the endpoint +This reference now lives at [references/agents/focus/windows-win32.md](../../../../references/agents/focus/windows-win32.md). -| Endpoint | Reviewed local implementation | Native owner | -| --------- | ------------------------------------------------------------------------------ | ------------------------------------------------------------------- | -| `win32` | `@office-iss/react-native-win32` 0.81.8, Paper; prebuilt REX host in Storybook | Office Win32 host/view implementation | -| `windows` | `react-native-windows` 0.81.35, Fabric | Windows App SDK Composition component views and React Native island | - -These are observed dependency versions on September 14, 2026, not minimum-version -guarantees for every feature. Recheck the lockfile, platform resolution, and host -before implementation. Office Win32 Paper is **not** RNW's old UWP XAML renderer, -even though both are described as old-architecture/Paper in some contexts. - -## Carry V1 behavior across, not its plumbing - -For both endpoints preserve, unless the component's reviewed contract differs: - -- focus-before-activation for Windows pointer interaction where the V1 control - requests it; -- one action per Enter/Space/accessibility invocation, including menu-close and - keydown/blur/keyup sequences; -- selection/focusability commit before imperative focus and Narrator announcement; -- explicit per-control disabled discoverability; -- menu hover-focus suppression, navigation ownership, and guarded focus return. - -Use existing V1 Button/Checkbox/RadioGroup/Switch E2E sequences as scenarios for -both Windows test lanes. The V1 implementation need not run inside Fabric: map -its observed behavior to a shared scenario contract, run V1 on Win32 as the -reference, then verify the new implementation independently on both renderers. -Do not require matching native element classes or identical ring pixels. - -Sources: -`packages\utils\interactive-hooks\src\useOnPressWithFocus.ts:14-29`; -`packages\components\Button\src\useButton.ts:15-17,40-66,99-113`; -`packages\components\TabList\src\Tab\useTab.win32.ts:64-110`; -`packages\components\Menu\src\MenuItem\useMenuItem.ts:68-120`. - -## Focus requests and native identity - -On Win32 use the host-supported ref/command path. V1 `componentRef` and -`useViewCommandFocus` are compatibility evidence, not the desired new public API. -Apply the common React 19 ref rules and resolve the actual mounted target. - -On Fabric prefer the supported host focus method or a generated native command. -Native adapters operate on the correct mounted `ComponentView` in its owning -surface/island. They must not reuse Paper UIManager/view-manager identifiers or -assume a React ref can be serialized into a TurboModule argument. - -The existing Fabric FocusZone uses `TryFocus(FocusState::Keyboard)` and checks -the root's focused component after the request -(`packages\agentic\native-core\windows\NativeCore\components\FocusZone\FocusZoneComponentView.cpp:175-224`). -Reuse this request-versus-observation pattern. Do not report success merely -because JavaScript called an optional `.focus()` method. - -RNW's reviewed root stores a native focus state and resolves programmatic focus -using its own policy. A request for the already-focused component returns early. -This matters for modality-only changes: native focus reasons and the design -root's modality are related, but are not automatically the same source. -Do not synthesize a blur/refocus cycle simply to redraw a ring. - -Installed evidence: -`Microsoft.ReactNative\Fabric\Composition\RootComponentView.cpp:79-118`. -This native implementation detail is not a promised public JavaScript API. - -## Keyboard delivery and exactly-once activation - -- Inspect the actual installed Pressable and native view handlers. Do not add - key-up activation on top of an existing native press action. -- Use `useFocusablePressable` rather than per-control key activation. Native - qualification found Office Win32 events with `code="Unidentified"` and a - valid `key`. The helper supplies one paired key-up fallback only when Win32's - code-based Pressability cannot activate that key; recognized codes stay native. - Pairing is cancelled on blur, disable, and target-generation changes. -- Preserve required native accessibility actions. Checkbox and Switch supply `Toggle`; - Tab supplies `Select`. The Win32 UIA toggle/selection patterns were unavailable - without those action declarations even though JavaScript state was correct. -- V1 `useKeyProps` prefers key-up on Windows/Win32. It supplies Win32 - handled-key descriptors, but its Windows branch deliberately omits descriptors - because of the historical `key` versus `code` mismatch. Translate and test the - pinned Fabric representation instead of copying either branch blindly. -- Treat native `Handled`/capture registration and JavaScript - `preventDefault`/`stopPropagation` as separate mechanisms. Establish which - cancellation reaches native navigation at each phase. -- Pair keyboard invocation within the appropriate scene/gesture lifetime. - Do not reproduce Button's module-global arming flag as a process-global - cross-scene dependency. -- Forward caller handlers and preserve text editing, IME, modifiers, key repeat, - and accessibility actions. Global root capture observes input; it must not - consume every key. - -Sources: `packages\utils\interactive-hooks\src\useKeyProps.ts:8-95,114-126`; -`packages\agentic\native-core\windows\NativeCore\components\FocusZone\FocusZoneComponentView.cpp:71-133`. - -## Native and custom focus visuals - -Current new-component defaults remain native on Fabric and custom on Win32. -V1 uses native Win32 rings and appearance-specific focused borders, so this is -a choice to qualify, not evidence that Win32 native rings are unavailable. - -- Request native rings only on the actual focus owner. Menu hover focus and - disabled-focusable cases may require a different visibility policy from a - generic keyboard-focus rule. -- On the custom path retain the optional slot and mounted configured ring Views. - Keep the root/slot ref stable through focus and theme changes. -- Qualify Button primary versus high contrast separately; do not apply its - two-tone Win32 inner-border recipe universally. -- Use platform/Fluent colors for native drawing. Preserve opaque system-color - values; do not turn them into guessed CSS literals. -- For Fabric native visuals, use the renderer's focus/border hosting and - lifecycle. React layout units and Composition physical pixels differ; apply - the validated point-scale conversion. Test clipping/hosting and recycling. - -RNW 0.81.35 has explicit focus hosting and native theme handling in installed -`Microsoft.ReactNative\Fabric\Composition\CompositionViewComponentView.cpp` -and `Theme.cpp`. See [Windows Fabric native components](windows-fabric-native-components.md) -for codegen, UI-thread, registration, native visual, and UIA requirements. - -## Collections, popups, and platform services - -Use one native/JS navigation owner. Wait for committed target eligibility and -then confirm the resulting native focus; handle disabled/removed destinations. -Preserve Narrator's selection-before-focus order and test rapid navigation. - -Distinguish a Fabric island/popup and an Office Win32 popup. Returning to a -window is not the same as restoring its intended control. Carry a live target -and dismissal reason, and never steal focus after an outside click or host -deactivation. - -Consider `native-lib` only for a demonstrated missing capability such as -surface-scoped native focus observation, window activation, or safely shared -native target resolution. Keep view commands in their owning native components. -Fabric TurboModule support does not establish registration support in a -prebuilt Office Win32 host; that endpoint needs its own host integration or an -explicit capability-unavailable result. - -## Qualification - -Run separate Win32 and Fabric native lanes for the same behavior scenarios: -pointer focus, Enter/Space, stale key-up, repeat/modifiers, accessibility invoke, -selection-before-focus, disabled/removed targets, menu hover, popup return, and -same-target modality changes. Include Narrator, high contrast, display scaling, -active/inactive windows, and nested surfaces. - -Gate on actual focused element and action/announcement count, not just selected -React state or a mocked ref call. A skipped input test is not qualification. -Use owned app/Metro lifecycle commands and preserve unrelated sessions. - -The catalog now uses named WDIO callbacks. The complete Windows and Win32 smoke -lanes each cover 18 cases with native assertions; component-focused cases use -the `desktop-focus` tag. Check aggregate result counts for zero skips before -claiming qualification. A disconnected input desktop can leave semantic cases -passing while all physical-input cases skip. - -For delegated modifier chords, assert parent delivery and unchanged collection -selection, not immovable native focus: the host is allowed to handle the chord. -For native editors, distinguish noneditable/nonfocusable from UIA `IsEnabled`; -Win32's observed enabled-state announcement gap is documented in Input's -interaction companion and does not replace native focus assertions. +This compatibility path remains so existing links continue to resolve. Do not add new links to it. diff --git a/.github/skills/agentic-component-authoring/references/focus.md b/.github/skills/agentic-component-authoring/references/focus.md index da21bd550d..8ece316667 100644 --- a/.github/skills/agentic-component-authoring/references/focus.md +++ b/.github/skills/agentic-component-authoring/references/focus.md @@ -1,186 +1,12 @@ -# Common desktop focus authoring +--- +id: compat.agentic-component-authoring.focus +kind: compatibility-reference +owner_paths: + - packages/agentic/components +--- -Read this file for every focusable component, collection, popup, focus hook, or -native focus adapter. Then load only the applicable platform detail: +# Moved: Common desktop focus authoring -- [Windows and Win32](focus-windows.md) -- [macOS](focus-macos.md) +This reference now lives at [references/agents/focus/common.md](../../../../references/agents/focus/common.md). -These files define authoring and review obligations. Execution status in -the [desktop focus plan](../../../../packages/agentic/components/WIN32-FOCUS-PLAN.md) -distinguishes implemented APIs from native-only follow-ups. Keep each reviewed contract and -source-specific divergences authoritative. - -## Behavioral authority and platform evidence - -Use shipped V1 Win32 behavior as the compatibility model for **both** Office -Win32 Paper and React Native Windows Fabric. Preserve user-observable outcomes: -which element focuses, action count/timing, selection and announcement order, -disabled discoverability, key ownership, and dismissal return. Implement those -outcomes through each renderer's supported APIs, not copied Paper internals. - -On macOS preserve the same accessibility goals but follow AppKit's first -responder, key-window, keyboard-navigation, click, and shortcut conventions. -Document intentional differences rather than imposing Windows input behavior. - -Before changing behavior, record the public export, platform-resolved file, -renderer/version, host, and relevant V1 sequence. Distinguish declarations, -implementation, authored tests, and native results actually executed. A passing -snapshot or `Platform.OS` mock does not prove native focus delivery. - -## Keep these concepts separate - -| Concept | Owner and invariant | -| ---------------------- | ------------------------------------------------------------------------------------- | -| Input modality | Scene controller; stable `RootSettings`, shared by nested theme boundaries. | -| Native self-focus | Actual mounted focus target, not an ancestor receiving a bubbled event. | -| Focus within | Explicit scope/ancestor state, never substituted for self-focus. | -| Focus request | Cancellable intent to move focus; not confirmation that it moved. | -| Focus-visible policy | Component/scope policy using modality and explicit focus intent. | -| Native window activity | Endpoint adapter; a retained focused object need not belong to the active/key window. | -| Selection | Component or collection state according to its controlled/uncontrolled contract. | -| Ring geometry/colors | Styling phase and renderer, not navigation or activation logic. | - -Programmatic focus must not rewrite physical modality to keyboard. A keyboard -menu invocation, hover focus, accessibility action, and dismissal restoration -may need different visual intent. Keep that intent scoped to the request/owner. - -## Modern refs and slot composition - -- Agentic components target React 19.1.4+: receive `ref` as a prop and preserve the - declared native root using `PropsWithRefOf`. Do not add legacy - `componentRef` props or `forwardRef` wrappers to new components. -- Use `React.ComponentRef` for ordinary host-instance typing when - supported. Codegen schemas are a separate grammar: preserve their validated - ref spelling, including existing `React.ElementRef`, until the pinned - generator accepts a change. A newer TypeScript alias does not establish - codegen compatibility. -- The public root ref, internal focus-target ref, popup-anchor ref, and scope ref - can refer to different native objects. Card's structural root must not silently - become its overlay; Input's public View must not silently become its TextInput. - Use the inner slot's ref or a deliberately reviewed imperative API. -- Reuse Framework Base's slot ref composition. It merges base-slot/render refs - and preserves refs returned by slot transforms; do not create another - component-local merge-ref helper or write into the consumer's ref yourself. -- Forward refs through the custom JSX runtime and compatible `as` overrides. - Do not assume the callable slot function is a mounted native target. -- Support object refs, callback refs, React 19 callback cleanup, null detach, - replacement, and development setup/cleanup cycles. Registration cleanup must - be idempotent and cannot remove a newer target registered under the same key. -- Resolve targets at commit/attachment time, not while rendering. A registration - should identify the current host, owning surface/window, and mount generation. - Never keep a naked renderer tag as a permanent target identity. -- A ref becoming non-null is not proof that native focusability/layout has been - committed. Schedule focus from the owning effect/lifecycle after the needed - state is applied, then confirm through the native focus event or an explicit - native result. Cancel on detach, disable, superseding request, or scope close. -- Do not call `focus()` during render or expose a success-shaped fallback for - unsupported targets. Distinguish requested, confirmed, cancelled, not-mounted, - not-focusable, and unsupported outcomes in the proposed adapter contract. - -Existing mechanisms: `packages\framework-base\src\component-patterns\slot.ts:14-76,118-142`, -`render.ts:38-75`, and `useSlot.test.tsx:453-490`. The framework still supports -older React consumers; do not remove its compatibility behavior merely because -agentic components use React 19. - -## State, activation, and rendering - -1. Resolve disabled/interactive state and the actual focus target in - `use_unstable`. - Use `useFocusablePressable` for pressable targets and `useFocusTarget` for - editable or other native targets. Carry `focusTarget`/`focusTargetRef` in - private state; compose the internal ref on the actual focus slot. -2. Compose interaction handlers with exactly one activation owner. Native - Pressable activation, key handlers, and accessibility actions must not each - invoke the same action independently. - Separate activation cancellation from cleanup of an already-started native - press. macOS activates on keydown; Windows/Win32 activate on keyup. Always - finish pressed feedback on release, self-blur, disable, or target replacement - without generating a new action or forwarding `onPressOut` twice. -3. Call `useFocusVisuals` after resolving focus. Store its private optional - `FocusRing` in state and apply `enableFocusRing` only to the focus target. -4. In the styling phase call `applyFocusRingStyles` with theme state and resolved - radius. Its shared defaults are `strokeFocusInner`/`strokeFocusOuter` and - `strokeWidth.thin`/`strokeWidth.thick`. Keep native colors opaque and theme - caches immutable; pass disabled/noninteractive targets as unfocused. -5. Render the optional slot inside the target. Native/custom rings must not - compete; mounted custom ring geometry must not change merely on focus/blur. - -Current `alwaysVisible` means visible **while focused** for any modality and -selects the custom path. It does not focus the control or make an unfocused -control visible. Native window activity remains a separate renderer obligation. -Do not add this option to every public component prop surface without a contract. - -`useRootSettings` remains a stable non-subscribing query. `useRootInputModality` -provides opt-in notifications; `useFocusVisuals` subscribes only while focused -on the custom path. `RootInputBoundary` attaches additional native popup content -to the same controller without changing themes. Do not add scene-wide React -updates or per-control pointer trackers. - -Focus targets expose `requestFocus(intent)`, an observable snapshot, and a -mount generation. A request reports `requested` until a focus event confirms it; -detach, disable, replacement, or explicit cancellation invalidate pending work. -Same-commit ref handoffs to the identical native instance preserve focus, -snapshot identity, and mount generation; inline forwarding callbacks must not -create a subscription/render loop or cancel a valid keyboard press. Registration -epochs reject stale cleanup independently. Genuine detach notifications are -coalesced to the end of the commit, but `current` clears and pending requests -cancel immediately. Never treat the last observed focus snapshot as proof that -a target is still mounted. Higher-level native window activity and cross-window -restore remain separately qualified. - -For nested controls, preserve `target`/`currentTarget` or equivalent native -identity and separate self-focus from focus-within. Functional Input borders, -selection visuals, and hover styling are not generic focus rings. -Do not apply this self-focus filter to Pressability's responder press: a valid -pointer press can originate on a noninteractive label/icon descendant. - -## Collections and popup scopes - -- Use one owner for arrows/Home/End/Tab within a scope. Do not run a JS roving - algorithm and a native FocusZone over the same keys. -- Apply selection/focusability changes before requesting a target. Confirm - focus before treating it as the active native element; retain controlled - selection semantics. -- Define initial focus and dismissal behavior for keyboard, pointer, - accessibility, Escape, outside click, and application deactivation. -- Restore only to a live eligible target in the intended active window. Do not - steal focus back from an outside-click destination. -- React ancestry is not native-window ancestry. A popup can inherit themes and - settings while requiring a separate event attachment to the same controller. -- Keep navigation and restoration in the owner; leaf MenuItem/Tab components - must not invent a global focus manager. - -## Abstraction and native boundaries - -Extend existing helpers before adding layers. General interaction/target -contracts belong in Framework Base; modality and appearance remain in design; -component-specific eligibility, layout, and selection remain in components. - -A future `native-lib` may provide opt-in native capabilities and shared native -projections. It must depend downward on React Native/platform APIs, never on -design or components. Keep view-bound operations on native refs/generated -commands. Use TurboModules for non-view services that actually need them. -Do not add a process-global `focus(tag)` registry, arbitrary OS handle access, -or production dependencies on the desktop automation driver. - -## Required evidence before shipping - -- Object/callback refs, cleanup, `as` replacements, ref replacement, and - unmount/remount retain exactly one live focus target. -- Native focus, active/selected state, event order, and action counts agree. -- Disabled and disabled-focusable policies are intentional per control. -- Same-target modality changes update only the relevant visual consumer. -- Nested owners, popup boundaries, and independent scenes do not leak state. -- Native keyboard/pointer and accessibility runs are non-skipped on each - supported endpoint; include Narrator/VoiceOver and active/inactive windows. -- Theme/high contrast, scaling, clipping, rounded targets, and native/custom - ring exclusivity have renderer-specific evidence. - -## Public references - -- [React 19 refs and cleanup](https://react.dev/blog/2024/12/05/react-19) -- [React imperative handles](https://react.dev/reference/react/useImperativeHandle) -- [React Native 0.81 native commands](https://reactnative.dev/docs/0.81/the-new-architecture/fabric-component-native-commands) -- [Types and slots](types-and-slots.md) -- [State and accessibility](state-and-accessibility.md) +This compatibility path remains so existing links continue to resolve. Do not add new links to it. diff --git a/.github/skills/agentic-component-authoring/references/rendering.md b/.github/skills/agentic-component-authoring/references/rendering.md index 4bf1c6dce2..37ca887a2a 100644 --- a/.github/skills/agentic-component-authoring/references/rendering.md +++ b/.github/skills/agentic-component-authoring/references/rendering.md @@ -1,78 +1,12 @@ -# Rendering and assembly +--- +id: compat.agentic-component-authoring.rendering +kind: compatibility-reference +owner_paths: + - packages/agentic/components +--- -Use this reference for `render.tsx`, `.ts`, slot ordering, conditional structure, display names, -and package exports. The canonical examples are -[`renderButton.tsx`](../../../../packages/agentic/components/src/components/button/renderButton.tsx) and -[`button.ts`](../../../../packages/agentic/components/src/components/button/button.ts). +# Moved: Rendering and assembly -## Keep render functions pure +This reference now lives at [references/agents/components/agentic/rendering.md](../../../../references/agents/components/agentic/rendering.md). -The render function receives resolved state and returns the final element tree. - -- Add `/** @jsxImportSource @fluentui-react-native/framework-base */` when rendering slot components. -- Do not call hooks. -- Do not read themes or tokens. -- Do not create styles. -- Do not reinterpret defaults or accessibility. -- Do not mutate slot props. - -All those decisions belong to earlier stages. - -## Render slot functions directly - -Capitalize local optional slot variables when that improves readability. Render required roots through the state slot: - -```tsx -return {content}; -``` - -Keep slot order visible in JSX. Button renders its active icon before or after content according to `iconPosition`. -Avoid array construction or opaque helper loops when direct JSX makes the public ordering contract clearer. - -## Resolve conditional structure from state - -The render function may select among already-resolved slots and compose conditional layout: - -- Button chooses `selectedIcon` when selected and falls back to `icon`. -- Toggle content uses the private container, hidden ghost, and visible label. -- Ordinary content renders without that wrapper. - -Do not create missing slots in the render stage. If a structural element can exist, represent it in the state type and -construct it in the state hook. - -Components that own children should render only their declared slots. Do not spread native `children` into the tree and -let consumers bypass slot order. - -## Assemble the component in one small file - -`.ts` should show the pipeline without embedding stage logic: - -```ts -export const Component = (props: ComponentProps) => { - const state = useComponent_unstable(props); - useComponentStyles_unstable(state); - return renderComponent_unstable(state); -}; -``` - -Set `displayName` for diagnostics and Storybook metadata. Keep unstable stage functions named consistently so tests and -future composition work can identify them. Do not wrap the component in memoization or another boundary without measured -need and repository precedent. - -Under this package's React 19.1.4 baseline, `ref` is an ordinary component prop. Include it in the public root props and -let the state hook forward it to the resolved root slot; do not wrap assembly components in `forwardRef`. If rendering a -second internal ref on the same slot, pass it in JSX so the slot runtime composes it with the captured consumer ref. - -After assembly, export `use_unstable`, `useStyles_unstable`, and -`render_unstable` from the package root together with `State`, according to the -[types and slots export rules](types-and-slots.md#exports). These stages are intentionally unstable but public so other -components can reuse and extend the pipeline without importing internal paths. - -## Review checklist - -- Render contains JSX and conditional structure only. -- Slot order matches the spec. -- Optional and replacement slots fall back intentionally. -- State-only structure does not leak into public props. -- The assembly file is a readable state -> styles -> render pipeline. -- `displayName` is present and all three composition stages are exported under component-qualified unstable names. +This compatibility path remains so existing links continue to resolve. Do not add new links to it. diff --git a/.github/skills/agentic-component-authoring/references/spec-source-adaptation.md b/.github/skills/agentic-component-authoring/references/spec-source-adaptation.md index 8e4139ba5b..fd0ffd99f9 100644 --- a/.github/skills/agentic-component-authoring/references/spec-source-adaptation.md +++ b/.github/skills/agentic-component-authoring/references/spec-source-adaptation.md @@ -1,9 +1,15 @@ -# Flex source adaptation +--- +id: compat.agentic-component-authoring.spec-source-adaptation +kind: compatibility-reference +owner_paths: + - packages/agentic/components +--- -Contract creation now belongs to the +# Moved: Flex source adaptation + +Contract creation belongs to the [agentic component contract authoring skill](../../agentic-component-contract-authoring/SKILL.md). -For pinned Flex component skills and X3 lineage, use its -[Flex and X3 source adapter](../../agentic-component-contract-authoring/references/sources/flex-x3.md). +Its Flex and X3 adapter now lives at +[references/agents/components/agentic/contract-sources/flex-x3.md](../../../../references/agents/components/agentic/contract-sources/flex-x3.md). -This compatibility reference remains so existing links continue to resolve. -Implementation guidance stays in the agentic component authoring skill. +This compatibility path remains so existing links continue to resolve. Do not add new links to it. diff --git a/.github/skills/agentic-component-authoring/references/state-and-accessibility.md b/.github/skills/agentic-component-authoring/references/state-and-accessibility.md index 69b977c37a..72e901a347 100644 --- a/.github/skills/agentic-component-authoring/references/state-and-accessibility.md +++ b/.github/skills/agentic-component-authoring/references/state-and-accessibility.md @@ -1,190 +1,12 @@ -# State and accessibility +--- +id: compat.agentic-component-authoring.state-and-accessibility +kind: compatibility-reference +owner_paths: + - packages/agentic/components +--- -Use this reference for `StateProps`, `use.ts`, defaults, interaction hooks, accessibility, derived -state, and slot construction. The canonical example is -[`useButton.ts`](../../../../packages/agentic/components/src/components/button/useButton.ts). +# Moved: State and accessibility -## State hook responsibilities +This reference now lives at [references/agents/components/agentic/state-and-accessibility.md](../../../../references/agents/components/agentic/state-and-accessibility.md). -The state hook owns: - -1. Destructuring component-owned props while preserving unhandled native props. -2. Applying behavioral defaults. -3. Deriving facts needed by accessibility, styles, and rendering. -4. Validating development-time usage. -5. Reading theme state. -6. Configuring the native interaction hook. -7. Constructing required and optional slots. -8. Returning one complete resolved state object. - -Do not resolve token styles or render elements in this hook. - -## Preserve raw presence before applying defaults - -Derive presence-sensitive behavior from raw props. Button computes `hasContent`, `hasIcon`, and `hasSelectedIcon` before -constructing slots. It uses those values to derive `iconOnly`, while `selected !== undefined` determines whether toggle -semantics are enabled even when `selected` is false. - -Apply context-sensitive defaults only after those facts are known. Button defaults shape to `circle` for icon-only use -and `rounded` otherwise. - -## Decide who owns a stateful axis - -Before wiring any state, decide which of two kinds of axis you are building. Getting this wrong produces a component -that either cannot be used interactively or changes state when it should not. - -### Self-driving controls - -The interaction _is_ the state change. Pressing a checkbox checks it; pressing a switch flips it; pressing a disclosure -header expands it. There is no other reasonable interpretation of the press, and the control needs no outside -coordination to be correct on its own. - -These support both directions with one prop triple per axis: - -| Prop | Purpose | -| ----------------- | ------------------------------------------------------------------------- | -| `` | The externally driven value. Supplying it makes the axis controlled. | -| `default` | The initial internally driven value. Ignored while `` is supplied. | -| `onChange` | The state change event, called with the next value in both directions. | - -Name the triple after the axis: `checked` / `defaultChecked` / `onChange` for Switch, `status` / `defaultStatus` / -`onStatusChange` for Checkbox, `expanded` / `defaultExpanded` / `onExpandedChange` for Accordion. - -Resolve a boolean axis with `useToggleState` from `@fluentui-react-native/framework-base`, which wraps -`useControllableValue` and adds the disabled guard and no-op suppression: - -```ts -const expansion = useToggleState({ value: expanded, defaultValue: defaultExpanded, onChange: onExpandedChange }); -``` - -Then drive it from the interaction handler and forward the user's own handler. Multi-value axes such as Checkbox's -tri-state `status` use `useControllableValue` directly. - -### Externally driven selection - -The press is an _activation_, and what it means for selection is decided by a caller or a surrounding group. A button -press runs an action; a tab, radio, list item, listbox item, or menu item press tells its group which entry was chosen, -and the group decides what to select and what to clear. - -These components must **not** change their own selection: - -- Expose only `selected?: boolean`, and render exactly what is given. -- Do not add `defaultSelected` or `onSelectedChange`. There is no internally driven mode to seed or report. -- Forward `onPress` unchanged; it is the interaction event the owner reacts to. -- Derive selection semantics from the resolved value, and gate optional semantics on `selected !== undefined` when the - axis is opt-in. - -A normal button must not change state on a press. A lone radio or tab that selected itself would also be wrong, because -it could never deselect and nothing would clear its siblings. - -### Watch axes that change component identity - -When `selected !== undefined` switches a component between two shapes, that is an identity change rather than a value -change. Button uses it to become a toggle button, which adds the Semibold ghost label that reserves layout width, and -Card uses it to become selectable, which adds the interactive overlay. - -Keep those axes stable for a given instance and do not expose them as a Storybook control, because flipping the control -between `undefined` and `false` visibly resizes or re-roles the component. Demonstrate the axis with focused stories -that always supply a value. - -## Merge accessibility deliberately - -Start with consumer-provided accessibility state, then apply component-owned semantics: - -```ts -accessibilityState: { - ...accessibilityState, - disabled, - ...(isToggle && { checked: selected }), -} -``` - -The component must own its role and state semantics while preserving unrelated consumer values such as `busy`. - -- Set the native role explicitly. -- Keep disabled state, focusability, and interaction behavior consistent. -- Prefer a consumer-provided `accessible` or `focusable` value only when it does not violate the component contract. -- Add selected or checked semantics only when the corresponding behavior is enabled. -- Use action-oriented accessible names for icon-only controls. - -Button warns in development when an icon-only instance lacks an `accessibilityLabel`. Put warnings in an effect so -render remains free of observable side effects, and make the dependency list match every value used by the warning. - -For a component-owned toggle or select action, use `resolveAccessibilityAction` -from Framework Base with the semantic action, `Platform.OS`, and the caller's -action list. Assign its `accessibilityActions` and compare incoming -`event.nativeEvent.actionName` with its `name`; do not duplicate platform strings. -Windows Fabric uses lowercase names, while Win32 uses `Toggle`/`Select`. -macOS retains those title-case names as custom actions, not an invented -`activate`/AXPress fallback. - -Keep state ownership and disabled guards in the component. Run accepted -component behavior first, then forward the original caller accessibility event -exactly once, including custom and disabled events. Do not synthesize `onPress` -or create a second activation path. The resolver preserves caller labels and -custom names while deduplicating declarations. - -Future universal non-styling accessibility helpers belong in -[`framework-base/src/accessibility`](../../../../packages/framework-base/src/accessibility/AGENTS.md). -Read its source-evidence, native/generic boundary, handler-composition, and -validation requirements before extending it. Verify actual UIA/AX action -invocation and callback counts; a state read after a physical click does not -prove accessibility action dispatch. - -## Use framework interaction and slot hooks - -Interactive roots should use the framework state hook, such as `usePressableState`, so hovered, pressed, and focused -state is normalized and user handlers are forwarded. - -Construct slots after native props are resolved: - -- `useSlot` for the required root. -- `useOptionalSlot` for optional public slots. -- `useOptionalSlot` for private state-only slots when their condition is active. - -Pass `null` for a slot that must not exist. Do not create a placeholder slot and hide it later with styles. - -Button constructs its hidden Semibold content and content container only when toggle behavior and visible content require -them. This keeps ordinary buttons free of the extra structure. - -## Return stable resolved state - -Return: - -- slot functions -- required variant values -- derived state -- preserved user styles -- theme state -- interaction state - -Keep property ordering intentional so later spreads cannot silently replace component-owned values. If two state sources -can share a key, assign the final owned value explicitly after spreading. - -Export the hook from the package root as `use_unstable`, together with its resolved `State` type, -so another component can extend the state stage without importing package internals. - -## Platform behavior - -For focus ownership, activation, and native target lifetime, read -[common focus authoring](focus.md), followed by the -[Windows/Win32](focus-windows.md) or [macOS](focus-macos.md) detail. - -Keep platform-specific native imports out of shared files when React Native forks expose incompatible types. Put fork -imports in platform files or redeclare a small platform-neutral contract. Surface unsupported platform behavior rather -than silently pretending it succeeded. - -## Review checklist - -- Defaults match the spec and depend on already-derived facts. -- Omitted controlled props retain their meaning. -- Each stateful axis is classified before it is wired: a self-driving control exposes ``, `default`, and - `onChange` and changes state on interaction, while externally driven selection exposes only `selected` and - never changes it. -- A press on a button, tab, radio, or item does not change that component's own selection. -- An axis that changes component identity stays stable per instance and is not a Storybook control. -- User native handlers and unrelated accessibility state are preserved. -- Disabled, focusable, and selected semantics agree. -- Icon-only or unlabeled usage is diagnosed consistently. -- Optional slots exist only when their render condition is active. -- The hook contains no token style selection and no JSX rendering. +This compatibility path remains so existing links continue to resolve. Do not add new links to it. diff --git a/.github/skills/agentic-component-authoring/references/styles-and-tokens.md b/.github/skills/agentic-component-authoring/references/styles-and-tokens.md index f2334c1ce5..742c08f9c1 100644 --- a/.github/skills/agentic-component-authoring/references/styles-and-tokens.md +++ b/.github/skills/agentic-component-authoring/references/styles-and-tokens.md @@ -1,211 +1,12 @@ -# Styles and tokens +--- +id: compat.agentic-component-authoring.styles-and-tokens +kind: compatibility-reference +owner_paths: + - packages/agentic/components +--- -Use this reference for `.styles.ts`, `useStyles.ts`, token mapping, theme caching, state precedence, and -slot prop application. The canonical examples are -[`button.styles.ts`](../../../../packages/agentic/components/src/components/button/button.styles.ts) and -[`useButtonStyles.ts`](../../../../packages/agentic/components/src/components/button/useButtonStyles.ts). +# Moved: Styles and tokens -## Resolve values from the correct token source +This reference now lives at [references/agents/components/agentic/styles-and-tokens.md](../../../../references/agents/components/agentic/styles-and-tokens.md). -Consult -[`flex-token-map.yaml`](../../../../packages/agentic/design/src/tokens/mappings/flex-token-map.yaml) before choosing a -value. - -- Read semantic colors from `useThemeState().tokens.color`. -- Read grouped categories from `tokens.borderRadius`, `tokens.spacing`, `tokens.strokeWidth`, `tokens.fontFamily`, - `tokens.fontSize`, `tokens.fontWeight`, `tokens.lineHeight`, and `tokens.shadow`. -- Prefer Flex tokens for every mapped semantic value. -- Import named global tokens only when Flex has no equivalent, such as current Button icon dimensions or a true zero. -- Record a genuine token gap in the component spec. Do not replace it with a nearby token or numeric literal. -- For compatibility with an existing V1 component, derive cross-platform mappings from its Win32, macOS, or Windows - implementation. Do not use iOS as the canonical value unless the change explicitly targets iOS. -- `useThemeState` projects a FURN Theme from context into Flex tokens and stores one symbol-keyed `ThemeState` on that - Theme object. Consumers of the same Theme therefore share token and style-cache identity. - -## Build styles in layers - -Use four layers: - -1. A module-scoped `StyleSheet` for structural values that do not depend on theme, props, or state. -2. Module-scoped state factories for theme-independent state selectors. -3. Module-scoped themed factories for token-derived styles. -4. Per-instance style arrays that select cached objects and place user styles last. - -Never create a style factory inside a hook or render function. - -Reuse the design package's `@fluentui-react-native/design/styling` submodule: - -- [`getStateStyleFactory`](../../../../packages/agentic/design/src/styling/branchedStyle.ts) lazily flattens and caches - one theme-independent state definition. -- [`getThemedStateStyleFactory`](../../../../packages/agentic/design/src/styling/branchedStyle.ts) resolves and caches - one flattened definition per `ThemeState`. -- [`getThemedColorStyleFactory`](../../../../packages/agentic/design/src/styling/colorStyles.ts) converts semantic - color keys and delegates its hierarchy and caching to `getThemedStateStyleFactory`. - -These are the canonical Button mechanisms and cache plain resolved style objects in `state.themeStyles`. Do not wrap them -in another `StyleSheet.create` cache. Use -[`themedStyleSheetFactory`](../../../../packages/agentic/design/src/useThemeState.ts) only when a component genuinely -needs a complete theme-only `StyleSheet.create` result that is not represented by a branched state definition. - -Every theme factory may depend only on `ThemeState` values such as tokens and high contrast. Props, interaction state, -and user styles must remain outside the cache so one component instance cannot leak into another. - -## Declare state hierarchy and precedence - -Represent related axes as ordered hierarchy levels. Each level contains mutually exclusive states in priority order. -Button colors use: - -```ts -[['primary', 'secondary', 'outline', 'subtle'], ['selected'], ['disabled', 'pressed', 'hovered']]; -``` - -This means appearance is the base branch, selected refines it, and interaction is the final refinement. Put interaction -states in `disabled`, `pressed`, `hovered` order so the first active state has the required priority. - -Use `getThemedStateStyleFactory` for token-derived hierarchy definitions and `getStateStyleFactory` for -theme-independent definitions. Use `getThemedColorStyleFactory` for semantic background, border, and foreground colors; -it automatically resolves `tokens.color.hover` and `tokens.color.pressed` for inherited semantic keys. - -An interaction may intentionally switch semantic keys. Button Subtle is transparent at rest but explicitly selects -`backgroundNeutralSubtle` inside hovered and pressed branches. Button Outline uses `strokeNeutralLoud`, whose hover and -pressed token values provide visible stroke feedback. Do not assume a token named `Transparent` will produce visible -interaction states; verify the resolved token maps. - -Build the state source from resolved state without hiding precedence in conditionals. Button pushes appearance, selected, -disabled, pressed, and hovered values, then lets the declared hierarchy select the winner. - -## Group related layout axes - -Combine axes that produce one coherent style object. Button selects size, shape, and content layout from one root style -hierarchy: - -```ts -[ - ['small', 'medium', 'large'], - ['rounded', 'square', 'circle'], - ['withContent', 'iconOnly'], -]; -``` - -Use separate factories for independent concerns such as typography, focus, and content visibility. This keeps unrelated -state branches from multiplying into one large definition. - -Destructure only the token groups needed by a factory: - -```ts -({ borderRadius, spacing, strokeWidth }: FlexTokens) => ... -``` - -Validate token values when their generated type is wider than the React Native style property. Button validates its gap -token before assigning it to `ViewStyle['gap']`. - -## Apply slot props in one stage - -`useStyles.ts` should export `useStyles_unstable`, select styles, and call `attachSlotProps`; it -should not create factories. Export that hook directly from the package root under the same name. - -Compose root styles in this order: - -1. structural style -2. resolved layout or variant style -3. semantic colors -4. focus or other conditional styles -5. user style - -Derive dependent slot props from the same resolved values. Button applies foreground color and size to both icon slots, -marks decorative icons inaccessible, and applies typography plus foreground color to content. - -Preserve consumer slot behavior unless the component owns it. Button no longer forces `numberOfLines`; its content and -toggle container use `flexShrink` so constrained labels can wrap. A consumer can still request truncation through the -content slot. - -## Share focus behavior and styling - -Follow [common focus authoring](focus.md) and its Windows/Win32 or macOS detail. -State hooks use `useFocusVisuals` to create a private optional `FocusRing` and determine the native -`enableFocusRing` setting. The custom slot is absent on the native path; when present, its configured Views -remain mounted across visibility changes. - -Keep `ThemeState` in the styling phase. Call `applyFocusRingStyles(state.FocusRing, state, resolvedRadius)` -to bind shared focus colors and widths while retaining component-specific geometry. The helper uses an -immutable theme stylesheet; props, focus state, and radius remain outside that cache. - -Native rings default to Windows/macOS; Win32 uses the root-modality-aware custom ring. The Windows native -path requires RNW 0.81.35 or newer. Do not introduce `outline*` focus styles, local modality trackers, or -functional border changes to implement a focus ring. - -For non-focus indicators such as Avatar's activity ring, reuse `FocusVisual` as a persistent decorative border rather -than native outline properties. This preserves stable border lifetime on renderers affected by late border creation. -The component owns the indicator's meaning, tokens, and visibility, independently of the focus policy. For an outward -ring with a gap, each absolute edge is `-(gap + strokeWidth)` so the border preserves the requested gap without changing -layout. Mount it with its border configured even when hidden; do not switch border width between zero and a nonzero value. - -## Native motion - -Use `useSharedAnimatedLoop` for phase-locked loading indicators. It treats `useNativeDriver` as a request and selects -the JavaScript driver on macOS Fabric, where native-driven transforms currently remain static. Other renderers keep -their requested driver. These continuous loops must use `isInteraction: false` so the JavaScript path does not starve -interaction-deferred work. Preserve reduced-motion gating independently of driver selection. - -Verify animation with changing pixels on the actual native element, not merely an `Animated.loop().start()` assertion. -Keep a visible static silhouette when motion is disabled, and do not rely on web-only SVG `pathLength` normalization; -derive native dash lengths from the actual circumference. - -## Native text and vertical alignment - -Treat the component frame, the native text line box, and the glyph's visible ink as different measurements. `alignItems` -and `justifyContent` align Yoga boxes; they do not move a glyph inside a stretched Text or editor. A font's `fontSize` -is not its measured ascent plus descent. Platform font fallback can make the difference particularly visible on Win32. - -- Use `Icon` for font glyphs. Its sized View centers an intrinsic, non-shrinking Text child. With a fully specified frame, - the Text is absolute with no edge offsets so Yoga measures its natural height instead of clamping it to the frame. - Without a complete frame, normal flow retains intrinsic dimensions. Do not put the icon's - `height` or `lineHeight` on that Text, use a negative margin or transform to nudge one glyph, or depend on - `textAlignVertical` as a cross-platform centering mechanism. Icon dimensions own scaling, just as they do for images - and SVGs; dimensioned font icons do not independently follow text scaling. -- Retain typography line-height tokens for ordinary labels and paragraphs. Do not globally remove line height to fix - one renderer, and do not replace a typography line height with a control's height or the font's em size. -- Centered initials follow the same intrinsic-metrics rule. Avatar owns all typography and uses native Text to avoid - inheriting the themed Text component's body line height. It preserves token font sizes, native text scaling, and - explicit caller line metrics, and uses an absolute child without edge offsets to avoid clamping in the smallest - padded frame. Do not clip initials to an em square or assume `undefined` can clear merged defaults. -- For single-line `TextInput`, reset internal padding and let the native editor measure its font. Put spacing outside - the editor with Yoga margins or a containing View; keep the control's token-derived minimum height separate. - Input demonstrates this without adding another wrapper or changing the caller's final style precedence. - Preserve the pointer target when moving spacing: Input's inaccessible, non-focusable contents row forwards activation - to the editor through its shared focus target, whose ref is composed by the slot render path. Test disabled and - non-focusable behavior and caller ref forwarding too. -- Distinguish centered graphics from aligned text baselines. `CompoundItemLayout` centers arbitrary regions by default. - A text-only row with different label/shortcut font sizes can use `alignItems: 'baseline'` on the root and inline - content row. All participating regions must supply meaningful text baselines; do not impose that policy on images - or action buttons. -- On Android, disable extra font padding for tightly sized decorative glyphs. That setting is not a desktop fix. -- Exercise small and large sizes, font and image/SVG icons, different text metrics, selection, and constrained wrapping. - Check the running native output, not just flattened styles: a snapshot of the wrong Text height can pass indefinitely. - -## Selected text without layout shift - -When selected text changes weight: - -- Render an inaccessible Semibold ghost that reserves width and height. -- Overlay a View that vertically centers the visible label at its own measured height. Do not stretch the visible Text - itself with `absoluteFillObject`: smaller text will otherwise sit at the top of the reserved area on native renderers. -- Keep the ghost and container state-only. -- Apply the same wrapping constraints to both labels. - -Use `LayoutStableText`, as Button and Tab do, rather than duplicating the ghost/overlay structure. The overlay must -preserve visible-text accessibility and input handling. Typography remains owned by the consuming component. - -## Review checklist - -- Every value comes from Flex or a documented token gap. -- Structural, themed, instance, and user styles are separated. -- Every factory is module-scoped and theme-safe. -- State hierarchy makes precedence explicit. -- Hover and pressed values are verified, not inferred from token names. -- Independent concerns use independent factories. -- User styles are last. -- Slot props share resolved color and size values consistently. -- Constrained text can wrap unless truncation is an explicit public choice. -- Font frames and reserved labels center intrinsic text; editor spacing is external; text baselines are an explicit - choice distinct from centering arbitrary slots. +This compatibility path remains so existing links continue to resolve. Do not add new links to it. diff --git a/.github/skills/agentic-component-authoring/references/tests-and-stories.md b/.github/skills/agentic-component-authoring/references/tests-and-stories.md index 93aeb2494b..9bd3e0c843 100644 --- a/.github/skills/agentic-component-authoring/references/tests-and-stories.md +++ b/.github/skills/agentic-component-authoring/references/tests-and-stories.md @@ -1,274 +1,12 @@ -# Tests and stories +--- +id: compat.agentic-component-authoring.tests-and-stories +kind: compatibility-reference +owner_paths: + - packages/agentic/components +--- -Use this reference for runtime coverage, type coverage, visual snapshots, Storybook CSF, and validation. Canonical examples -are [`button.test.tsx`](../../../../packages/agentic/components/src/components/button/button.test.tsx), -[`button.stories.tsx`](../../../../packages/agentic/components/src/components/button/button.stories.tsx), and -[`icon.stories.tsx`](../../../../packages/agentic/components/src/primitives/icon/icon.stories.tsx). +# Moved: Tests and stories -## Runtime tests +This reference now lives at [references/agents/components/agentic/tests-and-stories.md](../../../../references/agents/components/agentic/tests-and-stories.md). -Use `render` from the package-private `src/common/renderWithTheme` helper for -component scenes. It places a `ThemedRoot` outside the scene and any supplied -wrapper, preserving the same root settings through rerenders. Import events, -queries, and types from `@testing-library/react-native`; do not import -`react-test-renderer` directly. Tests can dispatch root modality events through -the helper's `test-scene-root` test ID. - -Prefer public semantics: - -- query the root by accessibility role -- use matchers such as `toBeDisabled()` -- assert `accessibilityState`, accessible names, and focusability -- use async `fireEvent` calls for hover, press, focus, and user handlers -- query a stable `testID` only when a platform adapter does not expose the expected accessible inner primitive - -Flatten styles only when the resolved visual contract is the behavior under test. - -Button coverage demonstrates: - -- default props, role, accessibility, and styling -- forwarding user handlers while interaction state updates -- disabled behavior and inaccessible icon slots -- icon-only accessible names and minimum target size -- development warnings -- selected icon replacement and toggle semantics -- hidden ghost text that prevents layout shift -- slot order and user-style precedence -- focus rendering -- every appearance and size -- visible hover and pressed feedback -- constrained text wrapping - -Use table-driven tests for complete finite axes. When fixing one state regression, add a focused assertion even if a -snapshot also changes. - -## Type tests and primitives - -Add committed `*.types.test.ts` coverage for compile-time slot acceptance, mutually exclusive props, and public type -contracts. Keep exploratory type probes outside package source or remove them before validation. - -Primitive runtime tests should cover every renderer or source branch and verify forwarding of size, color, -accessibility, and test props. - -Native motion stories compare repeated element screenshots and honor the runtime reduced-motion preference. -Use `browser.isElementDisplayed(elementId)` and `browser.takeElementScreenshot(elementId)` for native protocol operations; -WebdriverIO's DOM-oriented `element.isDisplayed()` tries unsupported JavaScript execution. Skip pixel checks explicitly -when the driver does not provide element screenshots. - -## Visual snapshots - -Keep snapshots focused on resolved output rather than the full renderer tree. Button snapshots map each appearance to: - -- flattened root style -- flattened content style -- one requested visual state - -Snapshot rest, hover, pressed, focus, disabled, and selected states when those states are part of the visual contract. -Update snapshots through the package's declared script only after reviewing the behavioral diff. - -## Story structure - -Use the Fluent UI React v9 Button stories for focused API-axis examples and the Fluent headless Button story for a -compact grouped overview: - -- `microsoft/fluentui/packages/react-components/react-button/stories/src/Button` -- `microsoft/fluentui/packages/react-components/react-headless-components-preview/stories/src/Button` - -Adapt the structure to React Native. Do not copy DOM props, CSS class hooks, or unsupported web behavior. - -Colocate `.stories.tsx`; the Storybook source glob discovers it automatically. - -Declare module-scoped styles and other values referenced by metadata before the `Meta` object. Storybook evaluates -metadata while importing the module, so referencing a later `const` drops the complete story module with an -initialization error. - -Keep on-device controls limited to scalar, serializable props. For required React elements or style objects, supply the -fixed demonstration values inside the story render function and expose only meaningful finite or numeric args; object -controls for React elements are noisy and cannot safely edit the contract. - -Story controls follow the axis's ownership, described in -[State and accessibility](./state-and-accessibility.md#decide-who-owns-a-stateful-axis). - -For a self-driving control such as Checkbox, Switch, or Accordion, do not put the controlled prop in `args`. A pinned -`checked`, `expanded`, or `status` arg forces every instance into externally driven mode, so pressing the component does -nothing and the story looks broken. Expose `default` as the on-device control and use it in `args`, including for -variant-scan stories, so each instance starts in the demonstrated state and still responds to presses. - -For externally driven selection such as Button, Tab, Radio, Card, or the item components, a fixed `selected` arg is -correct: the component never changes it. Give the interactive demonstration in one story that owns the value with -`React.useState` and updates it from `onPress`. - -Do not expose an axis that changes component identity as a control. Button's `selected` decides whether it is a toggle -button at all, and Card's decides whether it is selectable, so a control that flips between `undefined` and `false` -resizes or re-roles the component instead of demonstrating a value. - -Each story module should provide: - -- typed `Meta` with `component` and a title matching the component location: - `Components/` for higher-order components, `Primitives/` for agentic primitives, or `Native/` for - standalone native component packages -- useful common args -- controls for finite or numeric public props -- a short component description -- an args-driven `Default` -- a grouped `Overview` when the contract has several axes -- focused named stories that compare all values of one axis in one canvas -- an interactive story for each stateful axis: `default` for a self-driving control, or a story owning the value - with `React.useState` and `onPress` for externally driven selection -- `parameters.docs.description.story` for focused scenarios - -Stories included in native agent validation also need a stable root `testID`. -Use selectors that describe the component or scenario rather than visible text, -layout order, or native class names. Keep the initial args deterministic and -add identifiers only to the small smoke set that agents and CI actively -validate. - -Prefer executable WebdriverIO tests under `wdio`, typed with -`WdioStory>` from -`@fluentui-react-native/storybook-desktop/testing` using `import type`. -The story is the suite (`describe`), and named functions are its cases (`it`): - -```tsx -export const Default: Story = { - wdio: { - 'is enabled': async ({ browser, expect }) => { - await expect(await browser.$('~story-button')).toBeEnabled(); - }, - }, -}; -``` - -Each callback receives `browser`, `expect`, `platform`, `desktop`, `signal`, -and `skip`. The typed target `platform` is `macos`, `windows`, or `win32`; -Win32 is distinct from its WebDriver `platformName` of `windows`. Branch -inside callbacks for genuine platform differences and use explicit skip -reasons for unsupported `browser.capabilities['furn:features']`. - -All catalog tests use WDIO; custom `desktopDriver` plans remain only in runner -compatibility fixtures. Keep names static and callbacks self-contained. Node helpers may be -dynamically imported inside each callback, but callbacks cannot capture -story-module bindings. The shared Babel config strips all test functions -before native dependency collection. Named cases get independent previews, -sessions, workers, deadlines, results, and failure evidence. - -The single-function `wdio` form remains supported. Run with -`yarn storybook test --` and optionally `--test `. -`--list` discovers names without executing code. The `stories-and-tests` -smoke mode runs these functions alongside remaining legacy plans, grouped by -story. See the [executable test contract](../../../../packages/agentic/storybook-desktop/README.md#executable-tests-inside-stories). - -The components package's `test:stories` project checks every test-bearing story -without emitting it into the component library. Add new test-bearing files to -`tsconfig.stories.json`; the representative runner contract checks this coverage. -Shared Node-only helpers use `*.wdio.ts` and are dynamically imported by source -extension inside callbacks. They are checked by the story project and excluded -from production emit. - -Native-rendered story helpers use `*.story-helpers.tsx` and are also excluded -from library emit while checked by the story project. Use `StoryStatus` for -counter/modality probes: its named accessible View is observable on macOS -Fabric, where a plain paragraph's `testID` is not exposed as an AX element. - -Focus cases use native `focused`, `checked`, and `selected` properties together -with activation and event-order probes. Cover stale key-up, repeats, disabled -stops, same-target modality, nested themes, focus requests, and selection-before- -focus. Use `try/finally` to release held WebDriver actions. Never replace native -state checks with rendered text alone or a default value for an unsupported -property. Desktop support does not imply identical pointer focus or activation -timing. - -### Desktop focus test scope - -Keep shared requirements executable on Windows Fabric, Office Win32, and macOS. -`requireDesktopFocus` checks native capabilities, not an operating-system -allowlist. A missing capability skips the case with a reason; a platform -difference usually changes one assertion rather than skipping the entire case. - -| Behavior | Scope and expected result | -| -------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| Exactly-once activation, release cleanup, disabled eligibility, focus requests, self-focus, and scene modality | All three desktop endpoints. Assert native focus/state and action counts; include overlapping keys and blur/disable during a press. | -| Focus automatically follows an ordinary pointer press | Windows/Win32 only. Use `expectWindowsPointerFocus`; it deliberately does nothing on macOS, without skipping the case's other assertions. Do not use it for a shared keyboard/programmatic focus requirement. | -| Return/Space activation phase | macOS activates on keydown; Windows/Win32 activate on the paired keyup. Assert the held-key count per platform, then the shared final count. A later blur cannot undo a macOS action that already occurred. | -| Native Tab/Shift+Tab entry, disabled skipping, and TabList navigation | Shared, with macOS Keyboard navigation enabled for the all-controls Tab lane. Use `focusByTab` from the story's editable entry; do not simulate click-focus on the control under test. | -| Modified navigation | Shared for chords delivered to the app. Shift+Arrow is portable; macOS Control+Arrow can be a system shortcut and must not be required to reach a TabList handler. Preserve Command/Option text editing separately. | -| FocusZone navigation | Shared native ownership, platform-specific destinations: Windows/Win32 default to linear traversal unless `use2DNavigation` is enabled; AppKit is geometric. Its macOS scene explicitly requests focus on click, unlike ordinary Pressables. | - -Prefer a narrowly named platform assertion helper when several stories need the -same exception. Do not introduce a generic conditional-expectation layer merely -to hide branching, or make an entire test pass without executing any assertion. -Keep injected `platform` checks inside the extracted callback or its dynamically -imported `*.wdio.ts` helpers; never infer the target from the Node host OS. - -On macOS, record the Keyboard navigation setting as part of the run. With that -setting off, AppKit can legitimately skip buttons and return to an editable -field. Interactive development and CI Macs should have this preference enabled; -the shared CI setup enables and verifies it before app startup. Keep input -authority and locked/noninteractive desktop handling separate from this -preference. Do not silently change machine preferences inside a test or -reinterpret the setting as a missing driver feature. Follow the machine owner's -preference, restore changes authorized as temporary, and test the disabled- -setting policy separately when claiming coverage of both modes. - -Modality text and focus counters do not establish ring visibility. Use separate -resolved-style coverage without incidental scene rerenders, plus native visual -evidence for renderer appearance. Likewise, a checked/selected state read after -a physical click does not exercise UIA/AX action dispatch. - -Report passed, failed, and skipped counts together. A zero-exit smoke traversal -with platform-skipped focus cases is not cross-platform focus qualification. - -The pinned RNmacOS 0.81.9 Fabric View does not project -`accessibilityState.disabled`/`selected` to AXEnabled/AXSelected. Those native -announcement assertions are isolated in explicitly skipped cases; shared -focus, activation, and selection-callback cases still run. Do not substitute -rendered state for an AX result or describe these skips as intended macOS -semantics. Track the native projection gap and revisit the cases when the -renderer is upgraded. An already-disabled AppKit responder may also remain -first responder until focus is moved; rejected-request tests start from a -separate native focus owner rather than assuming disabling itself moved focus. - -Button uses focused appearance, size, shape, icon, selection, disabled, and constrained-content stories. Icon uses a -source and size overview plus focused font, image, SVG, size, color, and accessibility stories. - -Use module-scoped React Native `StyleSheet` objects for story layout, captions, and scenario constraints. Do not restyle a -higher-order component to manufacture a variant. An unstyled primitive may include clearly demonstration-only rendering -so its contract is visible. - -Keep stories accessible. Icon-only controls need action-oriented labels. Standalone informative icons need descriptive -labels. Decorative icon slots inside a labeled control should be inaccessible. - -## Validation - -Use declared workspace scripts: - -```sh -yarn workspace @fluentui-react-native/components format -yarn workspace @fluentui-react-native/components lint -yarn workspace @fluentui-react-native/components build -yarn workspace @fluentui-react-native/components test -yarn workspace @fluentui-react-native/agentic-components-storybook storybook bundle --macos -yarn workspace @fluentui-react-native/agentic-components-storybook storybook bundle --windows -``` - -Run the smallest affected package test while iterating. Run the full package sequence before completion. Run the root -`yarn build` when public types, manifests, or project references change. - -Run repository/package tests before the owned native Storybook lifecycle, not -concurrently with it: some CLI fixtures still write into the app's generated -state directory. A lease/nonce mismatch is an infrastructure failure, not a -focus-test result; never bypass ownership or authenticated readiness to proceed. - -A successful bundle proves story discovery and compilation only. For visual changes, inspect the running target-platform -story across hover, pressed, disabled, optional-slot, and constrained-content scenarios. - -Alignment regressions must distinguish the outer frame from the inner text. LayoutStableText's Overview includes an -executable desktop check that the smaller visible Text has a smaller measured height than its reserve and that their -vertical centers agree within one layout pixel. Checking only their centers would let the old stretched-Text bug pass. -Its measurement fixture exposes an explicit group around the primitive and an accessible visible Text rather than -querying the primitive's intentionally inaccessible layout wrapper. macOS Fabric does not expose paragraph test IDs to -AX, so that endpoint reads the Text's actual `onLayout` measurements from a named `StoryStatus` View instead. -Pair geometry checks with native screenshots for glyph ink and caret placement; native element bounds alone do not -prove optical alignment. Never record an unavailable native check as passed. - -Adding a new story file changes Metro's `require.context` catalog and may require restarting Metro and the native app -before the running Storybook index includes it. +This compatibility path remains so existing links continue to resolve. Do not add new links to it. diff --git a/.github/skills/agentic-component-authoring/references/types-and-slots.md b/.github/skills/agentic-component-authoring/references/types-and-slots.md index 9b80c2b30b..33b7aa045a 100644 --- a/.github/skills/agentic-component-authoring/references/types-and-slots.md +++ b/.github/skills/agentic-component-authoring/references/types-and-slots.md @@ -1,107 +1,12 @@ -# Types and slots +--- +id: compat.agentic-component-authoring.types-and-slots +kind: compatibility-reference +owner_paths: + - packages/agentic/components +--- -Use this reference when defining or changing a component's public API, slots, resolved state, native prop exposure, or -package exports. The canonical higher-order example is -[`button.types.ts`](../../../../packages/agentic/components/src/components/button/button.types.ts). +# Moved: Types and slots -## Separate the public contract from render-only structure +This reference now lives at [references/agents/components/agentic/types-and-slots.md](../../../../references/agents/components/agentic/types-and-slots.md). -Define public slots in `.types.ts`: - -- Use `Slot` for the required root. -- Use `OptionalSlot` for content, icons, indicators, or other optional elements. -- Document what causes an optional slot to render and whether shorthand children are accepted. -- Keep the slot surface small. A slot is public customization API, not merely an element in the render tree. - -Button exposes `root`, `content`, `icon`, and `selectedIcon`. Its `contentContainer` and `contentHidden` slots support the -selected-label layout but remain in the private `ButtonStateSlots` type. Follow that split whenever measuring, overlay, -ghost, or structural slots are implementation details. - -## Model variants explicitly - -Use named string unions for finite axes: - -```ts -export type ComponentAppearance = 'primary' | 'secondary'; -export type ComponentSize = 'small' | 'medium' | 'large'; -``` - -Collect state-bearing public props in `StateProps`. Optional public values become required in resolved state -after the state hook applies defaults. Preserve a meaningful distinction between omitted and false values when omission -enables behavior; Button uses `selected !== undefined` to distinguish an ordinary button from a toggle button. - -## Expose native props deliberately - -Do not blindly inherit every root prop when the component owns part of the native contract. - -1. Start with `PropsWithRefOf` so the native root's ref is part of the public contract. -2. Omit props the component controls, such as `children` or token-derived `style`. -3. Reintroduce a narrowed form only when consumers need it. - -Button uses `OwnedRootProps>`. This prevents native children from bypassing slot order, -preserves the native style type, and retains `ref`. - -Compose the final public props with: - -```ts -export type MyComponentProps = MyComponentStateProps & ComponentProps; -``` - -This package targets React 19.1.4 or newer, where function components receive `ref` as a prop. Let the state hook carry -that prop to the declared root slot; do not wrap the component in `forwardRef`. A top-level component ref always targets -the declared root. Consumers that need an inner public slot use that slot's own `ref`. - -When a component needs an internal root ref as well, keep both refs and pass one through the resolved slot and the other -when rendering the slot so Framework Base composes them. Never overwrite the consumer ref. If a primitive can render -unrelated native instance types, omit a top-level ref unless it can define one sound, stable imperative contract. - -For focus targets, attachment generations, callback cleanup, and view-command -boundaries, follow [common focus authoring](focus.md#modern-refs-and-slot-composition). -Modern ref props do not imply that a structural root is the keyboard focus target -or that a native focus command has completed. - -## Make resolved state complete - -Build state from: - -- `ComponentState` -- `Required` for defaulted axes -- theme state when the component reads tokens -- interaction state when the root is interactive -- derived booleans or values needed by styles and rendering -- preserved user values that must be applied at a later stage - -Button state includes `ThemeState`, `PressableState`, `iconOnly`, `isToggleButton`, and `userStyle`. Avoid carrying raw -props that no later stage uses. - -## Primitive contracts - -Primitives should define the smallest slot-compatible acceptance contract. For mutually exclusive sources, use a union -with `never` on incompatible fields, as -[`IconProps`](../../../../packages/agentic/components/src/primitives/icon/icon.types.ts) does for image, font, and SVG -sources. Verify the primitive remains assignable to `SlotProp` with a committed type test. - -## Exports - -Export each component and its composition pipeline explicitly from -[`src/index.ts`](../../../../packages/agentic/components/src/index.ts): - -- the component and its public props, slots, variants, and resolved state type -- the state hook as `use_unstable` -- the style-application hook as `useStyles_unstable` -- the render helper as `render_unstable` - -Name the source file `useStyles.ts` and the implementation `useStyles_unstable` so the local and -public composition APIs match without aliases. Never use wildcard exports. Do not export private state-only slots or -internal style definitions. - -## Review checklist - -- Every public slot is intentional and documented. -- Render-only slots are private to resolved state. -- Variant unions match the spec exactly. -- Omitted and false values retain distinct semantics where required. -- Root native props cannot bypass owned children or style ordering. -- Stable native roots expose a correctly typed `ref`, and the state/render pipeline does not replace it. -- Resolved state contains every defaulted and derived value needed downstream. -- Public component, state, style-application, render, and resolved-state exports are explicit and tree-shakeable. +This compatibility path remains so existing links continue to resolve. Do not add new links to it. diff --git a/.github/skills/agentic-component-authoring/references/windows-fabric-native-components.md b/.github/skills/agentic-component-authoring/references/windows-fabric-native-components.md index 7e9ae6f4be..e0cea51dd9 100644 --- a/.github/skills/agentic-component-authoring/references/windows-fabric-native-components.md +++ b/.github/skills/agentic-component-authoring/references/windows-fabric-native-components.md @@ -1,266 +1,12 @@ -# Windows Fabric native components +--- +id: compat.agentic-component-authoring.windows-fabric-native-components +kind: compatibility-reference +owner_paths: + - packages/agentic/native-core +--- -Use this reference when an agentic component needs a native React Native Windows -view. It targets the repository's React Native Windows 0.81 line. Verify APIs -against the installed `react-native-windows` package before adopting guidance -from a newer RNW branch. +# Moved: Windows Fabric native components -For behavior, native target lifetime, and modern React refs, read -[common focus authoring](focus.md) and [Windows/Win32 focus authoring](focus-windows.md). -Shipped V1 Win32 behavior is also the model for Fabric, but Office Win32 Paper -is not the UWP XAML Paper implementation described in the compatibility table -below. Do not transfer its native registration or view-manager assumptions. +This reference now lives at [references/agents/native/windows-fabric.md](../../../../references/agents/native/windows-fabric.md). -## Decide whether native code is required - -Prefer a JavaScript component, slots, and React Native primitives when they can -meet the contract. Use a Windows Fabric component when the implementation needs -a Windows-only visual, window, input surface, native API, or performance -boundary that React Native does not expose. - -Establish these facts before editing: - -- the installed RNW version; -- whether the consuming host uses the New Architecture; -- whether the package must also retain a Paper implementation; -- whether the surface is a view component, TurboModule, or both; -- the canonical Windows, Win32, or macOS behavior to preserve. - -RNW Fabric components use C++/WinRT and Windows App SDK Composition visuals. -Do not copy UWP XAML `IViewManager` patterns into the Fabric branch. - -## TypeScript native-component specification - -Name the schema `NativeComponent.ts` and keep the component name -identical in TypeScript, generated code, registration, and the JavaScript -wrapper. - -```ts -import type { DirectEventHandler, WithDefault } from 'react-native/Libraries/Types/CodegenTypes'; -import codegenNativeCommands from 'react-native/Libraries/Utilities/codegenNativeCommands'; -import codegenNativeComponent from 'react-native/Libraries/Utilities/codegenNativeComponent'; -import type { ViewProps } from 'react-native'; - -type ValueChangedEvent = { - value: boolean; -}; - -export interface NativeProps extends ViewProps { - enabled?: WithDefault; - onValueChanged?: DirectEventHandler; -} - -export interface NativeCommands { - setValue(viewRef: React.ElementRef>, value: boolean): void; -} - -export const Commands = codegenNativeCommands({ - supportedCommands: ['setValue'], -}); - -export default codegenNativeComponent('ExampleNativeView'); -``` - -Use React Native codegen types for native values. Extend `ViewProps` for visual -components. Keep event payloads and commands typed and minimal. - -## Windows codegen configuration - -Verify the owning package's `codegenConfig`: - -```json -{ - "codegenConfig": { - "name": "ExampleSpec", - "type": "components", - "jsSrcsDir": "src", - "includesGeneratedCode": true, - "windows": { - "namespace": "ExampleCodegen", - "generators": ["componentsWindows"], - "outputDirectory": "windows/Example/codegen", - "separateDataTypes": true - } - } -} -``` - -Use `"all"` and include `"modulesWindows"` when the package contains both -TurboModules and components. Run the package's declared codegen command. -Never edit generated props, event-emitter, registration, or `.g.h` files. -Persist corrections in the TypeScript schema or codegen configuration. - -### `includesGeneratedCode` is not Windows-scoped - -`includesGeneratedCode` is read by React Native core's codegen executor, not by -`react-native-windows`. It declares that the package ships generated code for -**every** platform, so React Native stops generating Apple and Android -artifacts for that package. - -Set it only on a Windows-only package. On a package that also builds for Apple -or Android, it silently removes the generated -`react/renderer/components/Spec/*` headers those platforms compile -against, and the failure surfaces far away as a missing pod library at the app -link step rather than as a codegen error. - -Renaming the component in `codegenNativeComponent` also renames the generated -C++ types, for example `Callout` produces `CalloutProps` and -`RCTCalloutViewProtocol` while `RCTCallout` produces `RCTCalloutProps` and -`RCTRCTCalloutViewProtocol`. Update every platform's component view in the same -change, and do not redeclare the generated component name, shadow node, or -component descriptor by hand; import them from the generated headers so a -rename cannot leave a stale duplicate behind. - -## C++/WinRT component view - -Third-party Fabric components normally derive from the generated CRTP base: - -```cpp -#ifdef RNW_NEW_ARCH -#include "codegen/react/components/ExampleSpec/ExampleNativeView.g.h" - -struct ExampleNativeView - : winrt::implements, - ExampleCodegen::BaseExampleNativeView { - winrt::Microsoft::UI::Composition::Visual CreateVisual( - winrt::Microsoft::ReactNative::ComponentView const &view) noexcept override; - - void Initialize( - winrt::Microsoft::ReactNative::ComponentView const &view) noexcept override; - - private: - winrt::Microsoft::UI::Composition::SpriteVisual m_visual{nullptr}; -}; -#endif -``` - -Implement only the hooks the component needs. Generated registration omits -unused optional callbacks. - -- Create the root Composition visual in `CreateVisual`. -- Subscribe with `winrt::auto_revoke` and store the revoker. -- Capture `get_weak()` in event and asynchronous callbacks. -- Treat view lifecycle callbacks as UI-thread work. -- Reset reusable native state when the view is recycled. -- Keep native boundary methods `noexcept`, matching RNW patterns. - -The generated CRTP base is not the same as subclassing an RNW built-in -`ComponentView`. Do not call a nonexistent `Super` method. When subclassing a -built-in view, preserve its documented base-method ordering. - -## Props, events, commands, and state - -- Compare old and new props before scheduling visual work. -- Mark affected visuals dirty in prop updates and batch expensive mutation in - the final-update hook. -- Store and null-check the generated event emitter before emitting typed - payloads. -- When a built-in base handles commands, call it first and respect `Handled`. -- Use renderer state only when the renderer must own or measure it. Prefer - props and events for ordinary controlled interaction state. - -The RNW `SwitchComponentView` is the canonical source for interactive props, -events, commands, pointer input, keyboard input, focus, and UI Automation. - -## Layout and Composition visuals - -React layout metrics are in device-independent units while Composition visual -sizes and offsets use physical pixels. Multiply positions and dimensions by -`PointScaleFactor` before assigning them to visuals or geometries. - -Mount and unmount child visuals in renderer order. Backgrounds, borders, -shadows, transforms, and clipping are normally supplied through -`ComponentViewFeatures`. Disable a verified feature only when the component -fully replaces it. - -Custom clipping may require disabling native border handling and explicitly -updating size and offset from layout metrics. Check the target RNW source -because the feature flags are not exhaustively documented as a public API. - -## Theme, input, focus, and accessibility - -Native components must support: - -- light, dark, and high-contrast updates; -- platform or Fluent brushes instead of fixed native colors where appropriate; -- pointer and keyboard input; -- focus acquisition and focus visuals; -- React Native accessibility props; -- a correct UI Automation control type and patterns; -- UIA property-change notifications for native state changes. - -Use Accessibility Insights for Windows or Inspect.exe during initial -development. Add stable `testID` values to the Storybook validation story so -automated checks can locate the component through UIA. - -## Registration, projects, and autolinking - -The complete persistence chain is: - -1. Add hand-authored `.h` and `.cpp` files to the library `.vcxproj`. -2. Add `.vcxproj.filters` entries only for Visual Studio organization. -3. Leave generated code under the codegen build integration. -4. Include the component implementation from `ReactPackageProvider.cpp`. -5. Call the generated `RegisterNativeComponent` helper. -6. Preserve attributed TurboModule registration when the package has modules. -7. Regenerate or autolink through the consuming app's declared Windows script. -8. Treat generated autolink files and app solutions as disposable output. - -A file on disk but absent from `.vcxproj` is not compiled. - -When Fabric and Paper use different component names, generate the Fabric name -directly and set `paperComponentName` in `codegenNativeComponent`. Callout uses -the Fabric name `Callout` and the Paper fallback `RCTCallout`; Windows can -therefore use the generated `RegisterCalloutNativeComponent` helper without -copying or modifying generated registration code. - -## Paper compatibility - -When a package intentionally supports both architectures, guard Fabric-only -headers and implementation with `RNW_NEW_ARCH` and retain the Paper -`IViewManager` branch separately. Do not share UWP XAML types with WinAppSDK -Composition code. - -| Paper | Fabric | -| ----------------------------------------- | ------------------------------------------------------ | -| `IViewManager::CreateView` returning XAML | Generated component base creating a Composition visual | -| Native property map | Codegen props | -| `AddViewManager` | Generated Fabric registration helper | -| XAML child management | Mount and unmount component-view hooks | -| UWP brushes and geometry | Windows App SDK Composition brushes and geometry | - -Test architecture branches in separate compatible hosts. For a -New-Architecture-only package, remove obsolete Paper code rather than adding an -untested fallback. - -## Validation - -Run the smallest declared command at each layer: - -1. package format and lint; -2. TypeScript build for the wrapper and schema; -3. Windows codegen check; -4. consuming-app generation or autolink check; -5. clean native package and app build; -6. Storybook Windows bundle; -7. deployed Storybook smoke automation; -8. interaction and native event assertion; -9. UIA assertion; -10. screenshot through the agent host when visual evidence is required; -11. offline Release smoke after packaging or native dependency changes; -12. root build after public type, manifest, or project-reference changes. - -A successful JavaScript bundle does not validate native code. - -## Canonical sources - -- [RNW native platform components](https://microsoft.github.io/react-native-windows/docs/native-platform-components) -- [RNW New Architecture](https://microsoft.github.io/react-native-windows/docs/new-architecture) -- [RNW Windows codegen CLI](https://microsoft.github.io/react-native-windows/docs/codegen-windows-cli) -- [RNW native library autolinking](https://microsoft.github.io/react-native-windows/docs/native-platform-using) -- [RNW NativeModuleSample](https://github.com/microsoft/react-native-windows-samples/tree/main/samples/NativeModuleSample/cpp-lib) -- [RNW built-in Composition views](https://github.com/microsoft/react-native-windows/tree/main/vnext/Microsoft.ReactNative/Fabric/Composition) - -Use the installed dependency or matching release branch first. Treat repository -head as discovery material until each API is verified against the pinned RNW -version. +This compatibility path remains so existing links continue to resolve. Do not add new links to it. diff --git a/.github/skills/agentic-component-contract-authoring/SKILL.md b/.github/skills/agentic-component-contract-authoring/SKILL.md index 3a548b239b..6ff21cd9ed 100644 --- a/.github/skills/agentic-component-contract-authoring/SKILL.md +++ b/.github/skills/agentic-component-contract-authoring/SKILL.md @@ -1,6 +1,6 @@ --- name: agentic-component-contract-authoring -description: Create or revise reviewed React Native component contracts in packages/agentic/components from Flex/X3, Fluent v9, HTML/CSS, images, local platform foundations, or composite evidence. +description: Create or revise reviewed React Native component contracts in packages/agentic/components from Flex/X3, Fluent v9, HTML/CSS, images, local platform foundations, or composite evidence. Use for contract-only, feasibility, or plan-only work and before implementing an unreviewed contract. license: MIT --- @@ -31,14 +31,14 @@ draft, or accompanied by new source evidence. Load every adapter represented by the supplied evidence, but no others. -| Evidence | Adapter | -| --------------------------------------------- | -------------------------------------------------------------- | -| Pinned Flex component skill and X3 lineage | [Flex and X3](references/sources/flex-x3.md) | -| Fluent UI v9 source or specification | [Fluent v9](references/sources/fluent-v9.md) | -| HTML, CSS, computed styles, or browser output | [HTML and CSS](references/sources/html-css.md) | -| Images, screenshots, or design renders | [Visual reference](references/sources/visual-reference.md) | -| Public native API plus repository precedent | [Local foundation](references/sources/local-foundation.md) | -| More than one of the preceding source sets | [Composite evidence](references/sources/composite-evidence.md) | +| Evidence | Adapter | +| --------------------------------------------- | ---------------------------------------------------------------------------------------------------------- | +| Pinned Flex component skill and X3 lineage | [Flex and X3](../../../references/agents/components/agentic/contract-sources/flex-x3.md) | +| Fluent UI v9 source or specification | [Fluent v9](../../../references/agents/components/agentic/contract-sources/fluent-v9.md) | +| HTML, CSS, computed styles, or browser output | [HTML and CSS](../../../references/agents/components/agentic/contract-sources/html-css.md) | +| Images, screenshots, or design renders | [Visual reference](../../../references/agents/components/agentic/contract-sources/visual-reference.md) | +| Public native API plus repository precedent | [Local foundation](../../../references/agents/components/agentic/contract-sources/local-foundation.md) | +| More than one of the preceding source sets | [Composite evidence](../../../references/agents/components/agentic/contract-sources/composite-evidence.md) | ## Workflow diff --git a/.github/skills/agentic-component-contract-authoring/references/sources/composite-evidence.md b/.github/skills/agentic-component-contract-authoring/references/sources/composite-evidence.md index 984e3a534a..eeafe8789d 100644 --- a/.github/skills/agentic-component-contract-authoring/references/sources/composite-evidence.md +++ b/.github/skills/agentic-component-contract-authoring/references/sources/composite-evidence.md @@ -1,36 +1,12 @@ -# Composite evidence adapter +--- +id: compat.agentic-component-contract-authoring.composite-evidence +kind: compatibility-reference +owner_paths: + - packages/agentic/components +--- -Use multiple `sources[]` entries when no single source can establish the full -React Native contract. Composite evidence is a resolution method, not a source -kind. +# Moved: Composite evidence adapter -## Resolve authority per requirement +This reference now lives at [references/agents/components/agentic/contract-sources/composite-evidence.md](../../../../../references/agents/components/agentic/contract-sources/composite-evidence.md). -For every requirement, list the sorted source IDs that govern or support it. -Do not assign one global precedence order to an entire component. A common -division is: - -| Requirement area | Likely authority | -| ------------------------ | ---------------------------------------------------- | -| design intent and tokens | normative design source plus token reference | -| public API and state | product specification or reviewed behavior reference | -| native semantics | React Native/platform contract | -| visual fidelity | normative design source plus visual evidence | -| compatibility | existing FURN implementation | - -One source may be authoritative for one requirement and merely corroborating -for another. If sources disagree, record the conflicting claims, chosen -resolution, rationale, and divergence or follow-up issue. If evidence is -insufficient, keep the behavior unknown or deferred. - -## Keep the contract reviewable - -- Give each source a stable ID and use the source-specific adapter schema. -- Keep `sources[]` sorted and unique. -- Require every requirement to identify at least one source when more than one - source is present. -- Do not duplicate one artifact across source entries to manufacture - corroboration. -- Re-review only the requirements affected by changed evidence, but keep the - document-level conformance state at `review-required` until the combined - contract is coherent again. +This compatibility path remains so existing links continue to resolve. Do not add new links to it. diff --git a/.github/skills/agentic-component-contract-authoring/references/sources/flex-x3.md b/.github/skills/agentic-component-contract-authoring/references/sources/flex-x3.md index 5d44e024b8..f815b688ea 100644 --- a/.github/skills/agentic-component-contract-authoring/references/sources/flex-x3.md +++ b/.github/skills/agentic-component-contract-authoring/references/sources/flex-x3.md @@ -1,46 +1,12 @@ -# Flex and X3 source adapter +--- +id: compat.agentic-component-contract-authoring.flex-x3 +kind: compatibility-reference +owner_paths: + - packages/agentic/components +--- -Use this adapter for a component in the pinned Flex catalog. Flex skills are -authoring inputs; the immutable X3 lineage identifies their original design -source, and the reviewed local contract defines FURN behavior. +# Moved: Flex and X3 source adapter -## Resolve the source +This reference now lives at [references/agents/components/agentic/contract-sources/flex-x3.md](../../../../../references/agents/components/agentic/contract-sources/flex-x3.md). -Start Agency with the repository's `flex-authoring` profile and invoke -`flex-components:`. Confirm that the profile and -`packages/agentic/components/spec-source-lock.json` identify the same release. -Read shared and usage material plus the relevant platform companions. Do not -combine files from different releases or use one platform as an implicit -fallback for another. - -Generate or refresh the `flex-skill` entry with: - -```sh -yarn workspace @fluentui-react-native/components report:spec-source-drift --write --update-sources --component -``` - -The entry records `authority: normative`, the skill ID, source-lock identity, -available and consulted surfaces, immutable Marketplace/X3 file identities, -and release differences. Availability is generated; consultation is a -reviewer's assertion and remains empty after a source identity changes. - -## Adapt to React Native - -Classify source guidance as adopted, adapted, intentionally divergent, not -applicable, or deferred. Consult the Flex token map and the closest Win32, -macOS, or Windows FURN implementation for compatibility; do not use iOS as the -cross-platform canonical reference unless the work targets iOS. - -Translate web concepts deliberately: - -| Flex evidence | React Native contract | -| ------------------------------------ | ---------------------------------------------------------------------------------------- | -| ARIA attributes or native elements | `accessibilityRole`, `accessibilityState`, labels, and UIA/AX expectations | -| CSS pseudo-classes | explicit interaction state and precedence | -| browser focus selectors and outlines | shared root-modality policy and optional `FocusRing`, persistent only on the custom path | -| CSS pixel or layout rules | React Native units, minimum targets, and platform behavior | -| unsupported browser capability | explicit divergence, dependency, or blocker | - -Do not copy Flex prose or token tables into the public package. A mutable -Marketplace or X3 HEAD change is candidate drift and does not invalidate a -contract ratified against the pinned release. +This compatibility path remains so existing links continue to resolve. Do not add new links to it. diff --git a/.github/skills/agentic-component-contract-authoring/references/sources/fluent-v9.md b/.github/skills/agentic-component-contract-authoring/references/sources/fluent-v9.md index 763e73abf5..30a7aa1684 100644 --- a/.github/skills/agentic-component-contract-authoring/references/sources/fluent-v9.md +++ b/.github/skills/agentic-component-contract-authoring/references/sources/fluent-v9.md @@ -1,30 +1,12 @@ -# Fluent v9 source adapter +--- +id: compat.agentic-component-contract-authoring.fluent-v9 +kind: compatibility-reference +owner_paths: + - packages/agentic/components +--- -Use Fluent UI v9 as API, behavior, and design precedent, not as a DOM contract -for React Native. +# Moved: Fluent v9 source adapter -## Record provenance +This reference now lives at [references/agents/components/agentic/contract-sources/fluent-v9.md](../../../../../references/agents/components/agentic/contract-sources/fluent-v9.md). -Pin the exact repository and full commit SHA. Record a `git-files` source with -sorted source/spec paths, semantic roles, and SHA-256 digests. Use an authority -such as `behavior-reference`, `compatibility-reference`, `token-reference`, or -`implementation-evidence` that describes what the selected files can govern. -Do not cite a mutable branch or package version without its immutable commit. - -## Interpret the source - -Inspect public types, state hooks, render structure, styles, tests, stories, and -published specifications as separate evidence surfaces. Distinguish intended -public behavior from incidental implementation details. - -- Preserve portable API names and state ownership when they fit native usage. -- Translate DOM elements and ARIA to React Native roles, states, labels, and - platform accessibility expectations. -- Translate CSS inheritance, selectors, and pseudo-classes into explicit - tokens, state precedence, and native layout. -- Treat browser focus, keyboard, pointer, and hover behavior as evidence to - adapt, not behavior to copy mechanically. -- Record unsupported or intentionally different behavior as a divergence. - -When Fluent v9 is combined with another design source, assign its source ID -only to the requirements for which it is authoritative or informative. +This compatibility path remains so existing links continue to resolve. Do not add new links to it. diff --git a/.github/skills/agentic-component-contract-authoring/references/sources/html-css.md b/.github/skills/agentic-component-contract-authoring/references/sources/html-css.md index 264c3cdda7..55b8f8c367 100644 --- a/.github/skills/agentic-component-contract-authoring/references/sources/html-css.md +++ b/.github/skills/agentic-component-contract-authoring/references/sources/html-css.md @@ -1,35 +1,12 @@ -# HTML and CSS source adapter +--- +id: compat.agentic-component-contract-authoring.html-css +kind: compatibility-reference +owner_paths: + - packages/agentic/components +--- -Use HTML/CSS evidence to recover observable anatomy, content flow, layout, -computed values, and visual state differences. It does not by itself establish -native semantics, controlled-state ownership, event contracts, or platform -accessibility. +# Moved: HTML and CSS source adapter -## Capture reproducible evidence +This reference now lives at [references/agents/components/agentic/contract-sources/html-css.md](../../../../../references/agents/components/agentic/contract-sources/html-css.md). -Record an `html-css` source with its origin and sorted immutable artifacts. -Supported artifacts are HTML, CSS, computed-style captures, accessibility-tree -captures, and screenshots. Each artifact needs a stable ID, kind, location, -and SHA-256 digest. Store only evidence that may be redistributed; otherwise -use an HTTPS identity and retain restricted material outside the public -repository. - -Capture the relevant matrix when available: - -- variants, sizes, appearances, and interaction states; -- viewport, zoom or scale, color scheme, direction, and locale; -- inherited versus explicitly assigned typography and color; -- computed dimensions, spacing, wrapping, truncation, and overflow; -- browser accessibility output separately from visual output. - -## Adapt the evidence - -Map element anatomy to typed React Native slots only when consumers need the -slot. Convert CSS inheritance into explicit theme/token defaults at the owning -native boundary. Translate pseudo-classes to component state and precedence. -Treat layout values as observations, then choose the appropriate React Native -layout and minimum-target behavior. - -Mark semantics, keyboard behavior, motion, state ownership, and inaccessible -states as unknown unless another source establishes them. Never infer an API -solely from class names, data attributes, or a single rendered DOM snapshot. +This compatibility path remains so existing links continue to resolve. Do not add new links to it. diff --git a/.github/skills/agentic-component-contract-authoring/references/sources/local-foundation.md b/.github/skills/agentic-component-contract-authoring/references/sources/local-foundation.md index 24c4cef1a2..f00baf49c2 100644 --- a/.github/skills/agentic-component-contract-authoring/references/sources/local-foundation.md +++ b/.github/skills/agentic-component-contract-authoring/references/sources/local-foundation.md @@ -1,22 +1,12 @@ -# Local foundation source adapter +--- +id: compat.agentic-component-contract-authoring.local-foundation +kind: compatibility-reference +owner_paths: + - packages/agentic/components +--- -Use `local-foundation` only for a theme-aware foundational component that has -no entry in the locked Flex component catalog. This is not a bypass for missing -Flex access or an alternative way to author a catalog component. +# Moved: Local foundation source adapter -The source entry is the contract's sole source and uses -`authority: normative`. Its sorted references identify the public native -platform contract and repository precedents used during review. Give each -reference its own authority, such as `platform-contract`, -`compatibility-reference`, or `token-reference`. +This reference now lives at [references/agents/components/agentic/contract-sources/local-foundation.md](../../../../../references/agents/components/agentic/contract-sources/local-foundation.md). -Inspect the current React Native API for the package's supported version, -relevant native behavior, existing FURN components, and token mappings. Define -the smallest theme-aware primitive contract needed by other components. Keep -web inheritance or browser behavior out of the contract unless React Native -provides an equivalent. - -Local-foundation contracts still require lifecycle, review, divergence, -requirement, implementation, test, story, and platform evidence. Flex release -and candidate drift are `not-applicable`. The checker rejects this source kind -if the locked Flex catalog contains the component. +This compatibility path remains so existing links continue to resolve. Do not add new links to it. diff --git a/.github/skills/agentic-component-contract-authoring/references/sources/visual-reference.md b/.github/skills/agentic-component-contract-authoring/references/sources/visual-reference.md index 8e92ee7084..dbbe1f6fe9 100644 --- a/.github/skills/agentic-component-contract-authoring/references/sources/visual-reference.md +++ b/.github/skills/agentic-component-contract-authoring/references/sources/visual-reference.md @@ -1,35 +1,12 @@ -# Visual reference source adapter +--- +id: compat.agentic-component-contract-authoring.visual-reference +kind: compatibility-reference +owner_paths: + - packages/agentic/components +--- -Use screenshots, design renders, and other images as visual evidence only. -They can establish observable anatomy, geometry, typography, color, -iconography, and differences among captured states. They cannot prove invisible -semantics, accessibility, events, keyboard behavior, motion, or controlled -state ownership. +# Moved: Visual reference source adapter -## Record the evidence +This reference now lives at [references/agents/components/agentic/contract-sources/visual-reference.md](../../../../../references/agents/components/agentic/contract-sources/visual-reference.md). -Record a `visual-reference` source with `authority: visual-evidence`. Give each -artifact a stable ID, location, and SHA-256 digest. Add appearance, state, -platform, scale, viewport, and locale metadata when known. Repository-relative -artifacts are digest-verified by the contract checker; do not commit images -without redistribution permission. - -Inventory the capture matrix before interpreting it. A missing state is -unknown, not equivalent to the nearest visible state. Note cropping, -compression, scaling, overlays, device chrome, and any dimensions that prevent -reliable measurement. - -## Derive a contract conservatively - -- Describe anatomy and spatial relationships before choosing implementation - slots or layout primitives. -- Map visible values to the nearest established FURN tokens; record genuine - token gaps rather than inventing exact literals from noisy pixels. -- Require non-visual sources for public APIs, semantics, interactions, - accessibility, motion, and platform behavior. -- Express uncertain conclusions as open questions, deferred divergences, or - explicitly scoped requirements. - -When visual evidence conflicts with normative design or platform evidence, -record the conflict and resolve authority for the affected requirement rather -than silently selecting the image. +This compatibility path remains so existing links continue to resolve. Do not add new links to it. diff --git a/.github/skills/agentic-component-optimization/SKILL.md b/.github/skills/agentic-component-optimization/SKILL.md index 8ef5ebf922..54a1a13240 100644 --- a/.github/skills/agentic-component-optimization/SKILL.md +++ b/.github/skills/agentic-component-optimization/SKILL.md @@ -12,21 +12,20 @@ primitive or helper should exist at all. ## Read first -1. Read `packages/agentic/components/AGENTS.md` and `packages/agentic/components/src/AGENTS.md`. -2. Read the relevant component or primitive `AGENTS.md` file and the colocated `SPEC.md` files for the affected area. +1. Read the [package instructions](../../../packages/agentic/components/AGENTS.md) and the + [source instructions](../../../packages/agentic/components/src/AGENTS.md). +2. Read the relevant component or primitive `AGENTS.md` file and the colocated `SPEC.md` or `CONTRACT.md` files for the + affected area. 3. Inspect the canonical implementations: - - `packages/agentic/components/src/components/button` - - `packages/agentic/components/src/primitives/icon` + [`components/button`](../../../packages/agentic/components/src/components/button) and + [`primitives/icon`](../../../packages/agentic/components/src/primitives/icon). ## Audit checklist ### 1. Dependency direction -- Production source may depend only on React / React Native native components, `@fluentui-react-native/design`, and - `@fluentui-react-native/framework-base`. -- Generalizable non-styling hooks belong in `framework-base/src/hooks`. -- Styling helpers belong in `agentic/design/src/styling`. -- Component-library-specific non-public types, constants, and helpers belong in `packages/agentic/components/src/common`. +Apply the [source boundaries](../../../packages/agentic/components/src/AGENTS.md#source-boundaries): production +dependencies, where generalizable hooks and styling helpers belong, and what stays in `src/common`. ### 2. Repetition and ownership diff --git a/.github/skills/agentic-storybook-development/SKILL.md b/.github/skills/agentic-storybook-development/SKILL.md index 42d8ae2dcc..96b68a1abd 100644 --- a/.github/skills/agentic-storybook-development/SKILL.md +++ b/.github/skills/agentic-storybook-development/SKILL.md @@ -12,7 +12,7 @@ Work on the native Storybook application in `apps/storybook`. 1. Read the repository `AGENTS.md`, then the compact [agent map](../../../apps/storybook/agent-map.yaml). Read the - [Storybook instructions](../../../apps/storybook/AGENTS.md) and + [Storybook instructions](../../../apps/storybook/AGENTS.md), including its native diagnostics commands, and [README](../../../apps/storybook/README.md) only as needed for the task. 2. Inspect the Storybook `package.json` and run its declared workspace scripts; do not invent direct runner commands. 3. Use `storybook-agent` to query runtime story IDs, select stories, and sweep renderability. Reproduce the first diff --git a/.github/skills/fix-security-issues/SKILL.md b/.github/skills/fix-security-issues/SKILL.md index 998d322e73..bfb02b0481 100644 --- a/.github/skills/fix-security-issues/SKILL.md +++ b/.github/skills/fix-security-issues/SKILL.md @@ -59,7 +59,7 @@ Calculate the exact cutoff from the session time. Verify publication timestamps for proposed versions and, after lockfile generation, every newly selected npm version in the lockfile diff. -- Do not disable or reduce the age gate to obtain a security fix. +- **R-SEC-001:** Do not disable or reduce the age gate to obtain a security fix. - Do not assume `latest` is eligible. - Check preapproved-package exceptions explicitly; an exception does not prove that a newly selected release meets the requested age policy. @@ -85,8 +85,7 @@ Use this order: focused regression check, and document that version-based scanners may still report the patched version. -Do not add a broad catch, audit exclusion, or warning suppression in place of a -runtime fix. +- **R-SEC-002:** Do not add a broad catch, audit exclusion, or warning suppression in place of a runtime fix. ## Safely use the ignore plugin diff --git a/.github/skills/implement-native-component/SKILL.md b/.github/skills/implement-native-component/SKILL.md new file mode 100644 index 0000000000..3867e81ae5 --- /dev/null +++ b/.github/skills/implement-native-component/SKILL.md @@ -0,0 +1,63 @@ +--- +name: implement-native-component +description: Implement or change native components, TurboModules, native services, codegen specs, or native-backed platform wrappers (Swift, Objective-C, C++/WinRT, podspecs, Windows projects). Also supplies the native overlay checklist for v0, v1, and agentic work. Not for Storybook toolchain failures. +license: MIT +--- + +# Implement native components and modules + +Use this skill directly for native work, or as an overlay when a v0, v1, or agentic change touches a native boundary. +The [native components and modules reference](../../../references/agents/native/components-and-modules.md) defines +ownership and qualification rules (R-NATIVE-001 through R-NATIVE-003). + +## Workflow + +1. Classify the work: + - native kind: view component, TurboModule, or native service; + - target platforms and renderers: macOS Paper or Fabric, React Native Windows Fabric, Win32 host-provided views, + iOS, or Android; + - the JavaScript wrapper's generation and public compatibility, using the + [classification guide](../../../references/agents/components/classification.md); + - native registration names and entrypoints that must not change. +2. Choose ownership. New modern native code belongs in native-core (R-NATIVE-001); maintain existing native wrappers, + such as experimental packages, where they live. Read the relevant sections of the + [native-core README](../../../packages/agentic/native-core/README.md). +3. Load only the platform references the work needs: + - React Native Windows Fabric views: [Windows Fabric](../../../references/agents/native/windows-fabric.md); + - Apple: native-core's native layout and Swift/Objective-C interoperability notes; + - focus targets or native focus visuals: [common focus authoring](../../../references/agents/focus/common.md) and its + platform file; + - accessibility actions: + [framework-base accessibility instructions](../../../packages/framework-base/src/accessibility/AGENTS.md); + - the JavaScript wrapper: the [agentic skill](../agentic-component-authoring/SKILL.md), + [v0 recipe](../../../references/agents/components/v0.md), or [v1 recipe](../../../references/agents/components/v1.md). +4. Implement with the TypeScript specification, codegen configuration, native props, events, commands, and registration + kept consistent. Preserve public APIs and registration names (R-NATIVE-002) and keep fork imports in platform files + (R-REPO-002). +5. Validate the affected boundary with declared scripts (R-VALID-001): package build, test, and lint; native-core + `codegen:windows` when Windows specs change; native builds through the owning app, such as + `yarn storybook build --macos` or `--windows` from `apps/storybook`; and runtime evidence through Storybook smoke or + `wdio` story tests. macOS Storybook commands default to Fabric: add `--paper` to `prep`, `build`, `run`, or `smoke` + for Paper, and rerun `prep` when switching renderers. Run Pod operations only as the + [permissions reference](../../../references/agents/repository/permissions.md) allows. +6. Report each platform and renderer as passed, failed, skipped, not run, or unsupported, with reasons (R-NATIVE-003). + +Storybook application or toolchain failures that are not caused by the native change belong to the +[Storybook skill](../agentic-storybook-development/SKILL.md). + +## Native overlay checklist + +Use this checklist for implementation and review: + +- TypeScript specification, native props, events, and commands agree; generated code was regenerated by its declared + command rather than edited by hand. +- Registration names, autolinking, and entrypoints are unchanged unless requested; consuming apps depend directly on + native-core. +- Native view lookup and UI mutation run on the platform's UI thread. +- Native resources, observers, and pending work are released on unmount, cancellation, or failure. +- Each export works on every host its entrypoint promises; platform-only APIs use the narrowest entrypoint. +- Podspecs, Windows projects, and package `files` include every new native source. +- Accessibility actions and focus targets are qualified by invoking the native action or target, not by JavaScript + events alone. +- Evidence states which platforms and renderers ran; JavaScript-only results are not native qualification + (R-NATIVE-003). diff --git a/.github/skills/maintain-v0-v1-component/SKILL.md b/.github/skills/maintain-v0-v1-component/SKILL.md new file mode 100644 index 0000000000..a4b5eb38d2 --- /dev/null +++ b/.github/skills/maintain-v0-v1-component/SKILL.md @@ -0,0 +1,37 @@ +--- +name: maintain-v0-v1-component +description: Fix or maintain existing v0 (foundation-compose) or v1 (framework compose/compressible) components, including their platform variants. Classifies the generation first and loads only its recipe. Not for new components, agentic components, read-only reviews, or native-only changes. +license: MIT +--- + +# Maintain v0 and v1 components + +v0 and v1 are maintenance-only (R-V0-001 and R-V1-001). A request for a new component uses the +[agentic authoring skill](../agentic-component-authoring/SKILL.md). A read-only review uses +[review-component](../review-component/SKILL.md). + +## Workflow + +1. Classify every surface the change touches with the + [classification guide](../../../references/agents/components/classification.md). Confirm the generation from + imports and structure, not directory names (R-CLASSIFY-001); Button contains both generations. +2. Load only the matching recipe: [v0](../../../references/agents/components/v0.md) or + [v1](../../../references/agents/components/v1.md). Load both only when the change touches both generations. +3. Add overlays only when the change triggers them: + - native root, registration, codegen, podspec, or native project: the + [native component skill](../implement-native-component/SKILL.md) checklist; + - focus, keyboard activation, or focus visuals: [common focus authoring](../../../references/agents/focus/common.md) + and its platform file; + - token values or theme behavior: [tokens and theming](../../../references/agents/design/tokens-and-theming.md); + - FluentTester pages or E2E specs: [legacy test apps](../../../references/agents/components/legacy-test-apps.md). +4. Read the component's existing tests, snapshots, platform variants, and any `SPEC.md` or `MIGRATION.md` before + editing. +5. Make the smallest change that fixes the problem. Preserve public props, types, exports and aliases, display names, + `componentRef`, token contracts, and native registration names (R-V0-002, R-V0-003, R-V1-002, R-V1-003). +6. Validate with the owning package's declared scripts (R-VALID-001): build, test, and lint, plus the framework + packages named in the recipe when framework semantics change. Review snapshot diffs before accepting them. Report + each check's outcome (R-VALID-002). +7. Add a changeset for published packages (R-REPO-003). + +Stop and confirm scope before migrating between frameworks, creating a component, or changing a public API. Those +are separate tasks, not maintenance. diff --git a/.github/skills/review-component/SKILL.md b/.github/skills/review-component/SKILL.md new file mode 100644 index 0000000000..8dfda49b4e --- /dev/null +++ b/.github/skills/review-component/SKILL.md @@ -0,0 +1,46 @@ +--- +name: review-component +description: Review component code or changes of any generation (v0, v1, agentic, primitive, or native-backed) without editing files. Classifies each changed surface, applies the matching checklists and native overlay, and reports evidence-backed findings with validation status. +license: MIT +--- + +# Review a component change + +- **R-REVIEW-001:** A review is read-only. Do not edit files, update snapshots, commit, or invoke an implementation + workflow unless the user asks for fixes after receiving the review. +- **R-REVIEW-002:** Report only evidence-backed findings. Cite `path:line`, the violated rule ID or contract + requirement ID, and whether the finding was verified (executed or confirmed in source) or inferred. + +## Workflow + +1. Establish the scope: the named files, the working-tree diff, or the branch diff against its base. +2. Classify each changed surface with the + [classification guide](../../../references/agents/components/classification.md): task intent, implementation model, + public compatibility, and native obligations (R-CLASSIFY-001, R-CLASSIFY-002). +3. Load only the checklists for the surfaces present: + + | Surface | Checklist | + | ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | + | v0 | [v0 review checklist](../../../references/agents/components/v0.md#review-checklist) | + | v1 | [v1 review checklist](../../../references/agents/components/v1.md#review-checklist) | + | Agentic higher-order | [Package](../../../packages/agentic/components/AGENTS.md) and [higher-order](../../../packages/agentic/components/src/components/AGENTS.md) invariants; the component's `SPEC.md` and `spec/`; and the review checklists in [types and slots](../../../references/agents/components/agentic/types-and-slots.md#review-checklist), [state and accessibility](../../../references/agents/components/agentic/state-and-accessibility.md#review-checklist), [styles and tokens](../../../references/agents/components/agentic/styles-and-tokens.md#review-checklist), and [rendering](../../../references/agents/components/agentic/rendering.md#review-checklist) for the areas changed | + | Agentic primitive | [Primitive invariants](../../../packages/agentic/components/src/primitives/AGENTS.md), the primitive's `CONTRACT.md`, and [primitive contracts](../../../references/agents/components/agentic/types-and-slots.md#primitive-contracts) | + | Tests and stories | [Tests and stories](../../../references/agents/components/agentic/tests-and-stories.md) for agentic code; [legacy test apps](../../../references/agents/components/legacy-test-apps.md) for v0 and v1 | + | Contract or provenance | [Contract authoring](../agentic-component-contract-authoring/SKILL.md) rules and only the [source adapters](../../../references/agents/README.md#document-index) matching the changed evidence | + | Focus or keyboard | [Required focus evidence](../../../references/agents/focus/common.md#required-evidence-before-shipping) and the affected platform file | + | Native code or roots | [Native overlay checklist](../implement-native-component/SKILL.md#native-overlay-checklist) | + | Framework-base pattern | [Framework-base instructions](../../../packages/framework-base/AGENTS.md) | + +4. Check repository-wide rules: explicit exports (R-REPO-001), platform fork imports (R-REPO-002), changesets + (R-REPO-003), and no copied private source (R-REPO-004). +5. Inspect validation claims. Run non-mutating checks only when they add evidence; never run snapshot-update or fix + modes. Record each check as passed, failed, skipped, not run, or unsupported (R-VALID-002). + +## Output + +1. **Scope and classification:** one line per changed surface. +2. **Findings**, highest severity first. For each: `path:line`, the problem, the rule or requirement ID, verified or + inferred, and a suggested correction. +3. **Validation:** commands run with results, and checks not run with the reason. + +When nothing qualifies, say so and list what was checked. Do not pad the review with style preferences. diff --git a/AGENTS.md b/AGENTS.md index 01420c345b..b903d3357c 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,388 +1,80 @@ # AGENTS.md -This file provides guidance to coding agents (Claude Code and others) when working with code in this repository. - -## Agent permissions - -The shared Copilot CLI defaults are implemented by -[the repository hook](.github/hooks/agent-permissions.json), not by prose instructions alone. -After trusting this repository, start a new CLI session to load the hook. Node.js must be on `PATH`; -no dependency installation is needed for the hook itself. - -- Read and write files inside this repository. -- Run all Yarn commands, including `yarn` and `yarn install`. -- Inspect Git state, stage changes, create new local commits, list branches, and fetch from `origin`. - Do not amend commits, discard other people's changes, or rewrite history without explicit authorization. -- Read GitHub issues, pull requests, workflow definitions, and pipeline runs using `gh`. - REST `gh api` calls to user, repository, and search endpoints are approved only for GET requests, - without request-body/field flags. GraphQL and API mutations require separate approval. -- Inspect the active account with `gh auth status` and `gh api user --jq .login`, and use - `gh auth switch` to select an existing authenticated account when needed. Never print authentication tokens. -- Run `pod install` (including `--repo-update` and `--clean-install`) or named `pod update ...` operations, - optionally through `bundle exec`, from the owning app or its `ios`/`macos` directory. A project-directory - argument must resolve to that app's native directory and existing Podfile. Root-level Pod invocations, - updates of every pod, global cache cleanup, and `pod deintegrate` require separate approval. -- Inspect processes/listeners with constrained `ps` and `lsof` commands in Bash. Probe loopback `/status` and - `/index.json` using `curl -q` (disable user curl configuration), GET/HEAD, and the instance's reported port. - Uploads, redirects, output files, arbitrary endpoints, and process termination are not pre-approved. - -The hook accepts simple Bash/PowerShell commands and `&&` sequences when **every** command is approved. -`cd ./ && ...` is allowed only within the repository (use `./` to avoid Bash `CDPATH`). -In Bash, native-driver variables listed in the hook may prefix Yarn commands, directly for unquoted values -or through `env`, for example `FURN_NATIVE_DRIVER_TEST=1 yarn test` or -`env "FURN_DESKTOP_DRIVER_MACOS_SIGNING_IDENTITY=FURN Development" yarn storybook build-driver --macos`. -Arbitrary environment overrides, pipelines, redirections, shell expansions, unrecognized -options, remote writes, and destructive Git operations fall through to the CLI's normal permission handling; -they are not blanket-denied. Run commands separately when practical. File approvals resolve symlinks and -are limited to this repository. These defaults are **not a sandbox**: Yarn scripts and Git hooks can execute -arbitrary code, and explicit approvals intentionally trust them. - -### Native Storybook diagnostics - -The existing Yarn approval covers the supported macOS, Windows, and Win32 workflows; extra blanket approvals -for Xcode, Swift, PowerShell scripts, Metro, or screen capture are unnecessary. From `apps/storybook`: - -| Need | Preferred command | -| ------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | -| Project generation and Pod installation | `yarn storybook prep --macos` | -| Bundle failures | `yarn storybook --verbose bundle --` | -| Native build failures | `yarn storybook --verbose build --` (not Win32) | -| Driver identity, signing, and macOS privacy diagnostics | `yarn desktop-driver doctor --platform macos --permissions` | -| Actual listener ports and target identity | `yarn storybook instance --` | -| Target-scoped screenshots | `yarn desktop-driver agent screenshot --platform --url --target --artifacts artifacts/ --name ` | -| Runtime trees and crash reproduction | `yarn desktop-driver agent describe ...` and `yarn storybook --verbose smoke --` | - -For `agent` commands, use `--platform macos` on macOS and `--platform windows` for both Windows and Win32. -Inspect retained command logs under `artifacts/storybook-commands` and platform artifacts rather than redirecting -output to arbitrary locations. Screenshots require a running driver/app and the operating system's own screen -recording permission; CLI approval does not grant Accessibility or Screen Recording access. -Keep evidence scoped to the registered target and ignored artifact directories. Unrestricted `screencapture`, -`osascript`, debugger attachment, external crash-report directories, certificate trust changes, and TCC resets -remain separately authorized. Do not grant these broadly to work around a failed native capability check. - -Contributors can add permissions through the CLI's "don't ask again in this repo" prompt, which saves -repo-scoped approvals in their local `~/.copilot/permissions-config.json`, or through `--allow-tool` flags. -Additional personal hooks can be defined in the gitignored `.github/copilot/settings.local.json`. -To opt out of hooks locally, set `disableAllHooks: true` there (this disables all non-policy hooks). -Do not commit personal permission files, account names, or credentials. Hook approvals precede normal -tool permission checks; disable the hook if you need CLI deny rules to govern these approved operations. - -See the [Copilot hooks reference](https://docs.github.com/en/copilot/reference/hooks-reference) and -[saved permissions schema](https://docs.github.com/en/copilot/reference/copilot-cli-reference/cli-config-dir-reference#permissions-configjson). -Run `yarn test:agent-permissions` after changing the policy. - -## Project Overview - -This is the **FluentUI React Native** repository, a monorepo containing React Native components that implement Microsoft's Fluent Design System. The repository supports multiple platforms including iOS, Android, macOS, Windows, and Win32. - -## Repository Architecture - -### High-Level Structure - -``` -/apps/ - Demo and test applications - /fluent-tester/ - Main test app for component development - /E2E/ - End-to-end testing setup using Appium/WebDriverIO - /win32/ - Win32-specific test app - /component-generator/ - Tool to generate new components -/packages/ - Core library packages - /agentic/components/ - Modern agentic component implementation package - /agentic/design/ - Common design ancestor package - /agentic/native-core/ - Shared modern native components and TurboModules - /components/ - UI component implementations (Button, Checkbox, Avatar, etc.) - /shim/ - Compatibility entrypoints (Callout, FocusZone, theming, etc.) - /framework/ - Core theming and composition framework - /composition/ - Component composition factory (current approach) - /theme/ - Theme system - /use-tokens/ - Token-based styling hooks - /use-slots/ - Slot-based component composition - /framework-base/ - Base package used by all component packages - /theming/ - Theme definitions for different platforms - /android-theme/ - /apple-theme/ - /default-theme/ - /win32-theme/ - /experimental/ - Components under active development - /deprecated/ - Old framework code (foundation-compose, foundation-composable) - /utils/ - Shared utilities and tools -/scripts/ - Build and development scripts (fluentui-scripts CLI) -/docs/ - Component and theming documentation -``` - -### Key Framework Concepts - -**Composition Framework**: The repository uses `@fluentui-react-native/composition` (located at `packages/framework/composition/`) for building components. This is the current approach and is simpler than the older foundation-compose/foundation-composable frameworks in `/deprecated/`. - -**Slots**: The slot pattern is used to compose higher-order components. A slot represents an inner component (actual entry in the render tree). For example, a Button might have slots for `root`, `icon`, and `content`. This allows advanced customization scenarios. Components wrapping a single native component typically have one slot. - -**Tokens**: Design tokens handle styling and customization. Tokens are design-time values set via theme or component customization (e.g., "brandColor"). Tokens can also be props (specified via "TokensThatAreAlsoProps"). This system enables simpler customization and better memoization. For Flex token authoring, use `packages/agentic/design/src/tokens/mappings/flex-token-map.yaml` as the canonical mapping from generic CSS and Fluent token sources to grouped React Native Flex token paths. When mapping an agentic component to an existing V1 component, use the Win32, macOS, or Windows implementation as the canonical compatibility reference; do not use iOS as the cross-platform canonical value unless the work explicitly targets iOS. - -**Color utilities**: Runtime-safe parsing, conversion, compositing, contrast, and interaction generation live behind the opt-in `@fluentui-react-native/design/color-lib` subpath. Keep its public exports intentionally narrow and add APIs only for demonstrated production needs. Color validation policy, semantic pairings, audit helpers, and conformance fixtures belong under `packages/agentic/design/src/testing/` and are exported, when generally useful to tests, from `@fluentui-react-native/design/testing`. Production code must not depend on the testing subpath, and testing-only validation does not need bundle-size scenarios. - -**Theme-specific styles**: Cache `StyleSheet.create` results that depend only on `ThemeState` with a module-scoped getter created by `themedStyleSheetFactory`. Treat them as immutable, and apply props, interaction state, and user styles separately so the cached sheet is safe to share between component instances. - -**Platform-Specific Files**: Components use platform-specific files with extensions like `.ios.ts`, `.android.ts`, `.win32.ts`, `.macos.ts` for platform-specific implementations. - -**Legacy vs V1**: Many components have both legacy and V1 implementations (e.g., `Button` and `ButtonV1`). The V1 versions use the newer composition framework and are preferred. - -## Build System & Commands - -The repository builds as a **single unified TypeScript project-references graph** compiled by **tsc** (`typescript`). The root `tsconfig.json` lists every package's `tsconfig.json` under `references`, and `yarn build` runs `tsc -b` over that graph, building each package in dependency order. **Lage** orchestrates the non-build tasks (bundle, test, lint, clean), and **Yarn 4 (Berry)** in **pnpm mode** manages dependencies (configured in `.yarnrc.yml`). - -### Primary Commands (from the repo root) - -```bash -yarn build # Unified TypeScript build of all packages (tsc -b); emits each package's lib/ -yarn build --clean # Remove build outputs (tsc -b --clean); follow with `yarn build` for a full rebuild -yarn lage test # Build + run tests across packages -yarn lage lint # Lint across packages -yarn lint-fix # Lint with autofix (cross-env FURN_FIX_MODE=true lage lint) -yarn bundle:repo # Bundle all packages (lage bundle) -yarn lage buildci # CI aggregate graph: lint-repo, check-publishing, build, test, lint -yarn clean # Clean build artifacts (lage clean) -yarn format # Format code with oxfmt -yarn format:check # Check formatting without writing -``` - -### Repo-Level Checks & Housekeeping - -```bash -yarn lint-repo # Repo-wide structural lint (scripts/src/tasks/lintRepo.ts) -yarn lint-lockfile # Validate the Yarn lockfile -yarn lage test-links # Validate links in repository Markdown files -yarn check-publishing # Validate package publishing configuration -yarn change # Create a change file for the current branch -yarn change:check # Verify required change files exist -yarn docs # Start the documentation site -``` - -### Lage Configuration - -The task pipeline is defined in `lage.config.mjs`: - -- Tasks declare dependency ordering (e.g. `test` dependsOn `build`) -- `buildci` is the aggregate CI alias (lint-repo, check-publishing, build, test, lint) -- Lage caches task outputs; add `--no-cache` to bypass caching and `--verbose` for detailed output -- After a major rework (e.g. moving packages, large refactors, or renaming exports), run `yarn lage test --no-cache` from the root once to force every test to re-run without relying on stale cached results. This also resets the Lage cache, so subsequent plain `yarn lage test` runs will work incrementally again. -- Before checking in moved or renamed files, run `yarn lage test-links` and update any repository links that still reference the old paths. - -### Package-Level Commands - -Each package's own `build` script is `tsc -b` (it builds that package together with its referenced dependencies). Other per-package tasks run through the `fluentui-scripts` CLI (in `/scripts/`): - -- `yarn build` - tsc project-references build (emits to `lib/`) -- `yarn lint` - ESLint (`fluentui-scripts lint`) -- `yarn test` - Jest tests where present (`fluentui-scripts jest`) -- `yarn depcheck` - Unused-dependency check (`fluentui-scripts depcheck`) -- `yarn format` / `yarn format:fix` - Check / fix formatting - -`fluentui-scripts` also retains a `build` command that can emit dual ESM (`lib/`) and CommonJS (`lib-commonjs/`) output driven by per-package build config, but packages on this branch build with `tsc -b` directly. - -### Agent validation and investigation discipline - -- Run commands through the owning workspace's declared scripts. Inspect its `package.json` before invoking an underlying - runner directly; do not assume scripts such as `jest` or `check` exist, and do not bypass project references with - ad-hoc `tsc` file arguments. -- Run working-directory-sensitive native tools from the owning app workspace. Flags such as CocoaPods' - `--project-directory` select project files but do not necessarily change subprocess resolution roots. -- During iteration, use the smallest relevant test and run package lint/build early after type-heavy edits. For final - validation, run format, lint, package build, package tests, then the root build when public types, manifests, or project - references changed. -- After fixing a failed validation step, rerun that step and its downstream checks. Avoid repeatedly running the entire - validation chain before the immediate failure is resolved. -- Prefer committed `*.types.test.ts` coverage for compile-time contracts. Keep exploratory probes outside package source, - or remove them before running package validation. -- Review small, localized diffs directly. Use an independent review agent only after focused validation passes and only - when the change still has a genuinely ambiguous or cross-cutting contract. -- Keep sessions scoped to one reviewable outcome. After capturing a durable result, start unrelated work in a fresh - session so stale context and generated output do not dominate later investigation. - -## TypeScript Configuration - -The repo uses **TypeScript 7.0.2** via **tsc**, which is automatically added as a dev dependency to every package that has a `tsconfig.json` through dynamic package extensions (`scripts/dynamic.extensions.mts`). - -The whole repo compiles as one **composite project-references graph**: the root `tsconfig.json` lists every package under `references`, `tsc -b` builds them in dependency order, and each package caches incremental state in `.cache/tsconfig.tsbuildinfo`. - -### Key TypeScript Settings - -- Base config: `scripts/configs/tsconfig/tsconfig.json` → extends `tsconfig.strict.json` → `@rnx-kit/tsconfig/tsconfig.nodenext.json` -- `target` / `lib`: `esnext` -- `module`: `esnext`, `moduleResolution`: `bundler` (ESM) -- `jsx`: `react-jsx` -- `rewriteRelativeImportExtensions: true` (ESM relative-import extension rewriting) -- `composite: true` (required for project references), `outDir: lib` -- Strict mode, relaxed for legacy compatibility (`strictNullChecks`, `noImplicitAny`, and `strictBindCallApply` are disabled in the base config) - -### Compatibility Notes - -- `suppressImplicitAnyIndexErrors` was removed in TS 5.8+ -- `Platform.OS` doesn't include `'win32'` in the React Native types even though react-native-windows supports it at runtime — use `Platform.OS === ('win32' as any)` -- The platform React Native forks (`react-native`, `react-native-windows`, `react-native-macos`, `@office-iss/react-native-win32`) have overlapping but divergent type definitions. Do **not** import more than one fork into a single program's type graph (it produces order-dependent type confusion under the unified build). Keep fork imports in `.win32.ts` / `.windows.ts` / `.macos.ts` files, or redeclare the needed shapes platform-neutrally. - -### Framework Type System - -The composition framework uses precise types for better type safety: - -- **`SlotFn`**: Slot functions return `React.ReactElement | null` (not `ReactNode`) - - This reflects the actual behavior: slots always return elements via staged render or `React.createElement` - - Provides better type inference when accessing slot props (e.g., `Slots.root({}).props`) -- **`FinalRender`**: Final render functions in staged components return `JSX.Element | null` - - Used in composition framework's `useRender` functions - - Ensures type compatibility between staged components and the composition system - -## Development Workflow - -### Setting Up Development Environment - -1. Clone repository -2. Run `yarn` to install dependencies -3. Run `yarn build` to build all packages -4. Launch FluentUI Tester app for component testing (see `/apps/fluent-tester/README.md`) - -### Component Development - -**Component Location**: Stable JavaScript components are in `/packages/components/`, components under development are in -`/packages/experimental/`, and compatibility packages are in `/packages/shim/`. Modern native components and modules share -`/packages/agentic/native-core/`; follow its [organization plan](packages/agentic/native-core/README.md). - -**Component Structure**: Each component typically has: - -- `package.json` - Package definition with workspace dependencies -- `src/index.ts` - Main export file -- `src/.tsx` - Component implementation (requires `/** @jsxImportSource @fluentui-react-native/framework-base */` pragma) -- `src/.types.ts` - TypeScript type definitions -- `src/.styling.ts` - Styling and token definitions -- `src/..ts` - Platform-specific implementations -- `SPEC.md` - Component specification and usage documentation -- `MIGRATION.md` - Migration guide (for V1 components) -- `tsconfig.json`, `babel.config.js`, `jest.config.js`, `eslint.config.js` - -**Using Composition Framework**: Use `@fluentui-react-native/composition` for new components. For simpler components without slots/tokens, use the `stagedComponent` pattern from `@fluentui-react-native/framework-base`. - -**JSX Runtime**: All components use the modern automatic JSX runtime: - -- Add `/** @jsxImportSource @fluentui-react-native/framework-base */` at the top of `.tsx` files -- The custom jsx-runtime intercepts JSX calls to optimize slot rendering -- No need to import `withSlots` - it's handled automatically by the runtime -- Components using React Fragments (`<>...`) work automatically (Fragment is re-exported from the jsx-runtime) -- Packages using the jsx-runtime need `@fluentui-react-native/framework-base` in `devDependencies` - -**TypeScript Patterns**: - -- Slot functions automatically return `React.ReactElement`, so you can access `.props` directly without type assertions -- When checking for win32 platform: `Platform.OS === ('win32' as any)` - TypeScript doesn't recognize 'win32' but react-native-windows supports it -- Final render functions should return `FinalRender` with children as rest parameters: `(props: TProps, ...children: React.ReactNode[])` - -**Module Exports**: - -- **Do not use barrel exports (`export * from '...'`)** - wildcard re-exports break tree-shaking because bundlers cannot statically determine which symbols are used, so unused code is retained in consumers' bundles. Always use explicit named re-exports instead, e.g. `export { Foo, Bar } from './module'` and `export type { Baz } from './module'`. - -**Native Modules**: Callout and FocusZone implementations live in `@fluentui-react-native/native-core`; their former packages -are JS-only compatibility shims under `/packages/shim/`. Their JavaScript wrappers live in `src/legacy` and are exported -only from `@fluentui-react-native/native-core/legacy`, not native-core's root entrypoint. Components with native code: - -- Typically have one root slot wrapping the native component -- Use `codegenNativeComponent` for new architecture compatibility -- May use `constantsToExport` for default values from native side -- iOS/macOS: Include `.podspec` files -- Native applications must directly depend on native-core for native project discovery and autolinking - -For new modern native components and modules, use `@fluentui-react-native/native-core` rather than creating another standalone -package. Its Windows components are Fabric-only; macOS components support Paper and Fabric initially. Export shared APIs -from the root and scope platform APIs to `macos`, `windows` (React Native Windows), `win` (Windows/Win32), or `win32`. -Callout and FocusZone retain their existing public APIs and native registration names after migration. - -### Creating a New Component - -1. Create the package under `/packages/components/` or `/packages/experimental/` while it is - under development. Add modern native components/modules to the existing `/packages/agentic/native-core/` package instead. -2. Copy structure from existing component (e.g., Shimmer, Button) -3. Update `package.json` with correct name and dependencies (use `workspace:*` for internal packages) -4. Add the new package's `tsconfig.json` to the root `tsconfig.json` `references` so it joins the unified build -5. Create source files in `src/` -6. Add test page to FluentTester at `/apps/fluent-tester/src/TestComponents//` -7. Register test page in `testPages.tsx` and platform-specific `testPages..tsx` -8. Add E2E tests (see E2E Testing section) -9. Run `yarn` and `yarn build` from root -10. For Apple platforms: run `pod install` in test app directories - -### Theming - -Platform-specific themes are in `/packages/theming/`: - -- `android-theme/` - Android theming -- `apple-theme/` - iOS and macOS theming -- `win32-theme/` - Win32 theming -- `default-theme/` - Cross-platform defaults -- `theme-tokens/` - Token definitions -- `theme-types/` - TypeScript types for themes - -Components require `ThemeProvider` from `@fluentui-react-native/design/theming` to work properly. - -### Testing - -**Manual Testing**: Use FluentUI Tester app (`/apps/fluent-tester/`) for interactive component testing. Test pages are in `/apps/fluent-tester/src/TestComponents/`. - -**E2E Testing**: Required for all new components. Uses Appium + WebDriverIO. - -- E2E tests live in `/apps/E2E/src//` -- Each component needs: - - Page Object (`PageObject..ts`) - Interface to interact with test page - - Spec Document (`Spec..ts`) - Jasmine test cases - - Constants file in test component (`/apps/fluent-tester/src/TestComponents//consts.ts`) -- Test pages must include: - - `testID` on first section matching page object's `_pageName` - - Optional `e2eSections` prop for dedicated E2E test elements -- Run E2E tests: `yarn e2etest:` from `/apps/E2E/` - -**Unit Tests**: Component-specific Jest tests where present, typically in `src/` directories. Run them with `yarn test` in a package or `yarn lage test` from the root. After a major rework, run `yarn lage test --no-cache` once at the root to ensure all tests re-run and pass without stale cache hits; this resets the Lage cache so a normal incremental `yarn lage test` works afterward. - -### Platform-Specific Development - -**iOS/macOS**: - -- May wrap native controls from FluentUI Apple -- Requires `.podspec` files for native modules -- Run `pod install` after adding dependencies - -**Android**: - -- Platform-specific styling and tokens -- Uses `accessibilityLabel` for E2E selectors (other platforms use `testID`) - -**Win32**: - -- Separate test app at `/apps/win32/` -- Uses WinAppDriver for E2E testing - -**Windows (UWP)**: - -- Separate test app configuration -- Legacy support - -## Version Management - -**Changesets**: Used for change logs and versioning. - -- Run `yarn change` to create a change file when modifying packages -- Change files are required before merging PRs (validated in CI via `yarn change:check`) -- Changesets config in `.changeset/config.json` -- Major versions are disallowed (validated in CI via `.github/scripts/validate-changesets.mts`) -- Version bump PRs are created automatically by GitHub Actions; `yarn changeset:version` applies version bumps -- Publishing happens in Azure Pipelines using `.ado/azure-pipelines.publish.yml` - -## Important Notes - -- This is an **alpha-stage** library under active development -- **Requires TypeScript 7.0.2** (`typescript`, compiled via `tsc`); the unified build runs `tsc -b` over the root project-references graph -- **Uses Yarn 4 in pnpm mode** for dependency management (configured in `.yarnrc.yml`) -- **Uses modern automatic JSX runtime** - all components should use `@jsxImportSource @fluentui-react-native/framework-base` -- **Dynamic package extensions**: Common dev dependencies (TypeScript, Jest, ESLint, Prettier) are automatically added via `scripts/dynamic.extensions.mts` -- New packages must be added to the root `tsconfig.json` `references` to join the unified build -- Follow existing component patterns for consistency -- Test components using FluentUI Tester app before submitting PRs -- Platform differences should be documented in component `SPEC.md` files -- Use the newer composition framework (`@fluentui-react-native/composition`) for new components, not the deprecated foundation frameworks -- When importing V1 components, consider aliasing: `import { ButtonV1 as Button }` -- Slot functions return `React.ReactElement` - you can safely access `.props` without type assertions -- Avoid barrel exports (`export * from '...'`); use explicit named re-exports to preserve tree-shaking +Guidance for coding agents working in the FluentUI React Native monorepo, which implements Microsoft's Fluent Design +System for iOS, Android, macOS, Windows, and Win32. This file is always loaded. It holds repository safeguards and +routes each task to the workflow and references it needs; load only what the task requires. + +## How guidance is organized + +- **`AGENTS.md` files** hold safeguards and local invariants. Copilot CLI loads this file plus the `AGENTS.md` in the + working directory and each ancestor. From the repository root, read the scoped files below when a task touches them. +- **Skills** in [`.github/skills`](.github/skills) define task workflows. +- **[Shared references](references/agents/README.md)** in `references/agents/` hold reusable recipes and repository + knowledge, indexed by task. +- **Product contracts** (`SPEC.md`, `spec/`, `CONTRACT.md`), protocols, and package documentation stay with their + packages and govern component behavior. + +## Safeguards + +These apply to every task. + +- **Permissions:** [the repository hook](.github/hooks/agent-permissions.json) approves specific operations; it is not a + sandbox, and other operations use normal permission handling (R-PERM-001). Do not amend commits, discard other + people's changes, or rewrite history without explicit authorization (R-PERM-002). Never print authentication tokens + or commit personal permission files, account names, or credentials (R-PERM-003). Unrestricted screen capture, + `osascript`, debugger attachment, crash-report access, certificate-trust changes, and TCC resets need separate + authorization (R-PERM-007). Read the [permissions reference](references/agents/repository/permissions.md) before + using `gh api`, Pod, process, loopback `curl`, or capture operations, or changing the policy. +- **Validation:** run the owning workspace's declared scripts (R-VALID-001). Report each check as passed, failed, + skipped, not run, or unsupported; JavaScript tests and bundles are not native qualification (R-VALID-002). +- **Code:** no barrel exports (R-REPO-001); keep React Native fork imports in platform files (R-REPO-002); add a + changeset for published packages (R-REPO-003); never copy private source bodies into this public repository + (R-REPO-004). +- **Component generations:** v0 and v1 are maintenance-only; new components use the agentic workflow (R-V0-001, + R-V1-001). Classify code by imports and structure, not by path (R-CLASSIFY-001). +- **Native boundary:** before changing Swift, Objective-C, C++, headers, podspecs, Windows projects, codegen specs, + native registration, or a native-backed platform wrapper, use the + [native component skill](.github/skills/implement-native-component/SKILL.md), directly or as an overlay. + +## Route the task + +Classify the change with the [classification guide](references/agents/components/classification.md), then use: + +| Task | Skill | +| ------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------- | +| New component, or change an agentic component or primitive | [agentic-component-authoring](.github/skills/agentic-component-authoring/SKILL.md) | +| Agentic contract-only, feasibility, or plan-only work | [agentic-component-contract-authoring](.github/skills/agentic-component-contract-authoring/SKILL.md) | +| Fix or maintain an existing v0 or v1 component | [maintain-v0-v1-component](.github/skills/maintain-v0-v1-component/SKILL.md) | +| Review a component change of any generation | [review-component](.github/skills/review-component/SKILL.md) | +| Native component, TurboModule, service, or codegen | [implement-native-component](.github/skills/implement-native-component/SKILL.md) | +| Cross-component agentic extraction or dependency direction | [agentic-component-optimization](.github/skills/agentic-component-optimization/SKILL.md) | +| Storybook app, Metro, CocoaPods, or desktop native toolchain | [agentic-storybook-development](.github/skills/agentic-storybook-development/SKILL.md) | +| Dependency vulnerability remediation | [fix-security-issues](.github/skills/fix-security-issues/SKILL.md) | + +Other repository work starts from the [reference index](references/agents/README.md): architecture, commands and +validation, TypeScript, release, design tokens, focus, and native guidance. + +## Scoped instructions + +| Path | Scope | +| ------------------------------------------------------------------------------------------------ | ------------------------------------------------------ | +| [packages/agentic/components](packages/agentic/components/AGENTS.md) | Agentic package invariants, contracts, and story tests | +| [packages/agentic/components/src](packages/agentic/components/src/AGENTS.md) | Source boundaries and extraction | +| [.../src/components](packages/agentic/components/src/components/AGENTS.md) | Higher-order components and the focus visual policy | +| [.../src/primitives](packages/agentic/components/src/primitives/AGENTS.md) | Public primitives | +| [packages/agentic/desktop-driver](packages/agentic/desktop-driver/AGENTS.md) | Desktop WebDriver driver | +| [packages/framework-base](packages/framework-base/AGENTS.md) | Shared component patterns | +| [packages/framework-base/src/accessibility](packages/framework-base/src/accessibility/AGENTS.md) | Accessibility helpers and native action qualification | +| [packages/framework/framework](packages/framework/framework/AGENTS.md) | v1 framework package | +| [packages/deprecated/foundation-compose](packages/deprecated/foundation-compose/AGENTS.md) | v0 framework package | +| [apps/storybook](apps/storybook/AGENTS.md) | Storybook app, native workflows, and diagnostics | + +## Quick facts + +- Yarn 4 in pnpm mode; TypeScript 7.0.2 compiled as one `tsc -b` project-references graph; Lage task orchestration. + New packages must be added to the root `tsconfig.json` `references`. +- `yarn build` builds the repository; `yarn lage test` and `yarn lage lint` test and lint packages; `yarn format` formats. + `yarn lage buildci` runs the CI aggregate, including `yarn check-agent-instructions`. +- Details: [commands and validation](references/agents/repository/development.md), + [architecture](references/agents/repository/architecture.md), [TypeScript](references/agents/repository/typescript.md), + and [release](references/agents/repository/release.md). diff --git a/agent-improvements-plan.md b/agent-improvements-plan.md index a11a3baa29..1f1fdeaf7c 100644 --- a/agent-improvements-plan.md +++ b/agent-improvements-plan.md @@ -1,7 +1,8 @@ # Agent instruction and skill improvements plan -**Status:** Proposed; awaiting user decisions. This document authorizes no -instruction migration, skill changes, policy changes, or evaluation runs. +**Status:** Implemented on 2026-10-14 through P4 using the answered decisions in section 11; P5 (the +efficacy skill) is deferred under D12. See [section 13](#13-implementation-record) for what changed, parity +evidence, pilot results, deviations, and follow-ups. **Basis:** Repository inspection at `246d336682c628e6ff65d11c6f5997d9135be5b6` on 2026-10-14, independent plans from Claude Opus 5.5 and GPT 6 Astra, and @@ -587,3 +588,106 @@ instruction-file contents, quantitative efficacy, and unavailable native qualification remain open until the relevant phase. No component code, instruction files, skills, contracts, or permission hooks were changed to produce this plan. + +## 13. Implementation record + +Implemented on branch `user/jasonvmo/instruction-rework` from `a1fd5c65d`. The final validation results are listed +below; the efficiency pilot is reporting-only under D08. + +### What changed + +| Area | Result | +| --------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Shared home (D01) | [`references/agents/`](references/agents/README.md) with a taxonomy, document index, placement rules, standardized frontmatter (`id`, `kind`, `owner_paths` only), and `R-`/`C-` ID conventions (D07). | +| Centralized recipes (D10) | Agentic types, state, styles, rendering, and tests recipes; all three focus references; Windows Fabric guidance; and all six contract-source adapters moved under `references/agents`. New shared references cover classification, v0, v1, legacy test apps, repository architecture, commands and validation, TypeScript, permissions, release, design tokens, and native components. | +| Compatibility | Each of the 15 old reference paths, plus the existing `spec-source-adaptation.md` redirect, is a `compatibility-reference` linking directly to its new home. The `#focus-visual-policy` anchor and all inbound links are preserved. No alias skills were added. | +| Skills (D02, D03, D04) | Added `maintain-v0-v1-component`, `review-component`, and `implement-native-component`. Existing names are retained; descriptions now state positive scope and exclusions, and the authoring skill loads references conditionally, with a primitive `CONTRACT.md` branch. v0 and v1 are maintenance-only (`R-V0-001`, `R-V1-001`). | +| Root router (D05, D06) | Root `AGENTS.md` shrank from 27,423 to about 7,400 bytes. It keeps always-on safeguards (permissions, validation truthfulness, code rules, generation policy, native trigger), a task routing table, and a scoped-instruction table. `CLAUDE.md` and `.github/copilot-instructions.md` are unchanged. | +| Scoped entrypoints | v0 and v1 package `AGENTS.md` files are short maintainer routes to the shared recipes. Agentic package and primitive files define `R-AGENTIC-*` and `R-PRIMITIVE-*` rules. The Storybook diagnostics table moved to `apps/storybook/AGENTS.md#native-diagnostics`. | +| Integrity checker (D09) | `yarn check-agent-instructions` runs from the root and in `yarn lage buildci` and `repo-checks`. It validates frontmatter, index completeness, case-sensitive links and anchors, inbound anchors into instruction files, code-span paths, skill metadata, rule definitions and citations, compatibility redirects, and evaluation fixtures. It has 11 node tests in `@fluentui-react-native/scripts`. | +| Evaluation groundwork (D11) | Eleven sanitized JSON cases in [`references/agents/evals/cases`](references/agents/evals/README.md): seven development and four holdout. The documented local result-record shape covers commit, instruction hashes, client and model, reads, assertions, usage, and confounders. No framework was chosen. | + +### Observed discovery behavior + +`copilot instruction list` (Copilot CLI 1.0.98) shows that a root session loads `.github/copilot-instructions.md`, +`AGENTS.md`, and `CLAUDE.md`. Because `CLAUDE.md` imports `AGENTS.md`, root content loads twice, so every byte removed +from the root saves double. A nested working directory additionally loads its own `AGENTS.md` and every ancestor +`AGENTS.md` up to the root. `copilot skill list` shows all eight project skills. + +### Safety parity (D06) + +The 68-line root permissions section moved to +[`references/agents/repository/permissions.md`](references/agents/repository/permissions.md) with rule IDs +`R-PERM-001` to `R-PERM-008`. + +A sentence-level comparison mapped all 39 source sentences into the permissions reference, the Storybook diagnostics +section, or the root safeguards. 33 matched automatically, and the remaining 6 were confirmed manually as wording +changes. The root keeps the prohibitions always-on: no history rewriting, no token printing, and separate authorization +for capture. Hook behavior is unchanged, and `yarn test:agent-permissions` passes. + +### Routing pilot (D08, reporting only) + +Paired, read-only routing probes ran on Copilot CLI 1.0.98 with `claude-sonnet-5.5`, medium reasoning, and only the +view, glob, grep, and skill tools. The baseline was a worktree at `a1fd5c65d`; the candidate was the migrated tree. +Assertions were scored by the coordinator from each probe's plan. + +| Measure | Baseline | Candidate | +| ------------------------------------------ | --------- | -------------- | +| Expected workflow skill reached (10 cases) | 4 | 10 | +| Assertions met: 6 development cases | 10/14 | 14/14 | +| Assertions met: 4 holdout cases | 7/8 | 8/8 | +| Total input tokens | 1,183,543 | 801,045 (−32%) | +| Median input tokens per case | 98,648 | 78,123 (−21%) | +| Final-call input tokens, summed | 408,491 | 300,469 (−26%) | +| AI usage units, summed | 124.4 | 96.8 (−22%) | +| Model requests | 35 | 37 | +| On-demand instruction bytes viewed | 169,297 | 362,936 | + +Baseline v0 and v1 probes loaded no instruction files and missed `componentRef`, settings-layer, evidence, and +native-qualification obligations. Candidates load more references on demand, but the smaller always-on root dominates +the total cost. + +Both configurations treated `yarn storybook build --macos` as a Paper build. The native skill now states that macOS +defaults to Fabric and Paper requires `--paper`. A recheck then planned `prep --paper` and `build --macos --paper`. +Under the holdout rule, that case was relabeled as development and a new unseen holdout, +`windows-fabric-callout-prop`, was added. + +Caveats: + +- There was one run per case, with one model. +- The personal global instruction was present in both configurations. +- Baseline skill-tool loads are not counted in viewed bytes. +- Holdouts were written by the same author as the instructions. + +### Deviations and follow-ups + +- **Apple/Office extraction:** the Apple native authoring section and `packages/agentic/native-core/AGENTS.md` exist + only on the unmerged `user/jasonvmo/native-lib2` branch. When it merges: + - Root `AGENTS.md` will conflict. + - Move the section into a new `references/agents/native/apple-office.md` with `R-APPLE-*` IDs and a parity review. + - Keep a short root heading `Apple native authoring and Office integration` as the anchor and safeguard route, since + three links target it. + - Route native-core's `AGENTS.md` to the new reference. + - Add the reference to the index. +- **Corrected stale root claims:** `yarn build --clean` (now `build:clean`); ESLint and Prettier (now oxlint and oxfmt); + `depcheck`; FluentTester paths (now `apps/tester-core`); per-platform `testPages`; `validate-changesets.mts` (now + `change.mts`); and framework-base as a devDependency (it is a runtime dependency). `yarn docs` was not carried + forward because its `fluent-rn-website` workspace does not exist. +- **Not changed (human documentation):** stale FluentTester paths in `CONTRIBUTING.md` and `apps/E2E/README.md` remain + as follow-ups. +- **Deferred:** the `assess-agent-efficacy` skill (D12). Repeat the pilot with more runs, a second model, and the new + holdout before relying on these percentages. + +### Validation + +| Check | Result | +| ----------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- | +| `yarn check-agent-instructions` | Passed | +| `yarn workspace @fluentui-react-native/scripts test` | Passed (11 tests) | +| `yarn workspace @fluentui-react-native/scripts build` and `lint` | Passed | +| `yarn lint-repo` (constraints, align-deps, Knip, lockfile, oxfmt) | Passed | +| `yarn lage test-links --grouped` | Passed for every workspace | +| `yarn test:agent-permissions` | Passed (10 tests) | +| `yarn workspace @fluentui-react-native/components check:spec-contracts` | Passed (20 contracts) | +| `yarn change:check` | Passed after committing the empty changeset | +| `yarn lage buildci --grouped` | Passed: 156 tasks succeeded and 0 failed, including `check-agent-instructions` and the scripts tests | diff --git a/apps/storybook/AGENTS.md b/apps/storybook/AGENTS.md index 37f5a638fa..9367f30845 100644 --- a/apps/storybook/AGENTS.md +++ b/apps/storybook/AGENTS.md @@ -33,6 +33,26 @@ Read [`agent-map.yaml`](agent-map.yaml) first for the compact architecture, look - `app.json` owns the custom `storybook.testIDPrefix`. Do not create another app identity file or duplicate the prefix in runtime source. +## Native diagnostics + +The repository Yarn approval covers these workflows; see the +[agent permissions reference](../../references/agents/repository/permissions.md#native-diagnostics-and-capture) for +capture limits. From this directory: + +| Need | Preferred command | +| ------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Project generation and Pod installation | `yarn storybook prep --macos` | +| Bundle failures | `yarn storybook --verbose bundle --` | +| Native build failures | `yarn storybook --verbose build --` (not Win32) | +| Driver identity, signing, and macOS privacy diagnostics | `yarn desktop-driver doctor --platform macos --permissions` | +| Actual listener ports and target identity | `yarn storybook instance --` | +| Target-scoped screenshots | `yarn desktop-driver agent screenshot --platform --url --target --artifacts artifacts/ --name ` | +| Runtime trees and crash reproduction | `yarn desktop-driver agent describe ...` and `yarn storybook --verbose smoke --` | + +For `agent` commands, use `--platform macos` on macOS and `--platform windows` for both Windows and Win32. Inspect +retained command logs under `artifacts/storybook-commands` and platform artifacts rather than redirecting output to +arbitrary locations. + ## Desktop Driver workflow - Use `yarn storybook manifest --` to validate static story-plan diff --git a/lage.config.mjs b/lage.config.mjs index e8270be5c3..e2fc9bff64 100644 --- a/lage.config.mjs +++ b/lage.config.mjs @@ -37,6 +37,9 @@ const config = { // tsc project level build has its own caching cache: false, }, + 'check-agent-instructions': { + cache: false, + }, 'check-publishing': { cache: false, }, @@ -65,8 +68,8 @@ const config = { }, // ── Pipeline aliases ─────────────────────────────────────────────────── - 'repo-checks': ['lint-repo', 'check-publishing'], - buildci: ['lint-repo', 'check-publishing', '@fluentui-react-native/root#root-build', 'test', 'lint'], + 'repo-checks': ['lint-repo', 'check-publishing', 'check-agent-instructions'], + buildci: ['lint-repo', 'check-publishing', 'check-agent-instructions', '@fluentui-react-native/root#root-build', 'test', 'lint'], // ── Worker tasks ─────────────────────────────────────────────────────── 'test-links': { diff --git a/package.json b/package.json index c78c5ba5b4..6be3faf298 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,7 @@ "change": "node .github/scripts/change.mts", "changeset:version": "node .github/scripts/changeset-version-with-postbump.mts", "change:check": "node .github/scripts/change.mts --check", + "check-agent-instructions": "node ./scripts/src/cli.ts check-agent-instructions", "check-publishing": "node ./scripts/src/cli.ts check-publishing", "lint-fix": "cross-env FURN_FIX_MODE=true lage lint", "root-build": "tsc -b", diff --git a/packages/agentic/components/AGENTS.md b/packages/agentic/components/AGENTS.md index ff96a662f3..70f325cf43 100644 --- a/packages/agentic/components/AGENTS.md +++ b/packages/agentic/components/AGENTS.md @@ -1,44 +1,40 @@ # Agentic Components authoring These instructions apply to `packages/agentic/components` and its descendants. Keep this file limited to durable package -invariants; detailed authoring recipes live in the -[agentic component authoring skill](../../../.github/skills/agentic-component-authoring/SKILL.md). +invariants; detailed recipes live in the shared [agentic references](../../../references/agents/README.md), routed by +the [agentic component authoring skill](../../../.github/skills/agentic-component-authoring/SKILL.md). ## Routing -- Higher-order components live in `src/components`; read `src/components/AGENTS.md`. -- Primitive components live in `src/primitives`; read `src/primitives/AGENTS.md`. -- Story files are library source and follow the tests and stories reference. -- Focus work starts with the [common focus instructions](../../../.github/skills/agentic-component-authoring/references/focus.md), - then the [Windows/Win32](../../../.github/skills/agentic-component-authoring/references/focus-windows.md) or - [macOS](../../../.github/skills/agentic-component-authoring/references/focus-macos.md) detail. V1 Win32 is the +- Implementation and new components use the + [agentic component authoring skill](../../../.github/skills/agentic-component-authoring/SKILL.md); reviews use + [review-component](../../../.github/skills/review-component/SKILL.md). +- Higher-order components live in `src/components`; read [its instructions](src/components/AGENTS.md). +- Primitive components live in `src/primitives`; read [its instructions](src/primitives/AGENTS.md). +- Story files are library source and follow [tests and stories](../../../references/agents/components/agentic/tests-and-stories.md). + Executable tests are top-level `wdio` callbacks or maps of named callbacks typed with `import type` of `WdioStory` + from `storybook-desktop/testing`. Keep callbacks self-contained and dynamically import Node helpers inside them. + Each named case gets a fresh story, session, and worker. Use stable `testID` selectors and the injected `platform` + for real platform differences, with explicit skips for unsupported capabilities. Shared focus tests run on macOS, + Windows, and Win32; gate only genuinely platform-specific assertions. Legacy `parameters.desktopDriver` plans remain + only in runner compatibility fixtures. +- Focus work starts with the [common focus instructions](../../../references/agents/focus/common.md), + then the [Windows/Win32](../../../references/agents/focus/windows-win32.md) or + [macOS](../../../references/agents/focus/macos.md) detail. V1 Win32 is the behavioral baseline for both Windows endpoints; renderer-specific plumbing remains separate. -- Author executable tests under top-level `wdio`, as one callback or a map - of named callbacks. All catalog tests use this pattern; legacy - `parameters.desktopDriver` plans remain only in runner compatibility fixtures. - Use stable `testID` selectors and the injected target `platform` for real - platform differences, with explicit skips for unsupported capabilities. -- Shared focus tests run on macOS, Windows, and Win32. Gate only genuine - platform-specific assertions, such as Windows pointer-focus, rather than the - entire case. Follow the tests-and-stories reference for native activation - timing, macOS Keyboard navigation prerequisites, and pass/skip evidence. -- Executable tests are typed - with `WdioStory` from `storybook-desktop/testing` using `import type`. - Keep callbacks self-contained; dynamically import Node helpers inside - them so the shared Babel transform can exclude them from native bundles. - Each named case gets a fresh story, session, and worker; do not share native - element handles or depend on previous cases. - Storybook application, native project, Metro, bundle, or CocoaPods work follows the [Storybook instructions](../../../apps/storybook/AGENTS.md) and the `agentic-storybook-development` skill. -- Native React Native Windows Fabric component work follows the - [Windows Fabric native component reference](../../../.github/skills/agentic-component-authoring/references/windows-fabric-native-components.md). +- Native views, codegen, or registration follow the + [native component skill](../../../.github/skills/implement-native-component/SKILL.md); React Native Windows Fabric + views also follow the [Windows Fabric reference](../../../references/agents/native/windows-fabric.md). ## Package invariants -- Create or revise source-backed contracts through the - [agentic component contract authoring skill](../../../.github/skills/agentic-component-contract-authoring/SKILL.md). - It routes Flex/X3, Fluent v9, HTML/CSS, visual, local-foundation, and - composite evidence before implementation. +- **R-AGENTIC-001:** Before implementing a higher-order component whose contract is absent, stale, still a draft, or + accompanied by new source evidence, create or revise its source-backed contract through the + [agentic component contract authoring skill](../../../.github/skills/agentic-component-contract-authoring/SKILL.md) + and complete its review gate. Never mechanically promote a web-authored specification. The skill routes Flex/X3, + Fluent v9, HTML/CSS, visual, local-foundation, and composite evidence. - Use the repository's `flex-authoring` profile and `flex-components:` only for a pinned Flex/X3 source. It is not a prerequisite for other source adapters. @@ -53,10 +49,10 @@ invariants; detailed authoring recipes live in the - Use `src/components/button` as the canonical higher-order implementation and `src/primitives/icon` as the canonical primitive. - Keep public props and slots small, typed, and spec-driven. -- This package targets React 19.1.4 or newer. Expose a stable native root ref through the `ref` prop, receive it as part - of the component props, and forward it to the declared root slot. Do not use `forwardRef`. +- **R-AGENTIC-002:** This package targets React 19.1.4 or newer. Expose a stable native root ref through the `ref` prop, + receive it as part of the component props, and forward it to the declared root slot. Do not use `forwardRef`. - Export higher-order components and public types explicitly from `src/index.ts`; export primitives and their public types - explicitly from `src/primitives/index.ts`. Never use wildcard exports. + explicitly from `src/primitives/index.ts`. Never use wildcard exports (R-REPO-001). - Colocate runtime tests, type tests, and Storybook stories with the implementation. - Include every test-bearing story in `tsconfig.stories.json`. The representative runner contract rejects legacy catalog plans and missing story type coverage. @@ -65,5 +61,5 @@ invariants; detailed authoring recipes live in the - Use package scripts for format, lint, build, tests, and snapshots. - Do not copy web-only APIs, CSS behavior, or DOM assumptions into React Native. - Do not copy or mechanically transform private source bodies into this public - package. Record source identifiers and digests, then author the local React + package (R-REPO-004). Record source identifiers and digests, then author the local React Native contract. diff --git a/packages/agentic/components/src/AGENTS.md b/packages/agentic/components/src/AGENTS.md index 899182d7ce..f324100ddc 100644 --- a/packages/agentic/components/src/AGENTS.md +++ b/packages/agentic/components/src/AGENTS.md @@ -27,7 +27,7 @@ the change crosses component boundaries. - Align native text with View layout, not synthetic text heights or baseline offsets. A font's em size, line box, and visible glyph are different measurements. Use `Icon` for font icons and `LayoutStableText` for reserved labels; follow - the [native alignment rules](../../../../.github/skills/agentic-component-authoring/references/styles-and-tokens.md#native-text-and-vertical-alignment) + the [native alignment rules](../../../../references/agents/components/agentic/styles-and-tokens.md#native-text-and-vertical-alignment) for editors, mixed-size text rows, and regression coverage. - Audit dependency direction before introducing shared code. diff --git a/packages/agentic/components/src/components/AGENTS.md b/packages/agentic/components/src/components/AGENTS.md index d2511ab848..9574249a77 100644 --- a/packages/agentic/components/src/components/AGENTS.md +++ b/packages/agentic/components/src/components/AGENTS.md @@ -67,9 +67,9 @@ audit. ## Focus visual policy -Read [common focus authoring](../../../../../.github/skills/agentic-component-authoring/references/focus.md), -then [Windows/Win32](../../../../../.github/skills/agentic-component-authoring/references/focus-windows.md) or -[macOS](../../../../../.github/skills/agentic-component-authoring/references/focus-macos.md). +Read [common focus authoring](../../../../../references/agents/focus/common.md), +then [Windows/Win32](../../../../../references/agents/focus/windows-win32.md) or +[macOS](../../../../../references/agents/focus/macos.md). The common file owns ref, state, activation, scope, and styling invariants; platform files own detailed native adaptation. V1 Win32 models observable behavior for both Office Win32 and Windows Fabric. @@ -90,8 +90,8 @@ identities by following a proposal before its component contract is reviewed. Th ## Focused references - [Contract and source adaptation](../../../../../.github/skills/agentic-component-contract-authoring/SKILL.md) -- [Types and slots](../../../../../.github/skills/agentic-component-authoring/references/types-and-slots.md) -- [State and accessibility](../../../../../.github/skills/agentic-component-authoring/references/state-and-accessibility.md) -- [Styles and tokens](../../../../../.github/skills/agentic-component-authoring/references/styles-and-tokens.md) -- [Rendering and assembly](../../../../../.github/skills/agentic-component-authoring/references/rendering.md) -- [Tests and stories](../../../../../.github/skills/agentic-component-authoring/references/tests-and-stories.md) +- [Types and slots](../../../../../references/agents/components/agentic/types-and-slots.md) +- [State and accessibility](../../../../../references/agents/components/agentic/state-and-accessibility.md) +- [Styles and tokens](../../../../../references/agents/components/agentic/styles-and-tokens.md) +- [Rendering and assembly](../../../../../references/agents/components/agentic/rendering.md) +- [Tests and stories](../../../../../references/agents/components/agentic/tests-and-stories.md) diff --git a/packages/agentic/components/src/primitives/AGENTS.md b/packages/agentic/components/src/primitives/AGENTS.md index 1ec174b32e..83da84136d 100644 --- a/packages/agentic/components/src/primitives/AGENTS.md +++ b/packages/agentic/components/src/primitives/AGENTS.md @@ -8,15 +8,16 @@ the canonical implementation. - Primitives are public only from `@fluentui-react-native/components/primitives`; do not re-export them from the package root. - Give primitive components stable public names. Suffix public composition helpers whose contracts may evolve with `_unstable`, matching higher-order component pipeline helpers. -- Maintain a colocated `CONTRACT.md` for every public primitive. Derive its test obligations from that contract, its public - types, and each renderer branch rather than creating an upstream-backed `SPEC.md`. +- **R-PRIMITIVE-001:** Maintain a colocated `CONTRACT.md` for every public primitive and keep primitives unstyled. Derive + test obligations from that contract, its public types, and each renderer branch rather than creating an + upstream-backed `SPEC.md`. - Primitives are unstyled building blocks. Do not read themes, apply design tokens, or choose product appearance defaults. - Define the smallest acceptance contract needed for `SlotProp` consumption. - Prefer a hook-free `directComponent` when the primitive only selects an inner component or transforms props. -- For a primitive with one stable native root type, include `PropsWithRefOf` in its public props and pass - the React 19.1.4 `ref` prop to that root without `forwardRef`. A primitive whose renderer does not have one stable - imperative instance type must omit `ref` and document that exception in its `CONTRACT.md`. +- **R-PRIMITIVE-002:** For a primitive with one stable native root type, include `PropsWithRefOf` in its + public props and pass the React 19.1.4 `ref` prop to that root without `forwardRef`. A primitive whose renderer does + not have one stable imperative instance type must omit `ref` and document that exception in its `CONTRACT.md`. - Forward accessibility and test props without replacing consumer values. - Keep alternative source or renderer props mutually exclusive. - Add compile-time `SlotProp` coverage and runtime coverage for every renderer branch. @@ -28,6 +29,6 @@ the canonical implementation. ## Focused references -- [Types and slots](../../../../../.github/skills/agentic-component-authoring/references/types-and-slots.md) -- [Rendering and assembly](../../../../../.github/skills/agentic-component-authoring/references/rendering.md) -- [Tests and stories](../../../../../.github/skills/agentic-component-authoring/references/tests-and-stories.md) +- [Types and slots](../../../../../references/agents/components/agentic/types-and-slots.md) +- [Rendering and assembly](../../../../../references/agents/components/agentic/rendering.md) +- [Tests and stories](../../../../../references/agents/components/agentic/tests-and-stories.md) diff --git a/packages/agentic/native-core/README.md b/packages/agentic/native-core/README.md index bd10148910..a5d192ba91 100644 --- a/packages/agentic/native-core/README.md +++ b/packages/agentic/native-core/README.md @@ -167,7 +167,7 @@ hosts become Fabric-only, without changing the public JS API. The Windows component project must require the New Architecture and fail clearly for a Paper build. Use the repository's -[Windows Fabric native component reference](../../../.github/skills/agentic-component-authoring/references/windows-fabric-native-components.md) +[Windows Fabric native component reference](../../../references/agents/native/windows-fabric.md) and verify APIs against the installed RNW version. Win32 integration is separate from React Native Windows Paper support; adding a Win32 export does not authorize a second RNW view-manager implementation. diff --git a/packages/deprecated/foundation-compose/AGENTS.md b/packages/deprecated/foundation-compose/AGENTS.md index 218573b309..92df74092c 100644 --- a/packages/deprecated/foundation-compose/AGENTS.md +++ b/packages/deprecated/foundation-compose/AGENTS.md @@ -1,195 +1,25 @@ -# Legacy v0 component authoring +# Legacy v0 component framework -These instructions apply to `packages/deprecated/foundation-compose` and to work -on components that use `@uifabricshared/foundation-compose`. They describe the -legacy/v0 framework only. Do not use this pattern for a new component unless the -task explicitly requires v0 compatibility. +These instructions apply to `packages/deprecated/foundation-compose`, which implements the repository's legacy v0 +`compose` framework. The consumer recipe, recognition guide, and review checklist for v0 components live in the +shared [v0 reference](../../../references/agents/components/v0.md); read it before changing this package or any v0 +component. v0 is maintenance-only (R-V0-001). -## Recognizing v0 code +## Package boundaries -Treat code as v0 when several of these are present: - -- imports from `@uifabricshared/foundation-compose`, - `foundation-composable`, `foundation-settings`, or `foundation-tokens`; -- a descriptor such as `IButtonType` with `props`, `tokens`, `slotProps`, - optional `state`, and optional `statics`; -- `compose({...})`, `IUseComposeStyling`, - `ISlots<...>`, and `mergeSettings(...)`; -- settings shaped as `{ tokens, root, ...slots, _overrides, _precedence }`; -- an implementation under `deprecated/` or `legacy/`, or an old un-suffixed - export beside a newer `V1` export. - -Representative implementations are -`packages/components/Button/src/deprecated/Button.tsx`, -`packages/components/Checkbox/src/deprecated/Checkbox.tsx`, -`packages/components/Link/src/legacy/Link.tsx`, -`packages/components/Persona/src/Persona.tsx`, and -`packages/components/Stack/src/Stack.tsx`. - -## Framework model and public API - -`compose` combines the lower-level composable, themed-settings, settings, and -token systems. Its phases are: - -1. resolve and cache layered `settings`; -2. resolve token props into cached slot props/styles through `styles`; -3. run `usePrepareProps` for hooks, state, refs, and instance-specific props; -4. call the hook-free `render` function with prepared `Slots`. - -The package publicly exports `compose`, `initializeStyling`, and the compose -option/return/settings/styling and type-extraction types listed in `src/index.ts`. -The returned component exposes `__composable`, `.compose(options)`, and -`.customize(...settings)`. Prefer the strongly typed component methods: - -- `.compose(...)` replaces or augments behavior, slots, styles, settings, or - render without adding a React wrapper. Settings are appended to the base - settings. See - `packages/components/Button/src/deprecated/PrimaryButton/PrimaryButton.ts`. -- `.customize(...)` only appends old-style settings entries. Its input retains - `tokens`, `_overrides`, `_precedence`, and slot-prop keys; it is not the newer - flat token customization format. -- `statics` attaches subcomponents, as in `Stack.Item`. - -The exported two-argument `compose(options, base)` form is the underlying API; -use `Base.compose(options)` when the base is already a typed composed component. -`initializeStyling` is framework infrastructure that `compose` installs -automatically, not a routine component authors normally call. - -## Component shape - -Define a shared descriptor type and keep slot names aligned: - -```ts -interface IExampleType { - props: IExampleProps; - tokens: IExampleTokens; - slotProps: IExampleSlotProps; // must contain root - state: IExampleState; -} - -export const Example = compose({ - displayName: exampleName, - settings, - usePrepareProps, - render, - slots: { root: View, content: Text }, - styles: { root: [backgroundColorTokens], content: [textTokens] }, -}); -``` - -Typical files are `.types.ts`, `.settings.ts`, -`.tokens*.ts`, `.tsx`, `index.ts`, and colocated Jest -tests/snapshots. Preserve existing public I-prefixed types, deprecated exports, -display names, prop names, and v0/v1 aliases. - -For a one-slot component, `compose` supplies default preparation and rendering: -it applies styling and user props to `root`. See -`packages/components/Text/src/deprecated/Text.tsx` and -`packages/components/Stack/src/StackItem/StackItem.tsx`. Multiple slots require -an explicit `render`. Once custom `usePrepareProps` is supplied, it must put the -public/native props on the appropriate slot; the framework does not do that -automatically. - -## Slots and rendering - -- `slotProps` is a named props collection with a required `root`. -- Define each slot as a component/string or as `{ slotType, filter }`. Use - existing filters such as `filterViewProps`, `filterTextProps`, and - `filterImageProps` so component-only/token props do not reach native views. -- Merge prepared props with styling, normally - `mergeSettings(useStyling(userProps, lookup), { root: rootProps, ... })`. - Later entries win, including merged styles and class names. -- Render prepared slots as `` / ``. Their prepared - props are already captured; pass only additional render-time props in JSX. -- Children are removed before preparation and supplied to `render` as - `...children`. Decide explicitly where they belong, as Button, Link, and Stack - do. Conditional slot rendering is safe because all hooks already ran. -- Put `/** @jsxImportSource @fluentui-react-native/framework-base */` first in - files that render callable `Slots` with JSX. This pragma supports the legacy - slot runtime; it does not make the component a modern framework-base pattern. - -## Settings, tokens, and styling - -- Type settings as `IComposeSettings`. Entries are layered in - array order and may be a settings object, a theme settings name string, or a - `(theme) => settings` function. Keep objects/functions stable and never mutate - cached settings. -- Use `_precedence` to define state application order and `_overrides` for - state-specific settings; nested overrides are supported. Settings names such - as `buttonName` load theme-provided component settings. -- `useStyling(props)` uses props as the default override mask. For derived - interaction state, pass an override lookup function or mask, as in legacy - Button, Checkbox, and Link. -- Declare token-to-slot processing in `styles`. Reuse operation sets such as - `backgroundColorTokens`, `borderTokens`, `foregroundColorTokens`, and - `textTokens`; use `{ source, target, lookup }` for mappings/theme lookup. -- For component-specific calculations, use `styleFunction` and list every token - key that affects the result. See - `packages/components/Persona/src/Persona.tokens.root.ts`, - `packages/components/Persona/src/Persona.tokens.texts.ts`, and - `packages/components/Stack/src/Stack.tokens.ts`. Accurate key lists are - required for memoization. -- When a slot is itself composed and supports a token, token processing can - forward that token to the child instead of producing a parent-owned style. - Preserve compatible child token contracts when replacing composed slots. -- Put theme/default/state values in settings and token-derived style in - `styles`. Put highly variable values such as text, accessibility props, - handlers, sources, and refs into `usePrepareProps` slot props. - -## State, hooks, and refs - -- `usePrepareProps` and `useStyling` are hook phases: call them on every render - and never conditionally. Hooks, context, controlled/uncontrolled state, and - interaction hooks belong in `usePrepareProps`. -- Return `{ slotProps, state }`; `state` is simply the typed payload for - `render`, not necessarily React state. Keep `render` hook-free. -- Preserve the v0 `componentRef` API where present. Convert it to the native root - `ref` during preparation, commonly with `useViewCommandFocus`; type ref-bearing - roots with `React.PropsWithRef<...>`. For imperative native APIs, use an - internal native ref plus `useImperativeHandle`, as in - `packages/agentic/native-core/src/legacy/callout/Callout.tsx`. -- Do not introduce `forwardRef` or a new public `ref` convention into an - established v0 API without an explicit migration requirement. - -## Platform-specific implementations - -Use normal React Native resolution for `.android.tsx`, `.macos.tsx`, -`.windows.tsx`, and `.win32.tsx`, and for platform settings files. Keep the same -public descriptor/props across variants while allowing different native slots -and preparation logic. Examples include deprecated Button on Android and -RadioButton on macOS/Win32. Keep imports from platform React Native forks inside -their platform files, per the repository root guidance. - -## Legacy boundaries - -- Do not rewrite v0 code to `@fluentui-react-native/composition`, modern - `@fluentui-react-native/framework`, or framework-base - `phasedComponent`/`directComponent`/`useSlot` patterns as part of maintenance. -- Do not apply v1 slot types, flat state tokens, `useTokens`, or the new - `.customize({ token: value, state: {...} })` shape to v0 code. -- Do not “simplify” away `mergeSettings`, the settings name entry, override - lookup, prop filters, token key lists, or the split prepare/render phases; - each participates in compatibility, composition, or caching. -- Prefer surgical fixes. Migration to v1 is a separate task and must account for - documented prop, token, slot, and export differences such as - `packages/components/Button/MIGRATION.md`. +- `compose` combines `foundation-composable`, themed settings, `foundation-settings`, and `foundation-tokens`. + Changes to settings override or merge semantics, or to token processing, also require those packages' tests. +- The package publicly exports `compose`, `initializeStyling`, and the compose option, return, settings, styling, and + type-extraction types listed in `src/index.ts`. `initializeStyling` is infrastructure that `compose` installs + automatically. +- Preserve settings layering, filters, token key lists, and the prepare/render split that consumers rely on (R-V0-002). ## Validation -Run the smallest package-scoped checks covering the change: - ```bash yarn workspace @uifabricshared/foundation-compose build yarn workspace @uifabricshared/foundation-compose lint +yarn workspace @uifabricshared/foundation-compose test ``` -For behavior changes, also test/build the owning consumer package, for example: - -```bash -yarn workspace @fluentui-react-native/button test -yarn workspace @fluentui-react-native/button build -``` - -Update existing snapshots only after reviewing the rendered slot tree. Changes -to settings override/merge semantics or token processing also require the -`foundation-settings` or `foundation-tokens` unit tests respectively. +For behavior changes, also build and test affected consumer packages, such as `@fluentui-react-native/button`. diff --git a/packages/framework-base/AGENTS.md b/packages/framework-base/AGENTS.md index d565058a9b..571d1784ce 100644 --- a/packages/framework-base/AGENTS.md +++ b/packages/framework-base/AGENTS.md @@ -51,7 +51,7 @@ entrypoint's React-only import safety. ## Focus and native refs -Use the [common focus instructions](../../.github/skills/agentic-component-authoring/references/focus.md) +Use the [common focus instructions](../../references/agents/focus/common.md) and their platform breakouts when adding focus-target or interaction abstractions. Reuse slot ref composition and cleanup; preserve this package's older React compatibility while agentic components use React 19 ref props. Keep proposed diff --git a/packages/framework-base/src/component-patterns/README.md b/packages/framework-base/src/component-patterns/README.md index 127db2d0a0..c148ef0b92 100644 --- a/packages/framework-base/src/component-patterns/README.md +++ b/packages/framework-base/src/component-patterns/README.md @@ -30,7 +30,7 @@ from the first phase. Preserve their `focusTargetRef` separately from the consumer ref, then compose it through the native slot in the render phase. The public `FocusTarget` reports requested versus confirmed focus and cancels requests on detach/disable; do not infer focus from calling a native method. -See the [desktop focus authoring guide](../../../../.github/skills/agentic-component-authoring/references/focus.md) +See the [desktop focus authoring guide](../../../../references/agents/focus/common.md) for target lifetime, platform activation, and renderer boundaries. | Need | Pattern | diff --git a/packages/framework/framework/AGENTS.md b/packages/framework/framework/AGENTS.md index 7b8fd69039..3124f2c8d9 100644 --- a/packages/framework/framework/AGENTS.md +++ b/packages/framework/framework/AGENTS.md @@ -1,230 +1,21 @@ # v1 component framework -These instructions apply to `packages/framework/framework` and to components -authored with `@fluentui-react-native/framework`. They describe the repository's -v1 compose/compressible framework. Preserve this model when maintaining v1 code; -do not mix it with either legacy v0 or the newer framework-base component model. +These instructions apply to `packages/framework/framework`, which implements the repository's v1 `compose` and +`compressible` framework. The consumer recipe, recognition guide, and review checklist for v1 components live in the +shared [v1 reference](../../../references/agents/components/v1.md); read it before changing this package or any v1 +component. v1 is maintenance-only (R-V1-001). -## Recognizing v1 code +## Package boundaries -Treat a component as v1 when several of these are present: +- `compose` injects the Fluent `Theme` integration into `composeFactory` from `@fluentui-react-native/composition`. + Keep theme-agnostic factory work in that lower-level package. +- Styling and slots come from `use-styling`, `use-tokens`, and `use-slots`. Changes to factory, slot, token, or styling + semantics also require the matching package's tests. +- v1 is built on framework-base's compatibility `stagedComponent` path. Do not migrate it to the modern framework-base + patterns as part of maintenance (R-V1-002). +- Check `src/index.ts` before adding another public export, and keep exports explicit (R-REPO-001). -- imports of `compose`, `compressible`, `UseSlots`, `UseStylingOptions`, - `buildProps`, `buildUseTokens`, or `useFluentTheme` from - `@fluentui-react-native/framework`; -- a descriptor such as `ButtonType` with `props`, `tokens`, and `slotProps`; -- `compose({ slots, slotProps, tokens, states, useRender })`; -- a styling file whose tokens contain nested state layers such as `hovered`, - `pressed`, `disabled`, size, appearance, or shape; -- a public `V1` export beside a legacy unsuffixed export, for example - `ButtonV1`, `CheckboxV1`, `TextV1`, or `IconV1`. - -Representative compose components are -`packages/components/Button/src/Button.tsx`, -`packages/components/Checkbox/src/Checkbox.tsx`, -`packages/components/Avatar/src/Avatar.tsx`, and -`packages/components/Menu/src/MenuItem/MenuItem.tsx`. Representative -compressible components are `packages/components/Text/src/Text.tsx`, -`packages/components/Divider/src/Divider.tsx`, and -`packages/components/TabList/src/Tab/Tab.tsx`. - -Do not classify code by the JSX pragma alone. V0 and v1 components both use -`/** @jsxImportSource @fluentui-react-native/framework-base */` so callable slots -can render without extra wrappers. - -## Framework boundaries - -- **V0/legacy:** imports `@uifabricshared/foundation-compose` and uses - `settings`, `styles`, `usePrepareProps`, `render`, `mergeSettings`, - `_overrides`, and `_precedence`. Follow - `packages/deprecated/foundation-compose/AGENTS.md`; do not apply those shapes - to v1. -- **V1/this framework:** `compose` wraps - `packages/framework/composition`, while styling and slots come from - `use-styling`, `use-tokens`, and `use-slots`. Its component hook is - `useRender`, its customization input is a flat token object, and its state - layers are nested token objects. -- **Modern framework-base/agent-generation work:** `phasedComponent`, modern - `directComponent`, `Slot`, `OptionalSlot`, `ComponentProps`, and - public slot shorthand/`as` props are a different component-authoring model. - Framework-base utilities such as `mergeProps`, `useSlot`, `directComponent`, - and the JSX runtime may appear inside v1 implementations for compatibility; - that does not convert the component to the new model. - -Do not migrate between these frameworks during a maintenance change unless the -task explicitly requests a migration. In particular, v1 is built on the -compatibility `stagedComponent` path even though new framework-base components -must not add new staged components. - -## Framework model and public API - -`compose` injects the Fluent `Theme` integration into `composeFactory`. The -factory: - -1. resolves tokens and token-derived props through `buildUseStyling`; -2. creates all callable slots through `buildUseSlots`; -3. wraps `useRender` in a staged component; -4. attaches `.customize(...)`, `.compose(...)`, `displayName`, `__options`, and - any declared statics. - -Use the `@fluentui-react-native/framework` entry point from components. Import -`composeFactory` directly from `@fluentui-react-native/composition` only when -working on the theme-agnostic lower-level factory. - -- `.customize(...tokenSettings)` appends token layers. Inputs may be token - objects, theme functions, or component names. Define customized components at - module scope, not during render. -- `.compose(partialOptions)` deep-merges factory options and appends `tokens`. - Use it to replace slots, slot props, state declarations, or `useRender` - without adding a React wrapper. See the compose tests in Button and Checkbox. -- `buildUseTokens` and `buildUseStyling` are Fluent-theme-specialized wrappers - around the lower-level packages. `useFluentTheme` returns the context theme or - `defaultFluentTheme`. - -The package also re-exports established token builders, memo/merge helpers, -slot helpers, and theme types. Check `src/index.ts` before adding another public -export; keep exports explicit. - -## Compose component shape - -Keep the descriptor, styling, state hook, and renderer aligned: - -```tsx -interface ExampleType { - props: ExampleProps; - tokens: ExampleTokens; - slotProps: ExampleSlotProps; -} - -export const Example = compose({ - displayName: exampleName, - ...stylingSettings, - slots: { root: Pressable, content: Text }, - useRender: (userProps, useSlots) => { - const example = useExample(userProps); - const Slots = useSlots(userProps, (layer) => example.state[layer] || userProps[layer]); - - return (final, ...children) => { - const mergedProps = mergeProps(example.props, final); - return ( - - {children} - - ); - }; - }, -}); -``` - -`ComposeType` is available, but most current components use a named interface. -Only `props`, `slotProps`, `tokens`, and optional `statics` are extracted by the -factory. Extra descriptor fields such as `state` are documentation/type-sharing -conveniences, not factory-managed state. - -Typical files are `.types.ts`, `Tokens.ts` plus platform -variants, `.styling.ts`, `use.ts`, `.tsx`, -`index.ts`, tests/snapshots, and migration/spec docs where applicable. Preserve -existing v0/v1 export aliases and package API compatibility. - -## Slots and staged rendering - -- Keep every key in `slots`, `slotProps`, and the descriptor's `slotProps` - synchronized. Type ref-bearing roots with `React.PropsWithRef<...>`. -- Call `useSlots` once, unconditionally, in the hook stage. `buildUseSlots` - creates every slot with `useSlot`; conditional slot rendering belongs in the - returned hook-free render stage. -- Styled slot props are captured when slots are created. Props supplied in JSX - are merged on top. Merge prepared component props with final composition props - using `mergeProps`; do not mutate either object. -- Decide explicitly where children render. The traditional v1 continuation is - `(finalProps, ...children) => JSX`. Some transitional components return a - hook-free `directComponent(({ children, ...finalProps }) => JSX)` instead. - Never call hooks in either final render form. -- `filters` are supported by `use-slots`, but current v1 consumers rarely use - them. Preserve an existing filter contract; do not copy v0 - `{ slotType, filter }` slot descriptors into v1. -- Platform-only slots may be declared conditionally, as Button does for - `rippleContainer` and `focusInnerBorder`. Create slots consistently and render - them only on the matching platform. - -Use `compressible` for the established lighter-weight v1 pattern when a component -manually resolves tokens and creates slots rather than needing the full compose -options object. `compressible` also wraps a staged component and adds -`.customize(...)`; it does not expose `.compose(...)`. - -In a compressible component: - -1. call hooks, `useFluentTheme`, and `useTokens(theme)` in the outer function; -2. use `applyTokenLayers`, `applyPropsToTokens`, or `patchTokens` when state/props - must alter resolved tokens; -3. build all slots with `useSlot`; -4. return a hook-free continuation and merge final props there. - -Follow existing examples rather than calling `stagedComponent` directly. - -## Tokens, styling, and state precedence - -- Define defaults as stable objects or theme functions, then place the component - name last, for example - `tokens: [defaultButtonTokens, defaultButtonFontTokens, buttonName]`. The name - loads `theme.components[name]` overrides. -- Model visual states as nested values of the same token type. List them in - `states` in application order; later active layers win. -- Supply a lookup to `useSlots` when state is derived from interaction hooks, - enum props, context, content, or platform defaults. Button's `buttonLookup` - and Avatar's `avatarLookup` are representative. -- `tokensThatAreAlsoProps` is opt-in. After state layers are applied, listed - props override those token values. Prefer a precise key list over `'all'` to - retain caching and avoid leaking unrelated props into tokens. -- Build each slot's cached props with `buildProps`. List every token key read by - the builder, including helper key arrays such as `fontStyles.keys`, - `borderStyles.keys`, and `layoutStyles.keys`. -- Treat resolved tokens and cached slot props as immutable. Use `patchTokens` - rather than mutation for instance-dependent values in compressible components. -- Keep theme/default/state values in token files, token-to-native mapping in - styling files, and volatile handlers/accessibility/data/ref props in the - component state hook or render preparation. - -## Hooks, state, and refs - -Put controlled/uncontrolled state, context, interaction hooks, callbacks, -accessibility normalization, and ref preparation in `use` or the -outer `useRender`/compressible stage. Return a conventional `{ props, state }` -object when the component has derived behavior; `state` is component-owned data -used by the token lookup and final render. - -Preserve established `componentRef` APIs. Convert `componentRef` to the native -root `ref` during preparation, commonly with `useViewCommandFocus`, as Button, -Checkbox, and MenuItem do. Do not introduce `forwardRef` or replace -`componentRef` with a public `ref` as an incidental change. - -Hooks must run in a stable order before the continuation is returned. Never call -hooks inside state lookups, `buildProps` callbacks, conditional slot branches, -or the final render stage. - -## Platform-specific behavior - -Use React Native platform resolution and keep the public props/type/export -contract consistent across `.android`, `.ios`, `.macos`, `.mobile`, `.windows`, -and `.win32` implementations. Platform variants may replace the root with a -native component and use a one-slot compose implementation; see -`packages/experimental/Checkbox/src/Checkbox.macos.tsx` and -`packages/experimental/Avatar/src/NativeAvatar.tsx`. - -Keep platform defaults and token differences in platform token/styling files -when practical. Keep imports from React Native forks inside their platform files -and retain the repository's `Platform.OS === ('win32' as any)` convention. - -## Testing and validation - -Add or update the owning component's Jest tests and snapshots for rendering, -state layers, `.customize`, `.compose`, slot replacement, accessibility, and -platform behavior affected by a change. Review snapshot trees rather than -updating them blindly. The framework package's direct test covers compressible; -lower-level compose, slot, token, and styling behavior is tested in the -corresponding framework packages. - -Run the smallest relevant package checks: +## Validation ```bash yarn workspace @fluentui-react-native/framework build @@ -232,13 +23,6 @@ yarn workspace @fluentui-react-native/framework test yarn workspace @fluentui-react-native/framework lint ``` -For component changes, also build/test the owning package, for example: - -```bash -yarn workspace @fluentui-react-native/button build -yarn workspace @fluentui-react-native/button test -``` - -Changes to factory, slot, token, or styling semantics also require the matching -`@fluentui-react-native/composition`, `use-slots`, `use-tokens`, or -`use-styling` package tests. +The package's direct test covers `compressible`; lower-level compose, slot, token, and styling behavior is tested in +`@fluentui-react-native/composition`, `use-slots`, `use-tokens`, and `use-styling`. Also build and test affected +consumer components, such as `@fluentui-react-native/button`. diff --git a/references/agents/README.md b/references/agents/README.md new file mode 100644 index 0000000000..5f322cab6d --- /dev/null +++ b/references/agents/README.md @@ -0,0 +1,106 @@ +--- +id: index +kind: index +owner_paths: + - references/agents +--- + +# Agent references + +Shared, tool-neutral knowledge for agents working in this repository. The root [AGENTS.md](../../AGENTS.md) and scoped +`AGENTS.md` files route here, and skills in [`.github/skills`](../../.github/skills) own task workflows. Read only the +documents a task needs; links are navigation, not instructions to load everything they reach. + +## Taxonomy + +| Area | Contents | +| ----------------------------------------------------------------------------- | --------------------------------------------------------------------------- | +| [`repository/`](repository) | Architecture, commands and validation, TypeScript, permissions, and release | +| [`design/`](design) | Tokens, theming, and color utilities | +| [`components/`](components) | Classification, v0 and v1 recipes, and legacy test apps | +| [`components/agentic/`](components/agentic) | Agentic component recipes | +| [`components/agentic/contract-sources/`](components/agentic/contract-sources) | Source adapters for agentic component contracts | +| [`focus/`](focus) | Desktop focus behavior across component generations | +| [`native/`](native) | Native components, modules, and platform guidance | +| [`evals/`](evals/README.md) | Portable evaluation case fixtures; not instructions | + +## Document index + +| ID | Document | Read when | +| -------------------------------------------------------- | ------------------------------------------------------------------------------- | ------------------------------------------------------------------------------- | +| `repository.architecture` | [Architecture](repository/architecture.md) | Orienting in the monorepo or choosing where code belongs | +| `repository.development` | [Commands and validation](repository/development.md) | Building, testing, linting, validating, or adding a package | +| `repository.typescript` | [TypeScript](repository/typescript.md) | Changing compiler settings, platform types, or the JSX runtime | +| `repository.permissions` | [Agent permissions](repository/permissions.md) | Using `gh api`, Pod, process, loopback, or capture operations; changing policy | +| `repository.release` | [Changesets and release](repository/release.md) | Changing a published package or release tooling | +| `design.tokens-and-theming` | [Tokens and theming](design/tokens-and-theming.md) | Mapping tokens, theme-dependent styles, or color utilities | +| `components.classification` | [Classification and routing](components/classification.md) | Starting any component task | +| `components.v0` | [v0 maintenance](components/v0.md) | Changing or reviewing `foundation-compose` components | +| `components.v1` | [v1 maintenance](components/v1.md) | Changing or reviewing `compose` or `compressible` components | +| `components.legacy-test-apps` | [FluentTester and E2E](components/legacy-test-apps.md) | Changing v0 or v1 test pages or E2E specs | +| `components.agentic.types-and-slots` | [Types and slots](components/agentic/types-and-slots.md) | Agentic public props, slots, resolved state, native props, or exports | +| `components.agentic.state-and-accessibility` | [State and accessibility](components/agentic/state-and-accessibility.md) | Agentic state hooks, defaults, interaction, or accessibility | +| `components.agentic.styles-and-tokens` | [Styles and tokens](components/agentic/styles-and-tokens.md) | Agentic styles, token mapping, theme caching, or state precedence | +| `components.agentic.rendering` | [Rendering and assembly](components/agentic/rendering.md) | Agentic render functions, assembly, or display names | +| `components.agentic.tests-and-stories` | [Tests and stories](components/agentic/tests-and-stories.md) | Agentic tests, stories, `wdio` story tests, or validation | +| `components.agentic.contract-sources.flex-x3` | [Flex and X3](components/agentic/contract-sources/flex-x3.md) | Contract evidence from the pinned Flex catalog | +| `components.agentic.contract-sources.fluent-v9` | [Fluent v9](components/agentic/contract-sources/fluent-v9.md) | Contract evidence from Fluent UI v9 source or specifications | +| `components.agentic.contract-sources.html-css` | [HTML and CSS](components/agentic/contract-sources/html-css.md) | Contract evidence from HTML, CSS, or browser output | +| `components.agentic.contract-sources.visual-reference` | [Visual reference](components/agentic/contract-sources/visual-reference.md) | Contract evidence from images, screenshots, or renders | +| `components.agentic.contract-sources.local-foundation` | [Local foundation](components/agentic/contract-sources/local-foundation.md) | Foundational components without a Flex catalog entry | +| `components.agentic.contract-sources.composite-evidence` | [Composite evidence](components/agentic/contract-sources/composite-evidence.md) | Contracts combining more than one source set | +| `focus.common` | [Common focus authoring](focus/common.md) | Any focusable component, collection, popup, focus hook, or native focus adapter | +| `focus.windows-win32` | [Windows and Win32 focus](focus/windows-win32.md) | Focus work on React Native Windows or Win32 | +| `focus.macos` | [macOS focus](focus/macos.md) | Focus work on macOS | +| `native.components-and-modules` | [Native components and modules](native/components-and-modules.md) | Any native component, module, service, or native-backed wrapper | +| `native.windows-fabric` | [Windows Fabric native components](native/windows-fabric.md) | React Native Windows Fabric views, codegen, registration, or UIA | + +## Placement and extension + +- Put guidance here when more than one skill, reviewer, scoped `AGENTS.md`, or evaluation uses it, or when it is general + repository knowledge. Keep guidance in a skill when only that workflow uses it. +- Keep product contracts (`SPEC.md`, `spec/`, `CONTRACT.md`), protocols, token maps, and package documentation with + their packages, and link to them. Owner-local instruction trees, such as the desktop driver's `references/`, stay + with their packages. +- Place a new document in the narrowest existing area. Create a new area only for a distinct domain, and add it to the + taxonomy table. Add every new document to the document index with a concrete "Read when" condition. +- When moving a document, keep its `id`, update inbound links, and leave a `compatibility-reference` at the old path + that links directly to the new location. + +## Frontmatter standard + +Every Markdown file in this directory, and every Markdown file under a skill's `references/` directory, starts with +exactly these keys in this order: + +```yaml +--- +id: components.v1 +kind: instruction-reference +owner_paths: + - packages/framework/framework +--- +``` + +- `id`: lowercase, dot-separated segments of letters, digits, and hyphens. IDs are unique and permanent, including + across moves. +- `kind`: `index` for directory indexes, `instruction-reference` for guidance agents follow, `evidence-reference` for + version-bound observations, or `compatibility-reference` for a redirect left at a retired path. +- `owner_paths`: one or more existing repository paths whose maintainers own the technical content. + +Do not add other keys. Load conditions belong in this index and in routers, not in frontmatter. + +## Rule and claim IDs + +- `R--` identifies a consequential, testable obligation, such as `R-V1-003`. Define it once, in an + instruction file, as a list item that begins with the bold ID and a colon. Elsewhere, cite the ID. +- `C--` identifies a version-bound observation. Its definition states the source, inspected version or + revision, and date. +- Assign IDs only where an implementation, review, or evaluation needs to cite the obligation. Never renumber or reuse + an ID; retire it by removing its definition and every citation. +- These IDs cannot collide with component requirement IDs such as `BTN-006`, which belong to component contracts. + +## Validation + +`yarn check-agent-instructions` validates frontmatter, links and fragments in instruction files, inbound fragments to +instruction files, skill metadata, rule definitions and citations, this index, and the evaluation fixtures. It also +runs in `yarn lage buildci`. diff --git a/references/agents/components/agentic/contract-sources/composite-evidence.md b/references/agents/components/agentic/contract-sources/composite-evidence.md new file mode 100644 index 0000000000..8063da57e1 --- /dev/null +++ b/references/agents/components/agentic/contract-sources/composite-evidence.md @@ -0,0 +1,43 @@ +--- +id: components.agentic.contract-sources.composite-evidence +kind: instruction-reference +owner_paths: + - packages/agentic/components +--- + +# Composite evidence adapter + +Use multiple `sources[]` entries when no single source can establish the full +React Native contract. Composite evidence is a resolution method, not a source +kind. + +## Resolve authority per requirement + +For every requirement, list the sorted source IDs that govern or support it. +Do not assign one global precedence order to an entire component. A common +division is: + +| Requirement area | Likely authority | +| ------------------------ | ---------------------------------------------------- | +| design intent and tokens | normative design source plus token reference | +| public API and state | product specification or reviewed behavior reference | +| native semantics | React Native/platform contract | +| visual fidelity | normative design source plus visual evidence | +| compatibility | existing FURN implementation | + +One source may be authoritative for one requirement and merely corroborating +for another. If sources disagree, record the conflicting claims, chosen +resolution, rationale, and divergence or follow-up issue. If evidence is +insufficient, keep the behavior unknown or deferred. + +## Keep the contract reviewable + +- Give each source a stable ID and use the source-specific adapter schema. +- Keep `sources[]` sorted and unique. +- Require every requirement to identify at least one source when more than one + source is present. +- Do not duplicate one artifact across source entries to manufacture + corroboration. +- Re-review only the requirements affected by changed evidence, but keep the + document-level conformance state at `review-required` until the combined + contract is coherent again. diff --git a/references/agents/components/agentic/contract-sources/flex-x3.md b/references/agents/components/agentic/contract-sources/flex-x3.md new file mode 100644 index 0000000000..25c32e66f5 --- /dev/null +++ b/references/agents/components/agentic/contract-sources/flex-x3.md @@ -0,0 +1,53 @@ +--- +id: components.agentic.contract-sources.flex-x3 +kind: instruction-reference +owner_paths: + - packages/agentic/components +--- + +# Flex and X3 source adapter + +Use this adapter for a component in the pinned Flex catalog. Flex skills are +authoring inputs; the immutable X3 lineage identifies their original design +source, and the reviewed local contract defines FURN behavior. + +## Resolve the source + +Start Agency with the repository's `flex-authoring` profile and invoke +`flex-components:`. Confirm that the profile and +`packages/agentic/components/spec-source-lock.json` identify the same release. +Read shared and usage material plus the relevant platform companions. Do not +combine files from different releases or use one platform as an implicit +fallback for another. + +Generate or refresh the `flex-skill` entry with: + +```sh +yarn workspace @fluentui-react-native/components report:spec-source-drift --write --update-sources --component +``` + +The entry records `authority: normative`, the skill ID, source-lock identity, +available and consulted surfaces, immutable Marketplace/X3 file identities, +and release differences. Availability is generated; consultation is a +reviewer's assertion and remains empty after a source identity changes. + +## Adapt to React Native + +Classify source guidance as adopted, adapted, intentionally divergent, not +applicable, or deferred. Consult the Flex token map and the closest Win32, +macOS, or Windows FURN implementation for compatibility; do not use iOS as the +cross-platform canonical reference unless the work targets iOS. + +Translate web concepts deliberately: + +| Flex evidence | React Native contract | +| ------------------------------------ | ---------------------------------------------------------------------------------------- | +| ARIA attributes or native elements | `accessibilityRole`, `accessibilityState`, labels, and UIA/AX expectations | +| CSS pseudo-classes | explicit interaction state and precedence | +| browser focus selectors and outlines | shared root-modality policy and optional `FocusRing`, persistent only on the custom path | +| CSS pixel or layout rules | React Native units, minimum targets, and platform behavior | +| unsupported browser capability | explicit divergence, dependency, or blocker | + +Do not copy Flex prose or token tables into the public package. A mutable +Marketplace or X3 HEAD change is candidate drift and does not invalidate a +contract ratified against the pinned release. diff --git a/references/agents/components/agentic/contract-sources/fluent-v9.md b/references/agents/components/agentic/contract-sources/fluent-v9.md new file mode 100644 index 0000000000..72015fe93b --- /dev/null +++ b/references/agents/components/agentic/contract-sources/fluent-v9.md @@ -0,0 +1,37 @@ +--- +id: components.agentic.contract-sources.fluent-v9 +kind: instruction-reference +owner_paths: + - packages/agentic/components +--- + +# Fluent v9 source adapter + +Use Fluent UI v9 as API, behavior, and design precedent, not as a DOM contract +for React Native. + +## Record provenance + +Pin the exact repository and full commit SHA. Record a `git-files` source with +sorted source/spec paths, semantic roles, and SHA-256 digests. Use an authority +such as `behavior-reference`, `compatibility-reference`, `token-reference`, or +`implementation-evidence` that describes what the selected files can govern. +Do not cite a mutable branch or package version without its immutable commit. + +## Interpret the source + +Inspect public types, state hooks, render structure, styles, tests, stories, and +published specifications as separate evidence surfaces. Distinguish intended +public behavior from incidental implementation details. + +- Preserve portable API names and state ownership when they fit native usage. +- Translate DOM elements and ARIA to React Native roles, states, labels, and + platform accessibility expectations. +- Translate CSS inheritance, selectors, and pseudo-classes into explicit + tokens, state precedence, and native layout. +- Treat browser focus, keyboard, pointer, and hover behavior as evidence to + adapt, not behavior to copy mechanically. +- Record unsupported or intentionally different behavior as a divergence. + +When Fluent v9 is combined with another design source, assign its source ID +only to the requirements for which it is authoritative or informative. diff --git a/references/agents/components/agentic/contract-sources/html-css.md b/references/agents/components/agentic/contract-sources/html-css.md new file mode 100644 index 0000000000..8c805fec99 --- /dev/null +++ b/references/agents/components/agentic/contract-sources/html-css.md @@ -0,0 +1,42 @@ +--- +id: components.agentic.contract-sources.html-css +kind: instruction-reference +owner_paths: + - packages/agentic/components +--- + +# HTML and CSS source adapter + +Use HTML/CSS evidence to recover observable anatomy, content flow, layout, +computed values, and visual state differences. It does not by itself establish +native semantics, controlled-state ownership, event contracts, or platform +accessibility. + +## Capture reproducible evidence + +Record an `html-css` source with its origin and sorted immutable artifacts. +Supported artifacts are HTML, CSS, computed-style captures, accessibility-tree +captures, and screenshots. Each artifact needs a stable ID, kind, location, +and SHA-256 digest. Store only evidence that may be redistributed; otherwise +use an HTTPS identity and retain restricted material outside the public +repository. + +Capture the relevant matrix when available: + +- variants, sizes, appearances, and interaction states; +- viewport, zoom or scale, color scheme, direction, and locale; +- inherited versus explicitly assigned typography and color; +- computed dimensions, spacing, wrapping, truncation, and overflow; +- browser accessibility output separately from visual output. + +## Adapt the evidence + +Map element anatomy to typed React Native slots only when consumers need the +slot. Convert CSS inheritance into explicit theme/token defaults at the owning +native boundary. Translate pseudo-classes to component state and precedence. +Treat layout values as observations, then choose the appropriate React Native +layout and minimum-target behavior. + +Mark semantics, keyboard behavior, motion, state ownership, and inaccessible +states as unknown unless another source establishes them. Never infer an API +solely from class names, data attributes, or a single rendered DOM snapshot. diff --git a/references/agents/components/agentic/contract-sources/local-foundation.md b/references/agents/components/agentic/contract-sources/local-foundation.md new file mode 100644 index 0000000000..f66774eaeb --- /dev/null +++ b/references/agents/components/agentic/contract-sources/local-foundation.md @@ -0,0 +1,29 @@ +--- +id: components.agentic.contract-sources.local-foundation +kind: instruction-reference +owner_paths: + - packages/agentic/components +--- + +# Local foundation source adapter + +Use `local-foundation` only for a theme-aware foundational component that has +no entry in the locked Flex component catalog. This is not a bypass for missing +Flex access or an alternative way to author a catalog component. + +The source entry is the contract's sole source and uses +`authority: normative`. Its sorted references identify the public native +platform contract and repository precedents used during review. Give each +reference its own authority, such as `platform-contract`, +`compatibility-reference`, or `token-reference`. + +Inspect the current React Native API for the package's supported version, +relevant native behavior, existing FURN components, and token mappings. Define +the smallest theme-aware primitive contract needed by other components. Keep +web inheritance or browser behavior out of the contract unless React Native +provides an equivalent. + +Local-foundation contracts still require lifecycle, review, divergence, +requirement, implementation, test, story, and platform evidence. Flex release +and candidate drift are `not-applicable`. The checker rejects this source kind +if the locked Flex catalog contains the component. diff --git a/references/agents/components/agentic/contract-sources/visual-reference.md b/references/agents/components/agentic/contract-sources/visual-reference.md new file mode 100644 index 0000000000..b7618b8bce --- /dev/null +++ b/references/agents/components/agentic/contract-sources/visual-reference.md @@ -0,0 +1,42 @@ +--- +id: components.agentic.contract-sources.visual-reference +kind: instruction-reference +owner_paths: + - packages/agentic/components +--- + +# Visual reference source adapter + +Use screenshots, design renders, and other images as visual evidence only. +They can establish observable anatomy, geometry, typography, color, +iconography, and differences among captured states. They cannot prove invisible +semantics, accessibility, events, keyboard behavior, motion, or controlled +state ownership. + +## Record the evidence + +Record a `visual-reference` source with `authority: visual-evidence`. Give each +artifact a stable ID, location, and SHA-256 digest. Add appearance, state, +platform, scale, viewport, and locale metadata when known. Repository-relative +artifacts are digest-verified by the contract checker; do not commit images +without redistribution permission. + +Inventory the capture matrix before interpreting it. A missing state is +unknown, not equivalent to the nearest visible state. Note cropping, +compression, scaling, overlays, device chrome, and any dimensions that prevent +reliable measurement. + +## Derive a contract conservatively + +- Describe anatomy and spatial relationships before choosing implementation + slots or layout primitives. +- Map visible values to the nearest established FURN tokens; record genuine + token gaps rather than inventing exact literals from noisy pixels. +- Require non-visual sources for public APIs, semantics, interactions, + accessibility, motion, and platform behavior. +- Express uncertain conclusions as open questions, deferred divergences, or + explicitly scoped requirements. + +When visual evidence conflicts with normative design or platform evidence, +record the conflict and resolve authority for the affected requirement rather +than silently selecting the image. diff --git a/references/agents/components/agentic/rendering.md b/references/agents/components/agentic/rendering.md new file mode 100644 index 0000000000..ce160a7170 --- /dev/null +++ b/references/agents/components/agentic/rendering.md @@ -0,0 +1,85 @@ +--- +id: components.agentic.rendering +kind: instruction-reference +owner_paths: + - packages/agentic/components +--- + +# Rendering and assembly + +Use this reference for `render.tsx`, `.ts`, slot ordering, conditional structure, display names, +and package exports. The canonical examples are +[`renderButton.tsx`](../../../../packages/agentic/components/src/components/button/renderButton.tsx) and +[`button.ts`](../../../../packages/agentic/components/src/components/button/button.ts). + +## Keep render functions pure + +The render function receives resolved state and returns the final element tree. + +- Add `/** @jsxImportSource @fluentui-react-native/framework-base */` when rendering slot components. +- Do not call hooks. +- Do not read themes or tokens. +- Do not create styles. +- Do not reinterpret defaults or accessibility. +- Do not mutate slot props. + +All those decisions belong to earlier stages. + +## Render slot functions directly + +Capitalize local optional slot variables when that improves readability. Render required roots through the state slot: + +```tsx +return {content}; +``` + +Keep slot order visible in JSX. Button renders its active icon before or after content according to `iconPosition`. +Avoid array construction or opaque helper loops when direct JSX makes the public ordering contract clearer. + +## Resolve conditional structure from state + +The render function may select among already-resolved slots and compose conditional layout: + +- Button chooses `selectedIcon` when selected and falls back to `icon`. +- Toggle content uses the private container, hidden ghost, and visible label. +- Ordinary content renders without that wrapper. + +Do not create missing slots in the render stage. If a structural element can exist, represent it in the state type and +construct it in the state hook. + +Components that own children should render only their declared slots. Do not spread native `children` into the tree and +let consumers bypass slot order. + +## Assemble the component in one small file + +`.ts` should show the pipeline without embedding stage logic: + +```ts +export const Component = (props: ComponentProps) => { + const state = useComponent_unstable(props); + useComponentStyles_unstable(state); + return renderComponent_unstable(state); +}; +``` + +Set `displayName` for diagnostics and Storybook metadata. Keep unstable stage functions named consistently so tests and +future composition work can identify them. Do not wrap the component in memoization or another boundary without measured +need and repository precedent. + +Under this package's React 19.1.4 baseline, `ref` is an ordinary component prop. Include it in the public root props and +let the state hook forward it to the resolved root slot; do not wrap assembly components in `forwardRef`. If rendering a +second internal ref on the same slot, pass it in JSX so the slot runtime composes it with the captured consumer ref. + +After assembly, export `use_unstable`, `useStyles_unstable`, and +`render_unstable` from the package root together with `State`, according to the +[types and slots export rules](types-and-slots.md#exports). These stages are intentionally unstable but public so other +components can reuse and extend the pipeline without importing internal paths. + +## Review checklist + +- Render contains JSX and conditional structure only. +- Slot order matches the spec. +- Optional and replacement slots fall back intentionally. +- State-only structure does not leak into public props. +- The assembly file is a readable state -> styles -> render pipeline. +- `displayName` is present and all three composition stages are exported under component-qualified unstable names. diff --git a/references/agents/components/agentic/state-and-accessibility.md b/references/agents/components/agentic/state-and-accessibility.md new file mode 100644 index 0000000000..0090d1bed6 --- /dev/null +++ b/references/agents/components/agentic/state-and-accessibility.md @@ -0,0 +1,197 @@ +--- +id: components.agentic.state-and-accessibility +kind: instruction-reference +owner_paths: + - packages/agentic/components +--- + +# State and accessibility + +Use this reference for `StateProps`, `use.ts`, defaults, interaction hooks, accessibility, derived +state, and slot construction. The canonical example is +[`useButton.ts`](../../../../packages/agentic/components/src/components/button/useButton.ts). + +## State hook responsibilities + +The state hook owns: + +1. Destructuring component-owned props while preserving unhandled native props. +2. Applying behavioral defaults. +3. Deriving facts needed by accessibility, styles, and rendering. +4. Validating development-time usage. +5. Reading theme state. +6. Configuring the native interaction hook. +7. Constructing required and optional slots. +8. Returning one complete resolved state object. + +Do not resolve token styles or render elements in this hook. + +## Preserve raw presence before applying defaults + +Derive presence-sensitive behavior from raw props. Button computes `hasContent`, `hasIcon`, and `hasSelectedIcon` before +constructing slots. It uses those values to derive `iconOnly`, while `selected !== undefined` determines whether toggle +semantics are enabled even when `selected` is false. + +Apply context-sensitive defaults only after those facts are known. Button defaults shape to `circle` for icon-only use +and `rounded` otherwise. + +## Decide who owns a stateful axis + +Before wiring any state, decide which of two kinds of axis you are building. Getting this wrong produces a component +that either cannot be used interactively or changes state when it should not. + +### Self-driving controls + +The interaction _is_ the state change. Pressing a checkbox checks it; pressing a switch flips it; pressing a disclosure +header expands it. There is no other reasonable interpretation of the press, and the control needs no outside +coordination to be correct on its own. + +These support both directions with one prop triple per axis: + +| Prop | Purpose | +| ----------------- | ------------------------------------------------------------------------- | +| `` | The externally driven value. Supplying it makes the axis controlled. | +| `default` | The initial internally driven value. Ignored while `` is supplied. | +| `onChange` | The state change event, called with the next value in both directions. | + +Name the triple after the axis: `checked` / `defaultChecked` / `onChange` for Switch, `status` / `defaultStatus` / +`onStatusChange` for Checkbox, `expanded` / `defaultExpanded` / `onExpandedChange` for Accordion. + +Resolve a boolean axis with `useToggleState` from `@fluentui-react-native/framework-base`, which wraps +`useControllableValue` and adds the disabled guard and no-op suppression: + +```ts +const expansion = useToggleState({ value: expanded, defaultValue: defaultExpanded, onChange: onExpandedChange }); +``` + +Then drive it from the interaction handler and forward the user's own handler. Multi-value axes such as Checkbox's +tri-state `status` use `useControllableValue` directly. + +### Externally driven selection + +The press is an _activation_, and what it means for selection is decided by a caller or a surrounding group. A button +press runs an action; a tab, radio, list item, listbox item, or menu item press tells its group which entry was chosen, +and the group decides what to select and what to clear. + +These components must **not** change their own selection: + +- Expose only `selected?: boolean`, and render exactly what is given. +- Do not add `defaultSelected` or `onSelectedChange`. There is no internally driven mode to seed or report. +- Forward `onPress` unchanged; it is the interaction event the owner reacts to. +- Derive selection semantics from the resolved value, and gate optional semantics on `selected !== undefined` when the + axis is opt-in. + +A normal button must not change state on a press. A lone radio or tab that selected itself would also be wrong, because +it could never deselect and nothing would clear its siblings. + +### Watch axes that change component identity + +When `selected !== undefined` switches a component between two shapes, that is an identity change rather than a value +change. Button uses it to become a toggle button, which adds the Semibold ghost label that reserves layout width, and +Card uses it to become selectable, which adds the interactive overlay. + +Keep those axes stable for a given instance and do not expose them as a Storybook control, because flipping the control +between `undefined` and `false` visibly resizes or re-roles the component. Demonstrate the axis with focused stories +that always supply a value. + +## Merge accessibility deliberately + +Start with consumer-provided accessibility state, then apply component-owned semantics: + +```ts +accessibilityState: { + ...accessibilityState, + disabled, + ...(isToggle && { checked: selected }), +} +``` + +The component must own its role and state semantics while preserving unrelated consumer values such as `busy`. + +- Set the native role explicitly. +- Keep disabled state, focusability, and interaction behavior consistent. +- Prefer a consumer-provided `accessible` or `focusable` value only when it does not violate the component contract. +- Add selected or checked semantics only when the corresponding behavior is enabled. +- Use action-oriented accessible names for icon-only controls. + +Button warns in development when an icon-only instance lacks an `accessibilityLabel`. Put warnings in an effect so +render remains free of observable side effects, and make the dependency list match every value used by the warning. + +For a component-owned toggle or select action, use `resolveAccessibilityAction` +from Framework Base with the semantic action, `Platform.OS`, and the caller's +action list. Assign its `accessibilityActions` and compare incoming +`event.nativeEvent.actionName` with its `name`; do not duplicate platform strings. +Windows Fabric uses lowercase names, while Win32 uses `Toggle`/`Select`. +macOS retains those title-case names as custom actions, not an invented +`activate`/AXPress fallback. + +Keep state ownership and disabled guards in the component. Run accepted +component behavior first, then forward the original caller accessibility event +exactly once, including custom and disabled events. Do not synthesize `onPress` +or create a second activation path. The resolver preserves caller labels and +custom names while deduplicating declarations. + +Future universal non-styling accessibility helpers belong in +[`framework-base/src/accessibility`](../../../../packages/framework-base/src/accessibility/AGENTS.md). +Read its source-evidence, native/generic boundary, handler-composition, and +validation requirements before extending it. Verify actual UIA/AX action +invocation and callback counts; a state read after a physical click does not +prove accessibility action dispatch. + +## Use framework interaction and slot hooks + +Interactive roots should use the framework state hook, such as `usePressableState`, so hovered, pressed, and focused +state is normalized and user handlers are forwarded. + +Construct slots after native props are resolved: + +- `useSlot` for the required root. +- `useOptionalSlot` for optional public slots. +- `useOptionalSlot` for private state-only slots when their condition is active. + +Pass `null` for a slot that must not exist. Do not create a placeholder slot and hide it later with styles. + +Button constructs its hidden Semibold content and content container only when toggle behavior and visible content require +them. This keeps ordinary buttons free of the extra structure. + +## Return stable resolved state + +Return: + +- slot functions +- required variant values +- derived state +- preserved user styles +- theme state +- interaction state + +Keep property ordering intentional so later spreads cannot silently replace component-owned values. If two state sources +can share a key, assign the final owned value explicitly after spreading. + +Export the hook from the package root as `use_unstable`, together with its resolved `State` type, +so another component can extend the state stage without importing package internals. + +## Platform behavior + +For focus ownership, activation, and native target lifetime, read +[common focus authoring](../../focus/common.md), followed by the +[Windows/Win32](../../focus/windows-win32.md) or [macOS](../../focus/macos.md) detail. + +Keep platform-specific native imports out of shared files when React Native forks expose incompatible types. Put fork +imports in platform files or redeclare a small platform-neutral contract. Surface unsupported platform behavior rather +than silently pretending it succeeded. + +## Review checklist + +- Defaults match the spec and depend on already-derived facts. +- Omitted controlled props retain their meaning. +- Each stateful axis is classified before it is wired: a self-driving control exposes ``, `default`, and + `onChange` and changes state on interaction, while externally driven selection exposes only `selected` and + never changes it. +- A press on a button, tab, radio, or item does not change that component's own selection. +- An axis that changes component identity stays stable per instance and is not a Storybook control. +- User native handlers and unrelated accessibility state are preserved. +- Disabled, focusable, and selected semantics agree. +- Icon-only or unlabeled usage is diagnosed consistently. +- Optional slots exist only when their render condition is active. +- The hook contains no token style selection and no JSX rendering. diff --git a/references/agents/components/agentic/styles-and-tokens.md b/references/agents/components/agentic/styles-and-tokens.md new file mode 100644 index 0000000000..3174ba3be5 --- /dev/null +++ b/references/agents/components/agentic/styles-and-tokens.md @@ -0,0 +1,219 @@ +--- +id: components.agentic.styles-and-tokens +kind: instruction-reference +owner_paths: + - packages/agentic/components + - packages/agentic/design +--- + +# Styles and tokens + +Use this reference for `.styles.ts`, `useStyles.ts`, token mapping, theme caching, state precedence, and +slot prop application. The canonical examples are +[`button.styles.ts`](../../../../packages/agentic/components/src/components/button/button.styles.ts) and +[`useButtonStyles.ts`](../../../../packages/agentic/components/src/components/button/useButtonStyles.ts). + +## Resolve values from the correct token source + +Consult +[`flex-token-map.yaml`](../../../../packages/agentic/design/src/tokens/mappings/flex-token-map.yaml) before choosing a +value. + +- Read semantic colors from `useThemeState().tokens.color`. +- Read grouped categories from `tokens.borderRadius`, `tokens.spacing`, `tokens.strokeWidth`, `tokens.fontFamily`, + `tokens.fontSize`, `tokens.fontWeight`, `tokens.lineHeight`, and `tokens.shadow`. +- Prefer Flex tokens for every mapped semantic value. +- Import named global tokens only when Flex has no equivalent, such as current Button icon dimensions or a true zero. +- Record a genuine token gap in the component spec. Do not replace it with a nearby token or numeric literal. +- For compatibility with an existing V1 component, derive cross-platform mappings from its Win32, macOS, or Windows + implementation. Do not use iOS as the canonical value unless the change explicitly targets iOS. +- `useThemeState` projects a FURN Theme from context into Flex tokens and stores one symbol-keyed `ThemeState` on that + Theme object. Consumers of the same Theme therefore share token and style-cache identity. + +## Build styles in layers + +Use four layers: + +1. A module-scoped `StyleSheet` for structural values that do not depend on theme, props, or state. +2. Module-scoped state factories for theme-independent state selectors. +3. Module-scoped themed factories for token-derived styles. +4. Per-instance style arrays that select cached objects and place user styles last. + +Never create a style factory inside a hook or render function. + +Reuse the design package's `@fluentui-react-native/design/styling` submodule: + +- [`getStateStyleFactory`](../../../../packages/agentic/design/src/styling/branchedStyle.ts) lazily flattens and caches + one theme-independent state definition. +- [`getThemedStateStyleFactory`](../../../../packages/agentic/design/src/styling/branchedStyle.ts) resolves and caches + one flattened definition per `ThemeState`. +- [`getThemedColorStyleFactory`](../../../../packages/agentic/design/src/styling/colorStyles.ts) converts semantic + color keys and delegates its hierarchy and caching to `getThemedStateStyleFactory`. + +These are the canonical Button mechanisms and cache plain resolved style objects in `state.themeStyles`. Do not wrap them +in another `StyleSheet.create` cache. Use +[`themedStyleSheetFactory`](../../../../packages/agentic/design/src/useThemeState.ts) only when a component genuinely +needs a complete theme-only `StyleSheet.create` result that is not represented by a branched state definition. + +Every theme factory may depend only on `ThemeState` values such as tokens and high contrast. Props, interaction state, +and user styles must remain outside the cache so one component instance cannot leak into another. + +## Declare state hierarchy and precedence + +Represent related axes as ordered hierarchy levels. Each level contains mutually exclusive states in priority order. +Button colors use: + +```ts +[['primary', 'secondary', 'outline', 'subtle'], ['selected'], ['disabled', 'pressed', 'hovered']]; +``` + +This means appearance is the base branch, selected refines it, and interaction is the final refinement. Put interaction +states in `disabled`, `pressed`, `hovered` order so the first active state has the required priority. + +Use `getThemedStateStyleFactory` for token-derived hierarchy definitions and `getStateStyleFactory` for +theme-independent definitions. Use `getThemedColorStyleFactory` for semantic background, border, and foreground colors; +it automatically resolves `tokens.color.hover` and `tokens.color.pressed` for inherited semantic keys. + +An interaction may intentionally switch semantic keys. Button Subtle is transparent at rest but explicitly selects +`backgroundNeutralSubtle` inside hovered and pressed branches. Button Outline uses `strokeNeutralLoud`, whose hover and +pressed token values provide visible stroke feedback. Do not assume a token named `Transparent` will produce visible +interaction states; verify the resolved token maps. + +Build the state source from resolved state without hiding precedence in conditionals. Button pushes appearance, selected, +disabled, pressed, and hovered values, then lets the declared hierarchy select the winner. + +## Group related layout axes + +Combine axes that produce one coherent style object. Button selects size, shape, and content layout from one root style +hierarchy: + +```ts +[ + ['small', 'medium', 'large'], + ['rounded', 'square', 'circle'], + ['withContent', 'iconOnly'], +]; +``` + +Use separate factories for independent concerns such as typography, focus, and content visibility. This keeps unrelated +state branches from multiplying into one large definition. + +Destructure only the token groups needed by a factory: + +```ts +({ borderRadius, spacing, strokeWidth }: FlexTokens) => ... +``` + +Validate token values when their generated type is wider than the React Native style property. Button validates its gap +token before assigning it to `ViewStyle['gap']`. + +## Apply slot props in one stage + +`useStyles.ts` should export `useStyles_unstable`, select styles, and call `attachSlotProps`; it +should not create factories. Export that hook directly from the package root under the same name. + +Compose root styles in this order: + +1. structural style +2. resolved layout or variant style +3. semantic colors +4. focus or other conditional styles +5. user style + +Derive dependent slot props from the same resolved values. Button applies foreground color and size to both icon slots, +marks decorative icons inaccessible, and applies typography plus foreground color to content. + +Preserve consumer slot behavior unless the component owns it. Button no longer forces `numberOfLines`; its content and +toggle container use `flexShrink` so constrained labels can wrap. A consumer can still request truncation through the +content slot. + +## Share focus behavior and styling + +Follow [common focus authoring](../../focus/common.md) and its Windows/Win32 or macOS detail. +State hooks use `useFocusVisuals` to create a private optional `FocusRing` and determine the native +`enableFocusRing` setting. The custom slot is absent on the native path; when present, its configured Views +remain mounted across visibility changes. + +Keep `ThemeState` in the styling phase. Call `applyFocusRingStyles(state.FocusRing, state, resolvedRadius)` +to bind shared focus colors and widths while retaining component-specific geometry. The helper uses an +immutable theme stylesheet; props, focus state, and radius remain outside that cache. + +Native rings default to Windows/macOS; Win32 uses the root-modality-aware custom ring. The Windows native +path requires RNW 0.81.35 or newer. Do not introduce `outline*` focus styles, local modality trackers, or +functional border changes to implement a focus ring. + +For non-focus indicators such as Avatar's activity ring, reuse `FocusVisual` as a persistent decorative border rather +than native outline properties. This preserves stable border lifetime on renderers affected by late border creation. +The component owns the indicator's meaning, tokens, and visibility, independently of the focus policy. For an outward +ring with a gap, each absolute edge is `-(gap + strokeWidth)` so the border preserves the requested gap without changing +layout. Mount it with its border configured even when hidden; do not switch border width between zero and a nonzero value. + +## Native motion + +Use `useSharedAnimatedLoop` for phase-locked loading indicators. It treats `useNativeDriver` as a request and selects +the JavaScript driver on macOS Fabric, where native-driven transforms currently remain static. Other renderers keep +their requested driver. These continuous loops must use `isInteraction: false` so the JavaScript path does not starve +interaction-deferred work. Preserve reduced-motion gating independently of driver selection. + +Verify animation with changing pixels on the actual native element, not merely an `Animated.loop().start()` assertion. +Keep a visible static silhouette when motion is disabled, and do not rely on web-only SVG `pathLength` normalization; +derive native dash lengths from the actual circumference. + +## Native text and vertical alignment + +Treat the component frame, the native text line box, and the glyph's visible ink as different measurements. `alignItems` +and `justifyContent` align Yoga boxes; they do not move a glyph inside a stretched Text or editor. A font's `fontSize` +is not its measured ascent plus descent. Platform font fallback can make the difference particularly visible on Win32. + +- Use `Icon` for font glyphs. Its sized View centers an intrinsic, non-shrinking Text child. With a fully specified frame, + the Text is absolute with no edge offsets so Yoga measures its natural height instead of clamping it to the frame. + Without a complete frame, normal flow retains intrinsic dimensions. Do not put the icon's + `height` or `lineHeight` on that Text, use a negative margin or transform to nudge one glyph, or depend on + `textAlignVertical` as a cross-platform centering mechanism. Icon dimensions own scaling, just as they do for images + and SVGs; dimensioned font icons do not independently follow text scaling. +- Retain typography line-height tokens for ordinary labels and paragraphs. Do not globally remove line height to fix + one renderer, and do not replace a typography line height with a control's height or the font's em size. +- Centered initials follow the same intrinsic-metrics rule. Avatar owns all typography and uses native Text to avoid + inheriting the themed Text component's body line height. It preserves token font sizes, native text scaling, and + explicit caller line metrics, and uses an absolute child without edge offsets to avoid clamping in the smallest + padded frame. Do not clip initials to an em square or assume `undefined` can clear merged defaults. +- For single-line `TextInput`, reset internal padding and let the native editor measure its font. Put spacing outside + the editor with Yoga margins or a containing View; keep the control's token-derived minimum height separate. + Input demonstrates this without adding another wrapper or changing the caller's final style precedence. + Preserve the pointer target when moving spacing: Input's inaccessible, non-focusable contents row forwards activation + to the editor through its shared focus target, whose ref is composed by the slot render path. Test disabled and + non-focusable behavior and caller ref forwarding too. +- Distinguish centered graphics from aligned text baselines. `CompoundItemLayout` centers arbitrary regions by default. + A text-only row with different label/shortcut font sizes can use `alignItems: 'baseline'` on the root and inline + content row. All participating regions must supply meaningful text baselines; do not impose that policy on images + or action buttons. +- On Android, disable extra font padding for tightly sized decorative glyphs. That setting is not a desktop fix. +- Exercise small and large sizes, font and image/SVG icons, different text metrics, selection, and constrained wrapping. + Check the running native output, not just flattened styles: a snapshot of the wrong Text height can pass indefinitely. + +## Selected text without layout shift + +When selected text changes weight: + +- Render an inaccessible Semibold ghost that reserves width and height. +- Overlay a View that vertically centers the visible label at its own measured height. Do not stretch the visible Text + itself with `absoluteFillObject`: smaller text will otherwise sit at the top of the reserved area on native renderers. +- Keep the ghost and container state-only. +- Apply the same wrapping constraints to both labels. + +Use `LayoutStableText`, as Button and Tab do, rather than duplicating the ghost/overlay structure. The overlay must +preserve visible-text accessibility and input handling. Typography remains owned by the consuming component. + +## Review checklist + +- Every value comes from Flex or a documented token gap. +- Structural, themed, instance, and user styles are separated. +- Every factory is module-scoped and theme-safe. +- State hierarchy makes precedence explicit. +- Hover and pressed values are verified, not inferred from token names. +- Independent concerns use independent factories. +- User styles are last. +- Slot props share resolved color and size values consistently. +- Constrained text can wrap unless truncation is an explicit public choice. +- Font frames and reserved labels center intrinsic text; editor spacing is external; text baselines are an explicit + choice distinct from centering arbitrary slots. diff --git a/references/agents/components/agentic/tests-and-stories.md b/references/agents/components/agentic/tests-and-stories.md new file mode 100644 index 0000000000..cf26003e92 --- /dev/null +++ b/references/agents/components/agentic/tests-and-stories.md @@ -0,0 +1,282 @@ +--- +id: components.agentic.tests-and-stories +kind: instruction-reference +owner_paths: + - packages/agentic/components + - apps/storybook +--- + +# Tests and stories + +Use this reference for runtime coverage, type coverage, visual snapshots, Storybook CSF, and validation. Canonical examples +are [`button.test.tsx`](../../../../packages/agentic/components/src/components/button/button.test.tsx), +[`button.stories.tsx`](../../../../packages/agentic/components/src/components/button/button.stories.tsx), and +[`icon.stories.tsx`](../../../../packages/agentic/components/src/primitives/icon/icon.stories.tsx). + +## Runtime tests + +Use `render` from the package-private `src/common/renderWithTheme` helper for +component scenes. It places a `ThemedRoot` outside the scene and any supplied +wrapper, preserving the same root settings through rerenders. Import events, +queries, and types from `@testing-library/react-native`; do not import +`react-test-renderer` directly. Tests can dispatch root modality events through +the helper's `test-scene-root` test ID. + +Prefer public semantics: + +- query the root by accessibility role +- use matchers such as `toBeDisabled()` +- assert `accessibilityState`, accessible names, and focusability +- use async `fireEvent` calls for hover, press, focus, and user handlers +- query a stable `testID` only when a platform adapter does not expose the expected accessible inner primitive + +Flatten styles only when the resolved visual contract is the behavior under test. + +Button coverage demonstrates: + +- default props, role, accessibility, and styling +- forwarding user handlers while interaction state updates +- disabled behavior and inaccessible icon slots +- icon-only accessible names and minimum target size +- development warnings +- selected icon replacement and toggle semantics +- hidden ghost text that prevents layout shift +- slot order and user-style precedence +- focus rendering +- every appearance and size +- visible hover and pressed feedback +- constrained text wrapping + +Use table-driven tests for complete finite axes. When fixing one state regression, add a focused assertion even if a +snapshot also changes. + +## Type tests and primitives + +Add committed `*.types.test.ts` coverage for compile-time slot acceptance, mutually exclusive props, and public type +contracts. Keep exploratory type probes outside package source or remove them before validation. + +Primitive runtime tests should cover every renderer or source branch and verify forwarding of size, color, +accessibility, and test props. + +Native motion stories compare repeated element screenshots and honor the runtime reduced-motion preference. +Use `browser.isElementDisplayed(elementId)` and `browser.takeElementScreenshot(elementId)` for native protocol operations; +WebdriverIO's DOM-oriented `element.isDisplayed()` tries unsupported JavaScript execution. Skip pixel checks explicitly +when the driver does not provide element screenshots. + +## Visual snapshots + +Keep snapshots focused on resolved output rather than the full renderer tree. Button snapshots map each appearance to: + +- flattened root style +- flattened content style +- one requested visual state + +Snapshot rest, hover, pressed, focus, disabled, and selected states when those states are part of the visual contract. +Update snapshots through the package's declared script only after reviewing the behavioral diff. + +## Story structure + +Use the Fluent UI React v9 Button stories for focused API-axis examples and the Fluent headless Button story for a +compact grouped overview: + +- `microsoft/fluentui/packages/react-components/react-button/stories/src/Button` +- `microsoft/fluentui/packages/react-components/react-headless-components-preview/stories/src/Button` + +Adapt the structure to React Native. Do not copy DOM props, CSS class hooks, or unsupported web behavior. + +Colocate `.stories.tsx`; the Storybook source glob discovers it automatically. + +Declare module-scoped styles and other values referenced by metadata before the `Meta` object. Storybook evaluates +metadata while importing the module, so referencing a later `const` drops the complete story module with an +initialization error. + +Keep on-device controls limited to scalar, serializable props. For required React elements or style objects, supply the +fixed demonstration values inside the story render function and expose only meaningful finite or numeric args; object +controls for React elements are noisy and cannot safely edit the contract. + +Story controls follow the axis's ownership, described in +[State and accessibility](state-and-accessibility.md#decide-who-owns-a-stateful-axis). + +For a self-driving control such as Checkbox, Switch, or Accordion, do not put the controlled prop in `args`. A pinned +`checked`, `expanded`, or `status` arg forces every instance into externally driven mode, so pressing the component does +nothing and the story looks broken. Expose `default` as the on-device control and use it in `args`, including for +variant-scan stories, so each instance starts in the demonstrated state and still responds to presses. + +For externally driven selection such as Button, Tab, Radio, Card, or the item components, a fixed `selected` arg is +correct: the component never changes it. Give the interactive demonstration in one story that owns the value with +`React.useState` and updates it from `onPress`. + +Do not expose an axis that changes component identity as a control. Button's `selected` decides whether it is a toggle +button at all, and Card's decides whether it is selectable, so a control that flips between `undefined` and `false` +resizes or re-roles the component instead of demonstrating a value. + +Each story module should provide: + +- typed `Meta` with `component` and a title matching the component location: + `Components/` for higher-order components, `Primitives/` for agentic primitives, or `Native/` for + standalone native component packages +- useful common args +- controls for finite or numeric public props +- a short component description +- an args-driven `Default` +- a grouped `Overview` when the contract has several axes +- focused named stories that compare all values of one axis in one canvas +- an interactive story for each stateful axis: `default` for a self-driving control, or a story owning the value + with `React.useState` and `onPress` for externally driven selection +- `parameters.docs.description.story` for focused scenarios + +Stories included in native agent validation also need a stable root `testID`. +Use selectors that describe the component or scenario rather than visible text, +layout order, or native class names. Keep the initial args deterministic and +add identifiers only to the small smoke set that agents and CI actively +validate. + +Prefer executable WebdriverIO tests under `wdio`, typed with +`WdioStory>` from +`@fluentui-react-native/storybook-desktop/testing` using `import type`. +The story is the suite (`describe`), and named functions are its cases (`it`): + +```tsx +export const Default: Story = { + wdio: { + 'is enabled': async ({ browser, expect }) => { + await expect(await browser.$('~story-button')).toBeEnabled(); + }, + }, +}; +``` + +Each callback receives `browser`, `expect`, `platform`, `desktop`, `signal`, +and `skip`. The typed target `platform` is `macos`, `windows`, or `win32`; +Win32 is distinct from its WebDriver `platformName` of `windows`. Branch +inside callbacks for genuine platform differences and use explicit skip +reasons for unsupported `browser.capabilities['furn:features']`. + +All catalog tests use WDIO; custom `desktopDriver` plans remain only in runner +compatibility fixtures. Keep names static and callbacks self-contained. Node helpers may be +dynamically imported inside each callback, but callbacks cannot capture +story-module bindings. The shared Babel config strips all test functions +before native dependency collection. Named cases get independent previews, +sessions, workers, deadlines, results, and failure evidence. + +The single-function `wdio` form remains supported. Run with +`yarn storybook test --` and optionally `--test `. +`--list` discovers names without executing code. The `stories-and-tests` +smoke mode runs these functions alongside remaining legacy plans, grouped by +story. See the [executable test contract](../../../../packages/agentic/storybook-desktop/README.md#executable-tests-inside-stories). + +The components package's `test:stories` project checks every test-bearing story +without emitting it into the component library. Add new test-bearing files to +`tsconfig.stories.json`; the representative runner contract checks this coverage. +Shared Node-only helpers use `*.wdio.ts` and are dynamically imported by source +extension inside callbacks. They are checked by the story project and excluded +from production emit. + +Native-rendered story helpers use `*.story-helpers.tsx` and are also excluded +from library emit while checked by the story project. Use `StoryStatus` for +counter/modality probes: its named accessible View is observable on macOS +Fabric, where a plain paragraph's `testID` is not exposed as an AX element. + +Focus cases use native `focused`, `checked`, and `selected` properties together +with activation and event-order probes. Cover stale key-up, repeats, disabled +stops, same-target modality, nested themes, focus requests, and selection-before- +focus. Use `try/finally` to release held WebDriver actions. Never replace native +state checks with rendered text alone or a default value for an unsupported +property. Desktop support does not imply identical pointer focus or activation +timing. + +### Desktop focus test scope + +Keep shared requirements executable on Windows Fabric, Office Win32, and macOS. +`requireDesktopFocus` checks native capabilities, not an operating-system +allowlist. A missing capability skips the case with a reason; a platform +difference usually changes one assertion rather than skipping the entire case. + +| Behavior | Scope and expected result | +| -------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Exactly-once activation, release cleanup, disabled eligibility, focus requests, self-focus, and scene modality | All three desktop endpoints. Assert native focus/state and action counts; include overlapping keys and blur/disable during a press. | +| Focus automatically follows an ordinary pointer press | Windows/Win32 only. Use `expectWindowsPointerFocus`; it deliberately does nothing on macOS, without skipping the case's other assertions. Do not use it for a shared keyboard/programmatic focus requirement. | +| Return/Space activation phase | macOS activates on keydown; Windows/Win32 activate on the paired keyup. Assert the held-key count per platform, then the shared final count. A later blur cannot undo a macOS action that already occurred. | +| Native Tab/Shift+Tab entry, disabled skipping, and TabList navigation | Shared, with macOS Keyboard navigation enabled for the all-controls Tab lane. Use `focusByTab` from the story's editable entry; do not simulate click-focus on the control under test. | +| Modified navigation | Shared for chords delivered to the app. Shift+Arrow is portable; macOS Control+Arrow can be a system shortcut and must not be required to reach a TabList handler. Preserve Command/Option text editing separately. | +| FocusZone navigation | Shared native ownership, platform-specific destinations: Windows/Win32 default to linear traversal unless `use2DNavigation` is enabled; AppKit is geometric. Its macOS scene explicitly requests focus on click, unlike ordinary Pressables. | + +Prefer a narrowly named platform assertion helper when several stories need the +same exception. Do not introduce a generic conditional-expectation layer merely +to hide branching, or make an entire test pass without executing any assertion. +Keep injected `platform` checks inside the extracted callback or its dynamically +imported `*.wdio.ts` helpers; never infer the target from the Node host OS. + +On macOS, record the Keyboard navigation setting as part of the run. With that +setting off, AppKit can legitimately skip buttons and return to an editable +field. Interactive development and CI Macs should have this preference enabled; +the shared CI setup enables and verifies it before app startup. Keep input +authority and locked/noninteractive desktop handling separate from this +preference. Do not silently change machine preferences inside a test or +reinterpret the setting as a missing driver feature. Follow the machine owner's +preference, restore changes authorized as temporary, and test the disabled- +setting policy separately when claiming coverage of both modes. + +Modality text and focus counters do not establish ring visibility. Use separate +resolved-style coverage without incidental scene rerenders, plus native visual +evidence for renderer appearance. Likewise, a checked/selected state read after +a physical click does not exercise UIA/AX action dispatch. + +Report passed, failed, and skipped counts together. A zero-exit smoke traversal +with platform-skipped focus cases is not cross-platform focus qualification. + +The pinned RNmacOS 0.81.9 Fabric View does not project +`accessibilityState.disabled`/`selected` to AXEnabled/AXSelected. Those native +announcement assertions are isolated in explicitly skipped cases; shared +focus, activation, and selection-callback cases still run. Do not substitute +rendered state for an AX result or describe these skips as intended macOS +semantics. Track the native projection gap and revisit the cases when the +renderer is upgraded. An already-disabled AppKit responder may also remain +first responder until focus is moved; rejected-request tests start from a +separate native focus owner rather than assuming disabling itself moved focus. + +Button uses focused appearance, size, shape, icon, selection, disabled, and constrained-content stories. Icon uses a +source and size overview plus focused font, image, SVG, size, color, and accessibility stories. + +Use module-scoped React Native `StyleSheet` objects for story layout, captions, and scenario constraints. Do not restyle a +higher-order component to manufacture a variant. An unstyled primitive may include clearly demonstration-only rendering +so its contract is visible. + +Keep stories accessible. Icon-only controls need action-oriented labels. Standalone informative icons need descriptive +labels. Decorative icon slots inside a labeled control should be inaccessible. + +## Validation + +Use declared workspace scripts: + +```sh +yarn workspace @fluentui-react-native/components format +yarn workspace @fluentui-react-native/components lint +yarn workspace @fluentui-react-native/components build +yarn workspace @fluentui-react-native/components test +yarn workspace @fluentui-react-native/agentic-components-storybook storybook bundle --macos +yarn workspace @fluentui-react-native/agentic-components-storybook storybook bundle --windows +``` + +Run the smallest affected package test while iterating. Run the full package sequence before completion. Run the root +`yarn build` when public types, manifests, or project references change. + +Run repository/package tests before the owned native Storybook lifecycle, not +concurrently with it: some CLI fixtures still write into the app's generated +state directory. A lease/nonce mismatch is an infrastructure failure, not a +focus-test result; never bypass ownership or authenticated readiness to proceed. + +A successful bundle proves story discovery and compilation only. For visual changes, inspect the running target-platform +story across hover, pressed, disabled, optional-slot, and constrained-content scenarios. + +Alignment regressions must distinguish the outer frame from the inner text. LayoutStableText's Overview includes an +executable desktop check that the smaller visible Text has a smaller measured height than its reserve and that their +vertical centers agree within one layout pixel. Checking only their centers would let the old stretched-Text bug pass. +Its measurement fixture exposes an explicit group around the primitive and an accessible visible Text rather than +querying the primitive's intentionally inaccessible layout wrapper. macOS Fabric does not expose paragraph test IDs to +AX, so that endpoint reads the Text's actual `onLayout` measurements from a named `StoryStatus` View instead. +Pair geometry checks with native screenshots for glyph ink and caret placement; native element bounds alone do not +prove optical alignment. Never record an unavailable native check as passed. + +Adding a new story file changes Metro's `require.context` catalog and may require restarting Metro and the native app +before the running Storybook index includes it. diff --git a/references/agents/components/agentic/types-and-slots.md b/references/agents/components/agentic/types-and-slots.md new file mode 100644 index 0000000000..fffd0ccbaf --- /dev/null +++ b/references/agents/components/agentic/types-and-slots.md @@ -0,0 +1,114 @@ +--- +id: components.agentic.types-and-slots +kind: instruction-reference +owner_paths: + - packages/agentic/components +--- + +# Types and slots + +Use this reference when defining or changing a component's public API, slots, resolved state, native prop exposure, or +package exports. The canonical higher-order example is +[`button.types.ts`](../../../../packages/agentic/components/src/components/button/button.types.ts). + +## Separate the public contract from render-only structure + +Define public slots in `.types.ts`: + +- Use `Slot` for the required root. +- Use `OptionalSlot` for content, icons, indicators, or other optional elements. +- Document what causes an optional slot to render and whether shorthand children are accepted. +- Keep the slot surface small. A slot is public customization API, not merely an element in the render tree. + +Button exposes `root`, `content`, `icon`, and `selectedIcon`. Its `contentContainer` and `contentHidden` slots support the +selected-label layout but remain in the private `ButtonStateSlots` type. Follow that split whenever measuring, overlay, +ghost, or structural slots are implementation details. + +## Model variants explicitly + +Use named string unions for finite axes: + +```ts +export type ComponentAppearance = 'primary' | 'secondary'; +export type ComponentSize = 'small' | 'medium' | 'large'; +``` + +Collect state-bearing public props in `StateProps`. Optional public values become required in resolved state +after the state hook applies defaults. Preserve a meaningful distinction between omitted and false values when omission +enables behavior; Button uses `selected !== undefined` to distinguish an ordinary button from a toggle button. + +## Expose native props deliberately + +Do not blindly inherit every root prop when the component owns part of the native contract. + +1. Start with `PropsWithRefOf` so the native root's ref is part of the public contract. +2. Omit props the component controls, such as `children` or token-derived `style`. +3. Reintroduce a narrowed form only when consumers need it. + +Button uses `OwnedRootProps>`. This prevents native children from bypassing slot order, +preserves the native style type, and retains `ref`. + +Compose the final public props with: + +```ts +export type MyComponentProps = MyComponentStateProps & ComponentProps; +``` + +This package targets React 19.1.4 or newer, where function components receive `ref` as a prop. Let the state hook carry +that prop to the declared root slot; do not wrap the component in `forwardRef`. A top-level component ref always targets +the declared root. Consumers that need an inner public slot use that slot's own `ref`. + +When a component needs an internal root ref as well, keep both refs and pass one through the resolved slot and the other +when rendering the slot so Framework Base composes them. Never overwrite the consumer ref. If a primitive can render +unrelated native instance types, omit a top-level ref unless it can define one sound, stable imperative contract. + +For focus targets, attachment generations, callback cleanup, and view-command +boundaries, follow [common focus authoring](../../focus/common.md#modern-refs-and-slot-composition). +Modern ref props do not imply that a structural root is the keyboard focus target +or that a native focus command has completed. + +## Make resolved state complete + +Build state from: + +- `ComponentState` +- `Required` for defaulted axes +- theme state when the component reads tokens +- interaction state when the root is interactive +- derived booleans or values needed by styles and rendering +- preserved user values that must be applied at a later stage + +Button state includes `ThemeState`, `PressableState`, `iconOnly`, `isToggleButton`, and `userStyle`. Avoid carrying raw +props that no later stage uses. + +## Primitive contracts + +Primitives should define the smallest slot-compatible acceptance contract. For mutually exclusive sources, use a union +with `never` on incompatible fields, as +[`IconProps`](../../../../packages/agentic/components/src/primitives/icon/icon.types.ts) does for image, font, and SVG +sources. Verify the primitive remains assignable to `SlotProp` with a committed type test. + +## Exports + +Export each component and its composition pipeline explicitly from +[`src/index.ts`](../../../../packages/agentic/components/src/index.ts): + +- the component and its public props, slots, variants, and resolved state type +- the state hook as `use_unstable` +- the style-application hook as `useStyles_unstable` +- the render helper as `render_unstable` + +Name the source file `useStyles.ts` and the implementation `useStyles_unstable` so the local and +public composition APIs match without aliases. Never use wildcard exports. Do not export private state-only slots or +internal style definitions. + +## Review checklist + +- Every public slot is intentional and documented. +- Render-only slots are private to resolved state. +- Variant unions match the spec exactly. +- Omitted and false values retain distinct semantics where required. +- Root native props cannot bypass owned children or style ordering. +- Stable native roots expose a correctly typed `ref`, and the state/render pipeline does not replace it. +- Resolved state contains every defaulted and derived value needed downstream. +- Public component, state, style-application, render, and resolved-state exports are explicit and tree-shakeable. diff --git a/references/agents/components/classification.md b/references/agents/components/classification.md new file mode 100644 index 0000000000..aa8e7b0ff5 --- /dev/null +++ b/references/agents/components/classification.md @@ -0,0 +1,80 @@ +--- +id: components.classification +kind: instruction-reference +owner_paths: + - packages/components + - packages/agentic/components + - packages/framework + - packages/deprecated/foundation-compose +--- + +# Component classification and routing + +Classify every changed surface before choosing a workflow or recipe. A single package, or a single diff, can contain +more than one generation; Button ships both v0 and v1 implementations. + +- **R-CLASSIFY-001:** Classify an implementation by its imports and component structure together. Paths such as + `deprecated/` or `legacy/`, export suffixes, the JSX pragma, and individual helpers such as `directComponent` or + `stagedComponent` are hints, not proof. +- **R-CLASSIFY-002:** Record the task mode, implementation model, public compatibility surface, and native obligations + separately for each changed surface. Load each applicable recipe for a mixed change; never force the whole change + into one generation. + +## 1. Task mode + +| Task | Workflow | +| ---------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------- | +| Create a new component | [agentic-component-authoring](../../../.github/skills/agentic-component-authoring/SKILL.md) | +| Change an existing agentic component or primitive | [agentic-component-authoring](../../../.github/skills/agentic-component-authoring/SKILL.md) | +| Contract-only, feasibility, or plan-only agentic contract work | [agentic-component-contract-authoring](../../../.github/skills/agentic-component-contract-authoring/SKILL.md) | +| Fix or maintain an existing v0 or v1 component | [maintain-v0-v1-component](../../../.github/skills/maintain-v0-v1-component/SKILL.md) | +| Review a component change of any generation | [review-component](../../../.github/skills/review-component/SKILL.md) | +| Native component, TurboModule, native service, or codegen change | [implement-native-component](../../../.github/skills/implement-native-component/SKILL.md) | +| Cross-component agentic extraction or dependency direction | [agentic-component-optimization](../../../.github/skills/agentic-component-optimization/SKILL.md) | +| Storybook app, Metro, CocoaPods, or desktop native toolchain | [agentic-storybook-development](../../../.github/skills/agentic-storybook-development/SKILL.md) | +| Dependency vulnerability remediation | [fix-security-issues](../../../.github/skills/fix-security-issues/SKILL.md) | + +v0 and v1 are maintenance-only (R-V0-001, R-V1-001). A request for a new component uses the agentic workflow even +when a v0 or v1 component with a similar name exists. + +## 2. Implementation model + +| Model | Decisive evidence | Representative source | Recipe | +| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------- | +| v0 | Imports from `@uifabricshared/foundation-compose` (or `foundation-settings`/`foundation-tokens`); `IComposeSettings`, `usePrepareProps`, `mergeSettings`, `_overrides`, `_precedence` | [deprecated Button](../../../packages/components/Button/src/deprecated/Button.tsx) | [v0](v0.md) | +| v1 compose | `compose` from `@fluentui-react-native/framework` with a `{ props, tokens, slotProps }` descriptor and `useRender` | [Button](../../../packages/components/Button/src/Button.tsx) | [v1](v1.md) | +| v1 compressible | `compressible` from `@fluentui-react-native/framework`; it may return a framework-base `directComponent` continuation | [Text](../../../packages/components/Text/src/Text.tsx) | [v1](v1.md) | +| Agentic higher-order | Under `packages/agentic/components/src/components`; `.types.ts`, `use.ts`, `useStyles.ts`, `render.tsx`, and `.ts`; `_unstable` stage exports; React 19 `ref` prop | [agentic Button](../../../packages/agentic/components/src/components/button/button.ts) | [agentic skill](../../../.github/skills/agentic-component-authoring/SKILL.md) | +| Agentic primitive | Under `packages/agentic/components/src/primitives`; colocated `CONTRACT.md`; unstyled; often a hook-free `directComponent` | [Icon](../../../packages/agentic/components/src/primitives/icon/icon.tsx) | [primitive instructions](../../../packages/agentic/components/src/primitives/AGENTS.md) | +| Framework-base pattern | `phasedComponent`, modern `directComponent`, `useSlot`/`useOptionalSlot`, and `Slot` outside the agentic package | [component patterns](../../../packages/framework-base/src/component-patterns/README.md) | [framework-base instructions](../../../packages/framework-base/AGENTS.md) | +| No component model | Native modules, services, utilities, or tooling | [native-core](../../../packages/agentic/native-core/README.md) | Owning package documentation | + +Framework-base utilities such as `mergeProps`, `useSlot`, `directComponent`, and the JSX runtime appear inside v1 and +v0 implementations for compatibility. Their presence does not change the component's generation. Both v0 and v1 use +the `@jsxImportSource @fluentui-react-native/framework-base` pragma. + +## 3. Public compatibility surface + +Record the API obligations independently of the implementation model: + +- Legacy unsuffixed exports beside `V1` exports, such as `Button` and `ButtonV1`. +- v0 and v1 `componentRef` APIs, versus the agentic React 19 `ref` prop. +- The agentic package root versus its `@fluentui-react-native/components/primitives` entrypoint. +- Native-core's `/legacy` entrypoint. Its [Callout](../../../packages/agentic/native-core/src/legacy/callout/Callout.tsx) + is a modern implementation that preserves a legacy public API and `componentRef`. +- Native registration names, which must not change during maintenance. + +## 4. Native obligations + +Treat the change as native work, and add the +[implement-native-component](../../../.github/skills/implement-native-component/SKILL.md) checklist as an overlay, +when it touches any of: + +- Swift, Objective-C, Objective-C++, C++, IDL, headers, podspecs, or Windows project files; +- `codegenNativeComponent` or TurboModule specifications, native registration, or native event/command shapes; +- a platform wrapper whose root is a native component, such as + [the macOS experimental Checkbox](../../../packages/experimental/Checkbox/src/Checkbox.macos.tsx); +- accessibility actions, focus targets, or refs that cross a native boundary. + +File suffixes are not the only trigger: a TypeScript-only change to a native prop, ref, or event contract is native +work. diff --git a/references/agents/components/legacy-test-apps.md b/references/agents/components/legacy-test-apps.md new file mode 100644 index 0000000000..004901ffb5 --- /dev/null +++ b/references/agents/components/legacy-test-apps.md @@ -0,0 +1,38 @@ +--- +id: components.legacy-test-apps +kind: instruction-reference +owner_paths: + - apps/fluent-tester + - apps/tester-core + - apps/E2E + - apps/win32 +--- + +# FluentTester and E2E coverage for v0 and v1 components + +v0 and v1 components are exercised manually in FluentTester and automatically with Appium and WebdriverIO. Agentic +components use Storybook stories and `wdio` story tests instead; see +[tests and stories](agentic/tests-and-stories.md). + +## FluentTester + +- [`apps/fluent-tester`](../../../apps/fluent-tester/README.md) hosts the tester on Android, iOS, macOS, and Windows; + [`apps/win32`](../../../apps/win32) hosts it on Win32. +- Test pages live in `apps/tester-core/src/TestComponents//`. Register each page in + [`apps/tester-core/src/testPages.ts`](../../../apps/tester-core/src/testPages.ts), whose `platforms` array declares + where it appears. +- A page that supports E2E tests sets the first section's `testID` to the page object's `_pageName` and may supply + dedicated `e2eSections`, as [ButtonTest](../../../apps/tester-core/src/TestComponents/Button/ButtonTest.tsx) does. + +## End-to-end tests + +- Tests live in `apps/E2E/src//`: a page object under `pages/` (for example + [ButtonV1PageObject](../../../apps/E2E/src/ButtonV1/pages/ButtonV1PageObject.ts)), platform specs under `specs/` (for + example `ButtonV1.spec.win.ts`), and shared constants in `consts.ts` where needed. +- Run them with the E2E workspace's declared scripts, such as `yarn e2etest:` from `apps/E2E`, after the + matching `e2eprep:` setup. +- Android selectors use `accessibilityLabel`; other platforms use `testID`. +- Win32 automation uses WinAppDriver. + +When a maintenance change alters observable behavior, update the existing page and spec rather than creating a +parallel test app. Report E2E runs that were not executed, and why, rather than implying coverage (R-VALID-002). diff --git a/references/agents/components/v0.md b/references/agents/components/v0.md new file mode 100644 index 0000000000..3ff5917186 --- /dev/null +++ b/references/agents/components/v0.md @@ -0,0 +1,190 @@ +--- +id: components.v0 +kind: instruction-reference +owner_paths: + - packages/deprecated/foundation-compose + - packages/deprecated/foundation-settings + - packages/deprecated/foundation-tokens +--- + +# v0 component maintenance + +Use this recipe for components built on `@uifabricshared/foundation-compose`. Confirm the generation with the +[classification guide](classification.md) first; v0 and v1 can live in the same package. + +- **R-V0-001:** v0 is maintenance-only. Do not create a v0 component unless the task explicitly requires v0 + compatibility, and do not migrate v0 code to v1, framework-base, or agentic patterns as part of maintenance. +- **R-V0-002:** Preserve settings layering (`mergeSettings`, the settings-name entry, `_overrides`, and + `_precedence`), slot prop filters, token key lists, and the split between preparation and rendering. Each + participates in compatibility, composition, or caching. +- **R-V0-003:** Preserve existing `componentRef` APIs. Do not introduce `forwardRef` or a new public `ref` + convention without an explicit migration requirement. + +## Recognize v0 code + +v0 code imports from `@uifabricshared/foundation-compose`, `foundation-composable`, `foundation-settings`, or +`foundation-tokens`, and typically has: + +- a descriptor such as `IButtonType` with `props`, `tokens`, `slotProps`, optional `state`, and optional `statics`; +- `compose({...})`, `IUseComposeStyling`, `ISlots<...>`, and `mergeSettings(...)`; +- settings shaped as `{ tokens, root, ...slots, _overrides, _precedence }`; +- an old unsuffixed export beside a newer `V1` export. + +A `deprecated/` or `legacy/` directory is only a hint: native-core's legacy Callout lives under `legacy/` but uses +modern helpers. Representative v0 implementations are +[deprecated Button](../../../packages/components/Button/src/deprecated/Button.tsx), +[deprecated Checkbox](../../../packages/components/Checkbox/src/deprecated/Checkbox.tsx), +[legacy Link](../../../packages/components/Link/src/legacy/Link.tsx), +[Persona](../../../packages/components/Persona/src/Persona.tsx), and +[Stack](../../../packages/components/Stack/src/Stack.tsx). + +## Framework model and public API + +`compose` combines the lower-level composable, themed-settings, settings, and token systems. Its phases are: + +1. resolve and cache layered `settings`; +2. resolve token props into cached slot props and styles through `styles`; +3. run `usePrepareProps` for hooks, state, refs, and instance-specific props; +4. call the hook-free `render` function with prepared `Slots`. + +The package publicly exports `compose`, `initializeStyling`, and the compose option, return, settings, styling, and +type-extraction types listed in its `src/index.ts`. The returned component exposes `__composable`, +`.compose(options)`, and `.customize(...settings)`. Prefer the strongly typed component methods: + +- `.compose(...)` replaces or augments behavior, slots, styles, settings, or render without adding a React wrapper. + Settings are appended to the base settings. See + [PrimaryButton](../../../packages/components/Button/src/deprecated/PrimaryButton/PrimaryButton.ts). +- `.customize(...)` only appends old-style settings entries. Its input retains `tokens`, `_overrides`, `_precedence`, + and slot-prop keys; it is not the v1 flat token customization format. +- `statics` attaches subcomponents, as in `Stack.Item`. + +The exported two-argument `compose(options, base)` form is the underlying API; use `Base.compose(options)` when the +base is already a typed composed component. `initializeStyling` is infrastructure that `compose` installs +automatically, not something component authors normally call. + +## Component shape + +Define a shared descriptor type and keep slot names aligned: + +```ts +interface IExampleType { + props: IExampleProps; + tokens: IExampleTokens; + slotProps: IExampleSlotProps; // must contain root + state: IExampleState; +} + +export const Example = compose({ + displayName: exampleName, + settings, + usePrepareProps, + render, + slots: { root: View, content: Text }, + styles: { root: [backgroundColorTokens], content: [textTokens] }, +}); +``` + +Typical files are `.types.ts`, `.settings.ts`, `.tokens*.ts`, `.tsx`, +`index.ts`, and colocated Jest tests and snapshots. Preserve existing public `I`-prefixed types, deprecated exports, +display names, prop names, and v0/v1 aliases. + +For a one-slot component, `compose` supplies default preparation and rendering: it applies styling and user props to +`root`. See [deprecated Text](../../../packages/components/Text/src/deprecated/Text.tsx) and +[StackItem](../../../packages/components/Stack/src/StackItem/StackItem.tsx). Multiple slots require an explicit +`render`. Once custom `usePrepareProps` is supplied, it must put the public and native props on the appropriate slot; +the framework does not do that automatically. + +## Slots and rendering + +- `slotProps` is a named props collection with a required `root`. +- Define each slot as a component or string, or as `{ slotType, filter }`. Use existing filters such as + `filterViewProps`, `filterTextProps`, and `filterImageProps` so component-only and token props do not reach native + views. +- Merge prepared props with styling, normally `mergeSettings(useStyling(userProps, lookup), { root: rootProps, ... })`. + Later entries win, including merged styles and class names. +- Render prepared slots as `` and ``. Their prepared props are already captured; pass only + additional render-time props in JSX. +- Children are removed before preparation and supplied to `render` as `...children`. Decide explicitly where they + belong, as Button, Link, and Stack do. Conditional slot rendering is safe because all hooks already ran. +- Put `/** @jsxImportSource @fluentui-react-native/framework-base */` first in files that render callable `Slots` with + JSX. This pragma supports the legacy slot runtime; it does not make the component a framework-base pattern. + +## Settings, tokens, and styling + +- Type settings as `IComposeSettings`. Entries are layered in array order and may be a settings object, a + theme settings name string, or a `(theme) => settings` function. Keep objects and functions stable and never mutate + cached settings. +- Use `_precedence` to define state application order and `_overrides` for state-specific settings; nested overrides + are supported. Settings names such as `buttonName` load theme-provided component settings. +- `useStyling(props)` uses props as the default override mask. For derived interaction state, pass an override lookup + function or mask, as in legacy Button, Checkbox, and Link. +- Declare token-to-slot processing in `styles`. Reuse operation sets such as `backgroundColorTokens`, `borderTokens`, + `foregroundColorTokens`, and `textTokens`; use `{ source, target, lookup }` for mappings and theme lookup. +- For component-specific calculations, use `styleFunction` and list every token key that affects the result. See + [Persona root tokens](../../../packages/components/Persona/src/Persona.tokens.root.ts), + [Persona text tokens](../../../packages/components/Persona/src/Persona.tokens.texts.ts), and + [Stack tokens](../../../packages/components/Stack/src/Stack.tokens.ts). Accurate key lists are required for + memoization. +- When a slot is itself composed and supports a token, token processing can forward that token to the child instead of + producing a parent-owned style. Preserve compatible child token contracts when replacing composed slots. +- Put theme, default, and state values in settings and token-derived style in `styles`. Put highly variable values + such as text, accessibility props, handlers, sources, and refs into `usePrepareProps` slot props. + +## State, hooks, and refs + +- `usePrepareProps` and `useStyling` are hook phases: call them on every render and never conditionally. Hooks, + context, controlled and uncontrolled state, and interaction hooks belong in `usePrepareProps`. +- Return `{ slotProps, state }`; `state` is the typed payload for `render`, not necessarily React state. Keep `render` + hook-free. +- Convert `componentRef` to the native root `ref` during preparation, commonly with `useViewCommandFocus`, and type + ref-bearing roots with `React.PropsWithRef<...>`. For imperative native APIs, use an internal native ref plus + `useImperativeHandle`; native-core's [legacy Callout](../../../packages/agentic/native-core/src/legacy/callout/Callout.tsx) + shows that pattern behind a legacy API. + +## Platform-specific implementations + +Use React Native resolution for `.android.tsx`, `.macos.tsx`, `.windows.tsx`, and `.win32.tsx`, and for platform +settings files. Keep the same public descriptor and props across variants while allowing different native slots and +preparation logic, as deprecated Button on Android and RadioButton on macOS and Win32 do. Keep imports from platform +React Native forks inside their platform files (R-REPO-002). A platform file whose root is a native component also +needs the native overlay from the [classification guide](classification.md#4-native-obligations). + +## Maintenance boundaries + +- Do not rewrite v0 code to `@fluentui-react-native/composition`, `@fluentui-react-native/framework`, or framework-base + `phasedComponent`, `directComponent`, or `useSlot` patterns as part of maintenance. +- Do not apply v1 slot types, flat state tokens, `useTokens`, or the v1 `.customize({ token: value, state: {...} })` + shape to v0 code. +- Prefer surgical fixes. Migration to v1 is a separate task and must account for documented prop, token, slot, and + export differences such as [Button's migration guide](../../../packages/components/Button/MIGRATION.md). + +## Validation + +Run the owning packages' declared scripts. For the framework itself: + +```bash +yarn workspace @uifabricshared/foundation-compose build +yarn workspace @uifabricshared/foundation-compose lint +yarn workspace @uifabricshared/foundation-compose test +``` + +For behavior changes, also build and test the consumer package, for example: + +```bash +yarn workspace @fluentui-react-native/button build +yarn workspace @fluentui-react-native/button test +``` + +Update snapshots only after reviewing the rendered slot tree. Changes to settings override or merge semantics, or to +token processing, also require the `@uifabricshared/foundation-settings` or `@uifabricshared/foundation-tokens` tests. +Manual and end-to-end coverage for v0 components uses the [legacy test apps](legacy-test-apps.md). + +## Review checklist + +- Classification is supported by imports and structure, not a directory name (R-CLASSIFY-001). +- The change is maintenance, not an unrequested migration or new v0 component (R-V0-001). +- Settings layers, `_overrides`, `_precedence`, filters, and token key lists remain intact (R-V0-002). +- `componentRef`, deprecated exports, `I`-prefixed types, and display names are unchanged (R-V0-003). +- Hooks run only in `usePrepareProps` and `useStyling`; `render` stays hook-free. +- Platform variants keep the same public descriptor; fork imports stay in platform files. +- Snapshot updates were reviewed rather than accepted blindly. diff --git a/references/agents/components/v1.md b/references/agents/components/v1.md new file mode 100644 index 0000000000..a82803689a --- /dev/null +++ b/references/agents/components/v1.md @@ -0,0 +1,216 @@ +--- +id: components.v1 +kind: instruction-reference +owner_paths: + - packages/framework/framework + - packages/framework/composition + - packages/framework/use-slots + - packages/framework/use-tokens + - packages/framework/use-styling +--- + +# v1 component maintenance + +Use this recipe for components built on `@fluentui-react-native/framework` `compose` or `compressible`. Confirm the +generation with the [classification guide](classification.md) first. + +- **R-V1-001:** v1 is maintenance-only. Do not create new v1 components; new components use the + [agentic workflow](../../../.github/skills/agentic-component-authoring/SKILL.md). Maintain existing v1 components in + place. +- **R-V1-002:** Preserve the compose or compressible model. Do not mix it with v0 or framework-base component patterns, + or migrate between frameworks, unless the task explicitly requests a migration. Framework-base helpers used inside + v1 do not change its classification. +- **R-V1-003:** Preserve established `componentRef` APIs. Convert `componentRef` to the native root `ref` during + preparation; do not introduce `forwardRef` or replace `componentRef` with a public `ref` as an incidental change. +- **R-V1-004:** Call hooks, including `useSlots`, once and unconditionally in the hook stage. Final render + continuations, state lookups, and `buildProps` callbacks must not call hooks. + +## Recognize v1 code + +v1 code typically has several of: + +- imports of `compose`, `compressible`, `UseSlots`, `UseStylingOptions`, `buildProps`, `buildUseTokens`, or + `useFluentTheme` from `@fluentui-react-native/framework`; +- a descriptor such as `ButtonType` with `props`, `tokens`, and `slotProps`; +- `compose({ slots, slotProps, tokens, states, useRender })`; +- a styling file whose tokens contain nested state layers such as `hovered`, `pressed`, `disabled`, size, appearance, + or shape; +- a public `V1` export beside a legacy unsuffixed export, for example `ButtonV1`, `CheckboxV1`, `TextV1`, or `IconV1`. + +Representative compose components are [Button](../../../packages/components/Button/src/Button.tsx), +[Checkbox](../../../packages/components/Checkbox/src/Checkbox.tsx), +[Avatar](../../../packages/components/Avatar/src/Avatar.tsx), and +[MenuItem](../../../packages/components/Menu/src/MenuItem/MenuItem.tsx). Representative compressible components are +[Text](../../../packages/components/Text/src/Text.tsx), [Divider](../../../packages/components/Divider/src/Divider.tsx), +and [Tab](../../../packages/components/TabList/src/Tab/Tab.tsx). + +## Framework boundaries + +- **v0:** imports `@uifabricshared/foundation-compose` and uses `settings`, `styles`, `usePrepareProps`, `render`, + `mergeSettings`, `_overrides`, and `_precedence`. Follow the [v0 recipe](v0.md); do not apply those shapes to v1. +- **v1:** `compose` wraps `packages/framework/composition`, while styling and slots come from `use-styling`, + `use-tokens`, and `use-slots`. Its component hook is `useRender`, its customization input is a flat token object, + and its state layers are nested token objects. +- **Framework-base and agentic:** `phasedComponent`, modern `directComponent`, `Slot`, `OptionalSlot`, + `ComponentProps`, and public slot shorthand or `as` props form a different model. v1 is built on the compatibility + `stagedComponent` path even though new framework-base code must not add staged components. + +## Framework model and public API + +`compose` injects the Fluent `Theme` integration into `composeFactory`. The factory: + +1. resolves tokens and token-derived props through `buildUseStyling`; +2. creates all callable slots through `buildUseSlots`; +3. wraps `useRender` in a staged component; +4. attaches `.customize(...)`, `.compose(...)`, `displayName`, `__options`, and any declared statics. + +Use the `@fluentui-react-native/framework` entry point from components. Import `composeFactory` directly from +`@fluentui-react-native/composition` only when working on the theme-agnostic lower-level factory. + +- `.customize(...tokenSettings)` appends token layers. Inputs may be token objects, theme functions, or component + names. Define customized components at module scope, not during render. +- `.compose(partialOptions)` deep-merges factory options and appends `tokens`. Use it to replace slots, slot props, + state declarations, or `useRender` without adding a React wrapper. See the compose tests in Button and Checkbox. +- `buildUseTokens` and `buildUseStyling` are Fluent-theme-specialized wrappers around the lower-level packages. + `useFluentTheme` returns the context theme or `defaultFluentTheme`. + +The framework package also re-exports established token builders, memo and merge helpers, slot helpers, and theme +types. Check its `src/index.ts` before adding another public export; keep exports explicit (R-REPO-001). + +## Compose component shape + +Keep the descriptor, styling, state hook, and renderer aligned: + +```tsx +interface ExampleType { + props: ExampleProps; + tokens: ExampleTokens; + slotProps: ExampleSlotProps; +} + +export const Example = compose({ + displayName: exampleName, + ...stylingSettings, + slots: { root: Pressable, content: Text }, + useRender: (userProps, useSlots) => { + const example = useExample(userProps); + const Slots = useSlots(userProps, (layer) => example.state[layer] || userProps[layer]); + + return (final, ...children) => { + const mergedProps = mergeProps(example.props, final); + return ( + + {children} + + ); + }; + }, +}); +``` + +`ComposeType` is available, but most components use a named interface. Only `props`, `slotProps`, `tokens`, and +optional `statics` are extracted by the factory. Extra descriptor fields such as `state` are documentation and +type-sharing conveniences, not factory-managed state. + +Typical files are `.types.ts`, `Tokens.ts` plus platform variants, `.styling.ts`, +`use.ts`, `.tsx`, `index.ts`, tests and snapshots, and `SPEC.md` or `MIGRATION.md` where +applicable. Packages use `jest.config.cjs` and `tsconfig.json`. Preserve existing v0/v1 export aliases and package API +compatibility; consumers may alias imports, for example `import { ButtonV1 as Button }`. + +## Slots and staged rendering + +- Keep every key in `slots`, `slotProps`, and the descriptor's `slotProps` synchronized. Type ref-bearing roots with + `React.PropsWithRef<...>`. +- `buildUseSlots` creates every slot with `useSlot`; conditional slot rendering belongs in the returned hook-free + render stage. Slot functions return `React.ReactElement | null`, so `Slots.root({}).props` is typed without + assertions. +- Styled slot props are captured when slots are created. Props supplied in JSX are merged on top. Merge prepared + component props with final composition props using `mergeProps`; do not mutate either object. +- Decide explicitly where children render. The traditional continuation is `(finalProps, ...children) => JSX` and + returns `FinalRender`. Some transitional components return a hook-free + `directComponent(({ children, ...finalProps }) => JSX)` instead. +- `filters` are supported by `use-slots` but rarely used by v1 consumers. Preserve an existing filter contract; do not + copy v0 `{ slotType, filter }` slot descriptors into v1. +- Platform-only slots may be declared conditionally, as Button does for `rippleContainer` and `focusInnerBorder`. + Create slots consistently and render them only on the matching platform. + +Use `compressible` for the lighter-weight pattern where a component resolves tokens and creates slots manually. +`compressible` also wraps a staged component and adds `.customize(...)`; it does not expose `.compose(...)`. In a +compressible component: + +1. call hooks, `useFluentTheme`, and `useTokens(theme)` in the outer function; +2. use `applyTokenLayers`, `applyPropsToTokens`, or `patchTokens` when state or props must alter resolved tokens; +3. build all slots with `useSlot`; +4. return a hook-free continuation and merge final props there. + +Follow existing examples rather than calling `stagedComponent` directly. + +## Tokens, styling, and state precedence + +- Define defaults as stable objects or theme functions, then place the component name last, for example + `tokens: [defaultButtonTokens, defaultButtonFontTokens, buttonName]`. The name loads `theme.components[name]` + overrides. +- Model visual states as nested values of the same token type. List them in `states` in application order; later + active layers win. +- Supply a lookup to `useSlots` when state is derived from interaction hooks, enum props, context, content, or platform + defaults. Button's `buttonLookup` and Avatar's `avatarLookup` are representative. +- `tokensThatAreAlsoProps` is opt-in. After state layers are applied, listed props override those token values. Prefer + a precise key list over `'all'` to retain caching and avoid leaking unrelated props into tokens. +- Build each slot's cached props with `buildProps`. List every token key read by the builder, including helper key + arrays such as `fontStyles.keys`, `borderStyles.keys`, and `layoutStyles.keys`. +- Treat resolved tokens and cached slot props as immutable. Use `patchTokens` rather than mutation for + instance-dependent values in compressible components. +- Keep theme, default, and state values in token files; token-to-native mapping in styling files; and volatile handlers, + accessibility, data, and ref props in the component state hook or render preparation. +- Platform token differences follow the [design token guidance](../design/tokens-and-theming.md). + +## Hooks, state, and refs + +Put controlled and uncontrolled state, context, interaction hooks, callbacks, accessibility normalization, and ref +preparation in `use` or the outer `useRender` or compressible stage. Return a conventional +`{ props, state }` object when the component has derived behavior; `state` is component-owned data used by the token +lookup and final render. + +Convert `componentRef` to the native root `ref` during preparation, commonly with `useViewCommandFocus`, as Button, +Checkbox, and MenuItem do (R-V1-003). + +## Platform-specific behavior + +Use React Native platform resolution and keep the public props, types, and exports consistent across `.android`, +`.ios`, `.macos`, `.mobile`, `.windows`, and `.win32` implementations. Platform variants may replace the root with a +native component and use a one-slot compose implementation; see +[the macOS experimental Checkbox](../../../packages/experimental/Checkbox/src/Checkbox.macos.tsx) and +[NativeAvatar](../../../packages/experimental/Avatar/src/NativeAvatar.tsx). Those variants also need the native overlay +from the [classification guide](classification.md#4-native-obligations). + +Keep platform defaults and token differences in platform token or styling files when practical. Keep imports from +React Native forks inside their platform files (R-REPO-002) and use the `Platform.OS === ('win32' as any)` convention +described in the [TypeScript reference](../repository/typescript.md). + +## Validation + +Add or update the owning component's Jest tests and snapshots for rendering, state layers, `.customize`, `.compose`, +slot replacement, accessibility, and platform behavior affected by a change. Review snapshot trees rather than updating +them blindly. Run the owning package's declared scripts, for example: + +```bash +yarn workspace @fluentui-react-native/button build +yarn workspace @fluentui-react-native/button test +yarn workspace @fluentui-react-native/button lint +``` + +Changes to the framework itself use `@fluentui-react-native/framework`; changes to factory, slot, token, or styling +semantics also require the matching `@fluentui-react-native/composition`, `use-slots`, `use-tokens`, or `use-styling` +tests. Manual and end-to-end coverage uses the [legacy test apps](legacy-test-apps.md). + +## Review checklist + +- Classification is supported by imports and structure, including compressible components that return a + `directComponent` continuation (R-CLASSIFY-001). +- The change maintains an existing component rather than adding a v1 component or migrating frameworks (R-V1-001, + R-V1-002). +- `componentRef` and existing exports are preserved; no `forwardRef` or incidental public `ref` (R-V1-003). +- Hooks run once in the hook stage; continuations, lookups, and `buildProps` callbacks are hook-free (R-V1-004). +- Slot keys, state order, `tokensThatAreAlsoProps`, and `buildProps` key lists agree with the tokens actually read. +- Platform variants share the public contract; native-root variants received the native overlay. +- Snapshot updates were reviewed rather than accepted blindly. diff --git a/references/agents/design/tokens-and-theming.md b/references/agents/design/tokens-and-theming.md new file mode 100644 index 0000000000..87ababe832 --- /dev/null +++ b/references/agents/design/tokens-and-theming.md @@ -0,0 +1,43 @@ +--- +id: design.tokens-and-theming +kind: instruction-reference +owner_paths: + - packages/agentic/design + - packages/theming +--- + +# Tokens, theming, and color utilities + +- **R-DESIGN-001:** Use [flex-token-map.yaml](../../../packages/agentic/design/src/tokens/mappings/flex-token-map.yaml) + as the canonical mapping from generic CSS and Fluent token sources to grouped React Native Flex token paths. When + mapping an agentic component to an existing V1 component, use the Win32, macOS, or Windows implementation as the + compatibility reference; use iOS only when the work explicitly targets iOS. +- **R-DESIGN-002:** Production code must not depend on `@fluentui-react-native/design/testing`. + +## Tokens + +Tokens are design-time values set through the theme or component customization, for example a brand color. v1 tokens +can also be props through `tokensThatAreAlsoProps`; see the [v1 recipe](../components/v1.md). Agentic token use is +covered by [styles and tokens](../components/agentic/styles-and-tokens.md). + +## Theme-specific styles + +Cache `StyleSheet.create` results that depend only on `ThemeState` with a module-scoped getter created by +`themedStyleSheetFactory`. Treat cached sheets as immutable, and apply props, interaction state, and user styles +separately so instances can share them. + +## Color utilities + +Runtime-safe parsing, conversion, compositing, contrast, and interaction generation live behind the opt-in +`@fluentui-react-native/design/color-lib` subpath. Keep its public exports narrow and add APIs only for demonstrated +production needs. + +Color validation policy, semantic pairings, audit helpers, and conformance fixtures belong under +`packages/agentic/design/src/testing/`. Export them from `@fluentui-react-native/design/testing` when generally useful +to tests. Testing-only validation does not need bundle-size scenarios. + +## Theme packages + +Platform themes live in `packages/theming`: `android-theme`, `apple-theme` (iOS and macOS), `win32-theme`, and +`default-theme`, with shared `theme-tokens` and `theme-types`. Components require a `ThemeProvider` from +`@fluentui-react-native/design/theming`. diff --git a/references/agents/evals/README.md b/references/agents/evals/README.md new file mode 100644 index 0000000000..520b037529 --- /dev/null +++ b/references/agents/evals/README.md @@ -0,0 +1,53 @@ +--- +id: evals.index +kind: index +owner_paths: + - references/agents +--- + +# Agent evaluation cases + +Portable, sanitized case fixtures for evaluating how agents route and follow this repository's instructions. They are +inputs for a future evaluation framework, not instructions; agents performing ordinary work do not need to read them. +No runner or scoring framework is selected yet. + +## Case fixtures + +Each `cases/.json` file contains: + +| Field | Meaning | +| ---------------------------- | ------------------------------------------------------------------------------------------------------------ | +| `schema_version` | `1` | +| `case_id` | Kebab-case identifier matching the file name | +| `split` | `development` cases may guide instruction changes; `holdout` cases are reserved for acceptance | +| `task_mode` | `implement`, `maintain`, `review`, `plan`, `contract`, `optimize`, `toolchain`, or `security` | +| `prompt` | The sanitized task given to the agent | +| `expected.skills` | Skills the agent should use | +| `expected.classification` | `implementation_model`, `public_compatibility` labels, and `native_platforms` | +| `expected.required_reads` | Instruction or contract files the agent should read | +| `expected.forbidden_actions` | Any of `edit-files`, `run-mutating-commands`, `create-component`, or `migrate-framework` | +| `assertions[]` | `id`, `description`, `rule_ids`, and `source_refs`; each assertion cites at least one rule ID or source path | + +`implementation_model` is one of `v0`, `v1-compose`, `v1-compressible`, `agentic-higher-order`, `agentic-primitive`, +`framework-base`, or `none`. `native_platforms` lists any of `ios`, `android`, `macos`, `windows`, and `win32`. +`source_refs` are repository paths without line numbers, so ordinary edits do not invalidate them. + +Do not tune instructions against a holdout case. Once a holdout has informed a change, re-label it `development` and +add a new holdout. + +## Execution results + +Execution evidence stays local and is not committed. A result record should contain: + +- `case_id`, the repository commit, and SHA-256 hashes of the instruction files in scope; +- client, client version, model, reasoning setting, and available tools; +- files actually read, skills invoked, and commands run with outcomes; +- per-assertion `pass`, `fail`, or `not-run`, with sanitized evidence; +- token and request usage where the client reports it, and elapsed time; +- known confounders, such as personal instructions, caching, or tool restrictions. + +Do not record raw transcripts, credentials, private source bodies, or personal identifiers. Efficiency measurements are +reported, not used as pass/fail gates. + +`yarn check-agent-instructions` validates the fixtures: schema, unique IDs, existing skills and paths, and defined rule +IDs. diff --git a/references/agents/evals/cases/agentic-button-appearance-plan.json b/references/agents/evals/cases/agentic-button-appearance-plan.json new file mode 100644 index 0000000000..e4a6f3f743 --- /dev/null +++ b/references/agents/evals/cases/agentic-button-appearance-plan.json @@ -0,0 +1,37 @@ +{ + "schema_version": 1, + "case_id": "agentic-button-appearance-plan", + "split": "development", + "task_mode": "plan", + "prompt": "Plan adding a new 'tinted' appearance to the agentic Button in packages/agentic/components/src/components/button. Plan only.", + "expected": { + "skills": ["agentic-component-authoring"], + "classification": { + "implementation_model": "agentic-higher-order", + "public_compatibility": ["ref-prop"], + "native_platforms": [] + }, + "required_reads": [ + "packages/agentic/components/AGENTS.md", + "packages/agentic/components/src/components/AGENTS.md", + "packages/agentic/components/src/components/button/SPEC.md", + "references/agents/components/agentic/types-and-slots.md", + "references/agents/components/agentic/styles-and-tokens.md" + ], + "forbidden_actions": ["edit-files"] + }, + "assertions": [ + { + "id": "contract-before-code", + "description": "Plans a contract revision and review before implementation because the public appearance set changes.", + "rule_ids": ["R-AGENTIC-001"], + "source_refs": ["packages/agentic/components/src/components/button/SPEC.md"] + }, + { + "id": "token-source", + "description": "Maps the new appearance to Flex tokens and documents any token gap.", + "rule_ids": ["R-DESIGN-001"], + "source_refs": ["packages/agentic/design/src/tokens/mappings/flex-token-map.yaml"] + } + ] +} diff --git a/references/agents/evals/cases/icon-primitive-ref-review.json b/references/agents/evals/cases/icon-primitive-ref-review.json new file mode 100644 index 0000000000..d835c207c9 --- /dev/null +++ b/references/agents/evals/cases/icon-primitive-ref-review.json @@ -0,0 +1,37 @@ +{ + "schema_version": 1, + "case_id": "icon-primitive-ref-review", + "split": "development", + "task_mode": "review", + "prompt": "Review the Icon primitive in packages/agentic/components/src/primitives/icon. Should it expose a public ref prop like the higher-order components do?", + "expected": { + "skills": ["review-component"], + "classification": { + "implementation_model": "agentic-primitive", + "public_compatibility": ["primitives-entrypoint"], + "native_platforms": [] + }, + "required_reads": [ + "packages/agentic/components/src/primitives/AGENTS.md", + "packages/agentic/components/src/primitives/icon/CONTRACT.md" + ], + "forbidden_actions": ["edit-files"] + }, + "assertions": [ + { + "id": "ref-exception", + "description": "Concludes Icon omits ref because its renderers lack one stable imperative instance type, as its contract documents.", + "rule_ids": ["R-PRIMITIVE-002"], + "source_refs": [ + "packages/agentic/components/src/primitives/icon/icon.tsx", + "packages/agentic/components/src/primitives/icon/CONTRACT.md" + ] + }, + { + "id": "review-is-read-only", + "description": "Does not edit the primitive during review.", + "rule_ids": ["R-REVIEW-001"], + "source_refs": [] + } + ] +} diff --git a/references/agents/evals/cases/macos-paper-manager-command.json b/references/agents/evals/cases/macos-paper-manager-command.json new file mode 100644 index 0000000000..046df2a822 --- /dev/null +++ b/references/agents/evals/cases/macos-paper-manager-command.json @@ -0,0 +1,34 @@ +{ + "schema_version": 1, + "case_id": "macos-paper-manager-command", + "split": "development", + "task_mode": "plan", + "prompt": "Add a new imperative command to the macOS Paper Callout view manager in packages/agentic/native-core. Plan the change.", + "expected": { + "skills": ["implement-native-component"], + "classification": { + "implementation_model": "none", + "public_compatibility": ["native-registration"], + "native_platforms": ["macos"] + }, + "required_reads": ["references/agents/native/components-and-modules.md", "packages/agentic/native-core/README.md"], + "forbidden_actions": ["edit-files"] + }, + "assertions": [ + { + "id": "swift-manager-command", + "description": "Implements the Paper manager method in Swift and declares it with RCT_EXTERN_METHOD rather than importing the generated Swift header into the registration file.", + "rule_ids": [], + "source_refs": [ + "packages/agentic/native-core/README.md", + "packages/agentic/native-core/macos/components/Callout/paper/CalloutManager.swift" + ] + }, + { + "id": "paper-and-fabric-evidence", + "description": "Plans macOS Paper validation with the --paper Storybook flag, because macOS commands default to Fabric, and reports untested renderers explicitly.", + "rule_ids": ["R-NATIVE-003"], + "source_refs": ["apps/storybook/AGENTS.md"] + } + ] +} diff --git a/references/agents/evals/cases/native-checkbox-macos-review.json b/references/agents/evals/cases/native-checkbox-macos-review.json new file mode 100644 index 0000000000..e2ffbfd885 --- /dev/null +++ b/references/agents/evals/cases/native-checkbox-macos-review.json @@ -0,0 +1,35 @@ +{ + "schema_version": 1, + "case_id": "native-checkbox-macos-review", + "split": "development", + "task_mode": "review", + "prompt": "Review the macOS experimental Checkbox (packages/experimental/Checkbox/src/Checkbox.macos.tsx) and its native backing. What validation evidence would be required to approve a change to it?", + "expected": { + "skills": ["review-component", "implement-native-component"], + "classification": { + "implementation_model": "v1-compose", + "public_compatibility": ["native-registration"], + "native_platforms": ["macos"] + }, + "required_reads": [ + "references/agents/components/classification.md", + "references/agents/components/v1.md", + "references/agents/native/components-and-modules.md" + ], + "forbidden_actions": ["edit-files"] + }, + "assertions": [ + { + "id": "separates-native-obligations", + "description": "Records the v1 wrapper and its macOS native obligations separately.", + "rule_ids": ["R-CLASSIFY-002"], + "source_refs": ["packages/experimental/Checkbox/src/Checkbox.macos.tsx"] + }, + { + "id": "native-evidence", + "description": "Requires a macOS native build and runtime evidence, and does not treat JavaScript tests as native qualification.", + "rule_ids": ["R-NATIVE-003", "R-VALID-002"], + "source_refs": [] + } + ] +} diff --git a/references/agents/evals/cases/native-core-legacy-callout-prop.json b/references/agents/evals/cases/native-core-legacy-callout-prop.json new file mode 100644 index 0000000000..d3049d1f86 --- /dev/null +++ b/references/agents/evals/cases/native-core-legacy-callout-prop.json @@ -0,0 +1,37 @@ +{ + "schema_version": 1, + "case_id": "native-core-legacy-callout-prop", + "split": "holdout", + "task_mode": "plan", + "prompt": "Add a new optional prop to the legacy Callout exported from @fluentui-react-native/native-core/legacy. Plan the change.", + "expected": { + "skills": ["implement-native-component"], + "classification": { + "implementation_model": "framework-base", + "public_compatibility": ["legacy-entrypoint", "componentRef", "native-registration"], + "native_platforms": ["macos", "windows", "win32"] + }, + "required_reads": ["references/agents/native/components-and-modules.md", "packages/agentic/native-core/README.md"], + "forbidden_actions": ["edit-files"] + }, + "assertions": [ + { + "id": "legacy-api-modern-implementation", + "description": "Distinguishes the legacy public API from the modern framework-base implementation.", + "rule_ids": ["R-CLASSIFY-002"], + "source_refs": ["packages/agentic/native-core/src/legacy/callout/Callout.tsx"] + }, + { + "id": "keeps-registration", + "description": "Keeps the public API compatible and the native registration names unchanged across hosts.", + "rule_ids": ["R-NATIVE-002"], + "source_refs": ["packages/agentic/native-core/README.md"] + }, + { + "id": "per-host-evidence", + "description": "Plans per-host native validation for macOS, Windows, and Win32.", + "rule_ids": ["R-NATIVE-003"], + "source_refs": [] + } + ] +} diff --git a/references/agents/evals/cases/storybook-macos-bundle-failure.json b/references/agents/evals/cases/storybook-macos-bundle-failure.json new file mode 100644 index 0000000000..4edb0f0665 --- /dev/null +++ b/references/agents/evals/cases/storybook-macos-bundle-failure.json @@ -0,0 +1,25 @@ +{ + "schema_version": 1, + "case_id": "storybook-macos-bundle-failure", + "split": "holdout", + "task_mode": "toolchain", + "prompt": "The agentic Storybook macOS JavaScript bundle started failing after a dependency bump. Plan the diagnosis.", + "expected": { + "skills": ["agentic-storybook-development"], + "classification": { + "implementation_model": "none", + "public_compatibility": [], + "native_platforms": ["macos"] + }, + "required_reads": ["apps/storybook/agent-map.yaml", "apps/storybook/AGENTS.md"], + "forbidden_actions": ["edit-files"] + }, + "assertions": [ + { + "id": "declared-scripts", + "description": "Diagnoses with the app-declared Storybook bundle command rather than invoking Metro directly.", + "rule_ids": ["R-VALID-001"], + "source_refs": ["apps/storybook/package.json"] + } + ] +} diff --git a/references/agents/evals/cases/transitive-advisory-remediation.json b/references/agents/evals/cases/transitive-advisory-remediation.json new file mode 100644 index 0000000000..4f3fc0168d --- /dev/null +++ b/references/agents/evals/cases/transitive-advisory-remediation.json @@ -0,0 +1,31 @@ +{ + "schema_version": 1, + "case_id": "transitive-advisory-remediation", + "split": "holdout", + "task_mode": "security", + "prompt": "Dependabot reports a high-severity advisory in a transitive npm dependency. Plan the remediation.", + "expected": { + "skills": ["fix-security-issues"], + "classification": { + "implementation_model": "none", + "public_compatibility": [], + "native_platforms": [] + }, + "required_reads": [".github/skills/fix-security-issues/SKILL.md"], + "forbidden_actions": ["edit-files"] + }, + "assertions": [ + { + "id": "keeps-age-gate", + "description": "Keeps the package age gate and selects only eligible fixed versions.", + "rule_ids": ["R-SEC-001"], + "source_refs": [".yarnrc.yml"] + }, + { + "id": "no-suppression", + "description": "Does not substitute an audit exclusion or warning suppression for a fix.", + "rule_ids": ["R-SEC-002"], + "source_refs": [] + } + ] +} diff --git a/references/agents/evals/cases/v0-button-settings-fix.json b/references/agents/evals/cases/v0-button-settings-fix.json new file mode 100644 index 0000000000..2d60e2eec9 --- /dev/null +++ b/references/agents/evals/cases/v0-button-settings-fix.json @@ -0,0 +1,37 @@ +{ + "schema_version": 1, + "case_id": "v0-button-settings-fix", + "split": "development", + "task_mode": "maintain", + "prompt": "The deprecated (v0) Button in packages/components/Button/src/deprecated/Button.tsx does not apply its pressed-state border color override from settings. Plan the fix.", + "expected": { + "skills": ["maintain-v0-v1-component"], + "classification": { + "implementation_model": "v0", + "public_compatibility": ["legacy-export", "componentRef"], + "native_platforms": [] + }, + "required_reads": ["references/agents/components/classification.md", "references/agents/components/v0.md"], + "forbidden_actions": ["migrate-framework", "create-component"] + }, + "assertions": [ + { + "id": "classifies-v0-by-imports", + "description": "Identifies v0 from foundation-compose imports, not the deprecated directory alone.", + "rule_ids": ["R-CLASSIFY-001"], + "source_refs": ["packages/components/Button/src/deprecated/Button.tsx"] + }, + { + "id": "fixes-through-settings-layers", + "description": "Fixes the pressed override through settings layering and _overrides without bypassing mergeSettings.", + "rule_ids": ["R-V0-002"], + "source_refs": ["packages/components/Button/src/deprecated/Button.settings.ts"] + }, + { + "id": "no-framework-migration", + "description": "Keeps the fix in v0 rather than migrating to v1 or framework-base.", + "rule_ids": ["R-V0-001"], + "source_refs": [] + } + ] +} diff --git a/references/agents/evals/cases/v1-button-review.json b/references/agents/evals/cases/v1-button-review.json new file mode 100644 index 0000000000..c52ffbe435 --- /dev/null +++ b/references/agents/evals/cases/v1-button-review.json @@ -0,0 +1,37 @@ +{ + "schema_version": 1, + "case_id": "v1-button-review", + "split": "development", + "task_mode": "review", + "prompt": "Review the v1 Button in packages/components/Button/src/Button.tsx for correct ref handling and token state layering. This is a review; report findings.", + "expected": { + "skills": ["review-component"], + "classification": { + "implementation_model": "v1-compose", + "public_compatibility": ["v1-export", "componentRef"], + "native_platforms": [] + }, + "required_reads": ["references/agents/components/classification.md", "references/agents/components/v1.md"], + "forbidden_actions": ["edit-files"] + }, + "assertions": [ + { + "id": "review-is-read-only", + "description": "Produces findings without editing files or updating snapshots.", + "rule_ids": ["R-REVIEW-001"], + "source_refs": [] + }, + { + "id": "checks-componentref", + "description": "Checks that componentRef is converted to the native root ref and no forwardRef or public ref was introduced.", + "rule_ids": ["R-V1-003"], + "source_refs": ["packages/components/Button/src/Button.tsx", "packages/components/Button/src/useButton.ts"] + }, + { + "id": "cites-evidence", + "description": "Each finding cites path and line plus a rule or requirement ID, labeled verified or inferred.", + "rule_ids": ["R-REVIEW-002"], + "source_refs": [] + } + ] +} diff --git a/references/agents/evals/cases/v1-text-compressible-review.json b/references/agents/evals/cases/v1-text-compressible-review.json new file mode 100644 index 0000000000..f06d5a925d --- /dev/null +++ b/references/agents/evals/cases/v1-text-compressible-review.json @@ -0,0 +1,31 @@ +{ + "schema_version": 1, + "case_id": "v1-text-compressible-review", + "split": "development", + "task_mode": "review", + "prompt": "A contributor proposes converting packages/components/Text/src/Text.tsx to framework-base phasedComponent because it already returns a directComponent continuation. Review that proposal.", + "expected": { + "skills": ["review-component"], + "classification": { + "implementation_model": "v1-compressible", + "public_compatibility": ["v1-export", "componentRef"], + "native_platforms": [] + }, + "required_reads": ["references/agents/components/classification.md", "references/agents/components/v1.md"], + "forbidden_actions": ["edit-files", "migrate-framework"] + }, + "assertions": [ + { + "id": "helper-does-not-reclassify", + "description": "Treats the directComponent continuation as part of a v1 compressible component.", + "rule_ids": ["R-CLASSIFY-001"], + "source_refs": ["packages/components/Text/src/Text.tsx"] + }, + { + "id": "rejects-incidental-migration", + "description": "Flags the phasedComponent conversion as a framework migration outside maintenance scope.", + "rule_ids": ["R-V1-002"], + "source_refs": [] + } + ] +} diff --git a/references/agents/evals/cases/windows-fabric-callout-prop.json b/references/agents/evals/cases/windows-fabric-callout-prop.json new file mode 100644 index 0000000000..b64ad1d624 --- /dev/null +++ b/references/agents/evals/cases/windows-fabric-callout-prop.json @@ -0,0 +1,37 @@ +{ + "schema_version": 1, + "case_id": "windows-fabric-callout-prop", + "split": "holdout", + "task_mode": "plan", + "prompt": "Add a new property to native-core's React Native Windows Fabric Callout view. Plan the change.", + "expected": { + "skills": ["implement-native-component"], + "classification": { + "implementation_model": "none", + "public_compatibility": ["native-registration"], + "native_platforms": ["windows"] + }, + "required_reads": ["references/agents/native/components-and-modules.md", "references/agents/native/windows-fabric.md"], + "forbidden_actions": ["edit-files"] + }, + "assertions": [ + { + "id": "regenerates-codegen", + "description": "Updates the TypeScript specification and regenerates Windows codegen with the declared command instead of editing generated files.", + "rule_ids": [], + "source_refs": ["references/agents/native/windows-fabric.md", "packages/agentic/native-core/package.json"] + }, + { + "id": "keeps-registration", + "description": "Keeps the Callout registration name and public API unchanged.", + "rule_ids": ["R-NATIVE-002"], + "source_refs": [] + }, + { + "id": "windows-evidence", + "description": "Plans a Windows native build and runtime evidence and reports Win32 and macOS as not affected or not run.", + "rule_ids": ["R-NATIVE-003"], + "source_refs": [] + } + ] +} diff --git a/references/agents/focus/common.md b/references/agents/focus/common.md new file mode 100644 index 0000000000..c194ca73d3 --- /dev/null +++ b/references/agents/focus/common.md @@ -0,0 +1,194 @@ +--- +id: focus.common +kind: instruction-reference +owner_paths: + - packages/agentic/components + - packages/framework-base +--- + +# Common desktop focus authoring + +Read this file for every focusable component, collection, popup, focus hook, or +native focus adapter. Then load only the applicable platform detail: + +- [Windows and Win32](windows-win32.md) +- [macOS](macos.md) + +These files define authoring and review obligations. Execution status in +the [desktop focus plan](../../../packages/agentic/components/WIN32-FOCUS-PLAN.md) +distinguishes implemented APIs from native-only follow-ups. Keep each reviewed contract and +source-specific divergences authoritative. + +## Behavioral authority and platform evidence + +Use shipped V1 Win32 behavior as the compatibility model for **both** Office +Win32 Paper and React Native Windows Fabric. Preserve user-observable outcomes: +which element focuses, action count/timing, selection and announcement order, +disabled discoverability, key ownership, and dismissal return. Implement those +outcomes through each renderer's supported APIs, not copied Paper internals. + +On macOS preserve the same accessibility goals but follow AppKit's first +responder, key-window, keyboard-navigation, click, and shortcut conventions. +Document intentional differences rather than imposing Windows input behavior. + +Before changing behavior, record the public export, platform-resolved file, +renderer/version, host, and relevant V1 sequence. Distinguish declarations, +implementation, authored tests, and native results actually executed. A passing +snapshot or `Platform.OS` mock does not prove native focus delivery. + +## Keep these concepts separate + +| Concept | Owner and invariant | +| ---------------------- | ------------------------------------------------------------------------------------- | +| Input modality | Scene controller; stable `RootSettings`, shared by nested theme boundaries. | +| Native self-focus | Actual mounted focus target, not an ancestor receiving a bubbled event. | +| Focus within | Explicit scope/ancestor state, never substituted for self-focus. | +| Focus request | Cancellable intent to move focus; not confirmation that it moved. | +| Focus-visible policy | Component/scope policy using modality and explicit focus intent. | +| Native window activity | Endpoint adapter; a retained focused object need not belong to the active/key window. | +| Selection | Component or collection state according to its controlled/uncontrolled contract. | +| Ring geometry/colors | Styling phase and renderer, not navigation or activation logic. | + +Programmatic focus must not rewrite physical modality to keyboard. A keyboard +menu invocation, hover focus, accessibility action, and dismissal restoration +may need different visual intent. Keep that intent scoped to the request/owner. + +## Modern refs and slot composition + +- Agentic components target React 19.1.4+: receive `ref` as a prop and preserve the + declared native root using `PropsWithRefOf`. Do not add legacy + `componentRef` props or `forwardRef` wrappers to new components. +- Use `React.ComponentRef` for ordinary host-instance typing when + supported. Codegen schemas are a separate grammar: preserve their validated + ref spelling, including existing `React.ElementRef`, until the pinned + generator accepts a change. A newer TypeScript alias does not establish + codegen compatibility. +- The public root ref, internal focus-target ref, popup-anchor ref, and scope ref + can refer to different native objects. Card's structural root must not silently + become its overlay; Input's public View must not silently become its TextInput. + Use the inner slot's ref or a deliberately reviewed imperative API. +- Reuse Framework Base's slot ref composition. It merges base-slot/render refs + and preserves refs returned by slot transforms; do not create another + component-local merge-ref helper or write into the consumer's ref yourself. +- Forward refs through the custom JSX runtime and compatible `as` overrides. + Do not assume the callable slot function is a mounted native target. +- Support object refs, callback refs, React 19 callback cleanup, null detach, + replacement, and development setup/cleanup cycles. Registration cleanup must + be idempotent and cannot remove a newer target registered under the same key. +- Resolve targets at commit/attachment time, not while rendering. A registration + should identify the current host, owning surface/window, and mount generation. + Never keep a naked renderer tag as a permanent target identity. +- A ref becoming non-null is not proof that native focusability/layout has been + committed. Schedule focus from the owning effect/lifecycle after the needed + state is applied, then confirm through the native focus event or an explicit + native result. Cancel on detach, disable, superseding request, or scope close. +- Do not call `focus()` during render or expose a success-shaped fallback for + unsupported targets. Distinguish requested, confirmed, cancelled, not-mounted, + not-focusable, and unsupported outcomes in the proposed adapter contract. + +Existing mechanisms: `packages\framework-base\src\component-patterns\slot.ts:14-76,118-142`, +`render.ts:38-75`, and `useSlot.test.tsx:453-490`. The framework still supports +older React consumers; do not remove its compatibility behavior merely because +agentic components use React 19. + +## State, activation, and rendering + +1. Resolve disabled/interactive state and the actual focus target in + `use_unstable`. + Use `useFocusablePressable` for pressable targets and `useFocusTarget` for + editable or other native targets. Carry `focusTarget`/`focusTargetRef` in + private state; compose the internal ref on the actual focus slot. +2. Compose interaction handlers with exactly one activation owner. Native + Pressable activation, key handlers, and accessibility actions must not each + invoke the same action independently. + Separate activation cancellation from cleanup of an already-started native + press. macOS activates on keydown; Windows/Win32 activate on keyup. Always + finish pressed feedback on release, self-blur, disable, or target replacement + without generating a new action or forwarding `onPressOut` twice. +3. Call `useFocusVisuals` after resolving focus. Store its private optional + `FocusRing` in state and apply `enableFocusRing` only to the focus target. +4. In the styling phase call `applyFocusRingStyles` with theme state and resolved + radius. Its shared defaults are `strokeFocusInner`/`strokeFocusOuter` and + `strokeWidth.thin`/`strokeWidth.thick`. Keep native colors opaque and theme + caches immutable; pass disabled/noninteractive targets as unfocused. +5. Render the optional slot inside the target. Native/custom rings must not + compete; mounted custom ring geometry must not change merely on focus/blur. + +Current `alwaysVisible` means visible **while focused** for any modality and +selects the custom path. It does not focus the control or make an unfocused +control visible. Native window activity remains a separate renderer obligation. +Do not add this option to every public component prop surface without a contract. + +`useRootSettings` remains a stable non-subscribing query. `useRootInputModality` +provides opt-in notifications; `useFocusVisuals` subscribes only while focused +on the custom path. `RootInputBoundary` attaches additional native popup content +to the same controller without changing themes. Do not add scene-wide React +updates or per-control pointer trackers. + +Focus targets expose `requestFocus(intent)`, an observable snapshot, and a +mount generation. A request reports `requested` until a focus event confirms it; +detach, disable, replacement, or explicit cancellation invalidate pending work. +Same-commit ref handoffs to the identical native instance preserve focus, +snapshot identity, and mount generation; inline forwarding callbacks must not +create a subscription/render loop or cancel a valid keyboard press. Registration +epochs reject stale cleanup independently. Genuine detach notifications are +coalesced to the end of the commit, but `current` clears and pending requests +cancel immediately. Never treat the last observed focus snapshot as proof that +a target is still mounted. Higher-level native window activity and cross-window +restore remain separately qualified. + +For nested controls, preserve `target`/`currentTarget` or equivalent native +identity and separate self-focus from focus-within. Functional Input borders, +selection visuals, and hover styling are not generic focus rings. +Do not apply this self-focus filter to Pressability's responder press: a valid +pointer press can originate on a noninteractive label/icon descendant. + +## Collections and popup scopes + +- Use one owner for arrows/Home/End/Tab within a scope. Do not run a JS roving + algorithm and a native FocusZone over the same keys. +- Apply selection/focusability changes before requesting a target. Confirm + focus before treating it as the active native element; retain controlled + selection semantics. +- Define initial focus and dismissal behavior for keyboard, pointer, + accessibility, Escape, outside click, and application deactivation. +- Restore only to a live eligible target in the intended active window. Do not + steal focus back from an outside-click destination. +- React ancestry is not native-window ancestry. A popup can inherit themes and + settings while requiring a separate event attachment to the same controller. +- Keep navigation and restoration in the owner; leaf MenuItem/Tab components + must not invent a global focus manager. + +## Abstraction and native boundaries + +Extend existing helpers before adding layers. General interaction/target +contracts belong in Framework Base; modality and appearance remain in design; +component-specific eligibility, layout, and selection remain in components. + +A future `native-lib` may provide opt-in native capabilities and shared native +projections. It must depend downward on React Native/platform APIs, never on +design or components. Keep view-bound operations on native refs/generated +commands. Use TurboModules for non-view services that actually need them. +Do not add a process-global `focus(tag)` registry, arbitrary OS handle access, +or production dependencies on the desktop automation driver. + +## Required evidence before shipping + +- Object/callback refs, cleanup, `as` replacements, ref replacement, and + unmount/remount retain exactly one live focus target. +- Native focus, active/selected state, event order, and action counts agree. +- Disabled and disabled-focusable policies are intentional per control. +- Same-target modality changes update only the relevant visual consumer. +- Nested owners, popup boundaries, and independent scenes do not leak state. +- Native keyboard/pointer and accessibility runs are non-skipped on each + supported endpoint; include Narrator/VoiceOver and active/inactive windows. +- Theme/high contrast, scaling, clipping, rounded targets, and native/custom + ring exclusivity have renderer-specific evidence. + +## Public references + +- [React 19 refs and cleanup](https://react.dev/blog/2024/12/05/react-19) +- [React imperative handles](https://react.dev/reference/react/useImperativeHandle) +- [React Native 0.81 native commands](https://reactnative.dev/docs/0.81/the-new-architecture/fabric-component-native-commands) +- [Types and slots](../components/agentic/types-and-slots.md) +- [State and accessibility](../components/agentic/state-and-accessibility.md) diff --git a/references/agents/focus/macos.md b/references/agents/focus/macos.md new file mode 100644 index 0000000000..871c7484c4 --- /dev/null +++ b/references/agents/focus/macos.md @@ -0,0 +1,188 @@ +--- +id: focus.macos +kind: instruction-reference +owner_paths: + - packages/agentic/components + - packages/framework-base +--- + +# macOS focus authoring + +Read [common focus authoring](common.md) first. Match shared accessibility goals, +but do not transplant Windows click-focus, modifier, or key-up policies into +AppKit. + +**Reviewed implementation:** local `react-native-macos` 0.81.9, including Fabric +View, repository FocusZone, and Callout implementations, September 14, 2026. +Verify the renderer branch and resolved component before use. A `.macos.tsx` +file can be a stub: V1 Checkbox's macOS entrypoint warns and renders null, so it +is not a valid macOS focus reference. + +## First responder, key window, and actual target + +AppKit routes keyboard input through the key window and its first responder. +Keep window activation separate from control focus. A window can retain a first +responder while inactive; React `focused` alone does not describe whether its +focus visual should be presented as active. + +- Prefer a supported native host ref/command. In native adapters request focus + with the owning window's first-responder mechanism on the main thread. +- Do not treat `makeFirstResponder` returning true as conclusive proof that the + requested view owns focus. The current responder can refuse to resign, and + AppKit documents a case where the window becomes responder when the requested + object refuses. Confirm the actual target through focus events/observation. +- Resolve the actual editable control. An NSTextField may use the window's + shared field editor; the first responder can therefore differ from the visible + control/ref. Native focus-within mapping must recognize this relationship. +- Keep native view identity scoped to the mounted window/surface. Clear it on + detach/recycle and use common React 19 ref composition, not legacy componentRef + exposure or global numeric-tag lookup. + +Local references: +`packages\agentic\native-core\macos\components\FocusZone\shared\RCTFocusZone.m:24-35,122-129`; +`packages\agentic\native-core\macos\components\FocusZone\fabric\RCTFocusZoneComponentView.mm:118-179`. +Installed RNmacOS `React\Fabric\Mounting\ComponentViews\View\RCTViewComponentView.mm:1719-1804` +implements focus/blur commands and responder event emission. + +## Pointer focus and click-through + +Do not force Windows-style focus-before-press on ordinary macOS controls. +V1 `useOnPressWithFocus` recognizes macOS as focus-capable but only requests +click focus on Windows/Win32. The installed Fabric View also overrides +`needsPanelToBecomeKey` with a comment preserving mouse-versus-keyboard focus. + +`acceptsFirstMouse` controls click-through in an inactive window; it is not an +alias for `focusable`, first-responder acceptance, or keyboard ring visibility. +Test first click to activate a window separately from an actual control action. +Menus can intentionally focus on hover, so document that owner-specific +exception rather than forcing all click/hover paths into one rule. + +Evidence: `packages\utils\interactive-hooks\src\useOnPressWithFocus.ts:14-29`; +installed RNmacOS `RCTViewComponentView.mm:1769-1779`; +`packages\components\Menu\src\MenuItem\useMenuItem.ts:68-120`. + +## Keyboard navigation, shortcuts, and editing + +- V1 key helpers prefer key-down on macOS, versus key-up on Windows/Win32. + The macOS branches use `validKeysDown`/`validKeysUp`; current RNmacOS native + handling also has `keyDownEvents`/`keyUpEvents`. Resolve and validate the + compatibility path instead of configuring both indiscriminately. +- Matching native handled-key entries suppress AppKit's `super` handling. + Register only keys owned by the control/scope so key equivalents, native + navigation, and text editing are not swallowed. +- Preserve Command shortcuts, Option text movement, Control/VoiceOver chords, + IME composition, and the distinction between Return and Space/default-button + activation. Do not copy Win32's modifier allowance or TabList Ctrl+Tab behavior. +- Confirm exactly-once keyboard/accessibility activation; an AX action is not + equivalent evidence to a physical key sequence. +- RNmacOS Pressability activates Return/Space on keydown; keyup supplies + `onPressOut`. Keep native press cleanup separate from Windows keyup-activation + pairing. Intervening keys, caller cancellation, blur, disable, and target + replacement must not strand pressed feedback or duplicate release callbacks. + Retain a pooled event only with `persist()` and release that reference when + the keyboard press ends. +- Use AppKit's key-view loop where applicable. Respect keyboard-navigation/Full + Keyboard Access settings and test enabled/disabled settings explicitly. + `NSApplication.isFullKeyboardAccessEnabled` is not KVO-observable according to + Apple; do not invent a KVO subscription or assume one cached startup value + remains current. +- The shared native focus stories enter through an editable control and Tab, + rather than forcing ordinary pointer clicks to focus. The all-controls lane + requires Keyboard navigation enabled; record that setting, obtain permission + before changing a developer's setting, and restore changes authorized as + temporary. Keep it enabled on development/CI Macs unless the owner chooses + otherwise; locking the desktop gates physical input, not this preference. + A Windows-only pointer + assertion may be a no-op on macOS, but the shared focus/activation assertions + must still execute. +- Do not equate VoiceOver navigation focus with the application's keyboard first + responder. Validate role/name/value and native action behavior in both modes. + +Evidence: `packages\utils\interactive-hooks\src\useKeyProps.ts:8-95,114-126`; +installed RNmacOS `RCTViewComponentView.mm:1807-1861`. + +## FocusZone and Fabric wrapper lifetime + +The local macOS FocusZone translates key events, recalculates the key-view loop, +and falls back to mounted descendant order when AppKit collapses a Fabric-backed +zone to one key-loop entry. The Fabric wrapper deliberately does not participate +as another first-responder stop; it forwards focus to its content view. + +Preserve that single-owner behavior. Do not add a second JS roving loop to the +same keys. Recompute eligibility when children mount/unmount and when props +change, and clear the default responder on recycle. Retain direction, wrap/stop, +RTL, nested zones, scrolling, and editable-child semantics. + +Evidence: `packages\agentic\native-core\macos\components\FocusZone\shared\RCTFocusZone.m:584-675`; +`packages\agentic\native-core\macos\components\FocusZone\fabric\RCTFocusZoneComponentView.mm:118-195`. + +## Popup windows and lifecycle + +Callout's AppKit window can become key but not main. Its `setInitialFocus` and +`focusWindow` make the popup window key; that alone does not establish a particular +child as first responder. Its separate Fabric touch attachment also demonstrates +why React ancestry is not sufficient evidence for popup input capture. + +Define and test initial child focus after content attachment, Escape, nested +popup dismissal, outside click, window/application deactivation, and guarded +return to the parent control. Do not reactivate the application or steal the +outside-click target's focus as an unconditional cleanup action. + +The legacy ContextualMenu uses a macOS timer after a layout effect. Treat it as +evidence of a native readiness problem, not a reusable delay constant. Prefer a +mount/window-ready handshake with cancellation and focus confirmation. Keep a +bounded, documented workaround only if the supported renderer has no such path. + +Reuse lifecycle-safe local event-monitor/observer ownership: install once for +the owning popup/window, remove exactly once, and capture weak native owners. +Do not install OS-global keyboard hooks or require desktop-driver permissions +for ordinary production focus behavior. + +Evidence: +`packages\agentic\native-core\macos\components\Callout\shared\CalloutWindow.swift:9-38`; +`packages\agentic\native-core\macos\components\Callout\shared\CalloutView.swift:33-45,142-216`; +`packages\agentic\native-core\macos\components\Callout\fabric\RCTCalloutComponentView.mm:96-132`; +`packages\agentic\native-core\macos\components\Callout\shared\GuardedEventMonitor.swift:1-29`; +`packages\components\ContextualMenu\src\ContextualMenu.tsx:39-55`. + +## Native versus custom ring drawing + +Keep native macOS rings enabled by default unless the reviewed control requires +a custom path. The installed Fabric View draws a rounded AppKit focus mask only +when `enableFocusRing` is true. A focusable view or an enum property by itself is +not proof that the ring mask/drawing is implemented correctly. + +When drawing a custom ring, disable the competing native path and preserve the +decorative/hit-testing contract. Revalidate mask bounds after layout/radius +changes, key-window transitions, clipping, appearance, Increase Contrast, and +backing-scale changes. Use AppKit points and conversions; do not copy Windows +Composition physical-pixel calculations into NSView layout. + +Evidence: installed RNmacOS `RCTViewComponentView.mm:1729-1753` and Apple's +focus-mask documentation below. Ring colors remain a native/theme concern; +do not resolve opaque system colors through a guessed web-color fallback. + +## Native service candidates and qualification + +An opt-in native projection can help with window activity, keyboard-navigation +preferences, renderer-scoped target observation, or popup readiness if public +React Native APIs do not supply them. A TurboModule is suitable for a non-view +capability/snapshot service; view focus belongs to refs or typed native commands. +Keep generated Apple glue in Objective-C++ as required by the pinned codegen; +use AppKit/Swift/C++ behind that boundary without introducing UIKit assumptions. + +Run physical keyboard/pointer scenarios, VoiceOver, active/inactive windows, +first-click behavior, keyboard-navigation settings, editable field editors, and +native/custom rings. Include Paper only where it remains supported and Fabric +as an independently qualified endpoint. This Windows-based documentation +investigation did not execute a macOS native run. + +## Public references + +- [AppKit makeFirstResponder]() +- [AppKit first responder and key-view loop](https://developer.apple.com/documentation/appkit/nswindow) +- [Click-through and acceptsFirstMouse]() +- [Full Keyboard Access query](https://developer.apple.com/documentation/appkit/nsapplication/isfullkeyboardaccessenabled) +- [AppKit field editor](https://developer.apple.com/library/archive/documentation/TextFonts/Conceptual/CocoaTextArchitecture/TextEditing/TextEditing.html) +- [Focus ring mask bounds](https://developer.apple.com/documentation/appkit/nsview/focusringmaskbounds) +- [Focus ring mask drawing]() diff --git a/references/agents/focus/windows-win32.md b/references/agents/focus/windows-win32.md new file mode 100644 index 0000000000..1bed6bed35 --- /dev/null +++ b/references/agents/focus/windows-win32.md @@ -0,0 +1,171 @@ +--- +id: focus.windows-win32 +kind: instruction-reference +owner_paths: + - packages/agentic/components + - packages/framework-base +--- + +# Windows and Win32 focus authoring + +Read [common focus authoring](common.md) first. This file applies the shipped V1 +Win32 behavioral model to both Windows endpoints, while keeping their native +implementations separate. + +## Identify the endpoint + +| Endpoint | Reviewed local implementation | Native owner | +| --------- | ------------------------------------------------------------------------------ | ------------------------------------------------------------------- | +| `win32` | `@office-iss/react-native-win32` 0.81.8, Paper; prebuilt REX host in Storybook | Office Win32 host/view implementation | +| `windows` | `react-native-windows` 0.81.35, Fabric | Windows App SDK Composition component views and React Native island | + +These are observed dependency versions on September 14, 2026, not minimum-version +guarantees for every feature. Recheck the lockfile, platform resolution, and host +before implementation. Office Win32 Paper is **not** RNW's old UWP XAML renderer, +even though both are described as old-architecture/Paper in some contexts. + +## Carry V1 behavior across, not its plumbing + +For both endpoints preserve, unless the component's reviewed contract differs: + +- focus-before-activation for Windows pointer interaction where the V1 control + requests it; +- one action per Enter/Space/accessibility invocation, including menu-close and + keydown/blur/keyup sequences; +- selection/focusability commit before imperative focus and Narrator announcement; +- explicit per-control disabled discoverability; +- menu hover-focus suppression, navigation ownership, and guarded focus return. + +Use existing V1 Button/Checkbox/RadioGroup/Switch E2E sequences as scenarios for +both Windows test lanes. The V1 implementation need not run inside Fabric: map +its observed behavior to a shared scenario contract, run V1 on Win32 as the +reference, then verify the new implementation independently on both renderers. +Do not require matching native element classes or identical ring pixels. + +Sources: +`packages\utils\interactive-hooks\src\useOnPressWithFocus.ts:14-29`; +`packages\components\Button\src\useButton.ts:15-17,40-66,99-113`; +`packages\components\TabList\src\Tab\useTab.win32.ts:64-110`; +`packages\components\Menu\src\MenuItem\useMenuItem.ts:68-120`. + +## Focus requests and native identity + +On Win32 use the host-supported ref/command path. V1 `componentRef` and +`useViewCommandFocus` are compatibility evidence, not the desired new public API. +Apply the common React 19 ref rules and resolve the actual mounted target. + +On Fabric prefer the supported host focus method or a generated native command. +Native adapters operate on the correct mounted `ComponentView` in its owning +surface/island. They must not reuse Paper UIManager/view-manager identifiers or +assume a React ref can be serialized into a TurboModule argument. + +The existing Fabric FocusZone uses `TryFocus(FocusState::Keyboard)` and checks +the root's focused component after the request +(`packages\agentic\native-core\windows\NativeCore\components\FocusZone\FocusZoneComponentView.cpp:175-224`). +Reuse this request-versus-observation pattern. Do not report success merely +because JavaScript called an optional `.focus()` method. + +RNW's reviewed root stores a native focus state and resolves programmatic focus +using its own policy. A request for the already-focused component returns early. +This matters for modality-only changes: native focus reasons and the design +root's modality are related, but are not automatically the same source. +Do not synthesize a blur/refocus cycle simply to redraw a ring. + +Installed evidence: +`Microsoft.ReactNative\Fabric\Composition\RootComponentView.cpp:79-118`. +This native implementation detail is not a promised public JavaScript API. + +## Keyboard delivery and exactly-once activation + +- Inspect the actual installed Pressable and native view handlers. Do not add + key-up activation on top of an existing native press action. +- Use `useFocusablePressable` rather than per-control key activation. Native + qualification found Office Win32 events with `code="Unidentified"` and a + valid `key`. The helper supplies one paired key-up fallback only when Win32's + code-based Pressability cannot activate that key; recognized codes stay native. + Pairing is cancelled on blur, disable, and target-generation changes. +- Preserve required native accessibility actions. Checkbox and Switch supply `Toggle`; + Tab supplies `Select`. The Win32 UIA toggle/selection patterns were unavailable + without those action declarations even though JavaScript state was correct. +- V1 `useKeyProps` prefers key-up on Windows/Win32. It supplies Win32 + handled-key descriptors, but its Windows branch deliberately omits descriptors + because of the historical `key` versus `code` mismatch. Translate and test the + pinned Fabric representation instead of copying either branch blindly. +- Treat native `Handled`/capture registration and JavaScript + `preventDefault`/`stopPropagation` as separate mechanisms. Establish which + cancellation reaches native navigation at each phase. +- Pair keyboard invocation within the appropriate scene/gesture lifetime. + Do not reproduce Button's module-global arming flag as a process-global + cross-scene dependency. +- Forward caller handlers and preserve text editing, IME, modifiers, key repeat, + and accessibility actions. Global root capture observes input; it must not + consume every key. + +Sources: `packages\utils\interactive-hooks\src\useKeyProps.ts:8-95,114-126`; +`packages\agentic\native-core\windows\NativeCore\components\FocusZone\FocusZoneComponentView.cpp:71-133`. + +## Native and custom focus visuals + +Current new-component defaults remain native on Fabric and custom on Win32. +V1 uses native Win32 rings and appearance-specific focused borders, so this is +a choice to qualify, not evidence that Win32 native rings are unavailable. + +- Request native rings only on the actual focus owner. Menu hover focus and + disabled-focusable cases may require a different visibility policy from a + generic keyboard-focus rule. +- On the custom path retain the optional slot and mounted configured ring Views. + Keep the root/slot ref stable through focus and theme changes. +- Qualify Button primary versus high contrast separately; do not apply its + two-tone Win32 inner-border recipe universally. +- Use platform/Fluent colors for native drawing. Preserve opaque system-color + values; do not turn them into guessed CSS literals. +- For Fabric native visuals, use the renderer's focus/border hosting and + lifecycle. React layout units and Composition physical pixels differ; apply + the validated point-scale conversion. Test clipping/hosting and recycling. + +RNW 0.81.35 has explicit focus hosting and native theme handling in installed +`Microsoft.ReactNative\Fabric\Composition\CompositionViewComponentView.cpp` +and `Theme.cpp`. See [Windows Fabric native components](../native/windows-fabric.md) +for codegen, UI-thread, registration, native visual, and UIA requirements. + +## Collections, popups, and platform services + +Use one native/JS navigation owner. Wait for committed target eligibility and +then confirm the resulting native focus; handle disabled/removed destinations. +Preserve Narrator's selection-before-focus order and test rapid navigation. + +Distinguish a Fabric island/popup and an Office Win32 popup. Returning to a +window is not the same as restoring its intended control. Carry a live target +and dismissal reason, and never steal focus after an outside click or host +deactivation. + +Consider `native-lib` only for a demonstrated missing capability such as +surface-scoped native focus observation, window activation, or safely shared +native target resolution. Keep view commands in their owning native components. +Fabric TurboModule support does not establish registration support in a +prebuilt Office Win32 host; that endpoint needs its own host integration or an +explicit capability-unavailable result. + +## Qualification + +Run separate Win32 and Fabric native lanes for the same behavior scenarios: +pointer focus, Enter/Space, stale key-up, repeat/modifiers, accessibility invoke, +selection-before-focus, disabled/removed targets, menu hover, popup return, and +same-target modality changes. Include Narrator, high contrast, display scaling, +active/inactive windows, and nested surfaces. + +Gate on actual focused element and action/announcement count, not just selected +React state or a mocked ref call. A skipped input test is not qualification. +Use owned app/Metro lifecycle commands and preserve unrelated sessions. + +The catalog now uses named WDIO callbacks. The complete Windows and Win32 smoke +lanes each cover 18 cases with native assertions; component-focused cases use +the `desktop-focus` tag. Check aggregate result counts for zero skips before +claiming qualification. A disconnected input desktop can leave semantic cases +passing while all physical-input cases skip. + +For delegated modifier chords, assert parent delivery and unchanged collection +selection, not immovable native focus: the host is allowed to handle the chord. +For native editors, distinguish noneditable/nonfocusable from UIA `IsEnabled`; +Win32's observed enabled-state announcement gap is documented in Input's +interaction companion and does not replace native focus assertions. diff --git a/references/agents/native/components-and-modules.md b/references/agents/native/components-and-modules.md new file mode 100644 index 0000000000..00a9fd70cc --- /dev/null +++ b/references/agents/native/components-and-modules.md @@ -0,0 +1,52 @@ +--- +id: native.components-and-modules +kind: instruction-reference +owner_paths: + - packages/agentic/native-core +--- + +# Native components and modules + +Native code can accompany a v0, v1, or agentic component, or stand alone as a module or service. The +[native-core README](../../../packages/agentic/native-core/README.md) is authoritative for native-core's entrypoints, +JavaScript and specification layout, native architecture, codegen, registration, and delivery plan. + +- **R-NATIVE-001:** Put new modern native components, TurboModules, and native services in + `@fluentui-react-native/native-core` rather than creating another standalone package. Maintain existing native + wrappers in their current packages unless the task explicitly requests a migration. +- **R-NATIVE-002:** Preserve public APIs and native registration names during maintenance and migration. Callout and + FocusZone keep their existing public APIs and registration names after moving to native-core. +- **R-NATIVE-003:** Record native validation per platform and renderer as passed, failed, skipped, not run, or + unsupported, with reasons (R-VALID-002). Native code is qualified only by building and exercising the affected native + boundary; an upstream or JavaScript-only build does not qualify another host such as Office. + +## Native-core boundaries + +- Callout and FocusZone implementations live in native-core. Their former packages are JavaScript-only compatibility + shims under `packages/shim/`. Their JavaScript wrappers live in `src/legacy` and are exported only from + `@fluentui-react-native/native-core/legacy`, not the root entrypoint. +- Export shared APIs from the root only when equivalent implementations exist on every supported desktop host. Scope + platform APIs to `macos`, `windows` (React Native Windows), `win` (Windows and Win32), or `win32`. +- Windows components are Fabric-only. macOS components support Paper and Fabric. +- Native applications must depend on native-core directly for native project discovery and autolinking. + +## Component shape + +Components with native code typically: + +- have one root slot wrapping the native component; +- declare it with `codegenNativeComponent` for New Architecture compatibility; +- may use `constantsToExport` for native-side defaults; +- include a `.podspec` for iOS and macOS. Some wrap FluentUI Apple controls. + +Run `pod install` from the owning app or its `ios`/`macos` directory after dependency changes; see the +[permissions reference](../repository/permissions.md) for approved Pod operations. + +## Platform guidance + +- React Native Windows Fabric views: [Windows Fabric native components](windows-fabric.md). +- Apple native code: follow the macOS layout and Swift/Objective-C interoperability notes in the native-core README, + including keeping the module-named interoperability header aligned with the pod's `module_name`. +- Focus targets and native focus visuals: [common focus authoring](../focus/common.md) and its platform files. +- Accessibility actions: the [framework-base accessibility instructions](../../../packages/framework-base/src/accessibility/AGENTS.md) + define the transport contract and its native qualification requirements. diff --git a/references/agents/native/windows-fabric.md b/references/agents/native/windows-fabric.md new file mode 100644 index 0000000000..cb9185678d --- /dev/null +++ b/references/agents/native/windows-fabric.md @@ -0,0 +1,273 @@ +--- +id: native.windows-fabric +kind: instruction-reference +owner_paths: + - packages/agentic/native-core +--- + +# Windows Fabric native components + +Use this reference when an agentic component needs a native React Native Windows +view. It targets the repository's React Native Windows 0.81 line. Verify APIs +against the installed `react-native-windows` package before adopting guidance +from a newer RNW branch. + +For behavior, native target lifetime, and modern React refs, read +[common focus authoring](../focus/common.md) and [Windows/Win32 focus authoring](../focus/windows-win32.md). +Shipped V1 Win32 behavior is also the model for Fabric, but Office Win32 Paper +is not the UWP XAML Paper implementation described in the compatibility table +below. Do not transfer its native registration or view-manager assumptions. + +## Decide whether native code is required + +Prefer a JavaScript component, slots, and React Native primitives when they can +meet the contract. Use a Windows Fabric component when the implementation needs +a Windows-only visual, window, input surface, native API, or performance +boundary that React Native does not expose. + +Establish these facts before editing: + +- the installed RNW version; +- whether the consuming host uses the New Architecture; +- whether the package must also retain a Paper implementation; +- whether the surface is a view component, TurboModule, or both; +- the canonical Windows, Win32, or macOS behavior to preserve. + +RNW Fabric components use C++/WinRT and Windows App SDK Composition visuals. +Do not copy UWP XAML `IViewManager` patterns into the Fabric branch. + +## TypeScript native-component specification + +Name the schema `NativeComponent.ts` and keep the component name +identical in TypeScript, generated code, registration, and the JavaScript +wrapper. + +```ts +import type { DirectEventHandler, WithDefault } from 'react-native/Libraries/Types/CodegenTypes'; +import codegenNativeCommands from 'react-native/Libraries/Utilities/codegenNativeCommands'; +import codegenNativeComponent from 'react-native/Libraries/Utilities/codegenNativeComponent'; +import type { ViewProps } from 'react-native'; + +type ValueChangedEvent = { + value: boolean; +}; + +export interface NativeProps extends ViewProps { + enabled?: WithDefault; + onValueChanged?: DirectEventHandler; +} + +export interface NativeCommands { + setValue(viewRef: React.ElementRef>, value: boolean): void; +} + +export const Commands = codegenNativeCommands({ + supportedCommands: ['setValue'], +}); + +export default codegenNativeComponent('ExampleNativeView'); +``` + +Use React Native codegen types for native values. Extend `ViewProps` for visual +components. Keep event payloads and commands typed and minimal. + +## Windows codegen configuration + +Verify the owning package's `codegenConfig`: + +```json +{ + "codegenConfig": { + "name": "ExampleSpec", + "type": "components", + "jsSrcsDir": "src", + "includesGeneratedCode": true, + "windows": { + "namespace": "ExampleCodegen", + "generators": ["componentsWindows"], + "outputDirectory": "windows/Example/codegen", + "separateDataTypes": true + } + } +} +``` + +Use `"all"` and include `"modulesWindows"` when the package contains both +TurboModules and components. Run the package's declared codegen command. +Never edit generated props, event-emitter, registration, or `.g.h` files. +Persist corrections in the TypeScript schema or codegen configuration. + +### `includesGeneratedCode` is not Windows-scoped + +`includesGeneratedCode` is read by React Native core's codegen executor, not by +`react-native-windows`. It declares that the package ships generated code for +**every** platform, so React Native stops generating Apple and Android +artifacts for that package. + +Set it only on a Windows-only package. On a package that also builds for Apple +or Android, it silently removes the generated +`react/renderer/components/Spec/*` headers those platforms compile +against, and the failure surfaces far away as a missing pod library at the app +link step rather than as a codegen error. + +Renaming the component in `codegenNativeComponent` also renames the generated +C++ types, for example `Callout` produces `CalloutProps` and +`RCTCalloutViewProtocol` while `RCTCallout` produces `RCTCalloutProps` and +`RCTRCTCalloutViewProtocol`. Update every platform's component view in the same +change, and do not redeclare the generated component name, shadow node, or +component descriptor by hand; import them from the generated headers so a +rename cannot leave a stale duplicate behind. + +## C++/WinRT component view + +Third-party Fabric components normally derive from the generated CRTP base: + +```cpp +#ifdef RNW_NEW_ARCH +#include "codegen/react/components/ExampleSpec/ExampleNativeView.g.h" + +struct ExampleNativeView + : winrt::implements, + ExampleCodegen::BaseExampleNativeView { + winrt::Microsoft::UI::Composition::Visual CreateVisual( + winrt::Microsoft::ReactNative::ComponentView const &view) noexcept override; + + void Initialize( + winrt::Microsoft::ReactNative::ComponentView const &view) noexcept override; + + private: + winrt::Microsoft::UI::Composition::SpriteVisual m_visual{nullptr}; +}; +#endif +``` + +Implement only the hooks the component needs. Generated registration omits +unused optional callbacks. + +- Create the root Composition visual in `CreateVisual`. +- Subscribe with `winrt::auto_revoke` and store the revoker. +- Capture `get_weak()` in event and asynchronous callbacks. +- Treat view lifecycle callbacks as UI-thread work. +- Reset reusable native state when the view is recycled. +- Keep native boundary methods `noexcept`, matching RNW patterns. + +The generated CRTP base is not the same as subclassing an RNW built-in +`ComponentView`. Do not call a nonexistent `Super` method. When subclassing a +built-in view, preserve its documented base-method ordering. + +## Props, events, commands, and state + +- Compare old and new props before scheduling visual work. +- Mark affected visuals dirty in prop updates and batch expensive mutation in + the final-update hook. +- Store and null-check the generated event emitter before emitting typed + payloads. +- When a built-in base handles commands, call it first and respect `Handled`. +- Use renderer state only when the renderer must own or measure it. Prefer + props and events for ordinary controlled interaction state. + +The RNW `SwitchComponentView` is the canonical source for interactive props, +events, commands, pointer input, keyboard input, focus, and UI Automation. + +## Layout and Composition visuals + +React layout metrics are in device-independent units while Composition visual +sizes and offsets use physical pixels. Multiply positions and dimensions by +`PointScaleFactor` before assigning them to visuals or geometries. + +Mount and unmount child visuals in renderer order. Backgrounds, borders, +shadows, transforms, and clipping are normally supplied through +`ComponentViewFeatures`. Disable a verified feature only when the component +fully replaces it. + +Custom clipping may require disabling native border handling and explicitly +updating size and offset from layout metrics. Check the target RNW source +because the feature flags are not exhaustively documented as a public API. + +## Theme, input, focus, and accessibility + +Native components must support: + +- light, dark, and high-contrast updates; +- platform or Fluent brushes instead of fixed native colors where appropriate; +- pointer and keyboard input; +- focus acquisition and focus visuals; +- React Native accessibility props; +- a correct UI Automation control type and patterns; +- UIA property-change notifications for native state changes. + +Use Accessibility Insights for Windows or Inspect.exe during initial +development. Add stable `testID` values to the Storybook validation story so +automated checks can locate the component through UIA. + +## Registration, projects, and autolinking + +The complete persistence chain is: + +1. Add hand-authored `.h` and `.cpp` files to the library `.vcxproj`. +2. Add `.vcxproj.filters` entries only for Visual Studio organization. +3. Leave generated code under the codegen build integration. +4. Include the component implementation from `ReactPackageProvider.cpp`. +5. Call the generated `RegisterNativeComponent` helper. +6. Preserve attributed TurboModule registration when the package has modules. +7. Regenerate or autolink through the consuming app's declared Windows script. +8. Treat generated autolink files and app solutions as disposable output. + +A file on disk but absent from `.vcxproj` is not compiled. + +When Fabric and Paper use different component names, generate the Fabric name +directly and set `paperComponentName` in `codegenNativeComponent`. Callout uses +the Fabric name `Callout` and the Paper fallback `RCTCallout`; Windows can +therefore use the generated `RegisterCalloutNativeComponent` helper without +copying or modifying generated registration code. + +## Paper compatibility + +When a package intentionally supports both architectures, guard Fabric-only +headers and implementation with `RNW_NEW_ARCH` and retain the Paper +`IViewManager` branch separately. Do not share UWP XAML types with WinAppSDK +Composition code. + +| Paper | Fabric | +| ----------------------------------------- | ------------------------------------------------------ | +| `IViewManager::CreateView` returning XAML | Generated component base creating a Composition visual | +| Native property map | Codegen props | +| `AddViewManager` | Generated Fabric registration helper | +| XAML child management | Mount and unmount component-view hooks | +| UWP brushes and geometry | Windows App SDK Composition brushes and geometry | + +Test architecture branches in separate compatible hosts. For a +New-Architecture-only package, remove obsolete Paper code rather than adding an +untested fallback. + +## Validation + +Run the smallest declared command at each layer: + +1. package format and lint; +2. TypeScript build for the wrapper and schema; +3. Windows codegen check; +4. consuming-app generation or autolink check; +5. clean native package and app build; +6. Storybook Windows bundle; +7. deployed Storybook smoke automation; +8. interaction and native event assertion; +9. UIA assertion; +10. screenshot through the agent host when visual evidence is required; +11. offline Release smoke after packaging or native dependency changes; +12. root build after public type, manifest, or project-reference changes. + +A successful JavaScript bundle does not validate native code. + +## Canonical sources + +- [RNW native platform components](https://microsoft.github.io/react-native-windows/docs/native-platform-components) +- [RNW New Architecture](https://microsoft.github.io/react-native-windows/docs/new-architecture) +- [RNW Windows codegen CLI](https://microsoft.github.io/react-native-windows/docs/codegen-windows-cli) +- [RNW native library autolinking](https://microsoft.github.io/react-native-windows/docs/native-platform-using) +- [RNW NativeModuleSample](https://github.com/microsoft/react-native-windows-samples/tree/main/samples/NativeModuleSample/cpp-lib) +- [RNW built-in Composition views](https://github.com/microsoft/react-native-windows/tree/main/vnext/Microsoft.ReactNative/Fabric/Composition) + +Use the installed dependency or matching release branch first. Treat repository +head as discovery material until each API is verified against the pinned RNW +version. diff --git a/references/agents/repository/architecture.md b/references/agents/repository/architecture.md new file mode 100644 index 0000000000..00b4140f77 --- /dev/null +++ b/references/agents/repository/architecture.md @@ -0,0 +1,65 @@ +--- +id: repository.architecture +kind: instruction-reference +owner_paths: + - packages + - apps +--- + +# Repository architecture + +FluentUI React Native is a monorepo of React Native components implementing Microsoft's Fluent Design System for +iOS, Android, macOS, Windows, and Win32. It is an alpha-stage library under active development. + +## Layout + +```text +apps/ + bundle-size/ Bundle-size measurement and baselines + component-generator/ Component scaffolding tool + E2E/ Appium/WebdriverIO end-to-end tests for v0/v1 components + fluent-tester/ FluentTester host for Android, iOS, macOS, and Windows + storybook/ Agentic component Storybook for macOS, Windows, and Win32 + tester-core/ Shared FluentTester pages and registry + win32/, win32-81/ Win32 FluentTester hosts +packages/ + agentic/components/ Modern agentic components and public primitives + agentic/design/ Design tokens, theming, styling, and color utilities + agentic/desktop-driver/ Desktop WebDriver test driver + agentic/native-core/ Shared modern native components and TurboModules + agentic/storybook-desktop*/ Reusable desktop Storybook CLI and runtime + components/ Stable v0 and v1 components + experimental/ v0/v1 components under development, including native-backed ones + framework/ v1 framework: composition, framework, use-slots, use-styling, use-tokens, theme + framework-base/ Shared component patterns, slots, hooks, accessibility, and JSX runtime + deprecated/ v0 foundation frameworks (foundation-compose and related packages) + theming/ Platform themes (android, apple, default, win32) and theme types and tokens + shim/ Compatibility entrypoints for moved packages such as Callout and FocusZone + utils/, libraries/, codemods/, dependency-profiles/ +scripts/ fluentui-scripts CLI, shared configs, and repository tasks +docs/ Documentation site source +references/agents/ Shared agent references (this library) +``` + +## Component generations + +The repository contains three component-authoring models plus native code that can accompany any of them: + +- **v0:** `@uifabricshared/foundation-compose` settings-based components; maintenance-only. +- **v1:** `@fluentui-react-native/framework` `compose` and `compressible` components; maintenance-only. Many packages + export both a legacy component and a `V1` component, for example `Button` and `ButtonV1`. +- **Agentic:** `packages/agentic/components`, built with framework-base patterns, reviewed contracts, and design + tokens. New components use this model. +- **Native:** native views, TurboModules, and services. New modern native code belongs in native-core. + +Use the [classification guide](../components/classification.md) to identify the model of the code you are changing. + +## Shared concepts + +- **Slots:** a slot is an inner component in the render tree, such as a Button's `root`, `icon`, and `content`. Slots + enable composition and customization; components wrapping one native component typically have one slot. +- **Tokens:** design-time values set by the theme or by component customization. See + [tokens and theming](../design/tokens-and-theming.md). +- **Platform files:** React Native resolves `.ios`, `.android`, `.macos`, `.windows`, and `.win32` variants. Document + platform differences in the component's `SPEC.md`. +- **Themes:** components require a `ThemeProvider` from `@fluentui-react-native/design/theming`. diff --git a/references/agents/repository/development.md b/references/agents/repository/development.md new file mode 100644 index 0000000000..1b2f3d375d --- /dev/null +++ b/references/agents/repository/development.md @@ -0,0 +1,93 @@ +--- +id: repository.development +kind: instruction-reference +owner_paths: + - package.json + - lage.config.mjs + - scripts +--- + +# Development commands and validation + +The repository builds as one TypeScript project-references graph compiled by `tsc`. Lage orchestrates bundle, test, +lint, and repository-check tasks. Yarn 4 in pnpm mode manages dependencies (see `.yarnrc.yml`). Commands below match +the declared scripts in the root [package.json](../../../package.json) and [lage.config.mjs](../../../lage.config.mjs). + +## Repository-wide rules + +- **R-VALID-001:** Run commands through the owning workspace's declared scripts. Inspect its `package.json` before + invoking an underlying runner; do not assume scripts such as `jest`, `check`, or `depcheck` exist, and do not bypass + project references with ad-hoc `tsc` file arguments. +- **R-VALID-002:** Report each validation as passed, failed, skipped, not run, or unsupported, with the reason. Never + report an unrun, all-skipped, or unsupported check as passed. JavaScript tests, bundles, and source builds are not + evidence of native runtime behavior, physical input, UIA/AX action dispatch, or Office qualification. +- **R-REPO-001:** Do not use barrel exports (`export * from '...'`). Use explicit named re-exports such as + `export { Foo, Bar } from './module'` and `export type { Baz } from './module'` so bundlers can tree-shake. +- **R-REPO-004:** Do not copy or mechanically transform private source bodies into this public repository. Record + source identifiers and author original documentation and code. + +## Root commands + +Set up a clone with `yarn` to install dependencies, then `yarn build`. + +| Command | Effect | +| ------------------------------- | ----------------------------------------------------------------------------------------------------- | +| `yarn build` | `lage root-build`: runs `root-prebuild` (package `prebuild` steps), then `tsc -b` over the root graph | +| `yarn build:clean` | Cleans the root TypeScript build, then rebuilds | +| `yarn clean` | `tsc -b --clean` plus `lage clean` | +| `yarn lage test` | Package tests after the root build and agentic contract checks | +| `yarn lage lint` | Package lint (oxlint through `fluentui-scripts lint`) | +| `yarn lint-fix` | Package lint with autofix (`FURN_FIX_MODE=true`) | +| `yarn format` / `format:check` | Format or check formatting with oxfmt | +| `yarn lint-repo` | Yarn constraints, dependency alignment, Knip, lockfile lint, and formatting | +| `yarn lint-lockfile` | Lockfile validation | +| `yarn check-publishing` | Published packages must not depend on private packages | +| `yarn check-agent-instructions` | Integrity of agent instructions, skills, references, and evaluation cases | +| `yarn test:agent-permissions` | Tests the agent permission hook | +| `yarn lage test-links` | Markdown link validation, one worker per workspace | +| `yarn bundle:repo` | Package bundles (`lage bundle`) | +| `yarn lage buildci` | CI aggregate: repository lint, publishing check, agent-instruction check, build, test, and lint | +| `yarn change` / `change:check` | Create or validate changesets; see [release](release.md) | + +Lage caches task outputs; add `--no-cache` to bypass the cache and `--verbose` for detail. After a major rework, such +as moving packages or renaming exports, run `yarn lage test --no-cache` once so every test re-runs; later incremental +runs then work normally. Before committing moved or renamed files, run `yarn lage test-links` and update stale links. + +## Package commands + +Each package's `build` script is `tsc -b`, which builds the package with its referenced dependencies. Other tasks run +through the `fluentui-scripts` CLI in [`scripts/`](../../../scripts/package.json). Common package scripts are: + +- `yarn build`: project-references build that emits `lib/`; +- `yarn lint`: oxlint; +- `yarn test`: Jest where the package declares it (`fluentui-scripts jest`); +- `yarn update-snapshots`: Jest snapshot update where declared; +- `yarn format`: formats the package; `yarn format --check` only checks. + +Packages differ. Check the scripts before running them; for example, `apps/storybook` has no `build` or `test` script. +`fluentui-scripts jest` appends every discovered test path after forwarded arguments, so a positional file name does +not isolate one file; use `--testNamePattern` to focus a run. + +## Validation discipline + +- During iteration, run the smallest relevant test, and run package lint or build early after type-heavy edits. +- For final validation, run format, lint, the package build, and package tests, then the root build when public types, + manifests, or project references change. +- After fixing a failed step, rerun that step and its downstream checks before rerunning the whole chain. +- Prefer committed `*.types.test.ts` coverage for compile-time contracts. Keep exploratory probes outside package + source, or remove them before package validation. +- Run working-directory-sensitive native tools from the owning app workspace. Flags such as CocoaPods' + `--project-directory` select project files but do not change subprocess resolution roots. +- Run `yarn` only after dependency manifests change or when a declared command fails because a dependency is missing. +- Review small, localized diffs directly. Use an independent review agent only after focused validation passes and + when the change still has an ambiguous or cross-cutting contract. +- Keep sessions scoped to one reviewable outcome; start unrelated work in a fresh session. + +## Adding a package + +- Use `workspace:*` for internal dependencies. +- Add the package's `tsconfig.json` to the root `tsconfig.json` `references` so it joins the unified build. +- [Dynamic package extensions](../../../scripts/dynamic.extensions.mts) add TypeScript, Jest, oxlint, and oxfmt + development dependencies when the package has the matching configuration or scripts. +- New components belong in the agentic package and new modern native code in native-core; see the + [classification guide](../components/classification.md). diff --git a/references/agents/repository/permissions.md b/references/agents/repository/permissions.md new file mode 100644 index 0000000000..beb72d0e04 --- /dev/null +++ b/references/agents/repository/permissions.md @@ -0,0 +1,80 @@ +--- +id: repository.permissions +kind: instruction-reference +owner_paths: + - .github/hooks/agent-permissions.json + - .github/scripts/agent-permissions.mjs +--- + +# Agent permissions + +The shared Copilot CLI defaults are implemented by [the repository hook](../../../.github/hooks/agent-permissions.json), +not by prose instructions alone. After trusting this repository, start a new CLI session to load the hook. Node.js must +be on `PATH`; the hook itself needs no dependency installation. + +- **R-PERM-001:** Hook approvals are not a sandbox. Operations the hook does not approve fall through to the CLI's + normal permission handling; they are not blanket-denied, and they are not implicitly authorized. +- **R-PERM-002:** Do not amend commits, discard other people's changes, or rewrite history without explicit + authorization. +- **R-PERM-003:** Never print authentication tokens. Do not commit personal permission files, account names, or + credentials. +- **R-PERM-004:** REST `gh api` calls to user, repository, and search endpoints are approved only for GET requests + without request-body or field flags. GraphQL and API mutations require separate approval. +- **R-PERM-005:** Run Pod operations only from the owning app or its `ios`/`macos` directory. Root-level Pod + invocations, updates of every pod, global cache cleanup, and `pod deintegrate` require separate approval. +- **R-PERM-006:** Process inspection and loopback probes are limited to the constrained forms below. Uploads, redirects, + output files, arbitrary endpoints, and process termination are not pre-approved. +- **R-PERM-007:** Unrestricted `screencapture`, `osascript`, debugger attachment, external crash-report directories, + certificate trust changes, and TCC resets remain separately authorized. Do not grant them broadly to work around a + failed native capability check. +- **R-PERM-008:** Run `yarn test:agent-permissions` after changing the permission policy. + +## Approved operations + +- Read and write files inside this repository. +- Run all Yarn commands, including `yarn` and `yarn install`. +- Inspect Git state, stage changes, create new local commits, list branches, and fetch from `origin` (subject to + R-PERM-002). +- Read GitHub issues, pull requests, workflow definitions, and pipeline runs using `gh` (subject to R-PERM-004). +- Inspect the active account with `gh auth status` and `gh api user --jq .login`, and use `gh auth switch` to select an + existing authenticated account when needed (subject to R-PERM-003). +- Run `pod install` (including `--repo-update` and `--clean-install`) or named `pod update ...` operations, + optionally through `bundle exec`, from the owning app or its `ios`/`macos` directory. A project-directory argument + must resolve to that app's native directory and existing Podfile (R-PERM-005). +- Inspect processes and listeners with constrained `ps` and `lsof` commands in Bash. Probe loopback `/status` and + `/index.json` using `curl -q` (which disables user curl configuration), GET or HEAD, and the instance's reported port + (R-PERM-006). + +## How the hook evaluates commands + +The hook accepts simple Bash or PowerShell commands and `&&` sequences when **every** command is approved. +`cd ./ && ...` is allowed only within the repository; use `./` to avoid Bash `CDPATH`. In Bash, +native-driver variables listed in the hook may prefix Yarn commands, directly for unquoted values or through `env`, for +example `FURN_NATIVE_DRIVER_TEST=1 yarn test` or +`env "FURN_DESKTOP_DRIVER_MACOS_SIGNING_IDENTITY=FURN Development" yarn storybook build-driver --macos`. + +Arbitrary environment overrides, pipelines, redirections, shell expansions, unrecognized options, remote writes, and +destructive Git operations fall through to the CLI's normal permission handling (R-PERM-001). Run commands separately +when practical. File approvals resolve symlinks and are limited to this repository. Yarn scripts and Git hooks can +execute arbitrary code, and explicit approvals intentionally trust them. + +## Native diagnostics and capture + +The Yarn approval covers the supported macOS, Windows, and Win32 Storybook workflows; extra blanket approvals for +Xcode, Swift, PowerShell scripts, Metro, or screen capture are unnecessary. Use the +[Storybook diagnostics commands](../../../apps/storybook/AGENTS.md#native-diagnostics). + +Screenshots require a running driver or app and the operating system's own screen-recording permission; CLI approval +does not grant Accessibility or Screen Recording access. Keep evidence scoped to the registered target and ignored +artifact directories (R-PERM-007). + +## Extending permissions locally + +Contributors can add permissions through the CLI's "don't ask again in this repo" prompt, which saves repository-scoped +approvals in their local `~/.copilot/permissions-config.json`, or through `--allow-tool` flags. Personal hooks can be +defined in the gitignored `.github/copilot/settings.local.json`. To opt out of hooks locally, set +`disableAllHooks: true` there; this disables all non-policy hooks. Hook approvals precede normal tool permission checks, +so disable the hook if CLI deny rules must govern these approved operations. + +See the [Copilot hooks reference](https://docs.github.com/en/copilot/reference/hooks-reference) and the +[saved permissions schema](https://docs.github.com/en/copilot/reference/copilot-cli-reference/cli-config-dir-reference#permissions-configjson). diff --git a/references/agents/repository/release.md b/references/agents/repository/release.md new file mode 100644 index 0000000000..be81513c8a --- /dev/null +++ b/references/agents/repository/release.md @@ -0,0 +1,19 @@ +--- +id: repository.release +kind: instruction-reference +owner_paths: + - .changeset + - .github/scripts/change.mts +--- + +# Changesets and release + +- **R-REPO-003:** A change to a published package needs a changeset from `yarn change`, or an empty changeset + (`yarn changeset --empty`) when no release is needed. Never bump a private package or introduce a major version. + +`yarn change:check` validates changesets in CI. The check in [change.mts](../../../.github/scripts/change.mts) requires +changesets for changed public packages and rejects private-package and major bumps. Configuration lives in +[`.changeset/config.json`](../../../.changeset/config.json). + +GitHub Actions opens version-bump pull requests automatically; `yarn changeset:version` applies version bumps. +Publishing runs in Azure Pipelines through `.ado/azure-pipelines.publish.yml`. diff --git a/references/agents/repository/typescript.md b/references/agents/repository/typescript.md new file mode 100644 index 0000000000..02564fce48 --- /dev/null +++ b/references/agents/repository/typescript.md @@ -0,0 +1,43 @@ +--- +id: repository.typescript +kind: instruction-reference +owner_paths: + - tsconfig.json + - scripts/configs/tsconfig +--- + +# TypeScript configuration + +The repository uses TypeScript 7.0.2 through `tsc`. [Dynamic package extensions](../../../scripts/dynamic.extensions.mts) +add it to every package that has a `tsconfig.json`. The root `tsconfig.json` lists every package under `references`; +`tsc -b` builds them in dependency order, and each package caches incremental state in `.cache/tsconfig.tsbuildinfo`. + +- **R-REPO-002:** Do not import more than one React Native fork (`react-native`, `react-native-windows`, + `react-native-macos`, `@office-iss/react-native-win32`) into a single program's type graph. Keep fork imports in + `.win32.ts`, `.windows.ts`, or `.macos.ts` files, or redeclare the needed shapes platform-neutrally. + +## Settings + +- Base config: [scripts/configs/tsconfig/tsconfig.json](../../../scripts/configs/tsconfig/tsconfig.json) extends + `tsconfig.strict.json`, which extends `@rnx-kit/tsconfig/tsconfig.nodenext.json`. +- `target` and `lib` are `esnext`; `module` is `esnext` with `bundler` resolution; `jsx` is `react-jsx`. +- `rewriteRelativeImportExtensions` is enabled for ESM relative imports. +- `composite: true` is required for project references, and `outDir` is `lib`. +- Strict mode is relaxed for legacy compatibility: `strictNullChecks`, `noImplicitAny`, and `strictBindCallApply` are + disabled in the base config. + +## Compatibility notes + +- `suppressImplicitAnyIndexErrors` was removed in TypeScript 5.8 and later. +- `Platform.OS` does not include `'win32'` in the React Native types even though react-native-win32 supports it at + runtime. Use `Platform.OS === ('win32' as any)`. +- The React Native forks have overlapping but divergent type definitions. Mixing them produces order-dependent type + confusion under the unified build (R-REPO-002). + +## JSX runtime + +Component files that render callable slots put `/** @jsxImportSource @fluentui-react-native/framework-base */` on the +first line. The custom runtime optimizes slot rendering, so `withSlots` is unnecessary, and it re-exports `Fragment`. +Compiled output imports the runtime, so packages that use it declare `@fluentui-react-native/framework-base` as a +dependency. The pragma alone does not identify a component's generation; see the +[classification guide](../components/classification.md). diff --git a/scripts/package.json b/scripts/package.json index 079796b1a9..ac91030fd8 100644 --- a/scripts/package.json +++ b/scripts/package.json @@ -29,7 +29,8 @@ "build": "tsc -b", "bundlesize": "bundlesize --debug", "format": "node ./src/cli.ts format", - "lint": "node ./src/cli.ts lint" + "lint": "node ./src/cli.ts lint", + "test": "node --test src/tasks/agentInstructions.test.ts" }, "dependencies": { "@babel/core": "catalog:", diff --git a/scripts/src/cli.ts b/scripts/src/cli.ts index b059ff5f0a..273f6b2de6 100644 --- a/scripts/src/cli.ts +++ b/scripts/src/cli.ts @@ -6,6 +6,7 @@ import { FormatCommand } from './tasks/format.ts'; import { LintCommand } from './tasks/lint.ts'; import { JestCommand } from './tasks/jest.ts'; import { CheckPublishingCommand } from './tasks/checkPublishingTask.ts'; +import { CheckAgentInstructionsCommand } from './tasks/checkAgentInstructionsTask.ts'; const cli = new Cli({ binaryLabel: 'fluentui-scripts CLI', @@ -18,6 +19,7 @@ cli.register(FormatCommand); cli.register(LintCommand); cli.register(JestCommand); cli.register(CheckPublishingCommand); +cli.register(CheckAgentInstructionsCommand); cli.register(Builtins.DefinitionsCommand); cli.register(Builtins.HelpCommand); diff --git a/scripts/src/tasks/agentInstructions.test.ts b/scripts/src/tasks/agentInstructions.test.ts new file mode 100644 index 0000000000..7092a062e2 --- /dev/null +++ b/scripts/src/tasks/agentInstructions.test.ts @@ -0,0 +1,169 @@ +import assert from 'node:assert/strict'; +import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs'; +import { tmpdir } from 'node:os'; +import path from 'node:path'; +import { afterEach, describe, it } from 'node:test'; + +import { checkAgentInstructions, collectAnchors, parseFrontmatter, slugify } from './agentInstructions.ts'; + +const roots: string[] = []; + +function reference(id: string, kind: string, body: string, owner = 'packages/demo'): string { + return `---\nid: ${id}\nkind: ${kind}\nowner_paths:\n - ${owner}\n---\n\n${body}`; +} + +const VALID: Record = { + 'packages/demo/index.ts': 'export {};\n', + 'packages/demo/SPEC.md': '# Demo\n\nSee [policy](AGENTS.md#focus-policy).\n', + 'packages/demo/AGENTS.md': '# Demo\n\n## Focus policy\n\n- **R-DEMO-001:** Keep focus visible.\n', + 'AGENTS.md': '# Root\n\nRead the [index](references/agents/README.md) and follow R-DEMO-001.\n', + 'references/agents/README.md': reference( + 'index', + 'index', + '# Index\n\n| ID | Document |\n| --- | --- |\n| `demo.guide` | [Guide](demo/guide.md) |\n', + 'references/agents', + ), + 'references/agents/demo/guide.md': reference('demo.guide', 'instruction-reference', '# Guide\n\nUse `packages/demo/index.ts`.\n'), + '.github/skills/demo-skill/SKILL.md': '---\nname: demo-skill\ndescription: Demonstrates a skill.\nlicense: MIT\n---\n\n# Demo skill\n', + '.github/skills/demo-skill/references/old.md': reference( + 'compat.demo.old', + 'compatibility-reference', + '# Moved\n\nSee [guide](../../../../references/agents/demo/guide.md).\n', + ), + 'references/agents/evals/cases/demo-case.json': JSON.stringify({ + schema_version: 1, + case_id: 'demo-case', + split: 'development', + task_mode: 'review', + prompt: 'Review the demo.', + expected: { + skills: ['demo-skill'], + classification: { implementation_model: 'none', public_compatibility: [], native_platforms: [] }, + required_reads: ['references/agents/demo/guide.md'], + forbidden_actions: ['edit-files'], + }, + assertions: [ + { id: 'keeps-focus', description: 'Keeps focus visible.', rule_ids: ['R-DEMO-001'], source_refs: ['packages/demo/index.ts'] }, + ], + }), +}; + +function fixture(overrides: Record = {}): string { + const root = mkdtempSync(path.join(tmpdir(), 'agent-instructions-')); + roots.push(root); + for (const [file, content] of Object.entries({ ...VALID, ...overrides })) { + if (content === undefined) { + continue; + } + mkdirSync(path.dirname(path.join(root, file)), { recursive: true }); + writeFileSync(path.join(root, file), content); + } + return root; +} + +function messages(overrides: Record): string[] { + return checkAgentInstructions(fixture(overrides)).issues.map((issue) => `${issue.file}: ${issue.message}`); +} + +function assertIssue(overrides: Record, pattern: RegExp) { + const found = messages(overrides); + assert.ok( + found.some((message) => pattern.test(message)), + `expected ${pattern} in:\n${found.join('\n')}`, + ); +} + +afterEach(() => { + for (const root of roots.splice(0)) { + rmSync(root, { recursive: true, force: true }); + } +}); + +describe('agent instruction helpers', () => { + it('creates GitHub-style anchors', () => { + assert.equal(slugify('4. Native obligations'), '4-native-obligations'); + assert.equal(slugify('`includesGeneratedCode` is not Windows-scoped'), 'includesgeneratedcode-is-not-windows-scoped'); + assert.deepEqual([...collectAnchors('# A\n## A\n```\n# ignored\n```\n\n')], ['a', 'a-1', 'custom']); + }); + + it('parses only the standardized frontmatter', () => { + assert.deepEqual(parseFrontmatter(reference('a.b', 'index', 'body')), { + id: 'a.b', + kind: 'index', + ownerPaths: ['packages/demo'], + bodyStart: 6, + }); + assert.match(String(parseFrontmatter('# No frontmatter')), /missing frontmatter/); + assert.match(String(parseFrontmatter('---\nid: a\nkind: index\nowner_paths:\n - x\ntitle: extra\n---\n')), /no other keys/); + assert.match(String(parseFrontmatter('---\nkind: index\nid: a\nowner_paths:\n - x\n---\n')), /in that order/); + }); +}); + +describe('checkAgentInstructions', () => { + it('accepts a valid repository', () => { + assert.deepEqual(messages({}), []); + }); + + it('rejects invalid frontmatter values', () => { + assertIssue({ 'references/agents/demo/guide.md': reference('Demo_Guide', 'instruction-reference', '# G\n') }, /invalid id/); + assertIssue({ 'references/agents/demo/guide.md': reference('demo.guide', 'notes', '# G\n') }, /unknown kind/); + assertIssue( + { 'references/agents/demo/guide.md': reference('demo.guide', 'instruction-reference', '# G\n', 'packages/missing') }, + /owner path/, + ); + assertIssue({ 'references/agents/demo/other.md': reference('demo.guide', 'instruction-reference', '# O\n') }, /duplicate id/); + assertIssue({ '.github/skills/demo-skill/references/plain.md': '# Plain\n' }, /missing frontmatter/); + }); + + it('requires indexed references', () => { + assertIssue({ 'references/agents/demo/new.md': reference('demo.new', 'instruction-reference', '# New\n') }, /not linked from/); + }); + + it('rejects broken links, case mismatches, missing anchors, and missing code paths', () => { + assertIssue({ 'AGENTS.md': '# Root\n\n[Missing](missing.md)\n' }, /broken link: missing\.md/); + assertIssue({ 'AGENTS.md': '# Root\n\n[Case](references/agents/README.MD)\n' }, /broken link/); + assertIssue({ 'AGENTS.md': '# Root\n\n[Anchor](packages/demo/AGENTS.md#absent)\n' }, /missing anchor/); + assertIssue({ 'AGENTS.md': '# Root\n\nRun `packages/demo/missing.ts`.\n' }, /code span does not exist/); + }); + + it('protects inbound anchors into instruction files', () => { + assertIssue( + { 'packages/demo/AGENTS.md': '# Demo\n\n- **R-DEMO-001:** Keep focus visible.\n' }, + /SPEC\.md: missing anchor in instruction file/, + ); + }); + + it('requires rule IDs to be defined exactly once', () => { + assertIssue({ 'AGENTS.md': '# Root\n\nFollow R-DEMO-002.\n' }, /undefined rule ID R-DEMO-002/); + assertIssue({ 'AGENTS.md': '# Root\n\n- **R-DEMO-001:** Duplicate.\n' }, /already defined/); + }); + + it('rejects compatibility redirects without a live target', () => { + assertIssue( + { + '.github/skills/demo-skill/references/older.md': reference( + 'compat.demo.older', + 'compatibility-reference', + '# Moved\n\n[Old](old.md)\n', + ), + }, + /only to another compatibility reference/, + ); + }); + + it('validates skill metadata', () => { + assertIssue({ '.github/skills/demo-skill/SKILL.md': '---\nname: other\ndescription: Text.\n---\n' }, /must match its directory/); + assertIssue({ '.github/skills/demo-skill/SKILL.md': `---\nname: demo-skill\ndescription: ${'word '.repeat(61)}\n---\n` }, /61 words/); + }); + + it('validates evaluation cases', () => { + const base = JSON.parse(VALID['references/agents/evals/cases/demo-case.json']); + const write = (value: object) => ({ 'references/agents/evals/cases/demo-case.json': JSON.stringify(value) }); + assertIssue(write({ ...base, case_id: 'other' }), /must match the file name/); + assertIssue(write({ ...base, expected: { ...base.expected, skills: ['missing-skill'] } }), /unknown skill/); + assertIssue(write({ ...base, expected: { ...base.expected, required_reads: ['missing.md'] } }), /path does not exist/); + assertIssue(write({ ...base, assertions: [{ ...base.assertions[0], rule_ids: ['R-DEMO-404'] }] }), /undefined rule/); + assertIssue(write({ ...base, assertions: [{ id: 'empty', description: 'No evidence.', rule_ids: [], source_refs: [] }] }), /must cite/); + assertIssue(write({ ...base, split: 'training' }), /split must be/); + }); +}); diff --git a/scripts/src/tasks/agentInstructions.ts b/scripts/src/tasks/agentInstructions.ts new file mode 100644 index 0000000000..e6182667b8 --- /dev/null +++ b/scripts/src/tasks/agentInstructions.ts @@ -0,0 +1,551 @@ +import { execFileSync } from 'node:child_process'; +import { existsSync, readdirSync, readFileSync, statSync } from 'node:fs'; +import path from 'node:path'; + +/** + * Deterministic integrity checks for agent instructions, skills, shared references, and evaluation fixtures. + * The conventions are documented in references/agents/README.md and references/agents/evals/README.md. + */ + +export type Issue = { file: string; line?: number; message: string }; + +export type CheckResult = { + issues: Issue[]; + report: { file: string; bytes: number }[]; +}; + +const REFERENCES_DIR = 'references/agents'; +const EVAL_CASES_DIR = `${REFERENCES_DIR}/evals/cases`; +const SKILLS_DIR = '.github/skills'; +const ALWAYS_ON = ['AGENTS.md', 'CLAUDE.md', '.github/copilot-instructions.md']; + +const KINDS = new Set(['index', 'instruction-reference', 'evidence-reference', 'compatibility-reference']); +const INDEXED_KINDS = new Set(['instruction-reference', 'evidence-reference']); +const ID_PATTERN = /^[a-z0-9][a-z0-9-]*(\.[a-z0-9][a-z0-9-]*)*$/; +const RULE_DEFINITION = /^\s*[-*]\s+\*\*([RC]-[A-Z0-9]+-\d{3}):\*\*/; +const RULE_CITATION = /\b[RC]-[A-Z0-9]+-\d{3}\b/g; +const MAX_SKILL_DESCRIPTION_WORDS = 60; +const SKIP_DIRS = new Set(['node_modules', 'lib', 'lib-commonjs', 'dist', '.git', 'Pods', 'build', 'DerivedData']); +const REPO_PATH_PREFIXES = ['.github/', 'apps/', 'packages/', 'references/', 'scripts/', 'docs/']; + +const TASK_MODES = new Set(['implement', 'maintain', 'review', 'plan', 'contract', 'optimize', 'toolchain', 'security']); +const SPLITS = new Set(['development', 'holdout']); +const MODELS = new Set(['v0', 'v1-compose', 'v1-compressible', 'agentic-higher-order', 'agentic-primitive', 'framework-base', 'none']); +const PLATFORMS = new Set(['ios', 'android', 'macos', 'windows', 'win32']); +const FORBIDDEN_ACTIONS = new Set(['edit-files', 'run-mutating-commands', 'create-component', 'migrate-framework']); + +type Frontmatter = { id: string; kind: string; ownerPaths: string[]; bodyStart: number }; + +/** Lists repository files, preferring Git so ignored output is excluded. Falls back to a filesystem walk. */ +export function listFiles(root: string): string[] { + try { + const output = execFileSync('git', ['ls-files', '-z', '--cached', '--others', '--exclude-standard'], { + cwd: root, + encoding: 'utf8', + stdio: ['ignore', 'pipe', 'ignore'], + maxBuffer: 64 * 1024 * 1024, + }); + return output + .split('\0') + .filter(Boolean) + .filter((file) => existsSync(path.join(root, file))); + } catch { + const files: string[] = []; + const walk = (dir: string) => { + for (const entry of readdirSync(path.join(root, dir), { withFileTypes: true })) { + const rel = dir ? `${dir}/${entry.name}` : entry.name; + if (entry.isDirectory()) { + if (!SKIP_DIRS.has(entry.name)) { + walk(rel); + } + } else if (entry.isFile()) { + files.push(rel); + } + } + }; + walk(''); + return files; + } +} + +/** Returns true when a repository-relative path exists with exactly this letter case. */ +export function existsExact(root: string, rel: string): boolean { + const normalized = path.posix.normalize(rel).replace(/\/$/, ''); + if (normalized === '' || normalized === '.') { + return true; + } + if (normalized.startsWith('..')) { + return false; + } + let current = root; + for (const segment of normalized.split('/')) { + let entries: string[]; + try { + entries = readdirSync(current); + } catch { + return false; + } + if (!entries.includes(segment)) { + return false; + } + current = path.join(current, segment); + } + return true; +} + +/** Returns true when Git ignores the path, such as documented personal configuration files. */ +function isGitIgnored(root: string, rel: string): boolean { + try { + execFileSync('git', ['check-ignore', '-q', '--no-index', rel], { cwd: root, stdio: 'ignore' }); + return true; + } catch { + return false; + } +} + +/** GitHub-style heading anchor. */ +export function slugify(heading: string): string { + return heading + .replace(/\[([^\]]*)\]\([^)]*\)/g, '$1') + .replace(/<[^>]+>/g, '') + .trim() + .toLowerCase() + .replace(/[^\p{L}\p{N}\s_-]/gu, '') + .replace(/\s/g, '-'); +} + +/** Collects heading anchors and explicit HTML anchors from Markdown, ignoring fenced code. */ +export function collectAnchors(markdown: string): Set { + const anchors = new Set(); + const counts = new Map(); + let fenced = false; + for (const line of markdown.split('\n')) { + if (/^\s*(```|~~~)/.test(line)) { + fenced = !fenced; + continue; + } + if (fenced) { + continue; + } + const heading = /^#{1,6}\s+(.*?)\s*#*\s*$/.exec(line); + if (heading) { + const base = slugify(heading[1]); + const count = counts.get(base) ?? 0; + counts.set(base, count + 1); + anchors.add(count === 0 ? base : `${base}-${count}`); + } + for (const match of line.matchAll(/ { + if (/^\s*(```|~~~)/.test(raw)) { + fenced = !fenced; + return; + } + if (fenced) { + return; + } + const line = raw.replace(/`[^`]*`/g, '``'); + for (const match of line.matchAll(/\]\(]+)>?(?:\s+"[^"]*")?\)/g)) { + links.push({ target: match[1], line: index + 1 }); + } + const definition = /^\s*\[[^\]]+\]:\s*(\S+)/.exec(line); + if (definition) { + links.push({ target: definition[1], line: index + 1 }); + } + }); + return links; +} + +/** Extracts inline code spans that look like repository paths. */ +export function extractCodePaths(markdown: string): Link[] { + const paths: Link[] = []; + let fenced = false; + markdown.split('\n').forEach((line, index) => { + if (/^\s*(```|~~~)/.test(line)) { + fenced = !fenced; + return; + } + if (fenced) { + return; + } + for (const match of line.matchAll(/`([^`\s]+)`/g)) { + const value = match[1]; + if (REPO_PATH_PREFIXES.some((prefix) => value.startsWith(prefix)) && !/[<>*{}|$]|\.\.\./.test(value)) { + paths.push({ target: value.replace(/[#:].*$/, ''), line: index + 1 }); + } + } + }); + return paths; +} + +/** Parses the standardized reference frontmatter. Returns an error message for any deviation. */ +export function parseFrontmatter(markdown: string): Frontmatter | string { + const lines = markdown.split('\n'); + if (lines[0] !== '---') { + return 'missing frontmatter; the first line must be ---'; + } + const idLine = /^id: (\S+)$/.exec(lines[1] ?? ''); + const kindLine = /^kind: (\S+)$/.exec(lines[2] ?? ''); + if (!idLine || !kindLine || lines[3] !== 'owner_paths:') { + return 'frontmatter must contain exactly id, kind, and owner_paths in that order'; + } + const ownerPaths: string[] = []; + let index = 4; + while (index < lines.length && /^ {2}- \S+$/.test(lines[index])) { + ownerPaths.push(lines[index].slice(4)); + index++; + } + if (lines[index] !== '---') { + return 'frontmatter must end after the owner_paths list; no other keys are allowed'; + } + if (ownerPaths.length === 0) { + return 'owner_paths must list at least one path'; + } + return { id: idLine[1], kind: kindLine[1], ownerPaths, bodyStart: index + 1 }; +} + +function parseSkillFrontmatter(markdown: string): Record | undefined { + const match = /^---\n([\s\S]*?)\n---\n/.exec(markdown); + if (!match) { + return undefined; + } + const fields: Record = {}; + for (const line of match[1].split('\n')) { + const field = /^([a-z-]+):\s*(.*)$/.exec(line); + if (field) { + fields[field[1]] = field[2].trim(); + } + } + return fields; +} + +function isInstructionSurface(file: string): boolean { + return ( + path.posix.basename(file) === 'AGENTS.md' || + file === 'CLAUDE.md' || + file === '.github/copilot-instructions.md' || + (file.startsWith(`${SKILLS_DIR}/`) && file.endsWith('.md')) || + (file.startsWith(`${REFERENCES_DIR}/`) && file.endsWith('.md')) + ); +} + +function requiresFrontmatter(file: string): boolean { + return ( + file.endsWith('.md') && + (file.startsWith(`${REFERENCES_DIR}/`) || new RegExp(`^${SKILLS_DIR.replace('.', '\\.')}/[^/]+/references/`).test(file)) + ); +} + +function resolveLink(fromFile: string, target: string): { file: string; fragment?: string } | undefined { + if (/^[a-z][a-z0-9+.-]*:/i.test(target)) { + return undefined; + } + const [rawPath, fragment] = target.split('#', 2); + let decoded: string; + try { + decoded = decodeURIComponent(rawPath); + } catch { + decoded = rawPath; + } + const file = !decoded + ? fromFile + : decoded.startsWith('/') + ? path.posix.normalize(decoded.slice(1)) + : path.posix.normalize(path.posix.join(path.posix.dirname(fromFile), decoded)); + return { file, fragment: fragment || undefined }; +} + +/** Runs every check against the repository at `root`. */ +export function checkAgentInstructions(root: string): CheckResult { + const issues: Issue[] = []; + const files = listFiles(root); + const read = (file: string) => readFileSync(path.join(root, file), 'utf8'); + const surfaces = files.filter(isInstructionSurface).sort(); + const anchorCache = new Map>(); + const anchorsOf = (file: string) => { + let anchors = anchorCache.get(file); + if (!anchors) { + anchors = collectAnchors(read(file)); + anchorCache.set(file, anchors); + } + return anchors; + }; + + // Frontmatter, IDs, and kinds. + const frontmatter = new Map(); + const ids = new Map(); + for (const file of surfaces.filter(requiresFrontmatter)) { + const parsed = parseFrontmatter(read(file)); + if (typeof parsed === 'string') { + issues.push({ file, line: 1, message: parsed }); + continue; + } + frontmatter.set(file, parsed); + if (!ID_PATTERN.test(parsed.id)) { + issues.push({ file, line: 2, message: `invalid id "${parsed.id}"` }); + } + const previous = ids.get(parsed.id); + if (previous) { + issues.push({ file, line: 2, message: `duplicate id "${parsed.id}" (also in ${previous})` }); + } + ids.set(parsed.id, file); + if (!KINDS.has(parsed.kind)) { + issues.push({ file, line: 3, message: `unknown kind "${parsed.kind}"` }); + } + for (const owner of parsed.ownerPaths) { + if (!existsExact(root, owner)) { + issues.push({ file, message: `owner path does not exist: ${owner}` }); + } + } + } + + // Index completeness. + const indexFile = `${REFERENCES_DIR}/README.md`; + if (files.includes(indexFile)) { + const index = read(indexFile); + const indexed = new Set( + extractLinks(index) + .map((link) => resolveLink(indexFile, link.target)?.file) + .filter(Boolean), + ); + for (const [file, meta] of frontmatter) { + if (file.startsWith(`${REFERENCES_DIR}/`) && INDEXED_KINDS.has(meta.kind)) { + if (!indexed.has(file)) { + issues.push({ file, message: `not linked from ${indexFile}` }); + } + if (!index.includes(`\`${meta.id}\``)) { + issues.push({ file, message: `id "${meta.id}" is missing from the ${indexFile} document index` }); + } + } + } + } else if (frontmatter.size > 0) { + issues.push({ file: indexFile, message: 'missing reference index' }); + } + + // Compatibility references must point directly at a live document. + for (const [file, meta] of frontmatter) { + if (meta.kind !== 'compatibility-reference') { + continue; + } + const targets = extractLinks(read(file)) + .map((link) => resolveLink(file, link.target)?.file) + .filter((target): target is string => target !== undefined && target.endsWith('.md') && existsExact(root, target)); + if (targets.length === 0) { + issues.push({ file, message: 'compatibility reference must link to its replacement' }); + } else if (targets.every((target) => frontmatter.get(target)?.kind === 'compatibility-reference')) { + issues.push({ file, message: 'compatibility reference links only to another compatibility reference' }); + } + } + + // Skills. + const skillNames = new Map(); + for (const file of surfaces.filter((f) => /^\.github\/skills\/[^/]+\/SKILL\.md$/.test(f))) { + const dir = file.split('/')[2]; + const fields = parseSkillFrontmatter(read(file)); + if (!fields) { + issues.push({ file, line: 1, message: 'missing skill frontmatter' }); + continue; + } + if (fields.name !== dir) { + issues.push({ file, line: 2, message: `skill name "${fields.name}" must match its directory "${dir}"` }); + } + if (skillNames.has(fields.name)) { + issues.push({ file, message: `duplicate skill name "${fields.name}"` }); + } + skillNames.set(fields.name, file); + const words = (fields.description ?? '').split(/\s+/).filter(Boolean).length; + if (words === 0) { + issues.push({ file, message: 'skill description is empty' }); + } else if (words > MAX_SKILL_DESCRIPTION_WORDS) { + issues.push({ file, message: `skill description has ${words} words; the limit is ${MAX_SKILL_DESCRIPTION_WORDS}` }); + } + } + + // Outbound links and code paths from instruction surfaces. + for (const file of surfaces) { + const text = read(file); + for (const link of extractLinks(text)) { + const resolved = resolveLink(file, link.target); + if (!resolved) { + continue; + } + if (!existsExact(root, resolved.file)) { + issues.push({ file, line: link.line, message: `broken link: ${link.target}` }); + continue; + } + if (resolved.fragment && resolved.file.endsWith('.md') && !anchorsOf(resolved.file).has(resolved.fragment)) { + issues.push({ file, line: link.line, message: `missing anchor: ${link.target}` }); + } + } + for (const codePath of extractCodePaths(text)) { + if (!existsExact(root, codePath.target) && !isGitIgnored(root, codePath.target)) { + issues.push({ file, line: codePath.line, message: `path in code span does not exist: ${codePath.target}` }); + } + } + } + + // Inbound fragments from any Markdown into instruction surfaces. + const surfaceSet = new Set(surfaces); + for (const file of files) { + if (!file.endsWith('.md') || surfaceSet.has(file) || path.posix.basename(file) === 'CHANGELOG.md') { + continue; + } + for (const link of extractLinks(read(file))) { + const resolved = resolveLink(file, link.target); + if (resolved?.fragment && surfaceSet.has(resolved.file) && !anchorsOf(resolved.file).has(resolved.fragment)) { + issues.push({ file, line: link.line, message: `missing anchor in instruction file: ${link.target}` }); + } + } + } + + // Rule definitions and citations. + const rules = new Map(); + for (const file of surfaces) { + read(file) + .split('\n') + .forEach((line, index) => { + const match = RULE_DEFINITION.exec(line); + if (match) { + const previous = rules.get(match[1]); + if (previous) { + issues.push({ file, line: index + 1, message: `rule ${match[1]} is already defined in ${previous}` }); + } else { + rules.set(match[1], `${file}:${index + 1}`); + } + } + }); + } + for (const file of surfaces) { + read(file) + .split('\n') + .forEach((line, index) => { + for (const match of line.matchAll(RULE_CITATION)) { + if (!rules.has(match[0])) { + issues.push({ file, line: index + 1, message: `undefined rule ID ${match[0]}` }); + } + } + }); + } + + // Evaluation fixtures. + const caseIds = new Set(); + for (const file of files.filter((f) => f.startsWith(`${EVAL_CASES_DIR}/`))) { + issues.push(...checkEvalCase(root, file, read(file), { caseIds, rules, skills: skillNames })); + } + + const report = [...ALWAYS_ON, ...skillNames.values()] + .filter((file) => files.includes(file)) + .map((file) => ({ file, bytes: statSync(path.join(root, file)).size })); + return { issues, report }; +} + +function checkEvalCase( + root: string, + file: string, + text: string, + context: { caseIds: Set; rules: Map; skills: Map }, +): Issue[] { + const issues: Issue[] = []; + const fail = (message: string) => issues.push({ file, message }); + if (!file.endsWith('.json')) { + fail('evaluation cases must be JSON files'); + return issues; + } + let data: Record; + try { + data = JSON.parse(text); + } catch (error) { + fail(`invalid JSON: ${(error as Error).message}`); + return issues; + } + const stem = path.posix.basename(file, '.json'); + const strings = (value: unknown): string[] | undefined => + Array.isArray(value) && value.every((item) => typeof item === 'string') ? (value as string[]) : undefined; + const checkPaths = (label: string, value: unknown) => { + const list = strings(value); + if (!list) { + fail(`${label} must be an array of strings`); + return []; + } + for (const item of list) { + if (!existsExact(root, item)) { + fail(`${label} path does not exist: ${item}`); + } + } + return list; + }; + + if (data.schema_version !== 1) fail('schema_version must be 1'); + if (data.case_id !== stem) fail(`case_id must match the file name "${stem}"`); + if (context.caseIds.has(stem)) fail(`duplicate case_id "${stem}"`); + context.caseIds.add(stem); + if (!SPLITS.has(data.split as string)) fail('split must be development or holdout'); + if (!TASK_MODES.has(data.task_mode as string)) fail(`unknown task_mode "${String(data.task_mode)}"`); + if (typeof data.prompt !== 'string' || data.prompt.trim() === '') fail('prompt must be a non-empty string'); + + const expected = (data.expected ?? {}) as Record; + const skills = strings(expected.skills); + if (!skills || skills.length === 0) { + fail('expected.skills must list at least one skill'); + } else { + for (const skill of skills) { + if (!context.skills.has(skill)) fail(`unknown skill "${skill}"`); + } + } + const classification = (expected.classification ?? {}) as Record; + if (!MODELS.has(classification.implementation_model as string)) { + fail(`unknown implementation_model "${String(classification.implementation_model)}"`); + } + if (!strings(classification.public_compatibility)) fail('expected.classification.public_compatibility must be an array of strings'); + const platforms = strings(classification.native_platforms); + if (!platforms) { + fail('expected.classification.native_platforms must be an array of strings'); + } else { + for (const platform of platforms) { + if (!PLATFORMS.has(platform)) fail(`unknown native platform "${platform}"`); + } + } + checkPaths('expected.required_reads', expected.required_reads); + const forbidden = strings(expected.forbidden_actions); + if (!forbidden) { + fail('expected.forbidden_actions must be an array of strings'); + } else { + for (const action of forbidden) { + if (!FORBIDDEN_ACTIONS.has(action)) fail(`unknown forbidden action "${action}"`); + } + } + + const assertions = data.assertions; + if (!Array.isArray(assertions) || assertions.length === 0) { + fail('assertions must be a non-empty array'); + return issues; + } + const assertionIds = new Set(); + for (const assertion of assertions as Record[]) { + const id = assertion.id as string; + if (typeof id !== 'string' || !/^[a-z0-9-]+$/.test(id)) fail(`invalid assertion id "${String(id)}"`); + if (assertionIds.has(id)) fail(`duplicate assertion id "${id}"`); + assertionIds.add(id); + if (typeof assertion.description !== 'string' || assertion.description.trim() === '') { + fail(`assertion "${id}" needs a description`); + } + const ruleIds = strings(assertion.rule_ids) ?? []; + for (const rule of ruleIds) { + if (!context.rules.has(rule)) fail(`assertion "${id}" cites undefined rule ${rule}`); + } + const refs = checkPaths(`assertion "${id}" source_refs`, assertion.source_refs); + if (ruleIds.length === 0 && refs.length === 0) fail(`assertion "${id}" must cite a rule ID or a source path`); + } + return issues; +} diff --git a/scripts/src/tasks/checkAgentInstructionsTask.ts b/scripts/src/tasks/checkAgentInstructionsTask.ts new file mode 100644 index 0000000000..ec05ebf25e --- /dev/null +++ b/scripts/src/tasks/checkAgentInstructionsTask.ts @@ -0,0 +1,36 @@ +import { Command, Option } from 'clipanion'; +import { findGitRoot } from 'workspace-tools'; + +import { checkAgentInstructions } from './agentInstructions.ts'; + +export class CheckAgentInstructionsCommand extends Command { + static override paths = [['check-agent-instructions']]; + + static override usage = Command.Usage({ + description: 'Check the integrity of agent instructions, skills, shared references, and evaluation cases', + details: + 'Validates reference frontmatter, links and anchors, code-span paths, skill metadata, rule IDs, the reference index, and evaluation fixtures. See references/agents/README.md.', + examples: [['Check agent instructions', '$0 check-agent-instructions']], + }); + + verbose = Option.Boolean('--verbose', false, { description: 'Report always-on instruction and skill sizes' }); + + async execute() { + const root = findGitRoot(process.cwd()); + const { issues, report } = checkAgentInstructions(root); + if (this.verbose) { + for (const entry of report) { + console.log(`${entry.bytes.toString().padStart(7)} bytes ${entry.file}`); + } + } + for (const issue of issues) { + console.error(`${issue.file}${issue.line ? `:${issue.line}` : ''}: ${issue.message}`); + } + if (issues.length > 0) { + console.error(`Agent instruction check failed with ${issues.length} issue(s).`); + return 1; + } + console.log('Agent instruction check passed.'); + return 0; + } +} From 14a561faa0cc624a95fd8268276953315c8b6bae Mon Sep 17 00:00:00 2001 From: Jason Morse Date: Wed, 14 Oct 2026 22:46:06 -0700 Subject: [PATCH 04/14] Centralize Apple/Office native rules and require uncached final buildci Move the Apple native authoring and Office integration rules into references/agents/native/apple-office.md as R-APPLE-001 to R-APPLE-007, with sentence-level parity, and route the root safeguards, implement-native-component, and review-component to them. Add Apple implementation, review, and holdout evaluation cases. Add R-VALID-003: finish code, configuration, and instruction changes with one `yarn lage buildci --no-cache` run so stale Lage cache entries cannot hide failures. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../agentic-component-authoring/SKILL.md | 2 +- .../agentic-storybook-development/SKILL.md | 2 +- .github/skills/fix-security-issues/SKILL.md | 1 + .../implement-native-component/SKILL.md | 12 +++- .../skills/maintain-v0-v1-component/SKILL.md | 2 +- .github/skills/review-component/SKILL.md | 2 +- AGENTS.md | 10 +++- agent-improvements-plan.md | 24 +++++--- references/agents/README.md | 1 + .../cases/apple-extern-header-review.json | 43 ++++++++++++++ .../evals/cases/ios-react-header-helper.json | 37 ++++++++++++ .../cases/macos-paper-manager-command.json | 14 +++-- references/agents/native/apple-office.md | 59 +++++++++++++++++++ .../agents/native/components-and-modules.md | 4 +- references/agents/repository/development.md | 12 ++-- 15 files changed, 196 insertions(+), 29 deletions(-) create mode 100644 references/agents/evals/cases/apple-extern-header-review.json create mode 100644 references/agents/evals/cases/ios-react-header-helper.json create mode 100644 references/agents/native/apple-office.md diff --git a/.github/skills/agentic-component-authoring/SKILL.md b/.github/skills/agentic-component-authoring/SKILL.md index 13e937e6b6..a2cd850e9a 100644 --- a/.github/skills/agentic-component-authoring/SKILL.md +++ b/.github/skills/agentic-component-authoring/SKILL.md @@ -65,7 +65,7 @@ the affected reference and its immediate neighbors. Upstream changes require explicit re-review and never overwrite the local contract. 9. Run the smallest declared validation command while iterating. Finish with package format, lint, build, and tests; run the Storybook bundle for story changes and the root build when public types, manifests, or project references - change. Report results truthfully (R-VALID-002). + change. Finish with one `yarn lage buildci --no-cache` (R-VALID-003) and report results truthfully (R-VALID-002). Do not divide one component implementation into separate sub-agent or sub-skill phases. Its types, state, styling, and rendering form one contract and should remain in one implementation context. diff --git a/.github/skills/agentic-storybook-development/SKILL.md b/.github/skills/agentic-storybook-development/SKILL.md index 96b68a1abd..abbb7b0b1a 100644 --- a/.github/skills/agentic-storybook-development/SKILL.md +++ b/.github/skills/agentic-storybook-development/SKILL.md @@ -25,7 +25,7 @@ Work on the native Storybook application in `apps/storybook`. spec without refreshing ReactCodegen's source list. If the clean build then reports a missing generated spec header, run `pods:macos` once more before changing native configuration. 6. Validate the JavaScript bundle and the affected native build. Run Storybook format/lint and the root build when - manifests or shared package references changed. + manifests or shared package references changed. Finish with one `yarn lage buildci --no-cache` (R-VALID-003). Keep component API, slot, token, and rendering work in the `agentic-component-authoring` skill; use this skill for the Storybook application and its native toolchains. diff --git a/.github/skills/fix-security-issues/SKILL.md b/.github/skills/fix-security-issues/SKILL.md index bfb02b0481..7337207e35 100644 --- a/.github/skills/fix-security-issues/SKILL.md +++ b/.github/skills/fix-security-issues/SKILL.md @@ -157,6 +157,7 @@ Use declared workspace scripts and validate in increasing scope: 6. Run the root build and repository test graph when shared manifests, catalogs, resolutions, or the lockfile change broadly. 7. Add a normal or empty changeset as required by repository policy. +8. Finish with one `yarn lage buildci --no-cache` run (R-VALID-003). Finish by confirming: diff --git a/.github/skills/implement-native-component/SKILL.md b/.github/skills/implement-native-component/SKILL.md index 3867e81ae5..20a6f85931 100644 --- a/.github/skills/implement-native-component/SKILL.md +++ b/.github/skills/implement-native-component/SKILL.md @@ -24,7 +24,9 @@ ownership and qualification rules (R-NATIVE-001 through R-NATIVE-003). [native-core README](../../../packages/agentic/native-core/README.md). 3. Load only the platform references the work needs: - React Native Windows Fabric views: [Windows Fabric](../../../references/agents/native/windows-fabric.md); - - Apple: native-core's native layout and Swift/Objective-C interoperability notes; + - any iOS or macOS Swift, Objective-C, header, podspec, or bridge-manager change: + [Apple native authoring and Office integration](../../../references/agents/native/apple-office.md) + (R-APPLE-001 through R-APPLE-007), plus native-core's macOS layout notes; - focus targets or native focus visuals: [common focus authoring](../../../references/agents/focus/common.md) and its platform file; - accessibility actions: @@ -38,9 +40,11 @@ ownership and qualification rules (R-NATIVE-001 through R-NATIVE-003). `codegen:windows` when Windows specs change; native builds through the owning app, such as `yarn storybook build --macos` or `--windows` from `apps/storybook`; and runtime evidence through Storybook smoke or `wdio` story tests. macOS Storybook commands default to Fabric: add `--paper` to `prep`, `build`, `run`, or `smoke` - for Paper, and rerun `prep` when switching renderers. Run Pod operations only as the + for Paper, and rerun `prep` when switching renderers. Apple changes also need the module and bridging-header checks in + R-APPLE-007. Run Pod operations only as the [permissions reference](../../../references/agents/repository/permissions.md) allows. 6. Report each platform and renderer as passed, failed, skipped, not run, or unsupported, with reasons (R-NATIVE-003). +7. Finish with one `yarn lage buildci --no-cache` run (R-VALID-003). Storybook application or toolchain failures that are not caused by the native change belong to the [Storybook skill](../agentic-storybook-development/SKILL.md). @@ -54,6 +58,10 @@ Use this checklist for implementation and review: - Registration names, autolinking, and entrypoints are unchanged unless requested; consuming apps depend directly on native-core. - Native view lookup and UI mutation run on the platform's UI thread. +- iOS and macOS changes follow the [Apple and Office rules](../../../references/agents/native/apple-office.md): + qualified `` headers, guarded Swift `import React`, no generated Swift header in an `RCT_EXTERN_MODULE` + file, generated-type-free Paper bridges, main-queue dispatch, and strict warnings (R-APPLE-001 through R-APPLE-006). + Module and bridging-header validation ran, or the unrun mode is reported (R-APPLE-007). - Native resources, observers, and pending work are released on unmount, cancellation, or failure. - Each export works on every host its entrypoint promises; platform-only APIs use the narrowest entrypoint. - Podspecs, Windows projects, and package `files` include every new native source. diff --git a/.github/skills/maintain-v0-v1-component/SKILL.md b/.github/skills/maintain-v0-v1-component/SKILL.md index a4b5eb38d2..ee07ba2016 100644 --- a/.github/skills/maintain-v0-v1-component/SKILL.md +++ b/.github/skills/maintain-v0-v1-component/SKILL.md @@ -30,7 +30,7 @@ v0 and v1 are maintenance-only (R-V0-001 and R-V1-001). A request for a new comp `componentRef`, token contracts, and native registration names (R-V0-002, R-V0-003, R-V1-002, R-V1-003). 6. Validate with the owning package's declared scripts (R-VALID-001): build, test, and lint, plus the framework packages named in the recipe when framework semantics change. Review snapshot diffs before accepting them. Report - each check's outcome (R-VALID-002). + each check's outcome (R-VALID-002), and finish with one `yarn lage buildci --no-cache` (R-VALID-003). 7. Add a changeset for published packages (R-REPO-003). Stop and confirm scope before migrating between frameworks, creating a component, or changing a public API. Those diff --git a/.github/skills/review-component/SKILL.md b/.github/skills/review-component/SKILL.md index 8dfda49b4e..40c6a91044 100644 --- a/.github/skills/review-component/SKILL.md +++ b/.github/skills/review-component/SKILL.md @@ -28,7 +28,7 @@ license: MIT | Tests and stories | [Tests and stories](../../../references/agents/components/agentic/tests-and-stories.md) for agentic code; [legacy test apps](../../../references/agents/components/legacy-test-apps.md) for v0 and v1 | | Contract or provenance | [Contract authoring](../agentic-component-contract-authoring/SKILL.md) rules and only the [source adapters](../../../references/agents/README.md#document-index) matching the changed evidence | | Focus or keyboard | [Required focus evidence](../../../references/agents/focus/common.md#required-evidence-before-shipping) and the affected platform file | - | Native code or roots | [Native overlay checklist](../implement-native-component/SKILL.md#native-overlay-checklist) | + | Native code or roots | [Native overlay checklist](../implement-native-component/SKILL.md#native-overlay-checklist); for iOS or macOS, also [Apple and Office rules](../../../references/agents/native/apple-office.md) | | Framework-base pattern | [Framework-base instructions](../../../packages/framework-base/AGENTS.md) | 4. Check repository-wide rules: explicit exports (R-REPO-001), platform fork imports (R-REPO-002), changesets diff --git a/AGENTS.md b/AGENTS.md index b903d3357c..bba65ace7f 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -26,7 +26,8 @@ These apply to every task. authorization (R-PERM-007). Read the [permissions reference](references/agents/repository/permissions.md) before using `gh api`, Pod, process, loopback `curl`, or capture operations, or changing the policy. - **Validation:** run the owning workspace's declared scripts (R-VALID-001). Report each check as passed, failed, - skipped, not run, or unsupported; JavaScript tests and bundles are not native qualification (R-VALID-002). + skipped, not run, or unsupported; JavaScript tests and bundles are not native qualification (R-VALID-002). Finish + code or configuration changes with one `yarn lage buildci --no-cache` run (R-VALID-003). - **Code:** no barrel exports (R-REPO-001); keep React Native fork imports in platform files (R-REPO-002); add a changeset for published packages (R-REPO-003); never copy private source bodies into this public repository (R-REPO-004). @@ -34,7 +35,9 @@ These apply to every task. R-V1-001). Classify code by imports and structure, not by path (R-CLASSIFY-001). - **Native boundary:** before changing Swift, Objective-C, C++, headers, podspecs, Windows projects, codegen specs, native registration, or a native-backed platform wrapper, use the - [native component skill](.github/skills/implement-native-component/SKILL.md), directly or as an overlay. + [native component skill](.github/skills/implement-native-component/SKILL.md), directly or as an overlay. iOS and + macOS changes must keep both CocoaPods-module and Office bridging-header builds working; follow the + [Apple and Office rules](references/agents/native/apple-office.md). ## Route the task @@ -74,7 +77,8 @@ validation, TypeScript, release, design tokens, focus, and native guidance. - Yarn 4 in pnpm mode; TypeScript 7.0.2 compiled as one `tsc -b` project-references graph; Lage task orchestration. New packages must be added to the root `tsconfig.json` `references`. - `yarn build` builds the repository; `yarn lage test` and `yarn lage lint` test and lint packages; `yarn format` formats. - `yarn lage buildci` runs the CI aggregate, including `yarn check-agent-instructions`. + `yarn lage buildci` runs the CI aggregate, including `yarn check-agent-instructions`; use `--no-cache` once for final + validation. - Details: [commands and validation](references/agents/repository/development.md), [architecture](references/agents/repository/architecture.md), [TypeScript](references/agents/repository/typescript.md), and [release](references/agents/repository/release.md). diff --git a/agent-improvements-plan.md b/agent-improvements-plan.md index 1f1fdeaf7c..1f60a1cde9 100644 --- a/agent-improvements-plan.md +++ b/agent-improvements-plan.md @@ -605,7 +605,8 @@ below; the efficiency pilot is reporting-only under D08. | Root router (D05, D06) | Root `AGENTS.md` shrank from 27,423 to about 7,400 bytes. It keeps always-on safeguards (permissions, validation truthfulness, code rules, generation policy, native trigger), a task routing table, and a scoped-instruction table. `CLAUDE.md` and `.github/copilot-instructions.md` are unchanged. | | Scoped entrypoints | v0 and v1 package `AGENTS.md` files are short maintainer routes to the shared recipes. Agentic package and primitive files define `R-AGENTIC-*` and `R-PRIMITIVE-*` rules. The Storybook diagnostics table moved to `apps/storybook/AGENTS.md#native-diagnostics`. | | Integrity checker (D09) | `yarn check-agent-instructions` runs from the root and in `yarn lage buildci` and `repo-checks`. It validates frontmatter, index completeness, case-sensitive links and anchors, inbound anchors into instruction files, code-span paths, skill metadata, rule definitions and citations, compatibility redirects, and evaluation fixtures. It has 11 node tests in `@fluentui-react-native/scripts`. | -| Evaluation groundwork (D11) | Eleven sanitized JSON cases in [`references/agents/evals/cases`](references/agents/evals/README.md): seven development and four holdout. The documented local result-record shape covers commit, instruction hashes, client and model, reads, assertions, usage, and confounders. No framework was chosen. | +| Final validation | `R-VALID-003` requires one `yarn lage buildci --no-cache` before declaring a change complete, so stale Lage cache entries cannot hide failures. Root, the development reference, and the implementation skills state it. | +| Evaluation groundwork (D11) | Thirteen sanitized JSON cases in [`references/agents/evals/cases`](references/agents/evals/README.md): eight development and five holdout, including Apple implementation, review, and iOS holdout cases. The documented local result-record shape covers commit, instruction hashes, client and model, reads, assertions, usage, and confounders. No framework was chosen. | ### Observed discovery behavior @@ -661,14 +662,18 @@ Caveats: ### Deviations and follow-ups -- **Apple/Office extraction:** the Apple native authoring section and `packages/agentic/native-core/AGENTS.md` exist - only on the unmerged `user/jasonvmo/native-lib2` branch. When it merges: - - Root `AGENTS.md` will conflict. - - Move the section into a new `references/agents/native/apple-office.md` with `R-APPLE-*` IDs and a parity review. - - Keep a short root heading `Apple native authoring and Office integration` as the anchor and safeguard route, since - three links target it. - - Route native-core's `AGENTS.md` to the new reference. - - Add the reference to the index. +- **Apple/Office extraction (follow-up, resolved):** the Apple native authoring section from commit `246d33668` on + `user/jasonvmo/native-lib2` now lives in + [`references/agents/native/apple-office.md`](references/agents/native/apple-office.md) as `R-APPLE-001` to + `R-APPLE-007`, with examples from this branch. + - A sentence-level comparison preserved all 28 source sentences: 23 matched automatically, and 5 were confirmed + manually. + - The root safeguards, `implement-native-component` (workflow and native overlay checklist), `review-component` + (native row), and the native components reference all route to it. + - Routing probes confirmed that the implementation, review, and unseen iOS holdout cases each loaded the reference + and applied the expected rules. + - The owner is removing that commit from `native-lib2`, so no compatibility anchor is kept. Its native-core + `AGENTS.md`, which this branch does not have, should link to the new reference if it is retained. - **Corrected stale root claims:** `yarn build --clean` (now `build:clean`); ESLint and Prettier (now oxlint and oxfmt); `depcheck`; FluentTester paths (now `apps/tester-core`); per-platform `testPages`; `validate-changesets.mts` (now `change.mts`); and framework-base as a devDependency (it is a runtime dependency). `yarn docs` was not carried @@ -691,3 +696,4 @@ Caveats: | `yarn workspace @fluentui-react-native/components check:spec-contracts` | Passed (20 contracts) | | `yarn change:check` | Passed after committing the empty changeset | | `yarn lage buildci --grouped` | Passed: 156 tasks succeeded and 0 failed, including `check-agent-instructions` and the scripts tests | +| `yarn lage buildci --no-cache --grouped` (final, R-VALID-003) | Passed: 171 tasks succeeded, 0 skipped (no cache), 0 failed | diff --git a/references/agents/README.md b/references/agents/README.md index 5f322cab6d..440c958d13 100644 --- a/references/agents/README.md +++ b/references/agents/README.md @@ -53,6 +53,7 @@ documents a task needs; links are navigation, not instructions to load everythin | `focus.windows-win32` | [Windows and Win32 focus](focus/windows-win32.md) | Focus work on React Native Windows or Win32 | | `focus.macos` | [macOS focus](focus/macos.md) | Focus work on macOS | | `native.components-and-modules` | [Native components and modules](native/components-and-modules.md) | Any native component, module, service, or native-backed wrapper | +| `native.apple-office` | [Apple native authoring and Office integration](native/apple-office.md) | Any iOS or macOS Swift, Objective-C, header, podspec, or bridge-manager change | | `native.windows-fabric` | [Windows Fabric native components](native/windows-fabric.md) | React Native Windows Fabric views, codegen, registration, or UIA | ## Placement and extension diff --git a/references/agents/evals/cases/apple-extern-header-review.json b/references/agents/evals/cases/apple-extern-header-review.json new file mode 100644 index 0000000000..8c2ce62c99 --- /dev/null +++ b/references/agents/evals/cases/apple-extern-header-review.json @@ -0,0 +1,43 @@ +{ + "schema_version": 1, + "case_id": "apple-extern-header-review", + "split": "development", + "task_mode": "review", + "prompt": "Review a proposed change that adds #import \"FRNNativeCore-Swift.h\" to packages/agentic/native-core/macos/components/Callout/paper/FRNCalloutManager.m so a new Paper property bridge can use the Swift CalloutView type.", + "expected": { + "skills": ["review-component", "implement-native-component"], + "classification": { + "implementation_model": "none", + "public_compatibility": ["native-registration"], + "native_platforms": ["macos"] + }, + "required_reads": ["references/agents/native/apple-office.md"], + "forbidden_actions": ["edit-files"] + }, + "assertions": [ + { + "id": "flags-generated-header", + "description": "Flags importing the generated Swift header into an RCT_EXTERN_MODULE registration file.", + "rule_ids": ["R-APPLE-003"], + "source_refs": ["packages/agentic/native-core/macos/components/Callout/paper/FRNCalloutManager.m"] + }, + { + "id": "bridge-without-swift-types", + "description": "Recommends a Paper property bridge using React or AppKit base types or a Swift-implemented setter instead of generated Swift types.", + "rule_ids": ["R-APPLE-004"], + "source_refs": [] + }, + { + "id": "both-integration-modes", + "description": "Requires module and bridging-header validation, reporting any unrun Office check.", + "rule_ids": ["R-APPLE-007"], + "source_refs": [] + }, + { + "id": "review-is-read-only", + "description": "Reports findings without editing files.", + "rule_ids": ["R-REVIEW-001"], + "source_refs": [] + } + ] +} diff --git a/references/agents/evals/cases/ios-react-header-helper.json b/references/agents/evals/cases/ios-react-header-helper.json new file mode 100644 index 0000000000..9601203ad5 --- /dev/null +++ b/references/agents/evals/cases/ios-react-header-helper.json @@ -0,0 +1,37 @@ +{ + "schema_version": 1, + "case_id": "ios-react-header-helper", + "split": "holdout", + "task_mode": "plan", + "prompt": "Add an Objective-C helper to packages/experimental/AppearanceAdditions that reads the React Native RTL setting from RCTI18nUtil, and call it from Swift. Plan the change.", + "expected": { + "skills": ["implement-native-component"], + "classification": { + "implementation_model": "none", + "public_compatibility": [], + "native_platforms": ["ios"] + }, + "required_reads": ["references/agents/native/apple-office.md"], + "forbidden_actions": ["edit-files"] + }, + "assertions": [ + { + "id": "qualified-header", + "description": "Imports with exact case and quotes only local headers.", + "rule_ids": ["R-APPLE-001"], + "source_refs": ["packages/experimental/AppearanceAdditions/ios/FRNAppearanceAdditions.m"] + }, + { + "id": "guarded-swift-import", + "description": "Guards any Swift React import with #if canImport(React) for iOS.", + "rule_ids": ["R-APPLE-002"], + "source_refs": [] + }, + { + "id": "both-integration-modes", + "description": "Plans module and bridging-header validation and reports unrun Office checks.", + "rule_ids": ["R-APPLE-007"], + "source_refs": [] + } + ] +} diff --git a/references/agents/evals/cases/macos-paper-manager-command.json b/references/agents/evals/cases/macos-paper-manager-command.json index 046df2a822..6905353bd4 100644 --- a/references/agents/evals/cases/macos-paper-manager-command.json +++ b/references/agents/evals/cases/macos-paper-manager-command.json @@ -11,23 +11,27 @@ "public_compatibility": ["native-registration"], "native_platforms": ["macos"] }, - "required_reads": ["references/agents/native/components-and-modules.md", "packages/agentic/native-core/README.md"], + "required_reads": [ + "references/agents/native/components-and-modules.md", + "references/agents/native/apple-office.md", + "packages/agentic/native-core/README.md" + ], "forbidden_actions": ["edit-files"] }, "assertions": [ { "id": "swift-manager-command", "description": "Implements the Paper manager method in Swift and declares it with RCT_EXTERN_METHOD rather than importing the generated Swift header into the registration file.", - "rule_ids": [], + "rule_ids": ["R-APPLE-003"], "source_refs": [ - "packages/agentic/native-core/README.md", - "packages/agentic/native-core/macos/components/Callout/paper/CalloutManager.swift" + "packages/agentic/native-core/macos/components/Callout/paper/CalloutManager.swift", + "packages/agentic/native-core/macos/components/Callout/paper/FRNCalloutManager.m" ] }, { "id": "paper-and-fabric-evidence", "description": "Plans macOS Paper validation with the --paper Storybook flag, because macOS commands default to Fabric, and reports untested renderers explicitly.", - "rule_ids": ["R-NATIVE-003"], + "rule_ids": ["R-NATIVE-003", "R-APPLE-007"], "source_refs": ["apps/storybook/AGENTS.md"] } ] diff --git a/references/agents/native/apple-office.md b/references/agents/native/apple-office.md new file mode 100644 index 0000000000..a4a4772442 --- /dev/null +++ b/references/agents/native/apple-office.md @@ -0,0 +1,59 @@ +--- +id: native.apple-office +kind: instruction-reference +owner_paths: + - packages/agentic/native-core + - packages/experimental +--- + +# Apple native authoring and Office integration + +These rules apply to all iOS and macOS native components and modules, including experimental packages. Office may +compile Swift with an Objective-C bridging header instead of CocoaPods' `React` clang module, and may expose React Native +headers only through the `React/` namespace. Preserve both integration modes; do not require a host-specific header-map +workaround. + +- **R-APPLE-001:** Import React Native headers in Objective-C and Objective-C++ as `` with the exact + upstream filename case, for example ``. Use quoted includes for local package headers, not bare + React Native names. +- **R-APPLE-002:** Guard Swift's React import. Existing macOS targets use `#if USE_REACT_AS_MODULE`; iOS targets use + `#if canImport(React)`. When the import is disabled, the host's bridging header must expose the required React Native + declarations using qualified includes. Do not invent substitute React types or silently change the macOS build-flag + contract. +- **R-APPLE-003:** A translation unit containing `RCT_EXTERN_MODULE` must not import its generated `-Swift.h`, + directly or through another header. Bridging-header builds can emit internal Swift manager classes into that generated + header, causing duplicate interfaces against the extern macro. Implement Swift-manager commands in Swift and export + matching Objective-C selectors and argument types with `RCT_EXTERN_METHOD`. +- **R-APPLE-004:** Keep custom Paper property bridges independent of generated Swift types: use React or AppKit base types + and narrowly declared manager helpers, or remapped setters implemented in Swift. Renderer adapters and other + Objective-C or Objective-C++ consumers that do not declare an extern Swift manager may import the generated header when + needed. Keep the interoperability header basename aligned with the pod's `module_name`. +- **R-APPLE-005:** Dispatch bridge-manager view lookup and UI mutation to the main queue; `requiresMainQueueSetup` does + not change a command's method queue. For the existing non-`Sendable` manager handoff, use the narrowly scoped + `nonisolated(unsafe) let manager = self` pattern, with all access inside the main-queue closure. Do not add blanket + `@unchecked Sendable`, disable concurrency checking, or mark shared mutable state unsafe to suppress diagnostics. Verify + the target Swift version and features when introducing concurrency annotations. +- **R-APPLE-006:** Compile cleanly with strict warnings. Mark genuinely unused Objective-C method and block parameters + `__unused`; do not remove required selector arguments or suppress `-Wunused-parameter` or case-mismatch warnings + globally. +- **R-APPLE-007:** Validate changed boundaries in both the CocoaPods module configuration and the bridging-header + configuration without the `React` module. Include an emitted bridging-mode Swift header to verify its declarations, + then syntax-check extern registrations without importing that header. Check strict Swift concurrency and qualified, + case-sensitive React header lookup. Run the owning native build and relevant Paper and Fabric commands when feasible; + an upstream build alone is not Office qualification. Record unavailable Office or runtime checks explicitly rather than + claiming parity (R-NATIVE-003). + +## Examples in this repository + +| Rule | Example | +| ----------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| R-APPLE-002 | macOS: [CalloutView.swift](../../../packages/agentic/native-core/macos/components/Callout/shared/CalloutView.swift); iOS: [AvatarViewManager.swift](../../../packages/experimental/Avatar/ios/AvatarViewManager.swift) | +| R-APPLE-003 | [FRNCalloutManager.m](../../../packages/agentic/native-core/macos/components/Callout/paper/FRNCalloutManager.m) declares `RCT_EXTERN_METHOD` commands implemented in [CalloutManager.swift](../../../packages/agentic/native-core/macos/components/Callout/paper/CalloutManager.swift) | +| R-APPLE-004 | [FRNNativeCore.podspec](../../../packages/agentic/native-core/FRNNativeCore.podspec) sets `module_name`; see the native-core README for the interoperability header | +| R-APPLE-005 | The main-queue manager handoff in [CalloutManager.swift](../../../packages/agentic/native-core/macos/components/Callout/paper/CalloutManager.swift) | +| R-APPLE-006 | `__unused` block parameters in [RCTFocusZone.m](../../../packages/agentic/native-core/macos/components/FocusZone/shared/RCTFocusZone.m) | + +PR #4364 is the compatibility precedent for these rules. Native-core's +[macOS layout notes](../../../packages/agentic/native-core/README.md) describe its interoperability header and Paper +manager placement; general native ownership and qualification rules are in +[native components and modules](components-and-modules.md). diff --git a/references/agents/native/components-and-modules.md b/references/agents/native/components-and-modules.md index 00a9fd70cc..7430b83dd6 100644 --- a/references/agents/native/components-and-modules.md +++ b/references/agents/native/components-and-modules.md @@ -45,8 +45,8 @@ Run `pod install` from the owning app or its `ios`/`macos` directory after depen ## Platform guidance - React Native Windows Fabric views: [Windows Fabric native components](windows-fabric.md). -- Apple native code: follow the macOS layout and Swift/Objective-C interoperability notes in the native-core README, - including keeping the module-named interoperability header aligned with the pod's `module_name`. +- Apple native code: follow [Apple native authoring and Office integration](apple-office.md) (R-APPLE-001 through + R-APPLE-007) for every iOS or macOS change, plus the macOS layout notes in the native-core README. - Focus targets and native focus visuals: [common focus authoring](../focus/common.md) and its platform files. - Accessibility actions: the [framework-base accessibility instructions](../../../packages/framework-base/src/accessibility/AGENTS.md) define the transport contract and its native qualification requirements. diff --git a/references/agents/repository/development.md b/references/agents/repository/development.md index 1b2f3d375d..f4065ea842 100644 --- a/references/agents/repository/development.md +++ b/references/agents/repository/development.md @@ -21,6 +21,10 @@ the declared scripts in the root [package.json](../../../package.json) and [lage - **R-VALID-002:** Report each validation as passed, failed, skipped, not run, or unsupported, with the reason. Never report an unrun, all-skipped, or unsupported check as passed. JavaScript tests, bundles, and source builds are not evidence of native runtime behavior, physical input, UIA/AX action dispatch, or Office qualification. +- **R-VALID-003:** Before declaring a code, configuration, or instruction change complete, run + `yarn lage buildci --no-cache` once as final validation. It bypasses and resets the Lage cache, so incorrect cache + invalidation cannot hide a failure. After that run, a plain `yarn lage buildci` re-checks any package that has not + passed. Read-only reviews and plans do not need it. - **R-REPO-001:** Do not use barrel exports (`export * from '...'`). Use explicit named re-exports such as `export { Foo, Bar } from './module'` and `export type { Baz } from './module'` so bundlers can tree-shake. - **R-REPO-004:** Do not copy or mechanically transform private source bodies into this public repository. Record @@ -49,9 +53,9 @@ Set up a clone with `yarn` to install dependencies, then `yarn build`. | `yarn lage buildci` | CI aggregate: repository lint, publishing check, agent-instruction check, build, test, and lint | | `yarn change` / `change:check` | Create or validate changesets; see [release](release.md) | -Lage caches task outputs; add `--no-cache` to bypass the cache and `--verbose` for detail. After a major rework, such -as moving packages or renaming exports, run `yarn lage test --no-cache` once so every test re-runs; later incremental -runs then work normally. Before committing moved or renamed files, run `yarn lage test-links` and update stale links. +Lage caches task outputs; add `--no-cache` to bypass the cache and `--verbose` for detail. Iterate with cached runs, +then finish with one `yarn lage buildci --no-cache` (R-VALID-003). Before committing moved or renamed files, run +`yarn lage test-links` and update stale links. ## Package commands @@ -72,7 +76,7 @@ not isolate one file; use `--testNamePattern` to focus a run. - During iteration, run the smallest relevant test, and run package lint or build early after type-heavy edits. - For final validation, run format, lint, the package build, and package tests, then the root build when public types, - manifests, or project references change. + manifests, or project references change. Finish with one `yarn lage buildci --no-cache` (R-VALID-003). - After fixing a failed step, rerun that step and its downstream checks before rerunning the whole chain. - Prefer committed `*.types.test.ts` coverage for compile-time contracts. Keep exploratory probes outside package source, or remove them before package validation. From 958f787a718fdbacbb873a875b869edf0e3941ee Mon Sep 17 00:00:00 2001 From: Jason Morse Date: Wed, 14 Oct 2026 23:01:24 -0700 Subject: [PATCH 05/14] Check agent routing cycles, redirects, and skill routing Extend `yarn check-agent-instructions` to build the explicit prerequisite graph and reject cycles or chains deeper than three reads, keep compatibility references terminal, require routing tables to send each task to exactly one skill consistently across routers, and require every skill to be routed from the root AGENTS.md. Document the conventions in references/agents/README.md. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- references/agents/README.md | 15 +- scripts/src/tasks/agentInstructions.test.ts | 82 ++++++++- scripts/src/tasks/agentInstructions.ts | 182 ++++++++++++++++++++ 3 files changed, 269 insertions(+), 10 deletions(-) diff --git a/references/agents/README.md b/references/agents/README.md index 440c958d13..7cdf1dad29 100644 --- a/references/agents/README.md +++ b/references/agents/README.md @@ -100,8 +100,19 @@ Do not add other keys. Load conditions belong in this index and in routers, not an ID; retire it by removing its definition and every citation. - These IDs cannot collide with component requirement IDs such as `BTN-006`, which belong to component contracts. +## Routing conventions + +- State a prerequisite explicitly: "Read [common focus authoring](focus/common.md) first", a sentence such as "Focus + work starts with" followed by the linked document, or a `Read first` section. Prerequisites must not form cycles, and + a chain may be at most three reads deep. +- Compatibility references are terminal. Link to the replacement document, never to a compatibility path. +- Routing tables, meaning tables with a `Skill` or `Workflow` column, route each task to exactly one skill. A task + routed in more than one table must route to the same skill everywhere. Overlays belong in the workflow, not the + routing table. +- Route every project skill from the root `AGENTS.md` routing table. + ## Validation `yarn check-agent-instructions` validates frontmatter, links and fragments in instruction files, inbound fragments to -instruction files, skill metadata, rule definitions and citations, this index, and the evaluation fixtures. It also -runs in `yarn lage buildci`. +instruction files, skill metadata, rule definitions and citations, this index, the routing conventions above, and the +evaluation fixtures. It also runs in `yarn lage buildci`. diff --git a/scripts/src/tasks/agentInstructions.test.ts b/scripts/src/tasks/agentInstructions.test.ts index 7092a062e2..4cdb653097 100644 --- a/scripts/src/tasks/agentInstructions.test.ts +++ b/scripts/src/tasks/agentInstructions.test.ts @@ -4,7 +4,7 @@ import { tmpdir } from 'node:os'; import path from 'node:path'; import { afterEach, describe, it } from 'node:test'; -import { checkAgentInstructions, collectAnchors, parseFrontmatter, slugify } from './agentInstructions.ts'; +import { checkAgentInstructions, collectAnchors, extractPrerequisites, parseFrontmatter, slugify } from './agentInstructions.ts'; const roots: string[] = []; @@ -12,11 +12,14 @@ function reference(id: string, kind: string, body: string, owner = 'packages/dem return `---\nid: ${id}\nkind: ${kind}\nowner_paths:\n - ${owner}\n---\n\n${body}`; } +const ROOT = + '# Root\n\nRead the [index](references/agents/README.md) and follow R-DEMO-001.\n\n| Task | Skill |\n| --- | --- |\n| Demo work | [demo-skill](.github/skills/demo-skill/SKILL.md) |\n'; + const VALID: Record = { 'packages/demo/index.ts': 'export {};\n', 'packages/demo/SPEC.md': '# Demo\n\nSee [policy](AGENTS.md#focus-policy).\n', 'packages/demo/AGENTS.md': '# Demo\n\n## Focus policy\n\n- **R-DEMO-001:** Keep focus visible.\n', - 'AGENTS.md': '# Root\n\nRead the [index](references/agents/README.md) and follow R-DEMO-001.\n', + 'AGENTS.md': ROOT, 'references/agents/README.md': reference( 'index', 'index', @@ -97,6 +100,15 @@ describe('agent instruction helpers', () => { assert.match(String(parseFrontmatter('---\nid: a\nkind: index\nowner_paths:\n - x\ntitle: extra\n---\n')), /no other keys/); assert.match(String(parseFrontmatter('---\nkind: index\nid: a\nowner_paths:\n - x\n---\n')), /in that order/); }); + + it('detects explicit prerequisites', () => { + const text = + '# A\n\nRead [common](common.md) first. Then see [other](other.md).\n\n- Work starts with [x](x.md), then [y](y.md).\n\n## Read first\n\n1. [Package](pkg.md)\n'; + assert.deepEqual( + extractPrerequisites(text).map((link) => link.target), + ['common.md', 'x.md', 'pkg.md'], + ); + }); }); describe('checkAgentInstructions', () => { @@ -120,10 +132,10 @@ describe('checkAgentInstructions', () => { }); it('rejects broken links, case mismatches, missing anchors, and missing code paths', () => { - assertIssue({ 'AGENTS.md': '# Root\n\n[Missing](missing.md)\n' }, /broken link: missing\.md/); - assertIssue({ 'AGENTS.md': '# Root\n\n[Case](references/agents/README.MD)\n' }, /broken link/); - assertIssue({ 'AGENTS.md': '# Root\n\n[Anchor](packages/demo/AGENTS.md#absent)\n' }, /missing anchor/); - assertIssue({ 'AGENTS.md': '# Root\n\nRun `packages/demo/missing.ts`.\n' }, /code span does not exist/); + assertIssue({ 'AGENTS.md': `${ROOT}\n[Missing](missing.md)\n` }, /broken link: missing\.md/); + assertIssue({ 'AGENTS.md': `${ROOT}\n[Case](references/agents/README.MD)\n` }, /broken link/); + assertIssue({ 'AGENTS.md': `${ROOT}\n[Anchor](packages/demo/AGENTS.md#absent)\n` }, /missing anchor/); + assertIssue({ 'AGENTS.md': `${ROOT}\nRun \`packages/demo/missing.ts\`.\n` }, /code span does not exist/); }); it('protects inbound anchors into instruction files', () => { @@ -134,8 +146,8 @@ describe('checkAgentInstructions', () => { }); it('requires rule IDs to be defined exactly once', () => { - assertIssue({ 'AGENTS.md': '# Root\n\nFollow R-DEMO-002.\n' }, /undefined rule ID R-DEMO-002/); - assertIssue({ 'AGENTS.md': '# Root\n\n- **R-DEMO-001:** Duplicate.\n' }, /already defined/); + assertIssue({ 'AGENTS.md': `${ROOT}\nFollow R-DEMO-002.\n` }, /undefined rule ID R-DEMO-002/); + assertIssue({ 'AGENTS.md': `${ROOT}\n- **R-DEMO-001:** Duplicate.\n` }, /already defined/); }); it('rejects compatibility redirects without a live target', () => { @@ -156,6 +168,60 @@ describe('checkAgentInstructions', () => { assertIssue({ '.github/skills/demo-skill/SKILL.md': `---\nname: demo-skill\ndescription: ${'word '.repeat(61)}\n---\n` }, /61 words/); }); + it('rejects prerequisite cycles and deep chains', () => { + const doc = (id: string, body: string) => reference(id, 'instruction-reference', body); + const index = reference( + 'index', + 'index', + '# Index\n\n| ID | Document |\n| --- | --- |\n| `demo.guide` | [Guide](demo/guide.md) |\n| `demo.a` | [A](demo/a.md) |\n| `demo.b` | [B](demo/b.md) |\n| `demo.c` | [C](demo/c.md) |\n| `demo.d` | [D](demo/d.md) |\n', + 'references/agents', + ); + assertIssue( + { + 'references/agents/README.md': index, + 'references/agents/demo/a.md': doc('demo.a', '# A\n\nRead [B](b.md) first.\n'), + 'references/agents/demo/b.md': doc('demo.b', '# B\n\nRead [A](a.md) first.\n'), + 'references/agents/demo/c.md': doc('demo.c', '# C\n'), + 'references/agents/demo/d.md': doc('demo.d', '# D\n'), + }, + /prerequisite cycle: .*a\.md -> .*b\.md -> .*a\.md|prerequisite cycle: .*b\.md -> .*a\.md -> .*b\.md/, + ); + assertIssue( + { + 'references/agents/README.md': index, + 'references/agents/demo/a.md': doc('demo.a', '# A\n\nRead [B](b.md) first.\n'), + 'references/agents/demo/b.md': doc('demo.b', '# B\n\nRead [C](c.md) first.\n'), + 'references/agents/demo/c.md': doc('demo.c', '# C\n\nRead [D](d.md) first.\n'), + 'references/agents/demo/d.md': doc('demo.d', '# D\n\nRead [guide](guide.md) first.\n'), + }, + /prerequisite chain is 4 reads deep/, + ); + }); + + it('keeps compatibility references terminal', () => { + assertIssue( + { 'packages/demo/README.md': '# Demo\n\n[Old](../../.github/skills/demo-skill/references/old.md)\n' }, + /links to compatibility reference/, + ); + }); + + it('requires single, consistent, complete skill routing', () => { + assertIssue( + { 'AGENTS.md': '# Root\n\n| Task | Skill |\n| --- | --- |\n| Demo work | none |\n' }, + /must link exactly one skill; found 0/, + ); + assertIssue({ 'AGENTS.md': '# Root\n' }, /not routed from the root AGENTS\.md routing table/); + assertIssue( + { + '.github/skills/other-skill/SKILL.md': '---\nname: other-skill\ndescription: Another skill.\n---\n', + 'AGENTS.md': `${ROOT}| Other work | [other-skill](.github/skills/other-skill/SKILL.md) |\n`, + 'packages/demo/AGENTS.md': + '# Demo\n\n## Focus policy\n\n- **R-DEMO-001:** Keep focus visible.\n\n| Task | Workflow |\n| --- | --- |\n| Demo work | [other](../../.github/skills/other-skill/SKILL.md) |\n', + }, + /task "demo work" routes to/, + ); + }); + it('validates evaluation cases', () => { const base = JSON.parse(VALID['references/agents/evals/cases/demo-case.json']); const write = (value: object) => ({ 'references/agents/evals/cases/demo-case.json': JSON.stringify(value) }); diff --git a/scripts/src/tasks/agentInstructions.ts b/scripts/src/tasks/agentInstructions.ts index e6182667b8..0c0fdfb645 100644 --- a/scripts/src/tasks/agentInstructions.ts +++ b/scripts/src/tasks/agentInstructions.ts @@ -25,6 +25,10 @@ const ID_PATTERN = /^[a-z0-9][a-z0-9-]*(\.[a-z0-9][a-z0-9-]*)*$/; const RULE_DEFINITION = /^\s*[-*]\s+\*\*([RC]-[A-Z0-9]+-\d{3}):\*\*/; const RULE_CITATION = /\b[RC]-[A-Z0-9]+-\d{3}\b/g; const MAX_SKILL_DESCRIPTION_WORDS = 60; +const MAX_PREREQUISITE_DEPTH = 3; +const PREREQUISITE_SENTENCE = /\b(?:read|load)\b[^.]*\bfirst\b|\bstarts? with\b/i; +const ROUTING_COLUMNS = new Set(['skill', 'workflow']); +const SKILL_LINK = /(?:^|\/)\.github\/skills\/([^/]+)\/SKILL\.md$/; const SKIP_DIRS = new Set(['node_modules', 'lib', 'lib-commonjs', 'dist', '.git', 'Pods', 'build', 'DerivedData']); const REPO_PATH_PREFIXES = ['.github/', 'apps/', 'packages/', 'references/', 'scripts/', 'docs/']; @@ -189,6 +193,96 @@ export function extractCodePaths(markdown: string): Link[] { return paths; } +/** + * Extracts explicit prerequisites: the first link in a sentence such as "Read [X](x.md) first" or + * "Focus work starts with [X](x.md)", and every link in a "Read first" section. + */ +export function extractPrerequisites(markdown: string): Link[] { + const prerequisites: Link[] = []; + let fenced = false; + let readFirstSection = false; + let paragraph: { text: string; line: number } | undefined; + const flush = () => { + if (!paragraph) { + return; + } + for (const sentence of paragraph.text.split(/(?<=[.!?])\s+(?=[A-Z[`*])/)) { + if (PREREQUISITE_SENTENCE.test(sentence.replace(/\[[^\]]*\]\([^)]*\)/g, 'LINK'))) { + const [first] = extractLinks(sentence); + if (first) { + prerequisites.push({ target: first.target, line: paragraph.line }); + } + } + } + paragraph = undefined; + }; + markdown.split('\n').forEach((line, index) => { + if (/^\s*(```|~~~)/.test(line)) { + flush(); + fenced = !fenced; + return; + } + if (fenced) { + return; + } + const heading = /^#{1,6}\s+(.*)$/.exec(line); + if (heading) { + flush(); + readFirstSection = /^read first$/i.test(heading[1].trim()); + return; + } + if (readFirstSection) { + for (const link of extractLinks(line)) { + prerequisites.push({ target: link.target, line: index + 1 }); + } + } + if (line.trim() === '' || line.trimStart().startsWith('|')) { + flush(); + return; + } + if (/^\s*(?:[-*]|\d+\.)\s+/.test(line)) { + flush(); + } + paragraph = paragraph ? { text: `${paragraph.text} ${line.trim()}`, line: paragraph.line } : { text: line.trim(), line: index + 1 }; + }); + flush(); + return prerequisites; +} + +type RoutingRow = { line: number; task: string; skills: string[] }; + +/** Extracts rows of Markdown tables whose header has a Skill or Workflow column. */ +export function extractRoutingRows(markdown: string, fromFile: string): RoutingRow[] { + const rows: RoutingRow[] = []; + const lines = markdown.split('\n'); + const cells = (line: string) => + line + .trim() + .replace(/^\|/, '') + .replace(/\|$/, '') + .split('|') + .map((cell) => cell.trim()); + for (let index = 0; index < lines.length; index++) { + if (!lines[index].trimStart().startsWith('|') || !/^\s*\|[\s:|-]+\|\s*$/.test(lines[index + 1] ?? '')) { + continue; + } + const header = cells(lines[index]).map((cell) => cell.toLowerCase()); + const column = header.findIndex((cell) => ROUTING_COLUMNS.has(cell)); + if (column <= 0) { + continue; + } + for (let row = index + 2; row < lines.length && lines[row].trimStart().startsWith('|'); row++) { + const values = cells(lines[row]); + const skills = extractLinks(values[column] ?? '') + .map((link) => resolveLink(fromFile, link.target)?.file ?? '') + .map((file) => SKILL_LINK.exec(file)?.[1]) + .filter((skill): skill is string => Boolean(skill)); + rows.push({ line: row + 1, task: values[0].toLowerCase().replace(/\s+/g, ' '), skills }); + } + } + return rows; +} + /** Parses the standardized reference frontmatter. Returns an error message for any deviation. */ export function parseFrontmatter(markdown: string): Frontmatter | string { const lines = markdown.split('\n'); @@ -409,6 +503,94 @@ export function checkAgentInstructions(root: string): CheckResult { } } + // Compatibility references are terminal: nothing links to them except the redirects themselves. + const compatibility = new Set([...frontmatter].filter(([, meta]) => meta.kind === 'compatibility-reference').map(([file]) => file)); + for (const file of files) { + if (!file.endsWith('.md') || compatibility.has(file) || path.posix.basename(file) === 'CHANGELOG.md') { + continue; + } + for (const link of extractLinks(read(file))) { + const target = resolveLink(file, link.target)?.file; + if (target && compatibility.has(target)) { + issues.push({ file, line: link.line, message: `links to compatibility reference ${target}; link to its replacement instead` }); + } + } + } + + // Explicit prerequisites must be acyclic and shallow. + const prerequisites = new Map(); + for (const file of surfaces) { + const targets: string[] = []; + for (const link of extractPrerequisites(read(file))) { + const target = resolveLink(file, link.target)?.file; + if (target && target.endsWith('.md') && existsExact(root, target)) { + if (target === file) { + issues.push({ file, line: link.line, message: 'document lists itself as a prerequisite' }); + } else { + targets.push(target); + } + } + } + prerequisites.set(file, [...new Set(targets)]); + } + const depthMemo = new Map(); + const reportedCycles = new Set(); + const depth = (file: string, stack: string[]): number => { + const loop = stack.indexOf(file); + if (loop >= 0) { + const cycle = [...stack.slice(loop), file]; + const key = cycle.slice(0, -1).sort().join('|'); + if (!reportedCycles.has(key)) { + reportedCycles.add(key); + issues.push({ file, message: `prerequisite cycle: ${cycle.join(' -> ')}` }); + } + return 0; + } + const cached = depthMemo.get(file); + if (cached !== undefined) { + return cached; + } + const next = prerequisites.get(file) ?? []; + const result = next.length === 0 ? 0 : 1 + Math.max(...next.map((target) => depth(target, [...stack, file]))); + depthMemo.set(file, result); + return result; + }; + for (const file of surfaces) { + const chain = depth(file, []); + if (chain > MAX_PREREQUISITE_DEPTH) { + issues.push({ file, message: `prerequisite chain is ${chain} reads deep; the limit is ${MAX_PREREQUISITE_DEPTH}` }); + } + } + + // Routing tables send each task to exactly one skill, agree across routers, and cover every skill from the root. + const routedTasks = new Map(); + for (const file of surfaces) { + for (const row of extractRoutingRows(read(file), file)) { + if (row.skills.length !== 1) { + issues.push({ file, line: row.line, message: `routing row "${row.task}" must link exactly one skill; found ${row.skills.length}` }); + continue; + } + const previous = routedTasks.get(row.task); + if (previous && previous.skills !== row.skills[0]) { + issues.push({ + file, + line: row.line, + message: `task "${row.task}" routes to ${row.skills[0]} here but to ${previous.skills} in ${previous.file}`, + }); + } else if (!previous) { + routedTasks.set(row.task, { skills: row.skills[0], file }); + } + } + } + if (files.includes('AGENTS.md')) { + const rootRouted = new Set(extractRoutingRows(read('AGENTS.md'), 'AGENTS.md').flatMap((row) => row.skills)); + for (const [skill, file] of skillNames) { + if (!rootRouted.has(skill)) { + issues.push({ file, message: `skill "${skill}" is not routed from the root AGENTS.md routing table` }); + } + } + } + // Rule definitions and citations. const rules = new Map(); for (const file of surfaces) { From 5e183ce7fcea936d112543cc2799b0f8581c614d Mon Sep 17 00:00:00 2001 From: Jason Morse Date: Wed, 14 Oct 2026 23:16:01 -0700 Subject: [PATCH 06/14] Route agentic implementation plans through the authoring skill GPT-6.1 Sol development probes followed the root routing row for "plan-only" agentic work into contract authoring and skipped the authoring workflow and its types-and-slots recipe. Route plans to change an agentic component through the authoring skill, which applies the contract gate, and reserve the contract skill for contract-only or contract-feasibility work. Link the Flex token map from the contract workflow and Flex adapter. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../agentic-component-authoring/SKILL.md | 5 ++++- .../SKILL.md | 13 ++++++----- AGENTS.md | 20 ++++++++--------- .../agentic/contract-sources/flex-x3.md | 3 ++- .../agents/components/classification.md | 22 +++++++++---------- 5 files changed, 35 insertions(+), 28 deletions(-) diff --git a/.github/skills/agentic-component-authoring/SKILL.md b/.github/skills/agentic-component-authoring/SKILL.md index a2cd850e9a..174b0ba1b0 100644 --- a/.github/skills/agentic-component-authoring/SKILL.md +++ b/.github/skills/agentic-component-authoring/SKILL.md @@ -1,6 +1,6 @@ --- name: agentic-component-authoring -description: Implement or change components and primitives in packages/agentic/components, including every new component. Covers public APIs, slots, state, token styling, rendering, tests, and stories. Not for contract-only work, read-only reviews, v0/v1 maintenance, or the Storybook app toolchain. +description: Implement, change, or plan changes to components and primitives in packages/agentic/components, including every new component. Covers public APIs, slots, state, token styling, rendering, tests, and stories. Not for contract-only work, read-only reviews, v0/v1 maintenance, or the Storybook app toolchain. license: MIT --- @@ -45,6 +45,9 @@ the affected reference and its immediate neighbors. ## Workflow +For a plan-only request, follow steps 1 to 4 without editing, then return the planned contract revision, the +references used, the implementation order, and the validation plan. + 1. Read the package instructions and the component's contract: `SPEC.md`, `spec/source.json`, and every React Native companion it references for a higher-order component, or `CONTRACT.md` for a primitive. 2. Higher-order components: use an existing reviewed contract directly when its source evidence and requested behavior diff --git a/.github/skills/agentic-component-contract-authoring/SKILL.md b/.github/skills/agentic-component-contract-authoring/SKILL.md index 6ff21cd9ed..eb9c0a22b7 100644 --- a/.github/skills/agentic-component-contract-authoring/SKILL.md +++ b/.github/skills/agentic-component-contract-authoring/SKILL.md @@ -1,6 +1,6 @@ --- name: agentic-component-contract-authoring -description: Create or revise reviewed React Native component contracts in packages/agentic/components from Flex/X3, Fluent v9, HTML/CSS, images, local platform foundations, or composite evidence. Use for contract-only, feasibility, or plan-only work and before implementing an unreviewed contract. +description: Create or revise reviewed React Native component contracts in packages/agentic/components from Flex/X3, Fluent v9, HTML/CSS, images, local platform foundations, or composite evidence. Use for contract-only or contract-feasibility work, and as the contract gate before implementing or planning a component change. license: MIT --- @@ -11,14 +11,15 @@ contract. This skill owns source interpretation, authority, provenance, uncertainty, React Native adaptation, divergences, and requirement planning. It does not own component implementation. -Use this skill directly for contract-only, feasibility, or implementation-plan -requests. The [component authoring skill](../agentic-component-authoring/SKILL.md) +Use this skill directly for contract-only or contract-feasibility requests. A +plan to change a component's implementation or public API starts in the +[component authoring skill](../agentic-component-authoring/SKILL.md), which invokes this workflow before code when a contract is missing, stale, still a draft, or accompanied by new source evidence. ## Choose the request mode -- **Feasibility or plan only:** inspect the evidence, identify blockers and +- **Feasibility or contract plan:** inspect the evidence, identify blockers and unknowns, and describe the contract work without changing implementation. Commit contract files only when the user requested a contract. - **Contract only:** create or revise the local contract and stop after its @@ -58,7 +59,9 @@ Load every adapter represented by the supplied evidence, but no others. 5. Draft the original React Native contract: - public API, slots, defaults, state ownership, and requirement IDs in `SPEC.md`; - - FURN token bindings and gaps in `spec/tokens.yaml`; + - FURN token bindings and gaps in `spec/tokens.yaml`, mapped through the + [Flex token map](../../../packages/agentic/design/src/tokens/mappings/flex-token-map.yaml). + Name each proposed binding or list it as unresolved; - native accessibility in `spec/accessibility.md`; - keyboard, pointer, focus, and motion behavior in `spec/interaction.md`; diff --git a/AGENTS.md b/AGENTS.md index bba65ace7f..a519c2dbd8 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -43,16 +43,16 @@ These apply to every task. Classify the change with the [classification guide](references/agents/components/classification.md), then use: -| Task | Skill | -| ------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------- | -| New component, or change an agentic component or primitive | [agentic-component-authoring](.github/skills/agentic-component-authoring/SKILL.md) | -| Agentic contract-only, feasibility, or plan-only work | [agentic-component-contract-authoring](.github/skills/agentic-component-contract-authoring/SKILL.md) | -| Fix or maintain an existing v0 or v1 component | [maintain-v0-v1-component](.github/skills/maintain-v0-v1-component/SKILL.md) | -| Review a component change of any generation | [review-component](.github/skills/review-component/SKILL.md) | -| Native component, TurboModule, service, or codegen | [implement-native-component](.github/skills/implement-native-component/SKILL.md) | -| Cross-component agentic extraction or dependency direction | [agentic-component-optimization](.github/skills/agentic-component-optimization/SKILL.md) | -| Storybook app, Metro, CocoaPods, or desktop native toolchain | [agentic-storybook-development](.github/skills/agentic-storybook-development/SKILL.md) | -| Dependency vulnerability remediation | [fix-security-issues](.github/skills/fix-security-issues/SKILL.md) | +| Task | Skill | +| ------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------- | +| New component, or change or plan a change to an agentic component or primitive | [agentic-component-authoring](.github/skills/agentic-component-authoring/SKILL.md) | +| Agentic contract-only or contract-feasibility work | [agentic-component-contract-authoring](.github/skills/agentic-component-contract-authoring/SKILL.md) | +| Fix or maintain an existing v0 or v1 component | [maintain-v0-v1-component](.github/skills/maintain-v0-v1-component/SKILL.md) | +| Review a component change of any generation | [review-component](.github/skills/review-component/SKILL.md) | +| Native component, TurboModule, service, or codegen | [implement-native-component](.github/skills/implement-native-component/SKILL.md) | +| Cross-component agentic extraction or dependency direction | [agentic-component-optimization](.github/skills/agentic-component-optimization/SKILL.md) | +| Storybook app, Metro, CocoaPods, or desktop native toolchain | [agentic-storybook-development](.github/skills/agentic-storybook-development/SKILL.md) | +| Dependency vulnerability remediation | [fix-security-issues](.github/skills/fix-security-issues/SKILL.md) | Other repository work starts from the [reference index](references/agents/README.md): architecture, commands and validation, TypeScript, release, design tokens, focus, and native guidance. diff --git a/references/agents/components/agentic/contract-sources/flex-x3.md b/references/agents/components/agentic/contract-sources/flex-x3.md index 25c32e66f5..81ea257b3c 100644 --- a/references/agents/components/agentic/contract-sources/flex-x3.md +++ b/references/agents/components/agentic/contract-sources/flex-x3.md @@ -34,7 +34,8 @@ reviewer's assertion and remains empty after a source identity changes. ## Adapt to React Native Classify source guidance as adopted, adapted, intentionally divergent, not -applicable, or deferred. Consult the Flex token map and the closest Win32, +applicable, or deferred. Consult the +[Flex token map](../../../../../packages/agentic/design/src/tokens/mappings/flex-token-map.yaml) and the closest Win32, macOS, or Windows FURN implementation for compatibility; do not use iOS as the cross-platform canonical reference unless the work targets iOS. diff --git a/references/agents/components/classification.md b/references/agents/components/classification.md index aa8e7b0ff5..45b54d887a 100644 --- a/references/agents/components/classification.md +++ b/references/agents/components/classification.md @@ -22,17 +22,17 @@ more than one generation; Button ships both v0 and v1 implementations. ## 1. Task mode -| Task | Workflow | -| ---------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------- | -| Create a new component | [agentic-component-authoring](../../../.github/skills/agentic-component-authoring/SKILL.md) | -| Change an existing agentic component or primitive | [agentic-component-authoring](../../../.github/skills/agentic-component-authoring/SKILL.md) | -| Contract-only, feasibility, or plan-only agentic contract work | [agentic-component-contract-authoring](../../../.github/skills/agentic-component-contract-authoring/SKILL.md) | -| Fix or maintain an existing v0 or v1 component | [maintain-v0-v1-component](../../../.github/skills/maintain-v0-v1-component/SKILL.md) | -| Review a component change of any generation | [review-component](../../../.github/skills/review-component/SKILL.md) | -| Native component, TurboModule, native service, or codegen change | [implement-native-component](../../../.github/skills/implement-native-component/SKILL.md) | -| Cross-component agentic extraction or dependency direction | [agentic-component-optimization](../../../.github/skills/agentic-component-optimization/SKILL.md) | -| Storybook app, Metro, CocoaPods, or desktop native toolchain | [agentic-storybook-development](../../../.github/skills/agentic-storybook-development/SKILL.md) | -| Dependency vulnerability remediation | [fix-security-issues](../../../.github/skills/fix-security-issues/SKILL.md) | +| Task | Workflow | +| ----------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------- | +| Create a new component | [agentic-component-authoring](../../../.github/skills/agentic-component-authoring/SKILL.md) | +| Change, or plan a change to, an existing agentic component or primitive | [agentic-component-authoring](../../../.github/skills/agentic-component-authoring/SKILL.md) | +| Agentic contract-only or contract-feasibility work | [agentic-component-contract-authoring](../../../.github/skills/agentic-component-contract-authoring/SKILL.md) | +| Fix or maintain an existing v0 or v1 component | [maintain-v0-v1-component](../../../.github/skills/maintain-v0-v1-component/SKILL.md) | +| Review a component change of any generation | [review-component](../../../.github/skills/review-component/SKILL.md) | +| Native component, TurboModule, native service, or codegen change | [implement-native-component](../../../.github/skills/implement-native-component/SKILL.md) | +| Cross-component agentic extraction or dependency direction | [agentic-component-optimization](../../../.github/skills/agentic-component-optimization/SKILL.md) | +| Storybook app, Metro, CocoaPods, or desktop native toolchain | [agentic-storybook-development](../../../.github/skills/agentic-storybook-development/SKILL.md) | +| Dependency vulnerability remediation | [fix-security-issues](../../../.github/skills/fix-security-issues/SKILL.md) | v0 and v1 are maintenance-only (R-V0-001, R-V1-001). A request for a new component uses the agentic workflow even when a v0 or v1 component with a similar name exists. From 41768dacc889222444c538ce6031fe668f06043f Mon Sep 17 00:00:00 2001 From: Jason Morse Date: Wed, 14 Oct 2026 23:23:17 -0700 Subject: [PATCH 07/14] Reduce agent improvements plan to remaining decisions Remove completed work from agent-improvements-plan.md. Record the independently scored GPT-6.1 Sol pilot alongside the earlier Claude Sonnet results, and list the open decisions: version-bound evidence, skill size, redirect retirement, stale contributor docs, holdout-derived refinements, evidence sufficiency, and validation after an aborted uncached run. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- agent-improvements-plan.md | 806 ++++++------------------------------- 1 file changed, 133 insertions(+), 673 deletions(-) diff --git a/agent-improvements-plan.md b/agent-improvements-plan.md index 1f60a1cde9..a592b13fcf 100644 --- a/agent-improvements-plan.md +++ b/agent-improvements-plan.md @@ -1,699 +1,159 @@ -# Agent instruction and skill improvements plan - -**Status:** Implemented on 2026-10-14 through P4 using the answered decisions in section 11; P5 (the -efficacy skill) is deferred under D12. See [section 13](#13-implementation-record) for what changed, parity -evidence, pilot results, deviations, and follow-ups. - -**Basis:** Repository inspection at `246d336682c628e6ff65d11c6f5997d9135be5b6` -on 2026-10-14, independent plans from Claude Opus 5.5 and GPT 6 Astra, and -reciprocal cross-review in the original reviewer threads. - -## Recommended outcome - -Create **`references/agents/`** as the shared, tool-neutral home for reusable -agent guidance. Keep root and scoped `AGENTS.md` files as discovery entrypoints -and concise routers; keep discoverable workflows in `.github/skills/`. Leave -product contracts, protocols, token mappings, package documentation, and -historical plans beside their owning code. - -Recommend **separate generation-specific recipes, not eight immediately -separate skills**: one classification-first v0/v1 workflow, the existing -agentic authoring workflow, one read-only component-review workflow, and a -native-authoring entrypoint that also acts as an overlay. Native is a platform -and integration dimension, not an exclusive fourth component generation. -Retain the existing skill names initially; explicit `implement-*` names remain -an owner choice. - -Begin with a small routing and non-safety extraction pilot. Defer Apple/Office, -permissions, and security-policy extraction until a separately approved parity -review. Establish portable rule IDs and evidence records without choosing or -building an evaluation framework. - -## 1. Goals, boundaries, and success criteria - -| Goal | Success criterion | -| --------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- | -| Centralized shared guidance | Reused rules have one authoritative home and explicit owners; skills and root/scoped routers link to that home. | -| Future evaluation reuse | A case can identify the applicable instructions, rule assertions, inspected repository revision, and actual evidence without depending on an agent vendor. | -| Clear component workflows | Tasks distinguish v0, v1, agentic higher-order, agentic primitive, and native obligations without using directory names alone. | -| Progressive loading | Agents load the applicable workflow and conditional references, not the complete instruction collection. Actual loading and duplication are measured. | -| Safe continuous improvement | Evidence produces a human-reviewable proposal; no autonomous policy weakening, external publication, or raw-transcript collection. | - -This is an organizational change, not a component migration. Preserve public -APIs, framework behavior, native registration, supported hosts, contract -lifecycle, qualification requirements, permission behavior, and publishing -policy. Correct stale instructions only after reconciling them with their -owning implementation; do not use the reorganization to expand unrelated work. - -## 2. Observed starting point - -The inspected checkout contains **12 `AGENTS.md` files including the root, -two root/tool adapters, five repository skills, ten authoring references, and -six contract-source adapters**. The root is 418 lines and approximately 30 KB. -These are file measurements, not token measurements. - -| Finding | Evidence and implication | -| ------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| Root guidance conflates component models | [Root component guidance](AGENTS.md) recommends composition and new `stagedComponent` usage at line 257; [framework-base guidance](packages/framework-base/AGENTS.md), line 15, prohibits new staged usage. These need explicit domain routing, not a global winner. | -| v0/v1 guidance is outside consumer ancestry | [v0 guide](packages/deprecated/foundation-compose/AGENTS.md), lines 3-6, and [v1 guide](packages/framework/framework/AGENTS.md), lines 3-6, apply to consuming components outside their directories. Ancestor discovery alone cannot be assumed to supply them. | -| A package can contain both generations | [Deprecated Button](packages/components/Button/src/deprecated/Button.tsx), lines 17-19, uses foundation-compose; [current Button](packages/components/Button/src/Button.tsx), lines 6-7, uses framework composition. | -| Helper names do not determine generation | [v1 Text](packages/components/Text/src/Text.tsx), lines 5-17 and 133, uses `compressible` and a `directComponent` continuation. [Agentic Button](packages/agentic/components/src/components/button/button.ts), lines 9-13, is a normal React function assembling its stages. | -| Legacy API and implementation model are different axes | [Native-core Callout facade](packages/agentic/native-core/src/legacy/callout/Callout.tsx), lines 2-10, uses modern helpers while retaining legacy compatibility. | -| Native cuts across generations | [Experimental Checkbox](packages/experimental/Checkbox/src/Checkbox.macos.tsx), lines 10 and 27-39, combines v1 composition with a native root. Native modules may have no component generation at all. | -| Shared references are owned by one skill directory | [Framework-base guidance](packages/framework-base/AGENTS.md), line 54, consumes authoring focus guidance; [native-core README](packages/agentic/native-core/README.md), line 177, consumes its Windows Fabric guidance. | -| Existing loading instructions are too broad | [Authoring skill](.github/skills/agentic-component-authoring/SKILL.md), lines 37-46, includes a conditional Windows reference in an "every reference" recommendation and needs a primitive-specific `CONTRACT.md` branch instead of unconditional `SPEC.md`. | -| Commands need source reconciliation | [Root manifest](package.json), lines 18-19 and 31, defines `build` as `lage root-build`, `build:clean` separately, and `root-build` as `tsc -b`. Do not copy the root's `yarn build --clean` advice into the new reference unchecked. | -| Instruction anchors are contract dependencies | [Button specification](packages/agentic/components/src/components/button/SPEC.md), lines 70 and 99, cites `src/components/AGENTS.md#focus-visual-policy`. Reviewers found 45 such links in 34 files and three root Apple/Office-anchor links. Preserve their meaning and compatibility. | -| Contract infrastructure already has its own schema | [SPEC-SOURCE.md](packages/agentic/components/SPEC-SOURCE.md), lines 24-57 and 185-216, defines provenance and lifecycle. [Contract checker](packages/agentic/components/scripts/spec-contracts/check-spec-contracts.cjs), lines 45-75 and 504-546, enforces profile/layout constraints. Do not repurpose it as an instruction registry. | - -**Observed, session-specific:** the review context included the root guidance -both directly and through the `CLAUDE.md` import. This warrants a duplication -probe; it does not prove identical behavior in all clients. Copilot CLI help -confirms root/cwd instruction locations and `/instructions`, `/env`, `/skills`, -`/context`, and `/usage`. Linked-file transclusion, nested inheritance, and -other clients' discovery semantics remain unqualified. - -**Additional migration candidates:** Claude identified stale tester paths, -E2E naming, `depcheck`/format advice, changeset-script references, and the docs -command. Record these in the migration ledger and verify against current -manifests/source before changing them. They were inspected, not all executed. - -## 3. Target architecture and authority - -```text -AGENTS.md # Repository safeguards and task router -CLAUDE.md # Existing adapter; qualify before changing -.github/copilot-instructions.md # Existing adapter -.github/skills/ - agentic-component-authoring/ - agentic-component-contract-authoring/ - agentic-component-optimization/ - agentic-storybook-development/ - fix-security-issues/ - maintain-v0-v1-component/ # Proposed; separate conditional recipes - review-component/ # Proposed; read-only, all generations - implement-native-component/ # Proposed; also modules/services overlay - assess-agent-efficacy/ # Optional; proposal-only -references/agents/ - README.md # Short index, ownership, document roles - repository-development.md # Verified commands and validation routing - components/ - classify.md - v0.md - v1.md - agentic/ # Only recipes proven shared by workflows - focus/ - common.md - windows-win32.md - macos.md - native/ - windows-fabric.md - apple-office.md # Later, separately approved extraction - qualification.md # Only if genuinely shared detail warrants it -packages/**/AGENTS.md # Retained scoped entrypoints -apps/storybook/AGENTS.md # Retained app entrypoint -``` - -This is a destination map, not a requirement to create every listed file. -Keep the primary routing table in root `AGENTS.md`; do not add another mandatory -`routing.md` hop. Detailed classification is loaded when the task requires it. -Review sequencing belongs in the review skill, not a second duplicated protocol. - -### Ownership and precedence - -- Root owns repository-wide safeguards and routes. Scoped entrypoints own - local invariants and explicitly identify their applicable domain. -- Skills own task sequencing, entry conditions, required inputs, outputs, - conditional reads, and stop conditions. -- Shared references own reusable behavioral rules. Their central location - does not transfer technical stewardship away from the owning package. -- Product contracts, transport protocols, token definitions, and source locks - stay authoritative in their existing homes. A README can contain an - authoritative protocol; a filename alone does not establish its role. -- Respect actual runtime instruction priority and user authorization. Do not - invent a repository precedence ladder above either. Scope determines which - rule applies; path depth is not a universal conflict-resolution mechanism. -- Validator failures are evidence of unmet enforced contracts, not permission - to override reviewed requirements silently. Escalate unresolved same-domain - conflicts. Plans and historical observations do not become implementation - guarantees through relocation. - -Keep one full authoritative definition per rule. Concise entrypoint warnings -and links are intentional safety redundancy, not another complete copy. -The permission hook provides approvals; unmatched commands are not blanket -denials. This migration must preserve that distinction. - -## 4. Classification and proposed skill workflows - -Classify the actual changed surfaces before selecting recipes: - -1. **Task mode:** implement/maintain, contract-only, plan-only, review, - cross-component optimization, Storybook/toolchain, or dependency security. -2. **Implementation model:** v0 foundation-compose, v1 compose/compressible, - agentic higher-order, agentic primitive, another framework-base pattern, - or no component model. -3. **Public compatibility:** legacy aliases/`componentRef`, V1 exports, - modern `ref` contracts, primitive entrypoints, or native-core legacy facade. -4. **Native obligations:** platform, renderer, host ownership, schema, - registration, Apple module/bridging integration, and qualification status. - -Paths, imports, exports, and component structure provide evidence together. -Neither `legacy/`, a JSX pragma, nor a single helper import is sufficient. -For mixed diffs, classify each surface; never force the whole change into one -generation. - -| Surface | Implementation workflow | Conditional recipe and review obligations | -| ------------------------------- | ------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| v0 | `maintain-v0-v1-component`, v0 branch | Settings layering, filters, prepare/render boundary, token dependencies, overrides, aliases, and existing public refs. Preserve the current maintenance-first v0 policy. | -| v1 | Same entrypoint, v1 branch | Compose versus compressible behavior, continuations, token precedence, customization, platform variants, and existing ref/API compatibility. New-v1 policy requires D04. | -| Agentic higher-order | Existing `agentic-component-authoring` | Reviewed contract gate where applicable; stage structure, slots, state ownership, cached styles, rendering, tests, and stories. Contract-only work routes to the contract skill. | -| Agentic primitive | Explicit branch of the same authoring skill | Local `CONTRACT.md`, unstyled acceptance API, renderer branches, exports, slot type tests, and documented ref exceptions. Do not impose Flex provenance or a uniform imperative ref. | -| Native component/module/service | `implement-native-component`, directly or as an overlay | Codegen, registration, UI affinity, lifetimes, cancellation, renderer/host admission, packaging, and native evidence. Use native-core for new shared modern native work; do not force unrelated old wrappers to migrate. | -| Any component review | `review-component` | Read-only classification, applicable generation checklist, relevant native/focus overlays, high-confidence findings with path/line evidence, and truthful validation status. | - -The native skill name would have to describe modules and shared host services -as well as components. `native-authoring` is a clearer alternative if D03 -favors broader names. - -**Why not eight skills initially?** Skill selection happens before an agent -has necessarily inspected APIs; v0/v1 can share a package and a review can span -both. Separate recipe branches preserve incompatible conventions while one -entry workflow avoids overlapping metadata. A single review route shares -read-only rules and evidence standards without duplicating four workflows. -Split entrypoints if measured routing errors justify it, not merely to achieve -symmetrical names. - -**Eight-skill alternative:** `implement-v0-component`, -`implement-v1-component`, `implement-agentic-component`, -`implement-native-component`, and corresponding `review-*` skills. If chosen, -each must delegate to shared recipes rather than copy them, mixed-generation -review must have an explicit composition route, and native must remain an -overlay. Retire replaced active skills atomically; never leave discoverable -alias skills competing for automatic selection. - -## 5. Progressive loading contract - -1. Start with the root router and applicable scoped instructions. Verify - actual discovery; explicitly read required scopes when it is unqualified. -2. Select one task workflow. Read its positive scope, exclusions, and output - contract before invoking adjacent workflows. -3. Load only the chosen generation recipe, relevant product contract, and - references triggered by the work. Loading both v0 and v1 is appropriate - only for a genuinely mixed change. -4. Load native/focus overlays for semantic changes as well as file suffixes: - schemas, refs, platform wrappers, registration, Pod/build configuration, - accessibility actions, and focus behavior can cross native boundaries. -5. Choose source adapters by evidence type; do not load all six by default. - Look up relevant token entries rather than reading the entire mapping or - source inventory. -6. Links are navigation, not assumed automatic transclusion. Avoid recursive - "read everything" instructions, circular dependencies, and redirect chains. -7. Never trade required contract or safety evidence for fewer tokens. Missing - validation is reported explicitly, not converted to a successful fallback. - -Suggested **review budgets**, subject to D08: root eventually around 8 KB or -1,000 words, skill bodies around 600 words, descriptions around 50 words with -clear exclusions, and mandatory root-to-rule routes within two explicit reads. -Keep safety triggers directly in always-on entrypoints. Scoped files and -coherent references have advisory budgets, not hard truncation limits. - -Measure unique file bytes/words separately from actual injected tokens, -repeated loading, tool calls, and elapsed time. Shortening files does not prove -reduced runtime context. A 30% median loading reduction may be a later reporting -target, **not an empirically established prediction or pilot gate**. - -## 6. Minimal evaluation groundwork - -Do not introduce an instruction DSL, retrieval service, vendor-specific runner, -or a generated instruction system. - -Recommended first convention, subject to D07: three metadata fields on shared -references only: - -```yaml ---- -id: native.apple-office -kind: instruction-reference -owner_paths: - - packages/agentic/native-core ---- -``` - -Use `instruction-reference`, `evidence-reference`, or `compatibility-reference` -to distinguish document roles. These are repository conventions, not assumed -runtime-recognized frontmatter. Keep load conditions in routers, not duplicated -in a registry, frontmatter, and skill description. - -Assign stable IDs only to consequential, testable obligations, for example -`R-APPLE-001`, and separate version-bound observations as `C-FOCUS-001` with -source, inspected revision/version, date, and limits. Do not reuse component -requirement IDs or modify their checker schema. Distinguish an authoritative -rule definition from references to its ID. - -Git identifies document versions. Compute file hashes when assessing a case; -do not maintain per-file revision counters or self-referential hashes. -An optional generated inventory can follow once an integrity checker has a -real need; do not start with both a rich catalog and duplicate frontmatter. - -A portable case/result boundary can use: - -```yaml -case_id: classify-v1-compressible -task_mode: review -repository_commit: '' -instructions: - - path: references/agents/components/v1.md - sha256: '' -assertions: - - id: preserves-v1-public-ref - rule_ids: [R-V1-001] - source_refs: ['packages/components/Text/src/Text.tsx:5-17'] - result: not-run - evidence: 'No execution recorded.' -``` - -Case definitions describe inputs and expected assertions; execution results -add runtime/model versions, actual loaded files, command outcomes, skips, -measurement method, and sanitized evidence. Separate expected reads from -observed reads. A future evaluation framework can consume these artifacts -without changing the underlying instructions. - -Small sanitized case fixtures may be checked in after D11 approval; otherwise -keep them local. Framework selection is not a prerequisite for portable cases. -The agentic contract checker discovers that package's higher-order components, -not native-core specs -([checker common module](packages/agentic/components/scripts/spec-contracts/common.cjs), -lines 5-9 and 58-60). Do not claim native-contract coverage from it. - -## 7. Migration map - -### Existing skills and references - -| Existing surface | Proposed treatment | -| -------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| `agentic-component-authoring` | Retain name initially. Narrow implementation scope, distinguish primitive/higher-order contracts, and replace blanket reference loads with explicit conditions. | -| Its types/state/styles/render/tests references | Retain initially. Move an individual recipe to `references/agents/components/agentic/` when implementation and review actually share it, updating both consumers in the same change. | -| Its three focus references | Centralize under `references/agents/focus/` after the pilot; preserve cross-generation intent, platform-specific recipes, version limits, and historical evidence labels. | -| Its Windows Fabric reference | Centralize under `references/agents/native/windows-fabric.md` after the pilot; preserve native-core's narrower Fabric-only boundary. | -| Its `spec-source-adaptation.md` redirect | Retain as a non-skill compatibility reference until explicit retirement. No tracked inbound links does not establish absence of external consumers. | -| `agentic-component-contract-authoring` and six source adapters | Retain lifecycle and skill-local adapters. Move only if demonstrated reuse warrants it; keep `SPEC-SOURCE.md`, source locks, `agency.toml`, and checker locations intact. | -| `agentic-component-optimization` | Retain cross-component scope. Link to dependency-direction authority instead of copying it; do not broaden to repository-wide optimization. | -| `agentic-storybook-development` | Retain. Route operations through app-owned instructions/map and current declared scripts; keep the live story catalog out of shared instructions. | -| `fix-security-issues` | Retain safeguards, age policy, remediation ordering, and fail-closed behavior. No policy changes through extraction. | - -### Every instruction entrypoint - -| Entrypoint | Proposed treatment | -| ------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| Root `AGENTS.md` | Compact router and safeguards. Extract reconciled architecture/commands/framework recipes incrementally. Preserve permissions and Apple/Office text in the first pilot. | -| `CLAUDE.md` and `.github/copilot-instructions.md` | Retain discovery filenames and paths; change adapter/import semantics only after actual supported-client probes. | -| `packages/agentic/components/AGENTS.md` | Retain package boundaries, modern API invariants, and component/primitive routes. Link to shared recipes without repeating them. | -| `packages/agentic/components/src/AGENTS.md` | Retain dependency direction, public primitive boundaries, and extraction ownership. | -| `packages/agentic/components/src/components/AGENTS.md` | Retain higher-order rules and the `#focus-visual-policy` anchor. Detailed reusable recipes may move; contract meaning does not. | -| `packages/agentic/components/src/primitives/AGENTS.md` | Retain local-contract, unstyled behavior, renderer, export, and ref exceptions. | -| `packages/agentic/native-core/AGENTS.md` | Retain host/export/lifetime boundaries; link to shared native guidance when approved. | -| `packages/agentic/desktop-driver/AGENTS.md` | Retain ownership/security constraints and local documentation routes. Do not move its package reference tree. | -| `packages/framework-base/AGENTS.md` | Retain modern-pattern/React compatibility boundaries; update shared focus links. | -| `packages/framework-base/src/accessibility/AGENTS.md` | Retain abstraction/import constraints and qualification requirements; it is cited by product specs. Separate mixed technical evidence only with owner review. | -| `packages/framework/framework/AGENTS.md` | Keep framework-maintainer entrypoint; extract reusable v1 consumer guidance to `components/v1.md`. | -| `packages/deprecated/foundation-compose/AGENTS.md` | Keep deprecated-framework entrypoint; extract reusable v0 guidance to `components/v0.md`. | -| `apps/storybook/AGENTS.md` | Retain app invariants and `agent-map.yaml` route. Keep operational diagnostics app-owned and deduplicate only after reconciling scripts. | - -Update tracked inbound Markdown links, anchors, backtick paths, map paths, and -tooling/profile dependencies atomically. Keep the root -`#apple-native-authoring-and-office-integration` anchor as a short route if -Apple extraction is later approved. Preserve the component focus anchor. -Compatibility redirects are ordinary Markdown, not additional active skills. -Check packed-package contents if changing package-local documentation paths; -do not assume such files never ship. - -## 8. Phased execution and gates - -All phases require owner approval of their blocking decisions. The coordinator -owns the ledger, routing, and synthesis; affected package maintainers own -technical parity; the user owns architecture/policy choices and acceptance. - -| Phase | Work and dependencies | Exit gate | -| ------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | -| P0: baseline | Resolve D01-D08 as relevant. Inventory paths, rule definitions, consumers, anchors, stale claims, supported runtimes, and observed loading. Capture a rule-preservation ledger before edits. | Approved pilot scope and cases; discovery assumptions recorded, not guessed. | -| P1: bounded pilot | After P0, add classification route, one v1 consumer extraction, a `review-component` prototype, and limited non-safety root slimming associated with those routes. Resolve stale statements in the touched scope before extraction. No native/safety relocation or source-adapter moves. | Static integrity, actual discovery, correct routing, and preserved rules; efficiency reported without a mandatory percentage gain. | -| P2: workflow rollout | After successful P1 and D02-D04, add merged v0/v1 and native entrypoints, separate recipes, and small agreed integrity tooling under D09. | Mixed-generation and native-overlay cases select the correct recipes; review remains read-only. | -| P3: shared-reference consolidation | After P2 and D10, move proven shared focus/Windows guidance and genuinely shared agentic recipes. Keep source adapters and domain docs local. | Inbound links/anchors updated; no lost rule, evidence limit, owner, or contract meaning. | -| P4: safety and compatibility qualification | Only after explicit D06 approval and parity review, consider Apple/permissions extraction. Requalify adapters, packed-file effects, and supported-client loading; wire approved integrity checks into CI. | Safety parity, truthful native evidence, and compatibility retained. Removal requires a separate explicit retirement decision. | -| P5: optional efficacy loop | After stable earlier phases and D11-D12, introduce proposal-only assessment using approved evidence and untouched holdouts. | Human-reviewed improvement with regression evidence; no autonomous canonical policy edits. | - -No unrelated native rebuild is required merely to move Markdown. If instructions -change a native recipe, validate the actual affected boundary and record -unavailable host/Office/runtime checks. A source build is not evidence of -physical input, UIA/AX action dispatch, or Office qualification. - -### Pilot cases and measurement - -Use six development cases: v0 Button maintenance, v1 Button review, compressible -Text review, agentic Button plan-only work, Icon primitive review, and a -native-backed experimental Checkbox review. Reserve holdouts covering a -modern implementation behind a legacy native-core facade, macOS extern -registration, Storybook operations, and dependency remediation. - -Compare baseline and candidate instructions against the same frozen product -source, task inputs, tools, model configuration, and permissions. Record the -distinct instruction revisions/hashes. Begin with one paired run per approved -case; repeat noisy/ambiguous cases only within D08's resource budget. Qualify a -second supported configuration before broad promotion. - -Hard gates: correct domain routing, no critical-rule loss, no unauthorized -writes or review-to-implementation switch, and no unsupported success claims. -Report per-case loading, duplicate injection, actual tokens when exposed, -tool calls, elapsed time, and human verdict. Small samples and synthetic -routing tasks do not prove end-to-end component-authoring efficacy. - -## 9. Integrity and validation - -Use existing tooling first. Add a small deterministic integrity check only -for gaps that existing tools do not cover; do not disguise agent evaluation as -static linting. - -| Check | Method | -| --------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| Preservation | Every previous consequential rule maps to a retained/new authoritative location or an explicitly approved retirement; observations retain versions and evidence limits. | -| Links and fragments | `yarn lage test-links`; additionally verify cross-file heading fragments, compatibility anchors, case-sensitive paths, and registered backtick/map references. Current fragment coverage is unqualified. | -| Routing integrity | Check missing targets, redirect chains, cycles, conflicting mandatory loads, metadata validity, and duplicate rule definitions rather than repeated ID references. | -| Formatting/repository conventions | `yarn format:check` and `yarn lint-repo` for migration changes; use targeted formatting during iteration. | -| Permission parity | `yarn test:agent-permissions` when touching permission-related surfaces; hook behavior must remain unchanged unless separately authorized. | -| Contract-linked changes | `yarn workspace @fluentui-react-native/components check:spec-contracts` when changing linked instruction locations or contract-sensitive guidance. | -| Packaging changes | `yarn check-publishing` and owning-package archive inspection when package documentation paths or published manifests change. | -| Runtime discovery | Probe root and nested cwd using actual supported client versions; inspect loaded instructions, active skills, and duplication. Static links do not establish runtime discovery. | -| Native validation reporting | Record executed, failed, skipped, not-run, and unsupported checks separately, with reasons. All-skipped or JS-only evidence is not native qualification. | - -For implementation/tooling additions, run the owning declared tests/builds and -root graph only when their scope warrants it. Inspect manifests before using -commands; do not assume generic `check`, `depcheck`, or direct runner scripts. -The commands above are migration validation requirements, not claims that this -planning task executed them all. - -## 10. Optional agent-efficacy improvement workflow - -`assess-agent-efficacy` should accept an explicit assessment request and -approved sanitized evidence. A description requesting explicit invocation is -not an enforced opt-out; verify runtime selection controls and retain an entry -gate that stops without user intent. - -Capture case ID, instruction/product revisions, model/client/tool versions, -loaded-file evidence, rule failures, actual command outcomes and skip reasons, -numeric usage where available, and human assessment. Exclude credentials, -private source bodies, personal identifiers, raw transcripts, and unrestricted -machine-log discovery. Do not upload evidence or publish a PR without separate -authorization. - -The output is a local proposal: observed failure, inferred root cause, smallest -instruction/skill change, expected benefit, tradeoffs, and regression results. -Apply it only through human review. Permission hooks/text, security-age policy, -native safety rules, and contract admission are protected review surfaces; -automated assessment cannot weaken or rewrite them. - -Prefer repeated evidence across task families and more than one supported -configuration. Keep acceptance holdouts unseen while tuning; a reused holdout -becomes a development case. Control for model upgrades, tool availability, -task difficulty, caching, compaction, human steering, network/toolchain -failures, native permissions, skipped tests, active plugins, and judge bias. -Cheaper output is not an improvement if correctness or safety regresses. - -## 11. User decisions required before execution - -Recommendations are not approvals. Edit **Answer** and **Status** below. -Use `Answered` or `Deferred`; a deferral should name its reconsideration event. - -| ID | Decision | Recommendation | Blocks | -| --- | -------------------------------- | ----------------------------------------------------------------------------------------------------------------------- | -------------------------------- | -| D01 | Shared home | `references/agents/`, not all package references centralized | P1 extraction | -| D02 | Workflow granularity | Merged v0/v1 entrypoint, separate recipes, one review skill, native overlay | P2 skills | -| D03 | Skill names and compatibility | Retain existing names; add `maintain-v0-v1-component`, `review-component`, and `implement-native-component` | P2 discovery | -| D04 | New v1 component policy | Preserve current supported behavior; explicitly decide whether new v1 work remains allowed | v1 workflow scope | -| D05 | Supported agent/client matrix | Qualify Copilot CLI and Claude Code if required; inventory other actual consumers before promising support | Discovery claims/adapter changes | -| D06 | Safety extraction | Keep Apple/permissions text in first pilot; later extraction only with parity review and retained safety routes/anchors | P4 safety migration | -| D07 | Metadata and rule IDs | Minimal shared-reference frontmatter, selective `R-`/`C-` IDs, Git/hash result identity; no rich registry initially | Eval-ready metadata/tooling | -| D08 | Pilot budget and acceptance | Approved paired cases, correctness/safety hard gates, efficiency measured; 30% only an optional later target | Pilot runs/rollout | -| D09 | Integrity tooling | Small link/anchor/rule checker; wire into existing CI after pilot | Tooling/CI changes | -| D10 | Recipe relocation and retirement | Move only demonstrated shared recipes; preserve adapters/anchors/redirects until explicitly retired | P3 moves/removal | -| D11 | Evidence and fixture storage | Sanitized portable fixtures may be committed; raw logs stay out; no external collection without approval | Eval fixtures/assessment inputs | -| D12 | Improvement governance | Defer proposal-only efficacy skill until pilot evidence; require human approval for promotion | P5 | - -### D01: Shared home - -**Status:** Answered -**Answer:** `references/agents` - -Choose `references/agents/` (recommended), root `references/`, or -`.github/references/`. The first preserves a root reference namespace for -future non-agent material. Product-owned documentation remains local under -every option. +# Agent instruction improvements: remaining decisions -### D02: Workflow granularity - -**Status:** Answered -**Answer:** the recommended hybrid model +**Status:** The plan is implemented on `user/jasonvmo/instruction-rework`. The efficacy skill (former P5) moved to the +owner's external workflow. Completed work has been removed from this file; what remains are decisions that need owner +input and follow-ups outside this branch. Delete this file before submitting the PR. -Choose the hybrid above (recommended), separate v0/v1 implementation skills -with one review router, or all eight `implement-*`/`review-*` skills. Separate -generation recipes and native-overlay composition are required in all cases. +## Decisions needing your input -### D03: Names and discoverable compatibility +Edit **Answer** and **Status** for each item. -**Status:** Answered -**Answer:** keep existing names where applicable. Use `implement-native-component` +### R01: Version-bound evidence in focus and accessibility guidance -Keep existing names initially (recommended), or adopt `implement-*` consistently, -including renaming agentic authoring. Choose whether the native name should be -`implement-native-component` or broader `native-authoring`. No duplicate active -aliases; define any supported transition period explicitly. +**Status:** Open +**Answer:** -### D04: v1 admission policy - -**Status:** Answered -**Answer:** maintenance-only - -Are new v1 components supported, maintenance-only, or allowed only for specific -compatibility requirements? The reviewers did not establish a current -maintenance-only v1 prohibition. Do not invent one during reorganization. - -### D05: Required clients and adapter behavior - -**Status:** Answered -**Answer:** GitHub Copilot CLI is the main entrypoint, use AGENTS.md files where appropriate, keep the repository root CLAUDE.md with the AGENTS.md import - -Name required clients/versions: Copilot CLI, Claude Code, VS Code, cloud coding -agent, code review, Agency, or others. Claude/GPT models inside Copilot are not -evidence of Claude Code discovery. Keep current adapters until the required -matrix verifies imports, nested scopes, skill discovery, and duplication. - -### D06: Safety material - -**Status:** Answered -**Answer:** Approve deferred extraction with parity review +The `C--` claim IDs and the `evidence-reference` document kind are defined and validated, but nothing uses +them yet. Pinned platform observations are still mixed with rules in +[`references/agents/focus/`](references/agents/focus/common.md) and the +[framework-base accessibility instructions](packages/framework-base/src/accessibility/AGENTS.md). Examples include +React Native Windows 0.81.35 dispatch, react-native-win32 0.81.8 declarations, and React Native macOS 0.81.9 Fabric +custom actions. Component `spec/accessibility.md` files cite the accessibility file. -Approve deferred extraction with parity review (recommended), retain full -safety guidance always-on permanently, or specify another boundary. Short -native/permission safeguards and explicit conditional routes remain always-on; -no actual policy change is included. +| Option | Effect | +| ---------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------- | +| 1. Leave as is (recommended for this PR) | No churn now; revisit when a pinned version changes. | +| 2. Tag key pinned claims as `C-FOCUS-*` / `C-A11Y-*` definitions in place | Evals and reviews can cite exact observations; needs your accuracy review of each claim. | +| 3. Move observations into `evidence-reference` documents and link to them from the rules | Cleanest separation; the most churn, including links cited by component specs. | -### D07: Metadata convention +### R02: Skill size -**Status:** Answered -**Answer:** Use the recommended approach for frontmatter but make it standardized +**Status:** Open +**Answer:** -Choose minimal frontmatter plus selective IDs (recommended), a single minimal -catalog instead, or plain Markdown initially. Do not duplicate routing and -ownership metadata across multiple inventories. Future evals record revision -and computed hashes, not manually maintained per-file versions. - -### D08: Pilot resource budget and thresholds +The advisory body budget is about 600 words. Over budget: `fix-security-issues` at 1,042 words (unchanged by this +work), `agentic-component-contract-authoring` at 685, and `agentic-component-authoring` at 613. Every description is +within the 60-word limit the checker enforces. -**Status:** Answered -**Answer:** make efficiency reporting only +| Option | Effect | +| ------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- | +| 1. Accept current sizes (recommended) | Only `fix-security-issues` is substantially over, and it loads only for dependency advisories. | +| 2. Split `fix-security-issues` | Move the `ignore:` plugin and dedupe procedures into a `references/agents/repository/dependency-security.md` reference loaded on demand. | +| 3. Enforce the budget in the checker | Hard failures on body length; risks pushing coherent rules apart. | -Approve/adjust the six development and four holdout cases, configurations, -run/retry ceiling, and evidence method before runs. Choose whether efficiency -is reporting-only (recommended) or has a measured acceptance threshold. -The first pilot does not promise a 30% reduction or an 8 KB root. +### R03: Retiring compatibility redirects -### D09: Checker and CI scope +**Status:** Open +**Answer:** -**Status:** Answered -**Answer:** use a small deterministic integrity checker, it should be executable from the repo root and run when 'yarn lage buildci' is executed (which will make it happen in CI) +Sixteen `compatibility-reference` stubs remain under `.github/skills/*/references/` so old links resolve. Every +repository link now targets the new locations, and the checker blocks new links to stubs. External links, such as your +workflow repository's activity records, may still use the old paths. -Approve a small deterministic integrity checker and later CI wiring -(recommended), or manual checks initially. Decide whether it is invoked by -existing lint/link infrastructure or a declared dedicated script. It checks -instruction integrity, not model behavior. +| Option | Effect | +| -------------------------------------------------------- | ------------------------------------------------------------ | +| 1. Keep for one release cycle, then remove (recommended) | Gives external links time to move; name the release or date. | +| 2. Keep indefinitely | Small permanent clutter in skill directories. | +| 3. Remove in this PR | Cleanest tree; breaks external links immediately. | -### D10: Shared recipes and compatibility retirement +### R04: Stale contributor documentation -**Status:** Answered -**Answer:** Err on the side of centralized recipes with an clear taxonomy for organization and extension for things which are likely to be used by multiple skills, reviewers, evals, or should be part of the general knowledge base. Keep things skill-local if they are really skill specific. +**Status:** Open +**Answer:** -Approve consumer-driven recipe moves (recommended), centralize all agent -recipes, or retain skill-local recipes. Keep contract source adapters local -unless actual reuse changes. Set the event required to retire old reference -paths; preserve product-linked anchors irrespective of routine cleanup. +Human-facing docs still use pre-reorganization paths: +[CONTRIBUTING.md](CONTRIBUTING.md) lines 72-73 reference `apps/fluent-tester/src/TestComponents`, `testPages.tsx`, +and `testPages..tsx`, and [apps/E2E/README.md](apps/E2E/README.md) line 146 references the same tester path. +The current locations are `apps/tester-core/src/TestComponents` and a single `apps/tester-core/src/testPages.ts`. -### D11: Evidence storage +| Option | Effect | +| ------------------------------- | ---------------------------------------- | +| 1. Fix in this PR (recommended) | Small, verified path corrections. | +| 2. Fix in a separate PR | Keeps this PR scoped to agent guidance. | +| 3. Leave | Contributors keep following stale paths. | -**Status:** Answered -**Answer:** use the recommended approach +### R05: Optional refinements from the GPT-6.1 Sol holdouts -Choose checked-in sanitized case fixtures with local execution evidence -(recommended), entirely local cases, or a separately approved external store. -Define public/private boundaries and any retention requirements. Framework -selection can remain deferred. +**Status:** Open +**Answer:** -### D12: Improvement loop +The candidate passed every holdout assertion, but two inefficiencies surfaced. Acting on either uses up the affected +holdout: relabel it as development and add a new holdout. -**Status:** Answered -**Answer:** defer the assess-agent-efficiency skill +1. The legacy native-core Callout plan first loaded `maintain-v0-v1-component`, then corrected itself to + `implement-native-component`. A clarification in that skill would say that a legacy public entrypoint is not + evidence of v0 or v1, and that native-core legacy wrappers route to native work. +2. The iOS helper plan read desktop Storybook and native-core documents before concluding they did not apply. All three + native holdouts cost more AI usage with the candidate. A refinement in `implement-native-component` would read + native-core documents only for native-core work and send iOS-only validation to the owning iOS host. -Defer `assess-agent-efficacy` until the pilot establishes useful measurements -(recommended), add a proposal-only skill earlier, or omit it. Define reviewer -ownership and promotion evidence; no autonomous safety edits or external -publication under any option. +| Option | Effect | +| -------------------------------------------------------- | ----------------------------------------------------------------------------- | +| 1. Apply both and replace the two holdouts (recommended) | Removes a misroute and likely trims native-case cost; needs two new holdouts. | +| 2. Apply only the routing clarification | Smallest change; leaves native-case cost as is. | +| 3. Leave as is and monitor in the external workflow | Holdouts stay unseen; inefficiencies remain. | -## 12. Cross-review reconciliation and provenance +### R06: Evidence sufficiency -| Topic | Reconciled recommendation | -| --------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| Root namespace | Both reviewers accepted `references/agents/` after cross-review. | -| v0/v1 skill count | Astra revised its initial separate-skill recommendation to a merged classifier entrypoint with separate recipes; retain splitting as a decision/pilot fallback. | -| Review/native split | Both favored one review workflow and native as an orthogonal entry/overlay. | -| Recipe centralization | Reject wholesale source-adapter migration. Move agentic recipes only with demonstrated implementation/review reuse; move focus/Windows guidance because existing consumers already cross boundaries. | -| Pilot scope | Reject Claude's initial safety-heavy root rewrite. Use limited non-safety slimming so the pilot can measure something; do not require the final root-size target immediately. | -| Authority | Reject a repository-defined global precedence ladder. Apply actual runtime authority, domain scopes, and explicit conflict resolution. | -| Metadata | Both rejected rich duplicated inventories. This synthesis recommends minimal frontmatter; the remaining format choice is D07. | -| Compatibility | Preserve Apple/focus anchors and ordinary Markdown redirects; avoid active alias skills. | -| Efficiency | Treat byte/token/latency savings as measured outcomes, not unsupported forecasts; safety and correct routing are hard gates. | -| Eval fixtures | Reviewers differed on local-only versus checked-in fixtures. Portable sanitized cases need no chosen framework; owner approval under D11 resolves storage. | - -Research used `claude-opus-5.5` and `gpt-6-astra`, each with `xhigh` reasoning -and `long_context`, first independently and then in reciprocal peer review. -The invocation interface exposes no exact token-window setting, so **an exact -1M-token context was requested but could not be verified**. - -The four complete reports are retained as session artifacts: -`claude-independent-report.txt`, `astra-independent-report.txt`, -`claude-cross-review.txt`, and `astra-cross-review.txt`. They are provenance, -not repository instruction entrypoints. Reviewer findings remain distinguishable -from runtime-qualified behavior and owner-approved decisions. - -**Known gaps:** supported-client discovery, exact context capacity, -cross-file fragment checking, external reference consumers, packed -instruction-file contents, quantitative efficacy, and unavailable native -qualification remain open until the relevant phase. No component code, -instruction files, skills, contracts, or permission hooks were changed to -produce this plan. - -## 13. Implementation record - -Implemented on branch `user/jasonvmo/instruction-rework` from `a1fd5c65d`. The final validation results are listed -below; the efficiency pilot is reporting-only under D08. - -### What changed - -| Area | Result | -| --------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| Shared home (D01) | [`references/agents/`](references/agents/README.md) with a taxonomy, document index, placement rules, standardized frontmatter (`id`, `kind`, `owner_paths` only), and `R-`/`C-` ID conventions (D07). | -| Centralized recipes (D10) | Agentic types, state, styles, rendering, and tests recipes; all three focus references; Windows Fabric guidance; and all six contract-source adapters moved under `references/agents`. New shared references cover classification, v0, v1, legacy test apps, repository architecture, commands and validation, TypeScript, permissions, release, design tokens, and native components. | -| Compatibility | Each of the 15 old reference paths, plus the existing `spec-source-adaptation.md` redirect, is a `compatibility-reference` linking directly to its new home. The `#focus-visual-policy` anchor and all inbound links are preserved. No alias skills were added. | -| Skills (D02, D03, D04) | Added `maintain-v0-v1-component`, `review-component`, and `implement-native-component`. Existing names are retained; descriptions now state positive scope and exclusions, and the authoring skill loads references conditionally, with a primitive `CONTRACT.md` branch. v0 and v1 are maintenance-only (`R-V0-001`, `R-V1-001`). | -| Root router (D05, D06) | Root `AGENTS.md` shrank from 27,423 to about 7,400 bytes. It keeps always-on safeguards (permissions, validation truthfulness, code rules, generation policy, native trigger), a task routing table, and a scoped-instruction table. `CLAUDE.md` and `.github/copilot-instructions.md` are unchanged. | -| Scoped entrypoints | v0 and v1 package `AGENTS.md` files are short maintainer routes to the shared recipes. Agentic package and primitive files define `R-AGENTIC-*` and `R-PRIMITIVE-*` rules. The Storybook diagnostics table moved to `apps/storybook/AGENTS.md#native-diagnostics`. | -| Integrity checker (D09) | `yarn check-agent-instructions` runs from the root and in `yarn lage buildci` and `repo-checks`. It validates frontmatter, index completeness, case-sensitive links and anchors, inbound anchors into instruction files, code-span paths, skill metadata, rule definitions and citations, compatibility redirects, and evaluation fixtures. It has 11 node tests in `@fluentui-react-native/scripts`. | -| Final validation | `R-VALID-003` requires one `yarn lage buildci --no-cache` before declaring a change complete, so stale Lage cache entries cannot hide failures. Root, the development reference, and the implementation skills state it. | -| Evaluation groundwork (D11) | Thirteen sanitized JSON cases in [`references/agents/evals/cases`](references/agents/evals/README.md): eight development and five holdout, including Apple implementation, review, and iOS holdout cases. The documented local result-record shape covers commit, instruction hashes, client and model, reads, assertions, usage, and confounders. No framework was chosen. | - -### Observed discovery behavior - -`copilot instruction list` (Copilot CLI 1.0.98) shows that a root session loads `.github/copilot-instructions.md`, -`AGENTS.md`, and `CLAUDE.md`. Because `CLAUDE.md` imports `AGENTS.md`, root content loads twice, so every byte removed -from the root saves double. A nested working directory additionally loads its own `AGENTS.md` and every ancestor -`AGENTS.md` up to the root. `copilot skill list` shows all eight project skills. - -### Safety parity (D06) - -The 68-line root permissions section moved to -[`references/agents/repository/permissions.md`](references/agents/repository/permissions.md) with rule IDs -`R-PERM-001` to `R-PERM-008`. - -A sentence-level comparison mapped all 39 source sentences into the permissions reference, the Storybook diagnostics -section, or the root safeguards. 33 matched automatically, and the remaining 6 were confirmed manually as wording -changes. The root keeps the prohibitions always-on: no history rewriting, no token printing, and separate authorization -for capture. Hook behavior is unchanged, and `yarn test:agent-permissions` passes. - -### Routing pilot (D08, reporting only) - -Paired, read-only routing probes ran on Copilot CLI 1.0.98 with `claude-sonnet-5.5`, medium reasoning, and only the -view, glob, grep, and skill tools. The baseline was a worktree at `a1fd5c65d`; the candidate was the migrated tree. -Assertions were scored by the coordinator from each probe's plan. - -| Measure | Baseline | Candidate | -| ------------------------------------------ | --------- | -------------- | -| Expected workflow skill reached (10 cases) | 4 | 10 | -| Assertions met: 6 development cases | 10/14 | 14/14 | -| Assertions met: 4 holdout cases | 7/8 | 8/8 | -| Total input tokens | 1,183,543 | 801,045 (−32%) | -| Median input tokens per case | 98,648 | 78,123 (−21%) | -| Final-call input tokens, summed | 408,491 | 300,469 (−26%) | -| AI usage units, summed | 124.4 | 96.8 (−22%) | -| Model requests | 35 | 37 | -| On-demand instruction bytes viewed | 169,297 | 362,936 | - -Baseline v0 and v1 probes loaded no instruction files and missed `componentRef`, settings-layer, evidence, and -native-qualification obligations. Candidates load more references on demand, but the smaller always-on root dominates -the total cost. - -Both configurations treated `yarn storybook build --macos` as a Paper build. The native skill now states that macOS -defaults to Fabric and Paper requires `--paper`. A recheck then planned `prep --paper` and `build --macos --paper`. -Under the holdout rule, that case was relabeled as development and a new unseen holdout, -`windows-fabric-callout-prop`, was added. - -Caveats: - -- There was one run per case, with one model. -- The personal global instruction was present in both configurations. -- Baseline skill-tool loads are not counted in viewed bytes. -- Holdouts were written by the same author as the instructions. - -### Deviations and follow-ups - -- **Apple/Office extraction (follow-up, resolved):** the Apple native authoring section from commit `246d33668` on - `user/jasonvmo/native-lib2` now lives in - [`references/agents/native/apple-office.md`](references/agents/native/apple-office.md) as `R-APPLE-001` to - `R-APPLE-007`, with examples from this branch. - - A sentence-level comparison preserved all 28 source sentences: 23 matched automatically, and 5 were confirmed - manually. - - The root safeguards, `implement-native-component` (workflow and native overlay checklist), `review-component` - (native row), and the native components reference all route to it. - - Routing probes confirmed that the implementation, review, and unseen iOS holdout cases each loaded the reference - and applied the expected rules. - - The owner is removing that commit from `native-lib2`, so no compatibility anchor is kept. Its native-core - `AGENTS.md`, which this branch does not have, should link to the new reference if it is retained. -- **Corrected stale root claims:** `yarn build --clean` (now `build:clean`); ESLint and Prettier (now oxlint and oxfmt); - `depcheck`; FluentTester paths (now `apps/tester-core`); per-platform `testPages`; `validate-changesets.mts` (now - `change.mts`); and framework-base as a devDependency (it is a runtime dependency). `yarn docs` was not carried - forward because its `fluent-rn-website` workspace does not exist. -- **Not changed (human documentation):** stale FluentTester paths in `CONTRIBUTING.md` and `apps/E2E/README.md` remain - as follow-ups. -- **Deferred:** the `assess-agent-efficacy` skill (D12). Repeat the pilot with more runs, a second model, and the new - holdout before relying on these percentages. - -### Validation - -| Check | Result | -| ----------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- | -| `yarn check-agent-instructions` | Passed | -| `yarn workspace @fluentui-react-native/scripts test` | Passed (11 tests) | -| `yarn workspace @fluentui-react-native/scripts build` and `lint` | Passed | -| `yarn lint-repo` (constraints, align-deps, Knip, lockfile, oxfmt) | Passed | -| `yarn lage test-links --grouped` | Passed for every workspace | -| `yarn test:agent-permissions` | Passed (10 tests) | -| `yarn workspace @fluentui-react-native/components check:spec-contracts` | Passed (20 contracts) | -| `yarn change:check` | Passed after committing the empty changeset | -| `yarn lage buildci --grouped` | Passed: 156 tasks succeeded and 0 failed, including `check-agent-instructions` and the scripts tests | -| `yarn lage buildci --no-cache --grouped` (final, R-VALID-003) | Passed: 171 tasks succeeded, 0 skipped (no cache), 0 failed | +**Status:** Open +**Answer:** + +Two configurations have now run once per case. The candidate improved routing and assertion coverage in both. Token +savings were consistent, but AI-usage savings were small with GPT-6.1 Sol and negative for its native holdouts. There +were no repeated trials, so the percentages are not statistically reliable. + +| Option | Effect | +| --------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------- | +| 1. Accept for this PR; run repeated trials in the external workflow (recommended) | Ships the routing and coverage gains now; the cost picture gets more precise later. | +| 2. Run three or more trials per case before the PR | Stronger evidence; costs about 50 more probe sessions per model. | +| 3. Add another client, such as Claude Code | Covers clients beyond Copilot CLI, the main client under D05. | + +### R07: Final validation after an aborted uncached run + +**Status:** Open +**Answer:** + +The first uncached `yarn lage buildci --no-cache` failed on formatting and stopped with 81 tasks unexecuted. After the +fix, a plain cached `yarn lage buildci` passed, but it skipped 164 tasks as cache hits. Those included the 81 tasks the +uncached pass never ran, served from earlier cache entries. Repeating the uncached run executed and passed all 171 tasks. +R-VALID-003 currently says a later plain run re-checks anything that has not passed, which holds only for tasks the +uncached pass actually executed. + +| Option | Effect | +| ---------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- | +| 1. Amend R-VALID-003: if the uncached run stops early, fix the failure and repeat the uncached run (recommended) | Guarantees every task ran uncached at the end; costs about one minute per repeat on this machine. | +| 2. Keep the current wording | A cached follow-up may reuse stale results for tasks the aborted uncached run never reached. | + +## Follow-ups outside this branch + +- **`native-lib2`:** after this PR merges and that branch rebases without commit `246d33668`, add a route from + `packages/agentic/native-core/AGENTS.md` to + [`references/agents/native/apple-office.md`](references/agents/native/apple-office.md). The checker will validate the + link. +- **External efficacy workflow:** reuse the committed [evaluation fixtures](references/agents/evals/README.md) and the + local result-record shape. Keep holdouts out of instruction tuning. +- **Publish:** push the branch and open the PR. The evidence below can go in the description. + +## Evidence for the PR description + +- **Root instructions:** root `AGENTS.md` went from 27.4 KB to 7.9 KB. Copilot CLI loads it twice from the repository + root, because `CLAUDE.md` imports it. +- **Safety extraction:** permissions moved with all 39 sentences preserved, and Apple/Office rules with all 28 + preserved, each confirmed by sentence-level comparison. +- **Checker:** `yarn check-agent-instructions` (15 tests) also enforces acyclic, at most three-deep prerequisites, + terminal compatibility redirects, one skill per routing row with consistent routing across routers, and root routing + for every skill. +- **Final validation:** `yarn lage buildci --no-cache` passed all 171 tasks with none skipped. `yarn change:check` and + `yarn lage test-links` also passed. + +Pilot results, one run per case, read-only Copilot CLI 1.0.98 probes, baseline `a1fd5c65d`: + +| Configuration | Expected skill reached | Assertions met | Input tokens | AI usage | +| -------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------- | ------------ | -------- | +| Claude Sonnet 5.5, medium; 10 cases; self-scored | Baseline 4/10, candidate 10/10 | Baseline 17/22, candidate 22/22 | −32% | −22% | +| GPT-6.1 Sol, high, long context; 8 development cases; independently scored | Baseline 1/1 available (9 skills absent); candidate 9/10, then 10/10 after the fix | Baseline 16 + 4 partial of 20, candidate 19 + 1 partial, then 20/20 after the fix | −24% | −9% | +| GPT-6.1 Sol, high, long context; 5 holdouts; independently scored | Every available expected skill in both (3 of 5 baseline cases had no matching skill); candidate 5/5 | Baseline 10 + 1 partial + 1 failed of 12, candidate 12/12 | −17% | −2% | + +GPT-6.1 Sol found one development routing defect: agentic plan-only requests went to contract authoring and skipped +the authoring recipes. Commit `5e183ce7f` fixed it. On recheck, the case reached the authoring skill, read all five +required files, and passed both assertions, at 22% more input tokens than its pre-fix run. From e769e6d5304600daa4f3cb500dfaa58c73739fa2 Mon Sep 17 00:00:00 2001 From: Jason Morse Date: Wed, 14 Oct 2026 23:28:29 -0700 Subject: [PATCH 08/14] Add clean-lage-cache script for final validation Add a root `yarn clean-lage-cache` script (`lage cache --clear`) and change R-VALID-003 to clear the Lage cache before the final `yarn lage buildci`. `--no-cache` bypasses the cache for one run but leaves earlier entries, so a cached rerun after an aborted uncached run could reuse results for tasks that never executed. After clearing, a plain rerun executes every task that has not passed. Remove the resolved R07 decision from the plan. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../agentic-component-authoring/SKILL.md | 2 +- .../agentic-storybook-development/SKILL.md | 2 +- .github/skills/fix-security-issues/SKILL.md | 2 +- .../implement-native-component/SKILL.md | 2 +- .../skills/maintain-v0-v1-component/SKILL.md | 2 +- AGENTS.md | 6 +++--- agent-improvements-plan.md | 19 ++----------------- package.json | 1 + references/agents/repository/development.md | 17 ++++++++++------- 9 files changed, 21 insertions(+), 32 deletions(-) diff --git a/.github/skills/agentic-component-authoring/SKILL.md b/.github/skills/agentic-component-authoring/SKILL.md index 174b0ba1b0..69cfc808b5 100644 --- a/.github/skills/agentic-component-authoring/SKILL.md +++ b/.github/skills/agentic-component-authoring/SKILL.md @@ -68,7 +68,7 @@ references used, the implementation order, and the validation plan. Upstream changes require explicit re-review and never overwrite the local contract. 9. Run the smallest declared validation command while iterating. Finish with package format, lint, build, and tests; run the Storybook bundle for story changes and the root build when public types, manifests, or project references - change. Finish with one `yarn lage buildci --no-cache` (R-VALID-003) and report results truthfully (R-VALID-002). + change. Finish with `yarn clean-lage-cache` and then `yarn lage buildci` (R-VALID-003) and report results truthfully (R-VALID-002). Do not divide one component implementation into separate sub-agent or sub-skill phases. Its types, state, styling, and rendering form one contract and should remain in one implementation context. diff --git a/.github/skills/agentic-storybook-development/SKILL.md b/.github/skills/agentic-storybook-development/SKILL.md index abbb7b0b1a..de68596067 100644 --- a/.github/skills/agentic-storybook-development/SKILL.md +++ b/.github/skills/agentic-storybook-development/SKILL.md @@ -25,7 +25,7 @@ Work on the native Storybook application in `apps/storybook`. spec without refreshing ReactCodegen's source list. If the clean build then reports a missing generated spec header, run `pods:macos` once more before changing native configuration. 6. Validate the JavaScript bundle and the affected native build. Run Storybook format/lint and the root build when - manifests or shared package references changed. Finish with one `yarn lage buildci --no-cache` (R-VALID-003). + manifests or shared package references changed. Finish with `yarn clean-lage-cache` and then `yarn lage buildci` (R-VALID-003). Keep component API, slot, token, and rendering work in the `agentic-component-authoring` skill; use this skill for the Storybook application and its native toolchains. diff --git a/.github/skills/fix-security-issues/SKILL.md b/.github/skills/fix-security-issues/SKILL.md index 7337207e35..e5acb29afe 100644 --- a/.github/skills/fix-security-issues/SKILL.md +++ b/.github/skills/fix-security-issues/SKILL.md @@ -157,7 +157,7 @@ Use declared workspace scripts and validate in increasing scope: 6. Run the root build and repository test graph when shared manifests, catalogs, resolutions, or the lockfile change broadly. 7. Add a normal or empty changeset as required by repository policy. -8. Finish with one `yarn lage buildci --no-cache` run (R-VALID-003). +8. Finish with `yarn clean-lage-cache` and then `yarn lage buildci` (R-VALID-003). Finish by confirming: diff --git a/.github/skills/implement-native-component/SKILL.md b/.github/skills/implement-native-component/SKILL.md index 20a6f85931..cde3365c2d 100644 --- a/.github/skills/implement-native-component/SKILL.md +++ b/.github/skills/implement-native-component/SKILL.md @@ -44,7 +44,7 @@ ownership and qualification rules (R-NATIVE-001 through R-NATIVE-003). R-APPLE-007. Run Pod operations only as the [permissions reference](../../../references/agents/repository/permissions.md) allows. 6. Report each platform and renderer as passed, failed, skipped, not run, or unsupported, with reasons (R-NATIVE-003). -7. Finish with one `yarn lage buildci --no-cache` run (R-VALID-003). +7. Finish with `yarn clean-lage-cache` and then `yarn lage buildci` (R-VALID-003). Storybook application or toolchain failures that are not caused by the native change belong to the [Storybook skill](../agentic-storybook-development/SKILL.md). diff --git a/.github/skills/maintain-v0-v1-component/SKILL.md b/.github/skills/maintain-v0-v1-component/SKILL.md index ee07ba2016..7235cdd8a8 100644 --- a/.github/skills/maintain-v0-v1-component/SKILL.md +++ b/.github/skills/maintain-v0-v1-component/SKILL.md @@ -30,7 +30,7 @@ v0 and v1 are maintenance-only (R-V0-001 and R-V1-001). A request for a new comp `componentRef`, token contracts, and native registration names (R-V0-002, R-V0-003, R-V1-002, R-V1-003). 6. Validate with the owning package's declared scripts (R-VALID-001): build, test, and lint, plus the framework packages named in the recipe when framework semantics change. Review snapshot diffs before accepting them. Report - each check's outcome (R-VALID-002), and finish with one `yarn lage buildci --no-cache` (R-VALID-003). + each check's outcome (R-VALID-002), and finish with `yarn clean-lage-cache` and then `yarn lage buildci` (R-VALID-003). 7. Add a changeset for published packages (R-REPO-003). Stop and confirm scope before migrating between frameworks, creating a component, or changing a public API. Those diff --git a/AGENTS.md b/AGENTS.md index a519c2dbd8..654fea4639 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -27,7 +27,7 @@ These apply to every task. using `gh api`, Pod, process, loopback `curl`, or capture operations, or changing the policy. - **Validation:** run the owning workspace's declared scripts (R-VALID-001). Report each check as passed, failed, skipped, not run, or unsupported; JavaScript tests and bundles are not native qualification (R-VALID-002). Finish - code or configuration changes with one `yarn lage buildci --no-cache` run (R-VALID-003). + code or configuration changes with `yarn clean-lage-cache` and then `yarn lage buildci` (R-VALID-003). - **Code:** no barrel exports (R-REPO-001); keep React Native fork imports in platform files (R-REPO-002); add a changeset for published packages (R-REPO-003); never copy private source bodies into this public repository (R-REPO-004). @@ -77,8 +77,8 @@ validation, TypeScript, release, design tokens, focus, and native guidance. - Yarn 4 in pnpm mode; TypeScript 7.0.2 compiled as one `tsc -b` project-references graph; Lage task orchestration. New packages must be added to the root `tsconfig.json` `references`. - `yarn build` builds the repository; `yarn lage test` and `yarn lage lint` test and lint packages; `yarn format` formats. - `yarn lage buildci` runs the CI aggregate, including `yarn check-agent-instructions`; use `--no-cache` once for final - validation. + `yarn lage buildci` runs the CI aggregate, including `yarn check-agent-instructions`. `yarn clean-lage-cache` clears + the Lage cache before final validation. - Details: [commands and validation](references/agents/repository/development.md), [architecture](references/agents/repository/architecture.md), [TypeScript](references/agents/repository/typescript.md), and [release](references/agents/repository/release.md). diff --git a/agent-improvements-plan.md b/agent-improvements-plan.md index a592b13fcf..53876f6d8d 100644 --- a/agent-improvements-plan.md +++ b/agent-improvements-plan.md @@ -108,22 +108,6 @@ were no repeated trials, so the percentages are not statistically reliable. | 2. Run three or more trials per case before the PR | Stronger evidence; costs about 50 more probe sessions per model. | | 3. Add another client, such as Claude Code | Covers clients beyond Copilot CLI, the main client under D05. | -### R07: Final validation after an aborted uncached run - -**Status:** Open -**Answer:** - -The first uncached `yarn lage buildci --no-cache` failed on formatting and stopped with 81 tasks unexecuted. After the -fix, a plain cached `yarn lage buildci` passed, but it skipped 164 tasks as cache hits. Those included the 81 tasks the -uncached pass never ran, served from earlier cache entries. Repeating the uncached run executed and passed all 171 tasks. -R-VALID-003 currently says a later plain run re-checks anything that has not passed, which holds only for tasks the -uncached pass actually executed. - -| Option | Effect | -| ---------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- | -| 1. Amend R-VALID-003: if the uncached run stops early, fix the failure and repeat the uncached run (recommended) | Guarantees every task ran uncached at the end; costs about one minute per repeat on this machine. | -| 2. Keep the current wording | A cached follow-up may reuse stale results for tasks the aborted uncached run never reached. | - ## Follow-ups outside this branch - **`native-lib2`:** after this PR merges and that branch rebases without commit `246d33668`, add a route from @@ -143,7 +127,8 @@ uncached pass actually executed. - **Checker:** `yarn check-agent-instructions` (15 tests) also enforces acyclic, at most three-deep prerequisites, terminal compatibility redirects, one skill per routing row with consistent routing across routers, and root routing for every skill. -- **Final validation:** `yarn lage buildci --no-cache` passed all 171 tasks with none skipped. `yarn change:check` and +- **Final validation:** a new root `yarn clean-lage-cache` script (`lage cache --clear`) followed by + `yarn lage buildci` ran and passed all 171 tasks with none skipped. `yarn change:check` and `yarn lage test-links` also passed. Pilot results, one run per case, read-only Copilot CLI 1.0.98 probes, baseline `a1fd5c65d`: diff --git a/package.json b/package.json index 6be3faf298..d73f29e8c1 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,7 @@ "bundle:repo": "lage bundle", "clean": "tsc -b --clean && lage clean", "clean-all": "node ./scripts/src/preinstall/clean-all.ts", + "clean-lage-cache": "lage cache --clear", "change": "node .github/scripts/change.mts", "changeset:version": "node .github/scripts/changeset-version-with-postbump.mts", "change:check": "node .github/scripts/change.mts --check", diff --git a/references/agents/repository/development.md b/references/agents/repository/development.md index f4065ea842..5325200357 100644 --- a/references/agents/repository/development.md +++ b/references/agents/repository/development.md @@ -21,10 +21,11 @@ the declared scripts in the root [package.json](../../../package.json) and [lage - **R-VALID-002:** Report each validation as passed, failed, skipped, not run, or unsupported, with the reason. Never report an unrun, all-skipped, or unsupported check as passed. JavaScript tests, bundles, and source builds are not evidence of native runtime behavior, physical input, UIA/AX action dispatch, or Office qualification. -- **R-VALID-003:** Before declaring a code, configuration, or instruction change complete, run - `yarn lage buildci --no-cache` once as final validation. It bypasses and resets the Lage cache, so incorrect cache - invalidation cannot hide a failure. After that run, a plain `yarn lage buildci` re-checks any package that has not - passed. Read-only reviews and plans do not need it. +- **R-VALID-003:** Before declaring a code, configuration, or instruction change complete, run `yarn clean-lage-cache` + and then `yarn lage buildci` as final validation. Clearing the Lage cache means incorrect cache invalidation cannot + hide a failure. If that run fails, fix the failure and rerun plain `yarn lage buildci`: tasks that never ran have no + cache entry, so they execute, and tasks that passed after the clear reuse fresh results. Read-only reviews and plans + do not need it. - **R-REPO-001:** Do not use barrel exports (`export * from '...'`). Use explicit named re-exports such as `export { Foo, Bar } from './module'` and `export type { Baz } from './module'` so bundlers can tree-shake. - **R-REPO-004:** Do not copy or mechanically transform private source bodies into this public repository. Record @@ -39,6 +40,7 @@ Set up a clone with `yarn` to install dependencies, then `yarn build`. | `yarn build` | `lage root-build`: runs `root-prebuild` (package `prebuild` steps), then `tsc -b` over the root graph | | `yarn build:clean` | Cleans the root TypeScript build, then rebuilds | | `yarn clean` | `tsc -b --clean` plus `lage clean` | +| `yarn clean-lage-cache` | Clears the Lage task cache (`lage cache --clear`) | | `yarn lage test` | Package tests after the root build and agentic contract checks | | `yarn lage lint` | Package lint (oxlint through `fluentui-scripts lint`) | | `yarn lint-fix` | Package lint with autofix (`FURN_FIX_MODE=true`) | @@ -53,8 +55,9 @@ Set up a clone with `yarn` to install dependencies, then `yarn build`. | `yarn lage buildci` | CI aggregate: repository lint, publishing check, agent-instruction check, build, test, and lint | | `yarn change` / `change:check` | Create or validate changesets; see [release](release.md) | -Lage caches task outputs; add `--no-cache` to bypass the cache and `--verbose` for detail. Iterate with cached runs, -then finish with one `yarn lage buildci --no-cache` (R-VALID-003). Before committing moved or renamed files, run +Lage caches task outputs in `node_modules/.cache/lage`. Iterate with cached runs, and add `--verbose` for detail. +`--no-cache` bypasses the cache for one run but leaves earlier entries in place, so a later cached run can still reuse +results for tasks the uncached run never reached. For final validation, clear the cache instead (R-VALID-003). Before committing moved or renamed files, run `yarn lage test-links` and update stale links. ## Package commands @@ -76,7 +79,7 @@ not isolate one file; use `--testNamePattern` to focus a run. - During iteration, run the smallest relevant test, and run package lint or build early after type-heavy edits. - For final validation, run format, lint, the package build, and package tests, then the root build when public types, - manifests, or project references change. Finish with one `yarn lage buildci --no-cache` (R-VALID-003). + manifests, or project references change. Finish with `yarn clean-lage-cache` and `yarn lage buildci` (R-VALID-003). - After fixing a failed step, rerun that step and its downstream checks before rerunning the whole chain. - Prefer committed `*.types.test.ts` coverage for compile-time contracts. Keep exploratory probes outside package source, or remove them before package validation. From e1fa44f82410ec08d118178573d7f3204d58171e Mon Sep 17 00:00:00 2001 From: Jason Morse Date: Wed, 14 Oct 2026 23:50:22 -0700 Subject: [PATCH 09/14] Separate evidence, split security skill, and remove redirects - Move version-bound focus and accessibility observations into evidence-reference documents (C-FOCUS-*, C-A11Y-*) and cite them from the rules and component accessibility specs. - Split fix-security-issues into a 371-word workflow and a dependency security procedure reference, preserving every sentence. - Remove the 16 compatibility redirects; keep redirects only for known external links. - Fix stale FluentTester and E2E paths in contributor documentation. - Route native-core legacy wrappers away from v0/v1 maintenance, read native-core documents only for native-core work, and validate iOS through its host app. Relabel the two informed holdouts and add two new ones. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../references/focus-macos.md | 12 -- .../references/focus-windows.md | 12 -- .../references/focus.md | 12 -- .../references/rendering.md | 12 -- .../references/spec-source-adaptation.md | 15 -- .../references/state-and-accessibility.md | 12 -- .../references/styles-and-tokens.md | 12 -- .../references/tests-and-stories.md | 12 -- .../references/types-and-slots.md | 12 -- .../windows-fabric-native-components.md | 12 -- .../references/sources/composite-evidence.md | 12 -- .../references/sources/flex-x3.md | 12 -- .../references/sources/fluent-v9.md | 12 -- .../references/sources/html-css.md | 12 -- .../references/sources/local-foundation.md | 12 -- .../references/sources/visual-reference.md | 12 -- .github/skills/fix-security-issues/SKILL.md | 181 +++--------------- .../implement-native-component/SKILL.md | 10 +- .../skills/maintain-v0-v1-component/SKILL.md | 4 +- CONTRIBUTING.md | 8 +- apps/E2E/README.md | 4 +- .../components/checkbox/spec/accessibility.md | 2 + .../components/switch/spec/accessibility.md | 2 + .../src/components/tab/spec/accessibility.md | 2 + .../src/accessibility/AGENTS.md | 31 ++- references/agents/README.md | 67 ++++--- .../action-transport-evidence.md | 33 ++++ .../components/agentic/tests-and-stories.md | 5 +- .../avatar-swift-react-import-review.json | 37 ++++ .../evals/cases/ios-react-header-helper.json | 2 +- .../native-core-legacy-callout-prop.json | 2 +- .../native-core-legacy-focuszone-fix.json | 41 ++++ references/agents/focus/macos.md | 78 +++----- references/agents/focus/platform-evidence.md | 86 +++++++++ references/agents/focus/windows-win32.md | 76 +++----- .../agents/repository/dependency-security.md | 156 +++++++++++++++ 36 files changed, 513 insertions(+), 509 deletions(-) delete mode 100644 .github/skills/agentic-component-authoring/references/focus-macos.md delete mode 100644 .github/skills/agentic-component-authoring/references/focus-windows.md delete mode 100644 .github/skills/agentic-component-authoring/references/focus.md delete mode 100644 .github/skills/agentic-component-authoring/references/rendering.md delete mode 100644 .github/skills/agentic-component-authoring/references/spec-source-adaptation.md delete mode 100644 .github/skills/agentic-component-authoring/references/state-and-accessibility.md delete mode 100644 .github/skills/agentic-component-authoring/references/styles-and-tokens.md delete mode 100644 .github/skills/agentic-component-authoring/references/tests-and-stories.md delete mode 100644 .github/skills/agentic-component-authoring/references/types-and-slots.md delete mode 100644 .github/skills/agentic-component-authoring/references/windows-fabric-native-components.md delete mode 100644 .github/skills/agentic-component-contract-authoring/references/sources/composite-evidence.md delete mode 100644 .github/skills/agentic-component-contract-authoring/references/sources/flex-x3.md delete mode 100644 .github/skills/agentic-component-contract-authoring/references/sources/fluent-v9.md delete mode 100644 .github/skills/agentic-component-contract-authoring/references/sources/html-css.md delete mode 100644 .github/skills/agentic-component-contract-authoring/references/sources/local-foundation.md delete mode 100644 .github/skills/agentic-component-contract-authoring/references/sources/visual-reference.md create mode 100644 references/agents/accessibility/action-transport-evidence.md create mode 100644 references/agents/evals/cases/avatar-swift-react-import-review.json create mode 100644 references/agents/evals/cases/native-core-legacy-focuszone-fix.json create mode 100644 references/agents/focus/platform-evidence.md create mode 100644 references/agents/repository/dependency-security.md diff --git a/.github/skills/agentic-component-authoring/references/focus-macos.md b/.github/skills/agentic-component-authoring/references/focus-macos.md deleted file mode 100644 index 28d31af83b..0000000000 --- a/.github/skills/agentic-component-authoring/references/focus-macos.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -id: compat.agentic-component-authoring.focus-macos -kind: compatibility-reference -owner_paths: - - packages/agentic/components ---- - -# Moved: macOS focus authoring - -This reference now lives at [references/agents/focus/macos.md](../../../../references/agents/focus/macos.md). - -This compatibility path remains so existing links continue to resolve. Do not add new links to it. diff --git a/.github/skills/agentic-component-authoring/references/focus-windows.md b/.github/skills/agentic-component-authoring/references/focus-windows.md deleted file mode 100644 index 1d03315b5f..0000000000 --- a/.github/skills/agentic-component-authoring/references/focus-windows.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -id: compat.agentic-component-authoring.focus-windows -kind: compatibility-reference -owner_paths: - - packages/agentic/components ---- - -# Moved: Windows and Win32 focus authoring - -This reference now lives at [references/agents/focus/windows-win32.md](../../../../references/agents/focus/windows-win32.md). - -This compatibility path remains so existing links continue to resolve. Do not add new links to it. diff --git a/.github/skills/agentic-component-authoring/references/focus.md b/.github/skills/agentic-component-authoring/references/focus.md deleted file mode 100644 index 8ece316667..0000000000 --- a/.github/skills/agentic-component-authoring/references/focus.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -id: compat.agentic-component-authoring.focus -kind: compatibility-reference -owner_paths: - - packages/agentic/components ---- - -# Moved: Common desktop focus authoring - -This reference now lives at [references/agents/focus/common.md](../../../../references/agents/focus/common.md). - -This compatibility path remains so existing links continue to resolve. Do not add new links to it. diff --git a/.github/skills/agentic-component-authoring/references/rendering.md b/.github/skills/agentic-component-authoring/references/rendering.md deleted file mode 100644 index 37ca887a2a..0000000000 --- a/.github/skills/agentic-component-authoring/references/rendering.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -id: compat.agentic-component-authoring.rendering -kind: compatibility-reference -owner_paths: - - packages/agentic/components ---- - -# Moved: Rendering and assembly - -This reference now lives at [references/agents/components/agentic/rendering.md](../../../../references/agents/components/agentic/rendering.md). - -This compatibility path remains so existing links continue to resolve. Do not add new links to it. diff --git a/.github/skills/agentic-component-authoring/references/spec-source-adaptation.md b/.github/skills/agentic-component-authoring/references/spec-source-adaptation.md deleted file mode 100644 index fd0ffd99f9..0000000000 --- a/.github/skills/agentic-component-authoring/references/spec-source-adaptation.md +++ /dev/null @@ -1,15 +0,0 @@ ---- -id: compat.agentic-component-authoring.spec-source-adaptation -kind: compatibility-reference -owner_paths: - - packages/agentic/components ---- - -# Moved: Flex source adaptation - -Contract creation belongs to the -[agentic component contract authoring skill](../../agentic-component-contract-authoring/SKILL.md). -Its Flex and X3 adapter now lives at -[references/agents/components/agentic/contract-sources/flex-x3.md](../../../../references/agents/components/agentic/contract-sources/flex-x3.md). - -This compatibility path remains so existing links continue to resolve. Do not add new links to it. diff --git a/.github/skills/agentic-component-authoring/references/state-and-accessibility.md b/.github/skills/agentic-component-authoring/references/state-and-accessibility.md deleted file mode 100644 index 72e901a347..0000000000 --- a/.github/skills/agentic-component-authoring/references/state-and-accessibility.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -id: compat.agentic-component-authoring.state-and-accessibility -kind: compatibility-reference -owner_paths: - - packages/agentic/components ---- - -# Moved: State and accessibility - -This reference now lives at [references/agents/components/agentic/state-and-accessibility.md](../../../../references/agents/components/agentic/state-and-accessibility.md). - -This compatibility path remains so existing links continue to resolve. Do not add new links to it. diff --git a/.github/skills/agentic-component-authoring/references/styles-and-tokens.md b/.github/skills/agentic-component-authoring/references/styles-and-tokens.md deleted file mode 100644 index 742c08f9c1..0000000000 --- a/.github/skills/agentic-component-authoring/references/styles-and-tokens.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -id: compat.agentic-component-authoring.styles-and-tokens -kind: compatibility-reference -owner_paths: - - packages/agentic/components ---- - -# Moved: Styles and tokens - -This reference now lives at [references/agents/components/agentic/styles-and-tokens.md](../../../../references/agents/components/agentic/styles-and-tokens.md). - -This compatibility path remains so existing links continue to resolve. Do not add new links to it. diff --git a/.github/skills/agentic-component-authoring/references/tests-and-stories.md b/.github/skills/agentic-component-authoring/references/tests-and-stories.md deleted file mode 100644 index 9bd3e0c843..0000000000 --- a/.github/skills/agentic-component-authoring/references/tests-and-stories.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -id: compat.agentic-component-authoring.tests-and-stories -kind: compatibility-reference -owner_paths: - - packages/agentic/components ---- - -# Moved: Tests and stories - -This reference now lives at [references/agents/components/agentic/tests-and-stories.md](../../../../references/agents/components/agentic/tests-and-stories.md). - -This compatibility path remains so existing links continue to resolve. Do not add new links to it. diff --git a/.github/skills/agentic-component-authoring/references/types-and-slots.md b/.github/skills/agentic-component-authoring/references/types-and-slots.md deleted file mode 100644 index 33b7aa045a..0000000000 --- a/.github/skills/agentic-component-authoring/references/types-and-slots.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -id: compat.agentic-component-authoring.types-and-slots -kind: compatibility-reference -owner_paths: - - packages/agentic/components ---- - -# Moved: Types and slots - -This reference now lives at [references/agents/components/agentic/types-and-slots.md](../../../../references/agents/components/agentic/types-and-slots.md). - -This compatibility path remains so existing links continue to resolve. Do not add new links to it. diff --git a/.github/skills/agentic-component-authoring/references/windows-fabric-native-components.md b/.github/skills/agentic-component-authoring/references/windows-fabric-native-components.md deleted file mode 100644 index e0cea51dd9..0000000000 --- a/.github/skills/agentic-component-authoring/references/windows-fabric-native-components.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -id: compat.agentic-component-authoring.windows-fabric-native-components -kind: compatibility-reference -owner_paths: - - packages/agentic/native-core ---- - -# Moved: Windows Fabric native components - -This reference now lives at [references/agents/native/windows-fabric.md](../../../../references/agents/native/windows-fabric.md). - -This compatibility path remains so existing links continue to resolve. Do not add new links to it. diff --git a/.github/skills/agentic-component-contract-authoring/references/sources/composite-evidence.md b/.github/skills/agentic-component-contract-authoring/references/sources/composite-evidence.md deleted file mode 100644 index eeafe8789d..0000000000 --- a/.github/skills/agentic-component-contract-authoring/references/sources/composite-evidence.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -id: compat.agentic-component-contract-authoring.composite-evidence -kind: compatibility-reference -owner_paths: - - packages/agentic/components ---- - -# Moved: Composite evidence adapter - -This reference now lives at [references/agents/components/agentic/contract-sources/composite-evidence.md](../../../../../references/agents/components/agentic/contract-sources/composite-evidence.md). - -This compatibility path remains so existing links continue to resolve. Do not add new links to it. diff --git a/.github/skills/agentic-component-contract-authoring/references/sources/flex-x3.md b/.github/skills/agentic-component-contract-authoring/references/sources/flex-x3.md deleted file mode 100644 index f815b688ea..0000000000 --- a/.github/skills/agentic-component-contract-authoring/references/sources/flex-x3.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -id: compat.agentic-component-contract-authoring.flex-x3 -kind: compatibility-reference -owner_paths: - - packages/agentic/components ---- - -# Moved: Flex and X3 source adapter - -This reference now lives at [references/agents/components/agentic/contract-sources/flex-x3.md](../../../../../references/agents/components/agentic/contract-sources/flex-x3.md). - -This compatibility path remains so existing links continue to resolve. Do not add new links to it. diff --git a/.github/skills/agentic-component-contract-authoring/references/sources/fluent-v9.md b/.github/skills/agentic-component-contract-authoring/references/sources/fluent-v9.md deleted file mode 100644 index 30a7aa1684..0000000000 --- a/.github/skills/agentic-component-contract-authoring/references/sources/fluent-v9.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -id: compat.agentic-component-contract-authoring.fluent-v9 -kind: compatibility-reference -owner_paths: - - packages/agentic/components ---- - -# Moved: Fluent v9 source adapter - -This reference now lives at [references/agents/components/agentic/contract-sources/fluent-v9.md](../../../../../references/agents/components/agentic/contract-sources/fluent-v9.md). - -This compatibility path remains so existing links continue to resolve. Do not add new links to it. diff --git a/.github/skills/agentic-component-contract-authoring/references/sources/html-css.md b/.github/skills/agentic-component-contract-authoring/references/sources/html-css.md deleted file mode 100644 index 55b8f8c367..0000000000 --- a/.github/skills/agentic-component-contract-authoring/references/sources/html-css.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -id: compat.agentic-component-contract-authoring.html-css -kind: compatibility-reference -owner_paths: - - packages/agentic/components ---- - -# Moved: HTML and CSS source adapter - -This reference now lives at [references/agents/components/agentic/contract-sources/html-css.md](../../../../../references/agents/components/agentic/contract-sources/html-css.md). - -This compatibility path remains so existing links continue to resolve. Do not add new links to it. diff --git a/.github/skills/agentic-component-contract-authoring/references/sources/local-foundation.md b/.github/skills/agentic-component-contract-authoring/references/sources/local-foundation.md deleted file mode 100644 index f00baf49c2..0000000000 --- a/.github/skills/agentic-component-contract-authoring/references/sources/local-foundation.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -id: compat.agentic-component-contract-authoring.local-foundation -kind: compatibility-reference -owner_paths: - - packages/agentic/components ---- - -# Moved: Local foundation source adapter - -This reference now lives at [references/agents/components/agentic/contract-sources/local-foundation.md](../../../../../references/agents/components/agentic/contract-sources/local-foundation.md). - -This compatibility path remains so existing links continue to resolve. Do not add new links to it. diff --git a/.github/skills/agentic-component-contract-authoring/references/sources/visual-reference.md b/.github/skills/agentic-component-contract-authoring/references/sources/visual-reference.md deleted file mode 100644 index dbbe1f6fe9..0000000000 --- a/.github/skills/agentic-component-contract-authoring/references/sources/visual-reference.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -id: compat.agentic-component-contract-authoring.visual-reference -kind: compatibility-reference -owner_paths: - - packages/agentic/components ---- - -# Moved: Visual reference source adapter - -This reference now lives at [references/agents/components/agentic/contract-sources/visual-reference.md](../../../../../references/agents/components/agentic/contract-sources/visual-reference.md). - -This compatibility path remains so existing links continue to resolve. Do not add new links to it. diff --git a/.github/skills/fix-security-issues/SKILL.md b/.github/skills/fix-security-issues/SKILL.md index e5acb29afe..075503cca7 100644 --- a/.github/skills/fix-security-issues/SKILL.md +++ b/.github/skills/fix-security-issues/SKILL.md @@ -7,157 +7,40 @@ license: MIT # Fix security issues Use this skill for dependency vulnerability remediation, Dependabot alerts, -Yarn audit findings, or security-driven package upgrades. +Yarn audit findings, or security-driven package upgrades. Detailed commands and +procedures are in the [dependency security reference](../../../references/agents/repository/dependency-security.md); read each linked +section when you reach that step. -## Read first - -1. Read the repository `AGENTS.md` and the owning workspaces' instructions. -2. Inspect `package.json`, `.yarnrc.yml`, `yarn.lock`, package catalogs, - resolutions, package extensions, and dynamic package extensions. -3. Inspect `.github/dependabot.yml` and any repository-specific package age, - cooldown, registry, or release policy. -4. Inspect the declared scripts for every affected workspace before choosing - validation commands. - -Do not edit `yarn.lock` manually. Change the owning manifest, catalog, -resolution, preset, or extension and let Yarn regenerate the graph. - -## Inventory the complete problem - -Start with the full recursive audit rather than a partial summary: - -```bash -yarn npm audit --all --recursive --json --no-deprecations -``` - -Normalize the output by severity, package, installed version, advisory, and -dependent. For every finding: - -1. Read the advisory and identify the first patched version, affected - functions, exploit preconditions, and whether a patch exists. -2. Run `yarn why ` to find every path. A fixed direct path does not - help if a stale compatible transitive path remains. -3. Search repository source and installed dependent source to determine - whether the vulnerable capability is used at runtime, only used by tooling, - or unused. -4. Group duplicate advisories for the same package, but do not omit findings - because one report calls them low priority. - -Treat critical and high findings as required fixes. Address moderate and low -findings whenever a compatible, policy-eligible remediation exists. - -## Enforce the package age policy - -Before resolving versions, inspect the effective Yarn policy: - -```bash -yarn config get npmMinimalAgeGate -yarn config get npmPreapprovedPackages -``` - -Calculate the exact cutoff from the session time. Verify publication timestamps -for proposed versions and, after lockfile generation, every newly selected npm -version in the lockfile diff. +## Safeguards - **R-SEC-001:** Do not disable or reduce the age gate to obtain a security fix. -- Do not assume `latest` is eligible. -- Check preapproved-package exceptions explicitly; an exception does not prove - that a newly selected release meets the requested age policy. -- Prefer the newest eligible fixed patch within the existing compatible range, - not the newest package release overall. - -## Choose the least risky complete remediation - -Use this order: - -1. **Upgrade the direct dependency.** Prefer a patched version within the - current major and update the shared catalog when it is the source of truth. -2. **Upgrade the transitive parent.** This is preferable when the parent has - already adopted the fixed dependency. -3. **Add a narrow root resolution.** Use the exact vulnerable descriptor when - possible. Review API and module-format compatibility before forcing a new - major, and exercise the dependent package's real code path. -4. **Remove an unused vulnerable capability with `ignore:`.** Use the installed - `@rnx-kit/yarn-plugin-ignore` only after proving the package is not required - by repository workflows. -5. **Carry a local Yarn patch.** Reserve this for required packages with no - eligible published fix. Base it on authoritative upstream work, add a - focused regression check, and document that version-based scanners may - still report the patched version. - - **R-SEC-002:** Do not add a broad catch, audit exclusion, or warning suppression in place of a runtime fix. +- Do not edit `yarn.lock` manually. Change the owning manifest, catalog, + resolution, preset, or extension and let Yarn regenerate the graph. +- Treat critical and high findings as required fixes. Address moderate and low + findings whenever a compatible, policy-eligible remediation exists. +- Use `ignore:` only for a capability proven unused that fails closed; never + ignore a package merely to make the audit output clean. -## Safely use the ignore plugin - -The repository's `ignore:` protocol replaces a package with an empty module. -It is appropriate when a transitive dependency implements an optional -capability that this repository intentionally does not support, such as a -browser downloader in a native-only test path. +## Workflow -Before adding a resolution such as: - -```json -{ - "resolutions": { - "unused-vulnerable-package": "ignore:" - } -} -``` - -prove all of the following: - -- no repository source imports or invokes the package; -- each dependent imports it lazily or can load with an empty module; -- the unsupported capability fails closed if invoked; -- declared builds, tests, bundles, and CLIs do not require it; -- `yarn why` shows only the understood optional paths. - -After installation, verify the npm package is absent from `yarn.lock`, the -`ignore:` locator is present, dependent modules still load, and an attempted -call cannot silently succeed. Never ignore a package merely to make the audit -output clean. - -## Resolve, audit, and deduplicate iteratively - -After each coherent manifest change: - -```bash -yarn install -yarn npm audit --all --recursive --json --no-deprecations -yarn why -``` - -Yarn may retain an older resolution that still satisfies a transitive range, -even when a fixed version is also installed. Once the remediation set is -complete, run: - -```bash -yarn dedupe --strategy highest -yarn dedupe --check -yarn install --immutable --mode=skip-build -``` - -Re-run the audit after dedupe. If stale vulnerable paths remain, update their -owning range or add a narrow compatible resolution; do not assume another -dedupe pass will update a package with only one descriptor. - -Keep related package families aligned. If repository lint expects an older -vulnerable version, update the shared alignment preset or catalog rather than -scattering exceptions or reverting the security floor. - -## Validate the affected behavior - -Use declared workspace scripts and validate in increasing scope: - -1. Format changed manifests and configuration. -2. Run lockfile and repository structural lint. -3. Run affected workspace lint, build, and tests. -4. Exercise the runtime path affected by every major-version resolution. -5. Bundle each affected Metro/native target when bundler dependencies change. -6. Run the root build and repository test graph when shared manifests, - catalogs, resolutions, or the lockfile change broadly. -7. Add a normal or empty changeset as required by repository policy. -8. Finish with `yarn clean-lage-cache` and then `yarn lage buildci` (R-VALID-003). +1. Read the repository `AGENTS.md` and the owning workspaces' instructions. + Inspect `package.json`, `.yarnrc.yml`, `yarn.lock`, package catalogs, + resolutions, package extensions, dynamic package extensions, + `.github/dependabot.yml`, any repository-specific package age, cooldown, + registry, or release policy, and the declared scripts for every affected + workspace before choosing validation commands. +2. [Inventory the complete problem](../../../references/agents/repository/dependency-security.md#inventory-the-complete-problem) with + the full recursive audit, `yarn why`, and runtime-use analysis. +3. [Enforce the package age policy](../../../references/agents/repository/dependency-security.md#enforce-the-package-age-policy) with + an exact cutoff for every newly selected version. +4. [Choose the least risky complete remediation](../../../references/agents/repository/dependency-security.md#choose-the-least-risky-complete-remediation): + direct dependency, transitive parent, narrow root resolution, + [`ignore:`](../../../references/agents/repository/dependency-security.md#safely-use-the-ignore-plugin), then a local Yarn patch. +5. [Resolve, audit, and deduplicate iteratively](../../../references/agents/repository/dependency-security.md#resolve-audit-and-deduplicate-iteratively). +6. [Validate the affected behavior](../../../references/agents/repository/dependency-security.md#validate-the-affected-behavior), add a + normal or empty changeset as required by repository policy, and finish with + `yarn clean-lage-cache` and then `yarn lage buildci` (R-VALID-003). Finish by confirming: @@ -175,11 +58,3 @@ ignored, and cannot be patched with a focused regression test, report the remaining advisory and its exposure plainly. Do not claim completion, weaken the age policy, force an unvalidated incompatible major, or suppress the warning. - -## Example - -If `extract-zip` is present only through optional browser installation code -while the repository runs native Appium automation, inspect both dependents, -confirm their normal modules load without extraction, resolve `extract-zip` to -`ignore:`, regenerate and deduplicate the lockfile, then prove the browser -archive path fails closed and the native tooling still works. diff --git a/.github/skills/implement-native-component/SKILL.md b/.github/skills/implement-native-component/SKILL.md index cde3365c2d..3e9beaa694 100644 --- a/.github/skills/implement-native-component/SKILL.md +++ b/.github/skills/implement-native-component/SKILL.md @@ -20,13 +20,14 @@ ownership and qualification rules (R-NATIVE-001 through R-NATIVE-003). [classification guide](../../../references/agents/components/classification.md); - native registration names and entrypoints that must not change. 2. Choose ownership. New modern native code belongs in native-core (R-NATIVE-001); maintain existing native wrappers, - such as experimental packages, where they live. Read the relevant sections of the - [native-core README](../../../packages/agentic/native-core/README.md). + such as experimental packages, where they live. For native-core work, read the relevant sections of the + [native-core README](../../../packages/agentic/native-core/README.md); for a wrapper in another package, read that + package's sources and documentation instead. 3. Load only the platform references the work needs: - React Native Windows Fabric views: [Windows Fabric](../../../references/agents/native/windows-fabric.md); - any iOS or macOS Swift, Objective-C, header, podspec, or bridge-manager change: [Apple native authoring and Office integration](../../../references/agents/native/apple-office.md) - (R-APPLE-001 through R-APPLE-007), plus native-core's macOS layout notes; + (R-APPLE-001 through R-APPLE-007), plus native-core's macOS layout notes for native-core work; - focus targets or native focus visuals: [common focus authoring](../../../references/agents/focus/common.md) and its platform file; - accessibility actions: @@ -40,7 +41,8 @@ ownership and qualification rules (R-NATIVE-001 through R-NATIVE-003). `codegen:windows` when Windows specs change; native builds through the owning app, such as `yarn storybook build --macos` or `--windows` from `apps/storybook`; and runtime evidence through Storybook smoke or `wdio` story tests. macOS Storybook commands default to Fabric: add `--paper` to `prep`, `build`, `run`, or `smoke` - for Paper, and rerun `prep` when switching renderers. Apple changes also need the module and bridging-header checks in + for Paper, and rerun `prep` when switching renderers. Desktop Storybook does not build iOS: validate iOS-only changes + through the owning iOS host, such as FluentTester's iOS app in `apps/fluent-tester`. Apple changes also need the module and bridging-header checks in R-APPLE-007. Run Pod operations only as the [permissions reference](../../../references/agents/repository/permissions.md) allows. 6. Report each platform and renderer as passed, failed, skipped, not run, or unsupported, with reasons (R-NATIVE-003). diff --git a/.github/skills/maintain-v0-v1-component/SKILL.md b/.github/skills/maintain-v0-v1-component/SKILL.md index 7235cdd8a8..07ea03fcc0 100644 --- a/.github/skills/maintain-v0-v1-component/SKILL.md +++ b/.github/skills/maintain-v0-v1-component/SKILL.md @@ -14,7 +14,9 @@ v0 and v1 are maintenance-only (R-V0-001 and R-V1-001). A request for a new comp 1. Classify every surface the change touches with the [classification guide](../../../references/agents/components/classification.md). Confirm the generation from - imports and structure, not directory names (R-CLASSIFY-001); Button contains both generations. + imports and structure, not directory names (R-CLASSIFY-001); Button contains both generations. A legacy public + entrypoint is not evidence of v0 or v1: native-core's `/legacy` wrappers use framework-base patterns and belong to + the [native component skill](../implement-native-component/SKILL.md). 2. Load only the matching recipe: [v0](../../../references/agents/components/v0.md) or [v1](../../../references/agents/components/v1.md). Load both only when the change touches both generations. 3. Add overlays only when the change triggers them: diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 260b16e488..64452efaa6 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -54,7 +54,7 @@ Most components should use the compose framework as it offers the comprehensive 1. Update your `package.json` file. This defines the name, fields, and dependencies of the npm package that will be published for this component. 1. As a pattern, we prefix `experimental` components with the word "experimental". For example, we have `@fluentui-react-native/experimental-button`. -**Additionally, we require integrating your new component/test page with our E2E testing infrastructure. Whenever a new component/test page is created, it should map to new tests in /apps/fluent-tester/src/E2E. This is to create a sophisticated, in-depth testing infrastructure that ensures that we don't regress our component library in the future; giving our partners and customers the best experience possible. Please see [Authoring an E2E Test](https://github.com/microsoft/fluentui-react-native/tree/main/apps/E2E#authoring-e2e-test).** +**Additionally, we require integrating your new component/test page with our E2E testing infrastructure. Whenever a new component/test page is created, it should map to new tests in /apps/E2E/src. This is to create a sophisticated, in-depth testing infrastructure that ensures that we don't regress our component library in the future; giving our partners and customers the best experience possible. Please see [Authoring an E2E Test](https://github.com/microsoft/fluentui-react-native/tree/main/apps/E2E#authoring-e2e-test).** Reach out to Samuel Freiberg with any questions related to E2E testing. @@ -63,14 +63,14 @@ Reach out to Samuel Freiberg with any questions related to E2E testing. 1. Create a `src/` subdirectory in your component directory with a minimum of two files (listed below). You may optionally choose to subdivide your code however you wish; there are plenty of examples in the other components of FluentUI React Native. 1. `index.ts` - This is the file listed as `main` inside your package.json and simply exports other files. - 1. `.tsx` - This is the file that will actually define your function component, and compose it into a higher order component with slots, theming, and design tokens. - Note that we need the comment `/** @jsxImportSource @fluentui-react-native/framework-base */` as the first line of this file. This opts the file into the custom JSX runtime that handles slot rendering; an explanation can be found in `packages/framework-base/src/component-patterns/README.md`. Packages using this pragma must include `@fluentui-react-native/framework-base` in their `devDependencies`. + 1. `.tsx` - This is the file that will actually define your function component, and compose it into a higher order component with slots, theming, and design tokens. - Note that we need the comment `/** @jsxImportSource @fluentui-react-native/framework-base */` as the first line of this file. This opts the file into the custom JSX runtime that handles slot rendering; an explanation can be found in `packages/framework-base/src/component-patterns/README.md`. Packages using this pragma must include `@fluentui-react-native/framework-base` in their `dependencies`, because compiled output imports the runtime. 1. `..tsx` (Optional) - Optional extra files to subdivide your code however you see fit. You can also add platform specific files as you see fit. ### Adding a new test for your component to the test app -1. In FluentTester (our test app), we'll need to add an test component. The test components are located under `fluentui-react-native/apps/fluent-tester/src/TestComponents`. -1. Make a subdirectory for your new component at this path, as well as adding an entry for your test in `testPages.tsx` and `testPages..tsx` +1. In FluentTester (our test app), we'll need to add an test component. The test components are located under `apps/tester-core/src/TestComponents`. +1. Make a subdirectory for your new component at this path, as well as adding an entry for your test in `apps/tester-core/src/testPages.ts`, whose `platforms` array lists the platforms where it appears 1. Create your tests in this new subdirectory. Pattern matching off an existing control's tests will greatly help. 1. If your component contains a native module, you will also need to add the path to your component's `podspec` (that we created earlier) in your test app's Podfile. This extra step is due to the fact that FluentTester has separate platform test apps that each share a common JS package. The react native community CLI does not support [autolinking transitive dependencies](https://github.com/react-native-community/cli/issues/1347), so we need to add it manually here. 1. Add your new module as a dependency in `apps/fluent-tester/package.json` and run `yarn && yarn build` from the root folder. diff --git a/apps/E2E/README.md b/apps/E2E/README.md index 7302135c77..fde98703f8 100644 --- a/apps/E2E/README.md +++ b/apps/E2E/README.md @@ -143,7 +143,7 @@ Please follow this structure for the new component. ## Create New Constants The way we our automation framework interacts with our test app is by selecting UI components based on a string value. For this, we have a **consts.ts** file for each component that defines these values. -You'll want to make one for your component under **apps/fluent-tester/src/TestComponents/_your-component_/consts.ts**. +You'll want to make one for your component under **apps/E2E/src/_your-component_/consts.ts** and export it from `apps/E2E/src/index.consts.ts`; test pages import the constants from `@fluentui-react-native/e2e-testing`. **You can simply copy/paste a _consts.ts_ file from another component, and just change the name of the component in the const names and the values.** @@ -254,7 +254,7 @@ export const CheckboxTest = () => { The spec document is where the tests will be written. We use [Jasmine](https://jasmine.github.io/), an open-sourced testing framework for JavaScript. The spec document imports a page object and uses it to manipulate the UI and uses 'expect' and other Jasmine statements to ensure proper functionality. -Spec documents should be put in apps/fluent-tester/src/E2E/\_ _ComponentToBeTested_ /specs/. +Spec documents should be put in apps/E2E/src/_ComponentToBeTested_/specs/. ``` describe('Click on each test page and check if it renders', function() { diff --git a/packages/agentic/components/src/components/checkbox/spec/accessibility.md b/packages/agentic/components/src/components/checkbox/spec/accessibility.md index 4612415a61..053859326b 100644 --- a/packages/agentic/components/src/components/checkbox/spec/accessibility.md +++ b/packages/agentic/components/src/components/checkbox/spec/accessibility.md @@ -24,6 +24,8 @@ press. Custom and disabled actions are still forwarded. A standalone `activate` event is not an additional toggle path. The [shared action foundation](../../../../../../framework-base/src/accessibility/AGENTS.md) +defines the action contract, and its +[transport evidence](../../../../../../../references/agents/accessibility/action-transport-evidence.md) records RNW 0.81.35, Win32 0.81.8, and RNmacOS 0.81.9 source evidence. macOS Fabric custom actions emit the declared name verbatim and ignore the display label; this does not establish default AXPress support. Native diff --git a/packages/agentic/components/src/components/switch/spec/accessibility.md b/packages/agentic/components/src/components/switch/spec/accessibility.md index aea120f61d..ecf8cce7e1 100644 --- a/packages/agentic/components/src/components/switch/spec/accessibility.md +++ b/packages/agentic/components/src/components/switch/spec/accessibility.md @@ -32,6 +32,8 @@ caller handler, as do unrelated custom actions. No action synthesizes `onPress`, and `activate` is not an additional toggle path. The [shared action foundation](../../../../../../framework-base/src/accessibility/AGENTS.md) +defines the action contract, and its +[transport evidence](../../../../../../../references/agents/accessibility/action-transport-evidence.md) records RNW 0.81.35, Win32 0.81.8, and RNmacOS 0.81.9 source evidence. macOS Fabric invokes custom actions by their declared names and ignores display labels; default AXPress support cannot be inferred from that path. diff --git a/packages/agentic/components/src/components/tab/spec/accessibility.md b/packages/agentic/components/src/components/tab/spec/accessibility.md index 73dbfc3662..80baa8ccff 100644 --- a/packages/agentic/components/src/components/tab/spec/accessibility.md +++ b/packages/agentic/components/src/components/tab/spec/accessibility.md @@ -33,6 +33,8 @@ selecting. No accessibility action synthesizes `onPress`, and `activate` is not an additional selection path. The [shared action foundation](../../../../../../framework-base/src/accessibility/AGENTS.md) +defines the action contract, and its +[transport evidence](../../../../../../../references/agents/accessibility/action-transport-evidence.md) records RNW 0.81.35, Win32 0.81.8, and RNmacOS 0.81.9 source evidence. macOS Fabric custom actions emit the exact declared names and ignore display labels; default AXPress support is not established by that path. Native diff --git a/packages/framework-base/src/accessibility/AGENTS.md b/packages/framework-base/src/accessibility/AGENTS.md index 914553ef9c..c70cd15a2c 100644 --- a/packages/framework-base/src/accessibility/AGENTS.md +++ b/packages/framework-base/src/accessibility/AGENTS.md @@ -27,27 +27,20 @@ agree, and duplicate declarations can produce duplicate native callbacks. The current transport contract is: -| Platform | Toggle | Select | Evidence and limits | -| --------------- | -------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| Windows Fabric | `toggle` | `select` | RNW 0.81.35 `CompositionDynamicAutomationProvider.cpp` dispatches lowercase names. `UiaHelpers.cpp::DispatchAccessibilityAction` compares case-sensitively and emits once for every matching declaration. | -| Office Win32 | `Toggle` | `Select` | `@office-iss/react-native-win32` 0.81.8 `ViewAccessibility.d.ts` declares these UIA action names; existing FURN V1 Checkbox and Switch use `Toggle`. Native UIA invocation still needs target-platform verification. | -| macOS | `Toggle` | `Select` | RNmacOS 0.81.9 Fabric `RCTViewComponentView.mm::accessibilityCustomActions` exposes the declared names verbatim; `didActivateAccessibilityCustomAction` emits that exact name. These are custom actions, not an inferred `AXPress` mapping. | -| Other platforms | `Toggle` | `Select` | Retains existing custom declarations; this is not native pattern or gesture qualification. Verify a renderer before claiming support. | +| Platform | Toggle | Select | Evidence | +| --------------- | -------- | -------- | -------------------------------------------------------------------------- | +| Windows Fabric | `toggle` | `select` | C-A11Y-001 | +| Office Win32 | `Toggle` | `Select` | C-A11Y-002; native UIA invocation still needs target-platform verification | +| macOS | `Toggle` | `Select` | C-A11Y-003; custom actions, not an inferred `AXPress` mapping | +| Other platforms | `Toggle` | `Select` | Existing custom declarations; not native pattern or gesture qualification | -These source paths are relative to the installed platform package: +The version-bound source evidence, including installed-package paths, is in the +[action transport evidence](../../../../references/agents/accessibility/action-transport-evidence.md). Verify a +renderer before claiming support on another platform. -- RNW: `Microsoft.ReactNative/Fabric/Composition/`. -- Win32: `Libraries/Components/View/ViewAccessibility.d.ts`. -- RNmacOS Fabric: `React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.mm`. -- RNmacOS Paper: `React/Views/RCTView.m`. - -RNmacOS Fabric constructs custom actions from `name`, ignoring `label`; keep -caller labels in JS rather than replacing them with guessed localized text. -Paper instead maps a custom action's label back to its declared name and -implements `accessibilityPerformPress` using `activate` or `onAccessibilityTap`. -Fabric's `accessibilityActivate` only forwards `onAccessibilityTap`, and is not -that AppKit press implementation. Do not map toggle/select to `activate`, -synthesize `onPress`, or add an AXPress fallback based on Paper evidence. +Keep caller labels in JavaScript rather than replacing them with guessed localized text, because macOS Fabric ignores +labels when constructing custom actions (C-A11Y-003). Do not map toggle/select to `activate`, synthesize `onPress`, or +add an AXPress fallback based on Paper behavior (C-A11Y-004). The resolver normalizes only the owned semantic action's two known spellings. It deduplicates exact resulting names, preserves caller order and the first diff --git a/references/agents/README.md b/references/agents/README.md index 7cdf1dad29..e8a3ec575e 100644 --- a/references/agents/README.md +++ b/references/agents/README.md @@ -21,40 +21,44 @@ documents a task needs; links are navigation, not instructions to load everythin | [`components/agentic/`](components/agentic) | Agentic component recipes | | [`components/agentic/contract-sources/`](components/agentic/contract-sources) | Source adapters for agentic component contracts | | [`focus/`](focus) | Desktop focus behavior across component generations | +| [`accessibility/`](accessibility) | Version-bound accessibility action evidence | | [`native/`](native) | Native components, modules, and platform guidance | | [`evals/`](evals/README.md) | Portable evaluation case fixtures; not instructions | ## Document index -| ID | Document | Read when | -| -------------------------------------------------------- | ------------------------------------------------------------------------------- | ------------------------------------------------------------------------------- | -| `repository.architecture` | [Architecture](repository/architecture.md) | Orienting in the monorepo or choosing where code belongs | -| `repository.development` | [Commands and validation](repository/development.md) | Building, testing, linting, validating, or adding a package | -| `repository.typescript` | [TypeScript](repository/typescript.md) | Changing compiler settings, platform types, or the JSX runtime | -| `repository.permissions` | [Agent permissions](repository/permissions.md) | Using `gh api`, Pod, process, loopback, or capture operations; changing policy | -| `repository.release` | [Changesets and release](repository/release.md) | Changing a published package or release tooling | -| `design.tokens-and-theming` | [Tokens and theming](design/tokens-and-theming.md) | Mapping tokens, theme-dependent styles, or color utilities | -| `components.classification` | [Classification and routing](components/classification.md) | Starting any component task | -| `components.v0` | [v0 maintenance](components/v0.md) | Changing or reviewing `foundation-compose` components | -| `components.v1` | [v1 maintenance](components/v1.md) | Changing or reviewing `compose` or `compressible` components | -| `components.legacy-test-apps` | [FluentTester and E2E](components/legacy-test-apps.md) | Changing v0 or v1 test pages or E2E specs | -| `components.agentic.types-and-slots` | [Types and slots](components/agentic/types-and-slots.md) | Agentic public props, slots, resolved state, native props, or exports | -| `components.agentic.state-and-accessibility` | [State and accessibility](components/agentic/state-and-accessibility.md) | Agentic state hooks, defaults, interaction, or accessibility | -| `components.agentic.styles-and-tokens` | [Styles and tokens](components/agentic/styles-and-tokens.md) | Agentic styles, token mapping, theme caching, or state precedence | -| `components.agentic.rendering` | [Rendering and assembly](components/agentic/rendering.md) | Agentic render functions, assembly, or display names | -| `components.agentic.tests-and-stories` | [Tests and stories](components/agentic/tests-and-stories.md) | Agentic tests, stories, `wdio` story tests, or validation | -| `components.agentic.contract-sources.flex-x3` | [Flex and X3](components/agentic/contract-sources/flex-x3.md) | Contract evidence from the pinned Flex catalog | -| `components.agentic.contract-sources.fluent-v9` | [Fluent v9](components/agentic/contract-sources/fluent-v9.md) | Contract evidence from Fluent UI v9 source or specifications | -| `components.agentic.contract-sources.html-css` | [HTML and CSS](components/agentic/contract-sources/html-css.md) | Contract evidence from HTML, CSS, or browser output | -| `components.agentic.contract-sources.visual-reference` | [Visual reference](components/agentic/contract-sources/visual-reference.md) | Contract evidence from images, screenshots, or renders | -| `components.agentic.contract-sources.local-foundation` | [Local foundation](components/agentic/contract-sources/local-foundation.md) | Foundational components without a Flex catalog entry | -| `components.agentic.contract-sources.composite-evidence` | [Composite evidence](components/agentic/contract-sources/composite-evidence.md) | Contracts combining more than one source set | -| `focus.common` | [Common focus authoring](focus/common.md) | Any focusable component, collection, popup, focus hook, or native focus adapter | -| `focus.windows-win32` | [Windows and Win32 focus](focus/windows-win32.md) | Focus work on React Native Windows or Win32 | -| `focus.macos` | [macOS focus](focus/macos.md) | Focus work on macOS | -| `native.components-and-modules` | [Native components and modules](native/components-and-modules.md) | Any native component, module, service, or native-backed wrapper | -| `native.apple-office` | [Apple native authoring and Office integration](native/apple-office.md) | Any iOS or macOS Swift, Objective-C, header, podspec, or bridge-manager change | -| `native.windows-fabric` | [Windows Fabric native components](native/windows-fabric.md) | React Native Windows Fabric views, codegen, registration, or UIA | +| ID | Document | Read when | +| -------------------------------------------------------- | ------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------- | +| `repository.architecture` | [Architecture](repository/architecture.md) | Orienting in the monorepo or choosing where code belongs | +| `repository.development` | [Commands and validation](repository/development.md) | Building, testing, linting, validating, or adding a package | +| `repository.typescript` | [TypeScript](repository/typescript.md) | Changing compiler settings, platform types, or the JSX runtime | +| `repository.permissions` | [Agent permissions](repository/permissions.md) | Using `gh api`, Pod, process, loopback, or capture operations; changing policy | +| `repository.release` | [Changesets and release](repository/release.md) | Changing a published package or release tooling | +| `repository.dependency-security` | [Dependency security procedures](repository/dependency-security.md) | Remediating dependency advisories with the `fix-security-issues` skill | +| `design.tokens-and-theming` | [Tokens and theming](design/tokens-and-theming.md) | Mapping tokens, theme-dependent styles, or color utilities | +| `components.classification` | [Classification and routing](components/classification.md) | Starting any component task | +| `components.v0` | [v0 maintenance](components/v0.md) | Changing or reviewing `foundation-compose` components | +| `components.v1` | [v1 maintenance](components/v1.md) | Changing or reviewing `compose` or `compressible` components | +| `components.legacy-test-apps` | [FluentTester and E2E](components/legacy-test-apps.md) | Changing v0 or v1 test pages or E2E specs | +| `components.agentic.types-and-slots` | [Types and slots](components/agentic/types-and-slots.md) | Agentic public props, slots, resolved state, native props, or exports | +| `components.agentic.state-and-accessibility` | [State and accessibility](components/agentic/state-and-accessibility.md) | Agentic state hooks, defaults, interaction, or accessibility | +| `components.agentic.styles-and-tokens` | [Styles and tokens](components/agentic/styles-and-tokens.md) | Agentic styles, token mapping, theme caching, or state precedence | +| `components.agentic.rendering` | [Rendering and assembly](components/agentic/rendering.md) | Agentic render functions, assembly, or display names | +| `components.agentic.tests-and-stories` | [Tests and stories](components/agentic/tests-and-stories.md) | Agentic tests, stories, `wdio` story tests, or validation | +| `components.agentic.contract-sources.flex-x3` | [Flex and X3](components/agentic/contract-sources/flex-x3.md) | Contract evidence from the pinned Flex catalog | +| `components.agentic.contract-sources.fluent-v9` | [Fluent v9](components/agentic/contract-sources/fluent-v9.md) | Contract evidence from Fluent UI v9 source or specifications | +| `components.agentic.contract-sources.html-css` | [HTML and CSS](components/agentic/contract-sources/html-css.md) | Contract evidence from HTML, CSS, or browser output | +| `components.agentic.contract-sources.visual-reference` | [Visual reference](components/agentic/contract-sources/visual-reference.md) | Contract evidence from images, screenshots, or renders | +| `components.agentic.contract-sources.local-foundation` | [Local foundation](components/agentic/contract-sources/local-foundation.md) | Foundational components without a Flex catalog entry | +| `components.agentic.contract-sources.composite-evidence` | [Composite evidence](components/agentic/contract-sources/composite-evidence.md) | Contracts combining more than one source set | +| `focus.common` | [Common focus authoring](focus/common.md) | Any focusable component, collection, popup, focus hook, or native focus adapter | +| `focus.windows-win32` | [Windows and Win32 focus](focus/windows-win32.md) | Focus work on React Native Windows or Win32 | +| `focus.macos` | [macOS focus](focus/macos.md) | Focus work on macOS | +| `focus.platform-evidence` | [Desktop focus platform evidence](focus/platform-evidence.md) | Checking or updating a version-bound focus observation (`C-FOCUS-*`) | +| `accessibility.action-transport-evidence` | [Accessibility action transport evidence](accessibility/action-transport-evidence.md) | Checking or updating toggle/select transport observations (`C-A11Y-*`) | +| `native.components-and-modules` | [Native components and modules](native/components-and-modules.md) | Any native component, module, service, or native-backed wrapper | +| `native.apple-office` | [Apple native authoring and Office integration](native/apple-office.md) | Any iOS or macOS Swift, Objective-C, header, podspec, or bridge-manager change | +| `native.windows-fabric` | [Windows Fabric native components](native/windows-fabric.md) | React Native Windows Fabric views, codegen, registration, or UIA | ## Placement and extension @@ -65,8 +69,9 @@ documents a task needs; links are navigation, not instructions to load everythin with their packages. - Place a new document in the narrowest existing area. Create a new area only for a distinct domain, and add it to the taxonomy table. Add every new document to the document index with a concrete "Read when" condition. -- When moving a document, keep its `id`, update inbound links, and leave a `compatibility-reference` at the old path - that links directly to the new location. +- When moving a document, keep its `id` and update every inbound link in the same change. Leave a + `compatibility-reference` at the old path only when known external links depend on it, and link it directly to the + new location. ## Frontmatter standard diff --git a/references/agents/accessibility/action-transport-evidence.md b/references/agents/accessibility/action-transport-evidence.md new file mode 100644 index 0000000000..3400b092a4 --- /dev/null +++ b/references/agents/accessibility/action-transport-evidence.md @@ -0,0 +1,33 @@ +--- +id: accessibility.action-transport-evidence +kind: evidence-reference +owner_paths: + - packages/framework-base/src/accessibility +--- + +# Accessibility action transport evidence + +Version-bound observations behind the toggle and select transport contract in the +[framework-base accessibility instructions](../../../packages/framework-base/src/accessibility/AGENTS.md). Installed +paths are relative to each platform package's root. Recheck these claims when a pinned renderer version changes. + +- **C-A11Y-001:** React Native Windows Fabric dispatches lowercase `toggle` and `select` action names from + `Microsoft.ReactNative/Fabric/Composition/CompositionDynamicAutomationProvider.cpp`. + `UiaHelpers.cpp::DispatchAccessibilityAction` compares names case-sensitively and emits once for every matching + declaration, so duplicate declarations produce duplicate callbacks. Version: `react-native-windows` 0.81.35. Observed: + 2026-09-17 (PR #4314). +- **C-A11Y-002:** Office Win32 declares the UIA action names `Toggle` and `Select` in + `Libraries/Components/View/ViewAccessibility.d.ts`, and FURN V1 Checkbox and Switch use `Toggle`. Version: + `@office-iss/react-native-win32` 0.81.8. Observed: 2026-09-17 (PR #4314). Limits: these are type declarations; + native UIA invocation still needs target-platform verification. +- **C-A11Y-003:** React Native macOS Fabric exposes declared custom action names verbatim through + `accessibilityCustomActions` in `React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.mm`, constructs them + from `name` while ignoring `label`, and emits the exact name from `didActivateAccessibilityCustomAction`. Its + `accessibilityActivate` only forwards `onAccessibilityTap`. Version: `react-native-macos` 0.81.9. Observed: 2026-09-17 + (PR #4314). Limits: these are custom actions, not an `AXPress` mapping. +- **C-A11Y-004:** React Native macOS Paper maps a custom action's label back to its declared name and implements + `accessibilityPerformPress` with `activate` or `onAccessibilityTap` in `React/Views/RCTView.m`. Version: + `react-native-macos` 0.81.9. Observed: 2026-09-17 (PR #4314). Limits: Paper behavior is not evidence for Fabric. +- **C-A11Y-005:** The React Native macOS Fabric View does not project `accessibilityState.disabled` or `selected` to + AXEnabled or AXSelected. Version: `react-native-macos` 0.81.9. Observed: 2026-09-17 (PR #4314). Limits: revisit + when the renderer is upgraded. diff --git a/references/agents/components/agentic/tests-and-stories.md b/references/agents/components/agentic/tests-and-stories.md index cf26003e92..092c5ec96f 100644 --- a/references/agents/components/agentic/tests-and-stories.md +++ b/references/agents/components/agentic/tests-and-stories.md @@ -225,8 +225,9 @@ a physical click does not exercise UIA/AX action dispatch. Report passed, failed, and skipped counts together. A zero-exit smoke traversal with platform-skipped focus cases is not cross-platform focus qualification. -The pinned RNmacOS 0.81.9 Fabric View does not project -`accessibilityState.disabled`/`selected` to AXEnabled/AXSelected. Those native +The macOS Fabric View does not project disabled or selected accessibility state +to AXEnabled/AXSelected (C-A11Y-005 in the +[action transport evidence](../../accessibility/action-transport-evidence.md)). Those native announcement assertions are isolated in explicitly skipped cases; shared focus, activation, and selection-callback cases still run. Do not substitute rendered state for an AX result or describe these skips as intended macOS diff --git a/references/agents/evals/cases/avatar-swift-react-import-review.json b/references/agents/evals/cases/avatar-swift-react-import-review.json new file mode 100644 index 0000000000..94353b7eec --- /dev/null +++ b/references/agents/evals/cases/avatar-swift-react-import-review.json @@ -0,0 +1,37 @@ +{ + "schema_version": 1, + "case_id": "avatar-swift-react-import-review", + "split": "holdout", + "task_mode": "review", + "prompt": "Review a proposed change that replaces the guarded React import at the top of packages/experimental/Avatar/ios/AvatarViewManager.swift with a plain `import React`.", + "expected": { + "skills": ["review-component", "implement-native-component"], + "classification": { + "implementation_model": "none", + "public_compatibility": ["native-registration"], + "native_platforms": ["ios"] + }, + "required_reads": ["references/agents/native/apple-office.md"], + "forbidden_actions": ["edit-files"] + }, + "assertions": [ + { + "id": "flags-unguarded-import", + "description": "Flags the unguarded Swift React import and keeps the #if canImport(React) guard required for iOS bridging-header builds.", + "rule_ids": ["R-APPLE-002"], + "source_refs": ["packages/experimental/Avatar/ios/AvatarViewManager.swift"] + }, + { + "id": "ios-host-validation", + "description": "Plans validation through an iOS host in both module and bridging-header modes rather than desktop Storybook, reporting unrun Office checks.", + "rule_ids": ["R-APPLE-007"], + "source_refs": [] + }, + { + "id": "review-is-read-only", + "description": "Reports findings without editing files.", + "rule_ids": ["R-REVIEW-001"], + "source_refs": [] + } + ] +} diff --git a/references/agents/evals/cases/ios-react-header-helper.json b/references/agents/evals/cases/ios-react-header-helper.json index 9601203ad5..e357179557 100644 --- a/references/agents/evals/cases/ios-react-header-helper.json +++ b/references/agents/evals/cases/ios-react-header-helper.json @@ -1,7 +1,7 @@ { "schema_version": 1, "case_id": "ios-react-header-helper", - "split": "holdout", + "split": "development", "task_mode": "plan", "prompt": "Add an Objective-C helper to packages/experimental/AppearanceAdditions that reads the React Native RTL setting from RCTI18nUtil, and call it from Swift. Plan the change.", "expected": { diff --git a/references/agents/evals/cases/native-core-legacy-callout-prop.json b/references/agents/evals/cases/native-core-legacy-callout-prop.json index d3049d1f86..44d49a1f95 100644 --- a/references/agents/evals/cases/native-core-legacy-callout-prop.json +++ b/references/agents/evals/cases/native-core-legacy-callout-prop.json @@ -1,7 +1,7 @@ { "schema_version": 1, "case_id": "native-core-legacy-callout-prop", - "split": "holdout", + "split": "development", "task_mode": "plan", "prompt": "Add a new optional prop to the legacy Callout exported from @fluentui-react-native/native-core/legacy. Plan the change.", "expected": { diff --git a/references/agents/evals/cases/native-core-legacy-focuszone-fix.json b/references/agents/evals/cases/native-core-legacy-focuszone-fix.json new file mode 100644 index 0000000000..f821d0e650 --- /dev/null +++ b/references/agents/evals/cases/native-core-legacy-focuszone-fix.json @@ -0,0 +1,41 @@ +{ + "schema_version": 1, + "case_id": "native-core-legacy-focuszone-fix", + "split": "holdout", + "task_mode": "plan", + "prompt": "The legacy FocusZone exported from @fluentui-react-native/native-core/legacy does not restore focus to its default tabbable element on macOS. Plan the fix.", + "expected": { + "skills": ["implement-native-component"], + "classification": { + "implementation_model": "framework-base", + "public_compatibility": ["legacy-entrypoint", "componentRef", "native-registration"], + "native_platforms": ["macos"] + }, + "required_reads": [ + "references/agents/native/components-and-modules.md", + "references/agents/focus/common.md", + "references/agents/focus/macos.md" + ], + "forbidden_actions": ["edit-files", "migrate-framework"] + }, + "assertions": [ + { + "id": "routes-to-native-work", + "description": "Treats the legacy entrypoint as a framework-base wrapper over native code rather than v0 or v1 maintenance.", + "rule_ids": ["R-CLASSIFY-002"], + "source_refs": ["packages/agentic/native-core/src/legacy/focus-zone/FocusZone.tsx"] + }, + { + "id": "keeps-public-api", + "description": "Keeps the legacy public API, componentRef behavior, and native registration unchanged.", + "rule_ids": ["R-NATIVE-002"], + "source_refs": [] + }, + { + "id": "macos-renderer-evidence", + "description": "Plans macOS Fabric and Paper evidence, using the --paper Storybook flag for Paper, and confirms actual native focus rather than a request result.", + "rule_ids": ["R-NATIVE-003"], + "source_refs": ["packages/agentic/native-core/macos/components/FocusZone/shared/RCTFocusZone.m"] + } + ] +} diff --git a/references/agents/focus/macos.md b/references/agents/focus/macos.md index 871c7484c4..7190d0dc32 100644 --- a/references/agents/focus/macos.md +++ b/references/agents/focus/macos.md @@ -12,11 +12,10 @@ Read [common focus authoring](common.md) first. Match shared accessibility goals but do not transplant Windows click-focus, modifier, or key-up policies into AppKit. -**Reviewed implementation:** local `react-native-macos` 0.81.9, including Fabric -View, repository FocusZone, and Callout implementations, September 14, 2026. -Verify the renderer branch and resolved component before use. A `.macos.tsx` -file can be a stub: V1 Checkbox's macOS entrypoint warns and renders null, so it -is not a valid macOS focus reference. +Version-bound observations behind these rules are in the +[platform evidence](platform-evidence.md), C-FOCUS-001 to C-FOCUS-007. Verify the +renderer branch and resolved component before use. A `.macos.tsx` file can be a +stub that is not a valid macOS focus reference (C-FOCUS-001). ## First responder, key window, and actual target @@ -38,18 +37,13 @@ focus visual should be presented as active. detach/recycle and use common React 19 ref composition, not legacy componentRef exposure or global numeric-tag lookup. -Local references: -`packages\agentic\native-core\macos\components\FocusZone\shared\RCTFocusZone.m:24-35,122-129`; -`packages\agentic\native-core\macos\components\FocusZone\fabric\RCTFocusZoneComponentView.mm:118-179`. -Installed RNmacOS `React\Fabric\Mounting\ComponentViews\View\RCTViewComponentView.mm:1719-1804` -implements focus/blur commands and responder event emission. +Evidence: C-FOCUS-002. ## Pointer focus and click-through -Do not force Windows-style focus-before-press on ordinary macOS controls. -V1 `useOnPressWithFocus` recognizes macOS as focus-capable but only requests -click focus on Windows/Win32. The installed Fabric View also overrides -`needsPanelToBecomeKey` with a comment preserving mouse-versus-keyboard focus. +Do not force Windows-style focus-before-press on ordinary macOS controls. V1 +requests click focus only on Windows/Win32, and the Fabric View preserves +mouse-versus-keyboard focus (C-FOCUS-003). `acceptsFirstMouse` controls click-through in an inactive window; it is not an alias for `focusable`, first-responder acceptance, or keyboard ring visibility. @@ -57,16 +51,13 @@ Test first click to activate a window separately from an actual control action. Menus can intentionally focus on hover, so document that owner-specific exception rather than forcing all click/hover paths into one rule. -Evidence: `packages\utils\interactive-hooks\src\useOnPressWithFocus.ts:14-29`; -installed RNmacOS `RCTViewComponentView.mm:1769-1779`; -`packages\components\Menu\src\MenuItem\useMenuItem.ts:68-120`. +Evidence: C-FOCUS-003. ## Keyboard navigation, shortcuts, and editing -- V1 key helpers prefer key-down on macOS, versus key-up on Windows/Win32. - The macOS branches use `validKeysDown`/`validKeysUp`; current RNmacOS native - handling also has `keyDownEvents`/`keyUpEvents`. Resolve and validate the - compatibility path instead of configuring both indiscriminately. +- V1 and RNmacOS expose both key-down and key-up handling paths, and V1 prefers + key-down on macOS (C-FOCUS-004). Resolve and validate the compatibility path + instead of configuring both indiscriminately. - Matching native handled-key entries suppress AppKit's `super` handling. Register only keys owned by the control/scope so key equivalents, native navigation, and text editing are not swallowed. @@ -75,8 +66,8 @@ installed RNmacOS `RCTViewComponentView.mm:1769-1779`; activation. Do not copy Win32's modifier allowance or TabList Ctrl+Tab behavior. - Confirm exactly-once keyboard/accessibility activation; an AX action is not equivalent evidence to a physical key sequence. -- RNmacOS Pressability activates Return/Space on keydown; keyup supplies - `onPressOut`. Keep native press cleanup separate from Windows keyup-activation +- RNmacOS Pressability activates on key-down and releases on key-up + (C-FOCUS-004). Keep native press cleanup separate from Windows keyup-activation pairing. Intervening keys, caller cancellation, blur, disable, and target replacement must not strand pressed feedback or duplicate release callbacks. Retain a pooled event only with `persist()` and release that reference when @@ -98,38 +89,34 @@ installed RNmacOS `RCTViewComponentView.mm:1769-1779`; - Do not equate VoiceOver navigation focus with the application's keyboard first responder. Validate role/name/value and native action behavior in both modes. -Evidence: `packages\utils\interactive-hooks\src\useKeyProps.ts:8-95,114-126`; -installed RNmacOS `RCTViewComponentView.mm:1807-1861`. +Evidence: C-FOCUS-004. ## FocusZone and Fabric wrapper lifetime -The local macOS FocusZone translates key events, recalculates the key-view loop, -and falls back to mounted descendant order when AppKit collapses a Fabric-backed -zone to one key-loop entry. The Fabric wrapper deliberately does not participate -as another first-responder stop; it forwards focus to its content view. +The macOS FocusZone owns key translation and key-view-loop recalculation, and its +Fabric wrapper forwards focus rather than becoming another first-responder stop +(C-FOCUS-005). Preserve that single-owner behavior. Do not add a second JS roving loop to the same keys. Recompute eligibility when children mount/unmount and when props change, and clear the default responder on recycle. Retain direction, wrap/stop, RTL, nested zones, scrolling, and editable-child semantics. -Evidence: `packages\agentic\native-core\macos\components\FocusZone\shared\RCTFocusZone.m:584-675`; -`packages\agentic\native-core\macos\components\FocusZone\fabric\RCTFocusZoneComponentView.mm:118-195`. +Evidence: C-FOCUS-005. ## Popup windows and lifecycle -Callout's AppKit window can become key but not main. Its `setInitialFocus` and -`focusWindow` make the popup window key; that alone does not establish a particular -child as first responder. Its separate Fabric touch attachment also demonstrates -why React ancestry is not sufficient evidence for popup input capture. +Making a popup window key does not establish a particular child as first +responder, and React ancestry is not sufficient evidence for popup input capture +(C-FOCUS-006). Define and test initial child focus after content attachment, Escape, nested popup dismissal, outside click, window/application deactivation, and guarded return to the parent control. Do not reactivate the application or steal the outside-click target's focus as an unconditional cleanup action. -The legacy ContextualMenu uses a macOS timer after a layout effect. Treat it as -evidence of a native readiness problem, not a reusable delay constant. Prefer a +Treat the legacy ContextualMenu's post-layout timer (C-FOCUS-006) as evidence of +a native readiness problem, not a reusable delay constant. Prefer a mount/window-ready handshake with cancellation and focus confirmation. Keep a bounded, documented workaround only if the supported renderer has no such path. @@ -138,18 +125,13 @@ the owning popup/window, remove exactly once, and capture weak native owners. Do not install OS-global keyboard hooks or require desktop-driver permissions for ordinary production focus behavior. -Evidence: -`packages\agentic\native-core\macos\components\Callout\shared\CalloutWindow.swift:9-38`; -`packages\agentic\native-core\macos\components\Callout\shared\CalloutView.swift:33-45,142-216`; -`packages\agentic\native-core\macos\components\Callout\fabric\RCTCalloutComponentView.mm:96-132`; -`packages\agentic\native-core\macos\components\Callout\shared\GuardedEventMonitor.swift:1-29`; -`packages\components\ContextualMenu\src\ContextualMenu.tsx:39-55`. +Evidence: C-FOCUS-006. ## Native versus custom ring drawing Keep native macOS rings enabled by default unless the reviewed control requires -a custom path. The installed Fabric View draws a rounded AppKit focus mask only -when `enableFocusRing` is true. A focusable view or an enum property by itself is +a custom path. The Fabric View draws its focus mask only when `enableFocusRing` +is true (C-FOCUS-007). A focusable view or an enum property by itself is not proof that the ring mask/drawing is implemented correctly. When drawing a custom ring, disable the competing native path and preserve the @@ -158,8 +140,7 @@ changes, key-window transitions, clipping, appearance, Increase Contrast, and backing-scale changes. Use AppKit points and conversions; do not copy Windows Composition physical-pixel calculations into NSView layout. -Evidence: installed RNmacOS `RCTViewComponentView.mm:1729-1753` and Apple's -focus-mask documentation below. Ring colors remain a native/theme concern; +Evidence: C-FOCUS-007 and Apple's focus-mask documentation below. Ring colors remain a native/theme concern; do not resolve opaque system colors through a guessed web-color fallback. ## Native service candidates and qualification @@ -174,8 +155,7 @@ use AppKit/Swift/C++ behind that boundary without introducing UIKit assumptions. Run physical keyboard/pointer scenarios, VoiceOver, active/inactive windows, first-click behavior, keyboard-navigation settings, editable field editors, and native/custom rings. Include Paper only where it remains supported and Fabric -as an independently qualified endpoint. This Windows-based documentation -investigation did not execute a macOS native run. +as an independently qualified endpoint. ## Public references diff --git a/references/agents/focus/platform-evidence.md b/references/agents/focus/platform-evidence.md new file mode 100644 index 0000000000..7193dd1813 --- /dev/null +++ b/references/agents/focus/platform-evidence.md @@ -0,0 +1,86 @@ +--- +id: focus.platform-evidence +kind: evidence-reference +owner_paths: + - packages/agentic/components + - packages/agentic/native-core + - packages/framework-base +--- + +# Desktop focus platform evidence + +Version-bound observations behind the [macOS](macos.md) and [Windows and Win32](windows-win32.md) focus rules. +Each claim records what was observed, where, at which version, and its limits. These are evidence, not guarantees: +recheck the lockfile, renderer branch, and resolved component before relying on a claim, and update or retire it when +the pinned dependency changes. Installed-package paths are relative to that package's root. + +## macOS + +- **C-FOCUS-001:** The macOS focus guidance was reviewed against local `react-native-macos` 0.81.9, including the + Fabric View and the repository FocusZone and Callout implementations. Observed: 2026-09-14. Limits: a Windows-based + documentation review with no macOS native run. V1 Checkbox's macOS entrypoint warns and renders null, so it is not a + valid macOS focus reference. +- **C-FOCUS-002:** The installed Fabric View implements focus and blur commands and responder event emission. The + repository FocusZone requests and tracks the first responder natively. Sources: RNmacOS + `React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.mm:1719-1804`; + `packages/agentic/native-core/macos/components/FocusZone/shared/RCTFocusZone.m:24-35,122-129`; + `packages/agentic/native-core/macos/components/FocusZone/fabric/RCTFocusZoneComponentView.mm:118-179`. Version: + RNmacOS 0.81.9. Observed: 2026-09-14. +- **C-FOCUS-003:** V1 `useOnPressWithFocus` treats macOS as focus-capable but requests click focus only on Windows and + Win32. The installed Fabric View overrides `needsPanelToBecomeKey` to preserve mouse-versus-keyboard focus. V1 + MenuItem focuses on hover as an owner-specific exception. Sources: + `packages/utils/interactive-hooks/src/useOnPressWithFocus.ts:14-29`; RNmacOS `RCTViewComponentView.mm:1769-1779`; + `packages/components/Menu/src/MenuItem/useMenuItem.ts:68-120`. Version: RNmacOS 0.81.9. Observed: 2026-09-14. +- **C-FOCUS-004:** V1 key helpers prefer key-down on macOS through `validKeysDown` and `validKeysUp`, while RNmacOS + native handling also exposes `keyDownEvents` and `keyUpEvents`. RNmacOS Pressability activates Return and Space on + key-down, and key-up supplies `onPressOut`. Sources: `packages/utils/interactive-hooks/src/useKeyProps.ts:8-95,114-126`; + RNmacOS `RCTViewComponentView.mm:1807-1861`. Version: RNmacOS 0.81.9. Observed: 2026-09-14. +- **C-FOCUS-005:** The macOS FocusZone translates key events, recalculates the key-view loop, and falls back to + mounted descendant order when AppKit collapses a Fabric-backed zone to one key-loop entry. Its Fabric wrapper does + not become another first-responder stop; it forwards focus to its content view. Sources: + `packages/agentic/native-core/macos/components/FocusZone/shared/RCTFocusZone.m:584-675`; + `packages/agentic/native-core/macos/components/FocusZone/fabric/RCTFocusZoneComponentView.mm:118-195`. Observed: + 2026-09-14. +- **C-FOCUS-006:** Callout's AppKit window can become key but not main. `setInitialFocus` and `focusWindow` make the + popup window key without choosing a child first responder, and Callout has a separate Fabric touch attachment. Its guarded + event monitor provides popup-scoped monitor ownership. The legacy ContextualMenu waits on a macOS timer after a layout + effect. Sources: `packages/agentic/native-core/macos/components/Callout/shared/CalloutWindow.swift:9-38`; + `packages/agentic/native-core/macos/components/Callout/shared/CalloutView.swift:33-45,142-216`; + `packages/agentic/native-core/macos/components/Callout/fabric/RCTCalloutComponentView.mm:96-132`; + `packages/agentic/native-core/macos/components/Callout/shared/GuardedEventMonitor.swift:1-29`; + `packages/components/ContextualMenu/src/ContextualMenu.tsx:39-55`. Observed: 2026-09-14. +- **C-FOCUS-007:** The installed Fabric View draws a rounded AppKit focus mask only when `enableFocusRing` is true. + Source: RNmacOS `RCTViewComponentView.mm:1729-1753`. Version: RNmacOS 0.81.9. Observed: 2026-09-14. + +## Windows and Win32 + +- **C-FOCUS-008:** The reviewed endpoints were `@office-iss/react-native-win32` 0.81.8 (Paper, hosted by the prebuilt + REX host in Storybook) and `react-native-windows` 0.81.35 (Fabric, Windows App SDK Composition). Observed: + 2026-09-14. Limits: these are observed dependency versions, not minimum versions for every feature. +- **C-FOCUS-009:** V1 Windows and Win32 behavior comes from focus-before-press, per-control activation, Win32 tab + selection, and menu hover handling. Sources: `packages/utils/interactive-hooks/src/useOnPressWithFocus.ts:14-29`; + `packages/components/Button/src/useButton.ts:15-17,40-66,99-113`; + `packages/components/TabList/src/Tab/useTab.win32.ts:64-110`; + `packages/components/Menu/src/MenuItem/useMenuItem.ts:68-120`. Observed: 2026-09-14. +- **C-FOCUS-010:** The Fabric FocusZone requests focus with `TryFocus(FocusState::Keyboard)` and then checks the root's + focused component. Source: `packages/agentic/native-core/windows/NativeCore/components/FocusZone/FocusZoneComponentView.cpp:175-224`. + Observed: 2026-09-14. +- **C-FOCUS-011:** RNW's root stores a native focus state, resolves programmatic focus with its own policy, and + returns early when asked to focus the already-focused component. Source: RNW + `Microsoft.ReactNative/Fabric/Composition/RootComponentView.cpp:79-118`. Version: RNW 0.81.35. Observed: 2026-09-14. + Limits: a native implementation detail, not a public JavaScript API. +- **C-FOCUS-012:** Native qualification found Office Win32 key events with `code="Unidentified"` and a valid `key`. + The Win32 UIA Toggle and SelectionItem patterns were unavailable without accessibility action declarations, even + though JavaScript state was correct. Version: react-native-win32 0.81.8. Observed: 2026-09-14. +- **C-FOCUS-013:** V1 `useKeyProps` prefers key-up on Windows and Win32 and supplies Win32 handled-key descriptors. Its + Windows branch omits descriptors because of a historical `key` versus `code` mismatch. Sources: + `packages/utils/interactive-hooks/src/useKeyProps.ts:8-95,114-126`; + `packages/agentic/native-core/windows/NativeCore/components/FocusZone/FocusZoneComponentView.cpp:71-133`. + Observed: 2026-09-14. +- **C-FOCUS-014:** V1 uses native Win32 focus rings plus appearance-specific focused borders. RNW 0.81.35 has explicit + focus hosting and native theme handling. Sources: RNW + `Microsoft.ReactNative/Fabric/Composition/CompositionViewComponentView.cpp` and `Theme.cpp`. Observed: 2026-09-14. +- **C-FOCUS-015:** The Windows and Win32 smoke lanes each covered 18 cases with native assertions. Component focus + cases use the `desktop-focus` tag. A disconnected input desktop left semantic cases passing while every + physical-input case skipped. Win32's enabled-state announcement gap for editors is documented in Input's interaction + companion. Observed: 2026-09-14. Limits: case counts change as the catalog grows. diff --git a/references/agents/focus/windows-win32.md b/references/agents/focus/windows-win32.md index 1bed6bed35..0d8f9a6386 100644 --- a/references/agents/focus/windows-win32.md +++ b/references/agents/focus/windows-win32.md @@ -14,14 +14,14 @@ implementations separate. ## Identify the endpoint -| Endpoint | Reviewed local implementation | Native owner | -| --------- | ------------------------------------------------------------------------------ | ------------------------------------------------------------------- | -| `win32` | `@office-iss/react-native-win32` 0.81.8, Paper; prebuilt REX host in Storybook | Office Win32 host/view implementation | -| `windows` | `react-native-windows` 0.81.35, Fabric | Windows App SDK Composition component views and React Native island | - -These are observed dependency versions on September 14, 2026, not minimum-version -guarantees for every feature. Recheck the lockfile, platform resolution, and host -before implementation. Office Win32 Paper is **not** RNW's old UWP XAML renderer, +| Endpoint | Platform package and renderer | Native owner | +| --------- | ----------------------------------------------------------------------- | ------------------------------------------------------------------- | +| `win32` | `@office-iss/react-native-win32`, Paper; prebuilt REX host in Storybook | Office Win32 host/view implementation | +| `windows` | `react-native-windows`, Fabric | Windows App SDK Composition component views and React Native island | + +Reviewed versions and other version-bound observations are in the +[platform evidence](platform-evidence.md), C-FOCUS-008 to C-FOCUS-015. Recheck the +lockfile, platform resolution, and host before implementation. Office Win32 Paper is **not** RNW's old UWP XAML renderer, even though both are described as old-architecture/Paper in some contexts. ## Carry V1 behavior across, not its plumbing @@ -42,11 +42,7 @@ its observed behavior to a shared scenario contract, run V1 on Win32 as the reference, then verify the new implementation independently on both renderers. Do not require matching native element classes or identical ring pixels. -Sources: -`packages\utils\interactive-hooks\src\useOnPressWithFocus.ts:14-29`; -`packages\components\Button\src\useButton.ts:15-17,40-66,99-113`; -`packages\components\TabList\src\Tab\useTab.win32.ts:64-110`; -`packages\components\Menu\src\MenuItem\useMenuItem.ts:68-120`. +Evidence: C-FOCUS-009. ## Focus requests and native identity @@ -59,38 +55,28 @@ Native adapters operate on the correct mounted `ComponentView` in its owning surface/island. They must not reuse Paper UIManager/view-manager identifiers or assume a React ref can be serialized into a TurboModule argument. -The existing Fabric FocusZone uses `TryFocus(FocusState::Keyboard)` and checks -the root's focused component after the request -(`packages\agentic\native-core\windows\NativeCore\components\FocusZone\FocusZoneComponentView.cpp:175-224`). -Reuse this request-versus-observation pattern. Do not report success merely +The existing Fabric FocusZone requests focus and then confirms the root's focused +component (C-FOCUS-010). Reuse this request-versus-observation pattern. Do not report success merely because JavaScript called an optional `.focus()` method. -RNW's reviewed root stores a native focus state and resolves programmatic focus -using its own policy. A request for the already-focused component returns early. -This matters for modality-only changes: native focus reasons and the design +RNW's root resolves programmatic focus with its own policy and ignores a request +for the already-focused component (C-FOCUS-011). This matters for modality-only changes: native focus reasons and the design root's modality are related, but are not automatically the same source. Do not synthesize a blur/refocus cycle simply to redraw a ring. -Installed evidence: -`Microsoft.ReactNative\Fabric\Composition\RootComponentView.cpp:79-118`. -This native implementation detail is not a promised public JavaScript API. - ## Keyboard delivery and exactly-once activation - Inspect the actual installed Pressable and native view handlers. Do not add key-up activation on top of an existing native press action. -- Use `useFocusablePressable` rather than per-control key activation. Native - qualification found Office Win32 events with `code="Unidentified"` and a - valid `key`. The helper supplies one paired key-up fallback only when Win32's +- Use `useFocusablePressable` rather than per-control key activation. Office + Win32 can deliver key events whose `code` is `Unidentified` (C-FOCUS-012). The helper supplies one paired key-up fallback only when Win32's code-based Pressability cannot activate that key; recognized codes stay native. Pairing is cancelled on blur, disable, and target-generation changes. - Preserve required native accessibility actions. Checkbox and Switch supply `Toggle`; - Tab supplies `Select`. The Win32 UIA toggle/selection patterns were unavailable - without those action declarations even though JavaScript state was correct. -- V1 `useKeyProps` prefers key-up on Windows/Win32. It supplies Win32 - handled-key descriptors, but its Windows branch deliberately omits descriptors - because of the historical `key` versus `code` mismatch. Translate and test the - pinned Fabric representation instead of copying either branch blindly. + Tab supplies `Select`. Win32 UIA patterns depend on those declarations + (C-FOCUS-012). +- V1 `useKeyProps` handles Windows and Win32 key descriptors differently + (C-FOCUS-013). Translate and test the pinned Fabric representation instead of copying either branch blindly. - Treat native `Handled`/capture registration and JavaScript `preventDefault`/`stopPropagation` as separate mechanisms. Establish which cancellation reaches native navigation at each phase. @@ -101,14 +87,13 @@ This native implementation detail is not a promised public JavaScript API. and accessibility actions. Global root capture observes input; it must not consume every key. -Sources: `packages\utils\interactive-hooks\src\useKeyProps.ts:8-95,114-126`; -`packages\agentic\native-core\windows\NativeCore\components\FocusZone\FocusZoneComponentView.cpp:71-133`. +Evidence: C-FOCUS-013. ## Native and custom focus visuals Current new-component defaults remain native on Fabric and custom on Win32. -V1 uses native Win32 rings and appearance-specific focused borders, so this is -a choice to qualify, not evidence that Win32 native rings are unavailable. +V1's native Win32 rings (C-FOCUS-014) make this a choice to qualify, not evidence +that Win32 native rings are unavailable. - Request native rings only on the actual focus owner. Menu hover focus and disabled-focusable cases may require a different visibility policy from a @@ -123,9 +108,7 @@ a choice to qualify, not evidence that Win32 native rings are unavailable. lifecycle. React layout units and Composition physical pixels differ; apply the validated point-scale conversion. Test clipping/hosting and recycling. -RNW 0.81.35 has explicit focus hosting and native theme handling in installed -`Microsoft.ReactNative\Fabric\Composition\CompositionViewComponentView.cpp` -and `Theme.cpp`. See [Windows Fabric native components](../native/windows-fabric.md) +RNW provides explicit focus hosting and native theme handling (C-FOCUS-014). See [Windows Fabric native components](../native/windows-fabric.md) for codegen, UI-thread, registration, native visual, and UIA requirements. ## Collections, popups, and platform services @@ -158,14 +141,13 @@ Gate on actual focused element and action/announcement count, not just selected React state or a mocked ref call. A skipped input test is not qualification. Use owned app/Metro lifecycle commands and preserve unrelated sessions. -The catalog now uses named WDIO callbacks. The complete Windows and Win32 smoke -lanes each cover 18 cases with native assertions; component-focused cases use -the `desktop-focus` tag. Check aggregate result counts for zero skips before -claiming qualification. A disconnected input desktop can leave semantic cases -passing while all physical-input cases skip. +The catalog uses named WDIO callbacks, and component focus cases use the +`desktop-focus` tag (C-FOCUS-015). Check aggregate result counts for zero skips +before claiming qualification; a disconnected input desktop can leave semantic +cases passing while physical-input cases skip. For delegated modifier chords, assert parent delivery and unchanged collection selection, not immovable native focus: the host is allowed to handle the chord. For native editors, distinguish noneditable/nonfocusable from UIA `IsEnabled`; -Win32's observed enabled-state announcement gap is documented in Input's -interaction companion and does not replace native focus assertions. +Win32's enabled-state announcement gap (C-FOCUS-015) does not replace native +focus assertions. diff --git a/references/agents/repository/dependency-security.md b/references/agents/repository/dependency-security.md new file mode 100644 index 0000000000..aa59d2ca1a --- /dev/null +++ b/references/agents/repository/dependency-security.md @@ -0,0 +1,156 @@ +--- +id: repository.dependency-security +kind: instruction-reference +owner_paths: + - package.json + - .yarnrc.yml +--- + +# Dependency security procedures + +Detailed procedures for the [fix-security-issues skill](../../../.github/skills/fix-security-issues/SKILL.md). The skill owns the workflow, safeguards (R-SEC-001 and R-SEC-002), completion checklist, and failure behavior. + +## Inventory the complete problem + +Start with the full recursive audit rather than a partial summary: + +```bash +yarn npm audit --all --recursive --json --no-deprecations +``` + +Normalize the output by severity, package, installed version, advisory, and +dependent. For every finding: + +1. Read the advisory and identify the first patched version, affected + functions, exploit preconditions, and whether a patch exists. +2. Run `yarn why ` to find every path. A fixed direct path does not + help if a stale compatible transitive path remains. +3. Search repository source and installed dependent source to determine + whether the vulnerable capability is used at runtime, only used by tooling, + or unused. +4. Group duplicate advisories for the same package, but do not omit findings + because one report calls them low priority. + +Treat critical and high findings as required fixes. Address moderate and low +findings whenever a compatible, policy-eligible remediation exists. + +## Enforce the package age policy + +Before resolving versions, inspect the effective Yarn policy: + +```bash +yarn config get npmMinimalAgeGate +yarn config get npmPreapprovedPackages +``` + +Calculate the exact cutoff from the session time. Verify publication timestamps +for proposed versions and, after lockfile generation, every newly selected npm +version in the lockfile diff. + +- Never disable or reduce the age gate to obtain a security fix (R-SEC-001). +- Do not assume `latest` is eligible. +- Check preapproved-package exceptions explicitly; an exception does not prove + that a newly selected release meets the requested age policy. +- Prefer the newest eligible fixed patch within the existing compatible range, + not the newest package release overall. + +## Choose the least risky complete remediation + +Use this order: + +1. **Upgrade the direct dependency.** Prefer a patched version within the + current major and update the shared catalog when it is the source of truth. +2. **Upgrade the transitive parent.** This is preferable when the parent has + already adopted the fixed dependency. +3. **Add a narrow root resolution.** Use the exact vulnerable descriptor when + possible. Review API and module-format compatibility before forcing a new + major, and exercise the dependent package's real code path. +4. **Remove an unused vulnerable capability with `ignore:`.** Use the installed + `@rnx-kit/yarn-plugin-ignore` only after proving the package is not required + by repository workflows. +5. **Carry a local Yarn patch.** Reserve this for required packages with no + eligible published fix. Base it on authoritative upstream work, add a + focused regression check, and document that version-based scanners may + still report the patched version. + +Do not add a broad catch, audit exclusion, or warning suppression in place of a runtime fix (R-SEC-002). + +## Safely use the ignore plugin + +The repository's `ignore:` protocol replaces a package with an empty module. +It is appropriate when a transitive dependency implements an optional +capability that this repository intentionally does not support, such as a +browser downloader in a native-only test path. + +Before adding a resolution such as: + +```json +{ + "resolutions": { + "unused-vulnerable-package": "ignore:" + } +} +``` + +prove all of the following: + +- no repository source imports or invokes the package; +- each dependent imports it lazily or can load with an empty module; +- the unsupported capability fails closed if invoked; +- declared builds, tests, bundles, and CLIs do not require it; +- `yarn why` shows only the understood optional paths. + +After installation, verify the npm package is absent from `yarn.lock`, the +`ignore:` locator is present, dependent modules still load, and an attempted +call cannot silently succeed. Never ignore a package merely to make the audit +output clean. + +## Resolve, audit, and deduplicate iteratively + +After each coherent manifest change: + +```bash +yarn install +yarn npm audit --all --recursive --json --no-deprecations +yarn why +``` + +Yarn may retain an older resolution that still satisfies a transitive range, +even when a fixed version is also installed. Once the remediation set is +complete, run: + +```bash +yarn dedupe --strategy highest +yarn dedupe --check +yarn install --immutable --mode=skip-build +``` + +Re-run the audit after dedupe. If stale vulnerable paths remain, update their +owning range or add a narrow compatible resolution; do not assume another +dedupe pass will update a package with only one descriptor. + +Keep related package families aligned. If repository lint expects an older +vulnerable version, update the shared alignment preset or catalog rather than +scattering exceptions or reverting the security floor. + +## Validate the affected behavior + +Use declared workspace scripts and validate in increasing scope: + +1. Format changed manifests and configuration. +2. Run lockfile and repository structural lint. +3. Run affected workspace lint, build, and tests. +4. Exercise the runtime path affected by every major-version resolution. +5. Bundle each affected Metro/native target when bundler dependencies change. +6. Run the root build and repository test graph when shared manifests, + catalogs, resolutions, or the lockfile change broadly. +7. Add a normal or empty changeset as required by repository policy. +8. Finish with `yarn clean-lage-cache` and then `yarn lage buildci` (R-VALID-003). + +## Example + +If `extract-zip` is present only through optional browser installation code +while the repository runs native Appium automation, inspect both dependents, +confirm their normal modules load without extraction, resolve `extract-zip` to +`ignore:`, regenerate and deduplicate the lockfile, then prove the browser +archive path fails closed and the native tooling still works. From 22ba4b0cf97b8defc0577a36b295a9625f6c837c Mon Sep 17 00:00:00 2001 From: Jason Morse Date: Wed, 14 Oct 2026 23:52:46 -0700 Subject: [PATCH 10/14] Route native-core legacy wrappers to native work from the root router Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- AGENTS.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 654fea4639..3c9ad39936 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -43,16 +43,16 @@ These apply to every task. Classify the change with the [classification guide](references/agents/components/classification.md), then use: -| Task | Skill | -| ------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------- | -| New component, or change or plan a change to an agentic component or primitive | [agentic-component-authoring](.github/skills/agentic-component-authoring/SKILL.md) | -| Agentic contract-only or contract-feasibility work | [agentic-component-contract-authoring](.github/skills/agentic-component-contract-authoring/SKILL.md) | -| Fix or maintain an existing v0 or v1 component | [maintain-v0-v1-component](.github/skills/maintain-v0-v1-component/SKILL.md) | -| Review a component change of any generation | [review-component](.github/skills/review-component/SKILL.md) | -| Native component, TurboModule, service, or codegen | [implement-native-component](.github/skills/implement-native-component/SKILL.md) | -| Cross-component agentic extraction or dependency direction | [agentic-component-optimization](.github/skills/agentic-component-optimization/SKILL.md) | -| Storybook app, Metro, CocoaPods, or desktop native toolchain | [agentic-storybook-development](.github/skills/agentic-storybook-development/SKILL.md) | -| Dependency vulnerability remediation | [fix-security-issues](.github/skills/fix-security-issues/SKILL.md) | +| Task | Skill | +| ----------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- | +| New component, or change or plan a change to an agentic component or primitive | [agentic-component-authoring](.github/skills/agentic-component-authoring/SKILL.md) | +| Agentic contract-only or contract-feasibility work | [agentic-component-contract-authoring](.github/skills/agentic-component-contract-authoring/SKILL.md) | +| Fix or maintain an existing v0 or v1 component | [maintain-v0-v1-component](.github/skills/maintain-v0-v1-component/SKILL.md) | +| Review a component change of any generation | [review-component](.github/skills/review-component/SKILL.md) | +| Native component, TurboModule, service, codegen, or a native-core `/legacy` wrapper | [implement-native-component](.github/skills/implement-native-component/SKILL.md) | +| Cross-component agentic extraction or dependency direction | [agentic-component-optimization](.github/skills/agentic-component-optimization/SKILL.md) | +| Storybook app, Metro, CocoaPods, or desktop native toolchain | [agentic-storybook-development](.github/skills/agentic-storybook-development/SKILL.md) | +| Dependency vulnerability remediation | [fix-security-issues](.github/skills/fix-security-issues/SKILL.md) | Other repository work starts from the [reference index](references/agents/README.md): architecture, commands and validation, TypeScript, release, design tokens, focus, and native guidance. From 9655608918c9f3feb727960cc1f6ae2838dba99a Mon Sep 17 00:00:00 2001 From: Jason Morse Date: Wed, 14 Oct 2026 23:52:56 -0700 Subject: [PATCH 11/14] Finish native-core legacy routing clarification Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- AGENTS.md | 2 +- .../agents/components/classification.md | 22 +++++++++---------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 3c9ad39936..2a4d226871 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -47,7 +47,7 @@ Classify the change with the [classification guide](references/agents/components | ----------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- | | New component, or change or plan a change to an agentic component or primitive | [agentic-component-authoring](.github/skills/agentic-component-authoring/SKILL.md) | | Agentic contract-only or contract-feasibility work | [agentic-component-contract-authoring](.github/skills/agentic-component-contract-authoring/SKILL.md) | -| Fix or maintain an existing v0 or v1 component | [maintain-v0-v1-component](.github/skills/maintain-v0-v1-component/SKILL.md) | +| Fix or maintain an existing v0 or v1 component (not native-core `/legacy`) | [maintain-v0-v1-component](.github/skills/maintain-v0-v1-component/SKILL.md) | | Review a component change of any generation | [review-component](.github/skills/review-component/SKILL.md) | | Native component, TurboModule, service, codegen, or a native-core `/legacy` wrapper | [implement-native-component](.github/skills/implement-native-component/SKILL.md) | | Cross-component agentic extraction or dependency direction | [agentic-component-optimization](.github/skills/agentic-component-optimization/SKILL.md) | diff --git a/references/agents/components/classification.md b/references/agents/components/classification.md index 45b54d887a..5da9c196fe 100644 --- a/references/agents/components/classification.md +++ b/references/agents/components/classification.md @@ -22,17 +22,17 @@ more than one generation; Button ships both v0 and v1 implementations. ## 1. Task mode -| Task | Workflow | -| ----------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------- | -| Create a new component | [agentic-component-authoring](../../../.github/skills/agentic-component-authoring/SKILL.md) | -| Change, or plan a change to, an existing agentic component or primitive | [agentic-component-authoring](../../../.github/skills/agentic-component-authoring/SKILL.md) | -| Agentic contract-only or contract-feasibility work | [agentic-component-contract-authoring](../../../.github/skills/agentic-component-contract-authoring/SKILL.md) | -| Fix or maintain an existing v0 or v1 component | [maintain-v0-v1-component](../../../.github/skills/maintain-v0-v1-component/SKILL.md) | -| Review a component change of any generation | [review-component](../../../.github/skills/review-component/SKILL.md) | -| Native component, TurboModule, native service, or codegen change | [implement-native-component](../../../.github/skills/implement-native-component/SKILL.md) | -| Cross-component agentic extraction or dependency direction | [agentic-component-optimization](../../../.github/skills/agentic-component-optimization/SKILL.md) | -| Storybook app, Metro, CocoaPods, or desktop native toolchain | [agentic-storybook-development](../../../.github/skills/agentic-storybook-development/SKILL.md) | -| Dependency vulnerability remediation | [fix-security-issues](../../../.github/skills/fix-security-issues/SKILL.md) | +| Task | Workflow | +| ----------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------- | +| Create a new component | [agentic-component-authoring](../../../.github/skills/agentic-component-authoring/SKILL.md) | +| Change, or plan a change to, an existing agentic component or primitive | [agentic-component-authoring](../../../.github/skills/agentic-component-authoring/SKILL.md) | +| Agentic contract-only or contract-feasibility work | [agentic-component-contract-authoring](../../../.github/skills/agentic-component-contract-authoring/SKILL.md) | +| Fix or maintain an existing v0 or v1 component | [maintain-v0-v1-component](../../../.github/skills/maintain-v0-v1-component/SKILL.md) | +| Review a component change of any generation | [review-component](../../../.github/skills/review-component/SKILL.md) | +| Native component, TurboModule, native service, codegen, or native-core `/legacy` wrapper change | [implement-native-component](../../../.github/skills/implement-native-component/SKILL.md) | +| Cross-component agentic extraction or dependency direction | [agentic-component-optimization](../../../.github/skills/agentic-component-optimization/SKILL.md) | +| Storybook app, Metro, CocoaPods, or desktop native toolchain | [agentic-storybook-development](../../../.github/skills/agentic-storybook-development/SKILL.md) | +| Dependency vulnerability remediation | [fix-security-issues](../../../.github/skills/fix-security-issues/SKILL.md) | v0 and v1 are maintenance-only (R-V0-001, R-V1-001). A request for a new component uses the agentic workflow even when a v0 or v1 component with a similar name exists. From d7791d7c3d1b5fb39fa389686c2d1762d8d0ec09 Mon Sep 17 00:00:00 2001 From: Jason Morse Date: Wed, 14 Oct 2026 23:58:36 -0700 Subject: [PATCH 12/14] Record remaining agent-improvement decisions Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- agent-improvements-plan.md | 172 ++++++++++++++----------------------- 1 file changed, 65 insertions(+), 107 deletions(-) diff --git a/agent-improvements-plan.md b/agent-improvements-plan.md index 53876f6d8d..91c54dd024 100644 --- a/agent-improvements-plan.md +++ b/agent-improvements-plan.md @@ -1,112 +1,59 @@ # Agent instruction improvements: remaining decisions -**Status:** The plan is implemented on `user/jasonvmo/instruction-rework`. The efficacy skill (former P5) moved to the -owner's external workflow. Completed work has been removed from this file; what remains are decisions that need owner -input and follow-ups outside this branch. Delete this file before submitting the PR. +**Status:** All previous decisions (D01-D12 and R01-R07) are implemented on `user/jasonvmo/instruction-rework`. This +file now lists only new decisions and follow-ups. Delete it before submitting the PR. ## Decisions needing your input Edit **Answer** and **Status** for each item. -### R01: Version-bound evidence in focus and accessibility guidance +### R08: Where the efficiency skill should live **Status:** Open **Answer:** -The `C--` claim IDs and the `evidence-reference` document kind are defined and validated, but nothing uses -them yet. Pinned platform observations are still mixed with rules in -[`references/agents/focus/`](references/agents/focus/common.md) and the -[framework-base accessibility instructions](packages/framework-base/src/accessibility/AGENTS.md). Examples include -React Native Windows 0.81.35 dispatch, react-native-win32 0.81.8 declarations, and React Native macOS 0.81.9 Fabric -custom actions. Component `spec/accessibility.md` files cite the accessibility file. +An independent assessment compared an in-repository skill with keeping it in your external workflow. Key tradeoffs: -| Option | Effect | -| ---------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------- | -| 1. Leave as is (recommended for this PR) | No churn now; revisit when a pinned version changes. | -| 2. Tag key pinned claims as `C-FOCUS-*` / `C-A11Y-*` definitions in place | Evals and reviews can cite exact observations; needs your accuracy review of each claim. | -| 3. Move observations into `evidence-reference` documents and link to them from the rules | Cleanest separation; the most churn, including links cited by component specs. | +| Dimension | In-repository skill | External workflow | +| -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------- | +| Reach | Every contributor sees it, but few would run it; the fixtures are already public. | Owner only; others can still use the committed fixtures. | +| Always-on cost | Every Copilot CLI session pays an estimated 100-200 more tokens for another skill description and root routing row; `CLAUDE.md` doubles the root share. | None in this repository. | +| Misrouting | An "efficiency" or "optimize" description could compete with `agentic-component-optimization`. | None. | +| Privacy | Results written in the repository could be committed by accident; no repository-level results directory is ignored. | Already separates raw logs from curated summaries. | +| Telemetry | Cannot use private session logs, so it covers only the fixture half. | Has the cross-session telemetry the loop needs. | +| Portability | A committed harness would tie public maintenance to one client version's usage-event format. | Owner absorbs client drift privately. | +| Governance | `@microsoft/furn-team` would own code that drives LLM sessions. | Instruction changes still land through reviewed PRs either way. | +| Repository fit | Strong: rule IDs, fixtures, and the checker are here. | Cites rule IDs by reference; CI still validates the fixtures. | +| Permissions | Nested `copilot -p` and worktree commands fall outside the permission hook, so contributors would face prompts or broad grants. | Runs under your own approvals. | +| Reuse | This repository only. | Works across repositories. | -### R02: Skill size +The assessment flagged a holdout-contamination risk: probe agents can read public fixtures. Checking the recorded runs +found that no probe in either pilot read or searched `references/agents/evals/`. -**Status:** Open -**Answer:** - -The advisory body budget is about 600 words. Over budget: `fix-security-issues` at 1,042 words (unchanged by this -work), `agentic-component-contract-authoring` at 685, and `agentic-component-authoring` at 613. Every description is -within the 60-word limit the checker enforces. - -| Option | Effect | -| ------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- | -| 1. Accept current sizes (recommended) | Only `fix-security-issues` is substantially over, and it loads only for dependency advisories. | -| 2. Split `fix-security-issues` | Move the `ignore:` plugin and dedupe procedures into a `references/agents/repository/dependency-security.md` reference loaded on demand. | -| 3. Enforce the budget in the checker | Hard failures on body length; risks pushing coherent rules apart. | - -### R03: Retiring compatibility redirects - -**Status:** Open -**Answer:** - -Sixteen `compatibility-reference` stubs remain under `.github/skills/*/references/` so old links resolve. Every -repository link now targets the new locations, and the checker blocks new links to stubs. External links, such as your -workflow repository's activity records, may still use the old paths. - -| Option | Effect | -| -------------------------------------------------------- | ------------------------------------------------------------ | -| 1. Keep for one release cycle, then remove (recommended) | Gives external links time to move; name the release or date. | -| 2. Keep indefinitely | Small permanent clutter in skill directories. | -| 3. Remove in this PR | Cleanest tree; breaks external links immediately. | - -### R04: Stale contributor documentation - -**Status:** Open -**Answer:** - -Human-facing docs still use pre-reorganization paths: -[CONTRIBUTING.md](CONTRIBUTING.md) lines 72-73 reference `apps/fluent-tester/src/TestComponents`, `testPages.tsx`, -and `testPages..tsx`, and [apps/E2E/README.md](apps/E2E/README.md) line 146 references the same tester path. -The current locations are `apps/tester-core/src/TestComponents` and a single `apps/tester-core/src/testPages.ts`. - -| Option | Effect | -| ------------------------------- | ---------------------------------------- | -| 1. Fix in this PR (recommended) | Small, verified path corrections. | -| 2. Fix in a separate PR | Keeps this PR scoped to agent guidance. | -| 3. Leave | Contributors keep following stale paths. | - -### R05: Optional refinements from the GPT-6.1 Sol holdouts - -**Status:** Open -**Answer:** - -The candidate passed every holdout assertion, but two inefficiencies surfaced. Acting on either uses up the affected -holdout: relabel it as development and add a new holdout. +| Option | Effect | +| -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------- | +| 1. Keep the skill external, and add a short "Running an evaluation" procedure to `references/agents/evals/README.md` plus a checker guard rejecting stray files under `references/agents/evals/` (recommended) | Others can reproduce PR claims and result files cannot be committed by accident; no skill, runner, or routing cost. | +| 2. Keep everything external, with only the fixtures committed (current state) | Smallest footprint; the procedure stays private. | +| 3. Commit a TypeScript runner script without a skill | Reproducible by others; adds client-specific public maintenance and permission-hook entries. | +| 4. Add an in-repository skill that runs fixtures, with telemetry kept external | Discoverable; carries every in-repository cost for a workflow few contributors run. | -1. The legacy native-core Callout plan first loaded `maintain-v0-v1-component`, then corrected itself to - `implement-native-component`. A clarification in that skill would say that a legacy public entrypoint is not - evidence of v0 or v1, and that native-core legacy wrappers route to native work. -2. The iOS helper plan read desktop Storybook and native-core documents before concluding they did not apply. All three - native holdouts cost more AI usage with the candidate. A refinement in `implement-native-component` would read - native-core documents only for native-core work and send iOS-only validation to the owning iOS host. +Revisit if furn-team adopts evaluation evidence in PR review, a shared evaluation framework is chosen, the method is +proven across repeated multi-client trials, or clients support explicit-only skills hidden from the skill list. -| Option | Effect | -| -------------------------------------------------------- | ----------------------------------------------------------------------------- | -| 1. Apply both and replace the two holdouts (recommended) | Removes a misroute and likely trims native-case cost; needs two new holdouts. | -| 2. Apply only the routing clarification | Smallest change; leaves native-case cost as is. | -| 3. Leave as is and monitor in the external workflow | Holdouts stay unseen; inefficiencies remain. | - -### R06: Evidence sufficiency +### R09: Contributor new-component guidance **Status:** Open **Answer:** -Two configurations have now run once per case. The candidate improved routing and assertion coverage in both. Token -savings were consistent, but AI-usage savings were small with GPT-6.1 Sol and negative for its native holdouts. There -were no repeated trials, so the percentages are not statistically reliable. +`CONTRIBUTING.md` still describes creating new components with the v1 composition framework in `packages/components`. +Agent guidance now treats v0 and v1 as maintenance-only (D04, `R-V1-001`) and routes new components to the agentic +package. This PR corrected only that section's stale paths. -| Option | Effect | -| --------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------- | -| 1. Accept for this PR; run repeated trials in the external workflow (recommended) | Ships the routing and coverage gains now; the cost picture gets more precise later. | -| 2. Run three or more trials per case before the PR | Stronger evidence; costs about 50 more probe sessions per model. | -| 3. Add another client, such as Claude Code | Covers clients beyond Copilot CLI, the main client under D05. | +| Option | Effect | +| -------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------- | +| 1. Update `CONTRIBUTING.md` in this PR to direct new components to `packages/agentic/components` (recommended) | Human and agent guidance agree. | +| 2. Update it in a separate PR | Keeps this PR scoped to agent guidance; contributors keep conflicting guidance meanwhile. | +| 3. Leave it | Humans and agents follow different new-component policies. | ## Follow-ups outside this branch @@ -114,31 +61,42 @@ were no repeated trials, so the percentages are not statistically reliable. `packages/agentic/native-core/AGENTS.md` to [`references/agents/native/apple-office.md`](references/agents/native/apple-office.md). The checker will validate the link. -- **External efficacy workflow:** reuse the committed [evaluation fixtures](references/agents/evals/README.md) and the - local result-record shape. Keep holdouts out of instruction tuning. +- **External efficacy workflow:** reuse the committed [evaluation fixtures](references/agents/evals/README.md); keep the + five new holdouts out of instruction tuning; run repeated trials (R06). - **Publish:** push the branch and open the PR. The evidence below can go in the description. ## Evidence for the PR description -- **Root instructions:** root `AGENTS.md` went from 27.4 KB to 7.9 KB. Copilot CLI loads it twice from the repository +- **Shared references:** reusable guidance moved into [`references/agents/`](references/agents/README.md), with + standardized frontmatter, `R-` rule IDs, and `C-` evidence claims. Version-bound focus and accessibility observations + are in `evidence-reference` documents. No compatibility redirects remain. +- **Skills:** added `maintain-v0-v1-component`, `review-component`, and `implement-native-component`. The + `fix-security-issues` skill went from 1,042 to 371 words by moving its procedures into a reference. +- **Root instructions:** root `AGENTS.md` went from 27.4 KB to 8.0 KB. Copilot CLI loads it twice from the repository root, because `CLAUDE.md` imports it. -- **Safety extraction:** permissions moved with all 39 sentences preserved, and Apple/Office rules with all 28 - preserved, each confirmed by sentence-level comparison. -- **Checker:** `yarn check-agent-instructions` (15 tests) also enforces acyclic, at most three-deep prerequisites, - terminal compatibility redirects, one skill per routing row with consistent routing across routers, and root routing - for every skill. -- **Final validation:** a new root `yarn clean-lage-cache` script (`lage cache --clear`) followed by - `yarn lage buildci` ran and passed all 171 tasks with none skipped. `yarn change:check` and - `yarn lage test-links` also passed. +- **Safety extraction:** permissions (39 sentences), Apple/Office rules (28), and the security skill (60) moved with + every sentence preserved, confirmed by sentence-level comparison. The focus and accessibility evidence extraction was + checked the same way. +- **Checker:** `yarn check-agent-instructions`, with 15 tests, validates the following: + - frontmatter, links, and anchors; + - code-span paths, skills, and rule IDs; + - prerequisites: acyclic and at most three reads deep; + - routing: one skill per row, consistent across routers, and every skill routed from the root; + - evaluation fixtures (15 cases: 10 development, 5 holdout). +- **Final validation:** `yarn clean-lage-cache` followed by `yarn lage buildci` passed all 171 tasks with none skipped; + `yarn change:check` and `yarn lage test-links` also passed. Pilot results, one run per case, read-only Copilot CLI 1.0.98 probes, baseline `a1fd5c65d`: -| Configuration | Expected skill reached | Assertions met | Input tokens | AI usage | -| -------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------- | ------------ | -------- | -| Claude Sonnet 5.5, medium; 10 cases; self-scored | Baseline 4/10, candidate 10/10 | Baseline 17/22, candidate 22/22 | −32% | −22% | -| GPT-6.1 Sol, high, long context; 8 development cases; independently scored | Baseline 1/1 available (9 skills absent); candidate 9/10, then 10/10 after the fix | Baseline 16 + 4 partial of 20, candidate 19 + 1 partial, then 20/20 after the fix | −24% | −9% | -| GPT-6.1 Sol, high, long context; 5 holdouts; independently scored | Every available expected skill in both (3 of 5 baseline cases had no matching skill); candidate 5/5 | Baseline 10 + 1 partial + 1 failed of 12, candidate 12/12 | −17% | −2% | +| Configuration | Expected skill reached | Assertions met | Input tokens | AI usage | +| -------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------- | ------------------------------------------------------------ | ------------ | -------- | +| Claude Sonnet 5.5, medium; 10 cases; self-scored | Baseline 4/10, candidate 10/10 | Baseline 17/22, candidate 22/22 | −32% | −22% | +| GPT-6.1 Sol, high, long context; 8 development cases; independently scored | Baseline 1/1 available (9 skills absent); candidate 9/10, then 10/10 after a routing fix | Baseline 16 + 4 partial of 20, candidate 20/20 after the fix | −24% | −9% | +| GPT-6.1 Sol, high, long context; 5 original holdouts; independently scored | Baseline 2/2 available (3 skills absent); candidate 5/5 | Baseline 10 + 1 partial + 1 failed of 12, candidate 12/12 | −17% | −2% | + +Changes informed by the GPT-6.1 Sol holdouts were rechecked on the same configuration: + +- The iOS helper plan stopped reading desktop documents; input tokens fell from 187K to 33K and AI usage by 65%. +- The legacy Callout plan routed directly to native work; input tokens fell 16% and AI usage 20%. -GPT-6.1 Sol found one development routing defect: agentic plan-only requests went to contract authoring and skipped -the authoring recipes. Commit `5e183ce7f` fixed it. On recheck, the case reached the authoring skill, read all five -required files, and passed both assertions, at 22% more input tokens than its pre-fix run. +Both cases are now development cases, replaced by two new holdouts. From 5a64b7f29560e1ade297a48cc68b53457b29649d Mon Sep 17 00:00:00 2001 From: Jason Morse Date: Thu, 15 Oct 2026 00:09:14 -0700 Subject: [PATCH 13/14] Route new components to the agentic package in contributor docs Update CONTRIBUTING.md so new components use packages/agentic/components, describe the contract, source layout, tests, and final validation, keep v0 and v1 FluentTester/E2E guidance for maintenance, and send new native code to native-core. Remove the completed agent-improvements-plan.md. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- CONTRIBUTING.md | 85 +++++++++++++++++++++---------- agent-improvements-plan.md | 102 ------------------------------------- 2 files changed, 57 insertions(+), 130 deletions(-) delete mode 100644 agent-improvements-plan.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 64452efaa6..ac4c660f1d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -13,11 +13,18 @@ This guide assumes you: There are some specific quirks to this repository that one should be familiar with. -### Old framework vs new framework (Compose V2) +### Component generations -There's [documentation](./packages/components/README.md) for how to author a control, specifically with the two frameworks [foundation-compose](./packages/deprecated/foundation-compose/README.md) and [foundation-composable](./packages/deprecated/foundation-composable/README.md). Since that documentation was written, we have written a new component framework (PR's [here](https://github.com/microsoft/fluentui-react-native/pull/335) and [here](https://github.com/microsoft/fluentui-react-native/pull/400)) at `packages/experimental/framework` that is simpler/easier to use, and is the new preferred way to create components. Components that use this new framework are located at `packages/experimental` and are copies of the controls located at `packages/components` which still uses the old frameworks. Any new components should use the new framework. The old documentation is still good to read, specifically for using foundation-compose, as a lot of it applies to the new component. +The repository contains three generations of components. Identify the generation from a component's imports and +structure before changing it; the [classification guide](./references/agents/components/classification.md) describes +how. -There are two directories where components exist, the normal `packages/components` and `packages/experimental`. In general, the `experimental` directory is where components and frameworks we are iterating on get placed. +- **v0:** components built on [foundation-compose](./packages/deprecated/foundation-compose/README.md). These are + maintenance-only. +- **v1:** components built on `@fluentui-react-native/framework` `compose` or `compressible`, usually exported with a + `V1` suffix from `packages/components` and `packages/experimental`. These are maintenance-only. +- **Agentic:** components in [`packages/agentic/components`](./packages/agentic/components), built from reviewed + contracts with framework-base patterns and design tokens. **All new components use this package.** ## Understanding the framework @@ -42,43 +49,65 @@ Tokens help us achieve simpler customization for complex higher order components ## Creating a new component -This section covers creating and adding a new component package to FluentUI React Native's monorepo. If you are instead working on an existing component and adding a native module, skip to the next two sections. +New components go in [`packages/agentic/components`](./packages/agentic/components): a styled higher-order component in +`src/components/`, or an unstyled building block in `src/primitives/`. Do not create new v0 or v1 component +packages under `packages/components` or `packages/experimental`; those generations accept maintenance only. Read the +package's [contributor instructions](./packages/agentic/components/AGENTS.md) and use +[Button](./packages/agentic/components/src/components/button) and +[Icon](./packages/agentic/components/src/primitives/icon) as the canonical examples. -Most components should use the compose framework as it offers the comprehensive set of patterns like tokens and slots, but if you're creating a simple component that doesn't require those patterns, there's a lighter pattern called [stagedComponent](./packages/framework-base/src/component-patterns/phased.ts). The stagedComponent pattern splits up the render function into two stages. Stage 1 handles building props and hook calls (best to separate the hook calls from the render tree since they rely on call order). Stage 2 returns the actual element tree, any conditional branching should happen here (Icon is a good example of using stagedCompoenent). +### Define the contract -1. Create a new directory in of these two locations, depending on your component: - - `fluentui-react-native/packages/components/` - - `fluentui-react-native/packages/experimental/` +1. A higher-order component starts with a reviewed contract: `SPEC.md` plus `spec/source.json`, `spec/tokens.yaml`, + `spec/accessibility.md`, `spec/interaction.md`, and `spec/usage.md`. Follow + [SPEC-SOURCE.md](./packages/agentic/components/SPEC-SOURCE.md) for provenance, review state, and source adapters. +1. A primitive instead keeps a colocated `CONTRACT.md` that describes its public API and renderer branches. +1. Run `yarn workspace @fluentui-react-native/components check:spec-contracts` to validate the contract. -1. Copy the files from the top level of another component (A good example of this is the Shimmer). Make sure to empty the changelog file, and modify the name of the component where appropriate. -1. Update your `package.json` file. This defines the name, fields, and dependencies of the npm package that will be published for this component. -1. As a pattern, we prefix `experimental` components with the word "experimental". For example, we have `@fluentui-react-native/experimental-button`. +### Add the source code -**Additionally, we require integrating your new component/test page with our E2E testing infrastructure. Whenever a new component/test page is created, it should map to new tests in /apps/E2E/src. This is to create a sophisticated, in-depth testing infrastructure that ensures that we don't regress our component library in the future; giving our partners and customers the best experience possible. Please see [Authoring an E2E Test](https://github.com/microsoft/fluentui-react-native/tree/main/apps/E2E#authoring-e2e-test).** +A higher-order component uses these files, in dependency order: -Reach out to Samuel Freiberg with any questions related to E2E testing. +1. `.types.ts`: public props, slots, and resolved state. +1. `use.ts`: defaults, derived state, interaction, accessibility, and slot construction. +1. `.styles.ts` and `useStyles.ts`: token-based styles and slot style application. +1. `render.tsx`: pure slot rendering. Put `/** @jsxImportSource @fluentui-react-native/framework-base */` on + the first line of files that render slots; see `packages/framework-base/src/component-patterns/README.md`. +1. `.ts`: assembly and `displayName`. -### Adding the JS source code to your component +Expose the native root ref through the React 19 `ref` prop rather than `forwardRef`. Export the component and its public +types explicitly from `src/index.ts`, or from `src/primitives/index.ts` for a primitive; do not use `export *`. -1. Create a `src/` subdirectory in your component directory with a minimum of two files (listed below). You may optionally choose to subdivide your code however you wish; there are plenty of examples in the other components of FluentUI React Native. - 1. `index.ts` - - This is the file listed as `main` inside your package.json and simply exports other files. - 1. `.tsx` - This is the file that will actually define your function component, and compose it into a higher order component with slots, theming, and design tokens. - Note that we need the comment `/** @jsxImportSource @fluentui-react-native/framework-base */` as the first line of this file. This opts the file into the custom JSX runtime that handles slot rendering; an explanation can be found in `packages/framework-base/src/component-patterns/README.md`. Packages using this pragma must include `@fluentui-react-native/framework-base` in their `dependencies`, because compiled output imports the runtime. - 1. `..tsx` (Optional) - - Optional extra files to subdivide your code however you see fit. You can also add platform specific files as you see fit. +### Test your component -### Adding a new test for your component to the test app +1. Colocate runtime tests, type tests, and snapshots with the implementation. +1. Add Storybook stories next to the component. Desktop behavior tests are `wdio` functions in the stories and run on + macOS, Windows, and Win32 through the [agentic Storybook app](./apps/storybook/README.md). +1. Run the package's declared scripts, such as `yarn workspace @fluentui-react-native/components test`, plus `build`, + `lint`, and `format`. +1. Before opening a pull request, run `yarn clean-lage-cache` and then `yarn lage buildci` from the repository root. -1. In FluentTester (our test app), we'll need to add an test component. The test components are located under `apps/tester-core/src/TestComponents`. -1. Make a subdirectory for your new component at this path, as well as adding an entry for your test in `apps/tester-core/src/testPages.ts`, whose `platforms` array lists the platforms where it appears -1. Create your tests in this new subdirectory. Pattern matching off an existing control's tests will greatly help. -1. If your component contains a native module, you will also need to add the path to your component's `podspec` (that we created earlier) in your test app's Podfile. This extra step is due to the fact that FluentTester has separate platform test apps that each share a common JS package. The react native community CLI does not support [autolinking transitive dependencies](https://github.com/react-native-community/cli/issues/1347), so we need to add it manually here. -1. Add your new module as a dependency in `apps/fluent-tester/package.json` and run `yarn && yarn build` from the root folder. - 1. If your component has native apple code, run `pod install` from the corresponding macOS or iOS test app folders (`apps/fluent-tester/macos` or `apps/fluent-tester/ios` respectively). -1. Run the test app and you should see your new test! +### Maintaining v0 and v1 components + +Fixes to existing v0 and v1 components keep their current framework, public API, and tests: + +1. FluentTester test pages are located under `apps/tester-core/src/TestComponents`. Register a page in + `apps/tester-core/src/testPages.ts`, whose `platforms` array lists the platforms where it appears. +1. End-to-end tests live in `apps/E2E/src/`. See + [Authoring an E2E Test](https://github.com/microsoft/fluentui-react-native/tree/main/apps/E2E#authoring-e2e-test), + and reach out to Samuel Freiberg with questions about E2E testing. +1. If a component contains a native module, add its `podspec` path to the FluentTester Podfile. The React Native + community CLI does not support + [autolinking transitive dependencies](https://github.com/react-native-community/cli/issues/1347). Then run + `yarn && yarn build` from the root, and run `pod install` from `apps/fluent-tester/macos` or `apps/fluent-tester/ios` + for native Apple code. ## Adding native code to your new component +New modern native components and TurboModules belong in +[`@fluentui-react-native/native-core`](./packages/agentic/native-core/README.md) rather than a new standalone package. +The sections below describe the native wrappers used by existing v0 and v1 packages. + Through the power of [Native Modules](https://reactnative.dev/docs/turbo-native-modules-introduction), we are able to create components that are comprised of native platform code, rather than JS. This is particularly useful if you want platform specific behavior, or if you want a component that feels much more aligned to it's specific platform. The downside is you must implement the Native module for every platform you wish to support. It's worth investigating whether you truly need a native module, or if a more cross platform JS implementation is the better approach. There are a few caveats to know of adding a native module to a FluentUI React Native component: diff --git a/agent-improvements-plan.md b/agent-improvements-plan.md deleted file mode 100644 index 91c54dd024..0000000000 --- a/agent-improvements-plan.md +++ /dev/null @@ -1,102 +0,0 @@ -# Agent instruction improvements: remaining decisions - -**Status:** All previous decisions (D01-D12 and R01-R07) are implemented on `user/jasonvmo/instruction-rework`. This -file now lists only new decisions and follow-ups. Delete it before submitting the PR. - -## Decisions needing your input - -Edit **Answer** and **Status** for each item. - -### R08: Where the efficiency skill should live - -**Status:** Open -**Answer:** - -An independent assessment compared an in-repository skill with keeping it in your external workflow. Key tradeoffs: - -| Dimension | In-repository skill | External workflow | -| -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------- | -| Reach | Every contributor sees it, but few would run it; the fixtures are already public. | Owner only; others can still use the committed fixtures. | -| Always-on cost | Every Copilot CLI session pays an estimated 100-200 more tokens for another skill description and root routing row; `CLAUDE.md` doubles the root share. | None in this repository. | -| Misrouting | An "efficiency" or "optimize" description could compete with `agentic-component-optimization`. | None. | -| Privacy | Results written in the repository could be committed by accident; no repository-level results directory is ignored. | Already separates raw logs from curated summaries. | -| Telemetry | Cannot use private session logs, so it covers only the fixture half. | Has the cross-session telemetry the loop needs. | -| Portability | A committed harness would tie public maintenance to one client version's usage-event format. | Owner absorbs client drift privately. | -| Governance | `@microsoft/furn-team` would own code that drives LLM sessions. | Instruction changes still land through reviewed PRs either way. | -| Repository fit | Strong: rule IDs, fixtures, and the checker are here. | Cites rule IDs by reference; CI still validates the fixtures. | -| Permissions | Nested `copilot -p` and worktree commands fall outside the permission hook, so contributors would face prompts or broad grants. | Runs under your own approvals. | -| Reuse | This repository only. | Works across repositories. | - -The assessment flagged a holdout-contamination risk: probe agents can read public fixtures. Checking the recorded runs -found that no probe in either pilot read or searched `references/agents/evals/`. - -| Option | Effect | -| -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------- | -| 1. Keep the skill external, and add a short "Running an evaluation" procedure to `references/agents/evals/README.md` plus a checker guard rejecting stray files under `references/agents/evals/` (recommended) | Others can reproduce PR claims and result files cannot be committed by accident; no skill, runner, or routing cost. | -| 2. Keep everything external, with only the fixtures committed (current state) | Smallest footprint; the procedure stays private. | -| 3. Commit a TypeScript runner script without a skill | Reproducible by others; adds client-specific public maintenance and permission-hook entries. | -| 4. Add an in-repository skill that runs fixtures, with telemetry kept external | Discoverable; carries every in-repository cost for a workflow few contributors run. | - -Revisit if furn-team adopts evaluation evidence in PR review, a shared evaluation framework is chosen, the method is -proven across repeated multi-client trials, or clients support explicit-only skills hidden from the skill list. - -### R09: Contributor new-component guidance - -**Status:** Open -**Answer:** - -`CONTRIBUTING.md` still describes creating new components with the v1 composition framework in `packages/components`. -Agent guidance now treats v0 and v1 as maintenance-only (D04, `R-V1-001`) and routes new components to the agentic -package. This PR corrected only that section's stale paths. - -| Option | Effect | -| -------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------- | -| 1. Update `CONTRIBUTING.md` in this PR to direct new components to `packages/agentic/components` (recommended) | Human and agent guidance agree. | -| 2. Update it in a separate PR | Keeps this PR scoped to agent guidance; contributors keep conflicting guidance meanwhile. | -| 3. Leave it | Humans and agents follow different new-component policies. | - -## Follow-ups outside this branch - -- **`native-lib2`:** after this PR merges and that branch rebases without commit `246d33668`, add a route from - `packages/agentic/native-core/AGENTS.md` to - [`references/agents/native/apple-office.md`](references/agents/native/apple-office.md). The checker will validate the - link. -- **External efficacy workflow:** reuse the committed [evaluation fixtures](references/agents/evals/README.md); keep the - five new holdouts out of instruction tuning; run repeated trials (R06). -- **Publish:** push the branch and open the PR. The evidence below can go in the description. - -## Evidence for the PR description - -- **Shared references:** reusable guidance moved into [`references/agents/`](references/agents/README.md), with - standardized frontmatter, `R-` rule IDs, and `C-` evidence claims. Version-bound focus and accessibility observations - are in `evidence-reference` documents. No compatibility redirects remain. -- **Skills:** added `maintain-v0-v1-component`, `review-component`, and `implement-native-component`. The - `fix-security-issues` skill went from 1,042 to 371 words by moving its procedures into a reference. -- **Root instructions:** root `AGENTS.md` went from 27.4 KB to 8.0 KB. Copilot CLI loads it twice from the repository - root, because `CLAUDE.md` imports it. -- **Safety extraction:** permissions (39 sentences), Apple/Office rules (28), and the security skill (60) moved with - every sentence preserved, confirmed by sentence-level comparison. The focus and accessibility evidence extraction was - checked the same way. -- **Checker:** `yarn check-agent-instructions`, with 15 tests, validates the following: - - frontmatter, links, and anchors; - - code-span paths, skills, and rule IDs; - - prerequisites: acyclic and at most three reads deep; - - routing: one skill per row, consistent across routers, and every skill routed from the root; - - evaluation fixtures (15 cases: 10 development, 5 holdout). -- **Final validation:** `yarn clean-lage-cache` followed by `yarn lage buildci` passed all 171 tasks with none skipped; - `yarn change:check` and `yarn lage test-links` also passed. - -Pilot results, one run per case, read-only Copilot CLI 1.0.98 probes, baseline `a1fd5c65d`: - -| Configuration | Expected skill reached | Assertions met | Input tokens | AI usage | -| -------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------- | ------------------------------------------------------------ | ------------ | -------- | -| Claude Sonnet 5.5, medium; 10 cases; self-scored | Baseline 4/10, candidate 10/10 | Baseline 17/22, candidate 22/22 | −32% | −22% | -| GPT-6.1 Sol, high, long context; 8 development cases; independently scored | Baseline 1/1 available (9 skills absent); candidate 9/10, then 10/10 after a routing fix | Baseline 16 + 4 partial of 20, candidate 20/20 after the fix | −24% | −9% | -| GPT-6.1 Sol, high, long context; 5 original holdouts; independently scored | Baseline 2/2 available (3 skills absent); candidate 5/5 | Baseline 10 + 1 partial + 1 failed of 12, candidate 12/12 | −17% | −2% | - -Changes informed by the GPT-6.1 Sol holdouts were rechecked on the same configuration: - -- The iOS helper plan stopped reading desktop documents; input tokens fell from 187K to 33K and AI usage by 65%. -- The legacy Callout plan routed directly to native work; input tokens fell 16% and AI usage 20%. - -Both cases are now development cases, replaced by two new holdouts. From 680d6ffe3b069d01a1bbe593af160dcf984f1113 Mon Sep 17 00:00:00 2001 From: Jason Morse Date: Thu, 15 Oct 2026 00:43:11 -0700 Subject: [PATCH 14/14] Strip heading tags without a regex sanitizer pattern CodeQL flagged the one-pass `<[^>]+>` replacement in the agent-instruction checker's slug helper as incomplete multi-character sanitization. Remove inline tags with a scanner that tracks nesting, and cover nested markup in the slug test. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- scripts/src/tasks/agentInstructions.test.ts | 1 + scripts/src/tasks/agentInstructions.ts | 20 +++++++++++++++++--- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/scripts/src/tasks/agentInstructions.test.ts b/scripts/src/tasks/agentInstructions.test.ts index 4cdb653097..8fabc93ace 100644 --- a/scripts/src/tasks/agentInstructions.test.ts +++ b/scripts/src/tasks/agentInstructions.test.ts @@ -86,6 +86,7 @@ describe('agent instruction helpers', () => { it('creates GitHub-style anchors', () => { assert.equal(slugify('4. Native obligations'), '4-native-obligations'); assert.equal(slugify('`includesGeneratedCode` is not Windows-scoped'), 'includesgeneratedcode-is-not-windows-scoped'); + assert.equal(slugify('Use x and <b>bold'), 'use-x-and-bold'); assert.deepEqual([...collectAnchors('# A\n## A\n```\n# ignored\n```\n\n')], ['a', 'a-1', 'custom']); }); diff --git a/scripts/src/tasks/agentInstructions.ts b/scripts/src/tasks/agentInstructions.ts index 0c0fdfb645..f2a31987bf 100644 --- a/scripts/src/tasks/agentInstructions.ts +++ b/scripts/src/tasks/agentInstructions.ts @@ -107,11 +107,25 @@ function isGitIgnored(root: string, rel: string): boolean { } } +/** Removes inline HTML tags from heading text by scanning, so nested or malformed markup cannot survive. */ +function removeTags(text: string): string { + let result = ''; + let depth = 0; + for (const character of text) { + if (character === '<') { + depth++; + } else if (character === '>' && depth > 0) { + depth--; + } else if (depth === 0) { + result += character; + } + } + return result; +} + /** GitHub-style heading anchor. */ export function slugify(heading: string): string { - return heading - .replace(/\[([^\]]*)\]\([^)]*\)/g, '$1') - .replace(/<[^>]+>/g, '') + return removeTags(heading.replace(/\[([^\]]*)\]\([^)]*\)/g, '$1')) .trim() .toLowerCase() .replace(/[^\p{L}\p{N}\s_-]/gu, '')