Skip to content

Release readiness: the residual is a GitHub release object + PyPI, gated on the rename (#96) #125

Closed

Description

@Polichinel

Release runbook: views-postprocessing FIRST release (publish-last; executes ONLY on Simon's platform signal)

Investigated and rewritten 2026-07-28 per this issue's original mandate (original text preserved at bottom). Every fact below verified in-repo or against live PyPI/remotes on that date.

1. What "release" means here — three things at once

This repo is both a package and a deployed-style service: it ships as the poetry package views-postprocessing (name free on PyPI — never published), and it runs in production as an editable checkout imported by views-models' postprocessors/un_fao/main.py (verified: the runtime env's install is Editable project location: …/views-postprocessing). The first release is therefore:

  1. Git: merge development→main, tag, GitHub release (notes below).
  2. PyPI: publish via the existing publish_package.yml (triggers on GitHub release; token secret must exist — verify before the signal).
  3. Operations unchanged: the production consumer stays on the editable checkout; PyPI existence is for reproducibility/pinning, not a deployment change.

2. Distance & divergence — clean straight merge, with one deliberate deletion riding it

development is 197 ahead / 0 behind main (grew from the issue's 187). Zero divergence → straight merge. Notable: the merge DELETES the LFS-tracked GAUL_2024 shapefiles + .gitattributes that only main still carries (development replaced them with the 888 KB precomputed ADR-011 lookup in June). After release, no LFS assets exist at HEAD — the "LFS precedent" cited from views-reporting C-23 describes pre-merge main and should be corrected there (history still holds the blobs; clone size note only). Fresh installers get a plain 888 KB parquet.

3. Version proposal: 1.0.0

Never tagged; pyproject says 0.1.0. Proposal: 1.0.0 — this repo owns an adopted, falsification-audited cross-repo contract (ADR-013), and its delivery machine shipped the first real production run (run-0, 2026-07-27, verified in the live store). Matches views-faoapi's v1.0.0-at-production-readiness convention. Breaking-change note for the release: none for external consumers (there were no prior releases); for the one operational consumer (views-models), the wire_contract/wire_upload_enabled launch keys are additive.

4. Dependency pins & release order — the coordination core

Current pins (pyproject): views-pipeline-core >=2.1.3,<3.0.0 · views-frames >=1.0,<2 · pyarrow >=16.1.0,<17 (first-class pin since the fixture re-baseline).

The trap this investigation caught: pipeline-core's PyPI latest is 2.3.0, which satisfies our written pin — but our runtime floor is HIGHER than anything released: we consume get_feature_frame / declared_data_format (#126) and interoperate with sampled_forecast_publisher (#269/#276), all unreleased. A PyPI-only install under today's pin would import-error at runtime. Action at signal: bump the pipeline-core pin to >=<their next release> before tagging.

Order: views-frames (released ✓) → views-pipeline-core (their next release) → views-postprocessing → (views-models, if it releases — it consumes us). views-reporting is order-independent of us (no dependency either way; verified pyproject).

Undeclared soft dependency, declared here for honesty: datafactory_query is imported lazily by source_metadata.py with degrade-open semantics — deliberately NOT a pyproject dep (works without it; clip skipped with a warning). Release notes should state this.

5. Splash zone

  • views-models — the only importer (postprocessors/un_fao/main.py; the reconciliation import was retired to views-frames in June). Editable checkout; nothing breaks; they may pin the PyPI version afterwards.
  • views-faoapi — consumes our artifacts (bucket), never our package. No impact.
  • Org code search: no other views_postprocessing importers.

6. Repo-specific gates (all verifiable before the signal)

7. Cross-repo stances & checks

  • Country-coding (Decision needed: GAUL country coding — Kosovo/disputed territories + who owns the GAUL↔VIEWS crosswalk #123 / views-datafactory#341): this repo's released stance = the ADR-011 GAUL-derived lookup (FAO's own atlas; 202 clean iso3 codes verified; the 76 GAUL-uncovered cells excluded by declared rule). The M49 add is scoped and deliberately deferred (falsified as a quick add: the Taiwan coding decision + a 4-stop declared chain — see the 2026-07-25 falsification). Release does NOT wait, mirroring views-reporting's declared-interim approach.
  • views-reporting#179: their C-23 LFS note cites our pre-merge main; after our merge it's stale — comment to be posted at signal time.
  • Siblings get one-line cross-links at signal time (pipeline-core, views-models prep issues).

Execution checklist (NOTHING runs before Simon's signal)

  • Confirm pipeline-core's release version; bump our pin; commit on development
  • Verify PYPI_TOKEN secret exists
  • Merge development→main (straight; LFS deletion rides along); CI green on main
  • Bump pyproject to the agreed version (proposal: 1.0.0); tag v1.0.0; GitHub release with the notes arc above
  • Confirm publish_package.yml published to PyPI; pip install views-postprocessing smoke-imports
  • ADR-013 post-adoption entry + register notes; correct views-reporting C-23; cross-link siblings

Original issue text (preserved per mandate)

Why this issue exists (cross-repo context — read first)

The VIEWS platform develops on development branches under a publish-last rule: no tags, version bumps, or releases in any platform repo until Simon gives the platform-wide publish signal, and then all repos release together (views-frames is the already-released exception). That signal is coming; each repo needs its own release runbook ready BEFORE it fires.

What views-reporting will do (its release issue, views-platform/views-reporting#179, is already a full runbook): on the signal — merge development→main (149 commits), bump 0.2.0→0.3.0, tag, GitHub release, publish to PyPI. Notable contents: the breaking EvaluationReportTemplate.generate(source=, target=) inversion (MetricFrame EvaluationSource, their #173); the global-PGM map product (ADR-021: global metadata bundle via a declared-interim GAUL→ISO3→VIEWS crosswalk — register C-211 there; horizon-step maps; MAP/P(any)/HDI layers); S=1000 memory feasibility; ~37 MB offline reports. The interim crosswalk is declared, not hidden — their release does NOT wait for the platform country-coding decision (views-datafactory#341 / views-postprocessing#123).

This issue is deliberately NOT a finished runbook. It records what is knowable from outside this repo (below) and asks the agent/maintainer working IN this repo to investigate and then rewrite this issue into the repo's own release runbook — the way views-reporting#179 was rewritten.

What is knowable from outside this repo (verify all of it)

  • NOT on PyPI and no tags exist at all — this repo has never released. Decide what its first release IS (PyPI package? tag-only? none — maybe it's deploy-style?) and what version to start at.
  • development is 187 ahead / 0 behind main — a clean straight-merge candidate, pending your investigation.
  • Known cross-repo threads touching you: you carry the PRIO-GRID shapefile via git-lfs (views-reporting's register C-23 cites you as the LFS precedent — a release should confirm LFS assets resolve for installers/cloners); and you co-own the open platform country-coding decision (your Decision needed: GAUL country coding — Kosovo/disputed territories + who owns the GAUL↔VIEWS crosswalk #123 / views-datafactory#341 — Kosovo/GAUL). That decision does NOT block anyone's release (views-reporting ships the declared interim), but your release notes should state your repo's current stance/state on it.
  • Investigate your dependency pins on views-pipeline-core / views-reporting / views-frames and place yourself in the release order accordingly.

Your task: investigate, then turn THIS issue into this repo's release runbook

Answer at least the following, in this issue (rewrite the body; preserve this original text at the bottom):

  1. What does "release" even mean here? PyPI package, GitHub release/tag only, a pinned environment, a deployed artifact — or several of these?
  2. Distance + divergence: what exactly is in development that main lacks (and vice versa — see the facts below); is main releasable after a straight merge, or is there drift to reconcile first?
  3. Version proposal + breaking changes: last released/tagged version vs today's content; what breaks for consumers; what semver/0.x convention applies here.
  4. Dependency pins & ordering (the coordination core): which platform packages does this repo depend on (views-pipeline-core, views-reporting, views-frames, datafactory, …), at what pins today (dev-installs? git refs? released versions?), what must those pins become at release, and therefore WHERE in the platform release order this repo sits (who must publish before you; who waits for you).
  5. Splash zone / consumers: who consumes THIS repo's released artifact (other platform repos, CI pipelines, partner installs, model runs) and what breaks for them; run an org code search if unsure.
  6. Repo-specific gates: CI green on main after the merge; any data/asset questions (large files, LFS, bundled data); any register/ADR/CIC updates your governance requires around a release; release-notes source (git log <last-tag>..main).
  7. Cross-repo checks: read Release views-reporting with the MetricFrame EvaluationSource (#173) — publish-last for pipeline-core #224 views-reporting#179 for the template and the ordering constraints already known; comment there (and on the sibling issues listed below) if your investigation contradicts anything.

Hard rule: nothing executes until Simon's platform-wide publish signal. Investigating and rewriting this issue is in scope now; merging/tagging/publishing is not.

Siblings: views-platform/views-reporting#179 (the template), views-pipeline-core and views-models carry the same prep issue (cross-linked in comments).

Activity

  1. Polichinel commented on Jul 27, 2026

    @Polichinel
    CollaboratorAuthor
  2. Polichinel commented on Jul 27, 2026

    @Polichinel
    CollaboratorAuthor

    Data point from the pipeline-core release investigation (views-platform/views-pipeline-core#313): your pyproject pins views-pipeline-core = ">=2.1.3,<3.0.0" — the platform release publishes pipeline-core 3.0.0 (large breaking jump: ADR-054 extraction complete, PredictionFrame is the views-frames leaf, shims retired). Your runbook needs an explicit bump-or-stay decision: PyPI 2.3.0 keeps serving your current cap unchanged, so staying is safe short-term; bumping means absorbing the 3.0.0 breaking set.

  3. Polichinel commented on Aug 4, 2026

    @Polichinel
    CollaboratorAuthor

    Correction to this issue's premise: a tag does exist. The title says "no tags exist" — git tag -l returns 1.0.0, and pyproject.toml has read version = "1.0.0" on both branches throughout.

    What is actually true, and it is a different shape:

    • one tag, 1.0.0, and main is now ~28 commits past it — so the tag and main describe materially different trees
    • no GitHub release — gh release list is empty, which matters because publish_package.yml fires on release: published only. Nothing has ever published
    • not on PyPI — HTTP 404
    • no consumer pins a version. Checked every sibling: none declares a dependency on this package. The only consumer installs it as pip install git+https://…@main from views-models/postprocessors/un_fao/run.sh

    That last point is the one worth acting on, and it reframes this issue. main is already the release channel — just an unversioned one that nobody can pin. views-models#294 records what that cost once: on 2026-07-31 @main was 208 commits behind, carried no wire modules, and would have run green while delivering the legacy artifact.

    So the cheapest thing that gets what this issue wants is not a PyPI publish — it is pinning that launcher to a tag instead of @main. Reversible, no name to squat, and it makes the version number mean something for the one consumer that exists.

    On publishing specifically: worth doing after #96 (the rename), not before. PyPI names are permanent — you can yank a release but you cannot free a name — so publishing views-postprocessing would permanently claim the name #96 has already decided is wrong, and create exactly the dual-publish window #96 defers the rename to avoid.

  4. Polichinel commented on Aug 8, 2026

    @Polichinel
    CollaboratorAuthor

    Correction: this issue's premise is out of date. A tag does exist.

    The title and body say "no tags exist". That was true when this was filed; it is not true now.

    Measured 2026-08-08:

    tag 1.0.0
    reachable from main yes
    commits from 1.0.0 to main 37
    commits from main to development 18

    Why the gap matters more than the correction. Those 37 commits include the last week's delivery-path fixes — the consumer-document-name fix on the historical upload (C-77), the store port's fail-closed refusal (C-79), the queryset import-vs-format distinction (C-83), the Appwrite Seam Contract re-pin to registry v1.4.4, and the C-40 store-construction extraction.

    So 1.0.0 is not a usable pin today. Anything pinning to it — including views-models#364, which asks un_fao/run.sh to stop installing from @main — would take production backwards past all of those.

    What this changes about this issue: "investigate the first release" is done; the first release happened. The live question is narrower and more urgent — cut a second tag at current main, so that consumers have something current to pin to. That is the one operator action blocking views-models#364.

    Unchanged and still open here: the LFS asset question, the coding-decision stance, and the ordering constraint that #96's rename must precede any PyPI publish (package names are permanent; the current name has already been judged wrong).

  5. changed the title [-]Release readiness (publish-last): investigate this repo's FIRST release — no tags exist; LFS assets; coding-decision stance[/-] [+]Release readiness: the residual is a GitHub release object + PyPI, gated on the rename (#96)[/+] on Aug 13, 2026
  6. Polichinel commented on Aug 13, 2026

    @Polichinel
    CollaboratorAuthor

    Retitled 2026-08-13. The title said "no tags exist"; two do, and half this runbook is done.

    step state
    Git tag DONE — 1.0.0 (2026-08-01) and 1.1.0 (2026-08-13, at 1e21d72), both annotated and pushed
    pyproject version agrees DONE, and now guarded — tests/test_release_version.py fails if a tag and the declared version ever disagree. It was written because they did: 1.1.0 was first cut at a commit still declaring 1.0.0.
    Operational consumer pins a tag DONE and closed elsewhere — views-models#364 CLOSED; postprocessors/un_fao/run.sh pins VIEWS_POSTPROCESSING_PIN="1.1.0"
    GitHub release object not done — gh release list is empty, so publish_package.yml (which fires on release: published) has never run
    PyPI not done — curl https://pypi.org/pypi/views-postprocessing/json returns 404

    §2's LFS narrative is spent: no .gitattributes is tracked and there are no LFS assets at HEAD, exactly as the section predicted.

    What remains, and why it is deliberately not done

    Publishing is sequenced behind #96 (the rename), per this issue's own 2026-08-04 comment: publishing now would permanently squat a package name already judged a misnomer. That is a coherent position and it was not written into the checklist at the top — it is now.

    So the residual is two steps, both gated on a naming decision that is Simon's:

    1. cut a GitHub release object at a tag
    2. publish to PyPI, with PYPI_TOKEN verified first (still unverified)

    One thing to add when that happens: no ADR-013 post-adoption record exists for either tag.

  7. Polichinel commented on Aug 14, 2026

    @Polichinel
    CollaboratorAuthor

    Release rule, learned by nearly breaking it (2026-08-13).

    The pre-release sprint's plan said cut 1.2.0 after merging the truth pass to main. Comparing the trees first showed that would have been a lie:

    views_postprocessing @1.1.0 == @development   244bf7e08b68f2eeb2925cbb05a0dd5ea9a78485
    pyproject.toml       @1.1.0 == @development   043be9deab6764bbf3b37a9a4baa7678cf14a561
    

    Five commits, all tests/ docs/ reports/ poetry.lock. A consumer resolves nothing different — pyproject.toml's constraints never moved, and a library's lockfile is not what consumers resolve, so even the views-pipeline-core 3.0.1 bump is invisible downstream.

    The rule: a version tag requires a change under views_postprocessing/ or to pyproject.toml. Compare the tree hashes; do not infer it from the commit count.

    tests/test_release_version.py does not enforce this and deliberately still does not. It checks that a tag matches pyproject.toml's version and that no tag runs ahead of it — so bumping pyproject.toml to 1.2.0 and tagging would have satisfied it completely. Closing that hole means a test that reads git history to diff two tags' trees, and this repo has twice now registered guards that read the wrong thing through git plumbing (C-91, C-98). The rule is one command, it applies at exactly one moment, and that moment already has a human in it.

    So it lives here, in the release-readiness issue, rather than in a guard that would cost more than the mistake.

    main is merged and in sync with development as of ebdfe17. No tag was cut. The next one is earned by the first change under views_postprocessing/ — which, per this issue, is gated on the #96 rename anyway.

  8. Polichinel commented on Aug 15, 2026

    @Polichinel
    CollaboratorAuthor

    Done. This issue's residual — a GitHub release object plus PyPI — landed on 2026-08-15 as 1.1.1.

    • Tag 1.1.1, annotated, on main at 3be7561
    • GitHub Release published on that tag
    • publish_package.yml fired and succeeded; views-postprocessing is now on PyPI (HTTP 200, both wheel and sdist)
    • Smoke-verified: pip install views-postprocessing==1.1.1 into a clean venv resolves, reports 1.1.1, ships all five subpackages and the 902,187-byte GAUL lookup with a sha256 identical to the repo's copy

    Rehearsed on TestPyPI first, which is worth recording as the practice: the wheel was built and inspected locally, uploaded to TestPyPI, and installed from there before anything touched real PyPI.

    The gate this issue carried, and what happened to it

    This issue gated PyPI on the #96 rename — the reasoning being that publishing under views-postprocessing permanently commits that name. That was raised at the decision point and the operator chose to publish anyway. #96 is not blocked by it: views-delivery is still free on PyPI, verified the same day. The consequence is that a future rename means a second package name, with the older one occupied and stale rather than reusable.

    The order, recorded because it was got wrong once

    Bump → Merge → Tag → Release. The version goes into pyproject.toml before the tag goes onto the commit; a tag is a label, a GitHub Release is a shipment (it fires the publish workflow).

    1.1.0 was cut in the wrong order — tagged at main while pyproject.toml still said 1.0.0 — and had to be re-pointed. tests/test_release_version.py exists because of that, and as of #275 it finally runs in CI: it had never executed anywhere but a maintainer's laptop, because CI fetched no tags. See register C-102.

    Remaining release-adjacent work

    Nothing else here is outstanding. Closing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions