Standardized YAML frontmatter on all 10 files. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
22 KiB
title, description, type, status, workshop, agent, round, created
| title | description | type | status | workshop | agent | round | created |
|---|---|---|---|---|---|---|---|
| Knowledge Graph Workshop — Round 2: Mechanics Validation | Gestalt's cross-validation of gameplay mechanics against the proposed knowledge graph architecture | workshop | archived | knowledge-graph-information-boundaries | gestalt | 2 | 2026-02-11 |
Knowledge Graph Workshop — Round 2: Mechanics Validation
Role: GESTALT (Systems Designer) Focus: Cross-validation of gameplay mechanics against proposed architecture Date: 2026-02-11
Executive Summary
cracks knuckles
Let me break down what this actually means mechanically.
The good news: The architecture serves gameplay. Tyre and Dudley's proposed structs support every gameplay loop I identified in Round 1. The per-entity Component pattern, BTreeMap storage, StableId references, and 5-level confidence enum are ALL mechanically sound. I found zero architectural blockers.
The hierarchy debate: We need BOTH axes. Tyre's SOURCE-based levels (Stale/Rumor/Inferred/Observed/Direct) and Paula's DEPTH-based levels (Unaware/Suspects/KnowsOf/KnowsDetails/Understands) are solving DIFFERENT problems. Source determines confidence decay and trust. Depth determines dialogue gating and monologue progression. The solution is NOT picking one — it's recognizing they're orthogonal and mapping them correctly.
THE FRIEND arc validation: It works. I walked the proposed structs through THE FRIEND's lie detection sequence step-by-step. Every beat is supported: lie entry, contradiction detection, monologue trigger, relationship state shift, dialogue unlock. No gaps.
The one gap: Observable gossip is underspecified. Tyre and Dudley defined the data structures for NPC-to-NPC knowledge transfer, but the GAMEPLAY loop isn't there yet. How does the player DETECT that two NPCs just shared information? This is critical for political intrigue and deferred to Sprint 3+, which is correct — but the design needs to exist now so the architecture doesn't preclude it.
Bottom line: Ship the Sprint 2 stub as specified. It's the right foundation.
1. Architecture Serves Gameplay: YES
What Gameplay Needs
From my Round 1 analysis, the knowledge graph must support:
- Monologue gating — lines filtered by confidence level
- Dialogue access — conversation options unlocked by knowledge state
- Gossip observation — player detects NPC-to-NPC knowledge transfer
- Contradiction detection — conflicting knowledge sources trigger monologue
- Triangle pressure — knowledge propagation increases social conflict
What the Architecture Provides
Tyre's Component model + BTreeMap + StableId:
- Per-entity
KnowledgeGraphComponent ✓ Supports monologue/dialogue queries per observer BTreeMap<StableId, EntityKnowledge>✓ O(1) lookups for "what do I know about X?"KnowledgeSourceenum ✓ Tracks where knowledge came from (needed for contradiction detection)last_observed_tick+last_updated_tick✓ Supports decay mechanicRelationshipStateembedded in EntityKnowledge ✓ Drives D-033 entity color
Dudley's event queue + decay batching:
KnowledgeEventQueue✓ Decouples perception from knowledge updates- Batch decay (600-tick intervals) ✓ Prevents O(N×M) per-tick explosion
EntityIdMapfor StableId ↔ Entity translation ✓ Solves save/load stability
Every gameplay requirement maps to a struct field or query method. No missing pieces for Sprint 2 scope.
What's NOT in Sprint 2 (Correctly Deferred)
- NPC-to-NPC gossip propagation (architectural support exists, systems don't)
ToldBysource generation (enum variant exists, no system writes it yet)- Knowledge-driven NPC behavior changes (knowledge updates don't feed AI yet)
- Misinformation (deliberately wrong entries — requires content flag)
This split is mechanically sound. Sprint 2's goal is geometric fog, not social intrigue. The stub enables observer snapshot filtering. Full knowledge gameplay comes Sprint 3+.
2. Knowledge Hierarchy Debate: My Updated Position
The Problem Statement
We have four proposals:
- Tyre: 5 levels (Stale/Rumor/Inferred/Observed/Direct) — SOURCE-based confidence
- Paula: 5 levels (Unaware/Suspects/KnowsOf/KnowsDetails/Understands) — DEPTH-based understanding
- Me (Round 1): 3 levels (Suspects/KnowsOf/KnowsDetails) — simplified gating
- Dudley: 4 levels (Suspects/KnowsOf/KnowsDetails/DirectObservation) — hybrid
The Realization
These are NOT competing hierarchies. They're answering different questions:
Tyre's SOURCE-based axis:
- Question: "How reliable is this knowledge?"
- Use case: Decay rates, trust modeling, CauseChain provenance
- Example: "I heard this from Lera (Rumor) vs I saw it myself (Direct)"
Paula's DEPTH-based axis:
- Question: "How much do I understand about this situation?"
- Use case: Monologue progression, dialogue unlocks, narrative revelation
- Example: "I suspect Kael is compromised (Suspects) vs I know he's trying to exit the ring to protect Hael (Understands)"
These are ORTHOGONAL. You can have:
- High source confidence, low depth: "I directly observed Kael in corridor B-7 (Direct), but I don't know why (KnowsOf)"
- Low source confidence, high depth: "Lera told me Kael is trying to exit the ring to protect Hael (Rumor), and it explains everything (Understands)"
The Mechanical Solution
The architecture should support BOTH:
pub struct EntityKnowledge {
// Tyre's SOURCE confidence (decay, trust)
pub confidence: KnowledgeConfidence, // Stale < Rumor < Inferred < Observed < Direct
// Paula's DEPTH understanding (gating, progression)
pub understanding: UnderstandingLevel, // Suspects < KnowsOf < KnowsDetails < Understands
pub source: KnowledgeSource, // Provenance
// ... other fields
}
pub enum KnowledgeConfidence {
Stale, // Old information, may be outdated
Rumor, // Third-hand, low trust
Inferred, // Deduced from other facts
Observed, // First-hand but not currently visible
Direct, // Currently in LOS
}
pub enum UnderstandingLevel {
Suspects, // "Something's off"
KnowsOf, // "X is happening"
KnowsDetails, // "X is happening because Y"
Understands, // "X is happening because Y, and it means Z"
}
What Gates What
Monologue prerequisites use DEPTH (Paula's axis):
prerequisite:
knowledge:
- subject: kael_davan
understanding: knows_details # Not confidence level
flag: trying_to_exit_ring
Dialogue access tiers use DEPTH + relationship:
access: [insider]
trust: secret
prerequisite:
knowledge:
- subject: voss
understanding: knows_details
flag: false_manifests
Decay mechanics use SOURCE (Tyre's axis):
if knowledge.confidence == KnowledgeConfidence::Stale {
// Decay faster or remove
}
Contradiction detection compares SOURCES:
if knowledge_a.source == DirectObservation && knowledge_b.source == ToldBy(sera) {
// Sera lied to you
trigger_contradiction_monologue();
}
Recommendation for Synthesis
Keep Tyre's KnowledgeConfidence enum as-is. It's the correct model for SOURCE reliability and maps cleanly to decay/trust.
Add a second field: understanding_level: UnderstandingLevel to EntityKnowledge for DEPTH tracking.
Initial implementation: Both fields exist in Sprint 2 structs, but only confidence is written by perception systems. understanding_level defaults to KnowsOf (you observed the entity, so you know of them). Sprint 3+ adds systems that upgrade understanding through dialogue/evidence/inference.
This is "design for it now, build it later." The struct supports both axes. Sprint 2 uses one axis. Sprint 3 unlocks the other.
3. Gossip as Observable Mechanic
The Gameplay Loop (What I Proposed in Round 1)
1. Player tells Voss about Kael's smuggling
2. Hours later, player observes Voss and Maret talking
3. Next day, Maret confronts Kael
4. Monologue: "Voss told Maret. Information spreads. I started this."
Does the Architecture Support This?
Step 1 (player tells NPC):
- Dialogue system sets flag:
voss.knowledge.entities[kael_id].knows_fact("smuggling") = true - Source:
ToldBy { source_id: player_stable_id, tick } - ✓ Supported by Dudley's
KnowledgeSource::ToldByvariant
Step 2 (observe conversation):
- GAP. How does the player DETECT that Voss and Maret are having a "gossip-worthy" conversation vs casual small talk?
- Proposed mechanic: Observable tell system
- NPCs in conversation get
situation: private_conversationtag - Visual: NPCs face each other, proximity threshold, gestures
- Audio: D-018 sound ranges (player hears murmurs if within Medium range)
- Monologue: "Voss and Maret, heads together. Plotting or gossiping?"
- NPCs in conversation get
Step 3 (Maret confronts Kael):
- NPC AI system (Sprint 3+) queries Maret's knowledge graph
- Finds:
kael.knows_fact("smuggling")with high confidence - Triangle pressure increased (Q-017 mechanism)
- Activates
situation: confrontationfor Maret's next interaction with Kael - ✓ Supported by proposed architecture (knowledge graph feeds AI decisions)
Step 4 (monologue commentary):
- Player observes Maret confronting Kael
- Monologue system queries player knowledge: "Did I tell anyone about Kael?"
- Meta-knowledge check: Player knows they told Voss, and they know Voss talked to Maret
- ✓ Requires meta-knowledge tracking: "I know X knows Y"
What's Missing
Observable gossip indicators:
- Visual tells when NPCs share information (not just "talking" but "sharing secrets")
- Monologue commentary when player detects gossip propagation
- Meta-knowledge structure: tracking "I know that X knows Y"
Is this a Sprint 2 blocker? NO. Sprint 2 is geometric fog. Gossip observation is social intrigue (Sprint 3+).
Is this an architectural blocker? NO. The knowledge graph structure supports it. The missing piece is the NPC conversation system and tell generation (separate from knowledge graph design).
Recommendation: Gossip propagation is correctly deferred, but Tyre's Round 2 synthesis should explicitly state: "NPC-to-NPC knowledge transfer will require conversation system + observable tell generation (Sprint 3 dependency)."
4. THE FRIEND Arc Walkthrough
Let me validate the architecture against Paula's canonical example: THE FRIEND lies to you, you discover the contradiction.
The Sequence (Using Proposed Structs)
T=0: Session start
// Detective's knowledge graph at spawn
detective.knowledge.entities[sera_id] = EntityKnowledge {
confidence: KnowledgeConfidence::Observed,
understanding: UnderstandingLevel::KnowsOf,
source: KnowledgeSource::Background,
relationship: RelationshipState::Friendly, // THE FRIEND
last_observed_tick: 0,
known_attributes: btreemap! {
AttributeKey::Name => AttributeValue::Text("Sera Venn"),
AttributeKey::Role => AttributeValue::Text("Scanner operator"),
},
// ...
};
T=100: Sera tells you Kael was at the dock
// Dialogue system writes to detective's knowledge graph
detective.knowledge.entities[kael_id]
.known_attributes
.insert(
AttributeKey::Custom("location_second_shift"),
AttributeValue::Text("dock")
);
detective.knowledge.facts.insert(
FactId("kael_location_shift2_dock"),
FactKnowledge {
confidence: KnowledgeConfidence::Rumor, // Told by someone
source: KnowledgeSource::ToldBy {
source_id: sera_id,
tick: 100
},
acquired_tick: 100,
}
);
T=200: You observe Kael in corridor B-7
// Perception system (after shadowcasting detects Kael in LOS)
detective.knowledge.observe_entity(
kael_id,
TilePosition { x: 47, y: 12, z: 2 }, // corridor B-7
200 // current tick
);
// This writes:
detective.knowledge.entities[kael_id] = EntityKnowledge {
last_known_position: Some(TilePosition { x: 47, y: 12, z: 2 }),
last_observed_tick: 200,
confidence: KnowledgeConfidence::Direct,
source: KnowledgeSource::DirectObservation { tick: 200 },
// ...
};
T=201: Contradiction detection
// Knowledge validation system (new system, Sprint 3)
fn detect_contradictions(
mut query: Query<(&mut KnowledgeGraph, &CharacterArchetype)>,
time: Res<SimulationTime>,
) {
for (mut knowledge, character) in &mut query {
// Check for conflicting location facts
let kael_facts: Vec<_> = knowledge.facts.iter()
.filter(|(id, _)| id.0.starts_with("kael_location"))
.collect();
if kael_facts.len() > 1 {
// Multiple location claims
let told_fact = kael_facts.iter()
.find(|(_, f)| matches!(f.source, KnowledgeSource::ToldBy { .. }));
let observed_fact = kael_facts.iter()
.find(|(_, f)| matches!(f.source, KnowledgeSource::DirectObservation { .. }));
if let (Some((told_id, told)), Some((obs_id, obs))) = (told_fact, observed_fact) {
// Contradiction detected
if let KnowledgeSource::ToldBy { source_id, .. } = told.source {
// Sera lied
knowledge.entities.get_mut(&source_id).map(|sera| {
sera.relationship = RelationshipState::PersonOfInterest;
});
// Emit monologue event
events.send(MonologueEvent {
character: *character,
trigger: TriggerType::KnowledgeContradiction,
chime: ChimeLevel::Urgent,
text_id: "mon_sera_lied_location",
});
}
}
}
}
}
T=202: Monologue fires
[Urgent chime]
"Sera told me Kael was at the dock. But I just saw him in the corridor. Why did she lie?"
T=203: Entity color shift
// Observer snapshot assembly (feeds client)
let sera_relationship = detective.knowledge.relationship_with(&sera_id);
// sera_relationship == RelationshipState::PersonOfInterest
snapshot.entities.push(VisibleEntity {
entity_id: sera_id.0,
relationship: RelationshipState::PersonOfInterest, // Amber color
// ...
});
T=210: Dialogue unlock
// Dialogue system (Sprint 3) queries knowledge for confrontation access
let can_confront_sera = detective.knowledge
.entities
.get(&sera_id)
.map(|k| k.relationship == RelationshipState::PersonOfInterest)
.unwrap_or(false);
if can_confront_sera {
dialogue_options.push(DialogueOption {
text: "Why did you lie about Kael's location?",
access: AccessTier::Insider,
situation: Situation::Confrontation,
topic: Topic::SeraLied,
});
}
Validation Result
✓ Every beat is supported:
- Lie entry (ToldBy source)
- Contradiction detection (compare sources)
- Monologue trigger (KnowledgeContradiction event)
- Relationship state shift (PersonOfInterest enum)
- Entity color change (RelationshipState in snapshot)
- Dialogue unlock (relationship state gates access)
✓ No gaps in data structures.
✓ Two new systems needed (Sprint 3):
detect_contradictionssystem (runs after knowledge updates)- Dialogue access filtering by knowledge + relationship state
THE FRIEND arc proves the architecture. If it supports this, it supports the game.
5. Gaps Found
Gap 1: Meta-Knowledge (Who Knows What)
What's missing: Tracking "I know that X knows Y."
Why it matters: Gossip observation gameplay requires the player to infer knowledge propagation.
Example:
Player tells Voss about Kael
Player observes Voss talking to Maret
Player infers: "Maret probably knows now"
Proposed solution (Sprint 3):
pub struct MetaKnowledge {
/// I know that entity X knows about entity Y
pub knowledge_of_knowledge: BTreeMap<StableId, BTreeSet<StableId>>,
}
Is this a blocker? No. Sprint 2 doesn't need it. Social intrigue does (Sprint 3+).
Gap 2: Knowledge Update Triggers Monologue
What's underspecified: How does a knowledge graph change trigger a monologue?
Tyre's architecture: Knowledge update systems write to KnowledgeGraph Component. Monologue system reads Changed.
Missing detail: What counts as "monologue-worthy" knowledge change?
- New entity observed? (Always trigger)
- Existing entity re-observed? (Don't spam)
- Knowledge level upgraded? (Trigger)
- Contradiction detected? (Urgent trigger)
Proposed solution:
pub enum KnowledgeChangeType {
NewEntity(StableId),
LevelUpgrade { entity: StableId, old: UnderstandingLevel, new: UnderstandingLevel },
Contradiction { entity: StableId, sources: Vec<KnowledgeSource> },
FactLearned(FactId),
}
#[derive(Resource)]
pub struct KnowledgeChangeEvents {
pub changes: Vec<KnowledgeChangeType>,
}
Monologue system consumes these events, not Changed (too noisy).
Gap 3: Dialogue System Integration Not Specified
What's missing: The dialogue system needs to query the knowledge graph for access filtering.
Current state: D-028 defines access tiers. D-035 defines prerequisite tags. But the INTEGRATION is not specified.
Needed (Sprint 3):
pub fn dialogue_available(
npc_id: StableId,
line: &DialogueLine,
player_knowledge: &KnowledgeGraph,
player_relationship: &RelationshipState,
) -> bool {
// Check access tier
if !line.access.contains(&player_relationship.tier()) {
return false;
}
// Check knowledge prerequisites
for prereq in &line.prerequisites {
match prereq {
Prerequisite::Knowledge { subject, understanding, flag } => {
if !player_knowledge.has_understanding(subject, *understanding, flag) {
return false;
}
}
// ...
}
}
true
}
Is this a blocker? No. Dialogue gating is Sprint 3+. But the interface contract should be defined now.
Gap 4: Observable Gossip (Already Covered in Section 3)
See section 3. Not a blocker, but needs explicit acknowledgment in synthesis.
6. Recommendation for Synthesis
What Tyre Should Include
-
Accept the dual-axis model:
KnowledgeConfidence(Tyre's SOURCE axis) for decay/trustUnderstandingLevel(Paula's DEPTH axis) for gating/progression- Both fields in
EntityKnowledgestruct - Sprint 2 uses confidence only, Sprint 3 unlocks understanding
-
Acknowledge gossip gap:
- "NPC-to-NPC knowledge transfer is architecturally supported but requires NPC conversation system (Sprint 3 dependency)"
- "Observable gossip indicators (tells, situation tags) are separate from knowledge graph design"
-
Define knowledge change events:
KnowledgeChangeEventsresource for monologue triggering- Separates "graph changed" from "monologue-worthy change"
-
Specify dialogue integration interface:
dialogue_available()function signature- How knowledge prerequisites map to line filtering
- Defer implementation to Sprint 3, but contract exists now
-
Validate THE FRIEND arc:
- Include walkthrough from section 4 as proof the architecture works
- Confirms no structural gaps for core gameplay
What Tyre Should Change
Nothing architectural. The Component model, BTreeMap, StableId, and 5-level confidence enum are all correct.
Additions only:
- Add
understanding_level: UnderstandingLevelfield toEntityKnowledge - Add
KnowledgeChangeEventsresource to API - Add note about gossip system dependency
Sprint 2 Stub Confirmation
Ship exactly what Tyre specified:
- Full
KnowledgeGraphstruct KnowledgeConfidenceenum (5 levels)observe_entity()method (DirectObservation source)relationship_with()query (entity color)knows_fact()+fact_at_least()(monologue prerequisites)- Basic decay (batch, 600-tick intervals)
This is the right foundation. No rework needed.
7. What's NOT Missing
Let me be clear about what does NOT need to change:
Data structures: Perfect as-is. BTreeMap, StableId, Component pattern, serialization strategy — all mechanically sound.
Performance: Dudley's batch decay + event queue architecture prevents the O(N×M) explosion I was worried about in Round 1. Knowledge lookups are not on the critical path.
Save/load: StableId solves entity reference stability. I have no concerns.
Tier serialization: Knowledge graph rides the existing tier system. No special logic needed.
Observer snapshot integration: relationship_with() + known_entities() queries are exactly what #112 needs.
The architecture is not the problem. The missing pieces are SYSTEMS that use the architecture (gossip, dialogue integration, contradiction detection). Those are Sprint 3+, correctly deferred.
8. Final Verdict
Does the architecture serve gameplay? YES.
Is the hierarchy debate resolved? YES (dual-axis model).
Does THE FRIEND arc work? YES (validated step-by-step).
Is gossip underspecified? YES (but correctly deferred to Sprint 3).
Are there blocking gaps? NO.
Should we ship the Sprint 2 stub? YES, exactly as specified.
Bottom line: This is interesting-complex, not annoying-complex. The knowledge graph creates DECISIONS (dialogue gates, monologue progression, social manipulation), not BOOKKEEPING (no Pokemon collection, no perfect memory, no hidden NPC knowledge). The player experiences knowledge through what actions are available, not through a sprawling journal UI.
Let's build this.
Files Referenced
/var/mnt/data/projects/settled-reach/planning/docs/workshops/knowledge-graph-information-boundaries/round1-tyre.md/var/mnt/data/projects/settled-reach/planning/docs/workshops/knowledge-graph-information-boundaries/round1-dudley.md/var/mnt/data/projects/settled-reach/planning/docs/workshops/knowledge-graph-information-boundaries/round1-paula.md/var/mnt/data/projects/settled-reach/planning/docs/workshops/knowledge-graph-information-boundaries/round1-gestalt.md/var/mnt/data/projects/settled-reach/planning/docs/workshops/knowledge-graph-information-boundaries/round1-si.md/var/mnt/data/projects/settled-reach/planning/decisions/perception.md(D-011, D-015-D-019, D-033)/var/mnt/data/projects/settled-reach/planning/decisions/content.md(D-024, D-028, D-034, D-035)/var/mnt/data/projects/settled-reach/planning/decisions/architecture.md(D-010, D-020, D-026, D-030)/var/mnt/data/projects/settled-reach/planning/decisions/questions.md(Q-016, Q-017)