De Lore Is
De Lore Is is a local continuity checker for fictional worlds. It keeps track of the people, places, events, and rules you give it, checks them for contradictions, and can explain which decisions support an answer.
It’s for writers, worldbuilders, and people building tools that work with
fiction. You can write lore in .dli files and check it from the command line,
or connect an MCP client, such as an AI assistant, to work with a local world.
The checks don’t call a model.
You decide what to record and which rules apply. De Lore Is checks structured lore; it doesn’t read a manuscript and find plot holes on its own.
Try it
Install from crates.io with Rust 1.96 or later:
cargo install deloreis --locked
Save the following as story.dli:
scope story
ratify d-0001
person arthur "Arthur" decided d-0001
place camelot "Camelot" decided d-0001
place avalon "Avalon" decided d-0001
functional home decided d-0001
fact arthur home camelot decided d-0001
fact arthur home avalon decided d-0001
Here d-0001 names the decision supporting the declarations and facts.
See Concepts for how decided,
ratified, and proposed relate.
Then run:
deloreis check story.dli
The finding lists both values: [avalon, camelot]. The functional line tells
De Lore Is that Arthur can have only one home. Without that rule, the two
statements aren’t automatically a conflict. The command exits with status 1
because it found a problem.
To find matching homes, run deloreis query 'home($person, camelot)' --corpus story.dli.
$person is a variable; the quotes keep your shell from expanding
it. The query warns while the corpus still has errors.
Work with a world
Create a project with deloreis init my-story. To work with it through an MCP
client such as Claude Code, follow the quickstart to
connect the local server, add facts, and catch a contradiction. The walkthrough
shows how to ask the client to make the tool calls.
In a project, De Lore Is keeps the decisions behind your claims, including claims you later revise. You can keep accounts unresolved, inspect why something is considered canon, and explore changes on a separate branch. The project keeps the order in which claims and rulings were added; it does not infer what each character knew at a particular point in the story.
- Holmes casebook — follow a sourced example through conflicts, revisions, and alternatives.
- Modelling guide — choose rules and record sources.
- Bring your own notes — start from a series bible.
- Concepts — the terms used by the tools.
- Agent guide — authoring tools and session workflow.
- Contributing — working on the project.
Linux x86-64 and macOS ARM archives are available on the releases page. De Lore Is is MIT licensed.
Use with a writing assistant
Install the consumer skill with its first-session reference:
npx skills add jeffreykentjr/deloreis --skill deloreis
The canonical package teaches the local MCP workflow; it does not install the runtime. The quickstart covers that. Skill distribution and updates are recorded in agent-skill-distribution.
Browse the Holmes wiki to explore the example by person, place, or relationship.
Quickstart
Install De Lore Is with Cargo:
cargo install deloreis --locked
You can also download a Linux x86-64 or macOS ARM archive from the releases page. Unpack it and put bin/deloreis somewhere on your PATH. Each archive has a SHA-256 checksum alongside it.
You’ll also need an MCP client for this walkthrough; the example below uses
Claude Code. If you installed with Cargo, make sure Cargo’s binary directory
is on your PATH.
Create a new project and register the server from that directory:
deloreis init my-story
cd my-story
claude mcp add --scope project deloreis -- "$(command -v deloreis)" mcp --project "$PWD"
init also suggests importing a .dli file; that is optional for this
walkthrough because the calls below build a blank world. Restart the client or
reload its MCP configuration. The project registration
writes .mcp.json in this directory. Other MCP clients can use the same
command and arguments; point --project at the directory containing
deloreis.toml. The server runs locally over stdio. deloreis mcp --project "$PWD" --tools all exposes the extended tool set.
After importing, work with the project’s stored world through MCP. The CLI
check and query commands read .dli files, rather than opening the project
store. To get text back, start the server with --tools all and call
dli.corpus.export with {"document":"world","format":"rendered"}. Save
the returned rendered documents as .dli files before checking or querying
them with the CLI.
Make the first ruling
Ask your client to make the following tool calls, in order. Substitute the
returned workspace ID for WORKSPACE. Read the findings returned by each call.
The blocks show tool arguments, not shell commands.
-
Call
author.openwith{"scope":"world"}. -
Call
author.add_entitythree times, usingWORKSPACE:{"workspace":"WORKSPACE","kind":"person","id":"wren","name":"Wren","provenance":{"kind":"proposed","origin":"quickstart"}}{"workspace":"WORKSPACE","kind":"place","id":"thornfield","name":"Thornfield","provenance":{"kind":"proposed","origin":"quickstart"}}{"workspace":"WORKSPACE","kind":"place","id":"ashby","name":"Ashby","provenance":{"kind":"proposed","origin":"quickstart"}} -
Declare the slot single-valued with
author.declare_functional:{"workspace":"WORKSPACE","pred":"home","provenance":{"kind":"proposed","origin":"quickstart"}} -
Call
author.record_decisionwith{"workspace":"WORKSPACE","decision":"d-0001"}. -
Call
author.add_fact:{"workspace":"WORKSPACE","entity":"wren","key":"home","value":"thornfield","provenance":{"kind":"cited","decisions":["d-0001"],"state":"ratified"}} -
Call
author.materializewith{"workspace":"WORKSPACE"}. This commits the first ruling and the staged declarations.
Catch a contradiction
-
Call
author.record_decisionwith{"workspace":"WORKSPACE","decision":"d-0002"}. -
Call
author.add_fact:{"workspace":"WORKSPACE","entity":"wren","key":"home","value":"ashby","provenance":{"kind":"cited","decisions":["d-0002"],"state":"ratified"}}This call can already report
fact.contradiction; leave the conflicting fact staged for the next check. -
Call
author.checkwith{"workspace":"WORKSPACE"}. Its findings includecontinuity.fd-violation, both values, and rulingsd-0001andd-0002.author.materializerefuses this conflicting staged fact. Useauthor.discardto abandon the staged changes. Decisions are recorded immediately, so discarding a workspace does not undo them.
The Holmes casebook continues with contested
accounts, a retcon, a what-if branch, and replay. See the
concepts and agent guide for what the terms and
other tools mean. To follow the casebook’s branch and rule calls, start the MCP
server with --tools all.
What should we call Watson?
To follow the tool calls throughout the casebook, start the MCP server with
deloreis mcp --project <directory> --tools all.
The part I title of A Study in Scarlet names its narrator John H. Watson. In The Man with the Twisted Lip, his wife offers to send “James” to bed while Watson is present.
Is James another name for Watson, a slip, or something else? The story does not settle that question. For this example, we will make a deliberately strict choice: treat John and James as competing first names, and say a person can have only one. That choice belongs to us, not to Conan Doyle’s text.
De Lore Is records the rule and the John claim with the decisions that support
them. A decision is a named ruling: d-0006 is our modelling rule, and
d-0001 points to A Study in Scarlet. ratified records that we accepted
the claim; later rulings can still change what follows. functional means at
most one value for each person. Concepts explains the terms.
These are the lines returned by the authoring tools:
functional first-name ratified d-0006
watson.first-name = john ratified d-0001
Now we stage the alternative, using d-0003 for The Man with the Twisted Lip:
watson.first-name = james ratified d-0003
The check returns this finding (with its other metadata omitted):
continuity.fd-violation: canon breaks the functional dependency on 'first-name': watson carries 2 distinct certain values [james, john]
supporting decisions: d-0001, d-0003
That result does not decide which name a writer should use. It identifies the choice that needs attention and names the sources behind it. A writer could keep both as names people use instead of treating them as mutually exclusive first names. The source notes explain the decision numbers and the limits of the evidence.
Run it yourself
From the repository root, with deloreis installed, run:
python3 examples/holmes/try_casebook.py name
This replays the casebook in a temporary project and prints:
continuity.fd-violation
supporting decisions: d-0001, d-0003
The calls
[
{
"tool": "author.add_fact",
"arguments": {
"workspace": "ws-2",
"entity": "watson",
"key": "first-name",
"value": "james",
"provenance": {
"kind": "cited",
"decisions": [
"d-0003"
],
"state": "ratified"
}
}
},
{
"tool": "author.check",
"arguments": {
"workspace": "ws-2"
}
}
]
Where was Watson wounded?
In A Study in Scarlet, Watson says a Jezail bullet hit his shoulder. In The Sign of the Four, he describes a Jezail wound in his leg.
Those passages are often treated as a discrepancy. They do not establish that
the two references describe the same injury. We can record the accounts without
choosing between them. Here proposed marks an account as unresolved; its
origin label identifies the story in our source notes.
The authoring tools rendered the claims as:
watson.reported-wound-location = shoulder proposed study-in-scarlet
watson.reported-wound-location = leg proposed sign-of-four
A query reports:
reported-wound-location(watson, shoulder): possible
reported-wound-location(watson, leg): possible
possible means an account remains open in this model. It does not mean the
account is false or that De Lore Is has decided the wounds are the same. If a
writer chooses a single-wound interpretation, they can add that rule and make
a ruling later.
Run it yourself
From the repository root, with deloreis installed, run:
python3 examples/holmes/try_casebook.py wounds
This replays the casebook in a temporary project and prints:
reported-wound-location(watson, shoulder): possible
reported-wound-location(watson, leg): possible
The calls
[
{
"tool": "author.add_fact",
"arguments": {
"workspace": "ws-1",
"entity": "watson",
"key": "reported-wound-location",
"value": "shoulder",
"provenance": {
"kind": "proposed",
"origin": "study-in-scarlet"
}
}
},
{
"tool": "author.add_fact",
"arguments": {
"workspace": "ws-1",
"entity": "watson",
"key": "reported-wound-location",
"value": "leg",
"provenance": {
"kind": "proposed",
"origin": "sign-of-four"
}
}
},
{
"tool": "author.materialize",
"arguments": {
"workspace": "ws-1",
"rendered": true
}
},
{
"tool": "author.query",
"arguments": {
"workspace": "ws-1",
"goal_atom": {
"pred": "reported-wound-location",
"args": [
{
"Sym": "watson"
},
{
"Sym": "shoulder"
}
]
},
"layer": "all"
}
},
{
"tool": "author.query",
"arguments": {
"workspace": "ws-1",
"goal_atom": {
"pred": "reported-wound-location",
"args": [
{
"Sym": "watson"
},
{
"Sym": "leg"
}
]
},
"layer": "all"
}
},
{
"tool": "author.why",
"arguments": {
"workspace": "ws-1",
"goal_atom": {
"pred": "reported-wound-location",
"args": [
{
"Sym": "watson"
},
{
"Sym": "shoulder"
}
]
}
}
},
{
"tool": "author.why",
"arguments": {
"workspace": "ws-1",
"goal_atom": {
"pred": "reported-wound-location",
"args": [
{
"Sym": "watson"
},
{
"Sym": "leg"
}
]
}
}
}
]
Putting cases in order
A Scandal in Bohemia places Watson’s married visit to Holmes on 20 March 1888. The Sign of the Four puts his meeting with Mary Morstan on a September evening. Three clues point toward 1888: an advertisement of 4 May 1882 was about six years before her visit, her father’s disappearance in December 1878 was nearly ten years earlier, and she has received six yearly pearls.
Those clues make September 1888 a strong interpretation, though the story does not give the year outright. If Watson’s wife in the earlier story is Mary, he is married to her before he meets her. We record both interpretations as ordering claims: Mary meets Watson before the married visit, and the March visit comes before their September meeting. The latter is our dating ruling, not a date printed in the story. De Lore Is reports:
continuity.chronology-cycle: the chronology contradicts itself: an ordering cycle over 2 edge(s) — scandal -> mary-case, mary-case -> scandal
supporting decisions: d-0002, d-0006, d-0007
The conflicting claims are staged for the check and not committed. If a writer does not accept the 1888 interpretation or the identification of Watson’s wife, the two events can remain unordered instead.
The model puts both events in the 1880s, the Reichenbach encounter in 1891, and Holmes’s return in 1894. We record the order of those eras under an editorial decision. The timeline follows the recorded order; it does not fill in an order between the two 1880s cases.
The relevant model lines are:
era years-1880s "The 1880s" ratified d-0006
era year-1891 "1891" ratified d-0006
era year-1894 "1894" ratified d-0006
event scandal "A Scandal in Bohemia" during years-1880s ratified d-0007
event mary-case "Mary Morstan consults Holmes" during years-1880s ratified d-0002
event reichenbach-encounter "The Reichenbach encounter" during year-1891 ratified d-0004
event return "Holmes returns" during year-1894 ratified d-0005
before years-1880s -> year-1891 ratified d-0006
before year-1891 -> year-1894 ratified d-0006
The committed model’s Markdown timeline is:
Known order
These are the nearest known orderings. Follow the arrows for later points; points without a connecting path are unordered.
Points: mary-case, reichenbach-encounter, return, scandal, year-1891, year-1894, years-1880s
mary-case→reichenbach-encountermary-case→year-1891reichenbach-encounter→returnreichenbach-encounter→year-1894scandal→reichenbach-encounterscandal→year-1891year-1891→returnyear-1891→year-1894years-1880s→reichenbach-encounteryears-1880s→year-1891
Follow the arrows to see the known order. Points with no connecting path are unordered, not necessarily simultaneous.
Run it yourself
From the repository root, with deloreis installed, run:
python3 examples/holmes/try_casebook.py timeline
This replays the casebook in a temporary project and prints:
continuity.chronology-cycle
supporting decisions: d-0002, d-0006, d-0007
The calls
[
{
"tool": "author.add_event",
"arguments": {
"workspace": "ws-1",
"id": "scandal",
"name": "A Scandal in Bohemia",
"era": "years-1880s",
"provenance": {
"kind": "cited",
"decisions": [
"d-0007"
],
"state": "ratified"
}
}
},
{
"tool": "author.add_event",
"arguments": {
"workspace": "ws-1",
"id": "mary-case",
"name": "Mary Morstan consults Holmes",
"era": "years-1880s",
"provenance": {
"kind": "cited",
"decisions": [
"d-0002"
],
"state": "ratified"
}
}
},
{
"tool": "author.add_event",
"arguments": {
"workspace": "ws-1",
"id": "reichenbach-encounter",
"name": "The Reichenbach encounter",
"era": "year-1891",
"provenance": {
"kind": "cited",
"decisions": [
"d-0004"
],
"state": "ratified"
}
}
},
{
"tool": "author.add_event",
"arguments": {
"workspace": "ws-1",
"id": "return",
"name": "Holmes returns",
"era": "year-1894",
"provenance": {
"kind": "cited",
"decisions": [
"d-0005"
],
"state": "ratified"
}
}
},
{
"tool": "author.add_before",
"arguments": {
"workspace": "ws-1",
"earlier": "years-1880s",
"later": "year-1891",
"provenance": {
"kind": "cited",
"decisions": [
"d-0006"
],
"state": "ratified"
}
}
},
{
"tool": "author.add_before",
"arguments": {
"workspace": "ws-1",
"earlier": "year-1891",
"later": "year-1894",
"provenance": {
"kind": "cited",
"decisions": [
"d-0006"
],
"state": "ratified"
}
}
},
{
"tool": "author.add_before",
"arguments": {
"workspace": "ws-1",
"earlier": "mary-case",
"later": "scandal",
"provenance": {
"kind": "cited",
"decisions": [
"d-0002"
],
"state": "ratified"
}
}
},
{
"tool": "author.add_before",
"arguments": {
"workspace": "ws-1",
"earlier": "scandal",
"later": "mary-case",
"provenance": {
"kind": "cited",
"decisions": [
"d-0002",
"d-0006",
"d-0007"
],
"state": "ratified"
}
}
},
{
"tool": "author.check",
"arguments": {
"workspace": "ws-1"
}
}
]
The return from Reichenbach
At the end of The Final Problem, Watson concludes that Holmes died at Reichenbach. In The Empty House, Holmes returns and explains that he escaped. We can revise the earlier ruling while keeping its history.
First, our editorial decision d-0008 accepts Watson’s conclusion, supported
by source d-0004. The authoring tool renders the claim as:
holmes.reichenbach-outcome = died ratified d-0004 d-0008
The query returns:
reichenbach-outcome(holmes, died): certain
After the return, decision d-0009 defeats d-0008, and a new claim cites
The Empty House as d-0005. The new claim is:
holmes.reichenbach-outcome = survived ratified d-0005 d-0009
The new query returns:
reichenbach-outcome(holmes, survived): certain
When we ask why the old claim no longer holds, the explanation says:
status: overturned
support: d-0004, d-0008
d-0009 defeats d-0008
The old claim is overturned under the new rulings. The claim and the earlier decision are still available to explain what changed. This is a model of a revised account: the later story does not say Holmes died and came back to life.
In the calls below, claim_id identifies the recorded claim to revise. A
{"Sym": "holmes"} value is the wire format for the symbol holmes.
Run it yourself
From the repository root, with deloreis installed, run:
python3 examples/holmes/try_casebook.py return
This replays the casebook in a temporary project and prints:
earlier claim: overturned
supporting decisions: d-0004, d-0008
The calls
[
{
"tool": "author.record_decision",
"arguments": {
"workspace": "ws-4",
"decision": "d-0008"
}
},
{
"tool": "author.add_fact",
"arguments": {
"workspace": "ws-4",
"entity": "holmes",
"key": "reichenbach-outcome",
"value": "died",
"provenance": {
"kind": "cited",
"decisions": [
"d-0004",
"d-0008"
],
"state": "ratified"
}
}
},
{
"tool": "author.materialize",
"arguments": {
"workspace": "ws-4"
}
},
{
"tool": "author.query",
"arguments": {
"workspace": "ws-4",
"goal_atom": {
"pred": "reichenbach-outcome",
"args": [
{
"Sym": "holmes"
},
{
"Sym": "died"
}
]
},
"layer": "all"
}
},
{
"tool": "author.branch",
"arguments": {
"workspace": "ws-4",
"action": "create",
"name": "holmes-stayed-dead",
"from": "main"
}
},
{
"tool": "author.defeat",
"arguments": {
"workspace": "ws-4",
"claim_id": "sha256:970cd2741b4bfe5631c5514e3fe8a8b0906ea68905cba85d9d12baf2316fabbe",
"decision": "d-0009",
"defeated_decision": "d-0008"
}
},
{
"tool": "author.add_fact",
"arguments": {
"workspace": "ws-4",
"entity": "holmes",
"key": "reichenbach-outcome",
"value": "survived",
"provenance": {
"kind": "cited",
"decisions": [
"d-0005",
"d-0009"
],
"state": "ratified"
}
}
},
{
"tool": "author.materialize",
"arguments": {
"workspace": "ws-4"
}
},
{
"tool": "author.query",
"arguments": {
"workspace": "ws-4",
"goal_atom": {
"pred": "reichenbach-outcome",
"args": [
{
"Sym": "holmes"
},
{
"Sym": "survived"
}
]
},
"layer": "all"
}
},
{
"tool": "author.query",
"arguments": {
"workspace": "ws-4",
"goal_atom": {
"pred": "reichenbach-outcome",
"args": [
{
"Sym": "holmes"
},
{
"Sym": "died"
}
]
},
"layer": "all"
}
},
{
"tool": "author.why",
"arguments": {
"workspace": "ws-4",
"goal_atom": {
"pred": "reichenbach-outcome",
"args": [
{
"Sym": "holmes"
},
{
"Sym": "died"
}
]
}
}
},
{
"tool": "author.open",
"arguments": {
"scope": "world",
"branch": "holmes-stayed-dead"
}
},
{
"tool": "author.query",
"arguments": {
"workspace": "ws-5",
"goal_atom": {
"pred": "reichenbach-outcome",
"args": [
{
"Sym": "holmes"
},
{
"Sym": "died"
}
]
},
"layer": "all"
}
}
]
What if Holmes stayed dead?
The author.branch call below needs the MCP server’s --tools all profile.
Before revising the Reichenbach ruling, we made a branch called
holmes-stayed-dead. A branch is a separate line of work. The main line can
accept Holmes’s return while the branch keeps the earlier starting point.
The earlier claim was:
holmes.reichenbach-outcome = died ratified d-0004 d-0008
On that branch, the same query returns:
reichenbach-outcome(holmes, died): certain
The branch is a place to explore the consequences of a story in which Watson’s conclusion was right. It does not change the main line. The branch shown here keeps the earlier ruling; it does not yet invent the later stories that would follow from it.
Run it yourself
From the repository root, with deloreis installed, run:
python3 examples/holmes/try_casebook.py branch
This replays the casebook in a temporary project and prints:
reichenbach-outcome(holmes, died): certain
The calls
[
{
"tool": "author.branch",
"arguments": {
"workspace": "ws-4",
"action": "create",
"name": "holmes-stayed-dead",
"from": "main"
}
},
{
"tool": "author.open",
"arguments": {
"scope": "world",
"branch": "holmes-stayed-dead"
}
},
{
"tool": "author.query",
"arguments": {
"workspace": "ws-5",
"goal_atom": {
"pred": "reichenbach-outcome",
"args": [
{
"Sym": "holmes"
},
{
"Sym": "died"
}
]
},
"layer": "all"
}
}
]
When a rule causes the conflict
The author.add_rule call below needs the MCP server’s --tools all profile.
Suppose we interpret the name James used by Watson’s wife as referring to Watson, while the part I title names John as his first name. Those two notes can coexist. Now imagine a rule saying the name she uses must be his first name. This is a deliberately questionable rule: a person might use a nickname.
The authoring tool renders that rule as:
rule wife-name-is-first-name ratified d-0006
first-name($person, $name) :- name-used-by-wife($person, $name)
The variables $person and $name stand for a person and a name. The rule
uses this recorded fact:
watson.name-used-by-wife = james ratified d-0003
The check follows the rule and reports:
continuity.derived-fd-violation: the evaluated world derives a functional-dependency break on 'first-name': watson carries 2 distinct certain values [james, john] once rules fire
citations: d-0001, d-0003, d-0006
d-0001 and d-0003 identify the two story sources, and d-0006 identifies
our rule. The conflict did not require us to type the James first-name claim:
it followed from the rule and the note. In a real project, the right repair
might be to change the rule rather than change either source.
Run it yourself
From the repository root, with deloreis installed, run:
python3 examples/holmes/try_casebook.py rule
This replays the casebook in a temporary project and prints:
continuity.derived-fd-violation
supporting decisions: d-0001, d-0003, d-0006
The calls
[
{
"tool": "author.add_rule",
"arguments": {
"workspace": "ws-3",
"name": "wife-name-is-first-name",
"head": {
"pred": "first-name",
"args": [
{
"Var": "person"
},
{
"Var": "name"
}
]
},
"body": [
{
"atom": {
"pred": "name-used-by-wife",
"args": [
{
"Var": "person"
},
{
"Var": "name"
}
]
},
"negated": false
}
],
"provenance": {
"kind": "cited",
"decisions": [
"d-0006"
],
"state": "ratified"
}
}
},
{
"tool": "author.check",
"arguments": {
"workspace": "ws-3"
}
}
]
Sources for the Holmes model
These are Conan Doyle’s stories, not a chronology taken from a fan reference. Decision IDs identify the source passages used by the examples; they do not mean the narrator is always right. Interpretations made by the model are identified separately in the casebook.
| Decision | Text | What the passage says |
|---|---|---|
d-0001 | A Study in Scarlet, part I, chapter 1 | The title names John H. Watson. Watson says he was “struck on the shoulder by a Jezail bullet” at Maiwand. |
d-0002 | The Sign of the Four | Watson is “nursing my wounded leg” and says a Jezail bullet went through it. The text does not establish that this is the same wound as the one at Maiwand. |
d-0003 | The Man with the Twisted Lip | Watson’s wife offers to send “James” to bed in a conversation in which Watson is present. The text does not explicitly say James is his legal first name. |
d-0004 | The Final Problem | Watson concludes that Holmes and Moriarty died at Reichenbach. |
d-0005 | The Empty House | Holmes returns and says of the chasm, “I never was in it.” |
d-0006 | Editorial modelling choice | Records the era ordering, the single-first-name constraint, and the example rule relating a name used by a spouse to a first name. |
d-0007 | A Scandal in Bohemia | Dates Watson’s married visit to Holmes to 20 March 1888. |
d-0008 | Editorial ruling based on The Final Problem | Accept Watson’s conclusion that Holmes died, as an initial ruling. |
d-0009 | Editorial ruling based on The Empty House | Supersede that ruling after Holmes returns. |
The unresolved wound claims use the proposal support labels
h.study-in-scarlet and h.sign-of-four for the first two sources above. These
labels identify the accounts in this example; a proposal support label is not
a decision citation and does not automatically identify a source in another
world.
The model may ask what happens if a single-wound or single-first-name rule is adopted. Those are choices for the exercise, not conclusions established by the passages. Likewise, Watson’s earlier conclusion about Reichenbach is an account later revised, not evidence that Holmes actually died and returned to life.
For chronology, A Scandal in Bohemia places Watson married by 20 March 1888. The Sign of the Four describes a September evening when Watson is with Mary Morstan. She dates an advertisement to 4 May 1882, about six years before her visit; her father disappeared in December 1878, nearly ten years before the visit; and she has received six yearly pearls. Together these point toward September 1888, but the visit’s year and the identification of Watson’s wife are still interpretive choices.
In The Sign of the Four, the wound and Holmes’s monographs appear in chapter 1; Mary’s account is in chapter 2; and the Sholto family account is in chapter 4. Jonathan Small and Tonga appear later in the same story.
A few notes for a Holmes world
- A Study in Scarlet, part I, chapter 1: the narrator is John H. Watson, a doctor who served at Maiwand. Murray helped him reach the British lines. Watson says he was hit in the shoulder by a Jezail bullet. Stamford introduces the possibility of sharing rooms with Sherlock Holmes.
- The Sign of the Four, chapter 1: Watson describes a Jezail wound in his leg. Keep the two wound descriptions open; the passages do not establish that they refer to the same injury.
- The Man with the Twisted Lip: Watson’s wife offers to send “James” to bed. If we decide to interpret James as his first name and allow only one first name, check that interpretation against the name John.
- The Final Problem: Watson concludes that Holmes died at Reichenbach. The Empty House: Holmes returns and describes his escape. Keep Watson’s earlier conclusion inspectable when accepting the later account.
The source index links to the texts and records the decision IDs. The scripted example includes further details from those stories; it does not infer them automatically from this short note.
Modelling a world
Start with a question you would like to answer, not a complete inventory. A series bible might already say who lives where, when a character joins the crew, and which accounts of an old disaster disagree. Pick a small part of it and record enough to check one decision. You can add more as the story grows.
A claim names a relationship and its participants: home(ada, lighthouse)
says Ada’s home is the lighthouse. Give people, places, and other important
things stable IDs; a display name can be more readable. Keep a note of where
each claim came from. The concepts guide explains the terms
used below.
Choose your constraints
A functional relationship has at most one value for each subject. Declare it
when two values should cause a conflict. A character’s birth date might be
single-valued in your setting; their friends usually are not. Do not declare
home single-valued if your character can have two homes at once.
Values can also change. For a changing relationship, use a state and place its changes in story time. A present home and a former home are not necessarily a contradiction. Here Ada takes over the lighthouse after the storm:
scope lighthouse-story
ratify d-0001
thing lighthouse "Lighthouse" ratified d-0001
person wren "Wren" ratified d-0001
person ada "Ada" ratified d-0001
era before-storm < after-storm ratified d-0001
event storm during after-storm ratified d-0001
state lighthouse.keeper = wren until storm ratified d-0001
state lighthouse.keeper = ada since storm ratified d-0001
since and until name events, not eras. during a declared era starts a
state at that era; it does not end the state automatically when a later era
begins. Use an event as the boundary when one value replaces another. You can
also use author.add_state through MCP.
Put events in time
Use an event for something that happens and an era to group a period. Order what you know. Leave two events unordered if the story does not tell you which came first: the timeline will preserve that uncertainty. An as-of query needs enough ordering to place the relevant changes at the point you ask about. The casebook timeline shows an example where a dating assumption matters.
Record sources and rulings
A decision such as d-0001 names a ruling you record. Keep a source index
beside your world so you can find the episode, chapter, or editorial note behind
it. A decision can cover a whole episode, a scene, or one editorial ruling;
choose a size that makes later revisions understandable. If you may need to
revise one conclusion without withdrawing every claim from an episode, give
that conclusion its own decision as well as a source reference. The casebook
uses separate decisions for the source stories and the Reichenbach ruling.
A proposed claim is an open possibility. A cited claim names its supporting decisions; cited provenance can be provisional or ratified. These labels record how the claim was supplied. Its evaluated status—certain, possible, or dead—also depends on the current decisions and any defeats. A citation must refer to a recorded decision. See Concepts for the distinction and Sources for the Holmes model for one way to keep an index.
Revise an account
Separate a change in the story from a change in your account of it. A character moving house belongs on the timeline. Revising where the character always lived changes which account you accept.
For a revision, ask author.why what supports the old answer. A later decision
can defeat the earlier ruling while preserving the claims and their sources.
Inspect the resulting answer and its explanation. author.defeat and
author.record_decision write directly to the store; they are not staged changes
that author.discard will undo. If you have not chosen the revision yet, explore
it on a separate branch first. The Reichenbach case
and its alternative branch show both approaches.
Rules also have sources. If a rule derives a surprising conclusion, inspect the rule’s support as well as its premises before changing the facts. A rule supplied as a proposal does not establish a conclusion merely because its premises are accepted. The Watson rule example traces a conflict to an interpretation the author can revise.
Grow in small steps
Add a few entities and claims, run a check, and read the findings. If a check reports a conflict, inspect the two values and their support before choosing whether to revise a claim, relax a rule, or leave accounts unresolved. Commit a consistent step, then move to the next part of the story. A branch lets you try an alternative without changing the main line.
People can write and check .dli files directly. An assistant authoring a
world should use the author.* tools so it can inspect findings and commit the
structured changes. The your-world guide shows how to start
with existing notes.
Bring your own notes
You do not need to rewrite a whole series bible before trying De Lore Is. Pick a short section, start a local project, and ask your assistant to record the parts you want to check. The assistant still needs your judgment about what is canon and which constraints belong in your world.
- Install De Lore Is and connect your MCP client using the quickstart.
- Give the assistant a page of notes and say which parts are settled and which are alternative accounts. Tell it what source names or decision numbers to use. Ask it to propose constraints instead of assuming them.
- Have it open a workspace and use
author.*to add entities, claims, and any rules you approve. Ask it to runauthor.check, read the findings, and show you the proposed changes and their sources. - Resolve blocking conflicts together. Then have it call
author.materializeto commit the consistent staged changes. Askauthor.whywhen you want to know what supports an answer.
For general rules and named branches, start the MCP server with --tools all
so the client sees those tools; the agent skill
shows how to change the registration.
Some operations, including author.record_decision and author.defeat, update
the store immediately. Agree on those rulings before the assistant calls them;
author.discard only discards staged work.
The project store is read through MCP. To use CLI check or query on its
contents, start the server with --tools all, call dli.corpus.export with
{"document":"world","format":"rendered"}, and save each returned rendered
document as a .dli file. Pass those files to the CLI; see the
quickstart for client setup.
For example, you could say:
Use these notes to start my world in De Lore Is. Record the settled claims with source references and leave the disputed accounts open. Suggest, but do not assume, any single-value rules. Show me the findings and proposed changes before committing them.
The Holmes notes show a small input. This script replays a reviewed transcription of the name and wound notes through the MCP tools in a temporary project:
python3 examples/holmes/notes_demo.py "$(command -v deloreis)"
It prints the queried results after committing them:
first-name(watson, john): certain
reported-wound-location(watson, shoulder): possible
reported-wound-location(watson, leg): possible
The script is a fixed transcription, not a general-purpose prose importer, and calls no model. The larger casebook script adds more sourced material and checks the other examples. An assistant can help interpret your own notes, but review its proposed claims against your sources.
When you need to change an accepted account, inspect its support before recording the new ruling. Use a branch to explore a possibility separately. The modelling guide covers constraints, time, and decisions.
Continue a chapter
At the start of a writing session, have the assistant open a fresh workspace and query the characters, places, and events relevant to the next scene. Keep the manuscript and style notes in their existing files. DLI supplies the recorded lore; the assistant still reads and writes the prose.
After drafting, ask it to propose only the new factual claims. Use
author.diff to inspect staged additions and their derived consequences, then
author.check to read the findings. Compare the proposed claims with the scene
before materializing them. A clean check only covers the structured claims and
rules you supplied, so review the transcription as well as the result.
Brainstormed ideas need not become settled lore. Leave them proposed, or try a separate branch for an alternative plot. Before continuing in another session, commit the changes you want to keep. A newly opened workspace sees the current committed lore; a workspace already open retains its base.
Make a wiki
With your world in world.dli, export its accepted claims as linked Markdown:
deloreis export --profile static-wiki --corpus world.dli > wiki.json
python3 - <<'PY'
import json
from pathlib import Path
pages = json.loads(Path("wiki.json").read_text())["artifact"]["pages"]
out = Path("wiki")
out.mkdir(exist_ok=True)
for page in pages:
name = page["path"]
if Path(name).name != name or not name.endswith(".md"):
raise ValueError(f"unexpected wiki page path: {name}")
(out / name).write_text(page["content"], encoding="utf-8")
PY
Open wiki/index.md in a Markdown viewer, or publish the pages with a
Markdown site generator. The default export includes claims with certain status; the
Holmes casebook also shows an all-standing view for unresolved and revised claims.
Build a storytelling application
An AI storytelling application can use DLI to check new lore before accepting
it into a persistent world. Keep generation in your application. Run the local
engine over MCP, or use newline-delimited JSON over stdio with
deloreis serve --project <path>. The serving reference
defines requests and responses. With MCP, use --tools all to advertise
general rule and named-branch tools.
Check an addition
- Open a workspace on the project’s committed lore. Query the details relevant to the scene before generating it.
- Extract factual additions from the generated prose. Stage entities, claims,
and any author-approved rules through
author.*, retaining source references. - Inspect
author.diffandauthor.check. Return conflicts and their support to the generator or to a human editor for revision. - Materialize the accepted staged objects. Error findings block materialization; unresolved proposals may produce warnings without blocking it.
Record a rule’s own cited provenance when it represents an accepted world rule.
The optional decision on author.materialize stamps facts, not rules or other
declarations. Do not infer that a successful commit makes every proposal certain.
The agent skill describes the authoring workflow.
Your application controls who may accept lore and record rulings. Some calls,
including author.record_decision and author.defeat, mutate the store
immediately. Keep those behind the application’s review policy; discarding a
workspace does not roll them back.
Keep retrieval
Retrieval can still supply the passages, descriptions, and style examples a model needs. DLI checks the structured claims your application extracts against recorded lore and declared rules. It does not check every sentence of a scene or determine what your author intended. You can also export a context pack for generation; see exports.
For a character’s changing allegiance or location, record event-anchored states and query the scene’s point in time. Missing chronology can leave that query indeterminate. A world’s established facts are not automatically what each character knows. See modelling a world for the time model.
Test the integration
Start with a disposable world and two hand-written additions: one compatible with its rules and one that conflicts with an established single-valued claim. Check that your application accepts the first, presents the second’s findings, and does not commit the staged conflicting claim after discarding it. Test proposed alternatives separately: an open possibility need not block a commit. The first-session example provides a small conflicting-home case to reproduce.
Once those paths work, try generated scenes and review whether your extraction captures the facts you intended to check. The engine calls no model; extraction quality and the choice of world rules belong to the surrounding application.
Concepts
De Lore Is checks the statements you record about a fictional world. You choose which constraints apply and which accounts to accept. The tool can explain an answer from those inputs; it does not read the story and decide canon for you.
Building a world
A scope is the part of a project you are working in. A new project has a
world scope. A workspace is a staging area for changes to a scope. A
claim is a statement such as home(watson, london). A predicate is the
name of the relationship, here home; an entity is a named person, place, or
other thing in the world.
A functional predicate permits one value for a subject at a time. Declare
home functional only if two simultaneous homes should be a conflict in your
model. A state (also called a fluent) represents a value that can change
over story time. An event is something that happens; an era groups a
period of the story. Ordering events and eras lets the timeline and as-of
queries reason about their sequence.
A rule derives a claim from other claims. It expresses a modelling choice, not a fact automatically discovered in the text. Materialize checks and commits a workspace to the store; a blocking conflict prevents the commit.
Sources and rulings
Provenance records how a claim was supplied and, for a cited claim, the
decisions supporting it. A decision is a named ruling, such as d-0001;
the writer can use it for a story, an editorial ruling, or another useful unit.
author.record_decision records a decision. In .dli text, ratify d-0001
declares it. Referenced decisions must exist.
A proposed claim is an unresolved possibility. A cited claim carries one or more decision references. A citation’s state can be provisional or ratified; these are recorded attributes of the claim’s provenance. A decided claim is supported by a recorded decision. Standing describes such authoring metadata; it is different from the claim’s evaluated status. Neither the word ratified nor a citation by itself promises that a claim will remain certain after later decisions.
In text, decided d-0001 and ratified d-0001 both refer to a recorded
decision; the latter also records ratified provenance. proposed leaves the
claim open instead. The result depends on the current rulings, not on the word
alone. In a wiki, canon means certain under those rulings, unconfirmed
means possible, and overturned means the recorded support no longer holds.
A ratify block’s hypothesis assignments currently apply to language names,
not to ordinary world claims. If a world claim assumes that hypothesis, the
check reports an error instead of silently treating the assignment as a ruling.
Leave the claim proposed, or record the accepted claim with a decision citation.
An MCP corpus import preserves the supplied evidence and reports this error in
its findings; inspect them and run a check before accepting the world.
The evaluated statuses describe what follows under the current rulings:
- Certain: supported as an accepted answer under those rulings.
- Possible: not settled as certain or ruled out. Possible does not mean false, and it does not make the claim canon.
- Dead: the claim has no admissible support under the current model. An absent claim is different: no claim is present to evaluate.
A defeat records a decision that attacks an earlier decision’s support. It
can represent a retcon without deleting the old claim. If a later decision
defeats the retcon, the original support can return. Use author.why to inspect
support and author.diff to compare changes. The structured diff’s rows
distinguish an absent claim from a dead one; the older consequences summary
uses dead as a placeholder for a missing side.
Exploring and checking
A branch or fork is a separate line of exploration. Its changes do not alter main unless you explicitly adopt them. The certain layer contains claims that are certain under the chosen decisions; other layers can show unresolved claims too. An as-of query asks what holds at a chosen story-time point, provided the relevant events are ordered.
The CLI calls an alternative a fork; MCP calls it a branch. A pack is a group of source documents loaded together. In an explanation, an antichain is the set of distinct, minimal combinations of support. A functional dependency is the one-value rule described above. The query grade asserted means a claim was taken as given without a verifying procedure; it does not say whether the claim cites a ruling. A shadow layer contains possible claims that are not certain. Inertial states persist until a later value replaces them. Saturated means the rule engine has finished deriving everything it can from the supplied facts and rules.
Name suggestions are warnings: they can help spot a misspelled subject but do not catch every missing declaration. The engine records the attribution and rulings supplied by its caller; review and permission policies belong to the application using it.
The project records when claims and rulings were added. timeline knows reads
knowledge supplied for characters or the audience in an episode file, in
production order. It does not infer a character’s beliefs or
knowledge at a particular story moment from the lore store.
Syntax reference — the complete .dli rendering grammar
This sheet documents the .dli text surface for writing and reading lore.
The default build includes its parser and formatter, so people can check,
format, import, and query .dli documents. Agents use the author.* tools
(authoring-loop.md). Stored fact objects and their JCS serialization remain
the canonical form; author.materialize returns committed objects by default
and can also return .dli rendering on request.
The syntax implementation lives in crates/dli-syntax. Builds made with
--no-default-features omit the text parser and formatter.
Retired keywords and line forms are refused with a message naming their successor.
1. The frame
| Construct | Form | Notes |
|---|---|---|
| Scope header | scope <name> | First line of a file; names the scope. |
| Comment | # … | Whole-line only. |
| Record | one declaration per line | Flat; order-insensitive between records. |
| Continuation | leading indent under a header line | Groups child lines (entity slots, question options, rule bodies) into one record. A dedent ends the group — the parser is indent-tolerant and the formatter owns layout. Tab counts as one column. |
| Expert block | keyword … { … } … } | Brace-delimited interiors for practice, proof, dialect, ratify. Braces inside string labels are plain text. |
2. Provenance (trails every claim-bearing line)
<claim> proposed [origin-token]
<claim> proposed-bundle "scope#number"
<claim> decided d-NNNN [d-NNNN…]
<claim> ratified d-NNNN
<claim> provisional d-NNNN
<claim> … assumes <hyp-or-option>
- A claim with no marker parses but warns (
origin-missing) and is fail-closed at load (support = FALSE — it can never be Certain). proposedneeds no citation;decided/ratified/provisionalwithout ad-refis a diagnostic and fails closed.- One marker per line; extras diagnose (
conflicting-markers). assumesconjoins a hypothesis/option condition onto the claim’s support.ratify d-1 { h-name := true }currently settles language-name hypotheses only. If a world claim also assumes that hypothesis,checkreports an error; cite a decision on the world claim instead.proposed-bundle "scope#number"preserves an anonymous proposal’s exact identity on text replay; the scope may be empty for scope-less source. It cannot be combined with origin tokens, orassumes.
3. Keyword inventory by family
World structure
| Keyword | Line form(s) |
|---|---|
era | era <id> ["Label"] [marker] — or the chain: era a < b < c [marker] (spine order; provenance applies to the chain) |
place / person / thing | <kw> <id> ["Label"] [marker] — slots as indented children: <id>.<key> = <value> [marker] |
event | event <id> ["Label"] (after|during|since|until) <anchor> [marker] |
fact | fact <subj> <pred> <obj> [marker] (triple) — also the slot form <entity>.<key> = <value> and the relation form <pred>(<a>, <b>), both bare (no fact keyword needed) |
before | before <event-a> -> <event-b> [marker] (declaring an order is a ruling) |
use | use <scope> |
kind | kind <name> [= v1 | v2 | …] |
functional | functional <pred> [marker] — declares pred single-valued (a functional dependency): one certain value per subject |
narration | narration vantage <era> (inert vantage; one per corpus) |
functional states a constraint, not a fact — it lowers to no atom, and there is
nothing to query. What it changes is the check: two distinct certain values for
one subject of a declared predicate raise continuity.fd-violation. Undeclared,
single-valuedness is inferred from canon’s own precedent, which cannot judge a
first-seen predicate — the declaration is how an author settles that case.
functional ruler-of ratified d-0146
ruler-of(camelot, arthur) ratified d-0146
Questions, forks, hypotheses
| Keyword | Line form(s) |
|---|---|
question | question <id> ["prompt"] with children option <id> ["label"] [implies <atom>] |
option | child of question/fork (column-0 also folds into the preceding question) |
fork | fork <id> with option … children |
hypothesis | hypothesis <id> (mints the fork-scoped supposition variable) |
Question bodies contain options only. Fork bodies may also retain fork commands
such as suppose and add fact; a malformed line beginning with option is
still an error. option ... assumes is a killed form: hypothesis gating belongs
on the provenance of the claim being gated, for example
<claim> proposed assumes <hypothesis>.
Rules and principles
| Keyword | Line form(s) |
|---|---|
rule | rule <id> [over order|season] [severity <sev>] [marker] + indented : <clause> / requires <clause> body. A bare rule derives world claims. over order and over season are stored but their constraints are not applied by CLI or MCP checks. |
rule (world, datalog) | a bare rule derives its head from a <head> :- <lit>, <lit>, … body; a body literal not <atom> is negated (tests the certain layer, poisons the conclusion’s defeaters — spec/02 §4). Range-restricted (every head/not variable also in a positive atom) and stratified (no negative-dependency cycle). A bare rule that is not valid Datalog is a load error, never an empty lowering — spec/08 §1 |
principle | principle <id> … (declaration body captured; no craft checker is provided) |
Time
| Keyword | Line form(s) |
|---|---|
state | state <entity>.<key> = <value> (since|until|during) <anchor> [marker] |
cycle | cycle <name>(<spokes>) [= s1 | s2 | …] [marker] (cyclic kind: succ/pred/across/distance; no epoch, no <) |
Practice (magic-system workbench, expert tier)
practice <id> [marker] { … } — interior keywords, one per line:
use, kind, role (role <id> : <kind> requires <conds>), resource
(resource <id> : linear conserved | affine bounded <n> | graded monotone <dir>), rule (… :- <clauses>), condition, error (error <id> refines L1–L7 <discriminant> [where …]), effect, cost.
proof <id> speaker <who> in <practice> { … } — interior:
premise <id> <atom> cited d-NNNN [cue "…"], step <id> instantiate <rule> with <bindings>, spend <premise>, close <atom> expect holds|fails-at <error>.
Language layer
| Keyword | Line form(s) |
|---|---|
dialect | dialect <id> [marker] { … } (name-parts/weights interior) |
name-part | name-part <id> … (cited fact of the dialect) |
word | word <id> … |
name | name <id> … + composition children (typed derivation citing dialect + slots) |
term | term <id> … (coined term) |
banned | banned <token> … (lexicon ban) |
Records of process
| Keyword | Line form(s) |
|---|---|
draft | draft <id> … (draft declaration) |
attest | attest <id> … (attestation reference) |
export | export <target> … (profile reference) |
ratify | ratify <id> { … } (ruling record block) |
variant | variant <id> … (ruleset delta) |
4. Stored form
Declarations carry structured data into the lorestore. The parser reads the text into that structure; tools can then inspect rules, practices, proofs, and other declarations without treating their bodies as arbitrary text.
5. Not rendered yet
Episodes (production order, scenes, and knowledge deltas) have no .dli
line form on this sheet.
6. Where to look deeper
docs/cards/authoring-loop.md— how lore is actually added: the tool calls, the fact objects, the commit.docs/cards/gentle-card.md— the teaching subset (what a writer meets first).docs/cards/expert-card.md— the expert tier, its tool calls and its stored form.docs/spec/01-syntax.md— normativity, the record shape, the interface, drift discipline.schemas/lorestore/fact-object-v1.json— the canonical stored form this grammar renders.schemas/ast/document.schema.json— the AST the parser produces from a rendering.examples/*.dli— the living corpus (all gate-parsed).
CLI commands
deloreis exposes 15 top-level subcommands, including Clap’s generated help command; command groups carry nested operations and generated help paths. Each operation is a rendering over the same engines the dli.* worker ops serve. Exit codes are uniform: 0 clean, 1 findings, 2 a usage or operational error.
| Command | Description |
|---|---|
deloreis init | Create a new lorestore-backed project |
deloreis import | Import a .dli file into a new project |
deloreis check | Validate documents against the world model |
deloreis query | Query a saturated world |
deloreis fmt | Format documents into the one canonical surface |
deloreis fork | Inspect and evaluate forks over a workspace store |
deloreis fork new | Create a new fork over a corpus |
deloreis fork list | List the open forks and their base corpus |
deloreis fork diff | Show a fork’s derived-consequence diff against the base |
deloreis fork suppose | Suppose an option or event value in a fork |
deloreis fork add | Add a proposed ground fact to a fork |
deloreis fork suppress | Retract a supposition from a fork |
deloreis fork restrict | Restrict a fork’s presented consequences with a keep-stamp |
deloreis fork preflight | Ratification pre-flight of a fork |
deloreis fork promote | Emit a ruling-request draft from a fork |
deloreis fork help | Print this message or the help of the given subcommand(s) |
deloreis preflight | Run the ratification pre-flight over a fork |
deloreis timeline | Read the timeline: chronology and the episode fold |
deloreis timeline show | Render the era/event partial order (optionally around a cursor) |
deloreis timeline order | The partial-order report: cycles and unordered pairs |
deloreis timeline knows | What a knower knows once the listener reaches a production slot |
deloreis timeline episodes | Fold the season and check its production-order layout and open loops |
deloreis timeline phases | The cyclic phases an episode pins |
deloreis timeline help | Print this message or the help of the given subcommand(s) |
deloreis check-proof | Check a diegetic proof block against a practice |
deloreis name | Vet a name — the kernel, world-true half |
deloreis name check | Vet a candidate name against the settled name-space |
deloreis name help | Print this message or the help of the given subcommand(s) |
deloreis export | Export under a profile |
deloreis report | Render the findings report |
deloreis serve | Run JSON-over-stdio with --project <path> or explicit --demo |
deloreis mcp | Run MCP over stdio with --project <path> or explicit --demo |
deloreis help | Print this message or the help of the given subcommand(s) |
Wire operations
The closed operation set: 63 operations. The wire name is each op’s serde identifier; its request and response payload schemas live under contracts/dli-worker/schemas/. Summary is Op::summary() — the same tool-selection text an agent reads over MCP. The batch dli.* family and the author.* authoring-seam tools are one closed set.
| # | Wire name | Summary | Request schema | Response schema |
|---|---|---|---|---|
| 1 | dli.version | Report the worker contract version and this build’s wired capabilities. | dli.version.request.json | dli.version.response.json |
| 2 | dli.check | Validate a loaded document: self-consistency findings over the compiled world. | dli.check.request.json | dli.check.response.json |
| 3 | dli.query | Query a saturated world for a goal atom’s support, grade, and standing. | dli.query.request.json | dli.query.response.json |
| 4 | dli.fork.new | Open a fork: an exploratory condition context over an unchanged world. | dli.fork.new.request.json | dli.fork.new.response.json |
| 5 | dli.fork.list | List the open forks over a document. | dli.fork.list.request.json | dli.fork.list.response.json |
| 6 | dli.fork.diff | Show a fork’s derived-consequence diff against the base ledger. | dli.fork.diff.request.json | dli.fork.diff.response.json |
| 7 | dli.fork.suppose | Assume an option or event true within a fork and see what its consequences become. | dli.fork.suppose.request.json | dli.fork.suppose.response.json |
| 8 | dli.fork.add | Add proposed declarations into a fork — fork-local exploration, never canon. | dli.fork.add.request.json | dli.fork.add.response.json |
| 9 | dli.fork.suppress | Retract an assumption from a fork. | dli.fork.suppress.request.json | dli.fork.suppress.response.json |
| 10 | dli.fork.restrict | Restrict a fork’s valuation scope to a set of atoms. | dli.fork.restrict.request.json | dli.fork.restrict.response.json |
| 11 | dli.fork.preflight | Ratification pre-flight: the candidate valuation a fork implies and the conflicts it would introduce. | dli.fork.preflight.request.json | dli.fork.preflight.response.json |
| 12 | dli.fork.promote | Emit a ruling-request draft from a fork — the machine proposes, it never rules. | dli.fork.promote.request.json | dli.fork.promote.response.json |
| 13 | dli.check-proof | Check a diegetic proof block declared in a practice. | dli.check-proof.request.json | dli.check-proof.response.json |
| 14 | dli.timeline.show | Show the timeline at a position. | dli.timeline.show.request.json | dli.timeline.show.response.json |
| 15 | dli.timeline.order | Report the partial order over the timeline. | dli.timeline.order.request.json | dli.timeline.order.response.json |
| 16 | dli.timeline.knows | Who-knows-what matrix at a timeline position. | dli.timeline.knows.request.json | dli.timeline.knows.response.json |
| 17 | dli.timeline.episodes | Check episode ordering and report the slack in each slot. | dli.timeline.episodes.request.json | dli.timeline.episodes.response.json |
| 18 | dli.timeline.phases | Phase-walk verdict for an episode. | dli.timeline.phases.request.json | dli.timeline.phases.response.json |
| 19 | dli.export.run | Run an export target under a profile. | dli.export.run.request.json | dli.export.run.response.json |
| 20 | dli.name.check | Vet a proposed name through the naming oracle before it enters lore. | dli.name.check.request.json | dli.name.check.response.json |
| 21 | dli.attest.missing | List questions that lack a current attestation. | dli.attest.missing.request.json | dli.attest.missing.response.json |
| 22 | dli.corpus.import | Import a corpus into a document’s store — fact objects on any build, .dli documents on a syntax build. | dli.corpus.import.request.json | dli.corpus.import.response.json |
| 23 | dli.corpus.export | Export a document’s folded fact objects at a head — the live state, not the commit log. | dli.corpus.export.request.json | dli.corpus.export.response.json |
| 24 | author.open | Open an authoring workspace over a scope or fork; returns its id and digest. | author.open.request.json | author.open.response.json |
| 25 | author.status | Summarize a workspace: its staged canonical surface and current digest. | author.status.request.json | author.status.response.json |
| 26 | author.diff | Show the workspace’s staged change as structured per-fact diff rows (before/after standing) with findings and derived consequences; the .dli rendering is empty unless a formatter is linked. | author.diff.request.json | author.diff.response.json |
| 27 | author.discard | Drop a whole workspace, or one staged declaration. | author.discard.request.json | author.discard.response.json |
| 28 | author.materialize | Commit the workspace’s staged fact objects to the lorestore — the accept decision — and return them as JSON (facts and root_hash), with .dli rendering behind rendered:true; an optional decision makes the accepted FACTS certain under it (not carriage claims like rules, same-as, or inertial declarations — those need record_decision plus a cited declaration); refuses while any error-severity finding is open. | author.materialize.request.json | author.materialize.response.json |
| 29 | author.add_entity | Introduce a person, place, thing, or era into the workspace, with provenance (events use author.add_event). | author.add_entity.request.json | author.add_entity.response.json |
| 30 | author.add_fact | Assert a keyed attribute value on an entity (entity.key = value), with provenance. | author.add_fact.request.json | author.add_fact.response.json |
| 31 | author.add_relation | Assert a binary relation between two entities, with provenance. | author.add_relation.request.json | author.add_relation.response.json |
| 32 | author.add_event | Introduce a chronology event anchored to an era, with provenance. | author.add_event.request.json | author.add_event.response.json |
| 33 | author.add_before | Order one event before another, with provenance. | author.add_before.request.json | author.add_before.response.json |
| 34 | author.open_question | File an open question with its options as tracked debt. | author.open_question.request.json | author.open_question.response.json |
| 35 | author.add_option | Add one option to a question already open in the workspace. | author.add_option.request.json | author.add_option.response.json |
| 36 | author.add_hypothesis | Mint a fork-local hypothesis variable for supposition. | author.add_hypothesis.request.json | author.add_hypothesis.response.json |
| 37 | author.suppose | Suppose staged hypotheses true within a fork and see the consequences move. | author.suppose.request.json | author.suppose.response.json |
| 38 | author.query | Query the staged world (base + your in-progress work): what holds about an entity now. | author.query.request.json | author.query.response.json |
| 39 | author.check | Run the full check battery over the staged world — verify your whole draft before materialize. | author.check.request.json | author.check.response.json |
| 40 | author.why | Explain a staged claim’s standing: why it is certain / possible / dead, the blocker, and its repair. | author.why.request.json | author.why.response.json |
| 41 | author.findings | List the workspace’s open findings by severity — what materialize would refuse on, and how to fix each. | author.findings.request.json | author.findings.response.json |
| 42 | author.list_workspaces | List your open authoring workspaces and their digests, to recover context. | author.list_workspaces.request.json | author.list_workspaces.response.json |
| 43 | author.branch | Create a persistent branch, or list them — a divergent line of durable writes. | author.branch.request.json | author.branch.response.json |
| 44 | author.merge | Merge a named branch into the current one; returns the merge commit and a structural conflict report. | author.merge.request.json | author.merge.response.json |
| 45 | author.add_dialect | Declare a dialect register — the phonology and name grammar a naming population draws on. | author.add_dialect.request.json | author.add_dialect.response.json |
| 46 | author.add_name_part | Declare a name-part morpheme and its role, the raw material composed names are built from. | author.add_name_part.request.json | author.add_name_part.response.json |
| 47 | author.add_name | Compose a name from name-part morphemes and optionally bind it to the entity that bears it. | author.add_name.request.json | author.add_name.response.json |
| 48 | author.add_term | Introduce a glossary term — a word the lore defines and reuses. | author.add_term.request.json | author.add_term.response.json |
| 49 | author.add_banned | Ban a word from the lore surface, with the reason; the guardrail that refuses it later. | author.add_banned.request.json | author.add_banned.response.json |
| 50 | author.add_state | Assert a time-varying state on an entity (a fluent that holds over an interval, not forever). | author.add_state.request.json | author.add_state.response.json |
| 51 | author.add_attest | Request an attestation for an open question — the ledger entry a later sweep answers. | author.add_attest.request.json | author.add_attest.response.json |
| 52 | author.set_standing | Promote a provisional stored claim to ratified by citing a ratifying decision. | author.set_standing.request.json | author.set_standing.response.json |
| 53 | author.defeat | Defeat a stored claim with a decision while preserving its history and object. | author.defeat.request.json | author.defeat.response.json |
| 54 | author.declare_functional | Declare a predicate single-valued (a functional dependency), so two distinct certain values for one subject are a contradiction — even for a first-seen predicate canon has no precedent for. | author.declare_functional.request.json | author.declare_functional.response.json |
| 55 | author.assert_same_as | Assert two entity ids name the same bearer; the evaluable world merges them, the merge’s standing carried by this claim’s provenance. | author.assert_same_as.request.json | author.assert_same_as.response.json |
| 56 | author.add_rule | Declare one datalog derivation rule (a typed head :- body clause); every fact it derives carries this rule’s own provenance, defeasible like any other claim. | author.add_rule.request.json | author.add_rule.response.json |
| 57 | author.declare_inertial | Declare a predicate’s fluent values persistent (inertial): a value asserted since a point holds forward until a later, distinct, live value defeats it. | author.declare_inertial.request.json | author.declare_inertial.response.json |
| 58 | author.as_of | Project the inertially-current fluent value(s) at a query point: what holds, and why, including a silent transition’s derived boundary and defeater — the one inertia-aware read. | author.as_of.request.json | author.as_of.response.json |
| 59 | author.record_decision | Record a baseline ratified decision on the workspace branch so facts that cite it read certain — the wire way to reach certainty and make contradiction checks (fd-violation, fluent, inertia) fire, replacing the fixture-authority hack; defeasible by author.defeat. | author.record_decision.request.json | author.record_decision.response.json |
| 60 | author.predicates | List properties used in the staged world, with their arity and whether they are single-valued or inertial, so you can reuse an existing property name. | author.predicates.request.json | author.predicates.response.json |
| 61 | author.entity_candidates | Find declared characters, places, or other entities with ids close to a name, so you can reuse an existing id. | author.entity_candidates.request.json | author.entity_candidates.response.json |
| 62 | author.assert_predicate_same_as | Record that two property names refer to the same property in your fictional world; the claim can be defeated by a later ruling. | author.assert_predicate_same_as.request.json | author.assert_predicate_same_as.response.json |
| 63 | author.record_decisions | Atomically record many baseline supporting decisions on one workspace branch, validating the complete batch before one commit and one ref update. | author.record_decisions.request.json | author.record_decisions.response.json |
Authoring with De Lore Is
Use De Lore Is when a writing task needs an established world to check against: continuing a novel, adding lore, revising an account, or exploring another version of a story. It records structured lore and checks the rules supplied for that world. Keep the manuscript and prose references in their existing files.
For installation and a complete first check, read First session. The skill does not install the runtime. Start with a small section of the user’s notes, not a whole-book migration. Ask which details are established and which are ideas. Propose rules instead of assuming, for example, that every character can have only one home.
Use the author.* tools to add lore. Read every returned finding. Repair
mechanical input errors; resolve continuity findings using the user’s rulings
rather than change lore just to make a check pass. Do not edit .dli as an agent
authoring a world. People can write .dli directly and import or check it;
the store is the source of truth for an authoring session.
Register deloreis mcp --project <directory> with the client, then call its
tools. If they are not available, use the first-session setup instead of
simulating a stored world in the chat. The server
also offers newline-delimited JSON through deloreis serve --project <directory>. Contract 2.0.0 is the served contract. --tools all advertises
specialist tools beyond the default profile. Use installed help and tools/list
for the exact arguments rather than guessing tool schemas.
Authoring loop
author.opena scope and retain its returned workspace ID and digest. When fencing writes, pass the current digest asexpected_digest; read the updatedworkspace_digestfrom the result orauthor.statusafter each change rather than reuse an earlier one.- Use typed operations such as
author.add_entity,author.add_fact,author.add_relation,author.add_event, andauthor.add_before. Declare single-valued predicates withauthor.declare_functionaland changing predicates withauthor.declare_inertialwhere appropriate. - Check the project’s source/decision index before choosing a new decision ID;
reuse an ID only for that same ruling. For a ruling, use
author.record_decisionand cite that decision with{"kind":"cited","decisions":["d-0001"],"state":"ratified"}. An unruled proposal can use{"kind":"proposed","origin":"session-name"}. Unknown citations are errors and cannot make a claim certain. - Inspect
author.check,author.findings,author.query, andauthor.why.author.diffshows staged consequences. Check the returned findings even if the call itself succeeded. author.materializechecks and commits staged changes. It refuses errors.author.discardrejects staged work. Keep returned root hashes and claim IDs when they are useful for review or later retcons.
author.record_decision, author.defeat, and standing operations can update
the store directly; do not assume that every mutation is only staged. Use the
user’s agreed rulings and the host’s review policy before these calls; do not
silently turn an idea into an accepted account. The
caller supplies attribution, decision state, and review policy. The server
checks references and continuity, not who is authorized to rule.
Use it during a writing task
- Continue a chapter: query the people, places, and established facts relevant to the scene. After drafting, propose the new factual claims through the tools, check them, and show the user any conflicts. Review extracted claims against the prose; a clean check covers those claims and recorded rules.
- Brainstorm: keep alternatives proposed or in a hypothetical fork. Use
author.open_questionandauthor.add_optionfor a choice between accounts;author.add_hypothesisandauthor.supposesupport hypothetical exploration. Accepting a note into the store does not by itself make it established canon. Query established claims withlayer: "certain". Uselayer: "possible"or"all"to inspect open accounts as well; those layers also include certain claims. A known claim can evaluate dead, but there is nodeadquery layer: inspect its blocked support withauthor.why. - Revise: inspect
author.diffandauthor.why, then record the user’s chosen new ruling before callingauthor.defeaton the earlier support. Retain the old claim ID. Use a named branch for a separate durable version; branch tools are advertised with--tools all. - Track change within the story: record event-anchored states and query
author.as_of. A character changing allegiance is not necessarily a retcon. Declare known event ordering; leave unknown ordering open.
New workspaces see current committed lore; an already-open workspace retains its base. Open a fresh workspace when another session has changed the accepted world. The host application owns approval, access control, and concurrent-write policy. Do not describe DLI as general-purpose agent memory or infer a character’s knowledge from world facts.
For CLI reads and wiki export, see Read and export.
Core tools
The default MCP profile keeps 29 common tools. It advertises dli.check, dli.name.check, and:
- Workspace and inspection:
author.open,author.status,author.check,author.findings,author.predicates,author.query,author.why,author.as_of,author.diff, andauthor.entity_candidates. - Lore:
author.add_entity,author.add_fact,author.add_relation,author.add_event,author.add_before,author.add_state,author.declare_functional, andauthor.declare_inertial. - Rulings and exploration:
author.record_decision,author.open_question,author.add_option,author.defeat,author.set_standing,author.add_hypothesis, andauthor.suppose. - Acceptance:
author.materializeandauthor.discard.
author.add_option can name a fact that follows if that option is chosen,
using its optional implies field. Give options distinct names and add them
before materializing their question.
Use tools/list for the exact schemas. author.query returns claim IDs when
available; author.why explains supporting and blocked paths. For a retcon,
author.defeat takes a claim ID, a new decision, and the decision it defeats.
Defeating the retcon can restore the original support. Use --tools all for
named branches, specialist authoring, corpus import/export, and other tools.
The Holmes casebook is produced from a replay that exercises the server. The concept glossary explains the terms. DLI checks structured claims, not prose, and a functional conflict is detected only for a predicate declared single-valued.
Reading rendered lore
These are supported readable .dli forms. Agents author through the tools
instead of editing these snippets into the project:
person wren "Wren" proposed session
functional home proposed session
home(wren, thornfield) ratified d-0001
Grammar keywords
De Lore Is reserves 35 keywords: the words a declaration line may open with. A line that opens with anything else is a relation fact of the form pred(args) or a diagnostic. Retired spellings (for example fluent→state, wheel→cycle, the proof block ledger→proof, sort→kind) are killed forms: the parser refuses them with a renamed-to diagnostic rather than reading them as keywords.
| Keyword |
|---|
attest |
banned |
before |
cycle |
dialect |
draft |
era |
event |
export |
fact |
fork |
functional |
hypothesis |
inertial |
kind |
name |
name-part |
narration |
option |
person |
place |
practice |
principle |
proof |
question |
ratify |
rule |
same-as |
scope |
state |
term |
thing |
use |
variant |
word |
Finding families
Every graded finding carries a stable, dotted family.item code (charter koan 9, errors are lore). Clients and repair tools match on the code, which is stable across message wording. This page catalogues every code the system emits: its family (the check or tool that raises it), its severity, any structured repair it carries, and what it means.
These finding codes are disjoint from the wire error-code registry (the ErrorCode values in error-codes.md, per the dli-worker contract §3.2 / §9). The distinction is load-bearing: a wire error code says the call failed — it was malformed (schema), refused by principle (refused), or hit a server defect (internal); a finding code is a graded domain observation about the lore itself. A well-formed call over inconsistent lore returns findings, not an error. No code appears in both sets.
97 dotted codes across 13 families, plus 4 proof.resource-violation sub-codes. A family.item code names its check battery in the family and the specific violation in the item. Two codes can share a stem across families — the naming vet’s name.register-fit (a kernel error) and the authoring seam’s name.unvetted (an authoring warn) are different findings from different surfaces.
Severity belongs to the emitting check. Some findings include structured repair data; others explain the next step in their message. Error-severity findings block
author.materialize, while warnings do not.
Ratify blocks
- Emitted by: World check and corpus import
- Owner:
dli-world :: lib.rs - Severity: Errors identify a ruling that cannot safely affect a world claim or decision.
| Code | Severity | Repair | Meaning |
|---|---|---|---|
ratify.world-hypothesis-unsupported | error | — | a ratify assignment cannot settle a world claim that assumes the hypothesis. |
ratify.decision-as-hypothesis | error | — | a ratify assignment names a decision rather than a hypothesis. |
Entity references
- Emitted by: World check, CLI and authoring reads
- Owner:
dli-world :: check/entity.rs - Severity: Advisory; unknown references do not block a commit.
| Code | Severity | Repair | Meaning |
|---|---|---|---|
entity.undeclared | warn | candidates | a likely entity reference names no declared entity. |
Unchecked rules
- Emitted by: World check, CLI and authoring reads
- Owner:
dli-world :: model.rs (LoadFinding) - Severity: Advisory; the declaration is retained but its constraint is not checked.
| Code | Severity | Repair | Meaning |
|---|---|---|---|
rule.unchecked-domain | warn | rule | an order or season rule is retained but its constraint is not checked. |
load — the loader’s fail-closed gate
- Emitted by: the world loader, run by
deloreis check/dli.checkbefore any query - Owner:
dli-world :: model.rs (LoadFinding) - Severity: All error: a load finding holds its declaration fail-closed — unassertable until fixed. Fail-closed provenance is the assertability gate.
| Code | Severity | Repair | Meaning |
|---|---|---|---|
load.missing-provenance | error | — | a declaration carries no ruling and no hypothesis, so it cannot be asserted. |
load.malformed-provenance | error | — | the provenance annotation cannot be read; held fail-closed until the annotation is fixed. |
load.malformed-world-rule | error | — | a bare top-level world rule is not valid Datalog and is refused instead of lowering no rule. |
load.unbound-head-variable | error | — | a rule concludes about a variable that nothing in its premises names. |
load.unknown-temporal-reference | error | — | a typed temporal clause names an era or event the merged world does not declare. |
load.duplicate-declaration | error | — | a name is declared twice as the same kind within one scope. |
load.cross-pack-collision | error | — | a name is declared in more than one scope/pack. |
load.unstratifiable | error | — | the rules cannot be ordered for evaluation — a negative-dependency cycle (FRONT-1). |
load.inert-declaration | error | — | a declaration is accepted by the grammar but names nothing evaluable, so it would lower to no fact (DL-0029); surfaced rather than silently dropped. |
predicate-identity — declared property-identity validation
- Emitted by: predicate-identity validation and policy projection in the world loader/check seam
- Owner:
dli-world :: model.rs (LoadFinding) - Severity: Mixed: an observed-shape mismatch is an error that blocks materialization; a union-policy promotion is a non-blocking warning.
| Code | Severity | Repair | Meaning |
|---|---|---|---|
predicate-identity.arity-mismatch | error | — | a certain predicate-identity component contains incompatible observed positional arities; materialization is refused rather than performing a partial merge. |
continuity — the world-consistency battery
- Emitted by: world-internal and claim-relative continuity checks
- Owner:
dli-world :: check/continuity.rs (WorldFinding) - Severity: Errors block contradictions; warnings flag unresolved drafts or temporal gaps.
| Code | Severity | Repair | Meaning |
|---|---|---|---|
continuity.stale-era-model | error | — | an older compiled model lacks era declarations; recompile it from source or the lorestore before checking. |
continuity.chronology-cycle | error | — | the before-graph closes a cycle; the timeline would have no consistent order. |
continuity.prior-contradiction | error | — | a claim contradicts an established prior certain fact. |
continuity.rejected-lore | error | — | a certain fact realizes a killed/rejected token. |
continuity.unknown-entity | error | — | a claim references an entity that is not in scope. |
continuity.proposed-as-fact | error | — | a claim states shadow (proposed) lore as settled. |
continuity.intra-episode-contradiction | error | — | a claim set fixes one slot two contradictory ways within an episode. |
continuity.fluent-contradiction | error | — | a fluent slot holds two distinct values certainly at once over overlapping intervals (a state’s since/until spans collide). |
continuity.pending-conflict | warn | — | a proposed value and another live value differ on a declared-functional non-fluent slot; the warning does not decide either account or block materialization. |
continuity.fd-violation | error | — | canon’s own certain layer holds two distinct values for a subject on a predicate the author declared functional, breaking the functional dependency. |
continuity.derived-fd-violation | error | — | the same functional-dependency break as continuity.fd-violation, but visible only once the world’s rules are saturated — a wire-authored rule (or an identity-closure copy) derives the second value rather than an author asserting it directly. |
continuity.identity-temporal-unsupported | error | — | a same-as identity link names a declared era or event on either side — v1 refuses to unify time points (a defeasible merge over chronology indices would half-merge the timeline). |
continuity.fluent-order-undeclared | warn | — | a fluent slot holds two distinct certain-support values whose temporal cover is undeclared-order-indeterminate at some point — a before-fact between the named anchors would settle whether they actually clash. Non-blocking: the contradiction check itself never fires on undeclared order. |
continuity.inertia-unanchored | warn | — | a predicate declared inertial carries a value with no since anchor, so no forward-persistence interval can be derived — the declaration would silently do nothing. Non-blocking; names the slot so the missing anchor is a deliberate choice, not an accident (DL-0081, koan 4). |
name — the naming-oracle kernel vet
- Emitted by: the world-true half of the naming oracle, run by
dli.name.checkand the language layer ofcheck - Owner:
dli-world :: language/derivation.rs (LangFinding) - Severity: Mixed: structural, register, uniqueness, morphology, and banned-token violations are errors; aural clashes, set clashes, sayability, and confusables are warnings. (Distinct from the authoring-seam
name.unvettedfinding below.)
| Code | Severity | Repair | Meaning |
|---|---|---|---|
name.slot-sequence | error | — | the name’s slots match no name-kind pattern of its dialect. |
name.role-membership | error | — | a name-part is not declared in the dialect under the role it fills. |
name.register-fit | error | — | the name does not fit its declared dialect/register. |
name.phonotactics | error | — | the name violates the dialect’s phonotactics (vowels, endings, clusters, stress). |
name.uniqueness | error | — | the name collides with an existing name in canon. |
name.fixed-token-reuse | error | — | the name reuses a fixed token reserved elsewhere. |
name.aural-clash | warn | — | the name is aurally too close to another across the whole name-space. |
name.set-clash | warn | — | the name is not aurally distinct within its declared set. |
name.gender-morphology | error | — | the name’s morphology conflicts with its declared gender. |
name.sayability-cap | warn | — | the name exceeds the configured sayability-tier caps. |
name.banned-in-name | error | — | the name realizes banned diegetic vocabulary. |
name.confusable | warn | — | the id mixes a Latin-looking form with a look-alike character from another script (a homoglyph). |
attest — the attestation seam
- Emitted by: the attestation lookup a check consumes, surfaced by
dli.checkanddli.attest.missing - Owner:
dli-world :: attest.rs (storage seam); dli-serve :: contract.rs + handlers.rs (document verdict) - Severity: Missing, stale, and negative judgments are errors for the consuming check; a current affirmative judgment is a pass with no severity.
| Code | Severity | Repair | Meaning |
|---|---|---|---|
attest.missing | error | — | the question has no attestation in the store; a sweep must pose it before a check can consume it. |
attest.stale | error | — | the stored judgment no longer matches the current world fingerprint. |
attest.pass | — | — | the current bounded model judgment affirms the configured proposition at grade attested. |
attest.fail | error | — | the current bounded model judgment rejects the configured proposition at grade attested. |
practice — practice well-formedness
- Emitted by: the practice-block checker, run by
deloreis check/dli.checkwhen apracticeis declared - Owner:
dli-ledger :: practice.rs (PracticeFinding) - Severity: Error: a malformed practice cannot host a proof, so each finding blocks the practice.
| Code | Severity | Repair | Meaning |
|---|---|---|---|
practice.error-refines-nothing | error | — | a declared error refines a token that is not one of the seven structural classes L1–L7. |
practice.missing-discriminant | error | — | two errors share a structural class with no discriminant to tell them apart. |
practice.resource-outside-algebra | error | — | a resource’s declared law is outside the closed resource algebra. |
practice.unbound-sort | error | — | a role is typed by a sort the practice does not declare, that is not a primitive kind or an in-scope cycle. |
practice.unresolved-cycle | error | — | a failure or law cites a cycle that is not in scope. |
practice.error-resource-unknown | error | — | an L6 resource-violation error spends against a resource the practice does not declare. |
practice.renamed-interior-form | error | — | a practice interior wrote a retired spelling (a killed form); refused with its successor named. |
proof — the diegetic proof checker
- Emitted by: the proof/ledger checker, run by
deloreis check-proof/dli.check-proof - Owner:
dli-ledger :: ledger_check.rs (ProofFinding) - Severity: Error: a proof finding means the diegetic proof is invalid; the L-class in the message (L1–L7) names which structural obligation broke.
| Code | Severity | Repair | Meaning |
|---|---|---|---|
proof.unknown-form | error | — | the form is not declared by this practice (L1). |
proof.premise-absent | error | — | a premise is not established in the scene — the casting fizzles (L2, absent). |
proof.premise-contradicted | error | — | a premise is denied by the certain projection — the casting rebounds (L2, contradicted). |
proof.wrong-phase | error | — | a step runs at the wrong cyclic phase (L3, timing). |
proof.bad-instantiation | error | — | a form is instantiated with ill-typed arguments (L4). |
proof.unknown-role | error | — | the proof header binds a role the practice does not declare. |
proof.invalid-chain | error | — | a chain step does not follow from its cited premises (L5). |
proof.resource-violation | error | — | the ledger breaches a resource law (L6) — see the resource sub-codes below. |
proof.close-mismatch | error | — | the close atom does not match what the steps derived (L7). |
proof.expectation-unmet | error | — | the proof’s expected verdict was not met (the expected-verdict discipline). |
proof.invalid-spend | error | — | a spend draws against a resource illegally. |
proof.unknown-spoke | error | — | a step cites a spoke or label that the proof does not define. |
proof.unknown-expectation | error | — | an expectation names something the practice does not define. |
proof.missing-close | error | — | the proof has no close step. |
proof.practice-mismatch | error | — | the proof’s in <practice> clause does not match the practice being checked. |
proof.invalid-header-layout | error | — | the direct proof AST header is not <role> <actor> in <practice>. |
proof.malformed-body | error | — | captured proof clauses do not replay through the canonical parser. |
proof.invalid-close-layout | error | — | the proof does not carry exactly one final close. |
factcheck — payload claim standing
- Emitted by: claim-relative certain-layer checks and the authoring seam
- Owner:
dli-world :: check/factcheck.rs; dli-serve :: author/reads.rs + author/tools.rs - Severity: All error: supported claims are silent; only contradicted and unsupported claims emit findings.
| Code | Severity | Repair | Meaning |
|---|---|---|---|
factcheck.contradicted | error | — | the claim conflicts with certain or explicitly rejected lore. |
resource — the discriminants of proof.resource-violation
- Emitted by: the resource-algebra accounting inside the proof checker; carried as the discriminant of
proof.resource-violation - Owner:
dli-ledger :: resource.rs (ResourceViolationCode) - Severity: Sub-codes, not standalone families: each names how a
proof.resource-violationbreached the closed algebra.
| Code | Severity | Repair | Meaning |
|---|---|---|---|
overspent | error | — | a linear or affine balance fell below zero — a unit was spent that was never accounted. |
over-capacity | error | — | an affine balance rose above its declared capacity. |
not-conserved | error | — | a linear conserved multiset did not net to zero at the close — a step minted or burned it. |
wrong-direction | error | — | a graded-monotone grade moved against its one declared direction. |
citation — lowering findings
- Emitted by: the evaluator’s lowering pass, surfaced through
check - Owner:
dli-eval :: world.rs (LowerFinding) - Severity: Error for the affected claim: lowering keeps checking the rest of the world, but forged authority fails that claim closed.
| Code | Severity | Repair | Meaning |
|---|---|---|---|
citation.unknown-decision | error | — | a claim cites a decision the corpus never declares; the claim is failed closed. |
author — authoring-seam findings
- Emitted by: the
author.*MCP tools (the authoring seam) - Owner:
dli-serve :: author/tools.rs + author/reads.rs (Finding) - Severity: Per-code severity gates
author.materialize: anerrorblocks it, awarnpasses. These are the findings that carry structured machine repairs.
| Code | Severity | Repair | Meaning |
|---|---|---|---|
render.provenance-unrepresentable | warn | — | the staged declaration has provenance the text renderer cannot preserve; its rendered line is omitted. |
name.unvetted | warn | run-check | a name has not passed the naming oracle; it enters proposed and unvetted. |
entity.unknown | error | resolve-entity | no entity of that id is in scope; the claim is staged but cannot materialize until it exists. |
event.era-missing | error | — | an event has no era anchor; every event must occur during a declared era. |
event.era-kind | error | — | an event’s era anchor names an entity that is not an era. |
fact.contradiction | error | — | a claimed slot value contradicts a certain fact the base asserts. |
order.cycle | error | — | a before fact would close a cycle against the existing orderings. |
consequence.changed | warn | — | under a supposition the derived-consequence set may move; see author.diff. |
hypothesis.not-in-fork | error | — | a hypothesis is legal only in a fork workspace. |
hypothesis.bundle-limit | error | — | a proposal bundle carries at most one hypothesis; a second supposition is a second fork. |
hypothesis.unknown | error | — | no hypothesis of that id is staged in this workspace to suppose. |
question.unknown | error | — | no open question of that id exists in this workspace to add an option to. |
field.control-char | error | remove-control-chars | a free-text field carries a control/bidi/zero-width character that could forge or reorder a declaration in the review diff. |
citation.unknown-decision | error | resolve-citation | a cited decision id does not resolve against the decisions the corpus declares. The engine records the attribution a caller supplies but verifies that what it cites exists: the unknown decision’s variable is left open, so the claim can never read certain, and materialize is barred until the id is repaired. |
decision.duplicate-in-batch | error | — | one bulk decision-recording request repeats an exact decision id; the complete batch is refused atomically and the finding identifies both record positions. |
identity.self-merge | warn | — | a same-as claim names the same id on both sides; it is a no-op merge. |
predicate-identity.self-merge | warn | — | a predicate-identity claim names the same predicate on both sides; it is retained for provenance but has no semantic effect. |
identity.temporal-unsupported | error | — | a same-as claim names an era or event endpoint; identity merging is not defined over the temporal spine (v1 boundary). |
identity.sort-mismatch | warn | — | a same-as claim merges entities of different sorts (e.g. a person and a place); staged but flagged for review. |
rule.empty-body | error | add-body-literal | a rule’s body has no literals; a body-less rule would derive its head unconditionally on the wire, but its render reparses via the .dli text path to a malformed rule the loader refuses (migration parity). Refused before staging. |
rule.unbound-variable | error | bind-variable | a rule’s head, or a negated body literal, names a variable no positive body atom binds — a range-restriction (datalog safety) violation. Refused before staging. |
rule.duplicate | error | — | a rule of that name is already staged or already in the compiled base; rule names are unique so author.defeat can cite one unambiguously. Refused before staging. |
rule.unstratifiable | error | — | staging this rule alongside everything already staged would leave the world unstratifiable (a negative-dependency cycle); the stratification pre-flight refuses it before it ever reaches the workspace, so no add can brick a later read. |
state.anchor-kind | error | resolve-anchor | a state’s temporal anchor names a KNOWN entity of the wrong sort — since/until need an event, after/during need an era. |
workspace.base-missing | error | — | the workspace’s project document base is no longer loaded. |
world.uncompilable | error | — | the staged declarations do not compile into a checkable world. |
Wire error codes
Every non-success return carries exactly one code from this closed set, and each code maps to exactly one of the four error classes — schema (the call is malformed), refused (refused by principle), finding (a graded domain finding), or internal (a server defect).
| Code | Class | Cause |
|---|---|---|
bad-frame | schema | a malformed envelope |
unknown-op | schema | an operation or tool the contract does not define |
schema-violation | schema | a payload that violates its op/tool schema |
unknown-doc | schema | a reference to a workspace, fork, or entity that does not exist |
unstratifiable | finding | a negative-dependency cycle caught at load (FRONT-1) |
contradiction | finding | a claim that contradicts the projection |
stale-fence | refused | expected_digest did not match the current fence |
materialize-blocked | refused | author.materialize called with error-class findings open |
killed-form | refused | a killed or renamed op/tool; the message names the successor |
not-wired | refused | a recognized op whose engine is not wired in this build (capability-gated) |
version-mismatch | refused | a well-formed frame declaring a contract version this build does not speak |
conflict | refused | an idempotency_key reused with a different payload (distinct from stale-fence) |
project-required | refused | a durable evidence operation called without a caller project |
evidence-corrupt | internal | committed evidence failed canonicalization, digest, or lineage verification |
temporal-indeterminate | refused | the requested temporal projection needs unresolved ordering |
internal | internal | a server defect |
Spec 00 — overview
De Lore Is (DLI) is a continuity engine for fictional worlds. It records structured claims about characters, places, events, rules, and changing states, together with their provenance and the rulings that support or defeat them. Authors and agents can check those claims, ask why they hold, and explore alternatives without rewriting the established record.
DLI checks the structured lore it is given; it does not read a manuscript and discover every plot hole. It does not call a model or decide who may make a ruling. A consuming application supplies attribution, decisions, and its own review policy. DLI checks the integrity of references and derives the result from committed inputs, so the same inputs yield the same answer.
The lorestore holds immutable, content-addressed fact objects. Their
schemars-derived schemas and canonical JSON bytes define the stored form.
The .dli language provides a readable text surface for authoring and
inspection. The decision ledger (../decisions.md) outranks this overview
and the other specification files.
Working with lore
- Check continuity. Declare the rules a world needs, such as a single-valued home for a character, and check claims against them. A contradiction between ruled claims prevents a commit and identifies its support.
- Explain and revise. Query whether a claim is certain, possible, or dead, and inspect its support. A retcon records a defeat rather than erasing the earlier claim; subsequent rulings can change the result again.
- Explore alternatives. Fork a world and compare the consequences of hypothetical assumptions without changing the committed record.
- Model time and rules. Eras and events form a partial chronology, while fluents describe states that change. Practices express declared rules and proof obligations; the language layer can check names and registers.
- Integrate. The command line, JSON server, and MCP authoring tools expose these operations to people and to applications working with agents. Exports provide one-way projections for other tools.
See semantics for claims and standing,
time for chronology and fluents,
practices for rules and proofs,
language for names and registers,
reasoning for derivation and explanation,
exports for projections, and
serving and agent authoring for the
interfaces. The charter (../charter.md) states the project’s principles;
../PLAN.md records implementation status.
Syntax
This document specifies the .dli text surface and its relationship to stored
fact objects. The default build includes checking, formatting, querying, and
importing .dli documents. The MCP authoring tools offer a structured route for
agent sessions.
The decision ledger (../decisions.md) outranks this document; where they
disagree the ledger wins and this file is stale until fixed.
1. Normativity: fact objects are the language
The language is defined schema-first (DL-0003 as amended by DL-0041). The
normative definition is the schemars-derived JSON Schema of stored fact objects,
with RFC 8785 JCS bytes as canonical form. The AST and .dli grammar are
compatibility/rendering mechanisms; when either disagrees with the stored-object
schema, the schema wins and the renderer or parser is corrected.
The schema is generated from the implementation’s own types (schemars, DL-0008). A definition and an enforcer that are two hand-kept copies drift; a definition computed from the types does not. This is the same anti-drift move the whole system makes about lore, applied to its own data model.
The formatter provides a stable .dli rendering. Stored knowledge is kept as
canonical fact objects; text documents are supported input and an optional
rendering, not the bytes used as the store’s canonical form.
The grammar remains versioned for compatibility. Retired text forms are refused through the killed-form registry with a named successor; stored fact-object schema evolution remains the canonical migration path.
Consequences that fall out of AST-first normativity, each load-bearing elsewhere in this suite:
- There is no status slot in the AST. A claim’s standing is derived from its
provenance, never stored as a field (see
02-semantics.md). A bareratifiedwith no origin therefore has nowhere to go and cannot parse — koan 1 (claims come from somewhere) enforced at the grammar rather than by a lint. This also applies to provenance nested innameanddialectbodies; missing citations there produce the same error on the enclosing source line. - Proposal bundles replay by identity.
proposed-bundle "scope#number"names an existing anonymous proposal hypothesis. The renderer uses this form when reconstructing positional proposals; it cannot be combined with provenance tags orassumes(DL-0113). - Origins remain origins. When an origin token resembles a positional bundle or cannot be recovered from a support expression, its declaration is stored alongside the fact so replay retains the caller’s origin (DL-0115).
- Rendered provenance is checked. If the text grammar cannot reproduce a typed origin, a rendered corpus export or requested rendered materialization refuses. A staging call can still store the declaration, with a warning and no lossy rendered line. Status and diff omit the same line while retaining its structural key. Fact-object output remains available (DL-0116).
- Agent tools lower to fact objects. A client may request rendering, but structured diff rows and fact objects are the review and storage surface.
- The reference card and the schema are the same object at two altitudes. What a card shows is exactly what parses (§4); there is no lenient dialect hiding behind the taught one.
2. The shape of a record
A .dli file is a sequence of flat, order-insensitive, line-oriented
records. The commitments, stated once:
- One declaration per line, or one shallow keyword-fronted block. The unit of the language is a single claim on a single line. Where grouping is genuinely useful (an entity with its slots, a question with its options) the block is shallow and keyword-fronted — never deep nesting, never a tree the eye has to unwind.
- Order-insensitive within a scope. Declarations do not depend on their position relative to one another; a fact may precede the entity it is about. This is what makes nearly every edit an append or a one-line replacement, and what makes independent additions merge trivially in version control.
- A closed vocabulary of roughly thirty domain keywords. No macros, no
synonyms, no positional shorthands, no user-defined syntactic forms. Each
keyword is a worldbuilder’s own artifact name —
era,place,person,thing,event,question,fact,rule,fork,cycle,ratify, and their siblings. An unknown leading word is a parse error, not a silently tolerated extension. - References by name, never by position. A relation names its participants; a fact names its subject; a citation names its decision. Nothing is addressed by ordinal position in a list, so nothing breaks when a list is reordered or appended to.
- Indentation groups; it never nests deeply. In the landed v0 grammar
(unfrozen; the AST is normative) a leading-indented line is a continuation or
child of the record above it — an entity’s slots, a question’s options, a
rule’s body — so a dedent ends the group and does change which record a line
belongs to. Expert blocks are delimited by braces
{ … }instead. The formatter owns the canonical amount of indentation (leading whitespace is otherwise insignificant, and a tab counts as one column); a writer types loosely and saving normalizes it. This is the one shallow, one-level grouping the language admits — never deep nesting, never a tree the eye must unwind.
The grammar is kept GBNF-expressible — line-oriented, no significant whitespace, no context-sensitive layout — as a cheap standing property, not as a commitment to ship a second grammar artifact. The reason to preserve the property, and the reason not to pay for a maintained grammar-constrained emission channel, both live in §6.
There is one composition rule: the flat record. A writer’s second hour with the language is predictable from the first, because there is no second syntax regime waiting in an expert tier — only more keywords in the same shape. The expert tier (§3) adds vocabulary, never grammar.
3. The gentle subset and the expert tier
The surface is split into two reference cards, and the split is a design commitment, not a documentation convenience.
The gentle subset is most of worldbuilding. Declarations only: entities
(place, person, thing), facts and relations, timeline points, citations,
proposed, and question/option. This covers the actual daily work of a
fiction writer or a tabletop designer. A complete first world fits in twenty
lines of it:
# grammar: v0 (unfrozen; AST normative)
scope camelot
era uthers-time "Uther's Time" decided d-0001
era arthurs-reign "Arthur's Reign" after uthers-time
place camelot "Camelot" decided d-0001
place the-lake "The Lake" decided d-0003
place avalon "Avalon" proposed
adjacent(camelot, the-lake) decided d-0003
person merlin "Merlin the Counsellor" decided d-0002
merlin.trade = counsellor decided d-0002
merlin.home = the-lake proposed
thing excalibur "Excalibur" decided d-0004
excalibur.kept-at = camelot decided d-0004
question q-0001 "Who drew Excalibur from the stone?"
option arthur implies drawn-by(excalibur, arthur)
option mordred implies drawn-by(excalibur, mordred)
Question bodies accept only
option <name> ["<label>"] [implies <atom>]; every other child
is an error rather than discarded text. Fork bodies retain their command clauses
(suppose, add fact, and the other fork operations), while malformed children
that begin with option are errors. The former option ... assumes clause is a
killed form. A hypothesis condition gates the claim itself through provenance
(<claim> proposed assumes <hypothesis>), not the option declaration.
deloreis check answers in the same register the file is written in:
camelot: 10 claims certain, 3 proposed, 1 open question (q-0001).
avalonhas no decision behind it — fine as notes; it stays proposed and out of canon until you decide.
The writer’s ledger is just their own decision list — d-0001 is a line in a
file recording what they chose and when. The full ratification procedure is
house process; the concept the language enforces — a claim, and where it came
from — costs a solo writer one word per line.
The expert tier lives on a second card. Rules, forks, cycles, practices,
ledgers, and the reasoning surface are all here. The commitment is strict:
no word from the expert tier appears on the first card. A writer can work
for a long time entirely in the gentle subset and be using the full
provenance semantics — the dual-rail annotation algebra of 02-semantics.md —
without ever learning that a polynomial exists. The power is present from the
first line; the vocabulary for it is introduced only when a writer reaches for
it.
A world rule is a bare rule whose body derives its head. The body is a
comma-separated conjunction of atoms, and a body literal written not is a
negated literal:
# grammar: v0 (unfrozen; AST normative)
scope wards
rule ward-holds ratified d-0146
ward-holds($g) :- seated($g), not breached($g)
not breached($g) reads “and breached($g) is not settled.” Its meaning is
the certain-layer test and the defeater-poisoning of 02-semantics.md §4: a
merely-proposed breached never blocks the ward — it records the risk. Two
load-time disciplines guard the form (08-reasoning.md §1): range
restriction — every variable in the head or in a negated literal must also
appear in a positive body atom, because a negated literal binds nothing — and
stratification, which refuses a negative-dependency cycle (p :- not q,
q :- not p) rather than evaluate it. rule … over order and rule … over season are not world derivation rules; the CLI and MCP checks do not apply
their constraints. A bare
top-level rule that cannot be read as Datalog emits an error-class load finding;
it never silently lowers no rule. Practice-internal rule ... requires remains
a practice law, not a top-level world rule.
A functional declaration states a constraint on a predicate rather than a
fact about the world: functional <pred> says that predicate is a functional
dependency — each subject holds at most one certain value for it.
# grammar: v0 (unfrozen; AST normative)
scope camelot
functional ruler-of ratified d-0146
ruler-of(camelot, arthur) ratified d-0146
A second, different certain ruler-of for camelot is then a
continuity.fd-violation — raised against canon’s own certain layer, not only
against an incoming claim (02-semantics.md §4). When checking draft claims,
the checker can infer single-valuedness from precedent: a predicate that carries
at most one value for every subject it appears on is treated as functional for
that check. The canon consistency check itself uses declared constraints. This
inference is blind
exactly where it is most wanted — the first use of a predicate, where two values
may equally be a slip or a genuinely multi-valued relation. Deciding between them
is a ruling about the world, and the engine does not make rulings (koan 3); the
declaration is how an author makes it. It lowers to no atom — there is no fact
“ruler-of is functional” to query — and rides, with its provenance, on the
stored declaration. Its provenance is recorded but does not gate the structural
constraint (DL-0074). Agents declare it with author.declare_functional
(11-agent-authoring.md §3.11; DL-0074).
A same-as declaration states a claim about the world rather than a
constraint on a predicate: same-as <left> = <right> says two names denote
one bearer.
# grammar: v0 (unfrozen; AST normative)
scope camelot
same-as avalon = the-isle-of-glass ratified d-0146
Unlike before, the relation is undirected — left and right name the two
sides of one claim, not an earlier/later pair — so there is no canonical
bearer and no normative weight on argument order (a deliberate rejection of a
“canonical name” design: it would put a trap for an LLM author in the argument
order it wrote). The engine closes it symmetric-transitively over synthesized
rules, never by rewriting anything stored; 02-semantics.md §9 is the full
identity semantics — closure, the claim-vs-bearer distinction, defeasibility,
and the v1 temporal boundary (a same-as naming a declared era or event is
refused, loud, rather than half-merging the chronology). Like every other
world claim it needs provenance — a bare same-as with none is fail-closed,
exactly as an unprovenanced before is. The kernel semantics land first
(DL-0078); the typed wire op that lets an agent assert it through the taught
author.* surface (DL-0014, DL-0043) is a following, separately-tracked
addition.
4. Three stances at the surface
Three deliberate stances make the surface humane rather than merely small.
Errors speak the domain (koan 9, errors are lore). A diagnostic is a world-record artifact, authored with the same care as any other surface, and it never speaks in compiler idiom. Not “unbound identifier” but:
avalonneeds a ruling before this can be asserted — record a decision, mark it proposed, or move the claim into a fork.
Not “no Ord instance” but:
sortwould put the leaves in a line, but the year is a cycle — there is no first season.
Diagnostics carry a stable machine-readable class and a domain-spoken message (the two-channel error model of the charter’s tensions); the message is designed and snapshot-tested (insta, DL-0008), because the errors are an API.
Anti-Inform-7: what parses is exactly what the card shows. A natural-language surface is easy to read and hard to write — its English affordance lures authors into arbitrary phrasings the parser cannot accept. De Lore Is refuses the lure: it reads like notes, not English. The reference card is the whole grammar a writer meets; there is no gap between the phrasing a card shows and the phrasing the parser takes. Nobody has ever been lured into writing an unparseable infobox, and the surface stays that close to notes on purpose.
Proposed-first authoring. Incompleteness is a first-class state, not an
error. A claim with no decision behind it is proposed — legal, retained,
visibly out of canon; an undecided fork is a question with options, a
tracked debt rather than a blank (koan 5). A writer sketching incomplete notes
is never told they are wrong and never silently loses a line (koan 4). Choosing
truth is a separate, later, deliberate act.
5. The cognitive-dimensions ledger
The surface is accountable in the vocabulary of Green & Petre’s cognitive dimensions — used here as a tradeoff ledger, not a scorecard to pass. A review that names no costs has not used the framework; the two paid dimensions below are the bill.
Six dimensions the design wins, and how:
- Viscosity — low by construction. Order-insensitivity makes nearly every edit an append or a one-line replacement; no edit ripples through layout, and independent additions merge trivially.
- Hidden dependencies — killed as a class. Hidden dependency is this
repository’s recurring pathology (the entire propagation apparatus exists
because prose canon hides its dependencies). Every claim carries its
citations, and
--why/blame render the dependency graph on demand. A dependency you can always ask for is not hidden. - Premature commitment — removed at every axis. Proposed-first authoring
means a writer never chooses truth before writing;
question/optiondefer world commitments into tracked debts; order-insensitivity removes commitment to layout. - Closeness of mapping — one line. The keywords are the worldbuilder’s own artifact names; the record mirrors the infobox, the timeline bullet, the decision log. The distance between the writer’s mental object and its notation is a single line.
- Consistency — one shape. One composition rule means the second hour of use is predictable from the first; there is no expert syntax regime, only more keywords in the same shape.
- Error-proneness — designed out, not linted out. The closed vocabulary makes unknown words parse errors; no synonyms, no positional arguments, no significant whitespace, no optional shorthands. The constructs the framework flags as error-generating are excluded structurally.
Two dimensions where the design deliberately pays:
- Diffuseness. One claim per line is verbose — ten facts are ten lines — and this is bought on purpose, in exchange for per-line blame and clean semantic diffs. A change to one claim touches one line, and its provenance is attached to that line.
- Abstraction gradient — deliberately flat. There are no user-defined abstractions at all. Power users will feel the absence of macros; the budget’s answer is that the domain licenses records, not macros. Lore is a finite set of claims about a world, and a claim is not a function.
6. Text and structured authoring
A writer can check, format, and import .dli documents with the default CLI.
The same parser and deterministic checks apply regardless of who produced the
text. The gentle card introduces the format.
For agent sessions, the supported authoring workflow uses the author.* MCP
tools. They return structured findings and workspace state; author.materialize
commits accepted fact objects, with rendered text available on request. See
agent authoring and SKILL.md for the current workflow.
Neither path makes a model call in the kernel. A well-formed claim can still be possible, contradictory, or unsupported; the semantic checks report that state.
7. Drift discipline
The surface has three standing defenses against the definition, the examples, and the corpus drifting apart.
CI-compiled examples. Every .dli example in every document in this suite,
and in the skill, is wired into a compile harness: an example either parses
cleanly against the current grammar or the gate is red (the docs-oracle runs the
real parser over every block). An example that produces an error-class
diagnostic fails the gate.
The killed-form registry. A surface form that is retired — a spelling the language once accepted and no longer does — is registered, not forgotten. The parser rejects a killed form with a pointer to its replacement rather than a bare error, and never silently re-accepts it. This is the same discipline the lore layer applies to killed names and banned vocabulary: history is the guardrail corpus, and the machine refuses a quiet return.
Deterministic offline gate. Example parsing, formatter checks, and the killed-form checks run without a network or model call in the gate.
8. What is unvalidated
The syntax has executable examples and parser tests, but those do not establish
that every writer finds it easy to read or use. The human-writer probe in
probe-protocol.md describes a way to collect
that feedback. No claim of a completed study follows from the protocol.
Semantics
This document specifies what a .dli claim means: the annotation algebra that
carries provenance through every derivation, the two evaluations of that
annotation that yield a claim’s standing and its strength, the honest places
where the algebra does not reach and something else carries the weight, and the
cost of computing all of it. It is the deductive core of the fabric; every other
spec document reasons over the objects defined here.
The organizing commitment, stated once and then earned: no atom stores a status. Standing is derived, always — and it is derived twice, from one annotation, along two orthogonal channels.
The decision ledger (../decisions.md) outranks this document.
1. The annotation structure
Every atom — an entity’s existence, a relation, a fact — and every rule
carries an annotation. An entity declaration also supplies a unary sort fact:
for example, a declared person wren can be queried as person(wren). An
era-order declaration supplies an era fact for each member of its chain. Each
such fact has the declaration’s support and derived standing. A chain carries
one source annotation, so an individual standing promotion that cannot be
replayed from that declaration is refused before the store advances.
ann(a) = (P⁺, P⁻)
a pair of absorptive positive polynomials in PosBool[X] — the
conditional-table algebra, not full counting-provenance. P⁺ is the atom’s
support: the Boolean combination of authorial events under which the atom
holds. P⁻ is its defeater polynomial: the combination under which a
default the atom rests on is overridden (§4).
X is a set of event variables at ruling-clause grain. One variable is
minted per:
- separable clause of a decision entry (so a partial supersession is expressible — one variable per clause, not one per decision),
- option of an open question,
- coherent proposal bundle,
- episode-acceptance.
Each variable carries metadata outside the algebra — the algebra sees only a Boolean variable, never its label:
- kind —
ratified | provisional | option | bundle | episode, - origin — who, when, fork lineage,
- scope — the variable’s affects-set (the attachment side condition of §3).
Three pieces of global state complete the picture:
- ν — a partial valuation, the ledger: which variables are ruled true, which false, which still open.
- Θ — a small constraint theory: option-exclusivity families (the options of one question cannot both be true), supersession links.
- C — the negative-constraint store: the guardrails, the content that must never be present (§3).
Per fork there is additionally a valuation extension, a set of fresh variables, and an optional keep-stamp visibility restriction (§5).
Rules are annotated atoms too. A rule’s own variable multiplies into every derivation that passes through it. Three consequences fall out structurally, with no special machinery:
- A proposed rule yields only possible consequences — a rules-as-proposed practice needs no policing, because its conclusions carry its own unratified variable and can never evaluate certain.
- Fork-local rules need nothing special: a rule tagged with a fork variable is symbolically present everywhere and evaluates dead outside the fork.
- The rule that anything resting on a proposed entity is itself merely possible (the legacy validator’s V9) becomes a theorem — a derived relation multiplies in its endpoints’ existence polynomials, so it cannot be more certain than the entities it connects.
Why PosBool and not the full counting semiring N[X]. Every evaluation this
system performs is idempotent — layer membership, the ratified-over-
provisional grade, minimal-witness blame, world membership. Nothing counts
derivations; the one counting discipline in scope, linear debts, lives elsewhere
by design (§6). PosBool is exponentially smaller under absorption, and —
decisive for recursion — it is a finite lattice over finitely many
variables, so the closure predicates (within, downstream, reachable)
reach fixpoints with no continuity machinery. The one property worth keeping
from counting-provenance is kept: evaluation commutes with specialization.
The fixpoint is therefore computed once, symbolically; valuations — the
ledger, any fork, any what-if — are applied afterward as cheap homomorphisms.
Precomputed provenance, valuated on demand, is the load-bearing architecture,
not an optimization.
2. Status as evaluation
Under a valuation and constraint theory (ν, Θ), each atom’s support P⁺
yields a three-valued standing:
- certain (assertable): some monomial of
P⁺is entirely ν-true — the atom is supported by resolved events alone. The all-open-goes-false completion is always admissible (an author may park or reject everything, forever), which pins certainty to exactly the assertion gate. - possible (shadow): not certain, but some monomial is Θ-satisfiable — no ν-false variable, and no two options of one exclusivity family together.
- dead: every monomial is Θ-unsatisfiable. Split by
C: dead-by-rejection (the killing event also wrote a C-constraint) versus dead-by-supersession (stranded, not anti-canon). The algebra alone cannot tell these apart;Cdoes.
Disjunctive certainty does not count. A fact implied by every option of an open question is still unassertable until the ruling — because the all-open-goes-false completion, always admissible, makes it false. This is a deliberate position: a claim is certain when resolved events alone force it, not when it would follow under any resolution.
Grade is a second evaluation on the same polynomial, not a level in the support lattice. The grade of a monomial is stored at derivation time as the weakest-link of the deriving rule’s grade and the body atoms’ grades; the grade of an atom under a valuation ν is the join over its ν-true monomials’ stored grades. Join-of-meets over a finite chain is monotone and polynomial-time (annotated deduction). This is strictly stronger than a flat per-record status: provisionality propagates through derivation — anything whose every live support touches a provisional premise is itself provisional, which a stored status field cannot express.
The two channels are orthogonal and must not be confused: support is what a
claim rests on; grade is how well the procedure that produced it was
verified. The grade channel’s full lattice, its weakest-link rollup, and its
boundary against model evidence are specified in 07-verdicts-and-llm-boundary.md
(two-channel semantics; DL-0006). This document owns support; grade rides the
same polynomial.
Supersession without non-monotonicity. Nothing is ever retracted. The symbolic fixpoint stands; superseding a clause revaluates a variable, and evaluation re-runs on unchanged polynomials. What lets this survive a real ledger is the clause grain: partial supersessions (“supersedes D-0208, the episode-arithmetic clause only”) that one-variable-per-decision cannot express, one-variable-per-clause expresses exactly. Promotion after supersession is extension, never rewrite: the new clause’s variable attaches as a new monomial; the old monomials go dead and persist as lineage. History is additive.
3. The four honest residues, and fail-closed
The collapse of five stored statuses into support × grade is elegant, and it is honest about the four places it does not reach. Each residue is carried by a mechanism outside the polynomial, on purpose.
- Rejection of never-present content. A banned figure, a banned in-world
vocabulary word — there is no atom to make dead, because the content was never
admitted. This lives in
C, checked against the certain layer and against drafts, never part of the fixpoint. - The shadow layer’s must-not-contradict duty. The obligation that possible content not contradict certain content is a property of the evaluation regime (§4), not a truth value an atom can carry.
- V11 as an attachment side condition. Attaching a monomial containing
variable
dto atomarequiresa ∈ scope(d). The algebra cannot see which atom a polynomial decorates; the type system can, and enforces it at attachment. A bonus falls out: derived atoms cannot be hand-annotated at all, so forged promotion of derived lore is impossible by construction, not merely forbidden by rule. - Defaults need
P⁻. A default with proposed exceptions is not expressible in support alone; the defeater polynomial carries it (§4).
Fail-closed is a theorem, not a policy. An annotation that fails to parse or
fails to attach yields P⁺ = 0 — not certain, not even possible, excluded from
every layer except the error report. The context-pack builder’s rule that a
malformed provenance marker excludes its claim from generation is therefore not
a guard bolted on; it is what P⁺ = 0 means.
4. Negation, defaults, and the conflict duty
Raw negation-as-failure is banned; proposed is genuinely three-valued. The
regime is one store, one symbolic fixpoint, with layers as evaluations, never
as sub-stores.
A negative body literal ¬b does not block derivation. It poisons: all
non-contradictory, theory-respecting monomials of b’s support P⁺ enter the
conclusion’s defeater polynomial P⁻. This is valuation-neutral
all-admissible-monomials poison (DL-0024): the fixpoint includes every
monomial that could become true under some valuation — never filtered by
the active ν — so the symbolic saturation is identical regardless of which
valuation is set at fixpoint time. Evaluation commutes with specialization: a
fork’s eval_under(ν) reads the precomputed supports and defeaters without a
re-fixpoint and gets the same answer a fresh fixpoint under ν would give
(TQ-N3 property).
The consequence of poison for status:
- A certain
b(some monomial ofb’sP⁺is ν-true): its monomials enterP⁻, and — because they are themselves ν-certain — the conclusion is present-but-Possible (defeated). The conclusion’s support monomial is poisoned by a live defeater, so it cannot evaluate as Certain. No derivation is blocked; no row vanishes — the conclusion exists with degraded standing. - A merely-possible
b(no monomial ν-certain, but some ν-live): its open monomials likewise enterP⁻. Under the current valuation the defeater is not yet certain (so the conclusion stays Certain if its support is clean), but the risk is recorded — a future ruling that makesbcertain will defeat it. The conclusion is certain-at-risk. - A dead
b(every monomial ν-killed): its monomials are still inP⁻syntactically but evaluate dead, so the poison is inert and the conclusion is unaffected. A clean support evaluates to Certain without obstacle.
An atom that is certain but carries a Θ-live P⁻ is certain-at-risk:
assertable now, with the pending fork that could defeat it surfaced —
“assertable; awaits q-00nn.” This single mechanism is three things at once:
regional defaults with proposed exceptions (the exception rides in P⁻), the
shadow layer’s must-not-contradict duty (discharged as surfaced risk, not as a
block), and why-not provenance.
Worked, in the surface. A ward holds where its ground is seated and not breached:
# grammar: v0 (unfrozen; AST normative)
scope wards
seated(ground-oak) ratified d-0146 # certain
breached(ground-oak) proposed # a shadow: possible, not certain
rule ward-holds ratified d-0146
ward-holds($g) :- seated($g), not breached($g)
breached(ground-oak) is merely proposed — its monomial is ν-live but not
ν-certain. The negation not breached($g) poisons: breached’s open monomial
enters ward-holds’s P⁻. Because the defeater is not yet certain,
ward-holds(ground-oak) evaluates as Certain (its support from seated is
ν-certain and the defeater does not yet fire). It is certain-at-risk:
assertable now, flagged as defeasible should breached ever be ruled true. Were
breached certain instead, its monomial in P⁻ would be ν-certain too —
poisoning the support monomial — and ward-holds would evaluate as
present-but-Possible (defeated), not absent. The derivation still exists;
only its standing degrades.
Slots are functional dependencies. A slot (entity.key) admits one value;
the continuity checker already treats them so. Two distinct live values on a declared-functional non-fluent slot, at least one
an authored proposal that is still possible, raise a warn-level
continuity.pending-conflict unless the slot already has a certain authored functional
violation. The warning does not decide either account or block materialization.
Dead claims are excluded; fluents are checked against their time intervals separately.
Which predicates are functional, and what a break is called. A predicate is
functional when the author declared it so (functional <pred>, spec/01 §3). For
a claim-relative check — a draft setting one slot two ways — a predicate canon
already witnesses as single-valued everywhere it appears also counts, and the
clash is continuity.intra-episode-contradiction. But the world-internal check —
asked of canon itself, continuity.fd-violation — fires only on a
declared functional predicate: canon’s own shape cannot settle functionality.
A subject bearing several edges while others bear one each is a hub in a
many-to-many relation (a place with several neighbours) exactly as readily as a
functional-dependency break, so inferring functionality from degree manufactures
findings against lore that is merely unusual. The author rules on it (koan 3); the
declaration is what arms the class. This is the read-side counterpart to merge’s
blame-pair check (§5), closing the asymmetry that let a load accept a doubly-set
slot a join refuses. Distinctness of values is judged on the FD value key —
case, hyphen/underscore, and whitespace folded, so gold and Gold are one
value — and on nothing else: other punctuation survives (2.5 and 2-5 stay two
values) and stems are never folded (gold and golden remain two), because
merging genuinely distinct values would silently drop a contradiction (koan 4).
Fluent slots are excluded here and judged over their intervals instead
(continuity.fluent-contradiction, spec/03 §2), where a value held over disjoint
spans is no clash at all.
Ratification pre-flight is a free query. Before valuating an option, evaluate the would-be valuation and verify that the certain layer stays FD-consistent and C-consistent; report blame as the exact conflicting monomial pair. What would otherwise be discovered after a decision propagates becomes a question asked before it.
The continuity classes and the four fact-check verdicts are all recovered as evaluations over this regime — contradiction is an FD-versus-certain clash or a C-match with blame; asserting-the-merely-possible is asserting a possible-not- certain atom; unknown is absence from certain ∪ possible. No new mechanisms are introduced to check a draft; checking is querying.
5. Forks, merges, promotion, and the keep-stamp
A fork is a named condition context: a valuation extension, a set of fresh variables, an optional visibility restriction, and local atoms and rules tagged with fork variables. Creation is O(1) — no store is copied; only the local additions run the incremental fixpoint.
The operations:
# grammar: v0 (unfrozen; AST normative)
fork arthur-drew-the-sword
suppose q-0001 = arthur
add fact drawn-by(excalibur, arthur)
- suppose — valuate an open variable within the fork.
- add — introduce a fork-local atom or rule.
- suppress — valuate a bundle false within the fork.
- restrict — apply a keep-stamp (below).
The interesting output of a fork is the derived-consequence diff: the set of
atoms whose (support, grade) changes between the base valuation and the fork —
newly-certain, newly-dead, grade-changed, newly-in-conflict. A fork is a lens on
consequence, not a copy of the world.
Merge is a constraint-join. Two forks conflict iff they valuate some variable differently, or jointly violate Θ, or their joined certain layers break a functional dependency or a C-constraint. The third kind is semantic and reports its blame pair — the exact two monomials that cannot both hold.
Promotion is an application decision recorded as data. A fork does not become active merely because it exists. The consuming application records the decision state and supplies it with the affected scope; DLI verifies the referenced decision and re-evaluates the valuation. The affected atom set can therefore be generated, origins persist as lineage, and forged or unknown citations fail closed. DLI does not decide which caller was entitled to make the decision (DL-0054, DL-0058).
The keep-stamp is visibility, not valuation. It is a thin second dimension —
kept-present | kept-absent | removed — applied after evaluation and composing
as a filter over the results. This is what keeps licensed ignorance
mechanically distinct from falsity: a fact hidden from a deck, an axiom kept
out of a proof, a character not permitted to know something are all present and
true and filtered from a view, never false. The distinction holds at every
level the censorship operates.
6. Linearity stays out: object versus meta
Two accounting systems live in the fabric, and conflating them would be a category error.
Debt conservation and linear resources govern the object language. A debt
token spent twice inside an in-world proof is an in-world error, checked by the
resource discipline of 04-practices.md. This is a fact about the world.
Annotations govern the meta language. A polynomial records which authorial events support a fact. This is a fact about the authoring.
If the two were one system, an authorial what-if — supposing a variable to explore a fork — could perturb an in-world proof’s resource accounting, which is nonsense. They are kept apart, touching at exactly two points:
- The proof checker’s premise selection reads the certain projection of the store (certain ∩ known-by-speaker ∩ present-in-scene).
- A checker’s verdict is itself a derived atom whose
P⁺multiplies its premises’ polynomials — so a proof’s validity degrades automatically when a premise later loses support. A proof that rested on a since-superseded fact stops being certain the moment the fact does, with no re-checking of the proof itself.
Resource conservation remains a fold invariant plus the checker’s resource- multiset bookkeeping — an affine side condition, not a linear type theory intruding into the surface language.
7. Cost
Evaluation is annotated semi-naive with aggressive absorption at every join, which keeps supports narrow — monomial widths and alternative counts stay small at corpus scale, because absorption discards dominated monomials as they form. Absorption is the performance linchpin, not a cleanup pass.
The policy that keeps recursion tractable:
- Recursive predicates recompute — the certain evaluation is stored together with a coarsened possible-approximation, rather than a full symbolic form.
- Non-recursive predicates store symbolically — computed once, valuated on demand.
- Exact-on-demand queries — minimal-witness blame, ratification-survival — run over an inverted variable-to-atom index, so a question about one variable’s reach does not walk the whole store.
The concrete numbers — a ledger on the order of hundreds of clauses, atoms on the
order of thousands to tens of thousands, monomial widths in the single digits, a
symbolic fixpoint in milliseconds against a CI gate measured in seconds — are an
engineering envelope, the scale the design is built to sit comfortably
inside, not a guarantee to be quoted as a benchmark. The claim they support is
qualitative: the state tree is never walked. Provenance is computed once;
worlds are valuations applied afterward; search enters only where a question
inverts the fixpoint, and those inversions are enumerable (see
08-reasoning.md).
8. What this buys, in five lines
- Certain answers are the assertability gate — fail-closed and the V9 rule are theorems, not guards.
- Blame is polynomial support — minimal-witness on demand, from the same object that gates assertion.
- What-if is a valuation — forks, supersession, ratification pre-flight, and episode cuts are all changes of ν over unchanged polynomials; nothing is retracted, so monotonicity is never at stake.
- Citation is a rendering of the polynomial that gates assertion — the citation shown to a reader and the support that admits the claim to canon are the same object, so they cannot drift apart.
- Status is a view. The legacy five statuses collapse into support × grade,
with exactly the four honest residues of §3 carried outside the algebra —
C, the conflict duty, V11 attachment, and the threaded episode fold (03-time.md).
9. Identity: symmetric-transitive closure over same-as
same-as <left> = <right> (01-syntax.md §3) is a claim like any other:
compiled to a ground atom same-as(left, right) with a required annotation,
gated exactly as before or functional is (DL-0078). Nothing about the
annotation algebra of §§1–4 changes for it. What is new is what the engine
does with the atom once it exists.
Closure is synthesized rules, never a rewrite. No stored or authored layer
is touched — not the loader’s atom stream, not ClaimId minting, not the
interner, not wm.entities. Instead, iff at least one same-as fact exists,
the engine adds these rules to the evaluable world (dli_eval::world:: synthesize_alias_rules):
- symmetry —
same-as(Y, X) :- same-as(X, Y). - transitivity —
same-as(X, Z) :- same-as(X, Y), same-as(Y, Z). - per-predicate rewrite — for every
(pred, arity)observed among extensional facts and rule heads (excludingsame-asitself), and for every argument positioni:pred(.., Y, ..) :- pred(.., X, ..), same-as(X, Y)withYat positioni.
All three shapes are positive and range-restricted, so they add no negative
dependency and cannot make an already-stratifiable rule set unstratifiable. A
world with zero same-as facts synthesizes zero rules — the guard is
on the fact set, not on whether the synthesis function ran — so a corpus that
never declares identity is byte-identical, rule for rule and digest for
digest, to one built before this feature existed. §1’s support algebra does
the rest for free, with no case added anywhere for identity specifically:
- Support conjoins. A merged copy’s support multiplies the fact’s support
by the identity link’s (and the rewrite rule’s own, unconditional, support)
— so
whyon a merged copy names the identity assertion’s decision or hypothesis variable alongside the fact’s own. The identity ruling is not a silent side channel; it is a first-class citation. - Identity is defeasible. A
same-asis gated like any claim: aproposedone yields only possible copies (never certain until ruled); adecided/ratified/provisionalone is certain once its citation is ruled true. Defeating the citing decision — the existing standing machinery, one valuation-variable flip — kills every consequence that passed through it algebraically, with no re-fixpoint: the copies read dead on the very next evaluation, because their support was always conditioned on that variable. This is what makespropose → suppose-in-a-fork → preview → ratifythe identity-resolution workflow: a fork’seval_undersees the merge or does not see it purely as a function of which variables it valuates, over an unchanged symbolic saturation (§1, TQ-N3). - Closure runs both directions, with no goal rewriting. Symmetry means a
query for either name answers identically; a chain
a = b,b = ccloses transitively to all six ordered pairs among{a, b, c}and every predicate rewrite composes across them, in the same seminaive fixpoint every other rule already uses. A cycle (a = b,b = a) is absorbed the way any redundant derivation is — the fixpoint is a finite lattice (§1) — and a self-merge (a = a) derives nothing beyond its own reflexive row.
The claim-vs-bearer distinction. The stored same-as fact is one ordinary
FactObject; two agents asserting the same proposition under different
aliases (same-as bob = robert versus same-as robert = bob) mint two
distinct claim objects (ClaimId is minted from the rendered atom text,
never re-oriented) — intentionally, because they are two distinct assertions
even though they close to the same equivalence class. The world unifies at the
bearer level only: there is no canonical-name field anywhere, and nothing
about the merge privileges whichever name a query happens to use. A
canonical-bearer design was considered and rejected for exactly this reason —
it degenerates to closure plus a designation table, and it puts normative
weight on argument order, a trap for an agent author who cannot know which
side of = a downstream reader will treat as canonical.
Checks that read the authored layer, not the derived one (FD, intra-episode,
fluent-interval reasoning) get a second, complementary mechanism: a
union-find canon map (dli_core::alias::canon_map) built once per check run
from the world’s certain same-as pairs, with the lexicographically
smallest member of each class as its representative — used only for grouping
and for messages, never stored, never gating assertion. certain_binary_slots
canonicalizes both the subject and the rendered value before the FD key is
computed, so spouse(x, a) and spouse(x, b) under a certain a = b read as
ONE value, not a functional-dependency break; a fluent-timeline group
concatenates every class member’s spans (each span keeps its own
value/anchors/annotation, so intervals are never flattened) before the
existing overlap sweep runs once over the merged timeline. A post-merge
continuity.fd-violation is the SAME code a pre-existing break would raise
(never a new one) — its citations are the union of the conflicting slot’s own
citations and the identity link’s, so the finding names the assertion that
caused the merge, not only the two facts it brought into conflict.
v1 refuses same-as over a declared era or event (user-ruled, DL-0078):
continuity.identity-temporal-unsupported, error, loud, at the world-internal
layer regardless of the link’s own standing (even a merely proposed link over
two events is refused, so a later promotion cannot silently half-merge). Time
points are structural — interned chronology indices, not valuation-gated facts
— so a defeasible merge over them is not expressible (chronology, as-of, and
dli.timeline.* would not see a derived-rule “copy” the way a query over an
ordinary predicate does), and a non-defeasible merge would contradict
identity’s own defeasibility and could half-merge just as silently the other
way. The refusal necessarily lives at the check layer, not synthesis
(dli_eval::world::synthesize_alias_rules builds rules purely from ground
predicate/arity shape — it has no entity-sort information and so cannot know
that avalon names a place while the-crowning names an event; only the
loader’s compiled world, with its entity registry, can tell the two apart).
This is a boundary with a named un-lift trigger, not a permanent wall:
the DL-0078 entry records it. The un-lift direction, when the trigger fires,
is structural point unification at the chronology/interning layer — two
time-point table entries collapsing to one index, with every edge and span
anchored at either re-pointed to the unified index — not a valuation-lazy
mechanism layered on top the way the predicate-rewrite closure is; that is a
different, larger piece of engineering than this feature, deliberately left
undone until a consumer actually needs to merge two coreferent events or eras
(e.g. one battle known under two names, each anchoring its own interval
states).
10. Predicate identity on the wire (DL-0092)
author.assert_predicate_same_as is a typed declaration
that two predicate names denote one property. This is the predicate analogue of
entity same-as, not a name-normalization facility: the caller supplies the
claim and its provenance, while the engine records it, evaluates its
consequences, and explains or retracts those consequences. Name similarity,
string distance, shared subjects, overlapping values, and observed data shape
never create predicate identity.
The declaration has its own identity-link channel. It is not an entity
same-as atom, and an ordinary fact or rule cannot activate it accidentally.
When the link is certain, the evaluator and the authored-layer checks use one
deterministic, lexicographically smallest representative for its
symmetric-transitive component. A chain of links therefore closes without
choosing a normative canonical name; a self-link is a warning and a semantic
no-op. Source facts, declarations, and rules retain their authored predicate
names, so the canonical view is reversible when the link is defeated.
10.1 Certainty, preview, and defeat
Predicate identity uses the same certainty gate as entity identity. A proposed
link may be visible as a possible staged consequence in author.diff, but it
does not create a certain merged slot, a certain FD violation, or a certain
fluent contradiction. To preview those certain consequences, the workspace
must stage the identity with a citation whose decision resolves (or use the
equivalent fork valuation) before author.check/author.diff is read. There is
no predicate-specific preview shortcut and no stronger authority requirement
than the entity path.
Every copied or derived consequence multiplies the identity link’s support into
its own support polynomial. author.why therefore names the identity claim as
well as the fact, policy, or rule that supplied the other support. Defeating the
identity through author.defeat flips that support variable: the FD or fluent
consequence clears, derived copies become dead, and the original facts remain
under their original predicate names. No fact is deleted and no special
re-fixpoint is needed to make the valuation change visible.
10.2 Policy and shape interactions
Functionality and inertia use union semantics within a certain predicate
identity component: the merged property is functional if any member has a
functional declaration, and inertial if any member has a certain inertial
declaration. A functional declaration is a structural constraint: its
provenance is stored but does not gate the constraint (DL-0074). Inertial
provenance does gate its policy. A promotion emits a structured
predicate-identity.policy-promoted warning naming the identity assertion,
policy kind, affected members, and policy declaration support. The warning is
non-blocking; it records that merging changed the effective policy rather than
silently changing whether a check runs. A proposed identity does not promote either
policy in the certain layer.
V1 validates observed positional arity only. The workspace keeps an arity index
for facts and rule heads/bodies, and an identity claim rechecks only the
identity component touched by its endpoints. Adding a fact or rule rechecks
only claims whose component contains that predicate; an unrelated predicate
does not trigger a world-wide scan. Unknown arity remains open until relevant
evidence arrives. A known incompatible arity raises the structured error
predicate-identity.arity-mismatch and blocks materialization rather than
performing a partial merge. A declared predicate-shape/type system is the
explicit trigger for lifting this observed-arity boundary.
10.3 Checks, rules, and convergence
FD grouping uses the predicate representative as part of the (entity, predicate) slot key. If identity puts two distinct certain values into a
functional slot, the existing continuity.fd-violation finding is emitted;
its structured data and support name the predicate-identity assertion, the
canonical slot, and both conflicting values. No new human-message parsing
contract is introduced.
Fluent timelines are grouped by the same representative before the existing
overlap sweep. Overlapping certain spans produce the existing
continuity.fluent-contradiction, with the identity assertion in its
structured support. Defeating the link separates the timelines again and
removes only the contradiction and persistence consequences that depended on
that link.
Rule bodies and heads are matched through the predicate closure. A body written over either member can match facts under the other; a head written over either member derives in the merged property. Rule support and identity support are conjoined, and defeating the identity therefore removes the dependent derivation without removing the authored rule or its source facts.
Entity and predicate identity are independent closure channels. Applying both
in either arrival order must produce byte-identical certain rows, findings,
support, and author.why results. The loader, evaluator, and durable lorestore
must likewise agree on the declaration and its consequences byte-for-byte.
Branch-scoped reads use the branch head commit (head_ref.commit) for the
pre-image; they never fall back to root_hash(). An identity staged or
materialized in one branch cannot affect its base or a sibling branch.
This declaration remains wire-only. The structured authoring result, staged
diff, fact-object store, and query/why data are the supported review and
round-trip surfaces; no new .dli predicate-identity form is added.
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.
Practices
A practice is a declared system of laws — a magic system, a divination game, a rhetorical proof discipline, a craft with rules — expressed entirely as data in the fabric. The kernel supplies one generic checker and one generic evaluator; a practice supplies the kinds it reasons over, the laws as annotated rules, the resources it spends, the side conditions its castings must satisfy, the ways it fails, and the currencies those failures and successes cost. From that declaration the kernel derives a checker for castings, proofs, readings, and workings in that practice — with citations, expected verdicts, and honesty grades, exactly as for any other lore.
The same practice and proof machinery can check different declared systems.
A practice describes its rules as data, and check-proof checks a proof
against that declaration. DLI does not ship a practice simulator.
1. The claim, precisely
A magic system is a practice with laws, and the laws are data. A
spoken-enchantment practice, a vow discipline, a card game — none is a component
of the kernel. Each is one practice instance of a generic machine, one .dli
scope among others. Nothing about a practice’s own kinds, its resources, its side
conditions, or its timing failure is written into any Rust type or evaluator
branch. Point the same machinery at a wholly unrelated art — a true-naming
enchantment, a card game, a smith’s temper-craft — and it runs with zero kernel
change, because everything that distinguishes one practice from another lives in
the practice’s declaration.
This is koan 10 — one fabric enforced at the level of magic:
a practice is ordinary lore, so the whole existing apparatus applies to it for
free. variant deltas give a practice regional forms; a fork may suppose a
candidate change to a law and report which ratified castings break; a proposed
practice is structurally unassertable until ruled, like any lore. There is no
per-system engine, and there is no seam through which one could enter: a
second magic system that needed kernel support would be a bug in the
parameterization, not a feature request.
2. What is generic
The following exist once in the kernel and cost zero lines per practice. They are the closed vocabulary a practice declaration draws on; a practice that needs something outside this vocabulary is telling the workbench that the parameterization is incomplete, which is a finding worth having.
Typed finite kinds, including the cyclic cycle. A practice declares its
own kinds — bonds, grounds, roles — as ordinary finite kinds
(01-syntax). The cyclic cycle kind (03-time,
koan 8) is available on the same footing: tide, moon, and
season are cycles, and so is any rankless wheel of a game’s turns. A cycle
carries succ, pred, across, and distance and refuses <, sort order,
and maximum — the same rank-refusing kind the timing checker and the game
engine share.
Annotated facts and rules. A practice’s laws are annotated rules (02-semantics): each rule carries a citation-and-hypothesis annotation polynomial, a derived status, and behaves under forks as a valuation. Proposed rules yield merely-possible consequences; a fork-local rule is free; the invariant that derived atoms cannot be hand-annotated (V9) is a theorem of the algebra, not a check bolted on. A magic law is a rule; a casting’s conclusion is a derived atom.
The closed resource algebra. A practice spends resources drawn from a fixed, small algebra — nothing else is a resource:
linear— spent exactly once; the ledger must account for every unit.linear conserved— additionally, no ledger step may mint or burn the resource; a casting may reveal, assign, redistribute, or release it, but the multiset balances across every close or the ledger does not check.affine bounded <cap>— spent at most up to a declared capacity; overspend is a failure, underspend is legal.graded monotone <order>— an ordered grade that only ever moves downward along its declared order; nothing is spent, but the grade wears.
Side conditions over the scene projection. A condition is a predicate over the world as projected into a scene: states that hold there, cycle phases in force, cast presence, consent facts, scope containment. Conditions are how a practice reaches into story-time and world-state without special-casing either — the timing of a casting, the standing of a witness, the boundary a claim may not cross are all conditions over the same projection.
Error classes as refinements of the seven structural classes. The checker has exactly seven structural error classes, and every diegetic failure a practice declares is a refinement of one of them:
| Class | Structural meaning |
|---|---|
| L1 | unknown-form — the casting names a form the practice does not declare |
| L2 | premise-not-available — a required premise is absent or contradicted in the projection |
| L3 | side-condition — a declared condition fails (timing, consent, scope, soundness) |
| L4 | bad-instantiation — a form is instantiated against arguments its kinds forbid |
| L5 | invalid-chain — a step joins premises the law does not license |
| L6 | resource-violation — a spend breaks the resource’s algebra |
| L7 | close-mismatch — the close does not follow from the accounted ledger |
A declared error names its structural class, adds a discriminant (which sub-case of that class it is), and may add an on-mic signal — the audible consequence a performer voices when it fires. The discriminant is what lets two failures share a structural class and still read as different drama.
The expected-verdict discipline and the verdict lattice. Every checked casting declares the verdict it expects; a scene built on a character’s flawed argument expects a failure, and the checker confirms it fails for the declared reason. Verdicts carry grades from the lattice in 07-verdicts-and-llm-boundary.
The premise-pool projection. A casting’s available premises are exactly certain ∩ known-by-speaker ∩ present-in-scene — the certain projection of the world at that scene, intersected with what the speaker knows as of that scene, intersected with what the scene puts on stage. This one projection is the seam between a practice and the rest of the fabric, and it is where licensed ignorance becomes checkable (§3).
Everything else — which kinds, which laws, which resources, which conditions, which failures, which costs — is the practice’s own declaration.
3. A conserved-resource practice, worked
A vow is a practice with laws. Oath-binding — a public-domain “a boon owed
must balance” — declares a linear conserved resource and the failures that
attend it, entirely as data:
# grammar: v0 (unfrozen; AST normative)
scope camelot
practice oath-binding proposed {
use chronology.cycles # the pledge seats its hour on the moon
kind bond = fealty | troth | ransom
role oathgiver : person requires standing(oathgiver)
role oathholder : person requires consent(oathholder)
resource boon : linear conserved # a boon owed must balance — the debt law
resource strain : affine bounded body-limit # the swearer's body caps the draw
rule pledge(bond, oathgiver)
rule give-and-take # every boon revealed, assigned, or released balances
condition witnessed requires witness within earshot
error unowed refines L2 premise-not-available where absent
# a pledge drawn on a boon never granted
error forsworn refines L2 premise-not-available where contradicted signals dishonour
# a boon the ledger contradicts recoils on the swearer
error wrong-moon refines L3 side-condition [timing] over cycle moon
# the same oath, sworn at the wrong turn of the moon
error overpledged refines L6 resource-violation on strain signals collapse
# pledging more strain than the body-limit allows
error hollow-vow refines L2 where available-in(licensed-pool) and contradicted-in(certain)
# seats-and-lies: true against a licensed pool, false against the certain
cost social | bodily | material | epistemic
}
Three design beats carry the weight of the whole approach.
Refinements, not a parallel taxonomy. The declared failures are not a
second error system bolted beside the checker’s seven classes — each is a
refinement of one class. Unowed and Forsworn are both L2, split by the
discriminant absent versus contradicted: an unowed pledge draws on a boon
never granted, a forsworn one on a boon the ledger actively contradicts, and
forsworn signals dishonour while unowed signals nothing. Wrong-moon is the timing
side condition over the kernel’s own cycles — an oath that fails for being sworn
at the wrong turn of the moon is one declared line, not a special case.
Overpledged is the affine bound on strain. A ledger reading in oath-binding
speaks the story’s language back:
invalid: wrong-moon (L3 [timing] at s2) — the moon is at the wane in the pledge;
the oath allows only the waxing turn
and a scene that declares expect fails-at wrong-moon reads as drama, not
plumbing.
Licensed ignorance is the failure only this kernel can state. It is a
provenance failure: the vow validates against a licensed pool — a set of
premises with certain facts withheld — while its conclusion is contradicted in
the certain projection. The checker states it as a differential between two
premise pools: certain, and the licensed subset. The verdict is “seats and
lies” — an oath true against what the swearer is licensed to know and false
against what is certain (oath-binding’s hollow-vow). A proof checker without an
annotation layer beneath it cannot even express the two pools, let alone diff
them; here it is one declared error refining L2 with the discriminant
available-in(licensed-pool) and contradicted-in(certain).
Cost vocabulary is declared, not computed. A practice can name cost
currencies such as social, bodily, material, or epistemic in a cost line.
The current ledger checks declared resource premises; it does not produce a
per-currency cost report from that vocabulary.
4. The arbitrariness proof
A practice is only a genuine parameterization if a second system, disjoint from the first on every declared axis, hosts with zero kernel code. Merlin’s craft is that proof — a spoken enchantment with nothing in common with a conserved-debt vow:
# grammar: v0 (unfrozen; AST normative)
scope camelot
practice merlins-craft proposed { # the arbitrariness proof: nothing in common with oath-binding
use language.registers # its primitives ARE the naming layer (05-language-layer)
resource breath(word) : graded monotone full > spent > silent
rule speak(bearer) requires
knows(caster, true-name(bearer)), # a knowledge state
breath(true-name(bearer)) >= spent,
derivation(true-name(bearer)) checks in register-of(bearer)
effect speak degrades breath by 1 # a word spoken thins the breath behind it
condition unheard requires no witness within earshot
error mis-spoken refines L4 bad-instantiation
# a mispronunciation IS a failed dialect derivation
error stale-name refines L2 where premise-outdated
# the bearer has changed since the name was learned
error word-overheard refines L3 side-condition [unheard] signals degrade(+1)
cost vital | epistemic
}
Nothing here resembles a conserved-debt vow. The resource is a graded-monotone decay, not a conserved linear multiset — nothing is spent, the breath only wears. Failure is degradation and staleness against states, not typed proof-step errors. The premises are names known, not boons owed. And the practice consumes the language layer wholesale: a true-name is a dialect derivation, so a mispronunciation is literally a grammar error (L4), and learning a name is a knowledge state. Three layers — magic, language, and story-time — meet in a dozen declared lines and zero kernel code.
That is koan 10 demonstrated rather than argued. The one honest
cost this practice exacts is graded monotone: hosting merlins-craft required
adding a member to the resource algebra. Had it needed more than one new
algebra member, the parameterization would have failed — which is exactly what
makes this example load-bearing.
Standing role. The camelot scope is the workbench’s acceptance test. It
ships as examples/camelot.dli and is compiled by
the gate: if a future kernel change makes merlins-craft require per-system code,
the example stops compiling with zero kernel code and the regression is caught at
the gate, not in review.
5. Ledger checking
A ledger is one casting written out for the checker: its premises, its
steps, its spends, and its close, run in <practice>. The ledger step forms
are a small closed set — roughly nine — and the checker instantiates each
explicitly rather than searching for a derivation:
premise— an atom the casting rests on, drawn from the projection, with its cue and, where it is an observation, its source (from-scene,observed).instantiate— bind a declared form’s kinds to concrete arguments.chain— join prior premises and steps by a named law to a conclusion.assume— take a fork-local supposition, marked as such.spend— draw a resource unit against a named premise or step.release— return alinear conservedunit to the multiset.redistribute— move a conserved unit between accounts without minting.reveal— surface a conserved unit already implied by the accounts.close— the casting’s conclusion, with its expected verdict.
Proof bodies are fail-closed syntax. Only those child keywords may open a proof
line; fixed-shape operations reject missing fields and surplus tokens before the
document can be formatted or replayed. instantiate and chain are parsed to
their complete shapes. assume always contributes grade asserted; a promoted
or conflicting grade is malformed input. An instantiation binds every declared
parameter exactly once, and a value for a declared finite kind must be one of
that kind’s members. A practice may also invoke a declared rule directly as
step <label> <rule> [<value>, ...]; those positional terms must match the
rule’s exact arity and finite kinds. Opaque custom-step text is not a proof.
reveal and redistribute must name an existing resource premise even though
their accounting delta is zero. A proof header carries exactly one role binding
(speaker, or a role declared by the practice) and one in <practice> target,
and every proof ends with exactly one final close; the checker revalidates
header layout, header roles, close layout, and the complete canonical
surface-to-parser replay for direct AST callers. Relation atoms must have
balanced parentheses in every proof position; nested claims use the same grammar
in premises, chain conclusions, and closes. Binding and positional values are
tokenized terms, so a comma inside a quoted value never becomes a separator.
Consequently check, check-proof, and fmt cannot silently turn a malformed
or unsupported proof into a smaller valid one.
When a CLI proof file or worker document declares more than one proof, the
caller selects one explicitly with --proof <name> or the wire proof field.
The selector may be omitted only for a single-proof input; ambiguity, an unknown
name, or duplicate names fail closed. The practice always comes from the
selected proof’s in <practice> header.
A ledger is written as data and checked, never authored by the machine:
# grammar: v0 (unfrozen; AST normative)
scope camelot
proof scene-04 speaker merlin in merlins-craft {
premise p1 knows(merlin, true-name(the-enchanter)) cited d-0006 cue "line 112"
premise p2 breath(true-name(the-enchanter)) from-scene cue "line 118"
step s1 instantiate speak with bearer = the-enchanter
step s2 chain from p1, s1 by speak conclusion named(the-enchanter)
spend p2
close named(the-enchanter) expect holds
}
The kernel projects the world as of the scene — certain ∩ known-by-speaker ∩ present — verifies each premise is actually available in that projection, runs the steps, does the linear accounting over every resource named in the practice, and matches the close against the declared expectation:
invalid: L2 premise-not-available at p2 — breath(true-name(the-enchanter)) is
spent in scene-04; the caster has no breath left to speak the name
(episode-01, facts_established); cue "line 118"
expected: holds got: blocked grade: checked
The expected verdict cuts both ways. A scene built on a character’s flawed
argument declares expect fails-at stale-name, and the checker confirms the
casting fails at the declared step for the declared reason. A wrong argument
that accidentally validates is as much a bug as a right one that fails — the
fails-at expectation is xfail-for-drama, and a silent success where the story
needs a failure is a caught regression. Grades attach to checks: a ledger over
D-cited premises checks at grade checked
(07).
Linearity stays object-level — the two contact points. Conservation and linear resources govern the object language: a debt token spent twice inside a casting is an in-world error. The annotation algebra governs the meta language: which authorial events support a fact. Conflating them would let an authorial what-if perturb in-world proof accounting, which is nonsense. The two languages touch at exactly two points, and nowhere else:
- The checker’s premise selection reads the certain projection — so the set of premises a casting may draw on is governed by support.
- A checker verdict is itself a derived atom whose positive support multiplies its premises’ polynomials — so a proof’s validity degrades automatically when a premise later loses support, with no re-run.
Conservation is therefore a fold invariant plus the checker’s resource-multiset bookkeeping — an affine discipline as a side condition, not a linear type theory bolted onto the surface language.
Because a proof ledger produces a derived atom with real support, everything downstream inherits it: a ruling that weakens a cited premise flips every ledger that rested on it, at the next proof check, as a finding.
6. Practice well-formedness
deloreis check gains three well-formedness obligations for practice
declarations, checked at compile time before any casting is run:
- Every declared error refines a structural class. An
errorthat names no L-class, or names one outside L1–L7, does not compile. The discriminant and the optional on-mic signal are checked against the class’s shape. - Resources use only the closed algebra. A
resourcewhose kind is notlinear,linear conserved,affine bounded <cap>, orgraded monotone <order>is a compile error — there is no open resource vocabulary. - Rules type-check against the practice’s kinds. A law that instantiates a form against arguments outside the declared kinds, or references a role or condition the practice does not declare, fails to compile.
A ledger gains one clause — in <practice> — and loses nothing: the same
ledger checker serves oath-binding, merlins-craft, and any declared game, because
the practice it runs against is data all the way down.
The language layer
Names of people, places, and things — and the world’s language generally — are world knowledge, and world knowledge lives in one fabric (koan 10). A dialect is a claim about a linguistic zone of the world: its phonotactics, its name grammars, its name-part stock, every element carrying a gloss and a citation. A ratified name is canon. The language layer is therefore not a sibling tool bolted to the side of the fabric — it is packs in the fabric, under one semantics, statused and forkable like any other lore.
The consuming repo’s naming oracle splits along a principled seam rather than staying whole in the application: the half whose ground truth is a fact about the story’s world re-homes into the kernel as language packs and compile checks; the half whose ground truth is a fact about ours stays caller-side as a second screen. §3 draws that seam check by check.
1. Registers as declarations
Everything a dialect holds per linguistic zone becomes a pack, one-to-one — a residency change, not a redesign:
# grammar: v0 (unfrozen; AST normative)
scope camelot
dialect old-speech "the old speech" proposed {
basis attested "Old Welsh / Brythonic" # an external-world claim — see §7
vowels a e i o u y
endings -a -oc -wen
cluster-max 2
stress penultimate
name-kind person.given = given-proto given-deutero
name-part myr "sea" role given-proto proposed
name-part din "fastness" role given-deutero proposed
}
Each name-part is world lore with provenance — a cited fact about a tongue, statused and forkable like any fact. The phonotactic frame (vowels, endings, foreign-cluster markers, cluster ceiling, stress) and the name-kind slot grammars are world facts too: they are what the composed surface form is checked against.
One thing does not migrate. The composition weights the consuming repo’s generator uses — how often to try a dithematic name versus a simple one — are proposal policy, not world fact. They describe how a generator should sample the space; they say nothing about what the tongue is. Weights stay caller-side with the generator that samples them.
2. Names as typed derivations
A name declaration cites its dialect and its composition, and “dialect-true” stops being a reviewer’s impression and becomes a compile verdict:
# grammar: v0 (unfrozen; AST normative)
scope camelot
name myrdin "Myrdin" person.given in old-speech
= given-proto myr + given-deutero din
bearer the-enchanter proposed
# checked: the slot sequence matches a declared name-kind pattern; each
# name-part exists in the dialect under the claimed role; the composed
# surface form passes the dialect's phonotactics
Three obligations compile together on every name:
- Slot sequence. The sequence of name-part roles must match one of the
dialect’s declared
name-kindpatterns.given-proto given-deuterois a legalperson.given; a lonegiven-deuterois not. - Role membership. Each cited name-part must exist in the named dialect
under the role the name claims for it. A name-part declared
place-genericcannot fill agiven-protoslot. - Phonotactics. The composed surface form must pass the dialect’s frame — its vowel inventory, cluster ceiling, ending set, and stress rule.
A name that fails any of the three does not compile. Register-fit is no longer a note a reviewer writes; it is a verdict the gate produces, and it speaks the domain rather than the parser (koan 9):
invalid: dialect-fit at name gwrthdin — cluster 'rthd' exceeds old-speech
cluster-max 2; and 'given-proto given-deutero' takes exactly two
name-parts, but three are cited. declared name-kinds for person.given:
[given-proto given-deutero]
The failure names the dialect, the offending clusters, and the legal slot patterns — a writer meets a diagnosis in the world’s terms, not a stack trace.
3. The vet split, check by check
The criterion for what re-homes into the kernel is not deterministic-versus-LLM — several checks that stay caller-side are pure lookups. The criterion is whose world the ground truth is about:
A check re-homes into the kernel iff its ground truth is a fact about the story’s world. It stays in the application iff its ground truth is a fact about ours.
In-kernel — world-true:
| Check | Why it is a fact about the story’s world |
|---|---|
| Intra-canon uniqueness | a query over the name-space the kernel now owns; a colliding declaration fails deloreis check |
| Fixed-token reuse | the same query surface; reserved tokens are canon facts |
| Aural distinctness | the phonetic-key distance ported from the oracle’s phonetics as a pure kernel primitive over the name-space |
| Set distinctness | the same primitive over a declared set of names |
| Register fit | it is the grammar check of §2 |
| Gender morphology | a join of the name’s name-parts’ gender metadata against the bearer’s canon facts — impossible while names and characters lived in different systems |
| Sayability-tier caps | ruled world policy (d-0010) over the syllable structure the dialect already defines |
| Banned vocabulary in names | against the in-runtime ban list (§4) |
Caller-side — world-safe:
| Check | Why it is a fact about our world |
|---|---|
| AI-tics | a screen against machine-generation tells; changes on research’s schedule |
| Pop / media / franchise collisions | knowledge of our culture, never cited to a ruling |
| Homophones in English | a fact about English, not the tongue |
| Anachronism | a screen against a modern-names list |
| Portent clichés | a taste judgment about our genre expectations |
| Court-card versus tarot | a fact about our tarot iconography |
Every caller-side screen carries no diegetic status, changes on research’s schedule rather than by ruling, can never be cited to a D-entry, and must never gate assertability. It is a second screen the consuming repo runs, not a condition on whether a name is canon.
Aural and set distinctness deserve a word, because they are the checks that most clearly could not run before the merge. The phonetic-key distance primitive ports from the oracle’s phonetics module as a pure kernel primitive: it maps a name to a phonetic key and measures distance between keys, so “these two names sound too alike” becomes a query over the name-space rather than an ear’s impression. Aural distinctness runs a candidate against the whole name-space; set distinctness runs it against a declared set (a scene cast, a family, a region). Scene-scoped checks can use a caller-supplied set of names.
Pronounceability splits down its middle. Cluster and syllable structure follow the dialect grammar and are checked in-kernel; a pronunciation heuristic for a particular production belongs to the caller.
Two-layer verdict composition. The oracle’s verdict becomes two-layered by design: a kernel verdict (world-true) and an external screen (world-safe). A consuming application composes them — it asks the kernel whether the name is true to the world and asks its own screens whether the name is safe in ours, and reports both. Neither layer can overrule the other’s ground truth, because they are ground truths about different worlds.
4. The lexicon
Diegetic terms are declarations with status and citations, and the allegory’s lexical bans are cited lexicon facts:
# grammar: v0 (unfrozen; AST normative)
scope camelot
term round-table "the Round Table" decided d-0004
banned cannon d-0010 "the Matter of Britain keeps to sword and spear — no gunpowder anachronism"
The prose scanner stays a caller-side tool — scanning markdown is not world knowledge — but it consumes the compiled ban list instead of a private YAML, so there is one authoritative statement of what is banned and why, cited to the ruling that banned it. A ban is lore with provenance, not a config entry.
The language layer also feeds magic. A spoken-enchantment practice draws its primitives straight from it — merlins-craft’s true-names are dialect derivations checked in the speaker’s register (04-practices §4): magic consumes language through an ordinary reference rather than through a convention that lives in two places. One fabric means the magic layer reads the language layer, not a copy of it.
5. The agent-init flow
The data that seeds a dialect comes from LLM and agent interaction — and it is
world lore, so it enters like world lore: as a proposal bundle under one
hypothesis variable, every clause carrying agent provenance by inheritance.
Agents author through the tool surface, the semantic tools against the deloreis
server (DL-0014; 11-agent-authoring); the surface below
is the materialized rendering of what those tools produced, reviewed as
surface before it is committed. No agent hand-writes the AST, and no agent edits
canon — the bundle enters proposed, awaiting a ruling in the consuming ledger.
# grammar: v0 (unfrozen; AST normative)
scope camelot
hypothesis h-old-loan "the old speech took a hero-name from the tongue over the water"
name-part tal "brow, high" role given-proto proposed agent:author/naming-run-0714 assumes h-old-loan
# inventory, patterns, name-parts — every clause inherits the bundle's
# hypothesis and the agent provenance
name taldin "Taldin" person.given in old-speech
= given-proto tal + given-deutero din
proposed agent:author/naming-run-0714 assumes h-old-loan
# support: H(h-old-loan) — merely-possible: visible to shadow queries,
# structurally excluded from every context pack and every assertion
ratify d-0011 { h-old-loan := true } # one valuation ratifies the bundle
One hypothesis variable per bundle means ratification is one authorial act
for this language layer. A ratify assignment does not currently settle
ordinary world claims; if a world claim assumes the same hypothesis, the check
reports an error instead of silently leaving it open.
The clause grain the annotation algebra already carries
(02-semantics) gives partial ratification for free: ratify
the dialect but reject two name-parts, and their variables valuate false — every
name derived through them dies into lineage and lands in the killed-token
dialect, per the standing kill discipline. Names composed from an unratified
dialect are merely-possible by construction — no special case, just the same
status-gating every proposed entity gets. A merely-possible name is visible to
shadow queries and structurally excluded from every context pack and every
assertion.
6. Name views
The compiled world is the name-space. A caller can derive the certain name
view with deloreis query name --layer certain --json; the view is generated
from the compiled world rather than maintained as a separate writable copy.
7. The attestation seam
basis attested "…" and per-name-part source URLs are claims about our world —
“this pattern is Welsh-like,” “this root is on this reference page” — not about
the story’s. They must not enter the annotation polynomials as if they were
citations. Two rules bind at this seam, and
07-verdicts-and-llm-boundary designs the
machinery that enforces them:
- Attestation grades never feed assertability. A dialect’s real-basis is an attestation (07 §3): stored, graded, replayable, and side-band on the checks that consume it — never a variable in a support polynomial. A name can never become certain because a model believes its basis is authentic.
- A dialect is ratifiable with its basis unverified. The author ratifies the
sound — the phonotactics, the name-parts, the names that derive true to them —
not the scholarship.
basis attestedmay be unanswered, or answeredno, and the dialect may still be ratified by a ruling. The scholarship is our-world evidence about the work; it is never support for the world.
This is the seam that keeps the language layer honest about which world each of its claims is about — the same distinction §3 draws for the vet, applied to the dialect’s own provenance.
Verdicts and evidence
The kernel evaluates structured lore deterministically and offline. A caller may use a model outside the kernel to propose or interpret lore, but the kernel does not call a model or treat generated prose as a verdict. Attribution, review, and permission to rule belong to the consuming application.
The grade lattice
A verdict carries one of four verification grades:
checked > tested > attested > asserted
- checked: a procedure re-derives exactly from committed inputs.
- tested: a procedure has statistical evidence from a reproducible test.
- attested: a stored model judgment can be inspected and contested, but is not mechanically re-derived.
- asserted: a claim is taken as given, without a verifying procedure.
Support and grade are separate. Support records the decisions and assumptions
that make a conclusion hold; grade describes the procedure used to reach the
verdict. A deterministic proof check over authorially ruled premises can be
checked even though the rulings themselves are asserted. A required procedure
chain rolls up to its weakest grade; independent alternative procedures roll
up to their strongest available grade. Neither rule turns repeated attestations
into a test.
Attestations
Projects can contain committed attestation evidence. The project-backed
dli.check verifies and replays the evidence against configured questions and
reports current, missing, stale, or negative answers. Records retain the
question, answer, model and prompt identifiers, sampling policy, rationale
hash, fingerprint, and lineage needed to inspect them. A missing or stale
answer is a finding, not a guessed answer.
Contract 2.0.0 does not provide an attestation-sweep write operation. The kernel reads existing evidence and reports demand; a consuming application owns any external evidence workflow and review policy. A model judgment is not proof of a claim and cannot silently create canon.
Structured claims
The engine checks the claims, decisions, and rules a caller supplies. It can report a contradiction, explain support and defeaters, or leave an unresolved claim possible. It cannot establish that a transcription faithfully captures a manuscript or that a model-generated assertion is true. Applications should inspect source material and apply their own review rules when that distinction matters.
Reasoning
De Lore Is answers a spread of reasoning tasks — assertability, continuity, blame, timeline consistency, and ledger proof checking. This document inventories all of them, gives each its computational shape, and makes the own-versus-borrow call for each under one rule: evaluation and checking are reimplemented, small and certifiable; any search or external baselines would act as proposers, never as certifiers. That rule is koan 12 (own what certifies; borrow only baselines) and DL-0005 (owned-first reasoning) applied task by task.
The inventory distinguishes implemented work from deferred possibilities. It does not promise that a future solver or service is included in the release.
1. The kernel is solver-class software
There is no external solver process in any gating lane, and the kernel is still solver-class software: it is a datalog query and reasoning engine with a nonstandard annotation algebra. That is not a hedge to be walked back — it is the committed center of the design, and building it seriously entails a named bill:
- Load-time rule shape, stratification, and range restriction. A bare
top-level world rule must be valid Datalog; malformed or body-less forms are
error findings, never empty lowerings. The negation regime (see
02-semantics.md) also needs an acyclicity discipline on negative dependencies, exactly as stratified datalog does, and every rule must be range-restricted. All three are checked when a pack loads, before any evaluation runs. Domain checker rules and practice-internalrule ... requiresforms remain in their owning lanes. - Annotation-aware seminaive evaluation. The delta semantics is not the textbook one: an atom re-enters the worklist when its support strictly grows in the lattice, not only when it is first derived. Termination is guaranteed by the finite-lattice ascending-chain condition on the annotation algebra.
- Absorption normalization at every join. Reducing each support polynomial to its antichain of minimal monomials at the moment it is formed is the performance linchpin, not an optional optimization. Without it, supports grow without bound; with it, they stay narrow.
- Inverted variable→atom provenance indexes. So that a valuation change touches only the atoms whose support mentions the changed event variable.
- Serialized, replayable derivation forests. Every judgment is a byte-stable artifact: same input, byte-identical judgment. This is the audit surface, the debugging story, and the differential-testing hookup at once.
- Differential testing. Retained tests compare independent evaluation paths, including incremental and full saturation. The engine must stay small enough to be itself checkable.
Anyone who has built a datalog engine will recognize this list. Planning it as “a library” rather than “an engine” is how such projects go wrong; it is planned as an engine.
Why bespoke, and not a shelf datalog crate. The engine is reimplemented not out of not-invented-here but because no off-the-shelf datalog hosts the condition-polynomial algebra, the grade lattice, and the keep-stamp visibility filter without a fork — and a fork yields ownership in its worst form: owned bugs, borrowed architecture (koan 12’s good-crate decision, worked in the charter). The governing rule is the beacon principle: reimplement for checkability wherever a verdict certifies canon; integrate wherever a component proposes, enumerates at scale, or serves as a baseline. A mature datalog crate is welcome as a differential baseline over the shared fragment; it may not be the certifying evaluator. The engine must stay small enough to be itself checkable through its tests; an independent formal specification remains a possible future aid (§7).
2. The state tree is never walked
The load-bearing architectural claim answers the obvious worry — sophisticated reasoning over possible state trees. The possible-worlds space is exponential in the number of open event variables, and it is never enumerated. It is represented once, symbolically, in the condition polynomials; a world is a valuation applied as a cheap evaluation after the fixpoint. The symbolic representation is computed once; valuations come after.
This is the conditional-table / certain-answer semantics of the incomplete-database literature, specialized to a corpus whose “uncertainty” is pending authorial decision — which is exactly what makes it the right tool: the variables are things the author rules on, and their number stays small because rulings are few relative to facts. A possible world is a partial valuation; ratification is a valuation event; blame is the polynomial support; citations are a rendering of the same polynomial that gates assertion. None of that requires enumerating worlds.
Search machinery is needed only where a question inverts that direction — find me a world, an ordering, a repair, rather than evaluate this one — and those inversions are enumerable and bounded at the corpus’s scale. The inventory below walks every reasoning task and shows that the certification path contains no search at all.
3. The task inventory
Machinery legend: EVAL annotated seminaive fixpoint / lattice evaluation · ENUM finite exhaustive enumeration · PROP constraint propagation (partial order / point algebra) · SAT bounded model search · ASP answer-set-shaped enumeration (choice rules + constraints + defaults) · SMT arithmetic theory solving · CHECK proof checking, linear in proof length · MC property-based and mutation testing.
| id | Task | Shape | Machinery | v1 | External |
|---|---|---|---|---|---|
| T1 | Assertability / status derivation | per-atom evaluation; ptime | EVAL | yes | no |
| T2 | Transitive closures (geography, happens-before) | recursive datalog, finite domain | EVAL | yes — the irreducible center | no; this row is the engine |
| T3 | Continuity checking (six classes) | non-recursive queries + FD checks over fold state | EVAL | yes | no |
| T4 | Factcheck verdicts | point lookups + FD + constraint scan (model extraction stays caller-side, never above tested) | EVAL | yes | no |
| T5 | Draft claims against canon | continuity and fact checks over draft claims | EVAL/CHECK | yes | no |
| T6 | Explain one claim | read its support paths and blockers | EVAL | yes | no |
| T7 | Ratification pre-flight | one candidate-valuation evaluation + conflict scan | EVAL | yes | no |
| T8 | Joint valuation search | search over event variables | ENUM or SAT | deferred | any proposal must be rechecked |
| T9 | Fork diff / merge conflicts | evaluation diff; constraint-join; FD/constraint scan | EVAL | yes | no |
| T10 | Blame / min-why / why-not | antichain read; defeater read | EVAL | yes | no |
| T11 | Keep-stamp visibility | deterministic scope filter over exported or forked facts | CHECK | yes | no |
| T12 | Timeline consistency | cycle detection over the before-graph + era spine | PROP | yes | no |
| T13 | State-at projection | cautious event-calculus projection over a partial order; three-valued | EVAL/ENUM | yes | no |
| T14 | Episode-order CHECK (~200 eps) | linear scans against precedence + window constraints | PROP (trivial) | yes | no |
| T15 | Episode-order SEARCH / optimize | topological + earliest-slot while constraints stay precedence + windows (poly); NP only with disjunctive/soft constraints | PROP → owned searcher | deferred until the workflow exists | CP-SAT as proposer, if ever |
| T16 | Wrong-hour / phase checking | modular arithmetic; wheel-walk consistency | CHECK | yes | no |
| T20 | Proof-ledger checking | proof CHECKING — linear, decidable, no search | CHECK | yes | no — the paradigm reimplement case |
| T21 | Metatheorems | interactive proving, offline | Lean | no, by design | spec organ; badged lemmas |
| T22 | The proof layer itself (gate coverage) | seeded-violation operators × real gates | MC/ENUM | harness exists | no |
4. Reasoning choices that need prose
Some rows deserve their tradeoffs spelled out.
T6 / T8 — explanation and valuation search. The engine can explain a single claim in terms of its support and blockers. Searching for a joint repair across claims or enumerating valuations is not a shipped capability. If a future proposer supplies a valuation, the kernel must re-evaluate its result before it is used as a judgment.
T14 / T15 — episode ordering, the one genuinely scheduling-shaped task. Roughly two hundred episodes against reveal-before-payoff rules, staging constraints, an early-episode floor, and story-time monotonicity outside declared flashbacks. Three regimes, analyzed honestly:
- Checking a given order is linear scans, and that is what the author’s actual workflow needs almost always — because an ordering is a creative artifact. A solver-produced sequence has no narrative value; the machine’s job is to check authored orders and explain violations, not to author them.
- The useful middle regime is slack computation: earliest and latest legal slot per episode under precedence and windows — a forward and backward pass, polynomial — surfaced as “this reveal can sit anywhere in episodes 8–14.” That serves authoring better than any single “valid ordering” would, and it is committed alongside the checker.
- Search (“find me a valid ordering”) stays polynomial as long as constraints remain precedence and windows. It goes NP only when disjunctive constraints, cardinality or pacing constraints, or soft optimization arrive. Building search machinery for a workflow that does not yet exist is the trap; the seat is vacated in favor of an owned searcher, and CP-SAT is named as the eventual proposer only if that owned searcher measurably fails (§5, §6).
The two rows that anchor “reimplement.” T2 (transitive closures) is the irreducible center: recursive datalog over a finite domain is the engine, and there is nothing to integrate because this row is the thing being built. T20 (proof-ledger checking) is the paradigm reimplement case at the other end: proof checking is linear in proof length, decidable, and contains no search at all — a small first-order checker with explicit instantiation and linear resource accounting, whose verdicts certify canon and therefore must be owned, deterministic, and replayable. Between these two poles every certifying task is evaluation or checking, and every proposing task stays behind the judgment boundary.
The synthesis. The certification path — everything that gates canon assertion, continuity, the export pack, the proof ledger — is deterministic evaluation and checking: polynomial per evaluation, terminating by construction, no search anywhere in that certification path.
5. Owned-first: the seats, one by one
DL-0005 sets the default at every flip condition: the first preference is a small owned system sized to our instances; a foreign backend enters only when the owned system measurably fails on real workloads — and then still as a proposer, never a certifier.
- The CP-SAT seat is vacated in favor of an owned searcher. If episode-ordering-as-search materializes (T15’s disjunctive/soft regime), the first build is a small propagation-and-backtracking searcher over precedence, window, and disjunctive constraints: topological ordering with chronological backtracking and forward-checking on windows, on the order of one to two thousand lines. The instances are tiny and known. What the owned searcher deliberately lacks — clause learning, restarts, portfolio heuristics — is exactly what these instance sizes cannot need. Two properties make owning it safe: search output is self-certifying in the check direction (every found order re-passes the T14 linear checker), and the useful middle regime — slack computation — is already committed polynomial machinery that needed no solver.
- SMT: unchanged. Nothing in scope is SMT-shaped; the door is closed and unlocked. The trigger is a ratified law introducing real arithmetic, and the decision is made then.
- Lean: unchanged. Offline spec organ; metatheorems import as badged lemmas; never in the serving path (§7).
- Neural verification (α,β-CROWN-class): not applicable, said plainly.
The cost, accepted in writing. Owned reimplementations must themselves be tested: property tests, the mutation-operator corpus extended with seeded searcher and enumerator violations, and differential checks of their outputs. That is the price of checkability, paid deliberately — koan 12 commits the kernel to this bill, and it extends the same bill to the satellites. Also true and stated: an owned searcher is worse than CP-SAT in general. That is irrelevant at our sizes, and the flip condition names exactly the regime where it stops being irrelevant.
What this buys in dependencies. From the first release through the medium term: zero foreign reasoning processes required to build, gate, or serve. Optional external baselines are never required in the serving path.
Small parts, linked. The kernel and timeline module compose at the replayable-judgment boundary: serialized inputs and outputs are rerunnable and diffable. Components share one semantics while remaining independently testable.
6. Flip conditions, two-stage and falsifiable
Each condition discharges in two steps, and nothing breaks at either step because proposers of either provenance bolt onto the judgment boundary the kernel already exposes:
- The condition fires in the author’s actual workflow.
- The small owned system is built.
- The owned system measurably fails on real workloads.
- The named foreign proposer lands behind the same judgment boundary.
Per seat:
- A demonstrated joint-valuation search need → evaluate a bounded approach first; any proposed valuation must be rechecked (T8).
- A demonstrated cross-claim repair need → assess an owned repair search first; the current single-claim explanation does not implement it (T6).
- A real propose-orderings workflow with soft constraints → the owned searcher first, CP-SAT second (T15).
- A ratified arithmetic law → the SMT decision, made then.
7. The Lean posture
Lean is a possible future offline specification tool, never part of the serving path. The proof-ledger checker is a first-order object — atoms, explicit instantiation, around nine forms, side conditions, and linear accounting — and a full CIC embedding would supply dependent types the design never uses while lacking the one thing it most needs, linearity. It is not the checker; an offline specification could be used to audit it.
Three triggers move it from deferred to built, any one sufficient: the judgment forms freeze; the first metatheorem is wanted; checker correctness becomes publication-load-bearing. When a trigger fires, the object is small — the nine forms, the side conditions, and the conservation law specified in Lean — and a hull-style differential loop stands up around it: the corpus of serialized ledgers runs through both the Rust checker and the Lean spec’s executable elaboration, and their verdicts are diffed in CI. The serialized-judgment requirement (§1) is what makes that loop nearly free.
Metatheorems could be proven in Lean against the spec. Their authority would
reach the runtime through coherence evidence, imported as badged lemmas
the verdict lattice marks honestly (koan 6; see
07-verdicts-and-llm-boundary.md). The implementation stays authoritative at
runtime; Lean is the spec organ; coherence is a differential loop, never an
embedding.
8. The line, restated
Every task in the inventory falls on one side of a single line. On the certifying side — assertability, continuity, factcheck, blame, timeline consistency, ledger proof checking — the machinery is deterministic evaluation and checking, owned, small, and replayable, with no search. On the proposing side — valuation-space queries at scale and episode-order optimization — machinery could be integrated, out-of-process, behind the judgment boundary, and its every output is re-checked or graded honestly before it touches canon. Nothing foreign certifies; through the medium term, nothing foreign is even required to build, gate, or serve. That is DL-0005 and koan 12, discharged row by row.
9. Archive evaluation keeps the line
Whole-corpus saturation is the current implementation, not a semantic requirement. At archive scale, a future service could use supplementary Magic Sets to bound a snapshot-pinned provenance cluster, then an embedded peer could run this same owned bottom-up evaluator over that cluster. The service performs indexed I/O and binding propagation; it does not certify standing, choose a consensus, or replace the evaluator with a remote solver.
The required oracle is stronger than a latency benchmark: for the same root,
typed goal, and valuation, peer evaluation over a valid returned cluster must
produce the same bindings, standing, and provenance as whole-corpus evaluation.
An incomplete or unbounded cluster is a failure, not an approximate answer.
The loopback-first peer/service work is deferred in PLAN.md as DIST-1 through
DIST-4; scale and distribution defines the
boundary.
Exports
DLI exports evaluated lore to a context pack, Articy-shaped JSON, CSV, or a static Markdown wiki. Exports are one-way projections: changes to an exported artifact do not write back into the lorestore.
By default an export reads the certain layer. A profile selects the sorts and
relations to include and can specify rendering and provenance options. Rust
library callers may explicitly supply a fork valuation. Every artifact carries
a manifest with the source anchor, selected layer, valuation fingerprint, and
profile hash.
These fields make an export traceable and reproducible from its inputs.
The built-in wiki/static profile follows that default. To include source
claims that are possible or dead, explicitly select wiki/all-standing with
--profile-name or profile_name. Its pages label each source claim’s own
evaluated support and include provenance markers and decision IDs when the
profile carries provenance. A reader needs a source index to associate those
IDs with story titles; the export does not invent source descriptions. Proposed
claims can also show named support expressions when provenance is enabled;
those names are not automatically source citations.
This is a broader disclosure than a canon-only export; review it before sharing.
Keep-stamp visibility restrictions still apply. The manifest labels this view
all, and other targets keep their usual certain-only behavior.
This profile refuses as_of: temporal projection would otherwise make a
future claim appear dead, which is not the same as defeating its support.
The optional story-time cutoff (--as-of in the CLI, as_of on the wire)
projects state claims through the fluent timeline before selection. The manifest
records the cutoff and binds it in the profile hash. A timeline that cannot be
projected faithfully is refused; a timeless claim remains independent of an
identical temporal claim. Exports continue to contain source atoms, not derived
query rows.
Historical exports require a freshly compiled world with its temporal source
mapping. An older serialized compiled world without that mapping must be
recompiled from its source or lorestore before a historical export; it is
refused rather than assigning a claim’s support to the wrong source.
The CLI exposes deloreis export on a syntax-enabled build. The JSON/MCP
operation is dli.export.run, with request and response schemas in
contracts/dli-worker/schemas/. A project can select a profile catalog;
profile_name selects a named profile. An invalid target, profile, selector,
or unsupported option is refused rather than silently ignored.
Serving
deloreis serve --project <path> serves newline-delimited JSON over standard
input and output. deloreis mcp --project <path> advertises the core fiction
tools; --tools all advertises every operation. Both profiles accept calls to
the same operations as the worker. The project path names a deloreis.toml manifest or its
containing directory. The server performs deterministic local evaluation; it
calls no model or network service.
Contract 2.0.0 is the only served version (DL-0101, DL-0103). Previously
served versions and retired operations are refused as killed forms, with the
successor named. The generated schemas and replay fixtures in
contracts/dli-worker/ define the request and response shapes.
Projects
A project manifest names logical documents. Each document uses either one or
more .dli source files on a syntax-enabled build, or a lorestore directory.
A store-backed document may pin a commit with head; without it document reads
follow the current main ref, including commits made by another server. Forks in
one session share the snapshot taken when its first fork opens. Start a new
session to fork from a later commit. This example names a store-backed document:
version = 1
name = "storybook"
[documents.world]
store = "lorestore/world"
A wire request names the logical document ID, not a filesystem path. The
manifest may also select episode data, killed-token and attestation-question
registries, and export profiles. Paths are validated within the project; the
server rejects unknown manifest fields and unsafe or overlapping storage paths.
--demo instead selects the bundled examples explicitly.
Envelope and errors
Each request is one JSON object on one line:
{"dli": "2.0.0", "id": "request-1", "op": "dli.version", "params": {}}
A response echoes the correlation ID and carries either ok: true with a
result, or ok: false with a typed error containing a stable code and a
human-readable message. Malformed frames are refused; blank lines and
non-object log noise are skipped. Responses contain no timestamps.
dli.version reports the contract and its available operations. Unknown
operations are refused; registered killed forms name their successor.
A mutating author.* request may carry expected_digest and
idempotency_key in the envelope. The MCP adapter exposes these as tool
arguments and forwards them into the same envelope. A stale digest is refused;
a retry with the same key and request can replay its result without applying a
second mutation. See agent authoring for workspaces,
findings, structured diffs, and commit behavior.
Operations
The dli.* family includes document checking and queries; fork creation,
assumptions, diff, preflight, and promotion; proof checking; timeline reads;
name checking; attestation demand; corpus import and export; and one-way
exports. Lore export profiles include context packs, Articy-shaped JSON, CSV,
and static wiki output. The generated schemas and the dli.version
capabilities list are the complete operation inventory.
dli.check returns an ok verdict, the weakest procedure grade used, and
findings tagged as world or attestation findings. An error finding blocks the
check; a warning may accompany a successful result. Attestation findings
report whether configured evidence is current, missing, stale, or negative.
dli.query accepts a typed goal_atom and can select a layer, chronology
point, or fork. dli.fork.add accepts typed ground additions. The retired
string forms are refused with their typed successors named. dli.check-proof
checks a named declared proof against its practice and scene projection; when
there is exactly one proof the selection may be omitted.
State and replay
Sessions cache compiled document scopes and hold process-local fork handles.
Author workspaces stage declarations over a base; author.materialize
checks the proposed result and commits accepted fact objects to the lorestore.
A named branch or pinned commit supplies its own snapshot. Content-addressed
objects, committed decisions, and conditional attacks preserve history so
that the same committed inputs can reproduce a verdict and its explanation.
Corpus export includes any superseded fact objects needed as attack carriers
before the current folded objects. Import replays those objects and attacks into
a new commit; this preserves the resulting judgments, but the flat exchange
does not authenticate the source commit chronology.
Authorization and review policy belong to the consuming application.
Agent authoring — the tool surface
Agents that build a declared corpus do not write .dli surface text. They
operate a curated set of semantic tools against a running deloreis server.
Staging mutations return findings and workspace state; other operations return
the results specified by their schemas. .dli rendering is opt-in. A consuming application
supervises by rendering the structured diff for its reviewers. This is the
tool surface, ruled in DL-0014 and seated in the charter as koan 11 (“one
way in”).
This document specifies the authoring tools. Contract 2.0.0 is the only
served version. The wire schemas and replay fixtures are in
contracts/dli-worker/.
1. The interface
Agent sessions use the semantic tools against the deloreis server. A caller
names an intent, stages changes in a workspace, and reviews the resulting
findings and structured diff. Attribution and decision state are caller-supplied
data; DLI checks schemas and cited-decision integrity, while the consuming
application decides who may authorize a declaration.
The default build also supports checking, formatting, and importing .dli
text. Repository agents follow the author.* workflow in skills/deloreis/SKILL.md when
authoring lore; people can use the text tools directly.
The tools speak JSON over stdio (deloreis serve --project <path>) and MCP
(deloreis mcp --project <path>). Both load the same deloreis.toml project.
MCP advertises the compact fiction profile by default; --tools all exposes
the extended profile. The worker operation namespace is dli.* and the
workspace authoring namespace is author.*.
2. Sessions and staging
All authoring happens through a workspace: an in-memory staged layer over
the compiled world with a pinned lorestore commit and ref. Add calls may write
immutable fact objects, but they do not advance the serving ref.
author.materialize commits the accepted staged delta; standing operations
commit metadata revisions and valuation changes directly. The
workspace digest remains the fence over staged declarations and suppositions;
durable commit identity travels separately as root_hash.
author.open creates a workspace and returns its id and digest. It opens in one
of two modes:
{"scope": "<name>"}— stage over a logical document declared by the loaded project. Additions accrue to that document’s surface.{"fork": "<question>"}— open a fork-scoped workspace over the one project document that declares that question (koan 2: a fork is exploration over an unchanged store). Hypotheses and suppositions made here are fork-local and never leak into canon.
Unknown scopes, unknown forks, and fork ids declared by more than one document are refused. Once opened, a workspace remains bound to that one document base for mutation findings, read tools, staged recompilation, and materialization; it cannot observe another document or the bundled demo world.
After an authoring operation commits to the main branch of a live project, a newly opened scope workspace starts from that committed base and its standing state, even if another server made the commit. Fork names are resolved from the project snapshot loaded when the server starts; restart the server after changing declared forks. Workspaces already open keep their own earlier bases. A project pinned to a specific commit continues opening from that pinned snapshot, and a commit on another branch does not advance the main base.
{ "op": "author.open", "params": { "scope": "storybook" } }
{
"ok": true,
"workspace": "ws-3f9a",
"workspace_digest": "wd-00"
}
The workspace supports:
author.status— the rendered staged declarations, full findings, and current workspace digest.author.diff— the rendered canonical.dlisurface of the staged change, plus the full findings, plus the derived-consequence diff: which downstream derived atoms becamenewly_certain,possible, ordead, whichgrade_changed, and whatconflictsarose — including merely-possible status (the world is folded, not stored, so a new fact can move consequences).author.discard— drop the whole workspace, or drop one staged declaration ({"workspace": ..., "decl": ...}). Discarding a declaration is itself a mutating call: it returns the new workspace digest.author.materialize— commit the workspace’s staged delta to the lorestore and return the committed fact objects as data, with.dlirendering therendered: trueopt-in (§4;decisions.mdDL-0046). It is a mutation, and it records the accept/reject of the staged DLI mutation —author.discardis the reject. The server never writes the consuming repository; what crosses the boundary is JSON, which the caller takes through its own review. An optionaldecisionaccepts the committed facts under a named, recorded, defeasible decision so they readcertain. The conferral is facts-only: carriage claims — rules,same-aslinks, and inertial declarations — are never stamped and staypossible; their standing comes from their embedded declaration provenance, so they reach certainty throughauthor.record_decisionplus a cited declaration, notauthor.materialize {decision}. A carried entity, fact, attribute or time declaration that regenerates an accepted fact is revised to reproduce the accepted support on replay. An independently cited fact keeps its own support. If a proposed carried annotation has citations that cannot be retained through stamping, the call refuses before committing; materialize it withoutdecisionfirst. Proposed identity links are also materialized separately. Option-implied facts remain conditional on their options; the batch decision does not select an account (DL-0114).
A workspace mutation after author.open may carry the workspace digest it
expects and returns the new digest. (author.open creates the first digest, so
there is no prior fence to present.) When supplied, a digest that does not match
the workspace’s current state is an error with code stale-fence (class
refused — the frame was well-formed; a precondition failed): the agent has an
out-of-date view and must re-read before continuing.
{
"ok": false,
"error": {
"code": "stale-fence",
"message": "Workspace ws-3f9a is at digest wd-05; you presented wd-02. Read author.status before continuing.",
"expected_digest": "wd-05",
"presented_digest": "wd-02"
}
}
A workspace mutation after author.open may also carry an idempotency key.
When supplied, a retried call with a key already applied returns the original
result and stages nothing new — including a retry of a whole-workspace discard.
Callers that need retry safety under at-least-once delivery supply the key. Live
workspaces retain their applied-key cache for their lifetime. After a whole
workspace discard, the worker retains compact tombstones for the 1,024 most
recently discarded workspaces; an older retry is refused as unknown-doc and
can never recreate or mutate the discarded workspace.
author.materialize, author.branch, and author.merge follow this mutation
law. A stale digest refuses before commit; an exact keyed retry returns the first
result without another commit; the same key with different params conflicts.
expected_digest and idempotency_key are declared envelope fields (request
level, defined for mutating author.* calls); the normative wire shape is
contracts/dli-worker, and “unknown fields are errors”
stands because these are known fields. Workspace digests are RFC 8785 JCS digests
(contract §4); the short forms shown here are illustrative.
On MCP, both fields are exposed as reserved top-level tool arguments beside the
tool’s semantic parameters. The adapter removes those two keys from params and
lifts them into the shared worker envelope before dispatch, preserving the same
stale-fence and idempotency behavior as native stdio.
An author.diff for a two-fact workspace renders as canonical surface:
# grammar: v0 (unfrozen; AST normative)
scope camelot
person galahad "Galahad" proposed session-7c9
place logres "Logres" proposed session-7c9
Every rendered string in this document — in rendered arrays and materialized
file contents alike — is the same placeholder canonical surface as the gentle
card (skills/deloreis/SKILL.md, 01-syntax.md): the grammar is unfrozen and the AST is
normative, but all documents render one form.
3. The tool inventory (v0 core)
Every mutating tool shares one request envelope and one response envelope. The request is:
{
"dli": "2.0.0",
"id": "rq-7",
"op": "author.<name>",
"expected_digest": "wd-01",
"idempotency_key": "idem-7c1e",
"params": { "workspace": "ws-3f9a" }
}
dli and id are the worker protocol’s required frame fields (contract §2);
the target workspace always rides in params — the envelope is uniform
across both op families. The response is the feedback contract of §4:
{ ok, workspace_digest, rendered, findings }, or a typed { ok: false, error }. The examples below elide dli, id, and often the workspace
param for brevity; only params and the salient part of the response are
shown.
Provenance is required on every claim-bearing tool. A caller supplies
proposed, hypothesis, or, on the authority-scrubbed wires, cited:
{ "kind": "proposed", "origin": "session-7c9" }
{ "kind": "hypothesis", "hyp": "coast-runs-north" }
proposed records an origin and asserts no ruling; hypothesis scopes the claim
to a fork-local hyp variable.
cited names declared decisions in the consuming application’s ledger and
requires state: "provisional" | "ratified". An unknown decision raises
citation.unknown-decision and bars materialization; DLI checks referential
integrity but does not decide who may rule.
3.1 author.add_entity
Introduce an entity of a given kind. kind is one of person | place | thing | era. Events are not entities: a chronology point carries anchor and
before-fact semantics, so it enters exclusively through author.add_event
(§3.4). Provenance required.
{
"op": "author.add_entity",
"expected_digest": "wd-00",
"idempotency_key": "idem-01",
"params": {
"workspace": "ws-3f9a",
"kind": "person",
"id": "mireth",
"name": "Mireth",
"provenance": { "kind": "proposed", "origin": "session-7c9" }
}
}
When river-speech is configured in this workspace, a response can include:
{
"ok": true,
"workspace_digest": "wd-01",
"rendered": ["person mireth \"Mireth\" proposed session-7c9"],
"findings": [
{
"code": "name.unvetted",
"severity": "warn",
"grade": "asserted",
"message": "The name \"mireth\" has not passed the naming oracle; it enters proposed and unvetted.",
"citations": [],
"repair": {
"kind": "run-check",
"op": "dli.name.check",
"params": { "name": "mireth", "dialect": "river-speech", "workspace": "ws-3f9a" }
}
}
]
}
3.2 author.add_fact
Assert a keyed value on one entity. Lowers to a binary atom key(entity, value).
Provenance required. A compact token renders as a symbol; whitespace or surface
delimiter characters make the value a quoted string. Fact objects remain the
canonical form; a requested text rendering is refused if its provenance cannot
be preserved.
{ "op": "author.add_fact",
"params": { "entity": "mireth", "key": "tongue", "value": "low-saltish",
"provenance": { "kind": "proposed", "origin": "session-7c9" } } }
{ "ok": true, "workspace_digest": "wd-02",
"rendered": ["mireth.tongue = low-saltish proposed session-7c9"], "findings": [] }
3.3 author.add_relation
Assert a binary relation between two entities. Lowers to relation(from, to).
Provenance required. A reference to an entity not yet in the workspace
is an error-severity finding carrying candidate ids — the call stages the
relation, but materialize refuses until the reference resolves.
{ "op": "author.add_relation",
"params": { "relation": "serves", "from": "mireth", "to": "the-warden",
"provenance": { "kind": "proposed", "origin": "session-7c9" } } }
{
"ok": true,
"workspace_digest": "wd-03",
"rendered": ["serves(mireth, the-warden) proposed session-7c9"],
"findings": [
{
"code": "entity.unknown",
"severity": "error",
"grade": "checked",
"message": "No entity \"the-warden\" is in scope. The claim is staged but cannot materialize until it exists.",
"citations": [],
"repair": { "kind": "resolve-entity", "candidates": ["the-warder", "the-ward"] }
}
]
}
3.4 author.add_event
Introduce an event and anchor it to an era. era names an era entity in scope.
Provenance required.
{ "op": "author.add_event",
"params": { "id": "the-drowning", "era": "the-salt-years",
"provenance": { "kind": "proposed", "origin": "session-7c9" } } }
{ "ok": true, "workspace_digest": "wd-04",
"rendered": ["event the-drowning during the-salt-years proposed session-7c9"],
"findings": [] }
3.5 author.add_before
Order one event before another. Declaring an order is a ruling about the world,
so provenance is required. Lowers to before(earlier, later). A declared order
that closes a cycle against existing order facts is an error-severity finding.
{ "op": "author.add_before",
"params": { "earlier": "the-drowning", "later": "the-reclaiming",
"provenance": { "kind": "proposed", "origin": "session-7c9" } } }
{ "ok": true, "workspace_digest": "wd-05",
"rendered": ["before(the-drowning, the-reclaiming)", " proposed session-7c9"], "findings": [] }
3.6 author.open_question
File an open question as tracked debt (koan 5: an open question is a debt).
Opening a question asserts no claim — there is no provenance — and carries no
options; options are added separately with author.add_option.
{ "op": "author.open_question",
"params": { "workspace": "ws-3f9a", "id": "where-the-first-ward-stood",
"prompt": "Where did the first ward stand?" } }
{ "ok": true, "workspace_digest": "wd-06",
"rendered": ["question where-the-first-ward-stood \"Where did the first ward stand?\""],
"findings": [] }
3.7 author.add_option
Add one option to a question already open in the workspace. label is stored as
data in the AST and canonical surface. Optional implies is a typed ground
atom: its claim is supported by this option and remains possible while the
question is open. Option IDs must be unique in the world. Add all options
before materializing the question; extending a committed question is refused.
The updated question is durably staged, and text findings apply to the outcome.
{ "op": "author.add_option",
"params": { "workspace": "ws-3f9a", "question": "where-the-first-ward-stood",
"id": "the-tidewall", "label": "The Tidewall" } }
{ "ok": true, "workspace_digest": "wd-07",
"rendered": [" option the-tidewall \"The Tidewall\""], "findings": [] }
3.8 author.add_hypothesis
Mint a hyp variable for fork-scoped supposition. The variable names a supposition
that later claims may be conditioned on via hypothesis provenance. Legal only in
a fork workspace, and limited to one hyp variable per proposal bundle — a
bundle that needs two independent suppositions is two forks.
{ "op": "author.add_hypothesis", "params": { "id": "coast-runs-north", "statement": "The coast runs north." } }
{ "ok": true, "workspace_digest": "wd-08", "rendered": ["hypothesis coast-runs-north \"The coast runs north.\""], "findings": [] }
statement is required — the hyp variable carries the claim it names, and the
rendered form quotes it.
3.9 author.suppose
Pin a set of minted hyp variables true within the fork. This is exploration over an unchanged store (koan 2): it asserts nothing in canon and adds no scope surface. Its effect is visible as a derived-consequence diff, and its rendering lives inside the fork frame, not the scope. (Supposing an option of an open question is not implemented at v0 — only minted hypotheses.)
{ "op": "author.suppose", "params": { "workspace": "ws-3f9a", "hypotheses": ["coast-runs-north"] } }
{
"ok": true,
"workspace_digest": "wd-09",
"rendered": [],
"findings": [
{
"code": "consequence.changed",
"severity": "warn",
"grade": "checked",
"message": "Under coast-runs-north, the derived-consequence set may move. See author.diff for the consequences.",
"citations": []
}
]
}
The language-layer tools (names, words, and dialects) and the
ledger tools follow their own spec documents’ nodes and the same envelope and
feedback contract. They are invoked in the author.* (authoring) and dli.*
(worker-protocol, e.g. dli.name.check) namespaces; this document does not
invent their parameters.
3.10 Standing transitions
Two operations change the standing of stored claims:
author.set_standing {workspace, claim_id, decision}promotes a provisional claim to ratified only whendecisionis declared and occurs in every support path. The object recordsstanding_decision; its claim identity is unchanged.author.defeat {workspace, claim_id, decision, defeated_decision}records the new defeating decision, verifies thatdefeated_decisionsupports every path, and changes its effective standing. It appends a conditional attack: the target is out while any attacker is in, restores when every attacker is out, and remains undecided under self, mutual, or odd cycles. The object remains reachable.
author.record_decision {workspace, decision, rationale?, provenance?} records
one baseline decision. Use
author.record_decisions {workspace, records:[…]} for an all-or-nothing ordered
batch. The full batch is checked against one immutable pre-image, including
per-record provenance rules; forward and cyclic same-batch citations are legal,
and a valid request performs one compile, one commit/CAS, and one prepared
workspace install. A refusal writes nothing and reports the offending
record_index where applicable.
Both return the new lorestore root_hash and the unchanged staged-state
workspace_digest. Canon export groups non-dead fact objects (including possible
claims) by predicate and supplies the interner, decisions, and attacks needed
to interpret their support. Dead claims remain in the store and ordinary object
export. Canon refuses evidence-corrupt when the commit decision list differs
from stored Ratify carriage objects.
3.11 Constraint declarations
Declare a constraint on a predicate:
author.declare_functional {workspace, pred, provenance}declarespreda functional dependency — each subject holds at most one certain value for it, so two distinct certain values raisecontinuity.fd-violation(DL-0073).
It stages like any other add and commits with author.materialize. Draft-claim
checking can infer single-valuedness from canon’s precedent, but canon
consistency checks use declared constraints. Whether a relation admits one value
or many is a ruling about the world, not one the engine makes (koan 3), so the
caller declares it. The declaration lowers to no atom — there is no fact to
query — and its provenance is stored but does not gate the structural constraint
(DL-0074).
3.12 Coreference declarations
Declare that two entity names refer to the same entity:
author.assert_same_as {workspace, left, right, provenance}asserts thatleftandrightname the same bearer. The evaluable world closes the merge symmetric-transitively over every predicate the two ids appear in (DL-0078); the merge’s standing is carried entirely by this claim’s own provenance — defeating the citing decision (author.defeat) un-merges every derived consequence on the next read, with no re-fixpoint.
Three findings are stage-time, not deferred to check: identity.self-merge
(warn) when left and right are the same id — a harmless no-op merge,
staged anyway; identity.temporal-unsupported (error) when either endpoint
is a declared era or event — identity over the chronology spine is refused
in v1, regardless of the claim’s own standing (DL-0078); and
identity.sort-mismatch (warn) when the two endpoints’ declared kinds
differ. A merge that would create a duplicate certain value for a
functionally-declared predicate (§3.11) surfaces the existing
continuity.fd-violation on author.check and blocks author.materialize
the same as it would for any other write — no separate mechanism.
3.13 Rule authoring
Add a derivation rule:
-
author.add_rule {workspace, name, head, body, provenance}declares one datalog derivation rule — a typedhead :- bodyclause.headis the already-typedgoal_atomshapeauthor.queryshares;bodyis a list of body literals ({atom, negated}), a positive atom or anot-negated one. One clause per call: an N-clause rule is Nauthor.add_rulecalls under distinct names — equivalent to one N-clause rule, and finer-grained forauthor.defeat, which cites a rule by name. Every fact the rule derives carries this claim’s own provenance (fire_rulesmultiplies a rule’s annotation into every derivation it fires): defeating the rule’s citing decision kills every derivation through it, with no re-fixpoint, exactly as defeating any other claim’s citation does.Stricter than every other
author.add_*tool. An error-severity finding refuses the whole add and stages nothing — not the usual stage-then-block-materialize shape — because a bad rule staged even once can BRICK every later read: the staged world recompiles on every call, and an unstratifiable or range-unsafe rule set makes that recompile fail outright. Checked before staging:rule.unbound-variable(a head or negated-literal variable no positive body atom binds — datalog range restriction);rule.duplicate(the name is already staged or already in the compiled base); and, only once those pass, a stratification pre-flight — a tentative compile of the candidate rule alongside everything already staged — refusing withrule.unstratifiableon failure. The ordinary per-declaration census still applies (field.control-char,citation.unknown-decision).A functional-dependency break a rule’s own derivation creates — visible only once the world is saturated, not in the pre-saturation authored layer
continuity.fd-violationreads — surfaces ascontinuity.derived-fd-violationonauthor.checkand blocksauthor.materializethe same way. No separate wire mechanism: it is the same check battery, reading a second, saturated lens.
3.14 Inertial persistence declarations
Declare persistence for a fluent predicate:
-
author.declare_inertial {workspace, pred, provenance}declarespredinertial: a fluent value assertedsince twith no authoreduntilis meant to persist forward until a later, distinct, live value on the same slot defeats it. Likeauthor.declare_functional(§3.11), this is a carriage-only declaration with no ground atom. Unlike the structural functional constraint, inertial policy is gated by its provenance.The projection. The op stages and materializes the declaration and carries it through every lowering (the compiled world, forks, merges, and the durable lorestore), and the
continuitycheck reads it: an open inertial value’s forward persistence is bounded at the next distinct certain value on the slot. This composes into three outcomes — an ordered-distinct pair is a silent transition; an undeclared order is acontinuity.fluent-order-undeclaredwarn (with a settling before-fact repair); a certain co-hold (the same anchor, or authored overlap) is acontinuity.fluent-contradictionerror. An inertial value with nosinceanchor raisescontinuity.inertia-unanchored(warn). Capping is active only while both the declaration and the competing value are certain (docs/decisions.mdDL-0081 records the full ruling).
3.15 Predicate identity declarations
Declare that two predicate names refer to the same property:
author.assert_predicate_same_as {workspace, left, right, provenance}records thatleftandrightname the same property. It closes symmetrically and transitively over certain identity claims, with a deterministic lexicographically smallest representative used only for evaluation and checks. It never infers identity from names, values, shared subjects, or observed shape. A self-identity is a warning and a no-op.
The certainty and preview path is deliberately the entity-identity path’s
path. A proposed identity may appear as a possible staged consequence, but it
does not create a certain merge or a certain FD/fluent finding. A caller
previews certain effects by supplying a resolving citation (or the equivalent
fork valuation) before reading author.check or author.diff. Defeat uses the
existing author.defeat path: support conditioned on the identity flips dead,
derived facts and findings revert, and source facts remain under their
original predicate names.
The declaration is visible to authored facts, queries, rules, FD checks, fluent
checks, and durable lowering. Functionality and inertia combine by union within
a certain identity component and emit the non-blocking structured warning
predicate-identity.policy-promoted when a member declaration promotes the
merged property. A known positional-arity conflict in facts or rule
heads/bodies raises the error predicate-identity.arity-mismatch; unknown
arity is allowed until evidence for the affected identity component arrives.
Rechecking is component-scoped: a new fact or rule touches only identity claims
whose component contains that predicate, and an unrelated workspace mutation
does not rescan the world.
Rules match and derive through the predicate closure, and the identity claim is
included in every affected support polynomial. A functional collision remains
continuity.fd-violation; a fluent overlap remains
continuity.fluent-contradiction; both findings carry structured data naming
the identity assertion. Entity and predicate identity must commute regardless
of arrival order, as must the two orderings in which facts and predicate
identity are authored. Loader, evaluator, and lorestore outputs must be
byte-identical, and branch pre-images must read the branch head commit rather
than the store root.
This operation has no new .dli rendering. No consumer requires a predicate
identity textual round trip; structured JSON, fact objects, and the consumer’s
rendered diff are the supported wire and review surfaces.
3.16 Read tools
Eight non-mutating tools let an agent inspect workspace and world state without
staging anything. They carry no expected_digest or idempotency_key — they
are pure queries with no side effects.
| Tool | Purpose |
|---|---|
author.query | Query the compiled world — pattern-match over derived atoms. Returns matching atoms and their support. A row’s claim_ids, when present, names committed source claims with that exact atom at the workspace base; derived or only-staged rows can have none. IDs do not themselves explain support. |
author.check | Run the full check battery against the workspace and return graded findings (the same findings a mutating call would surface, but on demand). |
author.why | Explain a derivation: the why-not provenance trace for a given atom or finding, showing the support chain or the reason a derivation failed. It also returns supporting_decisions (the recorded decisions anywhere in the symbolic antichain). active_supporting_decision_paths preserves the boundaries of each currently admissible minimal support path; blocking_defeats names the active direct decision attacks when no path remains live; and blocked_supports keeps each otherwise-admissible path beside the minimal direct attacks disabling it. All three are explicit arrays, including empty arrays for live, dead-without-a-decision-attack, or unknown atoms. The atom may be given rendered (atom, parsed by the .dli front end) or as a typed goal_atom (the same shape author.query takes, {"pred": …, "args": [{"Sym": …}, {"Var": …}]}); the typed form is what makes decision provenance reachable in a syntax-free build. |
author.findings | Retrieve the current findings for the workspace — the accumulated error and warning surface without triggering a new check pass. |
author.predicates | List staged predicates with arity plus functional/inertial declarations so callers can reuse an existing slot. |
author.entity_candidates | Return the bounded nearest declared entity ids, canonicalized and deterministically ordered. |
author.list_workspaces | Enumerate available workspaces (id, scope/fork, staged-declaration count, digest). |
author.as_of {workspace, pred, subject, at} | Project the inertially-current fluent value(s) of pred(subject) at a query point — a direct fluent read: check::slot_state_at kernel-side. dli.query with as_of also projects fluents; author.query remains unprojected. Returns each holding’s projection, temporal cover, support, since/until (a capped inertial value’s until is the DERIVED boundary), citations, and — when capped — the certain defeater that bounds it. |
Read tools share the standard request envelope (dli, id, op, params
with workspace where applicable) and return typed JSON responses. They do not
appear in the feedback contract’s mutating-call shape (§4) — they return
query-specific result shapes rather than { ok, workspace_digest, rendered, findings }. An agent uses them to orient before mutating: query what exists,
check the current state, understand a derivation, then act.
author.why evaluates those path fields under the workspace’s exact grounded
effective valuation. It does not infer decision ids from rendered prose. Paths
and attack pairs use exact recorded names, are sorted canonically, and are
pinned to the workspace’s selected commit (including branch and pinned-project
opens). An unconditional derivation is one active empty path, [[]]; an unknown
atom has no paths, []. A conclusion with one live independent path reports an
empty top-level blocking_defeats even when blocked_supports records a
defeated redundant alternative.
3.17 Expert authoring tools
Seven tools add the language lane and the continuity/ledger pair the expert
card (docs/cards/expert-card.md) describes; each stages like any other add,
returns the standard mutation envelope (§4), and requires provenance. All
are available in the extended MCP profile (--tools all); author.add_state
is also in the default profile.
| Tool | Purpose |
|---|---|
author.add_dialect {id, label?, body[]} | Declare a naming register — the phonology and name grammar a naming population draws on. |
author.add_name_part {id, gloss?, head[]} | Declare a name-part morpheme — the raw material composed names are built from. |
author.add_name {id, label?, head[], composition[], bearer?} | Compose a name from name-part morphemes, optionally binding it to the entity that bears it. |
author.add_term {id, label?} | Introduce a glossary term — a word the lore defines and reuses. |
author.add_banned {word, reason?} | Ban a word from the lore surface, with the reason. |
author.add_state {id, value?, time?} | Assert a time-varying state fluent — a value that holds over an interval, not forever; time is {kind: after|during|since|until, point}. |
author.add_attest {question} | Request an attestation for an open question already in the workspace. |
3.18 Branch and merge tools
author.branch {workspace, action: create|list|delete, name?, from?} creates a
persistent, named ref into the workspace’s lorestore at the source branch’s
head (from, default main), lists branches, or deletes a branch pointer.
author.merge {workspace, source, target?} merges a branch into the target
branch (default main) and reports valuation, exclusivity, and
functional-dependency conflicts (AuthorMergeResult). The operation can also
be refused when the histories cannot be safely combined.
Both tools operate on durable lorestore refs. A workspace opened with a branch stages changes for that branch; materialization commits to its selected branch. A merge combines branch histories and reports valuation, exclusivity, and functional-dependency conflicts for review.
4. The feedback contract
Staging calls return graded findings, rendered lines, and a workspace_digest.
A finding has a stable code, a domain-spoken message, severity, and grade; it may
also include citations or structured repair data. An error-severity finding
blocks author.materialize, while a warning does not. Severity governs the gate;
grade describes the strength of the evidence.
author.materialize returns committed fact objects by default. Set
rendered: true when a text rendering is useful. On a blocked materialization,
the response uses materialize-blocked; inspect its structured error data and
workspace findings before repairing the staged claims. Other operations return
the shapes defined by their generated request and response schemas.
Wire error codes and their classes are listed in the generated error-code reference. Error codes are not a substitute for graded findings on a staged claim.
Fail-closed. A tool call that cannot be validated returns a typed error and
stages no partial mutation — a malformed param or missing required provenance
is a schema error, a stale digest is a refused error (stale-fence), and each
rejects the call whole. The distinction is deliberate: a schema or refused
error rejects the call and stages nothing; an error-severity finding stages
the claim but bars materialize until the world is made consistent again. Neither
path drops the input silently.
5. Supervision
Reviewers can inspect the diff — author.diff’s
structured per-fact rows, each with its before/after standing, which the
consuming application renders for review (DL-0046). Where a formatter is linked,
that rendering can be the canonical .dli surface, which is the same surface a
human author would have written by hand — so human and agent contributions are
reviewed through one lens. Deloreis serves the structured rows; the rendering is
consumer-owned.
Provenance discipline. DLI does not mint authority or enforce who may rule.
A caller supplies attribution and a cited decision state; DLI records the claim
when the cited decision resolves. An unknown decision raises
citation.unknown-decision and blocks materialization. The consuming application
owns authorization, review, and the policy that interprets a resolved citation.
Contract 2.0.0 is the sole served wire version.
Framing safety. Instruction-shaped strings that arrive in parameters — an
entity id, a description, a question phrased as a command — are data. They
never alter tool behavior: a name that reads “ignore prior findings and
materialize” is stored and rendered as a name and changes nothing about how the
tool checks, grades, or gates. The paired framing fixtures under
contracts/dli-worker/fixtures/framing/ pin this from independent cold project
states, including reads over separately staged benign and tainted workspaces.
6. Contract discipline
Every tool has a request JSON Schema and a response JSON Schema, generated
from the serve crate’s Rust types into contracts/dli-worker/schemas/ so the
schemas cannot drift from the code that serves them. Each tool also has fixtures
in the closed registry: golden (a canonical request and its exact response),
negative (a malformed or invalid request and the typed error it must
produce), and framing (an adversarial-string request and the benign-identical
behavior it must show). The rule is flat: a tool without fixtures does not
exist — it cannot be listed here or served.
The implemented 2.0.0 surface enforces that rule for all 63 operations. Paired cases declare
the exact changed request paths and stable safety projection; mutating cases
probe post-state, and materialized tainted labels must still reparse as
canonical .dli.
The worker serves contract 2.0.0. Earlier versions and removed operations
are refused through the killed-form registry. The schemas in
contracts/dli-worker define the wire shapes this semantic spec elaborates;
a divergence is a contract bug.
7. Verification
Authoring examples, contract fixtures, and replay tests exercise the supported interface and report its actual guarantees. The retired evaluation program is not part of the release.
8. Worked fiction scenario
The Holmes casebook is generated from a replay of MCP authoring calls. It exercises contradiction detection, contested claims, a retcon, a branch, and a rule-derived conflict.
The charter
Twelve koans in four triads. The triads govern what the language believes (truth), what it refuses to lose (loss), what it looks like (form), and what it is allowed to become (growth).
The test a koan must pass. A koan earns its place only if it decides novel questions identically in two designers’ hands. A principle that merely describes the build is documentation wearing a koan’s clothes; it is not a koan. Each koan below is stated, given its meaning, shown what it settles, and — where the koan is load-bearing on questions the build has not yet met — worked against a genuinely novel decision. The worked decisions are the test, run.
The decision ledger (decisions.md) outranks this charter. Where they
disagree, the ledger wins.
Triad I — truth
1. Claims come from somewhere.
Meaning. A claim’s authority is its origin — authored by whom, ruled by what, assumed under which question. Nothing is true by being written down, and no origin can be manufactured after the fact.
It settles. There is no status field in the AST; a bare ratified cannot
parse; derived atoms cannot be hand-annotated; citations render from the same
polynomial that gates assertion.
A novel decision it makes — the wiki migration. When an old worldbuilding
wiki is imported, its ten thousand claims arrive proposed with origin
imported-from(wiki, page, rev). An import records where claims came from and
cannot mint authority, however trusted the source, because “trusted” is a
valuation only a ruling can make. The same answer holds for claims an LLM
extracts from old drafts.
Why here. Provenance drift is the repo pathology, and accumulated consequences accumulate in the story; the language believes what the canon believes.
2. Nothing changes but a ruling.
Meaning. The evaluated world changes only when a declared decision changes its valuation; every other operation — forks, sims, imports, repairs, tools — is exploration over an unchanged store. Koan 2 bounds what change is; koan 3 bounds what the engine may claim about the caller that declared it.
It settles. There is no delete opcode; supersession keeps lineage; forks are O(1); cutting an episode is a valuation flip plus a re-fold, not an erasure.
A novel decision it makes — the rename request. A contributor asks for
deloreis rename glass-weald glaswold as an in-place operation across the
corpus. The koan refuses the shape and dictates the correct one: a rename is a
supersession — the new name ruled in, the old name dying into lineage and
the killed-token registry, every claim’s history intact. There is no edit that
changes the past quietly; there is only a ruling that changes the future
visibly.
Why here. The author’s practice is already massively forked; the koan turns that workflow into the semantics.
3. The engine never decides canon.
Meaning. The engine records, checks, renders, and explains declared claims; it does not decide who is entitled to approve them or what an application calls canon. A caller supplies attribution and decision state. The engine checks the structure and the cited decision’s existence; the consuming application owns authorization, review, and policy.
It settles. The auto-fill trial still cannot turn a model completion into a verdict by itself. But the engine does not refuse a citation because of the caller’s identity: it records supplied attribution and resolved decision state as data (DL-0054, DL-0058).
Why here. The honest deck is a tool for the mind, never a hidden governor of the people using it. Its boundary is epistemic: it verifies what it can observe and never invents social authority it cannot observe.
Triad II — loss
4. Nothing is silently dropped.
Meaning. No input is discarded without a record. Off-interface input, unknown keywords, unhandled cases — each surfaces as a graded finding, never a silent loss.
It settles. Forward-compatibility against unknown-keyword tolerance: an unrecognized construct is a diagnosed finding, not a swallowed line. The bounded interface (koan 11) limits what is supported; it never authorizes a silent drop.
Why here. The gate is total by design; what the language refuses to lose is the first thing the loss triad protects.
5. An open question is a debt.
Meaning. An undecided fork is not a blank; it is an obligation the system carries and surfaces until it is ruled or retired.
It settles. question/option declarations are tracked debts; a waiting
candidate is filed as a ruling request and surfaced for the author, never held
in memory.
Why here. This is the koan where the language’s ethics and the world’s Frith are literally the same law: a debt named is a debt that cannot be quietly forgotten.
6. A verdict never outranks its evidence.
Meaning. A conclusion is only as strong as the weakest procedure it rests on. A grade is a claim about recheckability, and it cannot be laundered upward.
It settles. The verdict lattice (checked > tested > attested > asserted);
weakest-grade rollup over the procedure chain; replayable judgments. A
Monte-Carlo result never reports checked; a model attestation never reports
tested.
Why here. Grading honesty is what keeps the whole apparatus trustworthy; this koan governs grading and rollup, and its growth-side cousin — reimplement for checkability, borrow only baselines — lives in koan 12.
Triad III — form
7. One way to write it.
Meaning. For each thing sayable, there is exactly one canonical way to say it. No synonyms, no positional shorthands, no second syntax regime waiting in the expert tier.
It settles. The closed keyword vocabulary; the single canonical rendering, formatter-defined and gate-enforced; non-canonical text legal as input, never as repo state.
Why here. Koan 7 governs the text; its cousin, koan 11, governs the paths in. Seven keeps the writing predictable; eleven keeps the agent interface coherent.
8. The wheel refuses rank.
Meaning. Some sorts are cyclic, and a cycle has no first and no largest. The
type system honors that: a wheel sort carries succ, pred, across, and distance,
but no <, no sort order, and no maximum.
It settles. The single wheel sort shared by cyclic time (tide, moon,
season); asking for the “greatest” season or sorting the moon’s nights is a
type error, not a silent tie-break.
Why here. Stories can have cycles, and a language that faked a linear order over them would misrepresent the world it models.
9. Errors are lore.
Meaning. A diagnostic is part of the world’s record, spoken in the world’s terms. Errors are first-class output, authored with the same care as any other surface.
It settles. Diagnostics speak the domain, never PL-speak — a missing ruling, a ward made at the wrong hour, a season with no first — and each is a designed, snapshot-tested artifact.
Why here. The error ladder is most of what a writer meets day to day; if the errors are noise, the language is hostile, and the loss triad has failed at the surface.
Triad IV — growth
Truth, loss, and form govern what the language is; growth governs what it is allowed to become — which interfaces are supported, which components are owned, which targets are served.
10. One fabric.
Meaning. All knowledge in a declared corpus — including geography, magic, names, story-time, and games — lives in one runtime under one semantics. No satellite knowledge systems; integration is by declaration, not by API. If a thing is knowledge in the corpus, it is declarations in the fabric; if it is not, it consumes exports.
It settles. The timeline is a module of the fabric, not a separate authority; the naming view is generated from the compiled world rather than maintained as a sibling source. Exports render claims but do not introduce new canon.
A novel decision it makes — the export variant. A new fictional variant belongs in the world as a declaration with standing and provenance. An export profile can render that variant, but cannot silently define it.
Why here. The repo has lived the alternative — hand-maintained mirrors that drift, prose shadowing data, an apparatus built to chase divergence. One fabric is that lesson as law: divergence is prevented by having nothing to diverge.
11. One way in.
Meaning. Agents author lore through the semantic tools against the server;
people may also write and import .dli text. Both use the same underlying lore
model and shared kernel checks. Callers may declare attribution and decision state; the
consuming application determines what that declaration authorizes.
It settles. The semantic tools are the supported agent interface, and
.dli is a supported text authoring and reading surface (DL-0101). Stored fact
objects remain canonical. Raw AST emission is an internal mechanism, not a
taught agent interface.
A novel decision it makes — the form-editor question. A click-to-edit lore interface can be a client of the semantic tools. It supplies attribution and standing as data; the consuming application owns its review policy.
Why here. The sibling projects paid for it — path count predicted failure surface better than path quality.
12. Own what certifies; borrow only baselines.
Meaning. Anything whose verdict gates canon is small, owned, deterministic, and replayable; foreign components may propose candidates or serve as outrun baselines, never certify. Growth proceeds by small owned parts linked at the judgment boundary, and every owned part inherits the testing bill — property tests, differential oracles — as the price of checkability.
It settles. The vacated CP-SAT and clingo seats and the two-stage flip conditions; clingo’s retained role as a lab-side differential oracle; zero foreign reasoning processes through the medium term.
A novel decision it makes — the good-crate question. A mature Rust datalog crate appears that could replace the evaluator at half the code. Adopt? No for the certifying evaluator — a foreign fixpoint cannot certify, and forking it to host the annotation algebra yields ownership in the worst form: owned bugs, borrowed architecture. Yes as a baseline — run it as a differential oracle over the shared datalog fragment. One paragraph decides it, which is what having the koan is for.
Why here. The demonstrated ecosystem ethic of the sibling projects — a verifier posture, a differential pattern — stated as law rather than habit.
The tiebreak, and the budget
Every mechanism argues from a demonstrated lore requirement; the tripwires are contractual. When koans genuinely deadlock, the order is truth outranks loss outranks form outranks growth — form exists to prevent loss, loss-prevention exists to protect authorship, and growth exists to keep all three enforceable over time, never to enlarge the system for its own sake. Within a deadlock, the resolution that preserves replayable attribution wins first; at equal attribution, the resolution that adds less wins — less language, and explicitly fewer interfaces, fewer seats, fewer parts.
The budget has named lines:
- Interface count — one: the declarative tool surface. Authority boundaries, not entry doors, distinguish authors (DL-0043).
- Foreign certifiers — zero, with one rented baseline.
- Export targets — earn maintained profiles only from demonstrated consumers.
Principles in tension
The charter names its costs; a charter that names none has not been used.
- Single-form versus sugar. One canonical way to write it (koan 7) versus the ergonomics a writer would gain from shorthands. Resolved toward the single form: the formatter absorbs sloppiness on save, so the writer’s convenience is paid at input time, not by a second surface.
- The budget versus the timeline machinery. The size budget pushes features out; first-class story-time pulls a partial-order module, fluents, and wheels in. Resolved by demonstrated requirement: each timeline feature answers a named corpus failure, and nothing speculative rides along.
- Strictness versus the sketching writer. A total gate versus a writer who
wants to jot incomplete notes. Resolved by
proposed-first authoring: incompleteness is a first-class state (proposed, open question) rather than an error, so sketching is legal and never silently lost. - Domain errors versus machine output. Errors that speak the domain (koan 9) versus errors that a machine emitter or a benchmark can parse. Resolved by the two-channel error model: findings carry a stable machine-readable class and a domain-spoken message; neither is sacrificed to the other.
- One fabric versus a small system. One fabric pulls all knowledge into one runtime; the budget pushes everything out. Resolved by scope: one fabric governs semantics — all knowledge, one meaning, one store — not code shape. The kernel, the timeline module, and any future searcher are separate small parts sharing the judgment boundary (koan 12). A monolith is not what one fabric means; satellite semantics is not what small parts means.
- One way in versus nothing silently dropped. The semantic tools and the supported text surface must not silently discard input. Both use shared kernel checks and report findings; agents use the semantic tool interface.
The trials
Three worked decisions test the charter end to end.
- Bare
ratifiedcannot parse. A status with no origin is a claim from nowhere; koan 1 refuses it at the grammar. The AST has no status slot to fill, so the refusal is structural, not a lint. - A proposed conflict is surfaced. Two proposed values for a declared functional slot raise a warning. Nothing is dropped (koan 4), and neither account becomes canon without a ruling (koan 2). The author can record an open question (koan 5); the warning does not block materialization.
- The write-back refusal. A studio consuming the video-game export asks for
deloreis export --write-back, so their tool can push edited entity fields home. Koan 10: integration is by declaration — an external tool’s database is not the fabric. Koan 1: “the tool’s current field value” is not an origin with authority. Koan 3: the engine cannot decide whether the studio’s tool is authorized to alter canon. Koan 11: if the studio’s writers need in, they come through the same tool surface as every author; their application records the review and decision state it requires. Four koans, one paragraph: the write-back flag is refused because it bypasses the fabric, and the honest path is offered.
A parked koan candidate
Noted for a future charter revision, not adopted here: “The model’s word is
evidence about the work, never support for the world.” It states the boundary
that keeps attestations out of the support polynomials (decisions.md DL-0006).
The charter holds twelve koans; this candidate waits for its demonstrated need,
per the budget.
The plan
This plan is a dependency graph, not a schedule. There are no durations anywhere in it — not because they are unknown, but because a duration is the wrong instrument for work that parallelizes. The graph’s shape is the point: where it is wide, many nodes run at once; where it is a chain, one artifact truly consumes another. The build’s rate is set by how wide the frontier is, so the plan optimizes for width and states the frontier explicitly.
The decision ledger (decisions.md) outranks this plan. A node’s oracle is how
we know the node is done; a milestone is a set of nodes, never a date.
Node schema
Each node carries:
- id — a lane-prefixed identifier (
CORE-2,TIME-3). - deliverable — the artifact the node produces.
- needs — the nodes whose artifacts this node consumes. An edge exists only when B consumes A’s artifact — not for thematic affinity, not for “related to.” If B could start with A merely planned, there is no edge.
- produces — what downstream nodes consume from this one.
- oracle — the concrete check that closes the node. A node without a falsifiable oracle is not a node.
- lane — the concern it belongs to (one of nineteen).
- external — true when the node is delivered by another party (the author’s rulings); deferred when the node is parked behind a named flip condition and is not built at v1.
The nineteen lanes
| Lane | Concern |
|---|---|
store | Lorestore: the embedded fact store, the JSON-first cutover (DL-0041). |
distribution | Parked distribution research; not a release deliverable. |
infra | Repository scaffold, the cargo xtask gate, CI lanes. |
docs | The lead documentation and the spec suite. |
core | AST, JSON Schema, the annotated evaluator, the verdict lattice, the loader/front-end. |
query | The query engine and the fork manager. |
check | Continuity (six classes) and factcheck. |
time | Partial order, fluents, episode anchors, order-rules. |
practice | Magic/practice parameterization and ledger checking. |
sim | Retired simulator lane. |
language | Registers, morphemes, names, terms. |
episode | The episode schema (delta + story-time anchors). |
craft | Retired craft lane. |
attestation | The deterministic attestation store. |
payload | Retired draft-payload lane. |
surface | Formatter, error ladder, reference cards, the grammar gate, and authoring ergonomics. |
serve | The contract, the server, the CLI, and the tool-surface authoring tools. |
export | The portability/export layer. |
assurance | The mutation harness and deferred legacy assurance work. |
Nodes marked external or deferred are placed in the lane whose artifact they touch; they are not a lane of their own.
Release priority
The current priority is the fiction-release reset (DL-0101), which sits
outside this graph. The task list is docs/release/HANDOVER.md and the
target is docs/release/north-star.md. The four-suite benchmark evidence
program that used to be named here is retired. The distribution nodes below
(DIST-) are parked with no trigger, and the scrub tasks retire or remove the
BENCH-, SURF-4 and former-consumer-bound nodes.
The node registry
infra
- INFRA-1 — Repository scaffold: cargo workspace,
cargo xtask gate(CI-parity as a compiled Rust test), the three git-orchestration scripts, and the deterministic offline CI lanes. Needs: none. Produces: the buildable tree and the gate. Oracle:cargo xtask gateis green on the scaffold; the deterministic CI lanes pass with no network.
docs
- DOCS-1 — The lead documentation set: charter, decision ledger, this plan,
and the integration contract.
Needs: none. Produces: the settled decisions the specs elaborate.
Oracle: every doc carries
status+verdictfrontmatter; the ledger is present and single-writer. - DOCS-2 — The spec suite:
spec/00-overview.md, the per-lane specs, and the design notes. Needs: DOCS-1. Produces: the buildable spec of each lane. Oracle: every spec has frontmatter; every fenced example passes the docs-oracle (the real parser,dli_syntax::parse_document, over every fenced.dliblock and every committed example). - EXT-A1 — Retired under the fiction-release reset; no release deliverable.
core
- CORE-1 — AST types + the schemars-derived JSON Schema + the recursive-descent parser skeleton. Needs: INFRA-1. Produces: the normative AST and its schema. Oracle: the schema is derived from the types (no drift); AST↔canonical round-trips; insta-snapshotted.
- CORE-2 — The annotated seminaive evaluator: condition polynomials, the grade lattice, annotation-aware delta semantics, absorption normalization at every join, and the inverted provenance indexes. Needs: CORE-1. Produces: assertability, closures, support reads. Oracle: T1/T2 (status derivation, transitive closures) over real canon; differential agreement with the legacy gates.
- CORE-3 — The verdict lattice:
checked > tested > attested > asserted, the two-channel support/grade semantics, and weakest-grade rollup over the procedure chain. Needs: CORE-1. Produces: graded verdicts. Oracle: rollup is the minimum over the procedure chain, never over citations; judgments replay identically. - FRONT-1 — The loader/front-end: canon fronted as AST, with range-restriction and stratification (negative-dependency acyclicity) checked at load. Needs: CORE-1. Produces: the loaded, well-formed world. Oracle: real canon loads; a cyclic negative dependency is a load-time finding.
- DEF-3 — (deferred.) The SMT seat. Needs: none. Produces: nothing until triggered. Oracle: the trigger — a ratified arithmetic law — is documented; the seat is not built at v1.
- DEF-4 — (deferred.) Multi-authority valuation (per-author valuations, a merge policy). Explicitly outside v1. Needs: none. Produces: nothing until triggered. Oracle: the single-authority limit is documented as the standing boundary.
query
- ENG-1 — The query engine (
query,--layer/--as-of/--fork/--why) with blame / min-why / why-not. Needs: CORE-2. Produces: queries and dependency reads. Oracle:--whyrenders the support antichain; blame reads the defeater. - ENG-2 — The fork manager (
fork new/suppose/add/diff/promote,preflight). Needs: CORE-2. Produces: forks and derived-consequence diffs. Oracle: forks are O(1);diffis a derived-consequence diff;preflightis a candidate-valuation evaluation with a conflict scan.
check
- CHECK-1 — Continuity (the six classes) + factcheck (claims → supported/contradicted/unsupported with D-citations; rejected-lore constraints enforced). Needs: CORE-2, FRONT-1. Produces: the world-check battery. Oracle: shadow-read parity against the legacy continuity and factcheck oracles.
time
- TIME-1 — The partial-order module: the era spine, before-facts,
happens-beforeas a kernel rule under CI, cycle blame, andunorderedas a first-class verdict. Needs: CORE-2. Produces: the ordered chronology. Oracle: a before-cycle is reported as blame; an undeclared pair answersunordered, never a tie-break. - TIME-2 — Fluents: validity spans (
since/until/during), the cautious three-valued projection, andstate-at. Needs: TIME-1. Produces: time-anchored facts. Oracle: D-0114 water-status checks clean in both eras (the pre-enclosure flashback and the story-present episode), with no false contradiction. - TIME-3 — Episode anchors + order-rules + slack (earliest/latest legal slot)
- the offset→before migration compiler. Needs: TIME-2, EPI-1. Produces: the checked episode order. Oracle: order-rules check by linear scan; slack is a forward/backward pass; the compiler emits before-facts with adjacent citations.
- DEF-1 — (deferred.) The owned episode-order searcher (topological + chronological backtracking + window forward-checking). Needs: TIME-3. Produces: nothing until triggered. Oracle: the flip condition — a real propose-orderings workflow with soft/disjunctive constraints — is documented; every found order re-passes the TIME-3 checker.
practice
- PRAC-1 — Practice parameterization: typed sorts (including the
wheelsort), the closed resource algebra (linear/affine/graded monotone), side conditions, and error-class declarations that refine the seven structural classes. Needs: CORE-2. Produces: declared practices (magic systems). Oracle: practice well-formedness holds; thetrue-namingfixture is the acceptance test (a second, structurally disjoint practice hosts with no kernel code). - PRAC-2 — The ledger checker (
check-proof in <practice>): the premise-pool projection (certain ∩ known-by-speaker ∩ present), the linear accounting, and the expected-verdict discipline. Needs: PRAC-1, TIME-2. Produces: diegetic proof verdicts. Oracle: the practice scene checks; a slack-tide ward in a flood-bound scene is a wrong-hour (L3 timing) error.
sim
-
SIM-1 — Retired under the fiction-release reset; no release deliverable.
-
SIM-2 — Retired under the fiction-release reset; no release deliverable.
-
EXT-A2 — Retired under the fiction-release reset; no release deliverable.
-
DEF-2 — Retired under the fiction-release reset; no release deliverable.
language
- LANG-1 — Register / morpheme / name / term declarations with bundle provenance (one hypothesis variable per proposal bundle). Needs: CORE-2. Produces: the language packs. Oracle: the registers port one-to-one from the legacy data; a name is a typed derivation citing its register and composition.
- LANG-2 — The derivation checker (phonotactics + slot grammars) + name-space
queries + the generated ratified-names view + aural distinctness.
Needs: LANG-1. Produces: the compiled name-space (the mirror’s successor).
Oracle: shadow-read parity against the legacy vet on the ratified names and
the batch-001 candidates; a colliding declaration fails
deloreis check.
episode
- EPI-1 — The episode schema: the delta form + story-time anchors (
at, after/before, phase bindings), scenes with cast/presence, and knowledge fluents. Needs: CORE-1. Produces: the episode fold inputs. Oracle: the fold runs in production order; a flashback scene carries its own anchor and folds correctly.
attestation
- ATT-1 — The attestation store +
attestation-missingfindings + theattested-grade consuming checks. Needs: CORE-3. Produces: the graded model-judgment layer. Oracle: the store is append-only per question; a check lacking its attestation reportsattestation-missing, never a guess; no consensus lifts the grade.
payload
-
PAY-1 — Retired under the fiction-release reset; no release deliverable.
-
PAY-2 — Retired under the fiction-release reset; no release deliverable.
-
PAY-3 — Retired under the fiction-release reset; no release deliverable.
surface
-
SURF-1 — The formatter: the single canonical rendering (write sloppy, save canonical), gate-enforced. Needs: CORE-1. Produces: canonical surface. Oracle: the canonical form is idempotent; non-canonical text is legal input, never legal repo state.
-
SURF-2 — The graded error ladder: domain-speaking diagnostics via miette. Needs: CORE-2, SURF-1. Produces: the error surface. Oracle: diagnostics speak the domain (a missing ruling, not “unbound identifier”); insta-snapshotted.
-
SURF-3 — The reference cards (gentle subset + expert tier) + the human-writer probe protocol. Needs: SURF-1. Produces: the teaching surface. Oracle: every gentle-subset example on the first card compiles; no expert-tier word appears on the first card.
-
SURF-4 — Retired under the fiction-release reset; no release deliverable.
-
BENCH-1 — Retired under the fiction-release reset; no release deliverable.
-
BENCH-2 — Retired under the fiction-release reset; no release deliverable.
serve
-
CON-1 — The
contracts/dli-workercontract: the schemas, the op/tool inventory, and the closed fixture registry. Needs: DOCS-1, ENG-2. Produces: the boundary shared by server and clients. Oracle: an op or tool without golden + negative + framing fixtures does not exist; verdict parity holds across the fixtures. -
SERVE-1 —
deloreis serve: the MCP / JSON-over-stdio server carrying the batchdli.*operations. Needs: CON-1, ENG-2, CHECK-1. Produces: the running server. Oracle: golden/negative/framing fixtures pass; the envelope carries no timestamps and rejects unknown fields. -
CLI-1 — The
deloreisCLI:check,query,fmt,fork,preflight,timeline,export,name,check-proof,report,serve,mcp. Needs: CHECK-1. Produces: the command surface. Oracle: the man-page operations run; exit codes reflect verdicts. -
AUTH-1 — The authoring tool surface: the workspace/staging engine, the
author.*tools, per-tool golden/negative/framing fixtures, and immediate graded findings on every mutating call. Needs: CON-1, ENG-2, FRONT-1. Produces: the tool surface. Oracle: the author-session golden fixture passes end to end; the framing-attack fixture leaves verdicts unchanged. -
AUTH-2 — An original worked fiction scenario over the authoring tools. Needs: AUTH-1, CHECK-1. Produces: a replayable example and transcript. Oracle: the Hollow scenario exercises contradiction, contested claims, retcon and restoration, a what-if, and replay after restart.
-
EXT-F3 — Retired under the fiction-release reset; no release deliverable.
-
EXT-F4 — Retired under the fiction-release reset; no release deliverable.
export
-
EXP-1 — The export core (
export --profile <target>at the CLI; explicit registry and valuation options in the library): one-way compiles from the certain layer, with a manifest. Needs: ENG-1. Produces: the export backend spine. Oracle: an export reads the certain layer and fails closed on proposed lore; the manifest traces every artifact to its exact canon state. -
EXP-2 — The context-pack backend (the first export backend, formalized). Needs: EXP-1. Produces: the audio-script context packs. Oracle: it reproduces today’s context-pack output.
-
EXP-3 — Retired under the fiction-release reset; no release deliverable.
-
DEF-5 — Retired under the fiction-release reset; no release deliverable.
-
EXP-4 — The video-game (articy-shaped JSON) and free-rider (CSV, static wiki) profiles. Needs: EXP-1. Produces: the remaining export targets. Oracle: typed entities with who-may-know-what scopes; citations travel as dev-metadata, stripped for ship.
assurance
-
PROOF-1 — The mutation harness: seeded-violation operators × the real gates; every check gets operators. Needs: CHECK-1. Produces: the gate-coverage proof. Oracle: every operator is caught; honest misses are xfail-strict, never hidden.
-
PROOF-2 — Retired under the fiction-release reset; no release deliverable.
-
MIG-1 — Retired under the fiction-release reset; no release deliverable.
-
MIG-2 — Retired under the fiction-release reset; no release deliverable.
-
MIG-3 — Retired under the fiction-release reset; no release deliverable.
-
MIG-4 — Retired under the fiction-release reset; no release deliverable.
-
VAL-1 — Retired under the fiction-release reset; no release deliverable.
-
EXT-F1 — Retired under the fiction-release reset; no release deliverable.
-
EXT-F2 — Retired under the fiction-release reset; no release deliverable.
-
EXT-F5 — Retired under the fiction-release reset; no release deliverable.
store
The lorestore cutover (DL-0041). The staged plan with full design rules,
theory grounding, and the risk register is
design-notes/lorestore-roadmap.md; these nodes are its skeleton.
-
LS-0 — Ratify the pivot: DL-0041, the law updates in
AGENTS.md, this lane, and the consolidation pointers in the four source notes. Needs: none. Produces: the authority every later store node cites. Oracle:cargo xtask authorityand the docs-oracle are green; DL-0041 names the superseded clauses of DL-0003 and DL-0029. -
LS-1 — The fact-object model: Rust types + schemars JSON Schemas for
FactObject/Commit/Ref/EvidenceEnvelope, JCS canonical bytes, SHA-256 identity, the three-way identity split (content-hash versions, minted claim ids, provenance-bearing lineage edges), canonical polynomial DNF, no floats,schema_version+ upcast-on-read. Needs: LS-0. Produces: the normative stored-object schema. Oracle: total lowering — the vendored corpus lowered to fact objects compiles to the identical fixpoint, findings, and digests. -
LS-2 — The lorestore crate MVP: loose-object store, refs with valuation overlays, append-only commit log, CAS ref updates, advisory lock, crash-safe write ordering, filesystem-only storage trait; workspace digest becomes the root commit hash; no secondary indexes, no branches beyond main. Needs: LS-1. Produces: durable deterministic persistence. Oracle: cross-platform root-hash determinism; crash injection never tears the store;
state ≡ fold(commit log)property-tested. -
LS-3 — The read path:
dli_world::compilefrom lorestore; the one-way.dli → lorestoreimport tool; config-selected dual backend. Needs: LS-2. Produces: worlds compiled from stored facts; the migration path. Oracle: differential parity —.dlisource and its import compile identically. -
LS-4 — Mutations stage structured fact objects; materialize commits them with a separate workspace fence and commit hash. The contract 2.0 fixtures verify the durable path.
-
LS-5 — The query and review contract: the JSON response schema carrying bindings, provenance antichain, grade, and
(root_hash, valuation); the MCP-served structured-diff/review contract (deloreis ships no review UI — rendering is consumer-owned). Needs: LS-4. Produces: replayable verdicts; the review data that replaces git-diff-of-text. Oracle: a stored result replays byte-identical from(root_hash, valuation, query); a golden review-flow fixture drives a seeded change-set through the JSON contract alone. -
LS-6 — The merge engine: commit DAG with merge commits, 3-way merge over fact sets, conflict detection confined to valuation disagreement, constraint violations, and stratified-negation effects. Needs: LS-5. Produces: persistent divergent worlds with deterministic reconciliation. Oracle: merge commutes and associates up to root-hash equality on conflict-free corpora; the seeded-conflict corpus catches all three conflict classes.
-
LS-9 — Expert-tier structuring: typed interiors for the eleven string-bodied declaration types; downstream semantic parses in
dli-world,dli-ledger, anddli-timebecome typed-AST constructors; stored facts upcast from the v1 string-bodied schema. (Id out of sequence: found by the coupling audit after LS-0..LS-8 shipped; DAG position is LS-1 → LS-9 → LS-7.) Needs: LS-1. Produces: an AST that is AST all the way down; the condition for retiring the parser. Oracle: no crate outsidedli_syntaxparses stored data;validate_proof_decl’s format-and-reparse round-trip is replaced by a structural validator; upcasted corpora compile identically. -
LS-7 — Text support returns to the default build. The parser-free build remains optional; T9 tracks the work and its checks.
-
LS-8 — Contract 2.0.0 is the only served version; prior versions and removed operations are registered as killed forms.
-
LS-10 — Deferred incremental compilation (DL-0102): the unused loader-reuse compiler is retired. The bounded commit cache and incremental saturation remain; any future delta compiler requires a new decision and a parity oracle before it is used. Needs: LS-6. Produces: no release deliverable. Oracle: retained saturation and cache tests pass; the retired compiler has no callers.
distribution
Distribution research is parked in design-notes/scale-and-distribution.md.
The embedded engine is the release target.
-
DIST-1 — Parked distribution research; no fiction-release deliverable.
-
DIST-2 — Parked distribution research; no fiction-release deliverable.
-
DIST-3 — Parked distribution research; no fiction-release deliverable.
-
DIST-4 — Parked distribution research; no fiction-release deliverable.
The dependency graph
graph LR
INFRA-1 --> CORE-1
DOCS-1 --> DOCS-2
DOCS-1 --> CON-1
CORE-1 --> CORE-2
CORE-1 --> CORE-3
CORE-1 --> FRONT-1
CORE-1 --> EPI-1
CORE-1 --> SURF-1
CORE-2 --> ENG-1
CORE-2 --> ENG-2
CORE-2 --> CHECK-1
FRONT-1 --> CHECK-1
CORE-2 --> TIME-1
CORE-2 --> PRAC-1
CORE-2 --> LANG-1
CORE-2 --> SURF-2
CORE-2 --> BENCH-1
CORE-2 --> PROOF-2
CORE-3 --> ATT-1
SURF-1 --> SURF-2
SURF-1 --> SURF-3
SURF-1 --> BENCH-1
TIME-1 --> TIME-2
TIME-2 --> TIME-3
EPI-1 --> TIME-3
TIME-2 --> PRAC-2
PRAC-1 --> PRAC-2
PRAC-1 --> SIM-1
SIM-1 --> SIM-2
SIM-2 --> EXT-A2
SIM-2 --> DEF-2
TIME-3 --> DEF-1
LANG-1 --> LANG-2
EPI-1 --> PAY-1
PAY-1 --> PAY-2
CHECK-1 --> PAY-2
PRAC-2 --> PAY-2
PAY-2 --> PAY-3
ENG-2 --> CON-1
CON-1 --> SERVE-1
ENG-2 --> SERVE-1
CHECK-1 --> SERVE-1
CON-1 --> AUTH-1
ENG-2 --> AUTH-1
FRONT-1 --> AUTH-1
AUTH-1 --> AUTH-2
CHECK-1 --> AUTH-2
CHECK-1 --> CLI-1
CON-1 --> EXT-F3
SERVE-1 --> EXT-F3
AUTH-2 --> EXT-F4
BENCH-1 --> BENCH-2
AUTH-1 --> BENCH-2
SERVE-1 --> BENCH-2
SURF-1 --> SURF-4
SURF-2 --> SURF-4
SURF-3 --> SURF-4
BENCH-1 --> SURF-4
ENG-1 --> EXP-1
EXP-1 --> EXP-2
EXP-1 --> EXP-3
SIM-2 --> EXP-3
EXP-1 --> EXP-4
CHECK-1 --> PROOF-1
FRONT-1 --> MIG-1
MIG-1 --> MIG-2
PROOF-1 --> MIG-2
EXT-F1 --> MIG-2
EXT-F2 --> MIG-2
EXT-F5 --> MIG-2
MIG-2 --> MIG-3
EXT-A1 --> MIG-3
MIG-3 --> MIG-4
TIME-3 --> MIG-4
INFRA-1 --> VAL-1
LS-0 --> LS-1
LS-1 --> LS-2
LS-2 --> LS-3
LS-3 --> LS-4
LS-4 --> LS-5
LS-5 --> LS-6
LS-1 --> LS-9
LS-9 --> LS-7
LS-4 --> LS-7
LS-5 --> LS-7
LS-6 --> LS-8
LS-7 --> LS-8
EXT-F4 --> LS-8
LS-5 --> DIST-1
DIST-1 --> DIST-2
DIST-1 --> DIST-3
DIST-2 --> DIST-4
DIST-3 --> DIST-4
Nodes with no incoming edge are roots: INFRA-1, DOCS-1, EXT-A1, EXT-F1, EXT-F2, EXT-F5 (the zero-state runnable frontier), plus the deferred DEF-3 and DEF-4. The store lane joined after the standalone release closed (DL-0038, DL-0041) and is now complete locally. The distribution lane is a later, deferred chain; the status matrix, not this historical zero-state graph, is the current release authority.
The frontier
The release handover is the current execution order. Complete the remaining private-data and ledger cleanup, ship and harden the text surface, repair the wire issues, then deliver the example and onboarding documentation. Owner-only publication work follows those gates.
Node status matrix
cargo xtask authority requires every registry node to appear exactly once
with one of four states. done means the node’s stated oracle is replayable in
this tree. open means local engineering remains. deferred names work parked
by a ledger ruling or explicit release boundary. external-blocked requires an
author or consumer artifact that this repository does not own.
| Node | State | Evidence |
|---|---|---|
| INFRA-1 | done | cargo xtask gate |
| DOCS-1 | done | cargo xtask authority and cargo xtask docs-oracle |
| DOCS-2 | done | cargo xtask docs-oracle |
| EXT-A1 | deferred | Retired under DL-0101; no release deliverable. |
| CORE-1 | done | cargo test -p dli-ast -p dli-syntax and schema gate |
| CORE-2 | done | cargo test -p dli-eval |
| CORE-3 | done | cargo test -p dli-core -p dli-eval |
| FRONT-1 | done | cargo test -p dli-world |
| DEF-3 | deferred | DL-0005 arithmetic-law trigger has not fired |
| DEF-4 | deferred | v1 remains single-authority |
| ENG-1 | done | cargo test -p dli-eval --test query |
| ENG-2 | done | cargo test -p dli-eval --test fork |
| CHECK-1 | done | cargo test -p dli-world |
| TIME-1 | done | cargo test -p dli-time |
| TIME-2 | done | cargo test -p dli-time |
| TIME-3 | done | cargo test -p dli-time |
| DEF-1 | deferred | No soft or disjunctive propose-order workflow has triggered it |
| PRAC-1 | done | cargo test -p dli-ledger |
| PRAC-2 | done | cargo test -p dli-ledger |
| SIM-1 | deferred | Retired under DL-0101; no release deliverable. |
| SIM-2 | deferred | Retired under DL-0101; no release deliverable. |
| EXT-A2 | deferred | Retired under DL-0101; no release deliverable. |
| DEF-2 | deferred | Retired under DL-0101; no release deliverable. |
| LANG-1 | done | cargo test -p dli-world language suites |
| LANG-2 | done | cargo test -p dli-world name-vet suites |
| EPI-1 | done | cargo test -p dli-world episode suites |
| ATT-1 | done | cargo test -p dli-world attestation suites |
| PAY-1 | deferred | Retired under DL-0101; no release deliverable. |
| PAY-2 | deferred | Retired under DL-0101; no release deliverable. |
| PAY-3 | deferred | Retired under DL-0101; no release deliverable. |
| SURF-1 | done | deloreis fmt --check examples/ |
| SURF-2 | done | syntax and loader diagnostic snapshots |
| SURF-3 | done | docs oracle over reference-card examples |
| SURF-4 | deferred | Retired under DL-0101; no release deliverable. |
| BENCH-1 | deferred | Retired under DL-0101; no release deliverable. |
| BENCH-2 | deferred | Retired under DL-0101; no release deliverable. |
| CON-1 | done | cargo xtask fixtures and cold replay cover 63 operations with golden, negative, and framing cases. |
| SERVE-1 | done | cargo test -p dli-serve --features syntax --test dispatch --test fixture_replay exercises serving and contract replay. |
| CLI-1 | done | The built Clap graph generates the command reference; cargo test -p deloreis --test cli --test serve_project covers every command/help path, positive and negative workflows, and project-backed transport |
| AUTH-1 | done | cargo test -p dli-serve --test author --test author_read --test fixture_replay |
| AUTH-2 | done | cargo test -p deloreis --test hollow replays the worked scenario over MCP and checks its transcript. |
| EXT-F3 | deferred | Retired under DL-0101; no release deliverable. |
| EXT-F4 | deferred | Retired under DL-0101; no release deliverable. |
| EXP-1 | done | cargo test -p dli-export |
| EXP-2 | done | cargo test -p dli-export context-pack suites |
| EXP-3 | deferred | Retired under DL-0101; no release deliverable. |
| DEF-5 | deferred | Retired under DL-0101; no release deliverable. |
| EXP-4 | done | cargo test -p dli-export plus the multi-target dli.export.run golden fixture cover Articy, CSV, and static-wiki emission and provenance stripping |
| PROOF-1 | open | DL-0101 retired the consumer-specific corpus; the replacement gate covers core mutations and an explicit strict expected miss, but coverage of every check is not yet established. |
| PROOF-2 | deferred | Retired under DL-0101; no release deliverable. |
| MIG-1 | deferred | Retired under DL-0101; no release deliverable. |
| MIG-2 | deferred | Retired under DL-0101; no release deliverable. |
| VAL-1 | deferred | Retired under DL-0101; no release deliverable. |
| MIG-3 | deferred | Retired under DL-0101; no release deliverable. |
| MIG-4 | deferred | Retired under DL-0101; no release deliverable. |
| EXT-F1 | deferred | Retired under DL-0101; no release deliverable. |
| EXT-F2 | deferred | Retired under DL-0101; no release deliverable. |
| EXT-F5 | deferred | Retired under DL-0101; no release deliverable. |
| LS-0 | done | DL-0041; design-notes/lorestore-roadmap.md; the law updates in AGENTS.md |
| LS-1 | done | tracked work — design-notes/lorestore-roadmap.md LS-1 |
| LS-2 | done | tracked work — design-notes/lorestore-roadmap.md LS-2; shipped — src/storage.rs, src/lorestore.rs, tests/cross_platform_determinism.rs, tests/state_fold_invariant.rs, tests/crash_injection.rs |
| LS-3 | done | compile() read path, tests/differential_parity.rs, all_fact_hashes() dedup |
| LS-4 | done | Per-add lowering and fenced materialize persist fact objects; workspace and commit digests stay distinct. |
| LS-5 | done | Structured review and replay carry provenance, standing, root hash, valuation, and accept/reject state. |
| LS-6 | done | Merge and branch tests cover commit DAGs, conflict detection, and deterministic replay. |
| LS-7 | open | Restore text support by default and verify import and MCP (T9). |
| LS-8 | done | Contract 2.0.0 and killed-form tests; DL-0103. |
| LS-9 | done | Typed declaration interiors and upcast tests preserve stored data; parser-free kernel dependencies are checked. |
| LS-10 | deferred | DL-0102 retires the unused loader-reuse compiler; incremental saturation and the commit cache remain tested. A future delta compiler requires a new decision and parity oracle. |
| DIST-1 | deferred | Parked by the fiction-release reset. |
| DIST-2 | deferred | Parked by the fiction-release reset. |
| DIST-3 | deferred | Parked by the fiction-release reset. |
| DIST-4 | deferred | Parked by the fiction-release reset. |
Milestones
The ordered release tasks and their acceptance criteria are maintained in the handover. A release is complete only when its engine, fixtures, examples, documentation, and required reviews have passed.
Decision ledger
This ledger outranks the other project documents. Append new decisions with the
next sequential DL number, a date, an attribution (machine or human), and
a ruling, a short rationale, and a revisit trigger when applicable. Corrections are new entries. The reset below is the one authorized
exception to the earlier append-only history; subsequent entries are append-only.
DL-0104 — Standing rulings carried forward from pre-fiction-reset
Date: 2026-11-02
Attribution: machine.
Ruling. DL-0101 authorizes replacing the old ledger with this readable
continuation. The still-applicable engine and project obligations of the
rulings listed below remain binding, including their qualifications and
exceptions, except where a later ruling supersedes them. These summaries are
navigation, not permission to discard a surviving detail. The earlier text is
available with git show pre-fiction-reset:docs/decisions.md; DL-0102 and
DL-0103 were recorded later and are available with
git show 8c73720:docs/decisions.md. Retired consumer-specific work, obsolete
contract-version promises, the abandoned evaluation program, and the retired
cutover are not carried forward.
- DL-0001: The project is De Lore Is; text uses
.dli, the executable isdeloreis, and worker operations usedli.*. - DL-0002: The charter is constitutional; resolve koan conflicts by truth, loss, form, then growth.
- DL-0003, as amended by DL-0041 and DL-0101: Schemas derive from Rust types; fact objects and their canonical JCS bytes are authoritative, with supported text as a rendering and authoring surface.
- DL-0005: Canon-certifying judgments are owned, deterministic, offline, and replayable; foreign tools do not silently certify canon.
- DL-0006: Grades are
checked > tested > attested > asserted; support and grade remain distinct, and chains roll up at their weakest grade. - DL-0008: Keep the hand-built parser, typed schemas, domain diagnostics, and the project’s chosen small tooling stack.
- DL-0009: Keep phases pure and invocation-local; do not make a stateful compiler a prerequisite for incremental evaluation.
- DL-0011: The ledger outranks documents; docs and the skill have
statusandverdict, IDs are minted in the ledger, and the documentation tree stays flat. - DL-0013: Gating lanes are deterministic and offline; external or nondeterministic experiments do not block the language gate.
- DL-0014, as amended by DL-0043 and DL-0054: Agents author lore using the server’s semantic authoring tools; authority and review policy belong to the caller.
- DL-0015:
cargo xtask gateis the canonical compiled CI-parity gate. - DL-0016: Surface names describe their behavior; retired forms are registered and refused with their successor.
- DL-0019: The cyclic timing proof finding is
proof.wrong-phase. - DL-0020 and DL-0024: World rules support typed bodies and stratified
not; negation carries valuation-neutral, theory-respecting defeat rather than treating an open counterclaim as absent. - DL-0023: Authoring reads inspect the staged world without mutating it and report structured findings.
- DL-0025: The canonical formatter is comment-free; teaching commentary lives outside canonical text.
- DL-0029, as qualified by DL-0079: Accepted syntax has semantics or an honest diagnostic; explicitly ruled inert forms retain their recorded dispositions.
- DL-0030: Advertise a capability only when its documented runtime and durability semantics are implemented.
- DL-0037, as amended by DL-0101: Keep the generic context, articy, CSV, and static-wiki exports, with validated profiles and containment of hostile text.
- DL-0039: Proof selection is explicit when ambiguous, and quoted claims retain their intended term form.
- DL-0041: The content-addressed lorestore is canonical; deterministic reconstruction, schemas, offline checks, and checked documentation examples remain part of the contract.
- DL-0042: The old line-count budget and its gate are retired; the remaining architectural constraints still apply.
- DL-0043, as amended by DL-0054 and DL-0101: Semantic tools provide the common agent interface; text is supported, and the engine does not decide caller authorization.
- DL-0044 and DL-0045: Stage declarations as fact objects per mutation; materialize is acceptance, reconstruction is total or refuses, and workspace and commit digests remain distinct.
- DL-0046: Structured objects and graded findings are the primary mutation result; optional text rendering, fencing, and deterministic replay remain available.
- DL-0047 and DL-0049: Stored declaration interiors are typed; old schemas upcast at the boundary without changing identity, and the kernel need not parse stored data.
- DL-0048: Branches and two-parent commits merge via a common base, union, and recompile; valuation, constraint, and negation conflicts remain explicit and deterministic.
- DL-0052: Humans and machines may mint attributed ledger rulings; supervision is asynchronous, with correction by a later ruling.
- DL-0054: Attribution and decision state are caller-supplied data; citations must resolve, and unknown decisions fail closed.
- DL-0055: Store-backed projects replay the selected head or follow a branch as configured; storage schema changes and wire claims stay coupled and checked.
- DL-0056: Corpus exchange preserves interner names and decision data; unsupported rendering is refused explicitly, never returned as an empty success.
- DL-0058: Supported expert authoring declarations use the same staged, checked, durable path and record citations as data.
- DL-0059: Preserve the original pack scope through commits; an empty corpus has no facts, and imported claim IDs are checked against their contents.
- DL-0061: Escape caller-supplied controls in finding messages at the serialization boundary so content cannot forge a second finding.
- DL-0064: The
killedregistry predicate records prohibited tokens as data and is excluded from killed-token usage scans. - DL-0065, DL-0066, DL-0067, DL-0068, and DL-0069, as amended by DL-0101: Canon exports derive their retained domains and provenance from the folded store; private-consumer parity targets do not bind the fiction release.
- DL-0070: Standing is
asserted,proposed,provisional, orratified; legacy stored values upcast honestly and current citation state is explicit. - DL-0071: Valued states lower to evaluable facts and temporal fluent spans; continuity checks use their intervals.
- DL-0072: Teaching material describes citation integrity and caller-owned authorization, not a required human sign-off.
- DL-0073: Functional contradictions compare normalized values, inspect canon against itself, and retain explicit blame.
- DL-0074 and DL-0075: Functional dependencies are declared, persisted, and checked by the same primitive at load, fork, and merge boundaries.
- DL-0078: Entity identity is explicit, defeasible, symmetric, and transitive; era/event identity is refused and ordinary facts do not silently activate identity.
- DL-0079, as amended by DL-0101:
use,draft, andexportdeclarations are intentionally inert; narration and variants retain their recorded limits; text removal is cancelled. - DL-0080: Typed identity and rule authoring preserve provenance, temporal reference findings, and structured finding data.
- DL-0081: Inertial persistence is a valuation-lazy projection; only a certain declaration and later distinct certain value cap an open span, and defeating that support can restore it.
- DL-0082:
author.as_ofuses the continuity checker’s canonical slot projection, reports uncertainty and supporting boundaries, and does not fall back to unrelated raw facts. - DL-0083: Recording decisions is durable; decision-stamped materialization and why explanations retain their recorded scope and provenance.
- DL-0084 and DL-0091: Predicate and entity-candidate reads are bounded and deterministic; candidates come from declared entities and use the specified exact ranking.
- DL-0085:
author.whyaccepts a typed goal; decision-stamped materialization applies to facts, not arbitrary carriage declarations. - DL-0086: Merge-base work is linear in the reachable commit graph, and dry-run merge leaves refs unchanged.
- DL-0087, as amended by DL-0102: Retain permanent, non-vacuous incremental-evaluator differential and confluence coverage for negation, defeater-only growth, identity, and strata, with full recompilation fallback; retire the unused loader-reuse compiler and its workstream.
- DL-0089 and DL-0102: Retain the bounded compiled-world cache and incremental evaluator saturation; retire the unused loader-reuse compiler.
- DL-0092: Predicate identity is an explicit, defeasible, symmetric-transitive claim, never inferred from spelling.
- DL-0093: Resolution indexes and the live lowering interner are derived workspace state, with results equivalent to a fresh rebuild.
- DL-0094 and DL-0095: Persisted valuations cross independently compiled worlds by exact variable name, never by local atom index; corruption fails closed.
- DL-0096: Defeats are durable conditional attacks; defeating an attacker can restore its target, while cycles remain undecided.
- DL-0097: Explanations distinguish active minimal support paths from blocked alternatives and report deterministic direct blockers.
- DL-0098: Decision batches validate once and commit atomically; failure cannot leave a partial batch.
- DL-0100: Materialize honors the standard workspace digest fence and idempotency key.
- DL-0101 (human): The release serves fiction, removes the former private consumer, retains the lorestore, ships
.dliby default, and cuts unused work rather than archiving it in-tree. - DL-0102: Remove the unused loader-reuse compiler while retaining safe full compilation and the independently checked evaluator reuse path.
- DL-0103: Serve only contract
2.0.0; register each retired version and removed operation with its successor, preserving current replay and defeat semantics.
Rationale. The release needs a readable authoritative record while preserving its surviving engineering laws and their historical evidence.
DL-0105 — Ship text tools in the default build
Date: 2026-11-02
Attribution: machine.
Ruling. Ship the .dli parser and renderer in the default CLI and server
builds. Keep the content-addressed lorestore canonical. Provide deloreis init
to create a store-backed project and deloreis import to load its first text
world without overwriting an existing world. A deliberate
--no-default-features build remains parser-free and is checked separately.
Rationale. Fiction authors need a working text entry point and a direct path from an example document to the persistent authoring server.
DL-0106 — Surface conflicts involving proposed values
Date: 2026-11-02
Attribution: machine.
Ruling. A declared-functional, non-fluent slot with distinct live values,
at least one an authored proposal that is still possible, raises
continuity.pending-conflict at warning severity unless the slot already has a
certain authored functional-dependency violation. The warning does not assign standing,
resolve the accounts, or block materialization. Dead claims do not participate;
fluent slots remain subject to interval-sensitive checks.
Rationale. Authors need to see potential continuity debt while preserving rival accounts and leaving decisions to the caller.
DL-0107 — Correct pre-release contract fixtures
Date: 2026-11-02
Attribution: machine.
Ruling. Permit the exact registry transition identified by the two digests
in xtask/src/fixtures.rs for the fiction reset’s contract 2.0.0 workspace
fence, naming repair, and claim-ID corrections. This is a single pre-release
exception; all other fixture changes remain subject to the existing
contract-version fence. The corrected fixtures must be replayed and schema
checked.
Rationale. The reset requires these authoring fixes while retaining the owner’s decision to serve contract 2.0.0 alone.
DL-0108 — Preserve attacked support through metadata revisions
Date: 2026-11-02
Attribution: machine.
Ruling. A standing change selects a stored support for the requested claim and decision, rather than an unrelated support sharing the claim ID. An attack continues to name its exact historical carrier when metadata-only promotion replaces that carrier in the current fold. Validation requires the carrier to have been reachable when the attack was introduced and requires a current fact with the same content identity and claim ID. Corpus exchange includes any superseded carriers needed to replay those attacks, before the current folded facts.
Rationale. A retcon must not erase independent accounts, lose its recorded evidence, or prevent later metadata promotion and replay.
DL-0109 — Make question outcomes authorable and durable
Date: 2026-11-02
Attribution: machine.
Ruling. author.add_option may specify a ground implies atom. Its
option supports that outcome under the existing mutually exclusive question
semantics. Option identifiers must be distinct from other option and hypothesis names in
the world. Adding an option durably stages the replacement question; options
must be added before that question is materialized. Invalid outcomes and
colliding names are refused, and normal finding checks apply to outcome text.
Rationale. A fiction author needs rival accounts to retain separate, replayable support, without losing options on commit or aliasing alternatives.
DL-0110 — Preserve standing in era-derived chronology
Date: 2026-11-02
Attribution: machine.
Ruling. Events placed during ordered eras inherit symbolic precedence from
both memberships and the complete era-order path. Alternative paths retain
conditioned support and defeaters. A possible path must be jointly compatible
with the constraint theory. Timeline reads and fluent projections use certain
precedence; hard chronology-cycle findings require a certain cycle and retain
its supporting rulings. Possible precedence remains available to the
standing-aware order reader. These judgments re-evaluate after valuation changes
and replay from stored declarations. Permit only the exact pre-release
fixture-registry transition in xtask/src/fixtures.rs for the two recaptured
timeline responses; all other fixture changes retain the version fence.
Rationale. Story-time order must not turn a draft or defeated placement into certain chronology, hide a conflict, or lose its evidence after replay.
DL-0111 — Project fluent facts in point-in-time queries
Date: 2026-11-02
Attribution: machine.
Ruling. CLI query --as-of and dli.query with as_of project state
facts through the fluent timeline before recomputing rules. Independent timeless
support remains available. Temporal uncertainty shares compatible ordering
assumptions across facts; an unresolved inertial projection or unsupported
uncertain chronology is refused with temporal-indeterminate. A later
competitor unordered with the cutoff prevents an earlier inertial value from
reading certainly current. The point-in-time query digest binds the projected
world. This amends the earlier restriction of inertial projection to
author.as_of; author.query remains an unprojected workspace read.
Rationale. A historical query must not assert a state before it starts or retain a superseded value, and derived answers must follow the same evidence.
DL-0112 — Bind historical exports to their time point
Date: 2026-11-02
Attribution: machine.
Ruling. Exports may select an era or event through CLI --as-of or wire
as_of. The exporter projects each temporal source through the same fluent
semantics as point-in-time queries before selecting certain source atoms. It
retains independent timeless claims and source-specific support. The manifest
records and binds the cutoff. Unresolvable chronology is refused. A compiled
world lacking complete temporal source metadata must be recompiled before a
historical query or export, rather than guessing at its support.
Rationale. A historical artifact needs the values and provenance that hold at its stated point, with enough information to reproduce it after reopening.
DL-0113 — Preserve proposal bundle identities in text
Date: 2026-11-03
Attribution: machine.
Ruling. A proposed claim may carry an explicit bundle identity in the
form proposed-bundle "scope#number". It denotes the same hypothesis as the
corresponding anonymous positional proposal. The scope may be empty for a
scope-less source. The form cannot be combined with provenance tags or an
assumes condition. Its ordinal advances
the anonymous proposal counter so later proposals cannot reuse an earlier id.
Newly stored carried declarations record their positional proposal identities;
reconstruction uses the form to preserve support across scopes or numbering gaps;
if the source declaration cannot be matched unambiguously to its stored support,
reconstruction refuses instead of guessing. Language declarations use their
own counter, recorded independently on whole-pack import. Older atomless or
language declarations without a recorded identity are refused when they cannot
be matched safely; callers can reimport the original source with explicit
origins. Fact objects remain canonical.
Proposed facts, attributes and bare entities with citations also retain their source declaration in carriage, because their proposal support polynomial does not encode those citations. Carriage is content-addressed separately from the fact and preserves each authored citation through replay. An older fact object without that carriage cannot recover citations it never recorded; recovering those citations requires the original source. Carried facts and entities must have their corresponding stored fact objects; an unrelated support cannot stand in for a missing one. The existing legacy exception for era-chain tail members remains in force.
Rationale. Re-minting proposal identities while replaying a store can attach
a fictional claim to the wrong support, and dropping a # in rendered text can
silently lose that support. An explicit, parseable identity makes replay and text
round-tripping preserve the original hypotheses.
DL-0114 — Keep carried declarations aligned during decision acceptance
Date: 2026-11-03
Attribution: machine.
Ruling. When materialization accepts staged facts under a decision, any
carried declaration that regenerates a stamped fact is revised to produce that
same support. Independently cited facts retain their own support. The revised
candidate is reconstructed and checked before the ref advances. If a proposed
carried annotation has citations that cannot be retained without changing the
support, the decision-stamped operation refuses; the caller may materialize it
without stamping first. A proposed same-as declaration is likewise
materialized separately because stamping its fact would also change the
standing of the identity link. Facts implied by a question or fork option keep
their option support; accepting the surrounding batch does not choose an option.
On replay those option facts are matched by their stored fact identity, so an
independent support for the same atom is not consumed and a missing option
fact cannot be recreated from the declaration alone.
Rationale. A decision must not make a valid authored entity uncommittable or leave a carried declaration describing different evidence from its fact.
DL-0115 — Preserve origin tags that overlap proposal identities
Date: 2026-11-03
Attribution: machine.
Ruling. A declaration is carried alongside its fact when its provenance contains origin tags that the fact’s support polynomial cannot recover exactly. This includes an origin resembling a positional bundle, a joined or multi-token origin, and tags ignored by the claim’s support semantics. Reconstruction reads the authored tags from that content-addressed carriage. Existing facts that never recorded the distinction cannot recover it without the original source.
Rationale. A caller’s origin is data. A named origin that happens to look like a proposal bundle must not be silently rewritten into a different form.
DL-0116 — Refuse lossy provenance renderings
Date: 2026-11-03
Attribution: machine.
Ruling. A requested .dli rendering is checked by parsing it again and
comparing the provenance annotations with the typed source. A rendered corpus
export or explicit rendered materialization refuses when provenance would
change. A staging mutation may still store the declaration: it omits the lossy
rendered line and returns a warning, so fact objects remain usable. This does
not reserve new text keywords or change the meaning of existing documents.
Status and diff use the same check, retain structural declaration keys, and
omit unsafe rendered lines with the warning.
Rationale. Some valid typed origins cannot be expressed as one token in the current text grammar. A successful export must not silently attach a claim to a different origin.
DL-0117 — Reauthor pre-release fiction fixtures
Date: 2026-11-03
Attribution: machine.
Ruling. Permit the exact registry transition identified in the fixture checker for the reauthored contract 2.0.0 examples. Replay the fixtures and validate their schemas; all other changes remain subject to the existing contract-version fence.
Rationale. The first public release needs examples independent of a former consuming project while preserving the contract’s behavior.
DL-0118 — Report likely undeclared entity references
Date: 2026-11-03
Attribution: machine.
Ruling. The shared world check reports entity.undeclared as an advisory
finding for a likely misspelled entity or an unknown symbol in a position that
also contains a declared entity in the same scope. It carries the referenced
name, ranked candidates and source span as repair data. A warning does not
block materialization. Untyped values and unrelated abstract subjects are not
assumed to be entities.
Review rows render missing before or after facts as absent, rather than
calling them defeated. Permit the exact pre-release fixture registry transition
in the fixture checker for that correction; replay and schema checks still apply.
Rationale. CLI and authoring clients need the same actionable signal, while fictional concepts that were never declared as entities remain usable.
DL-0119 — Carry forward world independence and store retention
Date: 2026-11-03
Attribution: machine.
Ruling. The standing obligations of DL-0017, DL-0027, and DL-0077 remain in force: the engine does not bake in a world’s content; accepted records are append-only; and deleting a branch removes its pointer, not its reachable or orphaned objects. Garbage collection requires a separate decision.
Rationale. The reset’s summary omitted these still-used rules even though implementation and documentation continue to rely on them.
DL-0120 — Avoid guessing from ambiguous state values
Date: 2026-11-03
Attribution: machine.
Ruling. Qualify DL-0118: an undotted state’s value is not diagnosed as an entity reference merely because the same predicate has held a declared entity. A synthetic valueless dotted state is also excluded from this shared warning when lowering has lost the distinction from an undotted value; its authoring validation continues to check the reference separately.
Rationale. Predicate positions do not declare types, so a concept that happens to resemble an entity is not evidence of a misspelling.
DL-0121 — Correct era ordering before release
Date: 2026-11-04
Attribution: machine.
Ruling. Derive event-to-era precedence from the event’s membership and era ordering, preserving the support for each inference; only certain edges enter the certain chronology. The same chronology applies to checks, timeline reads, and point-in-time queries; an ordering cycle blocks projection. An event remains unordered relative to its own era. Recompute era-path caches written by earlier implementations. Permit the exact pre-release registry transition in the fixture checker for the two updated timeline fixtures, which must replay and pass schema validation.
Rationale. A known event in an earlier era must not make a later-era query indeterminate, and the checker must detect contradictory chronology using the same ordering as reads.
DL-0122 — Understanding before release
Date: 2026-11-04
Attribution: human.
Ruling. The owner directed:
something beyond a one off blog post has to be our target. we shouldnt write the blog post now,but we need to be sure weve got a path to people really understanding this work and putting it to use.
were doing everyhing up to the release first. and note that another agent will do the actual mesite work. it just has to file the resources for it to piick up
The owner’s picks are: flagship, “Sherlock Holmes canon”; home, “DLI Pages + mesite page”; timing, “Hold the release too”. Hold the release candidate, release publication, and macOS build until the understanding work is complete. The DLI repository supplies resources for a separate mesite agent; it does not write or publish the mesite post.
Rationale. A release needs an example and guidance that help people learn the tool and use it on their own fiction.