Move coordinator sight to native state

This commit is contained in:
Meta Vens 2026-07-11 20:48:19 +01:00
commit cd7ed532dd
5 changed files with 59 additions and 168 deletions

View file

@ -54,7 +54,6 @@ const DEFAULT_APP_SERVER: &str = r"C:\Users\Meta\.cargo-target-codex\debug\codex
const DEFAULT_MODEL_RUNTIME_BIN: &str = "epiphany-model-runtime";
const DEFAULT_TOOL_ADAPTER_BIN: &str = "epiphany-tool-codex-mcp-spine";
const WORKER_AUTO_TOOL_MAX_ROUNDS: usize = 24;
const REORIENT_BINDING_ID: &str = "reorient-worker";
const GRAPH_NODE_ID: &str = "reorient-target";
const WATCHED_RELATIVE_PATH: &str = "src/reorient_target.rs";
@ -352,7 +351,7 @@ fn run_coordinator(args: &Args) -> Result<Value> {
}
for index in 0..args.max_steps {
let status = collect_coordinator_status(&mut client, &thread_id)?;
let status = collect_coordinator_status(&runtime_store, &thread_id)?;
let mut coordinator = status
.get("coordinator")
.cloned()
@ -409,11 +408,7 @@ fn run_coordinator(args: &Args) -> Result<Value> {
"reviewResearchResult" | "reviewModelingResult" | "reviewVerificationResult" => {
let role_id = role_id_for_coordinator_action(&action)
.ok_or_else(|| anyhow!("unsupported review action {action}"))?;
let result = client.send(
"thread/epiphany/roleResult",
Some(json!({"threadId": thread_id, "roleId": role_id})),
true,
)?;
let result = read_role_result(&runtime_store, &thread_id, role_id)?;
push_event(
&mut step,
json!({"type": "roleResult", "roleId": role_id, "result": status_cli::sanitize_for_operator(result.clone())}),
@ -438,7 +433,7 @@ fn run_coordinator(args: &Args) -> Result<Value> {
&mut step,
json!({"type": "roleFailureReview", "roleId": role_id, "superseded": status_cli::sanitize_for_operator(superseded)}),
);
final_status = collect_coordinator_status(&mut client, &thread_id)?;
final_status = collect_coordinator_status(&runtime_store, &thread_id)?;
append_operator_step_jsonl(&steps_path, &step)?;
steps.push(step);
continue;
@ -473,10 +468,10 @@ fn run_coordinator(args: &Args) -> Result<Value> {
json!({"type": "roleAccept", "roleId": role_id, "accepted": status_cli::sanitize_for_operator(accepted)}),
);
}
final_status = collect_coordinator_status(&mut client, &thread_id)?;
final_status = collect_coordinator_status(&runtime_store, &thread_id)?;
}
"reviewReorientResult" => {
let result = read_reorient_result(&mut client, &thread_id)?;
let result = read_reorient_result(&runtime_store, &thread_id)?;
push_event(
&mut step,
json!({"type": "reorientResult", "result": status_cli::sanitize_for_operator(result.clone())}),
@ -547,12 +542,12 @@ fn run_coordinator(args: &Args) -> Result<Value> {
&mut step,
json!({"type": "workerRuntime", "roleId": role_id, "run": worker_run}),
);
let result = read_role_result(&mut client, &thread_id, role_id)?;
let result = read_role_result(&runtime_store, &thread_id, role_id)?;
push_event(
&mut step,
json!({"type": "roleResult", "roleId": role_id, "result": status_cli::sanitize_for_operator(result.clone())}),
);
final_status = collect_coordinator_status(&mut client, &thread_id)?;
final_status = collect_coordinator_status(&runtime_store, &thread_id)?;
if !matches!(
result["status"].as_str(),
Some("completed" | "failed" | "cancelled")
@ -601,12 +596,12 @@ fn run_coordinator(args: &Args) -> Result<Value> {
&mut step,
json!({"type": "workerRuntime", "roleId": "reorient-worker", "run": worker_run}),
);
let result = read_reorient_result(&mut client, &thread_id)?;
let result = read_reorient_result(&runtime_store, &thread_id)?;
push_event(
&mut step,
json!({"type": "reorientResult", "result": status_cli::sanitize_for_operator(result.clone())}),
);
final_status = collect_coordinator_status(&mut client, &thread_id)?;
final_status = collect_coordinator_status(&runtime_store, &thread_id)?;
if !matches!(
result["status"].as_str(),
Some("completed" | "failed" | "cancelled")
@ -775,144 +770,22 @@ fn assert_local_verse_brake_released(local_verse_store: &Path, runner_name: &str
Ok(())
}
fn collect_coordinator_status(
client: &mut status_cli::AppServerClient,
thread_id: &str,
) -> Result<Value> {
let read = client.send(
"thread/read",
Some(json!({"threadId": thread_id, "includeTurns": false})),
true,
)?;
let view = client.send(
"thread/epiphany/view",
Some(json!({"threadId": thread_id, "lenses": ["scene", "pressure", "jobs", "roles", "planning", "reorient", "crrc", "coordinator"]})),
true,
)?;
let scene = json!({
"threadId": thread_id,
"scene": view.get("scene").cloned().unwrap_or_else(|| json!(null)),
});
let pressure = json!({
"threadId": thread_id,
"source": "live",
"pressure": view.get("pressure").cloned().unwrap_or_else(|| json!(null)),
});
let reorient = view.get("reorient").cloned().unwrap_or_else(|| json!(null));
let jobs = json!({
"threadId": thread_id,
"source": "live",
"jobs": view.get("jobs").cloned().unwrap_or_else(|| json!([])),
});
let roles = view.get("roles").cloned().unwrap_or_else(|| json!(null));
let planning = view.get("planning").cloned().unwrap_or_else(|| json!(null));
let role_results = json!({
"imagination": client.send("thread/epiphany/roleResult", Some(json!({"threadId": thread_id, "roleId": "imagination"})), true)?,
"research": client.send("thread/epiphany/roleResult", Some(json!({"threadId": thread_id, "roleId": "research"})), true)?,
"modeling": client.send("thread/epiphany/roleResult", Some(json!({"threadId": thread_id, "roleId": "modeling"})), true)?,
"verification": client.send("thread/epiphany/roleResult", Some(json!({"threadId": thread_id, "roleId": "verification"})), true)?,
});
let reorient_result = client.send(
"thread/epiphany/reorientResult",
Some(json!({"threadId": thread_id})),
true,
)?;
let crrc = view.get("crrc").cloned().unwrap_or_else(|| json!(null));
let coordinator = view
.get("coordinator")
.cloned()
.unwrap_or_else(|| json!(null));
let root = env::current_dir()?;
let heartbeat_dir = root.join(".epiphany-heartbeats");
let persona_dir = root.join(".epiphany-persona");
let heartbeat = status_cli::native_json(
"epiphany-heartbeat-store",
fn collect_coordinator_status(runtime_store: &Path, thread_id: &str) -> Result<Value> {
let store = runtime_store.to_string_lossy();
status_cli::native_json(
"epiphany-mvp-status",
&[
"status",
"--store",
"state/agent-heartbeats.msgpack",
"--artifact-dir",
&heartbeat_dir.to_string_lossy(),
"--limit",
"8",
],
)?;
let latest_discord_persona = status_cli::native_json(
"epiphany-persona-discord",
&[
"latest",
"--artifact-dir",
&persona_dir.to_string_lossy(),
"--limit",
"8",
"--native",
"--json",
"--thread-id",
thread_id,
"--thread-state-store",
&store,
"--runtime-store",
&store,
],
)
.unwrap_or_else(|_| json!({"latestArtifacts": []}));
let latest_other_persona = status_cli::native_json(
"epiphany-persona-other",
&[
"latest",
"--artifact-dir",
&persona_dir.to_string_lossy(),
"--limit",
"8",
],
)
.unwrap_or_else(|_| json!({"latestArtifacts": []}));
let latest_reddit_persona = status_cli::native_json(
"epiphany-persona-reddit",
&[
"latest",
"--artifact-dir",
&persona_dir.to_string_lossy(),
"--limit",
"8",
],
)
.unwrap_or_else(|_| json!({"latestArtifacts": []}));
let mut latest_artifacts = latest_discord_persona
.get("latestArtifacts")
.and_then(Value::as_array)
.cloned()
.unwrap_or_default();
latest_artifacts.extend(
latest_reddit_persona
.get("latestArtifacts")
.and_then(Value::as_array)
.cloned()
.unwrap_or_default(),
);
latest_artifacts.extend(
latest_other_persona
.get("latestArtifacts")
.and_then(Value::as_array)
.cloned()
.unwrap_or_default(),
);
Ok(json!({
"threadId": thread_id,
"read": read,
"view": view,
"scene": scene,
"pressure": pressure,
"reorient": reorient,
"jobs": jobs,
"roles": roles,
"planning": planning,
"roleResults": role_results,
"reorientResult": reorient_result,
"crrc": crrc,
"coordinator": coordinator,
"heartbeat": heartbeat,
"persona": {
"status": "ready",
"artifactDir": persona_dir,
"latestArtifacts": latest_artifacts,
"availableActions": ["PersonaBubble", "characterTurn", "discordPersonaPost", "redditPersonaPost", "otherWorldPersonaRequest"],
},
}))
}
fn resolve_model_runtime_bin(root: &Path, configured: &Path) -> Result<PathBuf> {
if configured.components().count() != 1 {
return status_cli::absolute_path(configured);
@ -1329,27 +1202,12 @@ fn launch_worker_runtime_detached(
}))
}
fn read_role_result(
client: &mut status_cli::AppServerClient,
thread_id: &str,
role_id: &str,
) -> Result<Value> {
client.send(
"thread/epiphany/roleResult",
Some(json!({"threadId": thread_id, "roleId": role_id})),
true,
)
fn read_role_result(runtime_store: &Path, thread_id: &str, role_id: &str) -> Result<Value> {
Ok(collect_coordinator_status(runtime_store, thread_id)?["roleResults"][role_id].clone())
}
fn read_reorient_result(
client: &mut status_cli::AppServerClient,
thread_id: &str,
) -> Result<Value> {
client.send(
"thread/epiphany/reorientResult",
Some(json!({"threadId": thread_id, "bindingId": REORIENT_BINDING_ID})),
true,
)
fn read_reorient_result(runtime_store: &Path, thread_id: &str) -> Result<Value> {
Ok(collect_coordinator_status(runtime_store, thread_id)?["reorientResult"].clone())
}
fn maybe_apply_role_self_patch(accepted: &Value, agent_memory_dir: &Path) -> Result<Option<Value>> {
@ -1825,6 +1683,25 @@ fn home_dir() -> PathBuf {
mod tests {
use super::*;
#[test]
fn coordinator_status_and_result_reads_are_native() {
let source = include_str!("epiphany-mvp-coordinator.rs");
let status_start = source.find("fn collect_coordinator_status").unwrap();
let status_end = source.find("fn resolve_model_runtime_bin").unwrap();
let status = &source[status_start..status_end];
assert!(status.contains("epiphany-mvp-status"));
assert!(status.contains("--native"));
assert!(!status.contains("AppServerClient"));
assert!(!status.contains("thread/epiphany/"));
let reads_start = source.find("fn read_role_result").unwrap();
let reads_end = source.find("fn maybe_apply_role_self_patch").unwrap();
let reads = &source[reads_start..reads_end];
assert!(reads.contains("collect_coordinator_status"));
assert!(!reads.contains("AppServerClient"));
assert!(!reads.contains("thread/epiphany/"));
}
#[test]
fn continue_implementation_is_not_a_passive_stop_action() {
assert!(!is_stop_action("continueImplementation"));

View file

@ -1854,6 +1854,13 @@ publication. The bridge delegates both role and reorientation acceptance and
contains zero direct witness commits; its obsolete acceptance helpers and
imports are deleted. Begin `epiphany-mvp-coordinator` AppServerClient removal.
Coordinator read authority is now native. Status plus role/reorientation result
reads invoke `epiphany-mvp-status --native` over the shared CultCache store; six
Codex view/result calls were deleted and a source test rejects AppServerClient or
`thread/epiphany/*` inside those functions. Seven production mutation route
strings remain: bootstrap update/freshness, role acceptance, role/reorient
launch, and supersession update. Cut those next, then delete app-server startup.
## Immediate Re-entry Instruction
After compaction, first rehydrate and reorient from the listed files and git

View file

@ -234,3 +234,10 @@ The proof must observe the stores and receipts where the invariant lives:
The migration is not complete because the coordinator eventually converges on
the same state after an app-server round trip. The old path must be structurally
unable to decide the result.
Coordinator sight has crossed the boundary. `collect_coordinator_status`, role
result readback, and reorientation result readback now use the native status
projection over the shared thread/runtime CultCache store. The coordinator no
longer calls Codex for `view`, `roleResult`, or `reorientResult`. Bootstrap,
launch, acceptance dispatch, and supersession mutations still use app-server
routes and remain the live cut line.

Binary file not shown.

View file

@ -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: Wire `epiphany-mvp-coordinator` directly to `EpiphanyCoordinatorService` typed launch/result/role-accept/reorient-accept operations and native launch context, then remove every `thread/epiphany/*` call and AppServerClient dependency. Both acceptance families now delegate fully from the bridge with zero direct witness commits.
next_action: Replace `epiphany-mvp-coordinator` mutation routes—bootstrap update/freshness, role acceptance dispatch, role/reorient launch, and failure supersession—with `EpiphanyCoordinatorService` and native launch context. Status and all result reads are already native; remove AppServerClient after the remaining seven production `thread/epiphany/*` strings are cut.