versioning: refuse to publish a version below what the channel already serves
CI & Build / Build now, or wait for Android? (push) Successful in 2s
CI & Build / Python lint (push) Successful in 3s
CI & Build / TypeScript typecheck (push) Successful in 6s
CI & Build / Python tests (push) Successful in 17s
CI & Build / integration (push) Successful in 21s
CI & Build / Build & push image (push) Skipped
Desktop (Tauri) / Windows installer (cross-compiled) (push) Successful in 3m13s
Desktop (Tauri) / Tauri desktop (Linux) (push) Successful in 6m21s
Desktop (Tauri) / Update manifest (push) Successful in 5s
Android / Kotlin + Rust (APK) (push) Successful in 9m19s

Step 5 of M314, note 3127 §6.3. Everything else in this milestone derives a
number and trusts it; this compares the derived value against what the channel
is actually serving and fails the lane if it went down.

Too-low is the unrecoverable direction: every installed client reports "up to
date" forever, and no later build fixes it until one climbs back above the bad
number. #2183 and #2993 are both that symptom.

## Two hazards, two mechanisms

A shallow clone is now tested DIRECTLY, in `version.sh`, via
`--is-shallow-repository`. The empty-result guard only caught the case where
nothing matched — and run 4796 showed the worse one, where a partial match
returned a real six-days-stale answer. Asking the question outright costs no
network and covers artifacts with nothing published to compare against.

`guard-forward.sh` handles the rest: a squash or rebase merge rewriting the
committer date, a rebuild of an older commit, and clock skew between runners.

## The comparison is per artifact, and the operator differs

  desktop  derived >= published   commit time, so equality is the ORDINARY
                                  no-change case and `<=` would fail every
                                  build that changed nothing
  android  derived >  published   build time, so equality means two builds in
                                  one minute — and Android refuses to install
                                  an APK whose versionCode does not RISE

The server is deliberately unguarded: nothing compares its version, `:latest`
moves regardless, and rule 145 removed the version tags that would be the
published list. A too-low value there is a wrong date in a footer, not a
stranded client. It still gets the shallow-clone check.

## Proved to fire, not assumed

Cloned the repo, checked out a commit eight back, ran the guard against the
LIVE dev feed:

  at the tip     derived 1.0.3502151, published 1.0.3502151  -> pass
  eight back     derived 1.0.3501535, published 1.0.3502151  -> FAILS
  android tip    derived 3502171,     published 3502152      -> pass
  stable         derived 1.0.3502151, published 0.2.0        -> pass

That last row is worth keeping: stable still advertises the bare `0.2.0` from
the old Cargo.toml scheme, so the transition orders upward on BOTH channels,
not just the one being exercised.

A channel with nothing published passes rather than failing — otherwise the
first publish to a new channel could never happen.

The guard runs BEFORE the build in all three lanes, so a bad derivation costs
seconds rather than a five-minute compile and a publish to undo.

`compare` is exposed as an explicit mode so the ordering is testable without a
network and inspectable without a push — 16 cases including `1.0.9 < 1.0.10`,
which a string compare gets exactly backwards.

#3145

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-28 21:33:00 -04:00
co-authored by Claude Opus 5
parent 6e891357ff
commit 0ab7d94294
5 changed files with 288 additions and 0 deletions
+20
View File
@@ -126,6 +126,26 @@ jobs:
echo "apk=android/app/build/outputs/apk/debug/app-debug.apk" >> $GITHUB_OUTPUT
fi
# THE ONE CHECK THAT LOOKS AT REALITY (note 3127 §6.3). Everything else in this
# lane derives a number and trusts it; this compares the derived value against
# what the channel is actually serving, and fails the lane if it went DOWN.
#
# Placed before the build, not after: a bad derivation should cost seconds, not
# a five-minute compile and a publish that has to be undone. Too-low is the
# unrecoverable direction — every installed client reports "up to date" forever
# and no later build fixes it until one climbs back above the bad number
# (#2183, #2993).
- name: Guard — the version must not go backwards
if: github.ref == 'refs/heads/dev' || github.ref == 'refs/heads/main'
env:
GITHUB_TOKEN: ${ github.token }
run: |
case "$GITHUB_REF_NAME" in
main) channel=stable ;;
*) channel=dev ;;
esac
sh ../packaging/guard-forward.sh android "$channel"
- name: Make gradlew executable
run: chmod +x ./gradlew