Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,38 @@ semantics have been stable since 1.0.0.

## [Unreleased]

### Added — Dorian Loop Guard (vNext / 1.4.0)
- **`dorian loop`** (`src/dorian/loop.py`, `cmd_loop` in `commands.py`) — a deterministic,
token-free **steering layer for AI coding loops**, built as a thin classifier on top of
`revalidate` (no new verification, **no model at check time**):
- **`dorian loop preflight`** re-checks the claim warrants a change touched and emits a
**CONTINUE / REPAIR / ESCALATE** decision packet (`--format json|md|text`). The decision is
a pure function of the revalidate result, the `--policy` (`cautious|assist|unattended`), the
repair-attempt cap (`--max-repairs`/`--repair-attempts`/`--state-file`), and the
`--scope`/`--deny-path` lane. Exits **0 on success by default** (Dorian does not stop the loop
by itself); `--fail-on repair|escalate` opts into a hard exit-4 gate. Honors
`--deny-exec`/`--deny-shell`/`--checker-source`.
- **`dorian loop prompt`** renders the packet as a compact next-iteration instruction
(or `--from-json` a saved packet).
- **`dorian loop install`** scaffolds a project-local Claude Code skill at
`.claude/skills/dorian-loop-guard/` (**`/dorian-loop-guard`**) plus example LOOP.md / STATE.md /
run-log (`--with-state`) and a GitHub Actions example (`--with-action`). Writes files only;
idempotent; never overwrites without `--force`.
- **Packaged loop-guard templates** (`src/dorian/templates/claude_code/dorian-loop-guard/…`, shipped
as wheel package data): the skill, a bundle README, `reference/loop-decisions.md` +
`reference/safety-boundary.md`, and example `LOOP.md`/`STATE.md`/`loop-run-log.md`/`dorian-loop.yml`.
- **Docs**: [`docs/DORIAN_LOOP_GUARD.md`](docs/DORIAN_LOOP_GUARD.md),
[`docs/LOOP_ENGINEERING_ALIGNMENT.md`](docs/LOOP_ENGINEERING_ALIGNMENT.md),
[`docs/POSITIONING_LOOP_GUARD_2026_06_28.md`](docs/POSITIONING_LOOP_GUARD_2026_06_28.md), a README
"Using dorian inside AI coding loops" section, and a loop-memory note in
[`docs/CLAUDE_CODE_DORIAN_WORKFLOW.md`](docs/CLAUDE_CODE_DORIAN_WORKFLOW.md).

**No breaking changes** — purely additive: warrant schema, checker grammar, exit codes, fold policy,
and security posture are unchanged; the core stays zero-dependency; `REVOKED` remains a steering signal
(not a halt), `ERRORED` remains fail-closed evidence (not a false claim), and **no model touches the
verification path**. `claude_code.build_plan` gained an optional `manifest=` parameter (backward
compatible) so the loop installer reuses the same scaffolding machinery.

## [1.3.0] — 2026-06-28

The **Dorian Claim Warrants for Claude Code** integration: one command scaffolds a project-local Claude
Expand Down
24 changes: 24 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,30 @@ you) can emit the claims — but the canonical setup is **Claude Code**:
> is fail-closed, **not a sandbox**; see [SECURITY.md](SECURITY.md) and
> [docs/SECURITY_BOUNDARY.md](docs/SECURITY_BOUNDARY.md).

## Using dorian inside AI coding loops

When you hand an agent a long-running or unattended task, **Dorian Loop Guard** is the
deterministic verify step the loop runs *before each iteration*. `dorian loop preflight`
re-checks the claim warrants the change touched (token-free, the same `revalidate` engine)
and returns a steering signal — **Dorian does not stop the loop by default**:

- **`continue`** — the warranted claims still hold; do the next planned step.
- **`repair`** — a load-bearing claim is `REVOKED`; fix the smallest cause (or update the
claim if the change was intentional), re-check, and log the attempt.
- **`escalate`** — a checker `ERRORED`, a sensitive/denylisted path is involved, the repair
cap was hit, or the break is out of scope; stop autonomous edits and hand off to a human.

```bash
dorian loop preflight --since <base> --policy assist --format json # the decision packet
dorian loop install # scaffold /dorian-loop-guard
```

Sealed warrants become the loop's **deterministic memory**: the next iteration revalidates
them instead of re-deriving the facts. Loop Guard steers; it does **not** judge whole-loop
success, replace tests/review, or sandbox execution — and `REVOKED` is a steering signal,
not a halt. Full guide: [`docs/DORIAN_LOOP_GUARD.md`](docs/DORIAN_LOOP_GUARD.md); how it fits
loop engineering: [`docs/LOOP_ENGINEERING_ALIGNMENT.md`](docs/LOOP_ENGINEERING_ALIGNMENT.md).

## What gets committed

- the artifact (e.g. `docs/changes/login.md`),
Expand Down
7 changes: 7 additions & 0 deletions docs/CLAUDE_CODE_DORIAN_WORKFLOW.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,13 @@
5. On every later PR, the GitHub Action runs `dorian revalidate --since <base>`; if a later edit broke a
sealed claim, the warrant folds to **REVOKED** (exit 4) and the Action blocks — naming the claim.

> **Claim warrants are not only post-change receipts — they are loop memory.** In an autonomous
> coding loop, the warrants you seal here become the deterministic facts the *next* iteration
> revalidates: `dorian loop preflight` re-checks them before each step and returns
> **CONTINUE / REPAIR / ESCALATE** so a stale assumption surfaces as a steering signal instead of
> rotting silently. Same mechanism, pointed forward. See
> [`DORIAN_LOOP_GUARD.md`](DORIAN_LOOP_GUARD.md) and the `/dorian-loop-guard` skill.

## How the agent should write claims

Pick the **checker whose strength matches the claim** (the truth axis). Each claim is
Expand Down
155 changes: 155 additions & 0 deletions docs/DORIAN_LOOP_GUARD.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
# Dorian Loop Guard

**The deterministic truth layer for AI coding loops.** Loop Guard turns Dorian's claim
warrants from post-hoc receipts into the loop's *truth memory* and *steering signal*:
before each iteration it re-checks the warrants the change touched and returns a
**CONTINUE / REPAIR / ESCALATE** decision — token-free, no model at check time.

## What it is

A thin classifier on top of `dorian revalidate`. `dorian loop preflight` runs the same
deterministic re-check `revalidate` does, reads each touched warrant's post-fold trust
state, and maps the result to a loop decision packet (JSON / markdown / text). The
verdict is exactly `revalidate`'s — Loop Guard adds no new verification, no model, no
tokens.

## What it is not

- **Not a whole-loop judge.** It verifies only the specific warranted claims a change
touched — never that "the change is good" or "the loop succeeded". It cannot catch a
lie of omission.
- **Not a replacement** for tests, SAST, code review, or human judgment — complementary.
- **Not a loop runner.** It does not schedule the loop, manage worktrees, deliver
notifications, or estimate token cost. Those belong to a loop framework (e.g.
[loop-engineering](https://github.com/cobusgreyling/loop-engineering)'s loop-init /
loop-audit / loop-cost). See [LOOP_ENGINEERING_ALIGNMENT.md](LOOP_ENGINEERING_ALIGNMENT.md).
- **Not a sandbox.** `preflight` calls `revalidate`, which *runs* each touched claim's
checker; `C4 pytest:` / `C5 shell:` execute code. Use it on **trusted, internal repos**.

## Why loops need a deterministic truth layer

The loop-engineering failure catalog's single biggest trap is **weak verification**: the
agent that wrote the code grading its own work, or a verifier that says "looks good"
without running anything (*verifier theater*). The fix is an *independent* verifier — and
the most independent verifier possible is one that runs **no model at all**. A claim
warrant either matches the current code or flips to `REVOKED`, leaving an inspectable
sidecar. Dorian cannot rubber-stamp, cannot be talked past, and costs zero tokens — so it
is the natural deterministic floor under a loop's verify step.

## Preflight → continue / repair / escalate

```bash
dorian loop preflight --since <base> --policy assist --format json
```

The decision is a **pure function** of the revalidate result, the policy, the
repair-attempt count, and the scope/denylist (full table in
[loop-decisions.md](../src/dorian/templates/claude_code/dorian-loop-guard/reference/loop-decisions.md)):

| # | Condition | Decision |
|---|-----------|----------|
| 1 | a checker **ERRORED** (could not run) | **escalate** (`fix_environment`) |
| 2 | a break exists and `repair_attempts >= max_repairs` | **escalate** (infinite-fix cap) |
| 3 | a broken/errored claim is bound to a **sensitive / denylisted** path | **escalate** |
| 4 | a **load-bearing** break is **outside `--scope`** | **escalate** (over-reach) |
| 5 | `unattended` policy with **no `--scope`** and a load-bearing break | **escalate** |
| 6 | a **load-bearing** break, in scope, under the cap | **repair** |
| 7 | only **non-load-bearing** breaks | `cautious`: repair · else continue (warn) |
| 8 | no breaks (TRUSTED / nothing warranted touched) | **continue** |

Policies: **cautious** (repairs even non-load-bearing breaks; cap 1), **assist** (default;
repairs load-bearing, continues past non-load-bearing; cap 3), **unattended** (like assist
but requires `--scope` before repairing a load-bearing break — an L3 loop needs a bounded
lane; cap 3).

**Exit codes.** `preflight` exits **0 on success regardless of the decision** — Dorian
does not stop the loop by default; the decision lives in the output. For a hard CI gate,
opt in with `--fail-on repair` or `--fail-on escalate` (exit 4 at/above that severity) — the
same opt-in-to-block spirit as the claim-warrants Action's `fail_on: revoked`, but here the
default is non-blocking. The shipped Action example uses `--fail-on escalate`.

## Claim warrants as loop memory

After a change, the loop drafts and seals new warrants with **`/dorian-claim-warrants`**
and `dorian verify`. Those `.warrant` sidecars are the loop's **deterministic memory**:
the next iteration's preflight *revalidates* them instead of re-deriving the facts, and
`REVOKE`s the ones a later edit broke — so a stale assumption surfaces as a steering
signal rather than rotting silently in `STATE.md`.

## Example `LOOP.md` / `STATE.md` / run log

`dorian loop install --with-state` writes example
[`LOOP.md`](../src/dorian/templates/claude_code/dorian-loop-guard/templates/LOOP.md) (the
loop's durable spec: purpose, cadence, scope, verification commands, escalation rules),
[`STATE.md`](../src/dorian/templates/claude_code/dorian-loop-guard/templates/STATE.md)
(live working memory + a machine-read `repair_attempts` counter for `--state-file`), and
[`loop-run-log.md`](../src/dorian/templates/claude_code/dorian-loop-guard/templates/loop-run-log.md)
(append-only run history). These follow loop-engineering's conventions; Dorian only
*reads* the attempt count (via `--state-file`) — it never writes loop state.

## Example loop run

```text
$ dorian loop preflight --since main~1 --policy assist --format text
decision: REPAIR (policy=assist)
1 load-bearing claim(s) REVOKED, in scope and under the repair cap — repair the
smallest cause (or update the claim/doc if the change was intentional).
trust: trusted=0 warranted=0 degraded=0 revoked=1 errored=0
BROKEN login-timeout-30s [load-bearing] C3: regex_missing
paths=src/api/config.py -> repair_code
next: REPAIR: fix the smallest cause of these broken claim(s): login-timeout-30s …
```

The loop restores the timeout (or updates the claim), re-runs `dorian revalidate --since
main~1` to confirm TRUSTED, logs the attempt, and continues. After the repair cap, the
same break returns `escalate` instead of looping forever.

## Failure modes Dorian helps with

Weak verification, verifier theater, the infinite-fix-loop signal (paired with the cap),
and the escalation *trigger*. It partially helps with over-reach (`--scope`), state rot
(code-anchored warrants auto-revoke), token burn (a cheap deterministic pre-filter before
spawning sub-agents), and comprehension debt (a machine-checked ledger of guarantees).

## Failure modes Dorian does NOT solve

Parallel collision (worktree isolation / locks — defer to the loop runner), notification
*delivery* and digesting, budget/cost estimation, cognitive surrender and comprehension
debt at the cultural level, and live PR/ticket liveness (state rot beyond code-anchored
facts). Dorian provides the signal; the loop runner owns scheduling, delivery, and budget.

## Trusted-repo / not-a-sandbox boundary

`preflight` executes the touched claims' checkers through `revalidate`. `C4 pytest:` and
`C5 shell:` run code with your privileges — **this is not a sandbox**. Use Loop Guard on
trusted, internal repos. For semi-trusted contexts pass `--deny-exec` (refuse the
executable families — fail-closed) and/or `--checker-source base` (never run a PR-added
checker); neither is a sandbox. See [SECURITY_BOUNDARY.md](SECURITY_BOUNDARY.md).

## How to use with Claude Code

```bash
dorian loop install # scaffolds .claude/skills/dorian-loop-guard/ (/dorian-loop-guard)
dorian loop install --with-state --with-action # + LOOP.md/STATE.md/run-log + a GH Action example
```

Invoke **`/dorian-loop-guard`** when driving an autonomous loop. The skill tells Claude to
run preflight before each iteration and act on the decision; after a change it hands off to
`/dorian-claim-warrants` to seal new warrants.

## How to use with GitHub Actions

`dorian loop install --with-action` writes an example
[`.github/workflows/dorian-loop.yml`](../src/dorian/templates/claude_code/dorian-loop-guard/templates/dorian-loop.yml)
that runs `dorian loop preflight --fail-on escalate` on each pull request and writes the
markdown packet to the job summary. It blocks only on ESCALATE by default (tighten with
`--fail-on repair`). For public/fork PRs add `--checker-source base --deny-exec`.

## How to combine with loop-engineering tools

Dorian is a drop-in **L2/L3 verifier primitive**: run `dorian loop preflight` as the
deterministic verify step, map its decision onto the loop's commit-vs-escalate gate, and
let the `.warrant` trail complement `loop-run-log.md` as the deterministic-evidence ledger.
Keep scheduling, worktree isolation, cost estimation, and readiness scoring in
loop-engineering's own tools (loop-init / loop-audit / loop-cost) — Dorian does not compete
with them. See [LOOP_ENGINEERING_ALIGNMENT.md](LOOP_ENGINEERING_ALIGNMENT.md).
76 changes: 76 additions & 0 deletions docs/LOOP_ENGINEERING_ALIGNMENT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
# Loop-engineering alignment

How Dorian Loop Guard plugs into [loop-engineering](https://github.com/cobusgreyling/loop-engineering)
(MIT) — and, just as importantly, where it deliberately does **not** compete. Loop
engineering frames building autonomous agent loops out of primitives (scheduling,
run-until-done, worktrees, skills, connectors/MCP, sub-agents) plus durable memory, scored
by readiness levels L0→L3. Dorian is one primitive in that world: the **deterministic,
token-free verifier** under the maker/checker split.

> Treat loop-engineering as adjacent ecosystem and design inspiration; this is alignment,
> not a fork. Verify its facts live before relying on dated specifics.

## Primitive map

| loop-engineering primitive | Dorian's relationship |
|---|---|
| **Scheduling / automations** (`/loop`, cron, Actions) | Dorian runs *inside* a scheduled step; it never schedules. |
| **Run-until-done** (`/goal`) | A warrant's TRUSTED/REVOKED is a deterministic stop/continue input to the loop's condition. |
| **Worktrees** (isolation) | Dorian verifies inside a worktree; it does not manage them. |
| **Skills** | Dorian ships a skill (`/dorian-loop-guard`) and a verify step; complementary to project skills. |
| **Connectors / MCP** | Dorian is local + token-free; escalation *delivery* over connectors is the loop runner's. |
| **Sub-agents (maker/checker)** | Dorian is the deterministic floor under the *checker* role — it cannot grade its own work or rubber-stamp. |
| **Memory / state** (LOOP.md, STATE.md, run log) | `.warrant` sidecars are deterministic, revalidatable memory; Dorian *reads* a repair-attempt count, never writes loop state. |

## Readiness levels

- **L1 (report)** — `dorian loop preflight --format md|json` writes a deterministic finding
to state; a human acts. Maps to the `cautious`/`assist` policies with `--fail-on never`.
- **L2 (assisted)** — `assist` policy: repair load-bearing breaks under a cap, continue past
non-load-bearing. The `--max-repairs` cap + `--repair-attempts` accounting is the bounded
auto-fix gate L2 requires.
- **L3 (unattended)** — `unattended` policy **requires `--scope`** before autonomously
repairing a load-bearing break, mirroring L3's "denylist/bounded lane required" rule.
Pair with `--state-file`, `--deny-path`, and explicit human escalation.

## Where Dorian plugs in

The deterministic verify step. Its decision maps directly onto the loop's gate:

- **CONTINUE** → commit / open PR / next planned step.
- **REPAIR** → hand the broken claim(s) back to the implementer (smallest cause).
- **ESCALATE** → write to STATE.md "waiting on human" and ping via a connector.

The `.warrant` trail complements `loop-run-log.md` as the deterministic-evidence ledger.

## Where Dorian should NOT compete

| loop-engineering tool | Why Dorian stays out |
|---|---|
| **loop-init** | Scaffolds loop starters/budget/run-log. Dorian *consumes* these files; `dorian loop install` only adds the verify skill + examples. |
| **loop-audit** | Scores L0–L3 readiness. Dorian can be *evidence* for a checklist item ("independent deterministic verifier present"), never a re-implementation of scoring. |
| **loop-cost** | Estimates token spend. Dorian is token-free and models no cost. |
| **schedulers / worktree managers** | Cadence, durability, isolation, parallel-collision locks — entirely the loop runner's. |

## Failure-mode coverage

| loop-engineering failure mode | Dorian | Note |
|---|---|---|
| Weak verification / same-session verifier | **strong** | model-free, separate-by-construction verifier |
| Verifier theater | **strong** | can't say "looks good" — REVOKE leaves a sidecar |
| Infinite fix loop | **signal + cap** | repeated REVOKED + `--max-repairs` → escalate; the *counter* lives in STATE.md |
| Escalation failure | **trigger** | unambiguous escalate trigger; **delivery** is the loop runner's |
| Over-reach / wrong scope | **partial** | `--scope` escalates out-of-lane breaks; path allow/deny stays in skills |
| State rot | **partial** | code-anchored warrants auto-revoke; live PR/ticket liveness is the runner's prune |
| Token burn | **aligned** | token-free pre-filter before spawning sub-agents; caps stay with loop-cost |
| Comprehension debt | **partial** | warrant ledger documents guarantees; not a substitute for review |
| Notification fatigue | **marginal** | high-precision triggers; routing/digest is the runner's |
| Parallel collision | **no** | worktree/branch locks — defer entirely |
| Cognitive surrender | **cultural** | explicit claims state intent; human gates are process |

## One-line positioning

Dorian is the **deterministic truth layer for AI coding loops** — it keeps the loop honest
by warranting its load-bearing claims and steering the next iteration when one breaks. It
does not replace loop-engineering; it is the model-free verifier loop-engineering says every
serious loop needs.
Loading