# `supero-mobile.jsx` Layered Split — Verification & Manifest

Mirror of the [`web/`](../web) layout, applied to the React Native /
Expo variant `../supero-mobile.jsx`. 17 files in 5 folders, semantic
layering (api / services / ui-core / ui-service / ui-app), verified by
6-pass checker.

> **Canonical untouched.** `../supero-mobile.jsx` is unchanged. Nothing
> in any Expo app references `mobile/` — these files are an
> editing/reading aid only. The Expo app continues to bundle
> `supero-mobile.jsx` exactly as before.

The difference vs the web split (`web/`):
- Source uses **ES module exports** (`export class X`, `export const X = ...`)
  instead of `window.X = X` — verification checks `export` parity not
  `window.*` parity.
- File extension `.jsx`, not `.js`.
- No `node -c` parse check (JSX needs babel/esprima which we don't run by
  default). Pass 4 (per-symbol byte preservation) is the meaningful proof.

---

## TL;DR

- **Original:** `../supero-mobile.jsx` — 6,499 lines, 359,796 bytes
  (pre-port baseline 4,177 lines / 246,927 bytes / md5
  `59e039fbeb1651bcb8ee596e219130d1`, preserved as `.bak.phase1.*`)
- **Layered split:** 22 files in 5 folders, +95 lines from banners, 366,569 bytes
- **Top-level symbols:** 161 (was 98 pre-port, +63 / +64% growth)
- **Verification:** `python3 _verify_layered_mobile.py` — 6 passes, all green

**Phase 1 ports (data/contract layer, no RN UI):**
- `formatRelativeTime` — "5m ago" / "yesterday" timestamp formatter
- State machine helpers — `getEnabledOperations`, `parseStateTransitionError`, `getStateMetadata`, `_stateMachineLabel`
- Service-status cache upgrade — `FIX_S5` (30s soft-expire on error) + `FIX_M8` (reset-token to drop stale in-flight)
- Schema registry + qualifier — `SuperoSchemaRegistry` (load/get/references/parents/stateMachine/stateMachineFor), `_SYSTEM_TYPES`, `_qualifySchema` (idempotent — backward-compatible)
- `SuperoClient.init()` now kicks off `SuperoSchemaRegistry.load()` in background
- `SuperoClient` CRUD methods now call `_qualifySchema` so apps can pass either `'subject'` or `'tenant_corp:subject'`
- `CONFIG.APP_NAMESPACE` — new optional config field; fallback namespace when registry hasn't loaded yet

**Phase 2 ports (RN UI components):**

| Sub-batch | What was added | Notes |
|-----------|----------------|-------|
| 2.1 AI Chat | `aiChatStream` (XHR-based streaming, no new deps), `aiConfirmAction`/`aiRejectAction`, file/format/history helpers (`createFileFromEvent` returns data URI; AsyncStorage-backed history), `AIChatPanel` RN component with streaming + tool steps + confirmation gates + file events | Replaces web's `aiChatStream` (uses SSE via fetch ReadableStream which RN doesn't fully support). XHR works in RN without polyfills. |
| 2.2 Modal/Initiator | `SuperoModal` programmatic API (host-component pattern, no DOM), `SuperoModalHost` (auto-mounted in `MobileShell`), initiator-flow helpers (`_resolveInitiatorRef`/`Prefill`/`Refs`/`Template`/`_validateInitiatorFields`), `_renderInitiatorField` (RN-adapted, uses `TextInput`/`InlineSelect`/`Switch`), `_runInitiatorFlow`, `_deriveUuidField`, `openInitiatorFormModal`, `_InitiatorFormBody` | Web's `SuperoModal` uses `document.createElement` + `ReactDOM.createRoot`. RN replacement: module-level setter installed by `<SuperoModalHost />`. Same API surface (`SuperoModal.open({title, body, footer})` returns `{close, promise}`); same `confirm()` shortcut. |
| 2.3 Workflow widgets + showToast | `showToast` + `SuperoToastHost` (RN toast with slide-in animation, auto-mounted in `MobileShell`), `WorkflowBadge`, `WorkflowTriggerButton`, `SvgBarChart` (View-based, no SVG dep), `WorkflowTestPanel` + `_WfResults`, `SuperoWidgetRegistry`, `RecentListWidget` | `showToast` falls back to `Alert.alert` if host isn't mounted. `SvgBarChart` is a horizontal bar chart using View widths — no `react-native-svg` required (install + swap if you want true SVG). |
| 2.4 Field inputs | `_toArrayValue`, `_coerceListFields`, `ImageFieldInput` (optional `expo-image-picker` via dynamic require), `ImageListFieldInput`, `ParentChainSelector` (uses `SchemaRegistry.parents()`), `TagListInput` (chip input), `CheckboxGrid` | Image picker is OPTIONAL — works without `expo-image-picker` (shows URL-only fallback). Add to your app's `package.json` for native picking. `BrowseList` + `ReferenceDropdowns` skipped — web-layout-specific. |
| 2.5 Invoice | `InvoicePayPanel` — Stripe checkout via `Linking.openURL` (system browser), AsyncStorage-tracked pending invoice, manual payment recording, payment history list | No `@stripe/stripe-react-native` dependency. Use hosted Stripe checkout URL. For native pay sheet, swap `handleStripePayment` to use the Stripe RN SDK. |
| 2.6 UX overlay | `ACTION_OPS_UX` (data catalog for friendly result rendering), `_suiUxOverlay`, `_suiInterpolateResult`, `_suiRenderResultTemplate` (RN-rendered success card) | Pure data + render helper. Wire into your own action form by passing the rendered element above the JSON result. |

**Auto-mounted hosts:** `<SuperoModalHost />` and `<SuperoToastHost />` are mounted inside `MobileShell` automatically. Apps that bypass `MobileShell` must mount them themselves.

**Optional dependencies (work without them):**
- `expo-image-picker` — for native image picking in `ImageFieldInput`/`ImageListFieldInput`. Without it: URL-only fallback. Suggested if your app needs photos.
- `@stripe/stripe-react-native` — would replace `Linking.openURL` Stripe flow with native pay sheet in `InvoicePayPanel`. Not currently used; the port works via external browser.
- `react-native-svg` — would let `SvgBarChart` render real SVG instead of `<View>`-based bars. Not currently used.

```
mobile/
├── 00-header.jsx                     ← imports, Stack/Tab navigators, config
├── api/                              layer 10 — data contract + transport
│   ├── 10-client.jsx                   SuperoClient + client singleton
│   └── 11-helpers.jsx                  formatters, resolveImageUrl, status colors
├── services/                         layer 20 — service execution
│   ├── 20-map.jsx                      _svc, services map, aiChat
│   ├── 21-auth.jsx                     AuthContext, AuthProvider, useAuth
│   ├── 22-catalog.jsx                  SERVICE_MANIFESTS, SuperoServiceRegistry, ACTION_OPS
│   └── 23-transactional.jsx            SuperoTransactionalRegistry, Txn errors, classifier
├── ui-core/                          layer 30 — UI primitives
│   ├── 30-theme.jsx                    palette generators, ThemeContext, useTheme
│   └── 31-primitives.jsx               SuperoErrorBoundary, InlineSelect + styles
├── ui-service/                       layer 40 — service-aware UI
│   ├── 40-helpers.jsx                  buildFieldDefs, parse422Error, triggerWorkflow
│   ├── 41-service-action.jsx           ServiceWidget, _sui* helpers, _ServiceActionForm/Result
│   └── 42-transactional.jsx            TxnStateBadge, TxnActionButton, TxnActionSheet
└── ui-app/                           layer 50 — screens + navigation
    ├── 50-screens-auth.jsx             LoginScreen, SignupScreen
    ├── 51-screens-data.jsx             Dashboard/List/Detail/Form screens
    ├── 52-screens-other.jsx            AIChatScreen, SettingsScreen
    ├── 53-screens-transactional.jsx    TransactionalList/Detail screens
    └── 54-shell.jsx                    MobileShell + nav stacks + version
```

**Load order: `00-header.jsx`, then `api/*`, `services/*`, `ui-core/*`,
`ui-service/*`, `ui-app/*` in that exact folder sequence.** Same caveat
as `web/`: alphabetical sort would put `ui-app` before `ui-core`, so
the folder sequence must be enumerated explicitly.

---

## File manifest

| Layer | Path | Slots | Lines | Bytes | Contents |
|-------|------|-------|-------|-------|----------|
| 00 | `00-header.jsx` | 6 | 361 | 17,563 | File header docstring, React + react-native + nav imports, `Stack`/`Tab` navigators, `DEFAULT_CONFIG`/`CONFIG`/`setConfig`/`getConfig` |
| 10 | `api/10-client.jsx` | 2 | 147 | 6,911 | `SuperoClient` class, `client` singleton |
| 10 | `api/11-helpers.jsx` | 8 | 70 | 4,180 | `formatCurrency`/`Number`/`Date`/`DateTime`/`FieldValue`, `resolveImageUrl`, `STATUS_COLORS`, `getStatusStyle` |
| 20 | `services/20-map.jsx` | 3 | 132 | 10,119 | `aiChat`, `_svc`, `services` map |
| 20 | `services/21-auth.jsx` | 3 | 85 | 3,811 | `AuthContext`, `AuthProvider`, `useAuth` |
| 20 | `services/22-catalog.jsx` | 10 | 676 | 67,287 | `SERVICE_MANIFESTS`, `_archetypes`, `_BACKEND_ID_ALIAS`, `SuperoServiceRegistry`, services-status cache, `_superoBackendIdForKey`, `__superoStatusReset`, `ACTION_OPS` |
| 20 | `services/23-transactional.jsx` | 15 | 462 | 23,984 | `_txnCacheM`, `_txnReadyM`, `_txnLoadStartedM`, `_txnErrorHandlersM`, `SuperoTransactionalRegistry`, `TxnServiceError` + 5 subclasses, `_classifyHttpErrorM`, `_txnOpCategoryM`, `_txnBackoffM`, `_txnRunActionM` |
| 30 | `ui-core/30-theme.jsx` | 5 | 70 | 3,006 | `_hexToHSL`, `_hslToHex`, `generatePalette`, `ThemeContext`, `useTheme` |
| 30 | `ui-core/31-primitives.jsx` | 3 | 97 | 5,369 | `SuperoErrorBoundary`, `InlineSelect`, `selS` (StyleSheet) |
| 40 | `ui-service/40-helpers.jsx` | 3 | 38 | 2,100 | `buildFieldDefs`, `parse422Error`, `triggerWorkflow` |
| 40 | `ui-service/41-service-action.jsx` | 15 | 694 | 31,684 | `_ServiceStatusBadge`, `ServiceWidget`, `_sui*` helpers (type-render, interpolate, validation, field label/render, output secret sanitization), `_isSafeUrl`, `_ServiceActionResult`, `_ServiceActionForm` |
| 40 | `ui-service/42-transactional.jsx` | 8 | 161 | 7,110 | `TXN_PALETTES_M`, `txnColorM`, `TxnStateBadge`, `TxnActionButton`, `TxnActionSheetM`, `_txnDefaultErrorMessageM`, `_qualifySchemaNameM` |
| 50 | `ui-app/50-screens-auth.jsx` | 2 | 126 | 8,257 | `LoginScreen`, `SignupScreen` |
| 50 | `ui-app/51-screens-data.jsx` | 4 | 505 | 30,568 | `DashboardScreen`, `ListScreen`, `DetailScreen`, `FormScreen` |
| 50 | `ui-app/52-screens-other.jsx` | 2 | 108 | 6,348 | `AIChatScreen`, `SettingsScreen` |
| 50 | `ui-app/53-screens-transactional.jsx` | 2 | 428 | 18,748 | `TransactionalListScreen`, `TransactionalDetailScreen` |
| 50 | `ui-app/54-shell.jsx` | 8 | 131 | 6,197 | `MobileConfigContext`, `useMobileConfig`, `_AuthStack`, `_SchemaStack`, `_MainTabs`, `_Root`, `MobileShell`, `SUPERO_MOBILE_VERSION` |

**Totals:** 17 files, 98 top-level symbols, 38 ES exports, 4,262 lines, 252,146 bytes.

---

## Verification (run any time)

```bash
cd ~/supero/platform/infra/libs/py/src/supero/ui/mobile

# 1. Re-run the splitter (deterministic — same files come back)
python3 _layer_mobile.py

# 2. Run the 6-pass verifier
python3 _verify_layered_mobile.py
# expected: "RESULT: ALL PASSES GREEN ✓"
```

### What the 6 passes check

| Pass | What it verifies | Why it matters |
|------|------------------|----------------|
| 1. File existence + UTF-8 | All 17 layer files present and decode cleanly | basic sanity |
| 2. Top-level symbol coverage | All 98 unique top-level symbols defined in exactly one layer file | catches lost/extra symbols |
| 3. ES export parity | All 38 `export class/function/const/let/var X` declarations preserved (same set) | runtime contract preserved (consumers `import { ... } from './supero-mobile'`) |
| 4. Per-symbol content preservation | Each symbol's canonical byte range appears in its assigned layer file | proves no symbol body was modified |
| 5. Newline termination | Each file ends with `\n` | shell `cat` join preserves line grammar |
| 6. Size sanity | joined size == canonical size + sum of per-file banners | proves the joined output is exactly canonical's content plus banner overhead, nothing more |

Note: a *real* JSX parse check (via babel/esprima) is not run by default
because neither is installed. To do it manually:

```bash
cd mobile
cat 00-header.jsx api/*.jsx services/*.jsx ui-core/*.jsx \
    ui-service/*.jsx ui-app/*.jsx > /tmp/mobile.joined.jsx
# Install once: npm install -g @babel/parser
node -e "require('@babel/parser').parse(require('fs').readFileSync('/tmp/mobile.joined.jsx','utf8'), {sourceType:'module', plugins:['jsx']})"
# silent = success; any output = parse error
```

---

## Re-joining (to ship a change)

```bash
cd ~/supero/platform/infra/libs/py/src/supero/ui/mobile

# Folder order matters — alphabetical would put ui-app before ui-core.
cat 00-header.jsx \
    api/*.jsx \
    services/*.jsx \
    ui-core/*.jsx \
    ui-service/*.jsx \
    ui-app/*.jsx \
    > /tmp/supero-mobile.layered.jsx

diff -u ../supero-mobile.jsx /tmp/supero-mobile.layered.jsx | head -200  # see the moves

# To overwrite canonical (only after Expo Go smoke-test):
cp ../supero-mobile.jsx ../supero-mobile.jsx.bak.$(date +%Y%m%d-%H%M%S)
cp /tmp/supero-mobile.layered.jsx ../supero-mobile.jsx
md5sum ../supero-mobile.jsx   # new md5: 272a5fee5d78daf2b3f70e125e85eb71
```

**Recommendation: do not propagate until you've smoke-tested in Expo
Go.** Pass 6 proves byte preservation, not behavioral equivalence.
A symbol whose evaluation order changes (e.g., a `const X = someExpr()`
moved earlier than where `someExpr` initializes) could break at app
launch. Test the user-facing flows (auth → dashboard → list → detail →
form → service action → txn list/detail) before shipping.

---

## What's different from the web split

| Aspect | Web (`web/`) | Mobile (`mobile/`) |
|--------|----------------|--------------------------|
| Source | `supero-ui.js` (18,717 lines) | `supero-mobile.jsx` (4,177 lines) |
| Module system | Global script (`window.X = X`) | ES modules (`export X`) |
| Output extension | `.js` | `.jsx` |
| Layer files | 24 | 17 |
| Runtime | Web browser, single `<script>` tag | React Native, bundled by Expo Metro |
| Parse check | `node -c joined.js` (works) | `node -c` doesn't grok JSX; pass 4 byte-preservation is the proof |
| Verbatim backup | `web/_verbatim_backup/` (15 files) | None — `_layer_mobile.py` is deterministic and `../supero-mobile.jsx` is the immutable source of truth |
| Small moves applied | React aliases hoisted, `ChildListPanel`/`SuperoFieldEditor` moved out of api | None — every symbol stays in the layer the file naturally fell into |

---

## Files in this directory

| Path | Role |
|------|------|
| `_layer_mobile.py` | The mobile splitter. Reads `../supero-mobile.jsx` + `LAYER_MAP` dict, writes 17 layered files. Auto-creates folders. |
| `_verify_layered_mobile.py` | The 6-pass verifier. Imports from `_layer_mobile.py`. |
| `VERIFICATION.md` | This document. |
| `00-header.jsx` | Single root-level file (loads first). |
| `api/*.jsx` | Layer 10 — 2 files. |
| `services/*.jsx` | Layer 20 — 4 files. |
| `ui-core/*.jsx` | Layer 30 — 2 files. |
| `ui-service/*.jsx` | Layer 40 — 3 files. |
| `ui-app/*.jsx` | Layer 50 — 5 files. |
