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 existingModifierBindingStoreand decay machinery inlegacy/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.secsdeclares the seven condition Modifier templates;legacy/v1/examples/valenar/Content/characters/injuries/modifiers.secsdeclares the five injury Modifier templates;legacy/v1/examples/valenar/Content/common/tags.secsadds theConditionandInjurytag 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 Hungerfield 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 ingd-glossary.mdas 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
./ad-0003-story-content-layer-and-agent-audit.md./ad-0013-scope-state-channel-template-archetype-pattern.md../../systems/gd-characters-and-retinue.md../../systems/gd-state-axes-and-thresholds.md../../systems/gd-dynamic-quest-pressure-model.md.claude/rules/secs-concepts.md(the Modifier primitive).claude/rules/valenar-contract-backing.md