Build a grounded editorial newspaper

This commit is contained in:
Meta Vens 2026-08-27 06:37:43 +02:00
commit e84872e512
12 changed files with 1445 additions and 248 deletions

View file

@ -67,6 +67,11 @@ async fn main() -> anyhow::Result<()> {
})?;
let newspaper_title = std::env::var("GHOSTLIGHT_STRATEGIC_NEWSPAPER_TITLE")
.unwrap_or_else(|_| "The Underdeep Clarion".into());
let newspaper_voice = std::env::var("GHOSTLIGHT_STRATEGIC_NEWSPAPER_VOICE")
.unwrap_or_else(|_| {
"A sharp regional broadsheet for readers who already understand guild politics: skeptical of every throne, attentive to labor and material consequences, formally reported, and capable of one dry local barb without becoming satire."
.into()
});
let started = Instant::now();
let kernel = WorldKernel::start(store.clone());
let mut wave_reports = Vec::with_capacity(wave_count);
@ -177,15 +182,35 @@ async fn main() -> anyhow::Result<()> {
if issue_campaign.news.is_empty() {
anyhow::bail!("strategic wave {wave_index} produced no gated news")
}
let issue = ghostlight_dungeon::newspaper::compose_world_newspaper(
let issue_composition = ghostlight_dungeon::newspaper::compose_world_newspaper(
model.as_ref(),
&issue_campaign,
format!("{newspaper_title} — Issue {wave_index}"),
24,
)?;
&newspaper_voice,
5,
)
.await?;
for receipt in &issue_composition.model_receipts {
store.insert(
"persona_stage_receipt.v1",
"ghostlight.persona_stage_receipt.v1",
receipt.storage_key(),
receipt,
)?;
}
let issue_path = root.join(format!("newspaper-wave-{wave_index:02}.md"));
let issue_audit_path = root.join(format!("newspaper-wave-{wave_index:02}.audit.md"));
std::fs::write(
&issue_path,
ghostlight_dungeon::newspaper::render_world_newspaper_markdown(&issue),
ghostlight_dungeon::newspaper::render_world_newspaper_markdown(
&issue_composition.issue,
),
)?;
std::fs::write(
&issue_audit_path,
ghostlight_dungeon::newspaper::render_world_newspaper_audit_markdown(
&issue_composition.issue,
),
)?;
wave_reports.push(serde_json::json!({
"wave_index":wave_index,
@ -197,8 +222,11 @@ async fn main() -> anyhow::Result<()> {
"rejected_pulses":rejected_pulses,
"plan":plan,
"commit":committed,
"issue":issue,
"issue":issue_composition.issue,
"newspaper_grounding":issue_composition.grounding,
"newspaper_model_receipts":issue_composition.model_receipts,
"issue_path":issue_path,
"issue_audit_path":issue_audit_path,
}));
campaign = advanced.clone();
std::fs::write(
@ -215,15 +243,35 @@ async fn main() -> anyhow::Result<()> {
}))?,
)?;
}
let newspaper = ghostlight_dungeon::newspaper::compose_world_newspaper(
let newspaper_composition = ghostlight_dungeon::newspaper::compose_world_newspaper(
model.as_ref(),
&campaign,
&newspaper_title,
wave_count.saturating_mul(24),
)?;
&newspaper_voice,
6,
)
.await?;
for receipt in &newspaper_composition.model_receipts {
store.insert(
"persona_stage_receipt.v1",
"ghostlight.persona_stage_receipt.v1",
receipt.storage_key(),
receipt,
)?;
}
let newspaper_path = root.join("newspaper.md");
let newspaper_audit_path = root.join("newspaper.audit.md");
std::fs::write(
&newspaper_path,
ghostlight_dungeon::newspaper::render_world_newspaper_markdown(&newspaper),
ghostlight_dungeon::newspaper::render_world_newspaper_markdown(
&newspaper_composition.issue,
),
)?;
std::fs::write(
&newspaper_audit_path,
ghostlight_dungeon::newspaper::render_world_newspaper_audit_markdown(
&newspaper_composition.issue,
),
)?;
let first_plan = wave_reports[0]["plan"].clone();
let first_commit = wave_reports[0]["commit"].clone();
@ -239,7 +287,7 @@ async fn main() -> anyhow::Result<()> {
})
.collect::<Vec<_>>();
let result = serde_json::json!({
"schema":"ghostlight.live_strategic_smoke.v2",
"schema":"ghostlight.live_strategic_smoke.v3",
"scenario_id":scenario_id,
"pressure":pressure,
"wave_count":wave_count,
@ -253,8 +301,11 @@ async fn main() -> anyhow::Result<()> {
"waves":wave_reports,
"event_count":campaign.events.len(),
"news_count":campaign.news.len(),
"newspaper":newspaper,
"newspaper":newspaper_composition.issue,
"newspaper_grounding":newspaper_composition.grounding,
"newspaper_model_receipts":newspaper_composition.model_receipts,
"newspaper_path":newspaper_path,
"newspaper_audit_path":newspaper_audit_path,
"player_location_unchanged":true,
"player_state_unchanged":true,
"store":root.join("campaign.cc")

View file

@ -320,7 +320,9 @@ pub struct WorldNewspaperRequest {
pub owner_id: String,
pub authority_key: String,
pub title: String,
#[schemars(range(min = 1, max = 64))]
#[schemars(length(min = 1, max = 600))]
pub editorial_voice: String,
#[schemars(range(min = 1, max = 6))]
pub max_articles: u16,
}

View file

@ -5757,8 +5757,8 @@ pub(crate) mod tests {
let action = CellActionProposal {
subject_id: "refugees-east".into(),
intent: "coordinate the households' own water watch".into(),
intended_effect: "coordinate internal water-watch shifts without naming an external party"
.into(),
intended_effect:
"coordinate internal water-watch shifts without naming an external party".into(),
priority: 80,
state_references: vec![],
public_channels: vec![],
@ -5793,7 +5793,11 @@ pub(crate) mod tests {
.unwrap();
assert_eq!(canonical_events.len(), 1);
assert_eq!(canonical_events[0].kind, "gestalt_activity");
assert!(canonical_events[0].summary.contains("internal water-watch shifts"));
assert!(
canonical_events[0]
.summary
.contains("internal water-watch shifts")
);
let mut legacy = hierarchical_refugee_campaign();
let legacy_events = apply_strategic_tick_plan(
@ -9743,16 +9747,6 @@ pub(crate) mod tests {
crate::domain::committed_news_headline(&public_individuation.summary)
);
assert!(public_issue.headline.contains("Veska Rill"));
let newspaper =
crate::newspaper::compose_world_newspaper(&campaign, "The Underdeep Clarion", 8)
.unwrap();
let rendered = crate::newspaper::render_world_newspaper_markdown(&newspaper);
assert!(rendered.contains("Names to Know"));
assert!(rendered.contains("Veska Rill"));
assert!(
rendered.contains("selling the broken lower road twice"),
"{rendered}"
);
let proposals = store
.load_all::<StrategicGestaltIndividuation>("strategic_gestalt_individuation.v1")
.unwrap();

View file

@ -594,8 +594,8 @@ fn schema_catalog() -> Value {
"ghostlight.external_proposal_list.v1": schemars::schema_for!(crate::consumer::ExternalProposalList),
"ghostlight.external_proposal_acknowledgement.v1": schemars::schema_for!(crate::consumer::ExternalProposalAcknowledgement),
"ghostlight.external_proposal_receipt.v1": schemars::schema_for!(crate::consumer::ExternalProposalReceipt),
"ghostlight.world_newspaper_request.v1": schemars::schema_for!(crate::consumer::WorldNewspaperRequest),
"ghostlight.world_newspaper_issue.v1": schemars::schema_for!(crate::newspaper::WorldNewspaperIssue)
"ghostlight.world_newspaper_request.v2": schemars::schema_for!(crate::consumer::WorldNewspaperRequest),
"ghostlight.world_newspaper_issue.v2": schemars::schema_for!(crate::newspaper::WorldNewspaperIssue)
})
}
@ -627,8 +627,8 @@ mod tests {
assert!(schemas["ghostlight.campaign.v1"]["$schema"].is_string());
assert!(schemas["ghostlight.session_zero.v1"]["$schema"].is_string());
assert!(schemas["ghostlight.vault_source_manifest.v1"]["$schema"].is_string());
assert!(schemas["ghostlight.world_newspaper_request.v1"]["$schema"].is_string());
assert!(schemas["ghostlight.world_newspaper_issue.v1"]["$schema"].is_string());
assert!(schemas["ghostlight.world_newspaper_request.v2"]["$schema"].is_string());
assert!(schemas["ghostlight.world_newspaper_issue.v2"]["$schema"].is_string());
assert!(!schemas.contains_key("ghostlight.persona_stage_receipt.v1"));
assert!(!schemas.contains_key("ghostlight.world_mutation.v1"));
drop(catalog);

View file

@ -304,12 +304,19 @@ async fn handle_operation_inner(
success_response(request, "ghostlight.external_proposal_receipt.v1", &receipt)
}
ghostlight_dungeon::consumer::COMPOSE_NEWSPAPER_OPERATION => {
require_schema(payload_schema, "ghostlight.world_newspaper_request.v1")?;
require_schema(payload_schema, "ghostlight.world_newspaper_request.v2")?;
let command: ghostlight_dungeon::consumer::WorldNewspaperRequest =
decode_request(request)?;
require_schema(&command.schema, "ghostlight.world_newspaper_request.v1")?;
let issue = state.registry.compose_world_newspaper(command).await?;
success_response(request, "ghostlight.world_newspaper_issue.v1", &issue)
require_schema(&command.schema, "ghostlight.world_newspaper_request.v2")?;
let model = state
.model
.as_deref()
.ok_or_else(|| anyhow::anyhow!("newspaper editor is unavailable"))?;
let issue = state
.registry
.compose_world_newspaper(command, model)
.await?;
success_response(request, "ghostlight.world_newspaper_issue.v2", &issue)
}
_ => bail!("world consumer operation is not advertised by Ghostlight"),
};
@ -513,6 +520,24 @@ mod tests {
));
}
#[test]
fn native_newspaper_operation_rejects_the_retired_v1_schema() {
assert!(
require_schema(
"ghostlight.world_newspaper_request.v1",
"ghostlight.world_newspaper_request.v2"
)
.is_err()
);
assert!(
require_schema(
"ghostlight.world_newspaper_request.v2",
"ghostlight.world_newspaper_request.v2"
)
.is_ok()
);
}
#[test]
fn native_transport_delivers_large_response_after_long_keepalive_only_operation() {
let server_socket = UdpSocket::bind("127.0.0.1:0").unwrap();

File diff suppressed because it is too large Load diff

View file

@ -8,7 +8,7 @@ use crate::{
},
domain::{Campaign, CampaignLifecycleReceipt, VaultEvidenceReceipt},
kernel::WorldKernel,
model::ModelStageReceipt,
model::{ModelPort, ModelStageReceipt},
persistence::CampaignStore,
session_zero::PublishedSessionZeroSeed,
};
@ -379,10 +379,14 @@ impl CampaignRegistry {
pub async fn compose_world_newspaper(
&self,
request: WorldNewspaperRequest,
model: &dyn ModelPort,
) -> Result<crate::newspaper::WorldNewspaperIssue> {
if request.schema != "ghostlight.world_newspaper_request.v1"
if request.schema != "ghostlight.world_newspaper_request.v2"
|| request.max_articles == 0
|| request.max_articles > 64
|| request.max_articles > 6
|| request.editorial_voice.trim().is_empty()
|| request.editorial_voice.trim() != request.editorial_voice
|| request.editorial_voice.chars().count() > 600
{
return Err(anyhow!("world newspaper request is invalid"));
}
@ -405,11 +409,23 @@ impl CampaignRegistry {
.load::<Campaign>("campaign.v1", &request.campaign_id.to_string())?
.ok_or_else(|| anyhow!("campaign is missing"))?
.1;
crate::newspaper::compose_world_newspaper(
let composition_result = crate::newspaper::compose_world_newspaper(
model,
&campaign,
request.title,
request.editorial_voice,
usize::from(request.max_articles),
)
.await;
if let Err(error) = &composition_result
&& let Some(failure) =
error.downcast_ref::<crate::newspaper::WorldNewspaperCompositionFailure>()
{
persist_newspaper_model_receipts(&runtime, &failure.model_receipts)?;
}
let composition = composition_result?;
persist_newspaper_model_receipts(&runtime, &composition.model_receipts)?;
Ok(composition.issue)
}
pub async fn acknowledge_external_proposal(
@ -639,6 +655,42 @@ impl CampaignRegistry {
}
}
fn persist_newspaper_model_receipts(
runtime: &CampaignRuntime,
receipts: &[ModelStageReceipt],
) -> Result<()> {
for receipt in receipts {
if let Some((_, existing)) = runtime
.store
.load::<ModelStageReceipt>("persona_stage_receipt.v1", receipt.storage_key())?
{
if existing.same_receipted_content(receipt) {
continue;
}
return Err(anyhow!(
"immutable newspaper model receipt conflict: {}",
receipt.storage_key()
));
}
let inserted = runtime.store.insert(
"persona_stage_receipt.v1",
"ghostlight.persona_stage_receipt.v1",
receipt.storage_key(),
receipt,
);
if let Err(error) = inserted {
let repeated = runtime
.store
.load::<ModelStageReceipt>("persona_stage_receipt.v1", receipt.storage_key())?
.is_some_and(|(_, existing)| existing.same_receipted_content(receipt));
if !repeated {
return Err(error);
}
}
}
Ok(())
}
fn cleanup_staging_directory(root: &Path, staging: &Path) {
let safe = staging.parent() == Some(root)
&& staging
@ -664,12 +716,89 @@ fn validated_branch_name(name: String) -> Result<String> {
mod tests {
use super::*;
use crate::domain::{ActorState, BranchOrigin, Location, WorldCommitReceipt};
use crate::model::ModelStageRequest;
use crate::session_zero::{
ApprovedCampaignBrief, CampaignContract, CampaignDmPersona, CampaignGovernance,
CampaignMember, CampaignMembership, CharacterDraft,
};
use async_trait::async_trait;
use std::collections::{BTreeMap, BTreeSet};
struct RegistryNewspaperModel;
struct RejectingRegistryNewspaperModel;
fn registry_newspaper_editor_response(request: &ModelStageRequest) -> Result<String> {
let source_id = request
.output_schema
.as_ref()
.and_then(|schema| {
schema
.pointer("/$defs/EditorialArticleDraft/properties/source_news_ids/items/enum/0")
})
.and_then(serde_json::Value::as_str)
.ok_or_else(|| anyhow!("newspaper fixture lost its source enum"))?;
Ok(serde_json::json!({
"edition_label":"Court Edition",
"articles":[{
"section":"Front Page",
"headline":"Court Auditors Face a Singular Accusation",
"deck":"The inner court has turned a granary inquiry into a question of witchcraft and arithmetic.",
"byline":"By the political editor",
"dateline":"Room",
"source_news_ids":[source_id],
"paragraphs":[
"The inner court has accused three granary auditors of being one witch in a long coat, placing the allegation into the public record.",
"The charge leaves the auditors answering a court that has made one person out of three, at least for purposes of blame."
]
}]
})
.to_string())
}
#[async_trait]
impl ModelPort for RegistryNewspaperModel {
async fn run(&self, request: &ModelStageRequest) -> Result<String> {
if request.stage == "newspaper_copy_desk" {
return Ok(serde_json::json!({
"accepted":true,
"assessment":"The page is grounded in its cited public report.",
"findings":[]
})
.to_string());
}
registry_newspaper_editor_response(request)
}
fn provider(&self) -> &'static str {
"registry-newspaper-fixture"
}
}
#[async_trait]
impl ModelPort for RejectingRegistryNewspaperModel {
async fn run(&self, request: &ModelStageRequest) -> Result<String> {
if request.stage == "newspaper_copy_desk" {
return Ok(serde_json::json!({
"accepted":false,
"assessment":"The article states an outcome that its source does not support.",
"findings":[{
"article_index":0,
"category":"unsupported_fact",
"claim_or_phrase":"the allegation is proven",
"reason":"The cited report records only an accusation."
}]
})
.to_string());
}
registry_newspaper_editor_response(request)
}
fn provider(&self) -> &'static str {
"registry-newspaper-fixture"
}
}
fn seed(name: &str) -> Campaign {
let actor = ActorState {
id: "player".into(),
@ -1266,24 +1395,25 @@ mod tests {
let (_, repeated) = registry.admit_world_seed(request).await.unwrap();
assert_eq!(repeated.seed_digest, receipt.seed_digest);
let newspaper_request = crate::consumer::WorldNewspaperRequest {
schema: "ghostlight.world_newspaper_request.v1".into(),
schema: "ghostlight.world_newspaper_request.v2".into(),
campaign_id: campaign.id,
authority_id: "authority:external-hold".into(),
owner_id: "fixture-consumer".into(),
authority_key: authority_key.into(),
title: "The Consumer Gazette".into(),
max_articles: 12,
editorial_voice: "A skeptical regional court broadsheet.".into(),
max_articles: 4,
};
let mut denied_newspaper = newspaper_request.clone();
denied_newspaper.authority_key = "wrong-key".into();
assert!(
registry
.compose_world_newspaper(denied_newspaper)
.compose_world_newspaper(denied_newspaper, &RegistryNewspaperModel)
.await
.is_err()
);
let newspaper = registry
.compose_world_newspaper(newspaper_request.clone())
.compose_world_newspaper(newspaper_request.clone(), &RegistryNewspaperModel)
.await
.unwrap();
assert_eq!(newspaper.source_world_revision, 0);
@ -1500,7 +1630,7 @@ mod tests {
.await
.unwrap();
let newspaper = registry
.compose_world_newspaper(newspaper_request)
.compose_world_newspaper(newspaper_request.clone(), &RegistryNewspaperModel)
.await
.unwrap();
assert_eq!(newspaper.source_world_revision, 3);
@ -1511,9 +1641,57 @@ mod tests {
);
assert!(
newspaper.articles[0]
.body
.paragraphs
.join(" ")
.contains("one witch in a long coat")
);
let successful_receipts = runtime.store.keys("persona_stage_receipt.v1").unwrap();
assert!(
newspaper
.editorial_receipt_ids
.iter()
.all(|receipt_id| successful_receipts.contains(receipt_id))
);
let repeated_newspaper = registry
.compose_world_newspaper(newspaper_request.clone(), &RegistryNewspaperModel)
.await
.unwrap();
assert_eq!(repeated_newspaper.id, newspaper.id);
assert_eq!(
runtime
.store
.keys("persona_stage_receipt.v1")
.unwrap()
.len(),
successful_receipts.len()
);
let receipt_count_before_rejection = successful_receipts.len();
let error = registry
.compose_world_newspaper(newspaper_request, &RejectingRegistryNewspaperModel)
.await
.unwrap_err();
let failure = error
.downcast_ref::<crate::newspaper::WorldNewspaperCompositionFailure>()
.expect("registry must preserve the typed terminal editorial failure");
assert_eq!(failure.model_receipts.len(), 6);
let stored_after_rejection = runtime.store.keys("persona_stage_receipt.v1").unwrap();
let newly_observed_failure_receipts = failure
.model_receipts
.iter()
.map(|receipt| receipt.storage_key().to_owned())
.filter(|receipt_id| !successful_receipts.contains(receipt_id))
.collect::<BTreeSet<_>>();
assert_eq!(
stored_after_rejection.len(),
receipt_count_before_rejection + newly_observed_failure_receipts.len()
);
assert!(
failure.model_receipts.iter().all(|receipt| {
stored_after_rejection.contains(&receipt.storage_key().to_owned())
})
);
let proposal = ExternalWorldProposal {
schema: "ghostlight.external_world_proposal.v1".into(),
id: "external-proposal:test".into(),

View file

@ -434,11 +434,14 @@ producers share one admitted `WorldSeed` transaction; externally authoritative
institutions or Gestalts enter through revisioned WorldKernel snapshots; and strategic
actions aimed at them persist typed proposals for consumer acknowledgement over
the existing loopback CultNet RUDP operation server. Consumers can request an
authority-gated `ghostlight.world.newspaper.compose` projection. It renders
committed `NewsIssue` rows only when every cited committed `Event` exposes the
same public channel. Headline, channel, and reliability remain news-owned;
body, dateline, event IDs, and source revision remain event-grounded
provenance.
authority-gated `ghostlight.world.newspaper.compose` projection. It admits only
committed `NewsIssue` rows whose cited committed `Event` rows expose the same
public channel, then gives selection, grouping, tone, and copy to a bounded
editor model. A local validator and separate grounding copy desk gate the
proposal. Successful and identity-bound terminally rejected attempts persist
their model receipts idempotently. Reader Markdown escapes every plain-text field and omits source IDs,
channels, reliability, revision, and receipt IDs; a separate audit projection
retains that provenance. No editorial organ can write world state.
The scale and emergence gate is implemented and locally proven.
Delvehold is a consumer of Ghostlight's generic authored-world API, not a
@ -472,8 +475,9 @@ ran through Codex Connector
and admitted caller `ghostlight-dungeon-yggdrasil`. Four sequential atomic waves
advanced one campaign from revision 0 through 4, committed 41 unique events and
28 unique news rows, and
rendered 23 distinct provenance-bound articles across four independently
reviewed issues. Ilyra Quill appears in every issue; Mara's testimony persists
rendered 23 distinct provenance-bound articles across four issues reviewed for
political continuity and exact ID provenance; this was not a blind journalistic
review. Ilyra Quill appears in every issue; Mara's testimony persists
into later institutional postures; Tavia and Mara Venn emerge as accountable
organizers. One rejected wave-one pulse did not mutate the world. Player state
and location are unchanged, stderr is empty, and every copied artifact matches
@ -482,16 +486,22 @@ 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.
1. Have the game-side adapter lower its authored hierarchy into the published
1. Produce one reader-facing fantasy front page without runtime or debug framing
while retaining provenance separately.
2. Dispatch a blind reviewer with only that page and a neutral editorial brief;
preserve its untouched response.
3. Independently audit the accepted editorial copy against committed public
events before treating presentation quality as accepted.
4. Have the game-side adapter lower its authored hierarchy into the published
generic schemas; keep projection and economy translation consumer-owned.
2. Prove one consumer-owned effect crosses that public API, changes two foreign
5. Prove one consumer-owned effect crosses that public API, changes two foreign
political layers, causes one durable named figure to emerge and later act,
and returns attributed news or intent without mutating Delvehold-owned truth.
3. Measure provider cost and continuity on the existing 200-cell authored-world
6. Measure provider cost and continuity on the existing 200-cell authored-world
path without coupling wave width to the provider concurrency ceiling.
4. Leave the unpublished Mars seed and Kalsa refuge expansion unapproved unless
7. Leave the unpublished Mars seed and Kalsa refuge expansion unapproved unless
the operator explicitly admits either; retain Kalsa as regression evidence.
5. Keep `gamecult-ops` synchronized with every executable deployment and model
8. Keep `gamecult-ops` synchronized with every executable deployment and model
transport change. Use Idunn on Yggdrasil for all builds and deployments.
Do not weaken actor custody, private projection, unanimous approval,

View file

@ -930,10 +930,24 @@ cell membranes under a provider concurrency gate. Action-bound individuation
may create one consequential named person through the existing Gestalt member
commit primitive; that person enters later covers as the same canonical actor.
Public action channels produce committed `NewsIssue` records. The world-
consumer boundary can compose those records into a typed newspaper issue whose
articles retain exact event IDs, channels, reliability, and source revision.
Newspaper prose is derived from committed event summaries and cannot introduce
an event the kernel did not commit.
consumer boundary admits an authority-gated editorial-voice request, then
projects only those public issues and their cited committed events into a
bounded newsroom source desk. A capable editor model owns selection, grouping,
headline, deck, byline class, dateline, and article prose as a proposal. Local
validation binds every article to exact source-news IDs, known places, the
front-page budget, and reader-safe presentation labels. A separate copy-desk
stage may reject unsupported facts, unearned attribution, procedural leakage,
or mechanical state-ledger copy but cannot rewrite it. One same-source editor
correction is allowed; persistent failure returns no edition. The typed issue
retains exact event IDs, channels, reliability, source revision, and model
receipts as audit data. Terminally rejected attempts semantically rebind their
otherwise immutable model receipts to a collision-free invalid disposition and
return a typed failure carrying every completed receipt. The registry persists
those receipts idempotently before returning the rejection. 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
registry receipt persistence can write world state.
Session Zero compilation, expansion, and fission follow the same projection
rule. Review projections expose topology, pressures, source-use coverage, gaps,

View file

@ -148,4 +148,5 @@
{"branch": "codex/ghostlight-dungeon-mvp", "note": "Supersedes the blocking force of the 2026-08-26T21:01:00 DeepSeek HTTP-402 record, not its historical facts. Provider-backed Rainless Marches acceptance completed against Ghostlight source 8940efca0ff669f4681c99a7ff371fa8f0b76516 through Codex Connector 2f29b90eebd8fc1bbdc07cdd22ac4883f16849a5, model gpt-5.6-luna, admitted caller ghostlight-dungeon-yggdrasil. In 65.2456 seconds, four cell pipelines selected four actions across 22 stages; one semantic-invalid verifier result received a valid correction. The atomic result committed two institution actions, one actor communication, one Gestalt communication, two activity outcomes, six events, and five NewsIssue-backed newspaper articles while leaving the player unchanged. thornweald-assembly-convenor emerged and materialized. Local witnesses: F:\\GameCult\\GhostlightDungeon\\acceptance\\rainless-codex-20260827\\result.json, newspaper.md, campaign.cc, SHA256SUMS. Remote root: /var/lib/gamecult/ghostlight-dungeon/acceptance/rainless-codex-20260827/. SHA-256: result 964a306287da03c7c92f41eb025d8049c63673e2dd8e1bffdd29490fe61315c5; newspaper 6577b4abea3cc5e9ddd625676c3b4728d1dd8b25e3974f2f5112dc4dafd076ca; campaign dd19ee14e2a014a02cf142ebcbde224e3034f2e11a4f34721ef00471005c6569; binary 35e62eb71d0ead30e7e7aa860b04e7757e6f3d9f83b034f8fee6bc06cc38332f. Earlier connector attempts failed on harness identity or produced valid semantic rejection and do not weaken this witness.", "status": "accepted", "ts": "2026-08-26T22:31:43+00:00", "type": "live_acceptance"}
{"branch": "codex/ghostlight-dungeon-mvp", "note": "Two rejected sustained Rainless attempts falsified split source authority without committing wave 1. At dc5787e, cohesive Gestalt internal Coordinate was admitted by the owner-specific schema, prompt, and verifier contract but rejected by Persona, resolution, and kernel through a generic target rule; b98ca9a aligned that owner-specific path while keeping actor and named-member targeting strict. At b98ca9a, the canonical institution posture bound was 460 across schema and field validators, but resolution::substantive_text_change silently imposed 240 and resolution plus kernel called it after their explicit bound; 5e00a7b removed length authority from the generic semantic helper while retaining institution 460 and member-outcome 240 at their field owners. Full verification passed 384 library, 43 daemon, 4 agency-corpus, and 1 scale tests. The failed provider waves left world revision and fictional time unchanged.", "status": "ok", "ts": "2026-08-27T03:20:13+00:00", "type": "correction"}
{"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"}
{"ts": "2026-08-27T03:25:41+00:00", "type": "correction", "status": "ok", "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.", "branch": "codex/ghostlight-dungeon-mvp"}
{"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"}
{"ts": "2026-08-27T04:07:46+00:00", "type": "correction", "status": "ok", "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.", "branch": "codex/ghostlight-dungeon-mvp"}

View file

@ -150,3 +150,4 @@
{"key": "2026-08-27T032013Z0000-0148-713f0fa4da5a", "payload": "eyJicmFuY2giOiAiY29kZXgvZ2hvc3RsaWdodC1kdW5nZW9uLW12cCIsICJub3RlIjogIlR3byByZWplY3RlZCBzdXN0YWluZWQgUmFpbmxlc3MgYXR0ZW1wdHMgZmFsc2lmaWVkIHNwbGl0IHNvdXJjZSBhdXRob3JpdHkgd2l0aG91dCBjb21taXR0aW5nIHdhdmUgMS4gQXQgZGM1Nzg3ZSwgY29oZXNpdmUgR2VzdGFsdCBpbnRlcm5hbCBDb29yZGluYXRlIHdhcyBhZG1pdHRlZCBieSB0aGUgb3duZXItc3BlY2lmaWMgc2NoZW1hLCBwcm9tcHQsIGFuZCB2ZXJpZmllciBjb250cmFjdCBidXQgcmVqZWN0ZWQgYnkgUGVyc29uYSwgcmVzb2x1dGlvbiwgYW5kIGtlcm5lbCB0aHJvdWdoIGEgZ2VuZXJpYyB0YXJnZXQgcnVsZTsgYjk4Y2E5YSBhbGlnbmVkIHRoYXQgb3duZXItc3BlY2lmaWMgcGF0aCB3aGlsZSBrZWVwaW5nIGFjdG9yIGFuZCBuYW1lZC1tZW1iZXIgdGFyZ2V0aW5nIHN0cmljdC4gQXQgYjk4Y2E5YSwgdGhlIGNhbm9uaWNhbCBpbnN0aXR1dGlvbiBwb3N0dXJlIGJvdW5kIHdhcyA0NjAgYWNyb3NzIHNjaGVtYSBhbmQgZmllbGQgdmFsaWRhdG9ycywgYnV0IHJlc29sdXRpb246OnN1YnN0YW50aXZlX3RleHRfY2hhbmdlIHNpbGVudGx5IGltcG9zZWQgMjQwIGFuZCByZXNvbHV0aW9uIHBsdXMga2VybmVsIGNhbGxlZCBpdCBhZnRlciB0aGVpciBleHBsaWNpdCBib3VuZDsgNWUwMGE3YiByZW1vdmVkIGxlbmd0aCBhdXRob3JpdHkgZnJvbSB0aGUgZ2VuZXJpYyBzZW1hbnRpYyBoZWxwZXIgd2hpbGUgcmV0YWluaW5nIGluc3RpdHV0aW9uIDQ2MCBhbmQgbWVtYmVyLW91dGNvbWUgMjQwIGF0IHRoZWlyIGZpZWxkIG93bmVycy4gRnVsbCB2ZXJpZmljYXRpb24gcGFzc2VkIDM4NCBsaWJyYXJ5LCA0MyBkYWVtb24sIDQgYWdlbmN5LWNvcnB1cywgYW5kIDEgc2NhbGUgdGVzdHMuIFRoZSBmYWlsZWQgcHJvdmlkZXIgd2F2ZXMgbGVmdCB3b3JsZCByZXZpc2lvbiBhbmQgZmljdGlvbmFsIHRpbWUgdW5jaGFuZ2VkLiIsICJzdGF0dXMiOiAib2siLCAidHMiOiAiMjAyNi0wOC0yN1QwMzoyMDoxMyswMDowMCIsICJ0eXBlIjogImNvcnJlY3Rpb24ifQ==", "stored_at": "2026-08-27T03:20:13.592200+00:00", "type": "ghostlight.evidence_record.v0"}
{"key": "2026-08-27T032013Z0000-0149-83d223b59e0a", "payload": "eyJicmFuY2giOiAiY29kZXgvZ2hvc3RsaWdodC1kdW5nZW9uLW12cCIsICJub3RlIjogIkFjY2VwdGVkIHN1c3RhaW5lZCBwcm92aWRlci1iYWNrZWQgUmFpbmxlc3MgTWFyY2hlcyB3aXRuZXNzIGF0IGV4YWN0IEdob3N0bGlnaHQgc291cmNlIDVlMDBhN2JiYzNmNTQwZWYzNWQxOGFiZTg5Zjg0Njg3YjUwMDQ2NjQgdGhyb3VnaCBDb2RleCBDb25uZWN0b3IgMmYyOWI5MGVlYmQ4ZmMxYmJkYzA3Y2RkMjJhYzQ4ODNmMTY4NDlhNSwgbW9kZWwgZ3B0LTUuNi1sdW5hLCBjYWxsZXIgZ2hvc3RsaWdodC1kdW5nZW9uLXlnZ2RyYXNpbC4gRm91ciBpc29sYXRlZCB3YXZlcyBhZHZhbmNlZCByZXZpc2lvbnMgMCB0aHJvdWdoIDQgaW4gMzUzLjI2MTUgc2Vjb25kcywgY29tbWl0dGVkIDQxIHVuaXF1ZSBldmVudHMgYW5kIDI4IHVuaXF1ZSBuZXdzIHJvd3MsIGFuZCByZW5kZXJlZCAyMyB1bmlxdWUgcHJvdmVuYW5jZS1ib3VuZCBhcnRpY2xlcyBhY3Jvc3MgZm91ciBpc3N1ZXMgKDksIDYsIDQsIDQpLiBPbmUgd2F2ZS0xIGNhbmRpZGF0ZSBwdWxzZSB3YXMgcmVqZWN0ZWQgYmVmb3JlIHRoZSBhY2NlcHRlZCByZXRyeTsgbGF0ZXIgd2F2ZXMgY29tbWl0dGVkIHdpdGhvdXQgcmVqZWN0aW9uIGFuZCBzdGRlcnIgcmVtYWluZWQgZW1wdHkuIElseXJhIFF1aWxsIGFwcGVhcnMgaW4gZXZlcnkgaXNzdWUsIE1hcmEgdGVzdGltb255IHBlcnNpc3RzIGludG8gbGF0ZXIgaW5zdGl0dXRpb25hbCBwb3N0dXJlcywgYW5kIFRhdmlhIHBsdXMgTWFyYSBWZW5uIGVtZXJnZSBhcyBhY2NvdW50YWJsZSBvcmdhbml6ZXJzLiBQbGF5ZXIgc3RhdGUgYW5kIGxvY2F0aW9uIGFyZSB1bmNoYW5nZWQuIEluZGVwZW5kZW50IGFydGlmYWN0IHJldmlldyBmb3VuZCBkaXN0aW5jdCBzdWNjZXNzaXZlIGRldmVsb3BtZW50cyBhbmQgZXhhY3QgdW5pcXVlIGFydGljbGUvZXZlbnQvbmV3cyBJRHMuIExvY2FsIGltbXV0YWJsZSB3aXRuZXNzOiBGOlxcR2FtZUN1bHRcXEdob3N0bGlnaHREdW5nZW9uXFxhY2NlcHRhbmNlXFxyYWlubGVzcy1zcGljeS0yMDI2MDgyNy1zdXN0YWluZWQtNTsgcmVtb3RlIHJvb3Q6IC92YXIvbGliL2dhbWVjdWx0L2dob3N0bGlnaHQtZHVuZ2Vvbi9hY2NlcHRhbmNlL3JhaW5sZXNzLXNwaWN5LTIwMjYwODI3LXN1c3RhaW5lZC01LiBTSEEtMjU2OiByZXN1bHQgMTliYWY1ODgyZDYwZmZiZmZlMmY1ZTE2MWJhMWQ5MzI4NTg4MjI4ZmNiOTg1NWUzM2ZiZmFhMmNjN2U1NzEwODsgY29tYmluZWQgbmV3c3BhcGVyIDBlNGU1MjBiOTQ1ZmJhZTAyMjYwYTdjYzA0ZWViMjgzNWUxZGRiYjlhYTU3YzU1YTY0MjZmMzk2ZGJiMWU2NmM7IGNhbXBhaWduIGJjNGM1YWUyZTg5YjRiY2FlNDgzZjNhZDkyOTdkYTE4MGUxYTZjMDU2MjE4NTM3ZjYxYmI1OTIwMmJhMjVlMjk7IHByb3ZlbmFuY2UgNjgyZjdkMzYyNDdlYjc0Y2JjN2M1ZThhNmY1OGQxNWQzYjljMDNmYTc0MWI5YzdjZTFiYzcxYjkyNmJlMTdiZi4gVGhpcyBwcm92ZXMgc3VzdGFpbmVkIHBvbGl0aWNzIGluIGEgZm91ci1jZWxsIHNwYXJzZSB3b3JsZCwgbm90IGEgbGl2ZSAyMDAtY2VsbCBwcm92aWRlciB3YXZlIG9yIHRoZSBEZWx2ZWhvbGQgZ2FtZS1zaWRlIGFkYXB0ZXIuIiwgInN0YXR1cyI6ICJhY2NlcHRlZCIsICJ0cyI6ICIyMDI2LTA4LTI3VDAzOjIwOjEzKzAwOjAwIiwgInR5cGUiOiAibGl2ZV9hY2NlcHRhbmNlIn0=", "stored_at": "2026-08-27T03:20:13.757727+00:00", "type": "ghostlight.evidence_record.v0"}
{"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"}

View file

@ -193,6 +193,7 @@ live_architecture:
- Institution, faction, and consumer decision models
active_subgoals:
- Earn blind fantasy-newspaper acceptance: a separately dispatched reviewer receives only the rendered front page under a neutral editorial brief, finds it journalistically plausible, and does not independently identify it as procedural projection; a separate provenance audit confirms every factual claim remains grounded in committed public events.
- Connect a Delvehold-owned adapter to the admitted generic consumer contract: shared compiler/consumer WorldSeed admission, externally authoritative institution-or-Gestalt snapshots, attributed proposal acknowledgement, and committed-event newspaper composition over typed CultNet, without consumer-specific IDs or schemas in Ghostlight.
- Extend strategic/offscreen named-person individuation beyond the proven emerge-then-act path so people plot across sustained political waves, acquire relationships, fold, and return without granting a cell, arena, or model commit authority.
- Pressure the proven 200-cell dispatcher with authored consumer hierarchies and measure provider cost; the active budget ceiling is 240 while provider concurrency remains separately capped at 32.
@ -322,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. Headlines, channel, and reliability come from committed news state; article bodies, datelines, and event provenance come from the cited committed events. 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. It committed 41 unique events and 28 unique news rows, rendered 23 distinct provenance-bound articles across four issues, carried Ilyra Quill through every issue and Mara's testimony into later institutional postures, retained one rejected pre-commit pulse, and left player state and location unchanged. Full receipts, newspapers, state, provenance, and hashes live in the accepted evidence record and F:\GameCult\GhostlightDungeon\acceptance\rainless-spicy-20260827-sustained-5\SHA256SUMS. This proves sustained politics in a sparse provider world whose derived cover grew from four cells to six as named figures emerged, not a live 200-cell provider wave or the Delvehold game-side adapter. Yggdrasil still serves immutable Ghostlight release fdb6352e0dfdd0eeb129020ea9dea0b6225eccf9; Dungeon play remains regression pressure, and unpublished Mars and Kalsa destination previews remain unapproved.
next_action: Treat the accepted four-revision Rainless witness as the provider regression baseline. Next have the Delvehold game-side adapter lower its authored hierarchy into the published generic WorldSeed, external-subject, proposal, acknowledgement, and newspaper contracts. Then prove one consumer-owned Greathold effect crosses that API, changes two foreign political layers, causes one durable named figure to emerge and later act, and returns attributed news or intent without mutating Delvehold-owned player or quantitative-economy truth. Pressure the existing 200-cell path with provider cost measurement separately; keep Dungeon adversarial play active and add no Delvehold-specific core IDs or schemas.
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. The v2 newspaper path gives story selection, grouping, tone, and reader-facing copy to a bounded model editor, admits that draft through a local contract and a separate grounding copy desk, persists successful and terminally rejected editorial receipts, and renders reader copy separately from the provenance audit. 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. It committed 41 unique events and 28 unique news rows, rendered 23 distinct provenance-bound articles across four issues, carried Ilyra Quill through every issue and Mara's testimony into later institutional postures, retained one rejected pre-commit pulse, and left player state and location unchanged. Full receipts, newspapers, state, provenance, and hashes live in the accepted evidence record and F:\GameCult\GhostlightDungeon\acceptance\rainless-spicy-20260827-sustained-5\SHA256SUMS. This proves sustained politics in a sparse provider world whose derived cover grew from four cells to six as named figures emerged, not a live 200-cell provider wave or the Delvehold game-side adapter. The issue review established political continuity and provenance only; a blind baseline reviewer immediately recognized the old deterministic Markdown as a simulation ledger rather than plausible in-world journalism. Yggdrasil still serves immutable Ghostlight release fdb6352e0dfdd0eeb129020ea9dea0b6225eccf9; Dungeon play remains regression pressure, and unpublished Mars and Kalsa destination previews remain unapproved.
next_action: Treat the accepted sustained Rainless witness as the provider world-simulation baseline, not the newspaper-presentation baseline. Produce one fantasy newspaper front page grounded in committed public events but written and arranged as plausible in-world journalism. Dispatch an independent reviewer with only that front page and a neutral reader/editor brief that does not mention Ghostlight, simulation, provenance, or procedural generation. Record whether the reviewer independently diagnoses procedural construction and whether the page reads as coherent, selective, opinionated journalism. Separately verify that editorialization remains traceable to committed public evidence and invents no canonical facts. Resume the Delvehold adapter through generic contracts after this presentation boundary passes.
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.