The pentad in the app — measurement, surfaces, post-hoc actions, roadmap
From the Meridians Wiki · Public · Maintained · human-contract
Status: step 3 shipped. Flags and composition diagnostics are shipped as derived projections; PostHoc settings, MDL-based Acting growth, debt reads, κ analytics, and the Maintain surface are built. Acting growth runs at the generation seams in the arc, world, futures, game, and program-merge operations, deliberately not during extraction or creation. This document is the argument and the sequence for shifting the UI, generation, and measurement of Meridians around the pentad. Every "today" cell below was verified against the code at the paths cited; every "plan" cell is proposal. Where a formula is given it is a shipped calculation or an explicitly labelled diagnostic. The reader-first white paper's claim — discover what your choice changed — is what this plan exists to make true in the product.
Grounding: src/lib/core/forces/domain-utils.ts (raw forces, snapshots, activity),
force-grading.ts, theme-analysis.ts, thread-log.ts, thread-market.ts; the UI consumers
ScorecardPill, ForceAnalytics, ForceTimeline, DomainReport, ArcDetail/SceneDetail,
ThreadDetail, BranchGraphView; settings in src/types/domain/settings.ts and
DomainSettingsModal; spatial ops in src/lib/server/ops/catalog.ts; Acting in
EntityUnderstandingPanel.
1. Activity is not the world; it is the visible part of it
Today the engine measures activity: five raw per-scene quantities over the pentad, each rank-Gaussian normalised, then combined —
Fate_i = Σ_threads v_t · D_KL(p⁺ ‖ p⁻) belief moved on Threads this scene touched
World_i = ΔN + √ΔE entities and edges added
System_i = Σ_rules −log p(topic) surprisal of newly binding rules
Theme_i = Σ_signals |Δ salience| Theme movement in this Scene
Causality_i = Σ_flags w(stakes)·ln(1 + alternatives) choice-space removed in this Scene
A_i = Σ_c w_c · F_c,i the five-force activity curve
Every one of these is a per-scene increment: what this scene added to the record. That is exactly why they are the visible forces — a scene can be scored on its own. Theme is graded from structure breadth × depth over valid leaf slots, while recurrence (attention softmax over salience, entropy, coverage, grounding) remains a diagnostic across the whole selected prefix. Causality is graded from native Flag information against the prose-length density expectation for the selected interval.
The five channels are visible in activity, while their grade and diagnostics retain distinct currencies. Theme recurrence, Causality reach, and Fate's open mass remain longitudinal readings beside the scene-level curve. A reader can therefore see both what moved at a Scene and what that movement meant across the record.
So the measurement programme has two halves:
| Half | Forces | Kind of number | Today |
|---|---|---|---|
| Activity | World, System, Fate, Theme, Causality | per-scene increment, rank-Gaussian normalised | shipped |
| Composition | Theme recurrence, Causality reach, Fate open mass | relation over the record at a vantage | shipped as diagnostics |
The same five channels feed ForceSnapshot, Activity, and Signature. The scorecard still keeps grade
currencies and longitudinal diagnostics legible rather than collapsing them into one number.
Flag. Causality now contributes a scene-local Flag information series to Activity and Signature, while its Flag-information grade and H_open / reach remain longitudinal diagnostics. Theme contributes its canonical signal-point series in the same way, while Theme recurrence and structure realisation remain available beside it. Extraction and generation are five-force.
2. Candidate formulas for the invisible forces
All three are computed at a vantage (branch, entry) over the resolved entry prefix, like
scoreThemeSystem already is. None writes state. Each has a falsifier so it can be rejected.
2.1 Fate as weight (open mass)
H_open(v) = Σ_{Threads open at v} u_t · H(p_t(v)) u_t = log(1 + volume_t), H = Shannon entropy
What it says: how much unresolved, evidenced uncertainty the world carries right now. It falls when a
question collapses and rises when a scene opens one. Reads: Thread.closeEntryId, Stance logits via
stanceAsOf (thread-market.ts). Falsifier: if H_open does not drop at scenes readers name as
resolutions, the weighting is wrong. Volume decay also lowers H_open during quiet stretches, so the
closure-attributable drop (Σ collapse) is the discriminating quantity.
This is the number the current Fate increment cannot give: a scene of enormous belief movement (high
Fate_i) may leave the world more certain (low H_open) — a payoff — or less — a twist that adds
outcomes. Today the two are conflated in one line on the chart.
2.2 Theme as recurrence
Θ_i = Σ_{signals s in scene i} |Δsalience_s| · log(1 + n_θ(s), i) n_θ = prior scenes carrying Theme θ
What it says: a Theme's first appearance is worth nothing to the force (it is not yet a pattern); each
recurrence is worth more. This is recurrence, not development or growth: |Δsalience| counts rising
and falling signals alike, and the logarithmic count is frequency-weighted. The total is length-dependent,
so it should not be naively compared across Domains of different lengths. This is the rule that makes
Theme invisible in a single scene and visible across the record, made numeric. It replaces the flat
Σ|Δsalience| scene points that scoreThemeSystem currently caps at 25 rather than replacing them,
while keeping attention, entropy, coverage, and grounding as diagnostics.
Falsifier: in a Domain with one dominant Theme, Θ must rise monotonically with recurrence; in a Domain
whose signals are all singletons, Θ must be ~0 even if salience is high.
2.3 Causality as entropy flow and reach
Causality is the transition structure over Fate: not a scalar force but a graph, with a flow measure attached. Three layers.
-
A flag is a point event. Fate is a probability vector
p_tover a Thread's outcomes, moving scene by scene. A flag is the scene where it stops moving:p⁻ → p⁺ ≈ δ_k, outcomeknow fixed on this branch — concretely(thread, scene, k, p⁻, p⁺). World and System are counts of state; Fate is a distribution; a flag is a change of distribution. That is why it is the least visible force: it can only be seen between two scenes, never in one. -
Causality is the graph on flags. Edges are
dependsOn: flag B needed flag A to be possible — a DAG whose vertices are collapses and whose edges are "A's answer was in the support of B's question". A branch is an assignment of values to a set of questions; two branches with the same rules and cast differ exactly in their flag vector. This branch-as-Flag-vector claim is now a computed comparison surface:diffFlags/diffBranchFlagsretain the shared questions, mark divergent rows, and identify the first divergence without adding canonical state. "What can lead to what" is reachability in that DAG: divergent flags are vertices with high out-degree, conditional flags are vertices whose in-degree is what matters. -
The measure is a flow of uncertainty. With
H_open = Σ_open ln(1+v_t)·H(p_t)the stock of weighted possibility still in play, a collapse is the flow out of it:v_t·(H(p⁻) − H(p⁺)). Fate measures information gained (KL — how far belief moved); Causality measures possibility spent (entropy drop — how much of the open space was closed off). Same currency (nats), different operator: Fate is the derivative of belief, Causality is the divergence of possibility. The flows at a scene must sum exactly to the closure-attributable drop in the stock — that is what makes it a measure rather than a plausible number.
Fate says where the world is leaning; Causality says which leanings became facts, in what order, and what each fact foreclosed. It is invisible per scene by construction because it is a difference operator across scenes — as Theme is an integral across scenes.
Status: layers 1–3 ship as derived projections; dependency edges are derived from the record (co-movement, attribution edges, opens) — a model-refined edge stage remains optional.
For the shipped projection, the scene aggregate and retrospective diagnostic are:
u_t = log(1 + volume_t)
collapse_f = u_t · (H(p⁻) − H(p⁺)) at the closure node
C_i = Σ_{flags f collapsed at i} collapse_f atomic Causality at scene i
reach_f(v) = |{ flags g at or before v : f ∈ dependsOn_g }| + |forecloses_f| retrospective diagnostic
Here p⁻ is the belief at the previous trajectory node, p⁺ is the belief at the closure node, and
H is unnormalised Shannon entropy in natural-log units. collapse_f is the closure-attributable flow
of H_open: it is the open Fate mass that closure removed, weighted by the stance volume entering the
scene. More outcomes open means more mass; normalizedEntropy is the rejected alternative because it
would erase that option-set contribution. A flag that settled a 50/50 counts more than one that
confirmed a 95. The dependency term is zero until step 4; the shipped projection is deliberately the
direct collapse sum. reach cannot be known when the flag is minted — it is read at a later vantage
and is the closest thing the engine will have to this is the choice that mattered.
Falsifier: reach should rank the disclosure scene in the paper's worked example above the delegation
scene, and both above filler; if hand-labelled turning points do not rise to the top of reach on the
Harry Potter benchmark Domain, the dependency extraction is wrong, not the reader.
C_i is the Causality channel in the five-force chart; reach remains a per-flag readout, not a
series. ForceSnapshot carries all five rank-normalised channels. The grade uses native Flag information
against the prose-length density expectation; signed collapse and reach remain separate diagnostics.
openMassSeries currently replays the resolved prefix once per scene, so its implementation is O(n²)
over the timeline.
2.4 The scorecard
World grade deduction
System grade deduction
Fate grade deduction
Theme grade deduction + coverage / competition / grounding diagnostics
Causality grade deduction + open mass H_open at cursor, top flags by reach
overall Σ grades (each an anchored logistic of realisation)
Rendered in the order of visibility — World, System, Fate, Theme, Causality — not the current Fate-first order, so the UI teaches the frame every time it is opened.
3. Where the pentad lives in the app
Principle: Causality is shown at the things it connects, not as a fifth blob. A flag is shown at the Thread it collapsed and on the entry where it happened; its dependencies are edges between flags; its reach is a number on the flag. This mirrors how Theme is already shown (purple attribution beside the triad, a tree, a rack of carriers) and avoids a competing canvas cohort.
| Surface | Today (verified) | Plan |
|---|---|---|
ScorecardPill (topbar) | Fate, World, System, Theme rows; hard-coded colours | Five graded rows in visibility order; H_open at cursor; colours from tokens |
ForceAnalytics / ForceTimeline | Fate/World/System series + Theme + activity | add Θ (replacing flat Theme points) and C_i series; a second "composition" pane: H_open line with flag markers at collapses |
ThreadDetail | outcomes, stance, movement, replay | Flag block when closed: value, vantage, collapse, dependsOn (linked), opens/forecloses, reach at cursor, nature chips (divergent · conditional · personal) |
SceneDetail / ArcDetail | force sign readout, Theme moments | "What this scene collapsed" list → flags; "what it depended on" |
| Inspector: new Thread closures panel | — | branch's closure ledger in entry order; filter by nature, Thread, seat; click → Thread/entry |
| Stage: Threads view | lifecycle replay at cursor | collapsed Threads drawn with their flag; dependency edges between collapsed Threads as a faint overlay |
BranchGraphView / Branch Chat compare | lineage columns, compareBranchIds | Branch diff over Thread closures: shared question set, rows = Threads, cells = collapsed value per branch; differing cells highlighted; "first divergence" marker |
DomainReport / slides | force table, arc grades | five-force table; a "what led to what" page: top flags by reach with their dependency chains |
| Extraction job detail | World/Fate/System/Theme counts | a Causality phase card: flags minted, dependency edges, unresolved collapses |
Tokens (globals.css) | --color-fate/world/system, Theme violet; no fifth slot | add --color-causality + .category-color-causality; migrate the four hard-coded literal sites (report, scorecard, analytics) to tokens in the same change |
Colour is an open decision (§7 of the README). The constraint: it must sit beside red/green/blue/violet and read as "between" rather than "another thing" — amber is the obvious candidate; decide on the canvas.
4. Post-hoc actions: settings and the Maintain surface
Today: postHoc persists automatic-maintenance policy for spatial mapping and Acting growth; spatial
ops (generate-board-topology, drain-spatial-mapping-debt, rebuild-location-hierarchy) remain
manual/MCP with buttons in SpatialSettingsModal. Acting growth is MDL-based
(assessActingGrowth → maintainActingGrowth) with keep-evidence, interpret, reinterpret, and
hold verdicts. It runs at the arc, world, futures, game, and program-merge generation seams, and
deliberately does not run in extraction or creation, matching the
acting-maintenance-invariants.test.ts ratchet.
Post-hoc bindings are attributable and revisable: selectedBy: "post-hoc" may be maintained, while a
Will-selected binding is held and never overwritten.
Step 3 status: shipped — the postHoc settings block, pure spatial/Acting debt read,
κ composition analytics, Tier-0 read-post-hoc-debt, Maintain settings section, MDL-based Acting
growth from #234 are built. Will bindings are held; post-hoc bindings are attributable to the
maintenance pass.
Plan — one settings block and one surface:
// DomainSettings (flat, backward compatible; spatialAutoMaintain migrates in)
postHoc?: {
spatial: { autoMaintain: boolean; reorganizeAtBirth: boolean }; // from spatialAutoMaintain
acting: { autoMaintain: boolean }; // MDL growth; Will binding stays manual
flags: { detect: "off" | "on-extraction" | "on-commit"; }; // projection, never a write
questions: { synthesise: "off" | "on-extraction" | "on-commit"; }; // ranked report, never a write
}
Maintain — a surface (inspector utility panel or Domain menu) that lists every post-hoc operation with its last run, what it read, and a run button, grouped by what it interprets:
Interpretations of System Rebuild world · Map levels · Drain mapping debt · Maintain Acting growth
Invisible forces Detect flags (Causality) · Re-score Theme recurrence
Synthesis Synthesise questions at cursor
Rules: interpretations and projections may run automatically; binding an Acting read, committing
a synthesised question as a Thread, or overriding a detected flag's nature is Will and stays manual
and attributable. Each op is an ordinary catalog entry (params, result, commits, engine) so MCP
and the Program can call it; the Program's UPDATE_DOMAIN_PROGRAM_CONFIG gains optional post-hoc steps
after Merge.
5. Flag detection integrated in extraction and generation
5.1 Extraction — a Causality phase
Insert after Fate re-extraction and Domain assembly (the thread log is priced by then) and before the topic-tree/spatial phases:
phase Causality (flags) projection-first
read Threads (outcomes, closeEntryId, closeOutcome), ThreadLogNode{closed, infoGain, preVolume, updates},
scene threadDeltas/worldDeltas/systemDeltas, attribution edges, branch entry sequence
step 1 deterministic: every closure node → candidate Flag{threadId, outcomeId, vantage, collapse}
step 2 deterministic: derive co-movement, attribution, and opens edges plus transitive reach from
the record; model citation/refinement remains optional and is not built
emit Thread closure ledger + dependency edges as a derived extraction artifact keyed by branch
Step 1 needs no model and can ship first; it makes the Flags panel and C_i (without the dependency
term) real. Step 2 is the LLM stage and is where "effective extraction of flags from canonical text"
lives. Its prompt joins the prompts/core/forces.ts family; the existing "never a fourth causal axis"
locks are about Theme and stay.
5.2 Generation — flags as read-only context
generateScenes, scene-response, plan/prose/script generation get a <closures> block at the vantage:
collapsed values, their natures, and the open Threads with highest H and reach-weighted dependents.
Effect: continuation generation is constrained by what this timeline has already made certain and
oriented toward what is load-bearing, which is the concrete meaning of "all five forces integrated into
generation." Flags are input only; the reducer's typed deltas remain the only writer.
The ambient force reading is structured so each row remains inspectable at the generation boundary:
<forces read="World→System→Causality→Fate→Theme"
hint="five forces; read = display order and derivation from the record; couplings = measured per work">
<force id="World" is="what exists" carries="entities, places, artifacts, knowledge, state"/>
<force id="System" is="what binds" carries="rules, permissions, institutions, economies"/>
<force id="Fate" is="what is open" carries="p over each Thread's outcomes"/>
<force id="Theme" is="what recurs" carries="propositions across scenes"/>
<force id="Causality" is="Fate settling" carries="Flag = scene where a Thread becomes fact on this branch; forecloses the rest"/>
<will is="not a force" carries="attributable authority from outside — reader, seat, Director, agent — settles a Thread"/>
<generation serves="the world's recorded leaning" never="manufacture consequence | settle a Thread canon leaves open | disclose what a lens does not hold"/>
</forces>
Pulse also emits the record-specific Constitution and force-flow evidence:
<constitution source="fitted|unread">
<coupling from="World" to="System" lag="1" r="0.410" n="20" t="2.31" />
</constitution>
<flow>
<edge id="lag1:World>System" relation="coupling" lag="1" verdict="readable"
corr="0.410" pairs="20" t="2.31" />
</flow>
The <constitution> rows are emitted by src/lib/engine/ai/context-pulse.ts from fitted couplings in
the current record. Flow IDs use lag1: and lag2:; lag 1 is not a generating ring and lag 2 is not a
controlling star. read on the <forces> block is the symbolic display order only, while Constitution
and Flow are unconstrained, record-specific measurements. An empty fitted Constitution means the forces
read as independent; unread means the record is too short for a readable edge.
Flags make visible how Causality and Fate moved in this record: a settled Thread is the collapse that shapes this scene's branch. Questions make visible the Fate → Theme edge: what hangs on a question is what the scene must make legible. Reader-facing anticipation questions ship as a deterministic projection from settling Flags; they are never model-generated and remain ordinary scene question-bank records with optional provenance.
5.3 Question synthesis operation
synthesise-questions (catalog op, Tier-0): reads the five forces at a vantage; emits ranked open
questions with candidates and the forces that make each open. This is a deterministic read: the five
forces already carry the answer, and a model layer would only paraphrase it. Output is a
report/projection the Director can promote to Threads by an explicit commit. This is distinct from the
existing scene question-bank operation, which stays.
6. Roadmap — six steps, each with a falsifier
| # | Step | Ships | Falsifier / exit |
|---|---|---|---|
| 1 | Thread closure projection (deterministic) — closure nodes → Thread closure ledger; ThreadDetail closure block; Thread closures panel; --color-causality token | Causality visible for the first time — shipped (derived; ForceSnapshot unchanged) | every closed Thread on a benchmark Domain shows a Thread closure with the right value and entry |
| 2 | Composition measures — H_open, Θ recurrence, collapse; scorecard in visibility order | Theme and Fate-as-weight numerically distinct from activity — shipped (derived diagnostics; Theme recurrence and entropy flow graded; ForceSnapshot unchanged) | §2 falsifiers hold; Theme singleton-Domain ≈ 0 |
| 3 | Post-hoc settings + Maintain — postHoc block, Maintain surface, spatial/Acting/flags/questions ops registered | one place that keeps the interpretations of System current — shipped; MDL Acting growth (#234) runs at the arc/world/futures/game/program-merge generation seams, with post-hoc bindings attributable and Will bindings held | operators can run and audit every post-hoc op without losing manual authority |
| 4 | Dependency extraction (deterministic) — dependsOn/opens/forecloses/nature; reach | Causality as a force that can be understood — shipped (deterministic edges + reach; model refinement optional) | hand-labelled turning points rank top by reach |
| 5a | Causality alluvial — Thread closure collapse bands in Influence | shipped (projection-only) | settled Thread closures render by nature and collapse |
| 5b | Causality topic-tree leaves — native Flags plus surviving Thread closures under owning Thread topics | shipped | canonical Flags and legacy Thread closures remain inspectable without duplication |
| 5c | Causality extraction tab — assembled-Domain Flag summary and open mass | shipped | completed extraction exposes Flags and H_open |
| 5 | Branch diff over Thread closures — compare view; "first divergence" | parallel worlds differ in their Thread closures becomes a screen — shipped (diffClosures, diffBranchClosures, read-closure-diff, and the ThreadsView comparison surface) | two benchmark branches diff to exactly the Threads that differ |
| 6 | Synthesis + generation reads Flags — synthesise-questions; anticipation questions; <closures> context; conditioned questions → script pipeline; Script lens knowledge and descriptive force reading | deterministic synthesis, anticipation questions, and the conditioned generation reads — shipped; the reconfigurable pipeline configuration record in README §4.3 remains unbuilt | the three configurations in README §4.3 run from one config record |
Steps 1–2 are pure/core work with vitest coverage and touch no reducer action. Step 3 adds one settings
field group, MDL Acting maintenance, catalog entries, and a panel. Steps 4 and 6 add LLM stages behind callGenerate. Step 5 is
UI over a pure diffFlags(a, b).
7. Why this is the right path — the case, and how to reject it
- It builds on what measures well. The original per-scene activity math remains intact for Fate, World, and System while Theme and Causality now join the same five-force aggregate; longitudinal composition diagnostics remain beside it. No existing chart, grade, or test changes meaning.
- It makes the paper's claim testable. Discover what your choice changed is
reachon a flag and a branch diff. If step 4's falsifier fails, the claim fails visibly rather than rhetorically. - It respects the writer boundary. Every new quantity is a projection; every new op is interpretation or synthesis; every promotion to canon is an attributable commit.
- It teaches the frame by use. Visibility order in the scorecard, flags at Threads, dependencies as edges: the UI shows a reader why the invisible forces are invisible — you have to have read the whole record to see them.
- It is cheap to abandon. Steps 1–3 are worth having even if Causality never earns a grade.
Reject it if: closure nodes are too sparse on real Domains for flags to be interesting (measure on the
benchmark set first); or reach and hand-labelled turning points do not agree after one round of prompt
work — in which case Causality stays a named, unmeasured force and the paper's Appendix A sentence stands.