diff --git a/crates/ghostlight-dungeon/src/bin/ghostlight-strategic-smoke.rs b/crates/ghostlight-dungeon/src/bin/ghostlight-strategic-smoke.rs index 4f91766..478613b 100644 --- a/crates/ghostlight-dungeon/src/bin/ghostlight-strategic-smoke.rs +++ b/crates/ghostlight-dungeon/src/bin/ghostlight-strategic-smoke.rs @@ -1,3 +1,14 @@ +fn final_wave_field( + wave_reports: &[serde_json::Value], + field: &str, +) -> anyhow::Result { + wave_reports + .last() + .and_then(|wave| wave.get(field)) + .cloned() + .ok_or_else(|| anyhow::anyhow!("final strategic wave is missing {field}")) +} + #[tokio::main] async fn main() -> anyhow::Result<()> { use chrono::Utc; @@ -272,9 +283,9 @@ async fn main() -> anyhow::Result<()> { }))?, )?; } - let first_plan = wave_reports[0]["plan"].clone(); - let first_commit = wave_reports[0]["commit"].clone(); - let first_model_receipt_hash = wave_reports[0]["model_receipt_hash"].clone(); + let final_plan = final_wave_field(&wave_reports, "plan")?; + let final_commit = final_wave_field(&wave_reports, "commit")?; + let final_model_receipt_hash = final_wave_field(&wave_reports, "model_receipt_hash")?; let model_stage_receipts = wave_reports .iter() .flat_map(|wave| { @@ -309,10 +320,10 @@ async fn main() -> anyhow::Result<()> { "campaign_id":campaign.id, "elapsed_seconds":started.elapsed().as_secs_f64(), "model_runtime":model_selection.status("configured"), - "model_receipt_hash":&first_model_receipt_hash, + "model_receipt_hash":&final_model_receipt_hash, "model_stage_receipts":&model_stage_receipts, - "plan":&first_plan, - "commit":&first_commit, + "plan":&final_plan, + "commit":&final_commit, "waves":&wave_reports, "event_count":campaign.events.len(), "news_count":campaign.news.len(), @@ -364,10 +375,10 @@ async fn main() -> anyhow::Result<()> { "campaign_id":campaign.id, "elapsed_seconds":started.elapsed().as_secs_f64(), "model_runtime":model_selection.status("configured"), - "model_receipt_hash":first_model_receipt_hash, + "model_receipt_hash":final_model_receipt_hash, "model_stage_receipts":model_stage_receipts, - "plan":first_plan, - "commit":first_commit, + "plan":final_plan, + "commit":final_commit, "waves":wave_reports, "event_count":campaign.events.len(), "news_count":campaign.news.len(), @@ -697,3 +708,19 @@ fn strategic_campaign() -> ghostlight_dungeon::domain::Campaign { } campaign } + +#[cfg(test)] +mod tests { + use super::final_wave_field; + + #[test] + fn top_level_projection_uses_the_final_wave_head() { + let waves = vec![ + serde_json::json!({"commit":{"campaign":{"revision":1}}}), + serde_json::json!({"commit":{"campaign":{"revision":2}}}), + ]; + + let commit = final_wave_field(&waves, "commit").unwrap(); + assert_eq!(commit["campaign"]["revision"], 2); + } +} diff --git a/crates/ghostlight-dungeon/src/kernel.rs b/crates/ghostlight-dungeon/src/kernel.rs index 06f1dd7..64d23f2 100644 --- a/crates/ghostlight-dungeon/src/kernel.rs +++ b/crates/ghostlight-dungeon/src/kernel.rs @@ -2753,7 +2753,10 @@ fn apply_strategic_tick_plan( "strategic institution action would not change its posture".into(), )); } - let summary = format!("{} adopts posture: {}", institution.name, action.posture); + let summary = format!( + "{} announces a new course: {}", + institution.name, action.posture + ); events.push(crate::domain::Event { id: format!("strategic:{revision}:institution:{}", institution.id), at, @@ -5894,7 +5897,7 @@ pub(crate) mod tests { }, ); crate::resolution::ensure_agency_profiles(&mut value); - apply_strategic_tick_plan( + let events = apply_strategic_tick_plan( &mut value, StrategicTickPlan { institution_actions: vec![StrategicInstitutionAction { @@ -5907,6 +5910,13 @@ pub(crate) mod tests { }, ) .unwrap(); + assert_eq!(events.len(), 1); + assert!( + events[0] + .summary + .starts_with("Board announces a new course: ") + ); + assert!(!events[0].summary.contains("adopts posture")); assert_eq!( value.institutions["board"].posture.chars().count(), MAX_POSTURE_CHARS diff --git a/crates/ghostlight-dungeon/src/newspaper.rs b/crates/ghostlight-dungeon/src/newspaper.rs index 13e54fa..4e8ba41 100644 --- a/crates/ghostlight-dungeon/src/newspaper.rs +++ b/crates/ghostlight-dungeon/src/newspaper.rs @@ -659,39 +659,41 @@ fn newsroom_source( issue.id )); } + let summary = event.summary.trim().to_owned(); Ok(NewsroomEvent { event_ids: BTreeSet::from([event.id.clone()]), - summary: event.summary.trim().to_owned(), actor_names: event .actor_ids .iter() - .filter_map(|id| campaign.actors.get(id).map(|actor| actor.name.clone())) + .filter_map(|id| campaign.actors.get(id)) + .filter(|actor| summary_mentions_name(&summary, &actor.name)) + .map(|actor| actor.name.clone()) .collect(), institution_names: event .institution_ids .iter() - .filter_map(|id| { - campaign - .institutions - .get(id) - .map(|institution| institution.name.clone()) - }) + .filter_map(|id| campaign.institutions.get(id)) + .filter(|institution| summary_mentions_name(&summary, &institution.name)) + .map(|institution| institution.name.clone()) .collect(), population_names: event .gestalt_ids .iter() - .filter_map(|id| { - campaign - .gestalts - .get(id) - .map(|gestalt| gestalt.name.clone()) - }) + .filter_map(|id| campaign.gestalts.get(id)) + .filter(|gestalt| summary_mentions_name(&summary, &gestalt.name)) + .map(|gestalt| gestalt.name.clone()) .collect(), place_names: event .location_ids .iter() - .filter_map(|id| campaign.locations.get(id).map(|place| place.name.clone())) + .filter_map(|id| campaign.locations.get(id)) + .filter(|place| { + event.location_ids.len() == 1 + || summary_mentions_name(&summary, &place.name) + }) + .map(|place| place.name.clone()) .collect(), + summary, }) }) .collect::>>()?; @@ -708,6 +710,20 @@ fn newsroom_source( }) } +fn summary_mentions_name(summary: &str, name: &str) -> bool { + let summary = summary.to_lowercase(); + let name = name.to_lowercase(); + if name.is_empty() { + return false; + } + summary.match_indices(&name).any(|(start, matched)| { + let before = summary[..start].chars().next_back(); + let after = summary[start + matched.len()..].chars().next(); + before.is_none_or(|character| !character.is_alphanumeric()) + && after.is_none_or(|character| !character.is_alphanumeric()) + }) +} + fn newsroom_facts(events: &[NewsroomEvent]) -> Vec> { events .iter() @@ -1428,6 +1444,53 @@ mod tests { ); } + #[test] + fn newsroom_does_not_promote_involved_metadata_to_asserted_names_or_datelines() { + let mut campaign = campaign_with_two_news(); + let mut ann = campaign.actors["player"].clone(); + ann.id = "ann".into(); + ann.name = "Ann".into(); + campaign.actors.insert(ann.id.clone(), ann); + campaign.locations.insert( + "hall".into(), + crate::domain::Location { + id: "hall".into(), + name: "Hall".into(), + container_id: None, + routes: BTreeMap::new(), + persistent_features: vec![], + }, + ); + campaign.institutions.insert( + "unmentioned-regency".into(), + crate::domain::InstitutionState { + id: "unmentioned-regency".into(), + name: "Mossglass Regency".into(), + resources: vec![], + goals: vec![], + posture: "watching".into(), + }, + ); + let event = campaign + .events + .iter_mut() + .find(|event| event.id == "event:seal-scandal") + .unwrap(); + event.summary = "The Thorn Court announces that every officer shall answer.".into(); + event.actor_ids = vec!["ann".into()]; + event.institution_ids = vec!["unmentioned-regency".into()]; + event.location_ids = vec!["hall".into(), "yard".into()]; + + let sources = newsroom_sources(&campaign).unwrap(); + let source = sources + .iter() + .find(|source| source.news_ids.contains("news:seal-scandal")) + .unwrap(); + assert!(source.events[0].actor_names.is_empty()); + assert!(source.events[0].institution_names.is_empty()); + assert!(source.events[0].place_names.is_empty()); + } + #[tokio::test] async fn newspaper_rejects_a_headline_without_a_committed_event_before_inference() { let mut campaign = campaign_with_news(); diff --git a/crates/ghostlight-dungeon/src/outcome.rs b/crates/ghostlight-dungeon/src/outcome.rs index 0c8bf8e..c10a074 100644 --- a/crates/ghostlight-dungeon/src/outcome.rs +++ b/crates/ghostlight-dungeon/src/outcome.rs @@ -659,18 +659,19 @@ fn bind_outcomes( ) -> Result> { let sources = proposals .iter() - .map(|proposal| Ok((cell_action_digest(proposal)?, proposal.subject_id.clone()))) + .map(|proposal| Ok((cell_action_digest(proposal)?, proposal))) .collect::>>()?; let outcomes = bundle .outcomes .into_iter() .map(|proposal| { - let source_subject_id = sources + let action = sources .get(&proposal.action_digest) - .cloned() + .copied() .ok_or_else(|| anyhow!("outcome used an unknown action digest"))?; + let source_subject_id = action.subject_id.clone(); let effect = bind_effect(&proposal)?; - let summary = resolved_outcome_summary(campaign, &source_subject_id, &effect)?; + let summary = resolved_outcome_summary(campaign, action, &effect)?; Ok(StrategicActivityOutcome { schema: "ghostlight.strategic_activity_outcome.v1".into(), action_digest: proposal.action_digest, @@ -795,9 +796,10 @@ fn bind_effect(proposal: &OutcomeProposal) -> Result { fn resolved_outcome_summary( campaign: &Campaign, - source_subject_id: &str, + action: &CellActionProposal, effect: &StrategicOutcomeEffect, ) -> Result { + let source_subject_id = &action.subject_id; let source_name = subject_name(campaign, source_subject_id)?; let summary = match effect { StrategicOutcomeEffect::NoMaterialChange { .. } => { @@ -849,13 +851,15 @@ fn resolved_outcome_summary( .agency_relations .get(relation_id) .ok_or_else(|| anyhow!("relation outcome relation vanished"))?; - let direction = if *strength_delta > 0 { - "strengthens" - } else { - "strains" + let direction = match (&relation.kind, *strength_delta > 0) { + (crate::domain::AgencyRelationKind::Rivalry, true) => "deepens", + (crate::domain::AgencyRelationKind::Rivalry, false) => "eases", + (_, true) => "strengthens", + (_, false) => "weakens", }; + let action = activity_label(action); format!( - "An action by {source_name} {direction} the {} tie between {} and {}.", + "{action} by {source_name} {direction} the {} relationship between {} and {}.", agency_relation_label(&relation.kind), subject_name(campaign, &relation.from_subject_id)?, subject_name(campaign, &relation.to_subject_id)?, @@ -931,6 +935,31 @@ fn resolved_outcome_summary( Ok(summary) } +fn activity_label(action: &CellActionProposal) -> &'static str { + let mut activities = action.effects.iter().filter_map(|effect| match effect { + StrategicCellEffect::GestaltActivity { activity, .. } + | StrategicCellEffect::ActorActivity { activity, .. } + | StrategicCellEffect::MemberActivity { activity, .. } => Some(activity), + _ => None, + }); + let Some(activity) = activities.next() else { + return "A concerted campaign"; + }; + if activities.any(|candidate| candidate != activity) { + return "A concerted campaign"; + } + match activity { + StrategicActivityKind::Prepare => "Preparations", + StrategicActivityKind::Coordinate => "An organizing effort", + StrategicActivityKind::Investigate => "An inquiry", + StrategicActivityKind::Recruit => "A recruitment drive", + StrategicActivityKind::Obstruct => "An obstruction", + StrategicActivityKind::Trade => "A trade offer", + StrategicActivityKind::Communicate if action.public_channels.is_empty() => "A message", + StrategicActivityKind::Communicate => "A public appeal", + } +} + fn agency_relation_label(kind: &crate::domain::AgencyRelationKind) -> &'static str { use crate::domain::AgencyRelationKind; match kind { @@ -3462,9 +3491,14 @@ mod tests { }, }; - validate_activity_outcomes(&value, &[action], std::slice::from_ref(&outcome)).unwrap(); + validate_activity_outcomes( + &value, + std::slice::from_ref(&action), + std::slice::from_ref(&outcome), + ) + .unwrap(); assert_eq!( - resolved_outcome_summary(&value, "dockers", &outcome.effect).unwrap(), + resolved_outcome_summary(&value, &action, &outcome.effect).unwrap(), "At Dock, Dockers establishes reinforced public hazard marker." ); } @@ -3486,9 +3520,18 @@ mod tests { }, ); + let mut action = proposal(); + action.public_channels = vec!["dock broadsheet".into()]; + action.effects = vec![StrategicCellEffect::GestaltActivity { + gestalt_id: "dockers".into(), + activity: StrategicActivityKind::Communicate, + target_subject_ids: vec!["player".into()], + location_ids: vec!["dock".into()], + }]; + let summary = resolved_outcome_summary( &value, - "dockers", + &action, &StrategicOutcomeEffect::AgencyRelationShift { relation_id: "dock-command".into(), strength_delta: -5, @@ -3498,10 +3541,36 @@ mod tests { assert_eq!( summary, - "An action by Dockers strains the command tie between Dockers and Player." + "A public appeal by Dockers weakens the command relationship between Dockers and Player." ); assert!(!summary.contains("dock-command")); assert!(!summary.contains("-5")); + + value.agency_relations.insert( + "dock-rivalry".into(), + AgencyRelation { + schema: "ghostlight.agency_relation.v1".into(), + id: "dock-rivalry".into(), + from_subject_id: "dockers".into(), + to_subject_id: "player".into(), + kind: AgencyRelationKind::Rivalry, + strength: 50, + active: true, + evidence_receipt_ids: vec![], + }, + ); + assert_eq!( + resolved_outcome_summary( + &value, + &action, + &StrategicOutcomeEffect::AgencyRelationShift { + relation_id: "dock-rivalry".into(), + strength_delta: -5, + }, + ) + .unwrap(), + "A public appeal by Dockers eases the rivalry relationship between Dockers and Player." + ); } #[tokio::test] diff --git a/notes/fresh-workspace-handoff.md b/notes/fresh-workspace-handoff.md index 548aac2..00aafbe 100644 --- a/notes/fresh-workspace-handoff.md +++ b/notes/fresh-workspace-handoff.md @@ -486,31 +486,30 @@ world whose derived cover grew from four cells to six as named figures emerged; the 200-cell path remains a local scale proof and the Delvehold game-side adapter is not implemented here. -An isolated two-wave connector run against Ghostlight source `2f7c090` advanced -one fresh campaign from revision 0 to 2, committed 19 events and 14 news rows, -and left player state and location unchanged. Its reader pages and separate -provenance artifacts live at -`F:\GameCult\GhostlightDungeon\acceptance\root-wire-frontpage-blind-2f7c090-1`. -The wave-two page did not pass the blind journalistic gate. A fresh reviewer, -shown only that page under a neutral brief, diagnosed procedural construction -with 99 percent confidence because the copy exposed a canonical relation name, -a numerical state delta, and source-record language. This does not weaken the -world-simulation witness. It establishes that rendering and copy-desk gates -cannot repair a newsroom packet that presents mechanical background records as -editorial evidence. +The first newsroom projection cut was committed as `a585398` and exercised by +a fresh isolated two-wave Codex Connector run with `gpt-5.6-luna`. The campaign +advanced to revision 2 with 18 Events and 15 NewsIssues while player state and +location remained unchanged. Both presentation gates failed: the blind editor +returned FAIL at 99 percent confidence and the grounding auditor returned FAIL +at 0.94 confidence. The untouched responses and their review hashes live at +`F:\GameCult\GhostlightDungeon\acceptance\root-wire-frontpage-blind-a585398-1`. +Public Event prose still exposed abstract posture and relation transitions; the +newsroom promoted merely involved entity and location metadata into asserted +names and a dateline; and the smoke result projected the wave-one commit at top +level beside wave-two counts and head. The world run remains valid evidence of +autonomous transition, not newspaper acceptance. -The current local source cut rewrites public strategic summaries into -human-readable accounts at the event owner, gives the editor only opaque desk -citations and named public facts, deduplicates identical fact packets, and maps -accepted citations back to exact canonical `NewsIssue` and `Event` provenance. -All 392 library tests and every binary test pass, and the bounded Modeling -re-audit is clear. This is local verification only; no fresh provider page has -passed blind or grounding review yet. +A second local cut changes institution and relation public phrasing, includes +model-facing names only when the public account asserts them, suppresses +ambiguous multi-place metadata, and projects final-wave commit data at the smoke +result top level. All 393 library tests and every binary test pass, and the +bounded Modeling final review is clear after correcting a name-boundary +collision. The cut is not yet committed or run against the provider. -1. Commit and push the coherent newsroom and public-event cut. +1. Commit and push the exact locally verified second cut. 2. Run a fresh isolated exact-commit two-wave newspaper acceptance through Codex Connector with `gpt-5.6-luna`, preserving world, issue, audit, receipt, - build, and hash witnesses without mutating the live daemon. + build, review, and hash witnesses without mutating the live daemon. 3. Dispatch a fresh blind reviewer with only the strongest page and a neutral editorial brief; preserve its untouched response. 4. Independently audit that page against committed public events before diff --git a/notes/ghostlight-current-system-map.md b/notes/ghostlight-current-system-map.md index df1b8cc..7ce7085 100644 --- a/notes/ghostlight-current-system-map.md +++ b/notes/ghostlight-current-system-map.md @@ -939,7 +939,11 @@ machinery never become the public account. The newsroom does not re-decide which committed public events count as truth. It merges identical fact bundles under one opaque per-request citation while retaining every backing news row, event, channel, and reliability record for later audit. The editor receives -only those citations, human-readable accounts, and canonical names. News IDs, +only those citations, human-readable accounts, and canonical names asserted by +those accounts. Event membership metadata does not silently turn an involved +actor, institution, population, or ambiguous multi-place scope into an asserted +fact or dateline; a single event location may supply its canonical dateline, +while a multi-place event must name the place in its public account. News IDs, event IDs and kinds, channels, reliability, source headlines, and wall-clock timestamps remain internal. A capable editor model owns selection, grouping, headline, deck, byline class, dateline, and article prose as a @@ -963,7 +967,10 @@ strategic acceptance smoke use that same primitive before returning a rejection. In the acceptance smoke, an interim edition rejection is local to the consumer and cannot stop later world waves. A rejected final combined edition remains a nonzero acceptance failure, but first writes the completed -wave reports, rejection receipts, and failed status. The reader renderer has no +wave reports, rejection receipts, and failed status. Its top-level plan, +receipt hash, and commit always project the final completed wave, matching the +top-level counts and persisted campaign head; per-wave history remains under +`waves`. The reader renderer has no path to provenance fields and escapes all model- and consumer-supplied plain text before Markdown emission; the provenance renderer applies the same plain-text boundary to its audit data. Neither editor, copy desk, renderer, nor diff --git a/state/evidence.jsonl b/state/evidence.jsonl index 18824ec..ee555ec 100644 --- a/state/evidence.jsonl +++ b/state/evidence.jsonl @@ -150,4 +150,5 @@ {"branch": "codex/ghostlight-dungeon-mvp", "note": "Accepted sustained provider-backed Rainless Marches witness at exact Ghostlight source 5e00a7bbc3f540ef35d18abe89f84687b5004664 through Codex Connector 2f29b90eebd8fc1bbdc07cdd22ac4883f16849a5, model gpt-5.6-luna, caller ghostlight-dungeon-yggdrasil. Four isolated waves advanced revisions 0 through 4 in 353.2615 seconds, committed 41 unique events and 28 unique news rows, and rendered 23 unique provenance-bound articles across four issues (9, 6, 4, 4). One wave-1 candidate pulse was rejected before the accepted retry; later waves committed without rejection and stderr remained empty. Ilyra Quill appears in every issue, Mara testimony persists into later institutional postures, and Tavia plus Mara Venn emerge as accountable organizers. Player state and location are unchanged. Independent artifact review found distinct successive developments and exact unique article/event/news IDs. Local immutable witness: F:\\GameCult\\GhostlightDungeon\\acceptance\\rainless-spicy-20260827-sustained-5; remote root: /var/lib/gamecult/ghostlight-dungeon/acceptance/rainless-spicy-20260827-sustained-5. SHA-256: result 19baf5882d60ffbffe2f5e161ba1d9328588228fcb9855e33fbfaa2cc7e57108; combined newspaper 0e4e520b945fbae02260a7cc04eeb2835e1ddbb9aa57c55a6426f396dbb1e66c; campaign bc4c5ae2e89b4bcae483f3ad9297da180e1a6c056218537f61bb59202ba25e29; provenance 682f7d36247eb74cbc7c5e8a6f58d15d3b9c03fa741b9c7ce1bc71b926be17bf. This proves sustained politics in a four-cell sparse world, not a live 200-cell provider wave or the Delvehold game-side adapter.", "status": "accepted", "ts": "2026-08-27T03:20:13+00:00", "type": "live_acceptance"} {"branch": "codex/ghostlight-dungeon-mvp", "note": "Corrects the scope wording in the accepted sustained Rainless record at source 5e00a7b. The four waves were sequential atomic transitions in one campaign, advancing revisions 0\u21921\u21922\u21923\u21924, not isolated runs. Unique provider cell pipeline bindings were 4, 5, 6, and 6 across the waves as named-person emergence expanded the derived cover; selected-action counts were 4, 5, 6, and 4. The witness therefore proves sustained politics in a sparse dynamically expanding cover, not a fixed four-cell world. All accepted event, news, article, player-invariance, provenance, and hash claims remain unchanged.", "status": "ok", "ts": "2026-08-27T03:25:41+00:00", "type": "correction"} {"branch": "codex/ghostlight-dungeon-mvp", "note": "Narrows the review claim in the accepted sustained Rainless witness. The prior artifact review established distinct cross-issue developments, unique article/event/news IDs, and exact provenance. It was not a blind front-page review and did not establish journalistically plausible editorial presentation or resistance to procedural-generation diagnosis. A fresh blind baseline reviewer, given only issue 1 under a neutral editorial brief, immediately called it a simulation event ledger wearing a newspaper's clothes and independently identified world-revision metadata, engine-taxonomy sections, mechanical action language, event hashes, repetition, buried news judgment, truncation ellipses, and absent editorial voice. Sustained world causality remains accepted; fantasy-newspaper presentation quality remains unaccepted until a replacement page passes the same blind gate.", "status": "ok", "ts": "2026-08-27T04:07:46+00:00", "type": "correction"} -{"ts": "2026-08-27T07:59:01+00:00", "type": "correction", "status": "ok", "note": "A fresh two-wave Codex Connector run at Ghostlight source 2f7c090 advanced one campaign from revision 0 to 2, committed 19 events and 14 news rows, and left player state and location unchanged; artifacts live at F:\\GameCult\\GhostlightDungeon\\acceptance\\root-wire-frontpage-blind-2f7c090-1. The world witness succeeded, but a fresh reviewer shown only newspaper-wave-02.md under a neutral brief rejected it as procedurally constructed with 99 percent confidence. The page exposed the canonical relation name synod-guild-command, its five-point numerical delta, and source-record language. This falsifies the assumption that reader-safe rendering plus a copy-desk gate is sufficient when the model-facing newsroom packet still presents mechanical background records as editorial evidence. Fantasy-newspaper presentation remains unaccepted; the next cut belongs at newsroom projection and typed source curation, while canonical provenance remains internal and exact.", "branch": "codex/ghostlight-dungeon-mvp"} +{"branch": "codex/ghostlight-dungeon-mvp", "note": "A fresh two-wave Codex Connector run at Ghostlight source 2f7c090 advanced one campaign from revision 0 to 2, committed 19 events and 14 news rows, and left player state and location unchanged; artifacts live at F:\\GameCult\\GhostlightDungeon\\acceptance\\root-wire-frontpage-blind-2f7c090-1. The world witness succeeded, but a fresh reviewer shown only newspaper-wave-02.md under a neutral brief rejected it as procedurally constructed with 99 percent confidence. The page exposed the canonical relation name synod-guild-command, its five-point numerical delta, and source-record language. This falsifies the assumption that reader-safe rendering plus a copy-desk gate is sufficient when the model-facing newsroom packet still presents mechanical background records as editorial evidence. Fantasy-newspaper presentation remains unaccepted; the next cut belongs at newsroom projection and typed source curation, while canonical provenance remains internal and exact.", "status": "ok", "ts": "2026-08-27T07:59:01+00:00", "type": "correction"} +{"ts": "2026-08-27T09:26:57+00:00", "type": "correction", "status": "ok", "note": "Exact Ghostlight commit a585398 completed a fresh isolated two-wave Codex Connector run with gpt-5.6-luna at F:\\GameCult\\GhostlightDungeon\\acceptance\\root-wire-frontpage-blind-a585398-1. The campaign reached revision 2 with 18 Events and 15 NewsIssues and left player state and location unchanged. The blind editor rejected newspaper.md with 99 percent confidence, and the independent grounding audit rejected it with 0.94 confidence despite exact citation lowering and no private-state leakage. Public Event accounts still exposed abstract adopted-posture and relation-transition language; the newsroom promoted merely involved entity and location metadata into asserted names and a dateline, producing unsupported Rootvault and Mossglass Regency claims; and result.json projected the wave-one commit at top level beside wave-two counts and head. Untouched review hashes are blind 877CF72F267C0C623A7E2D1C5BFDC037AC6929296FE1AA829708E9F368DD822F and grounding A59B0BECE4F8E8D93E9B29327176070A1C965E9C2B3BFA1B0AEE91D448FFAAAE. This falsifies the sufficiency of the first newsroom projection cut while preserving the autonomous-world transition witness. Fantasy-newspaper presentation remains unaccepted.", "branch": "codex/ghostlight-dungeon-mvp"} diff --git a/state/ghostlight-state.cultcache.jsonl b/state/ghostlight-state.cultcache.jsonl index b468b62..58ad106 100644 --- a/state/ghostlight-state.cultcache.jsonl +++ b/state/ghostlight-state.cultcache.jsonl @@ -152,3 +152,4 @@ {"key": "2026-08-27T032541Z0000-0150-40338c1716ea", "payload": "eyJicmFuY2giOiAiY29kZXgvZ2hvc3RsaWdodC1kdW5nZW9uLW12cCIsICJub3RlIjogIkNvcnJlY3RzIHRoZSBzY29wZSB3b3JkaW5nIGluIHRoZSBhY2NlcHRlZCBzdXN0YWluZWQgUmFpbmxlc3MgcmVjb3JkIGF0IHNvdXJjZSA1ZTAwYTdiLiBUaGUgZm91ciB3YXZlcyB3ZXJlIHNlcXVlbnRpYWwgYXRvbWljIHRyYW5zaXRpb25zIGluIG9uZSBjYW1wYWlnbiwgYWR2YW5jaW5nIHJldmlzaW9ucyAw4oaSMeKGkjLihpIz4oaSNCwgbm90IGlzb2xhdGVkIHJ1bnMuIFVuaXF1ZSBwcm92aWRlciBjZWxsIHBpcGVsaW5lIGJpbmRpbmdzIHdlcmUgNCwgNSwgNiwgYW5kIDYgYWNyb3NzIHRoZSB3YXZlcyBhcyBuYW1lZC1wZXJzb24gZW1lcmdlbmNlIGV4cGFuZGVkIHRoZSBkZXJpdmVkIGNvdmVyOyBzZWxlY3RlZC1hY3Rpb24gY291bnRzIHdlcmUgNCwgNSwgNiwgYW5kIDQuIFRoZSB3aXRuZXNzIHRoZXJlZm9yZSBwcm92ZXMgc3VzdGFpbmVkIHBvbGl0aWNzIGluIGEgc3BhcnNlIGR5bmFtaWNhbGx5IGV4cGFuZGluZyBjb3Zlciwgbm90IGEgZml4ZWQgZm91ci1jZWxsIHdvcmxkLiBBbGwgYWNjZXB0ZWQgZXZlbnQsIG5ld3MsIGFydGljbGUsIHBsYXllci1pbnZhcmlhbmNlLCBwcm92ZW5hbmNlLCBhbmQgaGFzaCBjbGFpbXMgcmVtYWluIHVuY2hhbmdlZC4iLCAic3RhdHVzIjogIm9rIiwgInRzIjogIjIwMjYtMDgtMjdUMDM6MjU6NDErMDA6MDAiLCAidHlwZSI6ICJjb3JyZWN0aW9uIn0=", "stored_at": "2026-08-27T03:25:41.025860+00:00", "type": "ghostlight.evidence_record.v0"} {"key": "2026-08-27T040746Z0000-0151-1fbdb4e6c236", "payload": "eyJicmFuY2giOiAiY29kZXgvZ2hvc3RsaWdodC1kdW5nZW9uLW12cCIsICJub3RlIjogIk5hcnJvd3MgdGhlIHJldmlldyBjbGFpbSBpbiB0aGUgYWNjZXB0ZWQgc3VzdGFpbmVkIFJhaW5sZXNzIHdpdG5lc3MuIFRoZSBwcmlvciBhcnRpZmFjdCByZXZpZXcgZXN0YWJsaXNoZWQgZGlzdGluY3QgY3Jvc3MtaXNzdWUgZGV2ZWxvcG1lbnRzLCB1bmlxdWUgYXJ0aWNsZS9ldmVudC9uZXdzIElEcywgYW5kIGV4YWN0IHByb3ZlbmFuY2UuIEl0IHdhcyBub3QgYSBibGluZCBmcm9udC1wYWdlIHJldmlldyBhbmQgZGlkIG5vdCBlc3RhYmxpc2ggam91cm5hbGlzdGljYWxseSBwbGF1c2libGUgZWRpdG9yaWFsIHByZXNlbnRhdGlvbiBvciByZXNpc3RhbmNlIHRvIHByb2NlZHVyYWwtZ2VuZXJhdGlvbiBkaWFnbm9zaXMuIEEgZnJlc2ggYmxpbmQgYmFzZWxpbmUgcmV2aWV3ZXIsIGdpdmVuIG9ubHkgaXNzdWUgMSB1bmRlciBhIG5ldXRyYWwgZWRpdG9yaWFsIGJyaWVmLCBpbW1lZGlhdGVseSBjYWxsZWQgaXQgYSBzaW11bGF0aW9uIGV2ZW50IGxlZGdlciB3ZWFyaW5nIGEgbmV3c3BhcGVyJ3MgY2xvdGhlcyBhbmQgaW5kZXBlbmRlbnRseSBpZGVudGlmaWVkIHdvcmxkLXJldmlzaW9uIG1ldGFkYXRhLCBlbmdpbmUtdGF4b25vbXkgc2VjdGlvbnMsIG1lY2hhbmljYWwgYWN0aW9uIGxhbmd1YWdlLCBldmVudCBoYXNoZXMsIHJlcGV0aXRpb24sIGJ1cmllZCBuZXdzIGp1ZGdtZW50LCB0cnVuY2F0aW9uIGVsbGlwc2VzLCBhbmQgYWJzZW50IGVkaXRvcmlhbCB2b2ljZS4gU3VzdGFpbmVkIHdvcmxkIGNhdXNhbGl0eSByZW1haW5zIGFjY2VwdGVkOyBmYW50YXN5LW5ld3NwYXBlciBwcmVzZW50YXRpb24gcXVhbGl0eSByZW1haW5zIHVuYWNjZXB0ZWQgdW50aWwgYSByZXBsYWNlbWVudCBwYWdlIHBhc3NlcyB0aGUgc2FtZSBibGluZCBnYXRlLiIsICJzdGF0dXMiOiAib2siLCAidHMiOiAiMjAyNi0wOC0yN1QwNDowNzo0NiswMDowMCIsICJ0eXBlIjogImNvcnJlY3Rpb24ifQ==", "stored_at": "2026-08-27T04:07:46.779143+00:00", "type": "ghostlight.evidence_record.v0"} {"key": "2026-08-27T075901Z0000-0152-9abad4224fe1", "payload": "eyJicmFuY2giOiAiY29kZXgvZ2hvc3RsaWdodC1kdW5nZW9uLW12cCIsICJub3RlIjogIkEgZnJlc2ggdHdvLXdhdmUgQ29kZXggQ29ubmVjdG9yIHJ1biBhdCBHaG9zdGxpZ2h0IHNvdXJjZSAyZjdjMDkwIGFkdmFuY2VkIG9uZSBjYW1wYWlnbiBmcm9tIHJldmlzaW9uIDAgdG8gMiwgY29tbWl0dGVkIDE5IGV2ZW50cyBhbmQgMTQgbmV3cyByb3dzLCBhbmQgbGVmdCBwbGF5ZXIgc3RhdGUgYW5kIGxvY2F0aW9uIHVuY2hhbmdlZDsgYXJ0aWZhY3RzIGxpdmUgYXQgRjpcXEdhbWVDdWx0XFxHaG9zdGxpZ2h0RHVuZ2VvblxcYWNjZXB0YW5jZVxccm9vdC13aXJlLWZyb250cGFnZS1ibGluZC0yZjdjMDkwLTEuIFRoZSB3b3JsZCB3aXRuZXNzIHN1Y2NlZWRlZCwgYnV0IGEgZnJlc2ggcmV2aWV3ZXIgc2hvd24gb25seSBuZXdzcGFwZXItd2F2ZS0wMi5tZCB1bmRlciBhIG5ldXRyYWwgYnJpZWYgcmVqZWN0ZWQgaXQgYXMgcHJvY2VkdXJhbGx5IGNvbnN0cnVjdGVkIHdpdGggOTkgcGVyY2VudCBjb25maWRlbmNlLiBUaGUgcGFnZSBleHBvc2VkIHRoZSBjYW5vbmljYWwgcmVsYXRpb24gbmFtZSBzeW5vZC1ndWlsZC1jb21tYW5kLCBpdHMgZml2ZS1wb2ludCBudW1lcmljYWwgZGVsdGEsIGFuZCBzb3VyY2UtcmVjb3JkIGxhbmd1YWdlLiBUaGlzIGZhbHNpZmllcyB0aGUgYXNzdW1wdGlvbiB0aGF0IHJlYWRlci1zYWZlIHJlbmRlcmluZyBwbHVzIGEgY29weS1kZXNrIGdhdGUgaXMgc3VmZmljaWVudCB3aGVuIHRoZSBtb2RlbC1mYWNpbmcgbmV3c3Jvb20gcGFja2V0IHN0aWxsIHByZXNlbnRzIG1lY2hhbmljYWwgYmFja2dyb3VuZCByZWNvcmRzIGFzIGVkaXRvcmlhbCBldmlkZW5jZS4gRmFudGFzeS1uZXdzcGFwZXIgcHJlc2VudGF0aW9uIHJlbWFpbnMgdW5hY2NlcHRlZDsgdGhlIG5leHQgY3V0IGJlbG9uZ3MgYXQgbmV3c3Jvb20gcHJvamVjdGlvbiBhbmQgdHlwZWQgc291cmNlIGN1cmF0aW9uLCB3aGlsZSBjYW5vbmljYWwgcHJvdmVuYW5jZSByZW1haW5zIGludGVybmFsIGFuZCBleGFjdC4iLCAic3RhdHVzIjogIm9rIiwgInRzIjogIjIwMjYtMDgtMjdUMDc6NTk6MDErMDA6MDAiLCAidHlwZSI6ICJjb3JyZWN0aW9uIn0=", "stored_at": "2026-08-27T07:59:01.149526+00:00", "type": "ghostlight.evidence_record.v0"} +{"key": "2026-08-27T092657Z0000-0153-626142bc2520", "payload": "eyJicmFuY2giOiAiY29kZXgvZ2hvc3RsaWdodC1kdW5nZW9uLW12cCIsICJub3RlIjogIkV4YWN0IEdob3N0bGlnaHQgY29tbWl0IGE1ODUzOTggY29tcGxldGVkIGEgZnJlc2ggaXNvbGF0ZWQgdHdvLXdhdmUgQ29kZXggQ29ubmVjdG9yIHJ1biB3aXRoIGdwdC01LjYtbHVuYSBhdCBGOlxcR2FtZUN1bHRcXEdob3N0bGlnaHREdW5nZW9uXFxhY2NlcHRhbmNlXFxyb290LXdpcmUtZnJvbnRwYWdlLWJsaW5kLWE1ODUzOTgtMS4gVGhlIGNhbXBhaWduIHJlYWNoZWQgcmV2aXNpb24gMiB3aXRoIDE4IEV2ZW50cyBhbmQgMTUgTmV3c0lzc3VlcyBhbmQgbGVmdCBwbGF5ZXIgc3RhdGUgYW5kIGxvY2F0aW9uIHVuY2hhbmdlZC4gVGhlIGJsaW5kIGVkaXRvciByZWplY3RlZCBuZXdzcGFwZXIubWQgd2l0aCA5OSBwZXJjZW50IGNvbmZpZGVuY2UsIGFuZCB0aGUgaW5kZXBlbmRlbnQgZ3JvdW5kaW5nIGF1ZGl0IHJlamVjdGVkIGl0IHdpdGggMC45NCBjb25maWRlbmNlIGRlc3BpdGUgZXhhY3QgY2l0YXRpb24gbG93ZXJpbmcgYW5kIG5vIHByaXZhdGUtc3RhdGUgbGVha2FnZS4gUHVibGljIEV2ZW50IGFjY291bnRzIHN0aWxsIGV4cG9zZWQgYWJzdHJhY3QgYWRvcHRlZC1wb3N0dXJlIGFuZCByZWxhdGlvbi10cmFuc2l0aW9uIGxhbmd1YWdlOyB0aGUgbmV3c3Jvb20gcHJvbW90ZWQgbWVyZWx5IGludm9sdmVkIGVudGl0eSBhbmQgbG9jYXRpb24gbWV0YWRhdGEgaW50byBhc3NlcnRlZCBuYW1lcyBhbmQgYSBkYXRlbGluZSwgcHJvZHVjaW5nIHVuc3VwcG9ydGVkIFJvb3R2YXVsdCBhbmQgTW9zc2dsYXNzIFJlZ2VuY3kgY2xhaW1zOyBhbmQgcmVzdWx0Lmpzb24gcHJvamVjdGVkIHRoZSB3YXZlLW9uZSBjb21taXQgYXQgdG9wIGxldmVsIGJlc2lkZSB3YXZlLXR3byBjb3VudHMgYW5kIGhlYWQuIFVudG91Y2hlZCByZXZpZXcgaGFzaGVzIGFyZSBibGluZCA4NzdDRjcyRjI2N0MwQzYyM0E3RTJEMUM1QkZEQzAzN0FDNjkyOTI5NkZFMUFBODI5NzA4RTlGMzY4REQ4MjJGIGFuZCBncm91bmRpbmcgQTU5QjBCRUNFNEY4RThEOTNFOUIyOTMyNzE3NjA3MEExQzk2NUU5QzJCM0JGQTFCMEFFRTkxRDQ0OEZGQUFBRS4gVGhpcyBmYWxzaWZpZXMgdGhlIHN1ZmZpY2llbmN5IG9mIHRoZSBmaXJzdCBuZXdzcm9vbSBwcm9qZWN0aW9uIGN1dCB3aGlsZSBwcmVzZXJ2aW5nIHRoZSBhdXRvbm9tb3VzLXdvcmxkIHRyYW5zaXRpb24gd2l0bmVzcy4gRmFudGFzeS1uZXdzcGFwZXIgcHJlc2VudGF0aW9uIHJlbWFpbnMgdW5hY2NlcHRlZC4iLCAic3RhdHVzIjogIm9rIiwgInRzIjogIjIwMjYtMDgtMjdUMDk6MjY6NTcrMDA6MDAiLCAidHlwZSI6ICJjb3JyZWN0aW9uIn0=", "stored_at": "2026-08-27T09:26:57.082626+00:00", "type": "ghostlight.evidence_record.v0"} diff --git a/state/map.yaml b/state/map.yaml index f7a3899..7c04f28 100644 --- a/state/map.yaml +++ b/state/map.yaml @@ -323,6 +323,6 @@ data_flow: - If state grows faster than understanding, cut it back. current_status: - summary: Ghostlight's generic consumer boundary admits sparse WorldSeeds without forged civic runtime state, validates externally owned subject snapshots against the resulting world, runs the same hundreds-class parallel cell machinery used by Dungeon, and exposes an authority-gated newspaper projection over committed NewsIssue rows whose exact cited Events expose the same public channel. Sparse inhabited places compile a versioned civic apparatus and later passes deepen that exact apparatus. Local acceptance proves 1,000 subjects across 200 concurrently dispatched cells, two-wave durable named-person emergence and action, semantic civic receipt binding, and continuing public civic knowledge. Sustained provider acceptance against Ghostlight source 5e00a7bbc3f540ef35d18abe89f84687b5004664 through Codex Connector 2f29b90eebd8fc1bbdc07cdd22ac4883f16849a5 using gpt-5.6-luna advanced one Rainless Marches campaign through four sequential atomic waves from revision 0 to 4, committed 41 unique events and 28 unique news rows, carried named figures across issues, and left player state and location unchanged. That accepted world-simulation and provenance baseline remains valid. A newer isolated two-wave connector run at source 2f7c090 advanced revision 0 to 2, committed 19 events and 14 news rows, left player state and location unchanged, and produced reader pages plus separate audits at F:\GameCult\GhostlightDungeon\acceptance\root-wire-frontpage-blind-2f7c090-1. Its wave-two page failed the blind journalistic gate with 99 percent confidence because model-facing newsroom material leaked canonical relation names, numerical state deltas, and source-record language into otherwise vivid labor and water politics. The presentation boundary is therefore still unaccepted. The current local source cut moves public strategic summaries to human-readable accounts at the event owner, projects only opaque desk citations plus named public facts to the editor, deduplicates identical fact packets, and maps accepted citations back to exact canonical NewsIssue and Event provenance. All 392 library tests and every binary test pass, and the bounded Modeling re-audit is clear; no fresh provider or blind acceptance has yet exercised this cut. Yggdrasil still serves immutable Ghostlight release fdb6352e0dfdd0eeb129020ea9dea0b6225eccf9; Dungeon play remains regression pressure, the Delvehold game-side adapter remains unimplemented, and unpublished Mars and Kalsa destination previews remain unapproved. - next_action: Commit and push the coherent newsroom and public-event cut, then run a fresh isolated exact-commit two-wave newspaper acceptance through Codex Connector with gpt-5.6-luna. Preserve the world, issue, audit, receipt, build, and hash witnesses without mutating the live Ghostlight daemon. Send only the strongest resulting front page to a fresh blind editorial reviewer, preserve that response untouched, and separately audit every factual claim against the committed public events. Do not claim fantasy-newspaper acceptance until both gates pass. Resume the Delvehold game-side adapter through generic contracts only after this presentation boundary is accepted. + summary: Ghostlight's generic consumer boundary admits sparse WorldSeeds without forged civic runtime state, validates externally owned subject snapshots against the resulting world, runs the same hundreds-class parallel cell machinery used by Dungeon, and exposes an authority-gated newspaper projection over committed NewsIssue rows whose exact cited Events expose the same public channel. Local acceptance proves 1,000 subjects across 200 concurrently dispatched cells, two-wave durable named-person emergence and action, semantic civic receipt binding, and continuing public civic knowledge. The accepted four-wave Rainless witness at source 5e00a7b remains the provider world-simulation and provenance baseline. A fresh exact-commit a585398 Codex Connector run with gpt-5.6-luna advanced one isolated campaign through two waves to revision 2, committed 18 Events and 15 NewsIssues, and left player state and location unchanged. Its blind editorial review failed with 99 percent confidence and its grounding audit failed with 0.94 confidence. Public Event language still exposed abstract posture and relation transitions; the newsroom promoted merely involved entity and location metadata into asserted names and a dateline; and the smoke result projected wave-one commit data at top level beside wave-two counts and head. Untouched reviews and hashes live at F:\GameCult\GhostlightDungeon\acceptance\root-wire-frontpage-blind-a585398-1. Fantasy-newspaper presentation remains explicitly unaccepted. A second local cut now changes institution and relation public phrasing, filters model-facing names to names asserted in the public account, suppresses ambiguous multi-place metadata, and projects the final-wave commit at result top level. All 393 library tests and every binary test pass, and the bounded Modeling final review is clear after correcting a name-boundary collision; the cut has no provider acceptance yet. Yggdrasil still serves immutable Ghostlight release fdb6352e0dfdd0eeb129020ea9dea0b6225eccf9; Dungeon play remains regression pressure, the Delvehold game-side adapter remains unimplemented, and unpublished Mars and Kalsa destination previews remain unapproved. + next_action: Commit and push this exact locally verified second cut, then run a fresh isolated exact-commit two-wave newspaper acceptance through Codex Connector with gpt-5.6-luna, preserving world, issue, audit, receipt, build, review, and hash witnesses without mutating the live Ghostlight daemon. Send only the strongest resulting front page to a fresh blind editorial reviewer and separately audit every factual claim against committed public events. Do not claim fantasy-newspaper acceptance until both gates pass. Resume the Delvehold game-side adapter through generic contracts only after this presentation boundary is accepted. gestalt_population: Gestalt Personas own shared population baselines; persistent member deltas own named individuality; WorldKernel materialization and approval-gated fission preserve lineage and identity. Materialisation/folding changes only representation and the exact member delta, advances resolution epoch, and retires the old cover; it has no Gestalt-wide effect authority. Selected actionable member exceptions are mandatory Projector perspective owners, so aggregation cannot silently erase their decision. Presence projection is limited to nearby active leaves and exact-location dormant members, includes reciprocal player relationship evidence, and may propose an earned cast without committing it. Simulation cells are derived covers only.