Attribute inaction inside gestalt arenas

This commit is contained in:
Meta Vens 2026-08-18 04:52:55 +02:00
commit 97e05c571e
10 changed files with 317 additions and 49 deletions

View file

@ -110,9 +110,9 @@ async fn main() -> anyhow::Result<()> {
.filter(|appraisal| {
appraisal.actions.is_empty()
&& appraisal
.inaction_reason
.as_deref()
.is_some_and(|reason| !reason.trim().is_empty())
.inactions
.iter()
.any(|inaction| !inaction.reason.trim().is_empty())
})
.count();
for stage in &output.stages {

View file

@ -489,6 +489,14 @@ pub struct CellActionProposal {
pub effect: StrategicCellEffect,
}
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq)]
#[serde(deny_unknown_fields)]
pub struct CellInaction {
pub subject_id: String,
#[schemars(length(min = 1, max = 240))]
pub reason: String,
}
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq)]
pub struct CellAppraisal {
pub schema: String,
@ -497,7 +505,7 @@ pub struct CellAppraisal {
pub resolution_epoch: u64,
pub considered_subject_ids: BTreeSet<String>,
pub actions: Vec<CellActionProposal>,
pub inaction_reason: Option<String>,
pub inactions: Vec<CellInaction>,
}
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq)]

View file

@ -4165,7 +4165,16 @@ mod tests {
resolution_epoch: campaign.resolution_policy.resolution_epoch,
considered_subject_ids: cell.subject_ids.clone(),
actions: vec![],
inaction_reason: Some("No justified move.".into()),
inactions: cell
.subject_ids
.iter()
.next()
.map(|subject_id| crate::domain::CellInaction {
subject_id: subject_id.clone(),
reason: "No justified move.".into(),
})
.into_iter()
.collect(),
})
.collect(),
cover,
@ -4370,7 +4379,7 @@ mod tests {
location_ids: vec![],
},
}];
wave.appraisals[0].inaction_reason = None;
wave.appraisals[0].inactions.clear();
let mut rejected_verifier = resolution_stage(
&wave.cover.cells[0].id,
&persisted,

View file

@ -129,7 +129,7 @@ pub struct CellTerminalBundle {
#[serde(deny_unknown_fields)]
struct CellAppraisalProposal {
actions: Vec<CellActionCandidate>,
inaction_reason: Option<String>,
inactions: Vec<crate::domain::CellInaction>,
}
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq)]
@ -236,7 +236,7 @@ const CELL_PROJECTION_OUTPUT_CONTRACT: &str = r#"{
const CELL_APPRAISAL_OUTPUT_CONTRACT: &str = r#"{
"type":"object",
"required":["actions","inaction_reason"],
"required":["actions","inactions"],
"properties":{
"actions":{"type":"array","items":{"type":"object","required":["subject_id","intent","intended_effect","priority","state_references","public_channels","effect"],"properties":{
"subject_id":{"type":"string"},"intent":{"type":"string"},"intended_effect":{"type":"string"},"priority":{"type":"integer"},
@ -251,7 +251,7 @@ const CELL_APPRAISAL_OUTPUT_CONTRACT: &str = r#"{
{"type":"object","required":["type","destination_gestalt_id"],"properties":{"type":{"const":"member_migration"},"destination_gestalt_id":{"type":"string"}}}
]}
}}},
"inaction_reason":{"type":["string","null"]}
"inactions":{"type":"array","items":{"type":"object","required":["subject_id","reason"],"properties":{"subject_id":{"type":"string"},"reason":{"type":"string","minLength":1,"maxLength":240}}}}
}
}"#;
@ -823,7 +823,7 @@ impl CellProjectionEngine {
"target_subject_ids and location_ids must come from that exact subject's permissions. Every activity has at most four unique target_subject_ids; choose the four most causally relevant when more permitted subjects are involved. A member_activity uses exactly the member's source_location_id. Internal work is prepare with no targets. A local investigate may have no target and use the exact current location to seek information from the environment or an unnamed ordinary role; asking an unnamed clerk or dock master for facts maps here and records only the inquiry, never a reply or discovery. A local communicate may likewise have no target at the exact current location when the Persona speaks, sends, offers, asks permission, or notifies an unnamed ordinary role; it records only the source's outgoing attempt, never a listener, reply, acceptance, or outcome. Communication with a canonical subject requires that exact target ID. Never substitute a containing population, related institution, or merely permitted ID for an unnamed role. ",
"Write intended_effect as the attempted act, never its hoped-for outcome or target response. Institution posture must be a specific materially new commitment or withholding of at most 240 characters. already_committed_posture is state already in force: maintaining, continuing, or restating it is inaction and must not emit an institution action. Gestalt pressure_resolutions copy exact current_pressures; additions are new unresolved constraints, never completed actions. Use only permitted state references and public channels. ",
"A population that chooses to board, depart, or relocate together to one supplied migration_destinations key emits gestalt_migration; do not reduce it to prepare. It relocates only that exact population leaf and never implies a named member traveled. A named member who chooses to board, depart, travel, or join a supplied destination emits member_migration; use prepare only while departure remains unchosen. ",
"A population or arena cannot migrate a person. Runtime binds identity and effect owner IDs from subject_id. Do not emit institution_id, gestalt_id, actor_id, or member_id inside effect. Use an empty actions array plus a concrete inaction_reason when nobody acts."
"A population or arena cannot migrate a person. Runtime binds identity and effect owner IDs from subject_id. Do not emit institution_id, gestalt_id, actor_id, or member_id inside effect. Record every voiced subject that explicitly holds, waits, or merely continues already_committed_posture in inactions with its exact subject_id and a concrete reason. A subject cannot appear in both actions and inactions. When nobody acts, actions is empty and inactions must still contain at least one exact attributed decision."
),
slice.max_actions
);
@ -1013,7 +1013,7 @@ fn append_cell_correction(
rejected_appraisal: &str,
) {
request.lived_stream.push_str(&format!(
"\n\nCORRECTION TASK—THE PREVIOUS APPRAISAL WAS REJECTED.\nREJECTION: {error}\nPREVIOUS_REJECTED_APPRAISAL:\n{rejected_appraisal}\nReturn one corrected complete appraisal against the same snapshot, lived stream, Persona turn, and exact permission context. A rejected action is forbidden unchanged: remove it or replace it with a different, faithful, permitted typed consequence. Do not repeat its subject, intended_effect, and typed effect together. Every retained action must still carry a valid non-empty typed transition under the original contract. If an institution merely continues or restates already_committed_posture, omit that no-op action; it is holding steady. If the Persona chose travel but that subject has no exact permitted destination in reachable_destination_ids or migration_destinations, no movement transition is available: remove that action and use explicit inaction. If an attempted preparation, inspection, request, or deliberation has no permitted typed consequence, remove it and use explicit inaction; never emit an empty transition or upgrade consideration into a completed consequence."
"\n\nCORRECTION TASK—THE PREVIOUS APPRAISAL WAS REJECTED.\nREJECTION: {error}\nPREVIOUS_REJECTED_APPRAISAL:\n{rejected_appraisal}\nReturn one corrected complete appraisal against the same snapshot, lived stream, Persona turn, and exact permission context. A rejected action is forbidden unchanged: remove it or replace it with a different, faithful, permitted typed consequence. Do not repeat its subject, intended_effect, and typed effect together. Every retained action must still carry a valid non-empty typed transition under the original contract. If an institution merely continues or restates already_committed_posture, move that exact subject to inactions; it is holding steady. If the Persona chose travel but that subject has no exact permitted destination in reachable_destination_ids or migration_destinations, no movement transition is available: remove that action and record attributed inaction. If an attempted preparation, inspection, request, or deliberation has no permitted typed consequence, remove it and record attributed inaction; never emit an empty transition or upgrade consideration into a completed consequence."
));
}
@ -1179,7 +1179,7 @@ fn bind_cell_appraisal(
.map(|subject| subject.subject_id.clone())
.collect(),
actions,
inaction_reason: proposal.inaction_reason,
inactions: proposal.inactions,
})
}
@ -1209,16 +1209,41 @@ fn validate_cell_appraisal(
slice.max_actions
));
}
if appraisal.actions.is_empty()
&& appraisal
.inaction_reason
.as_deref()
.is_none_or(|reason| reason.trim().is_empty())
{
if appraisal.actions.is_empty() && appraisal.inactions.is_empty() {
return Err(anyhow!(
"an appraisal with no actions requires one concrete non-empty inaction_reason"
"an appraisal with no actions requires one exact attributed inaction"
));
}
let permitted_subject_ids = slice
.constituents
.iter()
.map(|subject| subject.subject_id.as_str())
.chain(
slice
.member_exceptions
.iter()
.map(|member| member.subject_id.as_str()),
)
.collect::<BTreeSet<_>>();
let action_subject_ids = appraisal
.actions
.iter()
.map(|action| action.subject_id.as_str())
.collect::<BTreeSet<_>>();
let mut inaction_subject_ids = BTreeSet::new();
for inaction in &appraisal.inactions {
if inaction.reason.trim().is_empty()
|| inaction.reason.len() > 240
|| !permitted_subject_ids.contains(inaction.subject_id.as_str())
|| action_subject_ids.contains(inaction.subject_id.as_str())
|| !inaction_subject_ids.insert(inaction.subject_id.as_str())
{
return Err(anyhow!(
"inaction for subject {} is empty, duplicate, outside the cell, or conflicts with an action",
inaction.subject_id
));
}
}
for action in &appraisal.actions {
if action.intent.trim().is_empty() || action.intended_effect.trim().is_empty() {
return Err(anyhow!(
@ -1512,6 +1537,11 @@ fn constrain_cell_proposal_schema(
.and_then(serde_json::Value::as_object_mut)
.ok_or_else(|| anyhow!("cell appraisal schema has no action array"))?;
actions.insert("maxItems".into(), slice.max_actions.into());
let inactions = properties
.get_mut("inactions")
.and_then(serde_json::Value::as_object_mut)
.ok_or_else(|| anyhow!("cell appraisal schema has no inaction array"))?;
inactions.insert("maxItems".into(), slice.max_actions.into());
let proposal = schema
.pointer_mut("/$defs/CellActionCandidate/properties")
.and_then(serde_json::Value::as_object_mut)
@ -1529,12 +1559,20 @@ fn constrain_cell_proposal_schema(
.collect::<Vec<_>>();
proposal.insert(
"subject_id".into(),
serde_json::json!({"type":"string","enum":subject_ids}),
serde_json::json!({"type":"string","enum":subject_ids.clone()}),
);
proposal.insert(
"priority".into(),
serde_json::json!({"type":"integer","minimum":0,"maximum":100}),
);
let inaction = schema
.pointer_mut("/$defs/CellInaction/properties")
.and_then(serde_json::Value::as_object_mut)
.ok_or_else(|| anyhow!("cell appraisal schema has no inaction properties"))?;
inaction.insert(
"subject_id".into(),
serde_json::json!({"type":"string","enum":subject_ids}),
);
Ok(())
}
@ -1643,7 +1681,7 @@ mod tests {
"public_channels":["public bulletin"],
"effect":{"type":"actor_move","destination_id":"forum"}
}],
"inaction_reason":null
"inactions":[]
})
.to_string());
}
@ -1663,7 +1701,7 @@ mod tests {
"public_channels":["public bulletin"],
"effect":{"type":"institution","posture":"published a bounded position","location_ids":["forum"]}
}],
"inaction_reason":null
"inactions":[]
}).to_string())
}
"cell_effect_verifier" => {
@ -1811,7 +1849,7 @@ mod tests {
"location_ids":["forum"]
}
}],
"inaction_reason":null
"inactions":[]
}).to_string())
}
"cell_effect_verifier" => {
@ -1879,21 +1917,47 @@ mod tests {
}
#[test]
fn empty_cell_appraisal_names_the_missing_inaction_reason() {
fn empty_cell_appraisal_requires_exact_attributed_inaction() {
let slice = fixture_cell_slice();
let appraisal = bind_cell_appraisal(
let mut appraisal = bind_cell_appraisal(
&slice,
CellAppraisalProposal {
actions: vec![],
inaction_reason: Some(" ".into()),
inactions: vec![crate::domain::CellInaction {
subject_id: "faction-06".into(),
reason: "The institution deliberately holds its current position.".into(),
}],
},
)
.unwrap();
validate_cell_appraisal(&slice, &appraisal).unwrap();
appraisal.inactions[0].reason = " ".into();
let error = validate_cell_appraisal(&slice, &appraisal).unwrap_err();
assert!(
error
.to_string()
.contains("concrete non-empty inaction_reason")
.contains("inaction for subject faction-06")
);
appraisal.inactions[0].reason = "The institution holds.".into();
appraisal.actions.push(crate::domain::CellActionProposal {
subject_id: "faction-06".into(),
intent: "publish a new position".into(),
intended_effect: "adopt a bounded commitment".into(),
priority: 50,
state_references: vec!["institution:faction-06".into()],
public_channels: vec!["public bulletin".into()],
effect: StrategicCellEffect::Institution {
institution_id: "faction-06".into(),
posture: "publishing a bounded new commitment".into(),
location_ids: vec!["forum".into()],
},
});
assert!(
validate_cell_appraisal(&slice, &appraisal)
.unwrap_err()
.to_string()
.contains("conflicts with an action")
);
}
@ -2009,7 +2073,7 @@ mod tests {
pressure_resolutions: vec![],
},
}],
inaction_reason: None,
inactions: vec![],
},
)
.unwrap();
@ -2150,7 +2214,7 @@ mod tests {
location_ids: vec!["forum".into()],
},
}],
inaction_reason: None,
inactions: vec![],
},
)
.unwrap();
@ -2258,7 +2322,7 @@ mod tests {
.lived_stream
.contains("preparation, inspection, request, or deliberation")
);
assert!(request.lived_stream.contains("use explicit inaction"));
assert!(request.lived_stream.contains("record attributed inaction"));
}
#[test]

View file

@ -1614,18 +1614,32 @@ pub fn validate_and_resolve_wave(
|| appraisal.world_revision != campaign.revision
|| appraisal.resolution_epoch != campaign.resolution_policy.resolution_epoch
|| appraisal.considered_subject_ids != cell.subject_ids
|| (appraisal.actions.is_empty()
&& appraisal
.inaction_reason
.as_deref()
.is_none_or(|reason| reason.trim().is_empty()))
|| (appraisal.actions.is_empty() && appraisal.inactions.is_empty())
{
return Err(anyhow!("cell appraisal is incomplete or stale"));
}
let quota = cell_action_limit(cell);
if appraisal.actions.len() > quota {
if appraisal.actions.len() > quota || appraisal.inactions.len() > quota {
return Err(anyhow!("cell appraisal exceeds its action quota"));
}
let action_subject_ids = appraisal
.actions
.iter()
.map(|proposal| proposal.subject_id.as_str())
.collect::<BTreeSet<_>>();
let mut inaction_subject_ids = BTreeSet::new();
for inaction in &appraisal.inactions {
if inaction.reason.trim().is_empty()
|| inaction.reason.len() > 240
|| action_subject_ids.contains(inaction.subject_id.as_str())
|| !inaction_subject_ids.insert(inaction.subject_id.as_str())
|| !cell_contains_attributed_subject(campaign, cell, &inaction.subject_id)
{
return Err(anyhow!(
"cell appraisal contains an invalid attributed inaction"
));
}
}
for proposal in &appraisal.actions {
validate_cell_proposal(campaign, cell, proposal)?;
proposals.push(proposal.clone());
@ -1907,6 +1921,23 @@ fn validate_cell_proposal(
Ok(())
}
fn cell_contains_attributed_subject(
campaign: &Campaign,
cell: &SimulationCell,
subject_id: &str,
) -> bool {
if subject_id == campaign.player_actor_id {
return false;
}
if let Some(member_id) = subject_id.strip_prefix("member:") {
return campaign
.gestalt_members
.get(member_id)
.is_some_and(|member| cell.subject_ids.contains(&member.gestalt_id));
}
cell.subject_ids.contains(subject_id)
}
pub fn substantive_text_change(current: &str, candidate: &str) -> bool {
let current = current.trim();
let candidate = candidate.trim();
@ -3079,7 +3110,7 @@ pub(crate) mod tests {
resolution_epoch: value.resolution_policy.resolution_epoch,
considered_subject_ids: cell.subject_ids.clone(),
actions: vec![proposal],
inaction_reason: None,
inactions: vec![],
}],
cover: cover.clone(),
model_receipt_hashes: vec![],
@ -3112,6 +3143,33 @@ pub(crate) mod tests {
},
};
assert!(validate_and_resolve_wave(&value, &make_wave(borrowed_secret)).is_err());
let valid_action = CellActionProposal {
subject_id: "faction-0000".into(),
intent: "publish a bounded position".into(),
intended_effect: "adopt a materially different posture".into(),
priority: 50,
state_references: vec![],
public_channels: vec![],
effect: StrategicCellEffect::Institution {
institution_id: "faction-0000".into(),
posture: "publishing a bounded position under the current pressure".into(),
location_ids: vec![],
},
};
let mut mixed = make_wave(valid_action.clone());
mixed.appraisals[0].inactions = vec![CellInaction {
subject_id: "faction-0001".into(),
reason: "The rival deliberately holds its separate position.".into(),
}];
validate_and_resolve_wave(&value, &mixed).unwrap();
let mut contradictory = make_wave(valid_action);
contradictory.appraisals[0].inactions = vec![CellInaction {
subject_id: "faction-0000".into(),
reason: "The same institution cannot also hold.".into(),
}];
assert!(validate_and_resolve_wave(&value, &contradictory).is_err());
}
#[test]
@ -3225,7 +3283,7 @@ pub(crate) mod tests {
resolution_epoch: value.resolution_policy.resolution_epoch,
considered_subject_ids: cover.cells[0].subject_ids.clone(),
actions: vec![proposal],
inaction_reason: None,
inactions: vec![],
}],
cover: cover.clone(),
model_receipt_hashes: vec![],
@ -3274,7 +3332,7 @@ pub(crate) mod tests {
resolution_epoch: value.resolution_policy.resolution_epoch,
considered_subject_ids: cover.cells[0].subject_ids.clone(),
actions: vec![member_activity.clone(), proposal.clone()],
inaction_reason: None,
inactions: vec![],
}],
cover: cover.clone(),
model_receipt_hashes: vec![],
@ -3345,7 +3403,7 @@ pub(crate) mod tests {
resolution_epoch: value.resolution_policy.resolution_epoch,
considered_subject_ids: cover.cells[0].subject_ids.clone(),
actions: vec![exact_rival_activity.clone(), lower_priority_pressure],
inaction_reason: None,
inactions: vec![],
}],
cover: cover.clone(),
model_receipt_hashes: vec![],
@ -3427,7 +3485,7 @@ pub(crate) mod tests {
action("faction-0001", 100),
action("faction-0002", 50),
],
inaction_reason: None,
inactions: vec![],
}],
cover,
model_receipt_hashes: vec![],

View file

@ -833,11 +833,24 @@ mod tests {
}
"cell_persona" => Ok("Each constituent watches and deliberately holds.".into()),
"cell_interpreter" if self.malformed_cell => Ok("not-json".into()),
"cell_interpreter" => Ok(serde_json::json!({
"actions": [],
"inaction_reason": "No constituent has a justified move this horizon."
})
.to_string()),
"cell_interpreter" => {
let subject_id = request
.output_schema
.as_ref()
.and_then(|schema| {
schema.pointer("/$defs/CellInaction/properties/subject_id/enum/0")
})
.and_then(serde_json::Value::as_str)
.ok_or_else(|| anyhow!("fixture Interpreter lacks a bound subject"))?;
Ok(serde_json::json!({
"actions": [],
"inactions": [{
"subject_id":subject_id,
"reason":"No justified move this horizon."
}]
})
.to_string())
}
stage => Err(anyhow!("unexpected fixture stage {stage}")),
}
}

View file

@ -123,6 +123,14 @@ outside the player's active location. A direct internal command may preserve a
named offscreen actor, but model-driven presence reconciliation cannot teleport
one into view. Existing member deltas are preferred when their relationship,
memory, obligation, or goal makes an ordinary encounter a meaningful callback.
The player actor's reciprocal relationship to each nearby dormant member is
projected beside that member's exact delta. Presence planning is scene casting,
not roster cleanup: an earned local callback may surface without the player
asking for the person, while an incompatible event or absence of durable
callback evidence may still produce no promotion. The model only proposes the
cast; `WorldKernel` validates exact member, gestalt, location, and versions
before materialization. Live harnesses persist the private presence preflight
even when their acceptance assertion fails.
## Canonical population migration
@ -273,6 +281,17 @@ Projector → Persona → Interpreter membrane:
- Interpreters emit a typed appraisal and exact constituent effects;
- malformed or semantically invalid output gets one same-snapshot correction.
An appraisal has two attributed lanes: `actions[]` for proposed transitions and
`inactions[]` for exact subjects that deliberately hold, wait, or continue an
already committed course. The old cell-wide inaction string is gone. This
matters in a mixed arena: one institution can act while two rivals hold without
the Interpreter manufacturing repeated posture transitions merely to preserve
their separately voiced decisions. Inaction IDs use the same constituent or
selected-member permission set, carry a bounded reason, cannot duplicate, and
cannot also appear in the action lane. They are audit/fairness evidence and
never mutate the world. A cell with no actions must still contain at least one
attributed inaction.
An arena may contain simultaneous views from distant locations. The Projector
names each subject's actual location and keeps those views as separate scenes;
a graph relation or activity target grants permission to attempt contact, not
@ -574,6 +593,44 @@ explicit aggregate `detail_focus_subject_id` and every singleton cell both
receive direct resolution attention. A named focus is not required for a
subject already receiving its own complete Persona pipeline.
The lease-preserving budget-4 profile committed all 23 requested sustained
waves and recovered from 18 rejected provider pulses without partial mutation.
It then failed its separate callback assertion because the presence planner
returned an empty cast. This isolated a player-experience fault from the agency
invariants: Mira remained the same dormant person in the correct destination,
but continuity was not surfaced. The focused `gestalt-callback-6253f9f-1` run
then passed: after one fresh 24-subject arena wave, the planner promoted
`mira-venn` from `harbor-neighbors` without the player naming her, and the
kernel rematerialized the same private and effective state. The full run took
18.465 seconds. Its strategic membrane used 18,662 prompt and 1,096 completion
tokens; the additional presence decision used 2,504 prompt and 78 completion
tokens.
Institution posture is projected as `already_committed_posture`. It is durable
state already in force, not a new pressure or decision. Projector, Persona, and
Interpreter contracts all treat continuing or restating it as explicit
inaction; only a materially different commitment may emit an institution
posture transition. Local validation remains strict rather than silently
dropping a no-op.
The 31-wave budget-8 / provider-parallelism-4 profile
`gestalt-lease-b8p4-6253f9f-1` passed after 1,211.592 seconds. All 24 subjects
received direct attention. Its sustained appraisals made 199 accepted proposals
from 22 distinct subjects; consequence resolution committed 51 institution
postures, 110 Gestalt activities, one population migration, and 31 named-member
activities, plus the initial arena consequence. Fifteen rejected pulses left
the world unchanged and later recovered. The same Mira callback passed after
the full run.
The profile used 1,468,039 strategic prompt tokens and 165,725 completion
tokens, reached a 60.69% cache-hit ratio, and spent 7,567 prompt tokens per
committed consequence. Interpreter remained the largest stage at 737,148
prompt tokens but reached 67.18% cache hits; Projector reached 70.03%. The
presence decision added 2,606 prompt and 78 completion tokens. Live traces
showed that the remaining repeated-posture failures occurred when several
constituents in one arena held while others acted, motivating the attributed
inaction lane rather than another prompt-only prohibition.
The run also exposes the next authority gap. Institution postures, population
migration, clocks, and history changed canonically, but most Gestalt activities
remain attributed attempts recorded as events. Over many ticks this can repeat

View file

@ -277,6 +277,50 @@ cover, so `advance_detail_debt` correctly treated it as directly resolved while
the harness counted only explicit aggregate focus labels. Acceptance now uses
the canonical union of explicit focus plus singleton subjects.
The corrected lease-preserving budget-4 run committed all 23 sustained waves
and recovered from 18 rejected pulses without partial world mutation. It failed
only after those waves when automatic presence planning returned an empty cast
for Mira's South Harbor callback. Mira's dormant identity, destination,
relationships, memories, and effective state were intact. `dab6aae` made the
planner's scene-casting objective explicit, projected the player's reciprocal
relationship beside the exact member delta, and began persisting
`presence-preflight.json` before assertions.
The first focused callback probe was blocked before casting by the dominant
long-run failure: an institution repeatedly emitted its already committed
posture as if it were a new action. `6253f9f` now projects that field as
`already_committed_posture` and defines continuation as inaction throughout the
Projector, Persona, Interpreter, and correction contexts. Validation still
rejects the no-op rather than silently repairing it. The same commit adds
`GHOSTLIGHT_LIVE_FIRE_PRESENCE_ONLY=1` for a baseline-backed focused acceptance
run.
`gestalt-callback-6253f9f-1` passed in 18.465 seconds. A fresh 24-subject arena
wave ran first; then the presence planner selected `mira-venn` from
`harbor-neighbors` without the player naming her. `WorldKernel` rematerialized
the same private and effective state at South Harbor. The strategic membrane
used 18,662 prompt / 1,096 completion tokens; presence used another 2,504 / 78.
`gestalt-lease-b8p4-6253f9f-1` then passed 31 sustained budget-8 waves at
provider parallelism 4 in 1,211.592 seconds. It covered all 24 subjects, made
199 accepted proposals from 22 subjects, committed 193 sustained consequences
plus the initial arena consequence, recovered 15 rejected pulses, preserved the
player and Mira, and passed the same-person callback. Strategic prompt use was
1,468,039 with 60.69% cache hits and 7,567 prompt tokens per committed
consequence. Interpreter consumed 737,148 prompt tokens at 67.18% cache hits;
Projector reached 70.03%.
That run falsified the prompt-only repeated-posture fix. Its trace showed mixed
arena turns where one constituent acted while other institutions explicitly
held their existing course. The old `CellAppraisal` could represent actions or
one global inaction reason, not attributed inaction beside another subject's
action. The Interpreter sometimes restated the holding institution's posture to
avoid dropping its choice. The appraisal contract now has exact `actions[]` and
exact `inactions[]`; the old global field is deleted. Inactions are bounded,
non-mutating, permission-checked, unique, and mutually exclusive with an action
from the same subject. The next live profile must compare retry rate and token
efficiency under this new contract before claiming the waste is cut.
Do not call setting-wide material agency finished yet. Most population output
is still an attributed `gestalt_activity` attempt stored as history. The long
run became repetitive around ferry loading and shelter requests because no

View file

@ -176,6 +176,21 @@ location, dormant members whose exact location matches, and materialized
members eligible for folding. The kernel rejects inactive hierarchy nodes and
location mismatches. Nested fission and migration therefore change which
baseline a durable delta composes with, never who the person is.
The projection also names the player actor's reciprocal relationship to each
nearby dormant member. The planner owns reversible scene casting and may surface
an earned callback without a player prompt; it cannot move a remote person or
commit the cast. Failed live casts retain a private presence-preflight artifact.
Institution `already_committed_posture` is projected as durable state, distinct
from unresolved pressure and fresh choice. Maintaining it is inaction. A model
may propose only a materially different posture; repeated posture remains a
locally rejected no-op and cannot enter an atomic wave.
Cell appraisals carry exact `actions[]` and exact `inactions[]`. Inactions name
the constituent or selected dormant member who holds and a bounded reason. They
cannot duplicate, cross cell authority, or coexist with an action from the same
subject. They are non-mutating audit/fairness evidence. The former global
inaction string is no longer an owner or runtime path.
The active cell budget and provider concurrency limit are separate controls.
Budget and pins increment `resolution_epoch` without advancing world revision or

View file

@ -267,6 +267,6 @@ data_flow:
- If state grows faster than understanding, cut it back.
current_status:
summary: GhostlightDungeon production remains on the verified Starfire release. Multiresolution pressure proves the callback, nested diaspora, fairness, and scale matrix. Lease-preserving pressure exposed two hidden faults now cut: redundant controls manufactured epochs, and migrated topology could leave a disconnected leased cell alive. Production state remains untouched.
next_action: Rerun the lease-preserving budget-4 profile with canonical singleton-plus-focus fairness accounting, then test budget 8 with independent provider parallelism. Require covers to survive valid leases and replan immediately around migration-broken adjacency.
gestalt_population: Gestalt Personas own shared population baselines; persistent member deltas own named individuality; WorldKernel materialization and approval-gated fission preserve lineage and identity. Presence projection is limited to nearby active leaves and exact-location dormant members. Simulation cells are derived covers only.
summary: GhostlightDungeon production remains on the verified Starfire release. The 31-wave budget-8/provider-parallelism-4 live profile covered every subject, committed 194 consequences, recovered 15 rejected pulses without partial mutation, preserved player and rival/member agency, and returned the same Mira. Its trace exposed the missing attributed-inaction representation; the global inaction string has now been replaced by exact non-mutating per-subject inactions. Production state remains untouched.
next_action: Run the same budget-8 pressure with the attributed-inaction contract and provider parallelism 8, then compare retry rate, cache efficiency, latency, and exact cover behavior against the 6253f9f parallelism-4 baseline. Keep a future strategic outcome resolver separate from Interpreter authority.
gestalt_population: Gestalt Personas own shared population baselines; persistent member deltas own named individuality; WorldKernel materialization and approval-gated fission preserve lineage and identity. Presence projection is limited to nearby active leaves and exact-location dormant members, includes reciprocal player relationship evidence, and may propose an earned cast without committing it. Simulation cells are derived covers only.