From dd5530ecb5d9aaf353e28526d4bb8d36d1d4e789 Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Fri, 22 May 2026 23:15:07 +0100 Subject: [PATCH] Clarify CultNet receipt authority --- epiphany-core/src/runtime_spine.rs | 22 ++++++++ ...epiphany.swarm-control-receipt.schema.json | 53 +++++++++++++++++- state/ledgers.msgpack | Bin 148588 -> 148840 bytes 3 files changed, 74 insertions(+), 1 deletion(-) diff --git a/epiphany-core/src/runtime_spine.rs b/epiphany-core/src/runtime_spine.rs index a116b2ea..b9c40652 100644 --- a/epiphany-core/src/runtime_spine.rs +++ b/epiphany-core/src/runtime_spine.rs @@ -1387,6 +1387,7 @@ fn epiphany_mutation_contracts() -> Vec { vec![RUNTIME_JOB_TYPE], vec![ "Worker launch requests are typed task-intent documents; runtime jobs are lifecycle receipts, not the source of work intent.", + "Core/coordinator policy owns the launch yes/no; transport bridges translate, gather host facts, execute side effects, and return receipts.", ], ), mutation_contract( @@ -1597,6 +1598,7 @@ fn epiphany_mutation_contracts() -> Vec { vec![ "Reorientation policy is the typed resume/regather verdict surface.", "Launch and acceptance stay review-gated through explicit typed intents.", + "The bridge may carry a reorient launch intent and report receipts, but the resume/regather verdict belongs to core.", ], ), read_only_surface_contract( @@ -1618,6 +1620,7 @@ fn epiphany_mutation_contracts() -> Vec { vec![ "Job reflection over typed job bindings and runtime-spine lifecycle receipts.", "Heartbeat/runtime-spine owns activation; callers submit typed intents and watch receipts.", + "Launch receipts must name decisionOwner and bridgeRole so transport never grows an unreadable second opinion.", ], ), coordinator_surface_contract( @@ -2282,6 +2285,25 @@ mod tests { schema.document_type.as_deref() == Some("epiphany.swarm_control_receipt.v0") && schema.schema_version.as_deref() == Some("epiphany.swarm_control_receipt.v0") })); + let receipt_schema_path = + epiphany_schema_root().join("epiphany.swarm-control-receipt.schema.json"); + let receipt_schema: serde_json::Value = + serde_json::from_str(&fs::read_to_string(receipt_schema_path)?)?; + let required = receipt_schema["required"] + .as_array() + .expect("receipt schema should list required fields"); + assert!(required.iter().any(|field| field == "decisionOwner")); + assert!(required.iter().any(|field| field == "bridgeRole")); + assert!( + receipt_schema["properties"]["decisionOwner"]["description"] + .as_str() + .is_some_and(|description| description.contains("epiphany-core")) + ); + assert!( + receipt_schema["properties"]["bridgeRole"]["description"] + .as_str() + .is_some_and(|description| description.contains("transport")) + ); assert!(schemas.iter().any(|schema| { schema.document_type.as_deref() == Some(OPENAI_MODEL_REQUEST_TYPE) && schema.schema_version.as_deref() == Some(OPENAI_MODEL_REQUEST_SCHEMA_VERSION) diff --git a/schemas/cultnet/epiphany.swarm-control-receipt.schema.json b/schemas/cultnet/epiphany.swarm-control-receipt.schema.json index d2c1b7aa..cb341de0 100644 --- a/schemas/cultnet/epiphany.swarm-control-receipt.schema.json +++ b/schemas/cultnet/epiphany.swarm-control-receipt.schema.json @@ -2,7 +2,7 @@ "$schema": "http://json-schema.org/draft-07/schema#", "$id": "https://gamecult.dev/epiphany/cultnet/epiphany.swarm-control-receipt.schema.json", "title": "Epiphany Swarm Control Receipt", - "description": "Generic receipt emitted after a coordinator-owned Epiphany control action.", + "description": "Generic receipt emitted after a coordinator-owned Epiphany control action. The receipt names the authority that made the final yes/no decision and the transport role that carried it out.", "type": "object", "additionalProperties": true, "properties": { @@ -18,6 +18,41 @@ "authority": { "type": "string" }, + "decisionOwner": { + "type": "string", + "description": "Authority that made the final yes/no verdict. For Epiphany-owned launch and reorientation flows this is epiphany-core, not the transport bridge." + }, + "bridgeRole": { + "type": "string", + "description": "What the bridge did for this action. Expected values are transport, translate, gatherHostFacts, executeSideEffects, and reportReceipt, alone or joined with '+'." + }, + "intentDocumentType": { + "type": [ + "string", + "null" + ] + }, + "decision": { + "type": "object", + "additionalProperties": true, + "properties": { + "verdict": { + "type": "string" + }, + "reason": { + "type": "string" + }, + "sourceDocumentType": { + "type": "string" + }, + "sourceRevision": { + "type": [ + "integer", + "null" + ] + } + } + }, "targetDocumentType": { "type": "string" }, @@ -51,6 +86,20 @@ "type": "string" } }, + "hostFacts": { + "type": "array", + "description": "Host/runtime facts gathered by the bridge before execution, expressed as readable labels or artifact refs rather than private context.", + "items": { + "type": "string" + } + }, + "sideEffects": { + "type": "array", + "description": "Side effects the bridge performed after the core/coordinator verdict.", + "items": { + "type": "string" + } + }, "note": { "type": "string" } @@ -60,6 +109,8 @@ "ok", "action", "authority", + "decisionOwner", + "bridgeRole", "targetDocumentType", "status", "summary" diff --git a/state/ledgers.msgpack b/state/ledgers.msgpack index f02fa3d7d754503afb6d311b833e274d80655795..220f5006ecb01471cab2221c513c281b89fa1fb0 100644 GIT binary patch delta 283 zcmZ8cu}T9$6y(ySwNCLsEHB0-v51{wYh`7(*?s07?Cra}_s%OVEEFpX;l4u<#Lge^ zKP>zR7t&b`!^|)-^yL(=FU-y;#-GT|2iv zS&n{I&F;T1`eavVpo5nd0%sFWAQ+H3Q^-6Nvg=8(ppSl&5F!Rr;tV`w6m6&jpCfF9 z;P1w%C`f`T=;pY4Oq-~Yc{ua-A|`?+K? delta 52 zcmaFS#rdX#bAmFHi_u2aLPo}I&C?m%r!z8cpU%j1z)5tAk%5t!u7Rnpkzt5|rIoRn Im7!r20M`l+yZ`_I