1
0
Fork 0
gibby/.opencode/plans/1775725199925-glowing-orchid.md
2026-04-20 23:41:44 +03:00

4.9 KiB

Plan: rewrite token selection around a simple disk-first state model

Goal

Throw away the current layered selection/cooldown/state model and replace it with a small implementation that:

  • reads the main JSON file on every /token request
  • keeps only the minimum necessary account fields on disk
  • decides from file state first
  • refreshes usage only when missing or stale
  • validates the selected token before returning it
  • moves invalid accounts to failed.json
  • does not touch the helper scripts in this pass

Required file model

Main state file

accounts.json

{
  "active_account": "user@example.com",
  "accounts": [
    {
      "email": "user@example.com",
      "access_token": "...",
      "refresh_token": "...",
      "token_refresh_at": 1710000000,
      "usage": {
        "primary": {
          "used_percent": 72,
          "reset_at": 1710018000
        },
        "secondary": {
          "used_percent": 18,
          "reset_at": 1710600000
        }
      },
      "usage_checked_at": 1710000000,
      "disabled": false
    }
  ]
}

Only these fields should exist for account state.

Failed state file

failed.json

{
  "accounts": [
    {
      "email": "bad@example.com",
      "access_token": "...",
      "refresh_token": "...",
      "token_refresh_at": 1710000000,
      "usage": {
        "primary": {
          "used_percent": 100,
          "reset_at": 1710018000
        },
        "secondary": {
          "used_percent": 100,
          "reset_at": 1710600000
        }
      },
      "usage_checked_at": 1710000000,
      "disabled": false
    }
  ]
}

Top-level must contain only accounts.

Selection rules

Active account first

For each /token request:

  1. Read accounts.json fresh from disk.
  2. Resolve active_account by email.
  3. Evaluate active first.

When an account is usable

An account is usable when:

  • disabled == false
  • secondary.used_percent < 100
  • primary.used_percent < GIBBY_EXHAUSTED_USAGE_THRESHOLD

Default threshold remains 95.

Usage freshness

Usage must be refreshed only when missing or stale.

Add env:

  • GIBBY_USAGE_STALE_SECONDS, default 3600

Usage is stale when:

  • usage is missing
  • usage_checked_at is missing
  • now - usage_checked_at > GIBBY_USAGE_STALE_SECONDS

If active account usage is stale or missing, refresh usage for that account before deciding if it is usable.

Fallback selection

If active account cannot be used, choose the next account by:

  • filtering to usable accounts
  • sorting by highest primary used_percent
  • using file order as the tie-breaker

If a new account is chosen, write its email into active_account in accounts.json.

Token flow

For the chosen account:

  1. Ensure token is fresh enough.
  2. If token_refresh_at says refresh is needed, refresh token and persist new values.
  3. After selection decisions are finished and the token is ready, validate it by calling:

https://chatgpt.com/backend-api/codex/models

  1. Only return the token if validation returns 200.

Invalid account handling

If refresh, usage auth, or final validation shows the token/account is invalid:

  1. Read current main state.
  2. Remove that full account object from accounts.json.
  3. Append the same full account object to failed.json.accounts.
  4. If it was the active account, clear active_account before reselection.
  5. Persist both files atomically.

No failed.txt in the rewritten core flow.

Files to rewrite

  • /home/wzray/AI/gibby/src/gibby/settings.py
    • keep only env needed for the new flow
  • /home/wzray/AI/gibby/src/gibby/store.py
    • rewrite as simple JSON read/write helpers for accounts.json and failed.json
  • /home/wzray/AI/gibby/src/gibby/client.py
    • keep only token refresh, usage fetch, and token validation calls
  • /home/wzray/AI/gibby/src/gibby/manager.py
    • rewrite into one small service for /token
  • /home/wzray/AI/gibby/src/gibby/app.py
    • keep thin FastAPI wiring for /health and /token

Files to remove or stop using

  • /home/wzray/AI/gibby/src/gibby/models.py
  • /home/wzray/AI/gibby/src/gibby/account_ops.py

Their logic should be folded into the new minimal data model and service flow instead of preserved.

Out of scope for this pass

  • do not touch scripts/oauth_helper.py
  • do not touch scripts/refresh_limits.py
  • do not preserve old cooldown, failed.txt, dual-state, or derived snapshot machinery unless absolutely required to keep app booting during rewrite

Verification

  • uv run pytest -q
  • API tests for:
    • /health returns ok
    • /token returns 503 when file has no usable accounts
    • /token prefers active account when usable
    • /token rereads the file between requests
    • stale usage triggers a refresh before decision
    • fresh usage skips refresh
    • invalid token moves full account object to failed.json
    • fallback chooses highest primary usage among usable non-disabled accounts
  • direct file tests for exact accounts.json and failed.json schema