Add a claim warrant, and stop it from moving the root gate - #111
Merged
Conversation
resolvable_reference() decides whether a parentless claim names anything checkable, and UnattributedRootError is a hard gate on that result. It checks shape, not existence, so it stops "trust me" but admits a well-formed DOI that was never registered. The warrant is the next rung: which kind of check applies, whether that check needs an oracle, and what happened when it ran. Four types - measured, inferred, analogized, cited - of which measured and cited are mechanically re-checkable and the other two are oracle-conditional. verify_outcome is three-valued on purpose: unverifiable must never collapse into rejected, because a dead URI costs an adversary nothing and produces no failed check. It goes in the evidence object, which was already required and already unconstrained. No node field changes, no migration, nothing requires a warrant, and untyped evidence resolves exactly as before. The gate interaction, which is why graph.py is touched at all: a warrant's source_digest is a hex string matching the accepted hash form. Flattened into evidence it would satisfy resolvable_reference and admit a root that was previously refused - a hard gate moving as a side effect of attaching metadata. A warrant describes how a claim could be checked; it is not evidence that the claim named anything. So placement is normative, under a reserved key, and resolvable_reference skips that key by name. Nested dicts are already skipped by the isinstance guard, so the skip is explicit rather than load-bearing today. It keeps the guarantee if that function ever learns to recurse. test_claim_warrant_gate.py pins all of it, including a test that demonstrates the failure being prevented: prose alone returns None, prose with a flattened warrant returns "hash". Vocabulary borrowed, nothing imported. The four-way typology is the classical set of pramanas; the modern formulation is cited by DOI (Kadaboina 2026, Apache-2.0). The schema is written locally with local field names, no external package is a dependency, and nothing here validates that work's formal claims. Design notes in docs/ record two corrections found in review. The mapping table is absent because ledger_adapter.py is not in this repository - one side of it does not exist yet. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.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.
resolvable_reference()decides whether a parentless claim names anything checkable, andUnattributedRootErroris a hard gate on it. It checks shape, not existence — so it stops "trust me" but admits a well-formed DOI that was never registered.The warrant is the next rung: which kind of check applies, whether it needs an oracle, and what happened when it ran.
The distinction
measured·cited→ deterministic, mechanically re-checkableinferred·analogized→ oracle_conditional, needs a judgeverify_outcomeis three-valued on purpose —unverifiablemust never collapse intorejected. A dead URI costs an adversary nothing and produces no failed check.Where it goes
The
evidenceobject, which was already required and already unconstrained. No node field changes, no migration, nothing requires a warrant, and untyped evidence resolves exactly as before.Why
graph.pyis touched at allA warrant's
source_digestis a hex string matching the acceptedhashform. Flattened intoevidenceit would satisfyresolvable_referenceand admit a root that was previously refused — a hard gate moving as a side effect of attaching metadata.A warrant describes how a claim could be checked. It is not evidence that the claim named anything. So placement is normative, under a reserved key, and
resolvable_referenceskips that key by name.Nested dicts are already skipped by the
isinstanceguard, so the skip is explicit rather than load-bearing today — it keeps the guarantee if that function ever learns to recurse.Verification
tests/test_claim_warrant_gate.py— 4 tests, including one that demonstrates the failure being prevented: prose alone returnsNone, prose with a flattened warrant returns"hash".25 tests green across
test_provenance,test_benchmarkand the new suite. Untyped inputs verified unchanged:{}→None,"trust me"→None, DOI→doi, URL→url, hash→hash, arxiv→arxiv.Vocabulary borrowed, nothing imported
The four-way typology is the classical set of pramāṇas. The modern formulation is cited by DOI (Kadaboina 2026, Apache-2.0). The schema is written locally with local field names — no external package is a dependency, and nothing here validates that work's formal claims. If that repo vanished, nothing here breaks.
Not included
The mapping table —
ledger_adapter.pyis not in this repository, so one side of it does not exist.docs/records two corrections found in review, including a coverage rule: untyped is a first-class stratum, and a screen that reports a clean result over a minority of classified nodes reproduces the exact failure it exists to catch.🤖 Generated with Claude Code