Repository navigation
fix(bazel): preserve macOS fixture and filename behavior - #3140
Conversation
rules_rust matches per_crate_rustc_flag on a prefix of the crate root's exec path, which carries the canonical repository name rules_rust++crate+crates__<name>-<version>; the generated +crate+crates__ filters matched nothing, so the perf opt-levels never applied. Windows cannot execute workspace_status.sh, which left the product SHA genrule without its stable key, and its runners have python but no python3 for the dashboard digest genrule. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01V8Xg2pQQcTiyVB8ndJc5oo
rules_rust stages each external build script's sources under <repo>/_bs.cargo_runfiles/<repo>/, spelling the canonical repository name twice, so the deepest vendored headers (tree-sitter grammars, aws-lc's jitterentropy) reached 260-270 characters and MSVC could not open them. A one-letter crate hub and a D:/b output base on Windows shorten every such path by 15 characters. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01V8Xg2pQQcTiyVB8ndJc5oo
Bazel hands genrule actions a stripped PATH on Windows, so neither python3 nor python resolved for the dashboard digest; pass the runner's PATH and take the first interpreter that starts. The workspace status wrapper now reads git directly, since a bare bash can resolve to WSL. CI also prints failing commands and keeps large build-script output. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01V8Xg2pQQcTiyVB8ndJc5oo
crate_universe folds a host triple's build-dependency features into its
normal build, so tree-sitter's build script widened serde_json with
preserve_order: JSON objects kept insertion order and every digest over
them drifted from Cargo builds. A generated crate annotation rebuilds
serde_json from Cargo's target resolution.
rust_test(crate=) takes cfgs from its own crate_features, so unit tests
compiled without their features; they now carry the variant's set.
Rustc runs unsandboxed so env!("CARGO_MANIFEST_DIR") stays readable at
test time, test binaries get 30 minutes, and the cold-cache Linux and
Clippy jobs get room to finish.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V8Xg2pQQcTiyVB8ndJc5oo
Cargo starts a test binary in its crate directory, and 53 suites read ../../tests/fixtures from there; the launchers now cd into the crate's runfiles directory and every test carries //tests:fixtures. aws-lc's four-level relative jitterentropy include still overran MAX_PATH once MSVC joined it to the staged build-script tree, so a crate patch names the header the builder already puts on the include path. Bazel runs the Windows status command through cmd.exe, which needs backslashes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01V8Xg2pQQcTiyVB8ndJc5oo
Fixture-path fixes: convert runtime std::fs::read_to_string to include_str! so fixture files are available through Bazel compile_data rather than relying on the test working directory. Ordering fixes: sort keys before comparing in domain schema and adjudication assertions — serde_json's preserve_order feature changes Map iteration from BTreeMap to IndexMap insertion-order. Sandbox-probe fix: catch PermissionDenied (errno 1) alongside EILSEQ (errno 92) in the non-UTF-8 filesystem probe — Bazel's darwin-sandbox rejects the probe write with PermissionDenied. Verified: //crates/tracedecay-code-extraction:main (625/625), //crates/tracedecay-domain:domain_suite (169/169), //crates/tracedecay-runtime-core:unit_test pass.
|
cbed880 to
dd7fba5
Compare
There was a problem hiding this comment.
Note
Newer findings are available below. Devin Review posted a newer report on this PR, in addition to the findings presented here.
🔍 Devin Review: 2 flags
Not posted on this PR by your GitHub settings — view them in Devin Review. (Configure)
The golden Pi fixture gained a contentless user message, which capture refuses, so the malformed-file test's fixture has nine lines and session_info spans the last one. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01V8Xg2pQQcTiyVB8ndJc5oo
Each member's tests resolved features on their own, as `cargo test -p` does, so every test build carried its own featured copy of its dependency closure: 534 libraries for 50 crates, more than a CI lane compiles inside its timeout on a cold cache. Tests now share two workspace-wide resolutions, as one `cargo test --workspace` does: the plain one, and one enabling every required feature plus the test-transport surfaces. That leaves 181 libraries. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01V8Xg2pQQcTiyVB8ndJc5oo
… HEAD # Conflicts: # crates/tracedecay-agent-hosts/BUILD.bazel # crates/tracedecay-application/BUILD.bazel # crates/tracedecay-automation-runtime/BUILD.bazel # crates/tracedecay-capture/BUILD.bazel # crates/tracedecay-code-index-retention/BUILD.bazel # crates/tracedecay-code-index-runtime/BUILD.bazel # crates/tracedecay-code-index/BUILD.bazel # crates/tracedecay-configuration/BUILD.bazel # crates/tracedecay-daemon-control/BUILD.bazel # crates/tracedecay-daemon-identity/BUILD.bazel # crates/tracedecay-daemon-protocol/BUILD.bazel # crates/tracedecay-daemon-service/BUILD.bazel # crates/tracedecay-dashboard-api/BUILD.bazel # crates/tracedecay-global-db/BUILD.bazel # crates/tracedecay-graph-db/BUILD.bazel # crates/tracedecay-graph-query/BUILD.bazel # crates/tracedecay-host-admission/BUILD.bazel # crates/tracedecay-lcm/BUILD.bazel # crates/tracedecay-lsp/BUILD.bazel # crates/tracedecay-maintenance/BUILD.bazel # crates/tracedecay-mcp-catalog/BUILD.bazel # crates/tracedecay-mcp/BUILD.bazel # crates/tracedecay-privacy/BUILD.bazel # crates/tracedecay-private-fs/BUILD.bazel # crates/tracedecay-project/BUILD.bazel # crates/tracedecay-query/BUILD.bazel # crates/tracedecay-runtime-core/BUILD.bazel # crates/tracedecay-rusqlite-runtime/BUILD.bazel # crates/tracedecay-session-memory/BUILD.bazel # crates/tracedecay-session-runtime/BUILD.bazel # crates/tracedecay-sessions/BUILD.bazel # crates/tracedecay-source-edit/BUILD.bazel # crates/tracedecay-store-runtime/BUILD.bazel # crates/tracedecay-store/BUILD.bazel # crates/tracedecay-temporal-query/BUILD.bazel # crates/tracedecay/BUILD.bazel # scripts/bazel/gen_builds.py
5118be5 added a contentless user message to the Pi golden fixture, which capture refuses, but the malformed-file test still split the fixture into eight lines and expected session_info at the eighth line's byte range. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01V8Xg2pQQcTiyVB8ndJc5oo
All test contexts now share a single workspace resolution with every required feature and test-transport surface enabled, as the hosted test partitions ran them: one test build per crate, 162 libraries in all. Cargo builds the binaries a test run spawns with that run's features, so each binary gains a `__test` build the suites' runtime overrides and CARGO_BIN_EXE_* name; the plain target stays the shipped one. Binaries' own #[cfg(test)] modules run as `<bin>_unit_test`, which covers the CLI's inline tests, and the host-CLI fixture example reaches its installer through TRACEDECAY_HOST_CLI_FIXTURE. CI exports the toolchain the rustup proxy hands `cargo test` (CARGO, RUSTUP_TOOLCHAIN, and both homes) and passes it to tests. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01V8Xg2pQQcTiyVB8ndJc5oo
The test-run builds of every binary (`<bin>__test`, from the shared test resolution) replace the tracedecay-cli `__test_transport` builds, keeping their testonly flag and shipped file name; the launcher's own CARGO, RUSTC, and RUSTUP_TOOLCHAIN supersede exporting them from CI. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01V8Xg2pQQcTiyVB8ndJc5oo
Binary unit tests stage the same runtime resource directories as library unit tests. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01V8Xg2pQQcTiyVB8ndJc5oo
There was a problem hiding this comment.
Note
Newer findings are available below. Devin Review posted a newer report on this PR, in addition to the findings presented here.
Devin Review found 1 new potential issue.
1 flag not posted on this PR by your GitHub settings — view it in Devin Review. (Configure)
| if path.ancestors().any(|ancestor| { | ||
| ancestor | ||
| .file_name() | ||
| .is_some_and(|name| path_component_eq(name, "bin")) | ||
| && ancestor | ||
| .parent() | ||
| .and_then(Path::parent) | ||
| .and_then(Path::file_name) | ||
| .is_some_and(|name| path_component_eq(name, "bazel-out")) |
There was a problem hiding this comment.
🟡 Bazel binary links enter host registrations
When PATH contains a bazel-bin link before the installed CLI, is_build_output_binary treats that link as an install. which_tracedecay_path_from selects it, leaving host integrations pointing at a disposable build artifact.
Learn more
The host registry embeds the executable selected by which_tracedecay_path_from in host configuration. Bazel exposes its build output as a bazel-bin convenience link as well as through bazel-out/<configuration>/bin. The check here recognizes only the latter spelling. A PATH lookup can therefore select a bazel-bin entry ahead of an installed executable and persist that build-only path.
Example: PATH starts with /work/tracedecay/bazel-bin/crates/tracedecay-cli and then /usr/local/bin. Both directories contain tracedecay. The first candidate is accepted, and installing an agent writes the build-linked executable instead of /usr/local/bin/tracedecay.
Recommended fix: Normalize candidates through canonicalization before classifying Bazel outputs, or recognize bazel-bin links explicitly while retaining the existing bazel-out and Cargo-target exclusions. Add a PATH-order regression case for the Bazel convenience link.
Was this helpful? React with 👍 or 👎 to provide feedback.
#3073 (lexical roster carry) and #3067 (ambiguous candidate gaps) merged on 2026-10-05 seconds apart and moved validation ranking; Linux tests moved to Bazel that evening and no master run has reached search-eval since, so the drift went unseen. The train receipt still matches its pin under Bazel, so the fixture inputs are unchanged and only the validation rows moved. The workload identity pin excludes receipts and stays. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01V8Xg2pQQcTiyVB8ndJc5oo
… claude/bazel-windows-fixes
tracedecay-lsp and tracedecay-code-index fail on Windows with a bare E0463 for every --extern their params file names, which is the locator dropping each candidate without recording a reason. On failure, rerun one lsp compile from its expanded params with the locator's info log. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01V8Xg2pQQcTiyVB8ndJc5oo
rules_rust sets a cargo_build_script's CFLAGS and CXXFLAGS from the C toolchain's own arguments, overwriting --action_env, so vendored C never got Cargo's -DNDEBUG on any host and MSVC never got /utf-8: tree-sitter grammars spelled symbol names in the ANSI code page, and every Windows extraction test panicked on invalid UTF-8. cc-rs appends HOST_ (native) or TARGET_ (cross) flags to CFLAGS, and rules_rust leaves those alone. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01V8Xg2pQQcTiyVB8ndJc5oo
.cargo/config.toml hands libsqlite3-sys's build script the worker-thread cap and disabled memory-status counters through [env]; Bazel never set them, so the Bazel lanes tested a differently configured SQLite. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01V8Xg2pQQcTiyVB8ndJc5oo
Tests reached rustup through a HOME Bazel had redirected, so cargo,
rustc, and rust-analyzer found an empty toolchain home ("missing
manifest in toolchain"); the runner leaves CARGO_HOME and RUSTUP_HOME
unset, so export them. The SDK journeys build sdks/typescript with pnpm
from the checkout, which the Cargo-era test jobs installed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V8Xg2pQQcTiyVB8ndJc5oo
… HEAD # Conflicts: # .github/workflows/ci.yml
There was a problem hiding this comment.
Devin Review found 3 new potential issues.
3 flags not posted on this PR by your GitHub settings — view them in Devin Review. (Configure)
| env = dict({"TRACEDECAY_DISABLE_GLOBAL_DB": "1"}, **{ | ||
| variable: "$(rootpath {})".format(label) | ||
| for variable, label in runfiles_env.items() |
There was a problem hiding this comment.
🔴 Spawned test binaries vanish outside runfiles
runfiles_env supplies relative executable paths, but isolated_tracedecay_command runs the CLI from a temporary home. The CLI cannot be found, so discovery tests fail.
Learn more
Bazel's rootpath expansion names a path relative to the test runfiles workspace. The previous launcher converted each runfile into an absolute TEST_SRCDIR path before exporting it. isolated_tracedecay_command changes the CLI child's working directory to a temporary home, and tracedecay_exe passes the exported path unchanged. Similar consumers include production_binary, which canonicalizes the executable.
Example: If CARGO_BIN_EXE_tracedecay is crates/tracedecay-cli/tracedecay__test, the discovery test launches it from /tmp/isolated-home. The OS looks for /tmp/isolated-home/crates/tracedecay-cli/tracedecay__test, not the executable in runfiles.
Recommended fix: Resolve every runfiles_env value to an absolute runfiles path at test launch, preserving support for Windows runfiles, before exposing it to tests or child processes.
Was this helpful? React with 👍 or 👎 to provide feedback.
| #[test] | ||
| fn test_bash_call_sites() { | ||
| let source = std::fs::read_to_string("../../tests/fixtures/sample.sh").unwrap(); | ||
| let source = std::fs::read_to_string("tests/fixtures/sample.sh").unwrap(); |
There was a problem hiding this comment.
🟡 Cargo extraction tests lose shared fixtures
Under Cargo, test_bash_call_sites runs from its crate directory, where tests/fixtures/sample.sh does not exist. The same replacement across language suites makes their fixture reads fail.
Learn more
The fixture files live under the repository-level shared fixtures, not under this crate's tests directory. Cargo invokes integration tests with the crate directory as their working directory, unlike Bazel's runfiles workspace. The previous relative path climbed to the repository root, while the new one works only for Bazel. Other changed language suites use the same relative path and have the same Cargo failure.
Example: Running the extraction suite via Cargo from crates/tracedecay-code-extraction looks for crates/tracedecay-code-extraction/tests/fixtures/sample.sh; the actual file is tests/fixtures/sample.sh at repository root.
Recommended fix: Anchor fixture reads to env!("CARGO_MANIFEST_DIR") and join ../../tests/fixtures/...; keep Bazel's compile-time manifest directory stable and include those fixtures in test runfiles.
Was this helpful? React with 👍 or 👎 to provide feedback.
| for directory in [ | ||
| "tests/fixtures", | ||
| "crates/tracedecay-code-extraction/fixtures", | ||
| ] { |
There was a problem hiding this comment.
🟡 Cargo fixture scan opens missing directories
Under Cargo, file_end_line_matches_the_lines_convention_for_every_checked_in_fixture runs from the crate directory. Its new directory names resolve there, so read_dir fails before checking any fixtures.
Learn more
The unit test's fixture scan calls read_dir for both entries and panics on either failure. Cargo runs it with the code-extraction crate as the current directory. The first new entry is tests/fixtures, while the shared files are two directories above; the second entry redundantly prefixes the current crate's path. Bazel uses a different working directory, so one set of plain relative paths cannot serve both.
Example: In a Cargo test run, read_dir("tests/fixtures") seeks crates/tracedecay-code-extraction/tests/fixtures, which is absent; the shared directory is at repository root.
Recommended fix: Build absolute paths from env!("CARGO_MANIFEST_DIR") for both the shared and crate-local fixture directories, and ensure Bazel carries each directory as runfiles.
Was this helpful? React with 👍 or 👎 to provide feedback.
Bazel tests need the same runtime fixtures and feature resolution as the workspace's existing tests. This branch incorporates the shared Bazel fixes and restores the original fixture reads and strict JSON assertions; the earlier fixture whitelists, swallowed traversal errors, and sorted-key workarounds have been removed.
On macOS, the invalid-filename capability probe now proves that ordinary files can be created and removed before interpreting the filesystem's refusal of a non-UTF-8 filename. Measured behavior is EILSEQ (92) on the host and EPERM (1) in the Bazel sandbox; other failures still propagate.
Validation through Bazel:
Full Linux, macOS, Windows, Clippy, and shipped-CLI validation is still running. This PR is not yet verified green across all platforms.