Skip to content

feat(debug-files): --strip-sources-content (re-targeted to main) - #50

Merged
krassx merged 9 commits into
mainfrom
fix/strip-sources-to-main
Sep 18, 2026
Merged

krassx merged 9 commits into
mainfrom
fix/strip-sources-to-main

Conversation

@krassx

@krassx krassx commented Sep 18, 2026

Copy link
Copy Markdown
Contributor

Re-open of #48, which never reached main — the same stacking trap as #46/#49. #48 targeted feat/upload-dry-run; #47 (that branch → main) merged first, so #48 merged into a branch that had already been consumed. main today has no --strip-sources-content. Same two commits, cherry-picked onto current main.

What it does

A source map's sourcesContent embeds the original source verbatim. It is what lets a symbolicated crash show source lines — and it also means the customer's code leaves the build machine on every upload. --strip-sources-content (--type sourcemaps) removes it from the copy that is uploaded.

Measured on a real esbuild bundle: 253 → 181 bytes uploaded, local map untouched.

  • The map on disk is never modified. It is the caller's build output and their own debugging.
  • The declared hash describes the stripped bytes, not the file that was read.
  • The debug-id is carried over untouched — it is the key, and re-deriving it would break the pair with the bundle sourcemaps inject stamped.
  • Indexed maps too (second commit, a SEV1 from the release review): a spec §Index-Map keeps its source inside sections[].map, so removing only the top-level key uploaded the source verbatim and reported success. Now stripped recursively.
  • A map with nothing to strip is uploaded byte-for-byte unchanged. This is a privacy preference, not a validator.
  • Rejected (exit 20) for every other --type — no other symbol format embeds source.
  • The JSON round-trip runs on spawn_blocking, like the zstd: on a multi-megabyte map it costs more than the compression does, and every upload future is polled by one task.

Tests

Five unit tests plus the flag-rejection matrix and two e2e flows that unzip the captured PUT. Eight mutants caught across both commits (flag ignored, always-on, nothing-to-strip re-serialized, wrong hash, accepted for other types, sections branch skipped, recursion dropped, top-level removal dropped).

Verified on this branch against current main: cargo fmt --check, clippy --all-targets -D warnings (0), all unit tests + suites, e2e_flows.py ALL PASS.

🤖 Generated with Claude Code

… their source uploaded

Audit §8: a source map's `sourcesContent` embeds the original source verbatim, and it rides into the
upload with the map. That is how a symbolicated crash shows source lines — and also means the
customer's code leaves the build machine. There was no way to say no.

`--strip-sources-content` (`--type sourcemaps`) removes it from the COPY that is uploaded. File,
line and column symbolication is unaffected; what is lost is the snippet shown beside a frame.

- The map on disk is never modified. It is the caller's build output and their own debugging, and
  the bundler plugins delete these maps after a successful upload anyway.
- The declared `hash` describes the stripped bytes, not the file that was read — the wire field is
  supposed to describe what was uploaded.
- The debug-id is carried over untouched: it is the key, and re-deriving it would break the pair
  with the bundle `sourcemaps inject` stamped.
- A map with no `sourcesContent`, or one that is not the JSON object we expect, is uploaded
  byte-for-byte unchanged. This is a privacy preference, not a validator.
- Rejected with exit 20 for every other `--type`, like the other sourcemap-only flags: no other
  symbol format embeds source.

Measured on a real esbuild bundle: 253 → 181 bytes uploaded, local map untouched. Five mutants
caught (flag ignored, always-on, nothing-to-strip still re-serialized, wrong hash, flag accepted for
other types) — the third needed the test strengthened to assert BYTE equality, not just the absence
of the key. Two new e2e flows unzip the captured PUT and check both the upload and the file on disk.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
…rce entirely

A review of the whole 0.7.11 delta found the flag's promise failing silently on the one map shape it
could not see. An INDEXED map (spec §Index-Map, `{"version":3,"sections":[{"offset":…,"map":{…}}]}`)
keeps its source inside each section, not at the top level — so `map.remove("sourcesContent")` found
nothing, the map uploaded verbatim WITH the source, and the run reported success without even a log
line. A team turning this on precisely so their code never leaves the build machine got the opposite,
quietly.

`sourcesContent` is now removed wherever a map can carry it: the top level and every
`sections[].map`, recursively (sections nest). Verified against a capturing mock — the PUT body no
longer contains the source, while sections, offsets, mappings and the debug-id survive.

Also from the same review: the strip's JSON round-trip moved to `spawn_blocking`. Parsing and
re-serializing a multi-megabyte map costs more than the zstd that already moved there, and every
upload future is polled by one task.

Docs: README's synopsis was missing `--strip-sources-content`, its "both flags" line had grown to
three, and neither document mentioned indexed maps. Three mutants caught (sections branch skipped,
recursion dropped, top-level removal dropped).

🤖 Generated with [Claude Code](https://claude.com/claude-code)
Comment thread src/cli/debug_files.rs Outdated
if !strip {
return Ok(None);
}
let bytes = std::fs::read(map_path)?;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

std::fs::read(map_path)? (and std::fs::write(&out, &stripped)? at line 1832) convert io::Error straight into anyhow::Error, bypassing crate::error::Error::Io. classify() (src/error.rs) only maps exit codes by downcast_ref::<Error>(), so an I/O failure here — the map file vanishing or losing read permission between sourcemap::identify() (line ~1609, upstream) and this second read, or a transient write failure into the fresh tmpdir — lands on ExitCode::Unexpected (1, "caller should fall back"), whereas the same file failing the first read in sourcemap::identify() is typed and correctly exits 10 (InputNotFound, no fallback). That's an integrator-visible inconsistency for the identical failure mode, gated only by whether --strip-sources-content is passed.

Suggest changing this function's return type to crate::error::Result<...> (or map the io errors explicitly with input_not_found/input_invalid) so failures here classify the same way as the rest of the sourcemap-identify path.

@claude

claude Bot commented Sep 18, 2026

Copy link
Copy Markdown

Code review

Adds debug-files upload --strip-sources-content (--type sourcemaps only): uploads a stripped copy of the map with sourcesContent removed — including recursively inside indexed maps' sections[].map — while leaving the on-disk file and its debug-id untouched, and recomputing the declared hash to describe the stripped bytes. The flag rejection, --help text, CHANGELOG/README, and test coverage (including the indexed-map and no-op cases) are all consistent with the stated design, and the change is properly opt-in (default upload path is untouched, confirmed by sources_content_is_kept_by_default).

Findings: 1 inline (0 blocking).

  • stripped_copy_without_sources (src/cli/debug_files.rs:1819, 1832) does its file I/O through bare ? into anyhow::Result, so an I/O failure there classifies as exit 1 (Unexpected, "fall back") instead of exit 10 (InputNotFound, "no fallback") — inconsistent with sourcemap::identify()'s typed handling of the very same file earlier in the same path. Narrow trigger (TOCTOU on the map file, or a tmpdir write failure) but worth a one-line fix given exit codes are a stable integrator contract.

Non-blocking — the flag's mainline behavior, wire shape, and hash semantics are correct. Fine to merge as is, or fix the error-typing nit first if you want the exit-code contract airtight for this new path.

… reading a zstd zip

Two failures on #50.

**The review's finding.** `stripped_copy_without_sources` read the map and wrote the copy through a
bare `?` into `anyhow`, so an I/O failure there classified as exit 1 ("unexpected — you may fall
back") while `sourcemap::identify` classifies one on the SAME file, moments earlier, as exit 10
("input not found — do not"). Both now go through `Error::Io`, and the JSON re-serialization failure
through `input_invalid`.

The test drives the function directly, because `run_sourcemap_upload` reads the map through
`identify` first — which is exactly why this could sit here unnoticed: an end-to-end test cannot
reach the strip's own read. It covers the map vanishing (the TOCTOU window), an unreadable map, and
an unwritable destination; both untyped-error mutants are caught.

**The CI failure, which was mine.** The e2e strip check reads the uploaded zip with Python's
`zipfile`, which cannot decompress zstd (method 93) before Python 3.14 — the macOS runner has 3.14,
ubuntu and windows do not, so the flow passed locally and failed on three runners. That flow now
uploads with `--no-zstd`; it is about `sourcesContent`, not compression, and `sourcemaps_upload`
already covers the zstd path. The "could not verify" branch is also no longer a `[warn]`: a check
that cannot run is a check that failed, and it reported as one.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
Comment thread src/cli/debug_files.rs
if std::env::var_os("USER").is_some_and(|u| u == "root") {
return;
}
use std::os::unix::fs::PermissionsExt;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cross-platform break: use std::os::unix::fs::PermissionsExt; has no #[cfg(unix)] guard, so this whole test module fails to compile on Windows (cargo test builds this crate there too, per CLAUDE.md). The existing test at src/inject/mod.rs:1166 does the exact same chmod-based check and correctly gates it with #[cfg(unix)] above #[test] — this test should follow the same pattern.

@claude

claude Bot commented Sep 18, 2026

Copy link
Copy Markdown

Code review

Adds --strip-sources-content to debug-files upload --type sourcemaps: it packs a stripped copy of the map (recursing into indexed maps' sections[].map, per the SEV1 the PR description calls out) into a temp dir on spawn_blocking, recomputes the SHA-1 hash over the stripped bytes, and leaves the on-disk file untouched. The design holds up — resolved_id is fixed from the original map's identity before packing, the byte-identical fast path avoids re-serializing when there's nothing to strip, the new flag is correctly rejected (exit 20) for non-sourcemap types following the existing --allow-empty/--concurrency pattern, I/O failures are typed to Error::Io (exit 10) rather than a bare anyhow!, and --help/README/CHANGELOG are all updated in step with the new flag.

Findings: 1 inline (1 blocking).

  • The new unit test a_strip_that_cannot_read_or_write_is_input_not_found uses std::os::unix::fs::PermissionsExt with no #[cfg(unix)] guard, which won't compile on Windows — this crate ships and is tested on Windows per CLAUDE.md. The same chmod-based technique elsewhere in the codebase (src/inject/mod.rs:1166) is correctly gated; this test should match that pattern.

Recommend fixing the Windows build break before merge; everything else is sound.

… for Windows in CI

The review caught a test that would not compile on Windows: `PermissionsExt` is Unix-only and my
new `a_strip_that_cannot_read_or_write_is_input_not_found` used it with no `#[cfg(unix)]`, unlike
`an_unreadable_map_fails_before_the_bundle_is_modified` right next door. Gated to match.

No job could have caught it, which is the more interesting half. The `test` matrix is ubuntu +
macOS, and the Windows job runs `cargo build --release` — the binary only, so `#[cfg(test)]` code is
never compiled for that target. All 13 checks passed on a tree that broke `cargo test` for every
Windows developer.

That job now builds `--all-targets`, so test code compiles there too. Compiling is not running: the
matrix above still owns behaviour, and this stays a build job. Running the suite on Windows would be
the fuller fix — 535 tests that have never executed there — but that is its own change, with its own
fallout, and not something to fold into a release.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
The suite has never executed on Windows: the matrix was ubuntu + macOS and the Windows leg built
the binary only. This ships there, so it should be tested there.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
@claude

claude Bot commented Sep 18, 2026

Copy link
Copy Markdown

Code review

Adds debug-files upload --strip-sources-content (sourcemaps-only): uploads a stripped copy of each .map with sourcesContent removed (recursively, including indexed-map sections[].map), leaves the on-disk file untouched, and recomputes the wire hash over the stripped bytes. Also tightens CI to cargo build --all-targets on the Windows cross target, which is what catches the kind of Unix-only-API-in-tests bug this PR's own new test (a_strip_that_cannot_read_or_write_is_input_not_found, correctly #[cfg(unix)]-gated) could otherwise introduce.

I read the full diff against CLAUDE.md's contracts (--help sync, stdout purity, exit codes, wire shapes, Cargo.toml table ordering, MSRV, daemonizing, cross-platform, network-I/O routing, generated release.yml) and traced the new code path end to end:

  • --help/CHANGELOG/README are all updated in the same change and agree with each other and the code.
  • The new I/O goes through tokio::task::spawn_blocking, same as the existing packing step — no blocking on the runtime thread.
  • Failures are typed (crate::error::Error::Io, input_invalid) rather than bare anyhow::anyhow!, so they classify to the same exit code (10) that sourcemap::identify already uses for the same file, and this is asserted by a dedicated test.
  • The recomputed hash genuinely describes the uploaded (stripped) bytes, not the on-disk file, and this is asserted by a dedicated test (stripping_updates_the_declared_hash).
  • The keying debug_id used for the registration POST (resolved_id) is resolved from the original file in pass 1, before stripping, so it's unaffected by anything the strip step does.
  • --type gating for the new flag reuses the existing config_invalid/exit-20 pattern and is covered by both a unit test and an integration test (tests/debug_files_flags.rs) with matching message text.
  • No stdout writes, no Cargo.toml table reordering, no Unix-only API left unguarded, no exit-code repurposing.

Findings: None.

This is a well-scoped, carefully tested change (including an indexed-source-map edge case that would otherwise have silently defeated the flag's whole purpose). Merge as is.

@claude

claude Bot commented Sep 18, 2026

Copy link
Copy Markdown

Code review

Adds debug-files upload --strip-sources-content (sourcemaps only): uploads a stripped copy of each .map (including nested sections[].map for index maps) with sourcesContent removed, recomputes the wire hash over the stripped bytes, and re-reads the debug-id from the copy rather than trusting the original identity. The file on disk is never touched. Bundled with an unrelated but sound CI fix: windows-latest added to the test matrix and --all-targets added to the Windows cross-compile build job, catching a pre-existing Unix-only PermissionsExt test that had no #[cfg(unix)] guard (now fixed here).

I traced the new path against every contract in CLAUDE.md: exit-code mapping (Error::Io → InputNotFound/10, config-invalid → 20 for other --types, matching the new tests), the recursive index-map stripping (verified it reaches nested sections[].map, not just the top level), the hash/identity recomputation matching what's actually zipped and PUT, the spawn_blocking offload for the parse/strip work (no runtime-thread blocking, no lock held across .await), the new #[cfg(unix)] guard on the permission-based test, and --help/README/CHANGELOG staying in sync with the new flag. No stdout leakage, no wire-format regression on the unstripped path (byte-identical when there's nothing to strip), and no MSRV or Cargo.toml table-ordering concerns (no Cargo.toml changes in this diff).

Findings: None.

Sound change, well-tested (including the TOCTOU/permission-failure and index-map edge cases), consistent with the codebase's existing conventions. Merge as is.

…at found

x64 and the native ARM64 runner now run `cargo test --all-targets`, not just a release build. The
release ships both, and they differ in more than instruction set — `ring`'s ARM64 Windows assembly
is why the release leg is pinned to a native runner rather than cross-compiled.

The first run found a real break, and it was not from this PR: `tests/env_robustness.rs` defines a
`cli()` helper used only by its `#[cfg(unix)]` tests, so on Windows it is dead code — and
`RUSTFLAGS: -D warnings` makes that a hard error. `cargo test` had been failing on Windows for
anyone who ran it, while every CI job stayed green, because no job compiled test code for the
platform. Gated the helper to match its users.

The ARM64 job drops the `--all-targets` I added a commit ago: with a real ARM test job it is back to
what it says on the tin, proof that the release profile still links for the target it ships.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
@claude

claude Bot commented Sep 18, 2026

Copy link
Copy Markdown

Code review

Adds debug-files upload --strip-sources-content (--type sourcemaps only): uploads a copy of each map with sourcesContent removed — recursively through indexed-map sections[].map — while leaving the on-disk file untouched and recomputing the declared hash over the stripped bytes. The change is well-scoped and consistent with the existing flag-rejection pattern (--allow-empty, --concurrency).

I traced the full path: flag threading through dispatch → run_sourcemap_upload → SourcemapUploadCtx → upload_one_sourcemap → stripped_copy_without_sources/remove_sources_content; the spawn_blocking usage and tempdir lifetime; the exit-code classification for I/O failures on the stripped-copy path (Error::Io → InputNotFound, matching sourcemap::identify's classification on the same file); the byte-identical no-op case when there's nothing to strip; the recursive indexed-map handling; and the --help/README/CHANGELOG updates for the new flag. All of it holds together, and the doc comment on the new strip_sources_content field satisfies the --help-drift rule.

Findings: None.

Recommend merging as is.

Gating `cli()` to unix left `use assert_cmd::Command;` unused there, which `-D warnings` also makes
a hard error — the next layer of the same break. Both are gated now, with the reason recorded: the
single test in this file needs a non-UTF-8 environment variable, which is not constructible on
Windows.

Swept the rest of the test surface for the same class: every other `os::unix` / `PermissionsExt`
use is already inside a gated item or block (update_e2e.rs is Windows-aware by construction), and
the two remaining greps are `is_symlink()`, which is cross-platform.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
@claude

claude Bot commented Sep 18, 2026

Copy link
Copy Markdown

Code review

This PR adds debug-files upload --strip-sources-content (sourcemaps only): it uploads a stripped copy of each .map — including sources nested inside an indexed map's sections[].map — while leaving the on-disk file untouched, and recomputes the declared hash from the stripped bytes. It also fixes real CI/test gaps found while widening the test matrix (windows-latest and windows-11-arm now run the full suite, not just a build), gating the one Unix-only test file behind #[cfg(unix)] so cargo test compiles on Windows.

I traced the new flag end-to-end against the checklist: the clap doc comment is present and accurate, --strip-sources-content is correctly rejected (exit 20 via config_invalid) for non-sourcemap types, the I/O and re-serialization failure paths in stripped_copy_without_sources map to typed errors (Error::Io → exit 10, matching the sibling sourcemap::identify path; input_invalid → exit 11) rather than a bare anyhow!, the recursive strip correctly reaches into indexed-map sections, sourcesContent-free maps are left byte-identical (no spurious hash change), nothing new touches stdout, and the spawn_blocking/JoinError handling matches the existing pattern used for zip packing right next to it. README/CHANGELOG were updated in the same change and match the implementation. The new CI matrix entries and the #[cfg(unix)] gating are consistent with the stated rationale and don't leave anything uncompiled on the platforms being added.

Findings: 0 inline (0 blocking) — none.

Recommend merging as is.

Running the suite on Windows for the first time found a real defect in the tests, not the product.
519 unit tests pass there; `tests/elf_upload.rs` did not:

    WARN symbol metadata POST: transport error; backing off attempt=1 error=error sending request
    error: upload failed: symbol metadata POST: error sending request     (exit 31, after 3 retries)

The tests clear the child's environment so an ambient `BUGSEE_*` or CI variable cannot decide what
the binary does. On Unix that is all `env_clear()` means. On Windows it also removes `SystemRoot`,
and WinSock cannot initialise without it — so every request from the child fails, with nothing in
the message to say why. `tests/update_e2e.rs` makes the same kind of call and passes precisely
because it does not clear the environment.

All three network-driving test files now share one `common::cli()` that clears the environment and
then puts the OS's own variables back on Windows (`SystemRoot`, `windir`, `SystemDrive`, `TEMP`,
`TMP`, `USERPROFILE`). Shared rather than repeated three times, so the next test written this way
cannot repeat it; `xcode_post_action.rs` keeps its local `cli()` name as a thin wrapper, since its
doc comment explains why that file in particular needs a hermetic environment.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
Comment thread src/cli/debug_files.rs
///
/// A map's `sourcesContent` carries your source verbatim, which is how a symbolicated crash
/// shows source lines. Stripping it keeps file/line/column symbolication and drops the
/// snippet, for teams who would rather their code did not leave the build machine. The map

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

--help drift: this doc comment (the actual --help text for --strip-sources-content) still only describes the flag as if sourcesContent lived at the top level. A later commit in this PR (fix(debug-files): --strip-sources-content missed an indexed map's source entirely) taught the implementation to also strip sections[].map recursively for indexed maps, and updated both README.md and CHANGELOG.md to say so — but this clap doc comment, which is what debug-files upload --help actually prints, was never revisited. Per CLAUDE.md's "--help is part of the public surface" rule, this should gain a line like the README's ("including the copies an indexed map keeps inside sections[].map") in the same change that added indexed-map support.

@claude

claude Bot commented Sep 18, 2026

Copy link
Copy Markdown

Code review

This PR adds debug-files upload --strip-sources-content (sourcemaps-only), which uploads a copy of each .map with sourcesContent removed (including recursively inside sections[].map for indexed maps, added in a follow-up commit after review), recomputes the declared hash/identity from the stripped bytes, and leaves the on-disk file untouched. It also fixes I/O error typing in the new strip path (Error::Io / input_invalid instead of a bare anyhow!, matching sourcemap::identify's classification), and closes a real CI gap where the Windows leg never compiled or ran cargo test, catching (and fixing) a #[cfg(unix)]-only helper/import and an env_clear()-on-Windows WinSock breakage along the way. The change is well-tested (byte-equality assertions, indexed-map coverage, typed-error tests, an e2e flow) and holds together soundly — I traced the hash/identity rebinding through upload_one_sourcemap and the exit-code mapping through error.rs, both check out.

Findings: 1 inline (0 blocking).

  • --help drift on strip_sources_content's doc comment: it wasn't updated for indexed-map support even though README.md and CHANGELOG.md were, in the same follow-up commit.

Nothing blocking. The one finding is a small doc-sync gap, easy to fix in a follow-up line; safe to merge as is or with that one-line addition.

Next thing running the suite on Windows found. `src/cli/update.rs` asks for `bugsee-cli-<triple>.zip`
on Windows and `.tar.xz` elsewhere (`artifact_urls`), and looks for `bugsee-cli.exe` inside — all
correct. The TEST hard-coded the Unix half: it mounted `.tar.xz` and packed a binary named
`bugsee-cli`, so the Windows run asked the mock for a file it had never mounted and read the 404 as
a product failure.

The harness now builds what this host's release would publish: a real ZIP containing
`bugsee-cli-<triple>/bugsee-cli.exe` on Windows (via the `zip` crate already in the tree), the plain
tar elsewhere, with the extension derived by the same split the product makes.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
@claude

claude Bot commented Sep 18, 2026

Copy link
Copy Markdown

Code review

This PR adds debug-files upload --strip-sources-content (--type sourcemaps only): it uploads a copy of each map with sourcesContent removed — recursing into indexed maps' sections[].map — while leaving the on-disk file untouched and recomputing the declared hash to describe the stripped bytes. It also expands the CI test matrix to windows-latest/windows-11-arm and fixes several tests that only ever ran on Unix (a Unix-only env helper now gated behind #[cfg(unix)], a new tests/common::cli() that preserves the Windows vars env_clear() would otherwise strip, and update_e2e.rs building a real .zip on Windows instead of assuming .tar.xz).

I traced the new flow against every category in scope: --help/doc-comment sync for the new flag, the --type-gating error message and its exit code (20, ConfigInvalid — unchanged meaning), the recomputed hash/content_sha1_hex wire value, the typed Error::Io classification for the strip path's read/write failures (matches sourcemap::identify's classification on the same file, exit 10), tmpdir isolation between concurrent uploads, and the Windows-focused CI/test changes for cfg correctness. All of it lines up with what CLAUDE.md requires and what the PR's own new tests (including the indexed-map and no-op cases) assert.

Findings: None.

Sound and thorough — merge as is.

@krassx
krassx merged commit 682e3cd into main Sep 18, 2026
20 checks passed
krassx added a commit that referenced this pull request Sep 18, 2026
… reading a zstd zip

Two failures on #50.

**The review's finding.** `stripped_copy_without_sources` read the map and wrote the copy through a
bare `?` into `anyhow`, so an I/O failure there classified as exit 1 ("unexpected — you may fall
back") while `sourcemap::identify` classifies one on the SAME file, moments earlier, as exit 10
("input not found — do not"). Both now go through `Error::Io`, and the JSON re-serialization failure
through `input_invalid`.

The test drives the function directly, because `run_sourcemap_upload` reads the map through
`identify` first — which is exactly why this could sit here unnoticed: an end-to-end test cannot
reach the strip's own read. It covers the map vanishing (the TOCTOU window), an unreadable map, and
an unwritable destination; both untyped-error mutants are caught.

**The CI failure, which was mine.** The e2e strip check reads the uploaded zip with Python's
`zipfile`, which cannot decompress zstd (method 93) before Python 3.14 — the macOS runner has 3.14,
ubuntu and windows do not, so the flow passed locally and failed on three runners. That flow now
uploads with `--no-zstd`; it is about `sourcesContent`, not compression, and `sourcemaps_upload`
already covers the zstd path. The "could not verify" branch is also no longer a `[warn]`: a check
that cannot run is a check that failed, and it reported as one.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
@krassx
krassx deleted the fix/strip-sources-to-main branch September 18, 2026 18:13
@krassx krassx mentioned this pull request Sep 18, 2026
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.

1 participant