Add typed Modeling request generations

This commit is contained in:
Meta Vens 2026-07-12 11:39:11 +01:00
commit abce2aaf0a
9 changed files with 629 additions and 43 deletions

View file

@ -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<PathBuf>,
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<Item = String>) -> Result<CloseArgs> {
})
}
fn parse_revise_modeling_args(args: impl Iterator<Item = String>) -> Result<ReviseModelingArgs> {
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<Item = String>) -> Result<PublishArgs> {
let mut workspace = None;
let mut epiphany_root = None;
@ -11168,6 +11210,118 @@ fn run_verify(args: CloseArgs) -> Result<Value> {
run_closure_pipeline(args, ClosurePhase::SoulOnly)
}
fn run_revise_modeling(args: ReviseModelingArgs) -> Result<Value> {
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, &current.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<Value> {
run_closure_pipeline(args, ClosurePhase::Full)
}
@ -11490,33 +11644,61 @@ fn run_closure_pipeline(args: CloseArgs, phase: ClosurePhase) -> Result<Value> {
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<Value> {
"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<Value> {
}))
}
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<PathBuf> {
@ -14239,8 +14423,9 @@ fn launch_repo_work_modeling_worker(
local_verse_store: &Path,
request: &RepoWorkModelingRequest,
item_slug: &str,
generation: u64,
) -> Result<Value> {
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<Value> {
.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<Value> {
.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<Value> {
&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 {

View file

@ -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;

View file

@ -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 {

View file

@ -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<Path>) -> Result<CultCache> {
cache.register_entry_type::<MindStateCommitReceipt>()?;
cache.register_entry_type::<RepoWorkModelingFinding>()?;
cache.register_entry_type::<RepoWorkModelingRequest>()?;
cache.register_entry_type::<RepoWorkModelingRoute>()?;
cache.register_entry_type::<RepoWorkMapEntry>()?;
cache.register_entry_type::<EyesEvidencePacket>()?;
cache.register_entry_type::<SubstrateGateRepoAccessGrantReceipt>()?;
@ -1849,6 +1854,183 @@ pub fn runtime_repo_work_modeling_request(
cache.get::<RepoWorkModelingRequest>(request_id)
}
pub fn commit_initial_repo_work_modeling_route(
store_path: impl AsRef<Path>,
request: &RepoWorkModelingRequest,
route: &RepoWorkModelingRoute,
) -> Result<RepoWorkModelingRoute> {
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::<SoulVerdictReceipt>(&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::<RepoWorkModelingRoute>(&route.route_id)? {
let stored_request = cache
.get::<RepoWorkModelingRequest>(&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::<RepoWorkModelingRequest>(&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<Path>,
route_id: &str,
) -> Result<Option<RepoWorkModelingRoute>> {
validate_non_empty(route_id, "Modeling route id")?;
let mut cache = runtime_spine_cache(store_path)?;
cache.pull_all_backing_stores()?;
cache.get::<RepoWorkModelingRoute>(route_id)
}
pub fn advance_repo_work_modeling_route(
store_path: impl AsRef<Path>,
request: &RepoWorkModelingRequest,
route: &RepoWorkModelingRoute,
mind_review: &MindGatewayReview,
) -> Result<RepoWorkModelingRoute> {
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::<RepoWorkModelingRoute>(&route.route_id)?
.ok_or_else(|| anyhow!("cannot advance a missing Modeling route"))?;
let current_request = cache
.get::<RepoWorkModelingRequest>(&current.request_id)?
.ok_or_else(|| anyhow!("current Modeling route lost its request"))?;
let previous_finding = cache
.get::<RepoWorkModelingFinding>(&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::<RepoWorkModelingRequest>(&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<Path>,
receipt_id: &str,
@ -2560,6 +2742,23 @@ fn epiphany_mutation_contracts() -> Vec<CultNetDocumentMutationContract> {
"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,

View file

@ -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,

View file

@ -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.

View file

@ -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

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: 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.