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
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ bench/real/
# tool working dirs (not release content)
.claude/
.gitnexus/
.dorian/local/ # ephemeral host-hook runtime packet (last-decision.json); never tracked
# ephemeral host-hook runtime packet (last-decision.json); never tracked
.dorian/local/

# internal program/audit working docs — provenance only, never shipped in the release
/docs/design/C4_IMPORT_BINDING_REPORT.md
Expand Down
4 changes: 3 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ semantics have been stable since 1.0.0.

## [Unreleased]

### Added — Dorian Loop Guard (vNext / 1.4.0)
## [1.4.0] — 2026-07-02

### Added — Dorian Loop Guard (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**):
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -458,7 +458,7 @@ jobs:
with:
fetch-depth: 0 # revalidate diffs against the PR base sha
persist-credentials: false # the Action only reads the diff + posts via GITHUB_TOKEN
- uses: ajaysurya1221/dorian/action@v1.3.0
- uses: ajaysurya1221/dorian/action@v1.4.0
with:
fail_on: revoked
# install defaults to the published PyPI package (dorian-vwp); pin a
Expand Down Expand Up @@ -625,7 +625,7 @@ work perishable, so you find out when it expired.
**reproducible on those frozen SHAs only** — not a real-world performance claim; the trigger and
truth layers are reported separately.
- **PyPI trusted publishing** — `dorian-vwp` is published to PyPI via a Trusted Publisher
(latest: **`v1.3.0`**); `pip install dorian-vwp` installs the released package.
(latest: **`v1.4.0`**); `pip install dorian-vwp` installs the released package.

Non-goals stay non-goals: no servers, no dashboards, no hosted control plane, no model at check time.
Local-first is the design center.
Expand Down
6 changes: 3 additions & 3 deletions action/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ jobs:
fetch-depth: 0 # REQUIRED: revalidate diffs against the PR base
# sha, which a shallow clone does not contain
persist-credentials: false # the Action reads the diff + posts via GITHUB_TOKEN
- uses: ajaysurya1221/dorian/action@v1.3.0
- uses: ajaysurya1221/dorian/action@v1.4.0
with:
fail_on: revoked
# install defaults to the published PyPI package (dorian-vwp);
Expand Down Expand Up @@ -78,7 +78,7 @@ self-attested-verdict problem for *non-executable* checkers — that is what

```yaml
# untrusted / public-fork posture
- uses: ajaysurya1221/dorian/action@v1.3.0
- uses: ajaysurya1221/dorian/action@v1.4.0
with:
deny_exec: "true" # C4/C5 ERROR instead of executing
```
Expand All @@ -94,7 +94,7 @@ executed). Implemented and proven by the

```yaml
# public / forked-PR posture: trusted checker specs + no code execution
- uses: ajaysurya1221/dorian/action@v1.3.0
- uses: ajaysurya1221/dorian/action@v1.4.0
with:
checker_trust: base # run only base-approved checker specs
deny_exec: "true" # and refuse to execute even those (belt and braces)
Expand Down
4 changes: 2 additions & 2 deletions docs/BENCHMARK_CURRENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ and are kept as-is for provenance.

| field | value |
| --- | --- |
| dorian version | `1.3.0` |
| dorian version | `1.4.0` |
| metric commit | `33e9eaf` (the benchmark figures were measured here, during the release audit) |
| release commit | `81cebbc` (1.0.1) → v1.0.2 announcement hotfix. The 1.0.1 changes (C4 leading-dash nodeid rejection, C5 reconcile per-query timeout, a byte-identical index-once `verify` refactor, the `suggest-claims` / `export --in-toto` commands) plus the v1.0.2 hotfix (export `.warrant` filename disambiguation, `suggest-claims` PEP 263 encoding read, a `symbol_index` non-git `GitError` guard, and CI/SCA/credential/doc hardening) touch no checker numeric behavior; both suites below were **re-run at 1.0.2 and reproduce the metric-commit figures exactly** — binding-lifecycle to the same content-derived `run_id` `168b50d9aa631d52` — so these changes do not move what the suites measure. v1.1.0 added the `dorian init` scaffolder, the PR-comment renderer enhancements (a status line, trust-change counts, sealed-at, and remediation), and build/VCS guards against editor/file-sync `… 2.py` duplicate files (untracked local artifacts — never tracked, never in a CI wheel or on PyPI); v1.1.1 makes the `dorian init` starter claim load-bearing (a scaffold default). All of this is a new command plus output formatting, scaffold defaults, and packaging hygiene only — touching no checker/binding/fold code — so the figures stand unchanged at 1.1.1 (the suites were last executed at 1.0.2, not re-run since). **v1.2.0** adds C4 import-aware binding (a trigger-axis watch widening that affects only C4 `pytest:` claims — not the C1/C3/C5 symbol/regex/string/path/data paths these suites exercise) and the opt-in, default-off `--strength-gate` (advisory; changes no checker verdict or binding). All three suites were **re-run at v1.2.0 and reproduce the metric-commit figures exactly** — binding-lifecycle again landing on the same content-derived `run_id 168b50d9aa631d52` — so 1.2.0 does not move what they measure. **v1.3.0** adds the Claude Code claim-warrants scaffolder (`dorian claude-code install-claim-warrants`: a new CLI subcommand, packaged skill/hook/settings templates, an opt-in reminder Stop hook, and docs) — a CLI/packaging/docs addition that touches **no checker, binding, or fold code**, so the figures stand unchanged at 1.3.0 (the suites were last executed at v1.2.0, not re-run since) |
| release commit | `81cebbc` (1.0.1) → v1.0.2 announcement hotfix. The 1.0.1 changes (C4 leading-dash nodeid rejection, C5 reconcile per-query timeout, a byte-identical index-once `verify` refactor, the `suggest-claims` / `export --in-toto` commands) plus the v1.0.2 hotfix (export `.warrant` filename disambiguation, `suggest-claims` PEP 263 encoding read, a `symbol_index` non-git `GitError` guard, and CI/SCA/credential/doc hardening) touch no checker numeric behavior; both suites below were **re-run at 1.0.2 and reproduce the metric-commit figures exactly** — binding-lifecycle to the same content-derived `run_id` `168b50d9aa631d52` — so these changes do not move what the suites measure. v1.1.0 added the `dorian init` scaffolder, the PR-comment renderer enhancements (a status line, trust-change counts, sealed-at, and remediation), and build/VCS guards against editor/file-sync `… 2.py` duplicate files (untracked local artifacts — never tracked, never in a CI wheel or on PyPI); v1.1.1 makes the `dorian init` starter claim load-bearing (a scaffold default). All of this is a new command plus output formatting, scaffold defaults, and packaging hygiene only — touching no checker/binding/fold code — so the figures stand unchanged at 1.1.1 (the suites were last executed at 1.0.2, not re-run since). **v1.2.0** adds C4 import-aware binding (a trigger-axis watch widening that affects only C4 `pytest:` claims — not the C1/C3/C5 symbol/regex/string/path/data paths these suites exercise) and the opt-in, default-off `--strength-gate` (advisory; changes no checker verdict or binding). All three suites were **re-run at v1.2.0 and reproduce the metric-commit figures exactly** — binding-lifecycle again landing on the same content-derived `run_id 168b50d9aa631d52` — so 1.2.0 does not move what they measure. **v1.3.0** adds the Claude Code claim-warrants scaffolder (`dorian claude-code install-claim-warrants`: a new CLI subcommand, packaged skill/hook/settings templates, an opt-in reminder Stop hook, and docs) — a CLI/packaging/docs addition that touches **no checker, binding, or fold code**, so the figures stand unchanged at 1.3.0 (the suites were last executed at v1.2.0, not re-run since). **v1.4.0** adds the Dorian Loop Guard (`dorian loop preflight|prompt|install`) and the governance foundation (`dorian goal`/`gate`/`governance install`, the atomic sidecar writer, the deterministic-core import-firewall test) — loop-steering/preflight commands, host-adapter templates, and docs that touch **no checker, binding, or fold code** — so the figures stand unchanged at 1.4.0 (the suites were last executed at v1.2.0, not re-run since) |
Comment on lines +13 to +15

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Escape the raw pipes in the benchmark table.

The | separators in dorian loop preflight|prompt|install break this Markdown table and trigger the lint warning. Rewrite that fragment or move the release-history narrative out of the table.

Suggested fix
- ... `dorian loop preflight|prompt|install` ...
+ ... `dorian loop preflight / prompt / install` ...
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
| dorian version | `1.4.0` |
| metric commit | `33e9eaf` (the benchmark figures were measured here, during the release audit) |
| release commit | `81cebbc` (1.0.1) → v1.0.2 announcement hotfix. The 1.0.1 changes (C4 leading-dash nodeid rejection, C5 reconcile per-query timeout, a byte-identical index-once `verify` refactor, the `suggest-claims` / `export --in-toto` commands) plus the v1.0.2 hotfix (export `.warrant` filename disambiguation, `suggest-claims` PEP 263 encoding read, a `symbol_index` non-git `GitError` guard, and CI/SCA/credential/doc hardening) touch no checker numeric behavior; both suites below were **re-run at 1.0.2 and reproduce the metric-commit figures exactly** — binding-lifecycle to the same content-derived `run_id` `168b50d9aa631d52` — so these changes do not move what the suites measure. v1.1.0 added the `dorian init` scaffolder, the PR-comment renderer enhancements (a status line, trust-change counts, sealed-at, and remediation), and build/VCS guards against editor/file-sync `… 2.py` duplicate files (untracked local artifacts — never tracked, never in a CI wheel or on PyPI); v1.1.1 makes the `dorian init` starter claim load-bearing (a scaffold default). All of this is a new command plus output formatting, scaffold defaults, and packaging hygiene only — touching no checker/binding/fold code — so the figures stand unchanged at 1.1.1 (the suites were last executed at 1.0.2, not re-run since). **v1.2.0** adds C4 import-aware binding (a trigger-axis watch widening that affects only C4 `pytest:` claims — not the C1/C3/C5 symbol/regex/string/path/data paths these suites exercise) and the opt-in, default-off `--strength-gate` (advisory; changes no checker verdict or binding). All three suites were **re-run at v1.2.0 and reproduce the metric-commit figures exactly** — binding-lifecycle again landing on the same content-derived `run_id 168b50d9aa631d52` — so 1.2.0 does not move what they measure. **v1.3.0** adds the Claude Code claim-warrants scaffolder (`dorian claude-code install-claim-warrants`: a new CLI subcommand, packaged skill/hook/settings templates, an opt-in reminder Stop hook, and docs) — a CLI/packaging/docs addition that touches **no checker, binding, or fold code**, so the figures stand unchanged at 1.3.0 (the suites were last executed at v1.2.0, not re-run since) |
| release commit | `81cebbc` (1.0.1) → v1.0.2 announcement hotfix. The 1.0.1 changes (C4 leading-dash nodeid rejection, C5 reconcile per-query timeout, a byte-identical index-once `verify` refactor, the `suggest-claims` / `export --in-toto` commands) plus the v1.0.2 hotfix (export `.warrant` filename disambiguation, `suggest-claims` PEP 263 encoding read, a `symbol_index` non-git `GitError` guard, and CI/SCA/credential/doc hardening) touch no checker numeric behavior; both suites below were **re-run at 1.0.2 and reproduce the metric-commit figures exactly** — binding-lifecycle to the same content-derived `run_id` `168b50d9aa631d52` — so these changes do not move what the suites measure. v1.1.0 added the `dorian init` scaffolder, the PR-comment renderer enhancements (a status line, trust-change counts, sealed-at, and remediation), and build/VCS guards against editor/file-sync `… 2.py` duplicate files (untracked local artifacts — never tracked, never in a CI wheel or on PyPI); v1.1.1 makes the `dorian init` starter claim load-bearing (a scaffold default). All of this is a new command plus output formatting, scaffold defaults, and packaging hygiene only — touching no checker/binding/fold code — so the figures stand unchanged at 1.1.1 (the suites were last executed at 1.0.2, not re-run since). **v1.2.0** adds C4 import-aware binding (a trigger-axis watch widening that affects only C4 `pytest:` claims — not the C1/C3/C5 symbol/regex/string/path/data paths these suites exercise) and the opt-in, default-off `--strength-gate` (advisory; changes no checker verdict or binding). All three suites were **re-run at v1.2.0 and reproduce the metric-commit figures exactly** — binding-lifecycle again landing on the same content-derived `run_id 168b50d9aa631d52` — so 1.2.0 does not move what they measure. **v1.3.0** adds the Claude Code claim-warrants scaffolder (`dorian claude-code install-claim-warrants`: a new CLI subcommand, packaged skill/hook/settings templates, an opt-in reminder Stop hook, and docs) — a CLI/packaging/docs addition that touches **no checker, binding, or fold code**, so the figures stand unchanged at 1.3.0 (the suites were last executed at v1.2.0, not re-run since). **v1.4.0** adds the Dorian Loop Guard (`dorian loop preflight|prompt|install`) and the governance foundation (`dorian goal`/`gate`/`governance install`, the atomic sidecar writer, the deterministic-core import-firewall test) — loop-steering/preflight commands, host-adapter templates, and docs that touch **no checker, binding, or fold code** — so the figures stand unchanged at 1.4.0 (the suites were last executed at v1.2.0, not re-run since) |
| dorian version | `1.4.0` |
| metric commit | `33e9eaf` (the benchmark figures were measured here, during the release audit) |
| release commit | `81cebbc` (1.0.1) → v1.0.2 announcement hotfix. The 1.0.1 changes (C4 leading-dash nodeid rejection, C5 reconcile per-query timeout, a byte-identical index-once `verify` refactor, the `suggest-claims` / `export --in-toto` commands) plus the v1.0.2 hotfix (export `.warrant` filename disambiguation, `suggest-claims` PEP 263 encoding read, a `symbol_index` non-git `GitError` guard, and CI/SCA/credential/doc hardening) touch no checker numeric behavior; both suites below were **re-run at 1.0.2 and reproduce the metric-commit figures exactly** — binding-lifecycle to the same content-derived `run_id` `168b50d9aa631d52` — so these changes do not move what the suites measure. v1.1.0 added the `dorian init` scaffolder, the PR-comment renderer enhancements (a status line, trust-change counts, sealed-at, and remediation), and build/VCS guards against editor/file-sync `… 2.py` duplicate files (untracked local artifacts — never tracked, never in a CI wheel or on PyPI); v1.1.1 makes the `dorian init` starter claim load-bearing (a scaffold default). All of this is a new command plus output formatting, scaffold defaults, and packaging hygiene only — touching no checker/binding/fold code — so the figures stand unchanged at 1.1.1 (the suites were last executed at 1.0.2, not re-run since). **v1.2.0** adds C4 import-aware binding (a trigger-axis watch widening that affects only C4 `pytest:` claims — not the C1/C3/C5 symbol/regex/string/path/data paths these suites exercise) and the opt-in, default-off `--strength-gate` (advisory; changes no checker verdict or binding). All three suites were **re-run at v1.2.0 and reproduce the metric-commit figures exactly** — binding-lifecycle again landing on the same content-derived `run_id 168b50d9aa631d52` — so 1.2.0 does not move what they measure. **v1.3.0** adds the Claude Code claim-warrants scaffolder (`dorian claude-code install-claim-warrants`: a new CLI subcommand, packaged skill/hook/settings templates, an opt-in reminder Stop hook, and docs) — a CLI/packaging/docs addition that touches **no checker, binding, or fold code**, so the figures stand unchanged at 1.3.0 (the suites were last executed at v1.2.0, not re-run since). **v1.4.0** adds the Dorian Loop Guard (`dorian loop preflight / prompt / install`) and the governance foundation (`dorian goal`/`gate`/`governance install`, the atomic sidecar writer, the deterministic-core import-firewall test) — loop-steering/preflight commands, host-adapter templates, and docs that touch **no checker, binding, or fold code** — so the figures stand unchanged at 1.4.0 (the suites were last executed at v1.2.0, not re-run since) |
🧰 Tools
🪛 LanguageTool

[grammar] ~15-~15: Ensure spelling is correct
Context: ...fix. The 1.0.1 changes (C4 leading-dash nodeid rejection, C5 reconcile per-query timeo...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)

🪛 markdownlint-cli2 (0.22.1)

[warning] 15-15: Table column count
Expected: 2; Actual: 4; Too many cells, extra data will be missing

(MD056, table-column-count)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/BENCHMARK_CURRENT.md` around lines 13 - 15, The benchmark table row
contains unescaped pipe characters in the release-history text, which breaks the
Markdown table structure. Update the entry in BENCHMARK_CURRENT so the `dorian
loop preflight|prompt|install` fragment no longer uses raw table separators, or
move the long narrative out of the table entirely; use the existing
benchmark/version row content as the anchor when editing.

Source: Linters/SAST tools

| Python | 3.12.4 |
| platform | darwin (CI matrix: 3.11 / 3.12 / 3.13) |
| reproduce | `dorian bench large-mutation` · `dorian bench binding-lifecycle` · `dorian bench realworld-usecases` |
Expand Down
2 changes: 1 addition & 1 deletion docs/CLAUDE_CODE_DORIAN_WORKFLOW.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ dorian status # trust state of every warranted arti
## GitHub Action

```yaml
- uses: ajaysurya1221/dorian/action@v1.3.0
- uses: ajaysurya1221/dorian/action@v1.4.0
with:
fail_on: revoked # block the PR when a sealed claim breaks
# for semi-trusted contributors, also: checker_source: base + deny_exec: true
Expand Down
4 changes: 2 additions & 2 deletions docs/SECURITY_AND_SAFE_RUNNERS.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ jobs:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # the action needs full history to revalidate --since base
- uses: ajaysurya1221/dorian/action@v1.3.0
- uses: ajaysurya1221/dorian/action@v1.4.0
with:
checker_trust: base # resolve checker SPECs from the trusted base ref
deny_exec: "true" # C4 pytest / C5 shell ERROR instead of executing
Expand Down Expand Up @@ -113,7 +113,7 @@ jobs:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: ajaysurya1221/dorian/action@v1.3.0
- uses: ajaysurya1221/dorian/action@v1.4.0
# checker_trust defaults to head, deny_exec defaults to false:
# executing checkers run, because contributors are trusted.
with:
Expand Down
121 changes: 121 additions & 0 deletions docs/releases/v1.4.0.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
# dorian v1.4.0 — Governance Foundation

A feature release: the deterministic spine of a **governance layer** for long-running AI coding loops —
a human-authored goal record, a path-derived coverage check, a host-mappable preflight **gate**, and
one concrete **Claude Code governance adapter** — plus the **Dorian Loop Guard** steering layer.
**No breaking changes** — purely additive. The warrant format, checker grammar, exit codes, fold
policy, and security posture are unchanged; the core stays zero-dependency; and **no model is added to
the verification path** (a build-time import firewall now proves it).

## Summary

dorian is growing from *"deterministically verify AI claims about code"* into a **deterministic
governance kernel** for agentic engineering. The guiding thesis: *dorian does not train agents to be
good — it makes bad actions non-continuable, non-mergeable, or human-reviewable.* "Crime" means an
observable contract violation; "punishment" means a mechanical loss of affordance (a blocked tool
call, a forced repair, a failed gate, a revoked warrant, a human escalation). dorian is the court;
host adapters and CI/branch-protection are the enforcement.

## What shipped

### Dorian Loop Guard (`dorian loop`)
- **`dorian loop preflight`** — re-checks the claim warrants a change touched and emits a
**CONTINUE / REPAIR / ESCALATE** decision packet (`--format json|md|text|prompt`). A pure function
of the `revalidate` result, `--policy` (`cautious|assist|unattended`), the repair-attempt cap, and
the `--scope`/`--deny-path` lane. Exits **0** by default; `--fail-on repair|escalate` opts into a
hard exit-4 gate.
- **`dorian loop prompt`** renders the packet as a next-iteration instruction.
- **`dorian loop install`** scaffolds the `/dorian-loop-guard` Claude Code skill (+ example
`LOOP.md`/`STATE.md`, `--with-action`).

### Governance foundation
- **`dorian goal add` / `show` / `check`** — a **human-authored goal record** (`.dorian/goals/<id>.goal.json`,
`schema_version: 1`) plus a deterministic, **path-derived coverage diff** (`goals.coverage_diff` →
`{covered, uncovered}` over git-changed, in-scope paths vs. warranted paths). `goal check
--fail-on-uncovered` exits **4**. The goal's `statement` is **human context only** — never fed to a
model, never used to decide a verdict or "completion".
- **`dorian gate`** — a host-mappable preflight body: reads a tool-call JSON on stdin, runs the same
pure loop preflight, and prints the `continue`/`repair`/`escalate` packet. Emits only contract codes
(`0`/`4`, plus `2` for malformed stdin); **never** uses exit 2 as a veto, reads a clock, or reads a
nonce.
- **Claude Code governance adapter** via **`dorian governance install`** — one concrete adapter: a
`SubagentStop` hook that shells `dorian gate` and writes an ephemeral
`.dorian/local/last-decision.json`, and a **fail-closed** `PreToolUse` veto (the exit-2 tool-block
lives in the host hook; fails closed under `unattended`/`godmode`, fails open when attended;
`FRESH_SECONDS = 900`), plus a settings example and bundle docs.
- **Safe sidecar writer** (`src/dorian/sidecars.py`) — atomic (temp + `os.replace`), deterministic
(sorted-key JSON), path-safe (`ensure_within`).
- **Deterministic core import firewall** (`tests/test_firewall_import_closure.py`) — an AST
import-closure guard proving no model/network import sits on the verdict path (covers the new
`sidecars`, `goals`, `governance` modules).
- **Release hardening** — functional PreToolUse-veto subprocess tests (identity mismatch, godmode,
freshness boundary, empty-path escalate, malformed-stdin open/closed) and an installed-wheel
`dorian governance install` scaffold test.
- **Docs** — [`GOVERNANCE_DATA_MODEL.md`](../GOVERNANCE_DATA_MODEL.md),
[`DORIAN_PANE.md`](../DORIAN_PANE.md) (pane vision — deferred), [`DORIAN_LOOP_GUARD.md`](../DORIAN_LOOP_GUARD.md),
and gate/C4-flakiness notes in [`SECURITY_BOUNDARY.md`](../SECURITY_BOUNDARY.md) /
[`VALIDATION_HONESTY.md`](../VALIDATION_HONESTY.md).

## Deterministic boundary (unchanged invariants)

No model on the verdict path (firewall-enforced); the core stays **zero-dependency**; the exit-code
contract, warrant schema, checker grammar, and fold policy are all **unchanged**. Every governance
detector is a pure function of observable artifacts (git diffs, paths, scope/deny globs, warrants,
`revalidate`/fold results, coverage diff, decision packets).

## Adapter boundary

`dorian gate` / core emit only `0`/`4`. The **exit-2 tool-block veto lives only in the Claude Code
`PreToolUse` host hook**; wall-clock and nonce are stamped only by the `SubagentStop` hook into
`.dorian/local/last-decision.json`. The adapter is **one concrete Claude Code adapter** — no generic
provider abstraction is introduced.

## Validation

- Full `uv run pytest` (incl. slow) green on CI across Python 3.11 / 3.12 / 3.13; `ruff check` and
`ruff format --check` clean.
- New tests cover the sidecar writer, the goal record + coverage diff, `dorian gate` (0/4, 2 only on
malformed), the firewall, and — as a real `pip install` path — a built wheel installed into a clean
venv that scaffolds the governance adapter and carries the exit-2 fail-closed veto.
- Landed via three merged PRs (#25 Loop Guard, #26 Governance Foundation, #27 hardening), each rebased
clean and CI-green before merge.

## Deferred / cut (not in v1.4)

- Claim **provenance** sidecars → **v1.5** (only if a deterministic consumer appears).
- **Effort presets** → **cut from core** (the binding-floor/breadth mapping lives in adapter docs).
- The **pane / TUI** → **deferred** (see Claude Design boundary below).
- A **generic provider abstraction** → **deferred** until a second concrete adapter exists.
- An authoritative ledger / hash-chain / off-repo signing → deferred (only meaningful with an external
trust root).

## Upgrade notes

Purely additive; **no migration required.** New subcommands are inert unless invoked; new sidecars are
written only by the new commands. Existing warrants, `verify`/`revalidate`, and the claim-warrants
integration are untouched. To adopt governance in a repo: `dorian governance install`, then merge the
`.claude/settings.dorian-governance.example.json` hooks into `.claude/settings.json`. Removing the
feature = delete `.claude/hooks/dorian_*.py` and the settings entries; the core is unaffected.

## Security & honesty notes

- **Not a sandbox.** C4 (`pytest:`) / C5 (`shell:`) checkers execute code; governance is policy and
steering, not isolation.
- **Host hooks enforce, but a repo-controlling actor can disable them.** The `PreToolUse` veto is a
Claude Code host hook — an agent that controls the repository can delete it, edit
`.claude/settings.json`, or rewrite its own goals/warrants. dorian's real strength is transparency
and auditability, not containment.
- **CI / GitHub branch protection is the real merge boundary.** Governance repos should branch-protect
`.dorian/` and `.claude/` and require the `dorian gate` status check. (Branch protection is not a
hard gate against administrators — human/admin bypass is a live surface to log.)
- **`.dorian/local/` is ephemeral and gitignored** — a transient steering signal, never an
authoritative ledger, and never read back into a verdict.
- **Coverage is a structural floor**, not a judgment of correctness; goal completion is **not**
model-evaluated. Weak binding or weak checker strength means low confidence, **not** a false claim.

## Claude Design boundary

The **pane / TUI / brand** are **deferred**. `docs/DORIAN_PANE.md` is a future-facing vision and data
contract, **not** an implementation — no UI/TUI code ships in this release. **Claude Design owns the
final TUI/pane/brand design** (layout, typography, color, interaction, README polish, banner, logo);
dorian/Opus owns the deterministic data contracts and governance invariants.
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "hatchling.build"

[project]
name = "dorian-vwp"
version = "1.3.0"
version = "1.4.0"
description = "Hold AI agents to what they said they did: deterministic, token-free verification of claims about a change."
readme = "README.md"
requires-python = ">=3.11"
Expand Down
2 changes: 1 addition & 1 deletion src/dorian/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@
PyPI distribution: `dorian-vwp`; import package: `dorian`; CLI: `dorian`.
"""

__version__ = "1.3.0"
__version__ = "1.4.0"
3 changes: 2 additions & 1 deletion src/dorian/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -499,7 +499,8 @@ def _add_goal_parser(sub: argparse._SubParsersAction) -> None:
add.add_argument(
"--min-strength",
default=None,
help="minimum checker strength for load-bearing claims (structural coverage contract)",
help="minimum checker strength for load-bearing claims — recorded in the goal "
"record only; not yet enforced (planned v1.5)",
)

show = gp_sub.add_parser("show", help="print a goal record as JSON")
Expand Down
25 changes: 25 additions & 0 deletions tests/test_version_sync.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,31 @@ def test_no_dorian_action_at_main_in_public_snippets() -> None:
)


# Live copy-paste pins must also track the released version: an immutable but STALE pin
# (e.g. @v1.3.0 shipped in the v1.4.0 README) hands users an action missing the release's
# commands. Asserted against the package version, not the tag (the tag exists only after
# release — the brief merged-but-untagged window is accepted). docs/releases/ and CHANGELOG
# are historical provenance and deliberately not scanned.
_ACTION_PIN_SURFACES = (
"README.md",
"action/README.md",
"docs/CLAUDE_CODE_DORIAN_WORKFLOW.md",
"docs/SECURITY_AND_SAFE_RUNNERS.md",
)


def test_dorian_action_pins_match_package_version() -> None:
version = _pyproject_version()
offenders: list[str] = []
for rel in _ACTION_PIN_SURFACES:
text = (REPO_ROOT / rel).read_text(encoding="utf-8")
for lineno, line in enumerate(text.splitlines(), start=1):
for pin in re.findall(r"dorian/action@v(\d+\.\d+\.\d+)", line):
if pin != version:
offenders.append(f"{rel}:{lineno}: @v{pin} != package v{version}")
assert not offenders, "stale dorian/action version pins:\n" + "\n".join(offenders)


# The attestation-interop example must not pin a fixed dorianVersion (Codex FINDING-07): a
# hardcoded "1.0.x" silently drifts every release. It is kept version-neutral instead.
def test_attestation_interop_dorian_version_is_version_neutral() -> None:
Expand Down
2 changes: 1 addition & 1 deletion uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading