“Don’t speak I know just what you’re sayin’ So please stop explainin’ Don’t tell me ‘cause it hurts Don’t speak I know what you’re thinkin’ I don’t need your reasons Don’t tell me ’cause it hurts” — No Doubt
Introduction
dont is a command-line tool for autonomous LLM harnesses. Its job is narrow and opinionated: stop an agent from turning ungrounded text into asserted project truth.
This book explains why the problem exists, how dont solves it, and where to find detailed specs and research.
In this repo’s workflow (not required to understand or use dont):
waihelps track workflow and design intentbdtracks concrete workdontgoverns epistemic discipline
This book is the reader-friendly front door for the project. It does not replace the fuller OpenSpec decomposition or the archived monolithic draft in wai research. Instead, it explains the problem the tool is meant to solve and points to the deeper source material in the repository, especially the tracked research notes under .wai/projects/dont/research/. These notes are part of the project’s workflow and research record managed with wai.
Read this book if you want to know
- what
dontis for - why ordinary “please double-check yourself” prompting is not enough
- how to ground repository facts with
dont ground, repository-relative locators, anddont trace - how the project’s research maps onto the proposed tool
Key sources
(Note: External links point to the main branch of the repository.)
- OpenSpec change specs (
openspec/changes/*/specs/) - Research: forcing doubt in minds and machines
- Research: designing doubt into AI interaction
- Repository README
Ecosystem
dont is part of the charly-vibes Tool Ecosystem — the map shows how it relates to its sibling tools.
Release Status
Generated artifact (docs.yml, from Cargo.toml + git ref). Do not hand-edit — the repo source files remain authoritative.
- Version: v0.4.0
- Built from: main @ 9d97a6d
Getting Started with dont
Prerequisites
Build dont from source and install it to your PATH:
git clone https://github.com/charly-vibes/dont.git
cd dont
cargo install --path .
Verify:
dont --version
When to reach for dont
The trigger is a thought that starts with “I think…”, “I believe…”, or “I’m assuming…” — combined with the fact that you haven’t verified it yet, and that being wrong would cost you time.
If you find yourself holding two competing explanations for a bug, that’s the clearest signal. dont gives those competing explanations a name, lets you track evidence for each, and forces you to resolve them before moving on.
A real session
You’re debugging a write-loss bug. Writes succeed (no errors returned) but data disappears after process restart. You have two theories.
First time? Initialize first:
dont init
Returning session? Orient first:
dont prime
This shows what claims are currently doubted or unverified — your open questions from the last session. If the store is clean, it exits 0 silently. If something is doubted, it exits 1 and tells you what to resolve first.
Step 1: Record the claim you’re investigating
dont conclude "CozoDb flushes writes lazily, causing data loss on unclean shutdown"
# → claim:01ABC123...
Step 2: Add a competing hypothesis
You’re not sure. Connection pool exhaustion could also cause silent write drops before any flush happens.
dont hypothesis add claim:01ABC123... --text "Connection pool exhaustion silently drops writes before flush"
# hypothesis added at index 0
Step 3: Gather evidence
You find the flush behavior in source:
dont flag claim:01ABC123... --file src/store.rs --lines 47-62
You check connection pool behavior and find it refutes the competing hypothesis. Hypotheses are referenced by 0-based index:
dont hypothesis assess claim:01ABC123... 0 --refuting src/pool.rs
Step 4: Confirm
Flagging evidence (dont flag) transitions the claim from unverified to verified. dont show lets you confirm the new state:
dont show claim:01ABC123...
Status is verified. The claim is grounded and the competing hypothesis is marked refuted.
Step 5: Lock when mature
dont lock requires ≥ 3 assessed hypotheses and ≥ 2 independent evidence sources. This tutorial used one hypothesis for brevity — see Competing hypotheses and atoms for the full lock pattern.
dont lock claim:01ABC123...
Lifecycle summary
| Step | Command | Status after |
|---|---|---|
| State the claim | dont conclude "..." | unverified |
| Add a competing explanation | dont hypothesis add <id> --text "..." | — |
| Attach evidence | dont flag <id> --file <path> --lines N-M | verified |
| Assess a hypothesis | dont hypothesis assess <id> <idx> --refuting <path> | — |
| Check status | dont show <id> | (read-only) |
| Freeze a mature claim | dont lock <id> | locked |
| Register doubt | dont trust <id> --reason "..." | doubted |
| Orient at session start | dont prime | exits 0 or 1 |
Fast path: one-shot grounding
When you already have both the claim and the evidence in hand:
dont ground "The crate exposes a test recipe" --file justfile --lines 12-13
This concludes and verifies in a single command.
Competing hypotheses and atoms
When you have more than one explanation
The most valuable use of dont is when you’re mid-investigation with two or more competing explanations for something you don’t yet understand.
| Use | When |
|---|---|
hypothesis | You have competing explanations — only one will survive |
atom | You have one explanation with multiple sub-conditions — all must hold |
dont hypothesis lets you record those competing explanations under a single claim and assess each one as evidence comes in. This prevents the common failure: you go looking for evidence of hypothesis A, find something that looks plausible, and declare it confirmed — without ever seriously checking hypothesis B.
Hypotheses: step by step
1. State the claim (the thing you’re investigating):
dont conclude "API calls to the pricing endpoint are timing out intermittently"
# → claim-abc
2. Add competing hypotheses:
You can add hypotheses at any point before locking — before or after gathering initial evidence. Each hypothesis is assigned a 0-based index in the order it was added.
dont hypothesis add claim-abc --text "The upstream rate limit is being hit on burst traffic"
# → index 0
dont hypothesis add claim-abc --text "A database lock contention slows queries beyond the client timeout"
# → index 1
dont hypothesis add claim-abc --text "TLS handshake overhead on cold connections is the bottleneck"
# → index 2
3. Assess each with evidence:
Both dont flag calls and --supporting assessments count as independent evidence sources toward the lockability threshold.
Hypotheses are referenced by their 0-based index (use dont show <claim-id> to see indices).
# Found rate limit logs — supports hypothesis 0
dont hypothesis assess claim-abc 0 --supporting logs/api-2026-05-07.log --lines 144-151
# Checked DB profiler — no long-running queries during timeout windows
dont hypothesis assess claim-abc 1 --refuting reports/db-profile.txt
# TLS session reuse is enabled — refuted
dont hypothesis assess claim-abc 2 --refuting src/client.rs --lines 88-94
4. Flag the surviving hypothesis as evidence for the claim:
dont flag claim-abc --file logs/api-2026-05-07.log --lines 144-151
5. Lock when mature (requires ≥ 3 assessed hypotheses + ≥ 2 independent evidence sources):
dont lock claim-abc
Atoms: when a claim has multiple independently checkable parts
Sometimes a single claim is composite — it holds if and only if several sub-conditions hold. dont atom lets you decompose it.
Example: “The API client correctly handles all three authentication scenarios” might require:
- evidence that bearer token auth succeeds
- evidence that expired token returns 401 and triggers refresh
- evidence that missing credentials return 403 without retry
dont conclude "The API client correctly handles all three authentication scenarios"
# → claim-xyz
dont atom define claim-xyz --text "Bearer token auth returns 200 on valid credentials"
# → index 0
dont atom define claim-xyz --text "Expired token triggers refresh and retries the request"
# → index 1
dont atom define claim-xyz --text "Missing credentials return 403 without retry"
# → index 2
Dismiss each atom as you verify it, using its 0-based index:
dont atom dismiss claim-xyz 0 --evidence tests/auth_test.rs --lines 12-28
dont atom dismiss claim-xyz 1 --evidence tests/auth_test.rs --lines 31-55
dont atom dismiss claim-xyz 2 --evidence tests/auth_test.rs --lines 58-74
Using dont as an LLM agent
Core pattern: conclude before asserting
Every time you are about to write something as established fact — in a reply, in a command, in a plan — and you haven’t verified it yet, use dont conclude first.
Then verify it. Then dont flag with the evidence.
This is the only discipline that matters. Everything else follows.
Trigger conditions
Use dont conclude when you find yourself producing text like:
- “The
store.rsfile handles connection pooling” - “This function is called only from the test harness”
- “The API rate limit is 500 requests per minute”
- “I believe the bug is in the flush path, not the connection path”
Each of these is an unverified claim. Record it before you act on it.
Do not use dont for
- Things you already verified before stating them (just state them)
- Universal facts not specific to this codebase (“Rust is memory-safe”)
- Housekeeping claims that will never be doubted (“the commit message is in conventional commits format”)
The overhead is worth it when being wrong would send you down a wrong path. Apply it selectively.
Session start
Always run dont prime before beginning investigation work:
dont prime
- If it exits 0: no open doubted claims. Proceed.
- If it exits 1: there are doubted claims from a previous session. Resolve them before introducing new claims.
This is your orientation step. It is not just a CI gate.
Full pattern for investigating a bug
# 1. Orient
dont prime
# 2. State what you believe (before you verify)
dont conclude "The write loss is caused by lazy flush in CozoDb"
# → claim-abc
# 3. Add competing explanations (before you go looking for evidence)
dont hypothesis add claim-abc --text "Connection pool exhaustion drops writes silently"
dont hypothesis add claim-abc --text "OS buffer not flushed on SIGTERM"
# 4. Find evidence — file + line span
dont flag claim-abc --file src/store.rs --lines 47-62
# 5. Assess hypotheses as evidence comes in (IDX is 0-based integer)
dont hypothesis assess claim-abc 0 --refuting src/pool.rs --lines 88-103
dont hypothesis assess claim-abc 1 --supporting src/shutdown.rs --lines 12-18
# 6. Check status
dont show claim-abc
What counts as evidence
Prefer repository-relative locators:
--file src/store.rs --lines 47-62 # file + line span
--file justfile --lines 12-13 # any text file in the repo
--file docs/spec.md --anchor "Flush" # anchor within a file
External URLs are accepted when the evidence lives outside the repo:
--evidence https://docs.example.com/api#rate-limits
Quick reference
dont prime # orient: any doubted claims?
dont conclude "..." # register an unverified claim
dont ground "..." --file F --lines N-M # register and verify in one step
dont flag <id> --file F --lines N-M # attach evidence to existing claim
dont hypothesis add <id> --text "..." # add a competing explanation
dont hypothesis assess <id> <idx> --refuting <path> [--lines N-M] # idx is 0-based integer
dont show <id> # check current status
dont trust <id> --reason "..." # mark as doubted (blocks prime)
dont ignore <id> --reason "..." # set aside a claim (scope change, no longer relevant)
dont lock <id> # freeze a mature verified claim
dont list # all claims and their statuses
dont trace <id> # explain why a claim is blocked
Why dont exists
The problem
Takeaway: Fluency is not epistemic discipline — and that distinction has real consequences in autonomous workflows.
Large language models are good at producing fluent answers, but fluency is not the same thing as epistemic discipline.
Once a model has started down a line of reasoning, it often tends to elaborate on that line instead of questioning it. In practice, this means an autonomous agent can:
- state a claim too early
- defend the claim after weak checking
- fold the claim into project memory or downstream actions
- create a trail of confident but poorly grounded work
The core idea behind dont is that this failure mode should not be handled as a style problem. It should be handled as a tooling problem.
The research-backed claim
Takeaway: Asking an LLM to reconsider is usually weaker than forcing it through an external verification path.
The research corpus in this repository points to a consistent conclusion:
- LLMs do not reliably self-correct just because they are asked to reconsider.
- Better results usually come from external checks such as retrieval, tests, verifiers, or independent critics.
- Durable human institutions solve similar problems by separating assertion from evaluation.
So the design stance of dont is simple:
Do not let an agent assert what it has not yet grounded.
What dont does
Takeaway: dont turns epistemic caution into explicit tool-mediated state transitions.
dont is a forcing function for autonomous LLM harnesses.
It gives the model a mechanical path between “I want to say this” and “this is now grounded enough to keep.”
Instead of letting every plausible sentence become accepted project state, dont introduces explicit transitions such as:
- conclude a claim
- define a term
- trust with a reason
- flag with evidence
- move entities through statuses like unverified, doubted, verified, or locked
The point is not to make the model sound cautious. The point is to make the workflow itself require justification.
Why a tool instead of a prompt
Takeaway: A tool enforces process outside the model’s own judgment; a prompt merely asks the same model to judge itself.
Prompting alone is weak because the same model that produced the claim is often asked to judge its own claim in the same context.
The draft spec and research notes both argue for the opposite pattern:
- make claims explicit
- record state changes
- surface unmet conditions as actionable refusals
- use fresh-context verification paths when independence matters
That is why dont is proposed as a peer CLI tool, not just a prompt template.
The role of dont in a harness
These companion tools reflect the surrounding workflow used in this repository; they are helpful context, not prerequisites for understanding dont.
A harness can use three different kinds of support:
- memory tools to remember what happened
- workflow tools to know what stage of work is happening
- epistemic tools to control what is allowed to count as grounded
dont is the third category.
It is meant to sit beside workflow and memory tools, but remain independent from them. Its concern is narrower: claims, terms, evidence, rules, and the right to assert.
Epistemic design choices
Grounding as a lifecycle shortcut
The underlying lifecycle for any claim in dont is:
conclude— introduce an unverified claimtrust— register doubtflag— verify with evidencelock— freeze a mature verified claim
dont ground is a convenience command that composes these steps. It is not a separate epistemic model; it records the claim and verifies it in one invocation while ensuring normal event history and status rules still apply.
Preference for repository-relative locators
dont prefers repository-relative locators (using --file and --lines) over opaque absolute file:// URIs. This is an intentional design choice to ensure that evidence remains:
- Readable: locators are easy for humans and agents to audit.
- Auditable: the evidence is scoped to the project root.
- Secure: absolute paths,
..traversal, and symlink escapes are refused to prevent project-evidence leaks.
Non-goals
dont is not meant to be:
- a general ontology editor
- a replacement for external validators or retrieval systems
- a memory system
- a workflow planner
- a magical truth machine
It is a guardrail and protocol layer.
A short example
An agent concludes: “The API rate limit is 500 requests per minute.”
Without dont, that sentence can quietly become accepted project state just because it sounds plausible.
With dont, the claim is recorded as unverified via dont conclude. Any attempt to promote or rely on it without evidence produces a structured refusal — one that tells the agent exactly what to do next: gather evidence, use dont flag, or request independent verification. Only after that grounding step can the claim earn a more trusted status.
That is the point of dont: not better-sounding caution, but enforced epistemic process.
Read next
Repository-grounding workflow
dont can act as a sidecar for any investigation session: claims you record are persisted to the store and survive context resets. Use it when you want repository facts to outlast a single chat session.
This page covers the three most common patterns: fast single-shot grounding, attaching evidence with repository-relative locators, and diagnosing blockers with dont trace.
Fast path: ground a documented fact
When the claim and repository evidence are both in hand, use dont ground:
dont ground "The project builds docs with mdBook" --file justfile --lines 48-49
Use repository-relative locators
For evidence inside the current project, use --file with an optional line span or anchor:
dont ground "The crate exposes a test recipe" --file justfile --lines 12-13
dont flag <id> --file src/main.rs --lines 188-205 --anchor "Ground"
Plain URI evidence is still supported for external sources:
dont flag <id> --evidence https://example.org/source
Note: URL evidence is not liveness-checked. dont verify-evidence reports URL outcomes as unchecked — live HTTP reachability checks are not yet implemented. File and git locators are fully checked.
Diagnose blockers with trace
If show, why, or prime reports stale dependencies, unresolved terms, or blocker labels without enough context, run:
dont trace <entity-id>
trace reports the blocker path so you can see which dependency or support relation needs attention before trying to verify or lock the claim.
Enforcement model
dont is designed so that there is no --no-verify equivalent — no flag that
lets a caller skip the verification gates. This page explains why, what the
actual gates are, and how to wire them into your toolchain.
Why there is no bypass flag
Git’s --no-verify works by skipping hook scripts — side-car processes that
live outside the binary. Because they’re external, they can be bypassed.
dont’s gates live inside the binary:
- State transitions are exhaustive
matcharms in Rust. The code for a forbidden transition (Doubted → Locked) simply doesn’t exist. - The dependency integrity check runs in-process before any verification is accepted. There is no code path around it.
dont primeis designed to be the terminal check — it exits 1 on doubted claims, regardless of project mode.
An agent or user can always choose not to call dont, but they cannot call it
while bypassing its rules.
The fundamental limit: this infrastructure makes it harder to accidentally
skip the check — it does not make skipping impossible. An agent that never
calls dont is invisible to these gates. The discipline works only when
agents register claims as they make them (see the
grounding workflow).
In-binary gates
1. State machine (src/model.rs)
All transitions are exhaustive matches. The only path to Locked is
Unverified → Verified → Locked. Doubted → Locked has no match arm.
2. Always-strict rules (src/rules/mod.rs)
Three rules are hardcoded to Strict severity before any config lookup:
| Rule | What it catches |
|---|---|
unresolved-terms | dependency reference (CURIE or term:uuid) that can’t be resolved |
stale-cascade | claim that depends on an unverified or doubted claim |
dangling-definition | explicit term:uuid ID reference to a term that no longer exists |
These cannot be downgraded to warnings via project config.
3. Dependency integrity gate
Before a claim can be verified with dont flag (read: “don’t flag this as a
concern”), dependency_gate_unmet_clauses()
walks all dependencies. If any are Unverified or Doubted, the command
emits a structured refusal and exits 1.
4. Hedge filter
dont trust and dont ignore reject assertions that contain hedge language
("I think", "maybe", "probably", etc.). The default list is compiled in;
config can only extend it, never replace it.
5. dont prime — orientation and terminal gate
dont prime has two roles:
Orientation — run it at the start of a session to see what’s doubted or unverified. It always prints the project status summary (mode, counts by status, and any blocking entities). When the store is clean it exits 0.
Terminal gate — run it at the end (CI, pre-commit, agent stop hook) to confirm nothing was left unresolved. It exits 1 if any claim is Doubted, regardless of project mode.
Projects initialize in permissive mode by default (pass --strict to dont init to start in strict mode). Unverified claims are allowed in permissive mode (where warnings replace errors for most rules); doubted claims are always blocking regardless of mode.
6. Lockability rule
To lock a verified claim you need ≥ 3 assessed hypotheses and ≥ 2 independent
evidence sources. This gate is enforced in-code inside the dont lock handler
and cannot be bypassed — dont lock exits 1 if the gate is unmet, regardless
of project mode or rule-engine config. (The lockable rule engine severity
defaults to Warn so it does not block non-lock operations in the background;
the hard enforcement is the in-command gate, not the background rule engine.)
Hook infrastructure
The same dont prime check runs at three points in the development cycle.
Git (prek pre-commit)
prek.toml includes a local hook that runs just check-claims (which runs
dont prime) before every commit. (prek is a pre-commit hook runner — install
it and run prek install to activate hooks.)
[[repos]]
repo = "local"
hooks = [
{ id = "dont-prime", name = "dont: block doubted claims",
entry = "just check-claims", language = "system",
pass_filenames = false, always_run = true },
]
If any claim is Doubted, the commit is rejected. Run prek install after
editing prek.toml to activate the hook.
Claude Code / agentic tools
Add a Stop hook to .claude/settings.local.json so the agent surfaces
doubted claims at the end of every session:
{
"hooks": {
"Stop": [
{
"matcher": "",
"hooks": [{ "type": "command", "command": "just check-claims" }]
}
]
}
}
When dont prime exits 1, Claude Code displays the output as an error — a
signal that the session ended with unresolved doubt. When claims are clean it
exits 0 (output is still produced but not treated as an error by Claude Code).
CI
just ci calls just check-claims, which calls dont prime. A doubted claim
fails the pipeline. See .github/workflows/ci.yml.
Scope of the gate: repo-local, opt-in (ADR)
Decision (2026-09, dont-bpuo): the epistemic gate (dont prime /
just check-claims) is repo-local and opt-in. dont ships the wiring
recipes above; each repository adopts the gate when it maintains a real claim
corpus. There is no suite-wide mandate and none is planned.
Why not suite-wide enforcement:
- A gate over no claims enforces nothing.
dont checkpasses trivially in a repository whose store has no claims — the gate only has teeth where agents actually register and ground claims. Wiring the hook into repos before the claim-creation habit exists adds friction without epistemic value (enforcement theater). - Uninitialized repos hard-fail.
dont checkexits 1 with “no .dont/ project found” outside an initialized project, so blanket pre-push wiring would block pushes from repos that never opted in. - It matches the design. dont is permissive by default; hooks are opt-in wiring, and the in-binary rules (strict transitions, dependency gate, lockability) need no cross-repo infrastructure to hold.
ah signalsis already repo-local by mechanism. It reads.dont/events/*.jsonfrom the same repository — no cross-repo claim enforcement is implied by that integration.
Adoption ladder for a suite repository:
dont initand start registering claims during development (dogfood period — see the grounding workflow).- Once the repo has real claim activity, wire
just check-claims(or thedont primeequivalent) into pre-commit/CI using the recipes above. - Document the gate in the repo’s contributor docs so the hook is not a surprise.
Note: within dont itself the gate runs at pre-commit (lefthook and
prek), not pre-push — earlier descriptions that placed it at pre-push were
inaccurate.
Research basis
- LLMs do not reliably self-correct in the same context that produced a claim.
- External signals such as evidence, tests, and independent critics are stronger.
donttranslates those conclusions into an explicit claim-handling protocol.
This page explains which research conclusions most directly shaped the design.
The documentation site is grounded in two main source categories in this repository:
- the OpenSpec change specs under
openspec/changes/*/specs/, together with the archived monolithic draft preserved inwairesearch - the tracked research artifacts under
.wai/projects/dont/research/, especially the long-form syntheses on why LLMs defend prior outputs and how institutions force better verification
Main conclusions carried into the tool design
1. In-context self-correction is weak
The research summary argues that models often rationalize earlier answers instead of reliably inspecting them. Asking the same model, in the same context, to “be more critical” often produces better-sounding prose rather than better epistemic behavior.1 2
This motivates dont’s refusal-oriented design: the system should block or route unsupported assertions instead of merely asking for a nicer explanation.
2. External signals are stronger than introspection
The strongest correction patterns in the cited literature depend on something outside the initial generation stream.1
- retrieved evidence
- tests or executable checks
- trained verifiers
- independent critics or clean-context retries
This is why the spec emphasizes evidence, explicit remediation, and spawn-style verification requests instead of plain self-reflection.
3. Institutions solve this with structure
The research notes compare LLM verification failures to long-studied human failures in science, law, medicine, and engineering. Across those domains, reliability improves when systems enforce the following patterns1 2:
- separation between generator and evaluator
- explicit burden of proof
- auditable state transitions
- procedures for challenge, review, and lock-in
dont translates that pattern into a CLI protocol for agents.
How this maps into the spec set
The current OpenSpec decomposition turns those conclusions into concrete design choices:
- a forcing-function CLI with a narrow role
- append-only history instead of silent rewrite
- explicit claim and term entities
- status transitions rather than implicit confidence
- refusal messages that tell the agent what to do next
- clean-context verification hooks when independent checking matters
Traceability
For deeper reading, start here:
- OpenSpec change specs (
openspec/changes/*/specs/) - Research: forcing doubt in minds and machines
- Research: designing doubt into AI interaction
Limits of this page
This page is a synthesis, not the full literature review. It is meant to explain why the project exists, not to replace the source documents.
Implementation status
dont has a working Rust implementation. This page summarises what is currently built, what is still in progress, and how the code maps to the OpenSpec specs.
What is implemented
All core lifecycle commands are functional:
| Command | What it does |
|---|---|
dont init | Initialise the claim store (permissive mode by default; --strict for strict mode) |
dont prime | Orient at session start; exits 1 on any doubted claim |
dont conclude | Register an unverified claim |
dont ground | Register and verify a claim in one step |
dont flag | Attach evidence to an existing claim |
dont trust | Mark a claim as doubted (blocks prime) |
dont ignore | Set a claim aside (scope change; no longer relevant) |
dont lock | Freeze a mature verified claim (requires ≥ 3 assessed hypotheses + ≥ 2 evidence sources) |
dont define | Record a project vocabulary term |
dont hypothesis add/assess | Track and assess competing explanations under a claim |
dont atom define/dismiss | Decompose a claim into independently checkable sub-conditions |
dont show | Inspect a single claim or term |
dont list | List all claims and their statuses |
dont why | Explain why a claim is in its current status |
dont trace | Walk the blocker path for a claim |
dont rules list/explain | List shipped rules and display their explanations |
The --json flag is available on all commands for machine-readable (Envelope-format) output.
How it maps to specs
The implementation is decomposed across OpenSpec specs in openspec/specs/. Key mappings:
| Spec | What it covers |
|---|---|
dont-core | Core claim lifecycle (conclude, flag, trust, lock) |
dont-data-model | Store schema and entity model |
dont-envelope | JSON Envelope and Error protocol |
dont-evidence-locators | Repository-relative file/lines/anchor syntax |
dont-status-lifecycle | Status transition rules and gate conditions |
dont-lifecycle-verbs | Command-by-command verb semantics |
dont-cli-surface | CLI flags, exit codes, output routing |
dont-errors | Structured error types and remediation messages |
dont-rule-engine | Shipped rules, severities, and config |
See Sources and status for the full source map.
What is not yet built
dont import(LinkML import surface) — spec exists inopenspec/specs/dont-linkml-import- MCP interface — spec exists in
openspec/specs/dont-mcp-interface - Spawn protocol — spec exists in
openspec/specs/dont-spawn-protocol - Completions (shell tab-completion) — wired up but not fully populated
Status
beta — core works and is dogfooded in anger; surface may shift before stable.
Implemented
| Command | Status | Notes |
|---|---|---|
dont check | stable | epistemic gate — fail on ungrounded claims |
dont conclude | stable | record grounded conclusions |
dont define | stable | define claim vocabulary/terms |
dont flag | stable | flag claims pending grounding |
dont trust | beta | trust boundaries for sources |
In progress
- Standardization rollout (docs restructure, dogfood matrix) — DDL-u8x epic.
Mapped to specs
openspec/proposals in-repo; epistemic gate ADR (dont-bpuo) enforced in vampiro’s CI too.
Dogfooding
- vampiro runs
dont checkas its epistemic gate in CI (vampiro-bf6) - testaruda pins
dont-cli@0.2.2 - dont’s own CI runs
just ci(prek, typos, vale, wai)
Sources and status
This documentation site is an overview, not the project’s only source of truth.
What each source is for
- This mdBook (
docs/) gives a reader-friendly explanation of the project’s purpose and research basis. - The OpenSpec change specs are the detailed design documents for the tool’s capabilities, decomposed by feature area.
- Tracked research notes capture the reasoning and source synthesis behind the project’s design, including the archived monolithic draft.
- OpenSpec is the structured specification workspace with top-level specs and capability-focused change proposals.
Current project status
dont has a working Rust implementation with an active CLI surface. The repository contains:
- a functional implementation (
src/) - OpenSpec change specs for planned capabilities (
openspec/changes/*/specs/) - top-level formal specs (
openspec/specs/) - tracked research artifacts explaining the design rationale (
.wai/projects/dont/research/) - this mdBook documentation (
docs/)
This book should be read as:
- an introduction to the project
- a guide to why the tool is designed this way
- a pointer to the more detailed project artifacts
How to read the project
If you are new here, read in this order:
- this book for the overview
openspec/changes/*/specs/for the detailed capability-by-capability design.wai/projects/dont/research/for the justification, research trail, and archived monolithic draftopenspec/for the broader structured specification workspace
Important distinction
The mdBook explains the project in narrative form.
The OpenSpec change specs and related artifacts define the project more precisely.
The .wai research artifacts explain why those design choices were made.
Specs
The OpenSpec specs define dont’s capabilities precisely and are the authoritative source for implementation decisions. They live in openspec/specs/ in the repository.
Core specs
| Spec | Description |
|---|---|
| dont-core | Core claim lifecycle: conclude, flag, trust, lock |
| dont-data-model | Store schema and entity relationships |
| dont-status-lifecycle | Status transitions, gate conditions, and mode enforcement |
| dont-lifecycle-verbs | Command-by-command verb semantics |
| dont-errors | Structured error types and remediation messages |
Interface specs
| Spec | Description |
|---|---|
| dont-cli-surface | CLI flags, subcommands, exit codes, output routing |
| dont-cli-core | Core CLI wiring and dispatch |
| dont-envelope | JSON Envelope and Error protocol (--json output) |
| dont-payload-types | Envelope payload schemas |
| dont-agent-help | Help text and agent-readable command descriptions |
Evidence and grounding specs
| Spec | Description |
|---|---|
| dont-evidence-locators | Repository-relative file/lines/anchor syntax |
| dont-ground-command | dont ground fast-path semantics |
| dont-trace-query | Blocker-path tracing for dont trace |
Rule engine specs
| Spec | Description |
|---|---|
| dont-rule-engine | Shipped rules, severities, and project config |
| dont-rule-cli | dont rules subcommand surface |
| dont-rule-claim-schema | Claim schema validation rule |
| dont-derived-queries | Datalog-based derived query layer |
Infrastructure specs
| Spec | Description |
|---|---|
| dont-build | Build, CI, and release pipeline |
| dont-project-layout | Repository layout conventions |
| dont-project-config | Per-project .dont/ configuration |
| dont-init-modes | Strict vs permissive initialisation modes |
| dont-glossary | Canonical terminology |
Future / in-progress specs
| Spec | Description |
|---|---|
| dont-harness-cli | Agent harness integration |
| dont-spawn-protocol | Clean-context spawn for independent verification |
| dont-mcp-interface | MCP server interface |
| dont-import-surface | External data import surface |
| dont-linkml-import | LinkML schema import |
cli-core Specification
Purpose
TBD - created by archiving change upgrade-genesis. Update Purpose after archive.
Requirements
Requirement: CLI scaffold uses Guide
The tool’s main.rs CLI setup SHALL use genesis::guide::Guide::builder()
when the tool adopts genesis::guide. Adoption is determined by the tool’s
maintainer — this spec defines the contract for how to do it.
Scenario: Guide builder replaces ad-hoc setup
- GIVEN the tool adopts
genesis::guide - WHEN
main.rsis updated to useGuide::builder(...) - THEN command handlers SHOULD return
Output<T>or useErrorSink - AND
cargo testSHALL pass
Scenario: ErrorSink for self-healing errors
- GIVEN the tool adopts
genesis::guide - WHEN a command returns an error
- THEN
ErrorSinkSHOULD print the error with a suggestion footer - AND it SHOULD write to the error scratch (for
--from-last-error)
config Specification
Purpose
TBD - created by archiving change upgrade-genesis. Update Purpose after archive.
Requirements
Requirement: Shared config management
The tool SHALL adopt genesis::config for shared config management.
Scenario: config struct implements ConfigFile
- GIVEN the tool needs config management
- WHEN the tool adopts
genesis::config - THEN the tool’s config SHALL implement
genesis::config::ConfigFile - AND all config file I/O (read, write, parse) SHALL delegate to genesis
- AND
cargo testSHALL pass
Scenario: config registered at startup
- GIVEN the tool starts up
- WHEN it initializes
- THEN it SHALL register its config struct with
ConfigRegistry - AND it SHOULD use
ConfigStorefor config discovery and validation
Scenario: dead config code removed
- GIVEN the tool has adopted genesis::config
- WHEN the old config parsing code is removed
- THEN
cargo clippySHALL introduce no new warnings - AND
cargo testSHALL pass
dont-agent-help Specification
Purpose
TBD - created by archiving change add-dont-harness-specs. Update Purpose after archive.
Requirements
Requirement: Managed agent-document block
The system SHALL own a managed documentation block describing dont usage in project docs. .dont/AGENTS.md MUST be the canonical LLM-facing document and MUST be a fully-managed file overwritten in whole by dont init and by dont doctor --fix. Root-level managed blocks in files such as AGENTS.md and CLAUDE.md MUST remain shorter pointers to .dont/AGENTS.md. The managed block boundaries MUST be delimited by the sentinel pair <!-- DONT:START --> and <!-- DONT:END -->, and the content between those sentinels MUST be treated as tool-owned and overwritable by dont doctor --fix. The root block content MUST be minimal but agent-prominent: it MUST contain (a) a clearly visible warning that the region is auto-managed and that edits inside the markers will be overwritten, (b) an actionable session-start command instructing the agent to run dont prime --json, and (c) an explicit pointer to .dont/AGENTS.md as the canonical document. The root block MUST NOT contain the full canonical instructions; the full instructions live only in .dont/AGENTS.md.
Scenario: managed block points to canonical docs
- WHEN
dontrenders the managed block into a project document - THEN the block tells the reader to run
dont prime --jsonat session start - AND it points to
.dont/AGENTS.mdas the canonical document - AND it does not duplicate the full canonical instructions inline
Scenario: managed block marks overwrite boundary
- WHEN a project document contains the managed block markers
<!-- DONT:START -->and<!-- DONT:END --> - THEN the content between those markers is treated as tool-owned and overwritable by
dont doctor --fix
Scenario: managed block carries a prominent do-not-edit warning
- WHEN an agent or operator reads a root document containing the managed block
- THEN the block opens with a visible warning that the region is auto-managed
- AND the warning is prominent enough that an agent scanning the file cannot reasonably skip the session-start instruction
Scenario: canonical file is fully managed
- WHEN
dont initordont doctor --fixrewrites.dont/AGENTS.md - THEN the entire file is replaced with the generator output
- AND the file’s header declares it is managed by
dontand must not be hand-edited
Requirement: Orientation prompt contract
The system SHALL provide a minimum-viable orientation prompt for LLM sessions. The orientation text MUST instruct the LLM to use --json, distinguish the core verbs from lifecycle verbs, explain permissive versus strict mode, require remediation-driven recovery on refusal, require harness fulfilment of spawn requests, recommend dont suggest-term before define, recommend supplying --label "<a noun phrase>" alongside --doc when coining terms (noting that the label is shape-checked and appears in diagrams), and point to dont help --tutorial for the full teaching walkthrough.
Scenario: refusal guidance in orientation block
- WHEN the reader consults the orientation block
- THEN it instructs them to read
data.remediation[0].commandand run it rather than guessing reformulations
Scenario: spawn guidance in orientation block
- WHEN the orientation block describes
spawn_requestenvelopes - THEN it tells the reader to invoke the harness subagent mechanism rather than performing the verification in the original session
Scenario: label coining guidance in orientation block
- WHEN the reader consults the orientation block
- THEN it explicitly recommends passing
--label '<a noun phrase>'alongside--docwhen coining terms - AND it explains that the label is what appears in diagrams and is shape-checked
- AND the guidance appears between the
suggest-termrecommendation line and thedont help --tutorialpointer
Scenario: orientation points to deeper docs
- WHEN the orientation block reaches the end of its quick-start guidance
- THEN it points to
dont help <cmd>,.dont/AGENTS.md,dont help --tutorial, anddont help --howto <topic>for more detail
Requirement: Help and teaching entry points
The system SHALL provide dont help as the primary agent-addressed help surface. Bare dont help MUST list the available commands and major help entry points. dont help <cmd> and <cmd> --help MUST route to the same command-specific help content. dont help --tutorial MUST print the first-session tutorial, dont help --howto <topic> MUST print a goal-oriented how-to guide, and dont help --topics MUST list the available tutorial and how-to topics.
Scenario: bare help lists commands and entry points
- WHEN the caller runs
dont help - THEN the output lists the available commands and the major tutorial/how-to entry points
Scenario: subcommand help routing matches help verb
- WHEN the caller runs
dont lock --help - THEN the output matches the content of
dont help lock
Scenario: tutorial help entry point
- WHEN the caller runs
dont help --tutorial - THEN the output is the sequenced first-session walkthrough rather than per-command reference text
Scenario: how-to topic selection
- WHEN the caller runs
dont help --howto harness-integration - THEN the output is the corresponding goal-oriented guide if that topic exists
Scenario: help topics listing
- WHEN the caller runs
dont help --topics - THEN the output lists the available tutorial and how-to entry points
Requirement: Canonical teaching artifacts
The system SHALL ship normative teaching artifacts beyond command reference. It MUST include a worked example showing the canonical define → conclude → spawn → flag → lock flow, a first-session tutorial that explains why each step is taken, and goal-oriented how-to guides for project-specific rule authoring, harness integration, and .dont/ store recovery.
Scenario: worked example teaches canonical flow
- WHEN the caller reads the worked example artifact
- THEN it shows a representative session beginning with
dont prime --json - AND it demonstrates spawn-based verification rather than self-verification
Scenario: tutorial emphasises orient-search-coin-conclude-spawn loop
- WHEN the caller reads the first-session tutorial
- THEN it presents the workflow as a sequenced learning path rather than as isolated command reference entries
Scenario: how-to corpus covers the three named operator goals
- WHEN the caller browses the how-to guides
- THEN the corpus includes guides for authoring a project-specific rule, integrating
dontinto a new harness, and recovering a corrupted.dont/store
Requirement: Help teaches structured repository evidence as the preferred grounding mode
The system SHALL teach structured repository evidence as the preferred mode for grounding repository facts. Operator-facing help and tutorials MUST recommend repository-relative file locators over opaque absolute file:// paths when the evidence source is inside the current project, while still noting that URI-only evidence remains supported for compatibility.
Scenario: tutorial recommends repository-relative evidence
- WHEN the caller reads the grounding-oriented tutorial or how-to material
- THEN the examples prefer repository-relative evidence locators over absolute
file://paths for project files
Scenario: compatibility path remains documented
- WHEN the help text describes repository evidence locators
- THEN it still notes that plain URI-only evidence remains supported as a compatibility path
Requirement: Help positions ground as the fast path for documented repository facts
The system SHALL teach dont ground as the fast path for recording a documented repository fact when the operator already has both the claim text and its supporting evidence. This teaching MUST preserve the core four verbs as the canonical underlying model rather than implying that ground replaces them conceptually.
Scenario: tutorial presents ground as sidecar fast path
- WHEN the caller reads the first-session tutorial or a repository-grounding how-to
- THEN the material may recommend
dont groundfor quick documented-fact capture while still explaining that it composes the underlyingconcludeandflagsemantics
Requirement: Help recommends trace when blocker labels are not enough
Operator-facing help and tutorials SHALL recommend dont trace <entity-id> as the next diagnostic step when a claim or term is blocked by dependency/support fallout and show or why alone do not explain the causal path clearly.
Scenario: blocked verification guidance points to trace
- WHEN the tutorial or a refusal-oriented how-to explains what to do after seeing a blocker such as
staleorunresolved-term - THEN it recommends
dont trace <entity-id>as the path-oriented diagnostic command
Requirement: Managed-docs staleness check
The system SHALL include a managed-docs staleness check in dont doctor. The check MUST regenerate the expected content for the root managed block and for .dont/AGENTS.md from the current project state, normalize both expected and on-disk content by converting line endings to \n and trimming trailing whitespace, compare the normalized outputs exactly (between the sentinels for the root block; whole-file for .dont/AGENTS.md), and report a warn status when any configured target diverges. The check MUST be read-only and MUST NOT modify any file.
Scenario: clean project produces an ok check
- WHEN the caller runs
dont doctor --jsonon a project whose managed blocks match the generator output - THEN the doctor envelope reports the managed-docs check with
status: "pass" - AND no file on disk is modified
Scenario: edited block produces a warn check
- WHEN a configured root managed block diverges from the generator output (for example, an operator has edited content between the sentinels)
- AND the caller runs
dont doctor --json - THEN the doctor envelope reports the managed-docs check with
status: "warn" - AND the check’s remediation entry instructs the operator to run
dont doctor --fix - AND no file on disk is modified
Scenario: missing block produces a warn check
- WHEN a configured root document exists but does not contain the
<!-- DONT:START -->/<!-- DONT:END -->markers - AND the caller runs
dont doctor --json - THEN the doctor envelope reports the managed-docs check with
status: "warn" - AND the remediation entry instructs the operator to run
dont doctor --fixto inject the block
Scenario: canonical file drift produces a warn check
- WHEN
.dont/AGENTS.mddiffers from the current generator output - AND the caller runs
dont doctor --json - THEN the doctor envelope reports the managed-docs check with
status: "warn" - AND the remediation entry instructs the operator to run
dont doctor --fix
Scenario: whitespace-only drift does not produce a warn
- WHEN the only difference between on-disk content and generator output is trailing whitespace or
\r\nversus\nline endings - AND the caller runs
dont doctor --json - THEN the managed-docs check reports
status: "pass"
Requirement: Managed-docs repair via doctor --fix
The system SHALL provide a --fix flag on dont doctor that rewrites the managed block in each configured root document and overwrites .dont/AGENTS.md with the current generator output. --fix MUST preserve content outside the managed block markers byte-for-byte in every configured root document. --fix MUST be idempotent with respect to detected project state: running dont doctor --fix twice in succession with no intervening change to the inputs the generator reads (configured managed_docs targets, dont version, installed rules, project mode) MUST produce identical files after the first run. Without --fix, dont doctor MUST remain read-only and MUST NOT touch any file.
Scenario: fix rewrites only the managed region
- WHEN the caller runs
dont doctor --fixon a project whose rootAGENTS.mdhas stale content between the managed markers - THEN the command rewrites the bytes between the
<!-- DONT:START -->and<!-- DONT:END -->markers with the current generator output - AND all bytes outside those markers in that file are preserved exactly
Scenario: fix overwrites the canonical file
- WHEN the caller runs
dont doctor --fixand.dont/AGENTS.mddiffers from the current generator output - THEN the command overwrites
.dont/AGENTS.mdin whole with the current generator output
Scenario: fix is idempotent
- WHEN the caller runs
dont doctor --fixand then immediately runsdont doctor --fixagain with no intervening change to project state - THEN the second invocation makes no further byte-level changes to any managed file
Scenario: doctor without fix is read-only
- WHEN the caller runs
dont doctorordont doctor --jsonwithout--fix - THEN no file on disk is modified, regardless of how many managed-docs checks report
warn
Scenario: fix produces output identical to init
- WHEN a project is freshly initialized by
dont initand thendont doctor --fixis run with no other state changes - THEN
dont doctor --fixmakes no byte-level changes to any managed file - AND the managed-docs check subsequently reports
status: "pass"
Scenario: fix injects the block when missing
- WHEN the caller runs
dont doctor --fixon a project whose rootAGENTS.mdexists but does not contain the managed-block sentinels - THEN the command injects the managed block into the file using the same placement rules
dont inituses - AND all preexisting content in the file is preserved exactly outside the inserted block
dont-build Specification
Purpose
TBD - created by archiving change implement-tracer-bullet. Update Purpose after archive.
Requirements
Requirement: Single Binary Distribution
The dont tool SHALL be distributed as a single binary with no runtime dependencies beyond the host operating system. Auxiliary import adapters (e.g., dont import linkml) MAY require separately installed tools; the core binary itself SHALL not.
Scenario: Binary executes without external runtime
- WHEN the
dontbinary is placed on a system without Rust, CozoDB, or anydont-specific runtime installed - THEN
dont --versionSHALL execute successfully and print a version string
Scenario: Core commands work without auxiliary tools
- WHEN a project uses only core verbs (
conclude,define,trust,dismiss) and query commands (show,list) - THEN no tool beyond the
dontbinary is required
Requirement: Embedded Storage Engine
The dont tool SHALL embed its storage engine within the binary, requiring no external database process or network connection for local operations.
Scenario: Database created locally without network
- WHEN
dont initis run in a directory with no priordontproject and no network connectivity - THEN a local database SHALL be created under
.dont/and the command SHALL succeed
Scenario: No external database process required
- WHEN
dontcommands are executed on an initialized project - THEN no external database process (e.g., PostgreSQL, Redis) SHALL be required for the commands to function
Requirement: Cold Start Performance
The dont tool SHALL achieve sub-50ms cold start for read-only operations on projects with fewer than 10,000 claims.
Scenario: Fast read on small project
- WHEN
dont list --jsonis run on a project containing 100 claims - THEN the command SHALL complete (exit) within 50 milliseconds of process start
Scenario: Fast read on medium project
- WHEN
dont show <id> --jsonis run on a project containing 5,000 claims - THEN the command SHALL complete within 50 milliseconds of process start
dont-cli-core Specification
Purpose
TBD - created by archiving change add-core-dont-specs. Update Purpose after archive.
Requirements
Requirement: Four primary epistemic verbs
The system SHALL define conclude, define, trust, and flag as the primary CLI verbs for driving claims and terms through the epistemic workflow. dismiss is a deprecated alias for flag retained for backward compatibility; new documentation and help text SHALL use flag.
Scenario: core CLI surface is limited to four primary verbs
- WHEN the specification describes the core CLI
- THEN it presents
conclude,define,trust, andflagas the primary epistemic verbs
Requirement: CLI verbs are interpreted in phrase form
The system SHALL define core CLI verb semantics in the phrase context dont <verb> rather than by the standalone English verb alone, and SHALL require command help and documentation to explain verbs in that phrase form.
Scenario: trust is read as do not trust
- WHEN the specification or help text explains
trust - THEN it explains
dont trustas an instruction to register doubt rather than to endorse
Scenario: phrase form is required in command help
- WHEN the core CLI documents a primary verb
- THEN the documentation explains the meaning of the full phrase such as
dont flagordont conclude
Requirement: Conclude introduces claims
The system SHALL let conclude introduce a claim with statement content and may allow structured metadata such as atoms, references, confidence, dependencies, session identity, and author identity.
Scenario: conclude creates an unverified claim
- WHEN an actor invokes
concludewith a statement - THEN the tool creates a claim entity
- AND the entity enters the
unverifiedstate as defined by the lifecycle specification
Scenario: conclude may accept unresolved claim references in permissive mode
- WHEN the project operates in a permissive mode that allows unresolved claim references at creation time
- THEN
concludemay still create theunverifiedclaim while recording that verification is blocked until those references are resolved
Scenario: conclude may refuse unresolved claim references in strict mode
- WHEN the project operates in a strict mode that forbids unresolved claim references at creation time
- THEN
concludeis refused until those references are resolved
Scenario: conclude refuses duplicate claim creation
- WHEN an actor invokes
concludefor a claim that is equivalent to an existing claim under the project’s deduplication rules - THEN the command is refused instead of creating a second claim identity
Requirement: Define introduces coined terms
The system SHALL let define introduce a project term with a CURIE and prose definition. define SHALL accept an optional --label "<noun phrase>" flag carrying the SK11 type-text — the singular indefinite noun phrase that would appear inside the box in an olog diagram (e.g. "a Ricci tensor"). When --label is supplied, the five verb-level shape validators (see “Define label shape validators” below) fire on it before any rule evaluation. When --label is absent, a warn-level subset of those validators fires on a leading-phrase extraction of --doc (the substring up to the first ., ?, !, or ;, trimmed, and capped at 80 characters or the first 15 tokens, whichever is shorter) without refusing the call.
Scenario: define creates an unverified term
- WHEN an actor invokes
definewith a CURIE and definition - THEN the tool creates a term entity
- AND the entity enters the
unverifiedstate as defined by the lifecycle specification
Scenario: define accepts an optional label
- WHEN an actor invokes
definewith both--docand--label - THEN the label is stored as the SK11 type-text and the shape validators fire on it
Scenario: define without label emits doc-extraction warnings, not refusals
- WHEN an actor invokes
definewithout--labeland the leading phrase of--docfails a shape check - THEN the command succeeds and the envelope carries a
term-doc-shape-*warning rather than refusing
Scenario: define refuses unresolved referenced terms
- WHEN an actor invokes
definewith parent, related, or attribute references that do not resolve - THEN the command is refused
Scenario: define may append a redefinition for an existing coined CURIE
- WHEN an actor invokes
definefor a CURIE that already exists as a coined project term - THEN the command may append a new definition event for that CURIE instead of creating an unrelated duplicate term
Requirement: Trust records explicit doubt
The system SHALL let trust move a non-terminal claim or term into the doubted state and SHALL require a non-empty, substantive reason for doing so.
Scenario: trust requires a reason and produces doubt
- WHEN an actor invokes
truston a non-terminal entity with a substantive reason - THEN the entity transitions to
doubted - AND the reason is recorded in history
Scenario: trust refuses hedge-style reasons
- WHEN an actor invokes
trustwith a vague hedge pattern or other low-information reason - THEN the command is refused
Scenario: trust refuses terminal entities
- WHEN an actor invokes
truston a locked or ignored entity - THEN the command is refused
Requirement: Flag verifies through evidence
The system SHALL let flag serve as the core CLI path into verified status for a claim or term using one or more evidence references, subject to deterministic local refusal conditions. dismiss is a deprecated alias for flag that emits a deprecation warning and behaves identically; all new documentation SHALL use flag.
Scenario: flag verifies an eligible claim or term with evidence
- WHEN an actor invokes
flagon an eligible claim or term with one or more evidence references - THEN the command is allowed to transition the target toward
verifiedaccording to the lifecycle rules
Scenario: flag may append evidence to an already verified entity
- WHEN an actor invokes
flagon an alreadyverifiedclaim or term with additional evidence references - THEN the command may append evidence to that entity’s history without creating a new identity or requiring a status change
Scenario: flag refuses when evidence is absent
- WHEN an actor invokes
flagwithout any evidence references - THEN the command is refused
Scenario: flag refuses terminal entities
- WHEN an actor invokes
flagon a locked or ignored entity - THEN the command is refused
Scenario: flag refuses malformed evidence references
- WHEN an actor invokes
flagwith evidence references that are malformed URIs or unresolved CURIE prefixes - THEN the command is refused
Scenario: flag refuses when referenced terms remain unresolved
- WHEN an actor invokes
flagfor a claim or term whose required referenced terms do not resolve - THEN the command is refused
Scenario: flag may require atom completion before claim verification
- WHEN a claim has declared atoms and at least one atom remains unverified or doubted
- THEN whole-claim verification may be refused until the atom-level verification requirements are satisfied
Scenario: dismiss deprecated alias emits warning
- WHEN an actor invokes
dismissinstead offlag - THEN the command succeeds with identical behavior to
flagbut emits a deprecation warning to stderr
Requirement: Define label shape validators
The system SHALL apply five deterministic, network-free validators to --label when it is supplied, in listed order, before any rule evaluation. The validators are individually disableable globally via the [define.shape] project configuration block and are NOT overridable per invocation. The first failure SHALL refuse the command and skip the remainder. Each refusal SHALL carry an error envelope with non-empty remediation[] pointing at the specific fix. The validators SHALL also fire at warn-level on the leading-phrase extraction of --doc when --label is absent, except for term-compound-undeclared which does not fire on doc extractions.
-
term-label-empty— the label is the empty string or whitespace-only after trim. -
term-shape-indefinite— the label does not begin withaoranfollowed by whitespace. Regex floor:^(a|an)\s+\S, case-insensitive. Source: SK11 §2.1.1(i). -
term-shape-punctuated— the label ends in.,?,!,;, or:. Regex floor:[.?!;:]\s*$. Source: SK11 §2.1.1(iv). -
term-compound-undeclared— the label starts with a compound marker (a pair,a triple,a quadruple,a sequence,a tuple,a set of,a list of, case-insensitive) and does not contain a valid parenthesised variable list before the firstwhereor end-of-string. For fixed-arity markers the list must contain exactly the required number of variables (a pair→ 2,a triple→ 3,a quadruple→ 4). For open-arity markers (a sequence,a tuple,a set of,a list of), the list must contain at least one variable. Source: SK11 §2.1.1(v). -
term-label-sentence— the label containsis,are,has,have,does,do,was, orwereas whitespace-bounded tokens outside a parenthesised variable declaration or awhere-clause (the text following the first occurrence of the tokenwherethat appears after a parenthesised variable list). Source: SK11 §2.1 and §2.2.1(ii), which reserves verb-led sentence shape for aspects.
Scenario: empty label is refused
- WHEN an actor invokes
definewith--label "" - THEN the command is refused with
code: "term-label-empty" - AND
remediation[0]suggests supplying a non-empty noun phrase
Scenario: label without article is refused
- WHEN an actor invokes
definewith--label "Ricci tensor" - THEN the command is refused with
code: "term-shape-indefinite" - AND
remediation[0]suggests prependingaoran
Scenario: sentence-punctuated label is refused
- WHEN an actor invokes
definewith--label "a Ricci tensor." - THEN the command is refused with
code: "term-shape-punctuated" - AND
remediation[0]suggests removing the trailing punctuation
Scenario: compound label without variable declaration is refused
- WHEN an actor invokes
definewith--label "a pair of integers"(no variable list) - THEN the command is refused with
code: "term-compound-undeclared" - AND
remediation[0]suggests a form like"a pair (x, y) where x and y are integers"
Scenario: sentence-shaped label is refused
- WHEN an actor invokes
definewith--label "a tensor that is Ricci" - THEN the command is refused with
code: "term-label-sentence" - AND
remediation[0]suggests rephrasing as a noun phrase without a verb token
Scenario: validators fire in order and stop at first failure
- WHEN an actor invokes
definewith a label that failsterm-shape-indefinite - THEN only the
term-shape-indefiniteerror is returned and subsequent validators are not evaluated
Scenario: well-formed label passes all validators
- WHEN an actor invokes
definewith--label "a Ricci tensor"and valid--doc - THEN all five validators pass and the term is created in
:unverified
Scenario: well-formed label with ‘an’ passes all validators
- WHEN an actor invokes
definewith--label "an amino acid found in dairy"and valid--doc - THEN all five validators pass and the term is created in
:unverified
Scenario: doc-extraction check fires warn not refuse
- WHEN an actor invokes
definewithout--labeland--doc "Ricci tensor. Extended explanation..." - THEN the command succeeds
- AND the envelope carries a
term-doc-shape-indefinitewarning
Scenario: doc-extraction does not fire compound check
- WHEN an actor invokes
definewithout--labeland--docthat starts witha pair of integers - THEN the
term-compound-undeclaredvalidator does not fire on the doc extraction
Scenario: verb token in where-clause does not fire term-label-sentence
- WHEN an actor invokes
definewith--label "a pair (x, y) where x is an integer and y is a real number" - THEN the
term-label-sentencevalidator does not fire because the verb tokens appear inside awhere-clause following a parenthesised variable list
Requirement: feedback subcommand
dont SHALL provide a feedback subcommand that files a structured issue against dont’s upstream repo via gh, wrapping genesis::feedback for the redactor, context-bundle, error-scratch, and gh-invocation machinery.
Scenario: agent files a bug with last error
- WHEN
dont feedback bug --from-last-error --yesis run after a non-zero exit - THEN dont SHALL read its own error scratch (
$XDG_CACHE_HOME/dont/errors.jsonl) - AND SHALL assemble and redact the body via
genesis::feedback - AND SHALL invoke
gh issue createagainst dont’sCargo.tomlrepositorywith labelsagent-reported,bug,has-repro.
Scenario: error with no self-healing fix
- WHEN dont exits non-zero and no
genesis::suggestions::Fixis available - THEN the error footer SHALL print
Feedback: dont feedback bug --from-last-error.
dont-cli-surface Specification
Purpose
TBD - created by archiving change add-dont-envelope-specs. Update Purpose after archive.
Requirements
Requirement: Universal flags on every subcommand
The system SHALL accept the following flags on every subcommand, parsed before subcommand-specific flags and never conflicting with them: --help (-h), --version, --json (-j), --plain, --author <id> (-a), and --direct. Universal flags SHALL be recognised in any position relative to positional arguments (GNU-style interspersed parsing).
Scenario: author flag overrides identity
- WHEN a subcommand is invoked with
--author <id>or-a <id> - THEN the author string for that invocation is the provided value instead of the default derived from
$DONT_AUTHORor$USER
Scenario: json flag emits structured envelope
- WHEN any subcommand is invoked with
--jsonor-j - THEN stdout contains only the JSON envelope per the
dont-envelopecontract and human logging moves to stderr
Scenario: human mode is default
- WHEN a subcommand is invoked without the
--jsonflag - THEN the output is human-readable text on stdout
- AND it follows the ANSI colour settings from terminal awareness
Scenario: plain flag suppresses formatting
- WHEN a subcommand is invoked with
--plain - THEN the output is human-readable text but without ANSI colours or terminal-specific formatting (e.g. for logging to a file)
Scenario: direct flag bypasses harness
- WHEN a subcommand is invoked with
--direct - THEN the tool behaves as if
DONT_DIRECT=1is set, ignoring any parent harness presence (e.g. skipping extra harness-facing hints or envelopes)
Requirement: No short-flag conflicts
The system SHALL ensure that subcommand-specific short flags do not collide with the universal short flags (-h, -j, -a), and SHALL document the full short-flag mapping via dont help <cmd> and the shell-completion generator. The widely reused per-command short flags are -r for --reason (on trust, undoubt, ignore) and -e for --evidence (on flag).
Scenario: help and completions document short flags
- WHEN
dont help <cmd>or the shell-completion generator is queried for a subcommand - THEN the output includes the complete short-flag mapping for that subcommand
Requirement: Colour and terminal awareness
The system SHALL colour human-mode output by default when stdout is a terminal, SHALL honour the NO_COLOR convention (disabling colour when NO_COLOR is set to any non-empty value, stdout is not a terminal, or --plain is passed), and SHALL support CLICOLOR_FORCE=1 to force colour even when stdout is redirected. Explicit --plain takes precedence over CLICOLOR_FORCE.
Scenario: colour enabled on terminal stdout
- WHEN stdout is a terminal and
NO_COLORis not set and--plainis not passed - THEN human-mode output includes ANSI colour codes
Scenario: colour disabled when stdout is not a terminal
- WHEN stdout is redirected to a file or pipe and
NO_COLORis not set and--plainis not passed andCLICOLOR_FORCEis not set - THEN output contains no ANSI colour codes
Scenario: colour disabled when NO_COLOR is set
- WHEN
NO_COLORis set to a non-empty value - THEN output contains no ANSI colour codes
Scenario: colour forced with CLICOLOR_FORCE
- WHEN
CLICOLOR_FORCE=1is set and stdout is redirected - THEN output includes ANSI colour codes despite redirection
Scenario: plain flag overrides CLICOLOR_FORCE
- WHEN both
--plainandCLICOLOR_FORCE=1are set - THEN output contains no ANSI colour codes
Requirement: Stdin piping for bulk entity operations
The system SHALL accept entity IDs from stdin when the ID argument is -, reading one ID per line, emitting one envelope per line in --json mode (NDJSON), and processing each line as an independent command invocation with its own transaction boundary. Lines SHALL be stripped of leading and trailing ASCII whitespace (including \r) before validation. A line that is empty after stripping SHALL be silently skipped. Invalid IDs produce an error envelope for that line and processing continues.
Scenario: entity IDs read from stdin
- WHEN a command receives
-as the entity ID argument - THEN it reads one entity ID per line from stdin and processes each independently
Scenario: stdin mode emits NDJSON
- WHEN a stdin-piped command runs with
--json - THEN each processed entity produces one envelope per line on stdout
Scenario: invalid ID in stdin produces per-line error
- WHEN an invalid entity ID appears in stdin
- THEN the command emits an error envelope for that line and continues processing remaining lines
Scenario: whitespace and CR/LF handled on stdin lines
- WHEN a stdin line contains leading/trailing whitespace or a trailing
\r - THEN the whitespace is stripped before validation
Scenario: stdin exit code is highest severity across all lines
- WHEN a stdin run completes with mixed results
- THEN the exit code follows the
dont-errorscontract with highest-severity-wins precedence:4>3>2>1>0
Requirement: Stdin-accepting verbs
The system SHALL support stdin piping for the following verbs: show, why, trust (with --reason), flag (with --evidence), ignore (with --reason), lock, reopen, verify-evidence. dismiss is a deprecated alias for flag that also accepts stdin. The system SHALL NOT support stdin piping for conclude or define (which take domain content, not just IDs), nor for list or vocab (which are sources, not sinks). Verbs not yet specified by a capability spec (show, why, list, vocab) are listed here for completeness and will be defined in future capabilities.
Scenario: show accepts stdin IDs
- WHEN
dont show -is invoked with entity IDs on stdin - THEN each entity is looked up and its envelope emitted
Scenario: conclude rejects stdin
- WHEN
dont conclude -is invoked - THEN the command exits
2with ausageerror; it does not read from stdin
Requirement: Shell completion generation
The system SHALL provide dont completions <shell> that prints a shell-completion script for bash, zsh, fish, powershell, and elvish in each shell’s native format, covering subcommands, universal flags, subcommand flags, and enum-valued flags (including at minimum status values, mode values, and shell names). The completions subcommand SHALL ignore --json and always emit the raw shell script to stdout.
Scenario: completions generate valid shell script
- WHEN
dont completions fishis invoked - THEN stdout contains a valid fish completion script with
complete -centries
Scenario: completions for unknown shell exits with usage error
- WHEN
dont completions <unknown-shell>is invoked - THEN the process exits
2with ausageerror listing supported shell names
Scenario: dynamic entity ID completion with bounded latency
- WHEN a completion script queries the local store for entity IDs matching a prefix
- THEN the query completes within a 10ms timeout or exits with no dynamic candidates rather than blocking the shell prompt
Requirement: Help surface structure
The system SHALL provide dont help and dont help <cmd> as the primary agent-addressed help surface, dont help --tutorial for the walkthrough, dont help --howto <topic> for how-to guides, and dont help --topics to list available tutorials and how-to guides. Help output follows a Diataxis structure: tutorial, how-to, and reference modalities.
Scenario: bare help lists available commands
- WHEN
dont helpis invoked with no arguments - THEN the output lists all available subcommands with brief descriptions
Scenario: command help shows usage and flags
- WHEN
dont help <cmd>is invoked - THEN the output is written to stdout and includes the command’s usage line, flags (including short flags), and a description
Scenario: tutorial help prints the walkthrough
- WHEN
dont help --tutorialis invoked - THEN the output prints the canonical tutorial walkthrough
Scenario: topics lists available help entries
- WHEN
dont help --topicsis invoked - THEN the output lists all available tutorials and how-to guides
Requirement: Ground is exposed as a standard subcommand
The system SHALL expose ground through the standard command surfaces used for other verbs. Bare help, command-specific help, and shell completions MUST list ground as an available subcommand with its statement-plus-evidence purpose.
Scenario: bare help lists ground
- WHEN the caller runs
dont help - THEN the available subcommands include
groundwith a brief description of one-shot claim grounding
Scenario: completions include ground
- WHEN the caller generates shell completions
- THEN the completion script includes
groundamong the available subcommands
Requirement: Ground does not participate in stdin ID bulk mode
The system SHALL treat ground like conclude and define for stdin bulk semantics: it takes domain content rather than an entity ID sink and therefore SHALL NOT read entity IDs from stdin via -.
Scenario: ground rejects stdin ID bulk mode
- WHEN
dont ground -is invoked - THEN the command exits with a
usageerror rather than reading entity IDs from stdin
Requirement: Entity ID resolution and ergonomics
The system SHALL resolve entity identifiers provided as arguments by checking against internal ULIDs (with optional claim:/term: prefixes), registered CURIEs for coined terms, and unique ULID prefixes.
The system SHALL apply the following resolution priority to resolve ambiguity:
- Full ULID Match: Exact match against a prefixed or unprefixed ULID.
- CURIE Match: Exact match against a registered term CURIE.
- Short-ID Prefix: Unique prefix match against a ULID.
If an identifier is ambiguous within a priority level (e.g. matches multiple CURIEs or multiple ULID prefixes), the command SHALL be refused with a multiple-matches error.
Scenario: lookup by CURIE
- WHEN
dont show WB:P001is invoked - THEN the system resolves
WB:P001to its internalterm:ULIDand displays the term
Scenario: lookup by short ULID
- WHEN
dont show 01KQZis invoked and only one entity starts with that prefix - THEN the system resolves the prefix to the full entity ID and proceeds
Scenario: kind-prefixing disambiguates short IDs
- WHEN
dont show claim:01KQZis invoked - THEN only claim entities are searched for the prefix match
Scenario: ambiguous identifier produces error
- WHEN an identifier matches multiple entities
- THEN the command exits
1with an error listing the ambiguous matches
Requirement: Stdin ID piping support
The system SHALL support - as an identifier argument to read a newline-delimited list of entity IDs or CURIEs from stdin. This allows bulk operations across commands like show, trust, flag, undoubt, ignore, and reopen.
Scenario: bulk show via stdin
- WHEN
echo "claim:1\nclaim:2" | dont show -is invoked - THEN the system displays each entity in sequence
- AND exits non-zero if any single resolution fails (unless a
--forceflag is specified)
dont-core Specification
Purpose
TBD - created by archiving change add-core-dont-specs. Update Purpose after archive.
Requirements
Requirement: Epistemic forcing-function purpose
The system SHALL define dont as a project-local command-line tool for managing epistemic entities and their statuses, whose purpose is to interrupt unsupported assertions and require grounding before claims or terms may become verified.
Scenario: tool purpose is framed around grounding before verification
- WHEN a project adopts
dont - THEN the tool is specified as enforcing doubt and grounding rather than task tracking, workflow orchestration, generic knowledge-base authoring, or acting as a truth oracle
Requirement: Core entity scope
The system SHALL treat claims and coined terms as the only first-class core epistemic entities in this initial capability split.
Scenario: core entity set is limited to claims and terms
- WHEN the core specification defines the primary objects managed by
dont - THEN it names claims and terms as the first-class entities
- AND any additional entity kind requires a future spec change instead of being implicitly introduced
Requirement: Tool independence
The system SHALL specify dont, wai, and beads/bd as independent tools that may share conventions but do not require shared code, shared configuration, or a shared runtime.
Scenario: companion tool roles remain distinct
- WHEN the project describes how
dontinteracts with companion tools - THEN
waiis treated as workflow context,beads/bdas memory or issue tracking, anddontas epistemic discipline
Scenario: dont can operate without companion-tool runtime coupling
- WHEN
dontis executed in a project that follows the shared conventions - THEN its specification does not require
waiorbeads/bdto be linked into the same runtime or configuration surface
Scenario: integrations cannot redefine core semantics
- WHEN a companion tool or harness integrates with
dont - THEN the integration may enrich workflow behavior
- AND it does not redefine the meaning of core entities, persisted statuses, or history semantics
Requirement: Append-only event history
The system SHALL represent state changes as append-only events and SHALL forbid destructive deletion or silent overwrite as normal state-management mechanisms.
Scenario: retraction is recorded instead of deleting history
- WHEN a claim or term is challenged, superseded, or otherwise reconsidered
- THEN the specification records that as a new event in history instead of deleting the earlier assertion
Requirement: Verification is justified status, not absolute truth
The system SHALL treat verified as a recorded status justified under the system’s evidence and rule model rather than as a claim of metaphysical or objective truth.
Scenario: verified status remains epistemically modest
- WHEN the specification describes what it means for a claim or term to be
verified - THEN it defines verification in terms of recorded justification and evidence
- AND it does not equate
verifiedwith unquestionable truth
Requirement: Core invariants constrain future capabilities
The system SHALL require later capabilities to compose with core invariants including standalone operation, append-only history, and explicit epistemic status rather than overriding them.
Scenario: later capability extends without breaking core guarantees
- WHEN a later capability adds commands, storage rules, or integration behavior
- THEN the added behavior preserves standalone operation, append-only history, and explicit epistemic status semantics
dont-data-model Specification
Purpose
TBD - created by archiving change add-dont-data-model-specs. Update Purpose after archive.
Requirements
Requirement: Entity structure
All stored objects SHALL be entities. Each entity SHALL have an id (ULID, prefixed per kind: claim:, term:, event:, evidence:, project:), a kind attribute (claim, term, hypothesis, evidence, project, …), a set of attribute assertions, and a history of events. Class membership SHALL be recognised by predicate match, not stored as a fact — “Is this entity an X?” is a rule evaluation, not a lookup. Attributes SHALL be first-class and globally defined; they are not owned by classes. The event:, evidence:, and project: prefixes are internal storage identifiers; only claim: and term: are externally-addressable as EntityId values in CLI verbs and input schemas (see dont-payload-types). Parsers MUST use a default branch for unknown entity_kind values; adding a new entity kind requires a minor-version envelope bump.
Scenario: entity has prefixed ULID identifier
- WHEN a new entity is created
- THEN it receives a ULID prefixed by its kind (e.g.
claim:01HX05A9K8VP)
Scenario: entity kind is an attribute not a class declaration
- WHEN a consumer queries “Is entity X a RicciTensor?”
- THEN the system evaluates a recognition predicate, not a stored class-membership fact
Scenario: attributes are globally scoped
- WHEN an attribute
statementis defined - THEN it is available to any entity, not bound to a single kind
Scenario: unknown entity kind uses default branch
- WHEN a parser encounters an entity with an
entity_kindvalue not in its known set - THEN it handles the entity through a default branch rather than failing
Scenario: project entity is reserved for project-wide audit
- WHEN the system needs to record a project-wide event such as
mode-changed - THEN it uses a reserved internal
project:entity rather than attaching the event to a claim or term
Requirement: Datom-based event-sourced storage
The system SHALL store all epistemic core facts (claims, terms, statuses, events) as datoms — immutable atomic facts in the shape (entity, attr, value, tx, assert_bit) — where tx is a monotonically increasing transaction number and assert_bit distinguishes assertion from retraction. In-place mutations are permitted for project infrastructure (e.g. config.toml) and cache artifacts, but the epistemic core MUST remain strictly append-only. Every CLI invocation SHALL be its own transaction; there is no batching across invocations. Within a transaction, event ordering SHALL be ULID-sorted. Derived orchestration commands (like assume or overlook) MUST NOT bypass core invariants; if they need to reach a final state, they MUST emit a sequence of valid events (e.g. concluded then dismissed) rather than jumping directly to a verified state.
Scenario: fact is stored as a datom
- WHEN a claim is concluded
- THEN the system writes datoms with entity, attribute, value, transaction number, and assertion bit
Scenario: each CLI invocation is one transaction
- WHEN two
dontcommands run sequentially - THEN each produces a distinct transaction with a higher transaction number than the previous
Scenario: events within a transaction are ULID-ordered
- WHEN a single transaction writes multiple events
- THEN the events are ordered by their ULID within the transaction
Scenario: attribute update produces assertion and retraction datoms
- WHEN a claim’s status changes from
unverifiedtodoubted - THEN a retraction datom (
assert_bit=0) for the old status and an assertion datom (assert_bit=1) for the new status are written in the same transaction
Requirement: Snapshot isolation and concurrency
The system SHALL use snapshot isolation: concurrent readers see a consistent snapshot and writers serialise. When parallel dont calls produce a write conflict, the system SHALL retry with exponential backoff (up to 3 attempts, 50 ms base). After the third retry exhausts, the call SHALL terminate with a non-zero exit status and emit an error envelope with code: "db-locked", unmet_clauses[] naming the transaction that held the lock, and remediation[] suggesting dont doctor and re-running the command (see dont-errors).
Scenario: concurrent readers see consistent state
- WHEN two read-only
dontcommands run concurrently - THEN each sees a consistent snapshot and neither blocks the other
Scenario: write conflict retries with backoff
- WHEN a write conflicts with another in-flight transaction
- THEN the system retries up to 3 times with exponential backoff from 50 ms base
Scenario: write conflict succeeds on retry
- WHEN a write conflicts with another transaction but the second retry succeeds
- THEN the command exits 0 and the transaction commits normally
Scenario: exhausted retries produce db-locked error
- WHEN all 3 retry attempts fail
- THEN the command exits non-zero with
code: "db-locked",unmet_clauses[]naming the blocking transaction, andremediation[]suggestingdont doctorand re-running the command
Requirement: Signal handling
The system SHALL handle SIGINT and SIGTERM by cleanly rolling back any in-flight transaction: no partial event is committed, and the process exits with code 130 (SIGINT) or 143 (SIGTERM). Signals received during the read-only preamble (argument parsing, config load, pending-spawn expiry sweep) SHALL cause a clean exit with no store mutation. SIGPIPE (closed downstream reader) SHALL be treated as normal termination with exit code 0. Signals received during network I/O inside dont verify-evidence or dont import SHALL cancel the in-flight request; any rows written for URIs already resolved before the signal SHALL remain committed. SIGKILL cannot be intercepted; the append-only log guarantees the store is never corrupted — at worst a transaction is lost, never half-applied.
Scenario: SIGINT during transaction rolls back cleanly
- WHEN SIGINT is received during a write transaction
- THEN the transaction is discarded, no partial event is committed, and the process exits 130
Scenario: SIGPIPE is normal termination
- WHEN SIGPIPE is received (e.g. from
head -n 1in a pipeline) - THEN the process exits with code 0
Scenario: SIGTERM during transaction rolls back cleanly
- WHEN SIGTERM is received during a write transaction
- THEN the transaction is discarded, no partial event is committed, and the process exits 143
Scenario: signal during read-only preamble exits cleanly
- WHEN SIGINT is received during argument parsing or config load
- THEN the process exits cleanly with no store mutation
Scenario: signal during verify-evidence preserves completed checks
- WHEN SIGINT is received during
dont verify-evidenceafter 3 of 5 URIs have been checked - THEN the 3 completed
evidence_checkedrows remain committed and the process exits 130
Scenario: SIGKILL does not corrupt store
- WHEN SIGKILL terminates the process mid-transaction
- THEN the append-only log guarantees no half-applied state; at worst the in-flight transaction is lost
Requirement: Time-travel query support
The system SHALL support reconstructing state at any past transaction by querying at a specific transaction number. Full bitemporality (transaction-time + valid-time) SHALL be available in the storage substrate for future features but the v0.3 CLI surface SHALL use transaction-time only.
Scenario: query at a past transaction
- WHEN a consumer queries state at transaction 42
- THEN the system returns the state as it was after transaction 42 committed
Scenario: valid-time is substrate-available but not CLI-exposed
- WHEN a consumer uses the v0.3 CLI
- THEN no valid-time query surface is available; every fact is stamped with wall-clock
atonly
Requirement: Core relations
The system SHALL maintain these logical relations derived from datoms:
entity { id, entity_kind, created_at, created_by }attribute { entity_id, name, value, tx }event { id, entity_id, event_kind, at, author, reason?, evidence_uri?, spawn_request_id? }evidence { id, entity_id, source_uri, kind, supports, quote? }depends_on { entity_id, dep_id }
Both evidence and depends_on SHALL key on entity_id (not claim_id) because dismiss targets both claims and terms and stale-cascade traverses term-to-term dependencies. depends_on rows for terms SHALL be synthesised from kind_of/related_to attributes at rule-evaluation time, not written directly by a verb; the relation’s shape admits both explicit and synthesised rows.
Scenario: evidence keyed on entity not claim
- WHEN evidence is attached to a term via
dismiss - THEN the
evidencerelation stores it withentity_idpointing to the term
Scenario: depends_on supports term-to-term edges
- WHEN a term has a
kind_ofreference to another term - THEN a
depends_onedge exists between them for stale-cascade traversal
Requirement: Kind disambiguation
The system SHALL namespace “kind” to avoid overloading: entity_kind for what an entity is (claim, term, event, evidence, project), event_kind for what happened (see canonical event kind list), envelope_kind for the envelope’s payload-type discriminator (see dont-envelope), and rule_kind for the applicable-rules discriminator (gate, flag; see dont-payload-types).
Scenario: entity_kind distinguishes stored object types
- WHEN a claim entity is queried
- THEN its
entity_kindis"claim"
Scenario: event_kind distinguishes event types
- WHEN a trust event is recorded
- THEN its
event_kindis"trusted"
Requirement: Canonical event kind list
The v0.3 system SHALL emit exactly these 12 event kinds: created, concluded, defined, trusted, dismissed, locked, ignored, spawn-requested, spawn-resolved, spawn-timeout, evidence-checked, mode-changed. Derived assessments such as stale SHALL NOT emit event kinds because they are recomputed trace results rather than persisted lifecycle changes. All event kinds SHALL be lower-kebab-case per the naming convention in dont-envelope. Additions SHALL require a minor-version envelope bump and MUST be documented in the changelog. Parsers MUST treat unknown event kinds as ignorable (forward compatibility) — unknown events SHALL appear in history[] output but SHALL NOT cause parser failure.
Scenario: complete event kind set
- WHEN the system processes events
- THEN it recognises exactly the 12 canonical event kinds listed for v0.3
Scenario: unknown event kind is ignorable
- WHEN a parser encounters an event kind not in the v0.3 set
- THEN it treats the event as ignorable rather than failing
Scenario: new event kind requires envelope version bump
- WHEN a new event kind is introduced
- THEN it requires a minor-version envelope bump and changelog documentation
Requirement: Claim-specific attributes
Claims SHALL carry these attributes: statement (string), status (lattice value per dont-status-lifecycle), confidence (float | null; null when no LLM-authored value was provided), provenance (structured object), atoms[] (repeated attribute rows, not JSON blobs), hypotheses[] (repeated attribute rows; each carries {idx, text, assessment} where assessment = {supporting: string[], refuting: string[]}), and refs[] (repeated attribute rows). atoms[], hypotheses[], and refs[] SHALL be stored as repeated attribute rows so that rules can reason over individual items. The lockable rule (see future rules spec) requires at least 3 assessed hypotheses to gate lock.
Scenario: atoms stored as individual rows
- WHEN a claim is concluded with 3 atoms
- THEN each atom is stored as a separate attribute row, not as a JSON blob
Scenario: confidence is uncalibrated
- WHEN a claim has
confidence: 0.78 - THEN consumers treat this as the LLM’s stated number, not a calibrated probability
Requirement: Term-specific attributes
Terms SHALL carry these attributes: curie (string, the term’s compact URI), definition (string, prose definition), label (string, optional; the SK11 type-text — a singular indefinite noun phrase suitable for use in olog diagrams), kind_of[] (array of CURIEs referencing parent terms), related_to[] (array of CURIEs referencing related terms), status (persisted lattice value per dont-status-lifecycle), and provenance (structured object). kind_of[] and related_to[] create edges traversed by stale-cascade derived-assessment computation and dangling-definition rules.
Seed entries (populated during dont init) that omit label in the seed YAML suppress the verb-level label-shape validators during seed installation only. Suppression is limited to the dont init seed pass; subsequent dont define calls on the same CURIE are subject to normal validator behaviour. The seed is pre-locked and is not round-tripped through define, so no term-shape-* refusal fires during seed installation regardless of whether label is present.
Scenario: term carries curie and definition
- WHEN a term is defined with
dont define proj:RicciTensor --doc "..." - THEN the stored term has
curie: "proj:RicciTensor"anddefinitionmatching the provided text
Scenario: term carries label when supplied
- WHEN a term is defined with
dont define proj:RicciTensor --label "a Ricci tensor" --doc "..." - THEN the stored term has
label: "a Ricci tensor"alongsidecurieanddefinition
Scenario: label is absent when not supplied
- WHEN a term is defined without
--label - THEN the stored term has no
labelattribute; the field is optional
Scenario: seed entry without label suppresses shape checks
- WHEN
dont initinstalls seed terms from YAML entries that omitlabel - THEN no
term-shape-*refusal fires for those entries during installation
Scenario: kind_of creates traversable edges
- WHEN a term has
kind_of: ["dont:Term"] - THEN the
stale-cascaderule can traverse from the parent term to this term
Requirement: Atom model
An atom SHALL be a sub-statement carrying {idx, text, status, evidence[]} where status uses a three-value sub-lattice (unverified, doubted, verified) — atoms cannot become locked or ignored, and stale appears only as a derived assessment on entity views, not as atom status. Atom-level evidence[] is stored but MAY be projected differently in view payloads (see dont-payload-types ClaimView). An atom SHALL transition to verified when a dismiss call names it via --atom <idx>. The --atom flag SHALL be repeatable on a single dismiss call so that one body of evidence can verify multiple atoms without issuing N separate commands.
Scenario: atom has its own status
- WHEN a claim with 3 atoms has atom 0 dismissed but atoms 1 and 2 undismissed
- THEN atom 0 has status
verifiedwhile atoms 1 and 2 remainunverified
Scenario: single dismiss verifies multiple atoms
- WHEN
dont dismiss --atom 0 --atom 1 --evidence <uri>is run - THEN both atom 0 and atom 1 transition to
verifiedwith the provided evidence
Requirement: Atom-completion gate
A claim SHALL reach verified in exactly one of two ways: (1) the claim has no declared atoms and a dismiss targets the claim with sufficient evidence, or (2) the claim has declared atoms and every declared atom has reached verified — the whole claim’s verified transition is then automatic on the last atom’s transition. A whole-claim dismiss (no --atom flags) on a claim that has declared atoms with any atom still unverified or doubted SHALL be refused with error code atoms-incomplete. This atom-completion gate is an absolute data-model invariant and MUST NOT be disabled or relaxed by project configurations or methodology rules. A trust call MAY target a single atom via --atom <idx>; doubting any atom SHALL cascade the parent claim to doubted.
Scenario: auto-promotion on last atom verified
- WHEN the last unverified atom of a claim is dismissed
- THEN the whole claim automatically transitions to
verifiedwithout an explicit whole-claim dismiss
Scenario: whole-claim dismiss refused when atoms incomplete
- WHEN
dont dismiss claim:X --evidence <uri>is run without--atomflags and atom 1 is stillunverified - THEN the command is refused with
code: "atoms-incomplete"
Scenario: doubting an atom cascades to parent claim
- WHEN
dont trust claim:X --atom 0 --reason "..."is run - THEN atom 0 transitions to
doubtedand the parent claim also transitions todoubted
Requirement: Import relations
The system SHALL maintain import relations separate from core entities: imported_term { curie, label, definition, xrefs, source, imported_at }, reference { uri, title, authors, year, source, imported_at }, and prefix { prefix, uri_base, canonical, imported_at }. Imports SHALL populate reference material; they are not entities in the core store and SHALL NOT be targetable by doubt/dismiss verbs. Auto-generation of local coined terms from imports is forbidden; imported terms remain in the imported graph until explicitly adopted via a define event. imported_term and term SHALL be queried together when checking CURIE resolution.
Scenario: imported term is not targetable by trust
- WHEN a consumer tries to
trustan imported term - THEN the command is refused because imported terms are not core entities
Scenario: CURIE resolution checks both tables
- WHEN a claim references CURIE
GO:0008150 - THEN resolution searches both the
termtable and theimported_termtable
Requirement: CURIE collision semantics
When the same CURIE exists in both term (coined) and imported_term (imported), the coined term SHALL shadow the imported one: the project’s definition is authoritative for rule evaluation and dismiss decisions. Re-importing a source SHALL NOT overwrite a coined term sharing its CURIE; the import operation SHALL record a warning. To replace a coined term with an import, the workflow is: trust <term-id> (doubt the coined term), re-import, the coined term remains doubted for audit while fresh claims resolve through imported_term.
Scenario: coined term shadows imported term
- WHEN both
termandimported_termhave CURIEproj:X - THEN rule evaluation and dismiss decisions use the coined term’s definition
Scenario: re-import does not overwrite coined term
- WHEN
dont import ols GOis re-run and a coined term shares a CURIE with an imported one - THEN the coined term is unchanged and the import logs a warning
Requirement: Five MVP primitives
The system SHALL recognise exactly five schema-level primitives and SHALL require written justification for adding a sixth:
attribute—{ ident, value_type, cardinality, predicate?, doc }wherevalue_typeis one ofstring,int,float,bool,ref,tuple,enum,exprandpredicateis an optional Datalog fragment or reference to an external verifier toolderived_class—{ ident, defining_attributes, extra_predicates, doc }— a named recognition query; not inheritanceenum—{ ident, values[], doc }prefix—{ prefix, uri_base, canonical }. While authoritative prefix definitions may reside in project infrastructure (config.toml), the data model MUST log the effective prefix mappings used by any event to ensure historical queries resolve correctly even if infrastructure changes.rule—{ name, body, severity: warn|strict, doc }
Explicitly not primitives: inheritance, mixins, slot_usage, identifier prefixes as a separate concept from prefix, class-owns-slot relationships.
Scenario: exactly five primitives
- WHEN the system’s schema is queried
- THEN it recognises exactly five primitive kinds: attribute, derived_class, enum, prefix, rule
Scenario: adding a sixth primitive requires justification and version bump
- WHEN a contributor proposes a sixth primitive
- THEN the pull request must demonstrate that no combination of existing primitives expresses the capability being added, and the addition requires a minor-version bump and changelog entry
Scenario: derived_class is recognition not inheritance
- WHEN a derived class query matches an entity
- THEN the entity is recognised as a member; multiple derived classes may apply simultaneously
Scenario: inheritance is not a primitive
- WHEN a consumer expects class inheritance
- THEN the system does not provide it; membership is by predicate match only
Requirement: Author identity format
Authors SHALL be identified by a string <actor-kind>:<id> where actor-kind is one of human, llm, tool, or ci, and id is an opaque stable identifier within that kind. Anonymous events are strictly forbidden; background processes, rule engines, and automated workflows MUST use a defined actor-kind (e.g., tool:rule-engine). Parsing SHALL split on the first : only — the id portion may itself contain colons. Empty id is invalid; actor_kind outside the four-value set is invalid. The actor_kind set is closed by design; extending it requires a minor-version bump and a parser upgrade gate. The convention is shared with beads and wai for cross-tool audit correlation.
Scenario: author string with colons in id
- WHEN author is
tool:github-actions:prod - THEN parsing yields
actor_kind = "tool"andid = "github-actions:prod"
Scenario: empty id is rejected
- WHEN author is
human: - THEN validation rejects it as invalid
Scenario: all four valid actor kinds are accepted
- WHEN author strings
human:alice,llm:claude-3,tool:ci-bot, andci:github-actionsare validated - THEN all four are accepted
Scenario: unknown actor kind is rejected
- WHEN author is
bot:gpt-4 - THEN validation rejects it because
botis not in the allowed set{human, llm, tool, ci}
Requirement: Confidence semantics
The confidence field on claims SHALL be stored as authored by the LLM, uncalibrated. The system SHALL NOT attempt to Platt-scale or post-process confidences in v0.3. Consumers treating confidences as probabilities SHALL read them as the author’s stated number, not as a calibrated estimate.
Scenario: confidence stored without transformation
- WHEN an LLM concludes a claim with confidence 0.78
- THEN the stored confidence is exactly 0.78, not a calibrated or scaled value
Scenario: out-of-range confidence is rejected
- WHEN a claim is concluded with
confidence: 1.3(outside 0.0-1.0) - THEN the command is refused with
code: "schema-mismatch"
Requirement: Provenance tracking
Every entity SHALL carry a provenance object recording at minimum author (AuthorString) and origin (string). Claims MAY additionally carry model, session_id, and context_hash in provenance. Every status transition SHALL record author, timestamp, and reason or evidence.
Scenario: claim provenance includes model
- WHEN a claim is concluded by
llm:claude-opus-4.7 - THEN provenance includes
author,origin: "llm", andmodel: "claude-opus-4.7"
Scenario: every transition records author and timestamp
- WHEN a claim is trusted
- THEN the event records
author,at(timestamp), andreason
dont-derived-queries Specification
Purpose
TBD - created by archiving change add-dont-harness-specs. Update Purpose after archive.
Requirements
Requirement: List and vocabulary query scopes
The system SHALL provide read-only collection queries for claims and terms. dont list MUST return claims by default, filtered by optional --status and --kind arguments. --status SHALL match only persisted lifecycle status. dont list --kind terms MUST return term entities with the same filter semantics. dont list --kind claims is equivalent to bare dont list.
Not yet implemented:
--derived-assessment(filter by computed assessments such asstale),--as-of(historical slice query),dont list --all(include both claims and terms), and thedont vocabalias fordont list --kind terms. These are planned but not available in the current implementation. When implemented,--derived-assessmentSHALL match computed assessments without treating them as persisted statuses;--as-ofSHALL evaluate entity state at a historical transaction point;dont vocabSHALL remain equivalent todont list --kind terms.
Scenario: list defaults to claims
- WHEN the caller runs
dont list --json - THEN the command returns
envelope_kind: "claims" - AND the payload contains claims rather than terms by default
Scenario: list –kind terms narrows to terms
- WHEN the caller runs
dont list --kind terms --status unverified --json - THEN the command returns
envelope_kind: "terms"with only term entities matching that status filter
Scenario: why explains current blockers
- WHEN the caller runs
dont why claim:01HX05A9K8VP --json - THEN the response includes the entity, its events, and the rules currently applicable to it
- AND unmet rules include remediation context the caller can act on
Requirement: Entity inspection queries
The system SHALL provide dont show <entity-id> for the current view of one entity and dont why <entity-id> for the current view plus explanatory context. dont show MUST include the entity’s event timeline in the response. dont why MUST include the entity, its events, and all currently applicable rules with remediation for unmet conditions.
Note: The
ClaimViewreturned bydont showalways includes aneventsarray in its payload. There is no--historyflag — the event timeline is unconditionally present. The spec originally described a--historyopt-in flag; the actual design bundles events into everyClaimViewresponse instead.
Scenario: show returns current entity view with events
- WHEN the caller runs
dont show claim:01HX05A9K8VP --json - THEN the command returns the current
ClaimVieworTermViewfor that entity - AND the response includes the entity’s
eventsarray unconditionally
Scenario: why explains current blockers
- WHEN the caller runs
dont why claim:01HX05A9K8VP --json - THEN the response includes the entity, its events, and the rules currently applicable to it
- AND unmet rules include remediation context the caller can act on
Requirement: Session-orientation query
The system SHALL provide dont prime as the session-start orientation query. dont prime --json MUST return the PrimeView payload describing the current project mode, rule activation, status counts, harness mode, and blocking work relevant to the next session step.
Scenario: prime on fresh project
- WHEN the caller runs
dont prime --jsonin a freshly initialised project - THEN the response returns
envelope_kind: "prime" - AND the payload reports zero-state counts and an empty blocking set
Scenario: prime reports harness mode
- WHEN harness-mode detection selects harness or direct mode
- THEN
PrimeView.harness_modereflects that resolved mode so the caller can verify it at session start
Requirement: Diagnostic and schema queries
The system SHALL provide diagnostic and schema queries. By default, these queries are read-only. dont doctor MUST report substrate reachability, rule compilation health, freshness/availability checks for auxiliary surfaces, and a managed-docs staleness check covering the root managed block(s) and .dont/AGENTS.md as defined in dont-agent-help. dont doctor MUST accept a --fix flag whose behaviour is defined by dont-agent-help (rewrite managed regions and the canonical file). Without --fix, dont doctor MUST remain read-only. dont schema <name> MUST print the JSON Schema for the named envelope or payload type, and bare dont schema MUST list available schema names.
Scenario: doctor reports health checks
- WHEN the caller runs
dont doctor --json - THEN the command returns
envelope_kind: "doctor" - AND the payload contains the diagnostic checks relevant to store health, rules, auxiliary tooling, and managed-docs staleness
Scenario: doctor strict mode escalates warnings
- WHEN the caller runs
dont doctor --strict --json - THEN warning or failing checks are treated according to the strict-exit semantics defined by
dont-cli-surface
Scenario: doctor without fix is read-only
- WHEN the caller runs
dont doctorordont doctor --jsonwithout--fix - THEN the command performs no writes to any file on disk
Scenario: doctor with fix performs managed-doc repair
- WHEN the caller runs
dont doctor --fix - THEN the command applies the repair behaviour defined by
dont-agent-helpfor managed regions and.dont/AGENTS.md - AND the doctor envelope subsequently reports the managed-docs check as
status: "pass"when re-run on the same project state
Scenario: schema without argument lists names
- WHEN the caller runs
dont schema --json - THEN the response lists schema names available for inspection rather than one schema document
Scenario: schema with name prints one schema
- WHEN the caller runs
dont schema claim --json - THEN the response returns the
SchemaDocfor the named schema target
Requirement: Canonical examples query
The system SHALL provide dont examples as a read-only query returning canonical worked examples that teach the intended workflow.
Scenario: examples returns worked-example set
- WHEN the caller runs
dont examples --json - THEN the command returns
envelope_kind: "examples" - AND the payload contains the canonical examples list described by
ExamplesList
Requirement: Trace is part of the read-only diagnostic query surface
The system SHALL provide dont trace <entity-id> as a read-only diagnostic query for dependency/support blocker inspection. trace complements show and why: show returns the current entity view, why explains current rules and history, and trace explains the blocker paths currently relevant to derived assessments or verification gating.
Scenario: trace returns blocker-oriented diagnostics
- WHEN the caller runs
dont trace claim:01HX05A9K8VP --json - THEN the command returns a trace-oriented payload rather than a ClaimView or WhyView
Scenario: healthy entity trace succeeds with empty blockers
- WHEN the caller runs
dont trace <entity-id> --jsonon a healthy entity with no current dependency/support fallout - THEN the command succeeds and returns an empty blocker set
dont-envelope Specification
Purpose
TBD - created by archiving change add-dont-envelope-specs. Update Purpose after archive.
Requirements
Requirement: Versioned output envelope
dont SHALL source its output envelope from genesis::envelope rather than a local src/envelope.rs. The deployed envelope_version "0.2" contract and all field semantics (ok, envelope_kind, hints, warnings) SHALL be preserved unchanged; genesis’s module SHALL conform to this contract.
Scenario: envelope shape unchanged after adoption
- WHEN
dont prime --jsonis run after adopting genesis - THEN the emitted JSON SHALL have top-level keys
ok,envelope_version,cli_version,envelope_kind,data,warnings,hints,meta - AND
envelope_versionSHALL remain"0.2" - AND no local
Envelopestruct SHALL remain insrc/envelope.rs.
Scenario: envelope contains version and cli_version independently
- WHEN an error envelope is serialized
- THEN
dataSHALL contain the structuredErrorResultfields (code,rule_name,remediation) - AND
envelope_versionandcli_versionSHALL be independent top-level fields
Scenario: minor envelope version does not break existing parsers
- GIVEN
dont --version --json - WHEN the version envelope is emitted
- THEN it SHALL be a structured envelope with
ok: true,envelope_kind: "version", anddata.versionas a semver string - AND parsers keyed on
envelope_versionSHALL continue to deserialize the envelope
Scenario: parsers do not branch on cli_version
- GIVEN a CLI error (e.g.
dont conclude "" --json) - WHEN the error envelope is emitted
- THEN it SHALL be a well-formed error envelope with
ok: falseandenvelope_kind: "error" - AND parsers SHALL NOT branch on
cli_versionto determine envelope structure
Requirement: Boolean success discriminator
The system SHALL include an ok field in every envelope that is true for success and false for refusal or error, and SHALL set envelope_kind to "error" when ok is false.
Scenario: successful command produces ok true
- WHEN a command completes successfully
- THEN the envelope has
ok: trueandenvelope_kindis the appropriate payload discriminator
Scenario: refused or errored command produces ok false
- WHEN a command is refused or encounters an error
- THEN the envelope has
ok: falseandenvelope_kindis"error"
Requirement: Typed payload discriminator
The system SHALL include an envelope_kind field that discriminates the shape of the data field, and SHALL define a canonical set of envelope_kind values for envelope version 0.2.
Scenario: envelope_kind matches the data payload type
- WHEN a command returns a claim payload
- THEN
envelope_kindis"claim"anddatacontains the claim payload shape
Scenario: canonical envelope_kind values are enumerated
- WHEN envelope version is 0.2
- THEN the canonical
envelope_kindvalues include at minimum:claim,claims,term,term_list,event,events,spawn_request,spawn_requests,rule,rule_result,prime,why,doctor,examples,schema_doc,version,empty,error
Requirement: Forward-compatible envelope_kind parsing
The system SHALL require that parsers have a default branch for unknown envelope_kind values so that new payload types in future minor versions do not break existing parsers.
Scenario: parser encounters unknown envelope_kind
- WHEN a parser receives an envelope with an
envelope_kindvalue not in its known set - THEN the parser handles the envelope through a default branch rather than failing
Requirement: Agent-addressed hints on success envelopes
The system SHALL include a hints key on every success envelope. The hints array contains ordered {command, description} pairs suggesting next actions for agents; it MAY be empty when no contextual action is applicable. Error envelopes SHALL NOT carry hints; agents SHALL use remediation[] inside the ErrorResult payload (see dont-errors) for next-action guidance. A success envelope that omits hints is non-conformant for producers. Parsers MAY treat a missing hints key as equivalent to hints: [] for backward-compatibility with historical envelopes, and SHOULD surface a conformance warning when doing so.
Scenario: success envelope always carries hints key
- WHEN a command succeeds
- THEN the envelope includes a
hintskey whose value is an array (possibly empty) of{command, description}entries
Scenario: error envelopes do not carry hints
- WHEN a command fails
- THEN the envelope does not include a
hintskey; agents useremediation[]inside theErrorResultdata payload
Scenario: missing hints on success is non-conformant for producers
- WHEN a producer emits a success envelope without a
hintskey - THEN that envelope is non-conformant with this specification
Scenario: parser tolerates historical envelope missing hints
- WHEN a parser receives a historical success envelope that omits
hints - THEN the parser may treat it as
hints: [] - AND the parser surfaces a conformance warning
Requirement: Rule warnings on non-refusing conditions
The system SHALL include a warnings key on every envelope. Each entry SHALL have the shape {rule_name: string, entity_id?: string, message: string, suggested_remediation?: string}. Warnings capture non-refusing rule flags, malformed-but-non-blocking inputs, and liveness stale signals.
Scenario: warning attached for non-refusing rule flag
- WHEN a command succeeds but a non-refusing rule condition was triggered
- THEN the envelope includes a
warningsentry withrule_nameandmessage, and optionallyentity_idandsuggested_remediation
Scenario: warnings may appear on error envelopes
- WHEN a command is refused but a non-refusing warning also fired during the operation
- THEN the error envelope includes the non-refusing warning in
warnings[]alongsideok: false
Requirement: Execution metadata
The system SHALL include a meta object on every envelope carrying duration_ms (non-negative integer, milliseconds), tx (transaction ID for mutations, null for read-only commands), and request_id (string spawn request ID when resolving a pending spawn, otherwise null). meta.tx MUST be null on read-only commands. When present on mutating commands, meta.tx MUST be an integer in the range [1, 2^53-1]; parsers MUST NOT assume it fits in a 32-bit integer.
Scenario: mutating command includes transaction ID
- WHEN a command writes to the store
- THEN the envelope’s
meta.txcontains an integer in the range [1, 2^53-1]
Scenario: read-only command has null transaction ID
- WHEN a command is read-only
- THEN the envelope’s
meta.txisnull
Scenario: spawn resolution populates request_id
- WHEN a command resolves a pending spawn request
- THEN
meta.request_idis set to the spawn request’s ID
Requirement: JSON-only stdout in json mode
The system SHALL emit only the JSON envelope on stdout in --json mode and SHALL route human-readable logging to stderr. The --json flag’s behaviour on the CLI surface is defined in dont-cli-surface.
Scenario: json mode produces only the envelope on stdout
- WHEN a command is invoked with
--json - THEN stdout contains exactly the JSON envelope and no other output
- AND any human-readable logging is written to stderr
Requirement: Entity ID representation in envelopes
The system SHALL format claim identifiers as claim:<ULID>, term identifiers as term:<ULID>, and spawn request identifiers as spawn:<ULID>, producing lexicographically sortable, timestamp-embedded identifiers.
Scenario: claim ID is prefixed ULID
- WHEN a new claim is created
- THEN its identifier has the format
claim:<ULID>where the ULID is lexicographically sortable and embeds a timestamp
Scenario: spawn request ID is prefixed ULID
- WHEN a new spawn request is created
- THEN its identifier has the format
spawn:<ULID>
Requirement: Naming and format conventions
The system SHALL use lower-kebab-case for rule names, event kinds, and status values, and RFC 3339 UTC with whole-second precision (no fractional seconds) for timestamps. Validity tuples SHALL be two-element arrays [RFC3339-timestamp, boolean].
Scenario: rule names are lower-kebab-case
- WHEN a rule name appears in output
- THEN it is formatted as lower-kebab-case
Scenario: timestamps are RFC 3339 UTC whole-second
- WHEN a timestamp appears in output
- THEN it is formatted as RFC 3339 in UTC with whole-second precision (e.g.
2026-04-18T14:20:00Z)
Scenario: validity tuple is a two-element array
- WHEN a validity value appears in output
- THEN it is a two-element array
[timestamp, boolean]wheretimestampfollows the RFC 3339 UTC convention
dont-errors Specification
Purpose
TBD - created by archiving change add-dont-envelope-specs. Update Purpose after archive.
Requirements
Requirement: Structured error envelope
The system SHALL represent errors using an ErrorResult payload with fields code (stable lowercase-kebab identifier), message (human-readable one-liner), rule_name (the specific §13 rule that refused, or null for verb-level validators), spec_ref (advisory pointer to the spec section — not stable across spec versions, MUST NOT be used for programmatic branching), entity_id (target of the refused operation, when applicable), unmet_clauses (structured list of failing conditions), and remediation (non-empty array of next-action pairs).
Scenario: error envelope contains structured fields
- WHEN a command is refused
- THEN the envelope has
ok: false,envelope_kind: "error", anddatacontains anErrorResultwith at minimumcode,message, andremediation
Scenario: rule_name distinguishes rule refusals from verb-level validators
- WHEN an error is caused by a rule-system refusal
- THEN
rule_nameidentifies the specific rule (e.g.lockable,unresolved-terms) - AND
codeisrule-not-met
Scenario: verb-level validators have dedicated codes and null rule_name
- WHEN an error is caused by a verb-level validator (not a rule)
- THEN
codeis the validator’s dedicated identifier (e.g.no-evidence,reason-required) andrule_nameisnull
Requirement: Scope boundary for rule-not-met
The system SHALL use rule-not-met exclusively for refusals originating from the §13 rule system and SHALL NOT use it for verb-level validators. Verb-level validators (no-evidence, reason-required, reason-not-hedge, atoms-incomplete, wrong-entity-kind, term-label-empty, term-shape-indefinite, term-shape-punctuated, term-compound-undeclared, term-label-sentence) SHALL have dedicated error codes and SHALL set rule_name to null.
Scenario: rule-not-met is not used for verb-level validators
- WHEN a verb-level validator refuses an operation
- THEN the error code is the validator’s own dedicated code, not
rule-not-met
Scenario: rule-not-met always carries a rule_name
- WHEN an error has
code: "rule-not-met" - THEN
rule_nameis non-null and identifies the specific rule that refused
Scenario: define label validator error has null rule_name
- WHEN
dont defineis refused by a term-shape validator - THEN
rule_nameisnullandcodeis the validator’s dedicated code (e.g.term-shape-indefinite)
Requirement: Remediation invariant
Every error envelope SHALL contain a non-empty remediation[] array of {command, description} pairs. An error envelope with empty or absent remediation[] is non-conformant with this specification. This is Invariant 3.2.5 — it applies to every code path that constructs an ErrorResult, including usage and internal errors.
Scenario: every error carries at least one remediation
- WHEN any error envelope is produced
- THEN the
remediationarray contains at least one{command, description}entry
Scenario: schema enforces remediation invariant
- WHEN the
ErrorResultJSON Schema is validated - THEN it requires
remediationwithminItems: 1
Scenario: usage errors point to help
- WHEN an error has
code: "usage" - THEN
remediationincludes an entry whosecommandpoints todont help <cmd>for the relevant command
Scenario: internal errors point to doctor and issue reporting
- WHEN an error has
code: "internal" - THEN
remediationincludes an entry pointing todont doctorand an entry pointing to issue reporting
Requirement: Remediation ordering
The system SHALL order remediation[] entries from most-specific to least-specific. The first entry (remediation[0]) MUST be the most directly actionable remediation for the specific refusal.
Scenario: first remediation is most actionable
- WHEN a harness reads
remediation[0] - THEN the entry is the most specific and directly actionable remediation for the refusal
Requirement: Unmet clauses with fix snippets
The system SHALL include an unmet_clauses[] array on error envelopes listing each failing condition with a clause description and a fix snippet showing how to satisfy it.
Scenario: error with multiple failing conditions lists each clause
- WHEN a command is refused due to multiple failing conditions
- THEN
unmet_clausescontains one entry per condition, each withclauseandfixfields
Requirement: Open error-code set
The system SHALL treat the error-code set as open, allowing new codes in minor envelope versions, and SHALL require that parsers have a default branch for unknown codes rather than failing closed.
Scenario: parser encounters unknown error code
- WHEN a parser receives an error with a code not in its known set
- THEN the parser handles it through a default branch rather than failing
Scenario: new error codes do not require a major version bump
- WHEN a new error code is added
- THEN it is introduced in a minor envelope version without breaking existing parsers
Requirement: Known error codes for envelope version 0.2
The system SHALL define the following error codes for envelope version 0.2. Refusal codes (exit 1, ok: false): no-evidence, reason-required, reason-not-hedge, atoms-incomplete, claim-not-found, term-not-found, claim-locked, term-locked, claim-ignored, term-ignored, claim-not-verified, term-not-verified, rule-not-met, wrong-entity-kind, already-initialised, unresolvable-uri, schema-mismatch, db-locked, config-missing, spawn-not-found, spawn-expired, linkml-unsupported-feature, usage, internal, term-label-empty, term-shape-indefinite, term-shape-punctuated, term-compound-undeclared, term-label-sentence. Warning codes (attached to warnings[] on ok: true envelopes), grouped by origin — verb-level: evidence-malformed, evidence-stale, term-doc-shape-indefinite, term-doc-shape-punctuated, term-doc-shape-sentence; rule-layer: term-nonfunctional-label, rule-claim-structure.
rule-claim-structure is a warn-severity rule-layer code attached to warnings[] on ok: true envelopes. It identifies tagged rule claims that are missing one or both mandatory slot markers ([TRIGGER] and one of [CONFIG] or [MODE]). The rule_name field SHALL be "rule-claim-structure" and the entity_id SHALL be the claim:uuid of the violating claim.
Scenario: domain refusal uses a known error code
- WHEN a domain rule or verb-level validator refuses an operation
- THEN the error
codeis one of the known domain error codes
Scenario: term-shape refusal exits code 1
- WHEN
dont defineis refused by a term-shape validator such asterm-shape-indefinite - THEN the process exits with code
1and the envelope hasok: false,envelope_kind: "error"
Scenario: doc-extraction warning appears on success envelope
- WHEN an actor invokes
definewithout--labeland the leading phrase of--docfails a shape check - THEN the envelope has
ok: trueandwarnings[]contains an entry with the appropriateterm-doc-shape-*code
Scenario: usage errors use the usage code with envelope fields
- WHEN a command receives malformed arguments or an unknown flag
- THEN the error
codeisusageand the envelope hasok: false,envelope_kind: "error"
Scenario: internal errors use the internal code
- WHEN an unexpected tool failure occurs
- THEN the error
codeisinternalandremediationpoints atdont doctorand at issue reporting
Scenario: warning codes appear on success envelopes
- WHEN an evidence URI is malformed but non-blocking
- THEN the envelope has
ok: trueandwarnings[]contains an entry with theevidence-malformedwarning code
Scenario: rule-claim-structure warning appears on success envelope
- WHEN
rule-claim-structureis enabled anddont primeevaluates a tagged rule claim missing a mandatory slot marker - THEN the envelope has
ok: trueandwarnings[]contains an entry withcode: "rule-not-met",rule_name: "rule-claim-structure", andentity_ididentifying the violating claim
Scenario: rule-claim-structure warning does not appear when rule is disabled
- WHEN
rule-claim-structureis not enabled in project config - THEN no
rule-claim-structurewarning code appears on any envelope regardless of claim content
Requirement: Exit-code contract
The system SHALL define a fixed set of exit codes that harnesses can branch on without parsing the envelope: 0 for success, 1 for refusal (LLM can retry via remediation[]), 2 for usage error, 3 for substrate/configuration error (requires operator action), 4 for internal error, 130 for SIGINT, 143 for SIGTERM.
Scenario: refusal exits with code 1
- WHEN a rule refuses or a verb-level validator trips
- THEN the process exits with code
1and the envelope hasok: false,envelope_kind: "error"
Scenario: usage error exits with code 2
- WHEN a command receives malformed arguments
- THEN the process exits with code
2and the envelope hasok: false,envelope_kind: "error",code: "usage"
Scenario: substrate error exits with code 3
- WHEN the store is unreachable or configuration is incomplete
- THEN the process exits with code
3
Scenario: internal error exits with code 4
- WHEN an unexpected bug occurs
- THEN the process exits with code
4
Requirement: Exit-code boundary semantics
The system SHALL define the boundary between exit 1 (refusal) and exit 3 (substrate) as the harness decision pivot: exit 1 means the LLM can make progress by reading remediation[]; exit 3 means the LLM should stop and the operator should check configuration or run dont doctor.
Scenario: harness distinguishes retryable from non-retryable errors
- WHEN a harness receives exit code
1 - THEN it reads
remediation[0].commandto determine the next action
Scenario: harness escalates substrate errors to operator
- WHEN a harness receives exit code
3 - THEN it stops LLM-driven retry and surfaces the error for operator intervention
Requirement: Exit code 1 consistency under doctor strict mode
Consistent with the exit-code contract above, the system SHALL exit 1 from dont doctor --strict when any check is warn or fail, preserving the convention that exit 1 means “something the caller should attend to; envelope says what.” Without --strict, exit 1 occurs only on fail checks.
Scenario: doctor strict exits 1 on warnings
- WHEN
dont doctor --strictfinds awarncheck - THEN the process exits with code
1
Scenario: doctor non-strict exits 0 on warnings
- WHEN
dont doctor(without--strict) finds onlywarnchecks and nofailchecks - THEN the process exits with code
0
dont-evidence-locators Specification
Purpose
Defines structured evidence locators for repository-grounded verification. Repository-relative file locators, optional line spans and anchors, captured excerpts, and stability fingerprints enable operators to point to a specific source location when grounding a claim and allow later readers to audit what text was relied on and detect drift.
Requirements
Requirement: Repository-scoped evidence locators
The system SHALL support structured evidence locators for repository-grounded verification. At minimum, a locator MUST be able to name a repository-relative file path and MAY additionally include a line span, anchor, and free-form note. Repository-relative locators SHALL resolve against the project root rather than the caller’s transient working directory. Plain URI-only evidence SHALL remain supported as a compatibility path, but repository-relative locators are the recommended form for project-grounded claims.
Scenario: repository file locator points to a line span
- WHEN an operator grounds a claim from
README.mdlines 10 through 18 - THEN the evidence locator can represent the repository-relative path and that line span without requiring an opaque absolute
file://URI
Scenario: repository file locator remains stable across subdirectories
- WHEN the caller invokes
dontfrom a nested working directory inside the same project - THEN the same repository-relative evidence locator still resolves to the same target file
Scenario: locator escaping the project root is refused
- WHEN an operator supplies a repository-relative locator that normalizes outside the project root, including via
..traversal or a symlinked escape - THEN the evidence locator is refused rather than resolving to content outside the project
Requirement: Captured excerpts and fingerprints remain auditable
The system SHALL allow evidence records to carry an optional captured excerpt and an optional stability fingerprint derived from the referenced source material. The excerpt is for later human audit of what text was relied on; the fingerprint is for later drift detection and SHALL NOT be treated as a proof of semantic correctness by itself.
Scenario: doc claim stores excerpt for later audit
- WHEN a claim is grounded in one sentence from a README section
- THEN the evidence record can carry that captured excerpt so a later reader can see what text supported the claim
Scenario: fingerprint detects later source drift
- WHEN the source file changes after the claim was verified
- THEN later inspection surfaces such as
dont show,dont why, ordont verify-evidencecan report that the stored fingerprint no longer matches the current source slice without changing the claim’s persisted status automatically
Scenario: line span no longer exists after edits
- WHEN the stored locator names a line span that no longer exists in the current file revision
- THEN later inspection reports the locator as unresolved or drifted for audit purposes without rewriting the entity’s persisted status automatically
Requirement: Structured locators are visible in inspection views
The system SHALL surface structured evidence locators in claim and term inspection outputs in addition to the existing human-readable evidence summary. JSON inspection payloads MUST preserve the locator structure so external harnesses can render or audit it without reparsing display text.
Scenario: show returns structured evidence details
- WHEN a caller runs
dont show <entity-id> --jsonon an entity grounded in repository evidence - THEN the returned evidence entries include the structured locator data needed to identify the file and line span
Scenario: why can explain evidence provenance without guessing from display text
- WHEN a harness runs
dont why <entity-id> --json - THEN the evidence details are available as structured fields rather than only as a flattened prose string
dont-glossary Specification
Purpose
TBD - created by archiving change add-dont-glossary-spec. Update Purpose after archive.
Requirements
Requirement: Epistemic entity
The glossary SHALL define “epistemic entity” as the umbrella concept representing any distinct assertion or definition whose status is managed by the system’s shared epistemic lattice. The glossary MUST state that claims and terms are the concrete instantiations of epistemic entities, and it MUST NOT specify their structural or behavioral rules, instead pointing to dont-data-model and dont-status-lifecycle.
Scenario: resolving epistemic entity meaning
- WHEN a reader looks up “epistemic entity”
- THEN they learn it is an umbrella concept covering both claims and terms
Requirement: Claim
The glossary SHALL define “claim” as a declarative assertion managed by dont. It MUST point to dont-data-model for its structure and dont-cli-core for its creation via conclude.
Scenario: resolving claim meaning
- WHEN a reader looks up “claim”
- THEN they learn it is a declarative assertion that instantiates an epistemic entity
Requirement: Term
The glossary SHALL define “term” as an LLM-coined or imported project vocabulary entry managed by dont. It MUST point to dont-data-model for its structure and dont-cli-core for its creation via define.
Scenario: resolving term meaning
- WHEN a reader looks up “term”
- THEN they learn it is a vocabulary entry that instantiates an epistemic entity
Requirement: Epistemic lattice
The glossary SHALL define “epistemic lattice” as the shared status model governing claims and terms. It SHALL explicitly list “status lattice” as an alias that maps identically to “epistemic lattice”. It MUST point to dont-status-lifecycle for the actual transition semantics.
Scenario: looking up status lattice alias
- WHEN a reader looks up “status lattice”
- THEN they are directed to the canonical concept “epistemic lattice”
Requirement: Atom
The glossary SHALL define “atom” as an independently checkable sub-statement of a claim. It MUST point to dont-data-model for its structural representation and scope.
Scenario: resolving atom meaning
- WHEN a reader looks up “atom”
- THEN they learn it is a sub-statement of a claim
Requirement: Atom-completion gate
The glossary SHALL define “atom-completion gate” as the rule that all atoms must verify before their parent claim becomes verified. It MUST point to dont-data-model for the enforcement of this invariant.
Scenario: resolving atom-completion gate meaning
- WHEN a reader looks up “atom-completion gate”
- THEN they learn it is the rule tying atom verification to parent claim verification
Requirement: Core four verbs
The glossary SHALL define “core four verbs” as the collective grouping of the four primary CLI verbs: conclude, define, trust, and dismiss. It MUST point to dont-cli-core for their contracts.
Scenario: resolving core four verbs meaning
- WHEN a reader looks up “core four verbs”
- THEN they learn it refers specifically to conclude, define, trust, and dismiss
Requirement: Lifecycle verb
The glossary SHALL define “lifecycle verb” as the collective grouping of secondary CLI verbs that manage entity states outside the core four: lock, reopen, ignore, and verify-evidence.
Scenario: resolving lifecycle verb meaning
- WHEN a reader looks up “lifecycle verb”
- THEN they learn it refers to lock, reopen, ignore, and verify-evidence
Requirement: Evidence
The glossary SHALL define “evidence” as the grounding material cited during verification workflows. It MUST point to dont-data-model for its structural representation as a core relation.
Scenario: resolving evidence meaning
- WHEN a reader looks up “evidence”
- THEN they learn it is the grounding material used in verification
Requirement: Hedge pattern
The glossary SHALL define “hedge pattern” as a configured vague-reason pattern that fails reason quality checks during explicit doubt. It MUST point to future configuration and CLI specs for enforcement rules.
Scenario: resolving hedge pattern meaning
- WHEN a reader looks up “hedge pattern”
- THEN they learn it is a vague reason that is actively rejected by the system
Requirement: Rule
The glossary SHALL define “rule” broadly as a named policy or predicate used to warn or refuse commands. It MUST point to the future dont-rules capability for the engine implementation.
Scenario: resolving rule meaning
- WHEN a reader looks up “rule”
- THEN they learn it is a named policy or predicate
Requirement: Author string
The glossary SHALL define “author string” as the identity format used for events, represented as <actor-kind>:<id>. It MUST point to dont-data-model for its structural validation and constraints.
Scenario: resolving author string meaning
- WHEN a reader looks up “author string”
- THEN they learn it represents the
<actor-kind>:<id>format
Requirement: Seed vocabulary
The glossary SHALL define “seed vocabulary” as the bootstrap dont: vocabulary installed at project initialization time. It MUST point to the future dont-init-modes capability for bootstrap logic.
Scenario: resolving seed vocabulary meaning
- WHEN a reader looks up “seed vocabulary”
- THEN they learn it is the initial vocabulary installed when a project begins
Requirement: Event
The glossary SHALL define “event” as an immutable history record representing an action or state change in the system. It MUST clarify that events are distinct from event kinds.
Scenario: resolving event meaning
- WHEN a reader looks up “event”
- THEN they learn it is an immutable history record
Requirement: Event kind
The glossary SHALL define “event kind” as the classifier identifying the type of an event. It MUST point to dont-data-model for the canonical list of closed event kinds.
Scenario: resolving event kind meaning
- WHEN a reader looks up “event kind”
- THEN they learn it classifies an event, distinct from the event itself
Requirement: Remediation
The glossary SHALL define “remediation” as actionable recovery guidance returned in error payloads. It MUST point to the future dont-envelope capability for the payload schema.
Scenario: resolving remediation meaning
- WHEN a reader looks up “remediation”
- THEN they learn it provides actionable recovery guidance
dont-ground-command Specification
Purpose
TBD - created by archiving change add-ground-command. Update Purpose after archive.
Requirements
Requirement: Ground captures a claim and its evidence in one command
The system SHALL provide dont ground <statement> as a derived orchestration command for the sidecar workflow where an operator already has both claim text and supporting evidence. In its initial version, ground SHALL accept statement text plus one or more evidence references, along with the standard invocation-level author override, and SHALL NOT introduce new convenience syntax for atoms, dependencies, or references beyond what the underlying core verbs already support. ground SHALL require at least one evidence reference and SHALL return a verified claim when all eligibility checks succeed.
Scenario: repository fact is grounded in one step
- WHEN an operator runs
dont ground "Chacana parses tensor expressions into a MathJSON-style AST." --evidence <locator> --json - THEN the command returns a verified claim entity without requiring a separate manual
concludefollowed bydismiss
Scenario: ground without evidence is refused
- WHEN an operator runs
dont ground "..."without any evidence - THEN the command is refused rather than silently creating an ungrounded claim
Requirement: Ground preserves the underlying core-verb semantics
ground SHALL be defined as a convenience orchestration over the core workflow rather than as an alternate lifecycle path. Its successful execution MUST emit the existing concluded event followed by the existing dismissed event, in that order, and it MUST respect all existing refusal conditions that would apply to the corresponding conclude and dismiss operations.
Scenario: successful ground emits the normal logical sequence
- WHEN
dont ground ...succeeds - THEN the resulting audit history reflects both claim introduction and evidence-backed verification rather than a single magical state jump
Scenario: atom rules still apply under ground
- WHEN a future
groundinvocation supplies a claim shape that would violate atom-verification invariants - THEN the command is refused under the same invariant rules that apply to the underlying lifecycle verbs
Requirement: Ground avoids confusing partial side effects by default
If ground cannot complete its verification step, the default behaviour SHALL avoid leaving behind a new partially created claim unless the operator explicitly opts into retaining that partial state in a future extension. The default sidecar workflow is optimized for high-signal fact capture rather than for littering the ledger with failed half-attempts. To satisfy this, a ground invocation SHALL execute as one atomic command transaction: either both the underlying claim-creation and evidence-backed verification effects commit, or neither commits.
Scenario: failed evidence validation leaves no new claim by default
- WHEN an operator runs
dont ground "..." --evidence <bad-locator>and the evidence is rejected before verification can complete - THEN the command fails without creating a new lingering unverified claim by default
Scenario: duplicate-equivalent claim follows conclude duplicate policy
- WHEN an operator runs
dont groundfor a statement that is equivalent to an existing claim under the project’s deduplication rules - THEN the command is refused under the same duplicate-claim policy that would apply to the underlying
concludeoperation
dont-harness-cli Specification
Purpose
Defines the invocation contract between dont and its callers — human operators, CI
pipelines, and LLM harnesses. Covers project directory resolution (DONT_DIR), config
loading, the --help / after_help surface, and the subprocess protocol that external
tools use when spawning dont as a child process.
Requirements
Requirement: Project directory resolution
The system SHALL resolve the active project directory through a deterministic two-step
lookup. First it MUST check the DONT_DIR environment variable; if set to a non-empty
value, that path is used as the project directory without walking the filesystem. If
DONT_DIR is absent or empty, the system MUST walk upward from the current working
directory until it finds a .dont/ directory with a config.toml inside it, or exhaust
the filesystem hierarchy. A walk that finds no .dont/ directory MUST produce a
config-missing error with exit code 3.
Scenario: DONT_DIR overrides filesystem walk
- WHEN
DONT_DIRis set to a non-empty path beforedontis invoked - THEN the system uses that path as the project directory without walking the current working directory upward
Scenario: filesystem walk finds nearest ancestor
- WHEN
DONT_DIRis unset and the current working directory is a subdirectory of a project - THEN the system walks upward and uses the nearest ancestor that contains
.dont/config.toml
Scenario: no project directory produces config-missing error
- WHEN
DONT_DIRis unset and the filesystem walk reaches the root without finding.dont/config.toml - THEN the process exits with code
3and the envelope hascode: "config-missing"with a remediation entry suggestingdont init
Scenario: DONT_DIR path that lacks config.toml is rejected
- WHEN
DONT_DIRpoints to a directory that exists but does not containconfig.toml - THEN the process exits with code
3and the envelope hascode: "config-missing"
Requirement: Config validation on every invocation
The system SHALL validate config.toml before executing any command payload. Validation
MUST parse the TOML file, check all known fields for type correctness and valid values,
and reject the invocation with a structured error when any field is invalid. The error
envelope MUST carry code: "config-invalid" and a message identifying the offending
field. Invalid configuration MUST be surfaced before any store mutation is attempted.
Scenario: invalid config field aborts invocation
- WHEN
config.tomlcontains a field with an invalid type or out-of-range value - THEN the process exits with code
3before any store mutation - AND the envelope has
code: "config-invalid"andmessagenames the offending field
Scenario: valid config proceeds to command execution
- WHEN
config.tomlparses and validates successfully - THEN the system proceeds to execute the requested command
Requirement: Author resolution order
The system SHALL resolve the author string for each invocation through a fixed priority
chain: the --author / -a flag (highest priority), then the $DONT_AUTHOR environment
variable, then the $USER environment variable. If none of these yields a value the
author is omitted from the event record for that invocation. The resolved author is stored
only on events produced during that invocation and does not persist to config.toml.
Scenario: explicit flag takes highest priority
- WHEN
--author agent:ci-botis supplied andDONT_AUTHOR=human:aliceis set - THEN the author recorded on the resulting event is
agent:ci-bot
Scenario: DONT_AUTHOR overrides USER
- WHEN
DONT_AUTHOR=agent:ci-botis set andUSER=aliceis also set and no--authorflag is provided - THEN the author recorded on the resulting event is
agent:ci-bot
Scenario: USER is the last fallback
- WHEN neither
--authornorDONT_AUTHORprovides a value andUSER=aliceis set - THEN the author recorded on the resulting event is
alice
Requirement: Help surface contract
The system SHALL expose two equivalent surfaces for per-command help: dont help <cmd>
and dont <cmd> --help. Both MUST produce the same output. The help output for each
command MUST include the command description, all flags with their types and defaults,
and an after_help examples block showing the canonical invocation patterns for that
command. The examples block MUST use the exact form dont <cmd> <typical-args> and MUST
cover at least the most common successful invocation and one invocation showing a
non-default flag. The global --help output (bare dont --help or dont help) MUST
list every subcommand with a one-line description and conclude with an after_help
block listing representative session-start examples.
Scenario: subcommand help is reachable two ways
- WHEN the caller runs
dont lock --help - THEN the output is identical to
dont help lock
Scenario: every command has an examples block
- WHEN the caller runs
dont <cmd> --helpfor any implemented subcommand - THEN the output contains an examples section showing at least one canonical invocation
Scenario: global help lists all subcommands
- WHEN the caller runs
dont --helpordont help - THEN the output lists every subcommand with a one-line description
- AND the output ends with a representative session-start examples block
Scenario: after_help examples use canonical flag form
- WHEN the examples block of a subcommand shows a flag
- THEN it uses the long form (e.g.
--status,--evidence) rather than the short form - AND each example is a complete, runnable
dontinvocation
Requirement: Subprocess invocation protocol
The system SHALL define a stable contract for external tools (CI pipelines, LLM harnesses,
shell wrappers) that invoke dont as a subprocess. A conformant subprocess caller MUST
always pass --json to get machine-readable output on stdout, MUST treat the process exit
code as the primary routing signal before parsing the envelope body, and MUST read the
full stdout before inspecting the exit code to avoid SIGPIPE on large outputs. On exit
code 0 the caller SHOULD read data for payload and warnings[] for non-blocking
notices. On exit code 1 the caller MUST read data.remediation[0].command and act on
it rather than retrying the same invocation. On exit codes 3 and 4 the caller MUST
stop LLM-driven retry and escalate to operator intervention. Exit code 2 indicates a
usage error in the caller’s own invocation and MUST be treated as a caller bug.
Scenario: subprocess caller routes on exit code before parsing body
- WHEN a subprocess caller receives a non-zero exit from
dont - THEN it branches first on the exit code (0/1/2/3/4) before inspecting the JSON body
Scenario: exit code 1 directs caller to remediation
- WHEN a subprocess caller receives exit code
1 - THEN it reads
data.remediation[0].commandand issues that command as the next action - AND it does not retry the refused invocation unchanged
Scenario: exit codes 3 and 4 halt LLM retry
- WHEN a subprocess caller receives exit code
3or4 - THEN it does not attempt further LLM-driven retries
- AND it surfaces the error envelope to the operator for manual intervention
Scenario: exit code 2 is treated as caller error
- WHEN a subprocess caller receives exit code
2 - THEN it treats this as a bug in its own argument construction
- AND reads
data.messageto understand the malformed argument
Scenario: subprocess caller reads full stdout before exit code
- WHEN a subprocess caller runs
dontwith--json - THEN it reads all of stdout before checking the process exit code to prevent SIGPIPE on the dont side
Requirement: Subprocess output routing
The system SHALL route all JSON envelope output to stdout and all human-readable
diagnostic text (warnings, errors in non-json mode) to stderr. When --json is active,
stdout MUST contain only the JSON envelope line and nothing else; log noise or debug text
MUST go to stderr. A subprocess caller that captures only stdout while discarding stderr
MUST receive a clean, parseable JSON line.
Scenario: JSON mode writes only the envelope to stdout
- WHEN
dont <cmd> --jsonis invoked - THEN stdout contains exactly one JSON object per invocation (or one per stdin line in NDJSON mode)
- AND no human-readable prose appears on stdout
Scenario: stderr carries warnings and diagnostic text
- WHEN
dont <cmd>is invoked in human mode and warnings are produced - THEN the warning text appears on stderr, not stdout
Scenario: subprocess can discard stderr safely
- WHEN a subprocess caller captures only stdout with
--json - THEN it receives a complete, parseable JSON envelope regardless of warning or debug output
Requirement: Environment variable reference
The system SHALL honour the following environment variables. DONT_DIR (string): path
to the project directory; overrides the filesystem walk. DONT_AUTHOR (string): default
author string used when --author is not supplied; may be in any format but <kind>:<id>
is recommended. NO_COLOR (any non-empty string): disables ANSI colour codes in human
output; see dont-cli-surface for full colour-resolution rules. CLICOLOR_FORCE=1:
forces ANSI colour even when stdout is redirected; see dont-cli-surface. No other
DONT_ variables are part of the stable contract; callers MUST NOT rely on
undocumented DONT_ variables between releases.
Scenario: DONT_DIR is the documented project-root override
- WHEN a caller sets
DONT_DIR=/tmp/myproject/.dont - THEN
dontuses that directory as the project root without walking the filesystem - AND this behaviour is stable across releases
Scenario: DONT_AUTHOR sets the default event author
- WHEN a CI pipeline sets
DONT_AUTHOR=agent:ci-pipeline - THEN all events recorded during that session carry
agent:ci-pipelineas the author unless--authoroverrides it
Scenario: undocumented DONT_ variables are not relied upon
- WHEN a caller script sets an undocumented variable such as
DONT_VERIFY_EVIDENCE_MOCK - THEN that variable is not guaranteed to exist or behave consistently in future releases
- AND the caller MUST NOT use it in production integrations
Requirement: DONT_DIR isolation for test environments
The system SHALL support test isolation through DONT_DIR. A test harness MUST be able
to point multiple concurrent dont invocations at distinct temporary directories using
DONT_DIR without those invocations interfering with each other or with the host
project. Each isolated DONT_DIR path MUST be independently initialised with dont init
before any other command is issued against it.
Scenario: two concurrent test invocations with distinct DONT_DIR values
- WHEN two
dontprocesses run concurrently withDONT_DIRpointing at different temporary directories - THEN each process operates on its own isolated project state with no cross-contamination
Scenario: test isolation does not require a parent .dont directory
- WHEN
DONT_DIRpoints at a path that is not named.dont(e.g./tmp/test-abc/) - THEN
dontaccepts that path as the project directory without requiring the name to be.dont
dont-import-surface Specification
Purpose
Defines the full dont import command family — syntax, supported adapters, output envelope,
idempotence contract, HTTP rate-limiting policy, URL-safety rules, and error-code mapping.
Import commands are lightweight grounding adapters: they fetch or read an external source and
project the result into local imported_term, reference, or prefix relations. They do not
invoke an LLM and do not require MCP. Only dont import linkml requires an auxiliary CLI tool
(see dont-linkml-import); all other adapters are self-contained.
The supported adapter set for v0.3 is:
dont import obo <path.owl|.obo|.ttl|url>— OBO/OWL/Turtle files or HTTP URLsdont import ols <ontology-prefix>— OLS REST APIdont import wikidata --entity <Qid> | --sparql <file.rq>— Wikidata entity or SPARQL querydont import openalex --work <doi> | --snapshot <path>— OpenAlex paper or local snapshotdont import bioregistry— Bioregistry prefix registrydont import jsonld <file>— local JSON-LD filedont import ttl <file>— local Turtle filedont import linkml <schema.yaml>— LinkML schema (subprocess adapter, lossy)
Every adapter produces a standard success envelope (ok: true) with an adapter, schema_name
(or equivalent source identifier), and stored count in data, plus any warnings in
warnings[]. Every error path produces a standard error envelope (ok: false) with a
deterministic code value and non-empty remediation[].
Each adapter may be disabled per-project by setting enabled = false under the corresponding
[import.<adapter>] block in config.toml. Disabled adapters refuse immediately with
code: "adapter-disabled" and a remediation showing the config stanza to re-enable.
Requirements
Requirement: Supported import command family
The system SHALL provide the following importer commands: dont import obo <path.owl|.obo|.ttl|url>, dont import ols <ontology-prefix>, dont import wikidata --entity <Qid> | --sparql <file.rq>, dont import openalex --work <doi> | --snapshot <path>, dont import bioregistry, dont import jsonld <file>, dont import ttl <file>, and dont import linkml <schema.yaml>.
Scenario: HTTP-backed ontology import
- WHEN the caller runs
dont import ols efo --json - THEN the command uses the OLS adapter contract for that ontology prefix
Scenario: local-file import variant
- WHEN the caller runs
dont import ttl ontology.ttl --json - THEN the command imports from the local Turtle file rather than requiring a network source
Scenario: obo importer accepts URL source
- WHEN the caller runs
dont import obo https://example.org/ontology.owl --json - THEN the command treats the argument as an HTTP-backed source for the OBO adapter
Scenario: openalex source forms are distinct
- WHEN the caller uses
dont import openalex --work <doi>or--snapshot <path> - THEN both forms are accepted as first-class source modes for the OpenAlex adapter
Requirement: Import writes and idempotence
The system SHALL treat import as a grounding operation that writes only to import-related local relations. Importers MUST write to imported_term, reference, or prefix as appropriate. Repeated import of the same source identity MUST be idempotent rather than duplicating imported state.
For idempotence, each importer SHALL derive a deterministic canonical_source_id with these normalization rules:
- OLS: lowercase trimmed ontology prefix
- Wikidata
--entity: uppercase trimmed QID - Wikidata
--sparql: SHA-256 hash of normalized query text where normalization removes full-line and trailing#...comments, normalizes line endings to\n, collapses contiguous whitespace to a single ASCII space, and trims leading/trailing whitespace - OpenAlex
--work: normalized DOI (trimmed, lowercased, with leadingdoi:andhttps://doi.org/removed before hashing/identity) - OpenAlex
--snapshot:realpathplus SHA-256 of file bytes - OBO/TTL/JSON-LD URL imports: normalized absolute URL (lowercased scheme/host, default port elision, dot-segment removal)
- Local file imports:
realpathplus SHA-256 of file bytes
Equivalent source identities MUST map to the same canonical_source_id. For local-file and snapshot imports, implementations MUST treat content hash as the deduplication authority when path aliases (symlink/hardlink/case variants) differ.
Scenario: import populates import relations
- WHEN an import succeeds
- THEN the resulting grounded data is written into the local import relations rather than directly into coined
termentities
Scenario: re-import is idempotent
- WHEN the caller imports the same canonical source identity twice
- THEN the second import does not duplicate previously imported rows for that source identity
Scenario: equivalent inputs map to same canonical source identity
- WHEN two importer inputs normalize to the same source identity (for example DOI case variation)
- THEN they produce the same
canonical_source_idand idempotent write behavior
Scenario: path aliases deduplicate by content identity
- WHEN the same local file bytes are imported through two path aliases
- THEN the second import is deduplicated by content-hash identity
Requirement: Import is non-LLM and non-MCP work
The system SHALL execute imports without invoking an LLM and without requiring MCP as a transport. Import commands are grounding adapters over HTTP, local files, or local subprocesses, not reasoning tasks.
Scenario: import does not spawn reasoning
- WHEN the caller runs any
dont import ...command - THEN the operation completes without emitting a
spawn_requestenvelope - AND it does not depend on harness-mediated reasoning
Requirement: Shared HTTP rate limiting
The system SHALL apply a shared network politeness contract to HTTP-backed importers. For importers that fetch over HTTP, the default behaviour MUST cap concurrency at four requests per invocation, sustain at most two requests per second per host with a burst of four, honour Retry-After on 429 and 503, and use the same User-Agent convention as evidence verification. Local-file imports MUST be exempt from HTTP rate limiting. Projects MUST be able to tighten these defaults on a per-importer basis through importer-specific configuration blocks.
Scenario: HTTP importer uses rate limits
- WHEN the caller runs an HTTP-backed importer such as
dont import wikidata --entity Q42 - THEN outbound requests observe the shared per-host concurrency and retry policy
Scenario: local snapshot is not rate-limited
- WHEN the caller runs
dont import openalex --snapshot snapshot.json - THEN the local-file import is not subject to HTTP request throttling
Scenario: importer-specific rate limits can be tightened
- WHEN the project config tightens the HTTP politeness settings for one importer
- THEN that importer uses the more restrictive per-importer limits instead of the shared defaults
Requirement: Auxiliary-tool expectation boundary
The system SHALL treat most importers as self-contained adapters, with LinkML as the explicit auxiliary-tool exception. Missing LinkML tooling MUST refuse the command with config-missing and remediation pointing at LinkML installation. dont doctor MUST report LinkML availability as a warning check rather than a hard failure so projects not using LinkML remain healthy.
Scenario: missing linkml cli refuses command
- WHEN the caller runs
dont import linkml schema.yamland thelinkmlCLI is not onPATH - THEN the command returns an error with code
config-missing - AND remediation points at installing the LinkML CLI
Scenario: doctor warns but does not fail on missing linkml
- WHEN LinkML tooling is unavailable in a project that otherwise works
- THEN
dont doctorreports that availability as a warning rather than a fail check
Requirement: Import URL scheme and destination policy
The system SHALL apply URL scheme and destination safety checks to HTTP-backed imports before issuing requests.
- Supported schemes for network fetch are
httpandhttpsonly. - Imports MUST refuse destinations resolving to loopback, link-local, multicast, or private-network address ranges (IPv4 and IPv6).
- If DNS resolution yields multiple addresses, the import MUST be refused when any resolved address is in a blocked class.
Scenario: loopback destination is refused
- WHEN a caller runs an HTTP-backed import pointing to a URL that resolves to
127.0.0.1,::1, or localhost-equivalent - THEN the command is refused before network fetch begins
Scenario: private-network destination is refused
- WHEN a caller runs an HTTP-backed import pointing to RFC1918 or ULA private address space
- THEN the command is refused before network fetch begins
Scenario: mixed DNS answer set is refused
- WHEN a URL resolves to multiple addresses and at least one address is in a blocked class
- THEN the command is refused before network fetch begins
Scenario: non-http scheme is refused
- WHEN a caller provides a URL with a non-HTTP(S) scheme
- THEN the command is refused before network fetch begins
Requirement: Import safety refusal-code mapping
The system SHALL use deterministic error-code mapping for import safety refusals:
- Unsupported URL scheme or blocked resolved destination class SHALL use
unresolvable-uri - Missing or invalid import safety configuration required to evaluate policy SHALL use
config-missing
All refusals SHALL include remediation describing a permitted endpoint or configuration correction.
Scenario: blocked destination uses unresolvable-uri
- WHEN an HTTP-backed importer blocks a destination because of scheme or resolved address class
- THEN the command returns
code: "unresolvable-uri"
Scenario: policy configuration failure uses config-missing
- WHEN the importer cannot evaluate safety policy due to missing or invalid required configuration
- THEN the command returns
code: "config-missing"
dont-init-modes Specification
Purpose
TBD - created by archiving change add-dont-operational-specs. Update Purpose after archive.
Requirements
Requirement: Init creates persistent per-project state
The system SHALL provide a per-project init operation that creates persistent project-local dont state, installs the project’s seed vocabulary snapshot, records the project’s initial operating mode as an auditable project event, writes the canonical .dont/AGENTS.md file in whole from the current generator output, and injects the managed dont block (delimited by <!-- DONT:START --> and <!-- DONT:END -->) into each configured root document listed by [harness].managed_docs in project configuration. init MUST produce byte-identical managed files to those that dont doctor --fix would produce for the same detected project state — the inputs the generator reads (configured managed_docs targets, dont version, installed rules, project mode) — so that running either command after the other with no state change is a no-op.
Scenario: init creates persistent project-local state
- WHEN an actor initializes
dontin a project directory - THEN the tool creates persistent project-local
dontstate for that directory - AND installs the seed vocabulary snapshot for that project
- AND records the project’s initial operating mode as an auditable project event
- AND writes
.dont/AGENTS.mdin whole from the current generator output - AND injects the managed
dontblock into each configured root document in[harness].managed_docs
Scenario: init preserves user-authored content in root documents
- WHEN a configured root document already exists with user-authored content and does not yet contain the managed
dontblock sentinels - AND an actor runs
dont init - THEN the tool inserts the managed block between the sentinels at a deterministic position
- AND all preexisting bytes outside the inserted block are preserved exactly
Scenario: init is consistent with doctor –fix
- WHEN a project has just been initialized by
dont init - AND the actor immediately runs
dont doctor --fixwith no other state changes - THEN the command makes no byte-level changes to
.dont/AGENTS.mdor to any configured root document
Scenario: repeated init refuses re-initialization
- WHEN an actor invokes
initfor a directory that is already initialized - THEN the command is refused as an already-initialized project rather than silently overwriting the existing project state
Scenario: init defaults to permissive mode unless strict is explicitly requested
- WHEN an actor initializes a new project without explicitly requesting strict mode
- THEN the project starts in permissive mode
Requirement: Init installs a locked seed vocabulary snapshot
The system SHALL install a seed vocabulary with the dont: prefix during initialization, SHALL snapshot that seed into project-local state, and SHALL make the snapshotted seed authoritative for that project until an explicit migration mechanism changes it.
Scenario: seed vocabulary is installed during init
- WHEN
initsucceeds for a new project - THEN the seed vocabulary is snapshotted into the project
- AND seed terms are created from that snapshot
Scenario: seed terms start locked
- WHEN the seed vocabulary is installed
- THEN each seed term enters the project in the
lockedstate
Scenario: tool upgrades do not silently rewrite the project seed snapshot
- WHEN the
donttool is upgraded after project initialization - THEN the existing project’s seed snapshot remains authoritative until an explicit seed-migration action occurs
Requirement: Canonical seed vocabulary content is constrained
The system SHALL define a canonical ten-term dont: seed vocabulary containing dont:Entity, dont:Claim, dont:Term, dont:Evidence, dont:kind_of, dont:related_to, dont:defined_as, dont:Hypothesis, dont:Retraction, and dont:external_ref, and SHALL exclude external ontology terms that would import unwanted semantics into the seed.
Scenario: seed vocabulary includes the canonical dont bootstrap terms
- WHEN the project inspects the installed seed vocabulary
- THEN it includes
dont:Entity,dont:Claim,dont:Term,dont:Evidence,dont:kind_of,dont:related_to,dont:defined_as,dont:Hypothesis,dont:Retraction, anddont:external_ref
Scenario: seed vocabulary excludes external ontology defaults
- WHEN the project inspects the installed seed vocabulary
- THEN it does not treat imported ontology defaults such as
owl:Thingorrdfs:subClassOfas part of the seed vocabulary
Requirement: Projects operate in permissive or strict mode
The system SHALL support permissive and strict operating modes with the invariant that no verified entity may depend on unresolved term references.
Scenario: permissive mode allows unresolved references at conclude time
- WHEN the project is in permissive mode
- AND an actor concludes a claim with unresolved term references
- THEN the claim may enter the project as
unverified - AND later verification remains blocked until the unresolved references are resolved
Scenario: strict mode refuses unresolved references at conclude time
- WHEN the project is in strict mode
- AND an actor concludes a claim with unresolved term references
- THEN the command is refused until those references are resolved
Scenario: verified entities never retain unresolved references
- WHEN an entity reaches
verified - THEN the project does not permit that entity to depend on unresolved term references
Requirement: Define always refuses dangling references
The system SHALL refuse define operations that reference undefined parent, related, or attribute terms regardless of project mode.
Scenario: permissive mode does not relax define references
- WHEN the project is in permissive mode
- AND an actor invokes
definewith unresolved referenced terms - THEN the command is refused
Scenario: strict mode also refuses unresolved define references
- WHEN the project is in strict mode
- AND an actor invokes
definewith unresolved referenced terms - THEN the command is refused
Requirement: Mode changes are recorded as project events
The system SHALL record the project’s initial mode and subsequent mode changes as auditable project events attached to project state.
Scenario: init records the initial mode
- WHEN a project is initialized
- THEN the chosen initial mode is recorded as an auditable project event
Scenario: later mode changes are recorded
- WHEN the project’s operating mode changes after initialization
- THEN the mode transition is recorded with the previous mode, new mode, author context, and timestamp
dont-lifecycle-verbs Specification
Purpose
TBD - created by archiving change add-dont-operational-specs. Update Purpose after archive.
Requirements
Requirement: Lock promotes verified claims to a terminal state
The system SHALL provide a lock operation that promotes a verified claim to the locked state only when the claim is in the verified state, has at least three assessed hypotheses, has at least two independent supporting evidence items, and has no derived assessment that compromises dependency integrity (stale, compromised-support, dangling-dependency, or unresolved-term).
Scenario: lock succeeds for an eligible verified claim
- WHEN an actor invokes
lockon a verified claim with at least three assessed hypotheses, at least two independent supporting evidence items, and an empty dependency-integrityderived_assessments[] - THEN the claim transitions to
locked
Scenario: lock refuses claims that do not satisfy the gate
- WHEN an actor invokes
lockon a claim that lacks the required assessed hypotheses or independent supporting evidence items - THEN the command is refused
Scenario: lock refuses non-verified claims
- WHEN an actor invokes
lockon a claim that is not in theverifiedstate - THEN the command is refused
Scenario: lock refuses already-locked claims
- WHEN an actor invokes
lockon a claim that is alreadylocked - THEN the command is refused
Scenario: lock refuses claims with stale support
- WHEN an actor invokes
lockon a verified claim whosederived_assessments[]containsstale - THEN the command is refused until the stale dependency trace is cleared
Scenario: lock refuses terms
- WHEN an actor invokes
lockon a term - THEN the command is refused because term locking is not supported for non-seed terms in this version
Requirement: Undoubt retracts explicit doubt
The system SHALL provide an undoubt operation that moves a doubted claim or term back to unverified, allowing the doubt to be retracted when it was registered in error or superseded. undoubt SHALL only target entities in the doubted state and SHALL refuse all other persisted statuses. Use reopen for ignored entities — undoubt and reopen are distinct recovery operations for distinct closure states.
Scenario: undoubt moves doubted entity to unverified
- WHEN an actor invokes
undoubton an entity whose persisted status isdoubted - THEN the entity transitions to
unverified
Scenario: undoubt refuses non-doubted entities
- WHEN an actor invokes
undoubton an entity whose persisted status isunverified,verified,ignored, orlocked - THEN the command is refused
Requirement: Reopen restores ignored entities
The system SHALL provide a reopen operation that moves an ignored claim or term to unverified so it can be reconsidered on its own merits. reopen SHALL NOT target stale or any other derived assessment, because derived assessments are computed trace results rather than persisted lifecycle states. reopen SHALL NOT unlock locked entities in v0.3.
Scenario: reopen moves ignored entity to unverified
- WHEN an actor invokes
reopenon an entity whose persisted status isignored - THEN the entity transitions to
unverified
Scenario: reopen refuses derived-stale entity
- WHEN an actor invokes
reopenon an entity whose persisted status isverifiedand whosederived_assessments[]containsstale - THEN the command is refused because
staleis not a persisted lifecycle state
Scenario: reopen refuses non-ignored entities
- WHEN an actor invokes
reopenon an entity whose persisted status isunverified,verified, ordoubted - THEN the command is refused
Scenario: reopen refuses locked entities
- WHEN an actor invokes
reopenon an entity whose persisted status islocked - THEN the command is refused because locked entities are not reopenable in v0.3
Requirement: Ignore moves entities to a terminal escape state
The system SHALL provide an ignore operation that moves a claim or term whose persisted status is unverified, verified, or doubted to the ignored state and SHALL require a non-empty, non-hedge-only reason for doing so. ignore SHALL refuse entities that are already ignored or locked.
Scenario: ignore moves eligible entity to ignored
- WHEN an actor invokes
ignoreon a claim or term with a non-empty, non-hedge-only reason - THEN the entity transitions to
ignored
Scenario: ignore requires a reason
- WHEN an actor invokes
ignorewithout a reason - THEN the command is refused
Scenario: ignore refuses hedge-only reasons
- WHEN an actor invokes
ignorewith a reason that contains only hedge language and no specific defect or justification - THEN the command is refused
Scenario: ignore refuses locked entities
- WHEN an actor invokes
ignoreon a locked entity - THEN the command is refused
Requirement: Verify-evidence checks liveness without changing status
The system SHALL provide a verify-evidence operation that checks the liveness of evidence references attached to a claim or term, records per-reference outcome details and warnings, and does not change the entity’s status.
Scenario: verify-evidence records per-reference liveness results
- WHEN an actor invokes
verify-evidencefor a claim or term with attached evidence references - THEN the tool records a per-reference outcome for those references
- AND includes any warning details associated with failed or degraded checks
- AND does not change the claim or term status
Scenario: verify-evidence returns partial results on per-reference timeout
- WHEN one evidence reference times out while others can still be checked
- THEN the command returns partial liveness results rather than aborting the whole verification run
Scenario: verify-evidence warns on stale or malformed evidence references
- WHEN the command encounters malformed, timed-out, or failing evidence references
- THEN the results include warnings describing those evidence problems
Scenario: verify-evidence fails structurally when no evidence can be checked
- WHEN an actor invokes
verify-evidencefor a target that has no attached evidence references - THEN the command fails structurally rather than reporting a successful verification run
Scenario: verify-evidence fails structurally for an unknown target
- WHEN an actor invokes
verify-evidencefor a target that does not exist - THEN the command fails structurally rather than recording liveness results
Requirement: Verify-evidence is separate from flag
The system SHALL keep evidence liveness verification separate from flag so that the flag operation remains deterministic and network-independent, and SHALL apply bounded network politeness measures when checking remote evidence.
Scenario: flag does not require live network verification
- WHEN an actor invokes
flagwith well-formed evidence references - THEN
flagbehavior does not depend on live network checks performed during that command
Scenario: verify-evidence handles network-sensitive checks
- WHEN a project wants to assess whether evidence references are still reachable
- THEN it uses
verify-evidencerather than changing theflagcontract
Scenario: verify-evidence uses bounded polite network behavior
- WHEN
verify-evidencechecks remote evidence references - THEN it uses bounded concurrency or retry behavior that avoids unbounded request flooding against cited hosts
Requirement: trust registers explicit doubt
The system SHALL provide trust to register explicit doubt about a claim or term. Skepticism recorded via trust SHALL transition an entity from unverified or verified to doubted.
Scenario: trust registers doubt with reason
- WHEN
dont trust claim:X --reason "..."is invoked - THEN the claim transitions to
doubtedand the reason is recorded in the event log
Requirement: flag (formerly dismiss) adds evidence
The system SHALL provide flag (formerly dismiss) to add evidence to an entity and transition it to verified. The dismiss verb remains supported as an alias for flag in v0.3 but is deprecated.
Scenario: flag transitions to verified with evidence
- WHEN
dont flag claim:X --evidence <uri>is invoked - THEN the claim transitions to
verifiedand the evidence is recorded
Scenario: flag on doubted entity clears doubt
- WHEN
dont flag claim:X --evidence <uri>is invoked on a doubted claim - THEN the claim transitions to
verifiedand doubt is cleared
Scenario: deprecated alias emits warning
- WHEN
dont dismissis invoked - THEN a deprecation warning is emitted to stderr suggesting
flaginstead - AND the command proceeds normally
- AND the deprecation warning goes to stderr regardless of whether
--jsonis set
dont-linkml-import Specification
Purpose
Specifies the dont import linkml <schema.yaml> adapter — the one importer that delegates to an
external subprocess rather than running as a pure-Rust in-process adapter.
The adapter lowers a LinkML YAML schema into local imported_term rows that dont can use for
CURIE resolution and rule evaluation. It is lossy by design: it grounds useful vocabulary
and constraints for dont; it is not a round-trip implementation of full LinkML semantics.
Projects needing complete LinkML fidelity should use LinkML tooling directly.
The adapter applies a three-tier feature policy:
-
Flattened silently —
is_ainheritance becomes a transitivekind_ofchain; mixins are merged into the inheriting class’s attribute set;slot_usagerefinements are expanded into per-class attribute predicates. These are expected structural lowerings, not failures. -
Imported with warning —
permissible_valuesenums, string-pattern constraints, value-range constraints, and minimum-cardinality constraints are translated approximately into local rule representations. The import result records a warning naming each approximated feature per source so consumers can spot-check the generated output. -
Refused — schemas containing SPARQL-evaluated expressions, reified slots, or
python_classinjections are rejected withcode: "linkml-unsupported-feature"and a list of every offending construct. No partial import occurs: either all terms are stored or none are.
When the LinkML CLI is unavailable the command refuses immediately with code: "config-missing"
and a remediation pointing at installation. dont doctor reports LinkML availability as a
warn check (not a fail) so projects that do not use this adapter remain healthy.
Requirements
Requirement: LinkML adapter shell-out and lowering contract
The system SHALL implement dont import linkml <schema.yaml> as a subprocess-backed adapter over the LinkML CLI. The adapter MUST shell out to LinkML tooling to derive intermediate forms, lower the result into local import relations, and MAY additionally generate Datalog rules for imported constraints.
Scenario: linkml import lowers to local relations
- WHEN the caller imports a supported LinkML schema
- THEN the adapter lowers the schema into local import relations usable by
dont
Scenario: linkml import may generate rules
- WHEN imported LinkML constraints can be represented as local rules
- THEN the adapter may emit generated Datalog rules as part of the import result
Requirement: Lossy-by-design adapter boundary
The system SHALL treat LinkML import as lossy by design rather than as a round-tripping implementation of full LinkML semantics. Projects needing full LinkML fidelity MUST use LinkML directly rather than relying on dont to preserve all semantics.
Scenario: grounding rather than round-tripping
- WHEN an operator uses
dont import linkml - THEN the adapter’s contract is to ground useful vocabulary and constraints for
dont - AND not to preserve every LinkML semantic feature losslessly
Requirement: Flattened feature tier
The system SHALL flatten certain LinkML features without warning because they are expected structural lowerings. is_a inheritance MUST become a transitive kind_of chain, mixins MUST be flattened into the inheriting class’s attribute set, and slot_usage refinements MUST be expanded into per-class attribute predicates.
Scenario: inheritance becomes kind_of chain
- WHEN a LinkML schema uses
is_a - THEN the import result represents that inheritance as a traversable
kind_ofchain
Scenario: mixins flatten silently
- WHEN a LinkML schema uses mixins
- THEN the mixin attributes are flattened into the inheriting class without producing a warning solely for that transformation
Requirement: Warning-tier feature handling
The system SHALL import certain LinkML features approximately and warn about that approximation. permissible_values enums, string patterns, value ranges, and minimum-cardinality constraints MUST be translated into local rule representations when possible, and the import result MUST record warnings naming each approximated feature per source.
Scenario: enum import warns
- WHEN the schema contains
permissible_values - THEN the adapter imports the constraint approximately
- AND records a warning naming that feature in the source schema
Scenario: pattern or range import warns
- WHEN the schema contains string-pattern or value-range constraints
- THEN the adapter lowers them into local rule forms where possible
- AND emits warnings so consumers know the translation is approximate
Requirement: Refusal-tier unsupported features
The system SHALL refuse schemas containing unsupported LinkML features that cannot be safely approximated. Expressions requiring SPARQL evaluation, reified slots, and custom python_class injections MUST trigger linkml-unsupported-feature and list the offending constructs.
Scenario: unsupported feature refuses import
- WHEN the schema uses reified slots or SPARQL-evaluated expressions
- THEN the import fails with code
linkml-unsupported-feature - AND the error identifies the unsupported constructs
Requirement: No partial import on refusal
The system SHALL avoid partial state when unsupported LinkML features are encountered. If refusal-tier features are present, the adapter MUST not import a partial subset of the schema.
Scenario: unsupported schema leaves no partial state
- WHEN
dont import linkmlrefuses because of an unsupported feature - THEN none of the schema’s terms or generated rules are imported into the local store
dont-mcp-interface Specification
Purpose
TBD - created by archiving change add-dont-harness-specs. Update Purpose after archive.
Requirements
Requirement: Optional MCP server mode
The system SHALL provide an optional dont mcp mode that runs dont as an MCP server over stdio. This mode SHALL expose the same command surface as MCP tools without redefining the underlying command semantics.
Scenario: harness starts MCP mode
- WHEN a harness launches
dont mcp - THEN
dontstarts an MCP server over stdio - AND the server exposes tool entry points corresponding to the supported
dontcommand surface
Requirement: Envelope-preserving MCP tool results
Every MCP tool exposed by dont mcp SHALL return the same Envelope JSON contract used by the direct CLI surface as its tool result payload. MCP transport MUST NOT introduce a second result schema that diverges from dont-envelope and dont-errors.
Scenario: MCP tool returns success
- WHEN an MCP client invokes a
donttool that succeeds - THEN the tool result contains the standard success envelope JSON
- AND the payload shape matches the corresponding CLI contract
Scenario: MCP tool returns refusal or error
- WHEN an MCP client invokes a
donttool that refuses or fails - THEN the tool result contains the standard error envelope JSON
- AND the remediation and error-code semantics match the direct CLI contract
Requirement: MCP is optional and non-privileged
The system SHALL treat MCP as an optional transport for harnesses that prefer MCP over direct CLI calls. MCP mode MUST NOT become the privileged or canonical integration surface; harnesses using generic shell execution SHALL retain the same behavioural contract.
Scenario: harness uses direct CLI instead of MCP
- WHEN a harness invokes
dontthrough direct CLI commands rather thandont mcp - THEN the harness receives the same behavioural surface and envelope contracts
- AND the absence of MCP does not reduce command availability
dont-payload-types Specification
Purpose
TBD - created by archiving change add-dont-data-model-specs. Update Purpose after archive.
Requirements
Requirement: ClaimView payload
The system SHALL return a ClaimView payload (envelope_kind: "claim") containing: id, entity_kind: "claim", statement, status (persisted lifecycle status only), derived_assessments[], confidence (float | null; null when no LLM-authored value was provided), atoms[] (each with idx, text, status; atom-level evidence[] is stored but not inlined in ClaimView — use dont why for atom-level evidence detail), hypotheses[] (each with idx, text, assessment where assessment = {supporting: string[], refuting: string[]}), evidence[] (each with source_uri, kind, supports, quote), depends_on[], provenance, created_at, updated_at, and applicable_rules. All array fields SHALL always be present (possibly empty), never omitted. derived_assessments[] SHALL contain zero or more names from dont-status-lifecycle (stale, compromised-support, dangling-dependency, unresolved-term) computed on demand. Atom status SHALL be a three-value sub-lattice (unverified, doubted, verified) — atoms cannot become stale, locked, or ignored. The applicable_rules object SHALL be keyed by rule name with values discriminated by rule_kind (see applicable_rules contract). Future minor versions MAY add new fields to ClaimView; such additions SHALL be optional from the parser’s perspective.
Scenario: ClaimView with atoms
- WHEN
dont show claim:X --jsonis run on a claim with 3 atoms - THEN the payload contains
atoms[]with 3 entries, each carryingidx,text, andstatus
Scenario: ClaimView includes applicable rules
- WHEN a claim has a
lockablerule that is not met - THEN
applicable_rules.lockablehasrule_kind: "gate",met: false, andunmetlisting failing clauses
Scenario: ClaimView separates status from derived assessment
- WHEN a verified claim depends on a doubted upstream entity
- THEN the payload has
status: "verified" - AND
derived_assessments[]containsstale
Scenario: ClaimView includes evidence array
- WHEN a claim has been dismissed with evidence
- THEN the payload contains
evidence[]entries withsource_uri,kind,supports, and optionalquote
Scenario: ClaimView updated_at reflects latest transition
- WHEN a claim is trusted after creation
- THEN
updated_atin the ClaimView is later thancreated_at
Requirement: TermView payload
The system SHALL return a TermView payload (envelope_kind: "term") containing: id, entity_kind: "term", curie, definition, kind_of[], related_to[], status (persisted lifecycle status only), derived_assessments[], confidence (float | null), provenance, created_at, and applicable_rules. derived_assessments[] has the same computed-on-demand contract as ClaimView. TermView intentionally omits updated_at — term status transitions are tracked through the event history (see dont why).
Scenario: TermView includes kind_of hierarchy
- WHEN
dont show term:X --jsonis run on a term withkind_of: ["dont:Term"] - THEN the payload contains
kind_of: ["dont:Term"]
Scenario: TermView includes applicable rules
- WHEN a term has an
unresolved-termsgate rule that is met - THEN
applicable_rules["unresolved-terms"]hasrule_kind: "gate",met: true,unmet: []
Requirement: EventView payload
The system SHALL return an EventView payload (envelope_kind: "event") containing: entity_id, tx, event_kind, at, author, reason (nullable), evidence_uri (nullable), and spawn_request_id (nullable).
Scenario: EventView for a trust event
- WHEN a claim is trusted and the event is queried
- THEN the payload contains
event_kind: "trusted",author,at, andreason
Scenario: EventView for a spawn event
- WHEN a spawn request is issued and the event is queried
- THEN the payload contains
event_kind: "spawn-requested"andspawn_request_id
Requirement: SpawnRequest payload
The system SHALL return a SpawnRequest payload (envelope_kind: "spawn_request") containing: request_id (prefixed spawn:), request_kind (one of assume, overlook, guess), entity_id (present for assume and overlook; absent for guess which targets a question, not an entity), context (with clean: bool, prompt, allowed_tools[], forbidden_tools[], model_hint, max_tool_calls), return_to (string identifying the originating harness, e.g. "claude-code"), issued_at, and expires_at. The expires_at SHALL be computed as issued_at + config.harness.spawn_timeout_hours. allowed_tools[] and forbidden_tools[] are open string arrays — parsers MUST NOT fail on unrecognised tool names.
Scenario: SpawnRequest for assume
- WHEN
dont assume claim:X --jsonis run - THEN the payload has
request_kind: "assume"andcontext.allowed_toolsincludesdont.dismiss,dont.trust,dont.show,web_searchandcontext.forbidden_toolsincludesdont.conclude,dont.lock,dont.reopen
Scenario: SpawnRequest for overlook
- WHEN
dont overlook claim:X --jsonis run - THEN the payload has
request_kind: "overlook"andcontext.promptinstructs the subagent to assume the claim is wrong and try to explain why
Scenario: SpawnRequest for guess has no entity_id
- WHEN
dont guess "What is X?" --jsonis run - THEN the payload has
request_kind: "guess"andentity_idis absent
Scenario: SpawnRequest carries expiry
- WHEN a spawn request is issued with
spawn_timeout_hours: 24 - THEN
expires_atisissued_atplus 24 hours
Requirement: PrimeView payload
The system SHALL return a PrimeView payload (envelope_kind: "prime") containing: project (name), mode ("permissive" or "strict"; parsers MUST handle unknown mode values gracefully), status_counts (map containing exactly the persisted-status keys unverified, doubted, verified, locked, and ignored with integer counts), assessment_counts (map containing exactly the derived-assessment keys stale, compromised-support, dangling-dependency, and unresolved-term with integer counts), rules (keyed by severity "strict" and "warn" with rule name arrays), ontologies[] (each with prefix and refreshed), blocking[] (entries with {id, statement, status} for entities currently in doubted state), pending_spawns (count), harness_mode (boolean), and invariants[] (human-readable invariant summaries).
Scenario: PrimeView on fresh project
- WHEN
dont prime --jsonis run on a just-initialized project - THEN
status_countsshows all zeros,assessment_countsshows all zeros,blockingis empty, andpending_spawnsis 0
Scenario: PrimeView reports blocking entities
- WHEN 3 claims are in
doubtedstatus - THEN
blocking[]contains 3 entries withid,statement, andstatus: "doubted"
Scenario: PrimeView reports harness mode
- WHEN
DONT_HARNESS=1is set - THEN
harness_modeistrue
Scenario: PrimeView reports active rules by severity
- WHEN a project has
unresolved-termsas strict andcorrelated-erroras warn - THEN
rules.strictcontains"unresolved-terms"andrules.warncontains"correlated-error"
Requirement: WhyView payload
The system SHALL return a WhyView payload (envelope_kind: "why") containing: entity (full ClaimView or TermView), history[] (EventView entries, oldest first), applicable_rules (same structure as in ClaimView/TermView; SHALL be identical to entity.applicable_rules — both are present for consumer convenience), and remediation[] (per-unmet-rule suggestions as {rule_name, command, description}). On a fully-satisfied entity, remediation SHALL be an empty array. WhyView remediation[] entries are per-unmet-rule suggestions, distinct from the error-envelope remediation[] which exists only on refusals.
Scenario: WhyView with unmet lockable rule
- WHEN
dont why claim:X --jsonis run andlockableis not met - THEN
remediation[]contains an entry withrule_name: "lockable"and a suggested command
Scenario: WhyView on fully satisfied entity
- WHEN
dont why claim:X --jsonis run and all rules pass - THEN
remediationis an empty array and allapplicable_rulesshow as met
Scenario: WhyView includes full event history
- WHEN a claim has 5 events in its history
- THEN
history[]contains 5 EventView entries ordered oldest first
Requirement: ClaimsList payload
The system SHALL return a ClaimsList payload (envelope_kind: "claims") containing: as_of (RFC 3339 timestamp), count (integer), and claims[] (array of ClaimView, default sort order: created_at descending).
Scenario: ClaimsList with status filter
- WHEN
dont list --status verified --jsonis run - THEN the payload contains only claims with
status: "verified"andcountmatches the array length
Requirement: DoctorReport payload
The system SHALL return a DoctorReport payload (envelope_kind: "doctor") containing: cli_version, checks[] (each with name, status in {pass, warn, fail}, and detail), and summary (counts of pass, warn, fail). Required check names include at minimum: substrate, rules_compile, seed_snapshot, pending_spawns, and remediation_invariant. Additional checks (e.g. aux_linkml, aux_sparql) SHALL be present when the corresponding tool is configured. Parsers MUST ignore unknown entries in checks[]. dont doctor --strict SHALL exit non-zero on any warn or fail; without --strict, only fail is non-zero.
Scenario: DoctorReport with all passing
- WHEN
dont doctor --jsonis run on a healthy project - THEN all
checks[]havestatus: "pass"andsummary.failis 0
Scenario: DoctorReport strict mode on warn
- WHEN
dont doctor --strictis run and one check hasstatus: "warn" - THEN the command exits non-zero
Scenario: DoctorReport reports missing auxiliary tool
- WHEN the
linkmlCLI is not on PATH - THEN
checks[]includes an entry withname: "aux_linkml"andstatus: "warn"
Requirement: ExamplesList payload
The system SHALL return an ExamplesList payload (envelope_kind: "examples") containing examples[] where each entry has title, summary, and transcript[] — a list of {command, envelope} pairs representing a worked example session.
Scenario: ExamplesList contains worked examples
- WHEN
dont examples --jsonis run - THEN the payload contains at least one example with title, summary, and a non-empty transcript
Requirement: SchemaDoc payload
The system SHALL return a SchemaDoc payload (envelope_kind: "schema_doc") containing schema_name, schema_version, and json_schema (a full JSON Schema document, draft 2020-12). dont schema with no argument SHALL return envelope_kind: "empty" with a hints[] list of available schema names.
Scenario: SchemaDoc for claim
- WHEN
dont schema claim --jsonis run - THEN the payload contains
schema_name: "claim"and a valid JSON Schema document
Scenario: schema with no argument lists names
- WHEN
dont schema --jsonis run with no argument - THEN the response has
envelope_kind: "empty"andhints[]listing available schema names
Requirement: Applicable rules gate/flag contract
Each entry in applicable_rules SHALL be discriminated by a rule_kind field (namespaced per dont-data-model Kind disambiguation). Two rule kinds exist in v0.3: "gate" (the rule gates a transition, payload: {rule_kind, met: bool, unmet: [string]} where unmet lists failing clauses) and "flag" (the rule flags a condition without gating, payload: {rule_kind, flagged: bool, detail?: string}). Command refusals and rule evaluations MUST evaluate against the combined computed trace (including derived_assessments[]), not merely the persisted status. New rule kinds SHALL require a minor envelope_version bump. Parsers MUST default-branch on unknown rule_kind values.
Scenario: gate rule that is not met
- WHEN a claim’s
lockablerule requires 3 hypotheses but only 1 exists - THEN
applicable_rules.lockablehasrule_kind: "gate",met: false,unmet: ["needs >=3 hypotheses; has 1"]
Scenario: flag rule that is not flagged
- WHEN a claim’s evidence comes from independent sources
- THEN
applicable_rules["correlated-error"]hasrule_kind: "flag",flagged: false
Scenario: unknown rule kind uses default branch
- WHEN a parser encounters an
applicable_rulesentry with an unknownrule_kind - THEN it handles it through a default branch rather than failing
Requirement: Suggest-term search behaviour
dont suggest-term "<string>" SHALL search (1) the local term table, (2) the local imported_term table, and (3) optionally, enabled import sources via their HTTP APIs. It SHALL return a ranked list. The tool SHALL NOT require suggest-term to have been run before define — the forcing function is soft — but the orientation block and prime output SHALL recommend it.
Scenario: suggest-term finds no matches
- WHEN
dont suggest-term "intrinsic curvature tensor" --jsonis run on a fresh project - THEN the response has
envelope_kind: "empty"
Scenario: suggest-term finds a coined term
- WHEN
dont suggest-term "Ricci" --jsonis run andproj:RicciTensorexists - THEN the response includes
proj:RicciTensorin the ranked results
Scenario: suggest-term searches imported terms
- WHEN
dont suggest-term "apoptosis" --jsonis run andGO:0006915is inimported_term - THEN the response includes
GO:0006915in the ranked results
Requirement: ConcludeInput schema
ConcludeInput SHALL accept: statement (required string), atoms? (string array), refs? (string array of URIs or CURIEs), confidence? (float 0.0-1.0), depends_on? (ClaimId array), session_id? (string), author? (AuthorString), origin? (string).
Scenario: conclude with atoms and refs
- WHEN
dont conclude "..." --atom "A" --atom "B" --ref proj:X --jsonis run - THEN the input is validated as
{statement, atoms: ["A","B"], refs: ["proj:X"]}
Scenario: conclude with optional confidence
- WHEN
dont conclude "..." --confidence 0.8 --jsonis run - THEN the claim is created with
confidence: 0.8
Requirement: DefineInput schema
DefineInput SHALL accept: curie (required string), doc (required string), kind_of? (string array of CURIEs), related_to? (string array of CURIEs), attribute? (AttrSpec array), author? (AuthorString).
Scenario: define with kind_of reference
- WHEN
dont define proj:X --doc "..." --kind-of dont:Term --jsonis run - THEN the input is validated with
curie: "proj:X"andkind_of: ["dont:Term"]
Requirement: TrustInput schema
TrustInput SHALL accept: entity_id (required EntityId), reason (required string), atom_idx? (integer for doubting a single atom), author? (AuthorString).
Scenario: trust targeting a single atom
- WHEN
dont trust claim:X --reason "..." --atom 2 --jsonis run - THEN the input targets atom index 2 specifically
Requirement: DismissInput schema
DismissInput SHALL accept: entity_id (required EntityId), evidence (required non-empty EvidenceSpec array where EvidenceSpec = {uri, kind?, quote?, supports?}), atom_idx? (repeatable integer array naming atoms to verify), note? (string), author? (AuthorString).
Scenario: dismiss with multiple atoms and evidence
- WHEN
dont dismiss claim:X --atom 0 --atom 1 --evidence uri1 --evidence uri2 --jsonis run - THEN atoms 0 and 1 are targeted and both evidence URIs are attached
Requirement: Lifecycle verb input schemas
The system SHALL accept: LockInput { entity_id, author? } (entity must resolve to a claim; terms refuse), ReopenInput { entity_id, author? }, IgnoreInput { entity_id, reason, author? }, VerifyEvidenceInput { entity_id, timeout_seconds? }.
Scenario: lock refuses terms
- WHEN
dont lock term:X --jsonis run - THEN the command refuses with
wrong-entity-kind
Scenario: ignore requires reason
- WHEN
dont ignore claim:X --jsonis run without a reason - THEN the command refuses because
reasonis required
Requirement: Spawn verb input schemas
The system SHALL accept: AssumeInput { entity_id, model_hint?, max_tool_calls? }, OverlookInput { entity_id, model_hint?, max_tool_calls? }, GuessInput { question, n?, temperature? }. The CLI MUST enforce the negative-shell phrase context globally: every command MUST read coherently as dont <verb> (e.g. dont assume, dont guess), and its documentation MUST define behavior according to this phrase meaning.
Scenario: assume with model hint
- WHEN
dont assume claim:X --model-hint verifier --jsonis run - THEN the spawn request carries
context.model_hint: "verifier"
Scenario: guess with multiple candidates
- WHEN
dont guess "What is X?" --n 3 --jsonis run - THEN the spawn request asks for 3 diverse candidates
Requirement: ImportInput schema
ImportInput SHALL carry at minimum source (required string identifying the importer: obo, ols, wikidata, openalex, bioregistry, jsonld, ttl, linkml). Additional fields vary by importer and are defined per-importer (see future import spec). The system SHALL validate the source value against the known importer set and refuse with code: "usage" on unknown importers.
Scenario: import with known source
- WHEN
dont import ols GO --jsonis run - THEN the input is validated with
source: "ols"and importer-specific fields
Scenario: import with unknown source
- WHEN
dont import unknown-source --jsonis run - THEN the command refuses with
code: "usage"
Requirement: EntityId and AuthorString type conventions
EntityId SHALL be claim:<ULID> or term:<ULID>. AuthorString SHALL be <actor-kind>:<id> as defined in the author identity format (see dont-data-model; this requirement defers to dont-data-model as the single source of truth for AuthorString and SHALL NOT duplicate its constraints). Cardinality notation: T[] is a possibly-empty array; T[] in a field marked required (no ?) is non-empty.
Scenario: EntityId accepts claim prefix
- WHEN an input contains
entity_id: "claim:01HX05A9K8VP" - THEN validation accepts it as a valid EntityId
Scenario: EntityId rejects unprefixed ULID
- WHEN an input contains
entity_id: "01HX05A9K8VP" - THEN validation rejects it because the prefix is missing
Scenario: required array must be non-empty
- WHEN
DismissInput.evidenceis provided as an empty array - THEN validation rejects it because required arrays must be non-empty
Requirement: Evidence entries may carry structured repository locator details
JSON payloads that expose evidence entries SHALL allow optional structured repository locator fields in addition to the existing URI summary form. When repository-grounded evidence is used, each evidence entry MAY be an object containing kind: "repo-file", repository-relative path, any available line_start, line_end, anchor, or note, and drift-audit fields such as excerpt, fingerprint, and audit.status. Parsers MUST ignore absent optional fields and MUST preserve present structured fields without reparsing display text.
Scenario: claim view exposes repository locator fields
- WHEN
dont show <entity-id> --jsonreturns a claim grounded in repository evidence - THEN the relevant evidence entry can include structured top-level repository locator fields with path and line-span details
Scenario: why view preserves drift-audit fields
- WHEN
dont why <entity-id> --jsonreturns evidence whose stored fingerprint no longer matches the current source slice - THEN the evidence entry can include structured drift-audit fields such as
audit.statuswithout changing the entity’s persisted status
Requirement: Forward-compatibility for payload and input shapes
Future minor versions MAY add new fields to output payload types (ClaimView, TermView, etc.); such additions SHALL be optional from the parser’s perspective and MUST NOT require parser changes to remain functional. Parsers MUST ignore unknown fields. Optional input schema fields (marked ?) MUST NOT become required except in a major version bump. All undocumented fields in payload types MUST be treated as optional by consumers.
Scenario: new output field does not break old parser
- WHEN a new minor version adds a field
tags[]to ClaimView - THEN parsers that do not recognise
tagscontinue to function without error
Scenario: optional input field remains optional across minor versions
- WHEN
ConcludeInput.confidenceis optional in v0.3 - THEN it remains optional in v0.3.x and v0.4; making it required needs a major version bump
Requirement: GroundInput schema
GroundInput SHALL accept: statement (required string), evidence (required non-empty EvidenceSpec array), and author? (AuthorString). In the initial version it SHALL NOT add ground-specific convenience fields for atoms, dependencies, or references; callers needing those richer shapes MUST use the underlying core verbs directly.
Scenario: ground with statement and evidence validates
- WHEN
dont ground "..." --evidence uri1 --jsonis run - THEN the input is validated as
{statement, evidence: [{uri: "uri1"}]}
Scenario: ground rejects empty evidence list
- WHEN
GroundInput.evidenceis provided as an empty array - THEN the command is refused rather than creating an ungrounded claim
Requirement: TraceView payload
The system SHALL return a TraceView payload (envelope_kind: "trace") containing: entity_id, blockers[], and as_of. Each blocker entry MUST identify the blocker kind, the starting entity, the traversed path, the blocking node or unresolved reference, and remediation[] entries in the normal {command, description} shape. The payload MAY additionally include bounded cycle notes or other explanatory metadata that does not change the core blocker semantics.
Scenario: trace payload includes one blocker path
- WHEN
dont trace <entity-id> --jsonis run on an entity with one currently relevant blocker path - THEN the payload contains one blocker entry with path and remediation details
Scenario: healthy entity trace has empty blockers
- WHEN
dont trace <entity-id> --jsonis run on an entity with no relevant blocker paths - THEN the payload succeeds with
blockers: []
Requirement: Human-mode output structure
The system SHALL provide a structured human-readable output format that presents entity details in a “card” or “table” view by default. This view SHALL include the entity’s ID, status, content (statement or definition), and any attached evidence, hypotheses, or atoms.
Scenario: ClaimView in human mode
- WHEN
dont show claim:Xis invoked without the--jsonflag - THEN the output displays a human-readable card including the claim’s statement and current status
Requirement: Hypothesis and Atom payloads
The system SHALL support structured payloads for hypotheses and atoms, including their assessment status and supporting/refuting evidence references.
Scenario: viewing assessed hypotheses
- WHEN a claim’s hypotheses are displayed in human or JSON mode
- THEN each entry includes its index, text, and the lists of supporting/refuting evidence identifiers
dont-project-config Specification
Purpose
TBD - created by archiving change add-dont-project-layout-specs. Update Purpose after archive.
Requirements
Requirement: Project identity and mode configuration
The system SHALL expose a [project] configuration block containing at least the project name and the current operating mode. The mode MUST support permissive and strict, and changing the configured mode MUST affect the runtime gating behaviour defined by the mode-dependent command specs without changing the stored data shape.
Scenario: permissive mode configured in project block
- WHEN
config.tomlsets[project].mode = "permissive" - THEN runtime behaviour follows the permissive-mode gating rules referenced by the command and rule specs
Scenario: strict mode does not change data shape
- WHEN a project switches from permissive to strict in configuration
- THEN existing stored entities remain valid as data
- AND only subsequent gating behaviour changes according to strict-mode rules
Requirement: Mode changes are auditable runtime events
The system SHALL treat initial mode establishment and subsequent mode changes as auditable runtime events. When a command start detects that the configured mode differs from the previously recorded mode, the tool MUST record a mode_changed event against the synthetic project entity with old mode, new mode, author, and timestamp. The synthetic project entity MUST be a reserved internal entity representing the project itself rather than a claim or term, and it is the canonical target for project-wide audit events.
Scenario: init records initial mode
- WHEN
dont initestablishes the project’s first mode - THEN the tool records a
mode_changedevent for that initial mode establishment
Scenario: config mode edit is recorded on next invocation
- WHEN an operator edits
config.tomlto change mode and then runs the nextdontcommand - THEN the tool records a
mode_changedevent reflecting the old and new modes
Requirement: Output default configuration
The system SHALL expose an [output] block for default output formatting. The default format MUST be able to select JSON as the project’s preferred output form.
Scenario: json default format configured
- WHEN
[output].default_format = "json" - THEN the project defaults to machine-readable output unless an invocation overrides it
Requirement: Direct-mode LLM configuration
The system SHALL expose an [llm] block for provider and model selection used only in direct mode. Harness mode MUST ignore this block for spawn-producing reasoning commands.
Scenario: llm config ignored in harness mode
- WHEN the project runs in harness mode
- THEN the configured
[llm]provider and model do not override the harness-managed spawn flow
Scenario: llm config used in direct mode
- WHEN direct mode is active for a spawn-producing command
- THEN the tool consults
[llm]to decide which provider/model to call
Requirement: Harness configuration surface
The system SHALL expose a [harness] block including the managed-doc targets and the spawn-timeout window. Managed-doc targets MUST determine which root files may receive the shorter dont block (those files become inputs to dont doctor’s managed-docs check and to dont doctor --fix), and the timeout window MUST inform spawn_request.expires_at computation.
Scenario: managed docs list targets root files
- WHEN
[harness].managed_docslistsAGENTS.mdandCLAUDE.md - THEN those files are eligible targets for
dont doctor --fixand for the managed-docs staleness check run bydont doctor
Scenario: spawn timeout config drives expiry
- WHEN
[harness].spawn_timeout_hours = 24 - THEN newly issued spawn requests expire twenty-four hours after issuance unless resolved earlier
Requirement: Rule severity configuration
The system SHALL expose a [rules] block for project-level severity assignment. This block MUST be able to declare strict and warn rule lists, while respecting the non-overridable boundaries defined by the rule-engine capability and the mode-driven default for ungrounded.
Scenario: correlated-error configured as warning
- WHEN
[rules].warnincludescorrelated-error - THEN that rule contributes warning entries rather than strict refusals unless another rule-engine constraint forbids override
Scenario: ungrounded mode default still applies
- WHEN the project omits an explicit override for
ungrounded - THEN its effective severity is still derived from the project mode
Requirement: Trust hedge configuration
The system SHALL expose [trust.hedges] patterns for the verb-level anti-hedge validator on trust --reason. Projects MAY extend these patterns for domain-specific epistemic mush, but this configuration MUST remain a verb-level validation aid rather than a rule-engine rule list.
Scenario: trust hedge pattern list extended
- WHEN a project adds a new hedge phrase under
[trust.hedges].patterns - THEN
dont trusttreats that phrase as part of the anti-hedge validation surface
Requirement: Storage tuning configuration
The system SHALL expose a [storage] block for store busy-retry tuning, including retry attempts and retry backoff base duration.
Scenario: storage retry tuned
- WHEN
[storage]sets busy retry attempts and base delay - THEN store-conflict recovery uses those configured values when applying its retry policy
Requirement: Evidence verification tuning
The system SHALL expose a [verify_evidence] block for network politeness and timeout tuning, including concurrency, per-host request rate, per-host burst, retry limit for 429/503, and default timeout.
Scenario: verify-evidence timeout configured
- WHEN
[verify_evidence].default_timeout_sis set - THEN
dont verify-evidenceuses that configured default when the caller does not supply--timeout
Scenario: verify-evidence politeness configured
- WHEN the project sets per-host rate and concurrency values
- THEN evidence verification uses those configured network politeness values
Requirement: Import adapter configuration
The system SHALL expose an [import] configuration block for adapter enablement and endpoint/base settings. The configuration surface MUST be able to represent importer-specific settings such as OLS base URL, Wikidata endpoint, OpenAlex base URL, and LinkML enablement. It MUST also support importer-specific sub-blocks for tightening shared defaults such as rate limits or adapter-local settings.
Scenario: importer endpoint configured
- WHEN
[import].wikidata.endpointis set - THEN the Wikidata importer uses that configured endpoint
Scenario: linkml adapter can be disabled or enabled
- WHEN
[import].linkml.enabledis configured - THEN the project can explicitly control availability of the LinkML adapter surface
Scenario: importer-specific sub-block tightens defaults
- WHEN the project config defines a per-importer sub-block such as
[import.wikidata]with stricter network settings - THEN that importer uses the stricter values for its own operations without changing the global defaults for other importers
Requirement: Define shape configuration
The system SHALL expose a [define.shape] configuration block for controlling the verb-level label-shape validators on dont define. Of the five validators, four are individually disableable (check_indefinite, check_punctuated, check_compound, check_sentence); term-label-empty has no config toggle because disabling a whitespace-only guard provides no value. Each configurable validator SHALL default to true (enforcing at refuse-level when --label is supplied, warn-level when only --doc is available). Setting any to false disables that validator entirely. The block SHALL also allow extending the list of compound-structure markers recognized by term-compound-undeclared through a compound_markers list. Setting compound_markers to an empty list disables marker matching entirely for that validator (equivalent to check_compound = false); to extend the default markers without replacing them, list the defaults plus any additions.
Scenario: validator disabled via config
- WHEN
[define.shape].check_sentence = false - THEN
dont definedoes not fireterm-label-sentenceeven when a verb token appears in the label
Scenario: default config enforces all checks
- WHEN the project omits
[define.shape]fromconfig.toml - THEN all four validators are active with their default refuse-on-label / warn-on-doc behaviour
Scenario: compound markers list is extensible
- WHEN a project adds
"a quintuple"to[define.shape].compound_markers - THEN
term-compound-undeclaredtreats"a quintuple"as a compound marker requiring a variable list
Requirement: Nonfunctional label rule configuration
The system SHALL expose a [rules.term_nonfunctional] configuration block for the term-nonfunctional-label warn rule. The block SHALL default to enabled = false. When enabled = true, the patterns list drives the token-match heuristic. The project MAY extend or replace the default pattern list to capture domain-specific non-functional relationship phrasing.
Scenario: rule enabled via config
- WHEN
[rules.term_nonfunctional].enabled = true - THEN
term-nonfunctional-labelemits warnings when a defined term’s label matches a configured pattern
Scenario: rule disabled by default
- WHEN the project omits
[rules.term_nonfunctional]fromconfig.toml - THEN
term-nonfunctional-labeldoes not fire
Scenario: pattern list is extensible
- WHEN a project adds
"wraps a"to[rules.term_nonfunctional].patterns - THEN labels containing
wraps aas a standalone phrase are candidates for the warning
dont-project-layout Specification
Purpose
TBD - created by archiving change add-dont-project-layout-specs. Update Purpose after archive.
Requirements
Requirement: Canonical project directory
The system SHALL use a .dont/ directory at the project root as the canonical per-project home for persistent dont state and documentation.
Scenario: project initialisation creates canonical root
- WHEN a project is initialised for
dont - THEN the project contains a
.dont/directory as the tool-owned root for per-project state
Requirement: Core layout entries and roles
The system SHALL reserve the following entries within .dont/: the primary store path, config.toml, AGENTS.md, seed/, vocab/, rules/, imports/, sessions/, and schemas/. Each entry MUST exist to serve its documented role: persistent store, project configuration, canonical LLM-facing docs, seed vocabulary snapshot, coined vocabulary files, rule files, import manifests, spawn/session scratch, and JSON Schema documents respectively.
Scenario: canonical agent docs live under .dont
- WHEN a harness or operator looks for the primary LLM-facing documentation
- THEN
.dont/AGENTS.mdis the canonical source
Scenario: rule files live under rules directory
- WHEN a project ships default or custom rules
- THEN those rule artifacts live under
.dont/rules/
Scenario: schema docs live under schemas directory
- WHEN the project exposes JSON Schemas for envelopes or payloads
- THEN those schema documents live under
.dont/schemas/
Requirement: Seed vocabulary snapshot
The system SHALL keep the installed seed vocabulary under .dont/seed/ as a project-local snapshot rather than relying solely on a global installation copy.
Scenario: seed snapshot exists in project
- WHEN a project has been initialised
- THEN
.dont/seed/dont-seed.yamlexists as the local seed vocabulary snapshot for that project
Requirement: Root-document managed block relationship
The system SHALL treat root-level project documents such as AGENTS.md and CLAUDE.md as optional hosts for a shorter managed dont block rather than as the canonical home of the full docs. Those root docs MUST point readers at .dont/AGENTS.md and be eligible targets for managed-block rewriting.
Scenario: root agents doc contains managed pointer
- WHEN the project includes a root
AGENTS.md - THEN it contains the shorter
dontmanaged block rather than replacing.dont/AGENTS.mdas the canonical document
Scenario: claude doc can also host managed block
- WHEN the project includes
CLAUDE.md - THEN it may also receive the managed
dontblock pointing back to.dont/AGENTS.md
Requirement: Rules, imports, and sessions are project-local state
The system SHALL keep rule artifacts, import manifests/provenance, and spawn/session scratch within .dont/ so that a project’s dont state remains self-contained and portable with the repository.
Scenario: import cursors remain in project
- WHEN import adapters track manifests or cursors
- THEN those files live under
.dont/imports/rather than in a global cache
Scenario: spawn scratch remains in project
- WHEN spawn requests or clean-context session artifacts are persisted
- THEN those artifacts live under
.dont/sessions/
dont-rule-claim-schema Specification
Purpose
TBD - created by archiving change add-rule-claim-schema. Update Purpose after archive.
Requirements
Requirement: Six semantic slots for rule-describing claims
The system SHALL define a six-slot semantic schema for claims that describe the behavior of a dont rule. The six slots are: INVOCATION MODEL, TRIGGER CONDITION, PRECONDITION GUARD, EVALUATION MODEL, MODE/CONFIG, and BOUNDARY. TRIGGER CONDITION and MODE/CONFIG SHALL be mandatory — a rule claim that omits both is incomplete by definition. MODE/CONFIG is satisfied by either a [CONFIG] marker (covers enablement: on/off by default), a [MODE] marker (covers severity behavior across project modes), or both — either sub-marker alone meets the requirement. INVOCATION MODEL, PRECONDITION GUARD, EVALUATION MODEL, and BOUNDARY SHALL be optional, each carrying an explicit default that the rule-claim-structure rule applies when the slot is absent.
The rule-claim-structure rule SHALL validate mandatory slot marker presence only. It SHALL NOT evaluate the accuracy of slot content — a structurally complete claim with incorrect content passes the rule. Content accuracy is a human responsibility enforced through the claim evidence and doubt mechanisms.
Slot defaults when omitted:
- INVOCATION MODEL: the rule runs as a background lint evaluated by
dont prime - PRECONDITION GUARD: the rule evaluates all inputs without a silent-skip threshold
- EVALUATION MODEL: the rule is stateless and demand-evaluated
- BOUNDARY: no explicit sibling-rule boundary exists for this rule
Scenario: trigger slot absence is a schema violation
- WHEN a claim is tagged as a rule claim and does not contain a TRIGGER CONDITION slot marker
- THEN the
rule-claim-structurerule flags it as a schema violation
Scenario: mode/config slot absence is a schema violation
- WHEN a claim is tagged as a rule claim and contains neither a MODE slot marker nor a CONFIG slot marker
- THEN the
rule-claim-structurerule flags it as a schema violation
Scenario: optional slot omission applies known default
- WHEN a rule claim omits the EVALUATION MODEL slot
- THEN the claim is treated as asserting that the rule is stateless and demand-evaluated
- AND the
rule-claim-structurerule does not flag the omission as a violation
Scenario: optional slot omission is not a violation when default is correct
- WHEN a rule claim for
ungroundedomits the INVOCATION MODEL slot - THEN the claim inherits the background-lint default
- AND no warning is emitted because the default is correct for
ungrounded
Requirement: Rule claim tagging via term dependency
The system SHALL support tagging a claim as a rule claim by including the term:uuid of the rule-claim-type term in its depends_on entries. The rule-claim-type term SHOULD be defined with dont define before creating rule claims; the resulting term:uuid is the correct depends_on value. Using the bare string rule-claim-type or an unregistered CURIE as a depends_on entry will trigger the unresolved-terms rule and is not a valid tag. The rule-claim-structure rule SHALL only evaluate claims that carry this tag; untagged claims SHALL be ignored by the rule regardless of their content.
Scenario: tagged claim is evaluated by rule-claim-structure
- WHEN a claim lists
rule-claim-typein itsdepends_on - AND the
rule-claim-structurerule is enabled - THEN the rule checks the claim’s text for mandatory slot markers
Scenario: untagged claim is not evaluated
- WHEN a claim does not include
rule-claim-typein itsdepends_on - THEN
rule-claim-structureignores the claim even if its text resembles a rule description
Requirement: Slot marker format and template
The system SHALL publish a canonical slot marker format for rule claim text. The format SHALL use bracketed uppercase slot symbols ([TRIGGER], [MODE], [CONFIG], [GUARD], [EVAL], [INVOCATION], [BOUNDARY]) as lexical markers that both human authors and the rule-claim-structure rule use to identify slot coverage. The canonical template SHALL be published in .dont/AGENTS.md (in the rule claim authoring section) and accessible via dont help --howto rule-claims.
Canonical template:
[INVOCATION] <rule-name> runs as: background lint | opt-in via `dont check --<flag>`
[CONFIG] Enabled by default: yes | no
[MODE] In permissive mode: warn | strict | same as strict | n/a
[TRIGGER] Fires when: <condition>
[GUARD] Silently skips: <inputs> (omit line if no guard)
[EVAL] Evaluation model: stateless demand | event-driven on <event> (omit if stateless demand)
[BOUNDARY] Does not handle: <edge cases>; defers to <other-rule> (omit if no boundary)
Optional slots SHALL be documented as omit-when-default to reduce claim verbosity.
Scenario: template is discoverable via help
- WHEN the caller runs
dont help --howto rule-claims - THEN the output includes the canonical template and per-slot guidance on when optional slots become load-bearing
Scenario: template is in agent-facing docs
- WHEN an agent reads
.dont/AGENTS.md - THEN the rule claim authoring section includes the canonical template
Scenario: claim authored from template passes structural validation
- WHEN an author creates a rule claim using the canonical template with both mandatory slots filled
- AND the claim includes the
rule-claim-typeterm:uuid in itsdepends_on - THEN
rule-claim-structuredoes not flag the claim
dont-rule-cli Specification
Purpose
TBD - created by archiving change add-dont-rules-specs. Update Purpose after archive.
Requirements
Requirement: Rule listing and inspection commands
The system SHALL provide dont rules list and dont rules show <rule-name> for inspecting the active rule catalogue. The canonical selector for show, test, and explain SHALL be the rule name rather than a numeric index. Listing MUST expose the available rule names and their configured severities. Showing one rule MUST surface the rule’s identity and explanation context sufficient for an operator or harness to understand what it checks.
Scenario: rules list returns active catalogue
- WHEN the caller runs
dont rules list --json - THEN the response enumerates the active rules available in the project
- AND includes their effective severities
Scenario: rules show returns one rule
- WHEN the caller runs
dont rules show lockable --json - THEN the response identifies the
lockablerule and exposes its operator-facing explanation context
Scenario: numeric index is not the canonical rule selector
- WHEN the operator consults command help or examples for
dont rules showordont rules test - THEN the documented selector form is the rule name rather than a numeric index
Requirement: Rule explanation command
The system SHALL provide dont explain <rule-name> to return the human-readable explanation for a rule. The explanation source of truth MUST be the rule’s sibling translation document rather than reverse-engineering the executable Datalog at call time.
Scenario: explain reads sibling translation
- WHEN the caller runs
dont explain ungrounded --json - THEN the response is derived from the human-readable translation paired with the
ungroundedrule
Scenario: explanation tells operator how to satisfy rule
- WHEN the caller reads the explanation for a rule
- THEN the content states both what the rule checks and how to satisfy or avoid tripping it
Requirement: Rule addition contract
The system SHALL provide dont rules add <file.dl> for installing a project-specific executable rule. A usable project-specific rule addition MUST consist of the executable rule file plus a sibling human-readable translation document, and the rule becomes active on the next dont invocation without requiring a process restart.
Scenario: added rule becomes active next invocation
- WHEN the caller adds a valid project-specific rule and its translation doc
- THEN the rule participates in rule evaluation on the next
dontinvocation
Scenario: added rule requires translation sibling
- WHEN a project-specific rule is installed
- THEN the rule surface expects a sibling explanation document so
dont explain <rule>can describe it
Requirement: Rule dry-run and compile testing
The system SHALL provide dont rules test <rule-name> as a dry-run command that compiles the named rule and evaluates it against the current store without emitting lifecycle events or mutating entity state. If compilation fails, the error surface MUST identify that compilation failure rather than silently ignoring the rule.
Scenario: rules test is non-mutating
- WHEN the caller runs
dont rules test no-appeal-to-common-knowledge --json - THEN the command reports the entities currently matched by the rule
- AND it does not emit events or change any entity status
Scenario: compile failure is surfaced explicitly
- WHEN the named rule does not parse or compile
- THEN the command returns an error describing the compilation failure so the operator can fix the rule file
Requirement: Severity guidance for added rules
The system SHALL support assigning project-specific rules as either warn or strict. Warn severity MUST emit warnings[] without refusing the triggering command, while strict severity MUST refuse with rule-not-met and the custom rule_name. Guidance for newly added rules SHOULD default operators toward warn unless the project truly requires an unconditional refusal.
Scenario: custom warn rule adds warning only
- WHEN a project-specific rule is configured as warn and matches an operation
- THEN the operation can still succeed with a warning entry naming that rule
Scenario: custom strict rule refuses with its own name
- WHEN a project-specific rule is configured as strict and matches an operation
- THEN the refusal uses code
rule-not-met - AND
rule_nameis the custom rule’s identifier
dont-rule-engine Specification
Purpose
TBD - created by archiving change add-dont-rules-specs. Update Purpose after archive.
Requirements
Requirement: Single rule source format with sibling translation
The system SHALL represent project rules in a single executable rule format rather than multiple interchangeable rule syntaxes. In v0.3 this executable format MUST be Cozo Datalog stored under the project rule surface, and every executable rule file MUST have a sibling human-readable translation document explaining what the rule does and how to satisfy it.
Scenario: shipped rule has translation sibling
- WHEN the system ships a rule such as
ungrounded - THEN the executable rule file is paired with a sibling English translation document for explanation and operator review
Scenario: dual-format hedge is not allowed
- WHEN a project defines rules in v0.3
- THEN the rule surface uses one executable format rather than maintaining multiple normative rule syntaxes in parallel
Requirement: Shipped rule catalogue
The system SHALL ship the following default rules with the documented semantics: ungrounded, unresolved-terms, stale-cascade, lockable, correlated-error, dangling-definition, term-nonfunctional-label, and rule-claim-structure.
term-nonfunctional-label is an off-by-default warn-severity rule that flags terms whose label text suggests a non-functional relationship has been folded into the type. Matching is heuristic (configurable token patterns; see dont-project-config); the rule is disabled by default because false-positives are expected on valid noun phrases. Its purpose is to surface candidates for aspect-shaped redesign once aspects land as a primitive (currently deferred to §17). Every shipped rule, including term-nonfunctional-label, MUST have a sibling human-readable translation document.
rule-claim-structure is an off-by-default warn-severity rule that validates claims tagged as rule claims (via rule-claim-type term:uuid in depends_on) against the six-slot semantic schema defined in dont-rule-claim-schema. The rule SHALL check for the presence of mandatory slot markers ([TRIGGER] and one of [MODE] or [CONFIG]) in the claim text. Missing mandatory slots SHALL produce a warning identifying the absent slot. Warn-severity violations SHALL NOT change the claim’s stored status — a verified claim that triggers a rule-claim-structure warning remains verified in the database. The rule MUST have a sibling human-readable translation document. The rule SHALL be disabled by default because it is only useful to projects that have adopted the rule claim convention.
Scenario: ungrounded flags or refuses unresolved CURIEs
- WHEN a claim references CURIEs that do not resolve
- THEN
ungroundedeither emits a warning or refuses the transition according to the project’s mode and severity configuration
Scenario: unresolved-terms blocks dismiss
- WHEN the caller attempts
dont dismisson a claim whose CURIEs remain unresolved in coined or imported vocabulary - THEN
unresolved-termsrefuses the dismissal
Scenario: stale-cascade computes derived stale assessment
- WHEN a trust transition moves an entity to persisted status
doubted - THEN
stale-cascadecomputesstaleas a derived assessment for verified dependents reached through supported dependency edges - AND it does not emit a status-changing event or mutate persisted dependent statuses
- AND locked and ignored entities are exempt from derived stale output
Scenario: stale-cascade traversal is cycle-safe
- WHEN dependency traversal revisits an entity already seen in the current trace
- THEN traversal stops for that branch
- AND the cycle itself does not trigger
stale
Scenario: lockable gates lock
- WHEN the caller attempts
dont lock - THEN
lockablechecks for verified status, at least three assessed hypotheses, and at least two independent supporting evidence items before allowing the transition
Scenario: correlated-error flags shared-source evidence
- WHEN a claim’s only evidence shares a source with its author
- THEN
correlated-erroremits a warning or strict refusal according to severity configuration
Scenario: dangling-definition blocks unresolved term relations
- WHEN
dont definenames--kind-ofor--related-toreferences that do not resolve - THEN
dangling-definitionrefuses the definition in both permissive and strict modes
Scenario: term-nonfunctional-label emits warning when enabled
- WHEN
term-nonfunctional-labelis enabled and an actor defines a term whose label matches a configured non-functional-relationship pattern (e.g."a node that has a child") - THEN the
definecommand succeeds and the envelope carries aterm-nonfunctional-labelwarning
Scenario: term-nonfunctional-label is disabled by default
- WHEN a project does not explicitly enable
term-nonfunctional-labelin its configuration - THEN no
term-nonfunctional-labelwarnings are emitted, regardless of label content
Scenario: term-nonfunctional-label has a translation sibling
- WHEN the system ships
term-nonfunctional-label - THEN the executable rule file is paired with a sibling English translation document for explanation and operator review
Scenario: rule-claim-structure warns on missing mandatory slot
- WHEN
rule-claim-structureis enabled and evaluates a tagged rule claim missing the[TRIGGER]marker - THEN the envelope carries a warning identifying the absent slot by name
- AND the triggering operation is not refused (warn severity)
Scenario: rule-claim-structure ignores untagged claims
- WHEN
rule-claim-structureis enabled and evaluates a claim withoutrule-claim-typein itsdepends_on - THEN no warning is emitted for that claim regardless of its text content
Scenario: rule-claim-structure is disabled by default
- WHEN a project does not explicitly enable
rule-claim-structurein its configuration - THEN no
rule-claim-structurewarnings are emitted
Scenario: rule-claim-structure has a translation sibling
- WHEN the system ships
rule-claim-structure - THEN the executable rule file is paired with a sibling English translation document for explanation and operator review
Requirement: Severity defaults and override boundaries
The system SHALL assign default severities by rule and mode. unresolved-terms, dangling-definition, and stale-cascade MUST remain strict and non-overridable. Strict stale-cascade means commands that gate on dependency integrity (including dont lock) MUST refuse when the computed trace contains stale; it does not mean the rule persists a stale status. lockable MUST remain a manual gate evaluated only on dont lock. ungrounded MUST default to warn in permissive mode and strict in strict mode, and remain overridable. correlated-error MUST default to warn in both modes and remain overridable.
Scenario: permissive mode keeps ungrounded as warning
- WHEN the project runs in permissive mode without overrides
- THEN
ungroundedproduces warnings rather than refusingdont conclude
Scenario: strict mode escalates ungrounded
- WHEN the project runs in strict mode without overrides
- THEN
ungroundedrefuses unresolved CURIE references
Scenario: non-overridable rules stay strict
- WHEN a project attempts to soften
unresolved-terms,dangling-definition, orstale-cascade - THEN those rules remain strict because their severity is not project-overridable
Scenario: lockable is manual gate only
- WHEN a verified claim exists but the caller does not invoke
dont lock - THEN
lockabledoes not autonomously refuse unrelated commands
Requirement: Rule outcomes and error taxonomy boundary
The system SHALL distinguish rule-layer outcomes from verb-level validators. Rule-layer strict failures MUST use error code rule-not-met with rule_name naming the specific rule. Rule-layer warn outcomes MUST surface in warnings[]. Verb-level validators such as reason-required, reason-not-hedge, no-evidence, atoms-incomplete, and wrong-entity-kind MUST use their own dedicated error codes and MUST set rule_name to null.
Scenario: strict rule failure uses rule-not-met
- WHEN
lockablerefuses adont lockoperation - THEN the error envelope uses code
rule-not-met - AND
rule_nameislockable
Scenario: warning rule emits warning payload
- WHEN
correlated-erroris configured as warn and triggers during an operation - THEN the operation may still succeed
- AND the envelope carries a warning entry naming
correlated-error
Scenario: verb-level validator is not presented as rule failure
- WHEN
dont trustrejects a hedge-only reason - THEN the error code is
reason-not-hedge - AND
rule_nameisnullrather thanvague-reason
Requirement: Vague-reason migration boundary
The system SHALL treat the v0.2 vague-reason rule as removed from the shipped rule set. In v0.3 the presence and anti-hedge checks on trust --reason MUST be implemented as verb-level validators, while projects remain free to add softer custom rule-layer checks on related reasoning quality concerns.
Scenario: shipped rule list excludes vague-reason
- WHEN the operator inspects the default rule catalogue
- THEN
vague-reasonis absent from the shipped rules
Scenario: custom softer rule remains possible
- WHEN a project wants a warn-level check adjacent to reason quality
- THEN it may add a project-specific rule without replacing the unconditional verb-level refusal semantics of
trust
dont-spawn-protocol Specification
Purpose
TBD - created by archiving change add-dont-harness-specs. Update Purpose after archive.
Requirements
Requirement: Spawn-producing derived commands
The system SHALL treat dont guess, dont assume, and dont overlook as spawn-producing derived commands. In harness mode they MUST emit spawn_request envelopes instead of calling an LLM directly. In direct mode they MUST call the configured provider and use the same command intent: guess requests diverse candidate answers, assume requests independent verification of one claim, and overlook requests a premortem-style adversarial challenge. dont guess MUST accept candidate-count and temperature shaping inputs so callers can request multiple diverse candidates.
Scenario: assume emits spawn request in harness mode
- WHEN the caller runs
dont assume claim:01HX05A9K8VP --jsonin harness mode - THEN the command returns
envelope_kind: "spawn_request" - AND it does not call an LLM provider directly
Scenario: overlook emits adversarial spawn request
- WHEN the caller runs
dont overlook claim:01HX05A9K8VP --jsonin harness mode - THEN the spawn request instructs a subagent to assume the claim is wrong and try to explain why
Scenario: guess requests candidate set
- WHEN the caller runs
dont guess "<question>" --json - THEN the command produces a spawn request for multiple candidate answers rather than writing to the claim store directly
Scenario: guess carries candidate count and temperature
- WHEN the caller runs
dont guess "<question>" --n 3 --temperature 0.7 --json - THEN the resulting direct call or spawn request preserves the requested candidate count and temperature settings
Scenario: direct mode bypasses spawn envelope
- WHEN direct mode is active for
guess,assume, oroverlook - THEN the tool calls the configured provider directly instead of emitting a harness-facing
spawn_request
Requirement: Harness-agnostic spawn contract
The system SHALL keep the spawn protocol transport-agnostic. A harness MUST be able to receive the spawn_request through direct CLI stdout, an MCP tool return, or another structured transport without changing the payload contract. Spawn requests MUST carry clean-context subagent instructions and restricted tool permissions, and the only terminal callback actions for verification spawns MUST be dont dismiss or dont trust.
Scenario: direct CLI harness consumes stdout envelope
- WHEN a harness runs
dont assume ... --jsonthrough the CLI - THEN it can detect
envelope_kind: "spawn_request"on stdout and start its own subagent mechanism from that data
Scenario: subagent tool permissions are restricted
- WHEN the harness fulfils an
assumeoroverlookrequest - THEN it starts the subagent with the exact allowed-tool list from the request context
- AND the subagent’s terminal action is limited to
dont dismissordont trust
Requirement: Harness-mode detection order
The system SHALL resolve harness mode in the documented priority order. --direct MUST force direct mode. Otherwise a truthy DONT_HARNESS environment value MUST force harness mode, invocation through dont mcp MUST force harness mode, non-terminal stdout MUST imply harness mode, and interactive terminal invocation without those signals MUST fall back to direct mode.
Scenario: direct flag overrides environment
- WHEN
DONT_HARNESS=1is set and the caller passes--direct - THEN direct mode is selected
Scenario: environment variable selects harness mode
- WHEN
DONT_HARNESSis set to a non-empty value other than0orfalse - THEN harness mode is selected even if stdout is a terminal
Scenario: piped stdout implies harness mode
- WHEN stdout is redirected or piped and no stronger signal applies
- THEN harness mode is selected
Scenario: interactive shell defaults to direct mode
- WHEN the caller runs a spawn-producing command in an interactive terminal without harness signals
- THEN direct mode is selected
Requirement: Spawn expiry and timeout recovery
The system SHALL assign every spawn request an expires_at timestamp derived from the configured spawn-timeout window. On subsequent invocations it MUST sweep pending spawns for expiry, emit spawn_timeout events for expired requests, restore the entity to its pre-spawn status, and avoid automatic re-spawn.
Scenario: expired spawn records timeout event
- WHEN a pending spawn passes its
expires_attime without a terminal callback - THEN the next
dontinvocation emits aspawn_timeoutevent with the timeout timestamps against the original entity
Scenario: timeout restores pre-spawn status
- WHEN a spawn expires
- THEN the entity is restored to the status it had when the spawn was issued
- AND an entity that was already
unverifiedremainsunverified
Scenario: timeout does not auto-respawn
- WHEN a spawn times out
- THEN the tool does not retry the verification automatically
- AND a caller must re-run
dont assumeordont overlookexplicitly to request another verification round
Requirement: Spawn listing queries
The system SHALL provide dont spawns to inspect spawn state. dont spawns --pending MUST list active non-expired requests sorted by age, dont spawns --timed-out MUST list expired requests that have not been re-assumed since, and dont spawns --all MUST return the full history.
Scenario: pending view excludes expired requests
- WHEN the caller runs
dont spawns --pending --json - THEN the response contains only non-expired pending spawn requests sorted by age
Scenario: timed-out view isolates unresolved expiries
- WHEN the caller runs
dont spawns --timed-out --json - THEN the response contains expired spawn requests that remain relevant because the original entity has not been re-assumed since timeout
Requirement: Late callbacks and spawn errors
The system SHALL use spawn-not-found when a callback references an unknown request ID and spawn-expired when a terminal callback arrives after expiry. Terminal resolver outcomes are timeout-restored (sweep restored pre-spawn status) and callback-applied (dismiss-or-trust callback applied). On spawn-expired, a late callback SHALL still be applied when no terminal resolver outcome has been committed for that request_id, while lateness is recorded as a warning. A late callback MUST NOT apply if the same request has already reached a committed terminal resolver outcome.
Scenario: unknown request id refuses callback
- WHEN a callback references a request ID the store does not know
- THEN the command returns an error with code
spawn-not-found
Scenario: expired callback still applies when unresolved
- WHEN a terminal callback arrives after
expires_at - AND no terminal resolver outcome has been committed yet
- THEN the command records a warning for
spawn-expired - AND it still applies the dismiss-or-trust action to the entity
Scenario: duplicate terminal callback does not re-apply transition
- WHEN a request has already reached a terminal resolver outcome
- THEN a later callback for the same request does not apply a second terminal transition
- AND the envelope includes a warning describing duplicate resolution
Requirement: Deterministic timeout-versus-callback race resolution
The system SHALL resolve races between spawn timeout sweep and terminal callback using transaction commit order. For a single request_id, exactly one terminal resolver (timeout restore or callback action) may mutate entity status. Any later competing resolver MUST be surfaced as an envelope warning and MUST NOT perform a second status mutation.
Scenario: timeout and callback race yields one status mutation
- WHEN a timeout sweep and terminal callback for the same request execute concurrently
- THEN only the first committed resolver mutates entity status
- AND the later resolver is surfaced as a warning with no additional status mutation
Scenario: callback wins if committed first
- WHEN a terminal callback transaction commits before timeout sweep commits
- THEN callback semantics apply and timeout sweep does not restore pre-spawn status for that request
Scenario: timeout wins if committed first
- WHEN timeout sweep commits before terminal callback commits
- THEN timeout restoration applies and the later callback is treated as late/no-op with warning
Scenario: concurrent duplicate callbacks resolve once
- WHEN two terminal callbacks for the same request race concurrently
- THEN exactly one callback produces
callback-applied - AND the other callback is surfaced as duplicate/no-op warning
dont-status-lifecycle Specification
Purpose
TBD - created by archiving change add-core-dont-specs. Update Purpose after archive.
Requirements
Requirement: Shared persisted status lattice
The system SHALL define a single persisted status lattice for claims and terms containing unverified, verified, doubted, locked, and ignored. In dont, status is what users explicitly record through commands; derived_assessments are what the system automatically infers from dependency graph integrity. The system SHALL NOT mix derived assessments into the persisted lattice.
Scenario: entity statuses are drawn from the shared lattice
- WHEN the specification describes the persisted state of a claim or term
- THEN that state is expressed using the shared persisted status lattice
Requirement: Persisted status and derived assessment are distinct
The system SHALL distinguish persisted status from derived assessment. A derived assessment is a read-time annotation computed on demand from an entity’s persisted status, dependency graph, imported references, and evidence liveness records. Derived assessments SHALL NOT be stored as lifecycle state; they SHALL be recomputed for read/query output and for command policy checks that consult dependency integrity.
The v0.3 derived assessment names are:
stale: at least one transitive support dependency has persisted statusdoubtedor itself carriesstaleduring the current trace.compromised-support: at least one transitive support dependency isignoredorlocked, making the dependent entity’s verification support constrained without changing its status.dangling-dependency: a persisted dependency edge points to an entity or imported term that no longer resolves.unresolved-term: a claim or term references a CURIE that resolves neither to a coined term nor to an imported term.
Each derived assessment SHALL clear automatically from later read/query output when its trigger condition is no longer true. For example, stale clears when all transitive support dependencies are no longer doubted; compromised-support clears when constrained dependencies stop being part of the support trace or leave constraining statuses; dangling-dependency clears when the referenced dependency resolves again or the dependency edge is removed; and unresolved-term clears when the CURIE resolves or the reference is removed.
Scenario: derived trace finding does not replace persisted status
- WHEN dependency analysis finds that a verified claim has a transitive dependency in
doubtedstatus - THEN the claim keeps its persisted
verifiedstatus - AND the output presents
staleinderived_assessments[]
Scenario: derived assessments are recomputed after status changes
- WHEN any persisted status transition occurs on an entity in the dependency graph
- THEN later read/query operations recompute derived assessments for affected entities rather than reading stored assessment state
Scenario: ignored entities are excluded from derived assessment output
- WHEN an entity has persisted status
ignored - THEN read/query output for that entity has an empty
derived_assessments[]unless a future spec explicitly opts into ignored-entity diagnostics
Requirement: Entry state is unverified
The system SHALL require both conclude and define to introduce new claims and terms in the unverified state.
Scenario: new claim enters as unverified
- WHEN an actor invokes
concludesuccessfully - THEN the new claim is introduced as
unverified
Scenario: new term enters as unverified
- WHEN an actor invokes
definesuccessfully - THEN the new term is introduced as
unverified
Requirement: Locked and ignored states reject normal transitions
The system SHALL treat locked and ignored as closure states that reject further normal state transitions. locked is not reopenable in v0.3. ignored rejects normal transitions but MAY be restored by the explicit reopen lifecycle operation defined in dont-lifecycle-verbs.
Scenario: locked entity refuses later transitions
- WHEN an actor attempts a normal state-changing operation on a locked entity
- THEN the operation is refused
Scenario: ignored entity refuses later transitions
- WHEN an actor attempts a normal state-changing operation on an ignored entity
- THEN the operation is refused
Requirement: Dependency fallout is computed on demand
The system SHALL compute dependency fallout on demand across claim-to-claim, claim-to-term, and term-to-term edges, and SHALL NOT persist automatic downstream status changes solely because a dependency became doubted. Dependency traversal SHALL be cycle-safe: a trace SHALL keep a visited set for the current traversal, SHALL terminate when it revisits an entity, and SHALL NOT treat the existence of a cycle by itself as a stale assessment.
Scenario: direct doubt does not cascade into persisted dependent status changes
- WHEN a dependency transitions to
doubted - THEN dependent entities do not receive automatic persisted lifecycle transitions solely from that dependency change
Scenario: verification support may be reported as compromised in trace output
- WHEN a verification or audit trace traverses a dependency that is
doubted, unresolved,ignored, orlocked - THEN the trace may report compromised or constrained support
- AND it does so without rewriting the dependent entity’s persisted status
Requirement: Reopen applies to ignored persisted lifecycle closure
The system SHALL treat reopen as operating only on the explicit persisted ignored lifecycle state in v0.3. reopen SHALL NOT target locked entities and SHALL NOT target derived assessments such as stale.
Scenario: reopened ignored entity leaves persisted closure
- WHEN an actor explicitly reopens an entity that has persisted status
ignored - THEN the entity transitions to
unverified - AND derived assessments for affected dependency traces are recomputed on later reads
Scenario: reopen does not target derived stale assessment
- WHEN an actor invokes
reopenon an entity whose persisted status isverifiedand whosederived_assessments[]containsstale - THEN the command is refused because
staleis not a persisted lifecycle state
Scenario: reopen refuses locked entities
- WHEN an actor invokes
reopenon an entity whose persisted status islocked - THEN the command is refused because locked entities are not reopenable in v0.3
Requirement: Status transitions record audit context
The system SHALL make status transitions auditable and SHALL allow transition-specific context such as reasons or evidence references to be attached when applicable.
Scenario: status transition carries audit context
- WHEN any persisted status transition occurs
- THEN the transition history records enough context for later audit according to the data-model specification
Scenario: trust transition records its reason
- WHEN an actor transitions an entity to
doubted - THEN the transition record can include the stated reason for doubt
Scenario: flag transition records evidence references
- WHEN an actor transitions an entity toward
verifiedthrough theflagoperation - THEN the transition record can include the evidence references used for that
flagoperation
Requirement: Derived assessments may inform later command policy
The system SHALL allow derived dependency-trace assessments to inform later command warnings or refusals without treating those assessments as persisted status changes.
Scenario: command policy can consult computed trace results
- WHEN a later command evaluates whether to proceed with an operation on a claim or term
- THEN it may consult computed dependency-trace results
- AND any resulting warning or refusal does not itself rewrite persisted lifecycle state
Requirement: Status transition terminology
The system SHALL use the terms trust, flag, and undoubt to describe the primary lifecycle transitions in v0.3. Documentation and error messages SHALL reflect this terminology while acknowledging deprecated aliases.
Scenario: trust command records its stated reason
- WHEN an actor transitions an entity to
doubtedusing thetrustcommand - THEN the transition record includes the stated reason for doubt
Scenario: flag command records evidence references
- WHEN an actor transitions an entity to
verifiedusing theflagcommand - THEN the transition record includes the evidence references used for that verification
Scenario: undoubt transition reverts doubt without evidence
- WHEN an actor transitions an entity from
doubtedtounverifiedusing theundoubtcommand - THEN the transition record captures the retraction without requiring evidence
dont-trace-query Specification
Purpose
TBD - created by archiving change add-trace-query. Update Purpose after archive.
Requirements
Requirement: Trace query explains dependency and support blockers
The system SHALL provide dont trace <entity-id> as a read-only diagnostic query that explains the currently relevant dependency or support paths for a claim or term. trace SHALL traverse the same dependency/support relationships consulted by current derived-assessment and blocker analysis, including dependency edges such as claim-to-claim, claim-to-term, and term-to-term relations where applicable, rather than inventing a separate graph model. The query SHALL focus on paths that explain derived assessments or gated verification decisions rather than dumping the entire graph indiscriminately.
Scenario: stale claim reveals blocking path
- WHEN a claim is currently blocked because one upstream dependency makes its support trace stale
- THEN
dont trace <claim-id> --jsonreturns the path from the claim to the blocking dependency and identifies the blocker condition
Scenario: unresolved term reveals missing resolution point
- WHEN a claim or term carries an unresolved-term condition
- THEN the trace output identifies the unresolved CURIE or missing target that caused the blockage
Requirement: Trace output is structured and actionable
The trace result SHALL expose enough structured information for a harness or human to act on it. At minimum, each reported blocker path MUST identify the starting entity, the traversed path, the blocking node or unresolved reference, the blocker kind, and one or more suggested next actions. Suggested next actions SHALL be valid commands for the blocker kind being reported and SHALL follow the normal remediation style used elsewhere in dont rather than inventing free-form pseudo-commands.
Scenario: trace includes remediation-rich blocker entry
- WHEN a claim is blocked by an upstream doubted or unresolved dependency
- THEN the trace output includes a blocker entry with a suggested next command such as inspecting, defining, or revisiting the blocking node
Scenario: multiple independent blockers are listed separately
- WHEN two unrelated dependency paths independently block one entity
- THEN the trace output reports them as separate blocker entries rather than collapsing them into one opaque summary
Scenario: cyclic dependency trace remains bounded
- WHEN the traversed dependency/support graph contains a cycle
- THEN
dont trace <entity-id> --jsonterminates without infinite expansion and reports the bounded path context needed for diagnosis
Requirement: Healthy entities yield empty blocker traces
The tracing query SHALL succeed on healthy entities and return an empty blocker set when no dependency or support fallout currently needs explanation.
Scenario: verified standalone claim has empty trace blockers
- WHEN a verified claim has no unresolved, dangling, stale, or otherwise blocking dependency conditions
- THEN
dont trace <claim-id> --jsonsucceeds and returns no blocker paths