Supersede stale resident continuations
This commit is contained in:
parent
fbd57b9b66
commit
3a5d2b5ee2
4 changed files with 216 additions and 22 deletions
|
|
@ -658,7 +658,7 @@ fn resident_coordinator_binding_for_grant(
|
|||
resident_store: &Path,
|
||||
policy: &ResidentSelfPolicy,
|
||||
grant: &ResidentSelfHeartbeatGrant,
|
||||
) -> Result<ResidentCoordinatorBinding> {
|
||||
) -> Result<Option<ResidentCoordinatorBinding>> {
|
||||
let mut cache = crate::runtime_spine_cache(&policy.runtime_store)?;
|
||||
cache.pull_all_backing_stores()?;
|
||||
let runtime_identity = cache
|
||||
|
|
@ -684,14 +684,16 @@ fn resident_coordinator_binding_for_grant(
|
|||
&policy.runtime_store,
|
||||
&receipt,
|
||||
)?
|
||||
}
|
||||
.ok_or_else(|| anyhow!("coordinator receipt no longer owns a safe continuation"))?;
|
||||
return Ok(ResidentCoordinatorBinding {
|
||||
};
|
||||
let Some(required_action) = required_action else {
|
||||
return Ok(None);
|
||||
};
|
||||
return Ok(Some(ResidentCoordinatorBinding {
|
||||
runtime_id: runtime_identity.runtime_id,
|
||||
thread_id: receipt.thread_id,
|
||||
typed_request: None,
|
||||
required_action: Some(required_action),
|
||||
});
|
||||
}));
|
||||
}
|
||||
let (prefix, request_flag) = match grant.pressure_kind.as_str() {
|
||||
"imagination-consideration" => (
|
||||
|
|
@ -707,12 +709,12 @@ fn resident_coordinator_binding_for_grant(
|
|||
"--proposal-modeling-request-id",
|
||||
),
|
||||
_ => {
|
||||
return Ok(ResidentCoordinatorBinding {
|
||||
return Ok(Some(ResidentCoordinatorBinding {
|
||||
thread_id: resident_coordinator_thread_id(&runtime_identity.runtime_id)?,
|
||||
runtime_id: runtime_identity.runtime_id,
|
||||
typed_request: None,
|
||||
required_action: None,
|
||||
});
|
||||
}));
|
||||
}
|
||||
};
|
||||
let request_id = grant
|
||||
|
|
@ -748,12 +750,12 @@ fn resident_coordinator_binding_for_grant(
|
|||
runtime_identity.runtime_id,
|
||||
));
|
||||
}
|
||||
Ok(ResidentCoordinatorBinding {
|
||||
Ok(Some(ResidentCoordinatorBinding {
|
||||
runtime_id: runtime_identity.runtime_id,
|
||||
thread_id,
|
||||
typed_request: Some((request_flag.into(), request_id.into())),
|
||||
required_action: None,
|
||||
})
|
||||
}))
|
||||
}
|
||||
|
||||
fn prepared_coordinator_thread_id(argv: &[String]) -> Result<String> {
|
||||
|
|
@ -1193,6 +1195,30 @@ fn resident_self_safe_continuation_action(
|
|||
| "commitFrontierPlanDecision"
|
||||
);
|
||||
if direct {
|
||||
let overtaken_by_terminal_result = match receipt.final_action.as_str() {
|
||||
"launchResearch" => role_result_is_terminal_for_continuation(
|
||||
runtime_store,
|
||||
&receipt.thread_id,
|
||||
crate::EpiphanyRoleResultRoleId::Research,
|
||||
crate::EPIPHANY_RESEARCH_ROLE_BINDING_ID,
|
||||
)?,
|
||||
"launchModeling" => role_result_is_terminal_for_continuation(
|
||||
runtime_store,
|
||||
&receipt.thread_id,
|
||||
crate::EpiphanyRoleResultRoleId::Modeling,
|
||||
crate::EPIPHANY_MODELING_ROLE_BINDING_ID,
|
||||
)?,
|
||||
"launchVerification" => role_result_is_terminal_for_continuation(
|
||||
runtime_store,
|
||||
&receipt.thread_id,
|
||||
crate::EpiphanyRoleResultRoleId::Verification,
|
||||
crate::EPIPHANY_VERIFICATION_ROLE_BINDING_ID,
|
||||
)?,
|
||||
_ => false,
|
||||
};
|
||||
if overtaken_by_terminal_result {
|
||||
return Ok(None);
|
||||
}
|
||||
return Ok(Some(receipt.final_action.clone()));
|
||||
}
|
||||
let action = match receipt.final_action.as_str() {
|
||||
|
|
@ -2270,6 +2296,109 @@ pub fn resident_self_policy_digest(policy: &ResidentSelfPolicy) -> String {
|
|||
])
|
||||
}
|
||||
|
||||
fn supersede_unlaunched_resident_self_continuation(
|
||||
path: &Path,
|
||||
grant: &ResidentSelfHeartbeatGrant,
|
||||
now_millis: u64,
|
||||
) -> Result<ResidentSelfTerminalAck> {
|
||||
if grant.pressure_kind != RESIDENT_SELF_COORDINATOR_CONTINUATION_PRESSURE_KIND
|
||||
|| grant.consumed_at_millis.is_some()
|
||||
|| grant.terminal_at_millis.is_some()
|
||||
{
|
||||
return Err(anyhow!(
|
||||
"only an exact unlaunched coordinator continuation grant may be superseded"
|
||||
));
|
||||
}
|
||||
let receipt_id = grant
|
||||
.provenance_ref
|
||||
.strip_prefix(RESIDENT_SELF_COORDINATOR_CONTINUATION_PROVENANCE_PREFIX)
|
||||
.filter(|value| !value.trim().is_empty())
|
||||
.ok_or_else(|| anyhow!("superseded continuation lost receipt provenance"))?;
|
||||
let cache = state_cache(path)?;
|
||||
let mut state = cache
|
||||
.get::<ResidentSelfState>(RESIDENT_SELF_STATE_KEY)?
|
||||
.ok_or_else(|| anyhow!("resident Self state missing while superseding continuation"))?;
|
||||
if state.active_turn.is_some() || state.prepared_launch.is_some() {
|
||||
return Err(anyhow!(
|
||||
"resident Self cannot supersede continuation while launch authority is active"
|
||||
));
|
||||
}
|
||||
let mut current_grant = cache
|
||||
.get::<ResidentSelfHeartbeatGrant>(&grant.grant_id)?
|
||||
.ok_or_else(|| anyhow!("superseded continuation grant is missing"))?;
|
||||
if current_grant != *grant {
|
||||
return Err(anyhow!("superseded continuation grant authority changed"));
|
||||
}
|
||||
let pressure = cache
|
||||
.get::<ResidentSelfPressure>(&grant.pressure_id)?
|
||||
.ok_or_else(|| anyhow!("superseded continuation pressure is missing"))?;
|
||||
if pressure.status != "consumed"
|
||||
|| pressure.consumed_by_grant_id.as_deref() != Some(grant.grant_id.as_str())
|
||||
{
|
||||
return Err(anyhow!(
|
||||
"superseded continuation pressure no longer belongs to its exact grant"
|
||||
));
|
||||
}
|
||||
let launch_digest = digest_parts([
|
||||
"resident-self-unlaunched-superseded",
|
||||
grant.grant_id.as_str(),
|
||||
receipt_id,
|
||||
]);
|
||||
let ack = ResidentSelfTerminalAck {
|
||||
schema_version: RESIDENT_SELF_ACK_SCHEMA_VERSION.into(),
|
||||
ack_id: format!("resident-self-superseded-{}", grant.grant_id),
|
||||
grant_id: grant.grant_id.clone(),
|
||||
heartbeat_schedule_id: grant.heartbeat_schedule_id.clone(),
|
||||
heartbeat_action_id: grant.heartbeat_action_id.clone(),
|
||||
launch_digest,
|
||||
coordinator_receipt_id: receipt_id.into(),
|
||||
terminal_status: "superseded".into(),
|
||||
completed_at_millis: now_millis,
|
||||
consumed_by_heartbeat_at_millis: None,
|
||||
private_state_exposed: false,
|
||||
};
|
||||
let envelopes = cache.snapshot_envelopes();
|
||||
let expected_state = envelopes
|
||||
.iter()
|
||||
.find(|entry| {
|
||||
entry.r#type == <ResidentSelfState as DatabaseEntry>::TYPE
|
||||
&& entry.key == RESIDENT_SELF_STATE_KEY
|
||||
})
|
||||
.cloned()
|
||||
.ok_or_else(|| anyhow!("superseded continuation state envelope is missing"))?;
|
||||
let expected_pressure = envelopes
|
||||
.iter()
|
||||
.find(|entry| {
|
||||
entry.r#type == <ResidentSelfPressure as DatabaseEntry>::TYPE
|
||||
&& entry.key == pressure.pressure_id
|
||||
})
|
||||
.cloned()
|
||||
.ok_or_else(|| anyhow!("superseded continuation pressure envelope is missing"))?;
|
||||
let expected_grant = envelopes
|
||||
.iter()
|
||||
.find(|entry| {
|
||||
entry.r#type == <ResidentSelfHeartbeatGrant as DatabaseEntry>::TYPE
|
||||
&& entry.key == grant.grant_id
|
||||
})
|
||||
.cloned()
|
||||
.ok_or_else(|| anyhow!("superseded continuation grant envelope is missing"))?;
|
||||
state.revision = state.revision.saturating_add(1);
|
||||
current_grant.consumed_at_millis = Some(now_millis);
|
||||
current_grant.terminal_at_millis = Some(now_millis);
|
||||
current_grant.terminal_status = Some("superseded".into());
|
||||
let (state_entry, _) = cache.prepare_entry(RESIDENT_SELF_STATE_KEY, &state)?;
|
||||
let (pressure_entry, _) = cache.prepare_entry(&pressure.pressure_id, &pressure)?;
|
||||
let (grant_entry, _) = cache.prepare_entry(¤t_grant.grant_id, ¤t_grant)?;
|
||||
let (ack_entry, _) = cache.prepare_entry(&ack.ack_id, &ack)?;
|
||||
if !SingleFileMessagePackBackingStore::new(path).compare_and_swap_batch(
|
||||
&[expected_state, expected_pressure, expected_grant],
|
||||
vec![state_entry, pressure_entry, grant_entry, ack_entry],
|
||||
)? {
|
||||
return Err(anyhow!("resident Self lost superseded-continuation CAS"));
|
||||
}
|
||||
Ok(ack)
|
||||
}
|
||||
|
||||
pub fn prepare_resident_self_launch(
|
||||
path: &Path,
|
||||
policy: &ResidentSelfPolicy,
|
||||
|
|
@ -2289,7 +2418,10 @@ pub fn prepare_resident_self_launch(
|
|||
let Some(mut grant) = pending_resident_self_grant(path)? else {
|
||||
return Ok(None);
|
||||
};
|
||||
let binding = resident_coordinator_binding_for_grant(path, policy, &grant)?;
|
||||
let Some(binding) = resident_coordinator_binding_for_grant(path, policy, &grant)? else {
|
||||
supersede_unlaunched_resident_self_continuation(path, &grant, now_millis)?;
|
||||
return Ok(None);
|
||||
};
|
||||
let wake = ResidentSelfWake::Explicit {
|
||||
objective: grant.objective.clone(),
|
||||
};
|
||||
|
|
@ -5502,15 +5634,65 @@ mod tests {
|
|||
})?;
|
||||
runtime.put(job_id, &role_worker_result(job_id, "modeling"))?;
|
||||
|
||||
let mut stale_receipt = receipt.clone();
|
||||
stale_receipt.receipt_id = "receipt-stale-launch-modeling".into();
|
||||
stale_receipt.status = "planned".into();
|
||||
stale_receipt.final_action = "launchModeling".into();
|
||||
runtime.put(&stale_receipt.receipt_id, &stale_receipt)?;
|
||||
enqueue_resident_self_pressure(
|
||||
&resident_store,
|
||||
&ResidentSelfPressure {
|
||||
schema_version: RESIDENT_SELF_PRESSURE_SCHEMA_VERSION.into(),
|
||||
pressure_id: "pressure-stale-launch-modeling".into(),
|
||||
kind: RESIDENT_SELF_COORDINATOR_CONTINUATION_PRESSURE_KIND.into(),
|
||||
provenance_ref: format!(
|
||||
"{RESIDENT_SELF_COORDINATOR_CONTINUATION_PROVENANCE_PREFIX}{}",
|
||||
stale_receipt.receipt_id
|
||||
),
|
||||
objective: "Stale launch must not outrank terminal Modeling evidence.".into(),
|
||||
created_at_millis: 2,
|
||||
status: "pending".into(),
|
||||
consumed_by_grant_id: None,
|
||||
private_state_exposed: false,
|
||||
},
|
||||
)?;
|
||||
let stale_grant = heartbeat_issue_resident_self_grant(
|
||||
&resident_store,
|
||||
"stale-schedule",
|
||||
"stale-action",
|
||||
3,
|
||||
)?
|
||||
.expect("stale continuation grant");
|
||||
let coordinator = temp.path().join("epiphany-mvp-coordinator");
|
||||
std::fs::write(&coordinator, b"witnessed executable")?;
|
||||
let mut policy = policy();
|
||||
policy.coordinator_bin = coordinator;
|
||||
policy.runtime_store = runtime_store.clone();
|
||||
policy.max_steps = 1;
|
||||
assert!(prepare_resident_self_launch(&resident_store, &policy, 4)?.is_none());
|
||||
let terminal = resident_self_grant_lifecycle_projection(
|
||||
&resident_store,
|
||||
Some(&stale_grant.grant_id),
|
||||
1,
|
||||
)?
|
||||
.pop()
|
||||
.expect("superseded stale continuation");
|
||||
assert_eq!(terminal.terminal_status.as_deref(), Some("superseded"));
|
||||
let stale_ack = pending_resident_self_acks(&resident_store)?
|
||||
.pop()
|
||||
.expect("superseded stale continuation acknowledgement");
|
||||
assert_eq!(stale_ack.coordinator_receipt_id, stale_receipt.receipt_id);
|
||||
heartbeat_consume_resident_self_ack(&resident_store, &stale_ack.ack_id, 5)?;
|
||||
|
||||
assert!(ingest_resident_self_coordinator_continuation_pressure(
|
||||
&resident_store,
|
||||
&runtime_store,
|
||||
2,
|
||||
6,
|
||||
)?);
|
||||
assert!(!ingest_resident_self_coordinator_continuation_pressure(
|
||||
&resident_store,
|
||||
&runtime_store,
|
||||
3,
|
||||
7,
|
||||
)?);
|
||||
let pressure = resident_self_pressures(&resident_store)?
|
||||
.into_iter()
|
||||
|
|
@ -5520,15 +5702,9 @@ mod tests {
|
|||
pressure.kind,
|
||||
RESIDENT_SELF_COORDINATOR_CONTINUATION_PRESSURE_KIND
|
||||
);
|
||||
heartbeat_issue_resident_self_grant(&resident_store, "schedule", "action", 4)?
|
||||
heartbeat_issue_resident_self_grant(&resident_store, "schedule", "action", 8)?
|
||||
.expect("one continuation grant");
|
||||
let coordinator = temp.path().join("epiphany-mvp-coordinator");
|
||||
std::fs::write(&coordinator, b"witnessed executable")?;
|
||||
let mut policy = policy();
|
||||
policy.coordinator_bin = coordinator;
|
||||
policy.runtime_store = runtime_store;
|
||||
policy.max_steps = 1;
|
||||
let prepared = prepare_resident_self_launch(&resident_store, &policy, 5)?
|
||||
let prepared = prepare_resident_self_launch(&resident_store, &policy, 9)?
|
||||
.expect("continuation preparation");
|
||||
assert_eq!(resident_prepared_launch_thread_id(&prepared)?, "thread-1");
|
||||
assert!(prepared.argv.windows(2).any(|pair| pair == ["--mode", "execute"]));
|
||||
|
|
|
|||
|
|
@ -1432,6 +1432,15 @@ unset until acceptance and is not pre-acceptance evidence. The failed process re
|
|||
the typed result owns cognitive review readiness. This route cannot launch a
|
||||
second worker or continue into Hands.
|
||||
|
||||
Receipt-derived launch actions are revalidated at preparation. A terminal role
|
||||
result overtakes an older `launchResearch`, `launchModeling`, or
|
||||
`launchVerification` continuation before process creation. Resident Self then
|
||||
atomically marks the exact unlaunched grant `superseded`, leaves its consumed
|
||||
pressure closed, and emits one Heartbeat acknowledgement bound to the
|
||||
originating receipt. The stale receipt remains evidence but no longer owns a
|
||||
launch; Heartbeat may then schedule the current typed review pressure. This is
|
||||
grant-owned closure, not deletion or retry repair.
|
||||
|
||||
Exact packaged `6dd9b132` proves that ownership cut live. One autonomous
|
||||
proposal Modeling coordinator exited while its detached worker continued; Self
|
||||
held the same lease as `awaiting-fulfillment`. Runtime later admitted one exact
|
||||
|
|
|
|||
|
|
@ -3730,3 +3730,12 @@ receipts are `.epiphany-run/live-swap-c007f-2e1ccaf7`. The corrected source
|
|||
requires the exact Modeling job link plus runtime-owned typed fulfillment; its
|
||||
focused lifecycle test now explicitly proves recovery while link result ID is
|
||||
absent. Package/restart/live replay of this correction is next.
|
||||
|
||||
Restarting exact fbd57b9b braked exposed a separately preserved grant from the
|
||||
older `launchModeling` continuation. The active source cut now revalidates
|
||||
receipt-derived launches at preparation: terminal Modeling evidence overtakes
|
||||
the stale launch, and one snapshot-fenced transaction terminalizes the exact
|
||||
unlaunched grant as `superseded`, preserves the consumed pressure, and emits a
|
||||
receipt-bound Heartbeat acknowledgement. The focused continuation test proves
|
||||
supersession and then proves the newer `reviewModelingResult` continuation can
|
||||
prepare normally. Commit/package/live replay remain next.
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ invariants:
|
|||
|
||||
current_status:
|
||||
summary: Epiphany is a supervised engineering alpha on Starfire, not deployment-ready. Exact b8138a6b active-child SIGTERM and restart, exact 9f369528 deterministic warm packaging, exact c930f505 cross-container ownership, exact ed15f83a resident lifecycle retention, exact 16c4b19f coordinator receipt and Heartbeat artifact retention, exact cce2b8ac narrow model-generation retention, exact eea15e82 model-turn JSON ingress, exact b1e8c89a semantic unattempted-generation retention, exact 6bc3ec3f semantic physical-epoch retirement, and exact 44230a05 Persona conversation retention are proven. Gamecult-ops 059821f owns Yggdrasil stream retention and exact 6ee08a4 owns stable Starfire Persona-permit firewall ingress. Exact 16c4b19f genuine idle and exact a987d2f7 active cognition completed 900-pulse bounded plateaus. Runtime-owned typed fulfillment is accepted live under exact 6dd9b132. Yggdrasil is the small public crossing body, never the release forge.
|
||||
latest_phase: Exact 2e1ccaf7 recovers internal typed review after a failed coordinator receipt and is pushed, packaged, inspected, and published as release sha256-243899f155e06e043de4c0d0ec7c6a0ab80296a6deb7ba83229b032555b06f29 with witness sha256-c7b8dc0399455173f1856bda9cbbe6353f58f4890c7d3f8dc89606921865cd70. Changed Cargo was 1m24s and identical warm replay 0.88s. c007e stopped both owners exit0; c007f started exact/authenticated/braked at revision 302. Live release falsified the cut's test-only assumption that a pre-acceptance Modeling runtime link already carries runtime_result_id: that field is written only during acceptance, so resident failed closed exit1 before consequence. The cognitive brake was immediately reengaged and Heartbeat remains alive. The corrected source requires the immutable exact Modeling job link plus runtime-owned typed fulfillment while explicitly proving result ID remains absent before review. This correction is tested but uncommitted/unpackaged. Yggdrasil never builds.
|
||||
latest_phase: Exact fbd57b9b removes the circular pre-acceptance result-link requirement, is pushed, packaged as release sha256-5c147465362db6b60d70217f2656aaa62a77326ec4d3e565e236457909c95968 with witness sha256-a4a20b63f6e7898b861c49fa302ffa3ca24f2a5f18359674c408f0ed03caee61, and runs as c007g braked at revision 303. Changed Cargo was 24.76s and identical warm replay 0.81s. Live state preserves an exact unlaunched grant from an older launchModeling continuation even though terminal Modeling evidence now derives reviewModelingResult. The active source cut makes preparation revalidate launch continuations; terminal role evidence atomically supersedes the stale exact grant and acknowledges it without process launch or pressure resurrection. The focused continuation test proves stale-grant closure followed by preparation of the current review route. This cut is uncommitted/unpackaged. Yggdrasil never builds.
|
||||
operational_state:
|
||||
local_worktree: exact_acd91e6e_imagination_runtime_owned_causality_live_accepted
|
||||
operator_wireguard: restored
|
||||
|
|
@ -70,7 +70,7 @@ current_status:
|
|||
yggdrasil_epiphany_service_contract: repaired_in_gamecult_ops_812b427_not_installed
|
||||
resident_physiology: c006_exact_c9a2a979_stopped_cleanly_braked_revision_34_after_36700_pulse_endurance
|
||||
unresolved_authority: Coordinator receipt, Heartbeat pulse-artifact, genuine-idle and active resource plateaus, exact-grant terminality, restart-persistent cooldown refusal, schedule/action issuance cardinality, runtime-owned typed fulfillment, all three packaged fulfilled-before-receipt lanes, genuine packaged Imagination option generation, narrow model-generation retention, published model-turn JSON ingress, semantic logical/physical retention, Persona conversation retention, stable Starfire firewall ownership, completed model-adapter session archival, normal coordinator lifecycle opening/error closure/legacy repair/archival, co-resident resident lifecycle retention, authenticated outer-worker attempt retention, deployed supervisor/projector journald ownership, Docker named-volume atomic snapshot visibility, and deterministic incremental release construction are accepted. Starfire's Docker Desktop F: bind is rejected for mutable authority after an exact packaged lockless reader observed an empty published snapshot during writer interruption. Historical migrated semantic attempts without recorded physical namespace remain durable and non-retirable. One completed Discord consequence remains blocked by the absent Yggdrasil credential. Exact aa841873 also makes legacy coordinator repair one full-snapshot transaction so a later hostile family cannot leave an earlier partial repair.
|
||||
next_action: Keep c007f's cognitive brake engaged; Heartbeat is live and resident exact 2e1ccaf7 is stopped after fail-closed exit1. Commit/push and package the corrected pre-acceptance job-link authority, then restart resident/Heartbeat only onto the authenticated body. Release one grant and prove result-worker-8ddf57c1-96b4-4a60-ae8b-b24404e9ce2f is accepted exactly once without worker relaunch or Hands/public consequence. Inspect the stale launchModeling continuation before allowing it to run; reengage the brake after the boundary. Yggdrasil never builds.
|
||||
next_action: Keep exact fbd57b9b c007g braked at revision 303. Commit/push/package the stale-continuation supersession cut and restart only onto its authenticated body. Release the brake and prove the preserved launchModeling grant is acknowledged as superseded without process launch, then prove result-worker-8ddf57c1-96b4-4a60-ae8b-b24404e9ce2f is accepted exactly once without worker relaunch or Hands/public consequence. Reengage the brake and inspect the next typed action. Yggdrasil never builds.
|
||||
|
||||
active_subgoals:
|
||||
- Prove Persona, Self, Eyes, Modeling, Imagination, Hands, Soul, Mind, Continuity, and nervous-system behavior individually.
|
||||
|
|
|
|||
Reference in a new issue