Move launch planning into core
This commit is contained in:
parent
8654a02faf
commit
e27d0dfcfe
7 changed files with 205 additions and 105 deletions
|
|
@ -26,7 +26,6 @@ use epiphany_core::EpiphanyStateUpdatedField;
|
|||
use epiphany_core::EpiphanyTokenUsageSnapshot;
|
||||
use epiphany_core::MIND_GATEWAY_REVIEW_TYPE;
|
||||
use epiphany_core::RuntimeSpineHeartbeatJobOptions;
|
||||
use epiphany_core::RuntimeSpineHeartbeatLaunchPlanOptions;
|
||||
use epiphany_core::SOUL_VERDICT_RECEIPT_TYPE;
|
||||
use epiphany_core::SUBSTRATE_GATE_REPO_ACCESS_GRANT_RECEIPT_TYPE;
|
||||
use epiphany_core::apply_coordinator_state_update_to_state;
|
||||
|
|
@ -35,22 +34,17 @@ use epiphany_core::build_epiphany_role_launch_request_with_dynamic_context;
|
|||
use epiphany_core::clear_epiphany_job_binding_backend;
|
||||
use epiphany_core::continuity_recovery_receipt_from_reorient_finding;
|
||||
use epiphany_core::epiphany_role_label;
|
||||
use epiphany_core::epiphany_state_update_validation_errors;
|
||||
use epiphany_core::evaluate_promotion;
|
||||
use epiphany_core::eyes_evidence_packet_from_research_finding;
|
||||
use epiphany_core::mind_state_commit_receipt;
|
||||
use epiphany_core::open_runtime_spine_heartbeat_job;
|
||||
use epiphany_core::plan_runtime_spine_heartbeat_launch;
|
||||
use epiphany_core::put_substrate_gate_repo_access_grant_receipt;
|
||||
use epiphany_core::replace_or_append_epiphany_job_binding;
|
||||
use epiphany_core::runtime_job_snapshot;
|
||||
use epiphany_core::runtime_substrate_gate_repo_access_grant_receipt;
|
||||
use epiphany_core::soul_verdict_receipt_from_verification_finding;
|
||||
use epiphany_core::substrate_gate_repo_access_grant_for_launch;
|
||||
use epiphany_state_model::EpiphanyEvidenceRecord;
|
||||
use epiphany_state_model::EpiphanyJobKind as CoreEpiphanyJobKind;
|
||||
use epiphany_state_model::EpiphanyRetrievalState;
|
||||
use epiphany_state_model::EpiphanyRuntimeLink;
|
||||
use epiphany_state_model::EpiphanyThreadState;
|
||||
|
||||
use crate::cultnet::EpiphanySurfaceSource;
|
||||
|
|
@ -210,125 +204,38 @@ pub async fn launch_epiphany_job_on_thread(
|
|||
// runtime-spine opens the typed job. The bridge stitches those typed
|
||||
// documents into Codex's JSON-RPC thread shell until CultNet owns the route.
|
||||
let current_state = thread.epiphany_state().await.unwrap_or_default();
|
||||
validate_expected_revision(request.expected_revision, current_state.revision)?;
|
||||
let runtime_store = thread.epiphany_runtime_spine_store_path().await;
|
||||
let completed_prior_link = terminal_runtime_link_for_binding(
|
||||
let launch_plan = epiphany_core::plan_coordinator_job_launch(
|
||||
¤t_state,
|
||||
request.binding_id.as_str(),
|
||||
&request,
|
||||
runtime_store.as_path(),
|
||||
)?;
|
||||
let mut planning_state = current_state.clone();
|
||||
if let Some(link) = completed_prior_link.clone() {
|
||||
planning_state.runtime_links.insert(0, link);
|
||||
}
|
||||
|
||||
let launcher_job_id = format!("epiphany-heartbeat-launch-{}", Uuid::new_v4());
|
||||
let backend_job_id = Uuid::new_v4().to_string();
|
||||
let launch_plan = plan_runtime_spine_heartbeat_launch(
|
||||
&planning_state,
|
||||
RuntimeSpineHeartbeatLaunchPlanOptions {
|
||||
binding_id: request.binding_id.clone(),
|
||||
kind: request.kind,
|
||||
scope: request.scope.clone(),
|
||||
owner_role: request.owner_role.clone(),
|
||||
authority_scope: request.authority_scope.clone(),
|
||||
linked_subgoal_ids: request.linked_subgoal_ids.clone(),
|
||||
linked_graph_node_ids: request.linked_graph_node_ids.clone(),
|
||||
instruction: request.instruction.clone(),
|
||||
launch_document: request.launch_document.clone(),
|
||||
output_contract_id: request.output_contract_id.clone(),
|
||||
organ_launch_contract: request.organ_launch_contract.clone(),
|
||||
max_runtime_seconds: request.max_runtime_seconds,
|
||||
runtime_job_id: backend_job_id.clone(),
|
||||
},
|
||||
format!("epiphany-heartbeat-launch-{}", Uuid::new_v4()),
|
||||
Uuid::new_v4().to_string(),
|
||||
)
|
||||
.map_err(|err| EpiphanyBridgeError::InvalidRequest(err.to_string()))?;
|
||||
let next_job_bindings = replace_or_append_epiphany_job_binding(
|
||||
current_state.job_bindings.clone(),
|
||||
launch_plan.binding,
|
||||
);
|
||||
|
||||
let mut runtime_links = Vec::new();
|
||||
runtime_links.push(launch_plan.runtime_link.clone());
|
||||
if let Some(link) = completed_prior_link {
|
||||
runtime_links.push(link);
|
||||
}
|
||||
|
||||
let validation_errors = epiphany_state_update_validation_errors(
|
||||
¤t_state,
|
||||
&EpiphanyStateUpdate {
|
||||
job_bindings: Some(next_job_bindings.clone()),
|
||||
runtime_links: runtime_links.clone(),
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
if !validation_errors.is_empty() {
|
||||
return Err(EpiphanyBridgeError::InvalidRequest(format!(
|
||||
"invalid Epiphany job launch patch: {}",
|
||||
validation_errors.join("; ")
|
||||
)));
|
||||
}
|
||||
open_epiphany_runtime_spine_job(
|
||||
runtime_store.as_path(),
|
||||
&planning_state,
|
||||
&launch_plan.planning_state,
|
||||
&request,
|
||||
backend_job_id.as_str(),
|
||||
launch_plan.backend_job_id.as_str(),
|
||||
)?;
|
||||
maybe_put_substrate_gate_launch_grant(
|
||||
runtime_store.as_path(),
|
||||
&request,
|
||||
backend_job_id.as_str(),
|
||||
launch_plan.backend_job_id.as_str(),
|
||||
)?;
|
||||
|
||||
let epiphany_state = apply_epiphany_state_update_to_thread(
|
||||
thread,
|
||||
EpiphanyStateUpdate {
|
||||
expected_revision: request.expected_revision,
|
||||
job_bindings: Some(next_job_bindings),
|
||||
runtime_links,
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
let epiphany_state =
|
||||
apply_epiphany_state_update_to_thread(thread, launch_plan.state_update).await?;
|
||||
|
||||
Ok(EpiphanyJobLaunchResult {
|
||||
epiphany_state,
|
||||
binding_id: request.binding_id,
|
||||
launcher_job_id,
|
||||
backend_job_id,
|
||||
launcher_job_id: launch_plan.launcher_job_id,
|
||||
backend_job_id: launch_plan.backend_job_id,
|
||||
})
|
||||
}
|
||||
|
||||
fn terminal_runtime_link_for_binding(
|
||||
state: &EpiphanyThreadState,
|
||||
binding_id: &str,
|
||||
runtime_store: &Path,
|
||||
) -> BridgeResult<Option<EpiphanyRuntimeLink>> {
|
||||
let Some(link) = state
|
||||
.runtime_links
|
||||
.iter()
|
||||
.find(|link| link.binding_id == binding_id && !link.runtime_job_id.trim().is_empty())
|
||||
else {
|
||||
return Ok(None);
|
||||
};
|
||||
if link.runtime_result_id.is_some() {
|
||||
return Ok(None);
|
||||
}
|
||||
let Some(snapshot) = runtime_job_snapshot(runtime_store, link.runtime_job_id.as_str())
|
||||
.map_err(|err| EpiphanyBridgeError::Fatal(err.to_string()))?
|
||||
else {
|
||||
return Ok(None);
|
||||
};
|
||||
let Some(result) = snapshot.result else {
|
||||
return Ok(None);
|
||||
};
|
||||
let mut terminal = link.clone();
|
||||
terminal.id = format!("{}-{}", link.id, result.result_id);
|
||||
terminal.surface = "runtimeResult".to_string();
|
||||
terminal.runtime_result_id = Some(result.result_id);
|
||||
Ok(Some(terminal))
|
||||
}
|
||||
|
||||
pub async fn interrupt_epiphany_job_on_thread(
|
||||
thread: &impl EpiphanyMutationHost,
|
||||
request: EpiphanyJobInterruptRequest,
|
||||
|
|
@ -1002,6 +909,30 @@ fn map_coordinator_admission_error(
|
|||
|
||||
#[cfg(test)]
|
||||
mod acceptance_architecture_tests {
|
||||
#[test]
|
||||
fn launch_route_delegates_planning_to_the_native_organ() {
|
||||
let source = include_str!("mutation_service.rs");
|
||||
let launch_start = source
|
||||
.find("pub async fn launch_epiphany_job_on_thread")
|
||||
.unwrap();
|
||||
let interrupt_start = source
|
||||
.find("pub async fn interrupt_epiphany_job_on_thread")
|
||||
.unwrap();
|
||||
let launch = &source[launch_start..interrupt_start];
|
||||
assert_eq!(launch.matches("plan_coordinator_job_launch").count(), 1);
|
||||
for forbidden in [
|
||||
["plan", "runtime", "spine", "heartbeat", "launch"].join("_"),
|
||||
["replace", "or", "append", "epiphany", "job", "binding"].join("_"),
|
||||
["runtime", "job", "snapshot"].join("_"),
|
||||
["epiphany", "state", "update", "validation", "errors"].join("_"),
|
||||
] {
|
||||
assert!(
|
||||
!launch.contains(&forbidden),
|
||||
"launch route regrew native planning policy {forbidden:?}"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn acceptance_routes_have_no_individual_or_post_state_receipt_writes() {
|
||||
let source = include_str!("mutation_service.rs");
|
||||
|
|
|
|||
140
epiphany-core/src/coordinator_launch.rs
Normal file
140
epiphany-core/src/coordinator_launch.rs
Normal file
|
|
@ -0,0 +1,140 @@
|
|||
use crate::*;
|
||||
use anyhow::{Result, anyhow};
|
||||
use epiphany_state_model::{EpiphanyRuntimeLink, EpiphanyThreadState};
|
||||
use std::path::Path;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct EpiphanyCoordinatorJobLaunchPlan {
|
||||
pub launcher_job_id: String,
|
||||
pub backend_job_id: String,
|
||||
pub planning_state: EpiphanyThreadState,
|
||||
pub heartbeat_plan: RuntimeSpineHeartbeatLaunchPlan,
|
||||
pub state_update: EpiphanyStateUpdate,
|
||||
}
|
||||
|
||||
pub fn plan_coordinator_job_launch(
|
||||
state: &EpiphanyThreadState,
|
||||
request: &EpiphanyJobLaunchRequest,
|
||||
runtime_store: &Path,
|
||||
launcher_job_id: String,
|
||||
backend_job_id: String,
|
||||
) -> Result<EpiphanyCoordinatorJobLaunchPlan> {
|
||||
if let Some(expected) = request.expected_revision
|
||||
&& expected != state.revision
|
||||
{
|
||||
return Err(anyhow!(
|
||||
"epiphany state revision mismatch: expected {expected}, found {}",
|
||||
state.revision
|
||||
));
|
||||
}
|
||||
let completed_prior_link =
|
||||
terminal_runtime_link_for_binding(state, &request.binding_id, runtime_store)?;
|
||||
let mut planning_state = state.clone();
|
||||
if let Some(link) = completed_prior_link.clone() {
|
||||
planning_state.runtime_links.insert(0, link);
|
||||
}
|
||||
let heartbeat_plan = plan_runtime_spine_heartbeat_launch(
|
||||
&planning_state,
|
||||
RuntimeSpineHeartbeatLaunchPlanOptions {
|
||||
binding_id: request.binding_id.clone(),
|
||||
kind: request.kind,
|
||||
scope: request.scope.clone(),
|
||||
owner_role: request.owner_role.clone(),
|
||||
authority_scope: request.authority_scope.clone(),
|
||||
linked_subgoal_ids: request.linked_subgoal_ids.clone(),
|
||||
linked_graph_node_ids: request.linked_graph_node_ids.clone(),
|
||||
instruction: request.instruction.clone(),
|
||||
launch_document: request.launch_document.clone(),
|
||||
output_contract_id: request.output_contract_id.clone(),
|
||||
organ_launch_contract: request.organ_launch_contract.clone(),
|
||||
max_runtime_seconds: request.max_runtime_seconds,
|
||||
runtime_job_id: backend_job_id.clone(),
|
||||
},
|
||||
)?;
|
||||
let job_bindings = replace_or_append_epiphany_job_binding(
|
||||
state.job_bindings.clone(),
|
||||
heartbeat_plan.binding.clone(),
|
||||
);
|
||||
let mut runtime_links = vec![heartbeat_plan.runtime_link.clone()];
|
||||
if let Some(link) = completed_prior_link {
|
||||
runtime_links.push(link);
|
||||
}
|
||||
let state_update = EpiphanyStateUpdate {
|
||||
expected_revision: request.expected_revision,
|
||||
job_bindings: Some(job_bindings),
|
||||
runtime_links,
|
||||
..Default::default()
|
||||
};
|
||||
let errors = epiphany_state_update_validation_errors(state, &state_update);
|
||||
if !errors.is_empty() {
|
||||
return Err(anyhow!(
|
||||
"invalid Epiphany job launch patch: {}",
|
||||
errors.join("; ")
|
||||
));
|
||||
}
|
||||
Ok(EpiphanyCoordinatorJobLaunchPlan {
|
||||
launcher_job_id,
|
||||
backend_job_id,
|
||||
planning_state,
|
||||
heartbeat_plan,
|
||||
state_update,
|
||||
})
|
||||
}
|
||||
|
||||
fn terminal_runtime_link_for_binding(
|
||||
state: &EpiphanyThreadState,
|
||||
binding_id: &str,
|
||||
runtime_store: &Path,
|
||||
) -> Result<Option<EpiphanyRuntimeLink>> {
|
||||
let Some(link) = state
|
||||
.runtime_links
|
||||
.iter()
|
||||
.find(|link| link.binding_id == binding_id && !link.runtime_job_id.trim().is_empty())
|
||||
else {
|
||||
return Ok(None);
|
||||
};
|
||||
if link.runtime_result_id.is_some() {
|
||||
return Ok(None);
|
||||
}
|
||||
let Some(snapshot) = runtime_job_snapshot(runtime_store, &link.runtime_job_id)? else {
|
||||
return Ok(None);
|
||||
};
|
||||
let Some(result) = snapshot.result else {
|
||||
return Ok(None);
|
||||
};
|
||||
let mut terminal = link.clone();
|
||||
terminal.id = format!("{}-{}", link.id, result.result_id);
|
||||
terminal.surface = "runtimeResult".to_string();
|
||||
terminal.runtime_result_id = Some(result.result_id);
|
||||
Ok(Some(terminal))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn stale_revision_refuses_launch_before_runtime_writes() {
|
||||
let state = EpiphanyThreadState {
|
||||
revision: 2,
|
||||
..Default::default()
|
||||
};
|
||||
let request = build_epiphany_role_launch_request(
|
||||
"thread-1",
|
||||
EpiphanyRoleResultRoleId::Modeling,
|
||||
Some(1),
|
||||
Some(60),
|
||||
&state,
|
||||
)
|
||||
.expect("role launch request");
|
||||
let error = plan_coordinator_job_launch(
|
||||
&state,
|
||||
&request,
|
||||
Path::new("missing.cc"),
|
||||
"launcher".to_string(),
|
||||
"backend".to_string(),
|
||||
)
|
||||
.expect_err("stale launch must fail");
|
||||
assert!(error.to_string().contains("revision mismatch"));
|
||||
}
|
||||
}
|
||||
|
|
@ -3,6 +3,7 @@ mod agent_memory;
|
|||
mod agent_utterance_state;
|
||||
mod continuity_gateway;
|
||||
mod coordinator_acceptance;
|
||||
mod coordinator_launch;
|
||||
mod coordinator_results;
|
||||
mod coordinator_service;
|
||||
mod coordinator_state;
|
||||
|
|
@ -139,6 +140,8 @@ pub use coordinator_acceptance::read_accepted_coordinator_state;
|
|||
pub use coordinator_acceptance::reorient_acceptance_claimed_effects;
|
||||
pub use coordinator_acceptance::role_acceptance_claimed_effects;
|
||||
pub use coordinator_acceptance::role_acceptance_enforceable_receipts;
|
||||
pub use coordinator_launch::EpiphanyCoordinatorJobLaunchPlan;
|
||||
pub use coordinator_launch::plan_coordinator_job_launch;
|
||||
pub use coordinator_results::EpiphanyCoordinatorReorientResultSnapshot;
|
||||
pub use coordinator_results::EpiphanyCoordinatorRoleResultSnapshot;
|
||||
pub use coordinator_results::read_reorient_result_snapshot;
|
||||
|
|
|
|||
|
|
@ -1820,6 +1820,16 @@ proof `.epiphany-smoke\repo-work-readiness-20260621-113714` and wrapper proof
|
|||
The machine is no longer cleared to move outward. Free it from the Codex organ
|
||||
first.
|
||||
|
||||
Native worker-launch planning has been extracted into
|
||||
`epiphany-core/src/coordinator_launch.rs`. It owns revision refusal, prior
|
||||
terminal-link discovery, heartbeat launch planning, binding replacement, and
|
||||
the derived state update. The bridge delegates those decisions and a source
|
||||
guard prevents the policy from growing back. The remaining launch wound is
|
||||
transactional: runtime documents, the optional Substrate Gate grant, and
|
||||
thread-state linkage are still separate compatibility writes. The next cut is
|
||||
one prepared atomic launch commit with injected-failure proof; do not remove
|
||||
the coordinator JSON-RPC dependency before that invariant is real.
|
||||
|
||||
## Immediate Re-entry Instruction
|
||||
|
||||
After compaction, first rehydrate and reorient from the listed files and git
|
||||
|
|
|
|||
|
|
@ -170,6 +170,22 @@ interruption. Best-effort rollback across two files is not atomicity. A negative
|
|||
smoke must inject failure after prerequisite receipts and after state write and
|
||||
prove no unwitnessed accepted state becomes authoritative.
|
||||
|
||||
## Launch Transaction Wound
|
||||
|
||||
Launch planning now belongs to `coordinator_launch`: it validates the expected
|
||||
thread revision, inspects the prior runtime binding, prepares the heartbeat
|
||||
launch documents, and derives the new thread-state linkage without writing.
|
||||
The Codex bridge is a compatibility actuator for that plan; it no longer owns
|
||||
those policy decisions.
|
||||
|
||||
Persistence is not yet a coherent launch transaction. Runtime identity,
|
||||
session, job, event, worker request, optional Substrate Gate grant, and thread
|
||||
state linkage are still published through separate writes. None may become the
|
||||
authoritative sign of a completed launch until one native commit primitive
|
||||
publishes the prepared set atomically, or a typed prepared transaction gives
|
||||
interruption a deterministic finish-or-refuse law. Rollback and reconciliation
|
||||
are not owners.
|
||||
|
||||
## Invariants
|
||||
|
||||
- There is one durable thread-state owner and one runtime lifecycle owner.
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -659,4 +659,4 @@ current_status:
|
|||
- Service lifecycle receipt history is now event-only: `load_epiphany_cultmesh_daemon_service_lifecycle_receipts` filters out the `epiphany-local/daemon-service-lifecycle-receipt/latest` mirror, while `load_latest_epiphany_cultmesh_daemon_service_lifecycle_receipt` remains the latest-state reader. Focused unit test `service_lifecycle_receipt_history_excludes_latest_mirror` proves two per-receipt writes return exactly two history records and latest points at the second. Fresh smoke `verse-query-lifecycle-history-filter-smoke` and live overview/triage `local-20260618-145632-497-efac6f0d` / `local-20260618-145632-321-867947af` prove compact swarm sights still expose service lifecycle attention plus the present elevated runbook artifact without daemon pokes or service mutation. Do not use a broad unscoped cargo test filter for this check on Windows; it fans out across bin harnesses and hit a broken pipe during this pass.
|
||||
- `epiphany-verse-query seed-compact` is the compact seed receipt for wrappers and read-only rites. The older `seed` command still prints the full local Verse context for explicit debugging, but compact wrapper modes now avoid full `seed`/final `query` readback after the combined overview proof exposed Windows debug stack overflow from full-context serialization pressure.
|
||||
last_completed_repo_swarm_cut: Huginn live-fire has crossed the first real repo Body from Persona pressure to branch-local Hands commit and Soul/Mind closure, and now has repeatable closure and redacted-public-proof mouths. On `E:\Projects\Huginn` branch `epiphany/huginn/first-awakening`, `tools/epiphany_local_run.ps1 -Mode repo-persona-intake` accepted item `huginn-inspection-readback-livefire`, Weksa/Persona intake remained sealed, and `epiphany-work derive-plan` lowered it to model-authored safe family `repo.status_section` for `README.md` only. `tools/epiphany_local_run.ps1 -Mode repo-swarm-run` advanced four bounded pulses: run-from-plan, adopt-from-plan, execute-from-plan, and close-from-execute. Huginn commit `e9b07c38fceabbdc89ea35c2156326c80c0393a2` updated only `README.md`; closure receipt `.epiphany\work\work-close-huginn-inspection-readback-livefire.json` records status `closed`, Soul verdict `passed`, Mind state commit `repo-work-close-huginn-inspection-readback-livefire-mind-commit`, safe family `repo.status_section`, path scope `README.md`, Bifrost publication gate, and `privateStateExposed=false`. Verification also ran Huginn's TypeScript compiler through concrete Node path: `C:\Program Files\nodejs\node.exe E:\Projects\Huginn\node_modules\typescript\bin\tsc -p tsconfig.json` passed. Native verifier `epiphany-repo-livefire-closure-smoke --workspace E:\Projects\Huginn --item huginn-inspection-readback-livefire --expected-path README.md --expected-family repo.status_section --expected-gate awaiting-publication --expected-blocker bifrost-publication-missing` checks closure receipt, Mind map entry, overview gate/blocker, authority seals, and `git show` changed paths, returning `status=ok`, `soulVerdict=passed`, `publicationGate=Bifrost`, and `privateStateExposed=false`. Wrapper `tools/epiphany_local_run.ps1 -Mode repo-livefire-closure -Workspace E:\Projects\Huginn -RepoWorkItem huginn-inspection-readback-livefire -SkipBuild` exposes the same read-only proof through the local operator mouth; artifact `local-20260621-190156-803-2f85ba15` printed commit, path, gate, blocker, Soul verdict, Mind commit receipt, and `privateStateExposed=False` without push, merge, publication, or target mutation. New wrapper `tools/epiphany_local_run.ps1 -Mode repo-work-public-proof -Workspace E:\Projects\Huginn -RepoWorkItem huginn-inspection-readback-livefire -SkipBuild` exports the redacted proof bundle and repo-local Verse index without publishing; artifact `local-20260621-190645-345-4555b1dc` wrote `.epiphany\public\proof-bundles\repo-work-public-proof-huginn-inspection-readback-livefire.json`, proof id `repo-work-public-proof-huginn-inspection-readback-livefire`, SHA-256 `857545b199f3e37eb06c2e757e5cfc201958e46667bb1f8403fd333a22620f63`, nine artifact rows, zero publication rows, and `privateStateExposed=False`. Gjallar readback `local-20260621-190701-140-b9519ec6` sees the `REPO-WORK-PUBLIC-PROOF` row and action row from Huginn's repo-local Verse with the same SHA-256 while Bifrost publication remains missing. Launcher compact modes are now named compact operator-context modes instead of read-only modes, and `bifrost-public-proof` summaries print item, target public Verse, room, proof SHA-256, review receipts, and credit receipts so publication receipts are auditable without claiming upstream sync.
|
||||
next_action: Extract worker launch into its own native organ, following `notes/native-coordinator-liberation-map.md`. Role/reorient acceptance now publishes state and every witness atomically through the unified store; obsolete cross-store writers are deleted and guarded. Move typed dynamic context, launch document construction, runtime-spine opening/interruption, and state linkage into the launch organ, then remove `thread/epiphany/*` JSON-RPC from `epiphany-mvp-coordinator`.
|
||||
next_action: Complete the native launch transaction described in `notes/native-coordinator-liberation-map.md`. Pure launch planning now belongs to `coordinator_launch`; the compatibility bridge only actuates its plan, but runtime documents, optional Substrate Gate grant, and thread-state linkage remain separate writes. Create one atomic prepared launch commit and negative failure proof before removing `thread/epiphany/*` JSON-RPC from `epiphany-mvp-coordinator`.
|
||||
|
|
|
|||
Reference in a new issue