ci: fail when a uses: points at a redirect, so there is no third time - #803
Merged
Conversation
Twice in two days an owner change stopped every merge and every deploy in this repo: `maonakamoto` → `catomean` on 2026-08-26, then the move to the `bitbaum` org, fixed in #800. Each fix pointed at a name that became a redirect the next time. Both outages were invisible in every place anyone looks. GitHub redirects a renamed owner for the REST API and for git remotes, so `gh api` answers and `git push` works; the Actions resolver is the one consumer that does not follow the redirect. It fails before any step exists, with "This run likely failed because of a workflow file issue" and no readable log, while pull requests stay green, clean and mergeable. The red run is on main, under a workflow nobody opens. Work simply stops shipping. Two instances is where the rule says stop fixing instances. This asks the REST API what each referenced repo is really called and fails when that disagrees with what the workflow says — that disagreement IS the bug, because REST resolves the redirect and Actions does not. A static allowlist could not catch it: after a rename the workflow file and the allowlist would both hold the same stale name and agree with each other. Only asking GitHub what the repo is called today can tell. Lives in CI rather than in `npm run verify`, which is the offline SSOT bundle and must stay deterministic; without a token this skips rather than passing quietly. A non-200 that is not a 404 exits 2 — could-not-look is never reported as looks-fine. Proven by mutation: restoring `catomean/dotfiles` fails with the canonical name to use. Also fixes the last CLAUDE.md reference to the 2026-08-26 owner. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012dpTLxh5GJWeWTF1UEvcD5
catomean
added a commit
that referenced
this pull request
Aug 28, 2026
Both branches edit the same line; one owner avoids a conflict that would stop CI running on whichever loses. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
github-actions Bot
pushed a commit
that referenced
this pull request
Aug 28, 2026
* fix(cat): @cat has resolved to nobody for two days The email-derived-handle retirement renamed the Cat. Its predicate — `username = split_part(email, '@', 1)` — is exactly right for a person, whose public crawlable handle then republishes their email local part. It is exactly wrong for the Cat, because deriving the handle from the address is HOW the Cat is created: cat-account.ts registers `cat@orangecat.invalid` precisely so handle_new_user mints `cat`. So the script matched, and at 2026-08-26 21:58:59Z the handle became `user_0234d5e38e66`. `@cat` is the platform's advertised interface and it resolves by username, so every `@cat` in a message or under a post has since resolved to nobody. No reply anywhere. Found by typing `@` in the composer in production and seeing no Cat in the menu. Nothing went red. The account still existed, /profiles/cat still 301'd through profile_username_history, CI was green, health was 200. The only symptom was silence from the feature whose job is to answer. Three defects, three fixes: POLICY — the retirement could not tell a leaked address from a system identity. Now excluded by property, not by name: RFC 2606 reserves `.invalid` for undeliverable addresses, so there is no mailbox, no owner, and nothing personal in the local part. Hardcoding 'cat' in SQL would have put a second definition of the handle where it could drift from config/cat-identity.ts. Applied to the rename script and to count_email_derived_usernames(), which would otherwise have gone from 0 to 1 and turned the nightly gate red for a profile that is fine. CODE — ensureCatAccount looked the Cat up BY USERNAME, so its self-healing walked straight past a rename: lookup missed, createUser said "already registered", second lookup missed, returned null on every tick. It now keys on the login address, which no policy has reason to rewrite, and treats the handle as a field to assert rather than a key to search by. A rename now repairs itself on the next tick. GATE — nothing checked the product's own claim. checkCatHandle asserts that the name we tell people to type reaches the Cat; a lookup by id would have stayed green throughout. The literal it carries is pinned to CAT_USERNAME by a test, so the duplicate cannot drift. Proven by mutation: restoring the username lookup, removing the re-assert, and drifting the gate's literal each turn the suite red. The migration was rehearsed against production inside a transaction and rolled back — UPDATE 1, DELETE 1, 28 real retired handles untouched, gate still reads 0. Also restores the github.com/catomean links lost when this worktree was reset; the owner rename left them pointing at a redirect. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012dpTLxh5GJWeWTF1UEvcD5 * fix(docs): point at bitbaum, the canonical owner catomean is itself a redirect now — the repos live in the bitbaum org. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012dpTLxh5GJWeWTF1UEvcD5 * fix(mentions): the @ menu was offering rows nobody can read Production has 84 profiles and 74 of them have no display name — the correct consequence of no longer minting names out of email local parts, where NULL is honest. The menu did not account for that, so what it actually showed on a bare `@` was two rows of `user_d58c7dccec41`, printed twice each: once as the name, because the name falls back to the handle, and once as the subtitle. Three changes, all following from the data rather than from taste. A nameless row now shows its handle ONCE, in the position the eye reads first, instead of the same hex string on two lines. A nameless profile is not offered at all on a bare `@`. You cannot be searching for a name that does not exist, so those rows were pure noise in the one position that matters most — the first thing anyone sees when they discover the feature. Type any part of the handle and they come straight back through the existing prefix rules. At equal match quality, a named profile outranks a nameless one. `@u` matches `ursula` and `user_d58c7dccec41` equally well by anything the score can see, and only one of them is a row a human can act on. The tiebreak sits after match quality, never before it, so an exact handle match still wins — a demotion that overrode "this is plainly the row being asked for" would be a worse bug than the one it fixed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012dpTLxh5GJWeWTF1UEvcD5 * chore: leave the CLAUDE.md owner line to #803 Both branches edit the same line; one owner avoids a conflict that would stop CI running on whichever loses. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: Georgy Butaev <41178744+g-but@users.noreply.github.com> Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Twice in two days an owner change stopped every merge and every deploy here:
maonakamoto→catomean(2026-08-26), then the move to thebitbaumorg (fixed in #800). Each fix pointed at a name that became a redirect the next time.Both outages were invisible everywhere anyone looks. REST and git follow a rename redirect —
gh apianswers,git pushworks — while the Actions resolver, the one consumer that does not follow it, dies before any step exists with no readable log. PRs stay green, clean and mergeable; the red run sits on main under a workflow nobody opens.Two instances is where the rule says stop fixing instances.
This asks the REST API what each referenced repo is really called and fails when that disagrees with the workflow. That disagreement is the bug — REST resolves the redirect, Actions does not. A static allowlist could not catch it: after a rename the workflow and the allowlist would hold the same stale name and agree with each other.
It runs in CI rather than
npm run verify(which must stay offline and deterministic), skips without a token rather than passing quietly, and exits 2 on a non-404 error — could-not-look is never reported as looks-fine.Proven by mutation: restoring
catomean/dotfilesfails with the canonical name to use.Supersedes #801, which duplicated #800's work; this keeps only the part that was not.
🤖 Generated with Claude Code
https://claude.ai/code/session_012dpTLxh5GJWeWTF1UEvcD5