v0.3 Gap Survey: Two-Way Hysteresis and NPC Action Verbs¶
Document File ID: docs/research/23-v03-hysteresis-and-action-verbs.md Date: 2026-08-26
TL;DR¶
- Part A: no literature — not CK, not Dwarf Fortress, not RimWorld,
not academic opinion-dynamics — publishes a two-way hysteresis model
for social/relationship state with the specific rigor Chronicle needs.
What does exist, robustly, is the generic control-theory pattern
(separate rising/falling thresholds around a dead band) and one directly
transferable game precedent with real published numbers: RimWorld's
mental-break severity ladder, which additionally demonstrates the
refractory-period technique Chronicle should combine with a dead band.
Recommendation: build Chronicle's de-escalation as a Schmitt-trigger
dead band (escalate at severity ≥ 0.6, de-escalate only once decayed
severity ≤ 0.3) plus an explicit tick-count dwell requirement before
the down-transition fires, modeled directly on
GrudgeDecayRule's existinggrudge_at/grudge_cooledread-path — no new literature numbers are load-bearing here, the structure is what's borrowed. - Part B: Skyrim's real, well-trodden action surface for
state-driven NPC behavior beyond scheduling is narrower than it looks
from outside — three mechanisms are genuinely load-bearing (dialogue
conditions incl.
GetRelationshipRank, AI package conditions on a global/quest variable, and vendor gold/price hooks), and everything richer (arrest, extortion, debt, brawls, combat initiation) that ships today is SkyrimNet/SeverActions LLM-conversation-triggered, not external-state-triggered, which is precisely the seam Chronicle would have to build itself if it wants one. Top 3 recommendation: (1) dialogue-gating onGetRelationshipRank[already targeted bychronicle-bridge-hydration-out.md], (2) grudge-driven vendor price/refuse-to-trade via barter script overrides, (3) an AI package condition keyed to a Chronicle-owned global (avoid/hostile-greettier) — ranked in that order by mechanism safety, not by drama value.
Part A — Two-way hysteresis / bidirectional threshold models¶
Findings¶
- [RISK] No source — academic or game — publishes a two-way hysteresis
model purpose-built for social/relationship state. Direct queries
against bounded-confidence and Hegselmann-Krause opinion-dynamics
literature (survey: Bounded confidence opinion dynamics: A survey,
ScienceDirect 2023, https://www.sciencedirect.com/science/article/pii/S0005109823004661;
Opinion Dynamics Model with Bounded Confidence and the Sleeper Effect,
PMC 2022, https://pmc.ncbi.nlm.nih.gov/articles/PMC9256380/) return
models that are single-threshold and symmetric — the confidence
bound determines whether opinions attract at all, and clustering
outcomes are studied as an emergent property of that one threshold,
not as an authored promotion/demotion pair for discrete relationship
categories. Dwarf Fortress's own grudge-formation mechanics are
publicly under-documented even for the forming half (DF Wiki:
Grudge, https://dwarffortresswiki.org/index.php/DF2014:Grudge —
"It is currently unknown how a grudge is formed" is the wiki's own
caveat) so it cannot be cited for a de-escalation half either. This
repo's own
ck-opinion-decay-and-threshold-tables.mdalready establishes CK2/CK3 opinion decay is continuous and one-directional (modifiers expire or decay to zero; there is no separate, harder threshold to re-cross before a relation category like Rival reverts) — that finding is confirmed, not contradicted, by anything found in this pass. - [BUILD-ON] The control-theory pattern itself is exactly what
Chronicle needs, and it is thoroughly documented outside game
contexts. A Schmitt trigger uses two thresholds around a dead band:
crossing the upper threshold flips the output high, and only
crossing the separate, lower threshold flips it back — the region
between the two thresholds is "memory," the system's state depends on
history, not just the current value (GeeksforGeeks, Schmitt Trigger,
https://www.geeksforgeeks.org/electronics-engineering/schmitt-trigger/;
general description of the up/down asymmetric-threshold pattern,
Northumbria game-AI teaching material on hysteresis in FSMs,
https://research.ncl.ac.uk/game/mastersdegree/gametechnologies/aitutorials/1state-basedai/AI%20-%20State%20Machines.pdf).
The game-AI framing of this pattern is stated with an example directly
usable as a template: "trigger the transition from state A to state B
when a value becomes higher than 11m/s, and trigger the reverse
transition when it becomes lower than 9m/s" — the same source names
the resulting untouched interval a "dead band" where no switching
occurs. This is structurally identical to what Chronicle needs for
AccumulationThresholdRule/grudge severity: an up threshold that forms/escalates, and a distinct, lower down threshold that demotes/dissolves, with no oscillation possible for any value inside the gap. - [BUILD-ON] Ecological "alternative stable states" literature adds one structural nuance worth keeping: hysteresis loops in real systems are frequently not symmetric, and reversal can require sustained time below threshold, not just an instantaneous crossing. "Once the system has crossed a tipping point, decreasing stress to the original value does not restore the system to the original state" — reversal needs either overshooting further past the original threshold, or dwelling there (Rethinking tipping points in spatial ecosystems, arXiv 2306.13571, https://arxiv.org/pdf/2306.13571; Alternative Stable States, Nature Scitable, https://www.nature.com/scitable/knowledge/library/alternative-stable-states-78274277/). Applied to Chronicle: a single tick where decayed severity dips below the down-threshold should not be sufficient by itself to demote a Grudge — the literature's own qualitative lesson is that reversal needs persistence, not just a momentary crossing, which is exactly the "stays there for N ticks" framing this task's own prompt already guessed at. No source gives a numeric N; it is a tuning parameter, not a literature-derived constant.
- [BUILD-ON] RimWorld's mental-break severity ladder is the single
most directly transferable prior-art number set found, and it
combines a graded threshold ladder with an explicit refractory
period — worth borrowing the shape, not the numbers. RimWorld
computes three severity thresholds off one base stat: Minor at the
full Mental Break Threshold value, Major at 4/7 of it, Extreme at 1/7
of it (RimWorld Wiki, Mental Break Threshold,
https://rimworldwiki.com/wiki/Mental_Break_Threshold), each mapped to
a named mood band (Stressed/On edge/About to break) and a
mean-time-to-break roll rather than an instant trigger (RimWorld Wiki,
Mental break, https://rimworldwiki.com/wiki/Mental_break). Critically,
once a break fires, mood is forcibly reset upward and breaks cannot
re-fire immediately — this is a refractory/cooldown mechanism, a
different but complementary technique to a dead band: it prevents
oscillation not by requiring a harder threshold to reverse, but by
making the up-transition itself temporarily un-repeatable. Chronicle's
own
ScheduleWriteBackRule/already_mourning-latch pattern already uses exactly this refractory idea for the escalation side (a log-derived latch prevents re-firing on repeat corroboration) — the new piece this report adds is applying the same latch discipline to a de-escalation direction that doesn't exist yet. - [RISK] The Sims' relationship decay is a useful negative example:
its "bidirectional" behavior is between-Sims asymmetry (satisfaction
can rise for one party while falling for the other), not a
category-level up/down hysteresis mechanism (Relationship
Satisfaction, Waffle's Mix-Ins via Patreon,
https://www.patreon.com/posts/relationship-it-116122721) — decay rate
differs by current relationship tier (low tiers decay first; "good
friends and soulmates decay very slowly") but there is no separate,
harder threshold gating a category downgrade the way this task
requires; a Sim's tier simply tracks the underlying scalar continuously.
Not usable as structural precedent beyond confirming, again, that
tier-decay-rate-by-level (slower decay once a relationship is
"crystallized," matching this project's
Relationshipcategory idea) is a reasonable design knob independent of hysteresis itself.
Recommendation — structural, for Chronicle's Grudge/Relationship machinery¶
No literature gives Chronicle numbers to import wholesale; what it gives is a validated shape. Structurally:
- Escalation threshold (existing):
AccumulationThresholdRule'scount >= thresholdandPairwiseEncounterWeightingRule'sseverity >= thresholdstay as-is — this is the Schmitt trigger's "rising" edge and is already correctly a one-shot latch pattern (GrudgeCreationRule'salready_existsgate,ScheduleWriteBackRule'salready_mourninggate). - De-escalation threshold (new): introduce a distinct, strictly
lower threshold (e.g.
AVOIDANCE_GRUDGE_THRESHOLDfor up, a newAVOIDANCE_GRUDGE_COOL_THRESHOLDfor down, with a documented gap — the dead band — between them) evaluated against the samesocial.grudge_at(...).severityread-pathGrudgeDecayRulealready wraps, never a new store field. - Dwell requirement, not instantaneous crossing: per the
alternative-stable-states literature's qualitative lesson, gate the
down-transition on the decayed severity having stayed at or below the
cool threshold for N consecutive evaluation ticks (or since a
recorded
gametswatermark) — mirroring howgrudge_cooled()already evaluates a decay function at a point in time, this just needs the previous evaluation's result carried forward by the driver (a caller-assembled boolean, per the T2.3 lesson every existing rule in this file follows — never a new store query inside the rule itself). N is a tuning constant, not a literature-derived one; this report does not recommend a specific value. - Category demotion mirrors
RoleVacancySuccessionRule's "fired = the effect" convention: aRelationshipDemotionRule(or extendingGrudgeDecayRule's tier) should fire only when a Relationship's category (not just its scalar) needs to step down one level, using the same log-derived latch idiom rules 12/15/17/18/19 already establish, so disabling it is a real behavioral toggle, not instrumentation-only.
This is the same rule-family idiom the ladder already uses end to end — the addition is one new threshold constant and one new caller-tracked dwell counter, not a new subsystem.
Part B — NPC action-verb inventory for Skyrim¶
Findings¶
- [BUILD-ON] Dialogue-gating on
GetRelationshipRankis a well-trodden, first-class vanilla mechanism — not something Chronicle would be pioneering.GetRelationshipRankis a native dialogue/ quest condition function usable directly in Creation Kit condition trees (e.g.GetRelationshipRank == -1for Rival-gated lines); modders already map story/quest-reward outcomes onto it viaSetRelationshipRank/player.setrelationshiprank(UESP, Skyrim:Disposition, https://en.uesp.net/wiki/Skyrim:Disposition; community modding-help thread confirming the condition-tree usage, https://forums.nexusmods.com/topic/13497763-need-help-with-applying-condition-to-dialogue/). This directly corroborates and narrows the scope ofdocs/design/chronicle-bridge-hydration-out.md's own claim (line ~93) that vanillaGetRelationshipRankconditions already make relationship rank changes dialogue-observable with zero new dialogue authoring — this pass finds no reason to doubt that claim and treats it as confirmed, not merely asserted. - [RISK] AI package conditions driven by a custom global/quest
variable are architecturally real but not independently documented
with the depth this task hoped for. Web search surfaced only
generic Creation Kit package-condition mechanics (package conditions
gate execution; formlists can override Spectator/Combat AI —
Beyond Skyrim's Arcane University: World Interactions,
https://wiki.beyondskyrim.org/wiki/Arcane_University:World_Interactions)
rather than a named mod demonstrating "package driven by an
externally-written global at runtime" end to end. This is the same
mechanism rule 18's avoidance override already uses in practice
(
schedule.sample_encounters'spair_thresholds, perPairwiseEncounterWeightingRule's own docstring) — the finding here is that this repo's own rule 18 is closer to the state of the art than anything independently documented elsewhere; there is no richer published pattern to build on beyond what Chronicle already ships. - [RISK] Vendor price/refuse-to-trade driven by reputation is not a
shipped, documented mod pattern either — search for exactly this
(faction-reputation-gated barter refusal/markup) returned only
generic trade-expansion mods (Trade and Barter, rewarding positive
relationships with better prices via favors — Nexus Mods,
https://www.nexusmods.com/skyrim/mods/34612) with no example of the
negative/refusal direction. The underlying mechanism is real and
already surveyed in this repo's own economy research (report 16/18:
vendor gold caps,
fBarterBuyMinprice floor, barter-menu-open population timing) — the gap is that nobody has wired grudge/ reputation state into it, which is an opportunity, not a wall. - [BUILD-ON] The richest documented "NPC action verb" inventory in the
entire Skyrim modding ecosystem is SkyrimNet's Action Repository plus
the community SeverActions plugin, and it is conversation-triggered,
not external-state-triggered — a distinction load-bearing for this
task. The Action Repository documents actions including
SellItem,CreateDebt/CollectPayment/CreateRecurringDebt,ExtortGold,DeclineBrawl/ChallengeBrawl,RejectPersuasion,RejectEscortPlea,AdjustRelationship(a numeric -15..+15 rapport/ trust/loyalty delta),SetCompanion,TransferOwnership,AttackTarget, andThugAttack(SkyrimNet Action Repository, https://goncalo22.github.io/SkyrimNet-GamePlugin/action-repository/; SeverActions README, 38-71 actions spanning combat/crafting/arrest/ follower systems, https://github.com/Severause/SeverActions). Every one of these is invoked from the LLM's evaluation of live conversation context (the docs frameThugAttackfiring "only after player refusal or hostile action," i.e. dialogue-turn-gated), not from an external simulation tick pushing a global/quest variable the way Chronicle's own architecture requires (per this repo's own 03/19/20's hybrid-neurosymbolic doctrine: symbolic state must govern engine logic, LLM output restricted to framing/dispatch). The actions themselves are a legitimate inventory of "verbs Skyrim NPCs can be made to perform" —ExtortGold,DeclineBrawl/ChallengeBrawl,AdjustRelationship,AttackTarget, and vendor/debt actions are all proof that the underlying Papyrus/SKSE hooks exist and work in production — but the trigger wiring (LLM-conversation vs. Chronicle-simulation-state) would have to be built fresh; nothing here is a drop-in adapter for Chronicle's own state. - [RISK] No independent "nemesis"/persistent-grudge combat-initiation
mod was found with published mechanics detail beyond gimmick mods.
"Nemesis Combat AI" and "Shadow of Skyrim" exist (Nexus Mods,
https://www.nexusmods.com/skyrimspecialedition/mods/185305; PC Gamer
coverage, https://www.pcgamer.com/this-skyrim-mod-recreates-the-best-part-of-shadow-of-mordor-the-nemesis-system/)
but both are single-encounter/randomized promotion systems (whoever
defeats the player becomes their nemesis) with no state feed from an
external reputation/grudge system, and neither publishes a
threshold model.
AttackTargetfrom SkyrimNet's Action Repository remains the only documented, general-purpose "make this specific NPC hostile toward this specific actor" primitive found.
Recommendation — ranked, buildable NPC action verbs for Chronicle¶
Ranked by (a) mechanism safety/well-trodden-ness and (b) fit to state
Chronicle already computes (Reputation, Grudge.severity,
Relationship category):
- Dialogue-gating on
GetRelationshipRank. Safest possible mechanism — pure vanilla condition-tree usage, zero new Papyrus, already the explicit target ofdocs/design/chronicle-bridge-hydration-out.md. Directly matches Chronicle'sRelationshiprank output with no translation layer. Effort is entirely on the authoring side (new dialogue topics/lines conditioned on rank), not the engine-hook side. - Grudge-severity-driven vendor refusal/price markup. Mechanism is
well-trodden in the abstract (barter price calc and vendor gold caps
are documented engine hooks per this repo's own reports 16/18) even
though no existing mod wires reputation into it — this is a real gap
Chronicle could fill first. Matches
Grudge.severityalmost exactly as-is (same scalar rule 18 already reads for avoidance), and is strictly lower-risk than combat or faction actions since it never changes hostility state, only a barter-menu price calculation. - AI-package-condition override keyed to a Chronicle-owned global
variable, for a small hostile-greeting/refuse-to-interact tier below
full avoidance. Same mechanism family as rule 18's existing
avoidance override (
pair_thresholds) — extending, not inventing. MatchesGrudge.severityat a lower threshold than avoidance (a "cold shoulder" tier between neutral and active avoidance), which also gives the two-way-hysteresis machinery from Part A a second, independently-observable behavioral rung to demonstrate de-escalation against. - (Lower priority, higher payoff if built)
AttackTarget-style nemesis combat initiation, state-triggered rather than conversation-triggered. The primitive is proven in production (SkyrimNet'sAttackTarget) but every existing use is LLM-turn- triggered; Chronicle would have to build the state→trigger wiring itself (a Chronicle-simulation tick deciding "grudge severity crossed a combat-initiation threshold" and dispatching an SKSE call) with no prior-art adapter to lean on. Higher drama value than 1-3, but real new engineering, and carries the same off-screen-actor-can't-act absence-of-execution wall already documented in report 17 — only viable for loaded/nearby actors. - (Not recommended near-term) Faction join/refusal driven by reputation. No prior art at all was found for state-driven faction admission/refusal; Story Manager involvement (per report 19/20's own "bypass the Story Manager for anything macro" lesson) makes this the riskiest of the five candidates. Held out of the ranked top-3 deliberately.
Caveats¶
- This is a single web-research pass (no independent second pass or source-code read of SeverActions/SkyrimNet exists yet, matching the caveat pattern already used by reports 19/21) — treat the SkyrimNet Action Repository summary as one page's documentation, not independently verified against the plugin's own YAML/C++ source.
- Part A's literature search found what it found: an absence of purpose-built bidirectional-hysteresis social-simulation literature is itself the finding, not a search failure — three separate query angles (control theory, bounded-confidence opinion dynamics, named game systems) converged on the same gap.
- Numeric claims to flag explicitly as not transferable: RimWorld's 4/7 and 1/7 severity fractions are cited only to illustrate the "graded ladder off one base stat" shape, not as values Chronicle should adopt for its own severity thresholds.