2eb3227ad0ec57fb259d12f754a183dfafb94de2
3
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
b5c9a3c04e |
feat(release): in-critical-section convergence hook — the WINNER refreshes release history atomically with its promotion (SCAFFOLD-TRANCHE-0012 §12C)
CI Pipeline / best-practices (push) Successful in 11s
CI Pipeline / canary (push) Successful in 35s
CI Pipeline / lint (push) Successful in 47s
CI Pipeline / documentation (push) Successful in 5s
Auto-version and Release / release (push) Successful in 1m7s
CI Pipeline / gitea-offline (push) Successful in 16s
CI Pipeline / type-check (push) Successful in 32s
CI Pipeline / unit-tests (push) Successful in 1m51s
CI Pipeline / required-checks (push) Successful in 2s
deploy / deploy (push) Failing after 4m35s
§12C design insight from the 0.66.51 deploy (run 7721): the workflow-step refresh fired (steps.promotion_truth gate worked) but the authority readback disagreed — an overlapping deploy had superseded the candidate between promotion-check and refresh-step. Correct refusal, but the WINNER never converges because its own refresh can hit the same race. DEPLOY_PROMOTION.PROMOTE now accepts on_promoted: invoked INSIDE the flock critical section immediately after the symlink swap — only the promotion WINNER converges, atomically with the swap. Failure degrades to STALE (software valid, history lags, retry converges). deploy_stage passes a convergence hook that builds evidence-derived notes and publishes to Gitea. Tests: 4 new (hook fires on promotion, NOT fired for stale, failure degrades not crashes, A→B race winner-converges-inside-lock); suite 1687 green; race tests all green. |
||
|
|
f765365d03 |
fix(release): ancestry check fetches canonical branch on divergent classification
CI Pipeline / best-practices (push) Successful in 7s
CI Pipeline / documentation (push) Successful in 4s
Auto-version and Release / release (push) Successful in 29s
CI Pipeline / canary (push) Successful in 32s
CI Pipeline / lint (push) Successful in 42s
CI Pipeline / type-check (push) Successful in 29s
CI Pipeline / gitea-offline (push) Successful in 17s
CI Pipeline / unit-tests (push) Successful in 1m8s
CI Pipeline / required-checks (push) Successful in 1s
deploy / deploy (push) Successful in 2m59s
Observed live on deploy 7449 (SHA
|
||
|
|
600008482d |
fix(release): monotonic promotion — stale deploy candidates fenced at promotion time (DEPLOY-MONOTONIC-001)
CI Pipeline / best-practices (push) Successful in 8s
CI Pipeline / documentation (push) Successful in 5s
CI Pipeline / canary (push) Successful in 32s
Auto-version and Release / release (push) Successful in 31s
CI Pipeline / lint (push) Successful in 43s
CI Pipeline / type-check (push) Successful in 28s
CI Pipeline / gitea-offline (push) Successful in 17s
CI Pipeline / unit-tests (push) Successful in 1m7s
CI Pipeline / required-checks (push) Successful in 1s
deploy / deploy (push) Successful in 2m58s
Root cause of the 2026-09-13 deploy-order race (deploys 7355/7359): deploy_stage.py promoted unconditionally in job-finish order — no comparison against the currently-deployed commit, no lock, unconditional PROMOTED claim. Two overlapping deploys (tranche push + auto-bump push) could finish out of order and move current BACKWARD. Repair (governed through the Tranche 4 live Scaffolding path — decision trace + contract DEPLOY-MONOTONIC-001 durably saved BEFORE edits): - deploy_promotion.py: promotion authority with GIT ANCESTRY ordering (canonical release branch; Rune's flow is linear master history): same -> NOOP_ALREADY_CURRENT (idempotent), descendant -> PROMOTED, ancestor -> STALE_SUPERSEDED (truthful non-error refusal), divergent or unknown -> fail closed. flock'd critical section: the ancestry check against the commit current ACTUALLY points to, the last-good refresh, and the symlink swap happen atomically — no check/swap interleaving window. Symlink swaps are os.replace(2) over temp links: current is never absent, never half-updated (crash-safe). - deploy_stage.py: promotion routed through the authority; truthful outcome reporting — STALE_SUPERSEDED prints no PROMOTED claim, mutates nothing, exits 0 (the newer build legitimately won); divergent/unknown exit 1 (fail closed). - deploy.yml: promotion truthfulness check reads the deployment authority (current symlink) after staging; runtime verification only runs for real promotions — build/stage success != promotion success. Test matrix (brief §20 A-M): 21 tests — the exact race shape with B promoting then A fenced, forward order, same-candidate idempotence, retry-after-promotion last-good integrity, stale mutation-freedom (current AND last-good untouched), divergent orphan-branch fail-closed, qualification gate regression, stage/promote distinction, promoted- outcome proven from deployment authority, auto-bump overlap shape, crash-safety coherence across mixed outcomes, red-candidate refusal regression, green forward deploy, concurrent-thread serialization, and the §21 LIVE two-process race: old process paused at its promotion barrier, new promotes, old resumes and is fenced — current remains the newer build, deterministic via file barrier (no sleeps). Suite 1497 green; henxels rc=0; pyright clean. |