docs/DEVELOPMENT.md
# Full build, package, and launch (recommended)
./Scripts/compile_and_run.sh
# Also run the sharded test suite before packaging/relaunching
./Scripts/compile_and_run.sh --test
# Just build and package (no tests)
./Scripts/package_app.sh
# Launch existing app (no rebuild)
./Scripts/launch.sh
Sources/CodexBar/./Scripts/compile_and_run.sh --test to test, rebuild, and launch~/Library/Logs/CodexBar/CodexBar.log (verbosity defaults to "Verbose")CodexBar does not run a prompt-capable startup Keychain migration. Unified config migration reads retired stores and clears them only after every source was readable and the new config was persisted. If a source is unreadable, cleanup and migration completion are deferred to a later launch.
Ad-hoc development builds can still prompt for browser or provider-owned items because their code-signing identity is not stable. Use a consistently signed packaged bundle for intentional live credential validation. Routine tests must use the repository's suppression-safe test harness and never query the real Keychain.
ThisDeviceOnly accessibility controls item availability and syncing; it does not repair a code-signature ACL
mismatch or prevent authorization prompts.See Keychain prompts for the current user-facing boundary and safe troubleshooting.
CodexBar checks Augment through the provider fetch pipeline. Auto mode tries the Augment CLI first, then the browser-cookie web path. The web path reuses cached cookies when possible and imports from supported browsers when the cache is missing or rejected.
If automatic import fails:
Key source, test, and packaging paths (not exhaustive):
CodexBar/
├── Sources/CodexBar/ # Main app (SwiftUI + AppKit)
│ ├── CodexbarApp.swift # App entry point
│ ├── StatusItemController*.swift # Menu bar icon, menu rendering, and actions
│ ├── UsageStore*.swift # Usage refresh, caching, widgets, and history
│ ├── SettingsStore*.swift # User preferences and config persistence
│ ├── Providers/ # App-side provider settings/runtime glue
│ └── Resources/ # Assets and localized strings
├── Sources/CodexBarCore/ # Shared business logic used by app, CLI, and widgets
│ ├── Config/ # Config file model, reader, writer, and validation
│ ├── Providers/ # Provider descriptors, fetchers, parsers, and status probes
│ ├── OpenAIWeb/ # OpenAI dashboard integration helpers
│ ├── WebKit/ # Web session helpers
│ └── Vendored/ # Embedded support code
├── Sources/CodexBarCLI/ # Bundled codexbar command-line tool
├── Sources/CodexBarWidget/ # WidgetKit support
├── WidgetExtension/ # Xcode wrapper for the packaged widget extension
├── Tests/CodexBarTests/ # macOS app/core test suite (XCTest + Swift Testing)
├── TestsLinux/ # Portable and Linux-specific CLI/core tests (target exists on macOS too)
└── Scripts/ # Build and packaging scripts
See the canonical provider authoring guide for the complete flow.
Sources/CodexBarCore/Providers/Providers.swift.Sources/CodexBarCore/Providers/YourProvider/.Sources/CodexBarCore/Providers/ProviderDescriptor.swift.ProviderImplementation under Sources/CodexBar/Providers/YourProvider/; implementations can use
protocol defaults when no custom UI or macOS integration is needed.Sources/CodexBar/Providers/Shared/ProviderImplementationRegistry.swift.Sources/CodexBar/Resources/.Tests/CodexBarTests/ and, for CLI/core behavior that must run on Linux, TestsLinux/../Scripts/compile_and_run.sh.subsystem:com.steipete.codexbar category:augment[augment-cookie] prefixmake test
Suite commands retain the default 180-second deadline, including SwiftPM startup and discovery.
The runner reports elapsed time and owned PIDs every 30 seconds even when test output is buffered.
It tracks process birth identities and descendants, including helpers that create separate process
groups or sessions, and drains them before retrying. Separate sessions are allowed: the shell runner
uses them to protect controlling-terminal ownership. The direct command remains unreaped until cleanup
finishes (waitid with WNOWAIT), preventing reuse of its PID/session. Observed descendant session
leaders can establish ownership of orphaned session members only while a matching live or unreaped
birth identity still anchors that session. Known descendants retain their identities after reparenting;
empty completed sessions retire. If an observed session loses its anchor while unaccounted live members
remain, cleanup fails without adopting or signaling those uncertain PIDs. Unavailable metadata for a
known identity also fails cleanup; an unreadable unrelated peer does not abort enumeration.
For the direct child, confirmed metadata absence (ESRCH/ENOENT) can precede a waitable exit on
Darwin. Its unreaped wait handle retains ownership while ordinary polling continues within the
original command deadline. Pending wait status is not completion; permission and I/O errors still
fail. Direct-child cleanup signals require retained wait ownership, even if no birth metadata was
captured. Other PIDs continue to require verified birth identities.
Cleanup sends TERM to verified individual identities, escalates after three seconds, and requires the
owned set to drain within five seconds. Error paths make a final bounded attempt against readable known
identities and reap the direct child, then propagate failure instead of starting another suite/retry.
Initialization failures similarly TERM/KILL/reap the still-owned direct child. Linux treats a zombie
leader with other threads as running. Other PIDs require birth checks before signals, with pidfds used
on Linux when available. macOS descendant signals use proc_signal_with_audittoken: a combined native
BSD/unique-identity read must match the tracked birth, then the kernel binds the signal to that PID's
generation while checking the caller's normal signal permissions. A stale generation (including an
exec race) is not signaled; later cleanup attempts can refresh it after matching birth again. Missing
native signal support, permission failures, and I/O errors fail cleanup without a numeric-PID fallback.
This is a bounded metadata polling tracker, not a daemon sandbox. A new session whose leader and attached
ancestry both disappear entirely between observations cannot be discovered reliably. Snapshot enumeration
is not atomic, and unreadable, never-observed descendants cannot be attributed. The initial swift test list
discovery/build path is unchanged; the 180-second bound applies to each suite/group command, including its
startup. Scripts/test_swift_test_sharding.sh includes synthetic containment tests; they do not launch
Swift, the app, or provider probes. Its native pthread regression runs only on Linux and uses the system C
compiler; macOS runs the corresponding metadata unit tests and reports the native case as skipped.
macOS also runs native audit-token fixtures: deliberately wrong generations leave owned children alive,
matching identities TERM/KILL and reap them, and unrelated sentinels survive. These fixtures use stop
files and self-expiry, with final cleanup restricted to their unreaped direct children.
Process-cleanup fixtures keep ancestry alive until the real ownership refresh observes matching ready
child identities (and the session-tree grandchild), then acknowledges a private fixture gate before drain.
The direct waitid fixture uses the same handshake without reaping its root. Immediate cases and a controlled
one-second readiness delay retain the two-second command budget; startup no longer adds a fixed 1.2-second
ancestry sleep. Gate waits are bounded and stop-file aware; helper self-expiry remains 20 seconds.
Cost performance and fair-scheduling corpora use exclusive initial fixture creation: the scanner only reads after setup has closed each file. This avoids per-file atomic publication and durability work without changing corpus contents or scan budgets. The shared atomic fixture writer remains available for replacement and publication tests.
Heuristics and timer tests seed disabled providers through testSettingsStore(config:), which saves the
file-backed config before settings initialization. They do not replay a synchronous config write for each
provider toggle during setup. The seed preserves provider defaults and explicitly keeps OpenAI web access
off; an existing config would otherwise enable it. Reset-boundary tests then enable only the stubbed Codex
provider. Timer intervals, polling deadlines, and production persistence behavior remain unchanged.
Profile/reuse and overlapping-capture tests wait for the fixture's expected Account: response with idle
completion disabled; PTY command echo alone is not functional completion. The profile/reuse test keeps both
immediate responses and a controlled 0.5-second response delay, beyond the former 0.1-second idle window.
Capture budgets remain two seconds for profile/reuse and five seconds for overlap. Account, environment,
launch-count, isolation, and stale-artifact assertions remain independent of the completion condition.
Ordinary tests deny Codex credential-file access at the Codex-owned I/O boundaries, before reads,
existence probes, or writes. Detection uses the actual process name/environment, independently of
the credential environment under test. HOME, CODEX_HOME, XDG_DATA_HOME, and
CODEXBAR_ALLOW_TEST_KEYCHAIN_ACCESS do not authorize files. The disabled live-account test remains
disabled; neither LIVE_TEST nor Keychain permission alone bypasses this boundary.
Use CodexCredentialFileAccess.withFixtureScope(.init(files: [...], roots: [...])) for an explicitly
owned fixture. Synchronous and asynchronous forms restore the previous scope on return or throw.
Roots use path components, reject symlinks within the fixture, and never implicitly authorize all
of /tmp. The CodexCredentialFixtures test trait creates and cleans up one owned root per test;
credential/account fixtures allocate their homes under CodexCredentialFixtures.root. Promotion
and scoped-refresh containers share that helper. The trait also binds the existing dashboard-cache
URL override to its owned root, so cache reads, writes, and clears stay local to the test.
_loadForUsageForTesting adds only its explicit
home fixture to the current scope; configured external roots still need explicit authorization.
Pure parsers and fingerprint(data:) need no scope. Default managed-account and workspace-cache
stores use fresh temporary paths in tests; explicit file overrides retain their existing behavior.
Task-local scopes inherit through structured tasks, but not detached work. Capture the immutable
fixtureScope and explicitly re-enter it in a detached task when fixture access is required; lost
context fails closed. Never authorize a path just because it appears in an environment dictionary.
Scripts/test.sh exports CODEXBAR_TEST_CODEX_FILE_ISOLATION=1 and removes inherited fixture grants.
Direct test commands that launch children should export the same signal. A child needing files must
receive FixtureScope(files: ..., roots: ...).childEnvironment(base: ...) naming only that child's
fixtures; this replaces any inherited grants without changing global environment state. The signal,
scope decoding, and denial are compiled in release builds too. bash Scripts/test_codex_file_isolation_child.sh
compiles the actual policy and detector with optimization and without DEBUG, then checks denied,
scoped-child, and non-test decisions against synthetic temporary files. It does not build or exercise
the complete release CLI, refresh a real account, or establish isolation for other providers.
OpenAIDashboardWebViewCacheTests uses explicit nonpersistent stores and a DEBUG preparation seam to suspend
acquisitions without navigation. Controlled continuations exercise eviction/replacement, stale completion, timeout
retry, store-scoped invalidation, and lease cleanup after cache loss. Host assertions check one cleanup request and
registration with WebKitTeardown; they do not establish WebContent process termination or diagnose CPU/RSS incidents.
The existing headless CI guards remain in place for these AppKit tests. The suite also contains older persistent-store
factory tests; exclude those when running a nonpersistent-only focused check.
The lint-build-test check in .github/workflows/ci.yml keeps its existing name and requires successful lint,
change detection, and the full build-linux-cli glibc matrix (x86_64 and ARM64 build, tests, and smoke checks).
Glibc Linux has no path or draft skip: failure, cancellation, skipped, empty, missing, or unknown matrix results
fail verification. macOS tests and the musl build may skip only when their path gates allow it; required macOS
tests deferred for a draft still leave the aggregate incomplete. Whole-workflow cancellation retains the existing
always() && !cancelled() condition, so the verifier does not run in that case.
Scripts/test_ci_path_gate.sh checks these result combinations and the workflow's Linux dependency and eighth
verifier argument. CodexBarLinuxTests includes the portable AntigravityLocalhostSessionLifetimeTests suite on
both macOS and Linux. It checks session reuse and concurrent synthetic loopback failures without credentials;
this coverage does not establish or fix the cause of Linux dispatch crashes.
swiftformat Sources Tests
swiftlint --strict
./Scripts/package_app.sh
# Creates: CodexBar.app with ad-hoc signing by default
./Scripts/sign-and-notarize.sh
# Creates: CodexBar-<version>.zip and CodexBar-<version>.dSYM.zip
See docs/RELEASING.md for full release process.
# Check crash logs
ls -lt ~/Library/Logs/DiagnosticReports/CodexBar* | head -5
# Check Console.app for errors
# Filter: process:CodexBar
Confirm the prompt's requested item and requesting binary, then check for another running or installed CodexBar copy. Do not validate a fix by querying the real Keychain from routine tests. See Keychain prompts.
Debug builds start the hang watchdog automatically. To diagnose a release build, enable it explicitly and restart CodexBar:
defaults write com.steipete.codexbar debugMainThreadHangWatchdog -bool true
Hangs are written to the app log. Hangs over two seconds also request a process
sample under ~/Library/Logs/CodexBar/. Disable the release opt-in with:
defaults delete com.steipete.codexbar debugMainThreadHangWatchdog
~/.codexbar/config.json for provider settings, manual cookies, and stored API keys