diff --git a/epiphany-core/src/runtime_spine.rs b/epiphany-core/src/runtime_spine.rs index 15f7cfc4..235be548 100644 --- a/epiphany-core/src/runtime_spine.rs +++ b/epiphany-core/src/runtime_spine.rs @@ -4,7 +4,10 @@ use anyhow::anyhow; use cultcache_rs::CultCache; use cultcache_rs::DatabaseEntry; use cultcache_rs::SingleFileMessagePackBackingStore; +use cultnet_rs::CultNetDocumentMutationContract; +use cultnet_rs::CultNetDocumentOperation; use cultnet_rs::CultNetMessage; +use cultnet_rs::CultNetMutationAuthority; use cultnet_rs::CultNetWireContract; use cultnet_rs::encode_cultnet_message_to_vec; use cultnet_rs::encode_frame; @@ -613,12 +616,16 @@ pub fn runtime_hello_frame(store_path: impl AsRef) -> Result> { role: Some("coordinator".to_string()), display_name: Some(identity.display_name), supported_document_types: Some(identity.supported_document_types), + supported_mutation_contracts: Some(epiphany_mutation_contracts()), supported_message_versions: Some(vec![ "cultnet.hello.v0".to_string(), "cultnet.document_put.v0".to_string(), "cultnet.snapshot_request.v0".to_string(), "cultnet.snapshot_response.v0".to_string(), + "cultnet.schema_catalog_request.v0".to_string(), + "cultnet.schema_catalog_response.v0".to_string(), ]), + supports_schema_catalog: Some(true), }; let payload = encode_cultnet_message_to_vec(&message, CultNetWireContract::CultNetSchemaV0)?; encode_frame(&payload) @@ -658,6 +665,142 @@ fn supported_runtime_document_types() -> Vec { ] } +fn mutation_contract( + document_type: impl Into, + payload_schema_version: impl Into, + operations: Vec, + authority: CultNetMutationAuthority, + intent_document_types: Vec<&str>, + receipt_document_types: Vec<&str>, + notes: Vec<&str>, +) -> CultNetDocumentMutationContract { + CultNetDocumentMutationContract { + document_type: document_type.into(), + payload_schema_version: Some(payload_schema_version.into()), + operations, + authority, + intent_document_types: (!intent_document_types.is_empty()).then(|| { + intent_document_types + .into_iter() + .map(str::to_string) + .collect() + }), + receipt_document_types: (!receipt_document_types.is_empty()).then(|| { + receipt_document_types + .into_iter() + .map(str::to_string) + .collect() + }), + notes: (!notes.is_empty()).then(|| notes.into_iter().map(str::to_string).collect()), + } +} + +fn epiphany_mutation_contracts() -> Vec { + vec![ + mutation_contract( + RUNTIME_IDENTITY_TYPE, + RUNTIME_SPINE_SCHEMA_VERSION, + vec![CultNetDocumentOperation::Snapshot], + CultNetMutationAuthority::ReadOnly, + vec![], + vec![], + vec!["Runtime identity is advertised by the coordinator, not remotely mutated."], + ), + mutation_contract( + RUNTIME_SESSION_TYPE, + RUNTIME_SPINE_SCHEMA_VERSION, + vec![ + CultNetDocumentOperation::Snapshot, + CultNetDocumentOperation::IntentSubmit, + CultNetDocumentOperation::ReceiptWatch, + ], + CultNetMutationAuthority::Coordinator, + vec!["epiphany.runtime.session_intent.v0"], + vec!["epiphany.swarm_control_receipt.v0"], + vec!["Sessions change through coordinator-reviewed typed intents."], + ), + mutation_contract( + RUNTIME_JOB_TYPE, + RUNTIME_SPINE_SCHEMA_VERSION, + vec![ + CultNetDocumentOperation::Snapshot, + CultNetDocumentOperation::IntentSubmit, + CultNetDocumentOperation::ReceiptWatch, + ], + CultNetMutationAuthority::Coordinator, + vec!["epiphany.heartbeat_pump_intent.v0"], + vec!["epiphany.swarm_control_receipt.v0"], + vec![ + "Heartbeat activation owns agent work; external callers submit intents and watch receipts.", + ], + ), + mutation_contract( + RUNTIME_JOB_RESULT_TYPE, + RUNTIME_SPINE_SCHEMA_VERSION, + vec![CultNetDocumentOperation::Snapshot], + CultNetMutationAuthority::ReadOnly, + vec![], + vec![], + vec![ + "Job results are evidence records; review and acceptance are separate typed flows.", + ], + ), + mutation_contract( + RUNTIME_EVENT_TYPE, + RUNTIME_SPINE_SCHEMA_VERSION, + vec![CultNetDocumentOperation::Snapshot], + CultNetMutationAuthority::ReadOnly, + vec![], + vec![], + vec!["Runtime events are append-only projections for inspection."], + ), + mutation_contract( + "epiphany.agent.memory", + "ghostlight.agent_state.v0", + vec![ + CultNetDocumentOperation::Snapshot, + CultNetDocumentOperation::IntentSubmit, + CultNetDocumentOperation::ReceiptWatch, + ], + CultNetMutationAuthority::Coordinator, + vec!["epiphany.agent_memory_intent.v0"], + vec!["epiphany.swarm_control_receipt.v0"], + vec![ + "Sub-agents request memory mutations; the coordinator accepts, rejects, or explains the refusal.", + ], + ), + mutation_contract( + "epiphany.heartbeat.state", + "epiphany.agent_heartbeat.v0", + vec![ + CultNetDocumentOperation::Snapshot, + CultNetDocumentOperation::IntentSubmit, + CultNetDocumentOperation::ReceiptWatch, + ], + CultNetMutationAuthority::Coordinator, + vec![ + "epiphany.heartbeat_pump_intent.v0", + "epiphany.circadian_rhythm_intent.v0", + ], + vec!["epiphany.swarm_control_receipt.v0"], + vec![ + "Aquarium controls heartbeat and circadian rhythm through typed intents, not blind state replacement.", + ], + ), + mutation_contract( + "epiphany.state_ledger", + "epiphany.state_ledger.v0", + vec![CultNetDocumentOperation::Snapshot], + CultNetMutationAuthority::ReadOnly, + vec![], + vec![], + vec![ + "The ledger is inspected as durable memory; writes are mediated by role-specific state flows.", + ], + ), + ] +} + fn validate_non_empty(value: &str, field: &str) -> Result<()> { if value.trim().is_empty() { return Err(anyhow!("{field} must be non-empty")); @@ -823,6 +966,7 @@ mod tests { runtime_id, runtime_kind, supported_document_types, + supported_mutation_contracts, .. } => { assert_eq!(runtime_id, "epiphany-test"); @@ -832,6 +976,20 @@ mod tests { .unwrap() .contains(&RUNTIME_JOB_RESULT_TYPE.to_string()) ); + let contracts = supported_mutation_contracts.unwrap(); + let heartbeat_contract = contracts + .iter() + .find(|contract| contract.document_type == "epiphany.heartbeat.state") + .expect("heartbeat state should advertise mutation contract"); + assert_eq!( + heartbeat_contract.authority, + CultNetMutationAuthority::Coordinator + ); + assert!( + heartbeat_contract + .operations + .contains(&CultNetDocumentOperation::IntentSubmit) + ); } other => panic!("expected hello, got {other:?}"), } diff --git a/notes/fresh-workspace-handoff.md b/notes/fresh-workspace-handoff.md index dadf749a..4b4c57f4 100644 --- a/notes/fresh-workspace-handoff.md +++ b/notes/fresh-workspace-handoff.md @@ -34,7 +34,8 @@ remembers doctrine; the branch remembers the blade. - Phase 6 has read-only `thread/epiphany/scene`, `thread/epiphany/jobs`, `thread/epiphany/roles`, `thread/epiphany/freshness`, `thread/epiphany/context`, `thread/epiphany/graphQuery`, `thread/epiphany/planning`, `thread/epiphany/pressure`, `thread/epiphany/reorient`, `thread/epiphany/crrc`, `thread/epiphany/coordinator`, `thread/epiphany/reorientResult`, and `thread/epiphany/roleResult`; durable `jobBindings` now act as a thin Epiphany-owned launcher seam with launcher id, authority scope, and heartbeat backend/job id. New `thread/epiphany/jobLaunch`, `thread/epiphany/jobInterrupt`, `thread/epiphany/roleLaunch`, and `thread/epiphany/reorientLaunch` writes open typed runtime-spine job receipts under `state/runtime-spine.msgpack` and do not require the Codex SQLite state runtime. Freshness carries watcher-backed invalidation inputs, graphQuery traverses authoritative typed graph neighborhoods and path/symbol matches without mutation, planning projects typed captures/backlog/roadmap/objective drafts without adopting work, roles project implementation/imagination/modeling/verification/reorientation ownership from existing signals without becoming a scheduler, `roleResult` and `reorientResult` read heartbeat-backed typed runtime-spine job results when present, `roleAccept` and `reorientAccept` accept completed heartbeat findings from typed runtime-spine results while remaining explicit review gates, `thread/epiphany/crrc` recommends the next explicit CRRC action without launching, accepting, compacting, scheduling, or mutating, and `thread/epiphany/coordinator` composes those signals into a fixed-lane MVP action recommendation without becoming a writer. - Native `epiphany-mvp-status` is the first dogfood operator view. It starts or reads a thread through app-server and prints scene, planning, pressure, reorient, jobs, roles, Imagination/modeling/verification role result read-backs, reorient result, heartbeat, Face bubbles, and CRRC recommendation as text or machine output. The old Python status module has been cut; native Rust/CultCache/CultNet surfaces are the smoked product path. - Native `epiphany-mvp-coordinator` is the first auditable fixed-lane coordinator runner. It starts or reads a thread through app-server, opens a native runtime-spine session, follows the harness-native coordinator action, can auto-launch modeling, verification, or reorient-worker jobs, records native runtime job/result receipts for terminal launched work, keeps semantic findings review-gated by default, and writes summary, steps, rendered snapshots, transcript, stderr, runtime-spine status, and final next-action artifacts under `.epiphany-dogfood/coordinator` or a caller-provided artifact directory. It refuses direct backend-completion mutation; full completion smoke needs live workers while execution is being cauterized into CultNet. -- Native `epiphany-runtime-spine` is the first Codex-independent runtime vertebra. It owns typed CultCache documents for runtime identity, sessions, jobs, job results, and events; opens/completes native jobs; snapshots jobs/results by runtime job id; projects job-result counts; and can emit a framed CultNet hello message advertising the native document contract. Codex app-server launch/read-back/acceptance is now a typed heartbeat/runtime-spine bridge with no Epiphany job-result dependency on the Codex SQLite runtime. +- Native `epiphany-runtime-spine` is the first Codex-independent runtime vertebra. It owns typed CultCache documents for runtime identity, sessions, jobs, job results, and events; opens/completes native jobs; snapshots jobs/results by runtime job id; projects job-result counts; and can emit a framed CultNet hello message advertising the native document and mutation contract surface. Codex app-server launch/read-back/acceptance is now a typed heartbeat/runtime-spine bridge with no Epiphany job-result dependency on the Codex SQLite runtime. +- CultNet APIs are advertised as compatible schemas plus mutation contracts. Hello frames now expose readable document types, allowed operations, mutation authority, typed intent document types, and typed receipt document types; Aquarium should use those contracts to submit state changes and watch receipts rather than growing a little bespoke verb zoo. - Native CRRC automation is now landed only at turn-complete safe boundaries. It may submit `Op::Compact` for coordinator-approved `compactRehydrateReorient` or for a successful pre-compaction checkpoint intervention's pending compact handoff, and it may launch the fixed `reorient-worker` for coordinator-approved `launchReorientWorker`. It does not auto-launch Imagination/modeling/verification, accept findings, promote evidence, edit implementation code, or keep going after reviewable semantic output. - Pre-compaction checkpoint intervention is now landed. On token-count events for loaded Epiphany threads, when current context usage reaches 80% of the active auto-compact/context limit, the harness steers the active turn once with a CRRC checkpoint directive so the agent banks working context before compaction/reorientation. Pressure ignores cumulative token spend; cumulative-only telemetry reports unknown instead of yelling. A successful steer now latches a turn-scoped compact handoff that is consumed at clean turn completion, preventing the brake from decaying into another implementation turn. This is still bounded steering plus compaction handoff, not automatic semantic acceptance, a broad scheduler, or implementation continuation. - The old Python dogfood/live-specialist runners were cut because they encoded the obsolete completion path. The replacement must be native Rust/CultCache/CultNet and complete heartbeat-owned runtime-spine job results with auditable artifacts. @@ -50,7 +51,7 @@ remembers doctrine; the branch remembers the blade. - The Face public-surface pass is landed as a bounded lane. Face's role dossier lives in `state/agents.msgpack`; `epiphany_specialists.toml` gives it a VoidBot-heartbeat-derived prompt stripped of moderation authority; `state/face-discord.toml` and the native `epiphany-face-discord` binary enforce that Face may interact only through #aquarium. Missing channel id or token writes candidate chat artifacts instead of posting elsewhere. - The first Epiphany-native character-loop pass is landed. `epiphany-character-loop turn --role face --stimulus ` loads Face's typed dossier from `state/agents.msgpack` and emits an auditable `epiphany.character_turn_packet.v0` packet with projected local context, visible stimulus, allowed outputs, and guardrails. The same packet builder works for other role ids, because every organ already has a Ghostlight-shaped dossier. - The heartbeat/Face operator API pass is landed. Native `epiphany-heartbeat-store status` returns machine-readable initiative state plus CultCache store presence, thought appraisals, and derived reactions; native `epiphany-face-discord bubble` writes Discord-independent `epiphany.face_bubble.v0` artifacts, and native `epiphany-mvp-status` includes `heartbeat` and `face` blocks. Aquarium should call native/backend surfaces rather than resurrecting deleted Python action shims. -- The native runtime-spine pass is landed as a first vertebra, not a full daemon. `state/runtime-spine.msgpack` is the default store, `.epiphany-dogfood/runtime-spine-job/runtime.msgpack` and `hello.cultnet` are the latest job/result smoke artifacts, and specialist launch/result flows now route through typed heartbeat/runtime-spine documents. +- The native runtime-spine pass is landed as a first vertebra, not a full daemon. `state/runtime-spine.msgpack` is the default store, `.epiphany-dogfood/runtime-spine-job/runtime.msgpack` and `hello.cultnet` are the latest job/result smoke artifacts, and specialist launch/result flows now route through typed heartbeat/runtime-spine documents. Its CultNet hello advertises mutation contracts for runtime/session/job/memory/heartbeat/ledger documents, with coordinator-owned intent/receipt paths for writes. - The Aetheria dogfood run has a contamination scar. The supervising Codex session directly edited and committed target-repo work on `E:\Projects\Aetheria-Economy` instead of only driving Epiphany lanes. Treat those Aetheria commits as supervisor-seeded implementation, not clean evidence that Epiphany coordinated the work. Future dogfood must run through the GUI/coordinator/fixed role lanes with auditable artifacts unless the user explicitly authorizes an operator intervention. Remember the sunburn: do not stare into the worker's objective until you become it. - The dogfood quarantine now has a direct-thought boundary. The supervisor may read coordinator actions, role/reorient statuses, structured finding summaries, reviewed state patches, rendered status snapshots, and artifact manifests. It must not read raw worker transcripts, direct worker messages, full turn logs, or `rawResult` payloads during normal dogfood. Those artifacts are sealed black reliquaries for explicit forensic debugging only. - Native `epiphany-agent-telemetry` is the safe instrument panel for sealed runs. Status/coordinator/GUI/dogfood/live-specialist tools generate telemetry JSON from sealed transcripts that preserves method names, call shape, job/status/path counts, and any visible function/tool names while sealing text, direct messages, and raw results. diff --git a/state/ledgers.msgpack b/state/ledgers.msgpack index 99a72fda..acc17f85 100644 Binary files a/state/ledgers.msgpack and b/state/ledgers.msgpack differ diff --git a/state/map.yaml b/state/map.yaml index 63821186..63087732 100644 --- a/state/map.yaml +++ b/state/map.yaml @@ -112,7 +112,8 @@ accepted_design: - Coordinator implementation clearance is stricter than review acceptance: an accepted verification/review finding only opens the implementation gate when its verdict is `pass`; accepted `needs-evidence`, `needs-review`, or `fail` findings send the loop back toward modeling/checkpoint strengthening or reorientation instead of blessing a sick machine. - Coordinator regather handling is now fixed-lane first, not supervisor improvisation: `regatherManually` is a fallback only after reorient/modeling/verification review and launch lanes cannot advance the state, completed modeling findings must be reviewed before verification, and the CLI coordinator's explicit test-only `--auto-review` mode may accept modeling state patches before launching verification while production semantics remain review-gated. - Native `epiphany-mvp-coordinator` is the first auditable coordinator runner: it drives the fixed single-user lanes through app-server APIs, opens a native runtime-spine session, records native runtime job/result receipts for launched specialists and reorient workers when terminal results arrive, keeps semantic findings review-gated by default, and writes coordinator summaries, JSONL steps, rendered snapshots, transcript, stderr, runtime-spine status, and final next-action artifacts. It refuses direct backend-completion mutation; full specialist completion smoke must use live workers while the execution path is cauterized into CultNet. - - Native `epiphany-runtime-spine` is the first Codex-independent runtime vertebra: it initializes and inspects typed CultCache `epiphany.runtime.identity`, `epiphany.runtime.session`, `epiphany.runtime.job`, `epiphany.runtime.job_result`, and `epiphany.runtime.event` documents, opens/completes native jobs with result records, snapshots jobs by id, and can emit a framed CultNet hello message advertising the native document contract. App-server launch now opens typed runtime jobs; `roleResult` / `reorientResult` read heartbeat-backed typed job results only; and `roleAccept` / `reorientAccept` accept completed heartbeat findings from the typed runtime-spine path without opening the Codex SQLite state runtime. + - Native `epiphany-runtime-spine` is the first Codex-independent runtime vertebra: it initializes and inspects typed CultCache `epiphany.runtime.identity`, `epiphany.runtime.session`, `epiphany.runtime.job`, `epiphany.runtime.job_result`, and `epiphany.runtime.event` documents, opens/completes native jobs with result records, snapshots jobs by id, and can emit a framed CultNet hello message advertising the native document and mutation contract surface. App-server launch now opens typed runtime jobs; `roleResult` / `reorientResult` read heartbeat-backed typed job results only; and `roleAccept` / `reorientAccept` accept completed heartbeat findings from the typed runtime-spine path without opening the Codex SQLite state runtime. + - CultNet public API shape is schema-first and mutation-contract-first: hello frames advertise readable document types, allowed operations, mutation authority, typed intent document types, and typed receipt document types. Aquarium and swarm clients should submit intents and watch receipts instead of inventing JSON-RPC verbs or blindly replacing authoritative state. - Limited native CRRC automation is now wired only at turn-complete safe boundaries: it can submit a compact operation when the read-only coordinator recommends `compactRehydrateReorient` or when a successful pre-compaction checkpoint intervention left a pending compact handoff for the completed turn, and it can launch the fixed `reorient-worker` when the coordinator recommends `launchReorientWorker`. It does not auto-launch modeling or verification, auto-accept findings, promote evidence, edit implementation code, or continue after reviewable semantic results; the rite may carry the stretcher, not declare the patient healed. - Pre-compaction checkpoint intervention is now wired at token-count boundaries: when a loaded Epiphany thread crosses the existing `shouldPrepareCompaction` pressure threshold during an active turn, the harness steers the active agent once for that turn with a bounded CRRC directive to bank scratch/checkpoint/map/evidence before compaction/reorientation. A successful steer marks a turn-scoped pending compact handoff consumed at clean turn completion; this is banking the fire before the dark, not semantic acceptance, broad scheduling, or automatic implementation continuation. - Native `epiphany-mvp-status` is the first dogfood operator view: it starts or reads a thread through app-server and renders scene, planning, pressure, reorient, jobs, role result read-backs, reorient result, CRRC recommendation, heartbeat, Face bubbles, and MVP role lanes as text or machine output without becoming a state store. The old Python status module has been cut; native Rust/CultCache/CultNet surfaces are the product path. diff --git a/vendor/cultcache-rs b/vendor/cultcache-rs index 8f868148..5f831c00 160000 --- a/vendor/cultcache-rs +++ b/vendor/cultcache-rs @@ -1 +1 @@ -Subproject commit 8f8681487ce51f875923d165a5b405e6d0b311ab +Subproject commit 5f831c00789fa24f5378413be1bd901cfb7ea23e diff --git a/vendor/cultnet-rs b/vendor/cultnet-rs index 38e796fb..42c7f9a7 160000 --- a/vendor/cultnet-rs +++ b/vendor/cultnet-rs @@ -1 +1 @@ -Subproject commit 38e796fb04ac3374f57d0b2d30017f1f56f04fce +Subproject commit 42c7f9a736291db0571e2b1f4c47cdb4fb7b22b3