Story-time
This document specifies how De Lore Is models time. There are two times, and the whole design turns on keeping them apart: production order — the sequence in which a listener meets the material — is the listener’s dimension; story-time — when things were so in the world — is the world’s. There remains exactly one meta-time, the ledger, and there is no calendar; story-time is object data with structure, not a second transaction dimension.
The model has four separated components, a state layer for facts with validity
spans, a disciplined composition with the annotation algebra of
02-semantics.md, and a set of capabilities that fall out once the pieces are
separated. It deliberately excludes metric time and the heavy temporal-logic
machinery; §7 says why.
The decision ledger (../decisions.md) outranks this document.
1. The time model: four components, separated
Time is not one axis. Collapsing world chronology, narrative structure, the narrator’s vantage, and cyclic phase into a single ladder is the failure mode this model exists to prevent. The four are separated by type.
(i) World chronology — an era spine plus events as points. A totally-ordered
era spine, and events that carry era membership. Within an era, events
are ordered only by explicit before(e1, e2) constraint facts — a partial
order, honestly partial. There is no offset: a per-event integer fakes a
total order nobody ruled and invites arithmetic on a calendar the world forbids.
Absent a before-fact, two same-era events are unordered, and a query answers
unordered as a first-class verdict — never a silent tie-break. Precedence
is a kernel rule (happens-before = era-order lifting composed with intra-era
before-closure), exercised under CI; consistency is cycle detection, and a cycle
is reported as blame — the exact before-facts that close the loop.
(ii) Narrative structure — its own layer. Acts and arcs are not eras. The opening act, the turn, the world-after are narrative-layer objects (a handful of acts, a couple hundred episodes; the kishōtenketsu labels attach here), each referencing a chronological anchor rather than being one. This is precisely why those act-shaped “eras” sit empty of events in a flattened model — they were never chronology. When acts are planned in earnest, their macro events (an act’s climax landing, a reveal breaking) enter chronology as events, and may, by ruling, initiate genuinely new world eras. Narrative structure references the spine; it never masquerades as it.
(iii) Narration-time — a second vantage, inert. One distinguished point,
narration-now, sits inside the-narrators-era, centuries out from
story-present. It is inert by construction (D-0104): declaring an event in
the narrator’s era is a type error, no state dynamics exist there, and no
machinery accrues to it. What the vantage buys is the knowledge asymmetry:
the narrator is retrospectively omniscient over story-present — their premise
pool is the certain layer as-of narration-now — while cast knowledge is
evaluated as-of scene position. Facts whose validity begins after
story-present — how the story came to be remembered, a later vindication —
are markable narration-frame, and premise selection structurally excludes
them from any cast premise pool: a narrators-era fact can never be cast
knowledge. That is the entire apparatus; the ruling forbids more, and the model
agrees with the ruling.
(iv) Cyclic time — the cycles. Tide, moon, and season are small modular
sorts, orthogonal to the linear spine: cycle tide(4), cycle moon(29), the
festival cycle. A cycle carries succ, pred, across, distance and refuses
<, sort, and max — asking for the “greatest” season or sorting the moon’s
nights is a type error, not a silent tie-break (koan 8, the wheel refuses
rank). The cycle type lives once in the kernel and is shared by chronology
consumers. There is no epoch: phase coordinates are local declarations,
and consistency is checked only along declared contiguous walks, not by
joining unrelated scenes into an absolute calendar.
The four edge operations are pinned (ruling M6), so the cycle’s laws are stated rather than accidental:
acrossis the half-turn forward step,floor(n/2). On an even cycle it is a true antipode and an involution (across ∘ across = id). On an odd cycle there is no exact opposite spoke, soacrossis deliberately non-involutive —across ∘ across = pred— because inventing an antipode the ring does not have would be a lie, and the cycle does not lie about its shape.distanceis directional — the count ofsuccsteps from one position to another, withdistance(a,b) + distance(b,a) = n. The symmetric shorter-arc reading is a separate operation,separation(= min(distance(a,b), distance(b,a))); the two are named apart so a timing check says which it means.- A position index
≥ nis read modulon— a documented normalization, not an error, because a phase coordinate is a congruence class with no epoch. The strict boundary is opt-in: a checked constructor rejects an out-of-range index at the point a raw number enters, but a walk already underway never faults.
2. Fluents: facts acquire validity spans
A fact may carry a span anchored to chronology: since <event>, until <event>, during <era>, or a combination.
The migration default makes the entire existing corpus valid unchanged. An
unanchored fact means since era(the-beleaguered-present), open-ended — which
is exactly what the continuity checker already implicitly assumes of every
atemporal row. Adding spans to the model therefore requires zero edits to the
current corpus; a fact acquires a span only when a writer has a reason to give it
one.
The semantics is event-calculus-shaped: events initiate and terminate states, and inertia holds a state between its initiator and its terminator. Because the event order is partial, projection is cautious and three-valued:
- holds-certainly — an initiator precedes the query point and no possibly- intervening terminator does,
- holds-possibly — an initiator may precede and a terminator may intervene, depending on undeclared order,
- order-indeterminate — the answer depends on an order no one has declared.
The water-status case resolves concretely: a fact that the parish water was held in common until the enclosure and enclosed since it (D-0114) lets a pre-enclosure flashback check clean and the story-present episode check clean. The false contradiction a flashback would otherwise raise against the story-present enclosure fact is gone — not suppressed, but structurally absent, because the two claims hold over disjoint spans.
Using the tools. Create eras with author.add_entity and events with
author.add_event, anchor states to them with author.add_state, and chain
chronology with pairwise author.add_before (a chain a < b < c is N-1
befores — finer blame than a single chained ordering, and the only spelling
the wire carries). Two close-out findings turn temporal dead ends into
repairable ones: state.anchor-kind (error, at add time) fires when a
known entity anchors a state with the wrong sort — since/until want an
event, after/during want an era — with a typed repair naming both kinds
and known entities of the right one; an anchor this workspace has never even
declared is still the load-side load.unknown-temporal-reference backstop,
unchanged. continuity.fluent-order-undeclared (warn, non-blocking) is the
order-indeterminate case above made actionable: two distinct certain-support
values whose cover depends on an undeclared order between named anchors,
with a typed repair ({kind: "declare-order", candidates: [[p, q], …]})
listing exactly the pairs an author.add_before would settle. Soundness is
unchanged either way — the contradiction check itself still never fires on
undeclared order; the warn only names the gap. dli.timeline.* reflects
materialized scaffolding only; staged scaffolding is visible through
author.query/author.check/author.why instead, by design (the review
surface is the staged layer, not a premature timeline render).
Declared inertial persistence. Every fluent projected
above is, by default, open-ended: a value since t with no authored until
holds forever, and two open-ended values on the same slot are a plain
continuity.fluent-contradiction — the engine never guesses which one a
later value was meant to supersede. author.declare_inertial {workspace, pred, provenance} names a predicate whose values are meant to behave
differently: a value asserted since t persists forward only until a
later, distinct value on the same slot takes over — the standard
event-calculus resolution to the frame problem, moved inside the engine
instead of being decided by whoever chooses since/until bounds by hand
(docs/decisions.md DL-0081 records the full ruling: later-distinct-value
defeat, per-predicate granularity, and the v1 scope boundaries with their
un-lift triggers).
scope camelot
inertial water-status proposed session-9c1
inertial <pred> is carriage-only — like functional <pred>, it mints no
ground atom and rides its own provenance — and it is carried through the
compiled world, forks, merges, and the durable lorestore exactly like
functional. A predicate never declared inertial is untouched: its open
values keep projecting through the plain open-interval reader above,
byte-identically to before.
On an inertial slot the effective upper boundary of an open value is
derived at projection time from which competing assertions are live, never
clipped into the stored span. This composes into the same three-valued
projection with no fourth outcome: a later distinct value is a transition
(silent) when its anchor certainly follows, a contest
(continuity.fluent-order-undeclared, warn) when the two anchors’ order is
undeclared, and a clash (continuity.fluent-contradiction, error) only
when two distinct values certainly co-hold (the same assertion point, or
authored overlapping intervals). An inertial value with no since anchor
raises continuity.inertia-unanchored (warn, koan 4). Because capping is read
lazily through each span’s standing — and applies only while the declaration
itself is certain — defeat needs no recompile: defeating the declaration
reverts the slot to the plain open-interval projection (the value never
vanishes), defeating a defeater re-extends the earlier value, and defeating the
originating assertion drops only its span. The full ruling, interactions, and
v1 scope boundaries are docs/decisions.md DL-0081 and
docs/design-notes/inertia-fluent-persistence.md.
When a predicate identity joins two names, this projection reads the
same certain predicate-identity component as the ordinary fluent check. Inertia
uses union semantics: one certain inertial declaration promotes the merged
property, and a structured predicate-identity.policy-promoted warning records
that promotion. A proposed identity remains possible only and does not combine
certain timelines. Once the identity is certain, spans from both names enter
one overlap sweep; an overlap is the existing
continuity.fluent-contradiction, carrying the identity assertion in its
support. Defeating the identity restores the separate timelines and removes
only the derived persistence and contradiction that depended on it.
Point-in-time dli.query and CLI query --as-of project fluent facts with
the same slot semantics as author.as_of, then recompute derived facts. A
timeless assertion remains independent of a temporal assertion of the same
atom. At an era cut, an event certainly placed in an earlier or later era is
ordered on the corresponding side of the cut. An event in the cut era stays
unordered relative to it; a possible membership does not establish an order.
The era ordering must follow certain era-to-era edges. A path through an event
can order two points without ordering the whole eras around them.
If the inferred order makes a cycle, both query and author.as_of refuse the
projection instead of treating both directions as certain.
Unordered plain bounds remain possible and share consistent ordering
assumptions across the query. If unresolved inertial ordering, a possible
chronology ruling, or a cycle prevents a faithful projection of the query, it is
refused with temporal-indeterminate rather than returning a guessed answer.
Without predicate-identity links, an unresolved fluent outside the query
predicate’s rule-dependency closure does not block the query. Relevant
temporal states are still projected. With predicate identities, the fallback
is conservative and refuses; a cycle also blocks the query.
The current evaluator may leave a value possible when multiple alternative
spans jointly cover every ordering; it does not promote their disjunction to
certain solely for that reason.
Compiled worlds serialized before temporal source mappings were recorded must
be recompiled before point-in-time queries. Older compiled worlds with era
memberships but no era-declaration mapping must also be recompiled from source
or the lorestore before checking or projecting them: a model cannot safely
recover an era declaration from a same-named ordinary fact.
3. Composition with the annotation algebra
Time goes in the atom; provenance stays the annotation; they must not
entangle. A state is atom × span, and its support answers a strictly
separate question: which authorial events back this claim about that span.
The two kinds of indeterminacy are different in kind and must stay distinguishable:
- Temporal indeterminacy — unordered events — is in-world incompleteness,
repaired by declaring a
before-fact. That is an act of world-modeling, owned at the continuity surface. - Authorial indeterminacy — open variables — is meta-level, repaired by a ruling. That is an act of authorship, owned at the ledger.
Collapsing them would make “the tide during an unordered scene” indistinguishable
from “an unratified fact” — but they call for different repairs, by different
owners, answering different queries. So they compose factorially: every
temporal query returns a (temporal verdict × support verdict) pair —
“holds-certainly, provisional (D-0072)”; “order-indeterminate under every
valuation.”
And the before-facts themselves carry annotations, like any fact:
declaring an order is a ruling. So the meta-level uniformly governs the object
level without leaking into it. One meta-time is preserved intact; story-time is
object data with structure, never a second transaction dimension over the store.
Because an order is a ruling, precedence itself is read through standing. The
raw graph reachability — is there a path of before-facts? — is standing-blind:
it answers Before for a merely-proposed edge exactly as for a ratified one. That
is the wrong answer for any checker that must not let an unratified order harden a
conclusion (the season slack, an anachronism verdict). So the standing-aware read
returns one of three: certain-order, when every before-fact on the connecting
path is certain; possible-order, when a path exists but rests on at least one
proposed edge — it holds only if that ruling lands; and unordered-under-uncertainty,
when no path relates the pair under any standing. A merely-proposed before-fact
thus yields possibly-before, never a hard before — the same discipline the
projection of §2 already applies to fluents, now applied to the order relation
that anchors them. Event order inferred from during memberships retains the
support of both memberships and the entire era-order path. A path is possible
only if its combined support is jointly consistent with the constraint theory.
Timeline reads and fluent projections use the certain graph; hard cycle
findings require a certain cycle and name its supporting rulings. Possible
precedence remains available to the standing-aware order reader.
4. What it unlocks: eight capabilities
The doctrine that organizes all eight: production order is the listener’s dimension; story-time is the world’s. The episode fold — the threaded, order-dependent computation that resolves last-writer-wins slots and opens and closes promises — still runs in production order, untouched. What changes is that folded atoms now carry story-time spans, so story-time becomes a projection dimension over the same store. A flashback episode writes its reveals at its production slot (that is when the listener learns) and its facts at its story anchor (that is when the world was so): two checks, two dimensions, both on existing machinery.
- Episode-to-story-time binding. The episode schema gains an
atanchor (era, optional after/before event anchors, phase bindings), distinct fromorder. An unanchored episode follows its predecessor inthe-beleaguered-present— the current behavior, preserved. Scenes may override: a flashback scene carries its own anchor. - Facts with spans. Per §2; the default rule makes migration free.
- Who-knew-what-when.
knowledgerows become knowledge states initiated at the episode’s story position; queryknows(who, fact) as-of <era | event | episode>. The listener is a distinguished knower whose ledger runs on production order — which is exactly where the staging constraints (the full-team floor, the no-deep-time rule, the reveal-placement constraints) attach. - Anachronism checking. An era-anchored episode or scene projects
state-at(anchor)and runs the continuity classes against that projection instead of the fold head. Anorder-indeterminateresult surfaces as its own violation class — “depends on an undeclared order; declarebefore(…)or accept” — which doubles as a ruling-request generator. - The partial-order checker over a full season. The intended design treats
ordering rules as data and reports the earliest and latest legal slot for each
episode. The CLI and MCP checks do not currently apply
over orderorover seasondeclarations; see the limitation below. - Intra-era precedence, real and kernel-queryable.
before-facts are kernel facts andhappens-beforeis a kernel rule under CI; a downstream toolchain emission becomes a consumer of this layer instead of its only expression. - Two story-times explicit, narration inert. The vantage of §1(iii) exists,
carries the knowledge asymmetry and the
narration-frameguardrail, and is forbidden dynamics by type rather than by remembering. - Cyclic time checkable. A timing premise resolves against the scene’s phase bindings instead of being taken on faith: a ward requiring the slack tide, spoken in a flood-bound scene, is a checker error; cycle-walk consistency holds within declared contiguity; and phase arc continuity — the moon cannot jump mid-episode — comes free from the walk check.
5. The timeline as a design tool
This is where the model pays the author directly. A client surface renders, over the same store:
- an era-lane timeline — eras as lanes, events as points with their partial-order edges drawn (and unordered pairs visibly unordered), episode and scene placements as markers, state spans as horizontal bars;
- a two-track view — the story-time track over the production-order track, in which a flashback is a visible crossing edge rather than an invisible convention;
- a who-knows-what matrix — characters against facts, the listener included, at any cursor position;
- violations in place — a before-cycle, an anachronism, a staging breach, each rendered where it occurs rather than in a separate report.
The scrubbable cursor is the tool: drag through story-time and watch world-state and every character’s knowledge change. The subcommands:
deloreis timeline show [--at <era | event | episode>]deloreis timeline order— the partial-order report: cycles, plus every unordered pair. Consumers may correlate these with check findings; the chronology report itself does not claim that every pair is load-bearing;deloreis timeline knows --at <pos> [--who <id>]deloreis timeline episodes --check— ordering constraints and slack;--proposeis reserved, gated on the T15 flip (08-reasoning.md);deloreis timeline phases <episode>
6. Surface forms
The chronology layer — the era spine, events and their before-facts, the inert narration vantage, the cycles, and states with spans:
# grammar: v0 (unfrozen; AST normative)
scope camelot
era uthers-time < arthurs-reign < after-camlann decided d-0001
# acts are NOT eras — narrative structure lives in its own layer and
# references chronology; the spine gains eras only by ruling
event the-sword-drawn during uthers-time decided d-0002
event the-round-table-founded during arthurs-reign decided d-0002
event the-grail-quest during arthurs-reign decided d-0003
event the-battle-of-camlann during arthurs-reign proposed
before the-round-table-founded -> the-battle-of-camlann decided d-0003
# no `offset`; the-grail-quest shares arthurs-reign with the-round-table-founded
# and no before-fact orders them, so happens-before(x, y) answers `unordered`
narration vantage after-camlann
# inert: declaring an event in this vantage era is a type error [narration-dynamics]
cycle moon(29) decided d-0009
cycle season(4) = spring | summer | autumn | winter decided d-0009
# a rankless wheel: succ/pred/across/distance exist; `<`, sort, max are type
# errors — no epoch, no calendar
state excalibur.keeper = in-the-stone until the-sword-drawn decided d-0008
state excalibur.keeper = the-king since the-sword-drawn decided d-0008
# an interregnum flashback checks clean AND the reign checks clean — the two
# claims hold over disjoint spans; unanchored facts default to the present era
Ordering-rule declarations can be stored, but the CLI and MCP checks do not currently apply them:
# grammar: v0 (unfrozen; AST normative)
rule uthers-time-held-back over order decided d-0009
: listener-learns(during uthers-time _) requires act > 1
# This declaration does not currently enforce a production-order constraint.
An episode binds a production order and a story-time at anchor separately, and
a scene may carry its own anchor to cross into story-time — a flashback. The following illustrative sketch shows that two-dimension picture; it is
not a supported .dli episode syntax:
episode e12 "The Last Battle" order 12 {
at after-camlann after e11 # default if omitted: follows prior
phase season := autumn -> winter # a walk, checked; not a timestamp
scene s3 flashback {
at arthurs-reign after the-round-table-founded before the-battle-of-camlann
# s3's world checks run against state-at(this anchor), not the fold head;
# its reveals still land at production slot 12 on the listener ledger
}
}
7. What stays out, and why
The model is deliberately narrow. Each exclusion answers a specific temptation.
- Metric time and calendars. The world has no ratified calendar, and the cycles’ no-epoch rule keeps the back door shut. Time is qualitative by ruling, not by omission.
- DatalogMTL / temporal Vadalog machinery. That literature solves metric temporal reasoning and pays for it in complexity up to EXPSPACE-hardness. This time is causal-ordinal by ruling — finitely many named point events, qualitative precedence, era membership — so the whole need is plain datalog over a finite partial order, free on machinery already built.
- Full Allen interval algebra. Thirteen relations and NP-hard general consistency buy nothing here: spans are anchored to named events, so every interval relation reduces to point precedence — the tractable point algebra, complete under simple propagation.
- Event-calculus solvers. The model takes the event-calculus shape (initiate / terminate / inertia) and none of the solving: events are declared, never abduced. No circumscription, no narrative abduction.
- Bitemporal storage. There is one meta-time, the ledger; ratification is a valuation, not a clock. No second transaction dimension.
- Narration-time dynamics. The ticking narrator’s clock is rejected, and the model makes that rejection structural: one inert vantage, a knowledge asymmetry, and nothing else, ever.