diff --git a/docs/commands/list.md b/docs/commands/list.md index 0ea0fa7e..14df4a7e 100644 --- a/docs/commands/list.md +++ b/docs/commands/list.md @@ -4,6 +4,7 @@ ```shell codex-auth list +codex-auth list --active codex-auth list --live codex-auth list --api codex-auth list --skip-api @@ -20,6 +21,7 @@ codex-auth list --skip-api ## Refresh Modes - Default mode performs foreground usage and account-name API refresh. +- `--active` refreshes usage only for the active account before rendering and skips account-name API refresh. Other rows use stored registry snapshots. - `--api` is accepted as an explicit equivalent to default mode. - `--skip-api` forbids remote API calls for this command. - `--live` keeps refreshing the terminal view and requires a TTY. diff --git a/src/cli/commands/list.zig b/src/cli/commands/list.zig index c1fc53c7..bdfa1935 100644 --- a/src/cli/commands/list.zig +++ b/src/cli/commands/list.zig @@ -15,6 +15,11 @@ pub fn parse(allocator: std.mem.Allocator, args: []const [:0]const u8) !types.Pa opts.live = true; continue; } + if (std.mem.eql(u8, arg, "--active")) { + if (opts.active_only) return common.usageErrorResult(allocator, .list, "duplicate `--active` for `list`.", .{}); + opts.active_only = true; + continue; + } if (std.mem.eql(u8, arg, "--api")) { switch (opts.api_mode) { .default => opts.api_mode = .force_api, diff --git a/src/cli/help.zig b/src/cli/help.zig index 2d7ab060..53f6e1e3 100644 --- a/src/cli/help.zig +++ b/src/cli/help.zig @@ -48,7 +48,7 @@ pub fn writeHelp( try writeCommandSummary(out, use_color, "--help, -h", "Show this help"); try writeCommandSummary(out, use_color, "help ", "Show command-specific help"); try writeCommandSummary(out, use_color, "--version, -V", "Show version"); - try writeCommandSummary(out, use_color, "list [--live] [--api|--skip-api]", "List available accounts"); + try writeCommandSummary(out, use_color, "list [--live] [--active] [--api|--skip-api]", "List available accounts"); try writeCommandSummary(out, use_color, "status", "Show auto-switch and service status"); try writeCommandSummary(out, use_color, "login [--device-auth]", "Login and add the current account"); try writeCommandSummary(out, use_color, "import", "Import auth files or rebuild registry"); @@ -202,7 +202,7 @@ fn writeUsageLines(out: *std.Io.Writer, topic: HelpTopic) !void { try out.writeAll(" codex-auth --help\n"); try out.writeAll(" codex-auth help \n"); }, - .list => try out.writeAll(" codex-auth list [--live] [--api|--skip-api]\n"), + .list => try out.writeAll(" codex-auth list [--live] [--active] [--api|--skip-api]\n"), .status => try out.writeAll(" codex-auth status\n"), .login => { try out.writeAll(" codex-auth login\n"); @@ -267,6 +267,7 @@ fn writeOptionLines(out: *std.Io.Writer, topic: HelpTopic) !void { switch (topic) { .list => { try out.writeAll(" --live Open a live-updating table.\n"); + try out.writeAll(" --active Refresh only the active account before rendering.\n"); try out.writeAll(" --api Load usage and account data from APIs.\n"); try out.writeAll(" --skip-api Load usage and account data from local data only (may be inaccurate).\n"); }, @@ -330,6 +331,7 @@ fn writeExampleLines(out: *std.Io.Writer, topic: HelpTopic) !void { }, .list => { try out.writeAll(" codex-auth list\n"); + try out.writeAll(" codex-auth list --active\n"); try out.writeAll(" codex-auth list --live\n"); try out.writeAll(" codex-auth list --api\n"); try out.writeAll(" codex-auth list --skip-api\n"); diff --git a/src/cli/types.zig b/src/cli/types.zig index 3312649e..b51cf4fc 100644 --- a/src/cli/types.zig +++ b/src/cli/types.zig @@ -7,6 +7,7 @@ pub const ApiMode = enum { pub const ListOptions = struct { live: bool = false, api_mode: ApiMode = .default, + active_only: bool = false, }; pub const LoginOptions = struct { device_auth: bool = false, diff --git a/src/workflows/list.zig b/src/workflows/list.zig index 1879fb2d..dc66dd09 100644 --- a/src/workflows/list.zig +++ b/src/workflows/list.zig @@ -15,7 +15,7 @@ const maybeRefreshForegroundAccountNamesWithAccountApiEnabled = account_names.ma const ensureLiveTty = preflight.ensureLiveTty; const apiModeUsesApi = preflight.apiModeUsesApi; const ensureForegroundNodeAvailableWithApiEnabled = preflight.ensureForegroundNodeAvailableWithApiEnabled; -const refreshForegroundUsageForDisplayWithBatchFetcherUsingApiEnabled = usage_refresh.refreshForegroundUsageForDisplayWithBatchFetcherUsingApiEnabled; +const refreshForegroundUsageForDisplayWithBatchFetcherUsingApiEnabledAndActiveOnly = usage_refresh.refreshForegroundUsageForDisplayWithBatchFetcherUsingApiEnabledAndActiveOnly; const loadInitialLiveSelectionDisplay = live_flow.loadInitialLiveSelectionDisplay; const SwitchLiveRuntime = live_flow.SwitchLiveRuntime; const switchLiveRuntimeMaybeStartRefresh = live_flow.switchLiveRuntimeMaybeStartRefresh; @@ -74,7 +74,7 @@ pub fn handleList(allocator: std.mem.Allocator, codex_home: []const u8, opts: cl } const usage_api_enabled = apiModeUsesApi(reg.api.usage, opts.api_mode); - const account_api_enabled = apiModeUsesApi(reg.api.account, opts.api_mode); + const account_api_enabled = apiModeUsesApi(reg.api.account, opts.api_mode) and !opts.active_only; try ensureForegroundNodeAvailableWithApiEnabled( allocator, @@ -85,11 +85,12 @@ pub fn handleList(allocator: std.mem.Allocator, codex_home: []const u8, opts: cl account_api_enabled, ); - var usage_state = try refreshForegroundUsageForDisplayWithBatchFetcherUsingApiEnabled( + var usage_state = try refreshForegroundUsageForDisplayWithBatchFetcherUsingApiEnabledAndActiveOnly( allocator, codex_home, ®, usage_api_enabled, + opts.active_only, ); defer usage_state.deinit(allocator); try maybeRefreshForegroundAccountNamesWithAccountApiEnabled( diff --git a/src/workflows/root.zig b/src/workflows/root.zig index e24d737f..b5ec0a3b 100644 --- a/src/workflows/root.zig +++ b/src/workflows/root.zig @@ -41,7 +41,9 @@ pub const max_usage_override_display_width = usage_refresh.max_usage_override_di pub const formatStatusOverrideAlloc = usage_refresh.formatStatusOverrideAlloc; pub const refreshForegroundUsageForDisplayWithApiFetcher = usage_refresh.refreshForegroundUsageForDisplayWithApiFetcher; pub const refreshForegroundUsageForDisplay = usage_refresh.refreshForegroundUsageForDisplay; +pub const refreshForegroundUsageForDisplayWithBatchFetcherUsingApiEnabledAndActiveOnly = usage_refresh.refreshForegroundUsageForDisplayWithBatchFetcherUsingApiEnabledAndActiveOnly; pub const refreshForegroundUsageForDisplayWithApiFetcherWithPoolInit = usage_refresh.refreshForegroundUsageForDisplayWithApiFetcherWithPoolInit; +pub const refreshForegroundUsageForDisplayWithApiFetchersWithPoolInitUsingApiEnabledAndPersistAndActiveOnly = usage_refresh.refreshForegroundUsageForDisplayWithApiFetchersWithPoolInitUsingApiEnabledAndPersistAndActiveOnly; const refreshForegroundUsageForDisplayWithBatchFetcherUsingApiEnabled = usage_refresh.refreshForegroundUsageForDisplayWithBatchFetcherUsingApiEnabled; const refreshForegroundUsageForDisplayWithApiFetchersWithPoolInitUsingApiEnabledAndPersist = usage_refresh.refreshForegroundUsageForDisplayWithApiFetchersWithPoolInitUsingApiEnabledAndPersist; pub const initForegroundUsagePool = usage_refresh.initForegroundUsagePool; diff --git a/src/workflows/usage.zig b/src/workflows/usage.zig index 5c041b8e..02b13637 100644 --- a/src/workflows/usage.zig +++ b/src/workflows/usage.zig @@ -122,7 +122,7 @@ pub fn refreshForegroundUsageForDisplayWithBatchFetcherUsingApiEnabled( reg: *registry.Registry, usage_api_enabled: bool, ) !ForegroundUsageRefreshState { - return refreshForegroundUsageForDisplayWithBatchFetcherUsingApiEnabledWithBatchFailurePolicy( + return refreshForegroundUsageForDisplayWithBatchFetcherUsingApiEnabledAndActiveOnly( allocator, codex_home, reg, @@ -131,6 +131,23 @@ pub fn refreshForegroundUsageForDisplayWithBatchFetcherUsingApiEnabled( ); } +pub fn refreshForegroundUsageForDisplayWithBatchFetcherUsingApiEnabledAndActiveOnly( + allocator: std.mem.Allocator, + codex_home: []const u8, + reg: *registry.Registry, + usage_api_enabled: bool, + active_only: bool, +) !ForegroundUsageRefreshState { + return refreshForegroundUsageForDisplayWithBatchFetcherUsingApiEnabledWithBatchFailurePolicyAndActiveOnly( + allocator, + codex_home, + reg, + usage_api_enabled, + false, + active_only, + ); +} + pub fn refreshForegroundUsageForDisplayWithBatchFetcherUsingApiEnabledWithBatchFailurePolicy( allocator: std.mem.Allocator, codex_home: []const u8, @@ -138,7 +155,25 @@ pub fn refreshForegroundUsageForDisplayWithBatchFetcherUsingApiEnabledWithBatchF usage_api_enabled: bool, batch_fetch_failures_are_fatal: bool, ) !ForegroundUsageRefreshState { - return refreshForegroundUsageForDisplayWithApiFetchersWithPoolInitUsingApiEnabled( + return refreshForegroundUsageForDisplayWithBatchFetcherUsingApiEnabledWithBatchFailurePolicyAndActiveOnly( + allocator, + codex_home, + reg, + usage_api_enabled, + batch_fetch_failures_are_fatal, + false, + ); +} + +pub fn refreshForegroundUsageForDisplayWithBatchFetcherUsingApiEnabledWithBatchFailurePolicyAndActiveOnly( + allocator: std.mem.Allocator, + codex_home: []const u8, + reg: *registry.Registry, + usage_api_enabled: bool, + batch_fetch_failures_are_fatal: bool, + active_only: bool, +) !ForegroundUsageRefreshState { + return refreshForegroundUsageForDisplayWithApiFetchersWithPoolInitUsingApiEnabledAndActiveOnly( allocator, codex_home, reg, @@ -147,6 +182,7 @@ pub fn refreshForegroundUsageForDisplayWithBatchFetcherUsingApiEnabledWithBatchF initForegroundUsagePool, usage_api_enabled, batch_fetch_failures_are_fatal, + active_only, ); } @@ -179,7 +215,31 @@ pub fn refreshForegroundUsageForDisplayWithApiFetchersWithPoolInitUsingApiEnable usage_api_enabled: bool, batch_fetch_failures_are_fatal: bool, ) !ForegroundUsageRefreshState { - return refreshForegroundUsageForDisplayWithApiFetchersWithPoolInitUsingApiEnabledAndPersist( + return refreshForegroundUsageForDisplayWithApiFetchersWithPoolInitUsingApiEnabledAndActiveOnly( + allocator, + codex_home, + reg, + usage_fetcher, + batch_fetcher, + pool_init, + usage_api_enabled, + batch_fetch_failures_are_fatal, + false, + ); +} + +pub fn refreshForegroundUsageForDisplayWithApiFetchersWithPoolInitUsingApiEnabledAndActiveOnly( + allocator: std.mem.Allocator, + codex_home: []const u8, + reg: *registry.Registry, + usage_fetcher: UsageFetchDetailedFn, + batch_fetcher: ?UsageBatchFetchDetailedFn, + pool_init: ForegroundUsagePoolInitFn, + usage_api_enabled: bool, + batch_fetch_failures_are_fatal: bool, + active_only: bool, +) !ForegroundUsageRefreshState { + return refreshForegroundUsageForDisplayWithApiFetchersWithPoolInitUsingApiEnabledAndPersistAndActiveOnly( allocator, codex_home, reg, @@ -189,6 +249,7 @@ pub fn refreshForegroundUsageForDisplayWithApiFetchersWithPoolInitUsingApiEnable usage_api_enabled, batch_fetch_failures_are_fatal, true, + active_only, ); } @@ -202,6 +263,32 @@ pub fn refreshForegroundUsageForDisplayWithApiFetchersWithPoolInitUsingApiEnable usage_api_enabled: bool, batch_fetch_failures_are_fatal: bool, persist_registry: bool, +) !ForegroundUsageRefreshState { + return refreshForegroundUsageForDisplayWithApiFetchersWithPoolInitUsingApiEnabledAndPersistAndActiveOnly( + allocator, + codex_home, + reg, + usage_fetcher, + batch_fetcher, + pool_init, + usage_api_enabled, + batch_fetch_failures_are_fatal, + persist_registry, + false, + ); +} + +pub fn refreshForegroundUsageForDisplayWithApiFetchersWithPoolInitUsingApiEnabledAndPersistAndActiveOnly( + allocator: std.mem.Allocator, + codex_home: []const u8, + reg: *registry.Registry, + usage_fetcher: UsageFetchDetailedFn, + batch_fetcher: ?UsageBatchFetchDetailedFn, + pool_init: ForegroundUsagePoolInitFn, + usage_api_enabled: bool, + batch_fetch_failures_are_fatal: bool, + persist_registry: bool, + active_only: bool, ) !ForegroundUsageRefreshState { var state = try initForegroundUsageRefreshState(allocator, reg.accounts.items.len); errdefer state.deinit(allocator); @@ -215,6 +302,8 @@ pub fn refreshForegroundUsageForDisplayWithApiFetchersWithPoolInitUsingApiEnable } if (reg.accounts.items.len == 0) return state; + const active_account_key = if (active_only) reg.active_account_key else null; + if (active_only and active_account_key == null) return state; const worker_results = try allocator.alloc(ForegroundUsageWorkerResult, reg.accounts.items.len); defer { @@ -232,6 +321,10 @@ pub fn refreshForegroundUsageForDisplayWithApiFetchersWithPoolInitUsingApiEnable defer fetch_account_indices.deinit(auth_path_arena); for (reg.accounts.items, 0..) |account, idx| { + if (active_only) { + const key = active_account_key.?; + if (!std.mem.eql(u8, account.account_key, key)) continue; + } if (skipsChatGptUsage(&account)) continue; try fetch_account_indices.append(auth_path_arena, idx); } @@ -276,11 +369,12 @@ pub fn refreshForegroundUsageForDisplayWithApiFetchersWithPoolInitUsingApiEnable batch_result.snapshot = null; } } else { - var use_concurrent_usage_refresh = reg.accounts.items.len > 1; + const refresh_job_count: usize = if (active_only) 1 else reg.accounts.items.len; + var use_concurrent_usage_refresh = refresh_job_count > 1; if (use_concurrent_usage_refresh) { pool_init( allocator, - @min(reg.accounts.items.len, foreground_usage_refresh_concurrency), + @min(refresh_job_count, foreground_usage_refresh_concurrency), ) catch |err| switch (err) { error.OutOfMemory => return err, else => use_concurrent_usage_refresh = false, @@ -294,14 +388,20 @@ pub fn refreshForegroundUsageForDisplayWithApiFetchersWithPoolInitUsingApiEnable reg, usage_fetcher, worker_results, + active_account_key, + active_only, ); } else { - runForegroundUsageRefreshWorkersSerially(allocator, codex_home, reg, usage_fetcher, worker_results); + runForegroundUsageRefreshWorkersSerially(allocator, codex_home, reg, usage_fetcher, worker_results, active_account_key, active_only); } } var registry_changed = false; for (worker_results, 0..) |*worker_result, idx| { + if (active_only) { + const key = active_account_key.?; + if (!std.mem.eql(u8, reg.accounts.items[idx].account_key, key)) continue; + } const outcome = &state.outcomes[idx]; outcome.* = .{ .attempted = true, @@ -354,12 +454,18 @@ const ForegroundUsageWorkerQueue = struct { reg: *registry.Registry, usage_fetcher: UsageFetchDetailedFn, results: []ForegroundUsageWorkerResult, + active_account_key: ?[]const u8, + active_only: bool, next_index: std.atomic.Value(usize) = .init(0), fn run(self: *ForegroundUsageWorkerQueue) void { while (true) { const idx = self.next_index.fetchAdd(1, .monotonic); if (idx >= self.reg.accounts.items.len) return; + if (self.active_only) { + const key = self.active_account_key.?; + if (!std.mem.eql(u8, self.reg.accounts.items[idx].account_key, key)) continue; + } foregroundUsageRefreshWorker( self.allocator, @@ -379,10 +485,12 @@ fn runForegroundUsageRefreshWorkersConcurrently( reg: *registry.Registry, usage_fetcher: UsageFetchDetailedFn, results: []ForegroundUsageWorkerResult, + active_account_key: ?[]const u8, + active_only: bool, ) !void { - const worker_count = @min(reg.accounts.items.len, foreground_usage_refresh_concurrency); + const worker_count = @min(if (active_only) 1 else reg.accounts.items.len, foreground_usage_refresh_concurrency); if (worker_count <= 1) { - runForegroundUsageRefreshWorkersSerially(allocator, codex_home, reg, usage_fetcher, results); + runForegroundUsageRefreshWorkersSerially(allocator, codex_home, reg, usage_fetcher, results, active_account_key, active_only); return; } @@ -392,6 +500,8 @@ fn runForegroundUsageRefreshWorkersConcurrently( .reg = reg, .usage_fetcher = usage_fetcher, .results = results, + .active_account_key = active_account_key, + .active_only = active_only, }; const helper_count = worker_count - 1; @@ -420,8 +530,14 @@ fn runForegroundUsageRefreshWorkersSerially( reg: *registry.Registry, usage_fetcher: UsageFetchDetailedFn, results: []ForegroundUsageWorkerResult, + active_account_key: ?[]const u8, + active_only: bool, ) void { - for (reg.accounts.items, 0..) |_, idx| { + for (reg.accounts.items, 0..) |account, idx| { + if (active_only) { + const key = active_account_key.?; + if (!std.mem.eql(u8, account.account_key, key)) continue; + } foregroundUsageRefreshWorker(allocator, codex_home, reg, idx, usage_fetcher, results); } } diff --git a/tests/cli_behavior_test.zig b/tests/cli_behavior_test.zig index 792737ae..13489210 100644 --- a/tests/cli_behavior_test.zig +++ b/tests/cli_behavior_test.zig @@ -232,6 +232,21 @@ test "Scenario: Given list with skip-api flag when parsing then local-only displ } } +test "Scenario: Given list with active flag when parsing then active-only refresh mode is preserved" { + const gpa = std.testing.allocator; + const args = [_][:0]const u8{ "codex-auth", "list", "--active" }; + var result = try cli.commands.parseArgs(gpa, &args); + defer cli.commands.freeParseResult(gpa, &result); + + switch (result) { + .command => |cmd| switch (cmd) { + .list => |opts| try std.testing.expect(opts.active_only), + else => return error.TestExpectedEqual, + }, + else => return error.TestExpectedEqual, + } +} + test "Scenario: Given list with live flag when parsing then live mode is preserved" { const gpa = std.testing.allocator; const args = [_][:0]const u8{ "codex-auth", "list", "--live" }; @@ -331,7 +346,7 @@ test "Scenario: Given help when rendering then login and command help notes are try std.testing.expect(std.mem.indexOf(u8, help, "Auto Switch: ON (5h<12%, weekly<8%)\n\nCommands:\n --help, -h") != null); try std.testing.expect(std.mem.indexOf(u8, help, "help ") != null); try std.testing.expect(std.mem.indexOf(u8, help, "--version, -V") != null); - try std.testing.expect(std.mem.indexOf(u8, help, "list [--live] [--api|--skip-api]") != null); + try std.testing.expect(std.mem.indexOf(u8, help, "list [--live] [--active] [--api|--skip-api]") != null); try std.testing.expect(std.mem.indexOf(u8, help, "login [--device-auth]") != null); try std.testing.expect(std.mem.indexOf(u8, help, "import [--alias ]") != null); try std.testing.expect(std.mem.indexOf(u8, help, "import --cpa [] [--alias ]") != null); @@ -370,8 +385,9 @@ test "Scenario: Given simple command help when rendering then examples are omitt const help = aw.written(); try std.testing.expect(std.mem.indexOf(u8, help, "codex-auth list") != null); try std.testing.expect(std.mem.indexOf(u8, help, "List available accounts.") != null); - try std.testing.expect(std.mem.indexOf(u8, help, "Usage:\n codex-auth list [--live] [--api|--skip-api]\n") != null); + try std.testing.expect(std.mem.indexOf(u8, help, "Usage:\n codex-auth list [--live] [--active] [--api|--skip-api]\n") != null); try std.testing.expect(std.mem.indexOf(u8, help, "Options:\n --live") != null); + try std.testing.expect(std.mem.indexOf(u8, help, "--active Refresh only the active account before rendering.") != null); try std.testing.expect(std.mem.indexOf(u8, help, "--skip-api Load usage and account data from local data only (may be inaccurate).") != null); try std.testing.expect(std.mem.indexOf(u8, help, "Examples:") == null); } diff --git a/tests/workflows_core_test.zig b/tests/workflows_core_test.zig index 33a361c8..ce22b350 100644 --- a/tests/workflows_core_test.zig +++ b/tests/workflows_core_test.zig @@ -516,6 +516,82 @@ test "Scenario: Given api usage refresh for list and switch when refreshing fore try std.testing.expectEqual(@as(f64, 55), reg.accounts.items[2].last_usage.?.secondary.?.used_percent); } +test "Scenario: Given active-only foreground usage refresh then only the active account is requested" { + const gpa = std.testing.allocator; + var tmp = fs.tmpDir(.{}); + defer tmp.cleanup(); + + const codex_home = try tmp.dir.realpathAlloc(gpa, "."); + defer gpa.free(codex_home); + try tmp.dir.makePath("accounts"); + + const TestUsageFetcher = struct { + var requested: usize = 0; + + fn snapshot(plan: registry.PlanType, used_5h: f64, used_weekly: f64) registry.RateLimitSnapshot { + return .{ + .primary = .{ + .used_percent = used_5h, + .window_minutes = 300, + .resets_at = 1773491460, + }, + .secondary = .{ + .used_percent = used_weekly, + .window_minutes = 10080, + .resets_at = 1773749620, + }, + .credits = null, + .plan_type = plan, + }; + } + + fn fetch(allocator: std.mem.Allocator, auth_paths: []const []const u8, max_concurrency: usize) ![]usage_api.BatchUsageFetchResult { + _ = max_concurrency; + requested += auth_paths.len; + const results = try allocator.alloc(usage_api.BatchUsageFetchResult, auth_paths.len); + for (results) |*result| { + result.* = .{ + .snapshot = snapshot(.team, 18, 40), + .status_code = 200, + }; + } + return results; + } + }; + + TestUsageFetcher.requested = 0; + + var reg = makeRegistry(); + defer reg.deinit(gpa); + try appendAccount(gpa, ®, primary_record_key, "user@example.com", "", .team); + try appendAccount(gpa, ®, secondary_record_key, "user@example.com", "", .team); + try registry.setActiveAccountKey(gpa, ®, secondary_record_key); + + try writeAccountSnapshotWithIds(gpa, codex_home, "user@example.com", "team", shared_user_id, primary_account_id); + try writeAccountSnapshotWithIds(gpa, codex_home, "user@example.com", "team", shared_user_id, secondary_account_id); + + var state = try main_mod.refreshForegroundUsageForDisplayWithApiFetchersWithPoolInitUsingApiEnabledAndPersistAndActiveOnly( + gpa, + codex_home, + ®, + usage_api.fetchUsageForAuthPathDetailed, + TestUsageFetcher.fetch, + main_mod.initForegroundUsagePool, + true, + false, + false, + true, + ); + defer state.deinit(gpa); + + try std.testing.expectEqual(@as(usize, 1), TestUsageFetcher.requested); + try std.testing.expectEqual(@as(usize, 1), state.attempted); + try std.testing.expect(!state.outcomes[0].attempted); + try std.testing.expect(state.outcomes[1].attempted); + try std.testing.expect(reg.accounts.items[0].last_usage == null); + try std.testing.expect(reg.accounts.items[1].last_usage != null); +} + test "Scenario: Given API key auth when refreshing foreground usage then overrides stay empty and rows remain placeholder-driven" { const gpa = std.testing.allocator; var tmp = fs.tmpDir(.{});