Partnership Origins
Partnerships remember how they began. When two elves become Partners, the stages they passed through on the way — a teaching bond, a shared grief, a rivalry that softened — are stamped onto the partnership as origin markers. Markers are identity, not numerical bonuses. They don't inflate strength. They make partnerships that grew through specific hardships harder to break, and they tell a visible story about what the bond is made of.
Overview
Every Partner relationship carries an origin_history: an append-only list of (origin, day) entries recorded in the order they happened. Three triggers currently write origins:
- Apprenticeship — the pair graduated a master-student bond before becoming Partners
- SharedCrisis — the pair comforted each other through grief or darkness
- RivalryToLove — the pair were once rivals (strength reached -30 or below) before becoming Partners
Non-Partner relationships do not carry origin history. When a partnership dissolves, origin_history is cleared — if the elves later re-partner, their history starts fresh.
Origins have one gameplay effect: they shift the dissolution pressure threshold. Baseline partnerships dissolve at pressure 100; partnerships with origins dissolve at 100 + delta, where the delta is computed from history with per-variant caps. When an origin-strengthened partnership survives a pressure cycle it would have dissolved under at baseline, a CulturalEvent::OriginSavedPartnership fires — the moment the mechanic becomes visible to you.
Source: crates/er-sim/src/sim/components.rs, Relationship::origin_history; crates/er-sim/src/sim/systems/origin_balance.rs.
How It Works
Origin Triggers
Each origin is written at the moment of a specific semantic event. The write is deduplicated per event, not per system firing — a single underlying event produces at most one entry even if the tick system it rides on runs many times.
| Origin | Trigger Condition | Timing |
|---|---|---|
| Apprenticeship | Master and student are already Partners at graduation, OR the pair graduated with bond_strength >= 60 (of 0--255) and later becomes Partners within 30 in-game days | Graduation tick (if already Partner) or partnership-formation tick (if pair partners later) |
| SharedCrisis | ComfortInDarkness or SharedGrief warmth catalyst fires for the pair during an active Mourning episode | Tick of the first catalyst firing within that mourning episode |
| RivalryToLove | Pair transitions to Partner AND strength_min <= -30 at the moment of transition | Partnership-formation tick |
Source: crates/er-sim/src/sim/systems/apprenticeship.rs, apprenticeship_graduation_system; crates/er-sim/src/sim/systems/romance.rs, SharedCrisis write in romance_warmth_system and RivalryToLove detector in romance_progression_system.
The strength_min Invariant
To detect RivalryToLove after the fact, every relationship tracks strength_min — the lowest strength it has ever held. Every strength change updates it via strength_min = strength_min.min(new_strength). The field is monotonic (only decreases) until dissolution, when it resets to the current strength.
Rivalry sits at strength -30 or below. If a pair's strength_min ever dipped to -30 or lower, then later climbed back up through reconciliation and courtship into partnership, the evidence of the old rivalry is still there when the partnership forms. Without strength_min there would be no way to know — the current strength at formation time is always positive.
Hard-set strength assignments (partnership formation at +80, betrayal at -50) also update strength_min inline, so dramatic swings are not missed.
Source: crates/er-sim/src/sim/components.rs, Relationship::strength_min and Relationship::adjust_strength.
SharedCrisis Deduplication
A bereaved elf enters a Mourning state for 150--300 ticks. During that time, romance_warmth_system runs every 50 ticks and may fire a ComfortInDarkness or SharedGrief catalyst between the mourner and a nearby friend — potentially 3--6 times over one bereavement.
Without deduplication, a single shared death would stamp 3--6 SharedCrisis entries onto the partnership's history, undermining the per-variant cap that prevents runaway resilience. Instead, the trigger keys on the (pair, departed_friend) tuple during the active Mourning episode: exactly one entry is written per mourning episode, regardless of how many catalyst firings occur within it. A second, later bereavement — a different departed friend — produces a second entry; repeated firings during the same bereavement do not.
This keeps the origin_history count of SharedCrisis entries equal to the number of distinct grief events the pair lived through together, not the number of times the sim ticked over during them.
Source: crates/er-sim/src/sim/systems/romance.rs, SharedCrisis dedup table keyed by mourning-instance ID.
Pending Origins
Some origins are determined before a partnership exists. When a strong apprenticeship (bond_strength >= 60) graduates without the pair being Partners yet, the trigger stores a pending entry in a resource-side table keyed by the pair's stable ID hash. If the pair later becomes Partners within 30 in-game days, the pending entry drains into the new partnership's origin_history with the formation tick. If either elf departs or dies before then, the pending entry is cleared.
Pending entries are not saved — they represent in-flight causal state, not persistent history. A save/load during the 30-day window will lose the pending entry. This is accepted: the window is short, and the mechanic does not rely on save durability.
Source: crates/er-sim/src/sim/pending_origins.rs, PendingOriginWrites resource.
Dissolution Resistance
Partnership dissolution pressure accumulates per the rules in Romance. The dissolution threshold is 100 + origin_threshold_delta, computed from origin_history:
| Origin | Per-Entry Delta |
|---|---|
| Apprenticeship | +20 |
| SharedCrisis | +15 (capped at 3 occurrences = +45 max) |
| RivalryToLove | -10 |
| Workshop, Revel, Critique, Rescue | 0 (reserved for future triggers) |
Stacking: Positive deltas sum, then clamp to a total of +50. Negative deltas (RivalryToLove) are added after the positive cap — so a rivalry bond always registers as fragility, even in a partnership stacked with Apprenticeship and SharedCrisis. Empty history gives delta 0 and the baseline threshold 100.
Worked examples:
[Apprenticeship]→ +20 → threshold 120[Apprenticeship, SharedCrisis]→ +35 → threshold 135[SharedCrisis × 5]→ +45 (cap of 3 holds) → threshold 145[Apprenticeship, SharedCrisis × 3]→ +65 → cap at +50 → threshold 150[Apprenticeship, SharedCrisis × 3, RivalryToLove]→ +50 capped, then -10 → threshold 140[RivalryToLove]alone → -10 → threshold 90 (dissolves easier than baseline)
Source: crates/er-sim/src/sim/systems/origin_balance.rs, origin_threshold_delta.
The Save Event
CulturalEvent::OriginSavedPartnership fires when romance_dissolution_system finds a partnership where:
- Accumulated pressure would cross baseline 100 (dissolution would have fired without origins), AND
- Pressure is still below the origin-adjusted threshold (dissolution does not fire with origins), AND
- The prior pressure was under 100 (this is the transition tick, not every tick afterward)
It fires once per such crossing and reads in the event feed as a brief line naming both elves and the origins that saved them: "<A> and <B>'s partnership endured — an apprenticeship once shared, a crisis weathered together". The phrasing varies per origin: SharedCrisis reads as "a crisis weathered together", RivalryToLove as "a rivalry turned to love", Apprenticeship as "an apprenticeship once shared".
This is the mechanic made visible. A silent dissolution-resistance modifier would be invisible to the player; the event surfaces the moment the bond's history actually kept it alive.
Source: crates/er-sim/src/sim/systems/romance.rs, romance_dissolution_system save-detection logic; crates/er-sim/src/sim/events.rs, CulturalEvent::OriginSavedPartnership::description.
Reading Origin History on an Elf
The Relationships screen renders each Partner's origin history as a single extra line below the partner row:
Partner Kellen [#####.....] 80
grew from apprenticeship (day 14), then shared crisis (day 47)
Format rules:
- The first entry uses grew from; subsequent entries use then, comma-separated
- Variants render as lowercase words:
Apprenticeship→ "apprenticeship",SharedCrisis→ "shared crisis",RivalryToLove→ "rivalry to love" - Day number =
tick / 100using the canonicalDAY_LENGTHconstant - Empty history → no line rendered at all (no placeholder text)
- Non-Partner relationships → no line
There is no truncation: a partnership with a dense history renders as one long line that may extend beyond the panel width. Dense histories are rare in practice; this is accepted until data shows otherwise.
Source: crates/er-sim/src/render_terminal.rs, format_origin_history_line.
Values & Formulas
Threshold Timeline Example
Two elves who graduated a strong apprenticeship together (Apprenticeship origin written at graduation, +20) and later lost a close friend together during a Mourning episode (one SharedCrisis entry written at the first catalyst firing, +15):
- Empty partnership threshold: 100
- After graduation (Apprenticeship origin): 120
- After shared bereavement (+ SharedCrisis origin): 135
If aesthetic drift later pushes dissolution pressure to 115, a baseline partnership dissolves. This one survives — pressure < 135 — and a CulturalEvent::OriginSavedPartnership fires on the tick pressure crosses 100. If pressure continues climbing to 140, the partnership eventually dissolves anyway, but it lasted meaningfully longer than an origin-less partnership under identical conditions.
Dissolution Clearing
All three partnership exit paths clear origin_history and reset strength_min on the surviving-side relationship record:
- Graceful dissolution from accumulated pressure (
romance_dissolution_system) - Satisfaction departure — partner leaves the settlement due to low satisfaction
- Farewell departure — partner dies of old age
After clearing, a surviving elf who later re-partners starts with empty history and a fresh strength_min. The new partnership's resilience is shaped only by the bond that forms next.
Tick Ordering
Same-tick writes are ordered by the tick pipeline: romance_progression_system at slot 9.6, apprenticeship_graduation_system at slot 23d. A graduation and a partnership-formation that land on the same tick write in a consistent order; tests assume this ordering. Future system reorderings must re-confirm the invariant.
Source: crates/er-sim/src/sim/world.rs, tick pipeline; slot documentation in system files.
Interactions
With Romance Dissolution
Origin history is currently the sole lever on the dissolution threshold. The tested and fragile flags on Relationship are computed but not yet read by romance_dissolution_system. If a future system wires those flags into threshold math, the interaction with origin history will need documentation.
With Apprenticeship
A graduation with bond_strength >= 60 records a pending-origin entry even if the pair is not yet Partners. If they become Partners within 30 in-game days, the entry drains. If not, it expires silently. A graduation with bond_strength < 60 does not qualify — the teaching did not sustain long enough for the bond to count as causal. See Apprenticeship for how bond_strength accumulates.
With Mourning
SharedCrisis writes are keyed to Mourning episodes. Two distinct bereavements produce two entries; repeated catalyst firings during one bereavement produce one. See Relationships § Departure Cascade for how Mourning is triggered.
With Save Files
origin_history and strength_min both persist across saves. Partnerships formed before this system existed load with empty history and strength_min equal to current strength (no lost-history assumption — the pre-feature data simply cannot be inferred).
PendingOriginWrites is a runtime-only resource and is not saved. An apprenticeship that graduates shortly before a save, followed by a partnership formation shortly after a load, will not record the Apprenticeship origin — the pending entry was lost. This window is narrow (graduation → partnership within 30 days, across exactly the save boundary) and accepted as a known limitation.
Tips
-
Watch for the endurance event.
... 's partnership enduredin the event feed means dissolution pressure crossed 100 but the partnership survived on origin resistance. It is the most reliable signal that origin markers are doing work in your settlement. These events first appear roughly day 30--80 in a typical playthrough — the earlier ones are memorable precisely because they're the first time the mechanic manifests. -
Apprenticeship + partnership is the strongest pairing. A master-student pair who reach
bond_strength60+ and later fall in love enter partnership with +20 resilience already banked. Pair elves with similar aesthetics during apprenticeship formation (smaller aesthetic distance means faster post-graduation friendship drift, which is a prerequisite for the romance warmth sources). See Apprenticeship. -
Grief forges durable bonds. A pair who comfort each other through the departure of a mutual friend earn a SharedCrisis origin — and the +6 warmth catalyst from SharedGrief often sparks the romance itself. Bereavement is generative, not purely corrosive. See Romance § Warmth Sources.
-
Rivalries that turn to love are fragile. RivalryToLove carries a -10 dissolution delta — partnerships that rose out of hostility dissolve easier than baseline, not harder. The identity is preserved as cost, not as protection. Expect these to break more readily under aesthetic drift; pair them with proximity (partners within 3 tiles get -2 pressure per cycle) if you want them to last.
-
Caps prevent runaway resilience. SharedCrisis stops contributing after 3 occurrences (+45 max) and the total positive delta clamps at +50 (threshold 150). A partnership that weathered five bereavements is not five-bereavements strong — it is three-bereavements strong in game terms, though the full count still renders in the UI. This is deliberate: long-running dynasties cannot accumulate infinite resistance and lock into un-dissolvable pairs.
-
Re-partnering is a clean slate. When a partnership dissolves,
origin_historyandstrength_minreset on the surviving partner. A widow who loses a 20-year-strong mentor-partnership and later pairs with someone new starts that new bond with the baseline threshold of 100. Grief carries forward as personality scars, not as partnership resilience. -
Dense histories are legible but long. The Relationships screen shows full origin history on one line. A partnership with five entries will produce a wide line. No truncation is applied; overflow is accepted until the shape of real-game data shows it matters.