From 1937ffce063bb3d0060ed6be2e277199adedd73d Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Fri, 1 May 2026 20:13:34 +0100 Subject: [PATCH] Add reciprocal Oz relationship stance --- .../agent-state-variable-glossary.md | 4 +- .../canonical-agent-state-schema.md | 4 ++ examples/agent-state.call-of-the-void.json | 71 +++++++++++++++++++ state/evidence.jsonl | 1 + state/map.yaml | 1 + tools/validate_agent_state.py | 5 ++ 6 files changed, 85 insertions(+), 1 deletion(-) diff --git a/docs/architecture/agent-state-variable-glossary.md b/docs/architecture/agent-state-variable-glossary.md index b1115bf..7fb5d27 100644 --- a/docs/architecture/agent-state-variable-glossary.md +++ b/docs/architecture/agent-state-variable-glossary.md @@ -218,7 +218,9 @@ they deserve their own canonical storage slot. ## Relationship Stance -Relationship stance is directional. `A -> B` can differ sharply from `B -> A`. +Relationship stance is directional. A `source_id -> target_id` record describes +how the source feels and predicts the target, not a shared relationship object. +Reciprocal records can disagree. | Label | Meaning | | --- | --- | diff --git a/docs/architecture/canonical-agent-state-schema.md b/docs/architecture/canonical-agent-state-schema.md index 06fa68c..81aea45 100644 --- a/docs/architecture/canonical-agent-state-schema.md +++ b/docs/architecture/canonical-agent-state-schema.md @@ -176,6 +176,10 @@ care. Fundamental attribution bias also lives here: an observer can mistake exceptional behavior for stable character. None of that requires corrupting the canonical state. +Relationship stance is directional. If Cat has a relationship record toward Oz, +Oz needs a separate record toward Cat. Mutual presence does not imply symmetric +trust, fear, obligation, dependence, or expectation of betrayal. + ## Dialogue Context Packs Scenes contain `dialogue_context_packs`. diff --git a/examples/agent-state.call-of-the-void.json b/examples/agent-state.call-of-the-void.json index 02d9ab8..8a7a273 100644 --- a/examples/agent-state.call-of-the-void.json +++ b/examples/agent-state.call-of-the-void.json @@ -1039,6 +1039,77 @@ "unresolved_incident_ids": [ "event-drone-lie" ] + }, + { + "relationship_id": "rel-oz-cat", + "source_id": "oz_operator", + "target_id": "cat_marrigan", + "stance": { + "trust": { + "mean": 0.44, + "plasticity": 0.56, + "current_activation": 0.39 + }, + "respect": { + "mean": 0.58, + "plasticity": 0.45, + "current_activation": 0.63 + }, + "resentment": { + "mean": 0.09, + "plasticity": 0.53, + "current_activation": 0.07 + }, + "dependence": { + "mean": 0.31, + "plasticity": 0.6, + "current_activation": 0.38 + }, + "fear": { + "mean": 0.42, + "plasticity": 0.61, + "current_activation": 0.55 + }, + "fascination": { + "mean": 0.52, + "plasticity": 0.55, + "current_activation": 0.6 + }, + "obligation": { + "mean": 0.34, + "plasticity": 0.57, + "current_activation": 0.42 + }, + "envy": { + "mean": 0.18, + "plasticity": 0.48, + "current_activation": 0.2 + }, + "moral_disgust": { + "mean": 0.03, + "plasticity": 0.5, + "current_activation": 0.02 + }, + "perceived_status_gap": { + "mean": 0.46, + "plasticity": 0.54, + "current_activation": 0.51 + }, + "expectation_of_care": { + "mean": 0.4, + "plasticity": 0.58, + "current_activation": 0.48 + }, + "expectation_of_betrayal": { + "mean": 0.36, + "plasticity": 0.6, + "current_activation": 0.44 + } + }, + "summary": "Oz reads Cat as dangerous but potentially protective, and keeps helping while testing whether the suspicion has teeth.", + "unresolved_incident_ids": [ + "event-drone-lie" + ] } ], "events": [ diff --git a/state/evidence.jsonl b/state/evidence.jsonl index 2d1a875..0481e93 100644 --- a/state/evidence.jsonl +++ b/state/evidence.jsonl @@ -16,3 +16,4 @@ {"ts": "2026-05-01T18:54:49+00:00", "type": "design", "status": "accepted", "note": "Removed verbal_response_strategy as a canonical layer. Defensive verbal moves should emerge during projection from interactions among underlying organization, dispositions, behavioral dimensions, presentation strategy, situational pressure, relationship context, and voice style."} {"ts": "2026-05-01T18:55:15+00:00", "type": "design", "status": "accepted", "note": "Kept sarcastic deflection, sincerity evasion, emotional evasion, banter-as-boundary, and verbal aggression as target emergent behaviors for projection tests rather than canonical state fields."} {"ts": "2026-05-01T19:09:58+00:00", "type": "design", "status": "accepted", "note": "Removed forbidden_context from dialogue context packs. Packs are positive speaker-local payloads; unknown or author-only material is omitted by the projection builder, while known secrets are expressed through goals, beliefs, memories, and presentation constraints."} +{"ts": "2026-05-01T19:13:15+00:00", "type": "design", "status": "accepted", "note": "Relationship stance is directional. Added Oz->Cat alongside Cat->Oz in the Call of the Void fixture and taught the validator to require reciprocal relationship edges for represented agent pairs."} diff --git a/state/map.yaml b/state/map.yaml index b1beed5..e35a895 100644 --- a/state/map.yaml +++ b/state/map.yaml @@ -80,6 +80,7 @@ accepted_design: - Defensive verbal responses are derived during projection from underlying organization, dispositions, behavioral dimensions, presentation strategy, situational pressure, relationship context, and voice style rather than stored as a separate canonical layer. - Sarcastic deflection, sincerity evasion, emotional evasion, banter-as-boundary, and verbal aggression are target emergent behaviors for projection tests. - Dialogue context packs are positive-context payloads; unknown or author-only material is omitted rather than listed as forbidden context, while known secrets are handled through goals, memories, beliefs, and presentation constraints. + - Relationship stance is directional; reciprocal agent pairs need separate edges because trust, fear, obligation, dependence, and betrayal expectations are not symmetric by default. current_status: summary: Ghostlight now has the Epiphany-style persistence spine, the first architecture payload, and the initial canonical agent-state schema seam for Call of the Void-style authoring examples. The schema is still v0 and should be hardened before building prompt projection on top of it. diff --git a/tools/validate_agent_state.py b/tools/validate_agent_state.py index 2d32c80..59555b1 100644 --- a/tools/validate_agent_state.py +++ b/tools/validate_agent_state.py @@ -165,15 +165,20 @@ def validate_document(document: dict[str, Any], required: dict[str, list[str]], event_ids = {event["event_id"] for event in document["events"]} + relationship_pairs: set[tuple[str, str]] = set() for index, relationship in enumerate(document["relationships"]): path = f"relationships[{index}]" require_keys(relationship, ["relationship_id", "source_id", "target_id", "stance", "summary"], path) require(relationship["source_id"] in agent_id_set, f"{path}.source_id references unknown agent") require(relationship["target_id"] in agent_id_set, f"{path}.target_id references unknown agent") + relationship_pairs.add((relationship["source_id"], relationship["target_id"])) validate_variable_map(relationship["stance"], required["relationship_stance"], f"{path}.stance") for event_id in relationship.get("unresolved_incident_ids", []): require(event_id in event_ids, f"{path}.unresolved_incident_ids references unknown event: {event_id}") + for source_id, target_id in relationship_pairs: + require((target_id, source_id) in relationship_pairs, f"relationships missing reciprocal edge: {target_id} -> {source_id}") + for index, event in enumerate(document["events"]): path = f"events[{index}]" require_keys(event, ["event_id", "kind", "summary", "participants", "pressure_tags"], path)