fix(samples): make "Drift to low performance" actually drift down

The model had no downward force on Performance, so its only flow
(improvement) could only pull it up. Performance rose 40 -> 60 while
the Standard fell 80 -> 60, contradicting the "slide downhill" story.

Add a constant decay outflow on Performance and seat it just below the
goal. Now both ratchet down in lockstep (Performance 70 -> 10, Standard
80 -> 20), a fixed gap apart — the genuine eroding-goals trap. Rewrite
the doc-comment, layout note, overview line, and blurb to match.
This commit is contained in:
Julien Calixte
2026-06-21 13:16:28 +02:00
parent 67c16a8d44
commit 37bbdf5650

View File

@@ -30,7 +30,8 @@
* no brake in the structure: an arms race. * no brake in the structure: an arms race.
* 10. Fixes that fail — a fix drains the symptom Stock (B) while its side * 10. Fixes that fail — a fix drains the symptom Stock (B) while its side
* effect refills it (R): the cure feeds the disease. * effect refills it (R): the cure feeds the disease.
* 11. Drift to low performance — a goal that erodes toward actual performance, so a * 11. Drift to low performance — a goal that erodes toward actual performance, so the
* effort it drives never overcomes a steady decay: a
* Reinforcing loop ratchets both downward. * Reinforcing loop ratchets both downward.
* *
* Next, the dynamic the book is named for, and the one the gallery has saved until a * Next, the dynamic the book is named for, and the one the gallery has saved until a
@@ -536,22 +537,27 @@ function fixesThatFail(): Model {
} }
/** /**
* Drift to low performance — the eroding-goals trap. The Standard you hold yourself * Drift to low performance — the eroding-goals trap. Performance is under steady
* to is not fixed: it slips toward whatever you are actually delivering. Improvement * erosion (a constant `decay` leak: entropy, wear, rising demands), and the only
* is driven by the gap (Standard → [+] and Performance → [] → improvement), and so * thing fighting it is improvement, driven by the gap to the Standard you hold
* is slippage of the Standard (Standard → [+] and Performance → [] → slippage). The * yourself to (Standard → [+] and Performance → [] → improvement). Were the
* two local Balancing loops look healthy, but together they close a Reinforcing * Standard fixed, improvement would find a floor — Performance would settle a little
* spiral — Standard → improvement → Performance → slippage → Standard, two `` → R: * below the goal (here, at 60) and hold. But the Standard is not fixed: it slips
* let Performance dip and the Standard follows it down, easing the gap, easing the * toward whatever you are actually delivering (Standard → [+] and Performance → []
* effort, so Performance drifts lower still. That R badge is the trap. * → slippage), so there is no floor. Every notch Performance loses, the Standard
* follows down, weakening improvement, and decay carries Performance lower still —
* the loop Standard → improvement → Performance → slippage → Standard, two `` → R.
* That R badge is the trap: both ratchet downhill in lockstep (Performance 70 → 10,
* Standard 80 → 20), the effort forever just losing to decay.
*/ */
function driftToLowPerformance(): Model { function driftToLowPerformance(): Model {
// Performance sits low-left (fed by improvement from a Source); Standard sits // Performance sits low-left (fed by improvement from a Source, leaked away by
// high-right (drained by slippage to a Sink). The two long links that close the // decay to a Sink straight below it); Standard sits high-right (drained by
// Reinforcing spiral cross in the open centre, where the R badge lands. // slippage to a Sink). The two long links that close the Reinforcing loop cross
// in the open centre, where the R badge lands.
const source = makeCloud({ x: -560, y: 120 }) const source = makeCloud({ x: -560, y: 120 })
const performance = makeStock({ x: -160, y: 120 }, "Performance") const performance = makeStock({ x: -160, y: 120 }, "Performance")
performance.initialValue = 40 performance.initialValue = 70
const improvement = makeFlow( const improvement = makeFlow(
midpoint(source.position, performance.position), midpoint(source.position, performance.position),
"improvement", "improvement",
@@ -559,24 +565,35 @@ function driftToLowPerformance(): Model {
performance.id, performance.id,
) )
// improvement closes the gap upward: 10% of (Standard Performance), pulling // improvement closes the gap upward: 10% of (Standard Performance), pulling
// Performance toward the Standard. // Performance toward the Standard — the one force resisting decay.
improvement.rule = { kind: "gap", factor: 0.1 } improvement.rule = { kind: "gap", factor: 0.1 }
// decay leaks Performance away at a steady 2/step: the ever-present downward
// pressure the gap-driven improvement has to offset. Without it, two gap-closing
// flows would just meet in the middle; this is what makes the goal's erosion bite.
const decaySink = makeCloud({ x: -160, y: 400 })
const decay = makeFlow(
midpoint(performance.position, decaySink.position),
"decay",
performance.id,
decaySink.id,
)
decay.rule = { kind: "constant", value: 2 }
const standard = makeStock({ x: 160, y: -120 }, "Standard") const standard = makeStock({ x: 160, y: -120 }, "Standard")
standard.initialValue = 80 standard.initialValue = 80
const sink = makeCloud({ x: 560, y: -120 }) const slippageSink = makeCloud({ x: 560, y: -120 })
const slippage = makeFlow( const slippage = makeFlow(
midpoint(standard.position, sink.position), midpoint(standard.position, slippageSink.position),
"slippage", "slippage",
standard.id, standard.id,
sink.id, slippageSink.id,
) )
// slippage erodes the *same* gap from the other side: the Standard drifts down // slippage erodes the Standard toward actual Performance, also at 10% of the gap.
// toward actual Performance. Both gaps close, so they meet — the Standard has // It is what removes the floor: the Standard sags after Performance instead of
// sagged from 80 to the middle, the eroding-goal trap. // holding above it, so the gap stays open at a fixed 10 while both slide down.
slippage.rule = { kind: "gap", factor: 0.1 } slippage.rule = { kind: "gap", factor: 0.1 }
return model( return model(
"Drift to low performance", "Drift to low performance",
[source, performance, improvement, standard, sink, slippage], [source, performance, improvement, decay, decaySink, standard, slippageSink, slippage],
[ [
link(standard, improvement, "+"), link(standard, improvement, "+"),
link(performance, improvement, "-"), link(performance, improvement, "-"),
@@ -838,7 +855,7 @@ export const SAMPLES: Sample[] = [
}, },
{ {
title: "Drift to low performance", title: "Drift to low performance",
blurb: "Goals erode toward actual: a Reinforcing slide downhill.", blurb: "An eroding goal leaves steady decay no floor: both slide downhill.",
build: driftToLowPerformance, build: driftToLowPerformance,
}, },
{ {