@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,155 @@
1
+ {
2
+ "flowId": "00000000-0000-0000-0000-000000000001",
3
+ "schemaVersion": 7,
4
+ "version": 1,
5
+ "defaultLocale": "en",
6
+ "locales": ["en"],
7
+ "entryScreenId": "dec_platform",
8
+ "theme": {
9
+ "primary": "#111827",
10
+ "primaryForeground": "#FFFFFF",
11
+ "background": "#FFFFFF",
12
+ "foreground": "#111827",
13
+ "borderRadius": 14
14
+ },
15
+ "screens": [
16
+ {
17
+ "id": "scr_ios_intro",
18
+ "name": "iOS intro",
19
+ "regions": {
20
+ "body": {
21
+ "id": "lyr_ios_body",
22
+ "kind": "stack",
23
+ "direction": "vertical",
24
+ "gap": 14,
25
+ "style": { "padding": { "t": 24, "r": 20, "b": 24, "l": 20 } },
26
+ "children": [
27
+ {
28
+ "id": "lyr_ios_title",
29
+ "kind": "text",
30
+ "text": { "default": "Welcome on iOS" },
31
+ "style": { "fontSize": 24, "fontWeight": 700, "color": "#111827" }
32
+ },
33
+ {
34
+ "id": "lyr_ios_continue",
35
+ "kind": "button",
36
+ "variant": "primary",
37
+ "action": { "kind": "continue" },
38
+ "direction": "horizontal",
39
+ "align": "center",
40
+ "distribution": "center",
41
+ "children": [
42
+ {
43
+ "id": "lyr_ios_continue_text",
44
+ "kind": "text",
45
+ "text": { "default": "Continue" },
46
+ "style": { "color": "#FFFFFF" }
47
+ }
48
+ ]
49
+ }
50
+ ]
51
+ }
52
+ },
53
+ "next": { "default": "scr_done" }
54
+ },
55
+ {
56
+ "id": "scr_android_intro",
57
+ "name": "Android intro",
58
+ "regions": {
59
+ "body": {
60
+ "id": "lyr_android_body",
61
+ "kind": "stack",
62
+ "direction": "vertical",
63
+ "gap": 14,
64
+ "style": { "padding": { "t": 24, "r": 20, "b": 24, "l": 20 } },
65
+ "children": [
66
+ {
67
+ "id": "lyr_android_title",
68
+ "kind": "text",
69
+ "text": { "default": "Welcome on Android" },
70
+ "style": { "fontSize": 24, "fontWeight": 700, "color": "#111827" }
71
+ },
72
+ {
73
+ "id": "lyr_android_continue",
74
+ "kind": "button",
75
+ "variant": "primary",
76
+ "action": { "kind": "continue" },
77
+ "direction": "horizontal",
78
+ "align": "center",
79
+ "distribution": "center",
80
+ "children": [
81
+ {
82
+ "id": "lyr_android_continue_text",
83
+ "kind": "text",
84
+ "text": { "default": "Continue" },
85
+ "style": { "color": "#FFFFFF" }
86
+ }
87
+ ]
88
+ }
89
+ ]
90
+ }
91
+ },
92
+ "next": { "default": "scr_done" }
93
+ },
94
+ {
95
+ "id": "scr_done",
96
+ "name": "Done",
97
+ "regions": {
98
+ "body": {
99
+ "id": "lyr_done_body",
100
+ "kind": "stack",
101
+ "direction": "vertical",
102
+ "gap": 14,
103
+ "style": { "padding": { "t": 24, "r": 20, "b": 24, "l": 20 } },
104
+ "children": [
105
+ {
106
+ "id": "lyr_done_title",
107
+ "kind": "text",
108
+ "text": { "default": "All set" },
109
+ "style": { "fontSize": 24, "fontWeight": 700, "color": "#111827" }
110
+ },
111
+ {
112
+ "id": "lyr_done_end",
113
+ "kind": "button",
114
+ "variant": "primary",
115
+ "action": { "kind": "end_flow" },
116
+ "direction": "horizontal",
117
+ "align": "center",
118
+ "distribution": "center",
119
+ "children": [
120
+ {
121
+ "id": "lyr_done_end_text",
122
+ "kind": "text",
123
+ "text": { "default": "Finish" },
124
+ "style": { "color": "#FFFFFF" }
125
+ }
126
+ ]
127
+ }
128
+ ]
129
+ }
130
+ },
131
+ "next": { "default": null }
132
+ }
133
+ ],
134
+ "decisionNodes": [
135
+ {
136
+ "id": "dec_platform",
137
+ "name": "Platform",
138
+ "cases": [
139
+ {
140
+ "id": "case_ios",
141
+ "name": "iOS",
142
+ "expression": {
143
+ "kind": "predicate",
144
+ "variable": { "kind": "builtin", "name": "platform" },
145
+ "predicate": { "type": "string", "pred": { "op": "eq", "value": "ios" } }
146
+ },
147
+ "next": "scr_ios_intro"
148
+ }
149
+ ],
150
+ "elseNext": "scr_android_intro"
151
+ }
152
+ ],
153
+ "externalSurfaceNodes": [],
154
+ "sdkAttributeKeys": []
155
+ }
@@ -0,0 +1,56 @@
1
+ {
2
+ "defaultLocale": "en",
3
+ "entry": "scr_welcome",
4
+ "theme": {
5
+ "primary": "#6D5DF6",
6
+ "primaryForeground": "#FFFFFF",
7
+ "background": "#FFFFFF",
8
+ "foreground": "#0A0A0A",
9
+ "borderRadius": 16
10
+ },
11
+ "screens": [
12
+ {
13
+ "id": "scr_welcome",
14
+ "name": "Welcome",
15
+ "header": [
16
+ { "kind": "back_button", "variant": "outline" }
17
+ ],
18
+ "body": [
19
+ { "kind": "text", "text": "Welcome to Acme", "style": { "fontSize": 28, "fontWeight": 700, "align": "center", "color": "#0A0A0A" } },
20
+ { "kind": "text", "text": "A few quick questions to set you up.", "style": { "fontSize": 16, "align": "center", "color": "#0A0A0A" } }
21
+ ],
22
+ "footer": [
23
+ { "kind": "button", "label": "Get started", "action": "continue", "labelStyle": { "color": "#FFFFFF" } }
24
+ ],
25
+ "next": "scr_goal"
26
+ },
27
+ {
28
+ "id": "scr_goal",
29
+ "name": "Goal",
30
+ "body": [
31
+ { "kind": "text", "text": "What's your main goal?", "style": { "fontSize": 22, "fontWeight": 700, "color": "#0A0A0A" } },
32
+ {
33
+ "kind": "single_choice",
34
+ "fieldKey": "goal",
35
+ "options": [
36
+ { "optionId": "focus", "label": "Stay focused", "style": { "border": { "width": 1, "color": "#E5E7EB" }, "radius": 12, "padding": { "t": 16, "r": 16, "b": 16, "l": 16 } }, "selectedStyle": { "border": { "width": 1, "color": "#6D5DF6" } }, "labelStyle": { "color": "#0A0A0A" } },
37
+ { "optionId": "relax", "label": "Relax more", "style": { "border": { "width": 1, "color": "#E5E7EB" }, "radius": 12, "padding": { "t": 16, "r": 16, "b": 16, "l": 16 } }, "selectedStyle": { "border": { "width": 1, "color": "#6D5DF6" } }, "labelStyle": { "color": "#0A0A0A" } }
38
+ ]
39
+ }
40
+ ],
41
+ "footer": [
42
+ { "kind": "button", "label": "Continue", "action": "continue", "labelStyle": { "color": "#FFFFFF" } }
43
+ ],
44
+ "next": "scr_done"
45
+ },
46
+ {
47
+ "id": "scr_done",
48
+ "name": "Done",
49
+ "body": [
50
+ { "kind": "text", "text": "You're all set", "style": { "fontSize": 24, "fontWeight": 700, "align": "center", "color": "#0A0A0A" } },
51
+ { "kind": "button", "label": "Finish", "action": "end_flow", "labelStyle": { "color": "#FFFFFF" } }
52
+ ],
53
+ "next": null
54
+ }
55
+ ]
56
+ }
@@ -0,0 +1,104 @@
1
+ {
2
+ "flowId": "00000000-0000-0000-0000-000000000001",
3
+ "schemaVersion": 7,
4
+ "version": 1,
5
+ "defaultLocale": "en",
6
+ "locales": ["en"],
7
+ "entryScreenId": "scr_welcome",
8
+ "theme": {
9
+ "primary": "#0A0A0A",
10
+ "primaryForeground": "#FFFFFF",
11
+ "background": "#FFFFFF",
12
+ "foreground": "#0A0A0A",
13
+ "borderRadius": 16
14
+ },
15
+ "screens": [
16
+ {
17
+ "id": "scr_welcome",
18
+ "name": "Welcome",
19
+ "regions": {
20
+ "body": {
21
+ "id": "lyr_welcome_body",
22
+ "kind": "stack",
23
+ "direction": "vertical",
24
+ "gap": 16,
25
+ "style": { "padding": { "t": 24, "r": 20, "b": 24, "l": 20 } },
26
+ "children": [
27
+ {
28
+ "id": "lyr_welcome_title",
29
+ "kind": "text",
30
+ "text": { "default": "Welcome to Acme" },
31
+ "style": { "fontSize": 28, "fontWeight": 700, "align": "center", "color": "#0A0A0A" }
32
+ },
33
+ {
34
+ "id": "lyr_welcome_body_text",
35
+ "kind": "text",
36
+ "text": { "default": "Set up your account in a few quick steps." },
37
+ "style": { "fontSize": 16, "fontWeight": 400, "align": "center", "color": "#0A0A0A" }
38
+ },
39
+ {
40
+ "id": "lyr_welcome_continue",
41
+ "kind": "button",
42
+ "variant": "primary",
43
+ "action": { "kind": "continue" },
44
+ "direction": "horizontal",
45
+ "align": "center",
46
+ "distribution": "center",
47
+ "children": [
48
+ {
49
+ "id": "lyr_welcome_continue_text",
50
+ "kind": "text",
51
+ "text": { "default": "Get started" },
52
+ "style": { "color": "#FFFFFF" }
53
+ }
54
+ ]
55
+ }
56
+ ]
57
+ }
58
+ },
59
+ "next": { "default": "scr_done" }
60
+ },
61
+ {
62
+ "id": "scr_done",
63
+ "name": "Done",
64
+ "regions": {
65
+ "body": {
66
+ "id": "lyr_done_body",
67
+ "kind": "stack",
68
+ "direction": "vertical",
69
+ "gap": 16,
70
+ "style": { "padding": { "t": 24, "r": 20, "b": 24, "l": 20 } },
71
+ "children": [
72
+ {
73
+ "id": "lyr_done_title",
74
+ "kind": "text",
75
+ "text": { "default": "You are ready" },
76
+ "style": { "fontSize": 24, "fontWeight": 700, "align": "center", "color": "#0A0A0A" }
77
+ },
78
+ {
79
+ "id": "lyr_done_finish",
80
+ "kind": "button",
81
+ "variant": "primary",
82
+ "action": { "kind": "end_flow" },
83
+ "direction": "horizontal",
84
+ "align": "center",
85
+ "distribution": "center",
86
+ "children": [
87
+ {
88
+ "id": "lyr_done_finish_text",
89
+ "kind": "text",
90
+ "text": { "default": "Finish" },
91
+ "style": { "color": "#FFFFFF" }
92
+ }
93
+ ]
94
+ }
95
+ ]
96
+ }
97
+ },
98
+ "next": { "default": null }
99
+ }
100
+ ],
101
+ "decisionNodes": [],
102
+ "externalSurfaceNodes": [],
103
+ "sdkAttributeKeys": []
104
+ }
@@ -0,0 +1,154 @@
1
+ {
2
+ "flowId": "00000000-0000-0000-0000-000000000001",
3
+ "schemaVersion": 7,
4
+ "version": 1,
5
+ "defaultLocale": "en",
6
+ "locales": ["en"],
7
+ "entryScreenId": "scr_intro",
8
+ "theme": {
9
+ "primary": "#18181B",
10
+ "primaryForeground": "#FFFFFF",
11
+ "background": "#FFFFFF",
12
+ "foreground": "#18181B",
13
+ "borderRadius": 16
14
+ },
15
+ "screens": [
16
+ {
17
+ "id": "scr_intro",
18
+ "name": "Intro",
19
+ "regions": {
20
+ "body": {
21
+ "id": "lyr_intro_body",
22
+ "kind": "stack",
23
+ "direction": "vertical",
24
+ "gap": 16,
25
+ "style": { "padding": { "t": 24, "r": 20, "b": 24, "l": 20 } },
26
+ "children": [
27
+ {
28
+ "id": "lyr_intro_title",
29
+ "kind": "text",
30
+ "text": { "default": "Unlock premium" },
31
+ "style": { "fontSize": 26, "fontWeight": 700, "align": "center", "color": "#18181B" }
32
+ },
33
+ {
34
+ "id": "lyr_intro_continue",
35
+ "kind": "button",
36
+ "variant": "primary",
37
+ "action": { "kind": "continue" },
38
+ "direction": "horizontal",
39
+ "align": "center",
40
+ "distribution": "center",
41
+ "children": [
42
+ {
43
+ "id": "lyr_intro_continue_text",
44
+ "kind": "text",
45
+ "text": { "default": "See plans" },
46
+ "style": { "color": "#FFFFFF" }
47
+ }
48
+ ]
49
+ }
50
+ ]
51
+ }
52
+ },
53
+ "next": { "default": "surf_paywall" }
54
+ },
55
+ {
56
+ "id": "scr_premium",
57
+ "name": "Premium success",
58
+ "regions": {
59
+ "body": {
60
+ "id": "lyr_premium_body",
61
+ "kind": "stack",
62
+ "direction": "vertical",
63
+ "gap": 16,
64
+ "style": { "padding": { "t": 24, "r": 20, "b": 24, "l": 20 } },
65
+ "children": [
66
+ {
67
+ "id": "lyr_premium_title",
68
+ "kind": "text",
69
+ "text": { "default": "Premium is ready" },
70
+ "style": { "fontSize": 24, "fontWeight": 700, "color": "#18181B" }
71
+ },
72
+ {
73
+ "id": "lyr_premium_end",
74
+ "kind": "button",
75
+ "variant": "primary",
76
+ "action": { "kind": "end_flow" },
77
+ "direction": "horizontal",
78
+ "align": "center",
79
+ "distribution": "center",
80
+ "children": [
81
+ {
82
+ "id": "lyr_premium_end_text",
83
+ "kind": "text",
84
+ "text": { "default": "Finish" },
85
+ "style": { "color": "#FFFFFF" }
86
+ }
87
+ ]
88
+ }
89
+ ]
90
+ }
91
+ },
92
+ "next": { "default": null }
93
+ },
94
+ {
95
+ "id": "scr_free",
96
+ "name": "Continue free",
97
+ "regions": {
98
+ "body": {
99
+ "id": "lyr_free_body",
100
+ "kind": "stack",
101
+ "direction": "vertical",
102
+ "gap": 16,
103
+ "style": { "padding": { "t": 24, "r": 20, "b": 24, "l": 20 } },
104
+ "children": [
105
+ {
106
+ "id": "lyr_free_title",
107
+ "kind": "text",
108
+ "text": { "default": "Continue with free" },
109
+ "style": { "fontSize": 24, "fontWeight": 700, "color": "#18181B" }
110
+ },
111
+ {
112
+ "id": "lyr_free_end",
113
+ "kind": "button",
114
+ "variant": "secondary",
115
+ "action": { "kind": "end_flow" },
116
+ "direction": "horizontal",
117
+ "align": "center",
118
+ "distribution": "center",
119
+ "children": [
120
+ {
121
+ "id": "lyr_free_end_text",
122
+ "kind": "text",
123
+ "text": { "default": "Finish" },
124
+ "style": { "color": "#FFFFFF" }
125
+ }
126
+ ]
127
+ }
128
+ ]
129
+ }
130
+ },
131
+ "next": { "default": null }
132
+ }
133
+ ],
134
+ "decisionNodes": [],
135
+ "externalSurfaceNodes": [
136
+ {
137
+ "id": "surf_paywall",
138
+ "name": "RevenueCat paywall",
139
+ "config": {
140
+ "provider": "revenuecat",
141
+ "offeringId": "default",
142
+ "presentation": "paywall"
143
+ },
144
+ "outcomes": {
145
+ "purchase_completed": "scr_premium",
146
+ "restore_completed": "scr_premium",
147
+ "dismissed": "scr_free",
148
+ "failed": "scr_free"
149
+ },
150
+ "fallback": "scr_free"
151
+ }
152
+ ],
153
+ "sdkAttributeKeys": []
154
+ }
@@ -0,0 +1,79 @@
1
+ # Animation import
2
+
3
+ Map motion from the customer codebase into Rheo’s **existing** animation IR. Do not invent custom keyframe DSL or cross-screen transitions.
4
+
5
+ ## Gates (required)
6
+
7
+ 1. **Intake Q6** — “Match motion from the codebase (may differ slightly from Rheo presets)?”
8
+ - **Yes** → apply motion from the audit (steps below).
9
+ - **No** → omit all `screen.animations`, `screen.stagger`, and layer `restingMotion` / `restingMotions`.
10
+
11
+ 2. **Plan** — Animations require **Grow+** (`entitlements.animations`).
12
+ - Dashboard bundle import strips motion when the workspace plan lacks animations.
13
+ - Offline agent imports: confirm plan in chat; strip motion before zip if Indie.
14
+
15
+ ## Rheo surfaces
16
+
17
+ | Surface | Manifest fields |
18
+ |---------|-----------------|
19
+ | Screen clips | `screen.animations[]` — `trigger`: `mount` \| `stagger` \| `unmount` |
20
+ | Stagger gap | `screen.stagger.stepMs` when any clip uses `trigger: stagger` |
21
+ | Resting loops | `layer.restingMotion` — presets: `translate`, `bounce`, `scale`, `pulse`, `rotate` (conservative only) |
22
+ | Lottie / video | **Assets only** — never duplicate as timeline clips |
23
+
24
+ **Out of scope:** `ScreenTransition`, CSS/Framer, gesture-driven pan/pinch, physics-accurate springs.
25
+
26
+ ## Audit workflow
27
+
28
+ ```bash
29
+ node scripts/audit-import.mjs --entry <screen.tsx> --root <repo> \
30
+ --out rheo-import.audit.md \
31
+ --suggest-animations rheo-import.animations.json
32
+ ```
33
+
34
+ Read **Animation And Motion Evidence** in the audit. Use `rheo-import.animations.json` for preset ids, `targetLayerId` hints, `durationMs`, and `staggerIndex`.
35
+
36
+ ### Preset ids (mount)
37
+
38
+ | Preset | Source hint |
39
+ |--------|-------------|
40
+ | `fade-in` | opacity 0→1 |
41
+ | `slide-up` | translateY +16→0 |
42
+ | `slide-down-in` | translateY -16→0 |
43
+ | `slide-in-left` / `slide-in-right` | translateX ±24→0 |
44
+ | `scale-in` | scale + opacity |
45
+
46
+ ### Preset ids (unmount)
47
+
48
+ `fade-out`, `slide-up-out`, `slide-down-out`, `slide-left-out`, `slide-right-out`, `scale-out`
49
+
50
+ ### Stagger
51
+
52
+ Only when source has **explicit** stagger (`staggerChildren`, delay arrays, Reanimated stagger). One clip per child: `trigger: stagger`, `staggerIndex` 0..n, shared preset.
53
+
54
+ ### Unmappable motion
55
+
56
+ Springs (`withSpring` custom damping), unknown curves → **omit** from manifest; keep audit note only. Do not pick “nearest” presets.
57
+
58
+ ### Timing
59
+
60
+ Use source `durationMs` / `delayMs` when 50–800ms; otherwise preset defaults (mount ~320ms, unmount ~280ms).
61
+
62
+ ## Manifest authoring
63
+
64
+ 1. Resolve `layer:<screenSlug>:<role>` hints to real layer ids on each screen.
65
+ 2. Build clips with full `tracks` (copy from suggest JSON or builder-equivalent shapes).
66
+ 3. Clip `id`: `clip-<preset>-<layerId-slug>`.
67
+ 4. Max ~12 clips per screen; prioritize hero + stagger children.
68
+ 5. Run `node scripts/validate-manifest.mjs ./rheo-import.manifest.json` — fixes `animation_target_layer_missing`, `animation_stagger_index_required`, etc.
69
+
70
+ ## Validation codes (warnings)
71
+
72
+ | Code | Meaning |
73
+ |------|---------|
74
+ | `animation_target_layer_missing` | `targetLayerId` not on screen |
75
+ | `animation_stagger_index_required` | `trigger: stagger` without `staggerIndex` |
76
+ | `animation_unmount_stagger_forbidden` | `unmount` clip has `staggerIndex` |
77
+ | `animation_duration_out_of_range` | `durationMs` outside 50–800 |
78
+
79
+ Publish gates do **not** require animations for publish.
@@ -0,0 +1,107 @@
1
+ # Rheo Capability Surface
2
+
3
+ > Generated from `@getrheo/contracts`. Do not hand-edit — run `pnpm --filter @getrheo/rheo-skill gen:capabilities`.
4
+
5
+ Manifest schema version: 7
6
+
7
+ Only the kinds, actions, permissions, and variants listed here are valid. Never invent a layer kind, action,
8
+ permission key, or variant — the dashboard import runs full Zod validation and rejects anything not below.
9
+
10
+ ## Layer kinds
11
+
12
+ Every layer `kind` accepted by the manifest:
13
+
14
+ - `stack`
15
+ - `text`
16
+ - `image`
17
+ - `lottie`
18
+ - `video`
19
+ - `icon`
20
+ - `button`
21
+ - `back_button`
22
+ - `progress`
23
+ - `loader`
24
+ - `counter`
25
+ - `single_choice`
26
+ - `multiple_choice`
27
+ - `text_input`
28
+ - `scale_input`
29
+ - `oauth_provider`
30
+ - `oauth_login`
31
+ - `email_password_auth`
32
+ - `email_password_field`
33
+ - `email_password_submit`
34
+ - `carousel`
35
+ - `hyperlink`
36
+ - `checkbox`
37
+
38
+ Container layers that **must** include a `children` array (or `slides` for carousel): `stack`, `carousel`,
39
+ `button`, `back_button`, `hyperlink`, `single_choice`, `multiple_choice`, `oauth_login`,
40
+ `oauth_provider` (custom variant), `email_password_auth`, `email_password_field`, `email_password_submit`.
41
+
42
+ ## Button / back_button variants
43
+
44
+ Allowed `variant` values (required on `button` and `back_button`): `primary`, `secondary`, `ghost`, `destructive`.
45
+
46
+ Map source/framework variants: `outline`/`bordered`/`tertiary` -> `secondary` or `ghost`; `text`/`link`/`plain`
47
+ -> `ghost`; `default`/`filled`/`solid` -> `primary`; `danger`/`error` -> `destructive`.
48
+
49
+ ## Button actions
50
+
51
+ Valid `action.kind` values on `button` layers:
52
+
53
+ - `none`
54
+ - `continue`
55
+ - `skip`
56
+ - `end_flow`
57
+ - `go_back_one_screen`
58
+ - `go_to_step`
59
+ - `request_os_permission`
60
+ - `play_media`
61
+ - `request_app_review`
62
+
63
+ - `go_to_step` requires `screenId`.
64
+ - `go_back_one_screen` accepts optional `fallbackScreenId`.
65
+ - `request_os_permission` requires `permissionKey` and `outcomes` (`granted`/`denied`/`blocked`).
66
+ - `play_media` requires `targetLayerIds` (≥1) pointing at Lottie/video layers on the same screen.
67
+ - `back_button` takes **no** `action` (back navigation is built in).
68
+
69
+ ## OS permission keys
70
+
71
+ Valid `permissionKey` values for `request_os_permission`:
72
+
73
+ - `notifications`
74
+ - `camera`
75
+ - `microphone`
76
+ - `photo_library`
77
+ - `contacts`
78
+ - `calendar`
79
+ - `reminders`
80
+ - `location_when_in_use`
81
+ - `location_always`
82
+ - `motion`
83
+ - `bluetooth`
84
+ - `app_tracking_transparency`
85
+ - `speech_recognition`
86
+ - `face_id`
87
+ - `health_kit`
88
+ - `media_library`
89
+ - `local_network`
90
+ - `nearby_interactions`
91
+ - `nfc`
92
+ - `full_screen_intent_android`
93
+ - `sms_android`
94
+ - `phone_android`
95
+
96
+ ## Inputs and auth
97
+
98
+ - `text_input` types: `plain`, `email`, `phone`, `url`, `multiline`.
99
+ - `text_input` classification: `safe`, `sensitive`.
100
+ - `oauth_login` preset providers: `github`, `google`, `apple`.
101
+ - `email_password_auth` modes: `sign_in`, `sign_up` (sign_up requires email + password + confirm fields).
102
+ - `icon` families: `ionicons`.
103
+
104
+ ## External surface outcomes (RevenueCat)
105
+
106
+ Normalized outcome keys for `externalSurfaceNodes[].outcomes`: `purchase_completed`, `purchase_cancelled`, `dismissed`, `failed`, `restore_completed`.
107
+ Every external surface also needs a `fallback` jump target.