Separate terminal observer lifecycle from coverage recovery
This commit is contained in:
parent
cb665dc636
commit
84e648eb37
4 changed files with 218 additions and 3 deletions
|
|
@ -2439,6 +2439,163 @@ fn reconcile_workspace_coverage_projector(
|
|||
)?;
|
||||
return Ok(());
|
||||
}
|
||||
let terminal_sight = match authenticate_current_workspace_coverage_terminal_sight(
|
||||
&args.store,
|
||||
&runtime_store,
|
||||
&args.runtime_id,
|
||||
host.entry(),
|
||||
) {
|
||||
Ok(sight) => sight,
|
||||
Err(error) => {
|
||||
println!(
|
||||
"{}",
|
||||
serde_json::to_string_pretty(&json!({
|
||||
"schemaVersion": "epiphany.workspace_coverage_reconcile.v0",
|
||||
"status": "terminal-observation-refused",
|
||||
"serviceId": policy.service_id,
|
||||
"reason": error.to_string(),
|
||||
"restarted": false,
|
||||
"privateStateExposed": false,
|
||||
}))?
|
||||
);
|
||||
return Ok(());
|
||||
}
|
||||
};
|
||||
if let Some(terminal) = terminal_sight {
|
||||
if terminal.launch_id != latest.launch_id {
|
||||
if let Err(error) = authenticate_workspace_coverage_replacement_lineage(
|
||||
&args.store,
|
||||
&args.runtime_id,
|
||||
&terminal.launch_id,
|
||||
&latest.launch_id,
|
||||
host.entry(),
|
||||
) {
|
||||
println!(
|
||||
"{}",
|
||||
serde_json::to_string_pretty(&json!({
|
||||
"schemaVersion": "epiphany.workspace_coverage_reconcile.v0",
|
||||
"status": "terminal-observation-refused",
|
||||
"serviceId": policy.service_id,
|
||||
"launchId": latest.launch_id,
|
||||
"reason": error.to_string(),
|
||||
"restarted": false,
|
||||
"privateStateExposed": false,
|
||||
}))?
|
||||
);
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
let observation = match observe_workspace_coverage_managed_process(
|
||||
&args.store,
|
||||
args.runtime_id.clone(),
|
||||
&latest.launch_id,
|
||||
host.entry(),
|
||||
) {
|
||||
Ok(observation) => observation,
|
||||
Err(error) => {
|
||||
println!(
|
||||
"{}",
|
||||
serde_json::to_string_pretty(&json!({
|
||||
"schemaVersion": "epiphany.workspace_coverage_reconcile.v0",
|
||||
"status": "terminal-observation-refused",
|
||||
"serviceId": policy.service_id,
|
||||
"launchId": latest.launch_id,
|
||||
"reason": error.to_string(),
|
||||
"restarted": false,
|
||||
"privateStateExposed": false,
|
||||
}))?
|
||||
);
|
||||
return Ok(());
|
||||
}
|
||||
};
|
||||
match observation {
|
||||
WorkspaceCoverageProcessLifecycleObservation::ExactAlive => {
|
||||
println!(
|
||||
"{}",
|
||||
serde_json::to_string_pretty(&json!({
|
||||
"schemaVersion": "epiphany.workspace_coverage_reconcile.v0",
|
||||
"status": "terminal-observer-alive",
|
||||
"serviceId": policy.service_id,
|
||||
"launchId": latest.launch_id,
|
||||
"terminalClaimId": terminal.claim_id,
|
||||
"restarted": false,
|
||||
"privateStateExposed": false,
|
||||
}))?
|
||||
);
|
||||
return Ok(());
|
||||
}
|
||||
WorkspaceCoverageProcessLifecycleObservation::Inaccessible
|
||||
| WorkspaceCoverageProcessLifecycleObservation::Indeterminate { .. } => {
|
||||
println!(
|
||||
"{}",
|
||||
serde_json::to_string_pretty(&json!({
|
||||
"schemaVersion": "epiphany.workspace_coverage_reconcile.v0",
|
||||
"status": "terminal-observation-degraded",
|
||||
"serviceId": policy.service_id,
|
||||
"launchId": latest.launch_id,
|
||||
"observation": format!("{observation:?}"),
|
||||
"restarted": false,
|
||||
"privateStateExposed": false,
|
||||
}))?
|
||||
);
|
||||
return Ok(());
|
||||
}
|
||||
WorkspaceCoverageProcessLifecycleObservation::BootSuperseded { .. }
|
||||
| WorkspaceCoverageProcessLifecycleObservation::ExactExited { .. }
|
||||
| WorkspaceCoverageProcessLifecycleObservation::Missing
|
||||
| WorkspaceCoverageProcessLifecycleObservation::Replaced { .. } => {}
|
||||
}
|
||||
if load_workspace_coverage_process_termination_observation(
|
||||
&args.store,
|
||||
args.runtime_id.clone(),
|
||||
&latest.launch_id,
|
||||
)?
|
||||
.is_none()
|
||||
{
|
||||
write_workspace_coverage_process_termination_observation(
|
||||
&args.store,
|
||||
args.runtime_id.clone(),
|
||||
&latest.launch_id,
|
||||
&host,
|
||||
)?;
|
||||
}
|
||||
let (termination, termination_digest) =
|
||||
authenticate_workspace_coverage_termination_with_envelope_digest(
|
||||
&args.store,
|
||||
args.runtime_id.clone(),
|
||||
&latest.launch_id,
|
||||
host.entry(),
|
||||
)?;
|
||||
args.service_command = Some(PathBuf::from(&policy.command));
|
||||
args.service_args = policy.args.clone();
|
||||
args.cwd = policy.cwd.clone().map(PathBuf::from);
|
||||
args.stdout_artifact = Some(PathBuf::from(policy.stdout_artifact.clone()));
|
||||
args.stderr_artifact = Some(PathBuf::from(policy.stderr_artifact.clone()));
|
||||
let launched = service_launch_internal(
|
||||
args,
|
||||
Some(CoverageReplacementEvidence {
|
||||
old_launch_id: latest.launch_id,
|
||||
termination_id: termination.termination_id,
|
||||
termination_envelope_digest: termination_digest,
|
||||
}),
|
||||
false,
|
||||
)?
|
||||
.coverage_launch
|
||||
.context("terminal workspace coverage observer restart returned no launch identity")?;
|
||||
println!(
|
||||
"{}",
|
||||
serde_json::to_string_pretty(&json!({
|
||||
"schemaVersion": "epiphany.workspace_coverage_reconcile.v0",
|
||||
"status": "terminal-observer-restarted",
|
||||
"serviceId": policy.service_id,
|
||||
"launchId": launched.launch_id,
|
||||
"terminalClaimId": terminal.claim_id,
|
||||
"restarted": true,
|
||||
"privateStateExposed": false,
|
||||
}))?
|
||||
);
|
||||
return Ok(());
|
||||
}
|
||||
let target = match authenticate_recovery_workspace_coverage_claim_sight(
|
||||
&args.store,
|
||||
&runtime_store,
|
||||
|
|
@ -4403,10 +4560,17 @@ mod semantic_projector_authority_tests {
|
|||
.find("load_latest_workspace_coverage_managed_process_launch")
|
||||
.unwrap();
|
||||
let stale_rotation = body.find("if !current_policy_matches_latest").unwrap();
|
||||
let terminal_sight = body
|
||||
.find("let terminal_sight = match authenticate_current_workspace_coverage_terminal_sight")
|
||||
.unwrap();
|
||||
let claim_sight = body
|
||||
.find("let target = match authenticate_recovery_workspace_coverage_claim_sight")
|
||||
.unwrap();
|
||||
assert!(latest < stale_rotation && stale_rotation < claim_sight);
|
||||
assert!(
|
||||
latest < stale_rotation
|
||||
&& stale_rotation < terminal_sight
|
||||
&& terminal_sight < claim_sight
|
||||
);
|
||||
let observation = body
|
||||
.rfind("let observation = match observe_workspace_coverage_managed_process")
|
||||
.unwrap();
|
||||
|
|
@ -4448,6 +4612,34 @@ mod semantic_projector_authority_tests {
|
|||
assert!(!body.contains("recover_workspace_coverage_projection"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn terminal_workspace_coverage_is_observed_without_recovery_authority() {
|
||||
let source = include_str!("epiphany-daemon-supervisor.rs");
|
||||
let start = source
|
||||
.find("let terminal_sight = match authenticate_current_workspace_coverage_terminal_sight")
|
||||
.unwrap();
|
||||
let end = source[start..]
|
||||
.find("let target = match authenticate_recovery_workspace_coverage_claim_sight")
|
||||
.map(|offset| start + offset)
|
||||
.unwrap();
|
||||
let branch = &source[start..end];
|
||||
let lineage = branch
|
||||
.find("authenticate_workspace_coverage_replacement_lineage")
|
||||
.unwrap();
|
||||
let observation = branch
|
||||
.find("observe_workspace_coverage_managed_process")
|
||||
.unwrap();
|
||||
let alive = branch.find("terminal-observer-alive").unwrap();
|
||||
let termination = branch
|
||||
.find("write_workspace_coverage_process_termination_observation")
|
||||
.unwrap();
|
||||
let restart = branch.find("service_launch_internal").unwrap();
|
||||
assert!(lineage < observation && observation < alive);
|
||||
assert!(alive < termination && termination < restart);
|
||||
assert!(!branch.contains("write_workspace_coverage_recovery_directive"));
|
||||
assert!(!branch.contains("recover_workspace_coverage_projection"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn workspace_coverage_none_to_some_claim_race_uses_post_terminal_sight() {
|
||||
let source = include_str!("epiphany-daemon-supervisor.rs");
|
||||
|
|
|
|||
|
|
@ -3021,3 +3021,26 @@ byte-for-byte.
|
|||
Next action: commit/push this correction, revoke `519574ce`, terminate exact
|
||||
Idunn transaction PGID `1534830`, verify signal-safe rollback, then cross the
|
||||
exact successor through Bifrost and Idunn under brake.
|
||||
|
||||
Candidate `cb665dc6362027a06d46c3fc856c8740f4ab50ce` proved the projector-side
|
||||
cut: release `sha256-026f83064dc1f20c6ae1d0528878ec96594d08692fb7ab3c0ccd24f48cd8aa46`
|
||||
reused terminal receipt `bf744be34d4c969db6b74b4813caa24e231f341ea13a9346d16457188b1b77d9`
|
||||
and remained idle across repeated pulses. All three services were stable,
|
||||
braked, and the full pre-witness check became healthy. Idunn still withheld
|
||||
admission because the supervisor independently ignored terminal sight and
|
||||
selected recovery claim `2a355736-3792-4f0f-8633-a246219bcf1b`, publishing
|
||||
bogus directive `7fb0a855-626a-4f94-bde2-cc88acad918c`.
|
||||
|
||||
Supervisor reconciliation now gives authenticated terminal sight its own
|
||||
process-lifecycle path before recovery claim selection. It proves any current
|
||||
observer is a host-signed descendant of the historical terminal launch. A live
|
||||
observer reports `terminal-observer-alive`; degraded sight does not actuate; a
|
||||
dead observer is terminated/evidence-fenced and replaced without writing a
|
||||
coverage recovery directive. Recovery authority remains exclusively for a
|
||||
nonterminal running claim. Focused supervisor authority tests prove terminal
|
||||
selection precedes recovery and the entire terminal branch contains no recovery
|
||||
writer or coverage-store mutation.
|
||||
|
||||
Next action: commit/push the supervisor correction, revoke `cb665dc6`, cancel
|
||||
exact Idunn PGID `1569316`, verify rollback, and deploy the exact successor
|
||||
through Bifrost and Idunn under brake.
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -3,7 +3,7 @@ experiment:
|
|||
repository_body_observer: "Native Rust explicitly binds caller workspace ID to an existing validated runtime/swarm/source identity plus canonical Git root/object format/scope/policy. Bind also installs one immutable runtime-side RuntimeRepositoryBodyStoreBinding naming the canonical external Body-store locator and exact Body-binding hash; every read revalidates runtime/swarm/workspace/path/body identity and refuses a second store. Two equal sanitized temporary-index scans enumerate ignore-aware UTF-8 paths, Git modes, and nonrecursive gitlinks with global excludes disabled; raw regular bytes/lengths and non-followed symlink-target bytes are SHA-256 hashed. The domain-separated ordered raw manifest root, not the clean-filtered auxiliary tree OID, is authoritative Body identity. Every Git subprocess strips ambient repository/worktree/object/ref/index/namespace/replacement/graft/shallow and config-injection authority. Observe cannot mint identity. CultCache MessagePack atomically commits immutable manifest+observation and a manifest-root head by exact CAS. Canonical store-path checks precede writes; corrupt HEAD, sparse checkout, unsafe/unrepresentable paths, racing reads, and repo/runtime substitution fail closed. Only accepted stable observations persist; errors advance no head. Unchanged raw manifest preserves generation; status is read-pure. The observer owns bounded current-state identity, makes no historical continuity claim, and has no Ready field. The runtime route is locator authority only, not proof of model grounding."
|
||||
modeling_body_grounding: "Every coordinator-owned Modeling launch observes and carries one typed RepositoryBodyObservationBasis before worker thought. The immutable launch binds runtime/swarm/workspace/scope, Body-binding hash, observation generation/id/root, and scan interval. Modeling output contract v3 requires the exact worker-authored echo; ingress reloads the immutable launch and rejects missing/swapped basis, while non-Modeling cargo is forbidden. Mind admission review v1 and receipt v5 preserve that basis; admission validates exact launch/result/review equality plus the immutable historical Body artifacts, then copies it into the CAS receipt. Admission never samples current Body as a substitute. Direct Mind plan adoption and legacy migration remain explicitly ungrounded."
|
||||
workspace_retrieval_coverage: "The production-unwired retrieval.rs oracle is deleted. Legacy EpiphanyRetrievalState is presentation-only: clean Ready projects Missing and its indexing job is unavailable/unowned. Native coverage binds an authenticated Repository Body manifest, exhaustive versioned inclusion policy, deterministic chunks/point payloads, immutable Ollama tag+artifact digest identity, vector dimensions, exact Qdrant point/payload/vector observations, and one terminal-CAS receipt/head. A read-only live evidence reader now re-scrolls the exact compatible collection, recomputes payload/vector bindings, and byte-revalidates the entire authority chain after the external read. Coverage success proves only this exact Body projection; Mind alone may join it with the other repository authorities."
|
||||
workspace_coverage_projector: "The dedicated workspace-coverage projector exclusively owns the pinned Redb coverage store and all obligation, plan, claim, attempt, checkpoint, progress, recovery, receipt, and head writes. Repository Body remains read-only substrate authority; local Verse carries signed claim sight, immutable terminal sight, and recovery directives, not private coverage truth. A succeeded claim and its historical managed launch own durable terminal coverage; later daemon launches may authenticate and reuse the exact signed terminal sight but cannot inherit or rewrite the terminal receipt. Advancement sight requires exact current-policy launch authority; terminal sight requires its exact historical host-signed launch/envelope and survives managed-policy rotation. Projector pulse classifies terminal coverage before process recovery, while recovery still precedes all nonterminal acquisition and claim work. Projection advances in waited Qdrant batches of at most 128 points with exact payload/vector readback before each authenticated checkpoint. The supervisor may observe provider-signed claim sight and publish a host-signed recovery directive after exact termination, but may not open or mutate the coverage store. Idunn owns deployment and recovery routing, never projection truth."
|
||||
workspace_coverage_projector: "The dedicated workspace-coverage projector exclusively owns the pinned Redb coverage store and all obligation, plan, claim, attempt, checkpoint, progress, recovery, receipt, and head writes. Repository Body remains read-only substrate authority; local Verse carries signed claim sight, immutable terminal sight, and recovery directives, not private coverage truth. A succeeded claim and its historical managed launch own durable terminal coverage; later daemon launches may authenticate and reuse the exact signed terminal sight but cannot inherit or rewrite the terminal receipt. Advancement sight requires exact current-policy launch authority; terminal sight requires its exact historical host-signed launch/envelope and survives managed-policy rotation. Projector pulse classifies terminal coverage before process recovery, while recovery still precedes all nonterminal acquisition and claim work. Supervisor reconciliation also classifies terminal sight before recovery claim sight: a current live descendant is observation-only, degraded observation cannot actuate, and a dead terminal observer may be process-evidence-fenced and replaced but never receives a coverage recovery directive. Projection advances in waited Qdrant batches of at most 128 points with exact payload/vector readback before each authenticated checkpoint. The supervisor may publish a host-signed recovery directive only for an authenticated nonterminal running claim after exact termination; it may not open or mutate the coverage store. Idunn owns deployment and recovery routing, never projection truth."
|
||||
workspace_coverage_checkpoint: "The pinned Redb workspace-coverage store is operational authority. A checkpoint binds one contiguous plan batch, exact waited Qdrant readback, predecessor lineage, current Body, claim, attempt, launch, provider, and release. Progress derives from the admitted checkpoint. Existing one-point histories remain valid predecessors, while current production cadence is bounded at 128 points to avoid pathological immutable-history amplification. Qdrant remains disposable derived state; counts without authenticated bindings cannot resume work or prove readiness."
|
||||
repository_readiness_join: "Mind owns the implemented race-bounded RepositoryReadinessProjection operation. It freshly observes Body R1; derives canonical RepoModel, the unique current v5 Body-grounded admission, and the complete succeeded Modeling semantic chain from one raw opening CultCache snapshot; authenticates live semantic vectors and live workspace coverage bound to R1; observes Body R2 and requires the same content identity/root; repeats both live reads; derives the closing authority from one raw full snapshot; and atomically appends a historical observed-ready-at projection only if that complete snapshot is unchanged. Stored projections never grant current readiness. A→B→A is acceptable only as equal content-addressed endpoints. CultCache now fences full-snapshot cardinality with tuple (type,key) identity. Legacy retrieval Ready, timestamps, watcher silence, Git OIDs, collection existence/counts, Hands receipts, Eve/Gjallar, and prior readiness projections are forbidden substitutes."
|
||||
coordinator_grounding_loop: "Cold start is owned by one typed UserObjectiveIntake that atomically seeds thread state and refuses objective replacement; a missing checkpoint routes to Modeling rather than a fictional prepareCheckpoint actuator. Explicit local-Verse path authority flows into launch context without sibling-path inference. Eyes and Modeling receive bounded source tools. Accepted CRRC regather findings hand source gathering to Eyes instead of recursively relaunching the continuity judge. Runtime/display/coordinator reads share the canonical typed role and reorient result readers. Failed or completed-unreviewable role outputs may be explicitly superseded by a typed review receipt; JSON null is not a state patch. Provider output schema is formatting guidance only, while canonical typed ingress and Mind remain admission authority. Modeling's provider schema now describes tagged patch operations, closed node/edge/profile vocabularies, canonical node/edge fields, anchors, code refs, observations, evidence, frontier items, and Body-basis echo. Live dogfood has accepted one code-grounded Eyes result, but no current Body-grounded Modeling admission yet: the latest Modeling run used eleven source-tool rounds then failed at the provider request boundary before publishing typed output."
|
||||
|
|
@ -796,4 +796,4 @@ current_status:
|
|||
- Candidate `b8517d9d7905eb62314db675cc2f0688510aa5bd` proved stale launch terminalization and current-policy replacement launch: supervisor PID `1147968` launched replacement workspace projector PID `1148157`, whose authenticated ready heartbeat advanced while the engaged brake held. Recovery still stalled because the old Body claim could be read only through current-claim authentication, which correctly rejected its historical policy before the mismatch recovery branch. The candidate was rolled back and revoked; no deployment witness exists. Claim sight now has two explicit authorities: current authentication is required for provider projection work and rejects stale launches; recovery authentication proves the old signed claim/body/route lineage only. Supervisor recovery and directive writing use the recovery reader, while old launch authentication inside the atomic lease-transfer transaction is historical and the replacement launch/heartbeat remain current-policy evidence. The full causal test performs an actual policy rotation and proves current refusal, historical recovery, directive fencing, lease transfer, successor current sight, and replay/tamper boundaries. Full core passes 573/573 with one ignored; all binaries compile.
|
||||
- Candidate `351355d3baee2d475f84168136380d2f50197ef8` authenticated the historical claim and started all three owners under the brake, but recovery refused because the claim named an older launch than the current replacement's direct parent. Repeated failed candidate deployments had formed a valid signed replacement chain, while recovery still assumed one direct old-to-new edge. The exact Idunn process group was terminated, all services stopped, active pointer and witness removed, and Bifrost authority revoked. No deployment witness exists. Recovery now authenticates the complete immutable chain from the claim's historical launch to the live current-policy descendant: every intermediate launch is host-signed, every parent termination id and envelope digest is exact, chronology is ordered, cycles and broken edges refuse, and the selected endpoint must be current. Directive writing, receipt verification, atomic lease transfer, and supervisor resume share this proof. Typed refusal now exposes the exact lineage fault instead of swallowing it. The causal recovery test contains a real intermediate generation and still proves the full fenced transaction. Full library passes 573/573 with one ignored; supervisor authority tests pass 24/24.
|
||||
- Candidate `a70fe275195740ecefc2984b32ebd8ae93658c18` proved the full signed multi-generation chain and emitted recovery directive `06165fb6-97bd-4c27-97e3-38af03dec72d` from historical claim `c6df4335-7080-4d5f-80fc-4fa18a9d16ce` to current launch `9414c32f-f53c-450c-bc19-a69f5ba04a29`. The replacement projector nevertheless kept refusing stale-policy claim sight because its pulse authenticated the not-yet-created current successor claim before looking for the recovery directive. Supervisor replayed the same directive without mutating Body, exposing a circular ordering fault. The exact Idunn process group was terminated, services stopped, pointer and witness removed, and Bifrost authority revoked; no deployment witness exists. Projector recovery directive consumption now precedes all ordinary projection work and invokes the already idempotent atomic transfer directly. Current claim sight is an output of recovery, never a prerequisite for it. Full library now passes 574/574 with one ignored; supervisor authority tests pass 24/24.
|
||||
next_action: "Candidate 519574ce is braked and Idunn-owned at PGID 1534830 but unadmitted: all services and pre-witness readiness are healthy, while terminal-sight authentication still treated the historical succeeded launch as current-policy authority, causing projector refusal and a bogus recovery directive. Commit/push the explicit sight-authority split (advancement=current launch; terminal=historical signed launch), revoke 519574ce, terminate only PGID 1534830, and verify signal-safe rollback. Then Bifrost-authorize and Idunn-deploy the exact successor under brake. Require immutable historical terminal-sight reuse, three stable services, typed resident readiness, signed aggregate health, and final deployment.env before the Starfire-offline ordinary-product rehearsal: persistent Modeling map -> autonomous bounded Imagination proposal, with organization feedback through Persona as additional attributed pressure; explicit Mind adoption -> Hands consequence -> Bifrost release -> Idunn deployment remain separate gates. Prove replay/tamper refusal and that conversation alone grants no Mind, Hands, Persona-speech, release, or deployment authority. No host reboot is authorized."
|
||||
next_action: "Candidate cb665dc6 is braked and Idunn-owned at PGID 1569316 but unadmitted. The projector now reuses historical terminal sight and remains idle, and pre-witness readiness is healthy, but the supervisor independently ignores terminal sight and writes a bogus recovery directive for the succeeded claim. Commit/push the dedicated supervisor terminal-observer path, revoke cb665dc6, terminate only PGID 1569316, and verify signal-safe rollback. Then Bifrost-authorize and Idunn-deploy the exact successor under brake. Require terminal-observer-alive without any recovery directive, immutable historical terminal-sight reuse, three stable services, typed resident readiness, signed aggregate health, and final deployment.env before the Starfire-offline ordinary-product rehearsal: persistent Modeling map -> autonomous bounded Imagination proposal, with organization feedback through Persona as additional attributed pressure; explicit Mind adoption -> Hands consequence -> Bifrost release -> Idunn deployment remain separate gates. Prove replay/tamper refusal and that conversation alone grants no Mind, Hands, Persona-speech, release, or deployment authority. No host reboot is authorized."
|
||||
|
|
|
|||
Reference in a new issue