- T-1070: scaffold_bodies.py heightmap link now conditional (bake criteria per import_heightmaps.py); surgical removal of the dead image line from all 2,131 body pages lacking the file (267 with the file keep theirs) - T-1071: 26 real link breaks fixed (knowledge/→concepts/, pre-governance decision anchors, 9 phantom catalog companions unlinked, cygni relink to the corporation page, wrong design-doc path); wiki/index.md counts fixed (301 systems) + Economics nav section; corporations/index.md regenerated from frontmatter (all 155, tier1.toml grouping — corrects 4 misfiled tier-1 corps); 25 orphaned station GTTRs linked from 11 system pages (own ##-heading so atlas sync cannot absorb it); GOVERNANCE.md + star-system template rewritten to the generated model (DB owns structured fields, wiki owns prose); triangles/index.md added - Sol markers conversion REVERTED before commit: atlas_viewer.gd:433 still renders the legacy geometry schema, so conversion would drop Sol's Atlas overlays — split to T-1073 (convert together with the client read path) Broken relative links: 2,180 → 25 (all remaining are intentional _templates/ placeholders). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
22 KiB
title, description, slug, category, status, created, updated, concept_type, tags, decision_refs, cross_refs
| title | description | slug | category | status | created | updated | concept_type | tags | decision_refs | cross_refs | |
|---|---|---|---|---|---|---|---|---|---|---|---|
| Entity Knowledge Attributes | Canonical keys for EntityKnowledge.known_attributes — 16 keys covering identity, social, behavioral, leverage, investigation, and role-perspective data | entity-attributes | concept | canonical | 2026-02-12 | 2026-02-13 | mechanical |
|
Entity Knowledge Attributes
Canonical keys for the EntityKnowledge.known_attributes BTreeMap (defined in server/src/knowledge/types.rs). These keys structure what one entity knows about another.
Overview
EntityKnowledge.known_attributes is a BTreeMap<String, String> — key-value pairs where:
- Key: Structured attribute name (defined below)
- Value: String representation of the attribute
The knowledge graph uses these attributes to determine:
- What the observer's monologue says about the target
- Which dialogue lines are available
- How the target renders in the observer snapshot (name vs. description)
- Whether leverage or contradictions are discovered
- How role-perspective assessments shape the observer's internal narrative
Identity Attributes
name
Value format: Full name as known to the observer.
Examples:
"{NPC Full Name}"— observer knows full name"{First Name}"— observer knows first name only"the dock worker"— observer doesn't know name, only role
Usage: Monologue and dialogue rendering. If name is absent or generic, monologue uses role description.
role
Value format: Occupational or social role.
Examples:
"dock worker""shift supervisor""bartender""off-duty security""ring coordinator"(if known)
Usage: Default descriptor when name is unknown. Access tier calculation (authority figures get different treatment).
faction
Value format: Institutional or organizational allegiance.
Examples:
"Commission"— institutional contacts, inspectors, detective"civilian"— most NPCs"ring member"— if discovered"unknown"— not yet determined
Usage: Access tier logic. Commission-tagged individuals trigger different NPC behavior. Dialogue selection uses this for authority/peer filtering.
species
Value format: Species identifier.
Examples:
"human"(default)- (Future: other species as defined)
Usage: Future-proofing for non-human NPCs.
Social Attributes
relationship_type
Value format: Relationship category from observer's perspective.
Examples:
"colleague"— ring member to fellow ring member"friend"— institutional contact to detective"authority figure"— shift supervisor to subordinates"suspect"— ring member to detective (post-discovery)"partner"— non-ring partner to ring member"stranger"— new NPCs
Usage: Drives RelationshipState calculation (Unknown/Known/Friendly/PersonOfInterest/Hostile). Determines emotional tone of monologue.
trust_level
Value format: Trust assessment (subjective, observer's judgment). This key encompasses both the read action (what the observer can observe about trust dynamics) and the state (what level of trust currently exists between observer and target). There is no separate trust_read key — the observation and the conclusion are stored together as a single assessment.
Values: trusted, reliable, uncertain, suspicious, compromised.
Examples:
"trusted"— high trust, shares sensitive information"reliable"— professional trust, predictable behavior"uncertain"— cautiously neutral, still reading the relationship"suspicious"— flagged for observation, inconsistencies detected"compromised"— known to be under pressure or bought
Usage: Gates trust-tier dialogue (surface/real/secret). Detective tracking trust levels determines which NPCs can be turned. Smuggler uses trust levels to gauge operational reliability. The trust read (what the observer notices) and the trust state (what the observer concludes) are one assessment — the observer watches, interprets, and assigns a level in the same cognitive step.
Behavioral Attributes
routine_pattern
Value format: Observed routine description.
Examples:
"morning shift at logistics hub, bar after shift""arrives 06:00, break 10:30, departs 14:00""irregular schedule, seen at odd hours"
Usage: Deviation detection. When current behavior doesn't match known routine_pattern, monologue flags it.
behavior_flags
Value format: Comma-separated behavioral observations.
Examples:
"nervous,lattice_checking"— ring member showing stress during contradiction arc"avoidance_pattern,evasive"— contact avoiding a suspect"spending_beyond_means"— inspector buying rounds on a dock worker's income"reliable,punctual"— baseline normal behavior
Usage: Tell system. Multiple flags accumulate as evidence. Detective's analytical lattice auto-flags some behaviors.
Leverage Attributes
leverage_held
Value format: What the observer believes gives them (or could give them) power over this entity. This covers secrets, debts, promises, obligations, and compromising positions — not just secrets. Any information or relationship that creates asymmetric power over the target qualifies as leverage.
Examples:
"ring membership"— detective's suspicion about a cargo handler (secret)"unreported evidence"— smuggler's read on an institutional contact (concealed information)"gambling debt"— if a peripheral member's vulnerability is discovered (financial obligation)"exit attempt"— if a ring member's unauthorized contact is discovered (betrayal)"owes favor to supervisor"— social obligation"witnessed the handoff"— compromising position
Usage: Drives PersonOfInterest state. Gates confrontation dialogue. Smuggler and detective track different leverage about the same NPCs. The broader scope (beyond secrets) allows content authors to capture debts, promises, and obligations that create power dynamics without requiring additional keys.
leverage_confidence
Value format: How certain the observer is about the leverage.
Examples:
"suspected"— behavioral flags suggest something, no proof"likely"— multiple indicators align"confirmed"— direct evidence or confession
Usage: Dialogue tone. "Suspected" leverage allows fishing questions. "Confirmed" leverage enables direct confrontation or exploitation.
Investigation Attributes
tell_observed
Value format: Specific tell noted by the observer.
Examples:
"looks left when lying"— ring member's tell"leaves when inspector arrives"— contact's avoidance"checks lattice before answering"— nervous behavior"forced casualness after shift supervisor passes"— hypervigilance
Usage: Behavioral profiling. Accumulated tells build case strength. Monologue references specific tells when they recur.
Implementation note: The server may track tells via
behavior_flagsinternally, since tells are a subset of behavioral observations.tell_observedis retained as a documented content-authoring key because it serves a distinct narrative purpose:behavior_flagsaccumulates raw observations ("nervous,lattice_checking"), whiletell_observedcaptures the observer's interpretive conclusion ("looks left when lying"). Content authors referencetell_observedfor monologue prerequisites that require the observer to have formed a specific read on the target. The server implementation may store both, or derivetell_observedfrombehavior_flagspattern matching.
contradiction_flagged
Value format: Boolean-ish flag (presence of key = true).
Examples:
"meeting_unknown_contact"— {THE FRIEND} smuggler path contradiction"avoidance_inconsistency"— {THE FRIEND} detective path contradiction"manifest_access_mismatch"— manifest clerk handling cargo they shouldn't process
Usage: THE FRIEND arc trigger. When this attribute is set, monologue tone shifts. Dialogue options change. RelationshipState moves toward PersonOfInterest.
Implementation note: Like
tell_observed, the server may track contradictions viabehavior_flags(e.g., a flag with acontradiction:prefix).contradiction_flaggedis retained as a documented key because it serves as a distinct narrative trigger — content authors check for its presence to gate arc transitions and monologue shifts. The separation keeps the content authoring interface clean even if the server implementation unifies storage.
Role-Perspective Attributes
These four keys capture the observer's subjective assessment of the target through the lens of their own role, goals, and ethical framework. The same key with the same value reads differently depending on the observer's archetype — interpretation lives in the content layer (monologue pools, voice registers, archetype briefs), not in the schema. See Archetype Interpretation Note below.
risk_assessment
Value format: Observer's assessment of how much danger this entity poses or attracts.
Values: none, low, moderate, high, critical.
Examples:
- Smuggler's assessment of a wavering ring member =
"high"(unpredictable, could expose the ring) - Detective's assessment of the shift supervisor =
"moderate"(obstructive but not directly dangerous) - Smuggler's assessment of an uninvolved partner =
"low"(unlikely to cause exposure)
Usage: Gates operational monologue. High-risk targets trigger cautionary internal dialogue. Critical-risk targets may trigger evasion or preemptive action. The nature of "risk" depends on the archetype (see interpretation note).
loyalty_assessment
Value format: Observer's assessment of this entity's reliability and loyalty.
Values: solid, dependable, uncertain, wavering, hostile.
Examples:
- Smuggler's assessment of the courier =
"dependable"(reliable, follows instructions) - Smuggler's assessment of {THE FRIEND} mid-game =
"wavering"(showing signs of doubt) - Detective's assessment of their institutional contact =
"solid"(committed to the investigation)
Usage: Determines how much the observer relies on or confides in the target. Wavering loyalty triggers monitoring behavior. Hostile loyalty triggers defensive or adversarial posture.
position_integrity
Value format: Observer's assessment of this entity's cover, position stability, or facade status.
Values: solid, thin, cracking, blown, N/A.
Examples:
- Ring member's assessment of a compromised colleague =
"thin"(behavior changes are visible to anyone watching) - Smuggler's self-assessment near the detective =
"cracking"(too many close calls) - Detective's assessment of a civilian witness =
"N/A"(no cover to assess)
Usage: Tracks active deception status. When position_integrity degrades, the observer's monologue reflects increasing anxiety or strategic recalculation. N/A is used when the concept of cover/facade does not apply to the target.
moral_weight
Value format: Observer's assessment of this entity's moral involvement in the situation.
Values: innocent, peripheral, complicit, compromised, willing.
Examples:
- Detective's assessment of an uninvolved partner =
"innocent"(caught in the fallout) - Smuggler's assessment of a debt-dragged peripheral =
"compromised"(dragged in by debt, not by choice) - Smuggler's assessment of the compromised inspector =
"willing"(fully aware, actively participating)
Usage: Shapes the observer's moral arc and internal conflict. Monologue tone shifts when the observer assigns moral weight — guilt, justification, and rationalization are driven by this key. The Comfort-Doubt-Reckoning-Compromise arc references moral_weight to determine which phase the observer occupies relative to specific NPCs.
Archetype note: Both smuggler and detective use moral_weight with the same enum values but different criteria. The smuggler assesses complicity in the ring's operations ("how dirty are their hands?") — see Smuggler Moral Arc. The detective assesses culpability in the crime ("how responsible are they for the harm?"). Same scale, different lens. A smuggler might mark a debt-driven peripheral as "compromised" (dragged in, not choosing) while the detective marks the same NPC as "complicit" (they knew and did nothing).
Archetype Interpretation Note
Role-perspective keys use the same value enums across all archetypes, but their meaning is archetype-dependent. The schema stores data; the content layer provides interpretation.
| Key | Smuggler reads as... | Detective reads as... |
|---|---|---|
risk_assessment |
Operational risk to the ring (exposure, interdiction, betrayal) | Threat to public safety or obstruction of the investigation |
loyalty_assessment |
Loyalty to the smuggling operation | Cooperation with the investigation or institutional loyalty |
position_integrity |
Cover identity stability (can I still pass as legitimate?) | Authority and credibility (does my badge still open doors here?) |
moral_weight |
Complicity in the ring's operations (how dirty are their hands?) | Culpability in the crime (how responsible are they?) |
This pattern scales to all 8 planned archetypes without schema modification. An administrator reads risk_assessment as political liability; a newcomer reads it as personal vulnerability; a merchant reads it as commercial exposure. Same keys, same enums, different meaning — the archetype brief and voice register provide the lens.
Examples: Full EntityKnowledge Entries
Smuggler's knowledge of {THE FRIEND} (early game, pre-contradiction)
EntityKnowledge {
last_known_position: Some(TilePosition { x: 45, y: 12 }), // logistics hub dock
last_observed_tick: 1200,
last_updated_tick: 1200,
confidence: KnowledgeConfidence::Direct, // currently in LOS
source: KnowledgeSource::DirectObservation { tick: 1200 },
state: KnowledgeState::Active,
relationship: RelationshipState::Friendly,
known_attributes: BTreeMap::from([
("name".to_string(), "{NPC Full Name}".to_string()),
("role".to_string(), "dock worker".to_string()),
("faction".to_string(), "ring member".to_string()), // smuggler knows
("species".to_string(), "human".to_string()),
("relationship_type".to_string(), "close colleague".to_string()),
("trust_level".to_string(), "trusted".to_string()),
("routine_pattern".to_string(), "morning shift 06:00-14:00, bar after shift".to_string()),
("behavior_flags".to_string(), "reliable,punctual".to_string()),
("risk_assessment".to_string(), "low".to_string()), // reliable, no exposure concern
("loyalty_assessment".to_string(), "solid".to_string()), // loyal to the ring
("position_integrity".to_string(), "solid".to_string()), // cover is intact
("moral_weight".to_string(), "willing".to_string()), // fully aware, active participant
]),
}
Detective's knowledge of a suspect (mid-game, post-contradiction discovery)
EntityKnowledge {
last_known_position: Some(TilePosition { x: 45, y: 12 }),
last_observed_tick: 2400,
last_updated_tick: 2600, // updated via inference after observation
confidence: KnowledgeConfidence::KnowsDetails, // not currently in LOS, but detailed knowledge
source: KnowledgeSource::Inferred {
basis: vec![
FactId("investigation.manifest_discrepancy".to_string()),
FactId("investigation.shift_mismatch".to_string()),
],
},
state: KnowledgeState::Active,
relationship: RelationshipState::PersonOfInterest, // flagged
known_attributes: BTreeMap::from([
("name".to_string(), "{NPC Full Name}".to_string()),
("role".to_string(), "dock worker".to_string()),
("faction".to_string(), "civilian".to_string()), // detective doesn't know ring membership yet
("species".to_string(), "human".to_string()),
("relationship_type".to_string(), "suspect".to_string()),
("trust_level".to_string(), "suspicious".to_string()),
("routine_pattern".to_string(), "morning shift, frequent late departures".to_string()),
("behavior_flags".to_string(), "nervous,lattice_checking,evasive".to_string()),
("tell_observed".to_string(), "looks left when lying".to_string()),
("leverage_held".to_string(), "unauthorized meetings".to_string()),
("leverage_confidence".to_string(), "confirmed".to_string()),
("contradiction_flagged".to_string(), "meeting_unknown_contact".to_string()),
("risk_assessment".to_string(), "high".to_string()), // obstructing investigation, potential flight risk
("loyalty_assessment".to_string(), "hostile".to_string()), // uncooperative, evasive
("position_integrity".to_string(), "cracking".to_string()), // their story is falling apart
("moral_weight".to_string(), "complicit".to_string()), // involved, degree of responsibility unclear
]),
}
Smuggler's knowledge of the institutional contact (if observant, mid-game)
EntityKnowledge {
last_known_position: Some(TilePosition { x: 78, y: 34 }), // bar
last_observed_tick: 1800,
last_updated_tick: 2100,
confidence: KnowledgeConfidence::KnowsOf,
source: KnowledgeSource::DirectObservation { tick: 1800 },
state: KnowledgeState::Active,
relationship: RelationshipState::Known, // not Friendly -- smuggler doesn't know them well
known_attributes: BTreeMap::from([
("name".to_string(), "{Institutional Contact Name}".to_string()),
("role".to_string(), "Commission field tech".to_string()),
("faction".to_string(), "Commission".to_string()),
("species".to_string(), "human".to_string()),
("relationship_type".to_string(), "institutional presence".to_string()),
("trust_level".to_string(), "uncertain".to_string()),
("routine_pattern".to_string(), "bar regular, off-duty evenings".to_string()),
("behavior_flags".to_string(), "observant,avoidance_pattern".to_string()),
("leverage_held".to_string(), "knows something about the inspector or someone close to the ring".to_string()),
("leverage_confidence".to_string(), "suspected".to_string()),
("risk_assessment".to_string(), "moderate".to_string()), // Commission presence is inherently risky
("loyalty_assessment".to_string(), "uncertain".to_string()), // unknown allegiance
("position_integrity".to_string(), "N/A".to_string()), // not maintaining a cover
("moral_weight".to_string(), "peripheral".to_string()), // not involved in ring, but near it
]),
}
Attribute Lifecycle
- Initial state (Unknown NPC): No attributes. EntityKnowledge doesn't exist yet.
- First observation (Direct confidence):
name(if visible via lattice query or introduction),role(inferred from context),species,routine_patternstarts accumulating. Initialrisk_assessmentandmoral_weightmay be set to defaults (noneandinnocentrespectively) or left absent until the observer forms an opinion. - Repeated interaction (KnowsOf):
relationship_typeevolves,trust_levelset,behavior_flagsaccumulate.loyalty_assessmentandposition_integritybecome meaningful as the observer builds a model of the target's reliability and stability. - Investigation depth (KnowsDetails):
leverage_held,tell_observed,contradiction_flaggedadded by detective. Smuggler gainsfactionclarity. Role-perspective keys (risk_assessment,loyalty_assessment,position_integrity,moral_weight) shift as the observer's understanding deepens — a target initially assessed asrisk_assessment: "low"may escalate to"high"after contradictions surface. - Knowledge decay: Attributes persist even as confidence decays. A KnowsOf entry retains all attributes; only positional certainty degrades. Role-perspective assessments are particularly sticky — once the observer has formed a judgment, it takes strong counter-evidence to revise it.
Content Authoring Guidance
- Monologue prerequisites can reference specific attributes:
known_attributes["leverage_held"] == "ring membership". - Dialogue access tiers can check
factionorrelationship_type. - Tell system observations append to
behavior_flagsas comma-separated values. - THE FRIEND contradiction is triggered by setting
contradiction_flagged. - Role-perspective prerequisites can gate monologue lines by assessment:
known_attributes["risk_assessment"] == "high"triggers different monologue pools depending on the observer's archetype (smuggler hears operational caution; detective hears investigative urgency). - Leverage-based confrontation uses
leverage_held+leverage_confidencetogether: a "suspected" leverage allows indirect probing dialogue, while "confirmed" leverage enables direct confrontation or exploitation moves. - Moral arc tracking uses
moral_weightto determine which phase of the observer's arc (Comfort/Doubt/Reckoning/Compromise) is active relative to each NPC.
Total canonical keys: 16 (4 identity, 2 social, 2 behavioral, 2 leverage, 2 investigation, 4 role-perspective). The 12 shared keys cover entity facts and relational dynamics; the 4 role-perspective keys capture the observer's subjective assessment. This schema scales to all 8 planned archetypes without modification — interpretation lives in content (monologue pools, voice registers, archetype briefs), not in the key definitions.