From bc19c8ae9d67772d69498e993ed433fce76323b4 Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Tue, 18 Aug 2026 02:38:33 +0200 Subject: [PATCH] Resume fairness after rejected scheduler pulses --- .../bin/ghostlight-gestalt-dynamics-smoke.rs | 79 +++++++++++++------ .../ghostlight-multiresolution-agency.md | 7 ++ notes/fresh-workspace-handoff.md | 8 ++ state/map.yaml | 2 +- 4 files changed, 69 insertions(+), 27 deletions(-) diff --git a/crates/ghostlight-dungeon/src/bin/ghostlight-gestalt-dynamics-smoke.rs b/crates/ghostlight-dungeon/src/bin/ghostlight-gestalt-dynamics-smoke.rs index b0fbf65..1c16409 100644 --- a/crates/ghostlight-dungeon/src/bin/ghostlight-gestalt-dynamics-smoke.rs +++ b/crates/ghostlight-dungeon/src/bin/ghostlight-gestalt-dynamics-smoke.rs @@ -60,6 +60,7 @@ impl ghostlight_dungeon::model::ModelPort for LiveFireModelRecorder { fn write_wave_failure( root: &std::path::Path, wave_index: usize, + pulse_attempt: usize, budget: u8, error: &anyhow::Error, calls: &std::sync::Arc>>, @@ -67,10 +68,13 @@ fn write_wave_failure( ) -> anyhow::Result<()> { let calls = calls.lock().expect("live-fire trace lock"); std::fs::write( - root.join(format!("sustained-wave-{wave_index:02}-failure.json")), + root.join(format!( + "sustained-wave-{wave_index:02}-pulse-{pulse_attempt:02}-failure.json" + )), serde_json::to_vec_pretty(&serde_json::json!({ "schema":"ghostlight.live_fire_model_failure.v1", "wave_index":wave_index, + "pulse_attempt":pulse_attempt, "budget":budget, "error":error.to_string(), "private_model_calls":&calls[trace_start..], @@ -109,6 +113,12 @@ async fn main() -> anyhow::Result<()> { .and_then(|value| value.parse::().ok()) .filter(|value| (1..=31).contains(value)) .unwrap_or_default(); + let max_rejected_pulses_per_wave = + std::env::var("GHOSTLIGHT_LIVE_FIRE_MAX_REJECTED_PULSES_PER_WAVE") + .ok() + .and_then(|value| value.parse::().ok()) + .filter(|value| *value <= 8) + .unwrap_or_default(); let root = std::env::var_os("GHOSTLIGHT_LIVE_FIRE_RESULT_ROOT") .map(PathBuf::from) .unwrap_or_else(|| { @@ -151,7 +161,7 @@ async fn main() -> anyhow::Result<()> { { Ok(output) => output, Err(error) => { - write_wave_failure(&root, 0, 1, &error, &model_calls, 0)?; + write_wave_failure(&root, 0, 1, 1, &error, &model_calls, 0)?; return Err(error); } }; @@ -308,6 +318,7 @@ async fn main() -> anyhow::Result<()> { anyhow::bail!("migration changed Mira's identity or effective personal state") } let mut sustained_waves = Vec::new(); + let mut rejected_wave_pulses = Vec::new(); let mut background_subject_ids = BTreeSet::new(); let mut detail_focus_subject_ids = BTreeSet::new(); detail_focus_subject_ids.extend(root_cell.detail_focus_subject_id.clone()); @@ -328,30 +339,44 @@ async fn main() -> anyhow::Result<()> { CommandResult::ResolutionUpdated { campaign, .. } => campaign, _ => anyhow::bail!("resolution budget change did not commit at a safe boundary"), }; - let trace_start = model_calls.lock().expect("live-fire trace lock").len(); - let sustained_output = match propose_resolution_wave( - model.clone(), - Arc::new(SnapshotPermit::new_resolution( - store.clone(), - advanced.id, - advanced.revision, - advanced.resolution_policy.resolution_epoch, - )), - &advanced, - ) - .await - { - Ok(output) => output, - Err(error) => { - write_wave_failure( - &root, - wave_index + 1, - budget, - &error, - &model_calls, - trace_start, - )?; - return Err(error); + let mut rejected_pulses_for_wave = 0; + let sustained_output = loop { + let trace_start = model_calls.lock().expect("live-fire trace lock").len(); + match propose_resolution_wave( + model.clone(), + Arc::new(SnapshotPermit::new_resolution( + store.clone(), + advanced.id, + advanced.revision, + advanced.resolution_policy.resolution_epoch, + )), + &advanced, + ) + .await + { + Ok(output) => break output, + Err(error) => { + rejected_pulses_for_wave += 1; + write_wave_failure( + &root, + wave_index + 1, + rejected_pulses_for_wave, + budget, + &error, + &model_calls, + trace_start, + )?; + rejected_wave_pulses.push(serde_json::json!({ + "wave_index":wave_index + 1, + "pulse_attempt":rejected_pulses_for_wave, + "world_revision":advanced.revision, + "resolution_epoch":advanced.resolution_policy.resolution_epoch, + "error":error.to_string(), + })); + if rejected_pulses_for_wave > max_rejected_pulses_per_wave { + return Err(error); + } + } } }; std::fs::write( @@ -524,6 +549,8 @@ async fn main() -> anyhow::Result<()> { "sustained_background_subject_ids":background_subject_ids, "sustained_detail_focus_subject_ids":detail_focus_subject_ids, "fairness_stress_waves":fairness_stress_waves, + "max_rejected_pulses_per_wave":max_rejected_pulses_per_wave, + "rejected_wave_pulses":rejected_wave_pulses, "fairness_missing_subject_ids":fairness_missing_subject_ids, "source_lineage_depth":source_lineage_depth, "destination_lineage_depth":destination_lineage_depth, diff --git a/docs/architecture/ghostlight-multiresolution-agency.md b/docs/architecture/ghostlight-multiresolution-agency.md index a9d7c54..46299ae 100644 --- a/docs/architecture/ghostlight-multiresolution-agency.md +++ b/docs/architecture/ghostlight-multiresolution-agency.md @@ -497,6 +497,13 @@ lineage parents as simulation subjects, and repeats the identity/callback check after sustained compression. Both migration endpoints must be active leaves at lineage depth two or greater. +Fairness acceptance treats a rejected provider wave the same way the daemon's +scheduler does: the failed pulse records its private trace and commits nothing; +a later pulse may try the same still-current revision again. The harness permits +a bounded, operator-selected number of these fresh pulses per desired committed +tick and reports every rejection in the final result. This does not expand the +single stage's one-correction rule or rebase stale output. + A deterministic diaspora test drives two durable people from the same depth-two refugee leaf to two different depth-two destination branches in one strategic plan. It proves exact effective capabilities, knowledge, goals, diff --git a/notes/fresh-workspace-handoff.md b/notes/fresh-workspace-handoff.md index 2d38d90..2be3401 100644 --- a/notes/fresh-workspace-handoff.md +++ b/notes/fresh-workspace-handoff.md @@ -220,6 +220,14 @@ also moves two remembered refugees from one depth-two source leaf to different depth-two destination branches in one plan and proves their effective and private state remains distinct. +The fairness harness now models scheduler continuation explicitly. Set +`GHOSTLIGHT_LIVE_FIRE_MAX_REJECTED_PULSES_PER_WAVE` from `0` through `8`. Each +failed pulse retains its exact world and resolution revisions, writes a numbered +private failure artifact, and commits nothing; the next pulse starts a new model +pipeline against the still-current snapshot. Final results enumerate the failed +pulses instead of hiding them. This is acceptance orchestration only and does +not add a second retry inside Projector/Interpreter validation. + The multiresolution body is deployed on Starfire. Typed health matched the immutable release, DeepSeek was ready, and the disposable live strategic-cell smoke committed three offscreen events plus three accessible news items without diff --git a/state/map.yaml b/state/map.yaml index 94419ae..3462cfe 100644 --- a/state/map.yaml +++ b/state/map.yaml @@ -268,5 +268,5 @@ data_flow: current_status: summary: GhostlightDungeon production remains on the verified Starfire release. The strict 24-subject multiresolution golden is complete: a budget-one rival arena, independent population/member migration, 26 background consequences across 11 subjects, stable identity through 1/4/8/4 resolution changes, and automatic rematerialization of the same refugee. Production state remains untouched. - next_action: Rerun the 24-tick budget-one fairness stress with the explicit four-target Interpreter contract. Require every canonical subject to receive runtime-selected debt focus, reject inactive lineage parents, preserve Mira through sustained aggregate activity, and rematerialize the same person. Then run the 1/4/8/32 scale matrix and compare cache and cognitive-efficiency profiles. + next_action: Rerun the 24-tick budget-one fairness stress with the explicit four-target Interpreter contract and bounded fresh scheduler pulses after rejected waves. Require every canonical subject to receive runtime-selected debt focus, reject inactive lineage parents, preserve Mira through sustained aggregate activity, and rematerialize the same person. Then run the 1/4/8/32 scale matrix and compare cache and cognitive-efficiency profiles. gestalt_population: Gestalt Personas own shared population baselines; persistent member deltas own named individuality; WorldKernel materialization and approval-gated fission preserve lineage and identity. Presence projection is limited to nearby active leaves and exact-location dormant members. Simulation cells are derived covers only.