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
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,20 @@ All notable changes to dorian (`dorian-vwp`) are recorded here. Full per-release
[`docs/releases/`](docs/releases/). The warrant format, checker grammar, exit codes, and trust
semantics have been stable since 1.0.0.

## [1.1.1] — 2026-06-19

Golden-path polish. **No breaking changes** (a scaffold default only; verification, warrant format,
checker grammar, exit codes, and trust semantics are unchanged).

### Changed
- **`dorian init`'s starter claim is now load-bearing.** Previously it sealed as a non-load-bearing
claim, so a later break folded the warrant to DEGRADED (exit 3) — which the scaffolded
`fail_on: revoked` Action does not block on, letting a first broken promise silently ship. The
starter is now load-bearing, so breaking it folds to **REVOKED (exit 4)** and the default Action
**blocks the PR** — the golden path now demonstrates the gate it advertises. This also matches the
scaffolded change note, which already described these as "load-bearing facts." Sealing is
unaffected (the starter still seals green on a fresh `dorian verify`).

## [1.1.0] — 2026-06-18

Productization release — easier first run, clearer PR output, cleaner package. **No breaking
Expand Down
10 changes: 6 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -356,8 +356,10 @@ dorian init # writes claims.json
dorian verify dorian-change-note.md --claims claims.json # seals the warrant — exit 0
```

Edit `claims.json` for the real facts your change depends on (add code claims with
`dorian suggest-claims <module.py>`), then commit `dorian-change-note.md.warrant`. For CI, add the composite
The starter claim is **load-bearing**: if a later change breaks it, `dorian revalidate` folds the
warrant to **REVOKED** (exit 4) and a default `fail_on: revoked` Action blocks the PR — so the broken
promise can't silently ship. Edit `claims.json` for the real facts your change depends on (add code
claims with `dorian suggest-claims <module.py>`), then commit `dorian-change-note.md.warrant`. For CI, add the composite
[GitHub Action](action/README.md) — it revalidates the claims a pull request touches and posts a
sticky PR comment. **Read its
[security notes](action/README.md#security-checker-execution-and-untrusted-pull-requests) first:**
Expand All @@ -381,7 +383,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.1.0
- uses: ajaysurya1221/dorian/action@v1.1.1
with:
fail_on: revoked
# install defaults to the published PyPI package (dorian-vwp); pin a
Expand Down Expand Up @@ -524,7 +526,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.1.0`**); `pip install dorian-vwp` installs the released package.
(latest: **`v1.1.1`**); `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.1.0
- uses: ajaysurya1221/dorian/action@v1.1.1
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.1.0
- uses: ajaysurya1221/dorian/action@v1.1.1
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.1.0
- uses: ajaysurya1221/dorian/action@v1.1.1
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.1.0` |
| dorian version | `1.1.1` |
| 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 adds the `dorian init` scaffolder, the PR-comment renderer enhancements (a status line, trust-change counts, sealed-at, and remediation), and removal of accidentally-packaged duplicate source files — a new command plus output formatting and packaging hygiene only, touching no checker/binding/fold code, so the figures stand unchanged at 1.1.0 (the suites were last executed at 1.0.2, not re-run at 1.1.0) |
| 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) |
| 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
73 changes: 73 additions & 0 deletions docs/releases/v1.1.1.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# dorian v1.1.1 — the golden path now blocks broken promises

A small, focused follow-up to [v1.1.0](v1.1.0.md). **No breaking changes** — this changes a single
scaffold default; the warrant format, checker grammar, exit codes, and trust semantics are unchanged.

## What changed

- **`dorian init`'s starter claim is now load-bearing.** In v1.1.0 the scaffolded starter claim sealed
as *non*-load-bearing, so when a later change broke it the warrant folded to **DEGRADED (exit 3)** —
and the scaffolded GitHub Action (`fail_on: revoked`) does **not** block on DEGRADED. So a first-time
user following the golden path would break the promise and watch it *silently ship*. The starter is
now load-bearing: breaking it folds to **REVOKED (exit 4)** and the default Action **blocks the PR**.

## Why it matters

dorian's whole pitch is "broken promises do not silently ship." The golden path (`dorian init`) is the
first thing a new user runs, and it should *demonstrate* the gate — not degrade quietly under the
default Action. This release makes the out-of-the-box experience match the promise. It also resolves an
internal inconsistency: the scaffolded change note already called these "load-bearing facts."

This was caught by the v1.1.0 public-install smoke test (install from PyPI → `init` → `verify` → break
→ `revalidate`), which observed `WARRANTED → DEGRADED` (exit 3) where the product promise calls for a
block.

## Install

```bash
pip install -U dorian-vwp # 1.1.1
dorian --version
```

## Quickstart (the golden path, now blocking)

```bash
cd your-repo
dorian init # claims.json + change note + workflow
dorian verify dorian-change-note.md --claims claims.json # seals green — exit 0
# ...later, a change breaks the sealed fact...
dorian revalidate --since <base> # REVOKED, exit 4 — the Action blocks the PR
```

## GitHub Action

Unchanged. The scaffolded workflow pins `ajaysurya1221/dorian/action@v1.1.1` and uses the default
`fail_on: revoked`, which now blocks when the starter claim is broken.

## Security notes

No change to the security boundary. `dorian init` still writes files only — it never runs a checker,
never executes code, never writes outside the repo root, and never overwrites without `--force`. The
starter claim remains a read-only C3 checker (`config-value:`/`path:`). `--deny-exec`/`--deny-shell`
and `checker_trust: base` are fail-closed policies, **not** a sandbox.

## Known limitations

- dorian verifies explicit, checkable claims — not arbitrary correctness; it is not a sandbox.
- One documented, reproduced real cross-PR catch (`encode/httpx`) — not broad real-world validation.
- A load-bearing starter means that if you keep the example claim *and* legitimately change the fact it
pins (e.g. rename your package), the next PR is REVOKED until you update the claim or supersede the
warrant — which is the intended "you changed a sealed fact, acknowledge it" behavior. Replace the
starter with your real load-bearing facts.

## Tests / gates

Full suite green at 1.1.1 (the 1.1.0 suite plus 2 new `dorian init` tests pinning the load-bearing
starter and the end-to-end break → REVOKED → exit 4), ruff clean, wheel/sdist build + `twine check`
pass, and a public-install smoke test from PyPI.

## Upgrade notes

`pip install -U dorian-vwp`. Already-sealed warrants are unaffected. If you want a previously
scaffolded starter claim to block, set its `load_bearing` to `true` in `claims.json` and re-seal with
`dorian verify`, or re-run `dorian init --force` to regenerate the scaffold.
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.1.0"
version = "1.1.1"
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.1.0"
__version__ = "1.1.1"
4 changes: 2 additions & 2 deletions src/dorian/init.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ def _starter(repo: Path) -> tuple[dict, str, str]:
"id": "package-name",
"text": f'The package name declared in pyproject.toml is "{name}".',
"kind": "fact",
"load_bearing": False,
"load_bearing": True,
"checkers": [{"type": "C3", "program": program}],
}
fact = f"The package name declared in `pyproject.toml` (`project.name`) is `{name}`."
Expand All @@ -123,7 +123,7 @@ def _starter(repo: Path) -> tuple[dict, str, str]:
"id": f"{_slug(Path(rel).stem)}-present",
"text": f"The file {rel} is present.",
"kind": "fact",
"load_bearing": False,
"load_bearing": True,
"checkers": [{"type": "C3", "program": f"path:{rel}"}],
}
return claim, f"The file `{rel}` is present.", f"path: {rel} exists"
Expand Down
45 changes: 45 additions & 0 deletions tests/test_init.py
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,51 @@ def test_init_falls_back_to_path_claim_without_pyproject(tmp_path: Path) -> None
claim = claims_io.load_claims(repo / init.CLAIMS_FILE)[0]
programs = [c.program for c in claim.checkers]
assert any(p.startswith("path:") for p in programs)
# the path-fallback starter must also be load-bearing, so breaking it REVOKES
# (exit 4, blocks) rather than only DEGRADES — same contract as the config-value path
assert claim.load_bearing is True


def test_init_starter_claim_is_load_bearing(tmp_path: Path) -> None:
"""The scaffold exists to demonstrate the gate: its starter claim is
load-bearing, so a later break REVOKES (which the default `fail_on: revoked`
Action blocks on) rather than only DEGRADES and silently ships."""
repo = tmp_path / "repo"
_seed_repo(repo)
plan = init.build_plan(repo)
claim = json.loads(_claims_content(plan))["claims"][0]
assert claim["load_bearing"] is True


def test_init_scaffold_blocks_a_broken_promise_end_to_end(tmp_path: Path) -> None:
"""The golden path's payoff: seal the starter claim, break it, and revalidate
folds the warrant to REVOKED with exit 4 — the verdict a default-configured
Dorian Action blocks the PR on. A non-load-bearing scaffold would only DEGRADE
(exit 3) and silently ship under the scaffolded `fail_on: revoked` gate."""
repo = tmp_path / "repo"
_seed_repo(repo)
_git(repo, "init", "-q")
_git(repo, "add", "-A")
_git(repo, "commit", "-q", "-m", "seed repo")

assert _dorian("init", repo=repo).returncode == 0
assert (
_dorian("verify", init.NOTE_FILE, "--claims", init.CLAIMS_FILE, repo=repo).returncode == 0
)
_git(repo, "add", "-A")
_git(repo, "commit", "-q", "-m", "dorian init + sealed warrant")

# break the starter config-value claim by renaming the package
(repo / "pyproject.toml").write_text(
'[project]\nname = "renamed-app"\nversion = "0.1.0"\n', encoding="utf-8"
)
_git(repo, "add", "-A")
_git(repo, "commit", "-q", "-m", "rename package — breaks the sealed claim")

r = _dorian("revalidate", "--since", "HEAD~1", repo=repo)
assert r.returncode == 4, f"{r.returncode}\n{r.stdout}\n{r.stderr}" # REVOKED, not DEGRADED(3)
assert "REVOKED" in r.stdout
assert "package-name" in r.stdout # the broken claim is named


def _claims_content(plan: init.InitPlan) -> str:
Expand Down
Loading