# Hidden Quirks Map

> The six non-obvious implementation quirks every maintainer should know: keychain accessibility migration, browser-cookie prompt suppression, a universal quota parser, Claude peak-hour awareness, a dual-path display link, and an orphan-detecting watchdog process. Each exists because a naive implementation either triggers repeated macOS permission dialogs, silently fails on diverse API shapes, or leaks child processes at app quit.

- Repository: steipete/CodexBar
- GitHub: https://github.com/steipete/CodexBar
- Human wiki: https://grok-wiki.com/public/wiki/steipete-codexbar-3494bea25492
- Complete Markdown: https://grok-wiki.com/public/wiki/steipete-codexbar-3494bea25492/llms-full.txt

## Source Files

- `Sources/CodexBar/KeychainMigration.swift`
- `Sources/CodexBarCore/BrowserCookieAccessGate.swift`
- `Sources/CodexBarCore/Providers/Synthetic/SyntheticUsageParser.swift`
- `Sources/CodexBarCore/Providers/Claude/ClaudePeakHours.swift`
- `Sources/CodexBar/DisplayLink.swift`
- `Sources/CodexBarClaudeWatchdog/main.swift`

---

<details>
<summary>Relevant source files</summary>
The following files were used as context for generating this wiki page:

- [Sources/CodexBar/KeychainMigration.swift](Sources/CodexBar/KeychainMigration.swift)
- [Sources/CodexBarCore/BrowserCookieAccessGate.swift](Sources/CodexBarCore/BrowserCookieAccessGate.swift)
- [Sources/CodexBarCore/Providers/Synthetic/SyntheticUsageStats.swift](Sources/CodexBarCore/Providers/Synthetic/SyntheticUsageStats.swift)
- [Sources/CodexBarCore/Providers/Claude/ClaudePeakHours.swift](Sources/CodexBarCore/Providers/Claude/ClaudePeakHours.swift)
- [Sources/CodexBar/DisplayLink.swift](Sources/CodexBar/DisplayLink.swift)
- [Sources/CodexBarClaudeWatchdog/main.swift](Sources/CodexBarClaudeWatchdog/main.swift)
</details>

# Hidden Quirks Map

CodexBar is a macOS menu-bar app that monitors AI coding-assistant quotas across multiple providers. Its implementation touches several system surfaces—Keychain, browser cookies, display hardware, OS signals—where the obvious first approach silently fails or produces user-hostile behavior. This page documents six non-obvious implementation decisions that exist because naive alternatives either fire repeated macOS permission dialogs, fail to parse the wild diversity of real provider API responses, or leave orphan processes running after the app quits.

Each section explains the concrete problem the quirk solves, where the code lives, and what would break if it were removed.

---

## 1. Keychain Accessibility Migration

### The Problem

macOS requires apps to re-authenticate every time they access a Keychain item stored with the default `kSecAttrAccessibleWhenUnlocked` policy in certain development scenarios—specifically after code-signing changes or rebuilds. During active development this generates a blocking permission dialog for every stored token on every relaunch, which is unusable.

### The Solution

`KeychainMigration` runs once per installation and rewrites every stored credential to use `kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly`. This policy allows access after the first post-boot unlock, with no per-access prompt, while still binding the items to the device (preventing iCloud Keychain sync of raw tokens).

The migration is gated by a `UserDefaults` flag (`KeychainMigrationV1Completed`) so it runs exactly once:

```swift
// Sources/CodexBar/KeychainMigration.swift:41-59
if !UserDefaults.standard.bool(forKey: self.migrationKey) {
    // migrate each item: read → delete → re-add with new accessibility
    UserDefaults.standard.set(true, forKey: self.migrationKey)
}
```

The actual migration for each item is a three-step atomic dance: read the existing data and attributes, delete the old item, then write it back with the new accessibility constant. If any step fails the error is logged but the flag is still set—a partial migration is better than an infinite retry loop that keeps prompting users.

| Item migrated | Keychain account key |
|---|---|
| OpenAI (Codex) cookie | `codex-cookie` |
| Claude cookie | `claude-cookie` |
| Cursor cookie | `cursor-cookie` |
| Factory cookie | `factory-cookie` |
| MiniMax cookie + API token | `minimax-cookie`, `minimax-api-token` |
| Augment cookie | `augment-cookie` |
| Copilot API token | `copilot-api-token` |
| Zai API token | `zai-api-token` |
| Synthetic API key | `synthetic-api-key` |

Sources: [Sources/CodexBar/KeychainMigration.swift:5-63]()

---

## 2. Browser-Cookie Prompt Suppression Gate

### The Problem

Reading browser cookies to fetch session tokens requires decrypting them with the browser's "Safe Storage" key, which lives in the macOS Keychain. On Chrome/Chromium-family browsers, if the Keychain is locked or the app lacks prior access, a system dialog asks the user to approve. CodexBar polls on a background schedule; a prompt popping up every few minutes is intolerable.

### The Solution

`BrowserCookieAccessGate` acts as a per-browser circuit breaker. Before any cookie read it runs a preflight Keychain check (`KeychainAccessPreflight.checkGenericPassword`) that probes the Safe Storage entry without triggering a prompt. If the result is `.interactionRequired`, the gate records a `deniedUntil` timestamp 6 hours in the future and blocks all cookie access for that browser until the timer expires:

```swift
// Sources/CodexBarCore/BrowserCookieAccessGate.swift:15-51
private static let cooldownInterval: TimeInterval = 60 * 60 * 6  // 6 hours

public static func shouldAttempt(_ browser: Browser, now: Date = Date()) -> Bool {
    guard browser.usesKeychainForCookieDecryption else { return true }
    // … preflight check; if interactionRequired → block for 6h
}
```

The blocked-until timestamps are persisted in `UserDefaults` so they survive app restarts. The gate also reacts to actual `BrowserCookieError.accessDenied` errors via `recordIfNeeded`, recording a fresh 6-hour cooldown so a transient denial doesn't trigger retries.

Only browsers that actually use Keychain for cookie decryption (Chromium family) are subject to the gate; Safari and others pass through unconditionally (`guard browser.usesKeychainForCookieDecryption else { return true }`).

```mermaid
flowchart TD
    A[Refresh tick] --> B{usesKeychainForCookieDecryption?}
    B -- no --> C[Proceed]
    B -- yes --> D{Blocked until timestamp\nin UserDefaults?}
    D -- still future --> E[Skip – no prompt]
    D -- expired or absent --> F[Preflight Keychain probe]
    F -- allowed --> C
    F -- interactionRequired --> G[Record 6h cooldown\nSkip – no prompt]
```

Sources: [Sources/CodexBarCore/BrowserCookieAccessGate.swift:1-113]()

---

## 3. Universal Quota Parser

### The Problem

The "Synthetic" provider is a generic AI API aggregator whose response schema is undocumented and has varied across API versions and plan types. Different plan tiers return quota data under different key names, in different nesting shapes, and with percentage values that may be 0–1 floats or 0–100 integers.

### The Solution

`SyntheticUsageParser` (embedded in `SyntheticUsageStats.swift`) is a deliberately over-engineered fuzzy parser. It handles multiple strategies in order of preference:

**1. Slot-positional fast path.** If the response contains any of the known Synthetic-specific keys (`rollingFiveHourLimit`, `weeklyTokenLimit`, `search.hourly`), the parser extracts them into fixed UI slots `[slot-0: rolling-5h, slot-1: weekly, slot-2: search-hourly]`. A missing slot stays `nil` rather than promoting the next entry into the wrong UI label:

```swift
// Sources/CodexBarCore/Providers/Synthetic/SyntheticUsageStats.swift:192-201
let slots: [[String: Any]?] = [rolling, weekly, searchHourly]
return slots.contains(where: { $0 != nil }) ? slots : nil
```

**2. Fuzzy fallback.** If no known keys are found, the parser walks 13 possible root-level or `data`-nested key names (`quotas`, `quota`, `limits`, `usage`, `entries`, `subscription`, `data`, and their `data`-nested counterparts), recursively descending into arrays and dicts to find objects that look like quota payloads.

**3. Per-field aliasing.** Each field within a quota object is resolved through an alias list. For example `usedPercent` is tried as any of: `percentUsed`, `usedPercent`, `usagePercent`, `usage_percent`, `used_percent`, `percent_used`, `percent`. Percentage values ≤ 1.0 are auto-scaled to 0–100:

```swift
// line 454-458
private static func normalizedPercent(_ value: Double?) -> Double? {
    guard let value else { return nil }
    if value <= 1 { return value * 100 }
    return value
}
```

**4. Time-window parsing.** The reset-window duration can arrive as an integer minutes/hours/days/seconds field, or as a free-text string like `"5hr"`, `"30min"`, `"2 days"`. Suffix matching is sorted longest-first so `"minutes"` always beats `"m"`:

```swift
// line 378-387
private static let windowSuffixMultipliers: [(suffix: String, multiplier: Double)] = {
    let raw: [(String, Double)] = [
        ("minutes", 1), ("minute", 1), ("mins", 1), ("min", 1), ("m", 1),
        ("hours", 60), …
    ]
    return raw.sorted { $0.0.count > $1.0.count } …
}()
```

**5. Reset-time vs. reset-description split.** When a `resetsAt` timestamp is available, `resetDescription` is explicitly left `nil` so the UI recalculates a live countdown on every render rather than freezing a stale "in Xm" string at parse time.

**6. Date value heuristics.** Timestamps may arrive as Unix seconds, Unix milliseconds, or ISO-8601 strings. The parser distinguishes seconds from milliseconds by checking whether the numeric value exceeds `1_000_000_000_000`.

Sources: [Sources/CodexBarCore/Providers/Synthetic/SyntheticUsageStats.swift:154-708]()

---

## 4. Claude Peak-Hour Awareness

### The Problem

Claude (Anthropic's API and Pro plan) degrades response quality and increases latency during US business hours due to high demand. Surfacing this in the menu bar helps users plan around slowdowns—but the calculation must account for time zones, weekends, and countdown-to-next-peak correctly.

### The Solution

`ClaudePeakHours` hard-codes peak time as **08:00–14:00 Eastern Time, Monday–Friday**. All comparisons are done in a `Calendar` configured with `America/New_York` timezone, regardless of the user's local zone:

```swift
// Sources/CodexBarCore/Providers/Claude/ClaudePeakHours.swift:4-6
private static let peakTimeZone = TimeZone(identifier: "America/New_York")!
private static let peakStartHour = 8
private static let peakEndHour = 14
```

When inside peak, the label is `"Peak · ends in Xh Ym"`. When outside, `nextPeakStart(after:)` computes the next weekday 08:00 ET, advancing past Saturday (skip 1 day) and Sunday (skip 2 days):

```swift
// line 56-63
let skip = switch weekday {
case 1: 1   // Sunday → Monday
case 7: 2   // Saturday → Monday
default: 0
}
```

One subtle detail: before extracting hour/minute components the code snaps the input date to the start of its minute (`calendar.dateInterval(of: .minute, for: date)?.start`) so that a date at 13:59:59 doesn't drift into "peak" for a full minute more than expected.

Sources: [Sources/CodexBarCore/Providers/Claude/ClaudePeakHours.swift:1-83]()

---

## 5. Dual-Path Display Link

### The Problem

SwiftUI needs a clock tick to drive live countdowns (quota resets, peak-hour timers) in a menu-bar popover. Using a simple `Timer` can miss beats when the main runloop is busy. Using `CVDisplayLink` directly on macOS 15+ is deprecated in favor of `NSScreen.displayLink`. But the app targets macOS 14, so both APIs must coexist.

### The Solution

`DisplayLinkDriver` picks the API at runtime:

- **macOS 15+**: creates an `NSScreen`-backed `CADisplayLink` with a locked `CAFrameRateRange` (min = max = preferred = target FPS), added to `.main` runloop in `.common` mode.
- **macOS 14**: falls back to `CVDisplayLink`, which fires on a non-main thread. The callback bridges back to the main actor via `Task { @MainActor [weak self] in self?.handleTick() }`.

Both paths share `handleTick()`, which applies a manual interval gate against `CACurrentMediaTime()` because neither API guarantees exactly the requested FPS—the gate ensures ticks are never fired faster than the configured interval:

```swift
// Sources/CodexBar/DisplayLink.swift:57-66
private func handleTick() {
    let now = CACurrentMediaTime()
    if self.lastTickTimestamp > 0, now - self.lastTickTimestamp < self.targetInterval {
        return
    }
    self.lastTickTimestamp = now
    self.tick &+= 1   // wrapping add; drives @Observable invalidation
    self.onTick?()
}
```

The `tick` counter uses `&+=` (wrapping addition) to avoid undefined behavior on overflow for a counter that is never read arithmetically—only used as an `@Observable` invalidation signal.

`deinit` dispatches `stop()` back onto the `@MainActor` because `CVDisplayLink` must be stopped from the same actor context it was started from, and `deinit` is actor-unaware.

Sources: [Sources/CodexBar/DisplayLink.swift:1-96]()

---

## 6. Orphan-Detecting Watchdog Process

### The Problem

CodexBar spawns a subprocess (the Claude CLI tool) to fetch usage data. If the parent app crashes, is force-quit, or is killed by `launchd` under memory pressure, the child process becomes an orphan: it keeps running, consuming memory and CPU, with no parent to wait for it. `atexit` handlers and `NotificationCenter` observers in the parent are not reliable across crash-induced terminations.

### The Solution

`CodexBarClaudeWatchdog` is a dedicated standalone binary that sits between CodexBar and the Claude CLI. CodexBar spawns the watchdog, the watchdog spawns the actual Claude process, and the watchdog poll-loops watching two conditions:

**1. Signal forwarding.** `SIGTERM`, `SIGINT`, and `SIGHUP` are caught and forwarded to the child process group:

```swift
// Sources/CodexBarClaudeWatchdog/main.swift:97-99
signal(SIGTERM, handleTerminationSignal)
signal(SIGINT, handleTerminationSignal)
signal(SIGHUP, handleTerminationSignal)
```

**2. Parent-death detection via `getppid() == 1`.** When a process's parent dies, `init` (PID 1) adopts it. The watchdog checks for this every 200 ms:

```swift
// Sources/CodexBarClaudeWatchdog/main.swift:115-120
if getppid() == 1 {
    terminateChild()
    _ = waitpid(globalChildPID, &status, 0)
    Darwin.exit(exitCode(fromWaitStatus: status))
}
```

Kill semantics are two-stage: `SIGTERM` to the process group (including any grandchildren spawned by the CLI), then a 500 ms grace period, then `SIGKILL` if still alive. The watchdog puts the child into its own process group immediately after spawn (`setpgid(globalChildPID, globalChildPID)`) so that the group kill can't accidentally catch the watchdog itself.

The exit-code translation hand-decodes `wait(2)` macros that Swift cannot import directly (they are C function-like macros): signal-killed processes exit as `128 + signal_number`, normal exits propagate the exit byte verbatim.

```mermaid
sequenceDiagram
    participant App as CodexBar
    participant WD as Watchdog
    participant CLI as Claude CLI

    App->>WD: posix_spawn(watchdog)
    WD->>CLI: posix_spawnp(claude-cli)
    WD->>WD: setpgid(childPID, childPID)
    loop every 200ms
        WD->>WD: waitpid(WNOHANG)
        WD->>WD: check getppid() == 1?
        WD->>WD: check globalShouldTerminate?
    end
    App--xWD: app crashes / killed
    Note over WD: getppid() becomes 1
    WD->>CLI: SIGTERM → process group
    WD->>CLI: SIGKILL (if still alive after 500ms)
    WD->>WD: exit
```

Sources: [Sources/CodexBarClaudeWatchdog/main.swift:1-122]()

---

## Summary

These six quirks form a defense-in-depth layer around the parts of CodexBar that touch system APIs. The keychain migration and browser cookie gate together prevent permission-dialog fatigue. The universal quota parser absorbs API schema drift without requiring code changes. The peak-hour clock converts a server-side load pattern into a user-visible, timezone-correct signal. The dual-path display link keeps live countdowns smooth across an OS-version boundary. And the watchdog ensures that spawned subprocesses never outlive the parent, regardless of how the parent terminates. Collectively they represent the kind of knowledge that exists only in commit history and code comments—this page is a map of that hidden terrain.

Sources: [Sources/CodexBarClaudeWatchdog/main.swift:85-122]()
