Skip to content

fix(ci): bound telemetry_kill_switch_dispatch so it stops flaking on Ubuntu - #6270

Merged
Hmbown merged 1 commit into
mainfrom
fix/nextest-bound-telemetry-kill-switch-20260916
Sep 16, 2026
Merged

Hmbown merged 1 commit into
mainfrom
fix/nextest-bound-telemetry-kill-switch-20260916

Conversation

@Hmbown

@Hmbown Hmbown commented Sep 16, 2026

Copy link
Copy Markdown
Owner

Partial fix for #6269 — the test-side half.

codewhale-cli::telemetry_kill_switch_dispatch::missing_preference_defaults_on_without_inventing_acceptance has failed twice on Ubuntu CI at telemetry_kill_switch_dispatch.rs:180 on changes touching neither telemetry nor the CLI: PR #6104 (c810bc0458, 2026-09-12) and PR #6267 (75e032b61d, 2026-09-16). Both times macOS and Windows passed.

Not an env race

CI runs cargo-nextest — one process per test — and the test mutates no process-global state: each case spawns the real binary with env_clear() and a fresh TempDir HOME/CODEWHALE_HOME (telemetry_kill_switch_dispatch.rs:231-276). The panic is a missing file, not a wrong value.

The actual mechanism

features list resolves to Surface::Cli, whose exit path waits CLI_PERSIST_TIMEOUT (250 ms, crates/telemetry/src/lib.rs:75) for a detached writer thread that must re-run decision::re_decide against disk and fsync an append before the process exits. It fails open, so a missed deadline silently leaves no receipt.

Why no existing group caught it

All three [[profile.default.overrides]] filter binary(integration). This is a different binary, so none of them ever matched — the five cases ran at full parallelism beside 15,774 tests. The CI log confirms saturation: neighbouring subprocess-spawning tests took 3.5–3.6 s for sub-second work.

This adds the missing override, placing the binary in the existing telemetry-contract group (max-threads = 1), for the same reason that group exists.

Verification (macOS aarch64)

Group assignment — before this change the filter matched nothing:

$ cargo nextest show-config test-groups -p codewhale-cli --all-features --locked \
    -E 'binary(telemetry_kill_switch_dispatch)'
group: exec-persistent-service (max threads = 1)
    (no matches)
group: spawns-binaries (max threads = 3)
    (no matches)
group: telemetry-contract (max threads = 1)
  * override for default profile with filter 'binary(telemetry_kill_switch_dispatch)':
      codewhale-cli::telemetry_kill_switch_dispatch:
          an_unparseable_telemetry_env_value_keeps_the_in_process_runtime_off
          config_set_true_reenables_a_historical_decline_through_settings
          env_off_beats_cli_on_end_to_end
          missing_preference_defaults_on_without_inventing_acceptance
          short_cli_exit_persists_without_network_delivery

Focused rerun — the failing test passes in isolation, which is what distinguishes flake from regression:

$ sh scripts/with-hermetic-test-home.sh cargo nextest run -p codewhale-cli \
    --all-features --locked -E 'binary(telemetry_kill_switch_dispatch)'
    Starting 5 tests across 1 binary (10 binaries skipped)
        PASS [   0.148s] (1/5) ... an_unparseable_telemetry_env_value_keeps_the_in_process_runtime_off
        PASS [   0.132s] (2/5) ... config_set_true_reenables_a_historical_decline_through_settings
        PASS [   0.100s] (3/5) ... env_off_beats_cli_on_end_to_end
        PASS [   0.182s] (4/5) ... missing_preference_defaults_on_without_inventing_acceptance
        PASS [   0.059s] (5/5) ... short_cli_exit_persists_without_network_delivery
     Summary [   0.621s] 5 tests run: 5 passed, 0 skipped

0.182 s here against 0.747 s to fail under CI load.

Not fixed here

The 250 ms budget itself. Bounding the group removes the load that makes the deadline reachable; a slow enough host can still miss it. The product-side options (hoist the re_decide disk read out of the window, join the writer for the local non-network case, or raise the constant) are behavioural decisions and stay in #6269.

No-Issue: partial fix only — #6269 stays open for the 250 ms CLI_PERSIST_TIMEOUT decision, which is behavioural and not a test-config change.

…Ubuntu

`codewhale-cli::telemetry_kill_switch_dispatch::missing_preference_defaults_on_without_inventing_acceptance`
has failed twice on Ubuntu CI at `telemetry_kill_switch_dispatch.rs:180`
("default-on writes dry run") on changes that touch neither telemetry nor
the CLI: PR #6104 (`c810bc0458`, 2026-09-12) and PR #6267 (`75e032b61d`,
2026-09-16). Both times macOS and Windows passed.

It is not an env race. CI runs cargo-nextest, which gives every test its own
process, and the test never mutates process-global state — each case spawns
the real binary with `env_clear()` and a fresh `TempDir` HOME/CODEWHALE_HOME
(`telemetry_kill_switch_dispatch.rs:231-276`). The panic is a *missing file*:
`$CODEWHALE_HOME/telemetry/dryrun.jsonl` was never written.

The mechanism is a wall-clock deadline. `features list` resolves to
`Surface::Cli`, whose exit path waits `CLI_PERSIST_TIMEOUT` — 250 ms
(`crates/telemetry/src/lib.rs:75`) — for a detached writer thread that must
re-run `decision::re_decide` against disk and then fsync an append before the
process exits. The code deliberately fails open, so a missed deadline silently
produces no receipt.

All three existing test-group overrides filter `binary(integration)`, and this
is a *different* binary, so none of them ever matched it: the five cases ran at
full parallelism beside 15,774 tests. The CI log shows the runner was saturated
at that moment — neighbouring subprocess-spawning tests took 3.5-3.6 s for work
that normally finishes well under a second, while this one failed in 0.747 s.

This adds the missing override, putting the binary in the existing
`telemetry-contract` group (max-threads = 1) for the same reason that group
exists: these tests spawn the real binary and cannot absorb scheduler latency.

Verified on this machine (macOS aarch64):

  cargo nextest show-config test-groups -p codewhale-cli --all-features \
    --locked -E 'binary(telemetry_kill_switch_dispatch)'
    group: telemetry-contract (max threads = 1)
      * override for default profile with filter
        'binary(telemetry_kill_switch_dispatch)':
          codewhale-cli::telemetry_kill_switch_dispatch: (all 5 tests)
    group: spawns-binaries (max threads = 3)   (no matches)

  sh scripts/with-hermetic-test-home.sh cargo nextest run -p codewhale-cli \
    --all-features --locked -E 'binary(telemetry_kill_switch_dispatch)'
    Summary [0.621s] 5 tests run: 5 passed, 0 skipped

Not fixed here: the 250 ms budget itself. Bounding the group removes the load
that makes the deadline reachable, but a slow enough host can still miss it.
The product-side options — raise CLI_PERSIST_TIMEOUT, hoist the `re_decide`
disk read out of the deadline window, or have the CLI path join the writer for
the local non-network case — are a behavioural decision, not a test fix, and
are left for the issue.

Signed-off-by: CodeWhale Bot <bot@codewhale.net>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Copilot AI lite review requested due to automatic review settings September 16, 2026 05:54
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@claude

claude Bot commented Sep 16, 2026

Copy link
Copy Markdown
Contributor

Claude finished @Hmbown's task in 1m 6s —— View job


Review: bound telemetry_kill_switch_dispatch in nextest config

  • Gather context (PR body, diff, CLAUDE.md/AGENTS.md)
  • Read .config/nextest.toml in full and check group semantics
  • Check telemetry_kill_switch_dispatch.rs test file referenced in the PR
  • Check for other binaries that might have the same "different binary name" gap
  • Post review findings

Verdict: looks correct, minimal, and well-evidenced. No blocking issues.

What the diff does

Adds one [[profile.default.overrides]] block routing binary(telemetry_kill_switch_dispatch) into the existing telemetry-contract group (max-threads = 1, .config/nextest.toml:32). Confirmed:

  • crates/cli/tests/telemetry_kill_switch_dispatch.rs exists and is a distinct nextest binary from binary(integration), so none of the three prior overrides (lines 42, 60, 65) could ever have matched it — the PR's root-cause claim checks out.
  • The group it's routed into already exists and already exists for this exact hazard class (real-binary-spawning telemetry tests), so this is reuse, not a new abstraction — correctly follows rung 2 of the ponytail ladder (grep for the existing thing, don't invent a parallel one).
  • Placement relative to the other override blocks doesn't matter here since all filters key on mutually-exclusive exact binary names ("first match wins" is moot when only one block can ever match a given binary).
  • The change is config-only, carries no product-code risk, and is accompanied by an actual before/after nextest show-config + focused-rerun verification in the PR description — good evidence per the repo's "claiming a test passed" standard.

Minor observation (non-blocking, out of scope for this PR)

crates/cli/tests/ has several other dispatch-test binaries that also spawn the real CLI binary (credential_handoff_dispatch, diagnostic_dispatch_read_only, mcp_server_proxy, model_resolve_provenance, notification_config, root_resume_dispatch, runtime_set_dispatch) and are likewise outside binary(integration), so none of the four groups touch them either. The PR is correctly scoped to the one binary with a proven timing failure (the CLI_PERSIST_TIMEOUT detached-writer race) rather than speculatively grouping all of them — that's the right call per the "not fixed here" section of the PR body. Worth a follow-up only if any of those others is ever observed to flake the same way; no action needed now.

The 250 ms CLI_PERSIST_TIMEOUT budget itself is explicitly out of scope here and tracked in #6269, which matches this PR's stated scope (test-side grouping only, not the product-side race).

@codewhale-agent codewhale-agent Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Codewhale review

Complete review coverage: 1/1 passes, 1 file patches, sha256:adb97c79f53eaf8b043e8c360ed3cab72500da773ddaad23d11790c270d51d5a.

Pass 1: Single-file CI config change: a new [[profile.default.overrides]] entry in .config/nextest.toml binds the telemetry_kill_switch_dispatch test binary to the existing telemetry-contract test group (max-threads = 1), with an explanatory comment. The addition is syntactically well-formed TOML, references a group that exists in the same file, and is order-independent with respect to the other overrides because its filter is disjoint from theirs.

Assessment

Pass 1: No defects found in the changed lines. Static inspection of the diff plus the supplied file context shows: (1) the new table is valid TOML ([[profile.default.overrides]] with filter/test-group string keys), inserted between the existing binary(integration) & test(/^telemetry_contract::/) override and the binary(integration) & test(/^exec_persistent_service::/) override, before the catch-all binary(integration) -> spawns-binaries override; (2) the referenced group telemetry-contract = { max-threads = 1 } is declared at line 32, so the assignment cannot fail config validation; (3) the new filter binary(telemetry_kill_switch_dispatch) is disjoint from every other override filter in the file (all others require binary(integration)), so the file's 'first matching override wins' ordering cannot be disturbed and no existing group assignment is stolen — the change is behaviour-additive only. I did not execute anything (no build, no cargo nextest show-config, no test run); the claim that the filter actually resolves to codewhale-cli::telemetry_kill_switch_dispatch comes only from the untrusted PR transcript, and it relies on the binary-name form already used by the neighbouring overrides. Two open questions that are not defects in this diff and that I am not asserting: (a) the filter is not package-qualified, so if any other crate in the workspace also produced a test target named telemetry_kill_switch_dispatch, those tests would silently join the serialized group as well (low impact, more serialization only); (b) the group bound limits concurrency among the five cases and against the telemetry-contract tests, but tests outside all groups still run at full parallelism, so static review cannot confirm that this alone makes the 250 ms CLI_PERSIST_TIMEOUT deadline unreachable on a saturated runner — the PR acknowledges this residual risk and leaves #6269 open. Both points are outside the evidence available in this diff.


Advisory review by Codewhale (codewhale review --pr 6270 --post, head 03cd07ab6ed4a62e6d127fa505db681915473880). Line-specific findings are also posted as inline review comments; mechanical fixes arrive as committable suggestions you can apply from the Files tab. CODEOWNERS approval still governs merge.

@Hmbown
Hmbown merged commit 21a02f1 into main Sep 16, 2026
32 of 33 checks passed
@Hmbown
Hmbown deleted the fix/nextest-bound-telemetry-kill-switch-20260916 branch September 16, 2026 06:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants