Skip to content

Surface Platform

Vinxi Surface Platform — Surface IR, the runtime, and the shells

Status: Accepted design (grill session, 2026-07-02). Each decision record below is marked Accepted (locked by Nikhil) or Provisional (standing recommendation; cheap to revisit before implementation starts, expensive after). Audience: engineers building the mobile and web shells and the Surface runtime; kernel engineers who own the seam-side dependencies this doc creates (P6 media, §15 merge); anyone reviewing the LLD §21 IR-3 amendment. Companion docs: lld.md (§17.1 Surfaces, §21 IR-3, §22 offline-is-a-branch), docs/architecture/ui-lexicon.md (shell/slot/three-planes design — reused, not superseded), the M1 plan (superpowers/plans/2026-07-02-m1-walking-skeleton.md).


1. Purpose

The LLD settles what the UI layer is at the architecture level: applications are Surface documents — declarative, ontology-validated, package-shipped — and shells interpret them (“shells ship once, realities ship Surfaces”, §17.1). It does not settle what the shells are built with, where the interpreter lives, how mobile and web share it, or what order any of it is built in. This doc settles those questions, records why, and defines the contracts between the three layers of the UI stack.

One principle generates most of the answers, so it goes first:

The one-interpreter principle. There is exactly one implementation of the Surface IR interpreter, written in TypeScript, shared verbatim by every shell. Generative UI — agents authoring Surfaces — is only trustworthy if a validated document renders identically everywhere. Two interpreters is a permanent divergence bug class; we never open it.

2. Glossary

TermMeaning
SurfaceA declarative application definition: a document whose data slots are IR-2 query references and whose buttons are Action references. Package content, validated against the ontology at install.
Surface IR (IR-3)The versioned document format Surfaces are written in. One of the kernel’s three IRs (LLD §21).
Runtime@vinxi/surface-runtime — the headless TS interpreter: parses/validates Surface docs, resolves queries, computes capability visibility, dispatches Actions, owns view-state. No rendering.
ShellA per-platform host that renders what the runtime resolves: the Expo app (mobile), the web app. A shell = slot layout + leaf widgets + platform services (camera, GPS, storage).
SlotA canonical named screen region (stage · primary · focus · drawer · hud · command · nav · status) each shell renders in its native idiom (Lexicon doc §3).
WidgetA leaf renderer for one Surface node or projection, registered per platform against the shared registry contract.
WorkspaceSurface IR archetype node: a collection query + a projection set + a focus contract.
ProjectionHow a Workspace’s collection draws: map · table · list · cards · board · timeline · chart. Several render at once, sharing one focus.
CompositionSurface IR archetype node: a card canvas; each card is a Workspace query at glance altitude (dashboards, briefings).
DialsCross-cutting view-state: focus · altitude · time · layers. Shell state, never document content. URL-encoded on web, deep-link-encoded on mobile.
Device branchThe implicit logical branch a device opens when offline; queued writes are proposed-class Acts; reconnect = §15 merge (LLD §22).
Adjudication WorkItemThe work item a merge conflict becomes, routed by §12 machinery to the field worker or supervisor.
sdkgen SDKThe branded TypeScript SDK generated from Reality IR by M1 Task 10. The runtime’s only dependency on the kernel repo.

3. Overview

Reality Package (per customer/domain)
┌──────────────────────────────────┐
│ ontology · actions · policies · │
│ workflows · SURFACE DOCUMENTS · │
│ theme tokens · fixtures │
└───────────────┬──────────────────┘
│ validated at install (P0)
LAYER 1 @vinxi/surface-runtime (headless TS — written ONCE)
parse/validate Surface IR · resolve IR-2 queries · capability
visibility · Action dispatch · view-state (dials) · offline
read-replica + device-branch queue · subscribe() facade
LAYER 2 widget registry contract (types written once; implementations per platform)
node kind → widget · trait kind → widget offer · package-registered widgets
┌────────────┴─────────────┐
▼ ▼
LAYER 3 MOBILE SHELL (leads) WEB SHELL (follows)
Expo / React Native React DOM (vinxi app-ui chrome:
PhoneShell slots · MapLibre Portkey slots · Items · Dashboard)
native Stage · vision-camera MapLibre GL JS / deck.gl Stage
op-sqlite · deep links URL-encoded dials
│ │
└────────────┬─────────────┘
lepton seam (sdkgen SDK · Connect HTTP)
Acts · Queries · presigned media URLs · sync tokens

Layers 1 and 2 (the logic and the contracts) exist once. Layer 3 is per-platform and dumb: leaf widgets and slot layout only. The mobile shell is built first and hosts the reference interpreter; the web shell is the proof that the interpreter was actually shared.

4. Requirements

4.1 Product requirements

#RequirementSourceHow this design meets it
R1Dynamic apps on the fly, per-customer UILLD §17.1 founding requirementSurfaces are package content interpreted at runtime; no per-customer shell builds
R2Generative UI (agent-authored)LLD IR-3 “agent-authorable”Agents emit Surface IR documents against a JSON schema projected from IR-1 — never code; one interpreter renders them identically everywhere
R3Config/workflow-driven UIPRD low-code trend; §14.2Process-instance-as-entity makes workflow state ordinary IR-2 queries; TaskInbox is a Workspace preset — no separate workflow-UI system
R4Themes, per-tenant customizationBRD; design system@vinxi/design tokens are the single vocabulary; tenant/package token overlays; both shells read tokens through a provider
R5Web (React) + mobile from one platformthis sessionOne TS runtime; RN + React DOM shells
R6Field capture: survey, barcode, geo-photo, splicingBRD §3.2.2Capture path (§5.5); vision-camera, expo-location; media traits via P6 presigned URLs
R7Offline in dead zones, no silent data lossBRD; LLD §22Read-replica sync + device branches; queued proposed Acts; merge-time adjudication
R8Capability-aware rendering (no hand-coded role logic)LLD IR-3(b)Runtime computes visibility from the session’s acquirable capabilities
R9Kind-driven widget offers (schema → forms)LLD IR-3(c)Registry maps parameterized kinds to widgets: geo(point) → map picker, categorical(hierarchical) → tree filter, media → gallery/camera

4.2 Journeys the design must serve

  1. Field viewer. A Jio technician opens the app, sees the fiber network on a full-bleed map Stage with a snap-point drawer list, taps a joint, gets its DetailCard, sees her assigned work in the TaskInbox.
  2. Field capture. She surveys a new pole: fills a schema-generated form, scans the barcode, the GPS stamps a geo trait, photographs the fixture (EXIF becomes an observed-location Assert). All of it lands as Acts through the seam.
  3. Dead zone. She descends into a basement vault. The map keeps rendering from offline tile packs; her splice record queues on the device branch. On reconnect, clean Acts commit; one conflict (“canon says this joint was retired at 03:40”) arrives in her TaskInbox as an adjudication item.
  4. Agent-authored Surface. Sanjaya (or a customer’s agent) authors a “storm damage triage” Surface for a one-week operation. It validates against the ontology at install and renders in the same shells with zero shell changes.
  5. Tenant theme. A customer ships a token overlay in their package; every Surface renders in their palette on both platforms without touching a widget.

4.3 Gates

GateStatement
SG-1 (field-beta gate)The app does not deploy to field hands until all three capture tiers work in a dead zone: (a) entity/attribute capture, (b) photo/media capture, (c) offline capture with merge. The legacy Flutter app covers the field until then. Locked by Nikhil.
SG-2 (month-6 shape)The 6-month Jio milestone shows a dogfood/demo app over live pilot-spine data — not a field deployment. Follows from SG-1.
SG-3 (interpreter CI gate)Golden-fixture suite: Surface doc in → resolved render tree out, snapshot-compared. Green suite is the merge gate for every runtime change and the compat proof for every Surface IR version bump.

4.4 Constraints

  • Stack: Rust kernel, Java services, TS userland (established). The vinxi monorepo (pnpm, all web TS today) holds app-ui, design, tunnel (Portkey), maps-ui.
  • Timeline: 6-month Jio pilot milestone; M1 (kernel walking skeleton) ships no UI but does ship the Act/Query seam and the sdkgen TS SDK the runtime consumes.
  • Legacy: the existing Flutter field app keeps running during the strangler period; it is marked Redesign in the PRD and receives no new investment.
  • The low-code cliff (LLD register #15): Surface IR stays declarative and closed; pressure for expressiveness routes to sandboxed custom widgets, never to schema conditionals. Health metric: the ratio of custom-widget slots to declarative nodes across shipped Surfaces.

5. Architecture

5.1 Layer 1 — @vinxi/surface-runtime (headless interpreter)

Pure TypeScript, no rendering, no platform APIs. Responsibilities:

  1. Parse + validate Surface documents against the Surface IR JSON schema (projected from IR-1, so validation is realm-aware: a binding to a nonexistent trait fails here).
  2. Resolve data slots: each Workspace collection and DetailCard section is an IR-2 query reference; the runtime binds it through the sdkgen SDK and exposes a subscribe() handle (§5.4) whose transport is invisible to widgets.
  3. Capability visibility: for every Action reference, compute show/enable/hide from the session’s acquirable capabilities — the same P0 vocabulary the kernel enforces with. No role conditionals exist anywhere in shell code.
  4. Action dispatch: button press → Act through the seam (or onto the device-branch queue when offline), including Pending(approval) outcomes surfaced as runtime states.
  5. View-state (dials): owns the typed {focus, altitude, time, layers, arrangement} object and its serialization grammar (§5.6).
  6. Offline: owns the read-replica sync protocol (Surface-declared queries → SQLite tables with freshness stamps, via a storage interface the shell implements) and the device-branch act queue (§5.5).

The runtime’s only kernel-repo dependency is the versioned sdkgen SDK. That is the entire cross-repo contract.

5.2 Layer 2 — the widget registry contract

A typed contract, defined once in TS, implemented per platform:

  • Node registry: Surface node kind → widget (Workspace, Composition, Page, Panel, Form, DetailCard) and projection kind → widget (map, table, list, cards, board, timeline, chart).
  • Offer table: parameterized trait kind → input/display widget (geo(point) → map picker/pin, categorical(scheme) → select or tree filter, media → gallery + camera capture, temporal → date/range). This is what makes Forms schema-generated: a Form node lists trait refs; the offer table supplies the widgets.
  • Package widgets (deferred): the registration point where a Reality Package’s sandboxed custom widgets (P9-class modules, verb-only imports) will plug in. The contract is designed now; only built-in widgets ship in the pilot (SUR-10).

Widgets are dumb by contract: they receive resolved data, resolved capability state, and theme tokens; they emit intents (focus(entity), dispatch(actionRef, payload), setDial(...)) back to the runtime. A widget never queries, never checks a capability, never reads a theme constant.

5.3 Layer 3 — the shells

Mobile shell (leads). Expo / React Native, in the vinxi monorepo. The Lexicon doc’s PhoneShell composition is the heart: full-bleed MapLibre Stage (native, GPU; RN is chrome around it), snap-point bottom drawer (peek→half→full) for list projections, push pages for focus, bottom-bar module nav, hud slot reserved for Sanjaya (present in the slot contract from day one, empty until agents ship). Platform services: react-native-vision-camera (camera + barcode), expo-location, op-sqlite (replica + act queue + Surface cache), MapLibre OfflineManager / PMTiles region packs pulled at sync time. Android-first EAS builds (Jio’s field fleet); iOS rides along. OTA updates via EAS Update for shell fixes between store releases.

Web shell (follows). React DOM, reusing app-ui chrome — Portkey slots, Items, Dashboard, ⌘K command bar (~65% reusable per the Lexicon doc §7). Same runtime, same registry contract, DOM widget implementations. Built once the RN beta stabilizes; its existence is the test that layers 1–2 were genuinely platform-free.

Watch/TV/VR (Lexicon shells) are out of scope until after the pilot; nothing in the slot contract precludes them.

5.4 Data layer

  • Transport: Connect over HTTP through the sdkgen SDK (LLD §17.5).
  • Realtime, pilot: ETag’d polling (foreground interval + pull-to-refresh); entity version is the ordering token.
  • Realtime, later: P26 resumable diff streams. Widgets only ever see subscribe(queryRef) → updates; swapping polling for P26 is a runtime-internal change (SUR-9).
  • Offline reads: Surface-declared queries sync as replica tables with freshness stamps; the runtime serves reads replica-first with staleness surfaced in the UI (a freshness chip, not a hidden lie).

5.5 Capture path

Three tiers, all required before field deployment (SG-1), built in this order because each is independently demoable:

TierWhatMachineryStatus of dependencies
(a) Entity/attribute capturesurvey forms, barcode-as-input, GPS-stamped traitsAct path + Form node + offer tableAct path ships in M1
(b) Photo/media capturegeo-photos, splice diagramsP6 blob store, media trait kind, seam-issued presigned URLs, EXIF ingest (LLD §17.3)Not built; kernel-side workstream
(c) Offline capturedead-zone writesdevice branches + §15 merge + §12 adjudication routing (LLD §22)Not built; kernel-side workstream

Offline mechanics follow §22 verbatim — no second machine: device branch = last sync token + local act log (proposed-class always); reconnect = write-set intersection → dry-run rebase with policy as-of-now → clean Acts auto-commit → conflicts become adjudication WorkItems. The act-queue schema ships in the app’s SQLite layer from the first build, even though merge lands in tier (c) — retrofitting a queue under a shipped capture UI is the expensive order.

Conflict UX (SUR-6): adjudication WorkItems are TaskInbox items. Each opens a DetailCard showing your-version vs canon (entity-level branch diff) with three resolutions: rebase onto canon / discard / edit-and-resubmit. Sync is non-blocking; supervisors see crew conflicts in their own inbox per §12 routing.

5.6 View-state and dials

The Lexicon’s dials (focus · altitude · time · layers · arrangement) are defined in the runtime as one typed, serializable object with one canonical string grammar. Web encodes it in the URL (every state is a shareable link); mobile encodes it in lepton:// deep links and mirrors it onto the navigation stack (back = dial history). Agents manipulate the UI by setting the same object — inspectable, shareable, undoable — which is the Lexicon’s “no agent backdoor” invariant carried to mobile. This resolves the Lexicon doc’s open fork #1 at the runtime level; the remaining choice (which dials elide to defaults in short links) is SQ-2.

5.7 Theming

@vinxi/design tokens are the single vocabulary (type scale, palette, geometry — light default, .theme-dark exists). Both shells consume tokens through a provider: CSS variables on web, a token provider (Unistyles) on RN. The pilot hardcodes the Lepton light theme but reads it through the provider from day one — the tenant-overlay mechanism (theme tokens as package content, validated at install like everything else) then requires no widget changes.

5.8 Generative UI

Agents author Surface IR documents, never code. Generation is constrained against the Surface JSON schema projected from the realm’s IR-1 — the agent cannot emit a binding to a trait the realm doesn’t have. In the pilot this is authoring-time only: agent-authored docs are fixtures/installs that pass the same validation as human-authored ones (nearly free once the schema exists). Post-pilot, ephemeral in-session Surfaces (Sanjaya rendering UI in a conversation) use the same schema, the same runtime validation, and the same capability path — streamed as partial Surface trees and rendered progressively. There is no second, less governed rendering mechanism at any point.

5.9 Testing and version skew

  • Golden fixtures (SG-3): the interpreter’s primary CI gate. Fixture Surfaces (including the pilot’s real app) → resolved render trees, snapshot-compared per platform target.
  • Version negotiation: the shell advertises its supported Surface IR version at session open; the server refuses to sync a Surface the shell can’t render — mismatches fail loudly at sync, never blankly at render. IR version bumps follow §14.5 migration classes (additive node/param = free; anything else = major).

6. Decision records

Format: context → decision → alternatives rejected → consequences. Statuses: Accepted (locked) / Provisional (standing unless vetoed).


SUR-1 · The pilot ships on a minimal Surface runtime — Accepted

Context. M1 ships no UI. The pilot needs one in ~6 months. Building interpreter + app is slower than hand-coding an app; but per-customer dynamic UI is the founding requirement. Decision. Build the thin runtime with only the nodes the pilot needs; the pilot UI is hand-authored Surface documents checked in as fixtures. Defer install governance, authoring tools, and the widget sandbox. Rejected. Hand-coded React app now, platform later — creates a throwaway that competes with the platform and defers the founding requirement past the first customer. Full platform first — blows the window. Schema-driven widgets without Surface IR — half-measure that still requires the rewrite. Consequences. Interpreter work is on the pilot’s critical path; SG-3 exists to keep it honest; the pilot app doubles as the platform’s first golden fixture.

SUR-2 · Mobile leads; the app hosts the reference interpreter — Accepted

Context. Nikhil: the app is more interesting than the web app; field operations are the product’s heart. Decision. The RN shell is built first; the web shell is the runtime’s second consumer. Consequences. The interpreter gets battle-tested under the harder constraints first (offline, connectivity, device storage); web inherits a hardened core. Web-facing demos lag mobile during the pilot.

SUR-3 · React Native + Expo, not KMP, not Flutter — Accepted

Context. Criterion set by Nikhil: whichever bootstraps both read and capture faster. Team is TS; vinxi monorepo is all TS; the one-interpreter principle wants the reference interpreter in the language web must use anyway. Decision. React Native with Expo (EAS builds, EAS Update OTA, expo modules). Rejected. KMP + Compose Multiplatform — best-in-class native capture on Android (CameraX/ML Kit), but: reference interpreter would live in Kotlin with a permanent second TS copy for web (breaks the one-interpreter principle); Compose-iOS young; MapLibre Compose bindings early; team takes a language + Gradle switch before line one. Flutter — interpreter in Dart, shared with nothing; PRD already marks the legacy app Redesign. Bare RN — same wins as Expo minus the managed toolchain; only revisit if Expo constraints bite at Jio (MDM, exotic native modules). Consequences. Capture toolkit is off-the-shelf TS (vision-camera, expo-location, op-sqlite, MapLibre RN + OfflineManager). Watch/VR shells are outside RN’s sweet spot — accepted, they’re post-pilot. If a truly native surface is ever required, the runtime remains reusable; only Layer 3 would be rewritten.

SUR-4 · Read and capture are co-equal — Accepted

Context. Q4 of the grill offered viewer-first vs capture-first. Nikhil: “Read and Capture are the two sides, both equally important.” Decision. The app is a viewer and a collector; neither is a bolt-on. Development order within capture is (a)→(b)→(c) (§5.5), but no field deployment until all three (SG-1). Consequences. P6 media and §15 merge become pre-field-beta dependencies — kernel-side workstreams that must start alongside the runtime (see §8).

SUR-5 · Field beta gates on full capture parity — Accepted

Context. Options ranged from shipping a viewer early to gating on full parity. Decision. “All three before field beta”: entity capture + photos + offline all work in a dead zone before field hands. Legacy Flutter covers the interim. Month 6 = dogfood/demo over live pilot data (SG-2), field rollout follows parity. Rejected. Early viewer-only field beta — faster presence, but a field tool that can’t capture in a dead zone next to a legacy app that can would burn credibility with the exact users the product must win. Consequences. The demanding option: three workstreams converge before deployment. The dogfood program (internal + friendly Jio users on connected devices) is the pressure-release valve — the app is used early, just not deployed as the tool of record.

SUR-6 · Conflicts are TaskInbox adjudication items — Provisional

Context. LLD §22 designs conflict routing (§12 WorkItems) but leaves the UX open — now on the critical path via SG-1. Decision. Adjudication WorkItems land in the same TaskInbox the viewer already renders; each opens your-version-vs-canon with rebase/discard/edit-resubmit; sync never blocks. Rejected. Blocking sync-review modal — blocks fieldwork on connectivity events, fights the async proposed-Acts model. Supervisor-only — §22’s own example (“confirm your splice target”) is answerable only by the person at the joint. Auto-resolve heuristics — silently guessing physical-world state is what proposed-class Acts exist to prevent.

SUR-7 · The runtime lives in the vinxi monorepo — Provisional

Decision. New packages @vinxi/surface-runtime, @vinxi/surface-widgets-native, @vinxi/surface-widgets-web (later), plus the Expo app — siblings of design, tunnel, app-ui. The kernel repo stays kernel; the only artifact crossing the boundary is the versioned sdkgen SDK. Rejected. Kernel repo — mixes userland into the kernel and drags Expo tooling into it. New repo — a third repo before there’s a team to need it.

SUR-8 · Dials are one serializable view-state object — Provisional

Decision. Typed {focus, altitude, time, layers, arrangement} owned by the runtime, one canonical string grammar; URL on web, deep links + nav stack on mobile (§5.6). Consequences. Shareable/undoable agent moves survive on mobile; Lexicon open fork #1 is resolved at the runtime level (residual: SQ-2).

SUR-9 · The pilot polls; subscribe() hides the transport — Provisional

Decision. ETag’d polling now; P26 diff streams later behind the same facade (§5.4). Rejected. Building P26 for the pilot — a gateway tier for an app with tens of users is premature; the facade makes the upgrade invisible.

SUR-10 · Built-in widgets only; sandbox contract designed, not shipped — Provisional

Decision. The registry’s package-widget registration point is typed and documented now; the P9-class sandboxed module hatch (Hermes/JSI or WASM, verb-only imports) lands with package install, post-pilot. The register-#15 health metric starts when the hatch opens.

SUR-11 · One token vocabulary through a provider — Provisional

Decision. §5.7. Pilot hardcodes Lepton light through the provider; tenant overlays are package content later.

SUR-12 · Generative UI is authoring-time only in the pilot — Provisional

Decision. §5.8. Agent-authored docs validate like human ones from day one; ephemeral session Surfaces post-pilot on the identical schema/validation/capability path.

SUR-13 · Real OIDC on every device build — Provisional

Decision. First beta builds authenticate via OIDC (Keycloak or Jio’s IdP) even while the seam’s M1 identity is stubbed; the X-Dev-Actor header never reaches a field device. Device registry deferred.

SUR-14 · The web shell is the runtime’s second consumer — Provisional

Decision. Built from app-ui chrome after the RN beta stabilizes; DOM widget implementations against the same registry contract. Its bring-up cost is the measured proof of the one-interpreter principle.


7. Worked example — the field viewer/capture Surface

A hand-authored pilot Surface (abridged; illustrative syntax — the concrete schema is SQ-1). This single document is journeys 1–3 of §4.2.

{
"surface": "networkaccess.field-app",
"version": "0.1.0",
"irVersion": "surface-ir/v0",
"root": {
"kind": "Page",
"children": [
{
"kind": "Workspace",
"id": "network",
// binds an interface, not a concrete type — works in every reality
// whose entities conform to Locatable (LLD §20 interfaces)
"collection": { "query": "ir2://entities?iface=Locatable&bbox=$dials.viewport" },
"projections": [
{ "kind": "map", "slot": "stage", "layers": ["p24://fiber-cables", "p24://joints"] },
{ "kind": "list", "slot": "drawer", "fields": ["name", "status", "lastInspected"] }
],
"focus": {
"kind": "DetailCard",
"sections": [
{ "traits": ["status", "capacity", "installedAt", "photos"] }
// "photos" is a media trait → the offer table renders a gallery
// with a camera-capture affordance; no widget is named here
],
"actions": [
{ "action": "act://createWorkOrder", "label": { "key": "wo.create" } },
{ "action": "act://recordSplice", "label": { "key": "splice.record" } }
// capability requirements live on the Action in P0; the runtime
// hides or disables these per session — nothing here says "if admin"
]
}
},
{
"kind": "Workspace",
"preset": "TaskInbox",
"id": "my-work",
"collection": { "query": "ir2://workitems?assignee=$session.actor" }
// adjudication WorkItems from device-branch merges arrive here too (SUR-6)
},
{
"kind": "Form",
"id": "survey-pole",
"action": "act://surveyPole",
"fields": [
{ "trait": "assetTag", "input": "barcode" }, // explicit offer override
{ "trait": "location" }, // geo(point) → map pin, GPS-prefilled
{ "trait": "poleClass" }, // categorical(scheme) → select
{ "trait": "photo" } // media → camera capture
]
}
]
}
}

What each layer does with it:

  1. Install/validate. The document validates against the realm’s projected schema: Locatable must exist, act://recordSplice must exist with its capability requirement, poleClass’s scheme must exist. A missing trait is a compat failure at this step — never a blank at render.
  2. Runtime. Binds both collections through the SDK; registers them for replica sync; computes that this session can acquire createWorkOrder but not recordSplice (button renders disabled with the reason); owns $dials.viewport and focus.
  3. Shell. PhoneShell mounts the Stage (map projection), the drawer (list projection), push-page focus (DetailCard), bottom-bar nav. Selecting a row in the drawer highlights the map pin and opens the card — same focus, two projections.
  4. Dead zone. Replica serves both collections with freshness chips; the survey Form’s submit lands on the device-branch queue as a proposed Act; reconnect merges; a conflict appears in my-work.

8. Critical path and risks

The long pole is kernel-side, not UI-side. SG-1 makes P6 media (blob store, media kind, presigned issuance, EXIF ingest) and the §15 merge pipeline (write-set intersection, dry-run rebase, §12 adjudication routing) prerequisites for field deployment. Neither is in M1. If these two workstreams don’t start alongside the runtime, the app team hits a wall at capture tiers (b) and (c) with nothing to build against. Sequencing ask: schedule P6 and §15-merge as M2-class kernel work, not as “when the app needs it.”

RiskMitigation
Interpreter on the pilot’s critical path (SUR-1)Minimal node set; SG-3 golden fixtures; pilot app = fixture
Kernel dependencies slip (P6, §15)Named here as M2-class asks; capture tier (a) is demoable without them
Low-code cliff (register #15)Closed declarative IR; escape hatch to sandboxed widgets; custom-slot ratio metric (SUR-10)
Legacy-app credibility gap during interimSG-1 itself — no half-capable field deployment; dogfood program for early usage
RN ecosystem edges at Jio (MDM, devices)Expo → bare-RN escape hatch is cheap early (SUR-3); Android-first builds match the fleet

9. Open questions

#QuestionOwner / gate
SQ-1Concrete Surface IR v0 JSON schema (node/param shapes in §7 are illustrative)First runtime milestone; becomes part of IR-1’s projection surface
SQ-2Dial-grammar details: which dials elide to defaults in short links; viewport encodingResolve against the first shareable-link implementation
SQ-3Adjudication DetailCard’s diff rendering for graph-edge traits (splice topology conflicts are not field-value diffs)Before capture tier (c)
SQ-4Dogfood cohort at Jio (who runs the pre-field-beta app, on what devices)With Jio, before month 4
SQ-5P26 adoption trigger (user count? battery data from polling?)Post-pilot review
SQ-6Sandbox execution substrate for package widgets: Hermes/JSI vs WASMWith package-install design, post-pilot