release: 1.2.0 to main - #300
Merged
Merged
Conversation
…machinery (#284, #285) #285's census records this repo as "enforcement test: none". It has seven. `tests/test_clone_readiness.py` proves both VERTICAL arrows of ADR-002 by importing packages in a subprocess with the others blocked — and its own docstring says a regex was tried first and rejected, because it missed `from ..contract import x` and `from views_postprocessing import contract`. There is even a mutation proof of the detector. That is stronger than a static contract: it proves the modules import in isolation, not merely that no import statement mentions them. What nothing proved is the HORIZONTAL arrow. Nothing stopped `crafd` and `unfao` importing each other, and that is the arrow keeping a partner liftable: the two are deliberate clones (C-33), so the realistic violation is a copy-paste leaving a sibling's import behind. `test_the_machinery_imports_without_any_partner` cannot see it — that test imports the machinery, and this is partner-to-partner. Two halves, matching the split this file already documents: the subprocess is load-bearing and sees transitive arrivals; the source scan is the supplement and covers `managers/`, which the subprocess deliberately skips because importing a manager needs views-pipeline-core and a purity check should not be contingent on a heavy framework being installed (C-40 (a)). Mutation-proven in both halves: a sibling import added to `crafd/product.py` fails the subprocess half; a sibling named in `crafd/managers/crafd.py` fails the source half. WHY NOT import-linter, as #284 proposes. It would add a dev dependency, a CI step and a config block to assert three things — of which two are already covered here, and covered more strongly. The one it would add is this test. #285 itself points approvingly at views-datafactory doing the same thing in two assertions in an existing file, with no new dependency and no graph library; that is the argument, and it applies here. If the platform later standardises on import-linter, adopting it is a one-line pyproject block and this test can stay or go — nothing here forecloses it. Suite: 470 passed, 3 skipped, 37 xfailed. ruff clean. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…view-diff The source half scanned the manager's whole text for the sibling's module path. This repository's comments cite module paths constantly — C-33's own text points at `unfao/product.py` — so a documentation comment naming the sibling would have failed the test for a prose reason. That is the false alarm ADR-014 §3 says gets a guard deleted, and it would have been deleted for being right about nothing. Now walks the AST and looks at `Import` / `ImportFrom` targets only. Re-mutation-proven, both directions: a real `from views_postprocessing.unfao import product` -> caught a comment naming views_postprocessing.unfao -> ignored Suite: 470 passed, 3 skipped, 37 xfailed. ruff clean. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…e-review high
Six findings. The first two are the same class this file's own docstring says
defeated the previous regex — reintroduced by me while "tightening" a substring
scan into an AST one.
1. MEDIUM — `and not node.level` skipped every relative import, so
`from ...unfao import product` in a manager passed. Lines 155-158 of this
module name `from ..contract import gaul_schema` as precisely the miss that
made the regex insufficient, and `from ..crafd import product` is the form
once used to demonstrate a real gap in `contract/enrichment.py`. `level` is
now resolved against the file's own package.
Note on the review's example: `from ..unfao import product` inside
`crafd/managers/` resolves to `views_postprocessing.crafd.unfao`, which is not
the sibling — so ignoring it is correct. From `crafd/product.py` the same
statement does reach the sibling, and is caught. The resolution is
depth-correct, verified at both depths.
2. MEDIUM — `from views_postprocessing import unfao` was invisible: the sibling's
name is on the alias, not the module. Each alias is now joined onto the
resolved prefix.
3. MEDIUM — the scan read only `managers/<partner>.py`, while the subprocess half
drops the whole `managers` package. `managers/__init__.py` carries a real
import today and was covered by neither. Now globs `managers/**/*.py`, which
also removes a FileNotFoundError traceback for a manager not named after its
partner.
4. LOW — `".managers" not in m` is a substring test: a module named
`managers_shared.py` would be dropped from the subprocess half while sitting
outside the AST half, exempt from the guard with no signal. Matches on the
package segment now.
5. LOW — `not m.endswith(".__init__")` was dead; `_modules_on_disk` already
filters those. Removed, with a note saying where inits are covered instead.
6. LOW — the fixture README insertion split the sentence it interrupted, leaving
the pyarrow explanation stranded after a views_frames digression. Restored,
new material in its own paragraph.
Mutation-proven against the real files, every form: 3-dot relative, the package
form, the dotted import, and a sibling import in `managers/__init__.py` — each
fails; an innocent `contract` import and a comment naming the sibling do not.
Suite: 470 passed, 3 skipped, 37 xfailed. ruff clean.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
No source changes. The register, plus two xfail stubs from the release-readiness audit. THREE NEW ENTRIES. C-110 (Tier 3) — the release path will block itself from 2026-10-18 and nothing tells the person it blocks. Measured against the live ruleset: protect_main is active, `test` is a REQUIRED check, bypass_actors is empty. The expiry tripwire added on 2026-08-19 reddens that check 30 days before 2026-11-17, so no PR merges and no release can be tagged. The escape is real and was verified — a PR setting ACKNOWLEDGED_UNTIL is green on its own branch — which is why it is Tier 3 rather than C-86's Tier 2. This entry exists because the interaction falls between two records and is in neither: C-84 never mentions the release path, C-86 never mentions the tripwire. The ruleset was not queried when the tripwire was added. C-111 (Tier 3) — a release can change whether a delivery fails, and the version number is the only thing that says so. Three exception types can now escape into a launcher, and DeliveryNotFindableError fails a delivery that previously succeeded silently. That is C-94 working as designed. There is no changelog, so views-models would take it with no notice. C-112 (Tier 3) — nothing here can see what production actually runs. Measured: both launchers pin 1.1.0; the newest tag is 1.1.1, eight days old, and 1.1.0 carries the C-99 fail-open that killed the first CRAF'd delivery. Meanwhile main holds 29 unreleased commits. The obvious fix — check the launchers' pin in CI — would add a fifth repo that can redden this build, which is C-86 with no bypass actors, so it is recorded as a decision rather than proposed. FROM review-rr TRIAGE AND STRATEGIC. - Cluster I closed: its own prescription landed. It asked for a test_register_integrity.py checking header counts, no RESOLVED under Open, and reference resolution. That file exists, does exactly those, and caught two real errors this week. The residual is recorded: the guard covers entries, not the cluster section — which is how Cluster I sat fully resolved and unmarked. - Cluster J: dropped a stale "(acute)" from a resolved C-22. - C-109 cross-linked to C-95, its nearest sibling. - C-81 re-measured. Its headline said CI verifies 8 fewer tests than local; it is 2 (468/5 skipped vs 470/3). PR #280 moved four checks into the gate four days ago and the entry — whose whole job is measuring that distance — kept quoting the old number. Trigger rewritten to the gap that remains: the checks needing views-datafactory artifacts that are not in its git repository. - Cluster N added, "This repository cannot see itself": C-109, C-107, C-95, C-110, C-111, C-112 and C-81. Six arrived in one sprint, each filed as unrelated. Its fix strategy is deliberately NOT more guards — C-109 records why the mechanical version fails; four of the seven were found by reading. - C-106 tagged [backlog]. THE STUBS. tests/test_falsification_release_readiness.py holds the two soft falsifications as xfail(strict=True), so the suite colour is unchanged. They are xfail rather than plain failures deliberately: `test` is a required check with zero bypass actors, so committing red tests would block their own fix — which is C-110. Register: 109 -> 112 total, 29 -> 32 open. Integrity guards green (40 tests), ruff clean, suite unchanged at the C-104 baseline. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…nd-falsification-stubs Register: C-110–C-112, Cluster N, and the release-readiness stubs
Running `poetry install` took the suite from 26 failed/470 passed to 6 failed/490 passed (pipeline-core 2.3.0 -> 3.0.1), then died building levenshtein 0.20.9 from source. What remains is one drifted package, pyarrow 23.0.1 against 16.1.0 locked, and all six failures are that cause: five C-72 byte-parity checks plus the lock check reporting it. Wheel availability, measured against the index: pyarrow 16.1.0 and levenshtein 0.20.9 both ship cp311; levenshtein stops at cp311, pyarrow at cp312. levenshtein is capped >=0.20,<0.21 by ingester3, so the ceiling is upstream. poetry.lock installs on cp311 alone. CI runs 3.11 and the delivery's conda prefix is 3.11.15 with pyarrow 16.1.0 — both match the lock. Only the developer venv (3.13.7) is off, and the command everyone is told to run cannot bring it back. That makes the entry's own mitigation half wrong in the way it names as a failure: test_locked_environment rules out printing a remedy that cannot work, and on 3.12+ its "Run `poetry install`" line does exactly that. Trigger and Location updated; what closes the entry is now a rebuild on 3.11, not a reinstall. The second half — pyproject declaring >=3.11,<3.15 when only 3.11 resolves — is filed as #295, being a declaration defect rather than an environment one. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…n-311-only docs(register): C-104 — the lock installs on cp311 only
CRAF'd received July 2026 as history. #297 established the month was real but ~1% reported — six cells of 64,742, zero in `ged_sb` but non-zero in `ged_ns` and `ged_os` — so the producer's inferred boundary declared it observed and the clip kept it, correctly, by its own contract. Establishing that took a day, because the artifact could not answer the question a partner asks afterwards: observed through when, and decided against what? The boundary was recoverable only because those six cells happened to land in the two columns that leave a trace. In `ged_sb` the data would have been mute. `build_provenance` now requires `observed_through` and always emits it. Required rather than optional because a caller that forgets it is the failure being fixed; always emitted because an absent key is indistinguishable from an artifact built before the field existed. An explicit null carries the degrade-open case — the boundary could not be read, so this delivery was NOT clipped — which is the case that most needs recording and exactly the one an omit-when-absent field drops. A third state is kept distinct: `UNREAD`, the managers' initial value, refuses at build time. Letting it collapse to null would report "clip skipped" for a run whose clip in fact ran — the C-103 conflation, one layer down. `observed_through` joins the essential set in `compact_description`, or the 255-char fallback would drop it precisely when descriptions are long, and joins the redaction guard's declared keyset deliberately: a month_id integer, no PII, no credential, and what the partner needs to tell a sparse month from a fabricated one. Both partners, not just CRAF'd. The managers are deliberate clones and the UN-FAO side has the external partner; a fix in one is half a fix. A test asserts they do not diverge. Deferred with a trigger: the producer also publishes a per-source `last_valid_month_ids` map. `datafactory_query.defaults` exposes only the scalar, and a multi-source map would not fit the 255-char carrier. When C-15's structured metadata field lands upstream and the ceiling goes, stamp the map too. 12 tests: the rule on primitives, and the wiring as declaration checks. One asserts the assignment precedes the degrade-open early return — placed after it, the path that most needs recording would raise at provenance time instead. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ange-boundary feat(delivery): stamp the observed-range boundary into provenance (#297)
Bumps 1.1.1 -> 1.2.0. MINOR rather than PATCH: the delta adds a new module (delivery/findability.py), three exception types, a changed return value on store_port.upload(), and a required keyword on build_provenance. Adds CHANGELOG.md, which this repository has never had. C-111's trigger was "the next version is cut — write what changed for a consumer, or record why the number alone is enough", and this is that release. The entry leads with the three exceptions that can now escape into a launcher, because the sharp one is C-94: a delivery whose artifacts land where the consumer cannot see them previously reported success and now raises. That is the intended behaviour and still a change views-models would otherwise learn about only as an integer. Venue chosen deliberately: the root CHANGELOG.md rather than the GitHub release body, so a consumer reading the source tree at a pinned version sees it without leaving the checkout. C-111 left the venue open; this is the choice, recorded. Two falsification probes asserted this gap and both retire here — S2 from the 2026-08-21 audit and H1 from the 2026-08-25 one. They are one finding located twice, because the second audit designed probes without reading the first's stubs. Noted in the test module rather than the register: it is a lesson about the audit procedure, not about the concern. Three probes stay open and xfail: the expiry tripwire blocking releases from 2026-10-18 (S1/C-110), tag-push not publishing (S3), and the publish job shipping without running tests (S4). None blocks this release; all three are about the release path rather than the package. Register: C-111 moved to Resolved, counts 32->31 open, 80->81 resolved, verified by tests/test_register_integrity.py. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
release: 1.2.0 — and tell consumers what changed about failing (C-111)
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.
Promotes
developmenttomainfor the 1.2.0 release.What ships
ProducerClientUnavailable— a broken producer client refuses rather than degrading openTornRunError— a torn upload names every object it left behindobserved_throughstamped into delivery provenance, in both partnersCHANGELOG.md, first one this repo has hadConsumer impact
Three exception types can now escape into a launcher, all deliberate, each replacing a silent failure with a loud one.
CHANGELOG.mdstates them; nothing in a launcher needs changing. If one fires after upgrading, it is reporting a pre-existing problem rather than creating a new one.Release audit
/falsifyon the readiness claim returned FALSIFIED on one hard finding — no consumer-facing notes — which this PR fixes. Two soft findings remain open and are about the release path, not the package:release: published. Tags1.0.0and1.1.0exist with no Release and were never published this way. The GitHub Release is the publishing step.Also noted: from 2026-10-18 the credential-expiry tripwire reddens the required
testcheck and no release can be cut untilACKNOWLEDGED_UNTILis set (C-110). 53 days out; does not affect this release.Verification
507 passed, 40 xfailed, 3 skipped. ruff clean. Register integrity green.
🤖 Generated with Claude Code