Skip to main content

ADR 0012 — Character Conditions and Injuries as Modifier Templates

Context

../../systems/gd-characters-and-retinue.md commits the named-character layer's three-way split of long-term identity (traits), temporary mutable state (conditions), and mutable medical or physical harm (injuries). The doc names seven conditions (Hungry, Cold, Exhausted, Afraid, Bleeding, Tainted, Poisoned) and five injuries (Wounded, Sprained Ankle, Burned Hands, Broken Rib, Concussion). The split is committed as the canonical state-vocabulary shape for named characters, but no doc owns the catalog itself, the state-axis ladder for each named condition, or the runtime backing shape. gd-canon.md already commits the rule that conditions describe mutable state while traits describe identity, matching the CK3 Modifier-vs-Trait split.

The Foundation Hardening needs the catalog plus the runtime backing shape committed for two reasons. First, the Wave 3a state-machine row for the people-retinue cross-act lane needs to bind to a typed condition surface (so a mission generator can read "the character is currently Cold and Exhausted" as a state predicate). Second, the dynamic quest pressure generator already lists "named-character state" as one of its world-state inputs per ../../systems/gd-dynamic-quest-pressure-model.md; without a catalog and a runtime shape, the generator has no typed target to bind against.

Two patterns were viable. The first was to extend ../../systems/gd-characters-and-retinue.md with the catalog inline. Rejected because that doc owns the taxonomy boundary (people, traits, conditions, injuries, assignments, retinue); the catalog and state-axis ladder are a sibling concern, and the existing state-axes precedent in ../../systems/gd-state-axes-and-thresholds.md already lives as its own owner doc. The second was to author a Modifier-only catalog with no doc, declaring conditions purely as Modifier templates in .secs source. Rejected because the seven conditions need ladder semantics (e.g., Hunger: well-fed -> satisfied -> peckish -> hungry -> starving), and a Modifier-only catalog has no canonical home for the state-axis ladder definitions.

The story-prose condition diaries already authored under ../../story/accepted/README.md (the act0-day1 condition-pre-blackout and condition-post-blackout entries) are symptom records of the MC's first-day physical state. Those entries are narrative artifacts, not catalog rows; they remain non-canon by default per ./ad-0003-story-content-layer-and-agent-audit.md and must not be promoted to runtime backing rows by this ADR.

Decision

The committed shape is a hybrid: a new owner doc plus a Modifier template runtime backing.

A new owner doc gd-character-conditions-and-injuries.md is authored in Wave 3a under examples/valenar/docs/systems/. The doc owns the catalog (the seven conditions and the five injuries already named in ../../systems/gd-characters-and-retinue.md), the state-axis ladder for each named condition, the apply / tend / clear binding rules, and the cross-references to traits (for acquired trait conversion when a condition becomes a lasting scar) and to the existing state-axes doc. The state-axis ladder for each condition is authoritative text in the doc, not a runtime enum.

Runtime backing for each condition is a Modifier template per the existing SECS Modifier primitive (per .claude/rules/secs-concepts.md). Each Modifier template declares stacking = unique (a character has one Hungry Modifier at a time, not seven stacked Hungry Modifiers), reapply = refresh (re-applying a Hungry Modifier refreshes its duration rather than stacking a second one), and an optional decay = linear (so Hungry decays toward well-fed over time when the character eats — the exact decay rate is a balance pass deferred to Wave 5a). Each condition Modifier targets the MainCharacter, retinue character, companion, or specialist scope row by the existing SECS bind / attach mechanism.

A new Condition tag value is added to legacy/v1/examples/valenar/Content/common/tags.secs. The Condition tag marks Modifier templates that participate in the condition catalog; it is the bind-time predicate that lets policies, systems, and the dynamic quest pressure generator query "all conditions currently attached to this character" without enumerating condition names. Tag values follow the existing tag-system pattern and are not a new SECS keyword.

Injuries are a sibling concern with the same shape: each injury is a Modifier template, the Injury tag value is added to Content/common/tags.secs, but injuries do NOT carry decay = linear by default. Injuries persist until cleared by an explicit heal / tend / cure mechanic (authored in Wave 5a as part of the retinue / camp care system). Acquired traits (lasting scars) are the trait-tier conversion target when an injury heals with a permanent mark; that conversion is authored in the Wave 3a owner doc as the binding rule between the injury Modifier template and the acquired trait.

No ConditionState enum is introduced by this ADR. The state-axis ladder for each condition lives as authoritative text in the Wave 3a owner doc; runtime carries each condition as Modifier presence / intensity (the Modifier's effect bundle expresses the ladder rung's mechanical consequences). A future wave may promote the ladder labels to a typed enum if the runtime needs to read them as state predicates; that promotion is not required at this ADR's acceptance time.

The catalog seeded by this ADR is exactly the seven conditions and the five injuries already named in ../../systems/gd-characters-and-retinue.md:

  • Conditions: Hungry, Cold, Exhausted, Afraid, Bleeding, Tainted, Poisoned.
  • Injuries: Wounded, Sprained Ankle, Burned Hands, Broken Rib, Concussion.

No additional condition names or injury names are invented by this ADR. The Wave 3a owner doc may extend the catalog only as a separate authorial decision recorded in that doc; this ADR commits the seeded list.

Scope is limited to named characters: MainCharacter, retinue, companions, specialists, and later named governors / commanders. Pop-level condition modeling (whether ordinary settlement-job pops carry per-pop conditions) is deferred. The Modifier-template shape is compatible with later pop extension if a future wave commits it, but this ADR does not authorize pop conditions.

Story-prose condition diaries under ../../story/accepted/README.md remain story prose and do not gain runtime backing rows from this ADR. They are symptom records of the MC's first-day state, not catalog entries.

Alternatives Considered

Extend ../../systems/gd-characters-and-retinue.md inline with the catalog and the state-axis ladder. Rejected because the retinue doc owns the people / traits / conditions / injuries / assignments / retinue taxonomy boundary. The catalog and the state-axis ladder are a sibling concern that mirrors the existing state-axes precedent: separate owner doc, cross-referenced from the taxonomy doc. Folding the catalog into the taxonomy doc would overload the taxonomy doc with content it does not own.

Modifier-only catalog with no doc (declare conditions purely as Modifier templates in .secs source under Content/characters/conditions/). Rejected because the seven conditions need ladder semantics (Hunger has five ladder rungs, Cold has five rungs, Tainted has progressive corruption rungs) and a Modifier-only catalog has no canonical home for the state-axis ladder definitions. The doc-plus-Modifier hybrid keeps the ladder authoritative in the owner doc and the runtime mechanics in the Modifier templates.

Tenets Applied

  • "Templates are data." Conditions are Modifier templates (data), not hand-written host code that mutates character state. The catalog and the ladder are authored content; the runtime is the existing SECS Modifier primitive.
  • "AAA = COMPLETE correct algorithm." The AAA precedent (RimWorld Hediff with per-condition severity, tend state, immunity progression; CK3 Modifiers vs Traits split — conditions describe mutable state, traits describe identity; Dwarf Fortress hunger / thirst / sleep with named-threshold rings) commits to a typed per-condition entity with named ladder rungs; this ADR commits Valenar to the same complete shape via Modifier templates plus authoritative ladder text.
  • "Future-proofed solutions only." The Modifier-template shape leaves room for later condition mechanics (immunity progression, tend rates, decay curves) without reshape. The Condition / Injury tag values give policies and systems a clean query surface.
  • "No backwards compatibility." Neither the catalog-in-retinue-doc fallback nor the Modifier-only fallback is preserved. The owner doc plus Modifier-template runtime is the committed shape.

Runtime Backing Status

Status at this ADR's acceptance time: contract-only.

  • Host/runtime owner: existing Modifier infrastructure in legacy/v1/examples/valenar/Host/ and the existing ModifierBindingStore and decay machinery in legacy/v1/src/SECS.Engine/ carry condition and injury Modifiers with no new host primitives. Apply / tend / clear actions are authored as Activities in Wave 5a.
  • Generated/.secs owner: legacy/v1/examples/valenar/Content/characters/conditions/modifiers.secs declares the seven condition Modifier templates; legacy/v1/examples/valenar/Content/characters/injuries/modifiers.secs declares the five injury Modifier templates; legacy/v1/examples/valenar/Content/common/tags.secs adds the Condition and Injury tag values. Matched by the parallel Generated stand-in tree.
  • Read-model/UI owner: the character info panel in the Valenar React client surfaces the character's currently attached condition Modifiers and injury Modifiers, with the state-axis ladder rung read from the Modifier's effect bundle.
  • Tests: deferred to the Wave 5a runtime-backing wave.
  • Known gaps: the exact ladder-rung thresholds for each condition (balance pass); the exact decay rates for the conditions that carry decay = linear; the apply / tend / clear Activity bodies; the acquired-trait conversion binding rule when an injury heals with a permanent mark.
  • Illegal fallback behavior: no per-character int Hunger field outside the Modifier mechanism may stand in for the Hungry Modifier. No tag-only "is hungry" predicate may stand in for the Modifier-presence query. No story-prose condition diary may be treated as a runtime backing row.
  • Next closure wave: Wave 5a runtime-backing wave authors the Modifier templates, the tag values, the apply / tend / clear Activities, and the matching Generated stand-ins. The Wave 3a owner doc authoring is a parallel track.

Glossary Propagation

The following terms MUST land in gd-glossary.md, gd-canon.md, and README.md:

  • MCCondition (umbrella term for the condition + injury surface on named characters).
  • Condition (tag value marking condition Modifier templates).
  • Injury (tag value marking injury Modifier templates).
  • "Condition state-axis ladder" (concept term referencing the ladder authored in the Wave 3a owner doc).
  • The seven named conditions (Hungry, Cold, Exhausted, Afraid, Bleeding, Tainted, Poisoned) and the five named injuries (Wounded, Sprained Ankle, Burned Hands, Broken Rib, Concussion) — already named in ../../systems/gd-characters-and-retinue.md; must appear in gd-glossary.md as cross-references to the forthcoming Wave 3a owner doc rather than as duplicate definitions.

Consequences

For ../../systems/gd-characters-and-retinue.md, the existing condition and injury bullet lists remain the taxonomy authority; the Wave 3a owner doc adds the catalog detail, the state-axis ladder, and the apply / tend / clear binding rules. The retinue doc gains a cross-reference to the new owner doc when Wave 3a authors it.

For ../../systems/gd-dynamic-quest-pressure-model.md, the "named-character state" world-state input gains a typed query surface: the generator may read "this character has Tainted attached with intensity X" via the Modifier-presence query, and may emit mission candidates bound to specific condition / injury states.

For ./ad-0013-scope-state-channel-template-archetype-pattern.md (the cross-cutting pattern ADR), this ADR is explicitly NOT an instantiation of the scope-state-channel + template-archetype pattern. Conditions are Modifiers (transient effect bundles), not scopes (persistent identities). The shape difference is intentional and documented in ad-0013.

For story-prose condition diaries under ../../story/accepted/README.md, the existing non-canon-by-default rule from ./ad-0003-story-content-layer-and-agent-audit.md holds. The diaries are symptom records, not catalog rows; the canon catalog is owned by the Wave 3a owner doc and the runtime backing is the Modifier templates committed here.

For the verifier, any future doc that introduces a condition or injury name outside the seeded twelve (seven conditions plus five injuries) without an explicit authorial extension in the Wave 3a owner doc is a tenet FAIL. Any doc that introduces a parallel per-character int field for a condition outside the Modifier mechanism is also a FAIL. Pop-level condition modeling requires superseding this ADR (or a parallel ADR that explicitly authorizes the pop extension).

References