Distributed systems — worked graph designs
These designs describe intended system behavior and qualification requirements. Inspect the system atlas for named ports and relation participants.
Event pipeline: durable admission, work and projections
What does an acknowledgement actually promise?
Inspect this design · Definition JSON · revision 52615423ff54
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.
| Marker | Meaning in this diagram |
|---|---|
| ↗ N19 | Tool / external API adapter |
| ≋ N24 | Queue / log / pubsub resource |
| ▣ N05 | Actor / statechart |
| ▰ N21 | Database / state authority |
| ⌕ N22 | Query / materialized view |
| ◎ N38 | Observer / query / visitor |
| ▣ P03 | Durable command / receipt |
| ◎ P24 | Inspection / evidence |
| ↦ P04 | Push event |
| ⇆ P02 | Request / response |
| → H02 | Value flow |
| ⇆ H05 | Request / reply binding |
| ↦ H04 | Event subscription |
| ⊢ H27 | Derivation / provenance |
Logic and ownership
A common Kotlin handler computes the new value; the authority compares the prior version and records the operation key in the same commit.
Failure and qualification
- Queue acknowledgement, effect commit and projection position are three distinct facts.
- At-least-once delivery requires idempotent effects; no exactly-once end-to-end claim follows from an edge.
- Backpressure and dead-letter retention are declared transport policies.
Hyperedge roles
| Relation | Pattern | Participants, preserving role positions |
|---|---|---|
| admit | H02 — Value flow {#h02--value-flow} | provider[0]: ingest.command; consumer[0]: queue.command |
| delivery | H02 — Value flow {#h02--value-flow} | provider[0]: queue.delivery; consumer[0]: worker.delivery |
| commit | H05 — Request / reply binding {#h05--request--reply-binding} | provider[0]: ledger.commit; consumer[0]: worker.effect |
| project | H02 — Value flow {#h02--value-flow} | provider[0]: ledger.events; consumer[0]: view.events |
| trace | H04 — Event subscription {#h04--event-subscription} | provider[0]: worker.span; consumer[0]: trace.span |
| lineage | H27 — Derivation / provenance {#h27--derivation--provenance} | intent[0]: ingest.operation; delivery[1]: queue.deliveryRef; effect[2]: ledger.receipt |
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("distributed-events", "52615423ff5461a87601f2e0eeb53dc8e6fa5cdb5a76afc3c31dbab2ce28f97e") {
region("control", "Control plane")
region("data", "Data plane")
node("ingest", "Admission service", "N19", "control", ports = listOf(
PortDefinition("command", "P03", "WorkCommandV1", PortPolarity.Offer),
PortDefinition("operation", "P24", "OperationRefV1", PortPolarity.Neutral)
))
node("queue", "Durable queue", "N24", "data", ports = listOf(
PortDefinition("command", "P03", "WorkCommandV1", PortPolarity.Require),
PortDefinition("delivery", "P04", "DeliveryV1", PortPolarity.Offer),
PortDefinition("deliveryRef", "P24", "DeliveryRefV1", PortPolarity.Neutral)
))
node("worker", "Processing actor", "N05", "data", ports = listOf(
PortDefinition("delivery", "P04", "DeliveryV1", PortPolarity.Require),
PortDefinition("effect", "P02", "EffectCommitV1", PortPolarity.Require),
PortDefinition("span", "P24", "TraceV1", PortPolarity.Offer)
))
node("ledger", "Effect ledger", "N21", "data", ports = listOf(
PortDefinition("commit", "P02", "EffectCommitV1", PortPolarity.Offer),
PortDefinition("events", "P04", "CommittedEventV1", PortPolarity.Offer),
PortDefinition("receipt", "P24", "ReceiptRefV1", PortPolarity.Neutral)
))
node("view", "Materialized view", "N22", "data", ports = listOf(
PortDefinition("events", "P04", "CommittedEventV1", PortPolarity.Require)
))
node("trace", "Telemetry collector", "N38", "control", ports = listOf(
PortDefinition("span", "P24", "TraceV1", PortPolarity.Require)
))
relation("admit", "H02",
Incidence("ingest", "command", "provider", 0),
Incidence("queue", "command", "consumer", 0)
)
relation("delivery", "H02",
Incidence("queue", "delivery", "provider", 0),
Incidence("worker", "delivery", "consumer", 0)
)
relation("commit", "H05",
Incidence("ledger", "commit", "provider", 0),
Incidence("worker", "effect", "consumer", 0)
)
relation("project", "H02",
Incidence("ledger", "events", "provider", 0),
Incidence("view", "events", "consumer", 0)
)
relation("trace", "H04",
Incidence("worker", "span", "provider", 0),
Incidence("trace", "span", "consumer", 0)
)
relation("lineage", "H27",
Incidence("ingest", "operation", "intent", 0),
Incidence("queue", "deliveryRef", "delivery", 1),
Incidence("ledger", "receipt", "effect", 2)
)
}
Sharded actors: routing, supervision and replica epochs
Which actor instance owns a key after a placement change?
Inspect this design · Definition JSON · revision b2f3314335b1
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.
| Marker | Meaning in this diagram |
|---|---|
| ▰ N21 | Database / state authority |
| ⌕ N22 | Query / materialized view |
| ▣ N05 | Actor / statechart |
| ≋ N24 | Queue / log / pubsub resource |
| ○ P01 | Value / immutable snapshot |
| ⇆ P02 | Request / response |
| ◎ P24 | Inspection / evidence |
| → H02 | Value flow |
| ⇆ H05 | Request / reply binding |
| ⋉ H15 | Partition / route |
| ⌂ H18 | Supervision |
Logic and ownership
Kotlin hashes a domain key using the published placement epoch. The mailbox handler serializes local state transitions and checks fencing at the storage authority.
Failure and qualification
- A placement update requires handoff/fencing; map coordinates are not shard ownership.
- Local actor serialization does not establish global ordering between shards.
- Restart policy names state restoration, mailbox loss and retry behavior explicitly.
Hyperedge roles
| Relation | Pattern | Participants, preserving role positions |
|---|---|---|
| placement | H02 — Value flow {#h02--value-flow} | provider[0]: registry.map; consumer[0]: router.placement |
| a | H05 — Request / reply binding {#h05--request--reply-binding} | provider[0]: a.mailbox; consumer[0]: router.a |
| b | H05 — Request / reply binding {#h05--request--reply-binding} | provider[0]: b.mailbox; consumer[0]: router.b |
| logA | H05 — Request / reply binding {#h05--request--reply-binding} | provider[0]: log.appendA; consumer[0]: a.log |
| logB | H05 — Request / reply binding {#h05--request--reply-binding} | provider[0]: log.appendB; consumer[0]: b.log |
| partition | H15 — Partition / route {#h15--partition--route} | router[0]: router.keyspace; shard[1]: a.range; shard[2]: b.range |
| supervision | H18 — Supervision {#h18--supervision} | supervisor[0]: supervisor.supervisor; child[1]: a.activation; child[2]: b.activation |
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("distributed-actors", "b2f3314335b1265d88480ce29ed2a486622b73270072b98f761b71365ebeaf89") {
region("control", "Control plane")
region("data", "Data plane")
node("registry", "Placement registry", "N21", "control", ports = listOf(
PortDefinition("map", "P01", "PlacementMapV1", PortPolarity.Offer)
))
node("router", "Key router", "N22", "control", ports = listOf(
PortDefinition("placement", "P01", "PlacementMapV1", PortPolarity.Require),
PortDefinition("a", "P02", "ActorCommandV1", PortPolarity.Require),
PortDefinition("b", "P02", "ActorCommandV1", PortPolarity.Require),
PortDefinition("keyspace", "P24", "KeyspaceRefV1", PortPolarity.Neutral)
))
node("supervisor", "Actor supervisor", "N05", "data", ports = listOf(
PortDefinition("supervisor", "P24", "SupervisorRefV1", PortPolarity.Neutral)
))
node("a", "Actor shard A", "N05", "data", ports = listOf(
PortDefinition("mailbox", "P02", "ActorCommandV1", PortPolarity.Offer),
PortDefinition("log", "P02", "ActorAppendV1", PortPolarity.Require),
PortDefinition("range", "P24", "RangeRefV1", PortPolarity.Neutral),
PortDefinition("activation", "P24", "ActivationRefV1", PortPolarity.Neutral)
))
node("b", "Actor shard B", "N05", "data", ports = listOf(
PortDefinition("mailbox", "P02", "ActorCommandV1", PortPolarity.Offer),
PortDefinition("log", "P02", "ActorAppendV1", PortPolarity.Require),
PortDefinition("range", "P24", "RangeRefV1", PortPolarity.Neutral),
PortDefinition("activation", "P24", "ActivationRefV1", PortPolarity.Neutral)
))
node("log", "State log", "N24", "data", ports = listOf(
PortDefinition("appendA", "P02", "ActorAppendV1", PortPolarity.Offer),
PortDefinition("appendB", "P02", "ActorAppendV1", PortPolarity.Offer)
))
relation("placement", "H02",
Incidence("registry", "map", "provider", 0),
Incidence("router", "placement", "consumer", 0)
)
relation("a", "H05",
Incidence("a", "mailbox", "provider", 0),
Incidence("router", "a", "consumer", 0)
)
relation("b", "H05",
Incidence("b", "mailbox", "provider", 0),
Incidence("router", "b", "consumer", 0)
)
relation("logA", "H05",
Incidence("log", "appendA", "provider", 0),
Incidence("a", "log", "consumer", 0)
)
relation("logB", "H05",
Incidence("log", "appendB", "provider", 0),
Incidence("b", "log", "consumer", 0)
)
relation("partition", "H15",
Incidence("router", "keyspace", "router", 0),
Incidence("a", "range", "shard", 1),
Incidence("b", "range", "shard", 2)
)
relation("supervision", "H18",
Incidence("supervisor", "supervisor", "supervisor", 0),
Incidence("a", "activation", "child", 1),
Incidence("b", "activation", "child", 2)
)
}
Distributed order saga: reservations and irreversible effects
Which state allows recovery after partial success?
Inspect this design · Definition JSON · revision 8b8092117f03
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.
| Marker | Meaning in this diagram |
|---|---|
| ⇢ N06 | Workflow / saga |
| ⊂ N33 | Policy / principal boundary |
| ↗ N19 | Tool / external API adapter |
| ▰ N21 | Database / state authority |
| ⇆ P02 | Request / response |
| ⊂ P20 | Authorization request / grant |
| ◎ P24 | Inspection / evidence |
| ⇆ H05 | Request / reply binding |
| ⊂ H23 | Scoped authorization |
| ↶ H17 | Compensation relation |
Logic and ownership
Ordinary Kotlin implements the transition function from journal state and new receipts; each external step remains an explicit effect boundary.
Failure and qualification
- Refund and cancellation can fail or be disallowed; surface unresolved compensation rather than claiming rollback.
- Replays use recorded step identity and compare authority versions.
- Independent authorities need a saga protocol or an explicitly qualified transaction coordinator.
Hyperedge roles
| Relation | Pattern | Participants, preserving role positions |
|---|---|---|
| stock | H05 — Request / reply binding {#h05--request--reply-binding} | provider[0]: stock.reserve; consumer[0]: coordinator.stock |
| pay | H05 — Request / reply binding {#h05--request--reply-binding} | provider[0]: payment.charge; consumer[0]: coordinator.payment |
| ship | H05 — Request / reply binding {#h05--request--reply-binding} | provider[0]: dispatch.submit; consumer[0]: coordinator.dispatch |
| grant | H23 — Scoped authorization {#h23--scoped-authorization} | provider[0]: approve.grant; consumer[0]: coordinator.authority |
| journal | H05 — Request / reply binding {#h05--request--reply-binding} | provider[0]: journal.commit; consumer[0]: coordinator.state |
| compensation | H17 — Compensation relation {#h17--compensation-relation} | coordinator[0]: coordinator.step; stock[1]: stock.release; payment[2]: payment.refund; dispatch[3]: dispatch.cancel |
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("distributed-saga", "8b8092117f03ae98324ad886acabb4bd540beea323873fcd1649648aa2cf210b") {
region("control", "Control plane")
region("data", "Data plane")
node("coordinator", "Durable saga coordinator", "N06", "control", ports = listOf(
PortDefinition("stock", "P02", "ReservationV1", PortPolarity.Require),
PortDefinition("payment", "P02", "ChargeV1", PortPolarity.Require),
PortDefinition("dispatch", "P02", "DispatchV1", PortPolarity.Require),
PortDefinition("authority", "P20", "OrderGrantV1", PortPolarity.Require),
PortDefinition("state", "P02", "SagaCommitV1", PortPolarity.Require),
PortDefinition("step", "P24", "SagaStepRefV1", PortPolarity.Neutral)
))
node("approve", "Order approval", "N33", "control", ports = listOf(
PortDefinition("grant", "P20", "OrderGrantV1", PortPolarity.Offer)
))
node("stock", "Stock service", "N19", "data", ports = listOf(
PortDefinition("reserve", "P02", "ReservationV1", PortPolarity.Offer),
PortDefinition("release", "P24", "CompensationRefV1", PortPolarity.Neutral)
))
node("payment", "Payment service", "N19", "data", ports = listOf(
PortDefinition("charge", "P02", "ChargeV1", PortPolarity.Offer),
PortDefinition("refund", "P24", "CompensationRefV1", PortPolarity.Neutral)
))
node("dispatch", "Dispatch service", "N19", "data", ports = listOf(
PortDefinition("submit", "P02", "DispatchV1", PortPolarity.Offer),
PortDefinition("cancel", "P24", "CompensationRefV1", PortPolarity.Neutral)
))
node("journal", "Saga journal", "N21", "control", ports = listOf(
PortDefinition("commit", "P02", "SagaCommitV1", PortPolarity.Offer)
))
relation("stock", "H05",
Incidence("stock", "reserve", "provider", 0),
Incidence("coordinator", "stock", "consumer", 0)
)
relation("pay", "H05",
Incidence("payment", "charge", "provider", 0),
Incidence("coordinator", "payment", "consumer", 0)
)
relation("ship", "H05",
Incidence("dispatch", "submit", "provider", 0),
Incidence("coordinator", "dispatch", "consumer", 0)
)
relation("grant", "H23",
Incidence("approve", "grant", "provider", 0),
Incidence("coordinator", "authority", "consumer", 0)
)
relation("journal", "H05",
Incidence("journal", "commit", "provider", 0),
Incidence("coordinator", "state", "consumer", 0)
)
relation("compensation", "H17",
Incidence("coordinator", "step", "coordinator", 0),
Incidence("stock", "release", "stock", 1),
Incidence("payment", "refund", "payment", 2),
Incidence("dispatch", "cancel", "dispatch", 3)
)
}
Hybrid compute: edge admission and native regional processing
How is a graph partition lowered across incompatible hosts?
Inspect this design · Definition JSON · revision 32911535d1e8
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.
| Marker | Meaning in this diagram |
|---|---|
| ▸ N27 | Runtime host / executor |
| ⊂ N33 | Policy / principal boundary |
| ⊞ N28 | Placement / resource pool |
| ◆ N16 | Native resource / engine |
| ▱ N26 | Blob / buffer / asset store |
| ↗ N19 | Tool / external API adapter |
| ⊂ P20 | Authorization request / grant |
| ⇆ P02 | Request / response |
| ◎ P24 | Inspection / evidence |
| ▣ P03 | Durable command / receipt |
| ⊂ H23 | Scoped authorization |
| ⇆ H05 | Request / reply binding |
| → H02 | Value flow |
| ⌖ H21 | Placement |
Logic and ownership
Common Kotlin selects a qualified placement from declared host capabilities. The target adapter invokes the native kernel and publishes an artifact receipt.
Failure and qualification
- A cross-host cut replaces local call assumptions with transport, serialization and deadline contracts.
- Capability availability does not authorize consuming a host or reading its data.
- Native ABI support and environment limits must be admitted before scheduling.
Hyperedge roles
| Relation | Pattern | Participants, preserving role positions |
|---|---|---|
| authorize | H23 — Scoped authorization {#h23--scoped-authorization} | provider[0]: policy.grant; consumer[0]: edge.authority |
| place | H05 — Request / reply binding {#h05--request--reply-binding} | provider[0]: scheduler.allocate; consumer[0]: edge.scheduler |
| input | H05 — Request / reply binding {#h05--request--reply-binding} | provider[0]: artifact.read; consumer[0]: native.input |
| output | H05 — Request / reply binding {#h05--request--reply-binding} | provider[0]: artifact.write; consumer[0]: native.output |
| receipt | H02 — Value flow {#h02--value-flow} | provider[0]: native.receipt; consumer[0]: result.receipt |
| placement | H21 — Placement {#h21--placement} | region[0]: scheduler.region; host[1]: native.host; workload[2]: edge.workload |
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("distributed-hybrid", "32911535d1e802871c9e87232134d28060f0619f3c2f6d59e15517810ee6325d") {
region("control", "Control plane")
region("data", "Data plane")
node("edge", "Edge admission", "N27", "control", ports = listOf(
PortDefinition("authority", "P20", "WorkloadGrantV1", PortPolarity.Require),
PortDefinition("scheduler", "P02", "AllocateV1", PortPolarity.Require),
PortDefinition("workload", "P24", "WorkloadRefV1", PortPolarity.Neutral)
))
node("policy", "Workload policy", "N33", "control", ports = listOf(
PortDefinition("grant", "P20", "WorkloadGrantV1", PortPolarity.Offer)
))
node("scheduler", "Regional scheduler", "N28", "control", ports = listOf(
PortDefinition("allocate", "P02", "AllocateV1", PortPolarity.Offer),
PortDefinition("region", "P24", "RegionRefV1", PortPolarity.Neutral)
))
node("native", "Native compute host", "N16", "data", ports = listOf(
PortDefinition("input", "P02", "ArtifactReadV1", PortPolarity.Require),
PortDefinition("output", "P02", "ArtifactWriteV1", PortPolarity.Require),
PortDefinition("receipt", "P03", "ComputeReceiptV1", PortPolarity.Offer),
PortDefinition("host", "P24", "HostProfileV1", PortPolarity.Neutral)
))
node("artifact", "Input / result artifacts", "N26", "data", ports = listOf(
PortDefinition("read", "P02", "ArtifactReadV1", PortPolarity.Offer),
PortDefinition("write", "P02", "ArtifactWriteV1", PortPolarity.Offer)
))
node("result", "Result service", "N19", "control", ports = listOf(
PortDefinition("receipt", "P03", "ComputeReceiptV1", PortPolarity.Require)
))
relation("authorize", "H23",
Incidence("policy", "grant", "provider", 0),
Incidence("edge", "authority", "consumer", 0)
)
relation("place", "H05",
Incidence("scheduler", "allocate", "provider", 0),
Incidence("edge", "scheduler", "consumer", 0)
)
relation("input", "H05",
Incidence("artifact", "read", "provider", 0),
Incidence("native", "input", "consumer", 0)
)
relation("output", "H05",
Incidence("artifact", "write", "provider", 0),
Incidence("native", "output", "consumer", 0)
)
relation("receipt", "H02",
Incidence("native", "receipt", "provider", 0),
Incidence("result", "receipt", "consumer", 0)
)
relation("placement", "H21",
Incidence("scheduler", "region", "region", 0),
Incidence("native", "host", "host", 1),
Incidence("edge", "workload", "workload", 2)
)
}
Distributed release: exact artifacts and partial rollout
What happens when only some hosts accept a candidate?
Inspect this design · Definition JSON · revision 8e854359c8e4
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.
| Marker | Meaning in this diagram |
|---|---|
| λ N34 | Build / compiler pass |
| ▧ N35 | Artifact / release manifest |
| ⧉ N36 | Test / Shadow scenario |
| ⊂ N33 | Policy / principal boundary |
| ⇧ N40 | Deployment / migration controller |
| ▸ N27 | Runtime host / executor |
| ▧ P23 | Artifact / schema reference |
| ◎ P24 | Inspection / evidence |
| ⊂ P20 | Authorization request / grant |
| ⇆ P02 | Request / response |
| ▧ H26 | Artifact dependency |
| ✓ H28 | Evidence supports claim |
| ⊂ H23 | Scoped authorization |
| ⇆ H05 | Request / reply binding |
| ⏻ H29 | Activation cohort |
Logic and ownership
Kotlin computes a candidate plan from observed host receipts, but only the independent gate grants execution for the fingerprinted plan.
Failure and qualification
- Per-host atomic publication does not imply simultaneous global activation.
- Mixed-version traffic requires a compatibility protocol or a declared isolation boundary.
- Partial rollout and stale receipts remain visible until reconciliation; rollback itself needs state and target qualification.
Hyperedge roles
| Relation | Pattern | Participants, preserving role positions |
|---|---|---|
| bundle | H26 — Artifact dependency {#h26--artifact-dependency} | provider[0]: build.artifacts; consumer[0]: bundle.input |
| test | H26 — Artifact dependency {#h26--artifact-dependency} | provider[0]: bundle.artifacts; consumer[0]: shadow.candidate |
| proof | H28 — Evidence supports claim {#h28--evidence-supports-claim} | provider[0]: shadow.evidence; consumer[0]: gate.evidence |
| decision | H23 — Scoped authorization {#h23--scoped-authorization} | provider[0]: gate.decision; consumer[0]: rollout.authority |
| a | H05 — Request / reply binding {#h05--request--reply-binding} | provider[0]: a.admit; consumer[0]: rollout.a |
| b | H05 — Request / reply binding {#h05--request--reply-binding} | provider[0]: b.admit; consumer[0]: rollout.b |
| cohort | H29 — Activation cohort {#h29--activation-cohort} | candidate[0]: bundle.revision; host[1]: a.epoch; host[2]: b.epoch; reconciler[3]: rollout.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("distributed-release", "8e854359c8e4e8a7da2e4149c7cfeed85fe3ab5897c4b598c0a363c93c49bd88") {
region("control", "Control plane")
region("data", "Data plane")
node("build", "Compiler / packager", "N34", "control", ports = listOf(
PortDefinition("artifacts", "P23", "ArtifactVectorV1", PortPolarity.Offer)
))
node("bundle", "Release artifacts", "N35", "control", ports = listOf(
PortDefinition("input", "P23", "ArtifactVectorV1", PortPolarity.Require),
PortDefinition("artifacts", "P23", "ArtifactVectorV1", PortPolarity.Offer),
PortDefinition("revision", "P24", "RevisionRefV1", PortPolarity.Neutral)
))
node("shadow", "Shadow scenarios", "N36", "control", ports = listOf(
PortDefinition("candidate", "P23", "ArtifactVectorV1", PortPolarity.Require),
PortDefinition("evidence", "P24", "EvidenceSetV1", PortPolarity.Offer)
))
node("gate", "Independent release gate", "N33", "control", ports = listOf(
PortDefinition("evidence", "P24", "EvidenceSetV1", PortPolarity.Require),
PortDefinition("decision", "P20", "ReleaseDecisionV1", PortPolarity.Offer)
))
node("rollout", "Rollout controller", "N40", "control", ports = listOf(
PortDefinition("authority", "P20", "ReleaseDecisionV1", PortPolarity.Require),
PortDefinition("a", "P02", "AdmissionRequestV1", PortPolarity.Require),
PortDefinition("b", "P02", "AdmissionRequestV1", PortPolarity.Require),
PortDefinition("reconciler", "P24", "ControllerRefV1", PortPolarity.Neutral)
))
node("a", "Host cohort A", "N27", "data", ports = listOf(
PortDefinition("admit", "P02", "AdmissionRequestV1", PortPolarity.Offer),
PortDefinition("epoch", "P24", "ActivationRefV1", PortPolarity.Neutral)
))
node("b", "Host cohort B", "N27", "data", ports = listOf(
PortDefinition("admit", "P02", "AdmissionRequestV1", PortPolarity.Offer),
PortDefinition("epoch", "P24", "ActivationRefV1", PortPolarity.Neutral)
))
relation("bundle", "H26",
Incidence("build", "artifacts", "provider", 0),
Incidence("bundle", "input", "consumer", 0)
)
relation("test", "H26",
Incidence("bundle", "artifacts", "provider", 0),
Incidence("shadow", "candidate", "consumer", 0)
)
relation("proof", "H28",
Incidence("shadow", "evidence", "provider", 0),
Incidence("gate", "evidence", "consumer", 0)
)
relation("decision", "H23",
Incidence("gate", "decision", "provider", 0),
Incidence("rollout", "authority", "consumer", 0)
)
relation("a", "H05",
Incidence("a", "admit", "provider", 0),
Incidence("rollout", "a", "consumer", 0)
)
relation("b", "H05",
Incidence("b", "admit", "provider", 0),
Incidence("rollout", "b", "consumer", 0)
)
relation("cohort", "H29",
Incidence("bundle", "revision", "candidate", 0),
Incidence("a", "epoch", "host", 1),
Incidence("b", "epoch", "host", 2),
Incidence("rollout", "reconciler", "reconciler", 3)
)
}