> ## Documentation Index
> Fetch the complete documentation index at: https://docs.modaal.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Duet Glossary: Every Term in the Cross-Platform Framework

> Every Duet term defined in one place — feature, reducer, behavior recording, the checks, the coexistence window — and the name the tools use for each.

This glossary defines every term used by **Duet**, Modaal's cross-platform parity framework for native iOS and Android apps: the parts of a feature, the recording-and-checking vocabulary, the steps of a migration, and the work items a migration is planned in. Each entry is one paragraph with a stable anchor and a link to the page that treats the subject in full; where one of the [nine Duet tutorials](/tutorials/duet) builds the thing by hand, the entry names that page too.

The entry heading is the name the documentation uses. Where the tools, the parity manifest or the framework's own pages use a different name for the same thing — *fixture* for behavior recording, *gate* for check, *ledger* for tracker, *oracle* for reference test — the entry states both names.

## The parts of a feature

<h3 id="feature">
  feature
</h3>

A unit of behavior declared as state, actions, effect payloads and a pure reducer, carried as one row in the parity manifest and checked by its own [behavior recordings](#fixture). See [how a feature gets built](/articles/duet#how-a-feature-gets-built).

<h3 id="reducer">
  reducer
</h3>

Where every decision happens: for each action the [Store](#store) feeds it, the reducer alone runs the guards, writes state and returns effects. It is pure — no platform calls, no minted time or identity — so its behavior replays byte-for-byte on both platforms.

<h3 id="effect">
  effect / effect payload
</h3>

What a [reducer](#reducer) returns beside the new state: a value naming work the platform must do, such as a wait on a clock, a port call or a delegate notification, never the work itself. The effect handler turns each payload into the actions it produces, and an effect id gives it cancel-in-flight semantics. Because an effect is data, a [behavior recording](#fixture) pins it byte for byte. [Tutorial 1](/tutorials/duet-01-first-feature#add-the-environment-and-the-runtime) writes the first two; [Tutorial 9](/tutorials/duet-09-analytics#emit-them-as-track-effects) adds an analytics event as one more.

<h3 id="store">
  Store
</h3>

Built once per [mount](#mount): it holds the feature's state, takes actions, runs the [reducer](#reducer), and hands returned effects to the effect handler. Nothing else runs feature logic. [Tutorial 1](/tutorials/duet-01-first-feature#add-the-environment-and-the-runtime) builds the first one and [drives it through the kernel's exhaustive test store](/tutorials/duet-01-first-feature#pin-the-safety-nets-timing-on-a-test-clock).

<h3 id="kernel">
  kernel
</h3>

The runtime every [Store](#store) is built on: it runs each action through the reducer, tracks returned effects in flight, and owns the effect-leak check at teardown. One kernel shape serves both platforms; its contract ships with [the framework](https://github.com/modaal-agent/duet).

<h3 id="shell">
  shell
</h3>

What stays on the platform side once logic has moved: three duties and no decisions — bind the view's intents to the store, project state into the view, bracket the lifecycle. See [what crossing one feature looks like](/articles/combineribs-to-duet#what-crossing-one-feature-looks-like); [Tutorial 2](/tutorials/duet-02-two-apps#write-the-swift-shell) writes the first Swift and Compose shells.

<h3 id="host">
  host / host root
</h3>

The level that [mounts](#mount) a feature: it builds the store, owns the slot the feature renders in, and takes its [delegate events](#delegate) as data. In the Swift flavor a *host root* is the package whose resolved dependencies must contain the Duet family and nothing else. [Tutorial 3](/tutorials/duet-03-composing-features#compose-the-tree-on-android) mounts a tree of features from state, one level hosting the next.

<h3 id="delegate">
  delegate events / the delegate seam
</h3>

How a feature reports an outcome upward: the reducer emits a plain enum case, and the [host](#host) receives it as data on the seam it handed in at build time. The event is a value, so a [scenario](#scenario) asserts it byte-for-byte. [Tutorial 3](/tutorials/duet-03-composing-features#write-the-root-level) receives each child's delegate events as one case of the parent's action type.

<h3 id="interactor">
  Interactor
</h3>

The CombineRIBs type that owns a RIB's business logic. In a migration every Interactor shrinks to a [shell](#shell) and is then deleted. See [what happens to each piece of your code](/articles/combineribs-to-duet#what-happens-to-each-piece-of-your-code).

<h3 id="worker">
  worker
</h3>

Stateful, lifecycle-bound processing on the environment side, adopted at [mount](#mount): its results re-enter feature state only through declared seams, and cancellation is its stop. Workers process, reducers decide; a worker has no [golden recording](#golden-fixture). [Tutorial 4](/tutorials/duet-04-workers#write-the-workers-on-android) writes the first two and [tests them with the worker harness](/tutorials/duet-04-workers#test-the-workers-with-the-harness).

<h3 id="package">
  package
</h3>

In the Swift flavor a feature's subtree holds two Swift packages: the gated feature half resolves the Duet family and nothing else, so the checks build it in isolation; the app-facing node half is where the app connects.

<h3 id="port">
  port
</h3>

An interface the shared logic calls and each app implements, such as the auth, purchases, items and account ports in the tutorials' app. Every operation starts work and returns, with the answer arriving through a callback that fires once, so a Swift class can implement it across the framework boundary; a stream on a port is a sticky value a [worker](#worker) observes. [Tutorial 3](/tutorials/duet-03-composing-features#declare-the-four-ports) declares the four ports behind mock services; [Tutorial 4](/tutorials/duet-04-workers#add-the-two-streams-to-the-ports) adds the streams and replaces the mocks with an on-device backend.

<h3 id="slice">
  slice
</h3>

A value a parent publishes downward out of its own state, delivered to a child as an action of the child's own: the child reads it and never writes it, and nothing below the parent holds the parent's [Store](#store). [Tutorial 4](/tutorials/duet-04-workers#project-the-entitlement-down-as-a-slice) projects the entitlement to two tabs as a slice.

<h3 id="lateral-state">
  lateral state
</h3>

A value that must cross between sibling features, neither of which holds the other's store: one [worker](#worker) owned by their lowest common ancestor, with a void update on one side and a sticky observation on the other. [Tutorial 5](/tutorials/duet-05-navigation-as-state#share-readiness-laterally) builds one for the onboarding steps' readiness.

<h3 id="main-actor">
  `@MainActor`
</h3>

The escalation a handler or worker takes to cross an isolation boundary. Marking a type unchecked-`Sendable` instead silences the compiler without proving anything, which is why the doctor pass flags it on a worker.

<h3 id="dsl">
  the fixture-authoring language
</h3>

The four verbs a [scenario](#scenario) is written in — given, when, then, then-effects — with a Swift and a Kotlin spelling. The framework's pages call it the scenario DSL.

## Recording and checking

<h3 id="scenario">
  scenario
</h3>

One test in [the fixture-authoring language](#dsl), pinning one outcome. Mutually exclusive endings are branches over one starting state; a chain scenario pins a [delegate seam](#delegate) that crosses features. [Tutorial 1](/tutorials/duet-01-first-feature#describe-the-behavior-as-a-scenario-and-record-it) writes the first scenario and records it.

<h3 id="fixture">
  behavior recording
</h3>

What recording a [scenario](#scenario) produces: a checked-in file replaying the scenario's exact state and effect bytes — a build product, never hand-edited, and it wins any disagreement with spec prose. The tools, the manifest and the framework's pages call these files fixtures.

<h3 id="golden-fixture">
  golden recording
</h3>

A [behavior recording](#fixture) in its replay role: the golden test reads committed recordings at runtime and fails on any byte that differs — a role, not a second kind of file. A [worker](#worker) has no golden.

<h3 id="chain">
  chain recording
</h3>

A [behavior recording](#fixture) that pins the seam between two features rather than one reducer: the delegate event one reducer emits, received by the next as its action, with each hop re-derived at verify time from the recorded bytes so an edited seam fails as structure drift. [Tutorial 3](/tutorials/duet-03-composing-features#pin-the-seams-with-chain-recordings) records the first four.

<h3 id="re-recording">
  re-recording
</h3>

Running the recorder again after a deliberate behavior change, then reading the recording diff like code. The run deletes before it harvests, so a leftover file means a stale run.

<h3 id="metadata-only">
  metadata-only
</h3>

The pass condition for a re-record that must not change behavior: the diff touches scenario source locations and step labels, and zero state or effect bytes.

<h3 id="mutation-drill">
  mutation drill
</h3>

The negative control on [the checks](#gate): break a reducer guard and watch the replay go red, then break a recording file and watch the test task go red — proof the checks are wired to what they claim to cover. Run in [a crossing's first half](/articles/combineribs-to-duet#what-crossing-one-feature-looks-like); [Tutorial 6](/tutorials/duet-06-checks-in-ci#write-the-first-mutation-rows) runs it from a mutation table in both directions.

<h3 id="gate">
  the checks
</h3>

What must pass before work is done, run by one command locally and in CI: the manifest lint, the recording check, the cross-platform verification and the doctor pass. The framework's pages call each one a gate. [Tutorial 6](/tutorials/duet-06-checks-in-ci#know-what-each-command-proves) lists every check with what it proves and [puts them in one CI workflow](/tutorials/duet-06-checks-in-ci#put-the-checks-in-a-workflow).

<h3 id="lane">
  lane
</h3>

One slice of a [checks](#gate) run, rooted at a specific package or module set, so a red names its platform and its root. Parity lanes derive from a feature's manifest row; generation lanes produce the test doubles and DI components. [Tutorial 6](/tutorials/duet-06-checks-in-ci#read-the-lanes-the-manifest-derives) reads the lanes report the tool derives.

<h3 id="swift-and-kotlin-lanes">
  the Swift and Kotlin lanes
</h3>

A manifest row's two implementation declarations; which of them a row carries tells [the checks](#gate) which platform lanes to run — one during ordinary work, both inside a [coexistence window](#coexistence-window).

<h3 id="duet-lint">
  the manifest lint
</h3>

The manifest's own consistency checks — the fast red, run on every manifest edit. The declaration-parity and subtree-geometry checks join only while a row declares both implementations.

<h3 id="run-tests-script">
  the headless test runner
</h3>

A script every scaffolded project ships that runs the iOS test suite from the command line, wrapping Apple's xcodebuild — for verifying outside the Modaal app and reproducing CI failures locally.

<h3 id="the-run-verb">
  the run verb
</h3>

The build-and-launch action in a Modaal session: one command that builds the selected target and launches it on a simulator, emulator or device. The action's name differs between session types, so Duet's documents say "the run verb".

## The migration, step by step

<h3 id="audit">
  the readiness audit
</h3>

A static census of a legacy app's distance to the target shape, producing the readiness report, [the migration tracker](#migration-ledger) and a spec draft per feature. The counts are pointers to verify, not verdicts. See [the stages](/articles/combineribs-to-duet#the-stages), step 1.

<h3 id="migration-ledger">
  the migration tracker
</h3>

One row per [surface](#surface): a derived status, four sizing columns, and the notes the per-feature plan is written into. The tools and the migration article call it the ledger — distinct from the divergence waivers [the parity contract keeps](/articles/cross-platform-ui-parity#divergence-is-ledgered-not-forbidden).

<h3 id="writing-the-ledger">
  updating the tracker
</h3>

Re-running [the readiness audit](#audit) in write mode: it creates or merges the tracker and refreshes the computed fields. It is the route's first step after [the stop-digging edit](#stop-digging-rule).

<h3 id="the-graft">
  the graft
</h3>

The one operation that adds the Kotlin core plane, the Android app shell and the parity workspace beside an existing tree while editing none of its sources — the checks exist before any behavior can change. See [the stages](/articles/combineribs-to-duet#the-stages), step 2.

<h3 id="wiring-the-core">
  wiring the core
</h3>

Run once the graft's checks are green: it links the assembled Kotlin core into the iOS app's build, so every later [flip](#the-flip) is feature work with no build wiring left to add. See [the stages](/articles/combineribs-to-duet#the-stages), step 2b.

<h3 id="feature-crossing">
  feature crossing
</h3>

One feature's move onto the shared Kotlin core, end to end: spec, Swift [twin](#twin), Kotlin twin, the Android [mount](#mount), the [flip](#the-flip). See [what crossing one feature looks like](/articles/combineribs-to-duet#what-crossing-one-feature-looks-like).

<h3 id="coexistence-window">
  the coexistence window
</h3>

The stretch of a crossing in which a feature's manifest row declares both implementations: both [twins](#twin) replay the same recordings and the parity lints run. A valid resting state for as long as the repository wants.

<h3 id="the-flip">
  the flip
</h3>

The one change that closes a [coexistence window](#coexistence-window): delete the Swift twin, make the feature's row Kotlin-only, verify both platforms.

<h3 id="twin">
  twin
</h3>

One of a feature's two implementations while its [coexistence window](#coexistence-window) is open — the Swift twin inside the app, the Kotlin twin in the shared core — replaying the same [behavior recordings](#fixture).

<h3 id="single-source">
  single-source
</h3>

The destination shape: one implementation of a feature's logic, in the shared Kotlin core, consumed by both apps. The Swift flavor's *single-core* is the same one-copy rule before a second platform consumes it. See [the two Duet templates](/articles/duet#the-two-duet-templates).

## The work items and the field kit

<h3 id="surface">
  surface
</h3>

A [tracker](#migration-ledger) row's unit of work: one RIB or one legacy worker in the app being converted, typed by the row's kind.

<h3 id="oracle">
  reference test
</h3>

The existing test a conversion is written against, ported one-to-one onto the deterministic test store or the worker test harness. A surface without one earns its reference test first; the audit's sizing table and the tracker call it the oracle.

<h3 id="manner-mass">
  look-and-feel work
</h3>

The part of a [surface](#surface) that does not move: tuned rendering, animation timing, gesture handling — re-authored on the other platform and sized by counting files. The audit calls it platform-manner mass; [manner](/articles/cross-platform-ui-parity#the-seam-a-five-layer-presentation-contract) is the contract layer it lives in.

<h3 id="note-grammar">
  the note format
</h3>

The shape every [tracker](#migration-ledger) note takes: a capitalized tag, an em dash, then clauses following the surface's kind — a RIB's note carries a crossing position; a worker's names the crossing it rides with.

<h3 id="stop-digging-rule">
  the stop-digging rule
</h3>

From step 0 on, a new feature in the legacy app is authored in the target shape — a reducer plus a thin shell, never a new Router — and new background work is a Duet [worker](#worker) from day one. See [the stages](/articles/combineribs-to-duet#the-stages), step 0.

<h3 id="adoption-seam">
  adoption seam
</h3>

Where a converted feature's SwiftUI view attaches to the legacy UIKit composition: a themed hosting controller takes the legacy child's slot, and activating the [shell](#shell) replaces attaching the child.

<h3 id="coexistence-kit">
  coexistence kit
</h3>

The migration package that stands for the migration's duration — the worker shim and the dual-import discipline — and whose dependency edge is deleted at completion. See [the finish line](/articles/combineribs-to-duet#the-finish-line).

<h3 id="stage-host">
  stage host
</h3>

What the Android app's root grows into when the app needs real navigation: one full-screen child chosen from route state, transitions as reducer edges, back handling in a documented order. The first [crossing](#feature-crossing) that needs a screen change grows it.

<h3 id="mount">
  mount
</h3>

Putting a feature into the app's composition: its [Store](#store) is built once at the mounting level, its screen renders from the mount row, and that level's own [workers](#worker) are adopted there.

<h3 id="spine">
  spine
</h3>

The app's navigation trunk, in two uses: the route state that survives process death, and the root and navigation surfaces that cross last, after the leaves. [Tutorial 5](/tutorials/duet-05-navigation-as-state#gather-the-route-spine-and-restore-from-it) gathers a route spine and rebuilds the tree from it.

## Common questions

<AccordionGroup>
  <Accordion title="Why does my repository say fixture where this page says behavior recording?" icon="file-lines">
    They are the same file. The headings here use the documentation's names; the tools, the parity manifest and the framework's pages use the framework-side names — fixture, gate, ledger, oracle — and each entry states both: [behavior recording](#fixture), [the checks](#gate), [the migration tracker](#migration-ledger), [reference test](#oracle).
  </Accordion>

  <Accordion title="How long can a coexistence window stay open?" icon="clock">
    As long as the repository wants — [the window](#coexistence-window) is a valid resting state, and the parity lints keep both twins honest while it is open. Holding one open costs the dual-language authoring premium the [migration article](/articles/combineribs-to-duet#what-crossing-one-feature-looks-like) quantifies; [the flip](#the-flip) closes it.
  </Accordion>

  <Accordion title="Where is each term treated in full?" icon="book-open">
    Three articles carry the depth: [the Duet overview](/articles/duet) for the feature shape and the two templates, [handling platform-specific UI](/articles/cross-platform-ui-parity) for the presentation contract, and [migrating a CombineRIBs app](/articles/combineribs-to-duet) for the route. The framework's own reference lives in [the Duet repository](https://github.com/modaal-agent/duet). The [nine Duet tutorials](/tutorials/duet) build each feature-level term by hand, and the [series index](/tutorials/duet#what-does-the-series-teach) maps every concept to the step that treats it.
  </Accordion>

  <Accordion title="How do I start a Duet project?" icon="rocket">
    Pick one of the two **Multiplatform** cards in the [Modaal](https://modaal.dev) new-project wizard — [starting a Duet project](/articles/duet#starting-a-duet-project) walks through the choice.
  </Accordion>
</AccordionGroup>

## Sources and further reading

* [The Duet framework repository](https://github.com/modaal-agent/duet) — the framework API, the emitted contracts and the family's own documentation.
* [Kotlin Multiplatform documentation](https://kotlinlang.org/docs/multiplatform.html) — the technology under the Kotlin Multiplatform flavor's shared core.
* [Swift concurrency documentation](https://docs.swift.org/swift-book/documentation/the-swift-programming-language/concurrency/) — the isolation model behind the [`@MainActor`](#main-actor) entry.

## Read next

<CardGroup cols={2}>
  <Card title="Duet: one shared core, two native apps" icon="mobile-screen-button" href="/articles/duet">
    The product overview: what a Duet project is, the two templates, and how a feature gets built.
  </Card>

  <Card title="Handling platform-specific UI" icon="layer-group" href="/articles/cross-platform-ui-parity">
    The five-layer presentation contract: kinds, manner, islands, and how deliberate divergence is recorded.
  </Card>

  <Card title="Migrating a CombineRIBs app" icon="arrow-right-arrow-left" href="/articles/combineribs-to-duet">
    The route the migration cluster's entries walk: audit, graft, crossings, the finish line.
  </Card>

  <Card title="Duet tutorials" icon="graduation-cap" href="/tutorials/duet">
    Feature, reducer, shell, worker, scenario, recording and the checks, each built by hand across nine tutorials.
  </Card>
</CardGroup>
