Proof statement v1 - #236
Merged
Merged
Conversation
The spec is a draft for ruling. Three decisions in section 1 block everything below them: the artifact shape, the predicateType URI namespace, and the representation_sha256 conflict. Each carries a recommendation so the tradeoffs are concrete rather than abstract. Nothing is implemented. Scope of the eventual change is deliberately narrow: it changes artifact shape and adds corroboration, and it changes no verification semantics. Worth recording why the shape work is not a foreign idea. v0.6.0 already implements artifact-type dispatch in crates/ethos-cli/src/grounding.rs, and the grounding validation report already requires a self-describing artifact_type field. Five other output artifacts have neither. This finishes what WP-0..WP-3 started. Also adds scripts/hooks/commit-msg. The v0.6.0 branch hit two distinct DCO failures in one afternoon: a commit with no sign-off, and commits whose sign-off sat outside the trailer block because a blank line separated it from Co-Authored-By. Git only parses trailers in the final paragraph, so the second form looks correct and fails. The hook uses git interpret-trailers --parse, the same mechanism as .github/scripts/check_dco.py, so local and CI agree. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: docushell-dev <doqshell@gmail.com>
1.1 Artifact shape: in-toto Statement. An established shape with existing tooling and growing auditor familiarity, for no invented format. Not DSSE as the on-disk artifact, because it base64-encodes the payload and readable evidence is half of what Ethos offers. 1.2 Namespace: an Ethos-owned domain, not DocuShell. The product argument is independence, and a predicateType reading docushell.com erodes it every time someone opens an artifact. The specific domain is not yet registered; that now blocks emitting any artifact, since the string is permanent. 1.3 Source identity: representation_sha256 stays authoritative, matching the standing ruling in docs/v0-6-0-release.md section 8. On the Grounding JSON path a foreign parser produced the representation and Ethos never touched the source PDF, so naming the PDF as what was read would assert something Ethos cannot know. 1.4 Subject shape: both, representation first. This revises the earlier draft, which said subject holds source documents only. subject is an array, so the honest answer and the useful one are not in conflict: subject[0] is always the representation Ethos read, subject[1] is the source document only when the binding is real. Consumers must not assume subject[0] is the PDF, which is a documentation obligation recorded in section 1.4. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: docushell-dev <doqshell@gmail.com>
Base URI is https://docushell.com/ethos/, shape <base>/<predicate>/v<n> for all seven types. Chosen over a dedicated Ethos domain because a purchase plus a perpetual renewal obligation is a poor trade against a weak branding signal. Independence is carried by the Apache-2.0 licence, offline key-free operation, and byte-reproducible results; a hostname affects none of them. The /ethos/ path segment scopes the namespace so a later move stays a rename with the old string kept as an alias. Records that v<n> versions the predicate schema and never the product, so grounding/v1 stays v1 across Ethos 0.6, 0.7, and 1.0. Adds docs/proof-statement-v1-implementation-plan.md: seven work packages in dependency order, each with a file-by-file touch list and its own acceptance evidence. Touch points were verified against main rather than assumed. Two findings shaped the sequencing: - verification_report_json_bytes in cmd/verify.rs is the only *_json_bytes serialiser in the CLI. The other five artifacts serialise inline at their write_output call sites, so WP-6 needs a shared serialiser introduced first rather than a single edit. - The config hash mechanism the attestation block needs already exists as sha256_hex(c14n(config)) at cmd/verify.rs:128, so WP-3 adds no new machinery. The load-bearing rule is one statement builder in ethos-core with no command hand-rolling a statement. That is the same constraint invariant 3 already places on c14n, and it is the specific failure mode observed in Signet, which builds four different signable shapes across two files with no schema anywhere. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: docushell-dev <doqshell@gmail.com>
Corroboration is dropped. It was designed from a threat model rather than from a user, and the case for it does not survive contact with the questions it raises: no external user has asked for it, two parsers sharing an upstream share failure modes so their agreement proves little, it doubles parse cost against a 20k docs/day design target, and nobody has measured the divergence rate on real documents. A near-zero rate makes it not worth building; a high rate makes it noise reviewers learn to ignore. Those need opposite decisions and the number does not exist. A compensating control already ships: DocuShell shows the reviewer a rendered crop of the actual page region, and a human reading real pixels catches parser errors directly. Section 7 records that revisiting requires a measured divergence number, not an argument. sources[] goes with it. It existed to carry multiple grounding sources for comparison. Keeping it would freeze an array at length one, never exercised, which is precisely the unvalidated shape that forces a v2 later. The singular grounding field is unchanged. evidence_tier survives. It is independent of corroboration and useful alone: one deterministic enum for how strong a match was, so a consumer reads one field instead of interpreting a capability matrix. Six work packages remain, six predicate types, no new capability. The release is now plumbing with a clear payoff: self-describing artifacts that name what produced them, which is what the 7-year retention and replay commitments in the DocuShell workbench architecture need in order to mean anything. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: docushell-dev <doqshell@gmail.com>
The gate was left orphaned by the CI cleanup: not in ci.yml, not in release-gates, failing when run by hand. Two failures, and one of them was self-inflicted. test_make_and_ci_run_the_offline_guard asserted that ci.yml contains the literal string "pip install -r examples/citation-emission/requirements-frameworks.txt". Removing that CI step broke the guard that asserts the step exists. This is the same prose-assertion class as the nine milestone-D guards deleted in the cleanup for exactly this reason, and it was reintroduced by the same commit that applied the rule elsewhere. That test is replaced by test_make_target_never_leaks_secrets_or_publishes, which keeps the half that guards content — no API keys, no publish commands in the target — and drops the half that asserts CI wiring. The second failure was environmental: the byte-identical-twice test needs langchain and llama_index, installed by the pip step that was removed. That test is real and worth keeping. It runs each framework example twice and asserts identical output with no API keys, which demonstrates the determinism claim in the exact place a newcomer looks. The job is restored to CI with its pip install. These examples are the adoption path, and broken examples fail for strangers evaluating Ethos rather than for us, so rotting between publishes is the wrong failure mode to accept. All four tests pass locally with the frameworks installed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: docushell-dev <doqshell@gmail.com>
Promotes section 1.5 from a provisional call to a ruling. document.ethos.json and chunks.jsonl are not wrapped in statements. A statement means "X asserts P about Y", and a document graph is not an assertion about the document; it is the document re-expressed. Wrapping it would read as "here is a claim about invoice.pdf, and the claim is invoice.pdf". Wrap everything and the word statement stops distinguishing anything. Three supporting reasons recorded: chunks.jsonl is streaming NDJSON that wrapping would bloat or break, both files are consumed by other tools including DocuShell's retrieval path, and neither needs the wrapper for provenance since both already carry a document fingerprint, profile hash, and config hash per SPEC.md. Same instinct as the II.1 rule in DocuShell's workbench architecture applied one level down: the thing being judged must not look like the judgment. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: docushell-dev <doqshell@gmail.com>
A developer landing in this repo would have been pointed the wrong way. docs/v0-6-0-release.md section 9 listed attestation as a non-goal for v0.6.0, which is now WP-3, a core deliverable. Both v0.6.0 documents described only the Grounding JSON scope. Neither said where to start. Adds to the implementation plan a "Start here" section (reading order, prerequisites including the commit-msg hook, the build and test loop, rules for claiming work, and what counts as a finished task) and a task board of 32 numbered tasks with dependencies and acceptance criteria. Adds WP-0, which keeps the multi-format path open without adding multi-format support. Three tasks: a test locking the geometry-free text path, bbox becoming Option in the trait while the schema stays strict, and recording the five gates from section 10.1. Multi-format itself is out of scope with a written trigger, because no DocuShell workflow needs it — the workbench scope contract puts non-PDF formats out of Part I and names no Part II trigger. Docling supporting every format is a fact about Docling, not a requirement on Ethos. One finding shaped WP-0's scope: GroundingCell already carries row, col, row_span, and col_span. Row and column addressing is R1C1, so an XLSX sheet maps onto the existing table model with no new locator concept. The only blocker is bbox on cells. Supersession notes added to both v0.6.0 documents, with the superseded non-goals struck through rather than deleted so the scope change stays visible. Execution status gains a dated entry. CONTRIBUTING gains a section naming the two rules easiest to break by accident: goldens move alone, and format questions are not decided in a PR. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: docushell-dev <doqshell@gmail.com>
Removes the status badge, the beta status block, the "Current evaluation support" framing, and every "evaluation" qualifier. Users should be able to try Ethos without wording that asks them to wait. The change worth explaining is the "Blocked" column. Five rows read Blocked — Windows artifacts, hosted API, bundled PDFium, benchmark claims, production positioning. That is internal release vocabulary meaning "not yet approved for publication". To someone evaluating Ethos it reads as broken or gatekept. The replacement states what is not supported as a fact about capability: "No OCR. Fails with ocr_required rather than guessing." Nothing here claims production readiness. claims_gate.py bans exactly that family of overclaim — production-ready, release-ready, fastest, world-class, state-of-the-art — and it passes, so removing hesitation did not buy an overclaim in exchange. Adds a statement rather than only cutting: no speed, footprint, or parser-quality comparison is published because no benchmark has been run whose numbers we would defend. Saying why there are no numbers is stronger than staying quiet about it. public-boundary-claims.json is updated to match. Five pinned README strings carried the retired beta wording; six replacements carry the same boundaries in the new voice, including the honest limits. Eleven install and PDFium claims are unchanged. CHANGELOG records three boundary-exception entries, which is what that gate exists to force. The README also now points at the v0.6.0 format plan and task board instead of naming the Grounding JSON prep document as "the v0.6.0 plan". Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: docushell-dev <doqshell@gmail.com>
docs/v0-6-0-release.md section 10.1 established by source audit that an evidence
ref of {element_id, expected_text} with no page locator and no bbox reaches
AnchorStatus::Bound at AnchorLevel::Text with no capability limit. Nothing
tested it. A refactor could have closed that path and no one would have found
out until someone tried a flow document years later.
Four tests, no production code touched. They assert the four steps rather than
only the outcome: page_locator_required is false via the element_id disjunct,
requires_bbox excludes AnchorLevel::Text, resolve_page returns
PageCheck::NotChecked rather than NotFound, and the anchor is Bound with an
empty capability-limit list.
The end-to-end test asserts checks.bbox is NotChecked and not
CapabilityLimited. The ref never asked for geometry, so nothing was downgraded
and the caller is owed no warning. Emitting CapabilityLimited there would put a
spurious limitation on every geometry-free verification, which is the kind of
noise that teaches consumers to ignore capability limits.
Verified by mutation rather than assumed. Changing the resolve_page fallthrough
to NotFound fails two of the four; adding AnchorLevel::Text to requires_bbox
fails two. Both mutations were reverted and the diff is additions only.
This is WP-0 task 0.1. It keeps the multi-format door open; it does not open it.
Multi-format support remains out of scope per docs/proof-statement-v1.md
section 7.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Signed-off-by: docushell-dev <doqshell@gmail.com>
…P-0 task 0.2) GroundingElement, GroundingSpan, GroundingTable, and GroundingCell now carry Option<[i64; 4]> instead of [i64; 4]. docs/v0-6-0-release.md section 10.1 framed the choice as an unread in-memory sentinel versus Option and a breaking change to the published 0.5.0 baseline. v0.6.0 is already a breaking change to that baseline — WP-3 adds a required attestation field to VerificationReport — so the honest option costs nothing extra now and would cost a second breaking release later. The schema does not move, which is the WP-0 constraint. ethos.grounding.v1 still requires bbox on element, span, table, and cell; media_type is still const application/pdf; the positive-area check still stands. The Rust type can now express absence, and the wire contract still refuses it. Verified: no schema or golden file changed. Read sites fail closed rather than treating absence as a wildcard. An element with no declared box contains nothing, so containment filters exclude it and a bbox query over a geometry-free source resolves to NotFound. The adjacency join declines when either element lacks geometry, matching the posture it already takes for CoordinateOrigin::Unknown. The arithmetic helpers — contains_bbox, bbox_area, union_bbox, element_bboxes_are_adjacent — keep taking [i64; 4]; the Option handling belongs at the call sites, not in the geometry. Two tests cover the new semantics, because behaviour that only a comment describes is behaviour that decays. Both were verified by mutation: turning is_some_and into map_or(true) inside resolve_bbox, so absence reads as a wildcard, fails absent_element_geometry_never_satisfies_a_bbox_query. The first attempt at that mutation hit the wrong call site and the test passed, which is the reason to run the mutation rather than assume the assertion is wired to anything. 406 tests pass, up from 404. fmt, clippy, verify dependency boundary, and the minimal grounding feature build are all green. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: docushell-dev <doqshell@gmail.com>
…sk 0.3) The analysis lives in docs/v0-6-0-release.md section 10.1, which is a release record and will eventually be superseded. The durable engineering content moves into docs/bring-your-own-parser.md, where someone implementing a GroundingSource already reads to find out what is required of them. Two audiences, one section. Adapter authors learn that geometry is required, that a zero-area sentinel is rejected outright, and that Option<[i64; 4]> on the trait is not an invitation to send None — the wire schema still requires the field. Maintainers get the five gates with their locations, and the reason the constraint is narrower than it looks: the verifier already binds text with no geometry, and the tests that keep that true are named so nobody deletes them during a cleanup. Also records why the sequencing is DOCX, XLSX, PPTX rather than the intuitive reverse, including the XLSX measurement that rules out computing column geometry — a 12.5% swing between Calibri 11 and Verdana 11 on the same nominal column, driven by system font metrics that ADR-0003 does not cover. Completes WP-0. Multi-format support is still out of scope; the trigger to revisit is in docs/proof-statement-v1.md section 7. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: docushell-dev <doqshell@gmail.com>
New crates/ethos-core/src/statement.rs. The single place a Statement is constructed, for the same reason ethos-core owns the one c14n implementation: a wire format assembled independently in several places drifts, and the drift stays invisible until two producers disagree. Signet is the cautionary case, building four different signable shapes across two files with no schema anywhere. The _type string was verified against the in-toto v1 specification before being frozen: https://in-toto.io/Statement/v1. Subject entries are ResourceDescriptors with a required digest map, which is why digest is a BTreeMap rather than a struct — key order is canonical without relying on serde field order, and the algorithm lives in the key, so the value is bare hex with no sha256: prefix. Statement::new encodes the section 1.4 subject ruling in the type signature rather than leaving it to convention. It takes the representation by value and the source as an Option, so subject[0] is always what Ethos actually read and an empty subject array — which in-toto forbids — is unrepresentable. On the Grounding JSON path a foreign parser produced the representation and Ethos never touched the PDF, so passing None is the honest answer and inventing a source subject is the failure this shape prevents. Gated behind the `full` feature rather than `verify-types` as the plan drafted, because statement_bytes routes through c14n, which needs serde_json. That makes invariant 4 safer rather than weaker: ethos-verify building with only the grounding feature cannot see this module at all. Verified — grounding-only, verify-types, and ethos-verify all still compile, and the dependency boundary check passes. Seven unit tests and one doctest. The load-bearing one is predicate_survives_wrapping_byte_for_byte, which asserts c14n of the unwrapped predicate equals c14n of the input. WP-2's payload-equivalence test depends on that property holding, so it is proven here before anything relies on it. wire_field_names_match_the_in_toto_spec guards the _type and predicateType spellings, since renaming either silently produces an artifact no in-toto tool recognizes. 414 tests pass, up from 406. No schema or golden file changed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: docushell-dev <doqshell@gmail.com>
verification_report_json_bytes now wraps the report in a Statement before canonicalizing. Both emit paths — single report and batch NDJSON — flow through that one function, so this is a single behavioural edit. Two decisions WP-2 forced that the plan had not anticipated. subject[0] digests the input file's bytes, not the report's document_fingerprint. The first attempt used the fingerprint and it broke foreign_source_without_fingerprint_blocks_fingerprint_pinned_citations: a foreign source that declares no fingerprint is a supported, tested case, and failing closed there turned a working verification into internal_error. That is a semantics change, which ground rule 1 forbids. Hashing the input bytes is also simply more correct — in-toto matches subjects by digest, so the value has to be something a consumer holding the same file can compute, and the document fingerprint is the canonical-graph identity, not derivable from the file. For the Grounding JSON path the two agree anyway, since representation_sha256 hashes exactly those bytes. subject[1] is deliberately absent. The only source binding available is GroundingJsonSource::source_sha256, documented as the producer-declared PDF hash, and section 5.1 already says a source-hash match proves only that the mapper declared the hash of the PDF you supplied. Recording a declaration in a field that in-toto matches by digest would invite a consumer to resolve it and conclude Ethos verified against bytes it never saw. Section 1.4's second subject stays unimplemented pending a ruling on whether an unauthenticated declaration belongs there at all. The goldens do not move, which is better than the plan's approach. verify_alpha_demo_report_predicates_match_goldens asserts the emitted predicate against the existing pre-0.6 goldens, so payload equivalence is proven permanently rather than at one moment: every byte the verifier produces is unchanged, only nested. Regenerating the goldens would have destroyed exactly the evidence needed to show that. It also means determinism CI never goes blind during this migration. A semantic change now fails against the goldens; a wrapper change fails in verify_emits_a_proof_statement, and keeping those separate is the point. Test migration is mechanical: a verify_report helper unwraps the predicate for assertions, and the ethos grounding check tests are deliberately untouched because that command is not wrapped until WP-5. 415 tests pass. No golden, schema, or example file changed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: docushell-dev <doqshell@gmail.com>
Records the ruling of 2026-08-09 and makes section 1.4's "only when the binding is real" concrete: real means Ethos read the bytes itself, not that a producer declared a hash. ethos verify emits no subject[1]. On the Grounding JSON path the only available source binding is GroundingJsonSource::source_sha256, documented as the producer-declared PDF hash, and section 5.1 already says a source-hash match proves only that the mapper declared the hash of the PDF you supplied. An in-toto subject means "this statement is about these artifacts" and tools match them by digest. A reviewer holding loan-file.pdf who sees its hash in subject[1] reasonably concludes Ethos checked their file. It did not; it read a JSON claiming to come from it. Editing that one line leaves the statement pointing at the right PDF and still reporting everything grounded. The gap is invisible at exactly the moment it matters. --crop-source-pdf is the one case where subject[1] would be honest, because Ethos loads and validates the real bytes there. If it is ever built, that is the only permitted source. Also records why subject[0] digests the input file bytes rather than document_fingerprint: consumers match subjects by digest, so the value must be computable from the file they hold. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: docushell-dev <doqshell@gmail.com>
Every VerificationReport now carries a required attestation block naming what produced the verdict. This promotes Part B of the citation-emission plan from proposal to shipped, and makes it non-optional rather than hardening-gated. The block is smaller than the spec drafted, deliberately. Section 4 listed four fields; two of them would have been duplicates. The config hash is already top-level as verification_config_sha256 and the source fingerprint is already top-level as document_fingerprint, so nesting copies of both is bloat, not attestation. The `replay` field is also dropped: a constant string repeated in every artifact is documentation misfiled as data, and the recipe is already present as data — verifier version, config hash, claims hash, document fingerprint. Part B's own text invited dropping it. What ships is what was actually missing: the verifier crate name and version, and a hash binding the report to its exact claims input. claims_sha256 is passed into verify_claims rather than computed there, because ethos-verify builds against ethos-core with only the grounding and verify-types features under invariant 4 — it has no c14n and no serde_json. That is the same reason config_sha256 has always been a parameter, so the shape is symmetric rather than novel. The verifier identity does come from ethos-verify's own env! macros, so a library caller gets the same attestation as a CLI user. Fixtures move, and this time legitimately: the report gained a field. All eight goldens are purely additive. The three schema examples are +9/-1 each, the minus being one comma; an earlier regeneration pass rewrote them in c14n key order and expanded compact arrays, which was correct content but an unreviewable diff, so the block is text-inserted instead and the hand-maintained formatting survives. Two tests, one mutation-checked. report_attests_the_verifier_config_and_claims compares the attested version against the crate's own CARGO_PKG_VERSION rather than a literal, so a bump that fails to flow through is caught; setting the version to 0.5.0 by hand fails it. claims_hash_covers_the_claims_and_not_their_ packaging proves different claims produce different hashes. 417 tests pass. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: docushell-dev <doqshell@gmail.com>
Adds EvidenceTier and an optional evidence_tier on every Check. Per check rather than per report, ruled 2026-08-09: a report legitimately mixes tiers — one quote bound to an element, one presence claim scoped to a page — and a single top-level value would have to aggregate, baking a judgment into the artifact that a consumer cannot undo. Worth recording honestly: this is a convenience projection, not a new fact. A consumer could derive it from match_method plus the claim's citation, both already on every check. The AetherProof idea it comes from does not fully transfer — model_root_type was load-bearing precisely because a receipt could not otherwise reveal whether the root was a weights hash or a typed name. It earns its place anyway, because an evidence product should not make every consumer write the same derivation and get it subtly wrong. Five variants, not the four the spec drafted. A table cell is a first-class v1 claim kind, and folding it into element_scoped would understate a precisely bound cell while a four-value enum would have forced that lie. The tier is set on FoundTarget where the target resolves, not re-derived from the citation afterwards, so it cannot drift from the locator precedence it describes. Two consequences follow: a quote grounded across two adjacent elements is element_scoped, because two elements joined is still element precision, and a check whose outcome was decided by a missing span or table capability, or an unknown coordinate origin, reports capability_limited rather than a precision it did not achieve. A check that resolved nothing carries no tier at all. Fixtures are purely additive, +13 -0. Goldens regenerate cleanly since they are already in c14n order; the hand-formatted schema examples take a line insertion after each match_method so their ordering and compact arrays survive. Two tests. each_check_states_how_precisely_it_bound_evidence asserts three tiers in one report, since a derivation that only ever sees one tier is untested; mislabelling the table cell as element_scoped fails it. unresolved_checks_state_ no_tier holds the fail-closed edge. 419 tests pass. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: docushell-dev <doqshell@gmail.com>
Introduces statement_json_bytes in main.rs as the single path from an Ethos verdict to bytes on disk, and moves all five emitters onto it: verify, grounding check, evidence anchor, security report, and crop_element. verify had its own copy from WP-2 and now shares this one, so the statement shape cannot drift between producers — the same reason ethos-core owns one c14n. WP-5 is four migrations, not five. answer-release is not emitted by the CLI at all: it is an app-layer envelope consumers build through derive_app_answer_release_decision. There is no producer to change, so there is nothing to wrap and no predicate type worth reserving on speculation. The grounding validation payload keeps its artifact_type field. Retiring it in favour of predicateType would break payload equivalence for no benefit today, and ADR-0016 freezes it as an input contract regardless. Redundant, and the redundancy is cheaper than the change. Not migrated, deliberately: ethos doc parse and ethos rag chunk. A document graph and a chunk stream are representations, not assertions about anything, and statements are for verdicts (section 1.5). A test asserts chunks.jsonl still has no _type. Zero fixture changes. Every one of the four migrations was a pure re-wrap, so no golden, schema, or example file moved — the same property WP-2 established, now demonstrated across the whole surface. every_verdict_command_emits_its_own_predicate_type covers all four commands in one test, because the failure mode is a single command quietly keeping its own serialisation, and that drift only surfaces when two producers disagree. Verified by mutation: restoring the old inline c14n path in security_report fails it. Also drops three now-unused EthosError imports the shared helper made redundant. 420 tests pass. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: docushell-dev <doqshell@gmail.com>
Adds docs/CLAIMS.md in four parts: what a verdict proves with the mechanism and the code it lives in, what it does not prove with what would close each gap, a conservative regulatory mapping carrying a residual-gap column, and the paragraph to paste into a security questionnaire. The does-not-prove table is the point of the document. Eight rows, including the ones easiest to leave out: Ethos verifies a claim against the representation and not the representation against the document, so a parser error that both drafts and verifies consistently is invisible; the attestation names the crate version and not the binary, so a hostile operator can lie in it; subject[0] is what Ethos read, which on the Grounding JSON path is a parser's output and not the PDF; a paraphrase of a true fact is a mismatch; and no speed, footprint, or parser-quality property is claimed because no benchmark has been run whose numbers we would defend. CLAIMS.md is added to the claims-gate surfaces. A claims document that is not itself claim-gated is the obvious gap. The payload-versus-envelope field table goes into proof-statement-v1.md section 2 rather than a separate contract document, because that table was the only thing the separate document would have held. It names the attack it exists to prevent: subject[].name is a caller-chosen label, in-toto matches artifacts by digest, and a consumer keying a release decision on the name can be handed a file called loan-file.pdf that is not the loan file. README gains a "What comes out" section showing the real emitted shape, verified against actual CLI output rather than written from the spec, plus the upgrade line: the report you already parse is the predicate, and jq .predicate returns the previous shape byte for byte. Completes WP-6 and the release. 420 tests pass. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: docushell-dev <doqshell@gmail.com>
The spec still read as a proposal after being fully implemented. Status was "ruled, not implemented", the predicate table listed answer-release as a pending migration when it has no producer, "all six" counted a predicate that does not exist, and a note told the reader to verify the in-toto _type revision before freezing a string that was frozen five commits ago. A reference document that describes a plan rather than the thing is worse than no document, because it reads as authoritative. Section 3 now records what exists: five predicates, each with the command that emits it, plus the two decisions a reader will otherwise re-litigate — why answer-release has no type, and why grounding-validation keeps its artifact_type field inside the predicate. Section 6 stops restating the proof tiers. They are defined once, for readers, in docs/CLAIMS.md section 5; two copies of a claim is how the two copies start disagreeing. What the format owes the tiers stays here: signatures attach to the named-but-empty wrapper layer, the predicate stays byte-identical when one is added, and DSSE is the signing envelope and deliberately not the artifact. The changelog told the v0.6.0 story across four separate entry blocks written at different times. Consolidated into one section that reads in the order a reader needs it — what changed, how to upgrade, what was added inside the report, and what the CI scoping did — with every boundary-exception marker preserved so the release-boundary gate still passes. 420 tests pass. No code changed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: docushell-dev <doqshell@gmail.com>
The gate parses a verification report at the top level, which moved under predicate when verify started emitting statements. Caught before pushing by running every CI job locally; the gate is not in the ordinary cargo test loop, which is exactly why WP-5 did not surface it. Worth recording what did not break: the example scripts themselves are unchanged. They key on the verifier's exit code, and the wrapper does not touch the exit-code contract. An integrator who copied those scripts needs no changes, which is the compatibility property that actually matters here. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: docushell-dev <doqshell@gmail.com>
…ields Three consumer surfaces read the verification report and none were covered by cargo test, so WP-3 through WP-5 left them broken. All three surfaced only when running every CI job locally before pushing. python/ethos_pdf/emit.py validates reports against strict field allowlists. attestation and evidence_tier are added as allowed rather than required, deliberately: the wrapper drives a caller-provided ethos binary that may predate 0.6, and requiring the fields would break those callers with a confusing error for no benefit. The npm package's generated type declarations were stale against the schema, and the TypeScript consumer fixture needed attestation now that the type requires it. The RAG framework examples gate parsed the report at the top level. Worth noting what did not break: the example scripts themselves are unchanged, because they key on the verifier's exit code and the wrapper does not touch the exit-code contract. An integrator who copied those scripts needs no changes. All seventeen CI checks pass locally: fmt, clippy, 420 tests, validator ceiling, fixtures, layout evaluator, Python surface, npm, three schema validators, RAG examples, verify portability, dependency boundary, minimal grounding build, and both dogfood directions. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: docushell-dev <doqshell@gmail.com>
The determinism workflow was skipped on this PR because its cross-platform job gates on a contract-change label that did not exist in the repository. Creating the label and applying it fired the job, which failed on all three platforms at "verification report repeated-byte and golden equality" — not a platform difference, but a consumer nothing in cargo test reaches. examples/verify/check_verify_alpha.py compares CLI output against the goldens directly. It now unwraps the predicate, and a load_predicate helper carries the reason: goldens stay in the pre-0.6 report shape on purpose, because comparing the predicate against them is what proves the wrapper is a pure re-wrap rather than a reshaping. A sweep for every file reading all_evidence_grounded or checks found three more: check_rendered_crops.py, test_citation_emission_v1_contract.py, and test_trust_benchmark_corpus.py. All three now read the predicate. The citation-emission gate additionally asserts that ci.yml contains "make citation-emission-v1-contract", which the CI scoping removed. Restored to the test job rather than weakening the assertion: seven of its eight tests are real, it runs in under a second, and it guards the documented pointer-first integration path and its runnable examples. Verified: make verify-alpha, citation-emission-v1-contract, trust-benchmark-corpus, rag-framework-examples, layout-evaluator-alpha, validator-ceiling-check, cargo test, Python surface, npm, schema validation, fmt, and clippy all pass. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: docushell-dev <doqshell@gmail.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.