Release safety net: push-triggered convergence + drift reconciliation (closes #192) #206

Merged
conductor merged 22 commits from release-safety-net into master 2026-09-08 01:03:16 +01:00
22 Commits
Author SHA1 Message Date
Nick 3a18534516 merge master (resolve: master's auto-version evolution wins — semver extraction is the newer reviewed state; #192 convergence machinery on this branch supersedes via its own PR review)
CI Pipeline / best-practices (push) Successful in 2s
CI Pipeline / documentation (push) Successful in 3s
CI Pipeline / canary (push) Successful in 26s
CI Pipeline / lint (push) Successful in 30s
CI Pipeline / unit-tests (push) Successful in 36s
CI Pipeline / gitea-offline (push) Successful in 13s
CI Pipeline / type-check (push) Successful in 23s
CI Pipeline / required-checks (push) Skipped
CI Pipeline / best-practices (pull_request) Successful in 3s
CI Pipeline / documentation (pull_request) Successful in 2s
CI Pipeline / lint (pull_request) Successful in 26s
CI Pipeline / canary (pull_request) Successful in 24s
CI Pipeline / gitea-offline (pull_request) Successful in 13s
CI Pipeline / unit-tests (pull_request) Successful in 34s
CI Pipeline / type-check (pull_request) Successful in 24s
Spec gate (headless) / spec-gate (pull_request) Successful in 8s
CI Pipeline / required-checks (pull_request) Successful in 1s
Auto-version and Release / release (pull_request) Failing after 7s
2026-09-08 00:58:18 +01:00
Nick 299f68886d merge master (behind-base update, round 2)
CI Pipeline / best-practices (push) Successful in 3s
CI Pipeline / documentation (push) Successful in 3s
CI Pipeline / lint (push) Successful in 24s
CI Pipeline / unit-tests (push) Successful in 34s
CI Pipeline / canary (push) Successful in 31s
CI Pipeline / type-check (push) Successful in 23s
CI Pipeline / gitea-offline (push) Successful in 13s
CI Pipeline / required-checks (push) Skipped
CI Pipeline / best-practices (pull_request) Successful in 2s
CI Pipeline / documentation (pull_request) Successful in 2s
CI Pipeline / lint (pull_request) Successful in 24s
CI Pipeline / canary (pull_request) Successful in 26s
CI Pipeline / type-check (pull_request) Successful in 23s
CI Pipeline / unit-tests (pull_request) Successful in 34s
CI Pipeline / gitea-offline (pull_request) Successful in 13s
CI Pipeline / required-checks (pull_request) Successful in 1s
2026-09-07 22:45:36 +01:00
Nick 45ed083f27 merge master (behind-base update for robot drain)
CI Pipeline / best-practices (pull_request) Successful in 3s
CI Pipeline / documentation (pull_request) Successful in 2s
CI Pipeline / lint (pull_request) Successful in 23s
CI Pipeline / unit-tests (pull_request) Successful in 35s
CI Pipeline / canary (pull_request) Successful in 27s
CI Pipeline / best-practices (push) Successful in 4s
CI Pipeline / documentation (push) Successful in 4s
CI Pipeline / gitea-offline (push) Successful in 15s
CI Pipeline / type-check (push) Successful in 23s
CI Pipeline / canary (push) Successful in 29s
CI Pipeline / gitea-offline (pull_request) Successful in 14s
CI Pipeline / type-check (pull_request) Successful in 21s
CI Pipeline / lint (push) Successful in 24s
CI Pipeline / unit-tests (push) Successful in 33s
CI Pipeline / required-checks (push) Skipped
CI Pipeline / required-checks (pull_request) Successful in 1s
2026-09-07 22:34:14 +01:00
Nick bf8a612e94 Merge remote-tracking branch 'gitea/master' into release-safety-net
CI Pipeline / lint (pull_request) Successful in 23s
CI Pipeline / unit-tests (pull_request) Successful in 31s
CI Pipeline / canary (pull_request) Successful in 24s
CI Pipeline / best-practices (pull_request) Successful in 2s
CI Pipeline / documentation (pull_request) Successful in 2s
CI Pipeline / type-check (pull_request) Successful in 20s
CI Pipeline / gitea-offline (pull_request) Successful in 13s
CI Pipeline / required-checks (pull_request) Successful in 0s
CI Pipeline / best-practices (push) Successful in 3s
CI Pipeline / documentation (push) Successful in 2s
CI Pipeline / lint (push) Successful in 37s
CI Pipeline / gitea-offline (push) Successful in 14s
CI Pipeline / canary (push) Failing after 35s
CI Pipeline / type-check (push) Successful in 31s
CI Pipeline / unit-tests (push) Failing after 39s
CI Pipeline / required-checks (push) Skipped
2026-09-07 21:01:14 +01:00
Nick 03f5a76ce4 Merge remote-tracking branch 'gitea/master' into release-safety-net
CI Pipeline / lint (push) Successful in 22s
CI Pipeline / unit-tests (push) Successful in 29s
CI Pipeline / canary (push) Successful in 25s
CI Pipeline / best-practices (push) Successful in 2s
CI Pipeline / documentation (push) Successful in 2s
CI Pipeline / type-check (push) Successful in 20s
CI Pipeline / gitea-offline (push) Successful in 13s
CI Pipeline / required-checks (push) Skipped
CI Pipeline / lint (pull_request) Successful in 47s
CI Pipeline / unit-tests (pull_request) Successful in 34s
CI Pipeline / canary (pull_request) Successful in 1m36s
CI Pipeline / best-practices (pull_request) Successful in 3s
CI Pipeline / documentation (pull_request) Successful in 4s
CI Pipeline / type-check (pull_request) Successful in 58s
CI Pipeline / gitea-offline (pull_request) Successful in 17s
CI Pipeline / required-checks (pull_request) Successful in 0s
2026-09-07 16:07:24 +01:00
Nick 61039c86b1 docs(pipeline): append discharge record for post-quality findings (#192)
Auto-label / priority-gate (pull_request) Skipped
Auto-label / auto-label (pull_request) Successful in 2s
CI Pipeline / lint (pull_request) Successful in 36s
CI Pipeline / unit-tests (pull_request) Successful in 43s
CI Pipeline / canary (pull_request) Successful in 25s
CI Pipeline / best-practices (pull_request) Successful in 2s
CI Pipeline / documentation (pull_request) Successful in 2s
CI Pipeline / type-check (pull_request) Successful in 22s
CI Pipeline / gitea-offline (pull_request) Successful in 13s
CI Pipeline / lint (push) Successful in 25s
CI Pipeline / unit-tests (push) Successful in 32s
CI Pipeline / canary (push) Successful in 24s
CI Pipeline / best-practices (push) Successful in 2s
CI Pipeline / documentation (push) Successful in 2s
CI Pipeline / type-check (push) Successful in 20s
CI Pipeline / gitea-offline (push) Successful in 13s
CI Pipeline / required-checks (pull_request) Successful in 0s
CI Pipeline / required-checks (push) Skipped
2026-09-07 15:46:47 +01:00
Nick 1a05634b9b fix(pipeline): log-and-continue instead of bare except: pass in maintenance.yml (#192) 2026-09-07 15:34:27 +01:00
Nick 73af931fc7 docs(pipeline): correct record — only auto-version.yml was pre-existingly unparseable (#192) 2026-09-07 15:34:27 +01:00
Nick fa29a70ff3 refactor(pipeline): drop pointless f-string on plain AUTO_BUMPED print (#192) 2026-09-07 15:34:27 +01:00
Nick 179893039e fix(pipeline): catch URLError in create_release error path (#192) 2026-09-07 15:26:33 +01:00
Nick 3c9a3f034f fix(pipeline): anchor attribution guard to the in-flight bump version (#192)
Quality-gate finding 7a: bump_branch_in_flight matched ANY chore/auto-bump-*
branch, so one stale historical branch (branches are never cleaned up) stood
the safety net down permanently. The guard now matches exactly
chore/auto-bump-{NEW} — the in-flight version — with new_version=None as the
documented legacy escape hatch; wait_out_closed_path passes NEW_VERSION
through.

Quality-gate finding 7b (docs): release-flow.md claimed the closed path was
'genuinely absent, not merely slow' and that the paths 'can never
double-bump'. Rewritten: attribution is best-effort; a >90s delayed closed
path can race into a double-bump, bounded by tag idempotency (duplicate
release creation is a no-op); stale-branch cleanup is a known limitation.

Red evidence: anchored-match tests failed against the prefix-wide guard.
Green: 12/12.
2026-09-07 15:08:02 +01:00
Nick 82bd50f410 fix(pipeline): send release-drift label as integer IDs, not names (#192)
Quality-gate finding 6: POST /issues 'labels' requires integer IDs in Gitea;
the script sent the label NAME string, so the label never attached. Label
names are now resolved server-side via GET /labels (repo convention per
shared/custodian.py), created when missing, and skipped — issue still files —
when resolution and creation both fail. main() gains an injectable api
client so the flow is testable with a fake API.

Red evidence: 3 fake-client tests failed against the pre-fix code (network
URLError — no injectable seam; name-string payload). Green: 13/13.
2026-09-07 15:00:53 +01:00
Nick b4ddd9d98c fix(pipeline): emit lowercase boolean env exports in auto-version.yml (#192)
Quality-gate finding 5: the convergence step printed json.load(...)['converged']
verbatim — Python True/False (capitalized) — while the safety-net steps
compared env.CONVERGED == 'false' (lowercase string). Both conditionals were
unreachable: the safety net could never bump and the drift advisory could
never fire. The export now wraps the value in str(...).lower().

Adds a static guard to tests/test_workflow_script_contract.py: every boolean
env export (CONVERGED/AUTO_BUMPED/DRIFT) in the parsed workflow run-steps
must apply .lower() or be built from lowercase literals; indirect echo
exports are resolved to their capture line.

Red evidence: guard flagged the json.load export (and its echo) on the
safety-net job. Green: 29/29 contract tests.
2026-09-07 14:52:51 +01:00
Nick 44323ce9b1 fix(pipeline): make the version surface failure fatal in auto_bump_pr (#192)
Spec-gate finding 4: a GET failure on src/rune/__init__.py was skipped like
an optional surface, so a bump PR could silently bump nothing. The version
surface is now fatal (exit 1 before any PR is filed); the other surfaces
stay optional (pattern absent — skip). The surface-edit loop is extracted to
apply_surface_edits() with an api callable seam for testing.

Red evidence: 4 new tests failed (no is_surface_fatal/apply_surface_edits;
abort test found no SystemExit). Green: 10/10.
2026-09-07 14:45:43 +01:00
Nick a7b9639a2e fix(pipeline): report version-behind-tag as drift with an anomaly flag (#192)
Spec-gate finding 3: assemble_release_decision returned converged=True when
master's __version__ was BELOW the latest tag — silent convergence for a
state that is drift (a bump merged without riding a release, or a tag moved
backwards). The decision now returns converged=False, warranted_bump=None
(the net must not act: bumping master cannot converge with a tag already
ahead) and a new 'anomaly': 'version_behind_tag' field (None otherwise).
The maintenance reconciler files the issue as before (DRIFT=true) and the
issue body now flags the anomaly explicitly.

Red evidence: 2 new decision tests failed with converged=True; the
drift-body anomaly-flag test failed; real-repo tag-ahead test added.
Green: 91/91 across convergence, drift-issue, and contract tests.
2026-09-07 14:29:02 +01:00
Nick a6a94c7352 fix(pipeline): classify merge-commit subjects by inner PR title (#192)
Spec-gate finding 2: classify_subject anchored at string start, so Gitea
merge commits ('Merge pull request <feat: x> (#N)') classified as 'other' —
a no-bump feat PR merged via merge-commit was invisible to the safety net
and to maintenance drift attribution.

Subjects matching the merge-commit form are now pre-processed: the quoted
inner title is classified; anything else classifies as before.

Red evidence: 6 new merge-commit fixtures failed (feat/feat(scope)/fix/
fix(scope)/feat!/perf inner titles all classified 'other'). Green: 64/64.
2026-09-07 13:56:19 +01:00
Nick 52e0bb0034 fix(pipeline): derive release API URLs in-script from workflow base coordinates (#192)
Spec-gate finding 1: create_release.py hard-read LIST_API_URL/RELEASE_API_URL
but auto-version.yml only ever passed RELEASE_SERVER_URL/RELEASE_REPO —
KeyError at runtime on both the PR-closed and safety-net release paths.

The script now derives both URLs from the base coordinates (explicit env
still overrides for direct use), and the workflow env blocks stay unchanged.
Adds tests/test_workflow_script_contract.py: a systematic guard that parses
every workflow env contract (step env + $GITHUB_ENV exports + shell exports)
against each invoked script's hard os.environ reads, plus an honesty check
that derived-default keys are never bare-subscripted.

Red evidence: contract test failed on both create_release invocations
(missing LIST_API_URL/RELEASE_API_URL); honesty test failed on the bare
subscripts. Green: 18/18.
2026-09-07 13:49:33 +01:00
Nick 5f73763745 docs(pipeline): plan completion record for release safety net (#192) 2026-09-07 12:21:28 +01:00
Nick 98afcd7ded feat(pipeline): maintenance drift reconciliation (#192) 2026-09-07 12:19:30 +01:00
Nick 98bdd5a70d feat(pipeline): push-triggered release safety net (#192) 2026-09-07 12:04:59 +01:00
Nick 76baaf9873 feat(pipeline): version convergence checker (#192) 2026-09-07 11:46:48 +01:00
Nick 00c80eb3ae docs(pipeline): implementation plan for release safety net (#192) 2026-09-07 11:46:48 +01:00