@getrheo/rheo-skill 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/README.md +76 -0
  2. package/package.json +51 -0
  3. package/rheo/SKILL.md +32 -0
  4. package/rheo/rheo-best-practices/SKILL.md +46 -0
  5. package/rheo/rheo-best-practices/examples/react-native-install-snippet.md +23 -0
  6. package/rheo/rheo-best-practices/examples/swiftui-install-snippet.md +20 -0
  7. package/rheo/rheo-best-practices/references/implement-workflow.md +27 -0
  8. package/rheo/rheo-best-practices/references/integrations.md +51 -0
  9. package/rheo/rheo-best-practices/references/react-native-bare.md +36 -0
  10. package/rheo/rheo-best-practices/references/react-native-expo.md +49 -0
  11. package/rheo/rheo-best-practices/references/swiftui.md +52 -0
  12. package/rheo/rheo-best-practices/references/troubleshooting.md +57 -0
  13. package/rheo/rheo-flow-import/SKILL.md +68 -0
  14. package/rheo/rheo-flow-import/examples/branching-onboarding.manifest.json +155 -0
  15. package/rheo/rheo-flow-import/examples/flow-spec.example.json +56 -0
  16. package/rheo/rheo-flow-import/examples/linear-onboarding.manifest.json +104 -0
  17. package/rheo/rheo-flow-import/examples/revenuecat-paywall.manifest.json +154 -0
  18. package/rheo/rheo-flow-import/references/animation-import.md +79 -0
  19. package/rheo/rheo-flow-import/references/capabilities.md +107 -0
  20. package/rheo/rheo-flow-import/references/carousel-import.md +64 -0
  21. package/rheo/rheo-flow-import/references/flow-spec.md +214 -0
  22. package/rheo/rheo-flow-import/references/font-import.md +67 -0
  23. package/rheo/rheo-flow-import/references/import-workflow.md +240 -0
  24. package/rheo/rheo-flow-import/references/layer-schema-pitfalls.md +179 -0
  25. package/rheo/rheo-flow-import/references/localization-import.md +128 -0
  26. package/rheo/rheo-flow-import/references/manifest-agent-profile-fallback.md +74 -0
  27. package/rheo/rheo-flow-import/references/manifest-rules.md +197 -0
  28. package/rheo/rheo-flow-import/references/publish-gates.md +91 -0
  29. package/rheo/rheo-flow-import/references/react-native-source-patterns.md +99 -0
  30. package/rheo/rheo-flow-import/references/swiftui-source-patterns.md +99 -0
  31. package/rheo/rheo-flow-import/scripts/audit-import.mjs +4 -0
  32. package/rheo/rheo-flow-import/scripts/audit-publish-manifest.mjs +4 -0
  33. package/rheo/rheo-flow-import/scripts/fetch-profile.mjs +4 -0
  34. package/rheo/rheo-flow-import/scripts/lib/rheo-cli.mjs +12753 -0
  35. package/rheo/rheo-flow-import/scripts/normalize-manifest.mjs +4 -0
  36. package/rheo/rheo-flow-import/scripts/print-manifest-summary.mjs +4 -0
  37. package/rheo/rheo-flow-import/scripts/scaffold-manifest.mjs +4 -0
  38. package/rheo/rheo-flow-import/scripts/validate-manifest.mjs +4 -0
  39. package/src/index.ts +32 -0
@@ -0,0 +1,64 @@
1
+ # Carousel import rules
2
+
3
+ Use when the source flow has an **in-screen pager** (`infoSteps`, `currentInfoStep`, horizontal `FlatList` / `PagerView`, dot indicators).
4
+
5
+ ## Rheo carousel behavior (swipe-only)
6
+
7
+ - Users move between slides by **swiping** (horizontal scroll with snap).
8
+ - Optional **`pageControl`** adds dot indicators — not buttons.
9
+ - There is **no** Next / Continue button on the carousel layer in the manifest or at runtime.
10
+ - On the **last slide** (when `loop` is false and there are 2+ slides), swiping to that slide emits a carousel completion so `screen.next` can run.
11
+ - **Single-slide** carousels do not auto-complete; add a `regions.footer` **Continue** (or other) button to advance the flow.
12
+ - **`loop: true`** carousels never auto-complete; pair with a separate screen-level CTA when the flow should move on.
13
+
14
+ ## Manifest shape
15
+
16
+ - One `kind: "carousel"` in `regions.body` (or inside a body stack).
17
+ - Each page → child **`stack`** in `carousel.slides[]`.
18
+ - `pageControl: { "position": "bottom" }` when source had dot indicators.
19
+
20
+ ## Do not duplicate paging UI
21
+
22
+ | Source | Import |
23
+ |--------|--------|
24
+ | Pager pages | `carousel.slides[]` |
25
+ | Dot indicators | `pageControl` only |
26
+ | Swipe between pages | Default — no extra buttons |
27
+ | Footer that only increments pager index | **Omit** — not `regions.footer` |
28
+ | Footer / CTA that exits to **next route** | `regions.footer` with `continue` / `go_to_step`, or rely on last-slide swipe completion + `screen.next` |
29
+
30
+ ## Example (swipe + dots)
31
+
32
+ ```json
33
+ {
34
+ "regions": {
35
+ "body": {
36
+ "kind": "stack",
37
+ "direction": "vertical",
38
+ "children": [
39
+ {
40
+ "kind": "carousel",
41
+ "slides": [
42
+ {
43
+ "kind": "stack",
44
+ "direction": "vertical",
45
+ "align": "center",
46
+ "children": [
47
+ { "kind": "text", "text": { "default": "Slide 1" }, "style": { "color": "#111111" } }
48
+ ]
49
+ }
50
+ ],
51
+ "pageControl": { "position": "bottom" }
52
+ }
53
+ ]
54
+ }
55
+ },
56
+ "next": { "default": "scr_next" }
57
+ }
58
+ ```
59
+
60
+ ## Completion gate
61
+
62
+ - [ ] No `button` layers for in-pager next/continue.
63
+ - [ ] `screen.next` targets the next **screen** in the flow, not “next slide”.
64
+ - [ ] Single-slide carousel screens include a flow-level CTA if the flow must advance without swiping.
@@ -0,0 +1,214 @@
1
+ # Flow Spec (recommended authoring path)
2
+
3
+ A **flow spec** is a compact, agent-authored description of a flow. Running
4
+ `node scripts/scaffold-manifest.mjs <flow-spec.json>` expands it into a
5
+ **schema-valid** Rheo `FlowManifest` skeleton: it generates `scr_*`/`lyr_*` ids,
6
+ wraps label copy into `children`, builds `optionBindings` and `branching` for
7
+ choices, maps button variants to the Rheo enum, and wires `next` edges. You then
8
+ enrich theme/styles and validate.
9
+
10
+ > The scaffold guarantees **schema validity only**. It does not invent colors,
11
+ > fonts, or publish-gate styling — set `theme` and layer `style` from audit
12
+ > evidence, then run `validate-manifest.mjs` + `audit-publish-manifest.mjs`.
13
+
14
+ Why use it: the scaffold removes the mistakes that cause `invalid_union` import
15
+ failures (missing `children`, UUID-as-layer-id, missing choice bindings, raw
16
+ source `variant` names). Hand-authoring is allowed but you own every mechanical
17
+ detail yourself.
18
+
19
+ ## Top-level shape
20
+
21
+ ```jsonc
22
+ {
23
+ "flowId": "00000000-0000-0000-0000-000000000001", // optional placeholder; dashboard replaces it
24
+ "version": 1,
25
+ "defaultLocale": "en",
26
+ "locales": ["en"],
27
+ "entry": "scr_welcome", // optional; defaults to the first screen's id
28
+ "theme": { /* @getrheo/contracts Theme */ },
29
+ "sdkAttributeKeys": ["sdk.plan"], // non-reserved keys used by decisions
30
+ "screens": [ /* ScreenSpec[] — required */ ],
31
+ "decisions": [ /* @getrheo/contracts DecisionNode[] */ ],
32
+ "externalSurfaces": [ /* ExternalSurfaceSpec[] */ ]
33
+ }
34
+ ```
35
+
36
+ ## Screens
37
+
38
+ ```jsonc
39
+ {
40
+ "id": "scr_welcome", // optional; generated from name when omitted
41
+ "name": "Welcome",
42
+ "containerStyle": { "backgroundFill": { "kind": "color", "color": "#0EA5E9" } },
43
+ "header": [ /* LayerIntent[] — back_button / progress chrome */ ],
44
+ "body": [ /* LayerIntent[] — required main content */ ],
45
+ "footer": [ /* LayerIntent[] — sticky CTAs */ ],
46
+ "next": "scr_goal" // jump target (scr_*/dec_*/surf_*) or null to end the flow
47
+ }
48
+ ```
49
+
50
+ `next` becomes the screen's `next.default`. Use `null` (or omit and rely on an
51
+ `end_flow` button) for terminal screens.
52
+
53
+ ## Layer intents
54
+
55
+ Every entry in `header`/`body`/`footer` is a **layer intent** — a friendly shape
56
+ the scaffold expands into a real layer. `id` is always optional. `style` and
57
+ `styleBreakpoints` are passed through verbatim (validated by the layer schema).
58
+
59
+ ### Text, image, icon, media
60
+
61
+ ```jsonc
62
+ { "kind": "text", "text": "Welcome to Rheo", "style": { "color": "#FFFFFF" } }
63
+ { "kind": "text", "text": { "default": "Hi", "translations": { "es": "Hola" } } }
64
+ { "kind": "image", "mediaAssetId": "00000000-0000-0000-0000-000000000101", "alt": "Hero" }
65
+ { "kind": "icon", "iconName": "sparkles-outline" } // family defaults to ionicons
66
+ { "kind": "lottie", "mediaAssetId": "…", "loop": true, "autoPlay": true }
67
+ { "kind": "video", "mediaAssetId": "…", "autoPlay": false } // needs a play_media button
68
+ ```
69
+
70
+ Copy fields accept a plain string (becomes `{ default }`) or an explicit
71
+ `{ default, translations }`.
72
+
73
+ ### Buttons, back buttons, links
74
+
75
+ The convenience fields `label`/`icon`/`labelStyle` are expanded into nested
76
+ `text`/`icon` children — you do not write `children` yourself unless you need
77
+ custom content. `action` defaults to `continue`. Source `variant` names
78
+ (`outline`, `text`, `link`, `default`, `filled`, `danger`) are mapped to the Rheo
79
+ enum automatically.
80
+
81
+ ```jsonc
82
+ { "kind": "button", "label": "Continue", "action": "continue", "variant": "primary",
83
+ "labelStyle": { "color": "#FFFFFF" } }
84
+ { "kind": "button", "label": "Maybe later", "action": "skip", "variant": "ghost" }
85
+ { "kind": "button", "label": "Go to plan", "action": { "kind": "go_to_step", "screenId": "scr_plan" } }
86
+ { "kind": "back_button", "icon": "chevron-back-outline", "variant": "ghost" } // header chrome, no action
87
+ { "kind": "hyperlink", "href": "https://rheo.app/terms", "label": "Terms" }
88
+ ```
89
+
90
+ Action shorthands: `"none"`, `"continue"`, `"skip"`, `"end_flow"`,
91
+ `"go_back_one_screen"`, `"request_app_review"`. Object forms:
92
+ `{ "kind": "go_to_step", "screenId": "scr_x" }`,
93
+ `{ "kind": "request_os_permission", "permissionKey": "…", "outcomes": { "granted": "scr_a", "denied": "scr_b", "blocked": "scr_c" } }`,
94
+ `{ "kind": "play_media", "targetLayerIds": ["lyr_video"] }`.
95
+
96
+ ### Stacks (layout)
97
+
98
+ ```jsonc
99
+ { "kind": "stack", "direction": "vertical", "align": "center", "gap": 12,
100
+ "style": { "padding": { "t": 24, "r": 16, "b": 24, "l": 16 } },
101
+ "children": [ { "kind": "text", "text": "Centered hero copy" } ] }
102
+ ```
103
+
104
+ Card chrome (border/shadow/background/radius) goes on a wrapping stack's `style`.
105
+
106
+ ### Choices
107
+
108
+ You provide `options`; the scaffold builds the option `stack` children,
109
+ `optionBindings`, and `branching`. `optionId` defaults to a slug of the label.
110
+ Map unselected chrome to `style` and selected chrome to `selectedStyle` per option.
111
+
112
+ ```jsonc
113
+ {
114
+ "kind": "single_choice",
115
+ "fieldKey": "gender",
116
+ "direction": "vertical",
117
+ "gap": 12,
118
+ "options": [
119
+ { "optionId": "male", "label": "Male",
120
+ "style": { "border": { "width": 1, "color": "#E5E7EB" }, "radius": 12, "padding": { "t": 16, "r": 16, "b": 16, "l": 16 } },
121
+ "selectedStyle": { "border": { "width": 1, "color": "#6D5DF6" }, "background": "#6D5DF610" },
122
+ "labelStyle": { "color": "#0A0A0A" } },
123
+ { "optionId": "female", "label": "Female", "labelStyle": { "color": "#0A0A0A" } }
124
+ ]
125
+ }
126
+ ```
127
+
128
+ `multiple_choice` adds optional `minSelections`/`maxSelections`. To branch per
129
+ option, set `branching: { "enabled": true, "conditions": [{ "choiceId": "male", "goTo": "scr_x" }] }`.
130
+
131
+ ### Inputs
132
+
133
+ ```jsonc
134
+ { "kind": "text_input", "fieldKey": "first_name", "placeholder": "Your name",
135
+ "inputType": "plain", "required": true, "classification": "safe" }
136
+ { "kind": "scale_input", "fieldKey": "fitness_level", "min": 1, "max": 5, "step": 1,
137
+ "minLabel": "Beginner", "maxLabel": "Pro" }
138
+ { "kind": "checkbox", "fieldKey": "accept_terms", "blocking": true }
139
+ ```
140
+
141
+ Use **at most one** input layer per screen, and add a `continue` button on
142
+ screens with `text_input`, `multiple_choice`, or `scale_input`.
143
+
144
+ ### Auth
145
+
146
+ ```jsonc
147
+ { "kind": "oauth_login", "providers": [
148
+ { "type": "preset", "provider": "apple" },
149
+ { "type": "preset", "provider": "google" },
150
+ { "type": "custom", "label": "SSO", "iconName": "key-outline", "buttonVariant": "secondary" }
151
+ ] }
152
+ { "kind": "email_password_auth", "fieldKey": "auth", "mode": "sign_up",
153
+ "submitLabel": "Create account", "minPasswordLength": 8 }
154
+ ```
155
+
156
+ Keep OAuth, email/password, and questionnaire inputs on **separate screens**.
157
+
158
+ ### Carousel (swipe-only onboarding pager)
159
+
160
+ ```jsonc
161
+ {
162
+ "kind": "carousel",
163
+ "pageControl": { "position": "bottom" },
164
+ "slides": [
165
+ { "align": "center", "gap": 12, "children": [
166
+ { "kind": "image", "mediaAssetId": "…" },
167
+ { "kind": "text", "text": "Track your streak", "style": { "color": "#0A0A0A" } }
168
+ ] },
169
+ { "align": "center", "gap": 12, "children": [
170
+ { "kind": "image", "mediaAssetId": "…" },
171
+ { "kind": "text", "text": "Stay consistent", "style": { "color": "#0A0A0A" } }
172
+ ] }
173
+ ]
174
+ }
175
+ ```
176
+
177
+ No buttons inside a carousel — paging is swipe-only ([carousel-import.md](carousel-import.md)).
178
+
179
+ ### Counter / progress / loader
180
+
181
+ ```jsonc
182
+ { "kind": "progress", "fillColor": "#6D5DF6", "trackColor": "#E5E7EB" }
183
+ { "kind": "counter", "startValue": 0, "endValue": 1000, "durationMs": 1200 }
184
+ { "kind": "loader", "variant": "circular", "targetPercent": 100, "onComplete": { "mode": "next" } }
185
+ ```
186
+
187
+ ## Decisions and external surfaces
188
+
189
+ `decisions` accepts full `@getrheo/contracts` `DecisionNode` objects (the scaffold
190
+ passes them through). RevenueCat paywalls use `externalSurfaces`:
191
+
192
+ ```jsonc
193
+ {
194
+ "id": "surf_paywall",
195
+ "provider": "revenuecat",
196
+ "offeringId": "default",
197
+ "presentation": "paywall",
198
+ "outcomes": { "purchase_completed": "scr_done", "restore_completed": "scr_done", "dismissed": "scr_offer2" },
199
+ "fallback": "scr_offer2" // required
200
+ }
201
+ ```
202
+
203
+ ## Run it
204
+
205
+ ```bash
206
+ node scripts/scaffold-manifest.mjs ./rheo-import.flow-spec.json --out ./rheo-import.manifest.json
207
+ node scripts/validate-manifest.mjs ./rheo-import.manifest.json
208
+ node scripts/audit-publish-manifest.mjs ./rheo-import.manifest.json
209
+ ```
210
+
211
+ If scaffold prints `scaffold_status=schema_valid`, the skeleton is structurally
212
+ correct. Then enrich `theme`/`style` from audit evidence and fix any
213
+ publish-gate blockers ([publish-gates.md](publish-gates.md)). A complete worked
214
+ spec lives at [../examples/flow-spec.example.json](../examples/flow-spec.example.json).
@@ -0,0 +1,67 @@
1
+ # Custom font import (rheo-import.fonts.json)
2
+
3
+ Fonts are **not** media assets. Never list `.ttf`, `.otf`, `.woff`, or `.woff2` files in `rheo-import.assets.json`.
4
+
5
+ ## Two files, two jobs
6
+
7
+ | File | Purpose |
8
+ |------|---------|
9
+ | `rheo-import.assets.json` | Images, Lottie JSON, videos only (`media.mediaAssetId` placeholders) |
10
+ | `rheo-import.fonts.json` | Custom font files for app branding + `manifest.theme.fontFamily` |
11
+
12
+ Dashboard import **rejects** a font path in `rheo-import.assets.json` when `type` is `image` (or any type that does not match the extension).
13
+
14
+ ## Copy paths
15
+
16
+ - Put font binaries under `assets/fonts/` in the ZIP (not mixed into image folders).
17
+ - Use a single `assets/` prefix in paths, e.g. `assets/fonts/CalSans-Regular.ttf` — avoid `assets/assets/fonts/...`.
18
+
19
+ ## rheo-import.fonts.json shape
20
+
21
+ ```json
22
+ {
23
+ "fontFamilies": [
24
+ {
25
+ "name": "CalSans",
26
+ "styles": [
27
+ {
28
+ "id": "00000000-0000-0000-0000-000000000501",
29
+ "weight": 400,
30
+ "italic": false,
31
+ "path": "assets/fonts/CalSans-Regular.ttf",
32
+ "filename": "CalSans-Regular.ttf"
33
+ }
34
+ ]
35
+ }
36
+ ]
37
+ }
38
+ ```
39
+
40
+ - `name` — family string referenced by `manifest.theme.fontFamily` (e.g. `"CalSans"`).
41
+ - `styles[].path` — ZIP path to the font file (extension must match the binary).
42
+ - `styles[].id` — stable placeholder UUID per style (weight + italic), not used in manifest layers.
43
+
44
+ Font rows do **not** use `type` or `contentType`. Upload MIME is inferred from the file extension on import.
45
+
46
+ ## Supported font MIME types (upload)
47
+
48
+ | Extension | MIME used on sign-upload |
49
+ |-----------|--------------------------|
50
+ | `.ttf` | `font/ttf` |
51
+ | `.otf` | `font/otf` |
52
+ | `.woff` | `font/woff` |
53
+ | `.woff2` | `font/woff2` |
54
+
55
+ ## Common agent mistakes (BLOCKING)
56
+
57
+ 1. **Font in `rheo-import.assets.json`** with `"type": "image"` and `image/png` — wrong file and wrong MIME.
58
+ 2. **Font in `rheo-import.assets.json`** with `"type": "font"` — still wrong file; use `rheo-import.fonts.json` only.
59
+ 3. **Only `manifest.theme.fontFamily`** without bundling font files — family name alone does not load the font.
60
+ 4. **Duplicate path prefix** — `assets/assets/fonts/...` from joining paths incorrectly.
61
+
62
+ ## Checklist before zipping
63
+
64
+ - [ ] Every `.ttf`/`.otf`/`.woff`/`.woff2` appears only in `rheo-import.fonts.json`, not in `rheo-import.assets.json`
65
+ - [ ] `manifest.theme.fontFamily` matches a `fontFamilies[].name`
66
+ - [ ] Each style `path` exists in the ZIP under `assets/fonts/`
67
+ - [ ] No font file uses `media.mediaAssetId` in the manifest
@@ -0,0 +1,240 @@
1
+ # Import Workflow
2
+
3
+ Generate a complete Rheo import from an existing app flow. If the source flow uses media, output a ZIP bundle. Raw JSON is acceptable only after an explicit asset audit finds no local media.
4
+
5
+ ## Mandatory Intake Questionnaire (blocking)
6
+
7
+ Ask these questions **before** running the audit or generating a manifest. Record every answer in chat. Do not skip when the repo structure looks obvious.
8
+
9
+ 1. Which file, route, or coordinator starts the existing flow?
10
+ 2. What is the business purpose of this flow in one sentence?
11
+ 3. Should the import optimize for **visual fidelity** or **editable structure** in Rheo?
12
+ 4. If source code and screenshots disagree, which is more current?
13
+ 5. Which steps must stay native/host-owned (signature pad, camera, custom WebView, paywall SDK) vs approximated in Rheo?
14
+ 6. Match motion from the codebase (may differ slightly from Rheo presets)? (yes/no)
15
+ 7. Does the app use i18n/localization? If yes, which locale is the default/fallback? All `text.default` values must be resolved strings from that locale — never raw keys.
16
+
17
+ If the user has not named an entry point, stop after question 1 and wait for an answer.
18
+
19
+ ## Steps
20
+
21
+ 1. Fetch the latest Rheo Manifest Agent Profile:
22
+ - Preferred raw docs URL: `/docs/md/developer-guide/agent-manifest-profile`.
23
+ - If fetching fails, read [manifest-agent-profile-fallback.md](manifest-agent-profile-fallback.md).
24
+ - Record the profile version used.
25
+ 2. Complete the mandatory intake questionnaire above. Do not proceed without answers.
26
+ 3. Run the bundled audit before generating — **from the target app root, not the skill folder**:
27
+ - `RHEO=/abs/path/to/rheo/rheo-flow-import` then `cd <target-app>`.
28
+ - `node "$RHEO/scripts/audit-import.mjs" --entry <flow-entry> --out rheo-import.audit.md`
29
+ - `--entry` is the flow's entry **file** (import graph is traced) or a **directory** (source subtree only). Repeat `--entry` for multiple roots, or comma-separate: `--entry a.tsx,b.tsx`. The audit does **not** scan the whole repo.
30
+ - Read the printed `audit_root` / `audit_entries` / `audit_scope` / `audit_scanned_files`. If `audit_root` is not the host app (e.g. it points inside the skill), fix `--entry`/`--root` and re-run — do not trust an audit rooted in the skill.
31
+ - When intake Q6 is **yes**, add `--suggest-animations rheo-import.animations.json`.
32
+ - Use the audit report as the evidence source for regions, styles, backgrounds, assets, and motion.
33
+ - If the audit cannot run, explain why and manually produce the same report sections.
34
+ 4. Identify the host stack: React Native / Expo, SwiftUI, or best-effort other mobile stack.
35
+ 5. Find the flow entry point:
36
+ - Ask for the file/route/coordinator if the user did not provide it.
37
+ - React Native examples: Expo Router path, React Navigation screen, `OnboardingScreen`.
38
+ - SwiftUI examples: `NavigationStack` root, onboarding view, coordinator push.
39
+ 6. Trace reachable steps:
40
+ - Follow imports and navigation calls.
41
+ - Extract UI strings, button labels, input field keys, paywall calls, auth calls, permission prompts, links, and terminal navigation.
42
+ - When the audit reports localization ([localization-import.md](localization-import.md)): resolve `t('key')` / `formatMessage` ids from the **default locale** JSON (e.g. `locales/en.json`), not the key itself. Set `manifest.defaultLocale` to that locale.
43
+ - Detect branches from conditionals, feature flags, platform checks, or stored user state.
44
+ 7. Infer screen regions from audit findings:
45
+ - Look for top bars, safe-area header containers, `Header`, `Toolbar`, back icons, close buttons, and progress indicators.
46
+ - Put back/close controls and flow progress into `regions.header` when they appear above main content.
47
+ - Look for sticky CTA containers, `Footer`, bottom safe-area wrappers, and buttons outside scroll/content containers.
48
+ - Put persistent bottom CTAs into `regions.footer`, not at the bottom of `regions.body`.
49
+ - Use `regions.body` for scrollable or main content.
50
+ 8. Extract style tokens from audit findings:
51
+ - Inspect theme files, design-token files, Tailwind/NativeWind config, CSS variables, `StyleSheet.create`, shared component variants, and button/text primitives.
52
+ - Map primary/background/foreground/accent colors into `manifest.theme` when clearly available.
53
+ - Reuse obvious font sizes, font weights, spacing, border radius, and CTA variants from source components.
54
+ - Set `style.color` on text layers when screens use dark/saturated backgrounds (`bg-red-600`, `text-white`, etc.).
55
+ - Prefer the app’s named tokens over arbitrary black-and-white defaults.
56
+ - Black-and-white fallback is allowed only when the audit finds no color evidence **and** the user confirms there is no theme file.
57
+ 9. Map screen backgrounds and gradients:
58
+ - For `BackgroundGradient`, `LinearGradient`, or `expo-linear-gradient`, set `screen.containerStyle.backgroundFill` to `{ "kind": "color", "color": "<linear-gradient CSS>" }`.
59
+ - Example: `linear-gradient(180deg, #CCFBF1 0%, #F5F5F4 100%)` for a teal-to-stone shell gradient.
60
+ - When a screen overrides with a solid brand color (e.g. red education pager), use that hex on the screen and white text.
61
+ - Do not put gradients only in `manifest.theme.background` — theme background is a flat default, not a per-screen gradient.
62
+ 10. Map carousels and in-screen pagers (see [carousel-import.md](carousel-import.md)):
63
+ - When source uses `infoSteps`, `currentInfoStep`, horizontal `translateX` pagers, or `pagingEnabled` lists, emit `kind: "carousel"`.
64
+ - Each slide is a vertical `stack` with image, title, and body text from that slide.
65
+ - Add `pageControl: { "position": "bottom" }` when dot indicators exist (dots only).
66
+ - Carousels are **swipe-only** — no Next/Continue button on the carousel layer. Do not add footer/body buttons for in-pager paging.
67
+ - Use `regions.footer` only when the source CTA advances the **next screen in the flow**, or for single-slide carousels that need an explicit Continue.
68
+ - Bundle every slide image; every asset referenced in the carousel must appear in `rheo-import.assets.json`.
69
+ - Do not collapse multi-slide routes into one static screen with a single image.
70
+ 11. Map layout, alignment, borders, and shadows:
71
+ - Parent vertical stacks that center content need `align: "center"` (and `justify: "center"` when appropriate).
72
+ - Hero images belong inside centered stacks, not as lone siblings in an unaligned vertical stack.
73
+ - Card rows (rating, testimonials, why-it-works cards) use wrapping stacks with `style.background`, `style.radius`, `style.padding`, `style.border`, and `style.shadow` from source.
74
+ 12. Map custom fonts from audit findings ([font-import.md](font-import.md)):
75
+ - Detect `Font.loadAsync`, `useFonts`, Tailwind `fontFamily`, and `font-*` classes tied to loaded families.
76
+ - Copy every `.ttf`, `.otf`, `.woff`, and `.woff2` file into `assets/fonts/` in the ZIP (path like `assets/fonts/CalSans-Regular.ttf`).
77
+ - Write `rheo-import.fonts.json` with stable placeholder UUIDs per font style (weight + italic).
78
+ - Set `manifest.theme.fontFamily` to the primary family name used in source (e.g. `CalSans`).
79
+ - **Do not** add font files to `rheo-import.assets.json` — not as `type: "image"`, not as `type: "font"`. Fonts are not `media.mediaAssetId` assets.
80
+ - Dashboard import uploads fonts and merges them into app branding; the manifest only references the family name string.
81
+ 13. Map choice option default and selected states:
82
+ - For `single_choice` and `multiple_choice`, each selectable option is a child `stack` layer.
83
+ - Map unselected Tailwind/classes to `style` on that stack (border, background, padding, radius).
84
+ - Map selected classes to `selectedStyle` on the same stack — the renderer merges selected over default.
85
+ - When label text color changes on selection, set default `style.color` on the nested text layer and selected color via `selectedStyle` on the stack or nested text as appropriate.
86
+ - Audit flags `selected === … ? … : …` ternaries — do not ship choice screens with only default styling.
87
+ 14. Extract assets from audit findings:
88
+ - Follow static imports, `require(...)`, image maps, Lottie JSON imports, video imports, and asset constants used by traced screens.
89
+ - Inspect shared components and design primitives too; hero illustrations and logos are often hidden behind `Illustration`, `Logo`, `Avatar`, `Mascot`, or `ImageBackground` components.
90
+ - Inspect platform asset catalogs and config references where relevant, for example Expo `assets/`, React Native image folders, Swift asset catalogs, and bundled Lottie/video directories.
91
+ - Copy referenced local assets into an `assets/` folder in the output bundle.
92
+ - For each asset, generate a stable placeholder UUID and use that UUID as `media.mediaAssetId` in the manifest.
93
+ - Write `rheo-import.assets.json` mapping placeholder UUIDs to bundled file paths.
94
+ - If a referenced asset cannot be copied, stop and report the missing asset instead of silently dropping the media layer.
95
+ 15. Ask targeted follow-up questions for audit ambiguity:
96
+ - Ask only when ambiguity changes the manifest materially.
97
+ - Continue with low-confidence review notes when not blocked.
98
+ - Examples: shared header vs per-step header, exact gradient mapping, light/dark asset choice, conflicting button token sources.
99
+ 16. Use screenshots/recordings if supplied:
100
+ - Use them for visual hierarchy, copy confirmation, colors, media placement, and step order.
101
+ - Do not override clear source behavior unless the user says the visual artifact is newer.
102
+ 17. Build a private flow model:
103
+ - ordered screens
104
+ - branches
105
+ - inputs
106
+ - external surfaces
107
+ - terminal outcomes
108
+ - unmappable native behavior
109
+ 18. Generate `rheo-import.manifest.json`:
110
+ - Emit a complete graph with explicit `next.default` edges.
111
+ - Use decision nodes for clear branches.
112
+ - Use RevenueCat external surfaces for detected RevenueCat paywalls.
113
+ - Prefer fidelity when the user chose visual fidelity: carousels, gradients, shadows, and centered stacks.
114
+ - **Layer ids:** `scr_*` / `lyr_*` only on screens and layers; UUID placeholders **only** for `media.mediaAssetId` and font sidecars ([layer-schema-pitfalls.md](layer-schema-pitfalls.md#layer-ids-vs-media-placeholder-uuids)).
115
+ - **Container layers:** every `button`, `back_button`, `hyperlink`, `stack`, choice input, and auth layer must include its `children` (or `slides`) array ([manifest-rules.md](manifest-rules.md#container-layers-required-children)).
116
+ - **Header back buttons:** `kind: "back_button"`, Rheo `variant` enum only (map source `outline`/`text`/`link`), nested `icon`/`text` children, **no `action`**, `icon.family: "ionicons"` ([layer-schema-pitfalls.md](layer-schema-pitfalls.md#back_button-header-chrome)).
117
+ - **Choice screens:** every `single_choice` / `multiple_choice` includes `fieldKey`, `children` (≥2 option stacks), `optionBindings`, and `branching` — never `"options"` arrays ([layer-schema-pitfalls.md](layer-schema-pitfalls.md#single_choice--multiple_choice)).
118
+ - When intake Q6 is **yes** and the workspace plan includes animations, apply motion from [animation-import.md](animation-import.md) (audit presets, stagger, conservative `restingMotion`). Omit motion when Q6 is **no** or plan lacks animations.
119
+ 19. Validate and publish gate audit:
120
+ - Run `node "$RHEO/scripts/validate-manifest.mjs" ./rheo-import.manifest.json` (schema + dashboard publish rules).
121
+ - Run `node "$RHEO/scripts/audit-publish-manifest.mjs" ./rheo-import.manifest.json` and read `rheo-import.publish-gates.md`.
122
+ - Fix every **blocking** issue (especially missing `style.color` on text and button labels, missing Continue on input screens, entry/completion path).
123
+ - See [publish-gates.md](publish-gates.md) for the full checklist mirrored from the builder **Publish** button.
124
+ - If safe normalization is needed, run `node "$RHEO/scripts/normalize-manifest.mjs" ./rheo-import.manifest.json`, then re-run validate + publish audit.
125
+ 20. Bundle:
126
+ - If there are no assets or fonts, provide `rheo-import.manifest.json` and explicitly state `no local assets found`.
127
+ - If there are assets or fonts, create `rheo-import.zip` containing `rheo-import.manifest.json`, optional `rheo-import.assets.json`, optional `rheo-import.fonts.json`, and files under `assets/`.
128
+ - Do not finish with JSON-only output when any traced screen references local image, Lottie, video, or font files.
129
+
130
+ ## Output
131
+
132
+ Primary artifact:
133
+
134
+ - No assets after explicit audit: `rheo-import.manifest.json`.
135
+ - With assets: `rheo-import.zip`.
136
+
137
+ `rheo-import.assets.json` — **images, Lottie, and video only** (never fonts):
138
+
139
+ ```json
140
+ {
141
+ "assets": [
142
+ {
143
+ "id": "00000000-0000-0000-0000-000000000101",
144
+ "path": "assets/hero.png",
145
+ "type": "image",
146
+ "contentType": "image/png",
147
+ "name": "hero.png"
148
+ }
149
+ ]
150
+ }
151
+ ```
152
+
153
+ **Supported `rheo-import.assets.json` MIME types (dashboard import rejects others):**
154
+
155
+ | `type` | `contentType` values | Extensions |
156
+ |--------|----------------------|------------|
157
+ | `image` | `image/png`, `image/jpeg`, `image/webp`, `image/gif`, `image/svg+xml` | `.png`, `.jpg`, `.jpeg`, `.webp`, `.gif`, `.svg` |
158
+ | `lottie` | `application/json` | `.json` |
159
+ | `video` | `video/mp4`, `video/webm`, `video/quicktime` | `.mp4`, `.webm`, `.mov` |
160
+
161
+ Do **not** put `.ttf`, `.otf`, `.woff`, or `.woff2` in this file — use `rheo-import.fonts.json` ([font-import.md](font-import.md)). Do **not** bundle HEIC or AVIF as images. Match `type` and `contentType` to the file extension (do not label a `.json` Lottie as `type: "image"`).
162
+
163
+ `rheo-import.fonts.json` — **custom fonts only** (separate from assets.json):
164
+
165
+ ```json
166
+ {
167
+ "fontFamilies": [
168
+ {
169
+ "name": "CalSans",
170
+ "styles": [
171
+ {
172
+ "id": "00000000-0000-0000-0000-000000000501",
173
+ "weight": 400,
174
+ "italic": false,
175
+ "path": "assets/fonts/CalSans-Regular.ttf",
176
+ "filename": "CalSans-Regular.ttf"
177
+ }
178
+ ]
179
+ }
180
+ ]
181
+ }
182
+ ```
183
+
184
+ Choice option stack example (each option child of `single_choice` / `multiple_choice`):
185
+
186
+ ```json
187
+ {
188
+ "id": "lyr_goal_a",
189
+ "kind": "stack",
190
+ "style": {
191
+ "border": { "width": 1, "color": "#E5E7EB" },
192
+ "background": "#F9FAFB",
193
+ "radius": 12,
194
+ "padding": 16
195
+ },
196
+ "selectedStyle": {
197
+ "border": { "width": 1, "color": "#6D5DF6" },
198
+ "background": "#6D5DF610"
199
+ },
200
+ "children": [
201
+ {
202
+ "id": "lyr_goal_a_text",
203
+ "kind": "text",
204
+ "text": { "default": "Option A" },
205
+ "style": { "color": "#0A0A0A" }
206
+ }
207
+ ]
208
+ }
209
+ ```
210
+
211
+ Wrap option stacks in a full choice layer — see [layer-schema-pitfalls.md](layer-schema-pitfalls.md#single_choice--multiple_choice) for the required `fieldKey`, `optionBindings`, and `branching` wrapper.
212
+
213
+ Mention review notes in chat, not inside the manifest, unless Rheo contracts support the metadata.
214
+
215
+ ## Completion Gate
216
+
217
+ Before final response, answer these for yourself:
218
+
219
+ - Did I complete the mandatory intake questionnaire and record answers in chat?
220
+ - Did I run audit with `--entry` and use carousel/style/background/layout/font/choice findings?
221
+ - Did I bundle custom fonts in `rheo-import.fonts.json` only (no `.ttf`/`.otf`/`.woff` rows in `rheo-import.assets.json`) and set `manifest.theme.fontFamily`?
222
+ - If the app uses i18n, did every `text.default` use resolved default-locale copy (not translation keys)?
223
+ - Did every choice option stack include both `style` and `selectedStyle` when the audit flagged selected ternaries?
224
+ - Did I emit `carousel` layers for every multi-slide route the audit flagged without duplicating pager buttons in `regions.footer`?
225
+ - Did I apply gradients to `screen.containerStyle.backgroundFill` and set text colors on colored screens?
226
+ - Did I center images and map card borders/shadows to wrapping stacks?
227
+ - Did I inspect source asset references from every traced screen and shared visual component?
228
+ - Did I either create `rheo-import.zip` or explicitly confirm no local assets were found?
229
+ - Does every `media.mediaAssetId` refer to a placeholder UUID listed in `rheo-import.assets.json` when assets exist?
230
+ - Does the ZIP include every file listed in `rheo-import.assets.json`?
231
+ - Did publish gate audit pass with zero blocking issues?
232
+ - Did every `back_button`, `button`, and `hyperlink` layer include a `children` array with nested label content (not omitted)?
233
+ - Did every layer use `lyr_*` ids (no asset UUIDs as layer ids)?
234
+ - Did every `back_button` use a Rheo `variant` enum value and omit `action`?
235
+ - Did every `single_choice` / `multiple_choice` include `fieldKey`, `optionBindings`, and `branching`?
236
+ - Did `node "$RHEO/scripts/validate-manifest.mjs"` exit 0 before zipping?
237
+
238
+ If any answer is no, the import is not complete.
239
+
240
+ Also verify that high-confidence audit findings were either used in the manifest/ZIP or explicitly explained in review notes.