Repository navigation
resolution/1.0 could not say "I decline to answer" - #21
Merged
Merged
Conversation
`documents.arxiv_version` is `''` for every row in both live notebooks, so a resolver that matches an entry's `quote_sha256` against corpus bytes and writes `current` for an entry declaring `version: v3` asserts a v3 pin confirmed by bytes of unknown version. derived-alg-geo-lean #170 requires the resolver to emit `not_applicable` there, and the enum had no such member. The four values this contract judges on are `pass` / `fail` / `not_run` / `not_applicable` (ADR-0005), and arXMCP's CLAUDE.md 4.10 rule 3 binds the same spelling across the seam. `join.py` already carries the constant and already states why it may not be folded into `not_run` -- the remedies differ. The corpus axis is simply its second user. Extended in place rather than bumped to `resolution/1.1`: no producer of `resolution/1.0` has ever existed. `attest/resolution.json` is absent from the topic repo and C-08 reports `not_run` in the v0.1.0 release notes, so there is no file in the world to invalidate. There will be after arXMCP #170 lands. - `result.resolution` gains `not_applicable`; `counts` gains its key. - A new conditional: `not_applicable` MUST carry a non-empty `reason`, and MUST NOT name a `resolved_source_version`. A result that both declines on version grounds and names a version is incoherent; and without the reason it is indistinguishable from `not_run` at the point of reading. - `coverage()` counted `resolution != not_run` as resolved, which would have reported the soundness hole as coverage -- exactly what the `reviewed` line next to it already refuses to do. Now excludes both, as that line does. Two rejection fixtures, both asserted at the schema and (through `test_validate.py`'s sweep of the directory) at the CLI. Also registers `containment-with-similarity.json` in `REJECTIONS`, where it never was. The directory sweep meant it was asserted to fail *somehow*, which is a weaker claim than it looks: `additionalProperties: false` rejects a document for a typo, so "the CLI said no" does not establish that the rule the fixture was written for is the rule that fired. `REJECTIONS` binds a fixture to its rule, and a new test now fails on any fixture that is in neither. And a structural test that `counts` has one key per `resolution` enum member, derived from the schema rather than listed -- an enum member with no counts key is a state every total silently swallows. 631 passed, 1 skipped (was 623, 1).
`quote_containment`'s conditional listed `current`/`drifted`/`unresolvable`, which made a containment match withheld by the version guard unrepresentable. The resolver would then have had to misreport the rung it actually used -- writing `quote_sha256` for a match it made by containment -- to say the one thing the guard requires it to say. An entry found by containment in a corpus that cannot name its arXiv version is withheld for exactly the reason a byte-equal match is. Which rung found the quote and whether the version can be confirmed are independent questions, and the schema now lets a result answer them independently. The valid fixture carries both combinations, so the accepted pairing is evidence rather than an absence of a rejection.
`/contract/build/` is in `.gitignore` and has been since packaging landed -- but eleven files under it went in with 6bd4739, the same commit that added the ignore, and `.gitignore` does not untrack what is already tracked. So a fresh checkout carried a `build/lib/` holding stale copies of `cli.py`, `lint.py`, `validate.py` and five schemas, and setuptools' `build_py` copies a source file into `build/lib` only when it is NEWER -- which on a fresh checkout, with every file stamped at the same moment, it need not be. Not hypothetical. It bit this branch: `pip install ./contract` shipped a `resolution-1.0.schema.json` predating `not_applicable`, so a resolver producing a valid document was told by its own bundled schema that the document was invalid. Eight of the eleven tracked files were stale. CI already installs the package rather than running it from the tree, and the comment there says why -- that is what catches a schema which failed to ship. It cannot catch one that shipped as the WRONG BYTES, because from outside those two look identical. So the new test compares the installed schemas against the source schemas byte for byte, and says to delete `build/` if they differ. Byte-identity rather than parsed equality: a schema can differ by a `$comment` that changes nothing and an enum member that changes everything, and only one of those survives a structural diff. It skips when `mfc` resolves to the source tree, which is what happens running pytest from `contract/`. CI runs it from the repo root against an installed package, which is the environment the claim is about.
`*.egg-info/` is in `.gitignore` beside `/contract/build/` and was tracked for the same reason -- committed in the packaging commit, before the ignore could take effect. Ten files, including a `contract/UNKNOWN.egg-info/` left behind by a build that ran before the project had a name. Less dangerous than the stale schemas: nothing reads `SOURCES.txt` to decide what a contract means. But every `pip install ./contract` rewrites `PKG-INFO` and `SOURCES.txt` in place, so the tree goes dirty on any build and each run offers the diff up for staging. The previous commit swept two of them in exactly that way. Deleted from the index only; the directories stay on disk and stay ignored.
This was referenced Sep 4, 2026
Merged
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.
Upstream half of
chris-dare-dev/derived-alg-geo-lean#170(epic #134), which requirestools/statement_resolve.pyto emitnot_applicable— nevercurrentwhendocuments.arxiv_version == ''. The enum had no such member, so the acceptance criterion was unstatable.arxiv_versionis''for every row in both live notebooks. Matching an entry'squote_sha256against corpus bytes and writingcurrentfor an entry declaringversion: v3asserts a v3 pin confirmed by bytes of unknown version.Why
not_applicableand notnot_runjoin.py:79already says it, for the review axes: the remedies differ.not_runis closed by running the resolver;not_applicableby fixing the corpus. ADR-0005 names the four values (pass/fail/not_run/not_applicable) and arXMCP'sCLAUDE.md§4.10 rule 3 binds the same spelling across the seam. The corpus axis is the constant's second user, not a new idea.Why extended in place rather than
resolution/1.1No producer of
resolution/1.0has ever existed —attest/resolution.jsonis absent from the topic repo and C-08 readsnot_runin the v0.1.0 release notes. There is no file in the world to invalidate. There will be as soon as arXMCP #170 lands, which is why this goes first.Changes
result.resolutiongainsnot_applicable;countsgains its key.not_applicableMUST carry a non-emptyreasonand MUST NOT name aresolved_source_version.coverage()countedresolution != not_runas resolved — that would report the soundness hole as coverage, which is exactly what thereviewedline beside it already refuses to do.Two things found while here
containment-with-similarity.jsonwas inREJECTIONSnowhere. The directory sweep intest_validate.pyasserted it fails somehow, which is weaker than it looks —additionalProperties: falserejects a document for a typo, so "the CLI said no" does not establish which rule fired. Registered, plus a test that fails on any fixture bound to no rule.countshas one key perresolutionenum member, derived from the schema. An enum member with no counts key is a state every total silently swallows.631 passed, 1 skipped (baseline 623, 1).