Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
bddde49
docs: draft Proof Statement v1 and add a DCO commit-msg hook
docushell-dev Aug 9, 2026
c27a625
docs: rule the three Proof Statement v1 decisions
docushell-dev Aug 9, 2026
f486e27
docs: lock the predicateType base URI and add the build plan
docushell-dev Aug 9, 2026
1c0af17
docs: cut corroboration and multi-source from Proof Statement v1
docushell-dev Aug 9, 2026
f81dccc
ci: restore the RAG framework examples gate, drop its wiring assertion
docushell-dev Aug 9, 2026
c06fa1a
docs: rule that representations stay bare
docushell-dev Aug 9, 2026
6287240
docs: make v0.6.0 pickup-ready and add WP-0
docushell-dev Aug 9, 2026
95f16ee
docs: drop the public-beta posture from the README
docushell-dev Aug 9, 2026
67dc849
test: lock the geometry-free text anchoring path (WP-0 task 0.1)
docushell-dev Aug 9, 2026
fb1d46d
feat(grounding): make bbox optional in the trait, schema unchanged (W…
docushell-dev Aug 9, 2026
db7d1b9
docs: record the five multi-format gates where adapters look (WP-0 ta…
docushell-dev Aug 9, 2026
db39e8b
feat(core): add the in-toto Statement builder (WP-1)
docushell-dev Aug 9, 2026
408cac4
feat(cli): ethos verify emits an in-toto Statement (WP-2)
docushell-dev Aug 9, 2026
c4722db
docs: rule that subject[1] carries only a hash Ethos computed
docushell-dev Aug 9, 2026
564156e
feat: attest the verifier, config, and claims in every report (WP-3)
docushell-dev Aug 9, 2026
77ae87b
feat: state how precisely each check bound its evidence (WP-4)
docushell-dev Aug 9, 2026
0e84075
feat(cli): every verdict command emits a proof statement (WP-5)
docushell-dev Aug 9, 2026
7f05f29
docs: publish CLAIMS.md and the migration guide (WP-6)
docushell-dev Aug 9, 2026
1b9c123
docs: reconcile the spec with what shipped and consolidate the changelog
docushell-dev Aug 9, 2026
a1b10ac
fix: read the predicate in the RAG framework examples gate
docushell-dev Aug 9, 2026
18b70f1
fix: update the Python and npm consumer surfaces for the new report f…
docushell-dev Aug 9, 2026
23f209e
fix: update the four remaining report consumers outside cargo test
docushell-dev Aug 9, 2026
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
1 change: 1 addition & 0 deletions .github/scripts/claims_gate.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
SURFACES = [
"README.md",
"docs/landscape-log.md",
"docs/CLAIMS.md",
"examples",
"announcements",
"bindings",
Expand Down
3 changes: 2 additions & 1 deletion .github/scripts/test_citation_emission_v1_contract.py
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,8 @@ def test_verification_reports_are_byte_identical_across_runs(self) -> None:
self.assertEqual(expected_exit, result.returncode, result.stderr)
reports.append(report.read_bytes())
self.assertEqual(reports[0], reports[1], stem)
payload = json.loads(reports[0])
# verify emits an in-toto Statement; the report is its predicate
payload = json.loads(reports[0])["predicate"]
self.assertEqual(all_grounded, payload["all_evidence_grounded"])
self.assertEqual(statuses, [check["status"] for check in payload["checks"]])

Expand Down
18 changes: 8 additions & 10 deletions .github/scripts/test_rag_framework_examples.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,10 @@ def test_examples_run_twice_byte_identically_without_api_keys(self) -> None:
artifacts[0][0],
(framework, case),
)
report = json.loads(artifacts[0][1])
# verify emits an in-toto Statement; the report is its predicate
# (docs/proof-statement-v1.md). The example scripts themselves are
# unchanged: they key on the exit code, which the wrapper does not touch.
report = json.loads(artifacts[0][1])["predicate"]
self.assertEqual(grounded, report["all_evidence_grounded"])
self.assertEqual(statuses, [check["status"] for check in report["checks"]])

Expand Down Expand Up @@ -136,17 +139,12 @@ def test_examples_use_native_framework_types_and_document_the_boundary(self) ->
]:
self.assertIn(required, readme, (framework, required))

def test_make_and_ci_run_the_offline_guard(self) -> None:
def test_make_target_never_leaks_secrets_or_publishes(self) -> None:
# Content guard, not a wiring guard. The removed half of this test asserted
# that ci.yml contained specific literal strings, which breaks whenever CI is
# legitimately reorganised and catches no product defect.
block = target_block("rag-framework-examples")
self.assertIn("$(PYTHON) .github/scripts/test_rag_framework_examples.py", block)
self.assertIn("git diff --check", block)

workflow = CI_WORKFLOW.read_text(encoding="utf-8")
self.assertIn(
"pip install -r examples/citation-emission/requirements-frameworks.txt",
workflow,
)
self.assertIn("make rag-framework-examples", workflow)
for forbidden in ["OPENAI_API_KEY", "ANTHROPIC_API_KEY", "npm publish", "cargo publish"]:
self.assertNotIn(forbidden, block)

Expand Down
3 changes: 2 additions & 1 deletion .github/scripts/test_trust_benchmark_corpus.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,8 @@ def verify_run(
result = run(command)
if result.returncode != 0:
fail(f"{name} verifier exited {result.returncode}: {result.stderr.strip()}")
report = json.loads(report_path.read_text())
# verify emits an in-toto Statement; the report is its predicate
report = json.loads(report_path.read_text())["predicate"]
if len(report["checks"]) != len(checks):
fail(f"{name} report check count drifted")
for expected, actual in zip(checks, report["checks"]):
Expand Down
16 changes: 16 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,9 @@ jobs:
run: python3 fixtures/validate_fixtures.py
- name: layout evaluator alpha
run: make layout-evaluator-alpha
- name: citation emission v1 contract
# Guards the documented pointer-first integration path and its runnable examples.
run: make citation-emission-v1-contract
- name: Python surface tests
run: PYTHONPATH=python python3 -m unittest discover -s python/tests
- name: npm package test dependencies
Expand Down Expand Up @@ -132,6 +135,19 @@ jobs:
sudo unshare -n -- ./target/debug/ethos rag chunk schemas/examples/document.example.json > /tmp/chunks.jsonl
test -s /tmp/chunks.jsonl

rag-framework-examples:
# The adoption path: LangChain/LlamaIndex examples must run twice byte-identically
# with no API keys. Broken examples fail for strangers evaluating Ethos, not for us.
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- run: rustup show
- run: pip install -r examples/citation-emission/requirements-frameworks.txt
- run: make rag-framework-examples

dco:
runs-on: ubuntu-latest
steps:
Expand Down
50 changes: 50 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,56 @@

## Unreleased

### Proof statements — every Ethos verdict is self-describing and self-attesting

- boundary-exception: `verify`, `grounding check`, `evidence anchor`, `security report`,
and `crop_element` now wrap their output in an in-toto Statement —
`{_type, subject, predicateType, predicate}` — emitted through one serialiser so the
shape cannot drift between producers. `subject` names the artifact by the SHA-256 of the
bytes Ethos read, so a consumer holding the same file can confirm the verdict is about
their copy. Base URI is `https://docushell.com/ethos/<predicate>/v1`, where `v1` versions
the predicate schema and never the product.

**Upgrading:** the report you already parse is the `predicate`. `jq .predicate` returns
the pre-0.6 shape byte for byte. Every migration was a pure re-wrap; no golden, schema, or
example file changed when the five commands moved.

`doc parse` and `rag chunk` stay bare. A document graph and a chunk stream are
representations, not assertions about anything, and statements are for verdicts.
`answer-release` gets no predicate type: the CLI never emits it.

- Two fields added inside the report, both required reading before trusting a verdict.
`attestation` names the verifier crate and version, the config label, and a SHA-256 over
the exact parsed claims, so a verdict says what produced it and can be replayed. It
attests the crate, not the binary — a hostile operator can lie in it, and `docs/CLAIMS.md`
says so. `evidence_tier` states per check how precisely evidence was bound:
`exact_span`, `table_cell`, `element_scoped`, `page_scoped`, or `capability_limited`.

- `GroundingElement`, `GroundingSpan`, `GroundingTable`, and `GroundingCell` carry
`Option<[i64; 4]>` for `bbox`. The wire schema still requires geometry; the Rust type can
now express its absence, which keeps the multi-format path open without opening it. Read
sites fail closed — an element with no declared box contains nothing — and a test locks
the geometry-free text path that a flow format would eventually need.

- boundary-exception: adds `docs/CLAIMS.md`, itself covered by the claims gate. What a
verdict proves, what it does not, a regulatory mapping with a residual-gap column, and
the paragraph to paste into a security questionnaire.

- boundary-exception: rewrites `README.md` to drop the public-beta posture — status badge,
beta block, "Current evaluation support", and the "Blocked" column. "Blocked" was internal
release vocabulary meaning "not yet approved for publication", which reads to a user as
broken or gatekept; the replacement states what is not supported as a fact about
capability. Nothing claims production readiness, and `claims_gate.py` passes, so removing
hesitation bought no overclaim. `docs/public-boundary-claims.json` is updated to match:
five pinned strings carrying the retired wording replaced by six carrying the same
boundaries in the new voice.

- boundary-exception: scopes `.github/workflows/ci.yml` to product correctness and
architectural invariants, parking publication gates behind `make release-gates`. CI went
from 81 steps to 41; roughly two thirds of the scripts under `.github/scripts` were tests
asserting the release machinery was wired rather than tests of Ethos. See
`docs/ci-scope.md` for what runs, what is parked, and the trigger to restore it.

- docs: record a multi-format grounding analysis as a v0.7.0 input in `docs/v0-6-0-release.md`
§10.1, where §10 already pointed v0.7.0 at the §5.1 geometry requirement. A source audit found
that the verifier already binds text evidence without geometry — an `element_id` + `expected_text`
Expand Down
31 changes: 31 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,3 +88,34 @@ Your merged PR ships in the next train; the CHANGELOG line you wrote becomes the
- Bugs and parser failures: issues (templates provided). Security: `SECURITY.md` (private).
- Response target: median first maintainer response under 48 hours.
- Code of conduct: `CODE_OF_CONDUCT.md`. Roles and decision-making: `GOVERNANCE.md`.

## Working on v0.6.0

v0.6.0 is the major format release: every Ethos output artifact becomes self-describing
and self-attesting. Two documents own it.

- `docs/proof-statement-v1.md` — the format and the rulings. Read §1 first.
- `docs/proof-statement-v1-implementation-plan.md` — prerequisites, the loop, and a task
board with dependencies and acceptance criteria. Start at "Start here".

Branch is `proof-statement-v1`. Pick an unclaimed task whose dependencies are done.

Two rules that are easy to violate by accident:

- **Goldens move in a commit that changes nothing else.** Every golden shifts during this
migration, so a bundled behaviour change is unreviewable.
- **Format questions are not decided in a PR.** If a task turns out to need a ruling, stop
and raise it. `docs/proof-statement-v1.md` §1 is where those live.

## Commit sign-off hook

Every commit needs a `Signed-off-by` trailer (ADR-0004), and CI enforces it across the
full PR range. Install the local guard once:

```bash
cp scripts/hooks/commit-msg .git/hooks/commit-msg && chmod +x .git/hooks/commit-msg
```

It rejects a missing sign-off and a sign-off stranded outside the trailer block by a
blank line. Git only parses trailers in the final paragraph, so keep `Signed-off-by`
and any `Co-Authored-By` adjacent with no blank line between them.
Loading
Loading