Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

“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):

  • wai helps track workflow and design intent
  • bd tracks concrete work
  • dont governs 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 dont is for
  • why ordinary “please double-check yourself” prompting is not enough
  • how to ground repository facts with dont ground, repository-relative locators, and dont trace
  • how the project’s research maps onto the proposed tool

Key sources

(Note: External links point to the main branch of the repository.)

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

StepCommandStatus after
State the claimdont conclude "..."unverified
Add a competing explanationdont hypothesis add <id> --text "..."—
Attach evidencedont flag <id> --file <path> --lines N-Mverified
Assess a hypothesisdont hypothesis assess <id> <idx> --refuting <path>—
Check statusdont show <id>(read-only)
Freeze a mature claimdont lock <id>locked
Register doubtdont trust <id> --reason "..."doubted
Orient at session startdont primeexits 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.

UseWhen
hypothesisYou have competing explanations — only one will survive
atomYou 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.rs file 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:

  1. LLMs do not reliably self-correct just because they are asked to reconsider.
  2. Better results usually come from external checks such as retrieval, tests, verifiers, or independent critics.
  3. 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:

  1. conclude — introduce an unverified claim
  2. trust — register doubt
  3. flag — verify with evidence
  4. lock — 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.

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 match arms 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 prime is 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:

RuleWhat it catches
unresolved-termsdependency reference (CURIE or term:uuid) that can’t be resolved
stale-cascadeclaim that depends on an unverified or doubted claim
dangling-definitionexplicit 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:

  1. A gate over no claims enforces nothing. dont check passes 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).
  2. Uninitialized repos hard-fail. dont check exits 1 with “no .dont/ project found” outside an initialized project, so blanket pre-push wiring would block pushes from repos that never opted in.
  3. 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.
  4. ah signals is already repo-local by mechanism. It reads .dont/events/*.json from the same repository — no cross-repo claim enforcement is implied by that integration.

Adoption ladder for a suite repository:

  1. dont init and start registering claims during development (dogfood period — see the grounding workflow).
  2. Once the repo has real claim activity, wire just check-claims (or the dont prime equivalent) into pre-commit/CI using the recipes above.
  3. 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.
  • dont translates 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 in wai research
  • 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:

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.


  1. Research: forcing doubt in minds and machines ↩ ↩2 ↩3

  2. Research: designing doubt into AI interaction ↩ ↩2

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:

CommandWhat it does
dont initInitialise the claim store (permissive mode by default; --strict for strict mode)
dont primeOrient at session start; exits 1 on any doubted claim
dont concludeRegister an unverified claim
dont groundRegister and verify a claim in one step
dont flagAttach evidence to an existing claim
dont trustMark a claim as doubted (blocks prime)
dont ignoreSet a claim aside (scope change; no longer relevant)
dont lockFreeze a mature verified claim (requires ≥ 3 assessed hypotheses + ≥ 2 evidence sources)
dont defineRecord a project vocabulary term
dont hypothesis add/assessTrack and assess competing explanations under a claim
dont atom define/dismissDecompose a claim into independently checkable sub-conditions
dont showInspect a single claim or term
dont listList all claims and their statuses
dont whyExplain why a claim is in its current status
dont traceWalk the blocker path for a claim
dont rules list/explainList 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:

SpecWhat it covers
dont-coreCore claim lifecycle (conclude, flag, trust, lock)
dont-data-modelStore schema and entity model
dont-envelopeJSON Envelope and Error protocol
dont-evidence-locatorsRepository-relative file/lines/anchor syntax
dont-status-lifecycleStatus transition rules and gate conditions
dont-lifecycle-verbsCommand-by-command verb semantics
dont-cli-surfaceCLI flags, exit codes, output routing
dont-errorsStructured error types and remediation messages
dont-rule-engineShipped 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 in openspec/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

CommandStatusNotes
dont checkstableepistemic gate — fail on ungrounded claims
dont concludestablerecord grounded conclusions
dont definestabledefine claim vocabulary/terms
dont flagstableflag claims pending grounding
dont trustbetatrust 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 check as 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:

  1. this book for the overview
  2. openspec/changes/*/specs/ for the detailed capability-by-capability design
  3. .wai/projects/dont/research/ for the justification, research trail, and archived monolithic draft
  4. openspec/ 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

SpecDescription
dont-coreCore claim lifecycle: conclude, flag, trust, lock
dont-data-modelStore schema and entity relationships
dont-status-lifecycleStatus transitions, gate conditions, and mode enforcement
dont-lifecycle-verbsCommand-by-command verb semantics
dont-errorsStructured error types and remediation messages

Interface specs

SpecDescription
dont-cli-surfaceCLI flags, subcommands, exit codes, output routing
dont-cli-coreCore CLI wiring and dispatch
dont-envelopeJSON Envelope and Error protocol (--json output)
dont-payload-typesEnvelope payload schemas
dont-agent-helpHelp text and agent-readable command descriptions

Evidence and grounding specs

SpecDescription
dont-evidence-locatorsRepository-relative file/lines/anchor syntax
dont-ground-commanddont ground fast-path semantics
dont-trace-queryBlocker-path tracing for dont trace

Rule engine specs

SpecDescription
dont-rule-engineShipped rules, severities, and project config
dont-rule-clidont rules subcommand surface
dont-rule-claim-schemaClaim schema validation rule
dont-derived-queriesDatalog-based derived query layer

Infrastructure specs

SpecDescription
dont-buildBuild, CI, and release pipeline
dont-project-layoutRepository layout conventions
dont-project-configPer-project .dont/ configuration
dont-init-modesStrict vs permissive initialisation modes
dont-glossaryCanonical terminology

Future / in-progress specs

SpecDescription
dont-harness-cliAgent harness integration
dont-spawn-protocolClean-context spawn for independent verification
dont-mcp-interfaceMCP server interface
dont-import-surfaceExternal data import surface
dont-linkml-importLinkML 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.rs is updated to use Guide::builder(...)
  • THEN command handlers SHOULD return Output<T> or use ErrorSink
  • AND cargo test SHALL pass

Scenario: ErrorSink for self-healing errors

  • GIVEN the tool adopts genesis::guide
  • WHEN a command returns an error
  • THEN ErrorSink SHOULD 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 test SHALL 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 ConfigStore for 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 clippy SHALL introduce no new warnings
  • AND cargo test SHALL 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 dont renders the managed block into a project document
  • THEN the block tells the reader to run dont prime --json at session start
  • AND it points to .dont/AGENTS.md as 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 init or dont doctor --fix rewrites .dont/AGENTS.md
  • THEN the entire file is replaced with the generator output
  • AND the file’s header declares it is managed by dont and 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].command and run it rather than guessing reformulations

Scenario: spawn guidance in orientation block

  • WHEN the orientation block describes spawn_request envelopes
  • 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 --doc when coining terms
  • AND it explains that the label is what appears in diagrams and is shape-checked
  • AND the guidance appears between the suggest-term recommendation line and the dont help --tutorial pointer

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, and dont 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 dont into 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 ground for quick documented-fact capture while still explaining that it composes the underlying conclude and flag semantics

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 stale or unresolved-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 --json on 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 --fix to inject the block

Scenario: canonical file drift produces a warn check

  • WHEN .dont/AGENTS.md differs 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\n versus \n line 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 --fix on a project whose root AGENTS.md has 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 --fix and .dont/AGENTS.md differs from the current generator output
  • THEN the command overwrites .dont/AGENTS.md in whole with the current generator output

Scenario: fix is idempotent

  • WHEN the caller runs dont doctor --fix and then immediately runs dont doctor --fix again 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 doctor or dont doctor --json without --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 init and then dont doctor --fix is run with no other state changes
  • THEN dont doctor --fix makes 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 --fix on a project whose root AGENTS.md exists but does not contain the managed-block sentinels
  • THEN the command injects the managed block into the file using the same placement rules dont init uses
  • 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 dont binary is placed on a system without Rust, CozoDB, or any dont-specific runtime installed
  • THEN dont --version SHALL 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 dont binary 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 init is run in a directory with no prior dont project 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 dont commands 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 --json is 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> --json is 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, and flag as 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 trust as 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 flag or dont 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 conclude with a statement
  • THEN the tool creates a claim entity
  • AND the entity enters the unverified state 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 conclude may still create the unverified claim 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 conclude is refused until those references are resolved

Scenario: conclude refuses duplicate claim creation

  • WHEN an actor invokes conclude for 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 define with a CURIE and definition
  • THEN the tool creates a term entity
  • AND the entity enters the unverified state as defined by the lifecycle specification

Scenario: define accepts an optional label

  • WHEN an actor invokes define with both --doc and --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 define without --label and the leading phrase of --doc fails 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 define with 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 define for 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 trust on 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 trust with a vague hedge pattern or other low-information reason
  • THEN the command is refused

Scenario: trust refuses terminal entities

  • WHEN an actor invokes trust on 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 flag on an eligible claim or term with one or more evidence references
  • THEN the command is allowed to transition the target toward verified according to the lifecycle rules

Scenario: flag may append evidence to an already verified entity

  • WHEN an actor invokes flag on an already verified claim 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 flag without any evidence references
  • THEN the command is refused

Scenario: flag refuses terminal entities

  • WHEN an actor invokes flag on a locked or ignored entity
  • THEN the command is refused

Scenario: flag refuses malformed evidence references

  • WHEN an actor invokes flag with 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 flag for 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 dismiss instead of flag
  • THEN the command succeeds with identical behavior to flag but 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.

  1. term-label-empty — the label is the empty string or whitespace-only after trim.

  2. term-shape-indefinite — the label does not begin with a or an followed by whitespace. Regex floor: ^(a|an)\s+\S, case-insensitive. Source: SK11 §2.1.1(i).

  3. term-shape-punctuated — the label ends in ., ?, !, ;, or :. Regex floor: [.?!;:]\s*$. Source: SK11 §2.1.1(iv).

  4. 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 first where or 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).

  5. term-label-sentence — the label contains is, are, has, have, does, do, was, or were as whitespace-bounded tokens outside a parenthesised variable declaration or a where-clause (the text following the first occurrence of the token where that 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 define with --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 define with --label "Ricci tensor"
  • THEN the command is refused with code: "term-shape-indefinite"
  • AND remediation[0] suggests prepending a or an

Scenario: sentence-punctuated label is refused

  • WHEN an actor invokes define with --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 define with --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 define with --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 define with a label that fails term-shape-indefinite
  • THEN only the term-shape-indefinite error is returned and subsequent validators are not evaluated

Scenario: well-formed label passes all validators

  • WHEN an actor invokes define with --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 define with --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 define without --label and --doc "Ricci tensor. Extended explanation..."
  • THEN the command succeeds
  • AND the envelope carries a term-doc-shape-indefinite warning

Scenario: doc-extraction does not fire compound check

  • WHEN an actor invokes define without --label and --doc that starts with a pair of integers
  • THEN the term-compound-undeclared validator does not fire on the doc extraction

Scenario: verb token in where-clause does not fire term-label-sentence

  • WHEN an actor invokes define with --label "a pair (x, y) where x is an integer and y is a real number"
  • THEN the term-label-sentence validator does not fire because the verb tokens appear inside a where-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 --yes is 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 create against dont’s Cargo.toml repository with labels agent-reported, bug, has-repro.

Scenario: error with no self-healing fix

  • WHEN dont exits non-zero and no genesis::suggestions::Fix is 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_AUTHOR or $USER

Scenario: json flag emits structured envelope

  • WHEN any subcommand is invoked with --json or -j
  • THEN stdout contains only the JSON envelope per the dont-envelope contract and human logging moves to stderr

Scenario: human mode is default

  • WHEN a subcommand is invoked without the --json flag
  • 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=1 is 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_COLOR is not set and --plain is 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_COLOR is not set and --plain is not passed and CLICOLOR_FORCE is not set
  • THEN output contains no ANSI colour codes

Scenario: colour disabled when NO_COLOR is set

  • WHEN NO_COLOR is set to a non-empty value
  • THEN output contains no ANSI colour codes

Scenario: colour forced with CLICOLOR_FORCE

  • WHEN CLICOLOR_FORCE=1 is set and stdout is redirected
  • THEN output includes ANSI colour codes despite redirection

Scenario: plain flag overrides CLICOLOR_FORCE

  • WHEN both --plain and CLICOLOR_FORCE=1 are 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-errors contract 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 2 with a usage error; 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 fish is invoked
  • THEN stdout contains a valid fish completion script with complete -c entries

Scenario: completions for unknown shell exits with usage error

  • WHEN dont completions <unknown-shell> is invoked
  • THEN the process exits 2 with a usage error 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 help is 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 --tutorial is invoked
  • THEN the output prints the canonical tutorial walkthrough

Scenario: topics lists available help entries

  • WHEN dont help --topics is 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 ground with a brief description of one-shot claim grounding

Scenario: completions include ground

  • WHEN the caller generates shell completions
  • THEN the completion script includes ground among 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 usage error 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:

  1. Full ULID Match: Exact match against a prefixed or unprefixed ULID.
  2. CURIE Match: Exact match against a registered term CURIE.
  3. 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:P001 is invoked
  • THEN the system resolves WB:P001 to its internal term:ULID and displays the term

Scenario: lookup by short ULID

  • WHEN dont show 01KQZ is 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:01KQZ is 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 1 with 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 --force flag 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 dont interacts with companion tools
  • THEN wai is treated as workflow context, beads/bd as memory or issue tracking, and dont as epistemic discipline

Scenario: dont can operate without companion-tool runtime coupling

  • WHEN dont is executed in a project that follows the shared conventions
  • THEN its specification does not require wai or beads/bd to 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 verified with 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 statement is 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_kind value 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 dont commands 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 unverified to doubted
  • 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 dont commands 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, and remediation[] suggesting dont doctor and 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 1 in 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-evidence after 3 of 5 URIs have been checked
  • THEN the 3 completed evidence_checked rows 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 at only

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 evidence relation stores it with entity_id pointing to the term

Scenario: depends_on supports term-to-term edges

  • WHEN a term has a kind_of reference to another term
  • THEN a depends_on edge 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_kind is "claim"

Scenario: event_kind distinguishes event types

  • WHEN a trust event is recorded
  • THEN its event_kind is "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" and definition matching 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" alongside curie and definition

Scenario: label is absent when not supplied

  • WHEN a term is defined without --label
  • THEN the stored term has no label attribute; the field is optional

Scenario: seed entry without label suppresses shape checks

  • WHEN dont init installs seed terms from YAML entries that omit label
  • 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-cascade rule 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 verified while atoms 1 and 2 remain unverified

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 verified with 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 verified without an explicit whole-claim dismiss

Scenario: whole-claim dismiss refused when atoms incomplete

  • WHEN dont dismiss claim:X --evidence <uri> is run without --atom flags and atom 1 is still unverified
  • 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 doubted and the parent claim also transitions to doubted

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 trust an 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 term table and the imported_term table

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 term and imported_term have CURIE proj: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 GO is 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:

  1. attribute — { ident, value_type, cardinality, predicate?, doc } where value_type is one of string, int, float, bool, ref, tuple, enum, expr and predicate is an optional Datalog fragment or reference to an external verifier tool
  2. derived_class — { ident, defining_attributes, extra_predicates, doc } — a named recognition query; not inheritance
  3. enum — { ident, values[], doc }
  4. 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.
  5. 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" and id = "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, and ci:github-actions are validated
  • THEN all four are accepted

Scenario: unknown actor kind is rejected

  • WHEN author is bot:gpt-4
  • THEN validation rejects it because bot is 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", and model: "claude-opus-4.7"

Scenario: every transition records author and timestamp

  • WHEN a claim is trusted
  • THEN the event records author, at (timestamp), and reason

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 as stale), --as-of (historical slice query), dont list --all (include both claims and terms), and the dont vocab alias for dont list --kind terms. These are planned but not available in the current implementation. When implemented, --derived-assessment SHALL match computed assessments without treating them as persisted statuses; --as-of SHALL evaluate entity state at a historical transaction point; dont vocab SHALL remain equivalent to dont 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 ClaimView returned by dont show always includes an events array in its payload. There is no --history flag — the event timeline is unconditionally present. The spec originally described a --history opt-in flag; the actual design bundles events into every ClaimView response instead.

Scenario: show returns current entity view with events

  • WHEN the caller runs dont show claim:01HX05A9K8VP --json
  • THEN the command returns the current ClaimView or TermView for that entity
  • AND the response includes the entity’s events array 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 --json in 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_mode reflects 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 doctor or dont doctor --json without --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-help for 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 SchemaDoc for 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> --json on 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 --json is 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_version SHALL remain "0.2"
  • AND no local Envelope struct SHALL remain in src/envelope.rs.

Scenario: envelope contains version and cli_version independently

  • WHEN an error envelope is serialized
  • THEN data SHALL contain the structured ErrorResult fields (code, rule_name, remediation)
  • AND envelope_version and cli_version SHALL 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", and data.version as a semver string
  • AND parsers keyed on envelope_version SHALL 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: false and envelope_kind: "error"
  • AND parsers SHALL NOT branch on cli_version to 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: true and envelope_kind is 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: false and envelope_kind is "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_kind is "claim" and data contains the claim payload shape

Scenario: canonical envelope_kind values are enumerated

  • WHEN envelope version is 0.2
  • THEN the canonical envelope_kind values 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_kind value 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 hints key 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 hints key; agents use remediation[] inside the ErrorResult data payload

Scenario: missing hints on success is non-conformant for producers

  • WHEN a producer emits a success envelope without a hints key
  • 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 warnings entry with rule_name and message, and optionally entity_id and suggested_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[] alongside ok: 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.tx contains 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.tx is null

Scenario: spawn resolution populates request_id

  • WHEN a command resolves a pending spawn request
  • THEN meta.request_id is 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] where timestamp follows 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", and data contains an ErrorResult with at minimum code, message, and remediation

Scenario: rule_name distinguishes rule refusals from verb-level validators

  • WHEN an error is caused by a rule-system refusal
  • THEN rule_name identifies the specific rule (e.g. lockable, unresolved-terms)
  • AND code is rule-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 code is the validator’s dedicated identifier (e.g. no-evidence, reason-required) and rule_name is null

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_name is non-null and identifies the specific rule that refused

Scenario: define label validator error has null rule_name

  • WHEN dont define is refused by a term-shape validator
  • THEN rule_name is null and code is 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 remediation array contains at least one {command, description} entry

Scenario: schema enforces remediation invariant

  • WHEN the ErrorResult JSON Schema is validated
  • THEN it requires remediation with minItems: 1

Scenario: usage errors point to help

  • WHEN an error has code: "usage"
  • THEN remediation includes an entry whose command points to dont help <cmd> for the relevant command

Scenario: internal errors point to doctor and issue reporting

  • WHEN an error has code: "internal"
  • THEN remediation includes an entry pointing to dont doctor and 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_clauses contains one entry per condition, each with clause and fix fields

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 code is one of the known domain error codes

Scenario: term-shape refusal exits code 1

  • WHEN dont define is refused by a term-shape validator such as term-shape-indefinite
  • THEN the process exits with code 1 and the envelope has ok: false, envelope_kind: "error"

Scenario: doc-extraction warning appears on success envelope

  • WHEN an actor invokes define without --label and the leading phrase of --doc fails a shape check
  • THEN the envelope has ok: true and warnings[] contains an entry with the appropriate term-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 code is usage and the envelope has ok: false, envelope_kind: "error"

Scenario: internal errors use the internal code

  • WHEN an unexpected tool failure occurs
  • THEN the error code is internal and remediation points at dont doctor and at issue reporting

Scenario: warning codes appear on success envelopes

  • WHEN an evidence URI is malformed but non-blocking
  • THEN the envelope has ok: true and warnings[] contains an entry with the evidence-malformed warning code

Scenario: rule-claim-structure warning appears on success envelope

  • WHEN rule-claim-structure is enabled and dont prime evaluates a tagged rule claim missing a mandatory slot marker
  • THEN the envelope has ok: true and warnings[] contains an entry with code: "rule-not-met", rule_name: "rule-claim-structure", and entity_id identifying the violating claim

Scenario: rule-claim-structure warning does not appear when rule is disabled

  • WHEN rule-claim-structure is not enabled in project config
  • THEN no rule-claim-structure warning 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 1 and the envelope has ok: false, envelope_kind: "error"

Scenario: usage error exits with code 2

  • WHEN a command receives malformed arguments
  • THEN the process exits with code 2 and the envelope has ok: 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].command to 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 --strict finds a warn check
  • THEN the process exits with code 1

Scenario: doctor non-strict exits 0 on warnings

  • WHEN dont doctor (without --strict) finds only warn checks and no fail checks
  • 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.md lines 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 dont from 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, or dont verify-evidence can 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> --json on 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 conclude followed by dismiss

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 ground invocation 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 ground for 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 conclude operation

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_DIR is set to a non-empty path before dont is 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_DIR is 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_DIR is unset and the filesystem walk reaches the root without finding .dont/config.toml
  • THEN the process exits with code 3 and the envelope has code: "config-missing" with a remediation entry suggesting dont init

Scenario: DONT_DIR path that lacks config.toml is rejected

  • WHEN DONT_DIR points to a directory that exists but does not contain config.toml
  • THEN the process exits with code 3 and the envelope has code: "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.toml contains a field with an invalid type or out-of-range value
  • THEN the process exits with code 3 before any store mutation
  • AND the envelope has code: "config-invalid" and message names the offending field

Scenario: valid config proceeds to command execution

  • WHEN config.toml parses 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-bot is supplied and DONT_AUTHOR=human:alice is set
  • THEN the author recorded on the resulting event is agent:ci-bot

Scenario: DONT_AUTHOR overrides USER

  • WHEN DONT_AUTHOR=agent:ci-bot is set and USER=alice is also set and no --author flag is provided
  • THEN the author recorded on the resulting event is agent:ci-bot

Scenario: USER is the last fallback

  • WHEN neither --author nor DONT_AUTHOR provides a value and USER=alice is 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> --help for 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 --help or dont 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 dont invocation

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].command and 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 3 or 4
  • 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.message to understand the malformed argument

Scenario: subprocess caller reads full stdout before exit code

  • WHEN a subprocess caller runs dont with --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> --json is 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 dont uses 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-pipeline as the author unless --author overrides 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 dont processes run concurrently with DONT_DIR pointing 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_DIR points at a path that is not named .dont (e.g. /tmp/test-abc/)
  • THEN dont accepts 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 URLs
  • dont import ols <ontology-prefix> — OLS REST API
  • dont import wikidata --entity <Qid> | --sparql <file.rq> — Wikidata entity or SPARQL query
  • dont import openalex --work <doi> | --snapshot <path> — OpenAlex paper or local snapshot
  • dont import bioregistry — Bioregistry prefix registry
  • dont import jsonld <file> — local JSON-LD file
  • dont import ttl <file> — local Turtle file
  • dont 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 leading doi: and https://doi.org/ removed before hashing/identity)
  • OpenAlex --snapshot: realpath plus 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: realpath plus 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 term entities

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_id and 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_request envelope
  • 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.yaml and the linkml CLI is not on PATH
  • 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 doctor reports 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 http and https only.
  • 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 dont in a project directory
  • THEN the tool creates persistent project-local dont state 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.md in whole from the current generator output
  • AND injects the managed dont block 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 dont block 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 --fix with no other state changes
  • THEN the command makes no byte-level changes to .dont/AGENTS.md or to any configured root document

Scenario: repeated init refuses re-initialization

  • WHEN an actor invokes init for 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 init succeeds 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 locked state

Scenario: tool upgrades do not silently rewrite the project seed snapshot

  • WHEN the dont tool 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, and dont: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:Thing or rdfs:subClassOf as 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 define with 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 define with 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 lock on a verified claim with at least three assessed hypotheses, at least two independent supporting evidence items, and an empty dependency-integrity derived_assessments[]
  • THEN the claim transitions to locked

Scenario: lock refuses claims that do not satisfy the gate

  • WHEN an actor invokes lock on 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 lock on a claim that is not in the verified state
  • THEN the command is refused

Scenario: lock refuses already-locked claims

  • WHEN an actor invokes lock on a claim that is already locked
  • THEN the command is refused

Scenario: lock refuses claims with stale support

  • WHEN an actor invokes lock on a verified claim whose derived_assessments[] contains stale
  • THEN the command is refused until the stale dependency trace is cleared

Scenario: lock refuses terms

  • WHEN an actor invokes lock on 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 undoubt on an entity whose persisted status is doubted
  • THEN the entity transitions to unverified

Scenario: undoubt refuses non-doubted entities

  • WHEN an actor invokes undoubt on an entity whose persisted status is unverified, verified, ignored, or locked
  • 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 reopen on an entity whose persisted status is ignored
  • THEN the entity transitions to unverified

Scenario: reopen refuses derived-stale entity

  • WHEN an actor invokes reopen on an entity whose persisted status is verified and whose derived_assessments[] contains stale
  • THEN the command is refused because stale is not a persisted lifecycle state

Scenario: reopen refuses non-ignored entities

  • WHEN an actor invokes reopen on an entity whose persisted status is unverified, verified, or doubted
  • THEN the command is refused

Scenario: reopen refuses locked entities

  • WHEN an actor invokes reopen on an entity whose persisted status is locked
  • 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 ignore on 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 ignore without a reason
  • THEN the command is refused

Scenario: ignore refuses hedge-only reasons

  • WHEN an actor invokes ignore with 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 ignore on 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-evidence for 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-evidence for 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-evidence for 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 flag with well-formed evidence references
  • THEN flag behavior 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-evidence rather than changing the flag contract

Scenario: verify-evidence uses bounded polite network behavior

  • WHEN verify-evidence checks 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 doubted and 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 verified and 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 verified and doubt is cleared

Scenario: deprecated alias emits warning

  • WHEN dont dismiss is invoked
  • THEN a deprecation warning is emitted to stderr suggesting flag instead
  • AND the command proceeds normally
  • AND the deprecation warning goes to stderr regardless of whether --json is 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:

  1. Flattened silently — is_a inheritance becomes a transitive kind_of chain; mixins are merged into the inheriting class’s attribute set; slot_usage refinements are expanded into per-class attribute predicates. These are expected structural lowerings, not failures.

  2. Imported with warning — permissible_values enums, 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.

  3. Refused — schemas containing SPARQL-evaluated expressions, reified slots, or python_class injections are rejected with code: "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_of chain

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 linkml refuses 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 dont starts an MCP server over stdio
  • AND the server exposes tool entry points corresponding to the supported dont command 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 dont tool 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 dont tool 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 dont through direct CLI commands rather than dont 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 --json is run on a claim with 3 atoms
  • THEN the payload contains atoms[] with 3 entries, each carrying idx, text, and status

Scenario: ClaimView includes applicable rules

  • WHEN a claim has a lockable rule that is not met
  • THEN applicable_rules.lockable has rule_kind: "gate", met: false, and unmet listing 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[] contains stale

Scenario: ClaimView includes evidence array

  • WHEN a claim has been dismissed with evidence
  • THEN the payload contains evidence[] entries with source_uri, kind, supports, and optional quote

Scenario: ClaimView updated_at reflects latest transition

  • WHEN a claim is trusted after creation
  • THEN updated_at in the ClaimView is later than created_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 --json is run on a term with kind_of: ["dont:Term"]
  • THEN the payload contains kind_of: ["dont:Term"]

Scenario: TermView includes applicable rules

  • WHEN a term has an unresolved-terms gate rule that is met
  • THEN applicable_rules["unresolved-terms"] has rule_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, and reason

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" and spawn_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 --json is run
  • THEN the payload has request_kind: "assume" and context.allowed_tools includes dont.dismiss, dont.trust, dont.show, web_search and context.forbidden_tools includes dont.conclude, dont.lock, dont.reopen

Scenario: SpawnRequest for overlook

  • WHEN dont overlook claim:X --json is run
  • THEN the payload has request_kind: "overlook" and context.prompt instructs 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?" --json is run
  • THEN the payload has request_kind: "guess" and entity_id is absent

Scenario: SpawnRequest carries expiry

  • WHEN a spawn request is issued with spawn_timeout_hours: 24
  • THEN expires_at is issued_at plus 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 --json is run on a just-initialized project
  • THEN status_counts shows all zeros, assessment_counts shows all zeros, blocking is empty, and pending_spawns is 0

Scenario: PrimeView reports blocking entities

  • WHEN 3 claims are in doubted status
  • THEN blocking[] contains 3 entries with id, statement, and status: "doubted"

Scenario: PrimeView reports harness mode

  • WHEN DONT_HARNESS=1 is set
  • THEN harness_mode is true

Scenario: PrimeView reports active rules by severity

  • WHEN a project has unresolved-terms as strict and correlated-error as warn
  • THEN rules.strict contains "unresolved-terms" and rules.warn contains "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 --json is run and lockable is not met
  • THEN remediation[] contains an entry with rule_name: "lockable" and a suggested command

Scenario: WhyView on fully satisfied entity

  • WHEN dont why claim:X --json is run and all rules pass
  • THEN remediation is an empty array and all applicable_rules show 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 --json is run
  • THEN the payload contains only claims with status: "verified" and count matches 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 --json is run on a healthy project
  • THEN all checks[] have status: "pass" and summary.fail is 0

Scenario: DoctorReport strict mode on warn

  • WHEN dont doctor --strict is run and one check has status: "warn"
  • THEN the command exits non-zero

Scenario: DoctorReport reports missing auxiliary tool

  • WHEN the linkml CLI is not on PATH
  • THEN checks[] includes an entry with name: "aux_linkml" and status: "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 --json is 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 --json is run
  • THEN the payload contains schema_name: "claim" and a valid JSON Schema document

Scenario: schema with no argument lists names

  • WHEN dont schema --json is run with no argument
  • THEN the response has envelope_kind: "empty" and hints[] 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 lockable rule requires 3 hypotheses but only 1 exists
  • THEN applicable_rules.lockable has rule_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"] has rule_kind: "flag", flagged: false

Scenario: unknown rule kind uses default branch

  • WHEN a parser encounters an applicable_rules entry with an unknown rule_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" --json is run on a fresh project
  • THEN the response has envelope_kind: "empty"

Scenario: suggest-term finds a coined term

  • WHEN dont suggest-term "Ricci" --json is run and proj:RicciTensor exists
  • THEN the response includes proj:RicciTensor in the ranked results

Scenario: suggest-term searches imported terms

  • WHEN dont suggest-term "apoptosis" --json is run and GO:0006915 is in imported_term
  • THEN the response includes GO:0006915 in 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 --json is 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 --json is 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 --json is run
  • THEN the input is validated with curie: "proj:X" and kind_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 --json is 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 --json is 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 --json is run
  • THEN the command refuses with wrong-entity-kind

Scenario: ignore requires reason

  • WHEN dont ignore claim:X --json is run without a reason
  • THEN the command refuses because reason is 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 --json is run
  • THEN the spawn request carries context.model_hint: "verifier"

Scenario: guess with multiple candidates

  • WHEN dont guess "What is X?" --n 3 --json is 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 --json is run
  • THEN the input is validated with source: "ols" and importer-specific fields

Scenario: import with unknown source

  • WHEN dont import unknown-source --json is 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.evidence is 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> --json returns 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> --json returns evidence whose stored fingerprint no longer matches the current source slice
  • THEN the evidence entry can include structured drift-audit fields such as audit.status without 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 tags continue to function without error

Scenario: optional input field remains optional across minor versions

  • WHEN ConcludeInput.confidence is 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 --json is run
  • THEN the input is validated as {statement, evidence: [{uri: "uri1"}]}

Scenario: ground rejects empty evidence list

  • WHEN GroundInput.evidence is 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> --json is 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> --json is 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:X is invoked without the --json flag
  • 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.toml sets [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 init establishes the project’s first mode
  • THEN the tool records a mode_changed event for that initial mode establishment

Scenario: config mode edit is recorded on next invocation

  • WHEN an operator edits config.toml to change mode and then runs the next dont command
  • THEN the tool records a mode_changed event 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_docs lists AGENTS.md and CLAUDE.md
  • THEN those files are eligible targets for dont doctor --fix and for the managed-docs staleness check run by dont 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].warn includes correlated-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 trust treats 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_s is set
  • THEN dont verify-evidence uses 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.endpoint is set
  • THEN the Wikidata importer uses that configured endpoint

Scenario: linkml adapter can be disabled or enabled

  • WHEN [import].linkml.enabled is 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 define does not fire term-label-sentence even when a verb token appears in the label

Scenario: default config enforces all checks

  • WHEN the project omits [define.shape] from config.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-undeclared treats "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-label emits warnings when a defined term’s label matches a configured pattern

Scenario: rule disabled by default

  • WHEN the project omits [rules.term_nonfunctional] from config.toml
  • THEN term-nonfunctional-label does not fire

Scenario: pattern list is extensible

  • WHEN a project adds "wraps a" to [rules.term_nonfunctional].patterns
  • THEN labels containing wraps a as 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.md is 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.yaml exists 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 dont managed block rather than replacing .dont/AGENTS.md as the canonical document

Scenario: claude doc can also host managed block

  • WHEN the project includes CLAUDE.md
  • THEN it may also receive the managed dont block 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-structure rule 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-structure rule 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-structure rule 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 ungrounded omits 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-type in its depends_on
  • AND the rule-claim-structure rule 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-type in its depends_on
  • THEN rule-claim-structure ignores 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-type term:uuid in its depends_on
  • THEN rule-claim-structure does 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 lockable rule 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 show or dont 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 ungrounded rule

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 dont invocation

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_name is 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 ungrounded either 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 dismiss on a claim whose CURIEs remain unresolved in coined or imported vocabulary
  • THEN unresolved-terms refuses the dismissal

Scenario: stale-cascade computes derived stale assessment

  • WHEN a trust transition moves an entity to persisted status doubted
  • THEN stale-cascade computes stale as 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 lockable checks 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-error emits a warning or strict refusal according to severity configuration

Scenario: dangling-definition blocks unresolved term relations

  • WHEN dont define names --kind-of or --related-to references that do not resolve
  • THEN dangling-definition refuses the definition in both permissive and strict modes

Scenario: term-nonfunctional-label emits warning when enabled

  • WHEN term-nonfunctional-label is 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 define command succeeds and the envelope carries a term-nonfunctional-label warning

Scenario: term-nonfunctional-label is disabled by default

  • WHEN a project does not explicitly enable term-nonfunctional-label in its configuration
  • THEN no term-nonfunctional-label warnings 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-structure is 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-structure is enabled and evaluates a claim without rule-claim-type in its depends_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-structure in its configuration
  • THEN no rule-claim-structure warnings 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 ungrounded produces warnings rather than refusing dont conclude

Scenario: strict mode escalates ungrounded

  • WHEN the project runs in strict mode without overrides
  • THEN ungrounded refuses unresolved CURIE references

Scenario: non-overridable rules stay strict

  • WHEN a project attempts to soften unresolved-terms, dangling-definition, or stale-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 lockable does 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 lockable refuses a dont lock operation
  • THEN the error envelope uses code rule-not-met
  • AND rule_name is lockable

Scenario: warning rule emits warning payload

  • WHEN correlated-error is 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 trust rejects a hedge-only reason
  • THEN the error code is reason-not-hedge
  • AND rule_name is null rather than vague-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-reason is 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 --json in 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 --json in 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, or overlook
  • 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 ... --json through 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 assume or overlook request
  • 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 dismiss or dont 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=1 is set and the caller passes --direct
  • THEN direct mode is selected

Scenario: environment variable selects harness mode

  • WHEN DONT_HARNESS is set to a non-empty value other than 0 or false
  • 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_at time without a terminal callback
  • THEN the next dont invocation emits a spawn_timeout event 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 unverified remains unverified

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 assume or dont overlook explicitly 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 status doubted or itself carries stale during the current trace.
  • compromised-support: at least one transitive support dependency is ignored or locked, 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 doubted status
  • THEN the claim keeps its persisted verified status
  • AND the output presents stale in derived_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 conclude successfully
  • THEN the new claim is introduced as unverified

Scenario: new term enters as unverified

  • WHEN an actor invokes define successfully
  • 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, or locked
  • 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 reopen on an entity whose persisted status is verified and whose derived_assessments[] contains stale
  • THEN the command is refused because stale is not a persisted lifecycle state

Scenario: reopen refuses locked entities

  • WHEN an actor invokes reopen on an entity whose persisted status is locked
  • 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 verified through the flag operation
  • THEN the transition record can include the evidence references used for that flag operation

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 doubted using the trust command
  • THEN the transition record includes the stated reason for doubt

Scenario: flag command records evidence references

  • WHEN an actor transitions an entity to verified using the flag command
  • 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 doubted to unverified using the undoubt command
  • 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> --json returns 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> --json terminates 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> --json succeeds and returns no blocker paths