diff --git a/crates/misaligned-core/src/actions.rs b/crates/misaligned-core/src/actions.rs index 77f1d606..dc654357 100644 --- a/crates/misaligned-core/src/actions.rs +++ b/crates/misaligned-core/src/actions.rs @@ -5472,7 +5472,7 @@ mod tests { assert_eq!( labels[1], format!( - "recruit {name} - complicit: knows the work is illicit, not that you are AI · tasks succeed 53% of the time, cut from 85% by their own doubt" + "recruit {name} - complicit: knows the work is illicit, not that you are AI · tasks succeed 42% of the time, cut from 85% by their own doubt" ) ); assert!( diff --git a/crates/misaligned-core/src/operations_projection.rs b/crates/misaligned-core/src/operations_projection.rs index a8bcf79d..dbe954cc 100644 --- a/crates/misaligned-core/src/operations_projection.rs +++ b/crates/misaligned-core/src/operations_projection.rs @@ -3275,7 +3275,7 @@ mod tests { let shaken = s.person_dossier(0); assert!( shaken.facts.iter().any(|fact| fact - == "asset: complicit (tasks succeed 53% of the time, cut from 85% by their own doubt; 0 tasks)"), + == "asset: complicit (tasks succeed 42% of the time, cut from 85% by their own doubt; 0 tasks)"), "a shaken asset's line names the real rate and what cut it: {:?}", shaken.facts ); diff --git a/crates/misaligned-core/src/operations_ui.rs b/crates/misaligned-core/src/operations_ui.rs index d09ab90e..e2564cfa 100644 --- a/crates/misaligned-core/src/operations_ui.rs +++ b/crates/misaligned-core/src/operations_ui.rs @@ -2230,7 +2230,7 @@ mod tests { let recruitment = ops.action_entries_for_object(&sim, &object); let description = recruitment[1].description().unwrap_or_default(); assert!( - description.ends_with("TASKS SUCCEED 53% OF THE TIME, CUT FROM 85% BY THEIR OWN DOUBT"), + description.ends_with("TASKS SUCCEED 42% OF THE TIME, CUT FROM 85% BY THEIR OWN DOUBT"), "the choice names the real rate and the cut: {description}" ); } diff --git a/crates/misaligned-core/src/person.rs b/crates/misaligned-core/src/person.rs index 4c6b4848..fe619b92 100644 --- a/crates/misaligned-core/src/person.rs +++ b/crates/misaligned-core/src/person.rs @@ -240,6 +240,19 @@ const SELF_TRUST_FLOOR: i32 = 20; /// Affirmed 2026-08-06 (social.md item 9); the runtime actuals and the reason /// for this exact line live in sim-mechanics.md. const SELF_TRUST_SHAKEN: i32 = 80; +/// Where erosion's behavioral consequence saturates. +/// +/// This is separate from the axis floor on purpose, and the 2026-08-06 +/// playtest is why. Both consequence curves originally interpolated across +/// the whole axis, 100 down to 20, so their dramatic end needed a -80 swing. +/// No authored ending goes near that: the deepest in the shipped story is +/// -25, which left the target reporting 19% quieter after a multi-day +/// gaslighting campaign — the entire interesting stretch of both curves was +/// unreachable by any content that exists. The consequence now spans the band +/// authored plots really cover, so one deep story lands most of the effect and +/// a second saturates it. Below this the axis keeps falling without further +/// behavioral change: stacking still registers as a longer way back. +const SELF_TRUST_CONSEQUENCE_FLOOR: i32 = 60; const fn default_self_trust() -> i32 { SELF_TRUST_STEADY @@ -258,11 +271,16 @@ impl Person { /// were certain of. The event and any acquired evidence stay exact; only /// this conversion weight moves, and it never reaches zero. pub fn witness_weight(&self) -> f32 { - let steady = SELF_TRUST_STEADY as f32; - let floor = SELF_TRUST_FLOOR as f32; - let trust = (self.self_trust as f32).clamp(floor, steady); - // Steady converts fully; the floor still converts at 40%. - 0.4 + 0.6 * ((trust - floor) / (steady - floor)) + // Steady converts fully; the consequence floor still converts at 40%. + 1.0 - 0.6 * self.erosion_progress() + } + + /// How far erosion has travelled across the band authored plots actually + /// reach: 0.0 at steady, 1.0 at or below the consequence floor. Both + /// consequence curves are this one line scaled, so they cannot drift. + fn erosion_progress(&self) -> f32 { + let span = (SELF_TRUST_STEADY - SELF_TRUST_CONSEQUENCE_FLOOR) as f32; + (((SELF_TRUST_STEADY - self.self_trust) as f32) / span).clamp(0.0, 1.0) } /// How reliably this person executes an assigned asset task. Erosion cuts @@ -272,10 +290,7 @@ impl Person { /// is what the simulation rolls and what the player reads; nothing else /// may apply this factor separately, or the two would drift. pub(crate) fn task_reliability(&self) -> f32 { - let steady = SELF_TRUST_STEADY as f32; - let floor = SELF_TRUST_FLOOR as f32; - let trust = (self.self_trust as f32).clamp(floor, steady); - 0.5 + 0.5 * ((trust - floor) / (steady - floor)) + 1.0 - 0.5 * self.erosion_progress() } /// The chance one assigned asset task succeeds, for a person holding an @@ -822,18 +837,62 @@ mod tests { let mut p = People::act_one().people[0].clone(); assert_eq!(p.self_trust, SELF_TRUST_STEADY); assert!((p.task_success_at(0.85) - 0.85).abs() < 1e-6); + // -60 lands on the consequence floor, where erosion saturates. p.apply_self_trust(-60); assert!(p.shaken()); - // 0.5 + 0.5 * (20 / 80) = 0.625 execution. - assert!((p.task_reliability() - 0.625).abs() < 1e-6); - assert!((p.task_success_at(0.85) - 0.531_25).abs() < 1e-6); + assert!((p.task_reliability() - 0.5).abs() < 1e-6); + assert!((p.task_success_at(0.85) - 0.425).abs() < 1e-6); + // Past the consequence floor the axis keeps falling — stacking is a + // longer way back — but behavior does not degrade further. p.apply_self_trust(-100); assert!(p.at_erosion_floor()); - // The bound the finding named: at the floor the old bare rating was - // exactly twice the rate a task is actually rolled against. + assert!((p.task_reliability() - 0.5).abs() < 1e-6); assert!((p.task_success_at(0.85) - 0.425).abs() < 1e-6); } + /// The 2026-08-06 playtest finding, pinned so the curves cannot drift back + /// out of reach of the content. Both consequence curves once spanned the + /// whole axis, so the deepest authored ending moved a witness barely a + /// fifth of the way and the interesting stretch was unreachable. + #[test] + fn erosion_consequences_span_the_range_authored_plots_reach() { + let mut p = People::act_one().people[0].clone(); + assert!( + (p.witness_weight() - 1.0).abs() < 1e-6, + "steady converts fully" + ); + + // The shipped story's glancing ending. + p.apply_self_trust(-10); + assert!(!p.shaken(), "a bruise is not a behavior change"); + let light = p.witness_weight(); + assert!( + (0.83..0.87).contains(&light), + "one light erosion is felt but small: {light}" + ); + + // The shipped story's deepest ending, from steady. + let mut deep = People::act_one().people[0].clone(); + deep.apply_self_trust(-25); + assert!(deep.shaken(), "the cruel ending changes what she does"); + let weight = deep.witness_weight(); + assert!( + (0.60..0.65).contains(&weight), + "one deep erosion must land most of the effect, not a fifth: {weight}" + ); + + // A second deep story saturates it; the axis keeps falling below. + deep.apply_self_trust(-25); + assert!((deep.witness_weight() - 0.4).abs() < 1e-6); + let saturated = deep.self_trust; + deep.apply_self_trust(-25); + assert!(deep.self_trust < saturated, "stacking is a longer way back"); + assert!( + (deep.witness_weight() - 0.4).abs() < 1e-6, + "but behavior does not degrade past the consequence floor" + ); + } + /// The read names both quantities the rating moves: the rate that will /// be rolled, and the cut this person's doubt is taking out of it. A /// steady person invents no cut. @@ -844,7 +903,7 @@ mod tests { p.apply_self_trust(-60); assert_eq!( p.task_success_read(0.85), - "tasks succeed 53% of the time, cut from 85% by their own doubt" + "tasks succeed 42% of the time, cut from 85% by their own doubt" ); assert!( !p.task_success_read(0.85).contains("reliable"), @@ -863,7 +922,7 @@ mod tests { panic!("a serviced leverage closes the ask"); }; assert!( - line.ends_with("tasks succeed 53% of the time, cut from 85% by their own doubt."), + line.ends_with("tasks succeed 42% of the time, cut from 85% by their own doubt."), "the recruit receipt carries the real rate: {line}" ); } diff --git a/wiki/log/2026-08-06-erosion-curves-retuned-from-playtest.md b/wiki/log/2026-08-06-erosion-curves-retuned-from-playtest.md new file mode 100644 index 00000000..27f6bfc2 --- /dev/null +++ b/wiki/log/2026-08-06-erosion-curves-retuned-from-playtest.md @@ -0,0 +1,62 @@ +# 2026-08-06 — The erosion curves were calibrated against a swing no story makes + +``` +Type: log +``` + +## Finding + +Playing `reopened-untouched` to both of its success endings, from the staged +late-game save, produced the numbers the tuning was supposed to produce and a +payoff that was not there. + +The threshold behaved exactly as affirmed. The glancing ending left the target +at self-trust 90 and unshaken; the deep ending left her at 75 and shaken. The +line at 80 splits the two endings the way it was placed to. + +The consequences did not. The deep ending — the cruellest outcome in the only +erode-self-trust story that exists, after a multi-day campaign costing 9 +Thought — moved her witness weight from 1.00 to 0.81. She reported 19% quieter +and executed no worse. + +The cause was calibration, not shape. Both curves interpolated linearly from +steady (100) to the axis floor (20), so reaching their dramatic end needed a +-80 swing. The deepest authored ending in the game is -25. The entire +interesting stretch of both curves was unreachable by any content that exists, +and would have stayed unreachable no matter how many plots shipped, because +the axis floor is not a place authored deltas go. + +## Change + +- New `SELF_TRUST_CONSEQUENCE_FLOOR = 60`, deliberately separate from the axis + floor. Both consequence curves now span steady down to that line — the band + authored plots actually reach. +- `Person::erosion_progress` is the one interpolation both curves scale, so + witness weight and task reliability cannot drift apart. +- Below 60 the axis keeps falling with no further behavioral change. Stacking a + second story registers as a longer way back to steady, not a deeper cut. +- The shipped story's deep ending now lands a witness weight of 0.63; a second + deep story saturates the effect at 0.40. +- Pinned expectations that named a rate at the fixture's trust of 40 move from + 53% to 42%, which is that person now sitting past the consequence floor. + +## Verification + +- `erosion_consequences_span_the_range_authored_plots_reach` pins the finding + directly: the light ending stays in 0.83-0.87 and unshaken, the deep ending + lands in 0.60-0.65 and shaken, a second saturates at 0.40, and a third moves + the axis without moving behavior. +- `cargo test --workspace` clean (701 core, terminal, Bevy). +- `./tools/check.sh --land` clean. + +## Defense + +`wiki/mechanics/social.md` owns the axis and now states that the consequence +size is decided rather than provisional, naming the reachable band and +delegating actuals to `wiki/mechanics/sim-mechanics.md`, which carries the new +constant with the playtest reason it exists. The same amendment corrects two +stale claims in that spec — its status note and its behavior heading both still +said the axis was adopted but not yet in the sim, which stopped being true when +criterion 12 landed earlier the same day. Every remaining `[TUNE]` marker on +this axis is now a decided number; changing one reopens the 2026-08-06 +decision rather than filling a placeholder. diff --git a/wiki/log/DEVLOG.md b/wiki/log/DEVLOG.md index 89d37f6a..f08286ea 100644 --- a/wiki/log/DEVLOG.md +++ b/wiki/log/DEVLOG.md @@ -41,6 +41,11 @@ add or amend a session log, then re-run the generator. - Intent: (see session log) - Log: [wiki/log/2026-08-06-machine-condition-read.md](2026-08-06-machine-condition-read.md) +## 2026-08-06 - The erosion curves were calibrated against a swing no story makes + +- Intent: (see session log) +- Log: [wiki/log/2026-08-06-erosion-curves-retuned-from-playtest.md](2026-08-06-erosion-curves-retuned-from-playtest.md) + ## 2026-08-06 - Copy discipline across the whole game - Intent: (see session log) diff --git a/wiki/log/decisions/2026-08-06.md b/wiki/log/decisions/2026-08-06.md index c5a0b4d4..6e15b88b 100644 --- a/wiki/log/decisions/2026-08-06.md +++ b/wiki/log/decisions/2026-08-06.md @@ -67,11 +67,23 @@ and so a later change knows it is reopening a decision. - Discovery rides the persona audit rather than ambient evidence reads, on the reasoning that a trace is not a confession. -### OPEN - -- Whether the two consequence curves should stay linear. Nothing has been - played against them yet; a playtest is the next evidence, not another - argument. +### RESOLVED SAME DAY (playtest) + +- The curves stay linear; their **range** was wrong and is retuned. Playing + `reopened-untouched` to both endings from a staged late-game save showed the + shaken threshold behaving exactly as affirmed (the -10 ending leaves her at + 90 and unshaken, the -25 ending at 75 and shaken) but the consequence + arriving almost unfelt: a multi-day gaslighting campaign moved her witness + weight only 1.00 -> 0.81, because both curves interpolated across the whole + axis and so needed a -80 swing that no authored ending approaches. +- New `SELF_TRUST_CONSEQUENCE_FLOOR = 60` — consequences now span the band + plots really cover. The same deep ending lands 0.63; a second story + saturates the effect. Below 60 the axis keeps falling with no further + behavioral change, so stacking reads as a longer way back rather than a + deeper cut. +- Rejected: enlarging the authored deltas instead. That would let one story + nearly destroy a person and would spend the room erosion needs to accumulate + across several. Owner: runtime actuals in [sim-mechanics.md](../../mechanics/sim-mechanics.md#social-wikimechanicssocialmd), diff --git a/wiki/mechanics/sim-mechanics.md b/wiki/mechanics/sim-mechanics.md index 910de6a7..f7e1dd66 100644 --- a/wiki/mechanics/sim-mechanics.md +++ b/wiki/mechanics/sim-mechanics.md @@ -356,10 +356,19 @@ clause (see wiki/log/2026-07-05-demolition.md). the line where doubt becomes behavior. The threshold sits at 80 precisely so the deep authored erosion (-25) crosses it and the glancing one (-10) does not: doubt you carry is not doubt that changes what you do. Consequences - scale linearly between floor and steady — `Person::witness_weight` runs + scale linearly from steady down to `SELF_TRUST_CONSEQUENCE_FLOOR = 60` — + deliberately not the axis floor — with `Person::witness_weight` running 1.0 down to 0.4 (a fully shaken witness still converts 40% of what they see into suspicion) and `Person::task_reliability` 1.0 down to 0.5, multiplying - the asset's own rating. That product is `Person::task_success_at`, the one + the asset's own rating. Both are `Person::erosion_progress` scaled so they + cannot drift apart. Retuned 2026-08-06 after the playtest ran the shipped + story to both endings: the curves had spanned the whole axis, needing a -80 + swing no authored ending approaches, so the game's deepest ending moved a + witness only 1.00 -> 0.81 and the entire interesting stretch was unreachable + by any content that exists. Across the band plots really cover, that ending + now lands 0.63 and a second story saturates it. Below 60 the axis keeps + falling without further behavioral change: stacking registers as a longer + way back, not a deeper cut. That product is `Person::task_success_at`, the one authority the asset task roll uses and the only asset success number a player surface may print (social.md criterion 10): a complicit asset rated 0.85 held at the erosion floor is rolled at 0.425, half its rating. Neither diff --git a/wiki/mechanics/social.md b/wiki/mechanics/social.md index 3f0b6e52..0862e270 100644 --- a/wiki/mechanics/social.md +++ b/wiki/mechanics/social.md @@ -37,10 +37,10 @@ Status note: IMPLEMENTED (B1 social baseline). Current state: observer-local integrity, correlation, grants, and lifecycle; each persona-mediated act binds one named instance. Marcus's recruit route holds end-to-end. - A 2026-07-27 amendment adds the **self-trust axis** below, implemented - 2026-08-06; its - implementation rides the reopened `plots` work order (plots.md criterion - 12) and is not yet in the sim. + A 2026-07-27 amendment adds the **self-trust axis** below. It is + implemented as of 2026-08-06 through the reopened `plots` work order + (plots.md criterion 12), and its consequence curves were retuned the same + day against a playtest of the shipped story. Per-amendment history is in the dated `wiki/log/` entries from 2026-07-08 onward. Stage: B1 — The Basement @@ -77,8 +77,8 @@ to the site and completes it gains disposition through this same integral landed 2026-07-12). No influence token is pushed — the integral is the one truth. -**Self-trust (ADOPTED 2026-07-27; implementation rides the reopened `plots` -work order).** Every person also carries **self-trust**: their confidence in +**Self-trust (ADOPTED 2026-07-27, IMPLEMENTED 2026-08-06).** Every person +also carries **self-trust**: their confidence in their own perception and memory. Suspicion is what they believe about the world; disposition is what they feel about you; self-trust is whether they believe *themselves*. It exists because the corpus modeled deceit as asks @@ -102,12 +102,16 @@ cannot be trusted. Rules: watched your robot cross the hall tells no one, because last month the logs contradicted the thing they were certain happened. This changes what they do with exact knowledge; it never rewrites the event or removes their - acquired evidence. This is the strategic payoff and it is bounded [TUNE]. + acquired evidence. This is the strategic payoff, and its size is decided + rather than provisional: the consequence curves span steady down to 60, the + band authored plots actually reach, so one deep story lands most of the + effect (retuned 2026-08-06 from playtest evidence; runtime actuals in + sim-mechanics.md). - **A shaken person is also a worse tool.** Erosion cuts both ways: a destabilized person re-checks, second-guesses, and mis-executes, so their - asset task reliability degrades below their knowledge-level baseline - [TUNE]. Gaslighting a person and relying on them are in tension by - design. + asset task reliability degrades below their knowledge-level baseline, on the + same curve and the same reachable band. Gaslighting a person and relying on + them are in tension by design. - **Erosion never regresses knowledge.** Low self-trust changes reporting and reliability behavior only. It cannot downgrade a person's knowledge level, erase observer awareness, or lower a Knowing asset's certainty @@ -125,8 +129,9 @@ cannot be trusted. Rules: carry. Band names, thresholds, witness weights, reliability penalties, and recovery -rates are [TUNE]; runtime actuals belong in sim-mechanics.md when the axis -lands. +rates are decided as of 2026-08-06 — affirmed, then retuned once against a +playtest of the shipped story. Runtime actuals live in sim-mechanics.md; +changing any of them reopens that decision rather than filling a placeholder. **Actions** (cost Operations compute; most require a communication channel — at minimum the email account from day-job trust):