Unify coordinator work projection

This commit is contained in:
Meta Vens 2026-08-25 00:59:40 +02:00
commit 2bcbf2687d
9 changed files with 41 additions and 53 deletions

View file

@ -1,7 +1,7 @@
use anyhow::{Context, Result};
use crate::{
EpiphanyCoordinatorInput, EpiphanyCrrcAction, EpiphanyCurrentWorkProjection,
EpiphanyRoleBoardInput, RepoFrontierPlanningLifecycle, RepoFrontierPlanningLifecycleStage,
EpiphanyCoordinatorInput, EpiphanyCurrentWorkProjection, EpiphanyRoleBoardInput,
RepoFrontierPlanningLifecycle, RepoFrontierPlanningLifecycleStage,
RepoFrontierResearchLifecycle, RepoFrontierResearchLifecycleStage, derive_role_board,
};
use serde_json::{Value, json};
@ -23,38 +23,25 @@ const MAX_OPERATOR_TEXT_CHARS: usize = 1200;
pub fn native_coordinator_json(runtime_store: &Path, thread_id: &str) -> Result<Value> {
let store_path = absolute_path(runtime_store)?;
let mind = if store_path.exists() {
Some(
crate::assemble_mind_view(&store_path).with_context(|| {
format!("failed to assemble keyed Mind {}", store_path.display())
})?,
)
} else {
None
};
let current_work = if mind.is_some() {
crate::project_current_work(&store_path)
.context("failed to derive current work from keyed Mind and runtime receipts")?
} else {
empty_current_work()
};
let latest_reorientation_decision = mind
let projected_work = store_path
.exists()
.then(|| {
crate::project_current_work(&store_path)
.context("failed to derive current work from keyed Mind and runtime receipts")
})
.transpose()?;
let mind_present = projected_work.is_some();
let projection_digest = projected_work
.as_ref()
.and_then(|mind| mind.reorientation_decisions.last());
let crrc_action =
if latest_reorientation_decision.is_some_and(|decision| decision.mode == "regather") {
EpiphanyCrrcAction::RegatherManually
} else {
EpiphanyCrrcAction::Continue
};
.map(|work| work.mind_projection_digest.clone());
let current_work = projected_work.unwrap_or_else(empty_current_work);
let roles = derive_role_board(EpiphanyRoleBoardInput {
mind_present: mind.is_some(),
mind_present,
current_work: current_work.clone(),
});
let coordinator = crate::recommend_coordinator_action(EpiphanyCoordinatorInput {
mind_present: mind.is_some(),
crrc_action,
mind_present,
current_work: current_work.clone(),
});
Ok(sanitize_for_operator(json!({
@ -62,8 +49,8 @@ pub fn native_coordinator_json(runtime_store: &Path, thread_id: &str) -> Result<
"read": {
"source": "native",
"mindStore": store_path,
"mindPresent": mind.is_some(),
"projectionDigest": mind.as_ref().map(|mind| mind.projection_digest.as_str()),
"mindPresent": mind_present,
"projectionDigest": projection_digest,
},
"roles": {"threadId": thread_id, "source": "native", "roles": roles},
"currentWork": current_work,
@ -74,6 +61,7 @@ pub fn native_coordinator_json(runtime_store: &Path, thread_id: &str) -> Result<
fn empty_current_work() -> EpiphanyCurrentWorkProjection {
EpiphanyCurrentWorkProjection {
mind_projection_digest: String::new(),
operator_regather_required: false,
body_modeling: None,
research: RepoFrontierResearchLifecycle {
stage: RepoFrontierResearchLifecycleStage::Terminal,

View file

@ -48,6 +48,7 @@ pub enum EpiphanyAgentPassAdmissionRefusalKind {
#[serde(rename_all = "camelCase")]
pub struct EpiphanyCurrentWorkProjection {
pub mind_projection_digest: String,
pub operator_regather_required: bool,
pub body_modeling: Option<EpiphanyBodyModelingCurrentWorkProjection>,
pub research: crate::RepoFrontierResearchLifecycle,
pub frontier_planning: crate::RepoFrontierPlanningLifecycle,
@ -519,8 +520,13 @@ pub fn project_current_work(store_path: impl AsRef<Path>) -> Result<EpiphanyCurr
} else {
(Vec::new(), None)
};
let operator_regather_required = mind
.reorientation_decisions
.last()
.is_some_and(|decision| decision.mode == "regather");
Ok(EpiphanyCurrentWorkProjection {
mind_projection_digest: mind.projection_digest,
operator_regather_required,
body_modeling,
research: crate::runtime_spine::repo_frontier_research_lifecycle(&cache)?,
frontier_planning: crate::runtime_spine::repo_frontier_planning_lifecycle(&cache)?,

View file

@ -298,7 +298,6 @@ pub use surfaces::EpiphanyCoordinatorAction;
pub use surfaces::EpiphanyCoordinatorDecision;
pub use surfaces::EpiphanyCoordinatorInput;
pub use surfaces::EpiphanyCoordinatorRoleStatus;
pub use surfaces::EpiphanyCrrcAction;
pub use surfaces::EpiphanyCrrcResultStatus;
pub use surfaces::EpiphanyReorientFindingInterpretation;
pub use surfaces::EpiphanyReorientWorkerLaunchDocument;

View file

@ -986,10 +986,10 @@ mod tests {
let result = crate::EpiphanyRuntimeReorientWorkerResult {
result_id: "reorientation-result".into(),
job_id: job_id.clone(),
mode: "resume".into(),
summary: "The sealed checkpoint remains coherent.".into(),
next_safe_move: "Continue the keyed migration.".into(),
checkpoint_still_valid: Some(true),
mode: "regather".into(),
summary: "The sealed checkpoint requires fresh external evidence.".into(),
next_safe_move: "Regather before resuming the keyed migration.".into(),
checkpoint_still_valid: Some(false),
files_inspected: Vec::new(),
frontier_node_ids: Vec::new(),
evidence_ids: Vec::new(),
@ -1028,7 +1028,17 @@ mod tests {
accept_reorientation_result(&store, &job_id, "2026-08-18T10:00:07Z")?,
receipt
);
assert!(crate::project_current_work(&store)?.reorientation.is_none());
let current_work = crate::project_current_work(&store)?;
assert!(current_work.reorientation.is_none());
assert!(current_work.operator_regather_required);
assert_eq!(
crate::recommend_coordinator_action(crate::EpiphanyCoordinatorInput {
mind_present: true,
current_work,
})
.action,
crate::EpiphanyCoordinatorAction::RegatherManually
);
let mut accepted = crate::runtime_spine_cache(&store)?;
accepted.pull_all_backing_stores()?;
assert!(

View file

@ -969,7 +969,6 @@ pub(crate) fn resident_self_current_work_action(
let projection_digest = current_work.projection_digest()?;
let decision = crate::recommend_coordinator_action(crate::EpiphanyCoordinatorInput {
mind_present: true,
crrc_action: crate::EpiphanyCrrcAction::Continue,
current_work,
});
if !matches!(

View file

@ -1,4 +1,3 @@
use super::EpiphanyCrrcAction;
use crate::EpiphanyCurrentWorkProjection;
use serde::{Deserialize, Serialize};
@ -46,7 +45,6 @@ pub enum EpiphanyCoordinatorAction {
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct EpiphanyCoordinatorInput {
pub mind_present: bool,
pub crrc_action: EpiphanyCrrcAction,
pub current_work: EpiphanyCurrentWorkProjection,
}

View file

@ -1,6 +1,6 @@
use crate::{
EpiphanyAgentPassContinuationAction, EpiphanyCoordinatorAction, EpiphanyCoordinatorDecision,
EpiphanyCoordinatorInput, EpiphanyCrrcAction, RepoFrontierPlanningLifecycleStage,
EpiphanyCoordinatorInput, RepoFrontierPlanningLifecycleStage,
RepoFrontierResearchContinuationAction,
};
@ -24,7 +24,7 @@ pub fn recommend_coordinator_action(
);
}
if input.crrc_action == EpiphanyCrrcAction::RegatherManually {
if input.current_work.operator_regather_required {
return decision(
EpiphanyCoordinatorAction::RegatherManually,
"The accepted continuity decision requires explicit operator regather.",

View file

@ -14,14 +14,3 @@ pub enum EpiphanyCrrcResultStatus {
Failed,
Cancelled,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum EpiphanyCrrcAction {
Continue,
LaunchReorientWorker,
WaitForReorientWorker,
ReviewReorientResult,
AcceptReorientResult,
RegatherManually,
}

View file

@ -8,7 +8,6 @@ mod worker_launch;
pub use coordinator_contract::*;
pub use coordinator_decision::recommend_coordinator_action;
pub use crrc::EpiphanyCrrcAction;
pub use crrc::EpiphanyCrrcResultStatus;
pub use research_decision::EpiphanyResearchDecision;
pub use role_board::EpiphanyRoleBoardInput;