# Fr Mvvm Contract

> Create or adapt ACDD Flutter projects across Android, iOS, macOS, Web, Windows, and Linux; audit application identity, Android/iOS icons, developer signing evidence, minimum platform versions, and push configuration; create, validate, or evolve FlowR contracts, typed Pages, cross-page modules, navigation shells, endpoint Behaviors, and frontend interaction Flows; audit or repair project-configured Figma-bound screens, including runtime text, formatting, data binding, and layout; generate, query, package, or synchronize BFF contracts; and assess Flutter packaging optimizations when explicitly requested. Use for acdd_scaffold, existing-project adaptation, application-information or release-readiness checks, contract-first FlowR work, typed-route or bottom-navigation refactors, BFF frontend interaction logic, changes to Views whose contracts declare Figma, Figma fidelity repair, BFF inventory or delivery, and explicit Flutter build optimization requests.

- Skill: `hu-wentao/fr-mvvm-contract` (Agent Skill, multi-file: 92 files)
- Install (CLI): `npx skillmds@latest add hu-wentao/fr-mvvm-contract`
- Raw SKILL.md: https://api.skillmd.com/api/skills/hu-wentao/fr-mvvm-contract/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Design & Media
- Author: Hu-Wentao (https://skillmd.com/u/hu-wentao)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/hu-wentao/fr-mvvm-contract

---


# FR MVVM Contract

Run every bundled Python entrypoint with `uv run --script <path>`. Add or
update its PEP 723 dependencies with `uv add --script <path> <dependency>`;
never invoke a bundled script with `python`, `python3`, or `uv run python`.

## Mode Selection

- For a new project or `acdd_scaffold`, read
  `references/acdd_scaffold.md` and use `scripts/acdd_scaffold.py`. Do not run
  the project profile resolver before the target project exists.
- For an existing Flutter project that must adopt the standard scaffold
  structure, run:

```bash
uv run --script <skill-root>/scripts/resolve.py --task adapt_project
```

  Follow the resolved inventory, mapping, approval, migration, and validation
  workflow. Treat this skill's `assets/acdd_scaffold/` templates and
  `references/acdd_scaffold.md` boundaries as the standard. Never run
  `acdd_scaffold.py --apply` against the existing project.
- To check what application-level configuration an existing Flutter project
  still lacks compared with a common complete Android/iOS app, run:

```bash
uv run --script <skill-root>/scripts/resolve.py --task check_app_info
```

  Read the resolved `references/check_app_info.md` instructions and report the
  evidence for every required item. Treat developer accounts and certificates
  that cannot be proven from repository files as externally unverified; do not
  edit identifiers, signing, platform targets, icons, or push configuration
  during the check.
- For contract work in an existing project, run:

```bash
uv run --script <skill-root>/scripts/resolve.py --task <gen_page|gen_component|extract_shared_ui|validate|validate_routes|validate_navigation_shell|audit_figma_fidelity|audit_figma_data|refresh|package_bff|generate_openapi>
```

  Read the resolved instructions once per `instructions_id`.
- For backend OpenAPI-to-Retrofit generation, run the resolver with task
  `generate_openapi`, read `references/generate_openapi.md`, and use
  `scripts/openapi_to_retrofit.py`. Project-specific generic request and
  response wrappers belong in `.agents/skills-config/fr-mvvm-contract/config.yaml`;
  never infer or hard-code their non-generic fields in the reusable skill.
- For a screen whose implementation must be audited or repaired against Figma,
  resolve `audit_figma_fidelity`, read its resolved project profile, and run
  the declared audit command before and after changes. Discover screen
  participation from each primary `.c.dart` contract's `Figma Fidelity:`
  disposition; never maintain a second page registry. Keep the viewport,
  asset-lock path, and regression-test owner in `.c.dart`. Keep only exact
  export identities, repository paths, and hashes in the referenced asset
  lock under `.agents/skills-config/fr-mvvm-contract/`; never hard-code one
  product screen in this reusable skill. Before implementing or approving the
  screen, read `references/figma_flutter_design_to_code.md` to constrain the
  mandatory Figma MCP acquisition skill to evidence collection, then follow
  `references/audit_figma_fidelity.md`, run its SVG scan and safe normalization
  pipeline for exported SVG assets, and preserve icon placement-box versus
  visual-glyph dimensions and exact typography. The SVG pipeline never
  auto-repairs geometry, and a structural audit pass is not visual approval.
- For a macOS window derived from a Figma Frame, establish whether the Frame
  specifies the Flutter content viewport or the visible native window frame
  before changing `MainFlutterWindow.swift`. Preserve the content viewport
  with `setContentSize` and `contentMinSize`/`contentMaxSize`. When the Frame
  explicitly specifies the visible macOS window, use `setFrame`, `minSize`,
  and `maxSize` instead; content-size constraints add the title-bar height and
  make the outer window too large. Keep native title-bar behavior unless the
  design expressly requires a borderless window, then verify with
  `fvm flutter build macos --debug`.
- Before editing any Figma-bound Page or component, read
  `references/figma_release_management.md` and run
  `scripts/resolve_figma_release.py` for its `.c.dart`. When project config
  declares releases, treat `active_release` as explicit authority; never infer
  the latest release by sorting names. A `stale` result requires lightweight
  structure discovery in the active file and touched-contract migration before
  implementation. Migrate automatically only when route identity, business
  responsibility, navigation context, primary Frame, and state Frames identify
  one unambiguous successor. Block on missing, split, merged, or ambiguous
  successors. Honor only a reasoned `Figma Release Override`; never create one
  automatically to silence drift.
- Treat copy, formatter, runtime-data binding, and layout-constraint edits as
  Figma-bound work whenever the owning `.c.dart` declares `Figma:`. Resolve
  `audit_figma_fidelity` and use
  `references/figma_flutter_design_to_code.md` before choosing a runtime text
  layout. Do not turn the width of one Figma sample value into a universal
  runtime constraint.
- For two or more destinations that share a persistent bottom navigation,
  resolve `validate_navigation_shell`, read
  `references/navigation-shells.md`, and run the declared validator before
  changing Pages or routes. Classify the shared shell before auditing each
  destination as a standalone screen.
- Do not propose command-line packaging or dependency-download optimizations
  during ordinary project creation, adaptation, validation, or repair. Only
  when the user explicitly requests build or packaging optimization, read
  `references/optional-build-packaging-optimizations.md`, present the relevant
  options, and obtain the authorization required by the target repository
  before applying them.

## Source-First Layout

Choose ownership and directory by reuse scope:

- Use feature directories only for grouping. Put every Page/Component module
  in its own basename-matching leaf directory; never place different module
  shells or `*.c.dart` contracts in the same directory.
- Put a route-owned component under
  `lib/app/<feature>/<component-name>/` beside its optional page adapter.
- Put a component reused by multiple routes under
  `lib/components/<component-name>/`. Do not duplicate it under each route.
- Keep every implementation artifact of a cross-route component in its own
  `lib/components/<component-name>/` directory. Do not place or leave
  module-specific files in `lib/core/`.
- Keep a Widget used only inside one component private in that component's
  `.v.dart`.
- Put a plain Widget reused inside one route under
  `lib/app/<route-segment>/widgets/`.
- Put a plain Widget reused by multiple routes under `lib/widgets/`.
- Do not give a plain presentation Widget a contract, Provider, Event, or
  ViewModel. Promote it to a component when it encapsulates a reusable business
  capability, app/page state integration, independent state, API, Event, or
  ViewModel responsibility.
- In an existing project with an established equivalent root, preserve that
  root unless the explicit `adapt_project` workflow approves a move. Preserving
  a root never permits several modules to share one leaf directory.

## Shared UI Discovery And Extension

Before modifying a Page or route-owned component, discover reusable UI in this
order:

1. Search `lib/components/` for a component whose `Capabilities:` matches the
   requested capability.
2. Search `lib/widgets/` for a shared Widget module whose `Capabilities:`
   matches the requested capability.
3. Reuse an existing public View or public Widget when it satisfies the
   requirement.
4. When an existing module owns the capability but has no suitable public
   entry, add a semantic public View or Widget in that same module and update
   its public list.
5. Create a new component only when no component owns the required stateful or
   business capability. Create a Widget module only for a cross-page pure
   presentation need.

Run `scripts/discover_ui_reuse.py --project-root <project-root> --capability
"<requested capability>"` before this decision. Treat its output as a catalog,
not an automatic ownership decision: resolve close semantic matches from the
module contract before creating a new module.

## Extract Shared UI

Use `extract_shared_ui` when an existing route-owned View contains a UI entry
that must be reused by another route. Resolve this task, read
`references/extract_shared_ui.md`, and run `extract_shared_ui.py` in dry-run
mode before changing Dart sources. The tool produces a reviewable ownership
and file-migration manifest; `--apply` is permitted only after the manifest is
approved.

Classify by ownership, not by similar appearance:

- A presentation entry with inputs and callbacks only is a shared Widget under
  `lib/widgets/`; it has no contract, Provider, Event, or ViewModel.
- An entry with independent state, API, Event, or ViewModel is a feature
  component under `lib/components/<name>/` and follows the complete
  `gen_component` contract workflow. Do not mechanically move it with the
  Widget extractor.
- Similar widgets with different visual or interaction contracts stay
  separate until an approved design decision defines a common public API.

After extraction, add `Capabilities:` and `Public Widgets:` to the shared
Widget provider, update consumers' `Widget Tree:` entries, rerun reuse
discovery, and validate formatting, tests, and analysis.

`Capabilities:` and public API lists are owned by the provider module:

- A component contract (`lib/components/<name>/<name>.c.dart`) declares
  `Capabilities:` and `Public Views:`.
- A shared Widget module public entry under `lib/widgets/` declares
  `Capabilities:` and `Public Widgets:`.
- Every item in a public list uses a bracket reference and must be accessible
  from that module's public entry.

Consumer Page contracts do not declare `Components:` or `Shared Widgets:`.
Their `Widget Tree:` records the key public Views and Widgets actually composed
by the Page. A Page owns placement and composition; the provider module owns
the reusable UI and its interaction.
- Group tightly related Pages into a cross-page module under one feature
  directory, but keep every Page/Component module in a separate child leaf
  directory. The feature's basename-matching module export must document
  `Pages:` and `Page Data Flow:`. Read `references/validate_routes.md` before
  creating or refactoring that boundary.

## Persistent Navigation Shell Ownership

Before treating several Figma Frames or route roots as complete Pages, determine
whether they are destinations of one persistent navigation shell. Read
`references/navigation-shells.md` whenever two or more destinations share a
bottom bar.

- One shell owns the outer Scaffold, persistent top-region host, and one bottom
  navigation instance.
- Branch Page adapters retain route inputs and page-scoped Providers; branch
  Views contain content only.
- The bottom-navigation Widget accepts selection state and callbacks. It never
  imports branch Page adapters, performs navigation, or owns guarded-entry
  preflight.
- A root action with no preflight may navigate from the Shell View callback. A
  root action requiring permission, validation, API, business policy, or async
  preflight uses a Shell-owned component ViewModel and listener; branch,
  bottom-navigation, and target Page ViewModels do not own its gate.
- Use `StatefulShellRoute.indexedStack` by default for transition-free branch
  switching with retained independent branch state.
- Retained branch state does not keep query data fresh. When a shell branch
  owns a query API, detect an actual inactive-to-active transition and dispatch
  its established load/refresh Event into the retained page ViewModel. Do not
  duplicate the initial Startup Event, refresh on an ordinary rebuild, or
  recreate the Provider. Make overlapping refreshes latest-result-safe.
- A zero-duration Page transition or `NoTransitionPage` is not a persistent
  shell repair.
- Root fullscreen routes and overlays cover the shell through the root
  navigator/overlay; a branch ViewModel does not hide or recreate shell chrome.

A reusable feature component is one Dart library:

```text
order_content/
  order_content.dart
  order_content.c.dart
  order_content.v.dart
  order_content.vm.dart        # only for page- or component-owned state
  order_content.srv.dart       # SDK adapter over lib/api/gen
  order_content.bff.md         # required in BFF-JSON mode
  order_content.freezed.dart   # generated by Freezed
  order_content.g.dart         # generated by json_serializable
```

`order_content.dart` owns all imports and part declarations. Its `.c.dart`,
`.v.dart`, and optional `.vm.dart` files use `part of 'order_content.dart';`
and never declare imports. Write every contract section in `.c.dart` with
consecutive `///` documentation comments; do not use `/* ... */` contract
blocks.

Treat `.c.dart` as the source contract, not the View implementation file. Put
its contract comment before `part of`, followed only by stable contract types
such as Models, Events, DTOs, and business enums when they exist. Put every
public and private Widget/View declaration, constructor, `build`, `FrView`, and
visual composition in `.v.dart`. New drafts follow this layout; the reader may
still recognize one legacy View declared in `.c.dart` for migration.

A page is that component plus an optional, independent typed route adapter:

```text
order_content.page.dart
```

The adapter imports `order_content.dart`, extends `GoRouteData`, owns the
page-scoped `FrProvider`, and builds `OrderContentView` below it. It is never a
Widget or a `part` of the component. Deleting the adapter removes the
page-owned lifecycle; app-owned and stateless components remain independently
usable by another page, sheet, tab, or dialog.

## Naming And Ownership

- `XxxPage` lives in `xxx.page.dart`, extends `GoRouteData` with the generated
  `$XxxPage` mixin, owns route inputs as constructor fields, and expands them
  into the page ViewModel factory and/or ordinary `XxxView` fields. It owns the
  page-scoped Provider and dispatches a startup Event only when the contract
  declares `Startup Event: [XxxStarted]`. It is not a Widget.
- Public `*View` entries live in `.v.dart` and are listed authoritatively under
  `Public Views:` in `.c.dart`. A component may expose multiple semantic Views.
  Each consumes its upstream page/app state or ordinary inputs and does not
  create a Provider by default.
- For every newly generated public Widget carrying `@FrAcddPage`, read
  `references/widget-preview.md` and put a fully configured Flutter SDK
  `@Preview(...)` on its public constructor. Derive `name` from `Figma.Frame`,
  `group` from the `@FrAcddPage` namespace, and `size` from the authoritative
  viewport; require a public wrapper that supplies its preview runtime context.
- `XxxViewModel extends FrBlocViewModel<XxxEvent, XxxModel>` lives in
  `.vm.dart` only when this module owns page- or component-scoped state; all
  external writes use `add(event)`.
- Component fields, models, DTOs, Events, BFF/service declarations, and the
  component contract belong to the component library.
- Name component state `XxxModel`, BFF request and response boundaries
  `XxxBffReq` and `XxxBffRsp`, and BFF-only nested data `XxxDto`. A project
  request-data-envelope profile may explicitly allow a root `XxxRequestDto`
  in place of `XxxBffReq`.
- A component Service imports the concrete SDK from `lib/api/gen` and calls it
  directly. Permit a semantic `typedef` for an SDK request type when the
  ViewModel constructs that request. Keep response signatures in their original
  generated SDK form by default. Every alias must preserve the exact SDK type,
  fields, generics, and serialization shape.
- When a frontend `BFF-UI-API` entry has the same HTTP method and path as a
  backend-owned business API, it is the same direct backend boundary rather
  than a second UI API. Its `XxxBffReq` reference must be an exact `typedef` of
  the generated SDK request payload; never declare a replacement Freezed DTO
  or copy, add, remove, or rename request fields. A multi-call UI aggregate must
  use a distinct approved UI boundary, or `BFF-UI-API: -` when it is local
  orchestration with no standalone UI HTTP endpoint.
- Assign interaction authority before declaring Events:
  - Keep BuildContext, unconditional known typed Page navigation, overlays,
    controllers, and other presentation-only operations in the View callback
    when they do not change durable/business/API state. Such operations normally
    need no Event and no `Interactions:` Flow. Common examples are
    non-exhaustive; do not add exhaustive operation syntax validation.
  - Use a Bloc Event and document an `Interactions:` Flow only when the
    ViewModel owns model/business changes, API or service work, validation,
    guard/concurrency policy, retry/recovery, or an observable state outcome.
  - Treat picker, URL, share, and clipboard actions as boundary cases. Keep the
    platform invocation in the View when presentation-only; move decisions and
    results through the ViewModel when permissions, validation, persistence,
    API work, business policy, or model changes are involved.
  - When Page entry requires permission, validation, API, business policy, or
    asynchronous/concurrent preflight, the click expresses intent only. Use
    `Trigger -> Event -> ViewModel preflight -> observable approved/blocked
    outcome + nullable semantic navigation signal -> View FrListener/FrConsumer
    -> typed Page navigation`. Inject gateways into the ViewModel; never await a
    gate in a StatefulWidget and navigate directly afterward. Default to
    `ignore-while-active`. Emit each declared phase atomically through one
    direct `emit(state.copyWith(...))`: Pending includes the signal reset,
    approved Success includes both the real approved outcome and exact signal,
    and blocked/exception outcomes never set the signal.
  - Do not generate Intent or callback-output protocols as a second state
    channel. Ordinary reusable Widgets may accept input callbacks.
- For business/API-result navigation, or a `Uses: local` Flow whose ViewModel
  owns a real state/business decision, use
  `Navigation: view-listener-on-success [Model].field = Enum.member`. Give each
  nullable semantic enum signal exactly one owning Flow. Reset it to `null` in
  Pending State, set only the exact member in Success State, and never assign
  its `field:` named argument from Failure, another Flow/handler, or undeclared
  ViewModel code. A local Flow's separate Success outcome must not be an obvious
  `field = state.field` no-op. A View `FrListener`/`FrConsumer` binds the exact
  ViewModel and Model generic types and uses only an exact transition parent,
  an exact equality early-return guard, or one exact transition-and-member `&&`
  condition before navigating inside the exact enum-member braced branch.
  The ViewModel must not own BuildContext, router types, Page objects, or router
  calls. Retain `Navigation: none` when no navigation follows. Direct
  presentation routing remains a View callback with no Event or Flow only when
  entry is unconditional.
- Put a Provider at the state owner's lifecycle and at the lowest common
  ancestor of all consumers:
  - `app-owned [AppViewModel]`: the root `AppProviders` owns it; the component
    consumes it directly and declares no local VM, Event, Model, or Provider.
  - `page-owned [XxxViewModel]`: `xxx.page.dart` creates it and dispatches the
    optional declared Startup Event; every Widget in that page subtree can
    consume it.
  - `none`: the component uses inputs/callbacks or Widget-local ephemeral
    state and declares no VM, Event, Model, or Provider.
  - `component-owned [XxxViewModel]`: explicit opt-in only for an independently
    embeddable compound component that owns a distinct lifecycle and has
    multiple descendants sharing its state.
- Cross-route shared components must not bind to a route-specific Page
  ViewModel. Consume app-owned state when the state is genuinely global;
  otherwise accept ordinary inputs/callbacks.

## Page Route Inputs And Component Inputs

- Do not declare `XxxPageArgs`. `XxxPage` is the single typed route input
  model; declare path, query, and `$extra` inputs as its constructor fields.
- The component library (`xxx.dart` and its parts) must never reference its own
  sibling `XxxPage`, generated route mixin, `GoRouterState`, or import its own
  `xxx.page.dart`. It may import another target route's `.page.dart` to use its
  generated Page helper or target-owned PageExtra for typed navigation.
- `XxxPage.build` consumes every route field in its ViewModel factory and/or
  ordinary named fields on `XxxView`.
- Do not declare component input wrappers named `XxxArgs` or `XxxConfig`.
- Pass only the route fields needed by `XxxViewModel` from the Page's Provider
  factory. Do not pass the Page route object into the component library.
- Use a route-owned `XxxPageExtra` only when several non-URL values must travel
  together through `$extra`. Declare it directly in the target `xxx.page.dart`,
  never in an independent model file. Treat it only as a route transport model,
  not domain data or ViewModel state. The target Page must expand it into
  ordinary View fields. Declare it with `@FrAcddFreezedJSON`, a redirecting
  `const factory`, and `fromJson`; add the page `.freezed.dart` part and retain
  its shared generated `.page.g.dart` part. Configure one application-owned
  `GoRouter.extraCodec` with tagged encode/decode cases for every PageExtra.
  Preserve the approved PageExtra field contract during migration; serialization
  requirements do not authorize changing field names, values, ownership, or
  lifecycle. Read `references/typed-routing.md` for the required shape.

| Type | File | Consumers |
|---|---|---|
| `OrderContentPage.orderId` | `order_content.page.dart` | Path input and route system |
| `OrderContentPage.entryPoint` | `order_content.page.dart` | Query input and route system |
| `OrderContentView.orderId` | `order_content.v.dart` | View only, when rendering needs it |
| `OrderContentView.entryPoint` | `order_content.v.dart` | View only, when rendering needs it |

Use this standard conversion shape:

```dart
@TypedGoRoute<OrderContentPage>(path: '/orders/:orderId')
class OrderContentPage extends GoRouteData with $OrderContentPage {
  const OrderContentPage({required this.orderId, required this.entryPoint});

  final String orderId;
  final String entryPoint;

  @override
  Widget build(BuildContext context, GoRouterState state) => FrProvider(
        (context) => OrderContentViewModel(
          orderId: orderId,
          entryPoint: entryPoint,
        ),
        onCreated: (context, vm) => vm.add(const OrderContentStarted()),
        child: const OrderContentView(),
      );
}
```

Page fields describe how the route enters and initialize page-owned state.
Ordinary View fields are reserved for values the View itself must render
without reading that state. Keep route types out of the component library.

## Page Contract

`xxx.page.dart` declares one route-to-Provider-to-view adapter:

```dart
@TypedGoRoute<OrderContentPage>(path: '/orders/:orderId')
class OrderContentPage extends GoRouteData with $OrderContentPage {
  /* route fields -> page ViewModel and/or View fields */
}
```

The route path is read from `@TypedGoRoute`, and the primary View is inferred
from `XxxPage.build`; do not duplicate either fact in documentation comments.
The primary View may compose any number of public/shared components and
Widgets recorded in its `Widget Tree:`; it does not limit a page to one
component. A page file may
declare additional typed Page variants for distinct URLs only when every
variant builds the same primary View; keep the basename-matching
`XxxPage` as the primary entry.

## Typed Routing

- New scaffolds use `go_router_builder` by default. Keep it and `build_runner`
  in `dev_dependencies`; every independent `xxx.page.dart` generates its own
  `$appRoutes`, and the root `app_router.dart` spreads those prefixed lists.
- Before adding or changing a route, read `references/typed-routing.md`.
- For a cross-page module or PageExtra migration, resolve `validate_routes`,
  read `references/validate_routes.md`, and run its module validator.
- For persistent bottom-navigation destinations, resolve
  `validate_navigation_shell`; branch switching uses the shell's `goBranch`
  API and is not Page-to-Page typed navigation.
- Make `XxxPage` the `GoRouteData`; do not create a separate `XxxRoute` or
  `XxxPageArgs`. Its `build` creates the page scope and constructs the primary
  `XxxView` below it.
- Navigate with generated route helpers when the destination is known in app
  code. Keep raw URI navigation only at explicit external/dynamic URI
  boundaries. `validate_routes` rejects fixed `context.go`/`push`/`replace`
  calls and `AppRoutes.xxx` indirection when the URI matches a typed Page;
  document exceptional compatibility boundaries with the required reasoned
  marker from `references/validate_routes.md`.

## Contract-First Workflow

1. Inspect Figma, run shared UI discovery, inspect the matching component and
   Widget module catalogs, nearby usage, and API context. Record one outcome:
   reuse an existing public entry; extend its owning module because no entry
   fits; or create a module because no module owns the capability. When the
   request supplies multiple Figma nodes or a Figma container node, first read
   `references/figma-screen-audit.md` and account for every supplied URL as a
   primary Frame, same-owner state, visual reference, or explicit exclusion.
   Inspect a container's structure before requesting any full design context.
   Find requested pages by their visible page-title text first, then resolve
   each matching text node to its owning Frame. Treat the Frame name as a
   candidate hint only because designers may leave it stale or unrelated.
   Confirm the screen from its hierarchy, navigation context, distinctive
   content, and render when needed; do not mistake a menu item or embedded
   title for the page itself. Record the confirmed `node-id`, current Frame
   name, and visible page title as one association. Select concrete Frames
   rather than reading the container as though it were a page.
   Present the resulting logical page/state ownership map before drafting.
   When primary Frames share a bottom navigation, also present their shell
   membership and classify shell roots, branch children, root fullscreen
   routes, and overlays before selecting Page or Scaffold ownership;
   never infer route or contract count from link count or visual similarity.
   Page drafts default to `BFF-JSON` when no concrete API is supplied.
   Shared component drafts default to `local` with `State Ownership: none`.
   Read
   `references/api-contract-semantics.md`; draw the cross-component data and
   business flow before defining DTOs.
2. For `gen_page`, draft `xxx.page.dart`, `xxx.dart`, `xxx.c.dart`, and the
   reviewable public-View stub in `xxx.v.dart`:

```bash
uv run --script <skill-root>/scripts/draft_contract.py \
  --name order_content --dir lib/app/order_content \
  --figma-url <url> --figma-frame <exact-current-frame-name> \
  --figma-page-title <exact-visible-page-title|none> \
  --mode bff-json --route <route> \
  --preview-width <width> --preview-height <height> \
  --preview-wrapper <public-wrapper> \
  --theme <none|material|app-shared|component>
```

   Read `references/widget-preview.md` before choosing the viewport and
   wrapper. Pass `--preview-wrapper-import <dart-uri>` when the public wrapper
   is declared outside the generated component library. The generator rejects
   missing Preview size or wrapper inputs instead of inventing them.
   For `app-shared` or `component`, also pass `--theme-type <ThemeType>`.
   Repeat `--extra-field name:DartType` when the Page needs multiple `$extra`
   values. The draft emits the required
   `@FrAcddFreezedJSON` PageExtra, generated parts, typed `$extra`, and
   PageExtra-to-ViewModel expansion. Register every emitted type in the
   application route-extra codec before final validation.

   Use `lib/app/<route-segment>/` for a route-owned component and
   `lib/components/<component-name>/ --component-only` for a component reused
   across routes. `--component-only` defaults to `--mode local --state-owner
   none`, so it emits no VM, Model, Event, Provider, Freezed, JSON, or BFF
   assets. Use `--state-owner app --state-type <AppViewModel>` when it consumes
   an existing root-provided VM. Use `--state-owner component` only after
   explicitly proving an independent component lifecycle; component-owned
   API/BFF modes require that flag. Use an existing project's established
   equivalent roots when they differ, unless an approved adaptation moves
   them.
3. Bind the primary Figma Frame and every declared `Figma States` Frame back
   to the generated Dart files before contract review. Never bind `Figma
   References` or `Figma Excluded`. Record the primary Frame's exact current
   name, visible page title, and complete node-specific URL under `Figma:` so
   `node-id ↔ Frame name ↔ Page Title` remains explicit. Use `Page Title: none`
   only for an intentionally titleless page or component. Record each `Figma
   States` target as only its `node-id`, resolved against that primary design
   file.
   Never repeat the design URL in `Figma States`. For an approved screen, add
   exactly one `Figma Fidelity:` section in this fixed shape:

```dart
/// Figma Fidelity:
/// - Viewport: 360 x 780
/// - Asset Lock: .agents/skills-config/fr-mvvm-contract/order-figma-assets.lock.json
/// - Regression Test: orderFigmaFidelity renders approved states
```

   Use `Asset Lock: none` only when the authoritative Frame has no exported
   assets. Do not put the Figma file/node, viewport, routes, copy, source
   assertions, or test names in the lock. Do not write contract metadata,
   shared plugin data, or visible cards into Figma.
   Use the one-line `Figma Fidelity: excluded | <reason>` form instead when
   the current implementation is explicitly outside the approved gate.
   Prepare page contracts and target Frames one at a time; a page contract must
   target its exact Figma Frame, never a Section containing several pages.
   Execute the
   emitted `verifyCode` in a second `use_figma` call and inspect its screenshot.
   Do not continue if the primary URL lacks `node-id`, a page target is a
   non-Frame,
   either representation is missing or stale, the card is not above its page,
   a path is not visibly rendered, or the independent readback differs.
4. Before writing a Figma-derived View, read `references/figma_fill_data.md`.
   Classify every non-copy Figma fill in the `.c.dart` `Figma Data:` section.
   A draft may retain a `TODO(figma-data)` marker or a `Binding: pending`
   declaration; contract and final validation reject both. Do not use Figma sample values as production
   defaults. Use `scripts/figma_fill_data.py` to report pending, invalid, and
   legacy-unreviewed declarations across a project.
5. Internally classify each UI-facing BFF endpoint as `query` or `command`;
   do not ask the user to choose a type or write an API-type field. Read
   `references/frontend-interactions.md`. Use its request boundary as the
   endpoint identity, write exactly one record under `Behaviors:` and one
   endpoint-scoped `Request Field Sources:` record, then define structured
   `Interactions:` Flows only for ViewModel-owned Trigger, Bloc Event, guard,
   state phases, concurrency, and navigation-signal coordination. Do not turn
   View-local callbacks into Events or Flows. The same component may own query and command
   endpoints. Do not author `SDK Calls`, `SDK Call Flow`, backend method/path
   annotations, or backend orchestration in `.c.dart`. Backend developers own
   those facts and edit only the protected backend section of `xxx.bff.md`.
   The skill may define and refresh the frontend UI data API, UI DTOs, state,
   endpoint behavior, interaction Flows, structure, and integration mapping.
   It must preserve the complete backend section byte-for-byte. Set `BFF
   Service` to the Dart SDK-adapter service class, such as
   `[OrderContentService]`; every BFF-JSON contract requires runtime
   integration. When a data boundary is unknown or the required evidence has
   not been supplied, add `Data Boundary:` with a stable
   `TODO(data-boundary): <capability> — <missing authority/evidence>` marker.
   Do not turn that uncertainty into `BFF-UI-API: -`, sample-data behavior, or an
   invented method/path. `BFF-UI-API: -` is allowed only after an explicit,
   approved API-less/local-only decision. Search for
   `TODO(data-boundary)` before approval; contract and final validation reject
   unresolved markers. Never invent `/bootstrap`, `nextRoute`, proof, result,
   or error placeholders.

   Write descriptive contract values in the resolved `Contract Description
   Language`. This applies to Behavior values, interaction state/feedback
   prose, Request Field Sources purpose prose, and Notes. Keep stable labels,
   Flow IDs, code identifiers, types,
   methods, paths, enum literals, and authoritative source expressions
   unchanged.

   Keep the remaining approval contract minimal: Figma, API/BFF, state ownership,
   reusable UI in the Widget Tree, theme, Event and ViewModel
   references, models, and concise notes. Page Support contains only route and
   primary View facts.
   Define `Widget Tree` as a concise hierarchy of key Widgets that lets a
   reviewer understand the View from `.c.dart` alone. Include a Widget when it
   is directly interactive, carries primary information, expresses an
   important state, determines the functional structure, or is a shared
   component developers must recognize. Prefer 4–8 key Widgets and keep even
   complex Views to at most 12; fold larger trees into business-level regions,
   use `× N` for repeated items, and mark conditional states briefly.

   When `Public Views:` lists more than one entry, write one `Widget Tree`
   bullet per public View in the same order. Do not add a discriminator enum
   merely to force semantically different entries through one generic View.

   Preserve only necessary hierarchy. Omit formulaic `_XxxViewBody` nodes,
   `FrProvider`, `FrConsumer`, `Builder`, layout glue such as `Padding`,
   `SizedBox`, `Spacer`, `Align`, `Expanded`, `Flexible`, and `SafeArea`, pure
   decoration such as `Divider` and `DecoratedBox`, and component-internal
   labels/icons/spacing already covered by the parent component. Omit `Row`,
   `Column`, `Stack`, and `Container` unless one is essential to disambiguate
   business structure. A semantic private Widget such as `[_HomeHeader]` may
   remain.

   Do not replace key Widget references with prose such as `confirmation form`.
   Do not draw a UI diagram or reproduce the complete runtime Widget tree.
   The provider module's public list is the dependency/reuse inventory; the
   concise `Widget Tree` records the entries actually composed here. Replace the generated TODO with an informative,
   concise tree before contract review and approval.
   For every `Behaviors:` record, remove the unused query or command fields;
   complete every `Interactions:` Flow, resolve every `TODO(data-boundary)` and
   other pending marker, then define DTO fields and synchronize typed `XxxPage`
   route fields to the final ordinary `XxxView` fields. The draft shell deliberately names not-yet-generated
   parts, so this review state is not a compilation or analyzer gate.
5. Present each UI API method/path and Req/Rsp/Error, its endpoint Behavior,
   endpoint-scoped field provenance, every frontend interaction Flow, and the
   SDK-adapter service class. A non-BFF local component may explicitly declare
   structured `Interactions:` only for ViewModel-owned local Flows; every such
   Flow uses `Uses: local` and requires no endpoint, BFF Service, SDK, or
   `bff.md`. Do not present, propose, or edit backend APIs or flow; backend
   developers maintain them in `xxx.bff.md`.
6. Validate the approved source contract before deriving files. This phase
   rejects semantic/API placeholders, missing or incomplete endpoint
   Behaviors, untraceable request fields, missing interaction coverage,
   invalid Event/Widget/Model/response references, UI-only command responses, invalid
   typed Page route-field conversion, incomplete Theme declarations, and missing direct
   dependencies, but does not require Freezed/JSON output yet:

```bash
uv run --script <skill-root>/scripts/validate_contract.py \
  --page-file path/to/xxx.page.dart --phase contract
```

7. For all non-contract work, read the contract through scripts rather than
   manually deriving decisions from raw Dart:

```bash
uv run --script <skill-root>/scripts/read_contract.py \
  --page-file path/to/xxx.page.dart
uv run --script <skill-root>/scripts/read_contract.py \
  --component-file path/to/xxx.dart
```

8. Prepare derived parts only from the approved reader output. The generator
   preflights the complete contract, Theme target, dependencies, and BFF
   extractor before committing any file. It prepares Theme changes, the BFF
   artifact, then `.vm.dart` and `.v.dart` stubs as one rollback-protected file
   set. An extractor or Theme failure must leave every prior file unchanged:

```bash
uv run --script <skill-root>/scripts/generate_from_contract.py \
  --page-file path/to/xxx.page.dart --write-stubs
```

Every BFF-JSON contract with runtime backend calls declares
`BFF Service: [Type]`. Use one Service per component. The Service imports the
required concrete SDK files from `lib/api/gen`, constructs configured generic
wrappers internally, and exposes semantic methods to its ViewModel. Do not
generate a frontend Retrofit client from the UI data API.

A project may require every `XxxBffRsp` to model a complete gateway response such as
`{state, code, message, data}`. In that case the original business response is
the value of its `data` field, not a replacement for the outer envelope.

Generated `*.bff.md` files begin with compact `bff-md-meta/v9` YAML Front
Matter containing schema, `@FrAcddPage` namespace/version, the authoritative
UI design source, a namespaced mdq v2 API-record contract, and no derivable
contract-file path. They separate inline UI
API DTOs from OpenAPI-owned backend operations, backend call flow, frontend UI
data, integration mapping, and a generated `API Query Records` verification
projection. The UI Contract contains UI State, endpoint Behaviors, generated
`前端交互逻辑`, and UI Structure in that order. Render UI State exclusively as
one JSON5 code block: every field has consecutive Model, Dart type, and
`Authority: Frontend` comments. Do not use Markdown tables for UI State. Read
`references/bff-dual-authority.md` before changing artifact structure,
ownership, generation, parsing, or validation.

Render BFF artifacts in this fixed order: title, `后端业务流程与业务逻辑 API`, and
`前端 UI 数据接口`. The backend domain contains only OpenAPI document references,
the business API annotations and business flow written by backend developers.
Each business API annotation retains only method/path, parameter names and
types, and response DTO type. Backend developers may add prose, JSON, DTO field
examples, schema excerpts, or code blocks elsewhere in this protected domain;
the parser preserves that material and excludes it from machine API records.
Backend developers alone create and edit this entire domain. AI may create only
UI-facing paths and `XxxBffReq`/`XxxBffRsp` DTOs from approved Figma/UI
requirements.
It must not treat a backend business API method/path as a new UI-facing path.
On an exact method/

…(truncated)
