Some checks failed
The rotating OAuth credential file is why the agents kept parking, and a long-lived token removes the failure by construction instead of recovering from it after the fact. MEASURED 2026-09-04. ~/.claude/.credentials.json holds a refresh token that ROTATES ON USE. Seeding both containers from the host left three clients holding one token; the first to refresh invalidated the other two, and on the failed refresh Claude Code CLEARS the stored tokens -- writes empty strings, keeps the metadata, and parks at "Login expired". decoder credentials emptied 13:04:28 decoder last transcript 13:04:29 <- one second later The emptying and the park are the same event, which is why it never self-heals: not a stale token a retry could fix, but no token at all, with no browser in the container to complete /login. A hollow file passes every "does it exist" check -- 508 B healthy against 280 B emptied -- which is how three separate diagnoses missed it. And recovery re-armed the bug: after re-seeding, host and decoder held the IDENTICAL refresh token hash. `claude setup-token` issues a long-lived token against the same Claude subscription. Checked, not assumed: `claude auth login` defaults to --claudeai and it is `--console` that means Console/API billing, so this is not the separate API bill. `CLAUDE_CODE_OAUTH_TOKEN` is recognised by the installed binary. Passed as an ENVIRONMENT VARIABLE, both halves of the failure are gone: nothing rotates, so peers cannot invalidate each other, and there is no file for Claude Code to empty on a failure. Both launchers read $HOME/.sylph-claude-token if present -- same pattern as SYLPH_GIT_CREDENTIALS -- and both entrypoints skip OAuth seeding entirely when the variable is set, because copying the rotating file in would re-create the exact collision the token exists to remove. Inert until the file exists. Without it, nothing changes. Also worth recording for the preflight work: `claude auth status` prints JSON with loggedIn/authMethod/subscriptionType. That is a far better SessionStart assertion than checking a file exists, and it would have caught this on the first iteration rather than the third incident.