Files
settled-reach/docs/workshops/knowledge-flow-npc-boundaries/paula-round1.md
T
jpmschweitzerandClaude Opus 4.6 60ffe2e02a docs(docs): add frontmatter to knowledge-flow-npc-boundaries workshop
Standardized YAML frontmatter on all 12 files.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 23:40:44 +01:00

27 KiB

title, description, type, status, workshop, agent, round, created
title description type status workshop agent round created
Paula — Round 1: Narrative Analysis Paula's Round 1 narrative analysis of knowledge grants, testimony semantics, and THE FRIEND arc mechanical requirements workshop archived knowledge-flow-npc-boundaries paula 1 2026-02-23

Paula — Round 1: Narrative Analysis

Knowledge Flow & NPC Information Boundaries Workshop

Date: 2026-02-23 Role: Narrative and Political Depth


Framing

The technical gap this workshop addresses — NPCs running on ground truth, gossip not transferring knowledge, contradiction detection not firing — is not just a systems problem. It is a narrative problem. Every one of these missing pieces corresponds to a moment in THE FRIEND arc that cannot yet land.

Sera Venn volunteers information that the detective doesn't yet know. That's unprompted disclosure (#172). The detective later observes something that contradicts Sera's account. That's contradiction detection (Q-026). The monologue that fires ("Sera said Kael was at the dock. I just saw him in B-7.") is the emotional centerpiece of v0.1 — and it currently cannot happen because the ToldBy source that would tie Sera's testimony to that contradiction was never constructed.

This workshop is asking what it means for an NPC to know something. My job is to make sure the answers we reach don't just pass technical review — they have to feel true to how people actually hold information, share it, and get caught in contradictions.


Topic 1: Knowledge Flow — The Grant Mechanism

The authoring philosophy question underneath the schema question

Before discussing whether KnowledgeGrant needs an entity_grant variant, there is a prior question: What does it mean for an NPC to "tell" the player something?

In the current system, dialogue lines are content artifacts. They sit in line pools, get scored and selected, get displayed. They don't do anything to the world's information state. The player reads "Kael runs the cargo intake" and nothing changes in the KG. The dialogue was informative in the sense that a book is informative — it delivered text — but it didn't create a knowledge relationship between the observer and the subject.

Testimony is not just text delivery. When Sera says "Kael was at the dock during second shift," she is creating a source-attributed claim about a specific person at a specific time. That claim has provenance (Sera, tick N), it has a confidence level (KnowsOf — she's not guessing, she observed), and it can later be contradicted. The grant mechanism is what makes the claim real in the system.

Position: KnowledgeGrant needs two variants

The current schema (KnowledgeGrant { fact_id: String, confidence: String }) only covers fact knowledge — entries in facts: BTreeMap<FactId, FactKnowledge>. But Sera's claim about Kael creates EntityKnowledge, not just a fact. It should update entities: BTreeMap<StableId, EntityKnowledge> with a new entry for Kael, sourced ToldBy { source_id: sera_sid, tick }.

I propose KnowledgeGrant be extended to a sum type:

enum KnowledgeGrantTarget {
    Fact { fact_id: FactId },
    Entity { entity_sid: StableId, attribute_key: String, attribute_value: String },
    EntityPosition { entity_sid: StableId, location_description: String },
}

This is not just a schema preference. The downstream consequences are:

  • Contradiction detection requires EntityKnowledge, not just facts. The canonical contradiction (Sera says "dock", player observes "B-7") operates on EntityKnowledge.last_known_position and known_attributes. If Sera's testimony only creates a FactKnowledge entry, the contradiction detector has nothing to compare against the DirectObservation that updates EntityKnowledge.
  • Source attribution requires the grant to create a ToldBy entry. If the detective's KG entry for Kael is created source: ToldBy { source_id: sera_sid, tick }, the contradiction later marks BOTH the DirectObservation entry AND the ToldBy entry as Contradicted. The system can then surface: "Sera told you X. You observed Y. These conflict." Without the entity grant, you lose the source chain.

Content authoring guardrail: what NPCs can grant = what they know

The workshop brief raises the question of content validation. My position: NPCs should only be able to grant knowledge they hold in their own KnowledgeGraph.

This is not merely an authoring convenience. It is a narrative truth constraint. If Sera's own KG doesn't contain an EntityKnowledge entry for Kael-at-the-dock, she cannot have told the detective about it — because she doesn't know. An author who writes a dialogue line with knowledge_grant: { entity: kael_sid, position: "dock 7" } on Sera's line has asserted that Sera knows this. The validator should check: does Sera's authored initial KG state include this entry? If not, flag it.

Runtime enforcement is harder (NPC KGs evolve during play), but the authoring-time check catches the obvious cases and forces authors to make NPC knowledge explicit.

POI discovery as knowledge flow

For POI knowledge, I prefer option (a): a FactId category "poi.*" (e.g., poi.dock_7_restricted). The reasons are narrative:

  • POI knowledge is about a location, not about a person. It fits the FactKnowledge model cleanly.
  • The player "discovering" a POI is precisely a DirectObservation event, just targeted at a location rather than an entity.
  • Dialogue-granted POI knowledge (an NPC saying "there's a restricted cargo bay off corridor B-7") should create a ToldBy FactKnowledge entry at Suspects or KnowsOf confidence — lower than direct discovery.
  • This integrates cleanly with filter_by_access: a player needs poi.dock_7_restricted in their KG at KnowsOf+ to get dialogue options about that location.

Physical evidence vs dialogue grants

I see these as the same grant mechanism but with different triggering events:

  • Dialogue-triggered: post-line-selection, the dialogue system constructs a KnowledgeEvent and pushes it to the queue
  • Evidence-triggered: player "reads" a document or examines evidence, a similar event fires

The difference is the source field: dialogue creates ToldBy, evidence examination creates DirectObservation (you're directly observing the document's contents). A new KnowledgeEventType::KnowledgeGranted { source: KnowledgeSource, target: KnowledgeGrantTarget } covers both cases cleanly without a separate variant for evidence.


Topic 2: NPC-to-NPC Knowledge Propagation (Q-024)

The routine intersection hook is narratively correct

The existing run_npc_conversations system pairing NPCs by proximity is already modeling something true about information flow in a community: people share information when they're in physical proximity, during routine overlap, not via some abstract information broadcast. This is the right hook. No separate system needed.

What I want to advocate for is the content of what transfers, not just the mechanism.

Trust-gated filtering: not just tiers, but relationship character

The proposed mapping (surface trust = public facts, real trust = observations/rumors, secret trust = sensitive knowledge) is correct in structure, but misses something. The character of what NPC A shares with NPC B depends not just on trust level but on subject matter:

  • Voss will share public knowledge about cargo schedules with anyone. He'll share his suspicions about manifest discrepancies only with people he trusts. He'll never share that he knows about the ring — regardless of trust tier — because he's terrified.
  • Kael will share warmth freely. His secret (trying to exit the ring) is not something he'd share even at the highest trust tier, because sharing it is dangerous.

This suggests trust-gated filtering should be necessary-but-not-sufficient. Alongside trust tier, NPCs should have a disclosure_threshold_override on individual KG entries — a flag that says "even at maximum trust, don't share this." This is how Major secrets should behave: they are withheld at the KG entry level, not filtered by trust tier.

Confidence downgrade on transfer: the proposed rule works

The proposed rule (ToldBy confidence = min(source_confidence, KnowsOf)) is right. When NPC A tells NPC B something, B doesn't have A's direct observation — B has A's report. That's KnowsOf at best. Direct observation never transfers through gossip; you can't tell someone else what it's like to see something and have them receive that experience.

The narrative effect is important: gossip chains dilute information. By the time the fifth NPC in a chain hears something, it's Suspects-level, not KnowsDetails. This is accurate to how communities work.

Rate limiting: 1-3 facts per conversation, but weighted by relationship

A fixed cap of 1-3 facts per conversation works mechanically. But I'd weight by trust level:

  • Surface trust: 0-1 facts (pleasantries and public knowledge only)
  • Real trust: 1-2 facts
  • Secret trust: up to 3 facts, but only if NPC has relevant high-trust entries

The goal is to prevent knowledge-explosion (correctly identified in the brief) while also preventing "people with deep trust never share anything." The weighting makes NPCs feel like they're calibrated to the relationship.

Overheard NPC conversations: always Suspects, no exceptions

If the player overhears an NPC-NPC conversation (D-078), the player's KG entry should always be at Suspects confidence, regardless of how much of the conversation survived occlusion. Here's why:

  1. The player lacks context. They heard words, not an explanation. "He said Kael was at the dock" means nothing without knowing who "he" is, what "the dock" refers to, whether this is past or present.
  2. Suspicion is the correct epistemic state. You overheard something. You don't know if it's true. Suspects is honest.
  3. It creates investigative motivation. You need to confirm. You seek out sources. You build the case. KnowsOf for overheard information would shortcut this.

The source variant should be KnowledgeSource::Heard { tick, range: Close } — not ToldBy, because the NPC didn't tell you anything; you were eavesdropping. This distinction matters for contradiction detection: an overheard claim is harder to attribute for contradiction purposes than a direct testimony.


Topic 3: Unprompted Disclosure Design (#172)

This is where I have the strongest opinions, because this is where THE FRIEND arc's emotional setup lives.

The setup that has to work

Sera Venn's relationship with the detective follows this arc (from D-034):

  • Phase 1 (trust): Commission tech, bar regular, warm and informative, socially comfortable
  • Phase 2 (data): Begins volunteering information about cargo processes, casually — this is unprompted disclosure
  • Phase 3 (recognition): Player observes a behavioral tell — Sera avoids Torek Lintar
  • Phase 4 (question): Player realizes Sera is sitting on unreported evidence
  • Phase 5 (contamination): Trust is re-evaluated

The whole arc requires that Phase 2 actually happen mechanically. Sera must volunteer information to the player before the contradiction. If she only speaks when spoken to (dialogue tree), the player has no expectation of Sera as a source — and when the contradiction lands, it doesn't mean anything. The sense of betrayal requires prior gift-giving.

This is the narrative case for getting unprompted disclosure right: without it, there is no FRIEND arc. There is only an NPC who sometimes lies.

"Do I know something you don't?" — Option A, firmly

NPCs should NOT check the player's KG before deciding to disclose. Option A (NPC checks own KG only) is the right choice, for narrative reasons that I think are non-negotiable:

  1. Dramatic irony requires asymmetric knowledge. When Sera tells the detective "Kael runs a tight intake process — never a discrepancy on his watch" in Phase 2 — and the player has already found manifest discrepancies — the dramatic irony is crushing. Sera doesn't know the player already knows. She's praising someone whose cover is already partially blown. If Sera checked the player's KG and saw KnowsOf for manifest discrepancies, she might not say this. You've lost the moment.

  2. NPCs not knowing what you know is a feature, not a bug. It's the source of almost all the interesting social texture. Characters talk at cross-purposes. People are still defending someone the player has already made a case against. The community is behind the detective.

  3. Option B creates a surveillance panopticon. If NPCs know what the player knows, they're behaving as if they have access to the player's cognitive state. That's not a knowledge boundary — that's a boundary violation.

The result is that NPCs may repeat information the player already has. This is fine. The player has heard this before — but hearing it from THIS person, at THIS trust level, after THIS much has been discovered, has different weight. Authors should lean into it: "She's still defending him. Doesn't she know?"

Trait filtering: both, with specific meaning for each

The brief asks: do traits affect WHAT is disclosed (filtering) or HOW (delivery)? The answer must be both, but they operate at different layers:

  • What (filtering): Cautious NPCs have a higher disclosure threshold — they need more trust and more of their specific mood conditions before any fact makes it to the disclosure candidates list. This is a KG-level filter. A cautious Sera might have 40 facts in her KG but only 2 that would clear her disclosure threshold at any given trust level.
  • How (delivery): Once a fact clears the threshold, Sera's specific personality shapes the line. Her warmth, her slight formality, her tendency to frame things in terms of professional competence. This is the line pool scoring modifier — same facts, different pool weighting.

The two-layer model means: trait changes "how many things Sera would ever say unprompted" (filtering) AND "how she says the things she does say" (delivery). This is richer than either alone and avoids the edge case where a cautious NPC with a Major secret that must be disclosed has no way to shape the delivery.

Trigger conditions

Proposed trigger set for unprompted disclosure:

  1. Trust threshold met (RelationshipState: Friendly minimum)
  2. Contentment above neutral (> 0 on the Contentment axis)
  3. Mood compatible (not Hostile, Anxious above threshold, or Frustrated) — use the 8-mood vocabulary from D-035/D-035 Amendment Sprint 14
  4. Location appropriate — this condition is missing from the brief and I want to argue for it explicitly (see below)
  5. Rate limit not exceeded (the existing LINE_COOLDOWN_TICKS: u64 = 600 from dialogue.rs line 39 should apply)

Location appropriateness as a disclosure gate:

Sera won't confide sensitive information at the Terminal (too many witnesses, professional context). She will at The Last Shift in a quiet corner. This maps to D-025's public/private spatial distinction — "functional cluster defines spatial identity including public/private."

Content authors should be able to tag disclosure candidates with a location_privacy: [private, semi-private, any] field. The disclosure trigger checks the NPC's current location against this. A private-tagged disclosure won't fire at The Terminal. This is not just realism — it creates spatial behavior patterns the player can learn: "If I want Sera to talk, I need to catch her at Lera's, not at work."

Rate limiting structure

Three-layer rate limiting:

  1. Per-fact cooldown (highest priority): once a fact has been disclosed, mark it in the NPC's disclosure state. Don't repeat it. The player has heard it. Repeating it is noise.
  2. Per-NPC cooldown (second): prevents disclosure spam in a single interaction. The LINE_COOLDOWN_TICKS (600 ticks) seems right as a floor.
  3. Global rate limit across all NPCs (third, lightest): exists mainly to prevent a degenerate case where the player visits 8 NPCs in rapid succession and gets flooded with disclosures in one game-hour.

The per-fact cooldown is the most important narratively: an NPC who says the same thing twice is a quest marker, not a person.


Topic 4: NPC Information Boundaries (#142)

The narrative argument for minimum viable first

The workshop brief correctly identifies minimum viable boundary as tell_state.rs + unprompted disclosure. I want to reinforce why this is the right order from a narrative standpoint — not just a feasibility standpoint.

tell_state.rs currently derives tell category from raw axes: Secret severity, stress vs. threshold, contentment, mood, relationships. This produces behaviors like "NPC is Guarded because they have a Major secret." That's correct. But what if the NPC's knowledge of their secret's exposure changes their behavior? Sera's Guarded tell should intensify if she knows the detective is investigating Kael — not just because her Secret severity is Major, but because her KG contains entries showing investigation proximity.

The minimum viable boundary retrofit for tell_state.rs is: allow KG-derived knowledge to influence the Secret's effective stress level. If the NPC knows someone is getting close to their secret, the threat multiplier on current_stress increases. This doesn't require a full KG query — just a check: "does my KG contain entries related to the entities/facts that my Secret references, at PersonOfInterest or higher?"

This is a very small addition that produces a very meaningful behavioral change: NPCs who know they're being watched act more stressed. NPCs who are unaware remain calm.

Priority order for system retrofits

From a narrative impact standpoint, I'd prioritize:

Priority System Narrative Payoff
1 tell_state.rs Direct: NPC observable behavior reflects what they know. Every investigation interaction benefits.
2 Unprompted disclosure (#172) Direct: Enables Phase 2 of THE FRIEND arc. Without this, Sera is mute pre-contradiction.
3 conversation.rs Medium: NPC-to-NPC conversations become information events, not just ambient noise.
4 routine.rs Low for v0.1: routine deviation is already a tell signal (D-027). Whether the routine query uses KG is less impactful initially.
5 path_follow.rs Very low / deferred: KG-based pathfinding creates stuck-NPC risk (noted in brief). For v0.1, ground truth pathfinding with KG-based decisions is the right split.

Fallback behavior: ground truth with logging, not silent

For all retrofitted systems, when the NPC's KG has no relevant information, fall through to ground truth with structured logging (not silent). The logging serves two purposes:

  1. QA visibility: we can see which systems are still operating on ground truth and why
  2. Design signal: if we see many log entries for a specific NPC/system, it indicates an authoring gap (NPC's initial KG state is missing entries that should be there)

Option (c) — "ask around" behavior — is compelling for future sprints. An NPC who doesn't know something seeking out information creates exactly the emergent scenes D-029 needs for the mundane 80%. But it's correctly deferred for v0.1.


Topic 5: Contradiction Detection Pipeline (Q-026)

This is where the mechanical and narrative come into direct contact, and I have specific things to say about the emotional sequence.

The THE FRIEND arc — full mechanical sequence

The brief asks for a complete walkthrough confirming every system fires correctly. Here is the canonical version from my narrative perspective:

Precondition: Sera's KG contains EntityKnowledge for Kael: last_known_position: Some(DockTile), source: DirectObservation { tick: T0 }, confidence: KnowsDetails. She observed Kael at the dock during second shift.

Step 1: The testimony

  • Detective initiates Talk with Sera. Dialogue pipeline selects a line with knowledge_grant: { entity: kael_sid, position: "dock intake, second shift", confidence: KnowsOf }.
  • KnowledgeEvent::KnowledgeGranted fires. Player's KG gains EntityKnowledge for Kael: source: ToldBy { source_id: sera_sid, tick: T1 }, confidence: KnowsOf, position claim: dock, time window: second shift.

Step 2: The observation

  • At tick T2 (same game-shift or overlapping time window), player's LOS includes Kael in corridor B-7.
  • KnowledgeEvent::DirectObservation fires. Player's KG entry for Kael: source: DirectObservation { tick: T2 }, confidence: Direct, position: B-7.

Step 3: Contradiction detection fires

  • Event-driven: on KG write (T2), contradiction detector runs against the new DirectObservation entry.
  • Check: same entity (Kael_sid), overlapping time window (T1 and T2 within second-shift window), different positions (dock vs B-7).
  • Both entries receive KnowledgeState::Contradicted.
  • ContradictionDetected event emits: observer = detective, entries = [ToldBy(Sera, dock), DirectObservation(B-7)], type = location.

Step 4: Downstream cascade

  • Monologue system picks up ContradictionDetected. Fires contradiction monologue line — must be authored to name Sera specifically. Generic "wait, that doesn't add up" is not sufficient here. The line must say: "Sera said Kael was at the dock during second shift. I'm looking at him in B-7 right now. One of them is wrong."
  • Anomaly system marks Kael AND Sera as PersonOfInterest (both entities are implicated — Kael by being in the wrong place, Sera by the testimony that is now contradicted).
  • D-033: both entities shift to amber.
  • Available dialogue with Sera unlocks Confrontation option.

Step 5: The ambiguity

  • The monologue fires Contradicted — not "Sera lied." The player doesn't know if Sera is lying, mistaken, or manipulated. The engine is correct to be epistemically neutral. The content must be too.

On the "both entries Contradicted" design

I want to explicitly endorse the design choice that both entries receive Contradicted state. This is narratively correct. When you have contradictory sources, you can't know which is wrong. The testimony might be wrong (Sera lied/was confused). The observation might be wrong (Kael has a double? Player misidentified?). The engine marks both as uncertain, which is the honest epistemic state.

Authors writing Sera's post-contradiction dialogue must NOT have Sera behave as if she knows she's been caught. She doesn't know the player has contradicted her testimony. Her behavior changes only if the player CONFRONTS her. This is the "no player-special-casing" principle (D-010) expressed in character psychology.

Contradiction content authoring requirements

This is a gap in the current framework that I want to surface for Round 2 discussion:

Contradiction monologue lines must be authored with source attribution. A generic trigger: contradiction_detected monologue line ("that doesn't add up") is insufficient. The player needs to understand WHO provided the contradicted claim. This requires the monologue system to receive the source entity's StableId and map it to a displayable name.

Options:

  1. Templated monologue lines with entity name substitution: "{source_name} said {entity_name} was at {claimed_location}. I just saw {entity_name} at {actual_location}." — clean but requires template string support in the monologue system
  2. Pre-authored lines per relationship phase — for THE FRIEND NPCs (Kael, Sera), write specific lines for each phase of the relationship that fire on contradiction. This is hand-authored, but THE FRIEND NPCs are already "no generation expansion, all hand-authored" per D-034.
  3. Generic fallback + authored override — generic template for auto-generated NPCs, hand-authored lines for FRIEND-pattern NPCs

My preference is option 3. For v0.1 with only two FRIEND NPCs, the hand-authored lines (option 2 flavored content in the generic system) are achievable. The template system is the right long-term architecture.

Attribute contradiction — typed keys vs authored pairs

The brief notes that known_attributes: BTreeMap<String, String> is untyped. For contradiction detection on attributes, I'd prefer content-authored pairs with structured key conventions over a full typing system.

Reasoning: attribute contradiction is rare and narratively significant. You don't want the engine silently marking two minor attribute discrepancies as contradicted. You want the author to say "this specific attribute combination is a contradiction that the player should notice."

Proposed approach: YAML schema extension with an optional contradicts field on KnowledgeGrant:

knowledge_grant:
  entity: kael_sid
  attribute_key: "occupation_status"
  attribute_value: "legitimate_cargo_handler"
  # authored contradiction pair:
  contradicts_attribute_value: "ring_member"

This surfaces the contradictions that matter narratively and keeps the detection system from firing on attribute mismatches that are informationally trivial.


Summary: Narrative Requirements for Round 2 Decisions

The decisions this workshop produces need to satisfy the following narrative requirements to make THE FRIEND arc work:

Requirement Topic System Priority
Testimony creates EntityKnowledge with ToldBy source 1 KnowledgeGrant schema Critical — blocks contradiction detection
NPCs cannot grant knowledge they don't hold 1 Content validation High — authoring integrity
POI knowledge via poi.* FactId 1 KnowledgeGrant schema Medium
Trust-gated NPC-to-NPC sharing with Major secret override 2 Propagation model High — prevents secret leakage
Overheard conversations always grant Suspects confidence 2 Event model High — preserves investigation motivation
Unprompted disclosure uses Option A (NPC checks own KG only) 3 Disclosure algorithm Critical — enables dramatic irony
Location privacy gates unprompted disclosure 3 Trigger conditions High — spatial behavior patterns
Per-fact cooldown prevents disclosure repetition 3 Rate limiting High — NPC authenticity
tell_state KG awareness (secret exposure intensity) 4 tell_state.rs retrofit High — behavioral authenticity
Contradiction monologue names the source entity 5 Content system Critical — emotional payoff
Both contradiction entries marked (epistemic neutrality) 5 Detection algorithm Critical — no premature accusation
Authored attribute contradiction pairs 5 Schema Medium

The three "Critical" items are preconditions for D-027 success criterion #3 ("player names an NPC they felt conflicted about"). If testimony doesn't create ToldBy EntityKnowledge, contradiction detection cannot fire on THE FRIEND arc. If Option B is chosen for unprompted disclosure, the dramatic irony that makes Sera's contradiction land is destroyed. If the contradiction monologue is generic and nameless, the emotional moment dissipates into noise.

Everything else can be iterated. These three cannot.