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.