#!/usr/bin/env bash
# claude-multiacc sandboxed test suite — covers BOTH providers (claude + codex).
# No network, no real accounts, no quota: fake `claude`/`codex` binaries + file://
# usage/token fixtures.
set -u

REPO_DIR="$(cd "$(dirname "$0")/.." && pwd -P)"
WORK="$(mktemp -d "${TMPDIR:-/tmp}/multiacc-test.XXXXXX")"
trap 'rm -rf "$WORK"' EXIT

PASS=0
FAIL=0
t_ok() { PASS=$((PASS+1)); printf 'ok   %s\n' "$1"; }
t_fail() { FAIL=$((FAIL+1)); printf 'FAIL %s%s\n' "$1" "${2:+ — $2}"; }
check() { # check <name> <expected-substring> <actual>
  case "$3" in
    *"$2"*) t_ok "$1" ;;
    *) t_fail "$1" "expected substring '$2', got: $(printf '%s' "$3" | head -c 200)" ;;
  esac
}

# ---- sandbox layout -------------------------------------------------------
export CLAUDE_ACCOUNTS_DIR="$WORK/accounts"
ACC="$CLAUDE_ACCOUNTS_DIR"
FAKEBIN="$WORK/fakebin"
mkdir -p "$ACC/tmp" "$FAKEBIN"

# Fake "real" claude: prints which config dir/token it ran under; scriptable failures.
cat > "$FAKEBIN/claude" <<'EOF'
#!/usr/bin/env bash
# fake real claude for tests (not a multiacc shim)
if [ "${1:-}" = "auth" ] && [ "${2:-}" = "status" ]; then
  if [ -n "${FAKE_AUTH_FAIL:-}" ]; then echo '{"loggedIn": false}'; exit 0; fi
  printf '{"loggedIn": true, "email": "%s"}\n' "${FAKE_EMAIL:-fake@test}"
  exit 0
fi
if [ "${1:-}" = "auth" ] && [ "${2:-}" = "login" ]; then
  [ -n "${FAKE_LOGIN_FAIL:-}" ] && { echo "login aborted" >&2; exit 1; }
  # simulate a completed full-scope login: write auto-refreshing creds to the config dir
  printf '{"claudeAiOauth":{"accessToken":"sk-ant-oat01-login","refreshToken":"r","expiresAt":9999999999999,"refreshTokenExpiresAt":9999999999999,"scopes":["user:inference"],"subscriptionType":"max"}}' > "${CLAUDE_CONFIG_DIR:-/dev/null}/.credentials.json"
  echo "Logged in."
  exit 0
fi
if [ "${1:-}" = "setup-token" ]; then
  echo "Open this sign-in link: https://claude.ai/oauth/authorize?fake=1"
  [ -n "${FAKE_TOKEN_FAIL:-}" ] && { echo "sign-in aborted" >&2; exit 1; }
  echo "Your token: sk-ant-oat01-FAKEFAKEFAKEFAKEFAKEFAKEFAKEFAKEFAKEFAKEFAKE"
  exit 0
fi
if [ $# -eq 0 ] && [ -n "${FAKE_DO_LOGIN:-}" ] && [ -n "${CLAUDE_CONFIG_DIR:-}" ]; then
  # simulate an interactive session in which the user completed /login
  printf '{"claudeAiOauth":{"accessToken":"sk-ant-oat01-new","refreshToken":"r","expiresAt":9999999999999,"refreshTokenExpiresAt":9999999999999}}' > "$CLAUDE_CONFIG_DIR/.credentials.json"
  exit 0
fi
ctl="${FAKE_CTL:-/nonexistent}"
acct="$(basename "${CLAUDE_CONFIG_DIR:-none}")"
if [ -f "$ctl" ] && grep -qx "fail:$acct" "$ctl" 2>/dev/null; then
  echo "API Error: 429 rate limit exceeded" >&2
  exit 1
fi
if [ -f "$ctl" ] && grep -qx "authfail:$acct" "$ctl" 2>/dev/null; then
  # the exact failure a dead OAuth grant produces
  echo "Failed to authenticate: OAuth session expired and could not be refreshed" >&2
  exit 1
fi
if [ -f "$ctl" ] && grep -qx "orgfail:$acct" "$ctl" 2>/dev/null; then
  # the exact failure an org-disabled account produces (authenticates, cannot infer)
  echo "Your organization has disabled Claude subscription access for Claude Code · Use an Anthropic API key instead, or ask your admin to enable access"
  exit 1
fi
for a in "$@"; do
  case "$a" in
    --exit7) echo "ordinary failure, not auth related" >&2; exit 7 ;;
    --echo-stdin) cat; exit 0 ;;
  esac
done
echo "CFG=$acct TOK=${CLAUDE_CODE_OAUTH_TOKEN:-none}"
EOF
chmod +x "$FAKEBIN/claude"

export PATH="$REPO_DIR/bin:$FAKEBIN:$PATH"
export FAKE_CTL="$WORK/ctl"
# Neutralize any ambient state from the invoking environment.
unset CLAUDE_CONFIG_DIR CLAUDE_CODE_OAUTH_TOKEN CLAUDE_ACCOUNT CLAUDE_SHIM_ACTIVE 2>/dev/null || true
export CLAUDE_MULTIACC_NO_SYNC=1
# Fixtures are local file:// URLs with no rate limit, so the anti-429 fetch throttle
# is off by default here; the throttle test re-enables it explicitly.
export CLAUDE_MULTIACC_MIN_FETCH=0
# The oauth token endpoint must NEVER be hit for real from tests: default to a missing
# file:// fixture (refresh fails fast, offline); the refresh tests override per-case.
export CLAUDE_MULTIACC_TOKEN_URL="file://$WORK/token-endpoint-missing.json"
# Same for the usage endpoint: the SHIM's opportunistic background `limits --quiet`
# kick inherits this default, so it can never reach a real endpoint from tests
# (every explicit limits test overrides the URL inline). The pre-armed .limits-kick
# throttle keeps those background kicks from racing explicit limits runs.
export CLAUDE_MULTIACC_USAGE_URL="file://$WORK/usage-endpoint-missing.json"
: > "$ACC/.limits-kick"

now="$(date +%s)"

# ---- 1. passthrough: no manifest yet -------------------------------------
out="$(claude 2>&1)"
check "passthrough without manifest" "CFG=none" "$out"

# ---- manifest + two oauth accounts ----------------------------------------
cat > "$ACC/accounts.json" <<EOF
{
  "version": 1,
  "server": "root@203.0.113.1",
  "server_root": "/root/.claude-accounts",
  "server_repo": "/root/claude-multiacc",
  "threshold": 90,
  "accounts": [
    {"id": "acct-01", "email": "a@test", "home": "mac", "added_at": "2026-07-13T00:00:00Z"},
    {"id": "acct-02", "email": "b@test", "home": "mac", "added_at": "2026-07-13T00:00:00Z"}
  ]
}
EOF
for i in 01 02; do
  mkdir -p "$ACC/acct-$i"
  printf '{"claudeAiOauth":{"accessToken":"sk-ant-oat01-test%s","refreshToken":"r","expiresAt":9999999999999,"refreshTokenExpiresAt":9999999999999,"scopes":["user:inference"],"subscriptionType":"max"}}' "$i" > "$ACC/acct-$i/.credentials.json"
done

# ---- 2-4. passthrough guards ----------------------------------------------
out="$(CLAUDE_CONFIG_DIR=/tmp/other claude 2>&1)"
check "passthrough with CLAUDE_CONFIG_DIR" "CFG=other" "$out"
out="$(CLAUDE_CODE_OAUTH_TOKEN=sk-test claude 2>&1)"
check "passthrough with CLAUDE_CODE_OAUTH_TOKEN" "CFG=none" "$out"
out="$(CLAUDE_MULTIACC_DISABLE=1 claude 2>&1)"
check "passthrough when disabled" "CFG=none" "$out"

# ---- 5. headroom selection: most WEEKLY headroom wins (session is only a tiebreaker) --
lj() { # lj <weekly> <session> <max>  -> a fresh limits.json body
  printf '{"fetched_at":%s,"weekly_percent":%s,"session_percent":%s,"max_percent":%s,"buckets":[]}' "$now" "$1" "$2" "$3"
}
# acct-01 weekly 80, acct-02 weekly 20 => always acct-02.
lj 80 10 80 > "$ACC/acct-01/limits.json"
lj 20 10 20 > "$ACC/acct-02/limits.json"
all2=1
for _ in $(seq 1 15); do
  case "$(claude 2>&1)" in *CFG=acct-02*) ;; *) all2=0 ;; esac
done
[ "$all2" = "1" ] && t_ok "picks the highest weekly-headroom account (weekly 20 over 80)" \
  || t_fail "headroom selection" "picked the more-utilized account"

# THE KEY CASE from the research: a high (but sub-threshold) SESSION bucket must NOT
# deprioritize an account whose weekly headroom is better. acct-01: session 85, weekly 10;
# acct-02: session 20, weekly 70. Both eligible (max<90). acct-01 is the better pick —
# its near-full bucket is the 5h session (self-heals), weekly is nearly untouched.
lj 10 85 85 > "$ACC/acct-01/limits.json"
lj 70 20 70 > "$ACC/acct-02/limits.json"
all1=1
for _ in $(seq 1 15); do
  case "$(claude 2>&1)" in *CFG=acct-01*) ;; *) all1=0 ;; esac
done
[ "$all1" = "1" ] && t_ok "high session does NOT beat better weekly headroom (10w/85s over 70w/20s)" \
  || t_fail "weekly-over-session" "ranked the account with less weekly headroom higher"

# equal weekly => session breaks the tie toward the account with more session headroom
lj 40 20 40 > "$ACC/acct-01/limits.json"
lj 40 80 80 > "$ACC/acct-02/limits.json"
all1=1
for _ in $(seq 1 15); do
  case "$(claude 2>&1)" in *CFG=acct-01*) ;; *) all1=0 ;; esac
done
[ "$all1" = "1" ] && t_ok "equal weekly: lower session wins the tiebreak" \
  || t_fail "session tiebreak" "did not use session to break a weekly tie"

# fully equal scores spread load across accounts
lj 10 10 10 > "$ACC/acct-01/limits.json"
lj 10 10 10 > "$ACC/acct-02/limits.json"
hits1=0; hits2=0
for _ in $(seq 1 40); do
  case "$(claude 2>&1)" in
    *CFG=acct-01*) hits1=$((hits1+1)) ;;
    *CFG=acct-02*) hits2=$((hits2+1)) ;;
  esac
done
{ [ "$hits1" -gt 0 ] && [ "$hits2" -gt 0 ] && [ $((hits1+hits2)) -eq 40 ]; } \
  && t_ok "equal scores spread randomly (acct-01=$hits1 acct-02=$hits2)" \
  || t_fail "tie spreading" "acct-01=$hits1 acct-02=$hits2 (want both >0, total 40)"

# opt-out: legacy uniform-random mode still available
hits1=0; hits2=0
lj 80 80 80 > "$ACC/acct-01/limits.json"
for _ in $(seq 1 40); do
  case "$(CLAUDE_SHIM_SELECT=random claude 2>&1)" in
    *CFG=acct-01*) hits1=$((hits1+1)) ;;
    *CFG=acct-02*) hits2=$((hits2+1)) ;;
  esac
done
{ [ "$hits1" -gt 0 ] && [ "$hits2" -gt 0 ]; } \
  && t_ok "CLAUDE_SHIM_SELECT=random restores uniform spread" \
  || t_fail "random opt-out" "acct-01=$hits1 acct-02=$hits2"

# stale telemetry is neutral, never assumed free
printf '{"fetched_at":1,"weekly_percent":1,"session_percent":1,"max_percent":1,"buckets":[]}' > "$ACC/acct-01/limits.json"
lj 30 30 30 > "$ACC/acct-02/limits.json"
out="$(claude 2>&1)"
check "stale 1% loses to fresh 30% (stale is not trusted)" "CFG=acct-02" "$out"
rm -f "$ACC"/acct-*/limits.json

# ---- 6. explicit pin -------------------------------------------------------
out="$(CLAUDE_ACCOUNT=acct-02 claude 2>&1)"
check "CLAUDE_ACCOUNT pin" "CFG=acct-02" "$out"

# ---- 6b. pin works for an auth-less dir (login ceremony path) ----------------
mkdir -p "$ACC/acct-07"
out="$(CLAUDE_ACCOUNT=acct-07 claude 2>&1)"
check "pin to auth-less dir (ceremony)" "CFG=acct-07" "$out"
rmdir "$ACC/acct-07"

# ---- 7. limited marker excludes account ------------------------------------
printf '%s\nbucket=weekly_scoped:Fable percent=95 reason=limits\n' "$((now+3600))" > "$ACC/acct-01/.limited"
all2=1
for _ in $(seq 1 15); do
  out="$(claude 2>&1)"
  case "$out" in *CFG=acct-02*) ;; *) all2=0 ;; esac
done
[ "$all2" = "1" ] && t_ok "limited account excluded from pool" || t_fail "limited account excluded" "acct-01 was still picked"

# ---- 7b. pin overrides marker ----------------------------------------------
out="$(CLAUDE_ACCOUNT=acct-01 claude 2>&1)"
check "explicit pin wins over marker" "CFG=acct-01" "$out"

# ---- 8. expired marker auto-clears ------------------------------------------
printf '%s\nbucket=session percent=95 reason=limits\n' "$((now-10))" > "$ACC/acct-01/.limited"
claude >/dev/null 2>&1
[ ! -f "$ACC/acct-01/.limited" ] && t_ok "expired marker auto-cleared" || t_fail "expired marker auto-cleared" "marker still present"

# ---- 9. all limited -> least utilized fallback ------------------------------
printf '%s\nx\n' "$((now+3600))" > "$ACC/acct-01/.limited"
printf '%s\nx\n' "$((now+3600))" > "$ACC/acct-02/.limited"
printf '{"fetched_at":%s,"max_percent":97,"buckets":[]}' "$now" > "$ACC/acct-01/limits.json"
printf '{"fetched_at":%s,"max_percent":91,"buckets":[]}' "$now" > "$ACC/acct-02/limits.json"
out="$(claude 2>&1)"
check "all-limited falls back to least utilized" "CFG=acct-02" "$out"
grep -q "all-limited fallback=acct-02" "$ACC/selection.log" \
  && t_ok "fallback logged" || t_fail "fallback logged" "no all-limited line in selection.log"
rm -f "$ACC/acct-01/.limited" "$ACC/acct-02/.limited"

# ---- 9b. codex-review regressions: auth/marker/threshold hardening -------------
# empty .credentials.json must NOT count as auth (interrupted write)
mkdir -p "$ACC/acct-06"
: > "$ACC/acct-06/.credentials.json"
out="$(claude 2>&1)"
case "$out" in *CFG=acct-06*) t_fail "empty creds not selectable" "acct-06 was picked" ;;
  *) t_ok "empty .credentials.json is not treated as auth" ;; esac
rm -rf "$ACC/acct-06"

# fresh telemetry >= threshold excludes even when the .limited marker is missing
printf '{"fetched_at":%s,"max_percent":95,"buckets":[]}' "$now" > "$ACC/acct-01/limits.json"
printf '{"fetched_at":%s,"max_percent":10,"buckets":[]}' "$now" > "$ACC/acct-02/limits.json"
rm -f "$ACC"/acct-*/.limited
all2=1
for _ in $(seq 1 12); do
  out="$(claude 2>&1)"
  case "$out" in *CFG=acct-02*) ;; *) all2=0 ;; esac
done
[ "$all2" = "1" ] && t_ok "telemetry backstop excludes >=90% without a marker" \
  || t_fail "telemetry backstop" "acct-01 (95%) was still selected"

# ...but STALE >=90% telemetry must not exclude (fail open, no invented exclusions)
printf '{"fetched_at":1,"max_percent":95,"buckets":[]}' > "$ACC/acct-01/limits.json"
rm -f "$ACC/acct-02/.credentials.json"   # leave acct-01 as the only candidate
out="$(claude 2>&1)"
check "stale >=90% telemetry does not block (fail open)" "CFG=acct-01" "$out"
printf '{"claudeAiOauth":{"accessToken":"sk-ant-oat01-test02","refreshToken":"r","expiresAt":9999999999999,"refreshTokenExpiresAt":9999999999999,"scopes":["user:inference"],"subscriptionType":"max"}}' > "$ACC/acct-02/.credentials.json"

# a garbled/partial marker is treated as ACTIVE and never deleted (concurrent-write race)
printf 'GARBAGE-NOT-AN-EPOCH\n' > "$ACC/acct-01/.limited"
printf '{"fetched_at":%s,"max_percent":10,"buckets":[]}' "$now" > "$ACC/acct-01/limits.json"
out="$(claude 2>&1)"
case "$out" in *CFG=acct-02*) t_ok "garbled marker treated as active (excluded)" ;;
  *) t_fail "garbled marker" "acct-01 was selected despite an unparseable marker" ;; esac
[ -f "$ACC/acct-01/.limited" ] && t_ok "garbled marker not deleted by the shim" \
  || t_fail "garbled marker deleted" "shim destroyed a possibly-mid-write marker"
rm -f "$ACC"/acct-*/.limited "$ACC"/acct-*/limits.json

# ---- 9c. DEAD LOGINS are never selected --------------------------------------
# Regression: an account whose refresh token had expired stayed "valid" (it has a
# .credentials.json), so the shim kept picking it and every run died with
# "Failed to authenticate: OAuth session expired and could not be refreshed".
HEALTHY_CREDS='{"claudeAiOauth":{"accessToken":"sk-ant-oat01-live","refreshToken":"r","expiresAt":9999999999999,"refreshTokenExpiresAt":9999999999999,"scopes":["user:inference"]}}'
DEAD_CREDS='{"claudeAiOauth":{"accessToken":"sk-ant-oat01-dead","refreshToken":"r","expiresAt":1000000000000,"refreshTokenExpiresAt":1000000000000}}'
printf '%s' "$DEAD_CREDS" > "$ACC/acct-01/.credentials.json"
all2=1
for _ in $(seq 1 12); do
  out="$(claude 2>&1)"
  case "$out" in *CFG=acct-02*) ;; *) all2=0 ;; esac
done
[ "$all2" = "1" ] && t_ok "expired refresh token excluded from selection" \
  || t_fail "expired login excluded" "the dead account was still selected"
grep -q "skipped-expired: acct-01" "$ACC/selection.log" \
  && t_ok "skipped-expired logged" || t_fail "skipped-expired log" "no line in selection.log"

# ...and a dead login is not even the all-limited fallback: degraded beats down, but
# dead is DOWN — a limit-marked account that can still authenticate wins.
printf '%s\nx\n' "$((now+3600))" > "$ACC/acct-02/.limited"
out="$(claude 2>&1)"
check "limited-but-alive beats a dead login in the fallback" "CFG=acct-02" "$out"
rm -f "$ACC/acct-02/.limited"

# no refreshToken at all + expired access token = dead too (looped: with a single run a
# random tie-break would let a broken implementation pass half the time)
printf '{"claudeAiOauth":{"accessToken":"sk-ant-oat01-x","expiresAt":1000000000000}}' > "$ACC/acct-01/.credentials.json"
all2=1
for _ in $(seq 1 12); do
  out="$(claude 2>&1)"
  case "$out" in *CFG=acct-02*) ;; *) all2=0 ;; esac
done
[ "$all2" = "1" ] && t_ok "credential with no refresh token is dead" \
  || t_fail "no-refresh-token dead" "the dead account was selected"

# expired ACCESS token with a live refresh token is NOT dead (claude refreshes it)
printf '{"claudeAiOauth":{"accessToken":"sk-ant-oat01-stale","refreshToken":"r","expiresAt":1000000000000,"refreshTokenExpiresAt":9999999999999}}' > "$ACC/acct-01/.credentials.json"
rm -f "$ACC/acct-02/.credentials.json"    # acct-01 is the only candidate
out="$(claude 2>&1)"
check "stale access token + live refresh token stays selectable" "CFG=acct-01" "$out"
printf '%s' "$HEALTHY_CREDS" > "$ACC/acct-02/.credentials.json"

# a dead credential beside a portable token still authenticates — via the token
printf '%s' "$DEAD_CREDS" > "$ACC/acct-01/.credentials.json"
printf '%s' "$DEAD_CREDS" > "$ACC/acct-02/.credentials.json"
printf 'sk-ant-oat01-rescue-token' > "$ACC/acct-01/server.token"
out="$(claude 2>&1)"
check "dead creds + portable token still authenticate" "TOK=sk-ant-oat01-rescue-token" "$out"
rm -f "$ACC/acct-01/server.token"

# every account dead => stock passthrough. The reason lands in selection.log; the
# stderr hint is terminal-only, so a service-spawned `claude -p` stays byte-clean.
out="$(claude 2>&1)"
check "all logins dead -> stock passthrough" "CFG=none" "$out"
case "$out" in *claude-multiacc:*) t_fail "all-dead stderr" "wrote a notice to a non-tty stderr" ;;
  *) t_ok "all logins dead -> no stderr noise for services" ;; esac
grep -q "all-expired: falling back" "$ACC/selection.log" \
  && t_ok "all logins dead -> logged with the fix" || t_fail "all-dead log" "no all-expired line"
printf '%s' "$HEALTHY_CREDS" > "$ACC/acct-01/.credentials.json"
printf '%s' "$HEALTHY_CREDS" > "$ACC/acct-02/.credentials.json"

# ---- 9d. the .expired marker: excludes, and self-heals on a newer credential ----
printf '%s\nreason=auth-error marked_at=now detail=test\n' "$now" > "$ACC/acct-01/.expired"
touch -t 202001010101 "$ACC/acct-01/.credentials.json"   # credential OLDER than the marker
all2=1
for _ in $(seq 1 10); do
  out="$(claude 2>&1)"
  case "$out" in *CFG=acct-02*) ;; *) all2=0 ;; esac
done
[ "$all2" = "1" ] && t_ok ".expired marker excludes the account" \
  || t_fail ".expired marker" "marked account was still selected"
touch -t 202001010101 "$ACC/acct-01/.expired"            # credential now NEWER than the marker
touch "$ACC/acct-01/.credentials.json"
out="$(claude 2>&1)"                                     # any selection pass re-evaluates it
[ ! -f "$ACC/acct-01/.expired" ] && t_ok "a newer credential clears the .expired marker" \
  || t_fail ".expired self-heal" "marker survived a re-login"
rm -f "$ACC/acct-01/.expired"

# ---- 9e. an auth failure parks the account (not a 10-minute cooldown) ----------
# Rate limits heal on their own; a dead grant does not — so it gets .expired, and the
# run still completes on another account.
printf '{"fetched_at":%s,"max_percent":1,"weekly_percent":1,"session_percent":1,"buckets":[]}' "$now" > "$ACC/acct-01/limits.json"
printf '{"fetched_at":%s,"max_percent":50,"weekly_percent":50,"session_percent":50,"buckets":[]}' "$now" > "$ACC/acct-02/limits.json"
echo "authfail:acct-01" > "$FAKE_CTL"
out="$(claude -p hello < /dev/null 2>/dev/null)"
rc=$?
{ [ "$rc" = "0" ] && case "$out" in *CFG=acct-02*) true ;; *) false ;; esac; } \
  && t_ok "auth failure retries onto a healthy account" \
  || t_fail "auth failure retry" "rc=$rc out=$out"
[ -f "$ACC/acct-01/.expired" ] && t_ok "auth failure writes .expired" \
  || t_fail "auth failure marker" ".expired missing"
[ ! -f "$ACC/acct-01/.limited" ] && t_ok "auth failure is not treated as a rate limit" \
  || t_fail "auth failure marker" "got a 10-minute .limited cooldown instead"
# The shim's park is a GUESS from one run, so it carries its own expiry: once the soft
# window passes the account returns to the pool with no external help.
grep -q "soft_until=" "$ACC/acct-01/.expired" \
  && t_ok "a shim-written park carries a soft expiry" \
  || t_fail "soft park" "no soft_until in the shim-written marker"
printf '%s\nreason=auth-error soft_until=%s detail=elapsed\n' "$((now-7200))" "$((now-10))" > "$ACC/acct-01/.expired"
touch -t 202001010101 "$ACC/acct-01/.credentials.json"
out="$(CLAUDE_ACCOUNT='' claude 2>&1)"
[ ! -f "$ACC/acct-01/.expired" ] && t_ok "an elapsed soft park releases the account" \
  || t_fail "soft park expiry" "the account stayed parked past soft_until"
touch "$ACC/acct-01/.credentials.json"
rm -f "$FAKE_CTL" "$ACC/acct-01/.expired" "$ACC"/acct-*/limits.json

# ...and the park is NOT triggered by the model merely talking about auth. This is the
# hot path for `claude -p`: the grep also sees the model's own answer on stdout.
cat > "$FAKEBIN/claude-chatty" <<'EOF'
#!/usr/bin/env bash
echo "Your nginx returns 403 Forbidden. Please run: nginx -t to check the config."
echo "hook failed" >&2
exit 2
EOF
chmod +x "$FAKEBIN/claude-chatty"
mv "$FAKEBIN/claude" "$FAKEBIN/claude.real"; mv "$FAKEBIN/claude-chatty" "$FAKEBIN/claude"
claude -p "why does nginx 403" < /dev/null >/dev/null 2>&1
mv "$FAKEBIN/claude" "$FAKEBIN/claude-chatty"; mv "$FAKEBIN/claude.real" "$FAKEBIN/claude"
if [ -f "$ACC/acct-01/.expired" ] || [ -f "$ACC/acct-02/.expired" ]; then
  t_fail "answer text must not park an account" "the model mentioning 403/'please run' parked an account"
else
  t_ok "a model answer mentioning 403 does not park an account"
fi
rm -f "$ACC"/acct-*/.limited "$ACC"/acct-*/.expired

# ---- 9f. org-disabled accounts are parked too ---------------------------------
# "Your organization has disabled Claude subscription access for Claude Code" is not an
# auth failure (the credential is perfectly valid) and not a rate limit — but the
# account fails EVERY call, so it must leave the pool. A re-login cannot fix it.
printf '{"fetched_at":%s,"max_percent":1,"weekly_percent":1,"session_percent":1,"buckets":[]}' "$now" > "$ACC/acct-01/limits.json"
printf '{"fetched_at":%s,"max_percent":50,"weekly_percent":50,"session_percent":50,"buckets":[]}' "$now" > "$ACC/acct-02/limits.json"
echo "orgfail:acct-01" > "$FAKE_CTL"
out="$(claude -p hello < /dev/null 2>/dev/null)"
rc=$?
{ [ "$rc" = "0" ] && case "$out" in *CFG=acct-02*) true ;; *) false ;; esac; } \
  && t_ok "org-disabled account retries onto a healthy one" \
  || t_fail "org-block retry" "rc=$rc out=$out"
grep -q "reason=org-blocked" "$ACC/acct-01/.expired" 2>/dev/null \
  && t_ok "org-disabled account is parked as org-blocked" \
  || t_fail "org-block marker" "no reason=org-blocked marker"
rm -f "$FAKE_CTL" "$ACC"/acct-*/limits.json
# it stays out of the pool...
all2=1
for _ in $(seq 1 10); do
  out="$(claude 2>&1)"
  case "$out" in *CFG=acct-02*) ;; *) all2=0 ;; esac
done
[ "$all2" = "1" ] && t_ok "org-blocked account excluded from selection" \
  || t_fail "org-block exclusion" "the blocked account was still selected"
# ...and a park for an ORG BLOCK survives a credential rewrite. The token refresher
# rewrites .credentials.json every few hours; treating that as "the account recovered"
# handed blocked accounts straight back to the pool and runs kept failing with
# "Your organization has disabled Claude subscription access".
touch "$ACC/acct-01/.credentials.json"          # credential now NEWER than the marker
all2=1
for _ in $(seq 1 10); do
  out="$(claude 2>&1)"
  case "$out" in *CFG=acct-02*) ;; *) all2=0 ;; esac
done
[ "$all2" = "1" ] && t_ok "an org block survives a credential refresh" \
  || t_fail "org-block stickiness" "a rewritten credential un-parked a blocked account"
[ -f "$ACC/acct-01/.expired" ] && t_ok "the org-block marker is not deleted by a refresh" \
  || t_fail "org-block stickiness" "marker removed by a credential rewrite"
# a CREDENTIAL park still self-heals on a newer credential (unchanged behavior)
printf '%s\nreason=auth-error detail=test\n' "$((now-100))" > "$ACC/acct-01/.expired"
touch -t 202001010101 "$ACC/acct-01/.expired"    # marker older than the credential
touch "$ACC/acct-01/.credentials.json"
claude >/dev/null 2>&1
[ ! -f "$ACC/acct-01/.expired" ] && t_ok "a credential park still clears on a newer credential" \
  || t_fail "credential park" "marker survived a fresh credential"
printf '%s\nreason=org-blocked detail=test\n' "$((now-100))" > "$ACC/acct-01/.expired"
# `expired` explains it and points at the same fix as any other dead login
out="$(claude-accounts expired 2>&1)"
check "expired labels an org-blocked account" "BLOCKED" "$out"
check "expired explains the org block" "organization has disabled" "$out"
check "expired points org blocks at relogin" "relogin acct-01" "$out"
out="$(claude-accounts list 2>&1)"
check "list flags org-blocked accounts" "ORG-BLOCKED" "$out"
# relogin targets them like any other dead login
out="$(CLAUDE_MULTIACC_FORCE_TTY=1 FAKE_EMAIL=a@test claude-accounts relogin --yes 2>&1)"
check "relogin targets org-blocked accounts" "acct-01 login saved" "$out"
[ ! -f "$ACC/acct-01/.expired" ] && t_ok "a successful re-login clears an org block" \
  || t_fail "org-block relogin" "marker survived the re-login"
rm -f "$ACC/acct-01/.expired"

# ---- 10. token-only account exports CLAUDE_CODE_OAUTH_TOKEN ------------------
mkdir -p "$ACC/acct-03"
printf 'sk-ant-oat01-tok-for-03' > "$ACC/acct-03/server.token"
out="$(CLAUDE_ACCOUNT=acct-03 claude 2>&1)"
check "token-only account exports token" "TOK=sk-ant-oat01-tok-for-03" "$out"

# ---- 10b. user args survive selection (regression: pick_best must not touch "$@") --
out="$(claude --echo-stdin </dev/null 2>&1)"
[ -z "$out" ] && t_ok "args reach the real binary intact (no positional clobber)" \
  || t_fail "arg passthrough" "--echo-stdin ignored, got: $out"

# ---- 11. exit code passthrough (no retry on non-auth failure) ----------------
claude -p --exit7 >/dev/null 2>"$WORK/err11"
rc=$?
[ "$rc" = "7" ] && t_ok "exit code passthrough (rc=7)" || t_fail "exit code passthrough" "rc=$rc"

# ---- 12. -p retry on rate limit switches account -----------------------------
rm -f "$ACC"/acct-*/.limited
echo "fail:acct-01" > "$FAKE_CTL"
ok12=1
for _ in $(seq 1 10); do
  out="$(claude -p hello < /dev/null 2>/dev/null)"
  rc=$?
  { [ "$rc" = "0" ] && case "$out" in *CFG=acct-0[23]*) true ;; *) false ;; esac; } || ok12=0
done
[ "$ok12" = "1" ] && t_ok "-p retry recovers via another account" || t_fail "-p retry" "some run failed or used acct-01 output"
[ -f "$ACC/acct-01/.limited" ] && t_ok "failed account got error-cooldown marker" || t_fail "cooldown marker" "missing"
rm -f "$FAKE_CTL" "$ACC/acct-01/.limited"

# ---- 12b. pipe stdin skips retry buffering but passes bytes through -----------
out="$(printf 'pipe-data' | claude -p --echo-stdin 2>/dev/null)"
[ "$out" = "pipe-data" ] && t_ok "pipe stdin passes through (no retry buffering)" || t_fail "pipe stdin passthrough" "got: $out"

# ---- 12c. HOME unset: shim still fails open into passthrough ------------------
out="$(env -u HOME -u CLAUDE_ACCOUNTS_DIR claude 2>&1)"
check "HOME unset -> passthrough, no crash" "CFG=none" "$out"

# ---- 12d. error-cooldown marker survives a clean limits pass -------------------
printf '%s\nbucket=error-cooldown percent=? reason=error-cooldown\n' "$(( $(date +%s) + 600 ))" > "$ACC/acct-01/.limited"
cat > "$WORK/usage-mid.json" <<'EOF'
{"limits":[{"kind":"session","percent":10,"resets_at":"2099-01-01T00:00:00+00:00","scope":null}]}
EOF
CLAUDE_MULTIACC_USAGE_URL="file://$WORK/usage-mid.json" claude-accounts limits --quiet
[ -f "$ACC/acct-01/.limited" ] && t_ok "error-cooldown marker survives clean limits refresh" || t_fail "cooldown vs limits" "marker was cleared early"
rm -f "$ACC/acct-01/.limited"

# ---- 12e. TTY stdin: retry disabled so the terminal is never swapped for /dev/null --
if command -v script >/dev/null 2>&1; then
  # `claude -p` on a TTY with no prompt arg reads the terminal. Under a PTY the shim
  # must take the plain exec path (stdin inherited), never the buffered retry path.
  if [ "$(uname -s)" = "Darwin" ]; then
    ptyout="$(script -q /dev/null env PATH="$PATH" CLAUDE_ACCOUNTS_DIR="$ACC" FAKE_CTL="$FAKE_CTL" claude -p --echo-stdin <<'PTYIN' 2>/dev/null
tty-typed-prompt
PTYIN
)"
  else
    ptyout="$(script -qec "claude -p --echo-stdin" /dev/null <<'PTYIN' 2>/dev/null
tty-typed-prompt
PTYIN
)"
  fi
  case "$ptyout" in
    *tty-typed-prompt*) t_ok "TTY stdin reaches claude (retry path does not eat it)" ;;
    *) t_fail "TTY stdin" "terminal input was lost: $(printf '%s' "$ptyout" | head -c 80)" ;;
  esac
else
  t_ok "TTY stdin test skipped (no script(1))"
fi

# ---- 13. stdin/stdout byte fidelity through retry path -----------------------
printf 'line1\nline2 with spaces\n' > "$WORK/stdin13"
out="$(claude -p --echo-stdin < "$WORK/stdin13" 2>/dev/null)"
expected="$(cat "$WORK/stdin13")"
[ "$out" = "$expected" ] && t_ok "stdin/stdout byte fidelity (-p pipe)" || t_fail "stdin fidelity" "got: $out"

# ---- 14. selection log written ------------------------------------------------
grep -qE '^[0-9]{4}-[0-9]{2}-[0-9]{2}T.*acct-0[123] weekly=[0-9?]+% session=[0-9?]+% pwd=' "$ACC/selection.log" \
  && t_ok "selection.log format" || t_fail "selection.log format" "no matching lines"
grep -qE 'sk-ant-oat|accessToken|refreshToken' "$ACC/selection.log" \
  && t_fail "selection.log has no secrets" "a token leaked into the log" \
  || t_ok "selection.log leaks no secrets"

# ---- 15. CLI: list / import / remove ------------------------------------------
out="$(claude-accounts list 2>&1)"
check "list shows accounts" "acct-01" "$out"
printf '{"claudeAiOauth":{"accessToken":"sk-ant-oat01-t4","refreshToken":"r","expiresAt":9999999999999,"refreshTokenExpiresAt":9999999999999}}' > "$WORK/import-creds.json"
out="$(claude-accounts import c@test --id acct-04 --creds "$WORK/import-creds.json" --mode copy --no-sync 2>&1)"
check "import account" "Imported acct-04" "$out"
[ -f "$ACC/acct-04/.credentials.json" ] && t_ok "import copied credentials" || t_fail "import copied credentials" "file missing"
out="$(claude-accounts list 2>&1)"
check "imported account listed" "c@test" "$out"
out="$(claude-accounts remove acct-04 --yes 2>&1)"
check "remove account" "Removed acct-04" "$out"
[ ! -d "$ACC/acct-04" ] && t_ok "remove deleted dir" || t_fail "remove deleted dir" "dir still there"

# ---- 15b. duplicate-email guards ------------------------------------------------
# add of a named, already-present email => graceful SKIP before any sign-in (exit 0)
out="$(claude-accounts add a@test 2>&1)"
rc=$?
check "add skips a duplicate email with a message" "already added as acct-01 — skipping" "$out"
[ "$rc" = "0" ] && t_ok "duplicate add exits 0 (graceful skip)" || t_fail "duplicate add rc" "rc=$rc"
[ ! -d "$ACC/acct-04" ] && t_ok "duplicate add created nothing" || t_fail "duplicate add" "dir created"
# import stays strict (it's the lower-level command): refuses a duplicate
out="$(claude-accounts import a@test --id acct-09 --no-sync 2>&1)"
rc=$?
check "import refuses duplicate email" "already registered as acct-01" "$out"
[ ! -d "$ACC/acct-09" ] && t_ok "duplicate import created nothing" || t_fail "duplicate import" "dir created"
out="$(claude-accounts import a@test --id acct-01 --no-sync 2>&1)"
check "import same-id update allowed" "Imported acct-01" "$out"

# ---- 15c. login-first add (DEFAULT full-login flow): registers after verified auth --
out="$(CLAUDE_MULTIACC_FORCE_TTY=1 FAKE_EMAIL=new@test claude-accounts add new@test 2>&1)"
check "add registers after verified login" "Registered acct-04 for new@test" "$out"
[ -f "$ACC/acct-04/.credentials.json" ] && t_ok "default add writes .credentials.json (full login)" || t_fail "add creds" "missing"
[ ! -f "$ACC/acct-04/server.token" ] && t_ok "default add does NOT mint a setup-token" || t_fail "add token" "unexpected server.token"
out="$(CLAUDE_ACCOUNT=acct-04 claude 2>&1)"
check "new account usable via pin (oauth creds)" "CFG=acct-04" "$out"
claude-accounts remove acct-04 --yes >/dev/null 2>&1

# ---- 15c1. add --token: portable setup-token instead of creds ------------------------
out="$(CLAUDE_MULTIACC_FORCE_TTY=1 FAKE_EMAIL=tok@test claude-accounts add tok@test --token 2>&1)"
check "add --token registers via portable token" "Registered acct-04 for tok@test" "$out"
[ -s "$ACC/acct-04/server.token" ] && t_ok "add --token writes server.token" || t_fail "add --token" "missing"
out="$(CLAUDE_ACCOUNT=acct-04 claude 2>&1)"
check "token account exports CLAUDE_CODE_OAUTH_TOKEN" "TOK=sk-ant-oat01-FAKE" "$out"
claude-accounts remove acct-04 --yes >/dev/null 2>&1

# ---- 15c0. add with NO email: derives it from the verified sign-in -------------------
out="$(CLAUDE_MULTIACC_FORCE_TTY=1 FAKE_EMAIL=derived@test claude-accounts add 2>&1)"
check "add without email registers the signed-in address" "Registered acct-04 for derived@test" "$out"
claude-accounts list 2>&1 | grep -q "derived@test" && t_ok "email-less add lands in the manifest" \
  || t_fail "email-less add" "derived@test not registered"
claude-accounts remove acct-04 --yes >/dev/null 2>&1
# add with no email, but the signed-in email is already registered => refuse + clean up
out="$(CLAUDE_MULTIACC_FORCE_TTY=1 FAKE_EMAIL=a@test claude-accounts add 2>&1)"
rc=$?
check "email-less add skips a duplicate signed-in email" "a@test is already added as acct-01 — skipping" "$out"
[ "$rc" = "0" ] && t_ok "email-less duplicate skip exits 0" || t_fail "email-less dup rc" "rc=$rc"
[ ! -d "$ACC/acct-04" ] && t_ok "email-less duplicate cleaned up" || t_fail "email-less dup cleanup" "dir left"
# add with no email but identity can't be read back => refuse (can't label the account)
out="$(CLAUDE_MULTIACC_FORCE_TTY=1 FAKE_AUTH_FAIL=1 claude-accounts add 2>&1)"
rc=$?
check "email-less add needs a readable identity" "identity could not be read back" "$out"
[ ! -d "$ACC/acct-04" ] && t_ok "unverifiable email-less add cleaned up" || t_fail "unverifiable cleanup" "dir left"

# ---- 15c2. --tui is accepted as a no-op alias (full login is the default now) --------
out="$(CLAUDE_MULTIACC_FORCE_TTY=1 FAKE_EMAIL=tui@test claude-accounts add tui@test --tui 2>&1)"
check "add --tui still works (alias for default login)" "Registered acct-04 for tui@test" "$out"
[ -f "$ACC/acct-04/.credentials.json" ] && t_ok "--tui writes creds like the default" || t_fail "tui creds" "missing"
claude-accounts remove acct-04 --yes >/dev/null 2>&1

# ---- 15d. aborted login leaves zero traces ------------------------------------------
out="$(CLAUDE_MULTIACC_FORCE_TTY=1 FAKE_LOGIN_FAIL=1 claude-accounts add ghost@test 2>&1)"
rc=$?
check "aborted login detected" "login failed or aborted" "$out"
[ "$rc" != "0" ] && t_ok "aborted add exits nonzero" || t_fail "aborted add rc" "rc=0"
[ ! -d "$ACC/acct-04" ] && t_ok "aborted add cleaned up its dir" || t_fail "aborted add cleanup" "dir left behind"
claude-accounts list 2>&1 | grep -q ghost@test && t_fail "aborted add not in manifest" "ghost@test registered" || t_ok "aborted add not in manifest"
# aborted --token path too
out="$(CLAUDE_MULTIACC_FORCE_TTY=1 FAKE_TOKEN_FAIL=1 claude-accounts add ghost2@test --token 2>&1)"
rc=$?
check "aborted --token add detected" "sign-in failed or aborted" "$out"
[ ! -d "$ACC/acct-04" ] && t_ok "aborted --token add cleaned up" || t_fail "aborted token cleanup" "dir left"

# ---- 15e. sign-in as an already-registered email is rejected + cleaned ---------------
out="$(CLAUDE_MULTIACC_FORCE_TTY=1 FAKE_EMAIL=a@test claude-accounts add brand@test 2>&1)"
rc=$?
check "wrong-account sign-in skips (already added)" "a@test is already added as acct-01 — skipping" "$out"
[ "$rc" = "0" ] && t_ok "wrong-account skip exits 0" || t_fail "wrong-account rc" "rc=$rc"
[ ! -d "$ACC/acct-04" ] && t_ok "wrong-account sign-in cleaned up" || t_fail "wrong-account cleanup" "dir left behind"

# ---- 15f. login command completes auth for an existing auth-less account -------------
claude-accounts import pending@test --id acct-08 --no-sync >/dev/null 2>&1
out="$(CLAUDE_MULTIACC_FORCE_TTY=1 FAKE_EMAIL=pending@test claude-accounts login acct-08 2>&1)"
check "login completes existing account (full login)" "acct-08 login saved" "$out"
[ -f "$ACC/acct-08/.credentials.json" ] && t_ok "login writes .credentials.json" || t_fail "login creds" "missing"
out="$(CLAUDE_MULTIACC_FORCE_TTY=1 FAKE_EMAIL=other@test claude-accounts login acct-08 2>&1)"
rc=$?
check "login email mismatch refused" "nothing saved" "$out"
[ "$rc" != "0" ] && t_ok "mismatched login exits nonzero" || t_fail "mismatched login rc" "rc=0"
# login --token writes a portable token for the same account
out="$(CLAUDE_MULTIACC_FORCE_TTY=1 FAKE_EMAIL=pending@test claude-accounts login acct-08 --token 2>&1)"
check "login --token saves a portable token" "acct-08 token saved" "$out"
[ -s "$ACC/acct-08/server.token" ] && t_ok "login --token writes server.token" || t_fail "login token" "missing"
claude-accounts remove acct-08 --yes >/dev/null 2>&1

# ---- 15f2. expired: the re-login worklist, and relogin fixes it ----------------------
# `expired` must agree with the shim exactly: what it lists is what selection skips.
printf '{"claudeAiOauth":{"accessToken":"sk-ant-oat01-dead","refreshToken":"r","expiresAt":1000000000000,"refreshTokenExpiresAt":1000000000000}}' > "$ACC/acct-01/.credentials.json"
out="$(claude-accounts expired 2>&1)"
rc=$?
check "expired lists the dead account" "acct-01" "$out"
check "expired explains why" "refresh token expired" "$out"
check "expired prints the fix" "claude-accounts relogin acct-01" "$out"
[ "$rc" = "1" ] && t_ok "expired exits 1 when a login needs a human" || t_fail "expired rc" "rc=$rc"
out="$(claude-accounts expired --quiet 2>&1)"
[ "$out" = "acct-01" ] && t_ok "expired --quiet prints bare ids" || t_fail "expired --quiet" "got: $out"
out="$(claude-accounts list 2>&1)"
check "list flags the dead login" "EXPIRED-LOGIN" "$out"
out="$(claude-accounts status 2>&1)"
check "status marks it unselectable" "LOGIN EXPIRED" "$out"
# relogin with no arguments targets exactly that worklist
out="$(CLAUDE_MULTIACC_FORCE_TTY=1 FAKE_EMAIL=a@test claude-accounts relogin --yes 2>&1)"
rc=$?
check "relogin re-authenticates the expired account" "acct-01 login saved" "$out"
check "relogin reports what it did" "re-authenticated 1 of 1" "$out"
[ "$rc" = "0" ] && t_ok "relogin exits 0 when every account recovered" || t_fail "relogin rc" "rc=$rc"
out="$(claude-accounts expired 2>&1)"
rc=$?
check "expired is clean after relogin" "can authenticate" "$out"
[ "$rc" = "0" ] && t_ok "expired exits 0 on a healthy pool" || t_fail "expired rc (healthy)" "rc=$rc"
out="$(claude 2>&1)"
case "$out" in *CFG=acct-0*) t_ok "re-authenticated account is selectable again" ;;
  *) t_fail "post-relogin selection" "got: $out" ;; esac
# a stale .expired marker must not survive a successful login
printf '%s\nreason=auth-error detail=test\n' "$now" > "$ACC/acct-01/.expired"
touch -t 202001010101 "$ACC/acct-01/.credentials.json"
CLAUDE_MULTIACC_FORCE_TTY=1 FAKE_EMAIL=a@test claude-accounts relogin acct-01 --yes >/dev/null 2>&1
[ ! -f "$ACC/acct-01/.expired" ] && t_ok "login clears the dead-auth marker" \
  || t_fail "login marker clear" ".expired survived a successful login"
# explicit ids are validated
out="$(claude-accounts relogin acct-99 --yes 2>&1)"
rc=$?
check "relogin rejects an unknown account" "unknown account" "$out"
[ "$rc" != "0" ] && t_ok "unknown relogin target exits nonzero" || t_fail "relogin unknown rc" "rc=0"

# ---- 15f3. a re-login that did NOT work must not be reported as fixed -----------------
# The target already HAS a (dead) .credentials.json, so "the file exists" proves nothing:
# an aborted sign-in would otherwise clear the dead-auth marker and hand the account back.
printf '{"claudeAiOauth":{"accessToken":"sk-ant-oat01-dead","refreshToken":"r","expiresAt":1000000000000,"refreshTokenExpiresAt":1000000000000}}' > "$ACC/acct-01/.credentials.json"
printf '%s\nreason=refresh-token-expired detail=test\n' "$now" > "$ACC/acct-01/.expired"
touch -t 202001010101 "$ACC/acct-01/.credentials.json"
out="$(CLAUDE_MULTIACC_FORCE_TTY=1 FAKE_LOGIN_FAIL=1 FAKE_EMAIL=a@test claude-accounts relogin acct-01 --yes 2>&1)"
rc=$?
check "aborted re-login is reported as a failure" "login failed or aborted" "$out"
[ "$rc" != "0" ] && t_ok "aborted relogin exits nonzero" || t_fail "aborted relogin rc" "rc=0"
[ -f "$ACC/acct-01/.expired" ] && t_ok "aborted re-login leaves the account parked" \
  || t_fail "aborted relogin" "the dead-auth marker was cleared by a failed sign-in"
out="$(claude-accounts expired --quiet 2>&1)"
check "the account is still on the worklist after a failed re-login" "acct-01" "$out"
# a real re-login then fixes it
out="$(CLAUDE_MULTIACC_FORCE_TTY=1 FAKE_EMAIL=a@test claude-accounts relogin acct-01 --yes 2>&1)"
check "a working re-login is reported as fixed" "re-authenticated 1 of 1" "$out"
[ ! -f "$ACC/acct-01/.expired" ] && t_ok "a working re-login clears the park" \
  || t_fail "relogin clear" "marker survived a successful sign-in"

# ---- 15f4. audit robustness: bad data must never read as a healthy pool ---------------
# a non-numeric expiry must not crash the audit (it would blank the worklist silently)
cp "$ACC/acct-01/.credentials.json" "$WORK/creds.bak"
printf '{"claudeAiOauth":{"accessToken":"a","refreshToken":"r","expiresAt":"soon","refreshTokenExpiresAt":[]}}' > "$ACC/acct-01/.credentials.json"
out="$(claude-accounts expired 2>&1)"
rc=$?
case "$out" in *Traceback*) t_fail "audit survives a weird credential" "python traceback" ;;
  *) t_ok "a non-numeric expiry does not crash the audit" ;; esac
out="$(claude-accounts list 2>&1)"
check "list survives a weird credential" "acct-01" "$out"
# a truncated (mid-write) credential is judged the SAME way the shim judges it
printf '{"claudeAiOauth":{"accessToken":"live","refreshToken":"rt","expiresAt":9999999999999,' > "$ACC/acct-01/.credentials.json"
out="$(claude-accounts expired --quiet 2>&1)"
case "$out" in *acct-01*) t_fail "audit agrees with the shim on a truncated credential" \
    "audit calls it dead while the shim still selects it" ;;
  *) t_ok "a truncated credential is judged like the shim judges it" ;; esac
cp "$WORK/creds.bak" "$ACC/acct-01/.credentials.json"

# ---- 15g. parallel adds (different terminals) get distinct ids, no lock-busy error ----
( CLAUDE_MULTIACC_FORCE_TTY=1 FAKE_EMAIL=par1@test claude-accounts add >"$WORK/p1.out" 2>&1 ) &
pA=$!
( CLAUDE_MULTIACC_FORCE_TTY=1 FAKE_EMAIL=par2@test claude-accounts add >"$WORK/p2.out" 2>&1 ) &
pB=$!
wait "$pA"; wait "$pB"
both="$(cat "$WORK/p1.out" "$WORK/p2.out" 2>/dev/null)"
printf '%s' "$both" | grep -q "in progress" \
  && t_fail "parallel add: no lock-busy error" "got the 'another add in progress' error" \
  || t_ok "parallel add: neither reports 'another add in progress'"
regA="$(claude-accounts list 2>&1 | grep par1@test | awk '{print $1}')"
regB="$(claude-accounts list 2>&1 | grep par2@test | awk '{print $1}')"
{ [ -n "$regA" ] && [ -n "$regB" ] && [ "$regA" != "$regB" ]; } \
  && t_ok "parallel add: both registered on distinct ids ($regA, $regB)" \
  || t_fail "parallel add ids" "regA=$regA regB=$regB"
# and no duplicate/second entry crept in for either email
{ [ "$(claude-accounts list 2>&1 | grep -c par1@test)" = "1" ] && [ "$(claude-accounts list 2>&1 | grep -c par2@test)" = "1" ]; } \
  && t_ok "parallel add: exactly one entry per email" || t_fail "parallel add dup" "duplicate created"
[ -n "$regA" ] && claude-accounts remove "$regA" --yes >/dev/null 2>&1
[ -n "$regB" ] && claude-accounts remove "$regB" --yes >/dev/null 2>&1

# ---- 15h. two parallel adds signing into the SAME email: exactly one wins ------------
( CLAUDE_MULTIACC_FORCE_TTY=1 FAKE_EMAIL=same@test claude-accounts add >"$WORK/s1.out" 2>&1 ) &
sA=$!
( CLAUDE_MULTIACC_FORCE_TTY=1 FAKE_EMAIL=same@test claude-accounts add >"$WORK/s2.out" 2>&1 ) &
sB=$!
wait "$sA"; wait "$sB"
n="$(claude-accounts list 2>&1 | grep -c same@test)"
[ "$n" = "1" ] && t_ok "same-email parallel add: exactly one registered (other skipped)" \
  || t_fail "same-email parallel add" "count=$n (expected 1)"
cat "$WORK/s1.out" "$WORK/s2.out" 2>/dev/null | grep -q "already added as\|skipping" \
  && t_ok "same-email parallel add: loser skipped gracefully" || t_fail "same-email skip msg" "no skip message"
regS="$(claude-accounts list 2>&1 | grep same@test | awk '{print $1}')"
[ -n "$regS" ] && claude-accounts remove "$regS" --yes >/dev/null 2>&1

# ---- 15i. dedupe removes accounts registered twice (keeps one per email) --------------
out="$(claude-accounts dedupe 2>&1)"
check "dedupe on a clean pool is a no-op" "No duplicate accounts" "$out"
# manufacture a duplicate directly in the manifest (a pre-fix leftover) + a dir for it
claude-accounts import twin@test --id acct-06 --creds "$WORK/import-creds.json" --mode copy --no-sync >/dev/null 2>&1
python3 - "$ACC/accounts.json" <<'EOF'
import json, os, sys
d = json.load(open(sys.argv[1]))
d['accounts'].append({'id': 'acct-07', 'email': 'twin@test', 'home': 'mac'})
json.dump(d, open(sys.argv[1] + '.tmp', 'w'), indent=2); os.replace(sys.argv[1] + '.tmp', sys.argv[1])
EOF
mkdir -p "$ACC/acct-07"
out="$(claude-accounts list 2>&1)"
check "list warns about a duplicate email" "twin@test is registered 2x" "$out"
out="$(claude-accounts dedupe --yes 2>&1)"
check "dedupe reports removal" "Removed 1 duplicate" "$out"
[ "$(claude-accounts list 2>&1 | grep -c twin@test)" = "1" ] && t_ok "dedupe keeps exactly one per email" || t_fail "dedupe count" "not 1"
# it kept the authed one (acct-06 has creds), removed the bare acct-07
claude-accounts list 2>&1 | grep -q "acct-06.*twin@test" && t_ok "dedupe kept the authenticated account" || t_fail "dedupe keep-authed" "kept the wrong one"
claude-accounts remove acct-06 --yes >/dev/null 2>&1

# ---- 16. CLI: limits marking via fixture endpoint ------------------------------
cat > "$WORK/usage-high.json" <<'EOF'
{"limits":[
  {"kind":"session","percent":29,"resets_at":"2099-01-01T00:00:00+00:00","scope":null},
  {"kind":"weekly_all","percent":55,"resets_at":"2099-01-02T00:00:00+00:00","scope":null},
  {"kind":"weekly_scoped","percent":93,"resets_at":"2099-01-03T00:00:00+00:00","scope":{"model":{"display_name":"Fable"}}}
]}
EOF
out="$(CLAUDE_MULTIACC_USAGE_URL="file://$WORK/usage-high.json" claude-accounts limits 2>&1)"
check "limits marks Fable bucket >=90%" "LIMITED weekly_scoped:Fable at 93%" "$out"
[ -f "$ACC/acct-01/.limited" ] && t_ok ".limited written by limits" || t_fail ".limited written" "missing"
grep -q "weekly_scoped:Fable" "$ACC/acct-01/limits.json" \
  && t_ok "limits.json has Fable bucket" || t_fail "limits.json Fable bucket" "missing"
out="$(CLAUDE_ACCOUNT='' claude 2>&1)"  # pool should now avoid marked accounts (all marked -> fallback fine)
cat > "$WORK/usage-low.json" <<'EOF'
{"limits":[
  {"kind":"session","percent":10,"resets_at":"2099-01-01T00:00:00+00:00","scope":null},
  {"kind":"weekly_scoped","percent":45,"resets_at":"2099-01-03T00:00:00+00:00","scope":{"model":{"display_name":"Fable"}}}
]}
EOF
out="$(CLAUDE_MULTIACC_USAGE_URL="file://$WORK/usage-low.json" claude-accounts limits 2>&1)"
check "limits clears marker under threshold" "marker cleared" "$out"
[ ! -f "$ACC/acct-01/.limited" ] && t_ok "marker removed after clear" || t_fail "marker removed" "still present"

# ---- 16-weekly. limits classifies session vs weekly and records both signals --------
cat > "$WORK/usage-3bucket.json" <<'EOF'
{"limits":[
  {"kind":"session","group":"session","percent":88,"resets_at":"2099-01-01T00:00:00+00:00","scope":null},
  {"kind":"weekly_all","group":"weekly","percent":40,"resets_at":"2099-01-05T00:00:00+00:00","scope":null},
  {"kind":"weekly_scoped","group":"weekly","percent":55,"resets_at":"2099-01-05T00:00:00+00:00","scope":{"model":{"display_name":"Fable"}}}
]}
EOF
CLAUDE_MULTIACC_USAGE_URL="file://$WORK/usage-3bucket.json" claude-accounts limits --quiet
python3 - "$ACC/acct-01/limits.json" <<'EOF'
import json, sys
d = json.load(open(sys.argv[1]))
assert d["session_percent"] == 88, d
assert d["weekly_percent"] == 55, d          # max of the two weekly buckets, not session
assert d["max_percent"] == 88, d             # peak of ALL buckets (drives exclusion)
groups = {b["name"]: b["group"] for b in d["buckets"]}
assert groups["session"] == "session", groups
assert groups["weekly_all"] == "weekly", groups
assert groups["weekly_scoped:Fable"] == "weekly", groups
EOF
[ $? -eq 0 ] && t_ok "limits records weekly_percent(55) + session_percent(88) with correct groups" \
  || t_fail "weekly/session classification" "see limits.json"
# a session bucket at 88 must NOT poison weekly ranking: score uses weekly(55), not 88
sc="$(python3 -c "import json;d=json.load(open('$ACC/acct-01/limits.json'));print(d['weekly_percent']*1000+d['session_percent'])")"
[ "$sc" = "55088" ] && t_ok "ranking score weighs weekly over session (55088)" \
  || t_fail "ranking score" "got $sc"
rm -f "$ACC/acct-01/limits.json" "$ACC/acct-01/.limited"

# ---- 16a. future-proofing: works if the Fable bucket separation disappears ---------
cat > "$WORK/usage-no-fable.json" <<'EOF'
{"limits":[
  {"kind":"session","percent":20,"resets_at":"2099-01-01T00:00:00+00:00","scope":null},
  {"kind":"weekly_all","percent":95,"resets_at":"2099-01-02T00:00:00+00:00","scope":null}
]}
EOF
out="$(CLAUDE_MULTIACC_USAGE_URL="file://$WORK/usage-no-fable.json" claude-accounts limits 2>&1)"
check "no-Fable payload still marks on weekly_all" "LIMITED weekly_all at 95%" "$out"
grep -q "weekly_scoped" "$ACC/acct-01/limits.json" && t_fail "no stale Fable bucket" "old bucket kept" || t_ok "buckets reflect current payload only"

# ---- 16a2. legacy payload (no limits[] at all) falls back to five_hour/seven_day ----
cat > "$WORK/usage-legacy.json" <<'EOF'
{"five_hour":{"utilization":12.0,"resets_at":"2099-01-01T00:00:00+00:00"},
 "seven_day":{"utilization":34.0,"resets_at":"2099-01-02T00:00:00+00:00"}}
EOF
out="$(CLAUDE_MULTIACC_USAGE_URL="file://$WORK/usage-legacy.json" claude-accounts limits 2>&1)"
check "legacy payload parsed via fallback" "five_hour=12%" "$out"
check "legacy marker cleared under threshold" "acct-01: ok" "$out"

# ---- 16a3. malformed/garbage payload entries never crash the refresher --------------
cat > "$WORK/usage-garbage.json" <<'EOF'
{"limits":[
  {"kind":"session","percent":"NaNsense","scope":{"model":"stringnotdict"}},
  "not-even-a-dict",
  {"percent":41,"scope":{"model":{"display_name":null,"id":"claude-fable-5"}}},
  {"kind":"weekly_all","percent":null}
]}
EOF
out="$(CLAUDE_MULTIACC_USAGE_URL="file://$WORK/usage-garbage.json" claude-accounts limits 2>&1)"
rc=$?
[ "$rc" = "0" ] && t_ok "garbage payload exits 0 (fail open)" || t_fail "garbage payload rc" "rc=$rc: $out"
check "parseable entry survives garbage siblings" "unknown:claude-fable-5=41%" "$out"

# ---- 16a4. fetch throttle: fresh data is not re-fetched -----------------------------
CLAUDE_MULTIACC_USAGE_URL="file://$WORK/usage-low.json" claude-accounts limits --quiet
before="$(python3 -c "import json; print(json.load(open('$ACC/acct-01/limits.json'))['fetched_at'])")"
mv "$WORK/usage-low.json" "$WORK/usage-low.hidden"   # a real fetch would now fail loudly
out="$(CLAUDE_MULTIACC_MIN_FETCH=45 CLAUDE_MULTIACC_USAGE_URL="file://$WORK/usage-low.json" claude-accounts limits 2>&1)"
after="$(python3 -c "import json; print(json.load(open('$ACC/acct-01/limits.json'))['fetched_at'])")"
{ [ "$before" = "$after" ] && ! printf '%s' "$out" | grep -q "fetch failed"; } \
  && t_ok "fresh data skips re-fetch (rate-limit protection)" \
  || t_fail "fetch throttle" "re-fetched despite fresh data: $out"
mv "$WORK/usage-low.hidden" "$WORK/usage-low.json"
out="$(CLAUDE_MULTIACC_MIN_FETCH=45 CLAUDE_MULTIACC_USAGE_URL="file://$WORK/usage-low.json" claude-accounts limits --force 2>&1)"
check "--force bypasses the throttle" "acct-01: ok" "$out"

# ---- 16a5. 429 sets backoff, is honored, and clears on success -----------------------
python3 - "$ACC/acct-01/limits.json" <<'EOF'
import json, os, sys, time
p = sys.argv[1]
d = json.load(open(p))
d['fetched_at'] = 0                        # stale enough to fetch
d['retry_after'] = int(time.time()) + 600  # but a 429 backoff is in force
d['backoff'] = 600
json.dump(d, open(p + '.tmp', 'w'), indent=1); os.replace(p + '.tmp', p)
EOF
out="$(CLAUDE_MULTIACC_USAGE_URL="file://$WORK/usage-low.json" claude-accounts limits 2>&1)"
check "429 backoff honored" "acct-01: backing off after 429" "$out"
out="$(CLAUDE_MULTIACC_USAGE_URL="file://$WORK/usage-low.json" claude-accounts limits --force 2>&1)"
check "--force overrides backoff" "acct-01: ok" "$out"
python3 -c "
import json, sys
d = json.load(open('$ACC/acct-01/limits.json'))
sys.exit(0 if 'retry_after' not in d and 'backoff' not in d else 1)" \
  && t_ok "successful fetch clears backoff state" || t_fail "backoff cleared" "retry_after/backoff persisted"

# ---- 16b. expired-bearer account: oauth refresh attempted; fail-open when it fails ----
mkdir -p "$ACC/acct-05"
printf '{"claudeAiOauth":{"accessToken":"sk-ant-oat01-old","refreshToken":"sk-ant-ort01-oldrefresh","expiresAt":1000,"refreshTokenExpiresAt":9999999999999}}' > "$ACC/acct-05/.credentials.json"
claude-accounts import e@test --id acct-05 --no-sync >/dev/null 2>&1
out="$(CLAUDE_MULTIACC_USAGE_URL="file://$WORK/usage-low.json" claude-accounts limits 2>&1)"
rc=$?
check "failed oauth refresh logged with backoff" "acct-05: oauth refresh failed" "$out"
check "expired bearer logged, not fatal" "acct-05: no fresh bearer" "$out"
[ "$rc" = "0" ] && t_ok "limits exits 0 with expired-bearer account" || t_fail "limits exit code" "rc=$rc"
[ ! -f "$ACC/acct-05/limits.json" ] && t_ok "no limits.json fabricated for expired account" || t_fail "expired acct limits.json" "unexpectedly written"
[ -f "$ACC/acct-05/.oauth-refresh.json" ] && t_ok "refresh failure recorded in .oauth-refresh.json" || t_fail "refresh backoff file" "missing"

# ---- 16b2. refresh backoff honored: even a now-working endpoint is not retried early --
cat > "$WORK/token-ok.json" <<'EOF'
{"access_token":"sk-ant-oat01-refreshednew","refresh_token":"sk-ant-ort01-rotatednew","expires_in":28800,"refresh_token_expires_in":2592000}
EOF
out="$(CLAUDE_MULTIACC_TOKEN_URL="file://$WORK/token-ok.json" CLAUDE_MULTIACC_USAGE_URL="file://$WORK/usage-low.json" claude-accounts limits 2>&1)"
check "refresh backoff honored (no early retry)" "acct-05: no fresh bearer" "$out"
grep -q "sk-ant-oat01-refreshednew" "$ACC/acct-05/.credentials.json" \
  && t_fail "backoff prevented refresh" "credentials rewritten inside the backoff window" \
  || t_ok "no refresh inside the backoff window"

# ---- 16b3. --force bypasses refresh backoff: rotated credential persisted + fetch ok --
out="$(CLAUDE_MULTIACC_TOKEN_URL="file://$WORK/token-ok.json" CLAUDE_MULTIACC_USAGE_URL="file://$WORK/usage-low.json" claude-accounts limits --force 2>&1)"
check "--force refreshes the expired oauth token" "acct-05: oauth access token refreshed" "$out"
check "refreshed account fetches telemetry" "acct-05: ok" "$out"
python3 - "$ACC/acct-05/.credentials.json" <<'EOF'
import json, os, stat, sys, time
p = sys.argv[1]
o = json.load(open(p))['claudeAiOauth']
assert o['accessToken'] == 'sk-ant-oat01-refreshednew', o['accessToken']
assert o['refreshToken'] == 'sk-ant-ort01-rotatednew', 'refresh token was not rotated'
assert o['expiresAt'] / 1000.0 > time.time() + 3600, 'expiresAt not advanced'
assert o['refreshTokenExpiresAt'] / 1000.0 > time.time() + 86400, 'refreshTokenExpiresAt not advanced'
mode = stat.S_IMODE(os.stat(p).st_mode)
assert mode == 0o600, oct(mode)
EOF
[ $? -eq 0 ] && t_ok "rotated credential persisted with 0600" || t_fail "credential rotation" "see assertions above"
[ ! -f "$ACC/acct-05/.oauth-refresh.json" ] && t_ok "successful refresh clears the backoff file" || t_fail "refresh backoff clear" "file still present"
[ -f "$ACC/acct-05/limits.json" ] && t_ok "telemetry written right after refresh" || t_fail "limits.json after refresh" "missing"
grep -qE "sk-ant-ort01|sk-ant-oat01-refreshednew" "$ACC/limits.log" \
  && t_fail "limits.log leaks no tokens" "a token leaked into limits.log" \
  || t_ok "limits.log leaks no tokens"

# ---- 16b4. steady state: fresh data means no refresh and no fetch (quiet skip) --------
out="$(CLAUDE_MULTIACC_MIN_FETCH=45 CLAUDE_MULTIACC_USAGE_URL="file://$WORK/usage-low.json" claude-accounts limits 2>&1)"
printf '%s' "$out" | grep -q "acct-05" \
  && t_fail "fresh account skipped silently" "unexpected acct-05 output: $out" \
  || t_ok "fresh account skipped silently (no refresh, no fetch)"

# ---- 16b5. an EXPIRED refresh token is never sent: clear re-login message -------------
printf '{"claudeAiOauth":{"accessToken":"sk-ant-oat01-old","refreshToken":"sk-ant-ort01-dead","expiresAt":1000,"refreshTokenExpiresAt":1000}}' > "$ACC/acct-05/.credentials.json"
rm -f "$ACC/acct-05/limits.json" "$ACC/acct-05/.oauth-refresh.json"
out="$(CLAUDE_MULTIACC_TOKEN_URL="file://$WORK/token-ok.json" CLAUDE_MULTIACC_USAGE_URL="file://$WORK/usage-low.json" claude-accounts limits 2>&1)"
check "expired refresh token => re-login message" "re-login needed" "$out"
grep -q "sk-ant-oat01-refreshednew" "$ACC/acct-05/.credentials.json" \
  && t_fail "dead refresh token never used" "credentials rewritten from a dead refresh token" \
  || t_ok "dead refresh token never used"
# ...and the account is PARKED, so the shim stops handing work to a login that cannot work
grep -q "reason=refresh-token-expired" "$ACC/acct-05/.expired" 2>/dev/null \
  && t_ok "limits parks an account with a dead refresh token" \
  || t_fail "limits .expired marker" "no .expired written for a dead refresh token"
# a later successful fetch (fresh login, or a token bearer) unparks it
printf 'sk-ant-oat01-portable-unpark' > "$ACC/acct-05/server.token"
rm -f "$ACC/acct-05/limits.json"
out="$(CLAUDE_MULTIACC_USAGE_URL="file://$WORK/usage-low.json" claude-accounts limits --force 2>&1)"
[ ! -f "$ACC/acct-05/.expired" ] && t_ok "a successful usage fetch clears the dead-auth marker" \
  || t_fail "unpark on success" ".expired survived a successful authenticated fetch"
# ...but an ORG-BLOCKED account authenticates fine — telemetry proves nothing about it,
# so its marker must survive a successful fetch (only a real call or re-login lifts it).
printf '%s\nreason=org-blocked marked_at=now detail=test\n' "$now" > "$ACC/acct-05/.expired"
rm -f "$ACC/acct-05/limits.json"
out="$(CLAUDE_MULTIACC_USAGE_URL="file://$WORK/usage-low.json" claude-accounts limits --force 2>&1)"
[ -f "$ACC/acct-05/.expired" ] && t_ok "a usage fetch does not unpark an org-blocked account" \
  || t_fail "org-block unpark" "telemetry cleared an org block it cannot observe"
rm -f "$ACC/acct-05/.expired" "$ACC/acct-05/server.token"

# ---- 16b6. RECENTLY-expired token is left alone (a live session owns it) --------------
# The 5-min REFRESH_MIN_EXPIRED gate is the rotation-safety core: a token that expired
# moments ago may be mid-refresh by a live claude session; grants must not race it.
# Not even --force may bypass this.
recent_ms="$(python3 -c 'import time; print(int((time.time()-100)*1000))')"
printf '{"claudeAiOauth":{"accessToken":"sk-ant-oat01-recent","refreshToken":"sk-ant-ort01-live","expiresAt":%s,"refreshTokenExpiresAt":9999999999999}}' "$recent_ms" > "$ACC/acct-05/.credentials.json"
rm -f "$ACC/acct-05/limits.json" "$ACC/acct-05/.oauth-refresh.json"
out="$(CLAUDE_MULTIACC_TOKEN_URL="file://$WORK/token-ok.json" CLAUDE_MULTIACC_USAGE_URL="file://$WORK/usage-low.json" claude-accounts limits --force 2>&1)"
check "recently-expired token is not refreshed (even --force)" "acct-05: no fresh bearer" "$out"
grep -q "sk-ant-oat01-recent" "$ACC/acct-05/.credentials.json" \
  && t_ok "recently-expired credential left untouched" \
  || t_fail "REFRESH_MIN_EXPIRED gate" "credential was rewritten within the 5-min grace window"
[ ! -f "$ACC/acct-05/.oauth-refresh.json" ] && t_ok "no backoff recorded for a gated (skipped) refresh" \
  || t_fail "gated refresh backoff" ".oauth-refresh.json written despite the gate"

# ---- 16b7. server.token accounts are NEVER oauth-refreshed (token bearer wins) ---------
printf '{"claudeAiOauth":{"accessToken":"sk-ant-oat01-old","refreshToken":"sk-ant-ort01-x","expiresAt":1000,"refreshTokenExpiresAt":9999999999999}}' > "$ACC/acct-05/.credentials.json"
printf 'sk-ant-oat01-portable-token-05' > "$ACC/acct-05/server.token"
rm -f "$ACC/acct-05/limits.json" "$ACC/acct-05/.oauth-refresh.json"
out="$(CLAUDE_MULTIACC_TOKEN_URL="file://$WORK/token-ok.json" CLAUDE_MULTIACC_USAGE_URL="file://$WORK/usage-low.json" claude-accounts limits --force 2>&1)"
check "token-bearer account fetches without refresh" "acct-05: ok" "$out"
grep -q "sk-ant-oat01-refreshednew" "$ACC/acct-05/.credentials.json" \
  && t_fail "server.token exempts oauth refresh" "oauth creds were rotated despite a portable token" \
  || t_ok "server.token account never oauth-refreshed (grant not run)"
python3 -c "import json,sys; sys.exit(0 if json.load(open('$ACC/acct-05/limits.json'))['source']=='token' else 1)" \
  && t_ok "telemetry fetched via the portable token" || t_fail "token bearer source" "source != token"
rm -f "$ACC/acct-05/server.token"

# ---- 16b9. a 4xx from the TOKEN endpoint must not park the whole pool -----------------
# Every account hits the same endpoint with the same client id, so a provider incident,
# a WAF page or a client-id change 4xxs ALL of them at once. Only OAuth's own
# invalid_grant (or a repeated refusal of this one account) is proof of a dead grant.
srv_script="$WORK/token-server.py"
cat > "$srv_script" <<'EOF'
import http.server, json, sys, threading
class H(http.server.BaseHTTPRequestHandler):
    def log_message(self, *a): pass
    def _send(self, code, body):
        raw = body.encode()
        self.send_response(code)
        self.send_header('Content-Type', 'application/json')
        self.send_header('Content-Length', str(len(raw)))
        self.end_headers()
        self.wfile.write(raw)
    def do_POST(self):
        try:
            self.rfile.read(int(self.headers.get('Content-Length') or 0))
        except Exception:
            pass
        if self.path == '/invalid-grant':
            self._send(400, json.dumps({'error': 'invalid_grant'}))
        else:
            self._send(400, '<html>gateway says no</html>')
srv = http.server.HTTPServer(('127.0.0.1', 0), H)
print(srv.server_address[1], flush=True)
srv.serve_forever()
EOF
port=""
python3 "$srv_script" > "$WORK/token-port" 2>/dev/null &
srv_pid=$!
for _ in $(seq 1 20); do
  port="$(head -1 "$WORK/token-port" 2>/dev/null)"
  case "$port" in ''|*[!0-9]*) port=""; sleep 0.2 ;; *) break ;; esac
done
if [ -z "$port" ]; then
  kill "$srv_pid" 2>/dev/null; wait "$srv_pid" 2>/dev/null || true
  t_ok "token-endpoint 4xx tests skipped (cannot bind a loopback port here)"
else
  dead_acct="$ACC/acct-05"
  mk_stale_creds() {
    printf '{"claudeAiOauth":{"accessToken":"sk-ant-oat01-old","refreshToken":"sk-ant-ort01-x","expiresAt":1000,"refreshTokenExpiresAt":9999999999999}}' > "$dead_acct/.credentials.json"
    rm -f "$dead_acct/limits.json" "$dead_acct/.oauth-refresh.json" "$dead_acct/.expired"
  }
  # a NON-invalid_grant 400 (provider incident): back off, do NOT park
  mk_stale_creds
  out="$(CLAUDE_MULTIACC_TOKEN_URL="http://127.0.0.1:$port/boom" CLAUDE_MULTIACC_USAGE_URL="file://$WORK/usage-low.json" claude-accounts limits --force 2>&1)"
  [ ! -f "$dead_acct/.expired" ] && t_ok "one opaque 4xx from the token endpoint does not park an account" \
    || t_fail "token 4xx park" "a single non-invalid_grant 400 parked the account"
  [ -f "$dead_acct/.oauth-refresh.json" ] && t_ok "an opaque 4xx still records a backoff" \
    || t_fail "token 4xx backoff" "no .oauth-refresh.json written"
  # ...but a repeatedly-refused account IS parked (3rd strike)
  CLAUDE_MULTIACC_TOKEN_URL="http://127.0.0.1:$port/boom" CLAUDE_MULTIACC_USAGE_URL="file://$WORK/usage-low.json" claude-accounts limits --force >/dev/null 2>&1
  CLAUDE_MULTIACC_TOKEN_URL="http://127.0.0.1:$port/boom" CLAUDE_MULTIACC_USAGE_URL="file://$WORK/usage-low.json" claude-accounts limits --force >/dev/null 2>&1
  [ -f "$dead_acct/.expired" ] && t_ok "a repeatedly-refused grant is parked on the third strike" \
    || t_fail "token 4xx strikes" "still not parked after three refusals"
  # invalid_grant is proof on the FIRST refusal
  mk_stale_creds
  out="$(CLAUDE_MULTIACC_TOKEN_URL="http://127.0.0.1:$port/invalid-grant" CLAUDE_MULTIACC_USAGE_URL="file://$WORK/usage-low.json" claude-accounts limits --force 2>&1)"
  grep -q "invalid_grant" "$dead_acct/.expired" 2>/dev/null \
    && t_ok "invalid_grant parks the account immediately" \
    || t_fail "invalid_grant park" "no marker for an explicit invalid_grant"
  kill "$srv_pid" 2>/dev/null; wait "$srv_pid" 2>/dev/null || true
  rm -f "$dead_acct/.expired" "$dead_acct/.oauth-refresh.json"
fi

# ---- 16b8. malformed claudeAiOauth (null) degrades that account ONLY (fail open) -------
# {"claudeAiOauth": null} is valid JSON from an interrupted/reset credential write; it
# must not abort the refresher — accounts AFTER it in the manifest must still be fetched.
# acct-01 is first in the manifest, so corrupting it exercises the loop guarantee.
cp "$ACC/acct-01/.credentials.json" "$WORK/acct01-creds.bak"
printf '{"claudeAiOauth": null}' > "$ACC/acct-01/.credentials.json"
rm -f "$ACC/acct-01/limits.json" "$ACC/acct-01/.oauth-refresh.json" "$ACC/acct-02/limits.json"
out="$(CLAUDE_MULTIACC_TOKEN_URL="file://$WORK/token-ok.json" CLAUDE_MULTIACC_USAGE_URL="file://$WORK/usage-low.json" claude-accounts limits --force 2>&1)"
rc=$?
[ "$rc" = "0" ] && t_ok "null claudeAiOauth exits 0 (fail open)" || t_fail "null claudeAiOauth rc" "rc=$rc: $out"
check "null claudeAiOauth degrades only that account" "acct-01: no fresh bearer" "$out"
[ -f "$ACC/acct-02/limits.json" ] && t_ok "accounts after a malformed one still refresh" \
  || t_fail "fail-open loop guarantee" "acct-02 was starved by acct-01's malformed creds"
cp "$WORK/acct01-creds.bak" "$ACC/acct-01/.credentials.json"
claude-accounts remove acct-05 --yes >/dev/null 2>&1

# ---- 16c. codex-review: security hardening -----------------------------------------
# path traversal via a hand-edited manifest id must never touch the filesystem
mkdir -p "$WORK/canary" && : > "$WORK/canary/DO_NOT_DELETE"
cp "$ACC/accounts.json" "$WORK/manifest.bak"
python3 - "$ACC/accounts.json" <<'EOF'
import json, os, sys
d = json.load(open(sys.argv[1]))
d['accounts'].append({'id': '../canary', 'email': 'evil@test', 'home': 'mac'})
json.dump(d, open(sys.argv[1] + '.tmp', 'w'), indent=2); os.replace(sys.argv[1] + '.tmp', sys.argv[1])
EOF
out="$(claude-accounts remove ../canary --yes 2>&1)"
rc=$?
[ -f "$WORK/canary/DO_NOT_DELETE" ] && t_ok "remove refuses path-traversal id (no deletion)" \
  || t_fail "path traversal" "remove ../canary DELETED files outside the pool"
[ "$rc" != "0" ] && t_ok "traversal id rejected nonzero" || t_fail "traversal rc" "rc=0"
claude-accounts list 2>&1 | grep -q "\.\./canary" \
  && t_fail "invalid ids filtered from listings" "traversal id surfaced" \
  || t_ok "invalid manifest ids are filtered out"
cp "$WORK/manifest.bak" "$ACC/accounts.json"

# sync is Mac-only (source of truth); its input-validation guards can only be exercised
# on Darwin. On Linux `sync` refuses up front, so skip with a note rather than fail.
if [ "$(uname -s)" = "Darwin" ]; then
  # remote command injection via manifest server_root
  python3 - "$ACC/accounts.json" <<'EOF'
import json, os, sys
d = json.load(open(sys.argv[1]))
d['server_root'] = "/tmp/x'; touch /tmp/multiacc_PWNED; #"
json.dump(d, open(sys.argv[1] + '.tmp', 'w'), indent=2); os.replace(sys.argv[1] + '.tmp', sys.argv[1])
EOF
  rm -f /tmp/multiacc_PWNED
  out="$(CLAUDE_MULTIACC_NO_SYNC=0 claude-accounts sync 2>&1)"
  [ ! -f /tmp/multiacc_PWNED ] && t_ok "sync rejects injected server_root (no command executed)" \
    || { t_fail "command injection" "server_root injection EXECUTED"; rm -f /tmp/multiacc_PWNED; }
  check "injected server_root refused" "not a plain absolute path" "$out"
  cp "$WORK/manifest.bak" "$ACC/accounts.json"

  # accountless manifest must not blank the server pool
  python3 - "$ACC/accounts.json" <<'EOF'
import json, os, sys
d = json.load(open(sys.argv[1])); d['accounts'] = []
json.dump(d, open(sys.argv[1] + '.tmp', 'w'), indent=2); os.replace(sys.argv[1] + '.tmp', sys.argv[1])
EOF
  out="$(CLAUDE_MULTIACC_NO_SYNC=0 claude-accounts sync 2>&1)"
  rc=$?
  check "empty manifest refuses to sync" "refusing to blank the target pools" "$out"
  [ "$rc" != "0" ] && t_ok "empty-manifest sync exits nonzero" || t_fail "empty sync rc" "rc=0"
  cp "$WORK/manifest.bak" "$ACC/accounts.json"

  # a REPLICA pool never pushes (side file, not manifest — the manifest is what
  # gets pushed TO replicas)
  printf 'replica\n' > "$ACC/sync-role"
  out="$(CLAUDE_MULTIACC_NO_SYNC=0 claude-accounts sync 2>&1)"
  rc=$?
  check "replica pool refuses to push" "sync replica" "$out"
  [ "$rc" = "0" ] && t_ok "replica sync exits 0 (informational, not an error)" || t_fail "replica sync rc" "rc=$rc"
  # ...and auto_sync (after a mutation) is silent about it
  out="$(CLAUDE_MULTIACC_NO_SYNC=0 claude-accounts import replica-test@x --id acct-31 --no-sync 2>&1
         CLAUDE_MULTIACC_NO_SYNC=0 claude-accounts remove acct-31 --yes 2>&1)"
  case "$out" in *"sync failed"*) t_fail "replica auto_sync" "a replica mutation warned about sync: $out" ;;
    *) t_ok "replica auto_sync is silent (no push, no warning)" ;; esac
  rm -f "$ACC/sync-role"

  # peer targets are validated with the same injection guards as the primary.
  # A fake ssh/rsync harness (prepended to PATH only for these calls) records every
  # remote invocation, so "nothing pushed" and "both targets pushed, in order" are
  # verified from the actual command stream, not inferred from silence.
  SSHFAKE="$WORK/sshfake"
  SSHLOG="$WORK/sshfake.log"
  mkdir -p "$SSHFAKE"
  cat > "$SSHFAKE/ssh" <<'EOF'
#!/usr/bin/env bash
printf 'ssh %s\n' "$*" >> "${SSHLOG:?}"
case "$*" in *"${SSH_FAIL_HOST:-@@none@@}"*) exit 1 ;; esac
exit 0
EOF
  cat > "$SSHFAKE/rsync" <<'EOF'
#!/usr/bin/env bash
printf 'rsync %s\n' "$*" >> "${SSHLOG:?}"
exit 0
EOF
  chmod +x "$SSHFAKE/ssh" "$SSHFAKE/rsync"
  export SSHLOG

  python3 - "$ACC/accounts.json" <<'EOF'
import json, os, sys
d = json.load(open(sys.argv[1]))
d['peers'] = [{'target': "gas@peer; touch /tmp/multiacc_PEER_PWNED", 'root': '/tmp/x', 'repo': '/tmp/y'}]
json.dump(d, open(sys.argv[1] + '.tmp', 'w'), indent=2); os.replace(sys.argv[1] + '.tmp', sys.argv[1])
EOF
  rm -f /tmp/multiacc_PEER_PWNED
  : > "$SSHLOG"
  out="$(PATH="$SSHFAKE:$PATH" CLAUDE_MULTIACC_NO_SYNC=0 claude-accounts sync 2>&1)"
  rc=$?
  check "injected peer target refused" "peer target is not a plain user@host" "$out"
  [ "$rc" != "0" ] && t_ok "bad peer exits nonzero" || t_fail "peer validation rc" "rc=0"
  [ ! -s "$SSHLOG" ] && t_ok "bad peer: nothing pushed anywhere (no ssh/rsync ran)" \
    || t_fail "peer pre-validation" "remote commands ran before peer validation: $(head -2 "$SSHLOG")"
  [ ! -f /tmp/multiacc_PEER_PWNED ] && t_ok "peer injection never executed" \
    || { t_fail "peer injection" "peer target injection EXECUTED"; rm -f /tmp/multiacc_PEER_PWNED; }
  cp "$WORK/manifest.bak" "$ACC/accounts.json"

  # a MALFORMED peer entry (missing fields / not an object) is fatal, never skipped
  python3 - "$ACC/accounts.json" <<'EOF'
import json, os, sys
d = json.load(open(sys.argv[1]))
d['peers'] = [{'root': '/tmp/x', 'repo': '/tmp/y'}]   # no target
json.dump(d, open(sys.argv[1] + '.tmp', 'w'), indent=2); os.replace(sys.argv[1] + '.tmp', sys.argv[1])
EOF
  out="$(PATH="$SSHFAKE:$PATH" CLAUDE_MULTIACC_NO_SYNC=0 claude-accounts sync 2>&1)"
  rc=$?
  check "peer with missing field is fatal, not skipped" "incomplete" "$out"
  [ "$rc" != "0" ] && t_ok "incomplete peer exits nonzero" || t_fail "incomplete peer rc" "rc=0"
  python3 - "$ACC/accounts.json" <<'EOF'
import json, os, sys
d = json.load(open(sys.argv[1]))
d['peers'] = ["gas@peer"]   # not an object
json.dump(d, open(sys.argv[1] + '.tmp', 'w'), indent=2); os.replace(sys.argv[1] + '.tmp', sys.argv[1])
EOF
  out="$(PATH="$SSHFAKE:$PATH" CLAUDE_MULTIACC_NO_SYNC=0 claude-accounts sync 2>&1)"
  check "non-object peer entry is fatal" "malformed" "$out"
  cp "$WORK/manifest.bak" "$ACC/accounts.json"

  # a SUCCESSFUL multi-target sync pushes primary first, then the peer — verified
  # from the recorded command stream
  python3 - "$ACC/accounts.json" <<'EOF'
import json, os, sys
d = json.load(open(sys.argv[1]))
d['peers'] = [{'target': 'gas@peer1', 'root': '/Users/gas/.claude-accounts', 'repo': '/Users/gas/claude-multiacc'}]
json.dump(d, open(sys.argv[1] + '.tmp', 'w'), indent=2); os.replace(sys.argv[1] + '.tmp', sys.argv[1])
EOF
  : > "$SSHLOG"
  out="$(PATH="$SSHFAKE:$PATH" CLAUDE_MULTIACC_NO_SYNC=0 claude-accounts sync 2>&1)"
  rc=$?
  [ "$rc" = "0" ] && t_ok "multi-target sync succeeds" || t_fail "multi-target sync" "rc=$rc: $out"
  check "multi-target sync reports the peer" "+ 1 peer(s)" "$out"
  grep -q "rsync.*root@203.0.113.1:/root/.claude-accounts/accounts.json" "$SSHLOG" \
    && grep -q "rsync.*gas@peer1:/Users/gas/.claude-accounts/accounts.json" "$SSHLOG" \
    && t_ok "manifest pushed to BOTH targets" \
    || t_fail "multi-target pushes" "missing a manifest push: $(grep accounts.json "$SSHLOG")"
  first_primary="$(grep -n "root@203.0.113.1" "$SSHLOG" | head -1 | cut -d: -f1)"
  first_peer="$(grep -n "gas@peer1" "$SSHLOG" | head -1 | cut -d: -f1)"
  [ -n "$first_primary" ] && [ -n "$first_peer" ] && [ "$first_primary" -lt "$first_peer" ] \
    && t_ok "primary target pushed before the peer" \
    || t_fail "target order" "primary=$first_primary peer=$first_peer"
  n_remov="$(grep -c "for dd in acct-" "$SSHLOG")"
  [ "$n_remov" = "2" ] && t_ok "removal propagation ran on each target" \
    || t_fail "per-target removal" "expected 2 removal sweeps, saw $n_remov"
  grep -q "post-sync" "$SSHLOG" && t_ok "post-sync hooks invoked" || t_fail "post-sync hooks" "none recorded"
  # a primary failure aborts the WHOLE sync: the peer is never contacted
  : > "$SSHLOG"
  out="$(PATH="$SSHFAKE:$PATH" SSH_FAIL_HOST="root@203.0.113.1" CLAUDE_MULTIACC_NO_SYNC=0 claude-accounts sync 2>&1)"
  rc=$?
  [ "$rc" != "0" ] && t_ok "primary failure fails the sync" || t_fail "fail-fast rc" "rc=0"
  check "primary failure is loud" "cannot reach" "$out"
  grep -q "gas@peer1" "$SSHLOG" \
    && t_fail "fail-fast" "the peer was contacted after the primary failed" \
    || t_ok "peer not contacted after a primary failure"
  cp "$WORK/manifest.bak" "$ACC/accounts.json"

  # only an EXACT 'replica' value suppresses sync ('not-replica' must still push)
  printf 'not-replica\n' > "$ACC/sync-role"
  out="$(PATH="$SSHFAKE:$PATH" CLAUDE_MULTIACC_NO_SYNC=0 claude-accounts sync 2>&1)"
  case "$out" in *"sync replica"*) t_fail "replica anchor" "'not-replica' suppressed sync" ;;
    *"sync ok"*) t_ok "only an exact 'replica' value suppresses sync" ;;
    *) t_fail "replica anchor" "unexpected: $out" ;; esac
  rm -f "$ACC/sync-role"
else
  t_ok "sync validation tests skipped (Mac-only feature; server refuses sync by design)"
fi

# API keys are never accepted as credentials (subscription-only requirement)
printf 'sk-ant-api03-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' > "$WORK/apikey.txt"
out="$(claude-accounts import apikey@test --id acct-11 --token-file "$WORK/apikey.txt" --no-sync 2>&1)"
rc=$?
check "import rejects an API key as token" "not a subscription setup-token" "$out"
[ ! -d "$ACC/acct-11" ] && t_ok "API-key import created nothing" || t_fail "apikey import" "acct-11 created"
out="$(printf 'sk-ant-api03-ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ' | claude-accounts mint acct-01 --paste 2>&1)"
check "mint --paste rejects an API key" "not a subscription setup-token" "$out"

# ---- 17b. verify authenticates the way the SHIM would ---------------------------------
# A dead credential next to a live portable token: the shim runs that account with the
# TOKEN, so verify must too — testing it with the dead credential would fail a healthy
# account and park it.
printf '{"claudeAiOauth":{"accessToken":"sk-ant-oat01-dead","refreshToken":"r","expiresAt":1000000000000,"refreshTokenExpiresAt":1000000000000}}' > "$ACC/acct-01/.credentials.json"
printf 'sk-ant-oat01-token-for-01' > "$ACC/acct-01/server.token"
rm -f "$ACC/acct-01/.expired"
out="$(claude-accounts verify 2>&1)"
case "$out" in *"acct-01 a@test: PASS"*) t_ok "verify uses the portable token when the credential is dead" ;;
  *) t_fail "verify token fallback" "expected acct-01 PASS, got: $(printf '%s' "$out" | grep acct-01)" ;; esac
[ ! -f "$ACC/acct-01/.expired" ] && t_ok "verify does not park an account its token can run" \
  || t_fail "verify token fallback" "healthy token-auth account was parked"
rm -f "$ACC/acct-01/server.token"
printf '{"claudeAiOauth":{"accessToken":"sk-ant-oat01-test01","refreshToken":"r","expiresAt":9999999999999,"refreshTokenExpiresAt":9999999999999}}' > "$ACC/acct-01/.credentials.json"

# ---- 17c. audit: machine vocabulary (home=server vs machine_kind=linux) ---------------
# `import --home` says mac|server; machine_kind() says mac|linux. If those are compared
# raw, an un-authenticated account ON the server reads as "the grant lives elsewhere"
# and silently drops off the re-login worklist.
mkdir -p "$ACC/acct-06"
python3 - "$ACC/accounts.json" <<'EOF'
import json, sys
doc = json.load(open(sys.argv[1]))
doc['accounts'] = [a for a in doc['accounts'] if a['id'] != 'acct-06']
doc['accounts'].append({'id': 'acct-06', 'email': 'srv@test', 'home': 'server'})
doc['accounts'].sort(key=lambda a: a['id'])
json.dump(doc, open(sys.argv[1], 'w'), indent=2)
EOF
out="$(python3 "$REPO_DIR/lib/audit.py" "$ACC" linux | awk -F'\t' '$1=="acct-06"{print $4}')"
[ "$out" = "missing" ] && t_ok "home=server on the server means NO LOGIN, not 'elsewhere'" \
  || t_fail "home vocabulary" "expected missing on linux, got: $out"
out="$(python3 "$REPO_DIR/lib/audit.py" "$ACC" mac | awk -F'\t' '$1=="acct-06"{print $4}')"
[ "$out" = "remote" ] && t_ok "home=server on the Mac is correctly 'elsewhere'" \
  || t_fail "home vocabulary" "expected remote on mac, got: $out"
python3 - "$ACC/accounts.json" <<'EOF'
import json, sys
doc = json.load(open(sys.argv[1]))
doc['accounts'] = [a for a in doc['accounts'] if a['id'] != 'acct-06']
json.dump(doc, open(sys.argv[1], 'w'), indent=2)
EOF
rm -rf "$ACC/acct-06"

# ---- 17d. an empty pool is never rendered as "all clear" ------------------------------
emptypool="$WORK/emptypool"
mkdir -p "$emptypool/tmp"
printf '{"version":1,"threshold":90,"accounts":[]}' > "$emptypool/accounts.json"
out="$(CLAUDE_ACCOUNTS_DIR="$emptypool" claude-accounts expired 2>&1)"
rc=$?
check "expired on an empty pool says so" "No accounts registered yet" "$out"
[ "$rc" = "0" ] && t_ok "empty pool exits 0" || t_fail "empty pool rc" "rc=$rc"
case "$out" in *"NO LOGIN"*) t_fail "empty pool phantom row" "invented an account from a blank line" ;;
  *) t_ok "empty pool invents no phantom account" ;; esac

out="$(claude-accounts verify --quick 2>&1)"
check "verify --quick passes oauth accounts" "acct-01 a@test: OK" "$out"
check "verify --quick counts" "0 failure(s)" "$out"

# ---- 18. CLI: status renders ------------------------------------------------------
out="$(claude-accounts status 2>&1)"
check "status shows threshold" "90%" "$out"
check "status shows account" "a@test" "$out"

# ---- 19. npm layer: cli.mjs dispatch + self-update + postinstall guards -------------
if command -v node >/dev/null 2>&1; then
  CLI="$REPO_DIR/bin/cli.mjs"
  pkgver="$(node -e "console.log(require('$REPO_DIR/package.json').version)")"
  out="$(node "$CLI" --version 2>&1)"
  check "cli --version matches package.json" "$pkgver" "$out"
  out="$(node "$CLI" --help 2>&1)"
  check "cli --help documents install" "install or update the addon" "$out"
  # passthrough to claude-accounts
  out="$(node "$CLI" list 2>&1)"
  check "cli passes through to claude-accounts (list)" "acct-01" "$out"
  out="$(node "$CLI" status 2>&1)"
  check "cli doctor/status passthrough" "threshold" "$out"
  # self-update on a non-npm, non-git tree is a logged no-op (never errors)
  out="$(claude-accounts self-update 2>&1)"
  rc=$?
  [ "$rc" = "0" ] && t_ok "self-update no-op exits 0 on a plain checkout" || t_fail "self-update rc" "rc=$rc"
  case "$out" in *"update manually"*|*"already latest"*|*"git pull"*) t_ok "self-update reports its path" ;;
    *) t_fail "self-update message" "unexpected: $out" ;; esac
  # postinstall must SKIP for a non-global install and never fail
  out="$(node "$REPO_DIR/scripts/postinstall.mjs" 2>&1)"
  rc=$?
  { [ "$rc" = "0" ] && printf '%s' "$out" | grep -q "skipping auto-setup"; } \
    && t_ok "postinstall skips (and exits 0) for a non-global install" \
    || t_fail "postinstall guard" "rc=$rc out=$out"
  out="$(CI=1 npm_config_global=true node "$REPO_DIR/scripts/postinstall.mjs" 2>&1)"
  printf '%s' "$out" | grep -q "CI environment" \
    && t_ok "postinstall skips under CI even when global" || t_fail "postinstall CI guard" "$out"
  # package.json is valid and ships the essential files list
  node -e "
    const p=require('$REPO_DIR/package.json');
    const need=['bin/','lib/','install.sh','scripts/postinstall.mjs'];
    if(!/^(\.\/)?bin\/cli\.mjs$/.test(p.bin['claude-multiacc'])) { console.error('bad bin'); process.exit(1); }
    for(const f of need) if(!p.files.includes(f)) { console.error('missing file entry: '+f); process.exit(1); }
    if(p.scripts.postinstall!=='node scripts/postinstall.mjs'){ console.error('bad postinstall'); process.exit(1); }
  " && t_ok "package.json bin/files/postinstall wired correctly" || t_fail "package.json" "see errors above"
else
  t_ok "npm-layer tests skipped (node not installed)"
fi

# ============================ CODEX PROVIDER =====================================
# The codex pool (bin/codex + bin/codex-accounts + lib/codex_audit.py) is separate
# code over separate state (~/.codex-accounts), so it gets its own sandboxed pass:
# fake `codex` binary, JWT-shaped auth fixtures, file:// usage/token endpoints.

export CODEX_ACCOUNTS_DIR="$WORK/codex-accounts"
CX="$CODEX_ACCOUNTS_DIR"
mkdir -p "$CX/tmp"
unset CODEX_HOME CODEX_ACCOUNT CODEX_SHIM_ACTIVE 2>/dev/null || true
export CODEX_MULTIACC_NO_SYNC=1
export CODEX_MULTIACC_MIN_FETCH=0
export CODEX_MULTIACC_TOKEN_URL="file://$WORK/cx-token-endpoint-missing.json"
# Default usage URL is an offline missing fixture: the SHIM's opportunistic
# background `limits --quiet` kick must never reach a real endpoint from tests
# (each limits test overrides the URL inline). The pre-armed .limits-kick throttle
# below keeps those background kicks from racing the explicit limits runs at all.
export CODEX_MULTIACC_USAGE_URL="file://$WORK/cx-usage-endpoint-missing.json"
export CODEX_MULTIACC_FORCE_TTY=1
export FAKE_CTL2="$WORK/ctl2"
: > "$CX/.limits-kick"

# Codex auth fixtures are real-shaped: auth.json carrying JWTs whose exp/email
# claims the audit decodes offline (exactly what the CLI does).
MKAUTH="$WORK/mk_cx_auth.py"
cat > "$MKAUTH" <<'EOF'
import base64, json, sys
def jwt(claims):
    enc = lambda o: base64.urlsafe_b64encode(json.dumps(o).encode()).rstrip(b'=').decode()
    return f"{enc({'alg':'RS256'})}.{enc(claims)}.sig"
path, email, exp, mode = sys.argv[1], sys.argv[2], float(sys.argv[3]), (sys.argv[4] if len(sys.argv) > 4 else '')
if mode == 'apikey':
    doc = {"auth_mode": "apikey", "OPENAI_API_KEY": "sk-test-api-key", "tokens": None, "last_refresh": None}
else:
    auth_claim = {"chatgpt_plan_type": "pro", "chatgpt_account_id": "acct-uuid"}
    tokens = {
        "id_token": jwt({"email": email, "exp": exp, "https://api.openai.com/auth": auth_claim}),
        "access_token": jwt({"exp": exp, "https://api.openai.com/auth": auth_claim}),
        "refresh_token": "rt-" + email,
        "account_id": "acct-uuid",
    }
    if mode == 'norefresh':
        tokens.pop('refresh_token')
    doc = {"auth_mode": "chatgpt", "OPENAI_API_KEY": None, "tokens": tokens,
           "last_refresh": "2026-01-01T00:00:00Z"}
json.dump(doc, open(path, 'w'))
EOF
export MKAUTH
mk_cx_auth() { python3 "$MKAUTH" "$@"; }
FUTURE_EXP=$((now + 864000))

# Fake "real" codex: prints which CODEX_HOME it ran under; scriptable login +
# failures via a control file. (Note: must not contain the hyphenated shim marker.)
cat > "$FAKEBIN/codex" <<EOF
#!/usr/bin/env bash
# fake real codex for tests (not a shim)
if [ "\${1:-}" = "login" ]; then
  shift
  # record the flags the CLI chose (device-auth default vs --browser opt-out)
  printf '%s\n' "\$*" > "\${FAKE_LOGIN_ARGS:-/dev/null}" 2>/dev/null || true
  [ -n "\${FAKE_LOGIN_FAIL:-}" ] && { echo "login aborted" >&2; exit 1; }
  # simulate a completed ChatGPT sign-in: write a JWT-shaped auth.json
  python3 "\$MKAUTH" "\${CODEX_HOME:-/dev/null}/auth.json" "\${FAKE_EMAIL:-fake@test}" "\$(( \$(date +%s) + 864000 ))"
  echo "Successfully logged in"
  exit 0
fi
ctl="\${FAKE_CTL2:-/nonexistent}"
acct="\$(basename "\${CODEX_HOME:-none}")"
if [ -f "\$ctl" ] && grep -qx "fail:\$acct" "\$ctl" 2>/dev/null; then
  echo "ERROR: 429 Too Many Requests — you have hit your usage limit" >&2
  exit 1
fi
if [ -f "\$ctl" ] && grep -qx "authfail:\$acct" "\$ctl" 2>/dev/null; then
  echo "Error: token expired. Please run codex login again" >&2
  exit 1
fi
if [ -f "\$ctl" ] && grep -qx "orgfail:\$acct" "\$ctl" 2>/dev/null; then
  echo "Codex has been disabled by your workspace admin"
  exit 1
fi
outfile=""
prev=""
for a in "\$@"; do
  case "\$prev" in -o|--output-last-message) outfile="\$a" ;; esac
  case "\$a" in
    --exit7) echo "ordinary failure, not auth related" >&2; exit 7 ;;
    --echo-stdin) cat; exit 0 ;;
  esac
  prev="\$a"
done
[ -n "\$outfile" ] && printf 'OK' > "\$outfile"
echo "CFG=\$acct"
EOF
chmod +x "$FAKEBIN/codex"

# ---- C1. passthrough: no manifest yet ---------------------------------------------
out="$(codex 2>&1)"
check "codex: passthrough without manifest" "CFG=none" "$out"

# ---- codex manifest + two chatgpt accounts ----------------------------------------
cat > "$CX/accounts.json" <<EOF
{
  "version": 1,
  "server": "root@203.0.113.1",
  "server_root": "/root/.codex-accounts",
  "server_repo": "/root/claude-multiacc",
  "threshold": 90,
  "accounts": [
    {"id": "acct-01", "email": "a@cx", "home": "mac", "added_at": "2026-08-21T00:00:00Z"},
    {"id": "acct-02", "email": "b@cx", "home": "mac", "added_at": "2026-08-21T00:00:00Z"}
  ]
}
EOF
for i in 01 02; do
  mkdir -p "$CX/acct-$i"
done
mk_cx_auth "$CX/acct-01/auth.json" a@cx "$FUTURE_EXP"
mk_cx_auth "$CX/acct-02/auth.json" b@cx "$FUTURE_EXP"

# ---- C2. passthrough guards --------------------------------------------------------
out="$(CODEX_HOME=/tmp/other codex 2>&1)"
check "codex: passthrough with CODEX_HOME" "CFG=other" "$out"
out="$(CODEX_MULTIACC_DISABLE=1 codex 2>&1)"
check "codex: passthrough when disabled" "CFG=none" "$out"

# ---- C3. headroom selection --------------------------------------------------------
cxlj() { printf '{"fetched_at":%s,"weekly_percent":%s,"session_percent":%s,"max_percent":%s,"buckets":[]}' "$now" "$1" "$2" "$3"; }
cxlj 80 10 80 > "$CX/acct-01/limits.json"
cxlj 20 10 20 > "$CX/acct-02/limits.json"
all2=1
for _ in $(seq 1 15); do
  case "$(codex 2>&1)" in *CFG=acct-02*) ;; *) all2=0 ;; esac
done
[ "$all2" = "1" ] && t_ok "codex: picks the highest weekly-headroom account" \
  || t_fail "codex headroom selection" "picked the more-utilized account"
# high session must NOT beat better weekly headroom
cxlj 10 85 85 > "$CX/acct-01/limits.json"
cxlj 70 20 70 > "$CX/acct-02/limits.json"
all1=1
for _ in $(seq 1 15); do
  case "$(codex 2>&1)" in *CFG=acct-01*) ;; *) all1=0 ;; esac
done
[ "$all1" = "1" ] && t_ok "codex: high session does NOT beat better weekly headroom" \
  || t_fail "codex weekly-over-session" "ranked the account with less weekly headroom higher"
# equal scores spread load
cxlj 10 10 10 > "$CX/acct-01/limits.json"
cxlj 10 10 10 > "$CX/acct-02/limits.json"
hits1=0; hits2=0
for _ in $(seq 1 40); do
  case "$(codex 2>&1)" in
    *CFG=acct-01*) hits1=$((hits1+1)) ;;
    *CFG=acct-02*) hits2=$((hits2+1)) ;;
  esac
done
{ [ "$hits1" -gt 0 ] && [ "$hits2" -gt 0 ] && [ $((hits1+hits2)) -eq 40 ]; } \
  && t_ok "codex: equal scores spread randomly (acct-01=$hits1 acct-02=$hits2)" \
  || t_fail "codex tie spreading" "acct-01=$hits1 acct-02=$hits2 (want both >0, total 40)"
# stale telemetry ranks neutral, never free
printf '{"fetched_at":1,"weekly_percent":1,"session_percent":1,"max_percent":1,"buckets":[]}' > "$CX/acct-01/limits.json"
cxlj 30 30 30 > "$CX/acct-02/limits.json"
out="$(codex 2>&1)"
check "codex: stale 1% loses to fresh 30%" "CFG=acct-02" "$out"
rm -f "$CX"/acct-*/limits.json

# ---- C4. pin -----------------------------------------------------------------------
out="$(CODEX_ACCOUNT=acct-02 codex 2>&1)"
check "codex: CODEX_ACCOUNT pin" "CFG=acct-02" "$out"
mkdir -p "$CX/acct-07"
out="$(CODEX_ACCOUNT=acct-07 codex 2>&1)"
check "codex: pin to auth-less dir (ceremony)" "CFG=acct-07" "$out"
rmdir "$CX/acct-07"

# ---- C5. limited marker excludes; pin overrides; expired marker self-clears ---------
printf '%s\nbucket=GPT-5.3-Codex-Spark:7d percent=95 reason=limits\n' "$((now+3600))" > "$CX/acct-01/.limited"
all2=1
for _ in $(seq 1 15); do
  case "$(codex 2>&1)" in *CFG=acct-02*) ;; *) all2=0 ;; esac
done
[ "$all2" = "1" ] && t_ok "codex: limited account excluded from pool" || t_fail "codex limited exclusion" "acct-01 was still picked"
out="$(CODEX_ACCOUNT=acct-01 codex 2>&1)"
check "codex: explicit pin wins over marker" "CFG=acct-01" "$out"
printf '%s\nbucket=5h percent=95 reason=limits\n' "$((now-10))" > "$CX/acct-01/.limited"
codex >/dev/null 2>&1
[ ! -f "$CX/acct-01/.limited" ] && t_ok "codex: expired .limited marker self-clears" \
  || t_fail "codex marker expiry" ".limited survived its reset time"

# ---- C6. all limited -> least-utilized fallback -------------------------------------
printf '%s\nbucket=7d percent=95 reason=limits\n' "$((now+3600))" > "$CX/acct-01/.limited"
printf '%s\nbucket=7d percent=99 reason=limits\n' "$((now+3600))" > "$CX/acct-02/.limited"
cxlj 95 10 95 > "$CX/acct-01/limits.json"
cxlj 99 10 99 > "$CX/acct-02/limits.json"
out="$(codex 2>&1)"
check "codex: all-limited falls back to least utilized" "CFG=acct-01" "$out"
grep -q "all-limited fallback=acct-01" "$CX/selection.log" \
  && t_ok "codex: all-limited fallback logged" || t_fail "codex fallback log" "no all-limited line"
rm -f "$CX"/acct-*/.limited "$CX"/acct-*/limits.json

# ---- C7. dead logins: .expired excludes, heals on newer credential ------------------
printf '%s\nreason=refresh-denied-http-400 marked_at=t detail=x\n' "$now" > "$CX/acct-01/.expired"
all2=1
for _ in $(seq 1 10); do
  case "$(codex 2>&1)" in *CFG=acct-02*) ;; *) all2=0 ;; esac
done
[ "$all2" = "1" ] && t_ok "codex: dead login (proven park) is never selected" \
  || t_fail "codex dead login" "a parked account was selected"
# ...not even as the all-limited fallback
printf '%s\nbucket=7d percent=95 reason=limits\n' "$((now+3600))" > "$CX/acct-02/.limited"
out="$(codex 2>&1)"
check "codex: limited-but-alive beats dead in the fallback" "CFG=acct-02" "$out"
rm -f "$CX/acct-02/.limited"
# a NEWER credential heals a credential-scoped park
sleep 1
mk_cx_auth "$CX/acct-01/auth.json" a@cx "$FUTURE_EXP"
out="$(CODEX_SHIM_SELECT=random codex 2>&1)"
[ ! -f "$CX/acct-01/.expired" ] && t_ok "codex: newer auth.json clears a credential park" \
  || t_fail "codex park heal" ".expired survived a newer credential"
# an org-blocked park survives a newer credential (policy, not credential)
printf '%s\nreason=org-blocked marked_at=t detail=x\n' "$now" > "$CX/acct-01/.expired"
sleep 1
mk_cx_auth "$CX/acct-01/auth.json" a@cx "$FUTURE_EXP"
codex >/dev/null 2>&1
[ -f "$CX/acct-01/.expired" ] && t_ok "codex: org-blocked park survives a credential refresh" \
  || t_fail "codex org park" "a token write cleared an org-policy park"
rm -f "$CX/acct-01/.expired"
# a soft-stamped park expires on its own
printf '%s\nreason=auth-error soft_until=%s marked_at=t detail=x\n' "$now" "$((now-5))" > "$CX/acct-02/.expired"
codex >/dev/null 2>&1
[ ! -f "$CX/acct-02/.expired" ] && t_ok "codex: soft_until park expires on its own" \
  || t_fail "codex soft park" "an elapsed soft park still excluded the account"

# ---- C8. exec auto-retry ------------------------------------------------------------
# acct-01 gets the strictly better score so headroom selection deterministically
# picks it FIRST; the scripted failure then forces the retry onto acct-02.
cx_first_01() { cxlj 5 5 5 > "$CX/acct-01/limits.json"; cxlj 20 20 20 > "$CX/acct-02/limits.json"; }
cx_first_01
# rate limit: retry on the other account + self-expiring cooldown for the failed one
echo "fail:acct-01" > "$FAKE_CTL2"
out="$(codex exec "hello" < /dev/null 2>&1)"
case "$out" in *CFG=acct-02*) t_ok "codex: exec retries a rate-limited account on another" ;;
  *) t_fail "codex retry" "did not land on acct-02: $out" ;; esac
if [ -f "$CX/acct-01/.limited" ]; then
  grep -q "error-cooldown" "$CX/acct-01/.limited" \
    && t_ok "codex: rate-limited account got a cooldown, not a park" \
    || t_fail "codex cooldown" "marker is not an error-cooldown"
else
  t_fail "codex cooldown" "no .limited cooldown written"
fi
rm -f "$CX/acct-01/.limited" "$FAKE_CTL2"
# auth failure parks (soft) instead of a cooldown
cx_first_01
echo "authfail:acct-01" > "$FAKE_CTL2"
codex exec "hello" < /dev/null >/dev/null 2>&1
if [ -f "$CX/acct-01/.expired" ]; then
  grep -q "reason=auth-error" "$CX/acct-01/.expired" && grep -q "soft_until=" "$CX/acct-01/.expired" \
    && t_ok "codex: auth failure parks the account with a soft stamp" \
    || t_fail "codex auth park" "marker malformed: $(cat "$CX/acct-01/.expired")"
else
  t_fail "codex auth park" "no .expired written after an auth failure"
fi
rm -f "$CX/acct-01/.expired" "$FAKE_CTL2"
# org-disabled failure parks as org-blocked
cx_first_01
echo "orgfail:acct-01" > "$FAKE_CTL2"
codex exec "hello" < /dev/null >/dev/null 2>&1
grep -q "reason=org-blocked" "$CX/acct-01/.expired" 2>/dev/null \
  && t_ok "codex: workspace-disabled failure parks as org-blocked" \
  || t_fail "codex org park from run" "marker: $(cat "$CX/acct-01/.expired" 2>/dev/null || echo none)"
rm -f "$CX/acct-01/.expired" "$FAKE_CTL2"
# ordinary failure: exit code passes through, no retry, no marker
out="$(codex exec --exit7 < /dev/null 2>&1)"
rc=$?
[ "$rc" = "7" ] && t_ok "codex: non-auth failure exit code passes through" || t_fail "codex rc passthrough" "rc=$rc"
[ ! -f "$CX/acct-01/.limited" ] && [ ! -f "$CX/acct-02/.limited" ] \
  && t_ok "codex: ordinary failure marks nothing" || t_fail "codex ordinary failure" "a marker appeared"
# retry only engages for exec: a plain (interactive-style) run with a failing account
# passes the failure straight through — no retry, no marker classification.
printf 'fail:acct-01\nfail:acct-02\n' > "$FAKE_CTL2"
out="$(codex "prompt" < /dev/null 2>&1)"
rc=$?
[ "$rc" = "1" ] && t_ok "codex: non-exec run is never retried (rc passes through)" \
  || t_fail "codex non-exec retry" "rc=$rc"
[ ! -f "$CX/acct-01/.limited" ] && [ ! -f "$CX/acct-02/.limited" ] \
  && t_ok "codex: non-exec failure writes no markers" \
  || t_fail "codex non-exec markers" "a marker appeared without the retry path"
rm -f "$FAKE_CTL2" "$CX"/acct-*/.limited "$CX"/acct-*/.expired
# stdin/stdout byte fidelity through the retry path
printf 'line1\nline2\n' > "$WORK/cx-stdin.txt"
out="$(codex exec --echo-stdin < "$WORK/cx-stdin.txt" 2>&1)"
[ "$out" = "$(printf 'line1\nline2')" ] && t_ok "codex: stdin passes byte-identically through retry buffering" \
  || t_fail "codex stdin fidelity" "got: $out"
# user args survive selection
out="$(codex exec "two words" --exit7 < /dev/null 2>&1)"
rc=$?
[ "$rc" = "7" ] && t_ok "codex: user args survive selection (pick_best untouched \$@)" \
  || t_fail "codex args" "rc=$rc out=$out"
# HOME unset: fail open into passthrough
out="$(env -u HOME -u CODEX_ACCOUNTS_DIR PATH="$PATH" "$REPO_DIR/bin/codex" 2>&1)"
check "codex: HOME unset fails open into passthrough" "CFG=none" "$out"
rm -f "$CX"/acct-*/limits.json

# ---- C9. selection log --------------------------------------------------------------
grep -qE 'acct-0[12]' "$CX/selection.log" && t_ok "codex: selection.log written" \
  || t_fail "codex selection.log" "no selections logged"

# ---- C10. CLI: list / import / remove / dedupe --------------------------------------
out="$(codex-accounts list 2>&1)"
check "codex-accounts list shows accounts" "a@cx" "$out"
check "codex-accounts list shows auth kind" "auth=chatgpt" "$out"
mk_cx_auth "$WORK/cx-import.json" c@cx "$FUTURE_EXP"
out="$(codex-accounts import c@cx --id acct-03 --auth "$WORK/cx-import.json" --no-sync 2>&1)"
check "codex-accounts import" "Imported acct-03" "$out"
[ -f "$CX/acct-03/auth.json" ] && t_ok "codex: imported auth.json in place" || t_fail "codex import" "auth.json missing"
perm="$(ls -l "$CX/acct-03/auth.json" | cut -c1-10)"
[ "$perm" = "-rw-------" ] && t_ok "codex: imported auth.json is 0600" || t_fail "codex import perms" "$perm"
# duplicate email refused without --force / --id
out="$(codex-accounts import c@cx --no-sync 2>&1)"
rc=$?
check "codex: duplicate import refused" "already registered as acct-03" "$out"
[ "$rc" != "0" ] && t_ok "codex: duplicate import exits nonzero" || t_fail "codex dup import rc" "rc=0"
# dedupe removes a forced duplicate, keeping the authed one
codex-accounts import c@cx --id acct-04 --force --no-sync >/dev/null 2>&1
out="$(codex-accounts dedupe --yes 2>&1)"
check "codex: dedupe removes the duplicate" "Removed 1 duplicate" "$out"
codex-accounts list 2>&1 | grep -q "acct-04" \
  && t_fail "codex dedupe" "acct-04 still listed" || t_ok "codex: dedupe kept one entry per email"
out="$(codex-accounts remove acct-03 --yes 2>&1)"
check "codex-accounts remove" "Removed acct-03" "$out"
[ ! -d "$CX/acct-03" ] && t_ok "codex: removed account dir deleted" || t_fail "codex remove" "dir still present"

# ---- C11. login-first add ceremony --------------------------------------------------
# derived email: no argument, identity read back from the sign-in
out="$(FAKE_EMAIL=new@cx codex-accounts add 2>&1)"
check "codex: add with no email derives it from the sign-in" "Registered acct-03 for new@cx" "$out"
grep -q '"email": "new@cx"' "$CX/accounts.json" && t_ok "codex: derived email registered" \
  || t_fail "codex add derive" "manifest lacks new@cx"
# signed in as a different account than named: register who actually authenticated
out="$(FAKE_EMAIL=other@cx codex-accounts add named@cx 2>&1)"
check "codex: mismatched sign-in registers the real account" "registering the account that actually authenticated" "$out"
grep -q '"email": "other@cx"' "$CX/accounts.json" && t_ok "codex: actual identity registered" \
  || t_fail "codex add mismatch" "manifest lacks other@cx"
codex-accounts remove acct-04 --yes >/dev/null 2>&1
# aborted login leaves zero traces
before="$(ls "$CX" | sort)"
out="$(FAKE_LOGIN_FAIL=1 codex-accounts add gone@cx 2>&1)"
rc=$?
[ "$rc" != "0" ] && t_ok "codex: aborted login exits nonzero" || t_fail "codex abort rc" "rc=0"
after="$(ls "$CX" | sort)"
[ "$before" = "$after" ] && t_ok "codex: aborted login leaves zero traces" \
  || t_fail "codex abort traces" "pool dir changed: $after"
grep -q "gone@cx" "$CX/accounts.json" && t_fail "codex abort manifest" "gone@cx registered" \
  || t_ok "codex: aborted login registered nothing"
# adding an already-registered email skips before sign-in
out="$(codex-accounts add a@cx 2>&1)"
rc=$?
check "codex: add of registered email skips gracefully" "already added as acct-01" "$out"
[ "$rc" = "0" ] && t_ok "codex: duplicate add exits 0 (no-op)" || t_fail "codex dup add rc" "rc=$rc"
# signing in AS an already-registered email (unnamed) also refuses a second entry
out="$(FAKE_EMAIL=a@cx codex-accounts add 2>&1)"
check "codex: sign-in as registered email refused" "already added as acct-01" "$out"
n="$(grep -c '"email": "a@cx"' "$CX/accounts.json")"
[ "$n" = "1" ] && t_ok "codex: no second entry for a re-signed-in email" || t_fail "codex dup signin" "count=$n"

# ---- C12. login command + expired worklist + relogin --------------------------------
# an account with no auth on this machine: login completes it
codex-accounts import d@cx --id acct-04 --no-sync >/dev/null 2>&1
out="$(codex-accounts expired 2>&1)"
rc=$?
check "codex: expired lists the auth-less account" "acct-04" "$out"
[ "$rc" != "0" ] && t_ok "codex: expired exits 1 when a human is needed" || t_fail "codex expired rc" "rc=0"
out="$(FAKE_EMAIL=d@cx codex-accounts login acct-04 2>&1)"
check "codex: login completes an auth-less account" "login saved" "$out"
[ -s "$CX/acct-04/auth.json" ] && t_ok "codex: login wrote auth.json" || t_fail "codex login" "no auth.json"
# wrong sign-in identity is refused AND the prior credential is restored — the wrong
# account's auth.json must never stay installed under this id (codex-review finding)
out="$(FAKE_EMAIL=wrong@cx codex-accounts login acct-04 2>&1)"
rc=$?
check "codex: login refuses a wrong-account sign-in" "you signed in as wrong@cx" "$out"
[ "$rc" != "0" ] && t_ok "codex: wrong-identity login exits nonzero" || t_fail "codex login identity rc" "rc=0"
got_email="$(python3 - "$CX/acct-04/auth.json" <<'EOF'
import base64, json, sys
t = json.load(open(sys.argv[1]))['tokens']['id_token']
p = t.split('.')[1]; p += '=' * (-len(p) % 4)
print(json.loads(base64.urlsafe_b64decode(p)).get('email', ''))
EOF
)"
[ "$got_email" = "d@cx" ] && t_ok "codex: refused login restores the prior credential" \
  || t_fail "codex login restore" "auth.json now belongs to: $got_email"
# the sign-in ceremony is DEVICE-CODE by default (the localhost browser callback
# cannot reach a remote/SSH machine); --browser opts into the callback flow
export FAKE_LOGIN_ARGS="$WORK/cx-login-args"
: > "$FAKE_LOGIN_ARGS"
FAKE_EMAIL=d@cx codex-accounts login acct-04 >/dev/null 2>&1
rc=$?
{ [ "$rc" = "0" ] && [ "$(cat "$FAKE_LOGIN_ARGS")" = "--device-auth" ]; } \
  && t_ok "codex: sign-in uses the device-code flow by default" \
  || t_fail "codex device default" "rc=$rc login args: '$(cat "$FAKE_LOGIN_ARGS")'"
: > "$FAKE_LOGIN_ARGS"
FAKE_EMAIL=d@cx codex-accounts login acct-04 --browser >/dev/null 2>&1
rc=$?
{ [ "$rc" = "0" ] && [ "$(cat "$FAKE_LOGIN_ARGS")" = "" ]; } \
  && t_ok "codex: --browser opts into the localhost callback flow" \
  || t_fail "codex --browser opt-out" "rc=$rc login args: '$(cat "$FAKE_LOGIN_ARGS")'"
unset FAKE_LOGIN_ARGS

# a parked account shows in expired and relogin fixes exactly that
printf '%s\nreason=refresh-denied-http-400 marked_at=t detail=x\n' "$now" > "$CX/acct-04/.expired"
out="$(codex-accounts expired 2>&1)"
check "codex: expired shows the parked account" "acct-04" "$out"
out="$(FAKE_EMAIL=d@cx codex-accounts relogin --yes 2>&1)"
check "codex: relogin re-authenticates the worklist" "re-authenticated 1 of 1" "$out"
[ ! -f "$CX/acct-04/.expired" ] && t_ok "codex: relogin cleared the park" || t_fail "codex relogin" "park survived"
# a relogin whose ceremony fails is NOT reported fixed: the dead credential stays
# on disk, and success is judged by whether the credential can AUTHENTICATE.
mk_cx_auth "$CX/acct-04/auth.json" d@cx 1000 norefresh
out="$(FAKE_LOGIN_FAIL=1 FAKE_EMAIL=d@cx codex-accounts relogin --yes 2>&1)"
rc=$?
check "codex: failed relogin says still failing" "still failing" "$out"
[ "$rc" != "0" ] && t_ok "codex: failed relogin exits nonzero" || t_fail "codex failed relogin rc" "rc=0"
out="$(codex-accounts expired 2>&1)"
check "codex: the un-fixed account is still on the worklist" "acct-04" "$out"
# api-key-only auth is not subscription auth: audited as unusable, never selected
mk_cx_auth "$CX/acct-04/auth.json" d@cx "$FUTURE_EXP" apikey
out="$(codex-accounts expired 2>&1)"
check "codex: api-key auth is called out as unsupported" "API key" "$out"
out="$(CODEX_SHIM_SELECT=random codex 2>&1)"
case "$out" in *CFG=acct-04*) t_fail "codex apikey selection" "api-key account was selected" ;;
  *) t_ok "codex: api-key account is never selected" ;; esac
codex-accounts remove acct-04 --yes >/dev/null 2>&1

# ---- C13. limits: fixture endpoint, marking, classification -------------------------
: > "$CX/.limits-kick"   # re-arm the shim-kick throttle: no background limits racing these
cat > "$WORK/cx-usage-high.json" <<EOF
{"email":"a@cx","plan_type":"pro",
 "rate_limit":{"allowed":true,"limit_reached":false,
   "primary_window":{"used_percent":42,"limit_window_seconds":18000,"reset_at":$((now+3600))},
   "secondary_window":{"used_percent":57,"limit_window_seconds":604800,"reset_at":$((now+90000))}},
 "code_review_rate_limit":null,
 "additional_rate_limits":[
   {"limit_name":"GPT-5.3-Codex-Spark","rate_limit":{"allowed":true,"limit_reached":false,
     "primary_window":{"used_percent":12,"limit_window_seconds":18000,"reset_at":$((now+3600))},
     "secondary_window":{"used_percent":95,"limit_window_seconds":604800,"reset_at":$((now+90000))}}}]}
EOF
out="$(CODEX_MULTIACC_USAGE_URL="file://$WORK/cx-usage-high.json" codex-accounts limits 2>&1)"
check "codex: >=90% model bucket marks the account" "LIMITED GPT-5.3-Codex-Spark:7d at 95%" "$out"
[ -f "$CX/acct-01/.limited" ] && t_ok "codex: .limited marker written" || t_fail "codex limits marker" "missing"
IFS= read -r first < "$CX/acct-01/.limited"
[ "$first" = "$((now+90000))" ] && t_ok "codex: marker carries the offender's reset epoch" \
  || t_fail "codex marker reset" "first line: $first"
python3 - "$CX/acct-01/limits.json" <<'EOF'
import json, sys
d = json.load(open(sys.argv[1]))
assert d['max_percent'] == 95, d['max_percent']
assert d['weekly_percent'] == 95, d['weekly_percent']
assert d['session_percent'] == 42, d['session_percent']
names = {b['name']: (b['group'], b['percent']) for b in d['buckets']}
assert names['5h'] == ('session', 42), names
assert names['7d'] == ('weekly', 57), names
assert names['GPT-5.3-Codex-Spark:5h'] == ('session', 12), names
assert names['GPT-5.3-Codex-Spark:7d'] == ('weekly', 95), names
EOF
[ $? -eq 0 ] && t_ok "codex: buckets classified session/weekly with per-model names" \
  || t_fail "codex bucket classification" "see assertions"
# below-threshold refresh clears the marker
cat > "$WORK/cx-usage-low.json" <<EOF
{"email":"a@cx","plan_type":"pro",
 "rate_limit":{"allowed":true,"limit_reached":false,
   "primary_window":{"used_percent":5,"limit_window_seconds":18000,"reset_at":$((now+3600))},
   "secondary_window":{"used_percent":9,"limit_window_seconds":604800,"reset_at":$((now+90000))}},
 "additional_rate_limits":[]}
EOF
out="$(CODEX_MULTIACC_USAGE_URL="file://$WORK/cx-usage-low.json" codex-accounts limits 2>&1)"
check "codex: below-threshold refresh clears the marker" "marker cleared" "$out"
[ ! -f "$CX/acct-01/.limited" ] && t_ok "codex: marker gone after clean pass" || t_fail "codex marker clear" "still present"
# a hard limit_reached verdict excludes even when no window is >= threshold
cat > "$WORK/cx-usage-blocked.json" <<EOF
{"email":"a@cx","plan_type":"pro",
 "rate_limit":{"allowed":false,"limit_reached":true,
   "primary_window":{"used_percent":50,"limit_window_seconds":18000,"reset_at":$((now+3600))}}}
EOF
out="$(CODEX_MULTIACC_USAGE_URL="file://$WORK/cx-usage-blocked.json" codex-accounts limits 2>&1)"
check "codex: limit_reached without a >=90% window still marks" "LIMITED limit_reached at 100%" "$out"
rm -f "$CX"/acct-*/.limited
# reshaped payload: windows are still found by the recursive fallback
cat > "$WORK/cx-usage-reshaped.json" <<EOF
{"totally":{"new":{"shape":{"used_percent":97,"limit_window_seconds":604800,"reset_at":$((now+90000))}}}}
EOF
out="$(CODEX_MULTIACC_USAGE_URL="file://$WORK/cx-usage-reshaped.json" codex-accounts limits 2>&1)"
check "codex: reshaped payload still tracked (fallback walk)" "LIMITED" "$out"
rm -f "$CX"/acct-*/.limited
# garbage payload degrades that account only, never the run
printf 'this is not json' > "$WORK/cx-usage-garbage.json"
out="$(CODEX_MULTIACC_USAGE_URL="file://$WORK/cx-usage-garbage.json" codex-accounts limits 2>&1)"
rc=$?
[ "$rc" = "0" ] && t_ok "codex: garbage payload fails open (rc=0)" || t_fail "codex garbage rc" "rc=$rc"
check "codex: garbage payload logged as failure" "usage fetch failed" "$out"
# fetch throttle: fresh data is not re-fetched
out="$(CODEX_MULTIACC_MIN_FETCH=9999 CODEX_MULTIACC_USAGE_URL="file://$WORK/cx-usage-low.json" codex-accounts limits 2>&1)"
printf '%s' "$out" | grep -q "acct-01" \
  && t_fail "codex fetch throttle" "fresh account was re-fetched: $out" \
  || t_ok "codex: fresh account skipped by the fetch throttle"
# 429 backoff honored
python3 - "$CX/acct-02/limits.json" "$now" <<'EOF'
import json, sys
json.dump({'fetched_at': 0, 'retry_after': int(sys.argv[2]) + 300, 'backoff': 120},
          open(sys.argv[1], 'w'))
EOF
out="$(CODEX_MULTIACC_USAGE_URL="file://$WORK/cx-usage-low.json" codex-accounts limits 2>&1)"
check "codex: 429 backoff honored" "acct-02: backing off after 429" "$out"
rm -f "$CX"/acct-*/limits.json

# ---- C14. oauth refresh via the token endpoint --------------------------------------
# expired bearer, missing endpoint: fail open with backoff
mk_cx_auth "$CX/acct-01/auth.json" a@cx 1000
out="$(CODEX_MULTIACC_USAGE_URL="file://$WORK/cx-usage-low.json" codex-accounts limits 2>&1)"
rc=$?
check "codex: failed oauth refresh logged with backoff" "acct-01: oauth refresh failed" "$out"
check "codex: expired bearer logged, not fatal" "acct-01: no fresh bearer" "$out"
[ "$rc" = "0" ] && t_ok "codex: limits exits 0 with an expired-bearer account" || t_fail "codex refresh rc" "rc=$rc"
[ -f "$CX/acct-01/.oauth-refresh.json" ] && t_ok "codex: refresh failure recorded for backoff" \
  || t_fail "codex refresh backoff file" "missing"
# a now-working endpoint is not retried inside the backoff window
python3 - "$WORK/cx-token-ok.json" "$((now + 864000))" <<'EOF'
import base64, json, sys
def jwt(claims):
    enc = lambda o: base64.urlsafe_b64encode(json.dumps(o).encode()).rstrip(b'=').decode()
    return f"{enc({'alg':'RS256'})}.{enc(claims)}.sig"
exp = float(sys.argv[2])
auth_claim = {"chatgpt_plan_type": "pro", "chatgpt_account_id": "acct-uuid"}
json.dump({"access_token": jwt({"exp": exp, "https://api.openai.com/auth": auth_claim, "marker": "REFRESHED"}),
           "refresh_token": "rt-rotated-new",
           "id_token": jwt({"email": "a@cx", "exp": exp, "https://api.openai.com/auth": auth_claim})},
          open(sys.argv[1], 'w'))
EOF
out="$(CODEX_MULTIACC_TOKEN_URL="file://$WORK/cx-token-ok.json" CODEX_MULTIACC_USAGE_URL="file://$WORK/cx-usage-low.json" codex-accounts limits 2>&1)"
check "codex: refresh backoff honored (no early retry)" "acct-01: no fresh bearer" "$out"
grep -q "rt-rotated-new" "$CX/acct-01/auth.json" \
  && t_fail "codex backoff" "auth.json rewritten inside the backoff window" \
  || t_ok "codex: no refresh inside the backoff window"
# --force refreshes: rotated tokens persisted 0600, telemetry follows
out="$(CODEX_MULTIACC_TOKEN_URL="file://$WORK/cx-token-ok.json" CODEX_MULTIACC_USAGE_URL="file://$WORK/cx-usage-low.json" codex-accounts limits --force 2>&1)"
check "codex: --force refreshes the expired token" "acct-01: access token refreshed" "$out"
check "codex: refreshed account fetches telemetry" "acct-01: ok" "$out"
python3 - "$CX/acct-01/auth.json" <<'EOF'
import base64, json, os, stat, sys
doc = json.load(open(sys.argv[1]))
t = doc['tokens']
payload = t['access_token'].split('.')[1]
payload += '=' * (-len(payload) % 4)
claims = json.loads(base64.urlsafe_b64decode(payload))
assert claims.get('marker') == 'REFRESHED', 'access token not replaced'
assert t['refresh_token'] == 'rt-rotated-new', 'refresh token not rotated'
assert doc.get('last_refresh', '').startswith('20'), 'last_refresh not stamped'
mode = stat.S_IMODE(os.stat(sys.argv[1]).st_mode)
assert mode == 0o600, oct(mode)
EOF
[ $? -eq 0 ] && t_ok "codex: rotated credential persisted with 0600" || t_fail "codex rotation" "see assertions"
[ ! -f "$CX/acct-01/.oauth-refresh.json" ] && t_ok "codex: successful refresh clears the backoff file" \
  || t_fail "codex refresh clear" "backoff file still present"
grep -q "rt-rotated-new" "$CX/limits.log" \
  && t_fail "codex token leak" "a refresh token leaked into limits.log" \
  || t_ok "codex: limits.log leaks no tokens"
# recently-expired token is left alone (a live codex session may own it) — even --force
mk_cx_auth "$CX/acct-01/auth.json" a@cx "$((now - 100))"
cp "$CX/acct-01/auth.json" "$WORK/cx-recent.bak"
rm -f "$CX/acct-01/limits.json" "$CX/acct-01/.oauth-refresh.json"
out="$(CODEX_MULTIACC_TOKEN_URL="file://$WORK/cx-token-ok.json" CODEX_MULTIACC_USAGE_URL="file://$WORK/cx-usage-low.json" codex-accounts limits --force 2>&1)"
check "codex: recently-expired token not refreshed (even --force)" "acct-01: no fresh bearer" "$out"
cmp -s "$CX/acct-01/auth.json" "$WORK/cx-recent.bak" \
  && t_ok "codex: recently-expired credential left untouched" \
  || t_fail "codex refresh gate" "credential rewritten inside the 5-min grace window"
# no refresh token at all: parked with a clear reason
mk_cx_auth "$CX/acct-01/auth.json" a@cx 1000 norefresh
rm -f "$CX/acct-01/limits.json" "$CX/acct-01/.oauth-refresh.json"
out="$(CODEX_MULTIACC_TOKEN_URL="file://$WORK/cx-token-ok.json" CODEX_MULTIACC_USAGE_URL="file://$WORK/cx-usage-low.json" codex-accounts limits --force 2>&1)"
grep -q "reason=no-refresh-token" "$CX/acct-01/.expired" 2>/dev/null \
  && t_ok "codex: refreshless expired credential is parked" \
  || t_fail "codex no-refresh park" "no marker written"
# a later successful authenticated fetch unparks it
mk_cx_auth "$CX/acct-01/auth.json" a@cx "$FUTURE_EXP"
rm -f "$CX/acct-01/limits.json"
out="$(CODEX_MULTIACC_USAGE_URL="file://$WORK/cx-usage-low.json" codex-accounts limits --force 2>&1)"
[ ! -f "$CX/acct-01/.expired" ] && t_ok "codex: a successful usage fetch clears the dead-auth marker" \
  || t_fail "codex unpark" ".expired survived an authenticated fetch"
# ...but never an org-blocked park (telemetry proves nothing about workspace policy)
printf '%s\nreason=org-blocked marked_at=t detail=x\n' "$now" > "$CX/acct-01/.expired"
rm -f "$CX/acct-01/limits.json"
CODEX_MULTIACC_USAGE_URL="file://$WORK/cx-usage-low.json" codex-accounts limits --force >/dev/null 2>&1
[ -f "$CX/acct-01/.expired" ] && t_ok "codex: a usage fetch does not unpark an org-blocked account" \
  || t_fail "codex org unpark" "telemetry cleared an org block it cannot observe"
rm -f "$CX/acct-01/.expired"
# a 4xx from the token endpoint must not park the pool; invalid_grant is proof
port=""
python3 "$srv_script" > "$WORK/cx-token-port" 2>/dev/null &
cx_srv_pid=$!
for _ in $(seq 1 20); do
  port="$(head -1 "$WORK/cx-token-port" 2>/dev/null)"
  case "$port" in ''|*[!0-9]*) port=""; sleep 0.2 ;; *) break ;; esac
done
if [ -z "$port" ]; then
  kill "$cx_srv_pid" 2>/dev/null; wait "$cx_srv_pid" 2>/dev/null || true
  t_ok "codex: token-endpoint 4xx tests skipped (cannot bind a loopback port here)"
else
  mk_cx_stale() {
    mk_cx_auth "$CX/acct-01/auth.json" a@cx 1000
    rm -f "$CX/acct-01/limits.json" "$CX/acct-01/.oauth-refresh.json" "$CX/acct-01/.expired"
  }
  mk_cx_stale
  CODEX_MULTIACC_TOKEN_URL="http://127.0.0.1:$port/boom" CODEX_MULTIACC_USAGE_URL="file://$WORK/cx-usage-low.json" codex-accounts limits --force >/dev/null 2>&1
  [ ! -f "$CX/acct-01/.expired" ] && t_ok "codex: one opaque 4xx does not park an account" \
    || t_fail "codex 4xx park" "a single non-invalid_grant 400 parked the account"
  CODEX_MULTIACC_TOKEN_URL="http://127.0.0.1:$port/boom" CODEX_MULTIACC_USAGE_URL="file://$WORK/cx-usage-low.json" codex-accounts limits --force >/dev/null 2>&1
  CODEX_MULTIACC_TOKEN_URL="http://127.0.0.1:$port/boom" CODEX_MULTIACC_USAGE_URL="file://$WORK/cx-usage-low.json" codex-accounts limits --force >/dev/null 2>&1
  [ -f "$CX/acct-01/.expired" ] && t_ok "codex: a repeatedly-refused grant is parked on the third strike" \
    || t_fail "codex 4xx strikes" "still not parked after three refusals"
  mk_cx_stale
  CODEX_MULTIACC_TOKEN_URL="http://127.0.0.1:$port/invalid-grant" CODEX_MULTIACC_USAGE_URL="file://$WORK/cx-usage-low.json" codex-accounts limits --force >/dev/null 2>&1
  grep -q "invalid_grant" "$CX/acct-01/.expired" 2>/dev/null \
    && t_ok "codex: invalid_grant parks the account immediately" \
    || t_fail "codex invalid_grant" "no marker for an explicit invalid_grant"
  kill "$cx_srv_pid" 2>/dev/null; wait "$cx_srv_pid" 2>/dev/null || true
fi
mk_cx_auth "$CX/acct-01/auth.json" a@cx "$FUTURE_EXP"
rm -f "$CX/acct-01/.expired" "$CX/acct-01/.oauth-refresh.json" "$CX"/acct-*/limits.json
# malformed tokens object (null) degrades that account only
cp "$CX/acct-01/auth.json" "$WORK/cx-auth01.bak"
printf '{"auth_mode":"chatgpt","tokens": null}' > "$CX/acct-01/auth.json"
out="$(CODEX_MULTIACC_TOKEN_URL="file://$WORK/cx-token-ok.json" CODEX_MULTIACC_USAGE_URL="file://$WORK/cx-usage-low.json" codex-accounts limits --force 2>&1)"
rc=$?
[ "$rc" = "0" ] && t_ok "codex: null tokens object fails open (rc=0)" || t_fail "codex null tokens rc" "rc=$rc"
check "codex: null tokens degrades only that account" "acct-01: no fresh bearer" "$out"
[ -f "$CX/acct-02/limits.json" ] && t_ok "codex: accounts after a malformed one still refresh" \
  || t_fail "codex fail-open loop" "acct-02 starved by acct-01's malformed auth"
cp "$WORK/cx-auth01.bak" "$CX/acct-01/auth.json"
rm -f "$CX"/acct-*/limits.json

# refresh is not attempted at all when the rotated credential could not be persisted
# (the grant rotates the refresh token — consuming it and then failing to write
# auth.json would strand the account; codex-review finding).
# Root ignores directory permission bits, so the unwritable-dir setup only works
# as a non-root user (the server suite runs as root — skip there).
if [ "$(id -u)" = "0" ]; then
  t_ok "codex: refresh persistence preflight test skipped (root ignores directory permissions)"
  t_ok "codex: refresh persistence preflight test skipped (root ignores directory permissions) [2]"
else
  mk_cx_auth "$CX/acct-01/auth.json" a@cx 1000
  rm -f "$CX/acct-01/limits.json" "$CX/acct-01/.oauth-refresh.json"
  chmod 500 "$CX/acct-01"
  out="$(CODEX_MULTIACC_TOKEN_URL="file://$WORK/cx-token-ok.json" CODEX_MULTIACC_USAGE_URL="file://$WORK/cx-usage-low.json" codex-accounts limits --force 2>&1)"
  chmod 700 "$CX/acct-01"
  check "codex: unpersistable credential skips the refresh grant" "refresh not attempted" "$out"
  grep -q "rt-rotated-new" "$CX/acct-01/auth.json" \
    && t_fail "codex refresh preflight" "the grant was consumed despite an unwritable dir" \
    || t_ok "codex: refresh grant not consumed when persistence would fail"
  mk_cx_auth "$CX/acct-01/auth.json" a@cx "$FUTURE_EXP"
  rm -f "$CX/acct-01/.oauth-refresh.json"
fi

# two parallel imports get distinct ids and both land in the manifest (lock coverage;
# codex-review finding)
codex-accounts import p1@cx --no-sync >/dev/null 2>&1 &
imp1=$!
codex-accounts import p2@cx --no-sync >/dev/null 2>&1 &
imp2=$!
wait "$imp1"; wait "$imp2"
n1="$(grep -c '"email": "p1@cx"' "$CX/accounts.json")"
n2="$(grep -c '"email": "p2@cx"' "$CX/accounts.json")"
id1="$(python3 -c "import json,sys; print(next((a['id'] for a in json.load(open('$CX/accounts.json'))['accounts'] if a['email']=='p1@cx'), ''))")"
id2="$(python3 -c "import json,sys; print(next((a['id'] for a in json.load(open('$CX/accounts.json'))['accounts'] if a['email']=='p2@cx'), ''))")"
{ [ "$n1" = "1" ] && [ "$n2" = "1" ] && [ -n "$id1" ] && [ -n "$id2" ] && [ "$id1" != "$id2" ]; } \
  && t_ok "codex: parallel imports get distinct ids ($id1, $id2)" \
  || t_fail "codex parallel import" "p1=$n1($id1) p2=$n2($id2)"
codex-accounts remove "$id1" --yes >/dev/null 2>&1
codex-accounts remove "$id2" --yes >/dev/null 2>&1

# ---- C15. verify --------------------------------------------------------------------
out="$(codex-accounts verify --quick 2>&1)"
check "codex: verify --quick passes chatgpt accounts" "acct-01 a@cx: OK" "$out"
check "codex: verify --quick counts" "0 failure(s)" "$out"
out="$(codex-accounts verify 2>&1)"
check "codex: full verify PASS via the real-call matrix" "acct-01 a@cx: PASS" "$out"
check "codex: full verify zero failures" "0 failure(s)" "$out"
# verify parks a dead login it discovers, and a PASS clears an existing park
echo "authfail:acct-02" > "$FAKE_CTL2"
printf '%s\nreason=auth-error marked_at=t detail=x\n' "$now" > "$CX/acct-01/.expired"
out="$(codex-accounts verify 2>&1)"
check "codex: verify flags the dead login" "acct-02 b@cx: FAIL" "$out"
check "codex: verify says what fixes it" "codex-accounts relogin acct-02" "$out"
grep -q "reason=auth-error" "$CX/acct-02/.expired" 2>/dev/null \
  && t_ok "codex: verify parks the dead login" || t_fail "codex verify park" "no marker"
[ ! -f "$CX/acct-01/.expired" ] && t_ok "codex: a verify PASS clears an existing park" \
  || t_fail "codex verify unpark" "PASS left the marker in place"
rm -f "$FAKE_CTL2" "$CX/acct-02/.expired"
# an org-disabled account is parked as org-blocked
echo "orgfail:acct-02" > "$FAKE_CTL2"
out="$(codex-accounts verify 2>&1)"
check "codex: verify calls out the workspace block" "ORG BLOCKED" "$out"
grep -q "reason=org-blocked" "$CX/acct-02/.expired" 2>/dev/null \
  && t_ok "codex: verify parks the org-blocked account" || t_fail "codex verify org park" "no marker"
rm -f "$FAKE_CTL2" "$CX/acct-02/.expired"

# ---- C16. audit vocabulary + empty pool ---------------------------------------------
mkdir -p "$CX/acct-06"
python3 - "$CX/accounts.json" <<'EOF'
import json, sys
doc = json.load(open(sys.argv[1]))
doc['accounts'] = [a for a in doc['accounts'] if a['id'] != 'acct-06']
doc['accounts'].append({'id': 'acct-06', 'email': 'srv@cx', 'home': 'server'})
doc['accounts'].sort(key=lambda a: a['id'])
json.dump(doc, open(sys.argv[1], 'w'), indent=2)
EOF
out="$(python3 "$REPO_DIR/lib/codex_audit.py" "$CX" linux | awk -F'\t' '$1=="acct-06"{print $4}')"
[ "$out" = "missing" ] && t_ok "codex: home=server on the server means NO LOGIN, not 'elsewhere'" \
  || t_fail "codex home vocabulary" "expected missing on linux, got: $out"
out="$(python3 "$REPO_DIR/lib/codex_audit.py" "$CX" mac | awk -F'\t' '$1=="acct-06"{print $4}')"
[ "$out" = "remote" ] && t_ok "codex: home=server on the Mac is correctly 'elsewhere'" \
  || t_fail "codex home vocabulary" "expected remote on mac, got: $out"
python3 - "$CX/accounts.json" <<'EOF'
import json, sys
doc = json.load(open(sys.argv[1]))
doc['accounts'] = [a for a in doc['accounts'] if a['id'] != 'acct-06']
json.dump(doc, open(sys.argv[1], 'w'), indent=2)
EOF
rm -rf "$CX/acct-06"
cxemptypool="$WORK/cx-emptypool"
mkdir -p "$cxemptypool/tmp"
printf '{"version":1,"threshold":90,"accounts":[]}' > "$cxemptypool/accounts.json"
out="$(CODEX_ACCOUNTS_DIR="$cxemptypool" codex-accounts expired 2>&1)"
rc=$?
check "codex: expired on an empty pool says so" "No accounts registered yet" "$out"
[ "$rc" = "0" ] && t_ok "codex: empty pool exits 0" || t_fail "codex empty pool rc" "rc=$rc"

# ---- C17. security hardening --------------------------------------------------------
mkdir -p "$WORK/cx-canary" && : > "$WORK/cx-canary/DO_NOT_DELETE"
cp "$CX/accounts.json" "$WORK/cx-manifest.bak"
python3 - "$CX/accounts.json" <<'EOF'
import json, os, sys
d = json.load(open(sys.argv[1]))
d['accounts'].append({'id': '../cx-canary', 'email': 'evil@cx', 'home': 'mac'})
json.dump(d, open(sys.argv[1] + '.tmp', 'w'), indent=2); os.replace(sys.argv[1] + '.tmp', sys.argv[1])
EOF
out="$(codex-accounts remove ../cx-canary --yes 2>&1)"
rc=$?
[ -f "$WORK/cx-canary/DO_NOT_DELETE" ] && t_ok "codex: remove refuses path-traversal id" \
  || t_fail "codex path traversal" "remove ../cx-canary DELETED files outside the pool"
[ "$rc" != "0" ] && t_ok "codex: traversal id rejected nonzero" || t_fail "codex traversal rc" "rc=0"
codex-accounts list 2>&1 | grep -q "\.\./cx-canary" \
  && t_fail "codex id filter" "traversal id surfaced" \
  || t_ok "codex: invalid manifest ids are filtered out"
cp "$WORK/cx-manifest.bak" "$CX/accounts.json"
if [ "$(uname -s)" = "Darwin" ]; then
  python3 - "$CX/accounts.json" <<'EOF'
import json, os, sys
d = json.load(open(sys.argv[1]))
d['server_root'] = "/tmp/x'; touch /tmp/cx_multiacc_PWNED; #"
json.dump(d, open(sys.argv[1] + '.tmp', 'w'), indent=2); os.replace(sys.argv[1] + '.tmp', sys.argv[1])
EOF
  rm -f /tmp/cx_multiacc_PWNED
  out="$(CODEX_MULTIACC_NO_SYNC=0 codex-accounts sync 2>&1)"
  [ ! -f /tmp/cx_multiacc_PWNED ] && t_ok "codex: sync rejects injected server_root" \
    || { t_fail "codex command injection" "server_root injection EXECUTED"; rm -f /tmp/cx_multiacc_PWNED; }
  check "codex: injected server_root refused" "not a plain absolute path" "$out"
  cp "$WORK/cx-manifest.bak" "$CX/accounts.json"
  python3 - "$CX/accounts.json" <<'EOF'
import json, os, sys
d = json.load(open(sys.argv[1])); d['accounts'] = []
json.dump(d, open(sys.argv[1] + '.tmp', 'w'), indent=2); os.replace(sys.argv[1] + '.tmp', sys.argv[1])
EOF
  out="$(CODEX_MULTIACC_NO_SYNC=0 codex-accounts sync 2>&1)"
  rc=$?
  check "codex: empty manifest refuses to sync" "refusing to blank the target pools" "$out"
  [ "$rc" != "0" ] && t_ok "codex: empty-manifest sync exits nonzero" || t_fail "codex empty sync rc" "rc=0"
  cp "$WORK/cx-manifest.bak" "$CX/accounts.json"

  # replica + peer guards, codex flavor
  printf 'replica\n' > "$CX/sync-role"
  out="$(CODEX_MULTIACC_NO_SYNC=0 codex-accounts sync 2>&1)"
  rc=$?
  check "codex: replica pool refuses to push" "sync replica" "$out"
  [ "$rc" = "0" ] && t_ok "codex: replica sync exits 0" || t_fail "codex replica sync rc" "rc=$rc"
  # ...and a codex replica's auto_sync (fired by remove) is silent — no push, no nag
  out="$(CODEX_MULTIACC_NO_SYNC=0 codex-accounts import rep@cx --id acct-31 --no-sync 2>&1
         CODEX_MULTIACC_NO_SYNC=0 codex-accounts remove acct-31 --yes 2>&1)"
  case "$out" in *"sync failed"*) t_fail "codex replica auto_sync" "a replica mutation warned about sync: $out" ;;
    *) t_ok "codex: replica auto_sync is silent (no push, no warning)" ;; esac
  rm -f "$CX/sync-role"
  python3 - "$CX/accounts.json" <<'EOF'
import json, os, sys
d = json.load(open(sys.argv[1]))
d['peers'] = [{'target': 'gas@peer', 'root': "/tmp/x'; touch /tmp/cx_multiacc_PEER_PWNED; #", 'repo': '/tmp/y'}]
json.dump(d, open(sys.argv[1] + '.tmp', 'w'), indent=2); os.replace(sys.argv[1] + '.tmp', sys.argv[1])
EOF
  rm -f /tmp/cx_multiacc_PEER_PWNED
  out="$(CODEX_MULTIACC_NO_SYNC=0 codex-accounts sync 2>&1)"
  rc=$?
  check "codex: injected peer root refused" "peer root is not a plain absolute path" "$out"
  [ "$rc" != "0" ] && t_ok "codex: bad peer exits nonzero" || t_fail "codex peer validation rc" "rc=0"
  [ ! -f /tmp/cx_multiacc_PEER_PWNED ] && t_ok "codex: peer injection never executed" \
    || { t_fail "codex peer injection" "peer root injection EXECUTED"; rm -f /tmp/cx_multiacc_PEER_PWNED; }
  cp "$WORK/cx-manifest.bak" "$CX/accounts.json"
else
  t_ok "codex: sync validation tests skipped (Mac-only feature)"
fi

# ---- C18. status + npm layer --------------------------------------------------------
out="$(codex-accounts status 2>&1)"
check "codex: status shows threshold" "90%" "$out"
check "codex: status shows account" "a@cx" "$out"
check "codex: status shows plan" "plan pro" "$out"
if command -v node >/dev/null 2>&1; then
  out="$(node "$REPO_DIR/bin/cli.mjs" codex list 2>&1)"
  check "cli.mjs codex passthrough (list)" "a@cx" "$out"
  out="$(node "$REPO_DIR/bin/cli.mjs" --help 2>&1)"
  check "cli --help documents the codex pool" "codex-accounts" "$out"
else
  t_ok "codex npm-layer tests skipped (node not installed)"
fi

# ---- summary ---------------------------------------------------------------------
echo
echo "passed: $PASS  failed: $FAIL"
[ "$FAIL" = "0" ] || exit 1
