upstream-drift #52
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: upstream-drift | |
| # THE DAILY QUESTION: did a new Claude Code break clode? | |
| # | |
| # clode reads a bundle it does not own, so upstream can break us without breaking | |
| # itself — and the failure is silent by nature: an anchor that no longer matches | |
| # means a hook is NOT applied, while everything still builds and PONGs. 2.1.210 | |
| # reshaped the pkg-manager autoupdater site (2.1.207 was fine), the redirect | |
| # stopped applying, and nobody noticed for weeks. | |
| # | |
| # WHY A SEPARATE JOB, when ci.yml also installs the provider: attribution. This | |
| # asks a DIFFERENT question than a push does. A push asks "did my diff break | |
| # clode?"; this asks "did Anthropic's release break clode?" Collapsing them into | |
| # one red light is how an upstream break lands on an innocent commit and someone | |
| # spends a session bisecting job conclusions to discover it was never their code. | |
| # Red HERE means upstream moved. It says so in the job name. | |
| # | |
| # Same doctrine as guest-versions.yml (the weekly sweep that backstops implicit | |
| # pins) — scripts/tjs-legs.mjs states it: Renovate owns explicit pins, a scheduled | |
| # sweep backstops the implicit ones. The provider is an implicit pin (every job | |
| # installs latest), so this is that backstop. | |
| # | |
| # START SMALL, FLESH OUT (user, 2026-07-17): one check today — every hook anchor is | |
| # present. Add checks to scripts/upstream-drift-check.mjs as we find more ways | |
| # upstream can break us. Deliberately NOT built on `inspect --strict`: that gates on | |
| # every unimplemented Bun.* member and is chronically red on versions that work | |
| # fine, and a job that is always red teaches people to ignore it. | |
| on: | |
| schedule: | |
| - cron: '23 5 * * *' # daily 05:23 UTC (off the hour; nothing else runs then) | |
| workflow_dispatch: {} | |
| permissions: | |
| contents: read | |
| jobs: | |
| anchors: | |
| runs-on: ubuntu-latest | |
| defaults: | |
| run: | |
| shell: bash | |
| steps: | |
| - uses: actions/checkout@v7.0.1 | |
| - uses: actions/setup-node@v7.0.0 | |
| with: | |
| node-version-file: .tool-versions | |
| # `next`, deliberately unpinned, and deliberately NOT `latest`. | |
| # | |
| # We watched both for one day and it settled the question: `next` is the leading | |
| # channel. 2.1.243 and 2.1.245 were both on `next` before `latest`, and on | |
| # 2026-08-25 `latest` moved from 2.1.241 to 2.1.245 — the code-split format that | |
| # `clode build` could not read — with no warning to anyone watching `latest` alone. | |
| # Watching the newer channel is the only version of this job that can tell us | |
| # something BEFORE every user is already broken. | |
| # | |
| # One channel is enough (user, 2026-08-25). When the two are the same version this | |
| # costs nothing; when they differ, `next` is the one worth knowing about. | |
| # | |
| # This job does NOT read UPSTREAM_PIN (the 2.1.251 pin ci.yml/naude-cross.yml/ | |
| # build-leg install to dodge the 2.1.257 SCC-merge break — see that file). Pinning | |
| # here would blind the early-warning system at exactly the moment we are choosing | |
| # to lag on purpose: this job's only reason to exist is to notice upstream moving | |
| # WHILE everything else holds still. Leave it unpinned. | |
| - name: Install the `next` Claude Code | |
| run: npm i -g @anthropic-ai/claude-code@next 2>&1 | tail -3 | |
| - name: Every clode hook anchor still matches the newest bundle | |
| run: | | |
| set -euo pipefail | |
| VER="$(npm view @anthropic-ai/claude-code@next version)" | |
| echo "upstream Claude Code (next): $VER" | |
| PROV="$(node scripts/find-provider.mjs)" | |
| # A missing provider must fail, not skip: "we could not check" is not | |
| # "nothing changed", and this job's only product is a trustworthy answer. | |
| if [ -z "$PROV" ]; then echo "ERROR: no Bun provider found under @anthropic-ai/claude-code" >&2; exit 1; fi | |
| echo "provider: $PROV" | |
| echo "$VER" > "$RUNNER_TEMP/upstream-version" | |
| node scripts/upstream-drift-check.mjs "$PROV" | |
| # WHEN IT BREAKS, SAY WHAT UPSTREAM SAID. Twice now upstream has broken | |
| # `clode build` with no change in this repo, and both times a public changelog | |
| # already named the area while we byte-diffed binaries for hours (2.1.238's | |
| # os.constants.errno read, and 2.1.243's switch to Bun code splitting). The | |
| # information was never hard to get; nothing connected "the daily check went | |
| # red" to "go read the release notes". | |
| # | |
| # Runs only on failure, and cannot turn a red into a green: the step above has | |
| # already failed by the time this runs, and the tool exits 0 even when it cannot | |
| # reach the network, so it never becomes a second thing to debug. | |
| # | |
| # LIMITATION, stated rather than hidden: we do not persist the last version this | |
| # job was green on, so there is no true "since we last passed" range. Passing a | |
| # version that is not in the changelog makes the tool print the newest few | |
| # sections, which is the useful 90% with no state to keep in sync. If we ever | |
| # persist a last-green version, pass it as --from and delete this comment. | |
| - name: What did upstream say? (context only — a LEAD, never a cause) | |
| if: failure() | |
| run: | | |
| VER="$(cat "$RUNNER_TEMP/upstream-version" 2>/dev/null || true)" | |
| node scripts/upstream-release-notes.mjs --from "0.0.0-no-last-green-recorded" ${VER:+--to "$VER"} | |
| # THE SECOND DAILY QUESTION: does a quaude built from the newest bundle still BOOT? | |
| # | |
| # The anchors job above asks whether our hook sites still match. It cannot see the | |
| # other way upstream breaks us: the bundle starts READING a node API the shim does | |
| # not implement. 2.1.238 began evaluating | |
| # new Map(Object.entries(require("os").constants.errno)) | |
| # at module init; os.constants carried only .signals, so Object.entries(undefined) | |
| # threw and EVERY quaude built against 2.1.238 was dead on arrival. The anchors | |
| # check was green throughout — nothing was mis-anchored, the shim was just missing | |
| # a table. | |
| # | |
| # Nothing ran the build smoke on a schedule, so the only way to find this was for a | |
| # human to dispatch a build; it surfaced by accident during unrelated pack work, | |
| # and presented as CI infrastructure noise. One cheap leg daily converts that into | |
| # a dated, attributed red light within a day of an upstream release. | |
| # | |
| # A FOURTH DAILY QUESTION, and the cheapest: has the Haiku blocker lifted? | |
| # | |
| # haiku-x64 fails because cross-platform-actions ships only an r1beta5 guest image and | |
| # HaikuPorts moved to the current release, so the guest cannot install packages at all. | |
| # Not our bug, and not something a build can fix. Running that leg on every push spent a | |
| # runner slot to re-derive an answer we had already written down, while the question we | |
| # actually care about — has a beta6 image appeared? — went unasked. | |
| # | |
| # This asks it in about a second. Green means "nothing we could do about Haiku today", | |
| # NOT "Haiku is fine": the leg is deliberately out of the push matrix and publishes | |
| # nothing (see BACKLOG). Red means an action just became possible. | |
| # linux-x64-musl deliberately: an alpine container, no qemu, and the engine comes | |
| # from the same PINS+patches cache the push legs use, so the marginal cost is the | |
| # fuse and the smoke — not an engine build. | |
| # | |
| # Red HERE means the newest bundle no longer boots under the shim. Same attribution | |
| # doctrine as the anchors job: it is upstream that moved, not the last commit. | |
| boots: | |
| uses: ./.github/workflows/tjs-legs.yml | |
| permissions: | |
| contents: read | |
| with: | |
| tier: release | |
| only: linux-x64-musl |