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 GitHubRaw Markdown source