Resume fairness after rejected scheduler pulses

This commit is contained in:
Meta Vens 2026-08-18 02:38:33 +02:00
commit bc19c8ae9d
4 changed files with 69 additions and 27 deletions

View file

@ -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<std::sync::Mutex<Vec<serde_json::Value>>>,
@ -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::<usize>().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::<usize>().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,

View file

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

View file

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

View file

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