Repository navigation
release: handle-upstream dispatches publish after it has tagged - #148
luisleo526 wants to merge 1 commit into
Conversation
handle-upstream now starts publish.yml itself, as the last normal step of its job in the bump, retag and republish modes, once the tag push has succeeded, with a second App token limited to Actions write on this repository and the tag taken from the job's own step output. A retag or republish of a version older than the latest ready release does not dispatch (it says why and succeeds), and a failed dispatch fails the job so the existing producer incident fires. Reconcile also reports a hub tag that has neither a candidate nor an image after a 30 minute grace period, through the existing incident, and never dispatches. The dry-run harness models the dispatch, the operator notices and docs say it, and the tests pin all of it. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
Graphify reviewed this change.
No issues found by static checks — no coupling regressions or blocking issues in the code graph. That is not the same as safe to merge: see what was not checked.
Not checked: tests were not run; formal verification proved 0 of 7 changed function(s) (7 not verified).
Formal verification. PR-changed functions: 0/7 verified (0 proven, 0 may-equivalent, 0 distinguished) · 7 not verified (4 vacuous, 3 unsupported).
Not verified on this run: main (vacuous: never exercised), raw (unsupported), reconcile (vacuous: never exercised), \_\_init\_\_ (unsupported), run (unsupported), main (vacuous: never exercised), gh (vacuous: never exercised).
Graphify review — findings
Automates publication after a hub tag is pushed: handle-upstream.yml now dispatches publish.yml on main with its own tag output, instead of leaving it to an operator command. Republish mode now pushes the existing tag idempotently, so a missing tag fails the job and fires the existing producer alert. A new publish-policy step in scripts/release_pipeline.py decides whether dispatch is allowed. Recovery for a version older than the latest ready release is skipped with a notice and the manual gh workflow run command. If the dispatch itself fails, the job errors and prints the same manual fallback. Dispatch runs with a separately minted hub App token scoped to actions: write. release-reconcile.yml now also reports tagged publications that have stalled past a grace period. The README, release-chain docs and tests are updated to match, including new coverage for the release-binding, pairing and workflow guard behaviour (PEP 440 prerelease normalization, fail-closed tag handling, delivery retry back-off).
Review partial — this diff was larger than one review pass covers, so later files were not reviewed; some findings may be missing.
Analysis details — impact, health, verification
Impact & health
Graphify review
Impact — 893 functions depend on the 840 functions this change touches.
Health — this change adds coupling hotspots:
- worse:
main()— 1 callers, 30 callees - worse:
Registry— 5 callers, 3 callees - worse:
reconcile()— 1 callers, 8 callees
Verification — 893 functions in the blast radius were not formally verified this run (proofs are advisory here).
Gate & verification
graphify gate
PASS — objectively clean (no health regressions, tests not run — proofs not run this pass (advisory)). Grounded, not self-assessed.
Advisory (not blocking):
- verification_scope: 881 function(s) in the blast radius were not formally verified this run
Docs that may be stale (advisory)
facts/README.md§ PineForge facts tokens (lines 1-19): references changed symbolsreleasesfacts/README.md§ Trades compared with TradingView (lines 20-33): references changed symbolsreleasesfacts/README.md§ Public repository descriptions (lines 74-114): references changed symbolsreleasesfacts/README.md§ Stage 1 validation and descriptions reconciliation (lines 115-187): references changed symbolsreadfacts/README.md§ TOP setup and migration checklist (lines 188-253): references changed symbolsrunfacts/README.md§ Stage 2 hub dispatch signal (lines 302-785): references changed symbolsread
Formal verification
Could not verify: Could not verify main.
The verifier did not have enough to check main, so it is saying so rather than guessing. No false assurance is the whole point.
Guarantee: No guarantee either way, this is an honest abstention, not a pass.
Note: Reason: not verifiable: all 1 sampled inputs raised on both versions — the function never executed, so 'no divergence' would be vacuous (mostly NameError — names the real obstacle, not a sampling gap)
Could not verify: Could not verify raw.
The verifier did not have enough to check raw, so it is saying so rather than guessing. No false assurance is the whole point.
Guarantee: No guarantee either way, this is an honest abstention, not a pass.
Note: Reason: method — first parameter is `self`, which needs a constructed instance (not synthesizable)
Could not verify: Could not verify reconcile.
The verifier did not have enough to check reconcile, so it is saying so rather than guessing. No false assurance is the whole point.
Guarantee: No guarantee either way, this is an honest abstention, not a pass.
Note: Reason: not verifiable: all 254 sampled inputs raised on both versions — the function never executed, so 'no divergence' would be vacuous (mostly NameError — names the real obstacle, not a sampling gap)
Could not verify: Could not verify \_\_init\_\_.
The verifier did not have enough to check \_\_init\_\_, so it is saying so rather than guessing. No false assurance is the whole point.
Guarantee: No guarantee either way, this is an honest abstention, not a pass.
Note: Reason: method — first parameter is `self`, which needs a constructed instance (not synthesizable)
Could not verify: Could not verify run.
The verifier did not have enough to check run, so it is saying so rather than guessing. No false assurance is the whole point.
Guarantee: No guarantee either way, this is an honest abstention, not a pass.
Note: Reason: method — first parameter is `self`, which needs a constructed instance (not synthesizable)
Could not verify: Could not verify main.
The verifier did not have enough to check main, so it is saying so rather than guessing. No false assurance is the whole point.
Guarantee: No guarantee either way, this is an honest abstention, not a pass.
Note: Reason: not verifiable: all 64 sampled inputs raised on both versions — the function never executed, so 'no divergence' would be vacuous (mostly NameError — names the real obstacle, not a sampling gap)
Could not verify: Could not verify gh.
The verifier did not have enough to check gh, so it is saying so rather than guessing. No false assurance is the whole point.
Guarantee: No guarantee either way, this is an honest abstention, not a pass.
Note: Reason: not verifiable: all 69 sampled inputs raised on both versions — the function never executed, so 'no divergence' would be vacuous (mostly TypeError — names the real obstacle, not a sampling gap)
· 1 grounded finding(s) anchored inline below; 2 more finding(s) on lines outside this diff (see the check run).
| @@ -878,7 +895,12 @@ def run_link(): | |||
|
|
|||
|
|
|||
| def reconcile(client, store): | |||
There was a problem hiding this comment.
reconcile()
fans out to 8 callees (efferent coupling).
Grounded coupling-delta finding (deterministic), not an LLM guess.
WHAT: handle-upstream dispatches publish.yml on main as the last normal step of its job in bump (incl. retag) and republish, after the tag push, with a second App token limited to Actions write on this one repository; the tag comes only from the job's own step outputs; recovery (retag/republish) of a version OLDER than the latest ready release does not dispatch and says why (manual command kept), equal is allowed (its image is what recovery restores); a failed dispatch fails the job so the producer incident fires; republish now also pushes the existing tag (a missing tag fails on purpose). reconcile reports a hub tag with no candidate and no image after a 30-minute grace period (annotated tagger date) through the existing incident and never dispatches. BEHAVIOUR CHANGE OF AN EXISTING FUNCTION: Registry.manifest now raises Invalid when the ghcr token endpoint is unreadable (it used to propagate Pending, which Registry.digest read as "image absent"); pinned by test_registry_token_error_is_not_an_absent_manifest; no caller caught Pending around it. TOKEN BLAST RADIUS: Actions write on this repository can dispatch, cancel or re-run ANY hub workflow; the control is the pinned step list (mint immediately followed by the dispatch step, failure reporter last), frozen by tests/test_release_workflows.py. publish.yml's
concurrency: release-candidate-<tag>(cancel-in-progress false) makes a second dispatch for the same tag queue instead of racing. publish.yml itself verifies (so "a tag exists" is not the control): main-ref dispatch, tag pattern, a tag that peels to a commit reachable from main, tag == VERSION, pins from the annotated tag message, the pairing rule. TESTED: spark2 arm64 differential base vs patched (above), README pins (494 lines, digest of 215-244) intact; the hub's own python-test CI on the PR is the final proof. OPEN (not done by this PR): App Actions write on the hub installation (prerequisite), the release-automation environment's reviewers (a required reviewer turns each dispatch into a click), whether the human dispatch of STABLE releases is an intentional public-image gate (owner decision), and activation of the hub workflows that were disabled_manually (reconcile/finalize/receipts/facts: the reconcile incident is inactive until then).🤖 Generated with Claude Code