Project Persona self-authored history

This commit is contained in:
Meta Vens 2026-08-26 16:16:57 +02:00
commit e692e2f5eb
2 changed files with 79 additions and 1 deletions

View file

@ -40,6 +40,9 @@ pub struct PermittedActorSlice {
/// from silently impersonating an already-durable person.
pub reserved_public_identities: BTreeSet<String>,
pub memories: Vec<String>,
/// Exact recent public responses already committed for this stable subject.
/// These are event-history witnesses, not private memory or new world fact.
pub recent_self_authored_turns: Vec<String>,
pub perceived_events: Vec<String>,
pub perceived_actors: std::collections::BTreeMap<String, String>,
pub relationships: Vec<String>,
@ -588,6 +591,11 @@ fn ground_actor_lived_stream(slice: &PermittedActorSlice, projection: &str) -> S
} else {
slice.memories[recent_memory_start..].join("; ")
};
let prior_public_response = if slice.recent_self_authored_turns.is_empty() {
"no earlier public response by this exact subject is active in this moment".to_owned()
} else {
slice.recent_self_authored_turns.join("; ")
};
let interaction = match slice.interaction_role {
ActorInteractionRole::DirectResponseExpected => {
"You are an exact direct addressee of the current speech. A response is expected from you now. You retain the agency to answer, refuse, or remain silent, but refusal or silence must be made observable rather than disappearing from the scene."
@ -619,7 +627,7 @@ fn ground_actor_lived_stream(slice: &PermittedActorSlice, projection: &str) -> S
}
};
format!(
"{projection}\n\n{subject_grounding} Your active self-identity: {identity}. In your social world, {established_peer_identities}. {interaction} Your reliable footing in this moment is narrow. What you know as external fact: {reliable_knowledge}. What you remember experiencing or being told: {remembered_experience}. These are your attributed recollections, not omniscient proof. What is happening now: {visible_now}. People you can presently perceive: {people_now}. Everything else in your impressions is feeling, inference, uncertainty, or possibility—not a remembered or witnessed fact."
"{projection}\n\n{subject_grounding} Your active self-identity: {identity}. In your social world, {established_peer_identities}. {interaction} Your reliable footing in this moment is narrow. What you know as external fact: {reliable_knowledge}. What you remember experiencing or being told: {remembered_experience}. These are your attributed recollections, not omniscient proof. Your exact recent public response history: {prior_public_response}. This response history records what you previously made observable; it does not prove that the content of those statements was true. What is happening now: {visible_now}. People you can presently perceive: {people_now}. Everything else in your impressions is feeling, inference, uncertainty, or possibility—not a remembered or witnessed fact."
)
}
@ -4681,6 +4689,9 @@ mod tests {
identity_experience: vec!["A tired navigator".into()],
reserved_public_identities: BTreeSet::new(),
memories: vec!["Proposed the eastern trail evacuation at dusk.".into()],
recent_self_authored_turns: vec![
"At world revision 3, your committed public response was exactly: Use the eastern trail.".into(),
],
perceived_events: vec![],
perceived_actors: std::collections::BTreeMap::from([(
"player".into(),
@ -4701,6 +4712,8 @@ mod tests {
assert!(grounded.contains("Your active self-identity: A tired navigator"));
assert!(grounded.contains("What you remember experiencing or being told"));
assert!(grounded.contains("Proposed the eastern trail evacuation at dusk."));
assert!(grounded.contains("Use the eastern trail."));
assert!(grounded.contains("does not prove that the content"));
assert!(grounded.contains("not omniscient proof"));
let result = engine.execute(slice).await.unwrap();
assert_eq!(result.stage_receipts.len(), 3);
@ -4718,6 +4731,7 @@ mod tests {
identity_experience: vec!["You are an unnamed patient.".into()],
reserved_public_identities: BTreeSet::new(),
memories: vec![],
recent_self_authored_turns: vec![],
perceived_events: vec!["The player asks your name.".into()],
perceived_actors: BTreeMap::from([("player".into(), "Player".into())]),
relationships: vec![],
@ -4766,6 +4780,7 @@ mod tests {
identity_experience: vec!["You are an unnamed patient.".into()],
reserved_public_identities: BTreeSet::new(),
memories: vec![],
recent_self_authored_turns: vec![],
perceived_events: vec!["A regulator is inspected.".into()],
perceived_actors: BTreeMap::from([("player".into(), "Player".into())]),
relationships: vec![],
@ -4861,6 +4876,7 @@ mod tests {
identity_experience: vec!["You are an unnamed patient.".into()],
reserved_public_identities: BTreeSet::new(),
memories: vec![],
recent_self_authored_turns: vec![],
perceived_events: vec!["The player asks your name.".into()],
perceived_actors: BTreeMap::from([("player".into(), "Player".into())]),
relationships: vec![],

View file

@ -281,6 +281,7 @@ pub async fn appraise_present(
let receipts = campaign.branch_origin.evidence_receipt_ids.clone();
let perceived_actors = perceived_actors.clone();
let reserved_public_identities = reserved_public_identities(campaign, &actor);
let recent_self_authored_turns = recent_self_authored_turns(campaign, &actor.id);
let semaphore = semaphore.clone();
let interaction_role = if response_expected_actor_ids.contains(&actor.id) {
ActorInteractionRole::DirectResponseExpected
@ -301,6 +302,7 @@ pub async fn appraise_present(
identity_experience: vec![format!("You are {}.", actor.name)],
reserved_public_identities,
memories: actor.memories,
recent_self_authored_turns,
perceived_events: vec![event],
perceived_actors,
relationships: actor
@ -329,6 +331,7 @@ pub async fn appraise_present(
let event = event_summary.to_owned();
let receipts = campaign.branch_origin.evidence_receipt_ids.clone();
let perceived_actors = perceived_actors.clone();
let recent_self_authored_turns = recent_self_authored_turns(campaign, &gestalt.id);
let semaphore = semaphore.clone();
let interaction_role = if response_expected_actor_ids.contains(&gestalt.id) {
ActorInteractionRole::DirectResponseExpected
@ -352,6 +355,7 @@ pub async fn appraise_present(
)],
reserved_public_identities: perceived_actors.values().cloned().collect(),
memories: vec![],
recent_self_authored_turns,
perceived_events: vec![event],
perceived_actors,
relationships: vec![],
@ -400,6 +404,26 @@ pub async fn appraise_present(
})
}
fn recent_self_authored_turns(campaign: &Campaign, subject_id: &str) -> Vec<String> {
const LIMIT: usize = 8;
let mut turns = campaign
.transcript
.iter()
.rev()
.filter(|turn| turn.speaker == subject_id)
.take(LIMIT)
.map(|turn| {
format!(
"At world revision {}, your committed public response was exactly: {}",
turn.revision,
turn.text.trim()
)
})
.collect::<Vec<_>>();
turns.reverse();
turns
}
fn reserved_public_identities(
campaign: &Campaign,
actor: &crate::domain::ActorState,
@ -609,6 +633,44 @@ mod tests {
campaign
}
#[test]
fn stable_subject_history_survives_folding_and_remains_bounded() {
let mut campaign = addressed_campaign();
for revision in 1..=10 {
campaign.transcript.push(NarrativeTurn {
revision,
at: Utc::now(),
speaker: "refugee-one".into(),
text: format!("response {revision}"),
persona_response_actor_ids: BTreeSet::new(),
});
}
campaign.transcript.push(NarrativeTurn {
revision: 11,
at: Utc::now(),
speaker: "refugee-two".into(),
text: "another person's answer".into(),
persona_response_actor_ids: BTreeSet::new(),
});
let before_folding = recent_self_authored_turns(&campaign, "refugee-one");
assert_eq!(before_folding.len(), 8);
assert!(before_folding[0].contains("world revision 3"));
assert!(before_folding[0].ends_with("response 3"));
assert!(before_folding[7].ends_with("response 10"));
assert!(
before_folding
.iter()
.all(|turn| !turn.contains("another person's answer"))
);
campaign.actors.remove("refugee-one");
assert_eq!(
recent_self_authored_turns(&campaign, "refugee-one"),
before_folding
);
}
#[test]
fn materialized_actor_receives_dormant_peer_identities_as_social_context() {
let mut campaign = crate::resolution::tests::campaign(0, 8);