DOTS v0.1.0 · MIT zero dependencies

One prompt in.
One running system out.
One review.

Stages
6
Deps
0
Tests
50
Reviews
1
CA
— not live yet —
dots · orchestrator live --:--:--
DOTS Waiting for a prompt
No run yet. Send a prompt and the response streams the six real stages — resolve · plan · gate · build · verify · ledger
01

Six agents, one goal

Not a roster you manage. One orchestrator holding the goal and switching modes — and the mode decides what it is allowed to touch.
01

RESOLVER

Turns a sentence into a typed, frozen, self-hashing contract. Fills the gaps that do not change the shape of the system and names every one of them out loud.

prompt → spec.lock
02

PLANNER

Spec into a DAG of bounded nodes. Each declares its dependencies, its capabilities and its outputs before it runs. Cycles die here, not mid-build.

spec → plan DAG
03

GATE

Four states per action. Two pinned so no config can delegate a credential or a payment. Everything held is batched into a single review, never drip-fed.

plan → decisions
04

BUILDER

Walks the plan, asks the provider for bytes, checks the gate before every write. A node that writes a file it never declared is an error, not a warning.

plan → tree
05

VERIFIER

Checks the tree against the contract — never against the plan. A plan that completed only tells you the agent did what it decided to do, which nobody asked.

tree → report
06

LEDGER

Append-only, hash-chained. Stopping a run does not undo it, so the next best thing is that nothing it did can be quietly rewritten afterwards.

run → chain
02

What DOTS actually is

Not a model. The frame around one: contract, bounds, gate, ledger.

The argument

A developer reading the always-on agent launches put it in one line: my throughput is limited by my approval. If that is true, a faster agent does not help you. A second does not help. Fifty do not help. They deepen the queue in front of the same bottleneck.

Every approval point is a wake-up you scheduled for yourself. Eleven of them in a routine is eleven interruptions, and by the tenth you are clicking without reading — including the one that mattered.

So DOTS does not compete on speed. It produces one artifact worth exactly one review, and it is built so that review can be short: the contract is explicit, the assumptions are listed, the bounds are enforced, and the result is reproducible enough that reading the diff is the whole job.

What you get back

  • spec.lock — the frozen contract, carrying its own digest. Edit it by hand and the next command refuses it.
  • A running tree — entrypoint, capability modules, config frozen from the spec, a manifest, and its own acceptance tests.
  • An assumption list — every default the resolver filled, named, with its reason.
  • One review queue — batched, with the full diff, once.
  • A replayable digest — same prompt, same lock, same bytes, or the run is not sealed.

The pipeline

prompt
  │
  ├─ RESOLVE  sentence → typed, frozen, self-hashing contract
  ├─ PLAN     DAG of nodes, each declaring what it may touch
  ├─ GATE     one pass over the plan, before the first write
  ├─ BUILD    content-addressed, every write checked
  ├─ VERIFY   against the spec, never against the plan
  └─ LEDGER   append-only, hash-chained, replayable

Each stage is a separate object, a separate command and a separate failure class, because the interesting failures happen between stages.

03

The gate, in four states

Two of them cannot be moved by any configuration. A rule that is silently ignored is worse than one that refuses.
ALLOW
Run it, log it. For everything routine enough that a prompt would be noise.
PREAPPROVED
Run it if a rule written in advance matches. Eligible is not the same as granted.
ASK
Held for the one review at the end. Collected, never drip-fed one at a time.
HANDOFF
Never delegated. Credential rotation and moving money are pinned here, permanently.

The ceiling nobody configures

An approval list has a hard limit before it stops working as one. Past a handful of prompts per run, people approve on reflex — so DOTS reports its own overuse rather than waiting to be noticed:

! 5 approval points in one run (ceiling 3). Move the routine
  ones to allow or to handoff — a long queue gets approved
  on reflex.

Pick the two or three moments where human judgement changes the outcome. Everything else goes up into allow or down into handoff. A gate you actually obey beats a gate you merely have.

Policy, in the repo

# dots.toml
[gate]
ceiling     = 3
preapproved = ["secrets.read"]

[policy]
"fs.write"        = "allow"
"net.http"        = "allow"
"notify.outbound" = "ask"
"shell.exec"      = "ask"
"secrets.rotate"  = "handoff"   # pinned
"funds.transfer"  = "handoff"   # pinned

Try to move a pinned class and the config is rejected at load time, not ignored at run time.

04

The tech

Python 3.10+, standard library only. A tool promising reproducible output should not inherit a dependency tree it cannot pin.
Runtime
3.10+
CPython, tested to 3.13 on Linux, macOS and Windows
Dependencies
0
Standard library at runtime; pytest and ruff for dev
Hash
SHA-256
One canonical encoder for spec, plan, cache and tree
Tests
50
Determinism asserted in CI on every commit, not just claimed

Modules

filedoes
hashing.pyCanonical JSON, four digest kinds, directory walk
spec.pyResolver, Spec, Check, Assumption, ambiguity budget
planner.pyDAG, stable topological order, wave grouping
gate.pyFour states, pinned classes, batch review, ceiling
builder.pyContent-addressed cache, output enforcement
verifier.pyFive check kinds, AST parse pass, manifest match
ledger.pyAppend-only JSONL with a hash chain
config.pydots.toml, with a reader fallback for 3.10
cli.pyEight commands, --json on all of them
providers/Pluggable emitters behind one determinism contract

Exit codes

codeclass
0sealed and verified
1DotsError — generic
2ResolveError
3AmbiguityError — strict mode
4PlanError — cycle or dangling need
5BuildError — node failed or over-reached
6VerifyError — or replay drift
7GateError — blocked or handed off
8LedgerError — chain broken

Distinct per failure class so CI branches on what went wrong instead of grepping stderr.

Install and run

# no dependencies, no keys, no build step
git clone https://github.com/0xwast3/dots.git
cd dots && pip install -e .

dots build "poll the odds api every 5 minutes
            and alert me on a 10% swing"

●●●●  odds-watch
  run     a0eaf93ee021
  spec    e67b2937a40b
  plan    bb533e6d12f1  (9 nodes, 0 cached)
  tree    c43ae69ed5ef
  checks  pass  8/8
  review  ask 1 · handoff 0
  sealed  reproducible

# the only honest test of determinism
dots replay a0eaf93ee021
  recorded  c43ae69ed5ef
  replayed  c43ae69ed5ef
  identical

Guarantees

boundarywhat it buys
spec digesta lock cannot be edited into saying something else
node outputsa node cannot write outside what it declared
pre-flight gatea refusal costs nothing instead of a tree
pinned classesno config delegates a credential or a payment
tree digest"the same build" is measured, not claimed
hash chaina quiet change becomes a loud one

What this is not

  • Not an always-on agent. No inbox, no calendar, no browser that stays logged in. A run starts when you start it.
  • Not a model. Swapping the model must not change the guarantees — that is the whole point of the boundary.
  • Not magic on a vague prompt. Garbage in, assumptions out: listed, hashed, and yours to read before you accept them.
  • Not a reason to stop reading diffs. It is an argument about how many deserve your attention, not a claim that none do.