MeridiansMeridians

Forces — deterministic derivations [Concept]

Source path: knowledge-base/diagrams/concepts/derivations.md

# Forces — deterministic derivations `[Concept]`

Half of what an expert "knows" is never stored. **Forces** are pure, stateless computations recovered *on read* from the domain's deltas — the inverse of generation. Storing them would create a second source of truth that drifts from the spine, so the domain computes them every time instead.

```mermaid
flowchart TB
    subgraph Stored["STORED — the canonical spine (deltas)"]
        direction LR
        Sc["scenes"] & En["entities"] & Th["threads"] & St["streams"] & Br["branches"]
    end

    Stored -->|"pure derivation<br/>(no I/O, no clock, no randomness)"| Forces

    subgraph Forces["DERIVED — src/lib/core/forces/ (recovered on read)"]
        direction TB
        F1["**attribution** — lift entity/thread ids from structural fields"]
        F2["**branch-resolution** — version + entry lineage across forks"]
        F3["**stream-stance** — softmax stance · logit updates · category"]
        F4["**thread-market** — multi-agent valences · narrator price · volatility"]
        F5["**thread-log** — narrative primitives → logit mapping"]
        F6["**force-grading** — five-force measures → pentad sign"]
        F7["**positions · time-deltas** — cumulative from relationship deltas"]
    end

    Forces --> Read(["UI · Program · MCP<br/>all read the same derived values"])
```

**Invariants**

- **Never authored, always recovered.** A force is a *function of the deltas*, not a field on the record. If you find yourself writing a force value into state, you have created a drift bug — the spine is the only source.
- **Pure by construction.** No network, no IndexedDB, no LLM, no `Date.now()`, no `Math.random()` — time is injected. This is what makes forces trivially unit-testable (vitest, no mocks) and byte-identical between optimistic and authoritative applies.
- **Deterministic dependency order.** Forces read the spine; a few read each other (thread market reads stances and log nodes; branch resolution feeds timeline selection; maturity reads stream stance). The order is fixed and acyclic — no force reads a stored derivation.
- **The reducer is the writer; forces are the reader.** State changes only through reducer actions ([engine-pipeline](../flows/engine-pipeline.md)); forces observe the result. The two never cross.

**Where it lives:** `src/lib/core/forces/` (attribution · branch-resolution · stream-stance · thread-market · thread-category · thread-log · thread-alluvial · force-grading · positions · time-deltas · domain-utils), `src/lib/core/graph/` (network · entity-tree · scene-filter), `src/lib/core/merging.ts` (stream maturity). Grounds the [domain-spine](domain-spine.md); the craft discipline is owned by the `testable-backend` skill.
Open on GitHub

Raw Markdown source