Manna — Kotlin Surface migration plan

6 October 2026 · Proposed implementation plan · Source inspected in the current working trees

Manna will have one Kotlin implementation of its application state, commands, navigation, screen composition, adaptive presentation and design recipes. Surface will realize that composition through Kotlin React on the web and Compose Multiplatform on Android, iOS and qualified desktop hosts. Tiptap and Excalidraw remain specialized, injected editor engines inside Surface-owned regions. Their presence does not make the surrounding application a web page embedded wholesale into native apps.

The completion criterion is shared screens, not merely shared data models. There must not be a Kotlin React Notes screen and a separately maintained Compose Notes screen. Renderer implementations necessarily differ; Manna's feature composition and behavior do not.

This plan applies the strict interpretation of that rule: final retirement includes Manna's TypeScript Worker application code. The client migration keeps the deployed API stable first; a separate backend wave replaces its implementation without combining UI migration with a database migration. The Reaktor documentation site's own implementation is outside this Manna product migration.

1. What is already present, and what is missing

The September Manna HLD and functional designs remain product and design inputs. Their dated implementation inventories and earlier database proposals do not override October source or deployed behavior. This plan supersedes their allowance for a separately authored React product UI. The existing manna/docs/MANNA_SURFACE_MIGRATION.md describes earlier implementation work, including a duplicated web adapter; it is not evidence that this migration is complete.

Observed sourceRetainRequired change
manna/modules/app/src/commonMain/.../Manna.kt, workspace/WorkspaceGraph.ktIdentity-scoped graphs, typed service/selection ports, session retirement and declared routesMake screen attachment renderer-neutral; preserve Graph ownership rather than introduce another router/store
manna/modules/app/src/commonMain/.../data/{api,records,sync,store}Kotlin clients, record codecs, revision-aware commands, stream/catch-up and local persistence workReconcile every current web contract; add typed rich Notes/graph content and portable draft handling
manna/modules/app/src/commonMain/.../featureExisting Kotlin feature behavior and useful native compositionsReplace toolkit-bound feature composition with the single Surface authoring path; prove web/native feature parity
manna/targets/appWeb/src/mannaCurrent product behavior and acceptance scenariosRetire React/TypeScript screens, CSS, sync/state duplicates and page registries after their Kotlin equivalents qualify
reaktor-surface/src/commonMainBehavior kernels, recipes, constraints, pane planning, collection/focus/editing contractsAdd only the portable composition and bindings needed by the first actual Manna slice
reaktor-surface-composeExisting Compose realizations and kernel/adapter testsRealize portable Surface nodes using existing controls; retain direct Compose APIs for other consumers
reaktor-surface/ts/src/index.tsxReference for existing Manna behaviorRemove Manna's dependency on its separately implemented TypeScript behavior; Kotlin React must consume the Kotlin kernels
reaktor-blueprint and compose-flowKotlin layout, graph geometry, viewport and canvas substrateQualify browser rendering and authored graph editing; preserve Atlas's visual language across realizations
reaktor-web, reaktor-ui/webBundle loading, WebView hosts, typed bridge, capability reporting and disposalPackage editor engines behind typed Kotlin adapters and prove required capabilities on each target
manna/targets/api, targets/appWeb/workerCurrent auth, API, WorkspaceHub, connector, MCP and storage semanticsKeep compatible during client waves; replace handwritten application code in the final Kotlin backend wave

The current Kotlin Recipe.kt is a component-recipe interface, not an existing portable screen renderer. No production SurfaceHostApplier or Kotlin React Surface renderer was found in the inspected Surface modules. The old reaktor-react project is excluded from Reaktor's build and contains legacy React Native/JSI work; it is not the Kotlin React web renderer to adopt. The 5 October Surface investigation must not be mistaken for a shipped renderer; its local evidence is recorded in reaktor/reaktor-surface/REACT_STRICT_DOM_INVESTIGATION_2026-10-05.md.

Notes, documents, drawings and authored graph notes already ship on Manna web. The native Memory/Journal surface does not provide equivalent rich editing. Migration must carry these capabilities forward rather than revert to the older native screen's smaller feature set.

2. Shared Kotlin authoring and renderer boundary

Manna migration: one feature, two realizations

HLD — Arrows follow one common feature composition into the two approved realizations and specialized capabilities. Open the diagram for full size.

Use a small Kotlin Surface composition vocabulary: groups and layout intent, text, controls, collections, panes, overlays and typed islands. Author feature compositions once in commonMain. Keep DOM nodes, React hooks, Compose UI Modifier, platform fonts and platform input types out of those compositions. Compose Runtime may provide the shared declarative execution model without making the emitted nodes Compose UI nodes.

The recommended first implementation is a Surface-specific Compose Runtime Applier that produces a keyed host structure. A Compose realization maps that structure to existing Surface Compose components. A Kotlin React realization maps it to ordinary React components through JetBrains wrappers. This uses the portable composition direction already described in Surface renderers, because Manna now has a concrete requirement for one authored composition. It is a proposed framework capability, not a renaming of the existing Compose adapter. Keep the pure reaktor-surface kernels free of Compose and React dependencies. Put the Compose Runtime authoring host in a separate adapter module, provisionally reaktor-surface-composition, depending on the pure contracts and Runtime but not Compose UI. Headless graph hosts need neither renderer.

Before expanding the vocabulary, prove a keyed list, controlled text field, button, supporting pane and one island through both realizations. If this prototype cannot preserve editing, focus, lifecycle and bounded work, resolve that framework problem before porting more Manna screens. Sharing only controllers while keeping two handwritten screen trees does not satisfy the gate.

Implementation rules:

  • Commit one coherent host update after composition. Preserve keyed identities on insert, reorder, move and removal. Retire subscriptions, callbacks and island sessions when their owner leaves the graph.
  • Kotlin behavior kernels remain authoritative. DOM and Compose event normalization are renderer responsibilities; clicks, touch, keyboard and accessibility activation must admit the same semantic command once.
  • React renders remain free of product effects. Subscribe to stable Kotlin snapshots and use current handlers. Commit-time attachment and teardown must tolerate React development remounts without sending duplicate commands.
  • Renderer-local state owns caret, IME composition, focus handles and transient gesture feedback. Feature state owns draft content, selected record, recovery choices and command outcomes. There is no second business store.
  • Represent lazy collections as bounded, keyed sources and view descriptors. Do not materialize every catalogue chapter into a portable host tree merely to share a list. Canvas motion and paint remain local to the renderer.
  • Use Kotlin externals/wrappers for vendor APIs. Do not move a TSX screen into a js() string or recreate CSS selectors through string concatenation.
  • An optional web styling substrate can be evaluated after the ordinary Kotlin React path qualifies. It must not add another renderer target or make a custom React-to-Compose reconciler part of Manna's critical path.

3. Module ownership

Dependency direction and ownership

HLD — Arrows show dependency direction. Toolkit adapters depend on neutral meanings; Manna services remain outside the toolkit. Open the diagram for full size.

Existing modules are extended before creating new ones. Names below that do not already exist are proposals, to be chosen during the first framework slice.

OwnerResponsibility
Reaktor Surface common moduleShared control behavior, portable value contracts, identity/lifetime, typed recipe values and adaptive planning; no toolkit dependency
Reaktor Surface composition adapterShared Kotlin authoring vocabulary and Compose Runtime Applier; publishes portable committed structure without Compose UI or React dependencies
Reaktor Surface Compose adapterNative control realization, measurement, input, focus, semantics and Surface island placement
Reaktor Surface Kotlin React adapterKotlin/JS React/DOM realization, subscriptions, measurement, accessible controls, typed style lowering and island placement
Reaktor graph/runtimeGraph definitions, typed ports, declared navigation, session lifecycle and command binding
reaktor-blueprint / graph-owned editor substrateShared graph model, validation, projection/layout, editing commands and target canvas realizations; compose-flow retains generic viewport/gesture work
reaktor-web + reaktor-ui/webEmbedded engine lifetime, bounded typed transport, origins, native/browser host capabilities and placement
Manna modules/app/commonMainProduct graph, data contracts, feature state, complete screen compositions, adaptive product policies, brand recipes and editor services
Manna target source setsEntry points and platform capabilities: auth handoff, window/insets, files, downloads, storage and lifecycle
Manna Kotlin Worker implementationServer validation, hub/connector state machines, auth/MCP/API routing and storage calls using existing Reaktor Cloudflare substrate

Do not put Manna navigation, privacy or Notes persistence inside Surface. Do not put graph semantics inside generic canvas primitives. Use the current reaktor-blueprint ownership shown by source; historical documentation names reaktor-flow, but that does not justify recreating a removed module.

4. Design and adaptive layouts

Manna adaptive presentation

HLD — Arrows show policy inputs and alternative placements. The current shared planner is a starting point, not the full Manna task layout. Open the diagram for full size.

Manna's paper/forest design, Atlas Blueprint cards and light/dark appearances remain the visual specification. Extract existing Manna design values into one Kotlin brand definition with logical colors, spacing, typography, radii, control metrics and semantic roles. Renderer recipes project those values; they do not carry independent palettes. Preserve the existing Reaktor design authorities for shared kit components.

Browser styles must be produced by a Kotlin-authored typed recipe backend. Generated style sheets or DOM style values are output, not authored product CSS. Vendor styles are confined to the editor island. A change to a Manna token must reach both renderers through the same definition.

Measure the actual container, usable height, safe insets, text scale, keyboard/IME occlusion and available input capabilities. Feed the same Kotlin Surface planning policy on every target. Existing 720/1120 effective-width thresholds are a starting baseline, not a reason to hardcode phone and desktop screens. Any tuning happens once in the shared policy.

ProfileShellNotes and Atlas
Compact / insufficient usable heightOne primary pane, compact navigation, reachable capture/searchRecord list and editor or map and inspector switch through shared pane intent; inspector can use a sheet; preserve editing/camera while switching
MediumNarrow rail and primary paneSupporting content opens when minimum readable sizes fit; otherwise use overlay/alternate pane
ExpandedNavigation, primary and supporting panesList/map/editor with inspector beside it; remember user pane sizes within current constraints

Window resize, rotation, split-screen and text-scale changes must not push a new route, recreate the workspace graph, reset a document, discard a draft or change source selection. Persist pane preferences separately from route and domain state. Match reading order, focus traversal and keyboard escape/back to the visible projection. Adaptive means the same capabilities remain usable with suitable arrangements, not identical pixels at every size.

5. Notes and specialized editor islands

Notes: four input types in one shared feature

HLD — Arrows show content-kind selection. Four inputs share the Notes workflow; target engines differ behind typed contracts. Open the diagram for full size.

Introduce a shared Notes session with a typed content kind: plain note, structured document, drawing or authored graph. Its Kotlin owner controls load/revision state, draft checkpoints, save admission, in-flight edit counters, recovery, search text and Google pairing/conflict state. Rename the native surface to Notes and preserve /notes plus old /journal backlinks and their entry/drawing parameters. Existing record IDs and the wire journal extension do not change merely because the presentation name changes.

Surface owns the record rail, creation actions, metadata, toolbar placement, dialogs, status, node inspector and adaptive layout. Engines own specialized editing behavior inside their region.

IslandHost and shared Kotlin contract
TiptapKotlin/JS wrapper in a scoped DOM region on web; the same packaged engine in Reaktor WebView on native. Typed commands for load, supported formatting, selection/readiness, editable JSON and checkpoint/export
ExcalidrawKotlin/JS React wrapper on web; packaged engine in Reaktor WebView on native. Typed scene/files, scene updates, change events and preview export; retain its internal drawing interaction/history
Reaktor graph canvasShared Kotlin graph definition/editing session and Blueprint geometry; browser canvas realization in Kotlin/JS and native Compose realization. Graph authoring is not implemented as an Excalidraw drawing

The same common feature composition declares an island. The host chooses its platform realization. A native WebView must not contain the Manna shell or an alternate Notes workflow. Tiptap/Excalidraw may retain necessary native editor controls, but Manna-specific actions and state remain Surface-owned.

Editor lifecycle and stale-message protection

HLD — Arrows follow the session lifecycle. Retirement invalidates the epoch; checkpoint failure retains recoverable work. Open the diagram for full size.

Define the bounded protocol before wiring the engines:

  1. Attach with entry ID, content revision, session ID/epoch, supported capabilities and allowed operations; wait for an explicit ready result.
  2. Load structured content without generating a new user-edit event. Preserve an active IME composition and selection when reconciling controlled values.
  3. Deliver edits with monotonic session sequence and current revision. Retired sessions and stale callbacks cannot change another record or workspace.
  4. Checkpoint before switching entries, backgrounding or releasing an engine. A failed save leaves the recoverable draft; release does not imply success.
  5. Produce previews/exports through bounded commands. Large drawing images use the media/asset path rather than unbounded bridge messages.
  6. Dispose listeners, workers, URLs and native engine resources on retirement. Test reattachment and restore, including graphics/engine failure.

Bundle and version editor assets; load no arbitrary runtime CDN scripts. Keep Manna tokens, workspace credentials and direct Memgraph access out of engine globals. A bridge exposes only the current authorized document operations. Reuse Reaktor WebRuntime/WebBridge capability checks; browser iframe evaluation, history and downloads do not have native parity. Required bundle/bridge/worker/font capabilities must be qualified for each editor. WebView availability alone is not proof that Excalidraw or Tiptap works there.

Continue existing Notes behavior: automatic version-checked saves, drafts surviving reload/offline use, explicit conflict choices, editable drawing backlinks and Google Docs synchronization for supported documents/drawings. Graph notes remain independent Memgraph graphs and do not gain Google pairing as part of this migration.

6. Atlas and authored graph parity

Nested graphs and typed boundary wiring

HLD — Arrows show graph ownership and typed boundary flow, not execution. The teal nodes describe deployed TypeScript behavior that Kotlin must preserve. Open the diagram for full size.

Reconcile the deployed schema 2 nested graph document with Reaktor's Kotlin graph definitions. Preserve scope-local node/edge IDs, scope-qualified selection identities, owned subgraphs, exposed boundary ports and their internal bindings, port keys and contract types, labels, descriptions, groups, route patterns, presentation positions and edge direction. Preserve port, navigation, event, transition and relation edge kinds, including their distinct metadata. Reaktor stores port edges consumer-to-provider; the canvas shows provider-to-consumer data flow. Port validation, provider fan-out and one provider per consumer must behave identically in Kotlin.

Promote reusable authored-graph model and commands to their Reaktor owner, with a compatibility codec for the existing Notes schema. Manna supplies the note/session and storage bindings. Do not invent a second graph semantic model inside the app. Authoring does not execute arbitrary graph nodes.

Graph snapshots remain native Memgraph parts

HLD — Arrows follow the existing graph save protocol: immutable native Memgraph parts precede the version-checked Memory pointer. Open the diagram for full size.

Keep native Memgraph snapshots scoped by tenant, workspace, note and hash, with graph/node/port/edge parts and existing relationships. Preserve the immutable snapshot followed by revision-checked Memory pointer update. Graph content does not migrate to R2 or a new canonical database. Keep current 250-node/500-connection limits across the whole document, the 12-level nesting limit and 96 KiB content limit. These are compatibility limits, not a performance qualification for a new renderer. Owned graphs already expand into recursive frames and can be edited in place on the deployed web release.

Atlas / Hangar concepts become editing operations

HLD — Arrows show authoring intent reaching navigation, shared commands and projection. Atlas/Hangar supply concepts and design; Stately supplies the usability bar. Open the diagram for full size.

Canvas requirements:

  • Reuse Atlas card, pin, frame, wire, grid, camera and theme definitions. Kotlin geometry is authoritative for both canvas realizations; qualify layout-provider differences rather than quietly accept different topology.
  • Port node creation/edit/delete, typed connect/disconnect, every supported edge kind, drag, arrange, undo/redo and import/export. A drag previews locally and commits one meaningful history command; it does not autosave every pointer sample.
  • Preserve camera/selection across inspector toggles, adaptive transitions and unrelated record updates. Arrange explicitly changes authored positions.
  • Share projection, culling, hit-testing and semantic identity. Browser paint uses Kotlin/JS canvas APIs; Compose paint uses native facilities. Do not ship the old TS React Flow graph editor behind the shared screen as a shortcut.
  • Preserve the full permission-scoped Atlas, chapter identities, directed links, filters, saved views and unresolved endpoint reporting. A graph note is not attached to or confused with the authoritative catalogue.
  • Provide a keyboard/accessibility list or structured inspector for canvas objects. Canvas-only pointer interaction is not cross-target completion.
  • Preserve Atlas/Hangar navigation: click inspects, double-click frames, outline/search reveals, semantic zoom controls detail and upstream/downstream focus explains dependencies. Expand owned frames without changing the active graph; explicit scope focus and breadcrumbs remain separate operations.
  • Nest selected contents into a new or existing sibling owner while preserving typed external wiring and exposed ports. Internalize connections whose endpoints converge under one owner. Reject unsupported boundary changes as one atomic command; undo restores topology and metadata.

Stately supplies a task usability benchmark, not a component design or a feature-parity specification. Benchmark finding a deeply owned node, editing it in place, nesting wired contents, following a dependency, undoing a structural change and recovering the saved result. Keep the Atlas/Hangar visual and interaction vocabulary across Reaktor+. A graph-view adapter and Surface's generic pane kernel do not implement those graph operations by themselves.

7. State, persistence and backend continuity

Draft, transient editing and persistence have different owners

HLD — Arrows distinguish canonical value, draft, transient editing and save outcome. Closing a view is not a successful save. Open the diagram for full size.

Retain the current Manna tenant and identities. Memgraph is the canonical personal-record and graph store, R2 holds original/media/document artifacts, WorkspaceHub orders mutations and owns revisions/receipts/change delivery, and existing derived indexes retain their permission filtering. This is not a Supabase/D1 canonical-record migration.

Consolidate the Kotlin and TS record contracts with fixture-based wire compatibility, including unknown extension preservation, server-only attribution beside records, revisions, sequence positions, deletion/redaction, private/restricted behavior and expected-revision refusal. A native codec that does not edit rich content must not strip its journal extension on another record edit. Note version and record revision remain distinct.

Use one Kotlin sync/outbox/draft owner per signed-in graph. Web OPFS/SQLite, native database/file facilities and account storage are platform adapters to the same persistence contracts. Preserve existing browser outbox entries and Notes drafts through a versioned, resumable local migration; switching the renderer must not silently create a new empty store. Keep the old reader until verification and rollback requirements are satisfied.

For each content kind, test load, offline edits, restart, reconnect, concurrent save, refusal/retry, sign-out, owner change and recovery. Match canonical JSON and hashing to existing saved-content fixtures before Kotlin starts writing them. Cross-language integer, null, ordering and encoding differences require explicit compatibility tests.

The API/edge Kotlin wave keeps URLs, principal/scopes, bindings, Durable Object class identities, SQLite tables, receipts, queue jobs, R2 keys and Memgraph queries compatible. Port validation/state transitions into shared Kotlin and platform IO into Kotlin/JS Worker adapters. No raw Record writes bypass systemCommands/WorkspaceHub. A new language does not grant a new write path.

Keep one active writer during backend cutover. Compare side-effect-free reads and validation with the old implementation before routing writes to Kotlin. Existing DO storage, alarms and in-flight connector tasks must survive a restart/rollback rehearsal. Separate any unavoidable storage migration from the renderer release and give it its own backup and compatibility gate.

Client migration preserves canonical authorities

HLD — Arrows follow client commands through existing authorities. Rendering and backend migration do not change the canonical storage roles. Open the diagram for full size.

8. Delivery waves and acceptance gates

Migration gates and the critical path

HLD — Arrows show the delivery dependency chain. K1, K3 and K4 are blocking capability gates, not calendar estimates. Open the diagram for full size.

The order is dependency-driven. Framework support and a complete Notes slice qualify before broad screen conversion. These waves are work packages, not calendar estimates.

WaveConcrete deliverableExit gate
K0 — Inventory and compatibilityRegister every current web/native route, domain, user capability, content schema, local store and target; record current production and test evidence; establish Kotlin wire fixturesEvery existing route and feature has an owner, migration destination and acceptance scenario; no undocumented feature loss
K1 — Shared Surface compositionMinimal Kotlin host composition/Applier, typed portable recipe values, Compose realization and Kotlin React realization for the proof sliceSame common source renders list, field, action, supporting pane and island on web and native; reorder, editing, focus, resize, disposal and duplicate activation tests pass
K2 — Session, shell and persistenceShared graph bootstrap/auth handoff, navigation registry, adaptive shell, brand recipes, sync/outbox and local migration; Capture/Search/settings/recovery sliceBrowser back/deep links and native back agree; user change retires the old session; existing records and local pending work survive migration
K3 — Notes and embedded editorsShared record rail and Kotlin Notes session/content codecs; plain notes, documents and drawings; packaged Tiptap/Excalidraw adapters; autosave, previews, conflict and Google controls; graph-session seam for K4The three editor journeys pass on web, physical Android and physical iPhone with the same common screen composition; graph wire compatibility passes; the default Notes route waits for K4
K4 — Atlas and complete graph NotesShared Blueprint scene/editor, browser canvas realization, native realization, catalogue/source/chapter journeys and saved views; all four Notes inputs completeAtlas geometry/semantics and Notes graph round trips qualify; authored poses, typed edges, history, camera and adaptive inspector survive transitions; full Notes parity permits route cutover
K5 — Remaining feature groupsPort route groups from the coverage register, reusing existing Kotlin behavior and shared organismsEvery group's existing web/native journeys pass before retiring its old renderer route; no placeholder replaces a working feature
K6 — Kotlin API/edgeKotlin/JS API, WorkspaceHub/ConnectorQueue implementations, edge auth/proxies and MCP; packaged generated entry pointsWire/privacy/idempotency/connector parity and restart/rollback rehearsals pass; API changes introduce no canonical-data migration
K7 — Cutover and retirementMake shared Kotlin application the default on qualified targets; remove legacy product TS/CSS and duplicated native screen pathsSource audit, target matrix, production acceptance and recovery gates pass; old code is deleted only after the rollback window closes

K5 covers, at minimum:

  • Home, Inbox/Needs you, notices and weekly review.
  • Learn, courses/chapters, review, skill trees, plans and recommendations.
  • Missions, work sessions, evidence, passport, capability routes and research.
  • Plan/rhythms, people, spaces/products/teams, shared projects and permissions.
  • Ask/search/intelligence, source repair, anchoring and relation proposals.
  • Arena, exchange, offers/engagements, creator/identity review and comparisons.
  • Imports, assignments/training/series, automation/attention, connectors, privacy, retention and recovery controls.

The register is generated from current feature/domain/page registrations at K0; this grouping is not a substitute for checking every registered capability. Public/share and legacy compatibility routes receive explicit destinations.

9. Verification matrix

AreaRequired evidence
Shared authoringOne common feature composition per route; renderer dependencies rejected from product common compositions; no React/Compose-specific duplicate feature trees
Surface behaviorSame kernel traces and command outcomes across JS/JVM/native; disabled/busy admission, current callbacks, reorder, retirement and remount
EditingIME composition, caret/selection, undo, paste, keyboards, large documents, assets, focus transfer and background/resume
Adaptive presentationCompact/medium/expanded, short windows, split-screen, rotation, text scaling, safe areas, RTL and keyboard occlusion; stable route/draft/camera
AccessibilityKeyboard traversal, screen-reader roles/names, visible focus, semantic canvas alternative, reduced motion and readable theme states
Data and authorityWire fixtures, canonical hash compatibility, privacy filtering, attribution, retries, idempotency, stale revisions, stream reset/catch-up and account separation
Editors/embedsReal bundled fonts/workers/assets, origin-bound bridge, bounded payloads, readiness, engine failure/restart and late callback rejection
PerformanceRecord a current web/native baseline at K0; qualify startup, first useful Notes view, editor readiness, typing/drag responsiveness, Atlas frame cost and memory/resource plateau. Retain existing budgets and investigate regressions; shared code is not evidence of performance parity
ReleaseSigned-in production browser acceptance, physical Android/iPhone acceptance, local-store upgrade, release packaging and rollback rehearsal

Android, iOS and browser are the existing Manna release priorities. macOS desktop is qualified when its Manna launcher and editor host are built. Windows/Linux remain explicit target qualification work: existing WebRuntime source does not establish real-window/editor packaging parity there. The same shared feature source must support them; the release matrix must state their actual status. Workers/servers compile relevant Kotlin contracts and services, not interactive screens.

Use the configured Gradle wrappers so builds follow Mac/worker capacity. Run browser checks on this Mac and Maestro/adb/install/UI checks on connected physical devices. Do not start emulators or simulators. Framework changes run the applicable Reaktor tests and BestBuds consumer regressions. A blocked physical-device check remains unqualified; source compilation does not replace it.

10. Source retirement and release checklist

The cutover is complete only when:

  1. Every supported Manna target uses the same common Kotlin Surface feature compositions, commands and adaptive policy.
  2. Manna's web host is Kotlin/JS + Kotlin React. Android/iOS/desktop hosts realize Surface through Compose and thin platform entry points.
  3. All four Notes inputs and Atlas retain their shipped behavior and data.
  4. Product TS/TSX/JS/JSX and authored CSS/SCSS/HTML are removed, including web screens, graph editor, sync/cache duplicates and final Worker application sources. Audits distinguish maintained source from generated output and locked third-party dependencies; arbitrary handwritten bridge strings fail the gate.
  5. Vendor exceptions name exact assets, owning Kotlin adapter, pinned version, asset/license inventory and lifecycle/qualification tests. Tiptap and Excalidraw do not become blanket exceptions for another product UI.
  6. Local drafts/outbox and server records survive upgrade. Privacy and revision behavior remain equivalent, and no old writer continues against a newly owned schema.
  7. Production release evidence records build/source identity, target, store version, API version, checks and recovery procedure. Retire the old frontend and backend independently so failures have a bounded rollback.

Do not rewrite the product while changing its language. New functionality, new graph execution capabilities and new canonical storage are separate decisions. Preserve the working Notes/Atlas release as the behavioral baseline.

11. First implementation slice

Start with K0 plus K1, then deliver a signed-in Notes shell with a plain note and a Tiptap document through the same common composition on web and physical mobile devices. Include one record conflict and one offline/restart recovery journey in that first slice. Add Excalidraw to finish K3 and Kotlin graph authoring in K4 before making shared Notes the default route.

The first reviewable artifacts are the capability register, Kotlin wire fixtures, the shared Surface proof composition, both renderer adapters and their lifecycle/editing evidence. This exposes the largest architectural risk before porting dozens of screens.

12. Feasibility benchmark against the Surface design

Can Surface handle Manna?

HLD — Arrows bring source evidence, design and product requirements into proof slices. Passing those gates justifies expansion; failing them requires resolving the relevant seam. Open the diagram for full size.

Architectural assessment: Surface can plausibly express Manna's requirements through its planned contracts, portable host and specialized-engine boundaries. Implementation assessment: it cannot yet deliver this migration end to end. The missing shared composition host and Kotlin React renderer block the core promise of one authored screen. Native rich editors and the Kotlin nested graph editor are further qualification gates. This is a source-backed architectural benchmark, not a completed two-renderer performance or device benchmark.

12.1 Requirement-to-design comparison

“Base” means useful implementation exists; “gap” means the required capability or evidence is absent. A base is not a completed Manna feature. The chapter links identify the planned Surface seam against which each requirement is tested.

Manna requirementPlanned Surface designInspected implementation and gapRequired proof
One Kotlin screen tree for both targetsPortable vocabulary and HostApplierBlocking gap. Recipe.kt resolves component appearance; it does not author screens. No production SurfaceHostApplier/composition adapter found. Existing feature composables use Compose UI.K1: one common composition, keyed insertion/reorder/removal, no duplicated React/Compose feature tree.
React web/JS realized from KotlinReact realizationBlocking gap. Surface's existing ts/src/index.tsx is handwritten React behavior. The October RSD probe is not a production Kotlin React renderer.K1/R50–R52: compiled Kotlin kernels, ordinary DOM controls, stable snapshots, bounded subscriptions and correct disposal.
Shared control meaningContracts and interactionBase. Pure behavior, collection, menu, command, focus, tab and pane kernels exist. Their 139 JVM tests pass. This does not prove browser event normalization.Replay activation, cancellation, controlled-value and stale-owner traces on both adapters.
Manna components and visual languageRecipes and theme loweringPartial base. Recipe/ThemeSnapshot interfaces and Compose appearances exist; a complete shared Manna brand and web style lowering are unqualified.Same semantic roles and card/pin/frame/wire recipes in both targets; typography, overlays, density and unavailable states included.
Adaptive task layout, not duplicated pagesPresentation policyBase with environment gap. Presentation.kt and Panes.kt plan from constraints. Current SurfaceConstraints has width, height, text scale and pointer capability, with no explicit IME/safe-inset fields.Normalize usable constraints at target boundaries; resize, text-scale and keyboard changes preserve entry/draft/camera and keep actions reachable.
Large catalogues and dense controlsBounded work, desktop collectionsBase on CMP. Collection sources/adapters and cost tests bound row work. The host collection descriptor and React realization are still planned.10,000-row fixture: demanded keys only; selection/hover affect the relevant rows; no unbounded host snapshot or one controller/coroutine per row.
Controlled text and recoveryEditing and host lifetimePartial base. Revisioned Draft.kt and toolkit fields exist; browser/native IME handoff through the portable host is unqualified.Type through reorder, supporting-pane change, acknowledgement and conflict; keep composition/caret/focus and recover the draft after restart.
Tiptap and ExcalidrawSpecialized engines, IslandBoundary base; engine gap. Compose Island coordinates focus/commands. WebRuntime/WebBridge provide session/epoch, grants and bounded transport. Neither establishes packaged editor capability on every native host.K3: exact engine versions on web and physical Android/iPhone, ready/load/edit/checkpoint/dispose, fonts/workers/files, background recovery and stale-session rejection.
Nested graph authoring in the Atlas languageBlueprint workbenchProduct evidence, Kotlin gap. Deployed schema 2 has owned frames, boundary ports and semantic edges. Kotlin Blueprint/compose-flow provide graph geometry/camera; they are not a finished compatibility codec/editor for all Notes commands.K4: identical document fixtures and command traces; nesting preserves wiring/metadata; undo, scope reveal, export and reload retain identity.
Responsive camera, drag and canvas paintLocal feedback and custom drawing, creative softwareProduct evidence only. Current TS projection has a directional CPU profile. There is no comparable portable-host + Kotlin/JS + native Notes frame profile.Same fixture on both targets: local gesture preview, culled detail, one completed history/save operation, CPU/frame/allocation/resource report.
Keyboard and accessibility across shell/canvasDesktop focus and commands, verificationCMP base; full workflow gap. Island/pane tests pass. A drawn graph still needs semantic object navigation, commands and a usable way out; browser and device assistive-technology behavior is unqualified.Complete the graph tasks with keyboard and semantic outline/inspector; focus returns after overlays, resize and engine exit; test screen readers on supported hosts.
Data, auth, sync and backend continuityGraph ownership plus Manna servicesExisting product authority. Memgraph/R2/WorkspaceHub behavior belongs to Manna/Reaktor services. Surface is not its database, sync engine or Worker replacement.Wire/hash/revision fixtures, tenant/workspace isolation, offline outbox replay and conflict recovery. K6 independently proves the Kotlin Worker replacement.

12.2 What was actually checked

On 6 October, the configured Gradle wrapper ran 139 pure Surface JVM tests and 22 Compose adapter tests from IslandTest, PaneHostTest, SurfaceCostTest and CollectionCostTest, with zero failures. This supports reducer/pane behavior, island focus/command boundaries and bounded CMP work. The cost tests include 25 controls of each tested kind with no extra retained coroutines at rest, and a 10,000-row source whose hover/selection changes target the affected rows. These are focused JVM checks, not Android/iOS, React or whole-app frame results.

The earlier deployed Notes interaction receipt contains 12 model, 19 API, 10 browser task, 3 Atlas/Hangar regression and 4 real Memgraph checks. Its 250-node/six-scope V8 projection profile measured an expanded median of 0.7 ms and maximum of 1.7 ms over 100 measured iterations; collapsed median was 0.1 ms. That is a TypeScript model-projection baseline, excluding total browser paint, the planned host, Kotlin/JS and native rendering. Carry the fixture and profiler method forward; do not present those numbers as Surface renderer performance.

12.3 Blocking experiments and decisions

GateSmallest meaningful experimentPass / stop decision
K1 — shared authoringOne signed-in Notes composition containing a keyed list, revisioned field, action, adaptive supporting pane and inert island, through React web and CMPPass only with one common source, stable edit/focus identity, coherent commits and retired callbacks rejected. If it fails, resolve the vocabulary/host seam before migrating more screens.
K3 — rich engine capabilityOne Tiptap document and one Excalidraw drawing on web and physical Android/iPhone, including offline/background and engine reattachmentPass only with versioned bundle, readiness, selection/IME, ordered edits, media/export and checkpoint recovery. An unavailable required capability blocks that host; WebView presence is insufficient.
K4 — graph compatibilitySame nested schema 2 fixture on both targets; reveal/edit a deep node, nest wired content, inspect each edge kind, undo and save/reloadPass only with preserved scope IDs, typed exposures, edge metadata, camera and Memgraph hashes/revisions. Revise the graph-owned codec/commands, not Surface's generic reducer vocabulary, when semantics fail.
Performance — both renderersProfile the same 250-node/six-scope scene, collapsed/expanded ownership, drag and inspector/resize cases; keep the existing TS path as the directional referenceRecord source, target/device, warm-up, samples, p50/p95/max, allocations, retained resources and CPU profile. Name budgets before changing a hot path. Passing projection alone does not pass frame or startup budgets.
Release continuityUpgrade an existing account with drafts, graphs and outbox entries; exercise conflict, catch-up, logout and rollbackData and recovery remain compatible; old writers are retired under the selected release gate. Surface rendering cannot substitute for this service evidence.

The planned design needs bounded implementation work in its existing seams: portable composition, React realization, usable-environment inputs, engine capability contracts and graph-owned editing/codec support. It does not need another renderer family, a generic node-per-pixel host, a new application store or a replacement persistence authority. Generic Surface may continue shipping direct CMP controls while Manna's stricter K1 requires both renderers early.

The proposed path uses a supported Compose Runtime concept: an Applier applies composition operations to a custom tree. JetBrains kotlin-react provides React bindings, and React's external-store contract requires cached immutable snapshots. These APIs establish viable integration seams; they do not supply Surface's missing implementation or prove that our selected Kotlin/Compose/React versions work together. K1 must establish that.

13. Sources and qualification boundaries

Local implementation evidence was inspected on 6 October 2026 in dirty working trees. File paths above identify the inspected sources; they do not claim that uncommitted source is an immutable release. The current Notes deployment and tests are recorded in manna/docs/build-log/2026-10-06-notes-graphs.md and manna/docs/build-log/2026-10-06-notes-graph-interaction.md. The HLD/source benchmark and publication receipt are in manna/docs/build-log/2026-10-06-surface-hld-feasibility.md. Existing kernel and adapter test reports are under each Surface module's build/test-results/jvmTest. No Kotlin migration implementation or device qualification is claimed by the publication of this plan.

  • Surface renderer architecture: direct adapters, portable Applier boundary, coherent commits and renderer duties.
  • Surface implementation plan: sequencing shared contracts and complete application qualification.
  • Reaktor WebView: current host capabilities, bundle and typed bridge ownership, platform gaps and editor asset ownership.
  • JetBrains Kotlin React wrapper: Kotlin authoring for React; wrapper major versions track React.
  • Compose Runtime Applier: custom tree application, including insertion, movement, removal and change boundaries. This supports the proposed authoring mechanism, not an assertion that Surface already implements it.
  • Tiptap setContent: structured content replacement and control of update emission for the document adapter.
  • Excalidraw props: scene, app-state and file change events for the drawing adapter. Qualify against the pinned release rather than assume upstream main is identical.