| summary | Development workflow: build/run scripts, logging, and keychain migration notes. | |||
|---|---|---|---|---|
| read_when |
|
# 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- Make code changes in
Sources/CodexBar/ - Run
./Scripts/compile_and_run.sh --testto test, rebuild, and launch - Check logs in Console.app (filter by "codexbar")
- Optional file log: enable Debug → Logging → "Enable file logging" to write
~/Library/Logs/CodexBar/CodexBar.log(verbosity defaults to "Verbose")
CI builds and tests with Xcode 27 only, on GitHub's xcode-27 image. Older
toolchains (the former Xcode 26.3 / macOS 15 compatibility build) are no longer
verified; the package manifest still declares Swift tools 6.2, but builds with
older Xcode releases are best effort.
Keep large initializer and #expect expressions simple: bind intermediate values
to explicitly typed locals when the type checker struggles. Use
ProviderColor(hex:) for provider colors instead of arithmetic inside spec initializers.
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.
- Keychain access control checks the executable's code signature and designated requirement.
- Ad-hoc builds and changed identities may no longer match an existing grant.
- Chromium and provider apps can rotate or recreate their foreign-owned items, replacing prior grants.
ThisDeviceOnlyaccessibility 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.
- Fresh-install default: Adaptive, between 2 and 30 minutes (configurable in Preferences → General). Existing installs without a stored cadence retain the legacy 5-minute fallback.
- Minimum: 1 minute
- Cookie import happens automatically when cached cookies need refresh
- Safari, Chrome variants, Edge variants, Brave, Arc variants, Dia, and Firefox.
If automatic import fails:
- Open Preferences → Providers → Augment
- Change "Cookie source" to "Manual"
- Paste cookie header from browser DevTools
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.
- Add the provider identity to
Sources/CodexBarCore/Providers/Providers.swift. - Add the descriptor and the fetcher, parser, settings-reader, or status-probe pieces the provider needs under
Sources/CodexBarCore/Providers/YourProvider/. - Follow the descriptor naming convention in the provider authoring guide.
- Add an app-side
ProviderImplementationunderSources/CodexBar/Providers/YourProvider/; implementations can use protocol defaults when no custom UI or macOS integration is needed. - Run
Scripts/regenerate-provider-manifests.shto update the Core descriptor manifest and the immutable app catalog. - Add icon assets under
Sources/CodexBar/Resources/. - Add focused tests under
Tests/CodexBarTests/and, for CLI/core behavior that must run on Linux,TestsLinux/.
- Enable Debug → Logging → "Enable file logging" or raise verbosity in the app settings.
- Reproduce with
./Scripts/compile_and_run.sh. - Check logs in Console.app:
- Filter:
subsystem:com.steipete.codexbar category:augment - Importer messages include the
[augment-cookie]prefix
- Filter:
Status-item creation checks the item's saved preferred position and its matching legacy key before assigning the
autosave name. Malformed, non-finite, non-positive, and out-of-bounds positions are removed; unrelated items are
untouched, and each removed key is logged. The bound is the widest connected display's width in points plus a
512-point margin, independent of display arrangement. Finite positive positions are preserved when no display bound
is available. Unlike the older global-coordinate bound, this also clears menu-manager parking positions beyond that
range. Preferred-position repair runs on each creation, independently of the one-time hidden-visibility repair flag.
Items are created with zero length, assigned their stable autosave name, registered, then given variable length.
Startup, provider vending, and visibility recovery all use this synchronous factory; recovery keeps codexbar-merged.
Dictionary-backed placement tests and a recording item cover cleanup and creation order without creating live status
items. AppKit exposes no public factory taking an autosave name, so zero-length creation cannot prove how a menu
manager enumerates an item inside AppKit's factory. These tests also do not establish the writer of a position that
changes after launch; recurring placement and Bartender UUID behavior still require isolated runtime evidence.
Runtime removal and visibility changes preserve the current saved position if AppKit clears it. Runtime removal
hides the item under its stable name, removes it with that name intact, then retires the autosave identity to prevent
later cleanup from clearing the restored position. This includes startup visibility recovery when Control Center
has not hosted the items yet: resetting a visible item's name before removal exposes a new automatic identity to
menu bar managers. Replacement items keep the existing codexbar-merged and codexbar-<provider> names. During
applicationWillTerminate, removal instead keeps the identity intact: renaming a host immediately before exit can
leave a blank Control Center slot on macOS 26.6.2. Status-menu Quit requests termination after menu tracking unwinds
and leaves cleanup to that callback; shutdown detaches menus without hiding or renaming the items before removal.
The deterministic tests use in-memory defaults, an injected recording status bar, and a hosting probe that misses
the first startup sample to check recovery and teardown ordering, identity, visibility, and placement restoration.
They compare already-hosted relaunches with delayed hosting; they do not reproduce Sparkle or Bartender's UUID store.
Native proof must use a signed, isolated app with visibly hosted
merged and provider items: record the exact old window IDs, quit normally, confirm those windows disappear, then
relaunch and check custom positions. Also exercise runtime removal/recreation and hide/show. Unit tests cannot prove
Control Center host removal or placement after process exit. This does not diagnose older out-of-range placement reports.
The shell test runners and all Make test targets source Scripts/test_environment.sh before launching Swift.
The Linux CI test step sources it too. It removes exported variables whose names contain TOKEN, KEY, SECRET,
PASSWORD, PASSWD, WEBHOOK, CREDENTIAL, COOKIE, PRIVATE, or _PAT, ignoring case. Explicit non-secret
exceptions preserve CODEXBAR_ALLOW_TEST_KEYCHAIN_ACCESS, CODEXBAR_SUPPRESS_TEST_KEYCHAIN_ACCESS,
CODEXBAR_DISABLE_KEYCHAIN_ACCESS, and CODEXBAR_USE_LOCAL_SWEETCOOKIEKIT. Standard build and loader search paths
(LD_LIBRARY_PATH, DYLD_LIBRARY_PATH, DYLD_FRAMEWORK_PATH, LIBRARY_PATH, and PKG_CONFIG_PATH) are also preserved:
their _PATH suffix otherwise matches _PAT. Other matching variables, including CODEXBAR_* credentials, are removed.
Use synthetic dictionaries or set synthetic sentinels inside fixtures; never depend on inherited real credentials.
For direct swift test, source the script in a Bash subshell first. This does not authorize live account tests.
@ProcessEnvironment provides count-only descriptions and reflection for stored process-environment dictionaries
throughout the app, CLI, provider contexts, and session scanners. Use it on every stored environment, including
captured configuration structs and optional dictionaries. Optional storage preserves nil versus an empty map;
equality still compares the original contents. Keep formerly immutable properties private(set).
Explicit dictionary access still returns the original values for provider/subprocess use; never log that dictionary.
Harness scrubbing remains essential and does not replace a review of debug output before sharing it.
ProcessEnvironmentStorageTests scans shipped Swift in Sources/ and WidgetExtension/ for environment-named
dictionary declarations (including optional, multiline, and Dictionary<String, String> spellings). This lexical
tripwire checks locals too; its exact-source allowlist documents only transient dictionaries and rejects stale or
duplicate exceptions. Computed getters and function parameters are not storage. Inferred types, aliases, differently
named dictionaries, and explicit dictionary logging still require code review; this is not a Swift dataflow analyzer.
Lint tools are installed at repository-pinned versions by Scripts/install_lint_tools.sh, with archive checksums
verified before installation. TypeScript 7 installs its native package for the running Node platform and architecture
(including Rosetta). Plugin typechecking uses only its declared libraries and source declarations, so unrelated
ancestor node_modules/@types packages do not affect the result. SwiftFormat targets the package's Swift 6.2 floor.
make testFor focused iteration, use native SwiftPM filters with the same file/Keychain isolation and process containment:
make test-fast FILTER='AdaptiveRefreshPolicyTests'
make test-skip-build FILTER='(?<suite>AdaptiveRefreshPolicyTests)'
./Scripts/test_fast.sh --filter FirstSuite --filter SecondSuite --configuration debugFILTER is passed literally, including Make/shell syntax and apostrophes. The script forwards all arguments to
swift test; SwiftPM owns regex syntax and repeated-filter selection. Runs are serial by default and have a
30-minute build-and-test deadline, configurable with CODEXBAR_TEST_NATIVE_TIMEOUT. An explicit --skip-build uses
existing binaries. No framework search-path override is added.
make test remains the complete sharded path. Measured expensive suites run in their own groups, retaining all
selections and their deadlines while avoiding whole-batch retries. Apple-Silicon macOS CI includes the CLI entry suite;
the former Intel-runner exclusion is retired.
Claude OAuth gate tests use ClaudeOAuthDefaultsFixtures() so each test case owns an in-memory preferences store.
Keep reset, expiry, and persisted-key assertions inside that scope; nested tasks inherit it, detached tasks do not.
The gates retain their normal production defaults domain. Continue serializing shared gate state and same-host
full-suite runs: isolating these preferences does not isolate every test dependency.
make test and make check require a python3 that provides os.waitid with WNOWAIT. Some macOS Python
builds, including Apple's /usr/bin/python3, do not provide it. The test runner then stops before its initial
Swift discovery/build and names the interpreter path, its version, and the missing attributes. Earlier
make check checks may already have run. For an installed Homebrew Python, select its generic commands with:
PATH="$(brew --prefix python@3.14)/libexec/bin:$PATH" make check
PATH="$(brew --prefix python@3.14)/libexec/bin:$PATH" make testScripts/test.sh --list-only does not need process containment, but still invokes swift test list, which may build.
The macOS test target explicitly links the existing Sparkle product and locates frameworks in the products directory
beside its XCTest bundle. This supports native focused tests on fresh SwiftPM builds. The sharded runner retains its
guarded runtime recovery for differing toolchain layouts, and make test remains the full validation path.
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, observation may retain that session as pending only while the direct command is still wait-owned
and running. A nested runner can then finish draining its own child: its unreaped wait handle is not
signal authority for the outer observer, and macOS may hide the exited leader's metadata. Pending members
remain birth-checked even if they change sessions. Uncertainty also follows observed descendants and peers
of a live matching pending session leader, with compatible birth ordering; those identities remain pending
after reparenting or session migration. Unreadable pending metadata fails closed; confirmed exits or replaced
pending births retire without claiming replacements. Pending status grants no ownership or signal authority.
Command exit and every cleanup path require session continuity and fail if uncertainty remains; a detected
replacement session-leader birth still fails immediately, even during observation.
No observation or cleanup deadline is extended. 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. Success/failure fixtures keep
their five-second command budget; timeout fixtures keep two seconds. A virtual-clock readiness test proves
one-second startup adds no fixed ancestry sleep. Gate waits are bounded and stop-file aware; helper
self-expiry remains 20 seconds.
The nested failure regression gates the inner drain and controls outer snapshots: the outer observer
sees a live session leader first, then its previously unseen orphan with the exited leader hidden.
Only the inner wait owner drains that orphan; the outer runner must complete without claiming it.
Contract tests also require unresolved sessions to fail at cleanup and reject reused leader births.
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.
Menu fixtures use enableTestProviders to arrange their initial provider selection without repeatedly persisting
already-correct config entries. The real setter still handles changed flags and selected-provider cleanup. Keep
provider-toggle actions under test on the production setter; the fixture helper is for setup before observing changes.
CostUsageJsonl finds complete physical LF spans with bounded libc memchr on Darwin, Glibc, and
Musl, using the same span algorithm with scalar LF search elsewhere. Borrowed pointers stay inside
the current withUnsafeBytes call; search counts and distances fit the existing 256 KiB read chunk.
Complete spans keep the original prefix-copy and flush path. Unfinished chunk suffixes still run the
original scalar JSON tail updater, preserving exact resumable state and EOF validation. CR bytes,
empty-line offsets, independent prefix/max limits, budgets, and cancellation/stop ordering are unchanged.
Persisted Codable checkpoints are a compatibility boundary, including unusual decoded counters. Skipping tail updates requires a nonnegative line count, safe container-depth arithmetic for the span, and nonnegative literal indices. Otherwise the same scalar updater runs before the existing flush; this preserves its checked operations and state when the flush does not reset a negative line count. Do not replace this guard with checkpoint normalization or a new corrupt-cache recovery policy.
CostUsageJsonlDifferentialTests in the portable TestsLinux target compares exact bytes, offsets,
sorted full Codable state, callback order, and error domain/code with the frozen e236a21b scalar scanner.
Its bounded default matrix covers every short-input split, threshold/chunk boundaries, mutations,
cross-version resumes, and safe unusual checkpoints. Keep that oracle frozen and the older scanner
oracle independently useful. Full optimized scratch parity must copy the final production source;
prototype results alone do not carry forward through formatting or edits. Scanner parity does not
establish pipeline performance; signed optimized builds and synthetic pipeline timing are separate proof.
The shared scanner remains a parser-hash input. The warm-refresh cursor repair adopts 9ca89383b9957b07
without rebuilding native rows or checkpoints; it changes scheduling, not persisted parsing semantics.
Published e0b0319de43e22d7 is also a tested compatible predecessor because LF-span scanning preserves persisted semantics, including the priority
cursor changes in #3318. The earlier 7e293e8fc9e25700 and existing predecessors remain supported.
Native adoption retains rows and checkpoints while invalidating old connection receipts.
Pi/OMP still reparses once when the hash changes,
with and without a catalog; subsequent unchanged reads use the current cache. No pricing-key alias is added.
Profile the cost-scan queue separately from the main thread. A busy background scan that later settles does not establish an infinite loop. Native timestamp conversion uses Foundation's modern ISO parser for strict RFC3339 input, retaining the previous formatter's millisecond truncation and rounding. Historical spellings and malformed input keep the formatter fallback. Claude reuses the parsed date for local day projection only on the strict path.
CostUsageTimestampTests compares exact dates with the prior formatter and checks local days, DST,
deduplication, and dated pricing. CostUsageTimestampOrderTests and CostUsageStoreCutoverTests count
timestamp comparisons: a known ordered prefix needs only the append boundary and new events; unknown
prefixes get one cancellable validation. Keep these assertions deterministic rather than timing gates.
Run these alongside scanner, cancellation, bounded-progress, fork, and performance-gate suites with
the test harness's Keychain and credential-file isolation enabled.
An optimized synthetic check on 2026-08-30 compared main 5a18e8ee9 with this change: three cold
Claude scans of 20,000 messages (5,237,780 bytes) had median CPU time of 3.430 → 1.273 seconds and
wall time of 5.838 → 2.107 seconds, with identical emitted token/cost totals and daily output. This
measures ingestion through the public fetcher, not idle-app CPU or the entire Codex scan pipeline.
Native Codex scans carry an opaque receipt from load to save. CostUsageStore owns one decoded persisted
baseline and compact file/count metadata, releasing it on save, superseding loads, mutations, failures,
or scan exit (including cancellation and debounce). Abandoned receipts also release through the actor.
No raw historical SQL snapshot or transaction stays alive across JSONL scanning. Filesystem/device,
anchor, and pending catch-up reconciliation rerun for comparisons; decoded reuse never freezes them.
Reuse requires the same connection generation and database inode, SQLite's open-file identity check,
same-connection data_version, own total_changes, and schema/parser metadata. Observations bracket
a successfully committed short read transaction. Save checks again under BEGIN IMMEDIATE, after
unchanged-path retention; external changes request a rescan without overwriting current content.
Retention that rewrites identical metadata requires a fresh locked semantic comparison. Existing callers
without a receipt read a fresh baseline at save and cannot establish freshness back to an earlier load.
CostUsageStoreReadWorkTests counts full load/save cycles: an uncontended unchanged receipt cycle reads
one scanner snapshot and decodes each exact usage row once, without reading raw token snapshots, with
one freshness write and no aggregate grouping visits. Changed files and fork ancestors hydrate token
history through the original receipt, validating its stamp after the read transaction commits. Synthetic
interleavings cover writer races, mutations, retention, replacement and receipt lifetime.
Initial decoding, semantic equality, filesystem reconciliation, report generation and priority aggregation
still cost work proportional to retained history. These counters do not measure installed-app idle CPU;
refresh cadence, scan budgets, timestamp parsing and incremental-order validation are unchanged.
Claude/Vertex metadata classification searches decoded ASCII strings with case-folded bytes, keeping
the original Foundation lowercase/substring predicate for non-ASCII or noncontiguous strings. Check
the whole string for ASCII before matching; combining characters after a marker can affect the old
predicate. The recursive dictionary/array walk visits dictionaries inside arrays without recursing into nested arrays.
CostUsageClaudeVertexClassifierTests compares with the frozen old predicate and checks complete filtered
rows, persisted daily tokens/costs, and reports, including decoded JSON escapes, Unicode boundaries,
nested arrays, and false/numeric metadata flags.
ClaudeJSONObject shares a shallow decoded-container view between field extraction and classification;
the parser reuses its message view for primary detection and usage extraction. On Darwin, ASCII-keyed
objects retain immutable Foundation containers and use scoped CF bulk access and type dispatch. Only
JSONSerialization results and their decoded descendants enter that path; arbitrary objects and coerced
entries use Swift casts. CF bridging is Darwin-only, and retained owners outlive all borrowed pointers
and temporary allocations.
Empty containers require no pointer arithmetic. Unicode-keyed objects use the actual conditional
[String: Any] coercion at each object boundary, preserving canonical-key collapse and whole-object
mixed-key rejection. The walker visits only the resolved entries. Independent coercions can choose
different collision winners, so tests assert resolved-entry behavior rather than a deterministic winner.
Linux uses the same view and walker with portable Swift coercions, with no CF bridging or separate
pricing path. ClaudeJSONObjectTests also belongs to the portable CLI/core test target.
Claude parsing returns only rows and parsed bytes. The scan owns reconciliation across streaming chunks,
parent files and subagents, then builds persisted days from the stored row model; there is no discarded
parser-day aggregation or second normalization. Daily tests exercise the real cache/report boundary.
Removing the unused field in the shared scanner changes the generated native parser hash, while Codex
semantics remain unchanged. Published 494eee446bb2e5f9 is a tested compatible predecessor; existing
predecessors and store receipt logic remain intact. Pi/OMP pricing keys include this hash and therefore
reparse once under the existing invalidation contract, also tested with and without a catalog.
A second optimized synthetic check against main 354191af9 used three fresh-cache scans per provider
with 32–128 KiB text bodies. Median CPU decreased by 3–18% across Claude/Vertex cases (the 3% case is
small); a separate long-provider-string stress case decreased by 73–75%. The isolated decoded metadata
predicate used about half the CPU on ordinary nested metadata. Every daily token component and cost
matched, including the existing unset public request counts. Fixture generation was outside timing;
wall time was recorded separately under host load. These results do not measure idle-app CPU.
Claude and Vertex scans share one synchronous invocation-owned pricing resolver across full/append file parsing, row normalization, and report repricing. It lazily snapshots the catalog, including an empty sentinel for unavailable artifacts, at the existing changed-file and nonempty-report preparation points. An exact report memo hit and an empty inventory with no rows do not load it. The internal standalone parser now owns one snapshot per parse, optionally supplied explicitly; the cancellable parser takes that owner directly. It does not reread pricing artifacts between rows.
Normalization and positive/negative catalog lookup memos use exact decoded UTF-8 keys, preserving Unicode spelling, dated raw versus stored identities, and non-idempotent normalization. Each memo retains at most 1,024 entries per invocation; after saturation, uncached inputs still resolve normally. This bounds entry growth, not model-string bytes or scan work. Every row still selects its own dated tariff and context tier and runs the existing monetary arithmetic. Historical pricing short-circuits before model lookup. Independent scalar Pi/Cursor/direct pricing retains its uncached resolution path. Both callers share one private tariff-selection helper whose nonescaping lazy lookup closure runs only after historical selection. The scalar calls the original normalizer and lookup directly; the scan resolver supplies memoized resolution. Both use the original monetary calculation.
DEBUG Claude scan metrics count normalization cache misses and actual catalog-model lookups with
positive/negative outcomes; the first lookup still normalizes internally. repricedRows continues to
count all rows. Measure through the synchronous scoped recorder because the public dispatch queue
does not inherit TaskLocal instrumentation. Resolver and scanner memo tests exercise cross-file reuse,
snapshot replacement, saturation, exact spelling, and report-only repricing against synthetic fixtures.
The shared pricing source changes the generated Codex parser hash, but Codex algorithms are unchanged;
6366caa15c925349 remains an explicitly tested compatible predecessor.
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.
The same runtime and inherited Codex-file isolation signal also redirects the scanner's default priority trace
database to a process-local, nonexistent temporary path before consulting the user home. Supplying a fixture
session root alone does not select a trace database. Tests exercising priority metadata should pass an explicitly
owned codexTraceDatabaseURL; these overrides and the production ~/.codex/logs_2.sqlite default are unchanged.
The optimized child-policy proof above also verifies this fallback, independently of Keychain opt-ins.
Cursor, Augment, Factory, and Notion session stores select a process-local temporary directory under Swift Testing or XCTest, before creating directories, loading saved files, or repairing permissions. The runtime guard is also compiled in release builds; allowing real Keychain access does not disable this file isolation. Production filenames and owner-only persistence remain unchanged.
Persistence tests should construct stores with an explicitly owned fileURL and clean up that fixture. Use fresh
writer/reader instances to prove disk reloads. The sharded runner exports CODEXBAR_TEST_SESSION_FILE_ISOLATION=1
for child processes; direct test commands that launch children should export it too. This covers these four default
session stores, not arbitrary file access or provider-owned credential databases.
bash Scripts/test_provider_session_file_isolation.sh compiles the actual path policy and runtime detector with
optimization and without DEBUG, then verifies inherited child isolation and unchanged production-relative paths
against fake Application Support files. It does not launch the app, read real sessions, or exercise a full release CLI.
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.
make check and portable CI lint run node Scripts/check-package-resolved.mjs and its synthetic regression tests
(node --test Scripts/test_package_resolved.mjs). This offline, read-only check compares every package identity,
revision, and version in the root and widget workspace Package.resolved files, including missing or extra pins.
Pin order and workspace-specific originHash values do not affect the comparison. After changing dependencies,
resolve the widget workspace from the repository root and commit both resolved files together:
xcodebuild -resolvePackageDependencies -project WidgetExtension/CodexBarWidgetExtension.xcodeprojThe check names each drifted package and prints this repair command before packaging can fail with an out-of-date resolved file when automatic resolution is disabled.
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)
when the shared macOS/Linux path gate requires it. Both platforms use the same docs/site allowlist; source,
test, widget, and workflow paths always require builds, including Markdown resources and fixtures. Linux has
no draft deferral. Only required=true / result=success or required=false / result=skipped passes; failure,
cancellation, missing or unknown values, and unexpected skips or executions fail verification.
The musl build retains its separate path gate; 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, shared path decisions, and the workflow's Linux
dependency, condition, and verifier arguments. 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.
CI and release builds install the static Linux SDK through Scripts/install_swift_static_sdk.sh. It downloads with
curl, verifies the pinned SHA-256, and passes a local archive to swift sdk install, avoiding SwiftPM's Linux
FoundationNetworking/TLS teardown crash. Portable lint checks cover checksum rejection, download failures, and installer
failure propagation without downloading an SDK.
Changes to the installer require a musl CI build.
swiftformat Sources Tests
swiftlint --strict./Scripts/package_app.sh
# Creates: CodexBar.app with ad-hoc signing by defaultFor an identity-signed package, set CODEXBAR_SIGNING=identity and APP_IDENTITY to an installed Developer ID Application signing
identity's full name, unique name substring, or SHA-1 certificate hash. Packaging resolves it through
security find-identity -p codesigning -v, derives the Team ID from the selected identity, and signs with that
certificate's hash. A missing or ambiguous match, a certificate other than Developer ID Application with a ten-character Team ID, or a conflicting
APP_TEAM_ID fails before entitlements are generated. Self-signed and Apple Development/Distribution certificates are not supported by this
path; use ad-hoc packaging or a Developer ID Application identity. Developer ID names carry a Team ID, whereas
development certificate names can carry a personal ID. Explicit identity selections are also validated in LLDB builds.
App and widget entitlements use the resolved team. Only upstream-team identity-signed release builds embed the
upstream provisioning profile and CloudKit entitlements; other teams package without those upstream resources.
Widget build failures and timestamp/signature failures still fail packaging. The sandboxed launch smoke check is
retained; the existing CODEXBAR_SKIP_LAUNCH_SMOKE=1 override explicitly reports that it skipped validation.
./Scripts/sign-and-notarize.sh
# Creates: CodexBar-<version>.zip and CodexBar-<version>.dSYM.zipsign-and-notarize.sh honors APP_IDENTITY and passes the same selection to packaging; it defaults to the upstream
Developer ID. Timestamping and hardened runtime remain required for all identity-signed releases.
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:CodexBarConfirm 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.
- Check the browser is supported by the Augment provider metadata
- Verify you're logged into Augment in that browser
- Check Preferences → Providers → Augment → Cookie source is "Automatic"
- Enable debug logging and check Console.app
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 trueHangs 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- No dock icon (LSUIElement = true)
- Status item only (NSStatusBar)
- SwiftUI for preferences, AppKit for menu
- Hidden 1×1 window keeps SwiftUI lifecycle alive
- Automatic browser import via SweetCookieKit
- Keychain cache for some imported browser cookies and OAuth/device-flow credentials
- The resolved config file for provider settings, manual cookies, and stored API keys: new installs use
~/.config/codexbar/config.json, while existing~/.codexbar/config.jsoninstalls retain their legacy path - Manual override for debugging
- Browser-cookie import when cached sessions need refresh
- Background timer (configurable frequency)
- Parallel provider fetches
- First failure can be suppressed when prior data exists
- WidgetKit snapshot for macOS widgets
make test remains serial by default. Invoke
./Scripts/test.sh --direct-workers 4 to request up to eight isolated group workers locally.
SwiftPM still builds and discovers the complete inventory. Before launch, the adapter enumerates
both XCTest and Swift Testing using the selected Xcode toolchain helpers and requires an exact
inventory match, including duplicate detection. An inventory mismatch fails the run before any
group executes. Local runs with missing helpers, unsupported toolchains, or Linux retain the serial
SwiftPM path with a diagnostic. On CI, requesting direct workers requires a verified direct runtime:
capability failures also fail the job instead of falling back to serial execution.
Hosted macOS CI runs the complete inventory with two direct workers on one xcode-27 (Xcode 27)
runner, retaining the 75-minute test step and 90-minute job limits. All groups are required:
discovery, admission, helper crashes, unrecovered timeouts and test failures fail the job. Group size remains
eight with a 120-second suite timeout and no retries for ordinary test failures. Timed-out groups retain
the existing isolated-selection retry policy; each retry has its own deadline. Three independent
hosted complete-inventory runs validated this mode before enabling it.
Both serial and direct modes print ordered selection groups and timing summaries.
Each macOS job restores a compiled SwiftPM cache bound to its toolchain, SDK, architecture, package state and build scripts/options. The helper verifies tracked input contents and permissions before restoring unchanged file timestamps. Changed contents invalidate preserved or backdated timestamps; added/deleted package paths or changed package symlink targets require clean products. Missing, invalid or unverifiable metadata also cleans products before rebuilding. Symlink payloads are checked without following them; reuse requires each package link to name a regular tracked file in both snapshots, whose contents are verified separately. External, untracked, chained and directory targets, including noncanonical paths that could conceal a symlink ancestor, require clean products without reading or modifying the target. Only successful main jobs with a clean tracked tree save new snapshots, and cleanup keeps one snapshot per active lane and deletes retired lanes. The first main run seeds the cache; a cache miss or a changed input graph still requires a cold build.
The adapter includes both public and private platform framework search paths and disables Swift Testing during XCTest discovery, matching SwiftPM's launcher. Probe failures print the helper, exit status or signal, and redacted stdout/stderr. On CI, signal failures also wait up to five seconds for fresh helper crash reports in the original and temporary homes. The workflow collects fresh test crash reports again after failures in the required full test job. Credential values and local home identities are redacted; unrelated process reports are excluded.
Each group has a fresh process and temporary HOME and CFFIXED_USER_HOME, with the existing credential
and session-file isolation, Keychain suppression, timeout, retry, and descendant cleanup.
Async process fixtures should use ProcessExitRelease.afterExit after signaling a Foundation child,
instead of blocking a cooperative executor in waitUntilExit(). PTY setup tests should keep the child
alive until its output is observed; Darwin can discard unread output when the slave closes. Keep
immediate-exit coverage in dedicated drain tests, and compare working directories after resolving symlinks.
Test output is buffered per group. A direct runtime failure after execution begins fails the run;
it does not silently rerun the suite through a different runtime. This opt-in adapter depends on
SwiftPM's toolchain helper contract and needs compatibility validation when updating Xcode.
--swift-command /path/to/swift-wrapper works when the wrapper forwards -print-target-info
unchanged to the selected Xcode Swift compiler and supports build --show-bin-path. The runner
queries the wrapper's products directory; a different compiler/target or command prefix arguments
are rejected (local serial fallback, CI failure). For a host requiring the native build backend,
use the same wrapper for serial and direct comparisons:
#!/bin/bash
set -euo pipefail
case "${1:-}" in
test|build)
subcommand="$1"
shift
exec /usr/bin/xcrun swift "$subcommand" --build-system native --jobs 4 -Xswiftc -gnone "$@"
;;
*) exec /usr/bin/xcrun swift "$@" ;;
esacSave it as an executable file, then invoke
./Scripts/test.sh --swift-command /path/to/swift-wrapper --direct-workers 4.
Wrappers may add build options; test-selection or runtime-environment changes inside a wrapper
cannot be reproduced by direct launch and are unsupported. Live/opt-in tests remain disabled by
their existing test conditions; this flag does not enable them.