Rebuild qualified world elaboration

This commit is contained in:
Meta Vens 2026-09-01 09:54:04 +02:00
commit 661cfbad9f
13 changed files with 6864 additions and 1078 deletions

View file

@ -0,0 +1,8 @@
Beyond the Confluence, approach and admit a politically inhabited elven forest realm whose watershed law, living-landscape institutions, rival offices, settlements, and public routes respond independently to the deep crisis.
Beyond the Confluence, approach and admit a politically inhabited human rail-capital realm with a government, opposition, university, mage interests, labor constituencies, dependent infrastructure, surrounding settlements, and public routes.
Beyond the Confluence, approach and admit a politically inhabited orcish oathland realm with several councils and communities, guardian-species obligations, contested enforcement, trade interests, dissent, and public routes.
Beyond the Confluence, approach and admit a politically inhabited halfling grain-and-river realm with farms, market towns, tenants, cooperatives, water authorities, insurers, faiths, political movements, and public routes.
Beyond the Confluence, approach and admit a politically inhabited goblin deep-route realm with distinct settlements, route guilds, kin networks, custodians, brokers, radicals, discounted expertise, and public routes.
Beyond the Confluence, approach and admit a second politically inhabited human polity: a tidal mercantile republic with elected harbor offices, hereditary creditors, dock unions, displaced fisher wards, rival courts, naval contracts, and public routes independent of the rail capital.
Beyond the Confluence, approach and admit a mixed border federation whose member towns, mobile communities, species, faiths, and trade leagues retain local authority while sharing delegates, mutual-aid stores, contested asylum law, watershed obligations, and public routes.
Beyond the Confluence, approach and admit an autonomous deep-ecology polity of dungeon organs, custodial communities, symbiotic species, and negotiated human-scale envoys, with legible law, dissent, material means, territorial claims, redress, and public routes; do not make it a single mystical will.

View file

@ -91,10 +91,16 @@ ontology or turn the Greathold into an autonomous character.
insurers suspend cover, armies secure infrastructure, displaced families
cross borders, and an unknown party circulates proof that several foreign
governments secretly financed the extraction they now condemn.`
- World locality elaborations: `5` (one per admitted realm jurisdiction)
- Ordered outer-realm requests:
`acceptance/full-world-delvehold-regions-v2.txt` (eight requests, one per
line; their order is part of the acceptance input)
- World locality elaborations: `8` (one per ordered outer-realm request)
- World elaboration parallelism: `8`
- Active cell budget: `240`
- Strategic waves: `12`
- Target active-cover basis points: `2000` (exactly `1200` qualified subjects)
- World complexity parallelism: `8`
- Maximum world complexity rounds: `128`
- Strategic waves: `1`
- Maximum rejected pulses per wave: `4`
- Provider routing: Luna fast, Terra balanced, Sol capable through the existing
Codex connector

File diff suppressed because it is too large Load diff

View file

@ -6,7 +6,7 @@ use std::collections::{BTreeMap, BTreeSet};
fn main() -> Result<()> {
let mut args = std::env::args().skip(1);
let store_path = args.next().context(
"usage: ghostlight-world-explore <campaign.cc> <overview|search|region|subject> [query]",
"usage: ghostlight-world-explore <campaign.cc> <overview|quality|search|region|subject> [query]",
)?;
let command = args.next().unwrap_or_else(|| "overview".into());
let operand = args.next();
@ -17,6 +17,7 @@ fn main() -> Result<()> {
let campaign = load_campaign(&store_path)?;
let output = match command.as_str() {
"overview" => overview(&campaign),
"quality" => quality(&campaign),
"search" => search(
&campaign,
operand
@ -35,12 +36,607 @@ fn main() -> Result<()> {
.as_deref()
.context("subject requires one exact subject ID")?,
)?,
other => bail!("unknown command {other:?}; expected overview, search, region, or subject"),
other => bail!(
"unknown command {other:?}; expected overview, quality, search, region, or subject"
),
};
println!("{}", serde_json::to_string_pretty(&output)?);
Ok(())
}
fn normalized_text(value: &str) -> String {
value
.chars()
.filter(|character| character.is_alphanumeric())
.flat_map(char::to_lowercase)
.collect()
}
fn terminal_name_token(value: &str) -> String {
value
.split(|character: char| !character.is_alphanumeric())
.filter(|token| !token.is_empty())
.last()
.map(normalized_text)
.unwrap_or_default()
}
fn duplicate_groups(entries: impl IntoIterator<Item = (String, String)>) -> Value {
let mut groups: BTreeMap<String, Vec<String>> = BTreeMap::new();
for (key, id) in entries {
if !key.is_empty() {
groups.entry(key).or_default().push(id);
}
}
let mut groups = groups
.into_iter()
.filter(|(_, ids)| ids.len() > 1)
.map(|(key, mut ids)| {
ids.sort();
let count = ids.len();
ids.truncate(20);
json!({"key":key,"count":count,"subjectIdSample":ids})
})
.collect::<Vec<_>>();
groups.sort_by(|left, right| {
right["count"]
.as_u64()
.cmp(&left["count"].as_u64())
.then_with(|| left["key"].as_str().cmp(&right["key"].as_str()))
});
let group_count = groups.len();
let occurrence_count = groups
.iter()
.filter_map(|group| group["count"].as_u64())
.sum::<u64>();
let member_sample_truncated = groups.iter().any(|group| {
group["count"].as_u64().unwrap_or(0)
> group["subjectIdSample"]
.as_array()
.map_or(0, |sample| sample.len()) as u64
});
groups.truncate(20);
json!({
"groupCount":group_count,
"occurrenceCount":occurrence_count,
"groupsSample":groups,
"truncated":group_count > 20 || member_sample_truncated,
"groupSampleLimit":20,
"subjectIdSampleLimitPerGroup":20,
})
}
fn gestalt_semantic_key(gestalt: &ghostlight_dungeon::domain::GestaltPersonaState) -> String {
serde_json::to_string(&(
&gestalt.shared_capabilities,
&gestalt.shared_knowledge,
&gestalt.goals,
&gestalt.pressures,
))
.unwrap_or_default()
}
fn lineage_depth(campaign: &Campaign, subject_id: &str) -> usize {
let mut depth = 0;
let mut current = subject_id;
let mut visited = BTreeSet::new();
while visited.insert(current.to_owned()) {
let Some(parent) = campaign
.agency_profiles
.get(current)
.and_then(|profile| profile.parent_subject_id.as_deref())
else {
break;
};
depth += 1;
current = parent;
}
depth
}
fn repeated_texts<'a>(values: impl IntoIterator<Item = (&'a str, &'a String)>) -> Value {
duplicate_groups(
values
.into_iter()
.map(|(id, value)| (normalized_text(value), id.to_owned())),
)
}
fn has_structurally_complete_named_person_shape(
has_goal: bool,
has_memory: bool,
has_detail_state: bool,
) -> bool {
has_goal && has_memory && has_detail_state
}
fn quality(campaign: &Campaign) -> Value {
let residual_ids = campaign
.gestalt_lineages
.values()
.map(|lineage| lineage.residual_child_id.clone())
.collect::<BTreeSet<_>>();
let raw_actionable_ids = campaign
.agency_profiles
.values()
.filter(|profile| profile.active_leaf && profile.simulation_eligible)
.map(|profile| profile.subject_id.clone())
.collect::<BTreeSet<_>>();
let qualified_ids = ghostlight_dungeon::elaboration::canonical_actionable_subject_ids(campaign);
let qualified_count =
ghostlight_dungeon::elaboration::canonical_actionable_subject_count(campaign);
let twenty_percent_target = u32::from(campaign.resolution_policy.active_cell_budget) * 5;
let qualified_target_delta = i64::from(twenty_percent_target) - i64::from(qualified_count);
let materialized_member_actor_ids = campaign
.gestalt_members
.values()
.flat_map(|member| {
member
.materialized_actor_id
.iter()
.cloned()
.chain(std::iter::once(format!("member:{}", member.id)))
})
.collect::<BTreeSet<_>>();
let person_name_records = campaign
.actors
.values()
.filter(|actor| {
actor.id != campaign.player_actor_id
&& !materialized_member_actor_ids.contains(&actor.id)
})
.map(|actor| (actor.name.clone(), format!("actor:{}", actor.id)))
.chain(
campaign
.gestalt_members
.values()
.map(|member| (member.name.clone(), format!("member:{}", member.id))),
)
.collect::<Vec<_>>();
let population_name_records = campaign
.gestalts
.values()
.filter(|gestalt| qualified_ids.contains(&gestalt.id))
.map(|gestalt| (gestalt.name.clone(), gestalt.id.clone()))
.collect::<Vec<_>>();
let invalid_person_identity_ids = person_name_records
.iter()
.filter(|(name, _)| normalized_text(name).is_empty())
.map(|(_, id)| id.clone())
.collect::<Vec<_>>();
let invalid_population_identity_ids = population_name_records
.iter()
.filter(|(name, _)| normalized_text(name).is_empty())
.map(|(_, id)| id.clone())
.collect::<Vec<_>>();
let duplicate_person_names = duplicate_groups(
person_name_records
.iter()
.map(|(name, id)| (normalized_text(name), id.clone())),
);
let duplicate_population_names = duplicate_groups(
population_name_records
.iter()
.map(|(name, id)| (normalized_text(name), id.clone())),
);
let repeated_person_terminal_tokens = duplicate_groups(
person_name_records
.iter()
.map(|(name, id)| (terminal_name_token(name), id.clone())),
);
let repeated_population_terminal_tokens = duplicate_groups(
population_name_records
.iter()
.map(|(name, id)| (terminal_name_token(name), id.clone())),
);
// This is a structural record-shape count, not a judgment of semantic quality.
// A counted standalone actor has at least one goal, at least one memory, and at
// least one capability, knowledge item, equipment item, condition, obligation,
// or relationship. The player and actor projections of gestalt members are
// excluded. A counted gestalt member has at least one goal, at least one memory,
// and at least one capability addition, knowledge addition, equipment item,
// condition, obligation, or relationship. Naming validity is measured separately.
let structurally_complete_actor_count = campaign
.actors
.values()
.filter(|actor| {
actor.id != campaign.player_actor_id
&& !materialized_member_actor_ids.contains(&actor.id)
})
.filter(|actor| {
has_structurally_complete_named_person_shape(
!actor.goals.is_empty(),
!actor.memories.is_empty(),
!actor.capabilities.is_empty()
|| !actor.knowledge.is_empty()
|| !actor.equipment.is_empty()
|| !actor.conditions.is_empty()
|| !actor.obligations.is_empty()
|| !actor.relationships.is_empty(),
)
})
.count();
let structurally_complete_member_count = campaign
.gestalt_members
.values()
.filter(|member| {
has_structurally_complete_named_person_shape(
!member.goals.is_empty(),
!member.memories.is_empty(),
!member.capability_additions.is_empty()
|| !member.knowledge_additions.is_empty()
|| !member.equipment.is_empty()
|| !member.conditions.is_empty()
|| !member.obligations.is_empty()
|| !member.relationships.is_empty(),
)
})
.count();
let actor_person_count = campaign
.actors
.values()
.filter(|actor| {
actor.id != campaign.player_actor_id
&& !materialized_member_actor_ids.contains(&actor.id)
})
.count();
let named_person_count = actor_person_count + campaign.gestalt_members.len();
let qualified_gestalts = campaign
.gestalts
.values()
.filter(|gestalt| qualified_ids.contains(&gestalt.id))
.collect::<Vec<_>>();
let semantic_clone_groups = duplicate_groups(
qualified_gestalts
.iter()
.map(|gestalt| (gestalt_semantic_key(gestalt), gestalt.id.clone())),
);
let repeated_goals = repeated_texts(qualified_gestalts.iter().flat_map(|gestalt| {
gestalt
.goals
.iter()
.map(|value| (gestalt.id.as_str(), value))
}));
let repeated_pressures = repeated_texts(qualified_gestalts.iter().flat_map(|gestalt| {
gestalt
.pressures
.iter()
.map(|value| (gestalt.id.as_str(), value))
}));
let direct_fission_deltas = qualified_gestalts
.iter()
.filter_map(|gestalt| {
let parent_id = campaign
.agency_profiles
.get(&gestalt.id)?
.parent_subject_id
.as_ref()?;
let parent = campaign.gestalts.get(parent_id)?;
Some((
gestalt.id.as_str(),
gestalt
.shared_capabilities
.difference(&parent.shared_capabilities)
.cloned()
.collect::<BTreeSet<_>>(),
gestalt
.shared_knowledge
.difference(&parent.shared_knowledge)
.cloned()
.collect::<BTreeSet<_>>(),
gestalt
.goals
.iter()
.filter(|goal| !parent.goals.contains(goal))
.cloned()
.collect::<Vec<_>>(),
gestalt
.pressures
.iter()
.filter(|pressure| !parent.pressures.contains(pressure))
.cloned()
.collect::<Vec<_>>(),
))
})
.collect::<Vec<_>>();
let repeated_direct_added_goals = repeated_texts(
direct_fission_deltas
.iter()
.flat_map(|(id, _, _, goals, _)| goals.iter().map(|value| (*id, value))),
);
let repeated_direct_added_pressures = repeated_texts(
direct_fission_deltas
.iter()
.flat_map(|(id, _, _, _, pressures)| pressures.iter().map(|value| (*id, value))),
);
let exact_direct_delta_clone_groups = duplicate_groups(direct_fission_deltas.iter().map(
|(id, capabilities, knowledge, goals, pressures)| {
(
serde_json::to_string(&(capabilities, knowledge, goals, pressures))
.unwrap_or_default(),
(*id).to_owned(),
)
},
));
let mut raw_relation_degrees = qualified_ids
.iter()
.map(|id| (id.clone(), 0usize))
.collect::<BTreeMap<_, _>>();
let mut non_inherited_relation_degrees = raw_relation_degrees.clone();
let mut raw_relation_kind_counts = BTreeMap::<String, usize>::new();
let mut non_inherited_relation_kind_counts = BTreeMap::<String, usize>::new();
let mut mechanically_inherited_relation_count = 0usize;
for relation in campaign
.agency_relations
.values()
.filter(|relation| relation.active)
{
if let Some(degree) = raw_relation_degrees.get_mut(&relation.from_subject_id) {
*degree += 1;
}
if let Some(degree) = raw_relation_degrees.get_mut(&relation.to_subject_id) {
*degree += 1;
}
let kind = format!("{:?}", relation.kind).to_lowercase();
*raw_relation_kind_counts.entry(kind.clone()).or_default() += 1;
if relation.id.contains(":fission:") {
mechanically_inherited_relation_count += 1;
continue;
}
if let Some(degree) = non_inherited_relation_degrees.get_mut(&relation.from_subject_id) {
*degree += 1;
}
if let Some(degree) = non_inherited_relation_degrees.get_mut(&relation.to_subject_id) {
*degree += 1;
}
*non_inherited_relation_kind_counts.entry(kind).or_default() += 1;
}
let mut individual_relationship_entries = 0usize;
let mut record_individual_relationship = |from_subject_id: &str, to_subject_id: &str| {
individual_relationship_entries += 1;
if let Some(degree) = raw_relation_degrees.get_mut(from_subject_id) {
*degree += 1;
}
if let Some(degree) = raw_relation_degrees.get_mut(to_subject_id) {
*degree += 1;
}
if let Some(degree) = non_inherited_relation_degrees.get_mut(from_subject_id) {
*degree += 1;
}
if let Some(degree) = non_inherited_relation_degrees.get_mut(to_subject_id) {
*degree += 1;
}
*raw_relation_kind_counts
.entry("individual_relationship".into())
.or_default() += 1;
*non_inherited_relation_kind_counts
.entry("individual_relationship".into())
.or_default() += 1;
};
for actor in campaign.actors.values() {
for other_subject_id in actor.relationships.keys() {
record_individual_relationship(&actor.id, other_subject_id);
}
}
for member in campaign.gestalt_members.values() {
let default_actor_id = ghostlight_dungeon::domain::gestalt_member_subject_id(&member.id);
let projected_actor_exists = campaign.actors.contains_key(&default_actor_id)
|| member
.materialized_actor_id
.as_ref()
.is_some_and(|actor_id| campaign.actors.contains_key(actor_id));
if projected_actor_exists {
continue;
}
for other_subject_id in member.relationships.keys() {
record_individual_relationship(&default_actor_id, other_subject_id);
}
}
let raw_isolated_subject_ids = raw_relation_degrees
.iter()
.filter(|(_, degree)| **degree == 0)
.map(|(id, _)| id.clone())
.collect::<Vec<_>>();
let non_inherited_isolated_subject_ids = non_inherited_relation_degrees
.iter()
.filter(|(_, degree)| **degree == 0)
.map(|(id, _)| id.clone())
.collect::<Vec<_>>();
let raw_degree_sum = raw_relation_degrees.values().sum::<usize>();
let non_inherited_degree_sum = non_inherited_relation_degrees.values().sum::<usize>();
let civic_signatures = duplicate_groups(campaign.civic_systems.values().map(|civic| {
let institutions = civic
.governing_institution_ids
.iter()
.filter_map(|id| campaign.institutions.get(id))
.map(|institution| {
(
normalized_text(&institution.posture),
institution
.goals
.iter()
.map(|value| normalized_text(value))
.collect::<BTreeSet<_>>(),
institution
.resources
.iter()
.map(|value| normalized_text(value))
.collect::<BTreeSet<_>>(),
)
})
.collect::<BTreeSet<_>>();
let residents = civic
.resident_population_ids
.iter()
.filter_map(|id| campaign.gestalts.get(id))
.map(gestalt_semantic_key)
.collect::<BTreeSet<_>>();
(
serde_json::to_string(&(institutions, residents)).unwrap_or_default(),
civic.jurisdiction_location_id.clone(),
)
}));
let event_kind_counts =
campaign
.events
.iter()
.fold(BTreeMap::<String, usize>::new(), |mut counts, event| {
*counts.entry(event.kind.clone()).or_default() += 1;
counts
});
let repeated_event_summaries = duplicate_groups(
campaign
.events
.iter()
.map(|event| (normalized_text(&event.summary), event.id.clone())),
);
let distribution_by_root = campaign
.locations
.values()
.filter(|location| location.container_id.is_none())
.map(|location| {
let places = descendant_location_ids(campaign, &location.id);
let count = qualified_ids
.iter()
.filter(|id| {
campaign
.agency_profiles
.get(*id)
.is_some_and(|profile| !profile.location_ids.is_disjoint(&places))
})
.count();
(location.id.clone(), count)
})
.collect::<BTreeMap<_, _>>();
let spread_by_jurisdiction = campaign
.civic_systems
.keys()
.filter(|location_id| campaign.locations.contains_key(*location_id))
.map(|location_id| {
let places = descendant_location_ids(campaign, location_id);
let location_counts = places
.iter()
.map(|place_id| {
let count = qualified_ids
.iter()
.filter(|id| {
campaign
.agency_profiles
.get(*id)
.is_some_and(|profile| profile.location_ids.contains(place_id))
})
.count();
(place_id.clone(), count)
})
.collect::<BTreeMap<_, _>>();
let occupied_location_count = location_counts
.values()
.filter(|count| **count > 0)
.count();
let location_assignment_count = location_counts.values().sum::<usize>();
let largest_location_count = location_counts.values().copied().max().unwrap_or(0);
(
location_id.clone(),
json!({
"qualifiedSubjects":qualified_ids.iter().filter(|id|campaign.agency_profiles.get(*id).is_some_and(|profile|!profile.location_ids.is_disjoint(&places))).count(),
"occupiedLocationCount":occupied_location_count,
"locationAssignmentCount":location_assignment_count,
"largestLocationShareBasisPoints":if location_assignment_count == 0 { None } else { Some(largest_location_count * 10_000 / location_assignment_count) },
"qualifiedSubjectCountsByLocation":location_counts,
}),
)
})
.collect::<BTreeMap<_, _>>();
let occupied_location_count = campaign
.locations
.keys()
.filter(|location_id| {
qualified_ids.iter().any(|id| {
campaign
.agency_profiles
.get(id)
.is_some_and(|profile| profile.location_ids.contains(*location_id))
})
})
.count();
json!({
"schema":"ghostlight.world_quality.v1",
"campaign":{"id":campaign.id,"revision":campaign.revision,"strategicTickCount":campaign.strategic_tick_count},
"scale":{
"rawActionableSubjects":raw_actionable_ids.len(),
"qualifiedActionableSubjects":qualified_count,
"residualActionableSubjects":raw_actionable_ids.intersection(&residual_ids).count(),
"twentyPercentTarget":twenty_percent_target,
"qualifiedTargetDelta":qualified_target_delta,
"qualifiedCoverBasisPoints":if qualified_count == 0 { None } else { Some(u32::from(campaign.resolution_policy.active_cell_budget) * 10_000 / qualified_count) },
"rawCoverBasisPoints":if raw_actionable_ids.is_empty() { None } else { Some(usize::from(campaign.resolution_policy.active_cell_budget) * 10_000 / raw_actionable_ids.len()) },
"maximumQualifiedLineageDepth":qualified_ids.iter().map(|id|lineage_depth(campaign,id)).max().unwrap_or(0),
"qualifiedDistributionByRootLocation":distribution_by_root,
"occupiedLocationCount":occupied_location_count,
"qualifiedSpreadByJurisdiction":spread_by_jurisdiction,
"geographyFissionCount":campaign.gestalt_lineages.values().filter(|lineage|lineage.partition_axis == ghostlight_dungeon::domain::AgencyAxis::Geography).count(),
},
"identity":{
"namedPersons":named_person_count,
"structurallyCompleteNamedPersons":structurally_complete_actor_count + structurally_complete_member_count,
"invalidPublicPersonIdentityIds":invalid_person_identity_ids,
"invalidQualifiedPopulationIdentityIds":invalid_population_identity_ids,
"duplicatePersonNames":duplicate_person_names,
"duplicateQualifiedPopulationNames":duplicate_population_names,
"repeatedPersonTerminalNameTokens":repeated_person_terminal_tokens,
"repeatedQualifiedPopulationTerminalNameTokens":repeated_population_terminal_tokens,
"lexicalDiagnosticsOnly":true,
},
"causality":{
"qualifiedGestalts":qualified_gestalts.len(),
"qualifiedGestaltsWithoutGoals":qualified_gestalts.iter().filter(|gestalt|gestalt.goals.is_empty()).count(),
"qualifiedGestaltsWithoutPressures":qualified_gestalts.iter().filter(|gestalt|gestalt.pressures.is_empty()).count(),
"qualifiedGestaltsWithoutCapabilities":qualified_gestalts.iter().filter(|gestalt|gestalt.shared_capabilities.is_empty()).count(),
"qualifiedGestaltsWithoutKnowledge":qualified_gestalts.iter().filter(|gestalt|gestalt.shared_knowledge.is_empty()).count(),
"inheritanceInclusiveExactSemanticCloneGroups":semantic_clone_groups,
"inheritanceInclusiveRepeatedGoals":repeated_goals,
"inheritanceInclusiveRepeatedPressures":repeated_pressures,
"exactDirectFissionDeltaCloneGroups":exact_direct_delta_clone_groups,
"repeatedDirectAddedGoals":repeated_direct_added_goals,
"repeatedDirectAddedPressures":repeated_direct_added_pressures,
},
"relationships":{
"collectiveAgencyActiveRelations":campaign.agency_relations.values().filter(|relation|relation.active).count(),
"individualRelationshipEntries":individual_relationship_entries,
"rawActiveRelationshipEntries":campaign.agency_relations.values().filter(|relation|relation.active).count() + individual_relationship_entries,
"estimatedMechanicallyInheritedFissionRelationsByIdConvention":mechanically_inherited_relation_count,
"nonInheritedActiveRelationshipEntries":campaign.agency_relations.values().filter(|relation|relation.active && !relation.id.contains(":fission:")).count() + individual_relationship_entries,
"rawRelationKindCounts":raw_relation_kind_counts,
"nonInheritedRelationKindCounts":non_inherited_relation_kind_counts,
"rawIsolatedQualifiedSubjects":raw_isolated_subject_ids.len(),
"rawIsolatedQualifiedSubjectSample":raw_isolated_subject_ids.into_iter().take(50).collect::<Vec<_>>(),
"nonInheritedIsolatedQualifiedSubjects":non_inherited_isolated_subject_ids.len(),
"nonInheritedIsolatedQualifiedSubjectSample":non_inherited_isolated_subject_ids.into_iter().take(50).collect::<Vec<_>>(),
"rawMeanQualifiedDegree":if raw_relation_degrees.is_empty() { 0.0 } else { raw_degree_sum as f64 / raw_relation_degrees.len() as f64 },
"nonInheritedMeanQualifiedDegree":if non_inherited_relation_degrees.is_empty() { 0.0 } else { non_inherited_degree_sum as f64 / non_inherited_relation_degrees.len() as f64 },
"inheritanceClassification":"estimate based on the current :fission: relation-ID convention; authored IDs can confound it",
},
"politics":{
"civicJurisdictions":campaign.civic_systems.len(),
"exactCivicSignatureCloneGroups":civic_signatures,
"exactSignatureDiagnosticsDoNotMeasureSemanticPoliticalDiversity":true,
},
"behavior":{
"eventKindCounts":event_kind_counts,
"exactRepeatedEventSummaryGroups":repeated_event_summaries,
"newsIssues":campaign.news.len(),
"exactSummaryDiagnosticsDoNotMeasureSemanticDramaticDiversity":true,
},
})
}
fn load_campaign(path: &str) -> Result<Campaign> {
let store = CampaignStore::open(path)?;
let key = store
@ -388,3 +984,50 @@ fn location_name<'a>(campaign: &'a Campaign, id: &str) -> Option<&'a str> {
.get(id)
.map(|location| location.name.as_str())
}
#[cfg(test)]
mod tests {
use super::{duplicate_groups, has_structurally_complete_named_person_shape};
#[test]
fn named_person_structural_completeness_requires_goal_memory_and_detail_state() {
assert!(has_structurally_complete_named_person_shape(
true, true, true
));
assert!(!has_structurally_complete_named_person_shape(
false, true, true
));
assert!(!has_structurally_complete_named_person_shape(
true, false, true
));
assert!(!has_structurally_complete_named_person_shape(
true, true, false
));
}
#[test]
fn duplicate_report_preserves_complete_prevalence_while_sampling_large_results() {
let mut entries = (0..25)
.map(|index| ("shared".to_owned(), format!("shared-{index:02}")))
.collect::<Vec<_>>();
for group in 0..21 {
entries.push((format!("group-{group:02}"), format!("group-{group:02}-a")));
entries.push((format!("group-{group:02}"), format!("group-{group:02}-b")));
}
let report = duplicate_groups(entries);
assert_eq!(report["groupCount"], 22);
assert_eq!(report["occurrenceCount"], 67);
assert_eq!(report["groupsSample"].as_array().unwrap().len(), 20);
assert_eq!(report["groupsSample"][0]["count"], 25);
assert_eq!(
report["groupsSample"][0]["subjectIdSample"]
.as_array()
.unwrap()
.len(),
20
);
assert_eq!(report["truncated"], true);
}
}

File diff suppressed because one or more lines are too long

View file

@ -1236,6 +1236,20 @@ pub enum WorldCommand {
#[serde(default)]
model_stage_receipts: Vec<crate::model::ModelStageReceipt>,
},
ElaborateGestaltFission {
expected_revision: u64,
preview: GestaltFissionPreview,
qualification: crate::elaboration::WorldComplexityFissionQualification,
#[serde(default)]
model_stage_receipts: Vec<crate::model::ModelStageReceipt>,
},
ElaborateGestaltIndividuation {
expected_revision: u64,
individuation: GestaltIndividuation,
qualification: crate::elaboration::WorldComplexityIndividuationQualification,
#[serde(default)]
model_stage_receipts: Vec<crate::model::ModelStageReceipt>,
},
ReconcileFissionCivicBindings {
expected_revision: u64,
},

File diff suppressed because it is too large Load diff

View file

@ -1144,6 +1144,74 @@ fn execute(
Some((transition, mutation_receipt)),
)
}
WorldCommand::ElaborateGestaltFission {
expected_revision,
preview,
qualification,
model_stage_receipts,
} => {
require_revision(&campaign, expected_revision)?;
if qualification.schema != "ghostlight.world_complexity_fission_qualification.v1"
|| !(1..=6).contains(&qualification.target_actionable_gain)
|| model_stage_receipts.is_empty()
{
return Err(KernelError::Invalid(
"elaborated fission lacks its exact qualification or model provenance".into(),
));
}
let parent = campaign
.gestalts
.get(&preview.parent_gestalt_id)
.ok_or_else(|| KernelError::Invalid("fission parent is unknown".into()))?;
let proposal = crate::elaboration::WorldComplexityProposal::Fission {
preview: preview.clone(),
qualification: qualification.clone(),
};
crate::resolution::validate_elaborated_fission(&campaign, &preview)
.and_then(|_| {
crate::elaboration::validate_complexity_fission_semantics(
&campaign,
parent,
&preview,
qualification.title,
&qualification.jurisdiction_location_id,
qualification.target_actionable_gain,
)
})
.and_then(|_| {
crate::elaboration::validate_world_complexity_semantic_provenance(
&campaign,
&proposal,
&qualification.semantic,
&model_stage_receipts,
)
})
.map_err(|error| KernelError::Invalid(error.to_string()))?;
let transition = crate::legacy_transition::lower_elaborated_fission(
&campaign,
&preview,
Utc::now() + Duration::minutes(5),
)
.map_err(|error| KernelError::Invalid(error.to_string()))?;
let mutation_receipt = crate::legacy_transition::apply_lowered_elaborated_fission(
&mut campaign,
&preview,
&transition,
Utc::now(),
)
.map_err(|error| KernelError::Invalid(error.to_string()))?;
commit_with_records(
store,
row,
campaign,
"elaborate_gestalt_fission",
Vec::new(),
Vec::new(),
model_stage_receipts,
None,
Some((transition, mutation_receipt)),
)
}
WorldCommand::ReconcileFissionCivicBindings { expected_revision } => {
require_revision(&campaign, expected_revision)?;
let transition = crate::legacy_transition::lower_fission_civic_reconciliation(
@ -1618,6 +1686,7 @@ fn execute(
campaign,
"materialize_gestalt_member",
"direct materialization command",
Vec::new(),
)
}
WorldCommand::IndividuateGestaltMember {
@ -1634,6 +1703,67 @@ fn execute(
campaign,
"individuate_gestalt_member",
"direct individuation command",
Vec::new(),
)
}
WorldCommand::ElaborateGestaltIndividuation {
expected_revision,
individuation,
qualification,
model_stage_receipts,
} => {
require_revision(&campaign, expected_revision)?;
if qualification.schema != "ghostlight.world_complexity_individuation_qualification.v1"
|| model_stage_receipts.is_empty()
{
return Err(KernelError::Invalid(
"elaborated individuation lacks its exact qualification or model provenance"
.into(),
));
}
let proposal = crate::elaboration::WorldComplexityProposal::Individuate {
individuation: individuation.clone(),
qualification: qualification.clone(),
};
if crate::elaboration::unique_containing_jurisdiction(
&campaign,
&BTreeSet::from([individuation.location_id.clone()]),
&BTreeSet::from([qualification.jurisdiction_location_id.clone()]),
)
.as_deref()
!= Some(qualification.jurisdiction_location_id.as_str())
{
return Err(KernelError::Invalid(
"elaborated individuation escaped its qualified jurisdiction".into(),
));
}
crate::resolution::validate_gestalt_individuation(&campaign, &individuation)
.and_then(|_| {
crate::elaboration::validate_complexity_individuation_semantics(
&campaign,
qualification.title,
&individuation,
)
})
.and_then(|_| {
crate::elaboration::validate_world_complexity_semantic_provenance(
&campaign,
&proposal,
&qualification.semantic,
&model_stage_receipts,
)
})
.map_err(|error| KernelError::Invalid(error.to_string()))?;
let before = campaign.clone();
apply_individuation(&mut campaign, &individuation)?;
commit_gestalt_presence(
store,
row,
before,
campaign,
"elaborate_gestalt_individuation",
"model-qualified world-complexity individuation",
model_stage_receipts,
)
}
WorldCommand::DematerializeGestaltMember {
@ -1650,6 +1780,7 @@ fn execute(
campaign,
"dematerialize_gestalt_member",
"direct dematerialization command",
Vec::new(),
)
}
WorldCommand::ReconcileGestaltPresence {
@ -1722,6 +1853,7 @@ fn execute(
candidate,
"reconcile_gestalt_presence",
&reason,
Vec::new(),
)
}
WorldCommand::ResolveReactionWave {
@ -2235,7 +2367,7 @@ fn apply_promotion(
}
let member = campaign
.gestalt_members
.get_mut(&promotion.member_id)
.get(&promotion.member_id)
.ok_or_else(|| KernelError::Invalid("gestalt member is unknown".into()))?;
if member.gestalt_id != promotion.gestalt_id
|| member.version != promotion.expected_member_version
@ -2250,11 +2382,19 @@ fn apply_promotion(
));
}
let actor_id = crate::domain::gestalt_member_subject_id(&member.id);
if campaign.actors.contains_key(&actor_id) {
if crate::resolution::canonical_subject_id_is_occupied_except_member(
campaign,
&actor_id,
Some(&promotion.member_id),
) {
return Err(KernelError::Invalid(
"materialized actor id collides".into(),
"materialized actor id collides with an established canonical subject identity".into(),
));
}
let member = campaign
.gestalt_members
.get_mut(&promotion.member_id)
.ok_or_else(|| KernelError::Invalid("gestalt member is unknown".into()))?;
let actor = materialize_actor(&gestalt, member, &actor_id, &promotion.location_id);
member.materialized_actor_id = Some(actor_id.clone());
member.last_location_id = Some(promotion.location_id.clone());
@ -3762,6 +3902,7 @@ fn commit_gestalt_presence(
mut campaign: Campaign,
kind: &str,
reason: &str,
model_receipts: Vec<crate::model::ModelStageReceipt>,
) -> Result<CommandResult, KernelError> {
let previous_resolution_epoch = campaign.resolution_policy.resolution_epoch;
crate::resolution::ensure_agency_profiles(&mut campaign);
@ -3853,7 +3994,14 @@ fn commit_gestalt_presence(
};
let key = format!("{}-{}", campaign.id, campaign.revision);
store
.append_gestalt_presence(&row, &campaign, &key, &receipt, &gestalt_receipt)
.append_gestalt_presence(
&row,
&campaign,
&key,
&receipt,
&gestalt_receipt,
&model_receipts,
)
.map_err(persist)?;
Ok(CommandResult::Committed { campaign, receipt })
}
@ -5301,6 +5449,92 @@ pub(crate) mod tests {
assert_eq!(persisted.actors["taren"].name, "Taren");
}
#[test]
fn individuation_validation_and_application_share_the_canonical_subject_namespace() {
let mut value = campaign();
value.gestalts.insert(
"refugees".into(),
GestaltPersonaState {
schema: "ghostlight.gestalt_persona_state.v1".into(),
id: "refugees".into(),
name: "Refugees".into(),
version: 0,
home_location_id: "room".into(),
shared_capabilities: BTreeSet::new(),
shared_knowledge: BTreeSet::new(),
resources: BTreeSet::new(),
goals: vec![],
pressures: vec![],
},
);
crate::resolution::ensure_agency_profiles(&mut value);
let member = GestaltMemberDelta {
schema: "ghostlight.gestalt_member_delta.v1".into(),
id: "relay-volunteer".into(),
gestalt_id: "refugees".into(),
version: 0,
name: "Relay Volunteer".into(),
capability_additions: BTreeSet::new(),
capability_removals: BTreeSet::new(),
knowledge_additions: BTreeSet::new(),
knowledge_removals: BTreeSet::new(),
equipment: BTreeSet::new(),
conditions: BTreeSet::new(),
obligations: BTreeSet::new(),
relationships: BTreeMap::new(),
goals: vec![],
memories: vec![],
last_location_id: Some("room".into()),
materialized_actor_id: None,
last_relevant_revision: 0,
relevance_lease_until_revision: 0,
};
let actor_id = crate::domain::gestalt_member_subject_id(&member.id);
let mut reservation = value.agency_profiles["refugees"].clone();
reservation.id = actor_id.clone();
reservation.subject_id = "detached-profile-subject".into();
reservation.active_leaf = false;
reservation.simulation_eligible = false;
value
.agency_profiles
.insert("detached-profile-key".into(), reservation);
let individuation = GestaltIndividuation {
gestalt_id: "refugees".into(),
expected_gestalt_version: 0,
member: member.clone(),
location_id: "room".into(),
};
let diagnostic = crate::resolution::validate_gestalt_individuation(&value, &individuation)
.unwrap_err()
.to_string();
assert!(diagnostic.contains("established canonical subject identity"));
value.gestalt_members.insert(member.id.clone(), member);
let error = apply_promotion(
&mut value,
&GestaltPromotion {
gestalt_id: "refugees".into(),
expected_gestalt_version: 0,
member_id: "relay-volunteer".into(),
expected_member_version: 0,
location_id: "room".into(),
},
)
.unwrap_err();
assert!(
error
.to_string()
.contains("established canonical subject identity")
);
assert!(!value.actors.contains_key(&actor_id));
assert!(
value.gestalt_members["relay-volunteer"]
.materialized_actor_id
.is_none()
);
}
#[test]
fn composite_selected_action_commits_atomically_and_rebuilds_derived_lanes() {
let mut value = campaign();

View file

@ -153,7 +153,28 @@ pub fn lower_fission(
preview: &GestaltFissionPreview,
expires_at: DateTime<Utc>,
) -> Result<LoweredLegacyTransition> {
crate::resolution::validate_fission(campaign, preview)?;
lower_fission_with_validation(campaign, preview, expires_at, false)
}
pub fn lower_elaborated_fission(
campaign: &Campaign,
preview: &GestaltFissionPreview,
expires_at: DateTime<Utc>,
) -> Result<LoweredLegacyTransition> {
lower_fission_with_validation(campaign, preview, expires_at, true)
}
fn lower_fission_with_validation(
campaign: &Campaign,
preview: &GestaltFissionPreview,
expires_at: DateTime<Utc>,
allow_semantic_additions: bool,
) -> Result<LoweredLegacyTransition> {
if allow_semantic_additions {
crate::resolution::validate_elaborated_fission(campaign, preview)?;
} else {
crate::resolution::validate_fission(campaign, preview)?;
}
let preview_digest = digest_serializable(preview)?;
let source_receipt_id = format!(
"fission-preview:{}",
@ -196,6 +217,81 @@ pub fn lower_fission(
.collect(),
remainder_population: Some(population_subject(&preview.residual_child_id)),
});
if allow_semantic_additions {
for child in &preview.children {
let subject = population_subject(&child.id);
for capability in child
.shared_capabilities
.difference(&campaign.gestalts[&preview.parent_gestalt_id].shared_capabilities)
{
mutations.push(WorldMutation::ChangeCapability {
subject: subject.clone(),
operation: CapabilityMutationOperation::Grant,
capability_id: capability.clone(),
description: Some(capability.clone()),
});
}
for statement in child
.shared_knowledge
.difference(&campaign.gestalts[&preview.parent_gestalt_id].shared_knowledge)
{
let (proposition, needs_admission) =
elaborated_fission_proposition(campaign, statement)?;
if needs_admission {
mutations.push(WorldMutation::AdmitEntity {
subject: proposition.clone(),
initial_components: BTreeSet::from([
WorldComponentKind::Knowledge,
WorldComponentKind::PropositionContent,
]),
initial_place: None,
initial_profile: Some(AdmittedEntityProfile::Proposition {
statement: statement.clone(),
scope: FactScope::ProvisionalLocal,
evidence_receipt_ids: BTreeSet::new(),
discoverable_at_places: BTreeSet::new(),
}),
admission_receipt_id: source_receipt_id.clone(),
});
}
mutations.push(WorldMutation::ChangeKnowledge {
operation: KnowledgeMutationOperation::Acquire,
proposition,
knower: Some(subject.clone()),
speaker: None,
recipients: Vec::new(),
channel: None,
});
}
for goal in child
.goals
.iter()
.skip(campaign.gestalts[&preview.parent_gestalt_id].goals.len())
{
mutations.push(WorldMutation::ChangeCommitment {
subject: subject.clone(),
operation: CommitmentMutationOperation::Create,
kind: CommitmentKind::Goal,
commitment_id: format!("goal:elaboration:{}", short_digest(goal)),
counterparty: None,
description: Some(goal.clone()),
});
}
for pressure in child.pressures.iter().skip(
campaign.gestalts[&preview.parent_gestalt_id]
.pressures
.len(),
) {
mutations.push(WorldMutation::ChangePressure {
pressure: gestalt_pressure_subject(&child.id, pressure),
owner: subject.clone(),
operation: PressureMutationOperation::Create,
amount: Some(4),
label: Some(pressure.clone()),
});
}
}
}
for (resource, child_id) in &preview.resource_child_assignments {
mutations.push(WorldMutation::TransferCustody {
resource: resource_subject(campaign, &parent, resource),
@ -227,6 +323,11 @@ pub fn lower_fission(
system,
});
}
let effect_ceiling = if allow_semantic_additions {
"Admit the approved child populations, preserve their inherited baseline, add only each child's explicitly qualified capabilities, knowledge, goals, and pressures, partition each exact resource once, transfer each named member once, and record one population lineage split."
} else {
"Admit the approved child populations, preserve their inherited baseline, partition each exact resource once, transfer each named member once, and record one population lineage split."
};
lower_exact_mutations(
campaign,
mutations,
@ -234,7 +335,7 @@ pub fn lower_fission(
None,
MutationOutcomeBinding::Deterministic,
Some(campaign.resolution_policy.resolution_epoch),
"Admit the approved child populations, preserve their inherited baseline, partition each exact resource once, transfer each named member once, and record one population lineage split.",
effect_ceiling,
&source_receipt_id,
Some(digest_serializable(&(
"fission",
@ -246,6 +347,51 @@ pub fn lower_fission(
)
}
fn elaborated_fission_proposition(
campaign: &Campaign,
statement: &str,
) -> Result<(SubjectRef, bool)> {
if let Some(fact) = campaign
.facts
.values()
.find(|fact| fact.statement == statement)
{
return Ok((proposition_subject(&fact.id), false));
}
let already_known = campaign
.actors
.values()
.any(|actor| actor.knowledge.contains(statement))
|| campaign
.gestalts
.values()
.any(|gestalt| gestalt.shared_knowledge.contains(statement))
|| campaign.gestalt_members.values().any(|member| {
member.knowledge_additions.contains(statement)
&& !member.knowledge_removals.contains(statement)
});
if already_known {
return Ok((
proposition_subject(&format!("legacy-knowledge:{}", short_digest(statement))),
false,
));
}
let fact_id = format!(
"fact:complexity-fission:{:x}",
Sha256::digest(statement.as_bytes())
);
if let Some(existing) = campaign.facts.get(&fact_id) {
return if existing.statement == statement {
Ok((proposition_subject(&fact_id), false))
} else {
Err(anyhow!(
"content-addressed complexity knowledge collided with another fact"
))
};
}
Ok((proposition_subject(&fact_id), true))
}
fn fission_civic_system_rebindings(
campaign: &Campaign,
preview: &GestaltFissionPreview,
@ -425,7 +571,30 @@ pub fn apply_lowered_fission(
transition: &LoweredLegacyTransition,
now: DateTime<Utc>,
) -> Result<WorldMutationReceipt> {
crate::resolution::validate_fission(campaign, preview)?;
apply_lowered_fission_with_validation(campaign, preview, transition, now, false)
}
pub fn apply_lowered_elaborated_fission(
campaign: &mut Campaign,
preview: &GestaltFissionPreview,
transition: &LoweredLegacyTransition,
now: DateTime<Utc>,
) -> Result<WorldMutationReceipt> {
apply_lowered_fission_with_validation(campaign, preview, transition, now, true)
}
fn apply_lowered_fission_with_validation(
campaign: &mut Campaign,
preview: &GestaltFissionPreview,
transition: &LoweredLegacyTransition,
now: DateTime<Utc>,
allow_semantic_additions: bool,
) -> Result<WorldMutationReceipt> {
if allow_semantic_additions {
crate::resolution::validate_elaborated_fission(campaign, preview)?;
} else {
crate::resolution::validate_fission(campaign, preview)?;
}
let snapshot = component_snapshot(campaign)?;
let application =
apply_component_world_batch(&snapshot, &transition.authority, &transition.batch, now)?;
@ -962,6 +1131,116 @@ fn project_accepted_region_expansion(
Ok(())
}
fn verify_fission_child_components(
next: &ComponentWorldState,
child: &GestaltPersonaState,
) -> Result<()> {
let subject = population_subject(&child.id);
let capabilities = next
.capabilities
.iter()
.filter(|(key, value)| key.subject == subject && !value.suspended)
.map(|(key, value)| {
if value.description != key.entry_id {
return Err(anyhow!(
"accepted fission capability identity diverged from its description"
));
}
Ok(key.entry_id.clone())
})
.collect::<Result<BTreeSet<_>>>()?;
let knowledge = next
.knowledge
.iter()
.filter(|(key, value)| key.knower == subject && value.status == "known")
.map(|(key, _)| {
next.propositions
.get(&key.proposition)
.map(|value| value.statement.clone())
.ok_or_else(|| anyhow!("accepted fission knowledge lost proposition content"))
})
.collect::<Result<BTreeSet<_>>>()?;
let goals = next
.commitments
.iter()
.filter(|(key, value)| {
key.subject == subject && value.kind == CommitmentKind::Goal && value.status == "active"
})
.map(|(_, value)| value.description.clone())
.collect::<BTreeSet<_>>();
let pressures = next
.pressures
.values()
.filter(|value| value.owner == subject && !value.resolved)
.map(|value| value.label.clone())
.collect::<BTreeSet<_>>();
if capabilities != child.shared_capabilities
|| knowledge != child.shared_knowledge
|| goals != child.goals.iter().cloned().collect()
|| pressures != child.pressures.iter().cloned().collect()
{
return Err(anyhow!(
"accepted fission component semantics diverged from the approved child"
));
}
Ok(())
}
fn project_elaborated_fission_facts(
campaign: &mut Campaign,
preview: &GestaltFissionPreview,
next: &ComponentWorldState,
) -> Result<()> {
let parent = campaign
.gestalts
.get(&preview.parent_gestalt_id)
.ok_or_else(|| anyhow!("accepted complexity fact lost its fission parent"))?;
let mut admitted = BTreeMap::new();
for child in &preview.children {
let knower = population_subject(&child.id);
for statement in child.shared_knowledge.difference(&parent.shared_knowledge) {
let (proposition_id, needs_admission) =
elaborated_fission_proposition(campaign, statement)?;
if !needs_admission {
continue;
}
let knowledge_key = KnowledgeKey {
knower: knower.clone(),
proposition: proposition_id.clone(),
};
if !next.knowledge.contains_key(&knowledge_key) {
return Err(anyhow!(
"accepted complexity fact was not acquired by its approved child"
));
}
let proposition = next
.propositions
.get(&proposition_id)
.ok_or_else(|| anyhow!("accepted complexity fact lost proposition content"))?;
admitted.insert(
proposition_id.id.clone(),
WorldFact {
id: proposition_id.id.clone(),
statement: proposition.statement.clone(),
scope: proposition.scope.clone(),
evidence_receipt_ids: proposition
.evidence_receipt_ids
.iter()
.cloned()
.collect(),
discoverable_at_location_ids: proposition
.discoverable_at_places
.iter()
.map(|place| place.id.clone())
.collect(),
},
);
}
}
campaign.facts.extend(admitted);
Ok(())
}
fn project_accepted_fission(
campaign: &mut Campaign,
preview: &GestaltFissionPreview,
@ -982,6 +1261,7 @@ fn project_accepted_fission(
})
.cloned()
.collect::<Vec<_>>();
project_elaborated_fission_facts(campaign, preview, next)?;
for approved in &preview.children {
let subject = population_subject(&approved.id);
@ -995,21 +1275,12 @@ fn project_accepted_fission(
.get(&format!("identity:canonical:{}", approved.id))
.filter(|identity| identity.subject == subject && identity.active)
.ok_or_else(|| anyhow!("accepted fission child lacks exact identity"))?;
campaign.gestalts.insert(
approved.id.clone(),
GestaltPersonaState {
schema: "ghostlight.gestalt_persona_state.v1".into(),
id: approved.id.clone(),
name: identity.value.clone(),
version: 0,
home_location_id: place.id.clone(),
shared_capabilities: parent.shared_capabilities.clone(),
shared_knowledge: parent.shared_knowledge.clone(),
resources: BTreeSet::new(),
goals: parent.goals.clone(),
pressures: parent.pressures.clone(),
},
);
verify_fission_child_components(next, approved)?;
let mut accepted = approved.clone();
accepted.name = identity.value.clone();
accepted.home_location_id = place.id.clone();
accepted.resources.clear();
campaign.gestalts.insert(approved.id.clone(), accepted);
}
let member_ids = campaign
@ -3888,6 +4159,111 @@ mod tests {
.unwrap();
}
#[test]
fn elaborated_fission_commits_qualified_child_semantics_in_one_typed_batch() {
let mut campaign = campaign();
let inherited_knowledge = campaign.facts["fact:route"].statement.clone();
let parent = GestaltPersonaState {
schema: "ghostlight.gestalt_persona_state.v1".into(),
id: "residents".into(),
name: "Residents".into(),
version: 0,
home_location_id: "room".into(),
shared_capabilities: BTreeSet::from(["keep the public stair open".into()]),
shared_knowledge: BTreeSet::from([inherited_knowledge.clone()]),
resources: BTreeSet::new(),
goals: vec!["keep the hall inhabited".into()],
pressures: vec!["the night watch is thin".into()],
};
campaign.gestalts.insert(parent.id.clone(), parent.clone());
crate::resolution::ensure_agency_profiles(&mut campaign);
let situated_knowledge =
"The eastern lintel rings when the sealed cistern changes pressure.".to_owned();
let mut specific = parent.clone();
specific.id = "lintel-keepers".into();
specific.name = "Lintel Keepers".into();
specific
.shared_capabilities
.insert("sound the lintel before opening the cistern".into());
specific.shared_knowledge.insert(situated_knowledge.clone());
specific
.goals
.push("win custody of the eastern cistern key".into());
specific
.pressures
.push("the cistern key is promised to two rival crews".into());
let mut residual = parent.clone();
residual.id = "residents-unresolved".into();
residual.name = "Unresolved Residents".into();
let preview = GestaltFissionPreview {
schema: "ghostlight.gestalt_fission_preview.v1".into(),
campaign_id: campaign.id,
expected_world_revision: campaign.revision,
parent_gestalt_id: parent.id.clone(),
partition_axis: crate::domain::AgencyAxis::Ideology,
children: vec![specific.clone(), residual],
child_partition_values: BTreeMap::from([
(specific.id.clone(), "lintel custody".into()),
("residents-unresolved".into(), "other/unknown".into()),
]),
residual_child_id: "residents-unresolved".into(),
member_child_assignments: BTreeMap::new(),
resource_child_assignments: BTreeMap::new(),
evidence_receipt_ids: vec![],
gaps: vec![],
canon_candidates: vec![],
requires_approval: true,
};
assert!(lower_fission(&campaign, &preview, Utc::now() + Duration::minutes(5)).is_err());
let transition =
lower_elaborated_fission(&campaign, &preview, Utc::now() + Duration::minutes(5))
.unwrap();
assert!(transition.batch.mutations.iter().any(|mutation| matches!(
mutation.mutation,
WorldMutation::AdmitEntity {
initial_profile: Some(AdmittedEntityProfile::Proposition { .. }),
..
}
)));
assert!(
transition.batch.mutations.iter().any(|mutation| matches!(
mutation.mutation,
WorldMutation::ChangeCapability { .. }
))
);
assert!(
transition
.batch
.mutations
.iter()
.any(|mutation| matches!(mutation.mutation, WorldMutation::ChangeKnowledge { .. }))
);
assert!(
transition.batch.mutations.iter().any(|mutation| matches!(
mutation.mutation,
WorldMutation::ChangeCommitment { .. }
))
);
assert!(
transition
.batch
.mutations
.iter()
.any(|mutation| matches!(mutation.mutation, WorldMutation::ChangePressure { .. }))
);
apply_lowered_elaborated_fission(&mut campaign, &preview, &transition, Utc::now()).unwrap();
assert_eq!(campaign.gestalts[&specific.id], specific);
assert!(campaign.facts.values().any(|fact| {
fact.statement == situated_knowledge
&& fact.scope == crate::domain::FactScope::ProvisionalLocal
}));
crate::transition::validate_component_world(&component_snapshot(&campaign).unwrap())
.unwrap();
}
#[test]
fn shared_learning_updates_the_materialized_member_projection() {
let mut campaign = campaign();

View file

@ -5498,12 +5498,14 @@ fn player_http_command_allowed(command: &WorldCommand, player_actor_id: &str) ->
| WorldCommand::MaterializeGestaltMember { .. }
| WorldCommand::DematerializeGestaltMember { .. }
| WorldCommand::IndividuateGestaltMember { .. }
| WorldCommand::ElaborateGestaltIndividuation { .. }
| WorldCommand::ReconcileGestaltPresence { .. }
| WorldCommand::ResolveReactionWave { .. }
| WorldCommand::ResolveNpcAction { .. }
| WorldCommand::SetProviderParallelism { .. }
| WorldCommand::ReplaceResolutionPins { .. }
| WorldCommand::FissionGestalt { .. }
| WorldCommand::ElaborateGestaltFission { .. }
| WorldCommand::ReconcileFissionCivicBindings { .. }
| WorldCommand::BindClockConsequences { .. } => false,
}

View file

@ -1174,6 +1174,7 @@ impl CampaignStore {
receipt_key: &str,
world_receipt: &WorldCommitReceipt,
gestalt_receipt: &GestaltMaterializationReceipt,
model_receipts: &[crate::model::ModelStageReceipt],
) -> Result<CultCacheEnvelope> {
let next_row = envelope(
&expected.r#type,
@ -1181,7 +1182,7 @@ impl CampaignStore {
&expected.key,
next,
)?;
let rows = vec![
let mut rows = vec![
next_row.clone(),
envelope(
"world_commit_receipt.v1",
@ -1196,10 +1197,34 @@ impl CampaignStore {
gestalt_receipt,
)?,
];
if !self
.inner
.compare_and_swap_batch(std::slice::from_ref(expected), rows)?
{
let mut expected_rows = vec![expected.clone()];
let mut unique_model_receipts = BTreeMap::<String, &ModelStageReceipt>::new();
for item in model_receipts {
if let Some(existing) = unique_model_receipts.get(item.storage_key()) {
if !existing.same_receipted_content(item) {
return Err(anyhow!(
"immutable model-stage receipt conflict: {}",
item.storage_key()
));
}
continue;
}
unique_model_receipts.insert(item.storage_key().to_owned(), item);
}
for item in unique_model_receipts.into_values() {
if let Some(existing) = self.matching_model_stage_receipt(item)? {
expected_rows.push(existing.clone());
rows.push(existing);
} else {
rows.push(envelope(
"persona_stage_receipt.v1",
"ghostlight.persona_stage_receipt.v1",
item.storage_key(),
item,
)?);
}
}
if !self.inner.compare_and_swap_batch(&expected_rows, rows)? {
return Err(anyhow!("stale CultCache snapshot"));
}
Ok(next_row)

View file

@ -18,6 +18,38 @@ pub fn information_channel_is_concrete(channel: &str) -> bool {
!channel.is_empty() && channel.len() <= 160 && !channel.eq_ignore_ascii_case("unknown")
}
/// Canonical world subjects and their agency projections share one ID space.
/// Keep this predicate deterministic and side-effect free: validators, rebases,
/// and kernel application all consult the same occupied-set definition.
pub(crate) fn canonical_subject_id_is_occupied(campaign: &Campaign, subject_id: &str) -> bool {
canonical_subject_id_is_occupied_except_member(campaign, subject_id, None)
}
pub(crate) fn canonical_subject_id_is_occupied_except_member(
campaign: &Campaign,
subject_id: &str,
permitted_member_id: Option<&str>,
) -> bool {
let permitted_member_id =
permitted_member_id.map(crate::domain::canonical_gestalt_member_local_id);
campaign.actors.contains_key(subject_id)
|| campaign.institutions.contains_key(subject_id)
|| campaign.gestalts.contains_key(subject_id)
|| campaign.gestalt_members.iter().any(|(member_id, member)| {
permitted_member_id.as_deref()
!= Some(crate::domain::canonical_gestalt_member_local_id(member_id).as_str())
&& crate::domain::gestalt_member_subject_id(&member.id) == subject_id
})
|| campaign
.agency_profiles
.iter()
.any(|(profile_key, profile)| {
profile_key == subject_id
|| profile.id == subject_id
|| profile.subject_id == subject_id
})
}
#[derive(Clone, Debug)]
struct Candidate {
left: String,
@ -2258,6 +2290,27 @@ pub fn validate_gestalt_individuation(
if member.name.chars().count() > 160 {
findings.push("member name exceeds 160 characters".to_owned());
}
let public_name_key = public_identity_key(&member.name);
if public_name_key.is_empty() {
findings.push("member name has no public identity characters".to_owned());
}
if campaign
.actors
.values()
.any(|actor| public_identity_key(&actor.name) == public_name_key)
&& !public_name_key.is_empty()
{
findings.push("member name duplicates an established public Actor identity".to_owned());
}
if campaign
.gestalt_members
.values()
.any(|existing| public_identity_key(&existing.name) == public_name_key)
&& !public_name_key.is_empty()
{
findings
.push("member name duplicates an established public population identity".to_owned());
}
if member.goals.len() > 8 {
findings.push("member has more than eight goals".to_owned());
}
@ -2278,11 +2331,11 @@ pub fn validate_gestalt_individuation(
{
findings.push("member id duplicates an established population identity".to_owned());
}
if campaign
.actors
.contains_key(&crate::domain::gestalt_member_subject_id(&member.id))
{
findings.push("member id duplicates an established Actor identity".to_owned());
if canonical_subject_id_is_occupied(
campaign,
&crate::domain::gestalt_member_subject_id(&member.id),
) {
findings.push("member id duplicates an established canonical subject identity".to_owned());
}
let unsupported_relationships = member
.relationships
@ -2313,6 +2366,17 @@ pub fn validate_gestalt_individuation(
}
}
/// Public names are an interface identity, not merely decorative text. Fold
/// case and punctuation so spelling variants such as `Flint-Measure` and
/// `Flintmeasure` cannot silently create two apparently identical people.
pub fn public_identity_key(value: &str) -> String {
value
.chars()
.filter(|character| character.is_alphanumeric())
.flat_map(char::to_lowercase)
.collect()
}
pub(crate) fn validate_strategic_individuation_proposals(
campaign: &Campaign,
proposals: &[StrategicGestaltIndividuation],
@ -3337,6 +3401,9 @@ pub(crate) fn project_fission_resolution(
campaign.gestalts.insert(child.id.clone(), child.clone());
let mut profile = profile_for_gestalt(child, &preview.evidence_receipt_ids);
profile.parent_subject_id = Some(preview.parent_gestalt_id.clone());
if child.id == preview.residual_child_id {
profile.simulation_eligible = false;
}
profile.facets = inherited.facets.clone();
profile
.facets
@ -3365,6 +3432,36 @@ pub(crate) fn project_fission_resolution(
}
pub fn validate_fission(campaign: &Campaign, preview: &GestaltFissionPreview) -> Result<()> {
validate_fission_shape(campaign, preview, false)
}
pub fn validate_elaborated_fission(
campaign: &Campaign,
preview: &GestaltFissionPreview,
) -> Result<()> {
validate_fission_shape(campaign, preview, true)
}
fn preserves_inherited_prefix_with_unique_additions(
values: &[String],
inherited: &[String],
) -> bool {
if !values.starts_with(inherited) {
return false;
}
let inherited_len = inherited.len();
let inherited = inherited.iter().collect::<BTreeSet<_>>();
let mut additions = BTreeSet::new();
values[inherited_len..]
.iter()
.all(|value| !inherited.contains(value) && additions.insert(value))
}
fn validate_fission_shape(
campaign: &Campaign,
preview: &GestaltFissionPreview,
allow_semantic_additions: bool,
) -> Result<()> {
let parent = campaign
.gestalts
.get(&preview.parent_gestalt_id)
@ -3457,8 +3554,8 @@ pub fn validate_fission(campaign: &Campaign, preview: &GestaltFissionPreview) ->
format!("child {label} version is not zero"),
);
require(
!campaign.gestalts.contains_key(&child.id),
format!("child {label} already exists"),
!canonical_subject_id_is_occupied(campaign, &child.id),
format!("child {label} collides with an established canonical subject identity"),
);
require(
campaign.locations.contains_key(&child.home_location_id),
@ -3470,20 +3567,45 @@ pub fn validate_fission(campaign: &Campaign, preview: &GestaltFissionPreview) ->
format!("child {label} changed home outside a geography fission"),
);
require(
child.shared_capabilities == parent.shared_capabilities,
format!("child {label} changed inherited capabilities"),
if allow_semantic_additions {
child
.shared_capabilities
.is_superset(&parent.shared_capabilities)
} else {
child.shared_capabilities == parent.shared_capabilities
},
format!("child {label} changed inherited capabilities outside its authority"),
);
require(
child.shared_knowledge == parent.shared_knowledge,
format!("child {label} changed inherited knowledge"),
if allow_semantic_additions {
child.shared_knowledge.is_superset(&parent.shared_knowledge)
} else {
child.shared_knowledge == parent.shared_knowledge
},
format!("child {label} changed inherited knowledge outside its authority"),
);
require(
child.goals == parent.goals,
format!("child {label} changed inherited goals"),
if allow_semantic_additions {
preserves_inherited_prefix_with_unique_additions(&child.goals, &parent.goals)
} else {
child.goals == parent.goals
},
format!(
"child {label} must preserve inherited goals as an exact prefix and add no duplicates"
),
);
require(
child.pressures == parent.pressures,
format!("child {label} changed inherited pressures"),
if allow_semantic_additions {
preserves_inherited_prefix_with_unique_additions(
&child.pressures,
&parent.pressures,
)
} else {
child.pressures == parent.pressures
},
format!(
"child {label} must preserve inherited pressures as an exact prefix and add no duplicates"
),
);
require(
preview.child_partition_values.contains_key(&child.id),
@ -3659,6 +3781,118 @@ pub(crate) mod tests {
value
}
#[test]
fn fission_validation_uses_the_unified_canonical_subject_id_namespace() {
let mut value = campaign(1, 1);
value.gestalts.insert(
"villagers".into(),
GestaltPersonaState {
schema: "ghostlight.gestalt_persona_state.v1".into(),
id: "villagers".into(),
name: "Villagers".into(),
version: 0,
home_location_id: "center".into(),
shared_capabilities: BTreeSet::new(),
shared_knowledge: BTreeSet::new(),
resources: BTreeSet::new(),
goals: vec![],
pressures: vec![],
},
);
ensure_agency_profiles(&mut value);
let mut detached_profile = value.agency_profiles["villagers"].clone();
detached_profile.id = "reserved-profile-id".into();
detached_profile.subject_id = "reserved-profile-subject".into();
detached_profile.active_leaf = false;
detached_profile.simulation_eligible = false;
value
.agency_profiles
.insert("reserved-profile-key".into(), detached_profile);
value.gestalt_members.insert(
"reserved-member".into(),
GestaltMemberDelta {
schema: "ghostlight.gestalt_member_delta.v1".into(),
id: "reserved-member".into(),
gestalt_id: "villagers".into(),
version: 0,
name: "Reserved Member".into(),
capability_additions: BTreeSet::new(),
capability_removals: BTreeSet::new(),
knowledge_additions: BTreeSet::new(),
knowledge_removals: BTreeSet::new(),
equipment: BTreeSet::new(),
conditions: BTreeSet::new(),
obligations: BTreeSet::new(),
relationships: BTreeMap::new(),
goals: vec![],
memories: vec![],
last_location_id: Some("center".into()),
materialized_actor_id: None,
last_relevant_revision: 0,
relevance_lease_until_revision: 0,
},
);
let child = |id: &str, name: &str| GestaltPersonaState {
schema: "ghostlight.gestalt_persona_state.v1".into(),
id: id.into(),
name: name.into(),
version: 0,
home_location_id: "center".into(),
shared_capabilities: BTreeSet::new(),
shared_knowledge: BTreeSet::new(),
resources: BTreeSet::new(),
goals: vec![],
pressures: vec![],
};
let preview = GestaltFissionPreview {
schema: "ghostlight.gestalt_fission_preview.v1".into(),
campaign_id: value.id,
expected_world_revision: value.revision,
parent_gestalt_id: "villagers".into(),
partition_axis: AgencyAxis::Ideology,
children: vec![
child("new-villagers", "New villagers"),
child("other", "Other"),
],
child_partition_values: BTreeMap::from([
("new-villagers".into(), "new".into()),
("other".into(), "other/unknown".into()),
]),
residual_child_id: "other".into(),
member_child_assignments: BTreeMap::new(),
resource_child_assignments: BTreeMap::new(),
evidence_receipt_ids: vec![],
gaps: vec![],
canon_candidates: vec![],
requires_approval: true,
};
for occupied_id in [
"player",
"faction-0000",
"villagers",
"reserved-profile-key",
"reserved-profile-id",
"reserved-profile-subject",
"member:reserved-member",
] {
let mut colliding = preview.clone();
colliding.children[0].id = occupied_id.into();
colliding.child_partition_values.remove("new-villagers");
colliding
.child_partition_values
.insert(occupied_id.into(), "new".into());
let diagnostic = validate_fission(&value, &colliding)
.unwrap_err()
.to_string();
assert!(
diagnostic.contains("collides with an established canonical subject identity"),
"{occupied_id} escaped the canonical subject namespace: {diagnostic}"
);
}
}
#[test]
fn profile_maintenance_never_promotes_knowledge_into_public_channels() {
let mut value = campaign(1, 1);
@ -5104,6 +5338,8 @@ pub(crate) mod tests {
assert!(!value.agency_profiles["villagers"].active_leaf);
assert!(value.agency_profiles["traditionalists"].active_leaf);
assert!(value.agency_profiles["other"].active_leaf);
assert!(value.agency_profiles["traditionalists"].simulation_eligible);
assert!(!value.agency_profiles["other"].simulation_eligible);
assert!(value.gestalts["villagers"].resources.is_empty());
assert!(value.gestalts["traditionalists"].resources.is_empty());
assert_eq!(
@ -5189,6 +5425,8 @@ pub(crate) mod tests {
.unwrap();
assert!(!value.agency_profiles["other"].active_leaf);
assert!(value.agency_profiles["other-smiths"].active_leaf);
assert!(value.agency_profiles["other-smiths"].simulation_eligible);
assert!(!value.agency_profiles["other-unknown"].simulation_eligible);
assert!(value.gestalts["other"].resources.is_empty());
assert!(value.gestalts["other-smiths"].resources.is_empty());
assert_eq!(

View file

@ -1292,8 +1292,8 @@ outside this authority.
- **Outputs:** one frozen ordered `world-regions-plan.json`, one immutable
proposal checkpoint before each mutation, one sequential kernel commit per
genuinely new realm, and an immutable `world-region-NN-checkpoint.json`
binding request, origin, committed jurisdiction, kernel receipt, and
model-receipt hashes. Committed
binding request, origin, committed jurisdiction, exact compiler mutation
proof, kernel receipt, and model-receipt hashes. Committed
jurisdiction IDs become the locality set for later elaboration and strategic
simulation.
- **Derived state:** `status.json` progress and the jurisdiction list are
@ -1309,8 +1309,10 @@ outside this authority.
- **Shared paths:** fresh and resumed runs use the same frozen ordered plan,
destination compiler, and kernel admission path. Resume rejects omitted or
reordered requests, skips a region only when its checkpoint and canonical
receipt agree, and recovers the kernel-commit/checkpoint crash interval from
the immutable proposal plus canonical campaign and commit receipt.
receipt and mutation proof agree. Foundation, region, titled, and complexity
recovery all bind the immutable prepared candidate to the exact canonical
receipt; the presence of a plausible ID or checkpoint file is never treated
as proof that its content committed.
- **Cut line:** whole-world scope is not overloaded onto the root compiler. The
driver reuses existing destination compilation and kernel admission; there is
no second region generator or event authority.
@ -1322,335 +1324,185 @@ outside this authority.
Runs 64 and 65 stopped before that claim was established and remain failure
evidence rather than accepted full-world runs.
#### Iterative latent-world complexity and elaborator continuity
#### Qualified latent-world complexity and elaborator continuity
- **Owner:** the strategic acceptance driver owns round orchestration,
deterministic titled-elaborator scheduling, checkpoint publication, and
bounded termination. `WorldScaleIntent` is consumer-authored;
`derive_world_elaboration_demand` deterministically owns the target, deficit,
realm shares, and per-round mutation pressure. Each titled elaborator owns
only its compact title-by-realm working frontier and mutation draft. The
acceptance driver owns deterministic parent-to-jurisdiction routing. After a
frozen parallel batch it also owns exact member-ID collision supersession by
retaining the lowest dispatch ordinal.
`WorldComplexityTool` owns construction of the complete frozen proposal from
the draft. `WorldKernel` remains the sole canonical mutation owner, and
Resolution remains the owner of the later active cover.
- **Inputs:** the admitted campaign after region and locality elaboration; its
active-cell entitlement; a target cover ratio; equal acceptance-driver realm
weights; a weighted titled-elaborator profile; bounded parallelism and round
limits; active, simulation-eligible Gestalt leaves without a currently
materialized member; the prior title-by-realm session checkpoint; and the
exact frozen parent state, members, profile, and local relations for each assigned
mutation. Scheduler title deterministically selects the proposal lane:
Hearth and Veil individuate one consequential member; the other six titles
fission the assigned population along their fixed agency axis. The model sees
the assigned parent projection and compacted session, but its action schema
admits only the lane-specific delta. A fission draft contains child IDs,
names, partition values, the residual child, and exact member/resource
assignments. An individuation draft is a compact
`WorldComplexityMemberDraft`: the model owns only the proposed local ID,
public name, capability and knowledge deltas, equipment, conditions,
obligations, exact-ID relationships, goals, and memories.
`elaboration::unique_containing_jurisdiction` is the single read-only owner
for deriving parent jurisdiction from canonical topology. For every exact
operating place in `AgencyProfile.location_ids`, it walks
`Location.container_id` ancestry until it reaches a consumer realm named by
`realm_subject_targets`. Exactly one
distinct realm target returns that jurisdiction. Zero targets or multiple
distinct targets return no jurisdiction; a containment cycle terminates that
ancestry walk without inventing one. Strategic candidate allocation and
title-by-realm session routing call this same resolver, and
`ModelWorldComplexityWorker` constructor admission calls it again against the
assigned jurisdiction before accepting the parent-to-jurisdiction map.
- **Outputs:** each round publishes an immutable frozen preview with demand,
wave and schedule receipts, exact parent bindings, proposals, and model
receipt hashes; then one immutable mutation checkpoint per sequentially
admitted fission or individuation; one compacted checkpoint for each title
that obtained an admitted mutation in that round; and a terminal round
checkpoint carrying the scheduler state, mutation paths, immutable
superseded-invocation checkpoint paths for same-batch duplicate member IDs,
the accumulated
title-by-realm session checkpoints keyed by stable
`<lowercase-title>:<jurisdiction-id>` session ID, and remeasured
actionable-subject count. Per-round session filenames contain the title plus
a stable digest suffix of that session ID; filenames are storage addresses,
while the map key and checkpoint's own `session_id` carry routing identity.
The final count and round reports are acceptance metadata, not canonical
world state.
- **Derived state:** actionable complexity is the count of canonical active,
simulation-eligible agency leaves. Dormant member rows, census texture,
model calls, dispatches, mutation budget, and compacted prose do not satisfy
the target. Inherited child capabilities, knowledge, goals, pressures,
evidence fields, campaign/revision binding, approval requirement, parent
version, partition axis, and individuation location are deterministic
projections of the frozen assignment, not model-authored state. An
individuated member's public name is presentation state attached to its stable local
member ID and canonical `member:<local-id>` subject ID; name equality does
not merge subjects, resolve an address, or grant authority over another
Actor or population member.
checkpoint's title, jurisdiction, frontier summary, unresolved leads,
rejection findings, digest chain, and recent commit IDs are bounded steering
memory. Raw provider sessions, request history, and transcripts are neither
continuity nor world state; the checkpoint is the only carried elaborator
context, while the campaign and kernel commit receipts remain truth.
Resolution cover membership and Nemesis decision windows are downstream
derivations and do not feed back into complexity admission authority.
Same-batch individuation supersession is scheduling evidence, not canonical
member state: the earliest dispatch ordinal for one canonicalized local
member ID is retained, while later matching invocations contribute no commit
or session-memory journal. The following round derives fresh demand from the
admitted campaign rather than treating supersession as completed complexity.
- **Forbidden writers:** elaborators, session compaction, the scheduler,
demand derivation, preview files, round files, and acceptance metadata cannot
write campaign state, declare a target satisfied, allocate the Resolution
cover, or turn narrative memory into facts. A fission proposal cannot approve
or commit itself, invent evidence, promote raw roster rows, or rewrite a
parent. An individuation cannot invent a population, duplicate an existing
exact actor or member ID, materialize itself, or acquire relationships to
nonexistent subject IDs. Public names are not writers of identity or
authority: no lookup, relationship, action target, or admission decision may
select or merge a subject by display-name equality. The model cannot emit or
override the member schema, parent Gestalt ID, version, exact location,
materialization state, or relevance revisions because those deterministic
fields are absent from `WorldComplexityMemberDraft`. The model cannot restate
or alter inherited child
Persona constitutions, campaign identity, parent identity/version, location,
partition axis, evidence, or approval authority because those fields do not
exist in its mutation-draft schema. The model provider cannot select a realm,
merge title sessions, persist a transcript as memory, or redirect a
checkpoint to another title or jurisdiction. Neither the proposing model nor
commit order may choose the winner of a same-batch exact member-ID collision;
later duplicate invocations cannot write commits or compaction journals.
Sequential commits may update
only the optimistic revision of a frozen fission proposal after the exact
parent binding remains unchanged; individuation instead revalidates its exact
Gestalt version and location against current state.
- **Shared paths:** all round workers use the existing generic elaborator
dispatcher and weighted schedule. `WorldComplexityTool` first lowers a
compact draft into the existing full proposal algebra. For fission, it clones
the frozen parent Persona into every child, replaces only child identity,
name, version, and assigned resources, builds the child-partition map,
attaches the exact campaign/revision/parent/axis and empty
evidence/gap/canon fields, and fixes `requires_approval=true`. For
individuation, it lowers `WorldComplexityMemberDraft` into the canonical
`GestaltMemberDelta`: it fixes the member schema, parent Gestalt ID, version
zero, exact first parent-profile location, unmaterialized state, and zeroed
relevance revisions around the model-owned semantic member content. Every
constructed proposal then passes the existing fission or individuation
validator and the existing
`WorldCommand::FissionGestalt` or `WorldCommand::IndividuateGestaltMember`
mailbox. `resolution::validate_gestalt_individuation` is the single local
authority for exact member-ID uniqueness, parent version, exact location,
bounds, and exact relationship subject IDs; strategic action-bound selection
and direct kernel admission both reuse it. Canonical member addressing uses
`canonical_gestalt_member_local_id` and `gestalt_member_subject_id`, while
strategic actions and relationships carry exact subject IDs rather than
public names. It accumulates every failed deterministic clause in
one rejection so the generic agent loop can repair the exact bounded draft in
its next semantic step; the tool does not add a second judge or relax kernel
admission. Direct individuation then follows the ordinary
presence path: `apply_individuation` inserts the canonical member delta and
promotes it into an Actor, `commit_gestalt_presence` calls
`ensure_agency_profiles`, and the new active simulation-eligible actor profile
becomes visible to `canonical_actionable_subject_count`. Parallel workers
read one frozen campaign. Before serial admission, the round runner
canonicalizes every proposed local member ID, retains the invocation with the
earliest dispatch ordinal for each ID, and publishes one immutable
`ghostlight.complexity_superseded_invocation.v1` checkpoint for every later
duplicate, binding its dispatch, retained ordinal, canonical member ID, and
diagnostic. Only retained invocations enter session routing, kernel commit,
and successful compaction journals. IDs already present in the frozen
canonical campaign remain ordinary semantic failures owned by
`validate_gestalt_individuation`; supersession does not weaken that check.
Retained commits serialize through WorldKernel. Unrelated
earlier fissions may be rebased,
while any change to the assigned parent, its profile, or its members rejects
the later proposal instead of silently reconciling it.
Compaction runs only after successful mutations have produced admitted commit
journals routed by the invocation's stable title-by-realm session ID. The
resulting checkpoint is stored under that ID and supplied only when a later
dispatch has the same title and deterministically derived jurisdiction.
The existing `ElaboratorSessionCompactionTool` owns the model-boundary
compaction contract: its action schema requires a nonempty frontier summary
of at most 4,000 characters, at most 32 unresolved leads, and each lead to
contain 1 through 600 characters. Tool admission rechecks those same bounds
and returns the complete indexed mismatch set; checkpoint construction then
revalidates the identical shape before hashing and persistence.
The exact failure partition, frozen schedule, completed proposals, receipts,
and diagnostics remain in the resumable failure checkpoint; they are not
folded into successful session compaction.
- **Cut line:** this cut reuses Gestalt fission, strategic Gestalt
individuation, WorldKernel admission, model receipts, the generic scheduler,
and immutable acceptance checkpoints. It adds no second population or member
insertion path, campaign store, parallel commit owner, cover allocator, or
transcript-shaped session log. `WorldComplexityAction` is a model-boundary
draft, not a new canonical mutation or persistent state authority; the
existing `WorldComplexityProposal` remains the checked checkpoint and kernel
handoff. The prior partial-wave checkpoint remains recovery for one frozen
dispatch partition; it is not
elaborator memory. The title-by-realm checkpoint map replaces both the former
title-only global memory and any temptation to preserve provider transcripts;
it introduces no provider-session store or second jurisdiction allocator.
Compact checkpoints replace accumulated working history without replacing
canonical commits. Individuation admission requires unique exact IDs, not a
global public-name registry; the cut adds no alias table, fuzzy resolver, or
name-disambiguation authority. `WorldComplexityMemberDraft` is only the
model-boundary semantic payload; it is not a second member-state schema or
persistence owner, and deterministic lifecycle fields do not cross the model
boundary. The compaction repair adds no compactor, checkpoint type, session
store, or memory authority; it closes the item-bound seam between the
existing model schema, tool admission, and checkpoint validator. Parallel
member-ID supersession adds no namespace allocator, alias registry,
reconciliation pass, or alternate admission owner; it is a deterministic
filter over one frozen round's already-produced invocations.
- **Verification layer:** checkpoint shape and digest bind title, session,
campaign, target location, generation, world revision, prior checkpoint, and
bounded narrative fields. Frozen parent digests make conflicting parallel
mutations uncommittable. Existing fission validation proves lineage, child
partitioning, resource custody, and member transfer; shared individuation
validation proves active parent and location, exact version, unique bounded
member/Actor IDs, exact valid relationship targets, and non-materialized
member state. Equal public names on distinct exact IDs remain admissible and
do not change address resolution or authority. The focused tool regression
admits a new member whose name matches an established Actor, still rejects a
colliding canonical Actor ID and unsupported relationship subject, and
confirms name collision is absent from the rejection. WorldKernel
revalidates either lane before atomic mutation. The model schema exposes only
the assigned operation, bounds fission children to two through eight,
requires exactly the frozen parent resource and dormant-member IDs as
assignment-map keys while leaving child custody model-authored, and for
individuation exposes only semantic member fields and contains no `schema`,
`gestalt_id`, `version`, `last_location_id`, `materialized_actor_id`,
`last_relevant_revision`, or `relevance_lease_until_revision`; a focused
schema regression proves those deterministic fields are absent and the tool
still constructs an admissible canonical individuation. Tool-level
construction additionally rejects duplicate child IDs/partitions or
incomplete exact
member assignment before shared semantic validation. The shared fission
validator owns one complete deterministic mismatch set, including frozen
binding, child structure and inheritance, member assignments, resource
conservation and custody, approval, and parent profile eligibility; every
caller receives that set rather than a generic first-failure umbrella.
Worker construction requires exactly one jurisdiction for every assigned
parent, rejects extra routes, and reuses
`unique_containing_jurisdiction` to prove that the assigned jurisdiction
contains that parent's exact profile locations. Resume and
worker construction require every checkpoint
map key to equal both the embedded session ID and the session ID recomputed
from its embedded title and target location; checkpoint validation binds
those fields to its digest and current campaign. Runtime lookup
derives the expected ID from the current dispatch title and routed realm.
Routing tests prove that equal titles in different realms and different
titles in one realm produce different IDs. Each round must strictly
increase the canonical actionable count; completion requires a freshly
derived zero deficit rather than exhaustion of calls or rounds. Focused tests
cover scale arithmetic, checkpoint tampering and cross-title rejection, exact
compaction ancestry, assigned-parent validation, and safe revision rebasing.
A focused compaction regression proves the generated schema carries the
per-lead 1/600 character bounds and that empty and overlong leads in one
action produce separate index-addressed findings rather than one generic
combined diagnostic.
A focused batch regression proves that two parallel individuations with one
canonical member ID retain the lower dispatch ordinal, publish the later
invocation as superseded with the exact retained ordinal and ID, and leave
an unrelated member ID retained.
- **Failure and resume:** a provider failure publishes an immutable
round-failure generation with the exact completed and failed dispatches and
receipts, then stops that run. Explicit resume rehydrates the latest failure,
preserves completed invocations, and invokes only the failed original
dispatches through `resume_elaboration_wave`; repeated failures publish
numbered generations without changing the frozen schedule. A published
successful round preview may also be reused on explicit resume. Existing
mutation checkpoints suppress replay, title-by-realm compaction checkpoints
suppress recompaction, immutable superseded-invocation checkpoints must
exactly match recomputed collision ownership and suppress commit/journal work
for those later duplicates, and consecutive completed round checkpoints
restore scheduler fairness and session memory before the first unfinished
round.
After mutation admission, independent title-by-realm journals compact in
parallel under the same bounded complexity parallelism entitlement. Each
compactor reads the same committed campaign revision and only its own prior
session checkpoint and journal; the runner persists every successful model
receipt and immutable session checkpoint before returning any sibling
failure. Session checkpoints remain independent memory owners rather than a
shared round transcript, and canonical mutation order remains serial.
Tool-internal rejection findings are not currently copied into later
successful compaction. That is a bounded continuity limitation: the compacted
title-by-realm mind may lose a useful rejected path, but no live proposal is
orphaned because exact failed dispatches remain owned by the resumable failure
checkpoint. This limitation was not the Run 93 blocker.
Missing eligible parents, stale or malformed checkpoints, changed parent
bindings, zero-growth rounds, and a nonzero deficit at the configured round
ceiling are terminal failures. Canonical CultCache state, not the checkpoint
prose, decides whether already admitted mutations exist after interruption.
Parallel fission proposals may independently choose the same globally scoped
child identifier. Rebase treats that as syntactic contention rather than a
semantic contradiction: only an occupied child ID is deterministically moved
into the assigned parent's content-addressed namespace, with partition,
residual, member, and resource references rewritten together before the
ordinary complete fission validator runs. Names, partition meaning, custody,
and membership remain model-authored; canonical state still admits the result
only through WorldKernel.
One boundary remains intentionally narrow in this acceptance-driver cut.
Fission and named-member individuation are live, but institution promotion is
not. Dormant member rows still do not count; the individuation lane earns its
increment by atomically promoting the admitted member into an Actor and deriving
that Actor's active agency profile through the shared presence-commit path.
Materializing one person does not retire their population as an elaboration
parent. A later fission transfers every exact member affiliation once. Dormant
members have their deltas rebased against the selected child baseline;
materialized people keep their Actor state byte-identical while their backing
member row moves to the selected child. The complete member-assignment map,
component membership transition, shared fission validator, and WorldKernel
commit remain the single path for both cases.
Architecture claims that complexity has no production caller, no durable
elaborator compaction owner, no partial-failure resume, or only a fission
proposal lane are stale.
The strategic acceptance runner exercises this path as a deliberate hybrid.
The existing destination compiler first commits the complete civic foundation
for one canonical locality. A second revision-bound titled wave then adds one
Patina child place with exact reciprocal routes, preserves the next civic
manifest version through Charter, and admits title-specific facts and a
political relation before independent civic verification and a second kernel
commit. The acceptance profile allocates three Patina calls and two calls to
each other title per locality; a seventeen-slot pass therefore invokes workers
in exact configured proportion under bounded parallelism. This proves the
titled path against a real provider without claiming that the older civic
foundation compiler has already been replaced by the swarm.
The strategic acceptance harness persists compiler, foundation, titled-wave,
and completed strategic-wave checkpoints beside the CultCache campaign. On an
explicit resume it loads canonical world state from CultCache, rehydrates model
receipts by content hash, restores scheduler state from the consumed schedule,
and continues at the first incomplete authority boundary. A committed
foundation is checked against its preserved civic apparatus. A titled preview
is not completion authority: only a post-kernel completion checkpoint bound to
the before/after revisions, semantic verifier, model receipts, exact
`WorldCommitReceipt`, and the kernel-owned mutation authority, batch, and
receipt may skip that commit. New checkpoints also bind the admission digest;
legacy inference instead binds the finalized candidate through the persisted
mutation proof before minting its checkpoint. The batch's
`intended_effect_digest` binds the complete finalized candidate before
deterministic aggregate projection, so the
resume verifier does not duplicate the reducer's set and ordering rules.
Run-32's older committed Canopy pass can be admitted once through that persisted
mutation proof plus its complete civic candidate, then gains the same durable
completion checkpoint. A partial titled wave routes through
`resume_elaboration_wave`. Each later terminal partial result is published as a
new immutable generation by same-directory atomic rename, preserving the prior
valid generation. A committed strategic tick without its matching durable wave
checkpoint is refused rather than guessed or replayed.
Both destination paths synthesize the smallest compatible branch-local routes,
geometry, people, supplies, procedures, capacity, responsibility, and doctrine
needed for play. Those choices remain visible as `branch_assumptions`; they may
vary between campaigns unless the Vault pins them. A `gap` is reserved for
contradictory canon baselines, an explicitly requested exact canon baseline that
cannot be anchored, or conflict with an approved capability. Ordinary source
silence is not a gap. Preview approval remains the only path from this proposal
into canonical world state.
- **Owner:** `elaboration::canonical_actionable_subject_ids` is the single
qualification derivation for world-scale complexity. It reads the canonical
campaign and returns the active subjects allowed to pay the scale target.
`derive_world_elaboration_demand` owns target and deficit arithmetic.
The strategic acceptance driver owns bounded round orchestration and selects
parents only from the same qualified-ID set. `WorldKernel` remains the sole
canonical mutation owner.
- **Inputs:** the admitted campaign; active, simulation-eligible agency profiles;
Gestalt lineages and residual IDs; each fission child's retained parent
semantics; the active-cell budget; the consumer-authored target cover basis
points; realm weights; topology-derived jurisdiction; realm pressure and
detail debt; bounded round and concurrency limits; model receipts; and the
compact title-by-jurisdiction elaborator checkpoint.
- **Qualification invariant:** compiler-admitted roots and individuated people
enter through their owning admission gates. A fission child qualifies only
when it is not any lineage residual, retains the exact parent baseline, and
has additions in at least two of capability, knowledge, goal, and pressure.
Residual populations preserve unresolved membership and lineage only. They
are non-simulation state, do not enter the public action namespace, do not pay
the complexity target, and cannot become recursive complexity parents.
Ordinary inherited-baseline fission children remain simulatable without
satisfying the qualified world-size bill.
- **Demand invariant:** the target actionable-subject count is
`ceil(active_cell_budget * 10_000 / target_cover_basis_points)`. An active
cell budget of 240 at a 20% cover target therefore requires 1,200 qualified
subjects. The demand is re-derived from accepted canonical state after every
round. The pre-wave phase boundary requires the qualified count to equal the
target exactly; a larger count is not accepted as a satisfied target. On
post-wave resume, the last fully validated complexity checkpoint owns that
historical pre-wave count because legitimate strategic individuation may
have increased the current campaign count.
- **Round dataflow:** frozen campaign -> qualified-ID derivation -> distinct
qualified parent selection by jurisdiction pressure and detail debt ->
deterministic titled dispatch -> parallel model proposals -> semantic
validation and same-batch supersession -> sequential kernel commits ->
qualified-ID re-derivation. Every accepted round must strictly increase the
qualified count. Missing parents, zero growth, or a nonzero deficit at the
round ceiling is terminal failure rather than completion telemetry.
- **Gain allocation:** `ModelWorldComplexityWorker` distributes the remaining
deficit over the assigned distinct parents, with an exact target gain of one
through six per dispatch. An elaborated fission emits target gain plus two
children because the retained parent retires and the residual child does not
qualify. The semantic validator checks the resulting
`children.len() - 2` against the qualification.
- **Proposal lanes:** Hearth, Tangle, and Veil individuate one consequential
member. Patina, Charter, Ledger, Ember, and Numen elaborate a population
fission. The model owns only the lane-specific draft; the tool reconstructs
campaign ID, revision, parent, axis, evidence and approval bindings around
that draft.
- **Fission draft and output:** each child draft contains an ID, public name,
partition value, home location, capability additions, knowledge additions,
goal additions, and pressure additions. The draft also names the residual
child and exact member and resource assignments. The tool clones the frozen
parent baseline, applies only those additions, and emits a
`WorldComplexityFissionQualification` binding title, jurisdiction, and exact
target actionable gain.
- **Fission semantic invariant:** every specific child has a normalized public
identity distinct from existing Gestalts and its siblings, remains inside its
assigned jurisdiction, and contributes at least two bounded causal
dimensions. Additions are bounded to two capabilities, two knowledge
statements, one goal, and one pressure, and must be semantically distinct
from the parent and siblings. Only Geography fission may move children away
from the parent home; when multiple admitted places exist it must use at
least two. The residual stays at the parent home with the exact parent
capability, knowledge, goal, and pressure baseline and the
`other/unknown` partition.
- **Title-specific fission shapes:** Patina requires a capability and goal.
Charter and Ledger require a goal, pressure, and either capability or
knowledge. Ember requires a pressure and either capability or knowledge.
Numen requires goal, pressure, and knowledge.
- **Individuation invariant:** every admitted member has a goal, a concrete
memory, and at least one additional identity-bearing dimension. Hearth also
requires a concrete care or belonging obligation. Tangle requires an exact
relationship to an existing canonical subject. Veil requires knowledge not
held by the parent population. The resolution validator rejects normalized
public-name collisions with existing actors and members; same-batch duplicate
canonical member IDs or public identities are superseded by the lowest
dispatch ordinal. Actor, institution, Gestalt, dormant member projection,
agency-profile key, profile ID, and profile subject ID share one canonical
subject-ID collision predicate at proposal validation, deterministic rebase,
and kernel application. Promotion alone may claim the exact member's own
reserved projection.
- **Typed mutation path:** `WorldCommand::ElaborateGestaltFission` carries the
preview, exact qualification, and model provenance. The kernel revalidates
both elaborated-fission structure and titled complexity semantics before
lowering. The lineage split first inherits the parent's component baseline.
Only this elaborated command may add the approved capability grants,
proposition admissions and knowledge acquisitions, goal commitments, and
pressures in the same component-world batch as identity, occupancy, custody,
membership, relationship, and civic rebindings.
- **Projection invariant:** component-batch application occurs against a cloned
overlay and validates the complete next world before aggregate projection.
Projection verifies exact capability entry IDs and descriptions, known
proposition statements, active goal descriptions, and unresolved pressure
labels against each approved child. It persists exactly the approved
preview-minus-parent knowledge additions as campaign facts through the
shared proposition resolver, copies the approved child semantics with typed
identity and occupancy, and derives resources from accepted custody. Campaign,
authority, batch, and receipt records persist only after projection succeeds.
- **Ordinary fission boundary:** `WorldCommand::FissionGestalt` retains the
strict inherited-baseline contract. Its lowering emits no semantic-delta
mutations. Elaborated semantic additions cannot enter through that path.
- **Relationship contribution:** the lineage reducer inherits every active
parent relationship to each child and aggregate projection retires the parent
relation. Fission has no free-form relationship-addition field. New
relationship texture enters qualified complexity through Tangle
individuation, while locality elaboration owns new civic relations.
- **Horizontal expansion boundary:** destination compilation and locality
elaboration own places, routes, population roots, institutions, civic
apparatus, and local political relations. Complexity fission may distribute
a Geography split across admitted places but cannot invent geography or a
parallel political apparatus. Horizontal civic foundations must therefore be
admitted before vertical complexity can elaborate them. Each admitted realm
must contain at least three mutually reachable internal places with resident
homes in at least three. At scale completion, each jurisdiction must place
qualified subjects in at least three locations and no one location may hold
more than four fifths of its qualified subjects.
- **Civic diversity context:** destination compilation and its semantic verifier
receive up to eight peer civic apparatus contexts. Candidate generation
requires materially executable means, preserves mixed, diasporic, and
cross-border constituencies, rejects ancestry-only civic partitions, and
admits locally coherent forms of redress. Verification separately requires
`political_form_distinct_or_deliberately_shared`,
`opposed_interests_have_distinct_means`, and
`candidate_text_intact`. Prompt excerpts are truncated only at word
boundaries.
- **Outputs:** canonical actors, Gestalts, members, agency profiles, lineages,
facts, component capabilities, knowledge, commitments, pressures,
relationships, memberships, occupancy, custody, civic bindings, and mutation
receipts. Qualified actionable IDs and the current deficit are always
derived from those canonical outputs; there is no independently writable
qualification flag.
- **Derived telemetry:** elaboration demand, round reports, previews, model
receipts, session compactions, supersession reports, and resume checkpoints
explain orchestration and recovery. They do not own world truth or
qualification. Session memory is bounded by title and jurisdiction and binds
only committed receipts; raw model sessions and transcripts are not
canonical state.
- **Recovery invariant:** every completed complexity round reopens its frozen
preview and validates every mutation, supersession, rejection, model receipt,
semantic verdict, canonical world receipt, and exact fission or individuation
mutation proof in one contiguous revision chain. An existing commit
checkpoint is accepted only after the same validator proves both historical
authority and the committed proposal effect. The cumulative session set must
equal prior memory plus every exact accepted/rejected invocation journal;
untouched sessions remain byte-equivalent and touched generations bind their
receipts, findings, prior digest, and round-end revision. Verifier transport
failure is a generational resumable checkpoint and never masquerades as
semantic rejection or causes the elaborator proposal to replay.
- **Forbidden writers:** model workers, schedulers, reports, checkpoints,
compaction text, cover construction, strategic cells, and renderers may not
create canonical subjects, mark a subject qualified, alter the target, or
repair semantic state after commit. Ordinary fission may not grant semantic
additions. Complexity fission may not create places, institutions, civic
systems, or arbitrary relationships. Residuals may not pay or recursively
service the target.
- **Shared paths:** initial runs and resumed runs derive demand and qualified
parents through the same functions. Rebase may update only the expected
revision when the frozen parent binding is unchanged; child-ID collisions are
renamed deterministically before revalidation. All fission writes, including
relationship inheritance and resource/member movement, pass through the same
typed batch and kernel commit boundary.
- **Cut line:** qualification is derived from canonical campaign semantics and
lineage, not stored beside them. The count, parent selector, progress check,
and completion check all consume that derivation. Any alternate active-leaf
counter, residual-inclusive quota path, or report-owned completion opinion is
outside the live authority model.
- **Verification layer:** focused tests prove 240 active cells at 20% require
1,200 subjects; residual and ordinary relabel fission children do not pay the
qualified bill; exact target gain is enforced; title-specific additions are
bounded and distinct; public identities cannot collide; duplicate
individuations supersede deterministically; elaborated fission commits its
capability, proposition, knowledge, commitment, pressure, relationship,
custody, membership, and fact projection atomically; component re-snapshot
validates; ordinary lowering rejects a semantic preview; and the destination
verifier enforces political distinctness, materially opposed means, and
intact candidate text.
Autonomous strategic waves run the same admitted agency state through parallel
cell membranes under a provider concurrency gate. Action-bound individuation