Add reciprocal Oz relationship stance

This commit is contained in:
Meta Vens 2026-05-01 20:13:34 +01:00
commit 1937ffce06
6 changed files with 85 additions and 1 deletions

View file

@ -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 |
| --- | --- |

View file

@ -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`.

View file

@ -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": [

View file

@ -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."}

View file

@ -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.

View file

@ -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)