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
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,7 @@ research_packets/
codex_validation/
.bench/
.release/

# editor / cloud-sync duplicate artifacts (e.g. "suggestclaims 2.py"); never tracked or shipped
* 2.py
* 2.md
42 changes: 42 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Changelog

All notable changes to dorian (`dorian-vwp`) are recorded here. Full per-release notes live in
[`docs/releases/`](docs/releases/). The warrant format, checker grammar, exit codes, and trust
semantics have been stable since 1.0.0.

## [1.1.0] — 2026-06-18

Productization release — easier first run, clearer PR output, cleaner package. **No breaking
changes** (command surface and output formatting only; verification is unchanged).

### Added
- **`dorian init`** — first-run scaffolding: a born-verifiable starter `claims.json`, the change
note it backs, and a `.github/workflows/dorian.yml` Action workflow. Writes files only (never runs
a checker or executes code), confined to the repo root, idempotent, with `--force`, `--dry-run`,
and the global `--json`.
- **Customer-readable PR comment** (`revalidate --format md`): an explicit `Status:` Blocked /
Passed / Errored verdict, an aggregate trust-change counts table, a `sealed in <artifact>.warrant`
line per affected artifact, and a verdict-keyed `What to do:` remediation line. The comment stays
deterministic and keeps its content-carryover bound.

### Packaging
- Added a `.gitignore` rule and a Hatch build `exclude` so stray editor/file-sync `… 2.py`
duplicate files can never be tracked or packaged into a wheel, even from a dirty working tree.
(These were untracked local artifacts — never in a CI build or on PyPI.)

See [`docs/releases/v1.1.0.md`](docs/releases/v1.1.0.md).

## [1.0.2] — 2026-06-17

Announcement-readiness hotfix: PyPI coherence, immutable Action ref, an SCA-scope fix, and two
edge-case bug fixes (`export` of a `*.warrant`-named artifact; `suggest-claims` PEP 263 reads). See
[`docs/releases/v1.0.2.md`](docs/releases/v1.0.2.md).

## [1.0.1] — 2026-06-17

Added `suggest-claims` (C3 scaffolds) and `export --in-toto`; C4/C5 edge-case fixes. See
[`docs/releases/v1.0.1.md`](docs/releases/v1.0.1.md).

## [1.0.0] — 2026-06-16

First PyPI release of the Validity Warrant Protocol reference implementation.
20 changes: 17 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -347,7 +347,17 @@ pip install 'dorian-vwp[data] @ git+https://github.com/ajaysurya1221/dorian.git'
pip install 'dorian-vwp[extract] @ git+https://github.com/ajaysurya1221/dorian.git' # + anthropic for LLM claim drafting (frozen/experimental)
```

Then run `dorian verify <artifact> --claims claims.json` on one change. For CI, add the composite
The fastest start is `dorian init`, which scaffolds a born-verifiable starter `claims.json`, the
change note it backs, and a GitHub Action workflow — so the very first `dorian verify` seals green:

```bash
cd your-repo
dorian init # writes claims.json + change note + .github/workflows/dorian.yml
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
[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 @@ -371,7 +381,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.0.2
- uses: ajaysurya1221/dorian/action@v1.1.0
with:
fail_on: revoked
# install defaults to the published PyPI package (dorian-vwp); pin a
Expand Down Expand Up @@ -409,6 +419,10 @@ The core loop is `verify` (auto-capture the read-set, run every checker, seal th
`revalidate` (re-check only what changed). `capture` + `seal` are the lower-level path for C1 span
claims.

- `dorian init [--force] [--dry-run]` — first-run scaffolding: writes a born-verifiable starter
`claims.json`, the change note it backs, and a `.github/workflows/dorian.yml` Action workflow.
Writes files only (never runs a checker or executes code), stays inside the repo, and skips
existing files unless `--force`. The global `--json` prints a machine-readable plan.
- `dorian verify <artifact> --claims claims.json` — the one-shot agent-claims entry point:
auto-derive the read-set from each C3/C4/C5 checker, then seal (born-verifiable). C1 span claims
use `dorian capture` + `dorian seal` instead.
Expand Down Expand Up @@ -510,7 +524,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.0.2`**); `pip install dorian-vwp` installs the released package.
(latest: **`v1.1.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.0.2
- uses: ajaysurya1221/dorian/action@v1.1.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.0.2
- uses: ajaysurya1221/dorian/action@v1.1.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.0.2
- uses: ajaysurya1221/dorian/action@v1.1.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.0.2` |
| dorian version | `1.1.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 |
| 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) |
| 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
72 changes: 72 additions & 0 deletions docs/releases/v1.1.0.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# dorian 1.1.0

A productization release that makes the first run easy. **No breaking changes**: the warrant
format, checker grammar, exit codes, and trust semantics are unchanged. 1.1.0 adds the missing
golden-path onboarding command, makes the PR-comment output customer-readable, and cleans up a
packaging-hygiene defect — it changes a command surface and output formatting, not verification.

## What's new

- **`dorian init` (new command)** — first-run scaffolding so a new user reaches a sealed warrant in
minutes instead of hand-writing JSON. It writes three files:
- a born-verifiable starter `claims.json` (a `config-value:` claim about the pyproject package
name when available — the same checker family that caught `encode/httpx` #3592 — otherwise a
`path:` existence claim about a file that is present, so the first `dorian verify` seals **green**, not red);
- `dorian-change-note.md`, the change note those claims back;
- `.github/workflows/dorian.yml`, the GitHub Action workflow, pinned to this package's version.

It writes files **only** — it never runs a checker, never executes code, never writes outside the
repo root, and never overwrites an existing file without `--force` (re-running is idempotent).
Supports `--dry-run` (print the plan, write nothing) and the global `--json` (machine-readable
summary). The scaffolded starter checker is always a read-only C3 family, never an executable
C4/C5 — safe by default.

- **Customer-readable PR comment** — `dorian revalidate --format md` (the body the GitHub Action
posts) now leads with an explicit **`Status:` Blocked / Passed / Errored** verdict, an aggregate
**trust-change counts** table (how many touched warrants this change moved to REVOKED / DEGRADED /
TRUSTED / UNKNOWN), a **`sealed in <artifact>.warrant`** line under each affected artifact, and a
verdict-keyed **`What to do:`** remediation line. The existing per-claim verdict table, fold
transitions, recall section, and stats footer are unchanged; the comment stays deterministic (no
timestamps, no absolute paths) and keeps the 160-char content-carryover bound on every detail cell.

## Packaging hygiene

- **Guards against editor/file-sync duplicate artifacts.** A `.gitignore` rule and a Hatch build
`exclude` now keep stray `… 2.py` sync-duplicate files (the kind macOS / cloud sync leaves behind,
e.g. `suggestclaims 2.py`) from ever being tracked or packaged into a wheel — even from a dirty
working tree. To be precise: these files were **never tracked in git**, so they were never in a
CI-built wheel or on PyPI; the guards additionally make a local `uv build` from a dirty tree
provably clean (33 modules, no space-named files).

## Tests & gates

Full suite green at 1.1.0 (883 tracked tests: the 1.0.2 suite plus 8 new `dorian init` tests, and
new assertions pinning the enhanced PR-comment output), ruff clean, wheel/sdist build +
`twine check` pass. The new `dorian init` golden path is covered end to end (`init` → `verify`
exits 0 and writes a warrant — a tool whose pitch is "don't ship false claims" must not ship a
false scaffold).

The reproducible benchmark suites are **not** re-run here: 1.1.0 adds a command, output formatting,
and a packaging cleanup, none of which touch the checker, binding, or fold code the suites measure,
so the recorded figures stand unchanged (last executed at 1.0.2; see
[`docs/BENCHMARK_CURRENT.md`](../BENCHMARK_CURRENT.md)).

## Honest scope (unchanged)

dorian has **one documented, reproduced real cross-PR catch** on frozen public SHAs (`encode/httpx`
`requires-python` floor; see [`docs/REAL_CATCH_LOG.md`](../REAL_CATCH_LOG.md)) — not broad
real-world validation. The benchmark suites are reproducibility evidence on frozen fixtures only.
`--deny-exec`/`--deny-shell` are fail-closed policies, **not** sandboxes; `checker_trust: base` is a
checker-source trust root, not a sandbox. `dorian init` and `suggest-claims` scaffold starter claims
for review — existence/value checks, not behavior (a gutted body keeps a `symbol:` claim green). A
warrant id is content-addressed and tamper-evident, but its body includes the seal timestamp, so a
fresh seal yields a different id — what reproduces is the outcome, not the id.

## Install

```bash
pip install dorian-vwp # 1.1.0 on PyPI

dorian init # scaffold a starter setup
dorian verify dorian-change-note.md --claims claims.json # seal the warrant — exit 0
```
5 changes: 4 additions & 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.0.2"
version = "1.1.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 All @@ -30,6 +30,9 @@ dev = ["pytest>=8", "pytest-cov>=5", "ruff>=0.8", "pyyaml>=6"]

[tool.hatch.build.targets.wheel]
packages = ["src/dorian"]
# defense in depth: never package editor/cloud-sync duplicate artifacts ("foo 2.py"),
# even from a dirty working tree (they are also git-ignored, so never tracked)
exclude = ["**/* 2.py"]

[tool.ruff]
line-length = 100
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.0.2"
__version__ = "1.1.0"
19 changes: 17 additions & 2 deletions src/dorian/cli.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
"""dorian CLI: capture | seal | verify | status | blast | bindings | revalidate |
report | suggest-data-checks | suggest-claims | sync | export | bench.
"""dorian CLI: init | capture | seal | verify | status | blast | bindings |
revalidate | report | suggest-data-checks | suggest-claims | sync | export | bench.

Exit codes: 0 ok/TRUSTED · 2 usage/infra · 3 DEGRADED · 4 REVOKED/integrity ·
5 ERRORED-only · 6 scope violation (ring 1).
Expand Down Expand Up @@ -51,6 +51,21 @@ def build_parser() -> argparse.ArgumentParser:
p.add_argument("--json", action="store_true", help="machine-readable output")
sub = p.add_subparsers(dest="command", required=True)

ini = sub.add_parser(
"init",
help="scaffold a starter claims.json, change note, and GitHub Action workflow",
description="Scaffold a born-verifiable starter setup so a first run reaches a"
" sealed warrant in minutes: a starter claims.json, the change note it backs, and"
" a GitHub Action workflow that re-checks the claims on every pull request. Writes"
" files only — never runs a checker or executes code, never writes outside the repo,"
" and never overwrites an existing file without --force. Use the global --json for a"
" machine-readable summary.",
)
ini.add_argument(
"--force", action="store_true", help="overwrite existing files (default: skip them)"
)
ini.add_argument("--dry-run", action="store_true", help="print the plan; write nothing")

cap = sub.add_parser("capture", help="build a read-set from a run")
cap.add_argument("--transcript", help="Claude Code session .jsonl")
cap.add_argument("--manual", action="append", default=[], help="path[:Lx-y] (repeatable)")
Expand Down
55 changes: 55 additions & 0 deletions src/dorian/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
claims_io,
datachecks,
gitio,
init,
intoto,
store,
strength,
Expand Down Expand Up @@ -881,6 +882,60 @@ def cmd_suggest_claims(args: argparse.Namespace) -> int:
return EXIT_OK


def cmd_init(args: argparse.Namespace) -> int:
"""Scaffold a born-verifiable starter setup (claims.json, change note,
GitHub Action workflow) so a first run reaches a sealed warrant in minutes.
Writes files only — never runs a checker, never executes code, never writes
outside the repo, never overwrites without --force. Re-running is idempotent:
existing files are left untouched. Path/permission problems are usage errors."""
repo = _repo(args)
if _missing_repo(repo, "init"):
return EXIT_USAGE
try:
plan = init.build_plan(repo)
result = init.apply(plan, force=args.force, dry_run=args.dry_run)
except (ValueError, OSError) as exc:
print(f"dorian init: {exc}", file=sys.stderr)
return EXIT_USAGE
if args.json:
print(
json.dumps(
{
"repo": str(repo),
"dry_run": args.dry_run,
"created": list(result.created),
"overwritten": list(result.overwritten),
"skipped": list(result.skipped),
"warnings": list(result.warnings),
"next_steps": list(init.NEXT_STEPS),
},
indent=2,
)
)
else:
_print_init_summary(plan, result, dry_run=args.dry_run)
return EXIT_OK


def _print_init_summary(plan, result, *, dry_run: bool) -> None:
blurbs = {f.path: f.blurb for f in plan.files}
header = "dorian init --dry-run (no files written)" if dry_run else "dorian initialized."
print(header)
written = list(result.created) + list(result.overwritten)
if written:
print("\nWould create:" if dry_run else "\nWrote:")
width = max(len(p) for p in written)
for p in written:
print(f" {p.ljust(width)} {blurbs.get(p, '')}")
if result.skipped:
print("\nSkipped (already present — use --force to overwrite):")
for p in result.skipped:
print(f" {p}")
print("\nNext:")
for i, step in enumerate(init.NEXT_STEPS, 1):
print(f" {i}. {step}")


def cmd_sync(args: argparse.Namespace) -> int:
repo = _repo(args)
if _missing_repo(repo, "sync"):
Expand Down
Loading
Loading