Mobile apps — worked graph designs

These designs describe intended system behavior and qualification requirements. Inspect the system atlas for named ports and relation participants.

Community app: nested navigation and authenticated messaging

How does opening a deep link lead to a scoped service effect?

Inspect this design · Definition JSON · revision 57940c5ac864

Diagram legend. Frames are named graph regions. Left/right pins show required/offered participation, not a universal schedule. Filled pins are connected; hollow pins are unbound. A junction card preserves one relation's role slots.

MarkerMeaning in this diagram
↪ N12Route / navigation
▤ N11Surface / view component
↯ N02Effectful operation
⊂ N33Policy / principal boundary
↗ N19Tool / external API adapter
▰ N21Database / state authority
◎ N38Observer / query / visitor
▷ P11Execution / continuation
↦ P04Push event
⊂ P20Authorization request / grant
⇆ P02Request / response
◎ P24Inspection / evidence
↪ H36Navigation / interaction route
→ H02Value flow
⊂ H23Scoped authorization
⇆ H05Request / reply binding
↦ H04Event subscription

Logic and ownership

Validate a draft with an ordinary Kotlin function, then invoke the typed send port. Derive trace context from the selected route and operation ID.

Failure and qualification

  • A route is an address, not an authorization grant. The service rechecks the current principal and conversation membership.
  • Back stack and nested container activation stay local to the device; no distributed back stack is inferred.
  • Retries carry the same operation ID; one local call does not establish durable delivery.

Hyperedge roles

RelationPatternParticipants, preserving role positions
navH36 — Navigation / interaction route {#h36--navigation--interaction-route}provider[0]: route.destination; consumer[0]: screen.route
intentH02 — Value flow {#h02--value-flow}provider[0]: screen.intent; consumer[0]: controller.input
grantH23 — Scoped authorization {#h23--scoped-authorization}provider[0]: auth.grant; consumer[0]: controller.authority
serviceH05 — Request / reply binding {#h05--request--reply-binding}provider[0]: chat.send; consumer[0]: controller.send
persistH05 — Request / reply binding {#h05--request--reply-binding}provider[0]: log.append; consumer[0]: chat.storage
observeH04 — Event subscription {#h04--event-subscription}provider[0]: controller.span; consumer[0]: trace.span
scopeH23 — Scoped authorization {#h23--scoped-authorization}principal[0]: auth.principal; effect[1]: controller.effect; resource[2]: log.resource

Kotlin common declaration

Import dev.shibasis.reaktor.portgraph.definition.* for this structural declaration. Behavior stays in ordinary Kotlin and binds through qualified runtime ports and adapters. Declaring a graph does not activate a system.

graphDefinition("mobile-community", "57940c5ac864e359093ec40028152376c918b29435b97abe63e86e70495f3139") {
    region("device", "Mobile device")
    region("edge", "Edge services")
    node("route", "App routes", "N12", "device", ports = listOf(
        PortDefinition("destination", "P11", "ConversationRouteV1", PortPolarity.Offer)
    ))
    node("screen", "Conversation Surface", "N11", "device", ports = listOf(
        PortDefinition("route", "P11", "ConversationRouteV1", PortPolarity.Require),
        PortDefinition("intent", "P04", "MessageIntentV1", PortPolarity.Offer)
    ))
    node("controller", "Conversation logic", "N02", "device", ports = listOf(
        PortDefinition("input", "P04", "MessageIntentV1", PortPolarity.Require),
        PortDefinition("authority", "P20", "ScopedGrantV1", PortPolarity.Require),
        PortDefinition("send", "P02", "SendMessageV1", PortPolarity.Require),
        PortDefinition("span", "P24", "TraceEnvelopeV1", PortPolarity.Offer),
        PortDefinition("effect", "P24", "EffectRefV1", PortPolarity.Neutral)
    ))
    node("auth", "Session boundary", "N33", "edge", ports = listOf(
        PortDefinition("grant", "P20", "ScopedGrantV1", PortPolarity.Offer),
        PortDefinition("principal", "P24", "PrincipalV1", PortPolarity.Neutral)
    ))
    node("chat", "Messaging service", "N19", "edge", ports = listOf(
        PortDefinition("send", "P02", "SendMessageV1", PortPolarity.Offer),
        PortDefinition("storage", "P02", "MessageLedgerV1", PortPolarity.Require)
    ))
    node("log", "Message authority", "N21", "edge", ports = listOf(
        PortDefinition("append", "P02", "MessageLedgerV1", PortPolarity.Offer),
        PortDefinition("resource", "P24", "ConversationRefV1", PortPolarity.Neutral)
    ))
    node("trace", "Trace exporter", "N38", "edge", ports = listOf(
        PortDefinition("span", "P24", "TraceEnvelopeV1", PortPolarity.Require)
    ))
    relation("nav", "H36",
        Incidence("route", "destination", "provider", 0),
        Incidence("screen", "route", "consumer", 0)
    )
    relation("intent", "H02",
        Incidence("screen", "intent", "provider", 0),
        Incidence("controller", "input", "consumer", 0)
    )
    relation("grant", "H23",
        Incidence("auth", "grant", "provider", 0),
        Incidence("controller", "authority", "consumer", 0)
    )
    relation("service", "H05",
        Incidence("chat", "send", "provider", 0),
        Incidence("controller", "send", "consumer", 0)
    )
    relation("persist", "H05",
        Incidence("log", "append", "provider", 0),
        Incidence("chat", "storage", "consumer", 0)
    )
    relation("observe", "H04",
        Incidence("controller", "span", "provider", 0),
        Incidence("trace", "span", "consumer", 0)
    )
    relation("scope", "H23",
        Incidence("auth", "principal", "principal", 0),
        Incidence("controller", "effect", "effect", 1),
        Incidence("log", "resource", "resource", 2)
    )
}

Offline field notebook: local edits and reconciled sync

Who owns an edit when a mobile device is disconnected?

Inspect this design · Definition JSON · revision 49761185d049

Diagram legend. Frames are named graph regions. Left/right pins show required/offered participation, not a universal schedule. Filled pins are connected; hollow pins are unbound. A junction card preserves one relation's role slots.

MarkerMeaning in this diagram
▤ N11Surface / view component
↯ N02Effectful operation
▰ N21Database / state authority
⇄ N25Cache / replica / sync agent
↗ N19Tool / external API adapter
⊂ N33Policy / principal boundary
↦ P04Push event
⇆ P02Request / response
○ P01Value / immutable snapshot
◎ P24Inspection / evidence
⊂ P20Authorization request / grant
→ H02Value flow
⇆ H05Request / reply binding
⊂ H23Scoped authorization
⊢ H27Derivation / provenance

Logic and ownership

A common Kotlin merge function computes a candidate from base, local and remote values; conflicts remain explicit values for the Surface.

Failure and qualification

  • Local durability and remote acceptance are separate receipts.
  • Offline grants expire; sensitive effects can require reauthentication before upload.
  • The merge law and tombstone retention must be specified for each data type; a replica relation does not make every edit commutative.

Hyperedge roles

RelationPatternParticipants, preserving role positions
inputH02 — Value flow {#h02--value-flow}provider[0]: view.edit; consumer[0]: edit.input
localH05 — Request / reply binding {#h05--request--reply-binding}provider[0]: store.commit; consumer[0]: edit.store
pendingH02 — Value flow {#h02--value-flow}provider[0]: store.pending; consumer[0]: sync.pending
remoteH05 — Request / reply binding {#h05--request--reply-binding}provider[0]: api.reconcile; consumer[0]: sync.remote
writeH05 — Request / reply binding {#h05--request--reply-binding}provider[0]: authority.conditional; consumer[0]: api.storage
grantH23 — Scoped authorization {#h23--scoped-authorization}provider[0]: policy.grant; consumer[0]: api.authority
lineageH27 — Derivation / provenance {#h27--derivation--provenance}localRevision[0]: store.revision; remoteRevision[1]: authority.revision; reconciler[2]: sync.reconciler

Kotlin common declaration

Import dev.shibasis.reaktor.portgraph.definition.* for this structural declaration. Behavior stays in ordinary Kotlin and binds through qualified runtime ports and adapters. Declaring a graph does not activate a system.

graphDefinition("mobile-offline", "49761185d049a8802597968e03c4942f3e314819d581eea6dc6068cdab35abbd") {
    region("device", "Mobile device")
    region("edge", "Edge services")
    node("view", "Notebook Surface", "N11", "device", ports = listOf(
        PortDefinition("edit", "P04", "EditIntentV1", PortPolarity.Offer)
    ))
    node("edit", "Local edit logic", "N02", "device", ports = listOf(
        PortDefinition("input", "P04", "EditIntentV1", PortPolarity.Require),
        PortDefinition("store", "P02", "LocalMutationV1", PortPolarity.Require)
    ))
    node("store", "Local durable store", "N21", "device", ports = listOf(
        PortDefinition("commit", "P02", "LocalMutationV1", PortPolarity.Offer),
        PortDefinition("pending", "P01", "PendingBatchV1", PortPolarity.Offer),
        PortDefinition("revision", "P24", "RevisionRefV1", PortPolarity.Neutral)
    ))
    node("sync", "Sync agent", "N25", "device", ports = listOf(
        PortDefinition("pending", "P01", "PendingBatchV1", PortPolarity.Require),
        PortDefinition("remote", "P02", "SyncBatchV1", PortPolarity.Require),
        PortDefinition("reconciler", "P24", "ImplementationRefV1", PortPolarity.Neutral)
    ))
    node("api", "Notebook service", "N19", "edge", ports = listOf(
        PortDefinition("reconcile", "P02", "SyncBatchV1", PortPolarity.Offer),
        PortDefinition("storage", "P02", "ConditionalWriteV1", PortPolarity.Require),
        PortDefinition("authority", "P20", "WorkspaceGrantV1", PortPolarity.Require)
    ))
    node("authority", "Notebook authority", "N21", "edge", ports = listOf(
        PortDefinition("conditional", "P02", "ConditionalWriteV1", PortPolarity.Offer),
        PortDefinition("revision", "P24", "RevisionRefV1", PortPolarity.Neutral)
    ))
    node("policy", "Workspace policy", "N33", "edge", ports = listOf(
        PortDefinition("grant", "P20", "WorkspaceGrantV1", PortPolarity.Offer)
    ))
    relation("input", "H02",
        Incidence("view", "edit", "provider", 0),
        Incidence("edit", "input", "consumer", 0)
    )
    relation("local", "H05",
        Incidence("store", "commit", "provider", 0),
        Incidence("edit", "store", "consumer", 0)
    )
    relation("pending", "H02",
        Incidence("store", "pending", "provider", 0),
        Incidence("sync", "pending", "consumer", 0)
    )
    relation("remote", "H05",
        Incidence("api", "reconcile", "provider", 0),
        Incidence("sync", "remote", "consumer", 0)
    )
    relation("write", "H05",
        Incidence("authority", "conditional", "provider", 0),
        Incidence("api", "storage", "consumer", 0)
    )
    relation("grant", "H23",
        Incidence("policy", "grant", "provider", 0),
        Incidence("api", "authority", "consumer", 0)
    )
    relation("lineage", "H27",
        Incidence("store", "revision", "localRevision", 0),
        Incidence("authority", "revision", "remoteRevision", 1),
        Incidence("sync", "reconciler", "reconciler", 2)
    )
}

Camera pipeline: permission, native buffers and inference

Which boundary can carry a pointer and which requires bytes?

Inspect this design · Definition JSON · revision 90903c09d808

Diagram legend. Frames are named graph regions. Left/right pins show required/offered participation, not a universal schedule. Filled pins are connected; hollow pins are unbound. A junction card preserves one relation's role slots.

MarkerMeaning in this diagram
▤ N11Surface / view component
⊂ N33Policy / principal boundary
⚙ N15Device / platform adapter
▱ N26Blob / buffer / asset store
◆ N16Native resource / engine
↗ N19Tool / external API adapter
◉ P08Signal / reactive state
○ P01Value / immutable snapshot
⇆ P02Request / response
⊂ P20Authorization request / grant
◆ P16Owned resource transfer
◎ P24Inspection / evidence
◇̸ P17Borrowed buffer / view
⊂ H23Scoped authorization
→ H02Value flow
— H01Exclusive binding
⇆ H05Request / reply binding
⊏ H19Ownership / containment

Logic and ownership

Use ordinary Kotlin to crop and decide whether to upload. Invoke native compute through the qualified FFI adapter; the graph exposes the lease contract.

Failure and qualification

  • A borrowed view expires at its owner-defined scope; asynchronous retention needs a separate ownership transfer.
  • A local pointer never becomes a network address. Serialize an encoded artifact for the edge upload.
  • JNI/cinterop evidence must match the target, ABI, alignment, thread affinity and release path; the diagram supplies no universal zero-copy guarantee.

Hyperedge roles

RelationPatternParticipants, preserving role positions
permissionH23 — Scoped authorization {#h23--scoped-authorization}provider[0]: permission.grant; consumer[0]: camera.permission
previewH02 — Value flow {#h02--value-flow}provider[0]: camera.preview; consumer[0]: surface.preview
frameH02 — Value flow {#h02--value-flow}provider[0]: camera.frame; consumer[0]: pool.frame
borrowH01 — Exclusive binding {#h01--exclusive-binding}provider[0]: pool.borrow; consumer[0]: native.input
resultH02 — Value flow {#h02--value-flow}provider[0]: native.result; consumer[0]: surface.result
uploadH05 — Request / reply binding {#h05--request--reply-binding}provider[0]: upload.put; consumer[0]: surface.upload
storageH05 — Request / reply binding {#h05--request--reply-binding}provider[0]: blob.put; consumer[0]: upload.store
lifetimeH19 — Ownership / containment {#h19--ownership--containment}owner[0]: pool.owner; borrower[1]: native.borrower; release[2]: camera.release

Kotlin common declaration

Import dev.shibasis.reaktor.portgraph.definition.* for this structural declaration. Behavior stays in ordinary Kotlin and binds through qualified runtime ports and adapters. Declaring a graph does not activate a system.

graphDefinition("mobile-camera", "90903c09d808b0d844575eca374673893ee42048ed9bcbf4517d71788f8cae26") {
    region("device", "Mobile device")
    region("edge", "Edge services")
    node("surface", "Capture Surface", "N11", "device", ports = listOf(
        PortDefinition("preview", "P08", "PreviewStateV1", PortPolarity.Require),
        PortDefinition("result", "P01", "DetectionV1", PortPolarity.Require),
        PortDefinition("upload", "P02", "MediaUploadV1", PortPolarity.Require)
    ))
    node("permission", "Camera permission", "N33", "device", ports = listOf(
        PortDefinition("grant", "P20", "DeviceGrantV1", PortPolarity.Offer)
    ))
    node("camera", "Camera adapter", "N15", "device", ports = listOf(
        PortDefinition("permission", "P20", "DeviceGrantV1", PortPolarity.Require),
        PortDefinition("preview", "P08", "PreviewStateV1", PortPolarity.Offer),
        PortDefinition("frame", "P16", "OwnedFrameV1", PortPolarity.Offer),
        PortDefinition("release", "P24", "ReleaseRefV1", PortPolarity.Neutral)
    ))
    node("pool", "Owned frame pool", "N26", "device", ports = listOf(
        PortDefinition("frame", "P16", "OwnedFrameV1", PortPolarity.Require),
        PortDefinition("borrow", "P17", "FrameLeaseV1", PortPolarity.Offer),
        PortDefinition("owner", "P24", "ResourceOwnerV1", PortPolarity.Neutral)
    ))
    node("native", "Native inference kernel", "N16", "device", ports = listOf(
        PortDefinition("input", "P17", "FrameLeaseV1", PortPolarity.Require),
        PortDefinition("result", "P01", "DetectionV1", PortPolarity.Offer),
        PortDefinition("borrower", "P24", "BorrowScopeV1", PortPolarity.Neutral)
    ))
    node("upload", "Upload service", "N19", "edge", ports = listOf(
        PortDefinition("put", "P02", "MediaUploadV1", PortPolarity.Offer),
        PortDefinition("store", "P02", "ObjectPutV1", PortPolarity.Require)
    ))
    node("blob", "Object store", "N26", "edge", ports = listOf(
        PortDefinition("put", "P02", "ObjectPutV1", PortPolarity.Offer)
    ))
    relation("permission", "H23",
        Incidence("permission", "grant", "provider", 0),
        Incidence("camera", "permission", "consumer", 0)
    )
    relation("preview", "H02",
        Incidence("camera", "preview", "provider", 0),
        Incidence("surface", "preview", "consumer", 0)
    )
    relation("frame", "H02",
        Incidence("camera", "frame", "provider", 0),
        Incidence("pool", "frame", "consumer", 0)
    )
    relation("borrow", "H01",
        Incidence("pool", "borrow", "provider", 0),
        Incidence("native", "input", "consumer", 0)
    )
    relation("result", "H02",
        Incidence("native", "result", "provider", 0),
        Incidence("surface", "result", "consumer", 0)
    )
    relation("upload", "H05",
        Incidence("upload", "put", "provider", 0),
        Incidence("surface", "upload", "consumer", 0)
    )
    relation("storage", "H05",
        Incidence("blob", "put", "provider", 0),
        Incidence("upload", "store", "consumer", 0)
    )
    relation("lifetime", "H19",
        Incidence("pool", "owner", "owner", 0),
        Incidence("native", "borrower", "borrower", 1),
        Incidence("camera", "release", "release", 2)
    )
}

Mobile purchase: deep link, payment and durable receipt

What survives a repeated checkout request?

Inspect this design · Definition JSON · revision 5d30a93454eb

Diagram legend. Frames are named graph regions. Left/right pins show required/offered participation, not a universal schedule. Filled pins are connected; hollow pins are unbound. A junction card preserves one relation's role slots.

MarkerMeaning in this diagram
↪ N12Route / navigation
↯ N02Effectful operation
⊂ N33Policy / principal boundary
⊟ N30External system boundary
▰ N21Database / state authority
≋ N24Queue / log / pubsub resource
▷ P11Execution / continuation
⊂ P20Authorization request / grant
⇆ P02Request / response
◎ P24Inspection / evidence
▣ P03Durable command / receipt
↪ H36Navigation / interaction route
⊂ H23Scoped authorization
⇆ H05Request / reply binding
→ H02Value flow
⊗ H16Atomic authority scope

Logic and ownership

Compute totals with a shared Kotlin pricing function. Reuse one idempotency key across retried payment attempts and reconcile provider receipts.

Failure and qualification

  • H16 identifies the ledger authority. It does not atomically include the external payment provider.
  • A webhook must authenticate its own source and reconcile against provider transaction identity.
  • Navigation success is not a purchase receipt; show pending, committed and disputed states separately.

Hyperedge roles

RelationPatternParticipants, preserving role positions
entryH36 — Navigation / interaction route {#h36--navigation--interaction-route}provider[0]: route.order; consumer[0]: checkout.order
authH23 — Scoped authorization {#h23--scoped-authorization}provider[0]: session.grant; consumer[0]: checkout.grant
payH05 — Request / reply binding {#h05--request--reply-binding}provider[0]: payment.charge; consumer[0]: checkout.pay
commitH05 — Request / reply binding {#h05--request--reply-binding}provider[0]: ledger.commit; consumer[0]: checkout.commit
outboxH02 — Value flow {#h02--value-flow}provider[0]: ledger.receipt; consumer[0]: events.receipt
purchaseH16 — Atomic authority scope {#h16--atomic-authority-scope}authority[0]: ledger.authority; intent[1]: checkout.intent; externalEffect[2]: payment.effect

Kotlin common declaration

Import dev.shibasis.reaktor.portgraph.definition.* for this structural declaration. Behavior stays in ordinary Kotlin and binds through qualified runtime ports and adapters. Declaring a graph does not activate a system.

graphDefinition("mobile-purchase", "5d30a93454eb79a62928dba66a0ff1fcc78f79f081b07b972a19bee696c90b66") {
    region("device", "Mobile device")
    region("edge", "Edge services")
    node("route", "Purchase route", "N12", "device", ports = listOf(
        PortDefinition("order", "P11", "OrderRouteV1", PortPolarity.Offer)
    ))
    node("checkout", "Checkout logic", "N02", "device", ports = listOf(
        PortDefinition("order", "P11", "OrderRouteV1", PortPolarity.Require),
        PortDefinition("grant", "P20", "PurchaseGrantV1", PortPolarity.Require),
        PortDefinition("pay", "P02", "ChargeV1", PortPolarity.Require),
        PortDefinition("commit", "P02", "PurchaseCommitV1", PortPolarity.Require),
        PortDefinition("intent", "P24", "PurchaseIntentV1", PortPolarity.Neutral)
    ))
    node("session", "Principal boundary", "N33", "edge", ports = listOf(
        PortDefinition("grant", "P20", "PurchaseGrantV1", PortPolarity.Offer)
    ))
    node("payment", "Payment adapter", "N30", "edge", ports = listOf(
        PortDefinition("charge", "P02", "ChargeV1", PortPolarity.Offer),
        PortDefinition("effect", "P24", "ExternalEffectRefV1", PortPolarity.Neutral)
    ))
    node("ledger", "Purchase ledger", "N21", "edge", ports = listOf(
        PortDefinition("commit", "P02", "PurchaseCommitV1", PortPolarity.Offer),
        PortDefinition("receipt", "P03", "PurchaseReceiptV1", PortPolarity.Offer),
        PortDefinition("authority", "P24", "TransactionAuthorityV1", PortPolarity.Neutral)
    ))
    node("events", "Receipt outbox", "N24", "edge", ports = listOf(
        PortDefinition("receipt", "P03", "PurchaseReceiptV1", PortPolarity.Require)
    ))
    relation("entry", "H36",
        Incidence("route", "order", "provider", 0),
        Incidence("checkout", "order", "consumer", 0)
    )
    relation("auth", "H23",
        Incidence("session", "grant", "provider", 0),
        Incidence("checkout", "grant", "consumer", 0)
    )
    relation("pay", "H05",
        Incidence("payment", "charge", "provider", 0),
        Incidence("checkout", "pay", "consumer", 0)
    )
    relation("commit", "H05",
        Incidence("ledger", "commit", "provider", 0),
        Incidence("checkout", "commit", "consumer", 0)
    )
    relation("outbox", "H02",
        Incidence("ledger", "receipt", "provider", 0),
        Incidence("events", "receipt", "consumer", 0)
    )
    relation("purchase", "H16",
        Incidence("ledger", "authority", "authority", 0),
        Incidence("checkout", "intent", "intent", 1),
        Incidence("payment", "effect", "externalEffect", 2)
    )
}

Mobile feature region: signed subgraph code push

Which feature can be replaced while an old screen is still active?

Inspect this design · Definition JSON · revision dfd0f654fb38

Diagram legend. Frames are named graph regions. Left/right pins show required/offered participation, not a universal schedule. Filled pins are connected; hollow pins are unbound. A junction card preserves one relation's role slots.

MarkerMeaning in this diagram
▸ N27Runtime host / executor
↪ N12Route / navigation
⌘ N10Function / blueprint region
⚙ N15Device / platform adapter
λ N34Build / compiler pass
▧ N35Artifact / release manifest
⊂ N33Policy / principal boundary
⊕ P19Capability descriptor
⇆ P02Request / response
◎ P24Inspection / evidence
▧ P23Artifact / schema reference
⊂ P20Authorization request / grant
⇥ H30Boundary export / alias
⇆ H05Request / reply binding
▧ H26Artifact dependency
⊂ H23Scoped authorization
⏻ H29Activation cohort

Logic and ownership

The graph DSL constructs the exported feature boundary; arbitrary common logic compiles to Kotlin/JS. A host-qualified loader publishes a new activation only after contract and artifact checks.

Failure and qualification

  • Existing calls remain pinned to their original generation until quiescence or explicit migration.
  • State conversion, native handles and UI thread ownership are separate admission obligations.
  • Code-push policy, signature verification and rollback are target-specific. This example is a design, not proof that app-store targets admit it.

Hyperedge roles

RelationPatternParticipants, preserving role positions
routesH30 — Boundary export / alias {#h30--boundary-export--alias}provider[0]: routes.export; consumer[0]: shell.routes
callsH05 — Request / reply binding {#h05--request--reply-binding}provider[0]: old.feature; consumer[0]: shell.feature
bundleH26 — Artifact dependency {#h26--artifact-dependency}provider[0]: build.bundle; consumer[0]: artifact.input
fetchH26 — Artifact dependency {#h26--artifact-dependency}provider[0]: artifact.bundle; consumer[0]: loader.bundle
admitH23 — Scoped authorization {#h23--scoped-authorization}provider[0]: gate.decision; consumer[0]: loader.admission
cohortH29 — Activation cohort {#h29--activation-cohort}oldActivation[0]: old.epoch; candidate[1]: artifact.revision; publisher[2]: loader.publisher

Kotlin common declaration

Import dev.shibasis.reaktor.portgraph.definition.* for this structural declaration. Behavior stays in ordinary Kotlin and binds through qualified runtime ports and adapters. Declaring a graph does not activate a system.

graphDefinition("mobile-feature-update", "dfd0f654fb38c3b54d0b8241a5bfe11e1505a653c827882767c264dc12b251c5") {
    region("device", "Mobile device")
    region("edge", "Edge services")
    node("shell", "Stable app shell", "N27", "device", ports = listOf(
        PortDefinition("routes", "P19", "RouteExportsV1", PortPolarity.Require),
        PortDefinition("feature", "P02", "FeatureContractV1", PortPolarity.Require)
    ))
    node("routes", "Feature route exports", "N12", "device", ports = listOf(
        PortDefinition("export", "P19", "RouteExportsV1", PortPolarity.Offer)
    ))
    node("old", "Active feature epoch", "N10", "device", ports = listOf(
        PortDefinition("feature", "P02", "FeatureContractV1", PortPolarity.Offer),
        PortDefinition("epoch", "P24", "ActivationRefV1", PortPolarity.Neutral)
    ))
    node("loader", "JS host / FFI adapter", "N15", "device", ports = listOf(
        PortDefinition("bundle", "P23", "BundleRefV1", PortPolarity.Require),
        PortDefinition("admission", "P20", "AdmissionV1", PortPolarity.Require),
        PortDefinition("publisher", "P24", "HostRefV1", PortPolarity.Neutral)
    ))
    node("build", "Feature packager", "N34", "edge", ports = listOf(
        PortDefinition("bundle", "P23", "BundleRefV1", PortPolarity.Offer)
    ))
    node("artifact", "Signed bundle", "N35", "edge", ports = listOf(
        PortDefinition("input", "P23", "BundleRefV1", PortPolarity.Require),
        PortDefinition("bundle", "P23", "BundleRefV1", PortPolarity.Offer),
        PortDefinition("revision", "P24", "RevisionRefV1", PortPolarity.Neutral)
    ))
    node("gate", "Host admission gate", "N33", "device", ports = listOf(
        PortDefinition("decision", "P20", "AdmissionV1", PortPolarity.Offer)
    ))
    relation("routes", "H30",
        Incidence("routes", "export", "provider", 0),
        Incidence("shell", "routes", "consumer", 0)
    )
    relation("calls", "H05",
        Incidence("old", "feature", "provider", 0),
        Incidence("shell", "feature", "consumer", 0)
    )
    relation("bundle", "H26",
        Incidence("build", "bundle", "provider", 0),
        Incidence("artifact", "input", "consumer", 0)
    )
    relation("fetch", "H26",
        Incidence("artifact", "bundle", "provider", 0),
        Incidence("loader", "bundle", "consumer", 0)
    )
    relation("admit", "H23",
        Incidence("gate", "decision", "provider", 0),
        Incidence("loader", "admission", "consumer", 0)
    )
    relation("cohort", "H29",
        Incidence("old", "epoch", "oldActivation", 0),
        Incidence("artifact", "revision", "candidate", 1),
        Incidence("loader", "publisher", "publisher", 2)
    )
}