# Modes and platform

> Visitor modes (Persuade, Operate, Read, Experience) and orthogonal platform values (web, ios, android, adaptive), including defaults, native reference loading, and web-only live/detect boundaries.

- Repository: pbakaus/impeccable
- GitHub: https://github.com/pbakaus/impeccable
- Human docs: https://grok-wiki.com/public/docs/pbakaus-impeccable-adadc04d8de4
- Complete Markdown: https://grok-wiki.com/public/docs/pbakaus-impeccable-adadc04d8de4/llms-full.txt

## Source Files

- `skill/SKILL.src.md`
- `skill/scripts/context.mjs`
- `skill/reference/operate.md`
- `skill/reference/new-work.md`
- `skill/reference/ios.md`
- `skill/reference/android.md`
- `skill/scripts/lib/artifact-schema.mjs`

---

---
title: Modes and platform
description: Visitor modes (Persuade, Operate, Read, Experience) and orthogonal platform values (web, ios, android, adaptive), including defaults, native reference loading, and web-only live/detect boundaries.
---

Impeccable separates **what the visitor comes to do** (mode) from **where the UI ships** (platform). Mode is per surface. Platform is per product, stored in `PRODUCT.md`, and steers which rulebooks load, which command variants apply, and whether browser tooling is available.

## Two orthogonal axes

| Axis | Question | Stored in | Scope |
|---|---|---|---|
| **Mode** | What does visitor success look like on this surface? | Surface brief under `.impeccable/surfaces/` | Per route, page, or artifact |
| **Platform** | What delivery target and native conventions apply? | `PRODUCT.md` → `## Platform` | Per product / project |

Mode does **not** live in `PRODUCT.md`. Platform does **not** live in surface briefs. A dashboard product can still have a Persuade marketing page; a fashion house can still have a Read docs site.

```mermaid
flowchart LR
  surface["Requested surface"] --> mode["Mode: Persuade / Operate / Read / Experience"]
  product["PRODUCT.md"] --> platform["Platform: web / ios / android / adaptive"]
  mode --> guidance["Surface strategy, color energy, motion, new-work questions"]
  platform --> refs["Native refs, command variants, web-only gates"]
```

## Visitor modes

Mode names the visitor's success condition on the surface in hand. Choose it from the **requested surface**, not from the product category.

| Mode | Visitor success | Typical surfaces | Design bias |
|---|---|---|---|
| **Persuade** | Decide and act; design *is* the product | Landing pages, marketing, campaigns, pricing | Earn attention and action; conversion lives inside the form's vocabulary |
| **Operate** | Complete a task | App UI, dashboards, editors, admin, settings, tools | Scanability, consistency, familiar affordances; brand in precise details |
| **Read** | Understand something | Docs, articles, guides, help, changelogs | Comprehension first; reading experience worth staying in |
| **Experience** | Be inside the work itself | Portfolios, galleries, showcases | Artifact leads from the first viewport; chrome recedes |

### Persistence and legacy Register

- Persist mode only in that surface's brief (scope + visitor mode, plus audience/job/constraints and direction).
- Write/read briefs with `node <skill>/scripts/surface-brief.mjs`.
- v4 retired `## Register` on `PRODUCT.md`. Nothing reads it. Boot staleness reports the section as deprecated so agents delete it when the user agrees instead of preserving it "just in case."

### Mode-sensitive guidance (without reloading every rulebook)

| Area | Persuade / Experience | Operate / Read |
|---|---|---|
| Color strategy | May take Committed, Full palette, or Drenched when the brief allows | Default Restrained (neutrals + one accent) |
| Typography | Faces with a point of view when the world earns them | System stacks and workhorse UI faces are usually right |
| Layout | Composition may be asymmetric or disruptive when the world earns it | Predictable structure, stable density, navigable linearity |
| Motion | May carry voice; prefer one rehearsed focal sequence | Feedback, state, continuity; fast routine transitions |
| Depth playbook | Mode bullets in `SKILL.md` + new-work for greenfield | `reference/operate.md` for Operate/Read depth |

Deeper Operate/Read craft (type scale, state vocabulary, product bans) lives in `skill/reference/operate.md`. New surfaces and replacement worlds load `skill/reference/new-work.md`, which asks mode-specific questions and enforces mode constraints at build time (for example: Operate may not obscure the task; Persuade must make the offer and action intelligible in the form's own vocabulary).

## Platform values

Platform answers delivery target and which native conventions apply. It is orthogonal to mode.

| Value | Meaning | References loaded at boot |
|---|---|---|
| `web` | Website or web app, including responsive mobile web | None extra (general skill rules apply) |
| `ios` | Native iOS / iPadOS app | `reference/ios.md` |
| `android` | Native Android app | `reference/android.md` |
| `adaptive` | One codebase that **adapts design language per OS** (Flutter, React Native, KMP when both targets differ by platform) | **Both** `ios.md` and `android.md` |

### Classification rules that people get wrong

- **Mobile web stays `web`.** A responsive site is not `ios` or `android`.
- **A native wrapper around a website does not make the design language native.** Record the language the app actually renders.
- **Material-everywhere on both phones is not `adaptive`.** A Flutter/RN app that uses one look (for example Material on iOS and Android) takes the single platform value that matches that language, not `adaptive`. `adaptive` means the UI genuinely adapts per OS.
- **Name the design language, not the toolchain.** Valid values are only `web`, `ios`, `android`, `adaptive`. Values like `flutter`, `react-native`, or `expo` are unrecognized.

### PRODUCT.md field shape

Record platform as a bare value under an exact `## Platform` heading:

```markdown
## Platform

web
```

or `ios`, `android`, or `adaptive`.

<ParamField body="## Platform" type="string" required>
Bare value on the first non-empty line after the heading. Case-insensitive. Exact heading match only (`## Platform notes` does not count). Empty section yields no value (treated as web).
</ParamField>

### Parsing, defaults, and aliases

`extractPlatform()` in `skill/scripts/context.mjs` implements:

| Input | Result |
|---|---|
| Missing `## Platform` or empty section | `null` → skill treats as **`web`** |
| `web` / `ios` / `android` / `adaptive` | That value |
| Short list of both targets only, e.g. `ios, android`, `android and ios`, `ios/android` | **`adaptive`** |
| Unrecognized value (`flutter`, prose, mixed junk) | `null` → treat as **`web`**, plus a **WARNING** directive naming the bad value |
| Prose that mentions both targets (`web only, not ios or android`) | **Not** adaptive; unrecognized |

Missing platform is silent web default (legacy-friendly). A *wrong* platform string is never silently web without a warning: boot prints that the value is unrecognized and that native references will not load until the field is fixed.

### Setting platform during init

1. Scan the project for stack and platform signals.
2. Form a hypothesis: `web`, `ios`, `android`, or `adaptive`.
3. Confirm when ambiguous (Step 3 of init).
4. Write the bare value under `## Platform` in `PRODUCT.md` (Step 4).
5. If the value is `ios`, `android`, or `adaptive`, load the matching native reference(s) immediately. Fresh projects had no `PRODUCT.md` when `context.mjs` ran, so init is the place those refs first enter the session.
6. Skip live-mode setup for native or non-runnable projects.

After init writes `PRODUCT.md`, resume design work **without** rerunning `context.mjs`; the native refs init loaded are the piece Setup could not have known.

## How platform changes a session

### Boot context (`context.mjs`)

Once per session, Setup runs `node <skill>/scripts/context.mjs` (optional `--target`). When product context loads successfully, the script:

1. Parses `## Platform` into `ctx.platform`.
2. Inlines native reference bodies as:

   `# NATIVE PLATFORM REFERENCE: IOS (reference/ios.md)`  
   and/or  
   `# NATIVE PLATFORM REFERENCE: ANDROID (reference/android.md)`

3. For native platforms, skips the web detector fallback directive (`MANUAL_DETECTOR_REQUIRED`).
4. For unrecognized raw platform strings, emits the WARNING above.

Native refs are distilled HIG / Material guidance (safe areas, system navigation, touch targets, semantic colors, platform controls). On native, visitor mode still applies, but platform conformance governs structure, navigation, and interaction; brand expresses in the layer the OS leaves open (tint, type, motion, content).

### Native command variants

The Commands table routes some work to platform-specific playbooks when `setup.platform` is native:

| Command | Web reference | Native reference |
|---|---|---|
| `audit` | `reference/audit.md` | `reference/audit.native.md` |
| `adapt` | `reference/adapt.md` | `reference/adapt.native.md` |

Web files carry a one-line guard: if the project is native, switch to the `.native.md` variant. Native audit scores platform conformance from source (SwiftUI / UIKit / Compose / React Native / Flutter); it does not use browser tooling or `detect.mjs`. One native variant covers ios, android, and adaptive; per-OS detail stays in the platform refs Setup already loaded.

Commands whose divergence is fully covered by platform refs (`animate`, `layout`, and most refine/enhance commands) have no separate native file.

### Web-only tooling boundaries

Live mode, the HTML/CSS detector, and the design edit hook operate on browser/HTML shapes. They are gated off for native platforms.

| Capability | Web (`web` or missing/legacy) | Native (`ios` / `android` / `adaptive`) |
|---|---|---|
| `/impeccable live` and live helpers | Available when configured | Do not lead with live; browser overlay does not apply |
| `detect.mjs` / `npx impeccable detect` | Available; routing may auto-scan dirty HTML/CSS | Skip; engine reads HTML/CSS, not native UI trees |
| Design hook (per-edit / Stop) | May run when enabled and manifests present | **Skipped** (`skipped: 'native-platform'`) |
| Manual detector fallback from context | Emitted when no hook is active | Not emitted |
| Native platform references | Not inlined | Inlined at boot |

Hook resolution uses the same PRODUCT.md path as the skill (`resolveProjectPlatform` → `extractPlatform`). That matters because React Native and Flutter trees use `.tsx` / `.ts` / `.js` extensions the hook already watches; without the platform gate, every native screen edit would draw web-shaped findings that contradict the native refs.

Routing (`reference/routing.md`) encodes the same rule for no-argument menus: if platform is native, do not lead with `live` or detect.

## Native platform expectations (summary)

### iOS (`reference/ios.md`)

Applies to SwiftUI, UIKit, React Native, Expo, and Flutter shipping to Apple hardware.

- Safe-area layout; system tab bar / nav stack / sheets
- Edge-swipe back stays alive; 44×44 pt minimum targets
- Dynamic Type + San Francisco for UI; semantic system colors; Dark Mode first-class
- Platform controls and SF Symbols; system transitions; honor Reduce Motion

### Android (`reference/android.md`)

Applies to Jetpack Compose, Views, React Native, Expo, and Flutter shipping to Android hardware.

- Material navigation by size (bar / rail / drawer); system Back always works
- Edge-to-edge with window insets; 48×48 dp targets
- Material type scale and color roles; Dynamic Color where it fits
- Material components and motion; honor Remove animations

### Adaptive

Loads **both** refs. Cross-platform code must still honor each OS's guarantees on that hardware (for example: even a Material-themed app on iPhone still owes safe-area insets, Reduce Motion, and edge-swipe back).

## Native evidence vs declared web

Tier-1 boot staleness (`checkNativePlatformEvidence`) only fires when the project **resolves to web** (explicit `web` or missing platform) but the tree shows native build evidence, for example:

- `pubspec.yaml` (Flutter → suggest `adaptive`)
- `ios/Podfile`, `ios/Runner.xcodeproj` → suggest `ios`
- `android/build.gradle` / `.kts` → suggest `android`
- `react-native`, `expo`, `@react-native/metro-config` dependencies → suggest `adaptive`

Finding id: `platform-native-evidence`, severity `mention`. Fix: ask whether `## Platform` should be the suggested value; if yes, write it and load the matching native reference before designing. Explicit native values are not re-checked this way.

## Worked examples

| Project | Mode on this task | Platform | What loads / what is blocked |
|---|---|---|---|
| SaaS marketing landing page | Persuade | `web` | General rules; live + detect allowed |
| Same SaaS app settings screen | Operate | `web` | `operate.md` depth; live + detect allowed |
| Product docs site | Read | `web` | Read mode + operate typography/consistency notes |
| iOS SwiftUI client | Operate (most screens) | `ios` | `ios.md` inlined; `audit.native` / `adapt.native`; no live/detect/hook scan |
| Flutter app with distinct iOS and Android chrome | Depends on surface | `adaptive` | Both native refs; web tooling off |
| Flutter app Material-everywhere on both OSes | Depends on surface | `android` (or the single language it actually uses), **not** `adaptive` | One platform ref |

## Verification checklist

After changing platform or starting a native project:

1. `PRODUCT.md` has `## Platform` with exactly one bare value.
2. `node <skill>/scripts/context.mjs` output includes the expected `# NATIVE PLATFORM REFERENCE: …` block(s), or none for web.
3. Unrecognized values produce a WARNING, not silent web guidance.
4. Native sessions do not receive `MANUAL_DETECTOR_REQUIRED` and do not run the design hook scan.
5. Surface briefs under `.impeccable/surfaces/` carry visitor mode for that surface only; no `## Register` on PRODUCT.md.

## Troubleshooting

| Symptom | Likely cause | Fix |
|---|---|---|
| Web slop rules on RN/Flutter edits | Platform missing or `web` | Set `## Platform` to `ios`, `android`, or `adaptive` |
| No iOS/Android guidance after init-less session | `context.mjs` ran before PRODUCT.md existed | After writing platform, load the native ref(s) yourself, or re-run context once PRODUCT.md is present |
| WARNING for `flutter` / `react-native` | Toolchain name in Platform | Replace with design language: `ios`, `android`, or `adaptive` |
| Live offered on a native app | Routing ignored platform | Treat live as web-only; use native audit/adapt and platform refs |
| Mode "stuck" on brand vs product | Legacy `## Register` | Remove Register; set mode on the surface brief |
| Material app marked adaptive but ships one look | Misclassified adaptive | Use the single platform that matches the shipped language |

## Related pages

<CardGroup cols={2}>
  <Card title="Project artifacts" href="/project-artifacts">
    PRODUCT.md Platform field, surface briefs, schema stamps, and deprecated Register.
  </Card>
  <Card title="Initialize product context" href="/init-and-document">
    Interview flow that records Platform and loads native refs on first write.
  </Card>
  <Card title="Commands and routing" href="/commands-and-routing">
    Native command variants, no-argument menu, and when live/detect are suppressed.
  </Card>
  <Card title="Live browser iteration" href="/live-mode">
    Web-only live mode, roots, and platform constraints.
  </Card>
  <Card title="Design hook" href="/design-hook">
    Native-platform skip behavior and detector hook controls.
  </Card>
  <Card title="Skill commands reference" href="/skill-commands-reference">
    Full command catalog including audit.native and adapt.native.
  </Card>
</CardGroup>
