From abce2aaf0a7e5c4ba7b0a084f462380dd0a6efd3 Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Sun, 12 Jul 2026 11:39:11 +0100 Subject: [PATCH] Add typed Modeling request generations --- epiphany-core/src/bin/epiphany-work.rs | 390 +++++++++++++++++++-- epiphany-core/src/lib.rs | 6 + epiphany-core/src/modeling_gateway.rs | 32 ++ epiphany-core/src/runtime_spine.rs | 199 +++++++++++ notes/epiphany-current-algorithmic-map.md | 13 + notes/epiphany-fork-implementation-plan.md | 12 +- notes/fresh-workspace-handoff.md | 18 + state/ledgers.msgpack | Bin 421277 -> 421827 bytes state/map.yaml | 2 +- 9 files changed, 629 insertions(+), 43 deletions(-) diff --git a/epiphany-core/src/bin/epiphany-work.rs b/epiphany-core/src/bin/epiphany-work.rs index 100129f6..42f07029 100644 --- a/epiphany-core/src/bin/epiphany-work.rs +++ b/epiphany-core/src/bin/epiphany-work.rs @@ -33,9 +33,11 @@ use epiphany_core::REPO_WORK_MAP_ENTRY_SCHEMA_VERSION; use epiphany_core::REPO_WORK_MODELING_FINDING_SCHEMA_VERSION; use epiphany_core::REPO_WORK_MODELING_OUTPUT_CONTRACT_ID; use epiphany_core::REPO_WORK_MODELING_REQUEST_SCHEMA_VERSION; +use epiphany_core::REPO_WORK_MODELING_ROUTE_SCHEMA_VERSION; use epiphany_core::RepoWorkMapEntry; use epiphany_core::RepoWorkModelingFinding; use epiphany_core::RepoWorkModelingRequest; +use epiphany_core::RepoWorkModelingRoute; use epiphany_core::RuntimeSpineHeartbeatJobOptions; use epiphany_core::RuntimeSpineInitOptions; use epiphany_core::SOUL_VERDICT_RECEIPT_SCHEMA_VERSION; @@ -44,8 +46,10 @@ use epiphany_core::SoulVerdictReceipt; use epiphany_core::SubstrateGateRepoAccessGrantReceipt; use epiphany_core::WeksaInterlinguaInput; use epiphany_core::WeksaSpeakerContext; +use epiphany_core::advance_repo_work_modeling_route; use epiphany_core::build_weksa_interlingua_packet; use epiphany_core::build_weksa_target_lowering_request; +use epiphany_core::commit_initial_repo_work_modeling_route; use epiphany_core::commit_repo_work_map_admission; use epiphany_core::default_launch_organ_contract; use epiphany_core::hands_action_review_for_intent; @@ -74,7 +78,6 @@ use epiphany_core::put_hands_command_receipt; use epiphany_core::put_hands_commit_receipt; use epiphany_core::put_hands_patch_receipt; use epiphany_core::put_hands_pr_receipt; -use epiphany_core::put_repo_work_modeling_request; use epiphany_core::put_soul_verdict_receipt; use epiphany_core::put_substrate_gate_repo_access_grant_receipt; use epiphany_core::record_weksa_target_lowering_receipt; @@ -87,6 +90,7 @@ use epiphany_core::runtime_latest_hands_receipt_chain_after; use epiphany_core::runtime_repo_work_map_entry; use epiphany_core::runtime_repo_work_modeling_finding; use epiphany_core::runtime_repo_work_modeling_request; +use epiphany_core::runtime_repo_work_modeling_route; use epiphany_core::write_epiphany_cultmesh_repo_work_map_entry; use epiphany_core::write_epiphany_cultmesh_repo_work_overview; use epiphany_core::write_epiphany_cultmesh_repo_work_public_proof; @@ -125,6 +129,9 @@ fn main() -> Result<()> { "execute" | "exec" => run_execute(parse_execute_args(args)?), "verify" | "soul-verify" => run_verify(parse_close_args(args)?), "close" | "closure" | "verify-close" => run_close(parse_close_args(args)?), + "revise-modeling" | "modeling-retry" => { + run_revise_modeling(parse_revise_modeling_args(args)?) + } "publish" => run_publish(parse_publish_args(args)?), "sync" | "sync-main" => run_sync(parse_sync_args(args)?), "overview" | "proof-bundle" | "status" => run_overview(parse_overview_args(args)?), @@ -297,6 +304,15 @@ struct CloseArgs { state_revision: u64, } +#[derive(Clone, Debug)] +struct ReviseModelingArgs { + workspace: PathBuf, + item: String, + runtime_store: Option, + rationale: String, + review_ref: String, +} + #[derive(Clone, Copy, Debug, PartialEq, Eq)] enum ClosurePhase { SoulOnly, @@ -958,6 +974,32 @@ fn parse_close_args(args: impl Iterator) -> Result { }) } +fn parse_revise_modeling_args(args: impl Iterator) -> Result { + let mut workspace = None; + let mut item = None; + let mut runtime_store = None; + let mut rationale = None; + let mut review_ref = None; + let mut args = args.peekable(); + while let Some(arg) = args.next() { + match arg.as_str() { + "--workspace" => workspace = Some(take_path(&mut args, "--workspace")?), + "--item" => item = Some(take_string(&mut args, "--item")?), + "--runtime-store" => runtime_store = Some(take_path(&mut args, "--runtime-store")?), + "--rationale" => rationale = Some(take_string(&mut args, "--rationale")?), + "--review-ref" => review_ref = Some(take_string(&mut args, "--review-ref")?), + other => return Err(anyhow!("unexpected revise-modeling argument {other:?}")), + } + } + Ok(ReviseModelingArgs { + workspace: workspace.context("missing --workspace")?, + item: item.context("missing --item")?, + runtime_store, + rationale: rationale.context("missing --rationale")?, + review_ref: review_ref.context("missing --review-ref")?, + }) +} + fn parse_publish_args(args: impl Iterator) -> Result { let mut workspace = None; let mut epiphany_root = None; @@ -11168,6 +11210,118 @@ fn run_verify(args: CloseArgs) -> Result { run_closure_pipeline(args, ClosurePhase::SoulOnly) } +fn run_revise_modeling(args: ReviseModelingArgs) -> Result { + let workspace = args + .workspace + .canonicalize() + .with_context(|| format!("failed to resolve {}", args.workspace.display()))?; + ensure_git_repo(&workspace)?; + let item_slug = sanitize(&args.item); + let close_path = work_receipt_path(&workspace, "close", &args.item); + let close = read_json(&close_path)?; + if close.get("status").and_then(Value::as_str) != Some("awaiting-modeling") { + return Err(anyhow!( + "revise-modeling requires an awaiting-modeling closure projection" + )); + } + let runtime_store = args + .runtime_store + .or_else(|| path_from_json(&close, &["runtimeStore"])) + .ok_or_else(|| anyhow!("awaiting-modeling closure has no runtime store"))?; + let route_id = format!("repo-work-modeling-route-{item_slug}"); + let current = runtime_repo_work_modeling_route(&runtime_store, &route_id)? + .ok_or_else(|| anyhow!("typed Modeling route {route_id:?} is missing"))?; + let current_request = runtime_repo_work_modeling_request(&runtime_store, ¤t.request_id)? + .ok_or_else(|| anyhow!("current Modeling request is missing"))?; + let previous_finding_id = format!("{}-finding", current.request_id); + let previous_finding = + runtime_repo_work_modeling_finding(&runtime_store, &previous_finding_id)? + .ok_or_else(|| anyhow!("revise-modeling requires the current typed finding"))?; + if previous_finding + .verdict + .trim() + .eq_ignore_ascii_case("passed") + { + return Err(anyhow!( + "passing Modeling truth cannot be revised into a retry" + )); + } + let generation = current.generation.saturating_add(1); + let request_id = format!("repo-work-close-{item_slug}-modeling-request-g{generation}"); + let now = Utc::now().to_rfc3339_opts(chrono::SecondsFormat::Secs, true); + let request = RepoWorkModelingRequest { + schema_version: REPO_WORK_MODELING_REQUEST_SCHEMA_VERSION.to_string(), + request_id: request_id.clone(), + item: current.item.clone(), + requester: "mind".to_string(), + soul_verdict_receipt_id: current_request.soul_verdict_receipt_id.clone(), + commit_sha: current_request.commit_sha.clone(), + changed_paths: current_request.changed_paths.clone(), + instruction: format!( + "Re-model the same Soul-verified consequence after reviewed correction {}. Previous finding {} said: {}. Review rationale: {}", + args.review_ref, + previous_finding.receipt_id, + compact_line(&previous_finding.finding), + compact_line(&args.rationale) + ), + requested_at: now.clone(), + private_state_exposed: false, + contract: "Mind-reviewed next generation of one immutable repo-work Modeling route; preserves the Soul-verified consequence and previous finding." + .to_string(), + }; + let gateway_id = format!("repo-work-modeling-route-{item_slug}-g{generation}-mind-review"); + let review = MindGatewayReview { + schema_version: MIND_GATEWAY_REVIEW_SCHEMA_VERSION.to_string(), + gateway_id: gateway_id.clone(), + source_kind: "repo_work_modeling_revision".to_string(), + source_role_id: "mind".to_string(), + decision: MindGatewayDecision::Accept, + allowed_effects: vec!["repoWork.modelingRoute".to_string()], + refused_effects: vec![ + "repoWork.map".to_string(), + "publication".to_string(), + "merge".to_string(), + "handsAction".to_string(), + ], + reasons: vec![ + format!("Reviewed correction reference: {}.", args.review_ref), + format!("Previous immutable verdict: {}.", previous_finding.verdict), + args.rationale.clone(), + ], + contract: "Mind review advances only the current Modeling request generation; it admits no map, publication, merge, or Hands effect." + .to_string(), + }; + let route = RepoWorkModelingRoute { + schema_version: REPO_WORK_MODELING_ROUTE_SCHEMA_VERSION.to_string(), + route_id: current.route_id.clone(), + item: current.item.clone(), + generation, + request_id: request.request_id.clone(), + previous_finding_receipt_id: previous_finding.receipt_id.clone(), + authority_owner: "mind".to_string(), + authority_witness_id: review.gateway_id.clone(), + updated_at: now, + private_state_exposed: false, + contract: "Sole current-generation pointer for repo-work Modeling; older requests and findings remain immutable evidence." + .to_string(), + }; + let route = advance_repo_work_modeling_route(&runtime_store, &request, &route, &review)?; + Ok(json!({ + "schemaVersion": REPO_WORK_MODELING_ROUTE_SCHEMA_VERSION, + "status": "modeling-route-advanced", + "workspace": workspace, + "item": route.item, + "routeId": route.route_id, + "generation": route.generation, + "requestId": route.request_id, + "previousFindingReceiptId": route.previous_finding_receipt_id, + "mindReviewId": route.authority_witness_id, + "reviewRef": args.review_ref, + "privateStateExposed": false, + "nextSafeMove": "Pulse the scheduler to launch the reviewed Modeling generation through Idunn." + })) +} + fn run_close(args: CloseArgs) -> Result { run_closure_pipeline(args, ClosurePhase::Full) } @@ -11490,33 +11644,61 @@ fn run_closure_pipeline(args: CloseArgs, phase: ClosurePhase) -> Result { declared_changed_paths.join(", ") ) }); - let modeling_request_id = format!("repo-work-close-{item_slug}-modeling-request"); - let mut modeling_request = RepoWorkModelingRequest { - schema_version: REPO_WORK_MODELING_REQUEST_SCHEMA_VERSION.to_string(), - request_id: modeling_request_id.clone(), - item: item.clone(), - requester: "self".to_string(), - soul_verdict_receipt_id: soul_verdict.receipt_id.clone(), - commit_sha: commit_sha.clone(), - changed_paths: declared_changed_paths.clone(), - instruction: "Model the Soul-verified consequence and return a bounded repo-map finding; do not admit state or route the next action.".to_string(), - requested_at: Utc::now().to_rfc3339_opts(chrono::SecondsFormat::Secs, true), - private_state_exposed: false, - contract: "Self-to-Modeling request over a passing Soul verdict; Self may route but cannot author the result.".to_string(), - }; - if let Some(existing) = - runtime_repo_work_modeling_request(&runtime_store, &modeling_request_id)? - { - modeling_request.requested_at = existing.requested_at.clone(); - if modeling_request != existing { - return Err(anyhow!( - "closure retry conflicts with immutable Modeling request" - )); - } - modeling_request = existing; + let modeling_route_id = format!("repo-work-modeling-route-{item_slug}"); + let (modeling_request, modeling_route) = if phase == ClosurePhase::Full { + let route = runtime_repo_work_modeling_route(&runtime_store, &modeling_route_id)? + .ok_or_else(|| anyhow!("closure requires the typed Modeling route"))?; + let request = runtime_repo_work_modeling_request(&runtime_store, &route.request_id)? + .ok_or_else(|| anyhow!("closure requires the current typed Modeling request"))?; + (request, route) } else { - put_repo_work_modeling_request(&runtime_store, &modeling_request)?; - } + let modeling_request_id = format!("repo-work-close-{item_slug}-modeling-request"); + let mut request = RepoWorkModelingRequest { + schema_version: REPO_WORK_MODELING_REQUEST_SCHEMA_VERSION.to_string(), + request_id: modeling_request_id.clone(), + item: item.clone(), + requester: "self".to_string(), + soul_verdict_receipt_id: soul_verdict.receipt_id.clone(), + commit_sha: commit_sha.clone(), + changed_paths: declared_changed_paths.clone(), + instruction: "Model the Soul-verified consequence and return a bounded repo-map finding; do not admit state or route the next action.".to_string(), + requested_at: Utc::now().to_rfc3339_opts(chrono::SecondsFormat::Secs, true), + private_state_exposed: false, + contract: "Self-to-Modeling request over a passing Soul verdict; Self may route but cannot author the result.".to_string(), + }; + if let Some(existing) = + runtime_repo_work_modeling_request(&runtime_store, &modeling_request_id)? + { + request.requested_at = existing.requested_at.clone(); + if request != existing { + return Err(anyhow!( + "closure retry conflicts with immutable Modeling request" + )); + } + request = existing; + } + let mut route = RepoWorkModelingRoute { + schema_version: REPO_WORK_MODELING_ROUTE_SCHEMA_VERSION.to_string(), + route_id: modeling_route_id.clone(), + item: item.clone(), + generation: 0, + request_id: request.request_id.clone(), + previous_finding_receipt_id: String::new(), + authority_owner: "soul".to_string(), + authority_witness_id: soul_verdict.receipt_id.clone(), + updated_at: Utc::now().to_rfc3339_opts(chrono::SecondsFormat::Secs, true), + private_state_exposed: false, + contract: "Current repo-work Modeling generation; generation zero is established atomically with its Soul-backed request, and later movement requires Mind review." + .to_string(), + }; + if let Some(existing) = + runtime_repo_work_modeling_route(&runtime_store, &modeling_route_id)? + { + route.updated_at = existing.updated_at.clone(); + } + let route = commit_initial_repo_work_modeling_route(&runtime_store, &request, &route)?; + (request, route) + }; if phase == ClosurePhase::SoulOnly || !model_closure_passed { let awaiting_receipt = json!({ "schemaVersion": "epiphany.repo_work_closure_receipt.v0", @@ -11536,6 +11718,8 @@ fn run_closure_pipeline(args: CloseArgs, phase: ClosurePhase) -> Result { "closureReview": closure_review, "modeling": { "requestId": modeling_request.request_id, + "routeId": modeling_route.route_id, + "generation": modeling_route.generation, "requester": modeling_request.requester, "findingReceiptId": Value::Null }, @@ -14170,8 +14354,8 @@ fn run_queue(args: QueueArgs) -> Result { })) } -fn repo_work_modeling_job_id(item_slug: &str) -> String { - format!("repo-work-modeling-{item_slug}") +fn repo_work_modeling_job_id(item_slug: &str, generation: u64) -> String { + format!("repo-work-modeling-{item_slug}-g{generation}") } fn resolve_repo_work_model_runtime(epiphany_root: &Path) -> Result { @@ -14239,8 +14423,9 @@ fn launch_repo_work_modeling_worker( local_verse_store: &Path, request: &RepoWorkModelingRequest, item_slug: &str, + generation: u64, ) -> Result { - let job_id = repo_work_modeling_job_id(item_slug); + let job_id = repo_work_modeling_job_id(item_slug, generation); let verified_diff = git_output( workspace, &[ @@ -14730,10 +14915,11 @@ fn run_tick(args: TickArgs) -> Result { .clone() .or_else(|| path_from_json(&partial_close, &["runtimeStore"])) .ok_or_else(|| anyhow!("awaiting-modeling closure has no runtime store"))?; - let request_id = string_from_json(&partial_close, &["modeling", "requestId"]) - .ok_or_else(|| anyhow!("awaiting-modeling closure has no typed request id"))?; - let request = runtime_repo_work_modeling_request(&runtime_store, &request_id)? - .ok_or_else(|| anyhow!("typed Modeling request {request_id:?} is missing"))?; + let route_id = format!("repo-work-modeling-route-{item_slug}"); + let route = runtime_repo_work_modeling_route(&runtime_store, &route_id)? + .ok_or_else(|| anyhow!("typed Modeling route {route_id:?} is missing"))?; + let request = runtime_repo_work_modeling_request(&runtime_store, &route.request_id)? + .ok_or_else(|| anyhow!("current Modeling request {:?} is missing", route.request_id))?; let finding_id = format!("{}-finding", request.request_id); if let Some(finding) = runtime_repo_work_modeling_finding(&runtime_store, &finding_id)? { if finding.verdict.trim().eq_ignore_ascii_case("passed") { @@ -14781,7 +14967,7 @@ fn run_tick(args: TickArgs) -> Result { .to_string(); } } else { - let job_id = repo_work_modeling_job_id(&item_slug); + let job_id = repo_work_modeling_job_id(&item_slug, route.generation); match runtime_job_snapshot(&runtime_store, &job_id)? { Some(snapshot) if matches!( @@ -14829,6 +15015,7 @@ fn run_tick(args: TickArgs) -> Result { &lifecycle_store, &request, &item_slug, + route.generation, )?; status = "modeling-launched".to_string(); reason = @@ -16724,7 +16911,24 @@ mod authority_tests { private_state_exposed: false, contract: "test".to_string(), }; - put_repo_work_modeling_request(&store, &request)?; + let route = RepoWorkModelingRoute { + schema_version: REPO_WORK_MODELING_ROUTE_SCHEMA_VERSION.to_string(), + route_id: "modeling-route-1".to_string(), + item: request.item.clone(), + generation: 0, + request_id: request.request_id.clone(), + previous_finding_receipt_id: String::new(), + authority_owner: "soul".to_string(), + authority_witness_id: request.soul_verdict_receipt_id.clone(), + updated_at: "2026-07-12T00:00:01Z".to_string(), + private_state_exposed: false, + contract: "test".to_string(), + }; + commit_initial_repo_work_modeling_route(&store, &request, &route)?; + assert_eq!( + runtime_repo_work_modeling_route(&store, &route.route_id)?, + Some(route.clone()) + ); let mut finding = RepoWorkModelingFinding { schema_version: REPO_WORK_MODELING_FINDING_SCHEMA_VERSION.to_string(), receipt_id: "modeling-finding-1".to_string(), @@ -16821,6 +17025,120 @@ mod authority_tests { ); Ok(()) } + + #[test] + fn modeling_route_generation_requires_mind_and_preserves_previous() -> Result<()> { + let temp = tempfile::tempdir()?; + let store = temp.path().join("runtime.cc"); + initialize_runtime_spine( + &store, + RuntimeSpineInitOptions { + runtime_id: "modeling-route-test".to_string(), + display_name: "Modeling Route Test".to_string(), + created_at: "2026-07-12T00:00:00Z".to_string(), + }, + )?; + let soul = SoulVerdictReceipt { + schema_version: SOUL_VERDICT_RECEIPT_SCHEMA_VERSION.to_string(), + receipt_id: "route-soul-1".to_string(), + source_result_id: "result-1".to_string(), + source_job_id: "job-1".to_string(), + verdict: "passed".to_string(), + summary: "verified".to_string(), + evidence_ids: Vec::new(), + risks: Vec::new(), + emitted_at: "2026-07-12T00:00:00Z".to_string(), + contract: "test".to_string(), + }; + put_soul_verdict_receipt(&store, &soul)?; + let request0 = RepoWorkModelingRequest { + schema_version: REPO_WORK_MODELING_REQUEST_SCHEMA_VERSION.to_string(), + request_id: "route-request-g0".to_string(), + item: "route-item".to_string(), + requester: "self".to_string(), + soul_verdict_receipt_id: soul.receipt_id.clone(), + commit_sha: "abc123".to_string(), + changed_paths: vec!["README.md".to_string()], + instruction: "Model consequence.".to_string(), + requested_at: "2026-07-12T00:00:01Z".to_string(), + private_state_exposed: false, + contract: "test".to_string(), + }; + let route0 = RepoWorkModelingRoute { + schema_version: REPO_WORK_MODELING_ROUTE_SCHEMA_VERSION.to_string(), + route_id: "route-item-current".to_string(), + item: request0.item.clone(), + generation: 0, + request_id: request0.request_id.clone(), + previous_finding_receipt_id: String::new(), + authority_owner: "soul".to_string(), + authority_witness_id: soul.receipt_id.clone(), + updated_at: "2026-07-12T00:00:01Z".to_string(), + private_state_exposed: false, + contract: "test".to_string(), + }; + commit_initial_repo_work_modeling_route(&store, &request0, &route0)?; + let finding0 = RepoWorkModelingFinding { + schema_version: REPO_WORK_MODELING_FINDING_SCHEMA_VERSION.to_string(), + receipt_id: "route-request-g0-finding".to_string(), + item: request0.item.clone(), + model_ref: "model-1".to_string(), + soul_verdict_receipt_id: soul.receipt_id.clone(), + verdict: "needs-work".to_string(), + finding: "Need the verified diff.".to_string(), + summary: "Context incomplete.".to_string(), + changed_paths: request0.changed_paths.clone(), + commit_sha: request0.commit_sha.clone(), + emitted_at: "2026-07-12T00:00:02Z".to_string(), + private_state_exposed: false, + contract: "test".to_string(), + request_id: request0.request_id.clone(), + }; + put_repo_work_modeling_finding(&store, &finding0)?; + let review = MindGatewayReview { + schema_version: MIND_GATEWAY_REVIEW_SCHEMA_VERSION.to_string(), + gateway_id: "route-g1-mind-review".to_string(), + source_kind: "repo_work_modeling_revision".to_string(), + source_role_id: "mind".to_string(), + decision: MindGatewayDecision::Accept, + allowed_effects: vec!["repoWork.modelingRoute".to_string()], + refused_effects: vec!["repoWork.map".to_string()], + reasons: vec!["Reviewed missing context.".to_string()], + contract: "test".to_string(), + }; + let request1 = RepoWorkModelingRequest { + request_id: "route-request-g1".to_string(), + requester: "mind".to_string(), + instruction: "Re-model with reviewed context.".to_string(), + requested_at: "2026-07-12T00:00:03Z".to_string(), + ..request0.clone() + }; + let route1 = RepoWorkModelingRoute { + generation: 1, + request_id: request1.request_id.clone(), + previous_finding_receipt_id: finding0.receipt_id.clone(), + authority_owner: "mind".to_string(), + authority_witness_id: review.gateway_id.clone(), + updated_at: "2026-07-12T00:00:03Z".to_string(), + contract: "test generation one".to_string(), + ..route0.clone() + }; + advance_repo_work_modeling_route(&store, &request1, &route1, &review)?; + assert_eq!( + runtime_repo_work_modeling_route(&store, &route0.route_id)?, + Some(route1.clone()) + ); + assert_eq!( + runtime_repo_work_modeling_request(&store, &request0.request_id)?, + Some(request0) + ); + assert_eq!( + runtime_repo_work_modeling_finding(&store, &finding0.receipt_id)?, + Some(finding0) + ); + assert!(advance_repo_work_modeling_route(&store, &request1, &route1, &review).is_err()); + Ok(()) + } } fn adopted_action_item_from_plan(plan: &Value) -> Value { diff --git a/epiphany-core/src/lib.rs b/epiphany-core/src/lib.rs index ac959d10..6cd41a3d 100644 --- a/epiphany-core/src/lib.rs +++ b/epiphany-core/src/lib.rs @@ -659,9 +659,12 @@ pub use modeling_gateway::REPO_WORK_MODELING_FINDING_SCHEMA_VERSION; pub use modeling_gateway::REPO_WORK_MODELING_FINDING_TYPE; pub use modeling_gateway::REPO_WORK_MODELING_REQUEST_SCHEMA_VERSION; pub use modeling_gateway::REPO_WORK_MODELING_REQUEST_TYPE; +pub use modeling_gateway::REPO_WORK_MODELING_ROUTE_SCHEMA_VERSION; +pub use modeling_gateway::REPO_WORK_MODELING_ROUTE_TYPE; pub use modeling_gateway::RepoWorkMapEntry; pub use modeling_gateway::RepoWorkModelingFinding; pub use modeling_gateway::RepoWorkModelingRequest; +pub use modeling_gateway::RepoWorkModelingRoute; pub use organ_dependencies::EPIPHANY_LAUNCH_ORGAN_CONTRACT_SCHEMA_VERSION; pub use organ_dependencies::EPIPHANY_ORGAN_DEPENDENCY_SCHEMA_VERSION; pub use organ_dependencies::EPIPHANY_STANDING_ORGANS; @@ -770,8 +773,10 @@ pub use runtime_spine::RuntimeSpineInitOptions; pub use runtime_spine::RuntimeSpineJobOptions; pub use runtime_spine::RuntimeSpineJobResultOptions; pub use runtime_spine::RuntimeSpineSessionOptions; +pub use runtime_spine::advance_repo_work_modeling_route; pub use runtime_spine::append_runtime_event; pub use runtime_spine::clear_epiphany_job_binding_backend; +pub use runtime_spine::commit_initial_repo_work_modeling_route; pub use runtime_spine::commit_repo_work_map_admission; pub use runtime_spine::complete_runtime_job; pub use runtime_spine::create_runtime_job; @@ -817,6 +822,7 @@ pub use runtime_spine::runtime_reorient_worker_result; pub use runtime_spine::runtime_repo_work_map_entry; pub use runtime_spine::runtime_repo_work_modeling_finding; pub use runtime_spine::runtime_repo_work_modeling_request; +pub use runtime_spine::runtime_repo_work_modeling_route; pub use runtime_spine::runtime_role_worker_result; pub use runtime_spine::runtime_schema_catalog_response; pub use runtime_spine::runtime_soul_verdict_receipt; diff --git a/epiphany-core/src/modeling_gateway.rs b/epiphany-core/src/modeling_gateway.rs index 42699217..e28c2b74 100644 --- a/epiphany-core/src/modeling_gateway.rs +++ b/epiphany-core/src/modeling_gateway.rs @@ -6,6 +6,8 @@ pub const REPO_WORK_MODELING_FINDING_SCHEMA_VERSION: &str = pub const REPO_WORK_MODELING_REQUEST_TYPE: &str = "epiphany.modeling.repo_work_request"; pub const REPO_WORK_MODELING_REQUEST_SCHEMA_VERSION: &str = "epiphany.modeling.repo_work_request.v0"; +pub const REPO_WORK_MODELING_ROUTE_TYPE: &str = "epiphany.modeling.repo_work_route"; +pub const REPO_WORK_MODELING_ROUTE_SCHEMA_VERSION: &str = "epiphany.modeling.repo_work_route.v0"; pub const REPO_WORK_MAP_ENTRY_TYPE: &str = "epiphany.repo_work.map_entry"; pub const REPO_WORK_MAP_ENTRY_SCHEMA_VERSION: &str = "epiphany.repo_work.map_entry.v0"; @@ -75,6 +77,36 @@ pub struct RepoWorkModelingRequest { pub contract: String, } +#[derive(Debug, Clone, PartialEq, Eq, DatabaseEntry)] +#[cultcache( + type = "epiphany.modeling.repo_work_route", + schema = "RepoWorkModelingRoute" +)] +pub struct RepoWorkModelingRoute { + #[cultcache(key = 0)] + pub schema_version: String, + #[cultcache(key = 1)] + pub route_id: String, + #[cultcache(key = 2)] + pub item: String, + #[cultcache(key = 3)] + pub generation: u64, + #[cultcache(key = 4)] + pub request_id: String, + #[cultcache(key = 5)] + pub previous_finding_receipt_id: String, + #[cultcache(key = 6)] + pub authority_owner: String, + #[cultcache(key = 7)] + pub authority_witness_id: String, + #[cultcache(key = 8)] + pub updated_at: String, + #[cultcache(key = 9)] + pub private_state_exposed: bool, + #[cultcache(key = 10)] + pub contract: String, +} + #[derive(Debug, Clone, PartialEq, Eq, DatabaseEntry)] #[cultcache(type = "epiphany.repo_work.map_entry", schema = "RepoWorkMapEntry")] pub struct RepoWorkMapEntry { diff --git a/epiphany-core/src/runtime_spine.rs b/epiphany-core/src/runtime_spine.rs index 598c54b4..e7161f6e 100644 --- a/epiphany-core/src/runtime_spine.rs +++ b/epiphany-core/src/runtime_spine.rs @@ -38,6 +38,7 @@ use crate::mind_gateway::MIND_THOUGHT_SCHEMA_VERSION; use crate::mind_gateway::MIND_THOUGHT_TYPE; use crate::mind_gateway::MIND_VERSE_ADOPTION_RECEIPT_SCHEMA_VERSION; use crate::mind_gateway::MIND_VERSE_ADOPTION_RECEIPT_TYPE; +use crate::mind_gateway::MindGatewayDecision; use crate::mind_gateway::MindGatewayReview; use crate::mind_gateway::MindStateCommitReceipt; use crate::modeling_gateway::REPO_WORK_MAP_ENTRY_SCHEMA_VERSION; @@ -46,9 +47,12 @@ use crate::modeling_gateway::REPO_WORK_MODELING_FINDING_SCHEMA_VERSION; use crate::modeling_gateway::REPO_WORK_MODELING_FINDING_TYPE; use crate::modeling_gateway::REPO_WORK_MODELING_REQUEST_SCHEMA_VERSION; use crate::modeling_gateway::REPO_WORK_MODELING_REQUEST_TYPE; +use crate::modeling_gateway::REPO_WORK_MODELING_ROUTE_SCHEMA_VERSION; +use crate::modeling_gateway::REPO_WORK_MODELING_ROUTE_TYPE; use crate::modeling_gateway::RepoWorkMapEntry; use crate::modeling_gateway::RepoWorkModelingFinding; use crate::modeling_gateway::RepoWorkModelingRequest; +use crate::modeling_gateway::RepoWorkModelingRoute; use crate::organ_dependencies::EpiphanyLaunchOrganContract; use crate::soul_gateway::SoulVerdictReceipt; use crate::soul_gateway::*; @@ -734,6 +738,7 @@ pub fn runtime_spine_cache(store_path: impl AsRef) -> Result { cache.register_entry_type::()?; cache.register_entry_type::()?; cache.register_entry_type::()?; + cache.register_entry_type::()?; cache.register_entry_type::()?; cache.register_entry_type::()?; cache.register_entry_type::()?; @@ -1849,6 +1854,183 @@ pub fn runtime_repo_work_modeling_request( cache.get::(request_id) } +pub fn commit_initial_repo_work_modeling_route( + store_path: impl AsRef, + request: &RepoWorkModelingRequest, + route: &RepoWorkModelingRoute, +) -> Result { + if request.schema_version != REPO_WORK_MODELING_REQUEST_SCHEMA_VERSION { + return Err(anyhow!("unsupported Modeling request schema")); + } + validate_non_empty(&request.request_id, "Modeling request id")?; + validate_non_empty(&request.item, "Modeling request item")?; + validate_non_empty( + &request.soul_verdict_receipt_id, + "Modeling request Soul verdict receipt id", + )?; + validate_non_empty(&request.commit_sha, "Modeling request commit sha")?; + validate_non_empty(&request.instruction, "Modeling request instruction")?; + validate_non_empty(&request.requested_at, "Modeling request timestamp")?; + if request.requester != "self" || request.private_state_exposed { + return Err(anyhow!( + "initial Modeling request must be Self-routed and private-state sealed" + )); + } + if route.schema_version != REPO_WORK_MODELING_ROUTE_SCHEMA_VERSION { + return Err(anyhow!("unsupported Modeling route schema")); + } + validate_non_empty(&route.route_id, "Modeling route id")?; + validate_non_empty(&route.item, "Modeling route item")?; + validate_non_empty(&route.request_id, "Modeling route request id")?; + validate_non_empty(&route.authority_owner, "Modeling route authority owner")?; + validate_non_empty( + &route.authority_witness_id, + "Modeling route authority witness", + )?; + validate_non_empty(&route.updated_at, "Modeling route timestamp")?; + if route.generation != 0 + || !route.previous_finding_receipt_id.is_empty() + || route.authority_owner != "soul" + || route.authority_witness_id != request.soul_verdict_receipt_id + || route.item != request.item + || route.request_id != request.request_id + || route.private_state_exposed + { + return Err(anyhow!( + "initial Modeling route must be generation zero, Soul-backed, request-matched, and private-state sealed" + )); + } + let mut cache = runtime_spine_cache(store_path)?; + cache.pull_all_backing_stores()?; + require_identity(&cache)?; + let soul = cache + .get::(&request.soul_verdict_receipt_id)? + .ok_or_else(|| anyhow!("initial Modeling route requires its Soul verdict"))?; + if soul.verdict.trim().to_ascii_lowercase() != "passed" { + return Err(anyhow!( + "initial Modeling route requires passing Soul truth" + )); + } + if let Some(existing) = cache.get::(&route.route_id)? { + let stored_request = cache + .get::(&existing.request_id)? + .ok_or_else(|| anyhow!("existing Modeling route lost its request"))?; + if existing == *route && stored_request == *request { + return Ok(existing); + } + return Err(anyhow!( + "initial Modeling route already belongs to different routing truth" + )); + } + if cache + .get::(&request.request_id)? + .is_some() + { + return Err(anyhow!( + "Modeling request exists without its atomic initial route" + )); + } + let (request_envelope, _) = cache.prepare_entry(&request.request_id, request)?; + let (route_envelope, _) = cache.prepare_entry(&route.route_id, route)?; + cache.put_prepared_batch(vec![request_envelope, route_envelope])?; + Ok(route.clone()) +} + +pub fn runtime_repo_work_modeling_route( + store_path: impl AsRef, + route_id: &str, +) -> Result> { + validate_non_empty(route_id, "Modeling route id")?; + let mut cache = runtime_spine_cache(store_path)?; + cache.pull_all_backing_stores()?; + cache.get::(route_id) +} + +pub fn advance_repo_work_modeling_route( + store_path: impl AsRef, + request: &RepoWorkModelingRequest, + route: &RepoWorkModelingRoute, + mind_review: &MindGatewayReview, +) -> Result { + if request.schema_version != REPO_WORK_MODELING_REQUEST_SCHEMA_VERSION + || route.schema_version != REPO_WORK_MODELING_ROUTE_SCHEMA_VERSION + { + return Err(anyhow!("unsupported Modeling route generation schema")); + } + validate_non_empty(&request.request_id, "next Modeling request id")?; + validate_non_empty(&request.item, "next Modeling request item")?; + validate_non_empty( + &request.soul_verdict_receipt_id, + "next Modeling request Soul verdict", + )?; + validate_non_empty(&request.commit_sha, "next Modeling request commit")?; + validate_non_empty(&request.instruction, "next Modeling request instruction")?; + validate_non_empty(&request.requested_at, "next Modeling request timestamp")?; + validate_non_empty(&route.route_id, "Modeling route id")?; + validate_non_empty( + &route.previous_finding_receipt_id, + "previous Modeling finding receipt id", + )?; + validate_non_empty(&route.updated_at, "next Modeling route timestamp")?; + if request.requester != "mind" + || request.private_state_exposed + || route.private_state_exposed + || route.authority_owner != "mind" + || route.authority_witness_id != mind_review.gateway_id + || route.request_id != request.request_id + || route.item != request.item + || mind_review.decision != MindGatewayDecision::Accept + || mind_review.source_kind != "repo_work_modeling_revision" + || !mind_review + .allowed_effects + .iter() + .any(|effect| effect == "repoWork.modelingRoute") + { + return Err(anyhow!( + "next Modeling generation requires a private-state-sealed Mind acceptance that owns only the route transition" + )); + } + let mut cache = runtime_spine_cache(store_path)?; + cache.pull_all_backing_stores()?; + require_identity(&cache)?; + let current = cache + .get::(&route.route_id)? + .ok_or_else(|| anyhow!("cannot advance a missing Modeling route"))?; + let current_request = cache + .get::(¤t.request_id)? + .ok_or_else(|| anyhow!("current Modeling route lost its request"))?; + let previous_finding = cache + .get::(&route.previous_finding_receipt_id)? + .ok_or_else(|| anyhow!("next Modeling generation requires the previous finding"))?; + if route.generation != current.generation.saturating_add(1) + || previous_finding.request_id != current.request_id + || previous_finding.item != current.item + || previous_finding + .verdict + .trim() + .eq_ignore_ascii_case("passed") + || request.item != current_request.item + || request.soul_verdict_receipt_id != current_request.soul_verdict_receipt_id + || request.commit_sha != current_request.commit_sha + || request.changed_paths != current_request.changed_paths + { + return Err(anyhow!( + "next Modeling generation must follow one non-passing current finding and preserve the Soul-verified consequence" + )); + } + if cache + .get::(&request.request_id)? + .is_some() + { + return Err(anyhow!("next Modeling request id already exists")); + } + let (request_envelope, _) = cache.prepare_entry(&request.request_id, request)?; + let (review_envelope, _) = cache.prepare_entry(&mind_review.gateway_id, mind_review)?; + let (route_envelope, _) = cache.prepare_entry(&route.route_id, route)?; + cache.put_prepared_batch(vec![request_envelope, review_envelope, route_envelope])?; + Ok(route.clone()) +} + pub fn runtime_repo_work_modeling_finding( store_path: impl AsRef, receipt_id: &str, @@ -2560,6 +2742,23 @@ fn epiphany_mutation_contracts() -> Vec { "Schedulers and raw CLI fields cannot substitute for this persisted receipt.", ], ), + mutation_contract( + REPO_WORK_MODELING_ROUTE_TYPE, + REPO_WORK_MODELING_ROUTE_SCHEMA_VERSION, + vec![ + CultNetDocumentOperation::Snapshot, + CultNetDocumentOperation::IntentSubmit, + CultNetDocumentOperation::ReceiptWatch, + ], + CultNetMutationAuthority::Coordinator, + vec![REPO_WORK_MODELING_REQUEST_TYPE, MIND_GATEWAY_REVIEW_TYPE], + vec![REPO_WORK_MODELING_ROUTE_TYPE], + vec![ + "The route is the sole current-generation owner for repo-work Modeling requests.", + "Generation zero is Soul-backed; later generations require a Mind review and preserve the previous finding.", + "Filesystem closure artifacts and Self are projections/routers, not route writers.", + ], + ), mutation_contract( REPO_WORK_MAP_ENTRY_TYPE, REPO_WORK_MAP_ENTRY_SCHEMA_VERSION, diff --git a/notes/epiphany-current-algorithmic-map.md b/notes/epiphany-current-algorithmic-map.md index 677ac9d6..2f24b66d 100644 --- a/notes/epiphany-current-algorithmic-map.md +++ b/notes/epiphany-current-algorithmic-map.md @@ -152,6 +152,19 @@ artifacts, and publishes an process identity. Self receives that receipt ID as routing evidence only. Live item `idunn-owned-modeling` crossed this boundary and reached the Bifrost gate. +The current Modeling request is owned by +`epiphany.modeling.repo_work_route.v0`, not the filesystem closure projection. +Generation zero lands atomically with its Soul-backed request. A later +generation can advance only through `advance_repo_work_modeling_route`: the +current finding must exist and be non-passing, the new request must preserve +item/Soul/commit/paths, and a Mind acceptance must grant only +`repoWork.modelingRoute`. Request, Mind review, and stable route pointer commit +in one CultCache batch; old request/finding documents remain immutable. +`epiphany-work revise-modeling --review-ref ... --rationale ...` is the explicit +review mouth. Scheduler job IDs include the route generation and derive the +stable route key from the item, so neither close JSON nor an old completed job +can select current work. + The accepted interpretation is persisted as `epiphany.modeling.repo_work_finding.v0`; it references the passing Soul verdict, diff --git a/notes/epiphany-fork-implementation-plan.md b/notes/epiphany-fork-implementation-plan.md index 34ed0ea4..4106a3b1 100644 --- a/notes/epiphany-fork-implementation-plan.md +++ b/notes/epiphany-fork-implementation-plan.md @@ -204,9 +204,9 @@ Delete or demote: ## Immediate Next Action -Design and implement a typed Mind-reviewed request-generation pointer for -non-passing immutable repo-work Modeling findings. Idunn now owns detached -worker launch through its existing lifecycle receipt mouth. Preserve the old -request/finding as immutable evidence, make one typed owner select the current -generation, and keep filesystem closure artifacts and Self from becoming -mutable routing authorities. +Expose the launched worker executable's build/schema fingerprint in Idunn's +lifecycle receipt and preflight required runtime document registration before +launch. A stale `epiphany-openai-runtime` binary already proved it can otherwise +impersonate a Modeling failure after the producer adds a schema. Keep the new +typed route owner and reviewed generation transition intact; do not repair +stale consumers by mutating requests or findings. diff --git a/notes/fresh-workspace-handoff.md b/notes/fresh-workspace-handoff.md index ca39e2dd..85c3a6ef 100644 --- a/notes/fresh-workspace-handoff.md +++ b/notes/fresh-workspace-handoff.md @@ -2097,6 +2097,24 @@ admission, and overview gate `awaiting-publication` with `bifrost-publication-missing`. Next design the typed Mind-reviewed generation pointer for a non-passing immutable finding; do not mutate the old request or let a filesystem close artifact become the new routing authority. +That typed owner is now implemented. `RepoWorkModelingRoute` is registered in +runtime CultCache/CultNet. Generation zero is atomically committed with the +Soul-backed request. `revise-modeling` requires explicit review reference and +rationale, reads the current non-passing finding, creates a Mind acceptance +whose only allowed effect is `repoWork.modelingRoute`, and atomically commits +the next request, review, and stable route pointer. The transition preserves +item, Soul receipt, commit, and changed paths; passing findings cannot retry; +stale repeat advancement is refused; prior requests/findings remain readable. +Scheduler job IDs are generation-scoped and scheduler reads the stable typed +route rather than `work-close-*.json` request cargo. Authority tests pass 4/4, +core library 248/248, and model runtime 8/8. Authenticated live item +`typed-modeling-route-v2` proved generation zero through Idunn, model-authored +finding, Mind/map admission, and closure. The preceding +`typed-modeling-route` probe correctly failed because the already-built model +runtime lacked the new route schema; rebuilding the consumer made v2 pass. +Next expose executable build/schema fingerprint in the Idunn lifecycle receipt +and refuse launch before work when the consumer cannot register required typed +documents. ## Immediate Re-entry Instruction diff --git a/state/ledgers.msgpack b/state/ledgers.msgpack index 0d057a219ccc6180dc9c99590303e757d39f42a8..d42d91a2ab2810508c35845a076a3085ab6dd073 100644 GIT binary patch delta 579 zcmZWmy-wUf5RM%gHW^ z9z{>z7V-B*9C5VwOO7>;_1i_Po>I*)(zxTmdZPrDPO<= z1)XFfpE8!k^Dsx}cVJH$nOLk9&boT79M5+DPXU7$zH8*ZJ|}HUAB2%mkun}FkYP-5 z(xw6onWPfVv66q};e^53_pDGF=TKR6XzT6=V=^Z+IZNh|q$MzdYzrwIAsfIAp4B8$|F0S>+oT55|3V#UhNxmy^M0JYgllcvT`y z29p$aRgJ5CB<0^dBppstk096Nc=k%6#Vjb@$q_B1c1J8Cl*}?yM%V`MqUsv527H06 L-a5~sDZ}&+h=0-w delta 57 zcmX?nUvlnk$qf>W+=dobCKgf07}#>EHY+n0GBUnsp32ldm5C9EK{PWEvj8#c_Nh#4 G9N7Rdni2E> diff --git a/state/map.yaml b/state/map.yaml index de11eaa1..dd013570 100644 --- a/state/map.yaml +++ b/state/map.yaml @@ -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: Design and implement a typed Mind-reviewed request-generation pointer for non-passing immutable repo-work Modeling findings. Idunn now owns detached worker launch through the existing daemon service lifecycle receipt mouth. Preserve each prior request/finding as immutable evidence; filesystem closure artifacts and Self must not become mutable routing authorities. + next_action: Expose the launched worker executable build/schema fingerprint in Idunn's lifecycle receipt and preflight required runtime document registration before launch. The first typed-route live probe found a stale epiphany-openai-runtime binary; after rebuild, generation-zero item typed-modeling-route-v2 closed successfully. Preserve the typed route owner and Mind-reviewed generation transition; do not mutate requests/findings to compensate for stale consumers.