From f65b11bfd4ae2247f19785b0c2a9d1577e600aa1 Mon Sep 17 00:00:00 2001 From: Ajay Surya Date: Fri, 19 Jun 2026 00:32:31 +0530 Subject: [PATCH 1/2] fix(init): make the `dorian init` starter claim load-bearing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In v1.1.0 the scaffolded starter claim was non-load-bearing, 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. Both starter branches (config-value and the path: fallback) are now load-bearing, so a break folds to REVOKED (exit 4) and the default Action blocks the PR — the golden path now demonstrates the gate it advertises. Sealing is unaffected (the starter still seals green; load_bearing only changes the fold on a later break). Adds a unit assertion for both branches and an end-to-end break -> REVOKED -> exit 4 test. Co-Authored-By: Claude Opus 4.8 (1M context) --- src/dorian/init.py | 4 ++-- tests/test_init.py | 45 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 2 deletions(-) diff --git a/src/dorian/init.py b/src/dorian/init.py index 467f932..ecffe46 100644 --- a/src/dorian/init.py +++ b/src/dorian/init.py @@ -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}`." @@ -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" diff --git a/tests/test_init.py b/tests/test_init.py index 722ea5d..744b938 100644 --- a/tests/test_init.py +++ b/tests/test_init.py @@ -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: From af7c95ff35f767127ed0a7508fd2fe3dc82d3d8d Mon Sep 17 00:00:00 2001 From: Ajay Surya Date: Fri, 19 Jun 2026 00:32:31 +0530 Subject: [PATCH 2/2] release: dorian v1.1.1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bump 1.1.0 -> 1.1.1 (a scaffold default only; no breaking changes). README's Getting-started section now states the load-bearing starter blocks on a break (REVOKED, exit 4); action refs move to v1.1.1. Corrects a residual overclaim in BENCHMARK_CURRENT.md ("removal of accidentally-packaged duplicate source files" -> the `… 2.py` dups were untracked local artifacts, never tracked or shipped) and re-stamps it at 1.1.1 (figures unchanged; no checker/binding/fold code touched). New CHANGELOG entry and docs/releases/v1.1.1.md. Co-Authored-By: Claude Opus 4.8 (1M context) --- CHANGELOG.md | 14 ++++++++ README.md | 10 +++--- action/README.md | 6 ++-- docs/BENCHMARK_CURRENT.md | 4 +-- docs/releases/v1.1.1.md | 73 +++++++++++++++++++++++++++++++++++++++ pyproject.toml | 2 +- src/dorian/__init__.py | 2 +- 7 files changed, 100 insertions(+), 11 deletions(-) create mode 100644 docs/releases/v1.1.1.md diff --git a/CHANGELOG.md b/CHANGELOG.md index 5edf6af..ecaed77 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/README.md b/README.md index c70b998..22df131 100644 --- a/README.md +++ b/README.md @@ -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 `), 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 `), 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:** @@ -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 @@ -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. diff --git a/action/README.md b/action/README.md index 79694b3..34b2373 100644 --- a/action/README.md +++ b/action/README.md @@ -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); @@ -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 ``` @@ -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) diff --git a/docs/BENCHMARK_CURRENT.md b/docs/BENCHMARK_CURRENT.md index 8315b75..0a4c0e1 100644 --- a/docs/BENCHMARK_CURRENT.md +++ b/docs/BENCHMARK_CURRENT.md @@ -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` | diff --git a/docs/releases/v1.1.1.md b/docs/releases/v1.1.1.md new file mode 100644 index 0000000..e2d4a3a --- /dev/null +++ b/docs/releases/v1.1.1.md @@ -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 # 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. diff --git a/pyproject.toml b/pyproject.toml index 9738f51..a5582b4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -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" diff --git a/src/dorian/__init__.py b/src/dorian/__init__.py index 7e9003d..c4b0097 100644 --- a/src/dorian/__init__.py +++ b/src/dorian/__init__.py @@ -3,4 +3,4 @@ PyPI distribution: `dorian-vwp`; import package: `dorian`; CLI: `dorian`. """ -__version__ = "1.1.0" +__version__ = "1.1.1"