Skip to content

fix(bazel): preserve macOS fixture and filename behavior - #3140

Merged
ScriptedAlchemy merged 35 commits into
masterfrom
fix/macos-bazel-tests
Oct 7, 2026
Merged

ScriptedAlchemy merged 35 commits into
masterfrom
fix/macos-bazel-tests

Conversation

@ScriptedAlchemy

@ScriptedAlchemy ScriptedAlchemy commented Oct 6, 2026 •

Copy link
Copy Markdown
Owner

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:

  • Domain suite: 169 passed with the original assertions and fixture reads.
  • Focused runtime-core churn tests: 2 passed under the macOS sandbox.
  • Runtime environment probe, private-fs, and framing targets passed.
  • Repository gates passed on this PR head.

Full Linux, macOS, Windows, Clippy, and shipped-CLI validation is still running. This PR is not yet verified green across all platforms.

claude and others added 6 commits October 6, 2026 08:23
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.
@changeset-bot

changeset-bot Bot commented Oct 6, 2026 •

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: cda29a8

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@devin-ai-integration devin-ai-integration Bot 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.

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)

Devin Review

ScriptedAlchemy and others added 18 commits October 6, 2026 15:54
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

@devin-ai-integration devin-ai-integration Bot 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.

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)

Devin Review

Comment on lines +813 to +821
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"))

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.

🟡 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.

Devin Review


Was this helpful? React with 👍 or 👎 to provide feedback.

@ScriptedAlchemy ScriptedAlchemy changed the title fix: Bazel test compatibility on macOS fix(bazel): preserve macOS fixture and filename behavior Oct 6, 2026
ScriptedAlchemy and others added 11 commits October 6, 2026 16:52
#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
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

@devin-ai-integration devin-ai-integration Bot 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.

Devin Review found 3 new potential issues.

3 flags not posted on this PR by your GitHub settings — view them in Devin Review. (Configure)

Devin Review

Comment on lines +23 to +25
env = dict({"TRACEDECAY_DISABLE_GLOBAL_DB": "1"}, **{
variable: "$(rootpath {})".format(label)
for variable, label in runfiles_env.items()

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.

🔴 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.

Devin Review


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();

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.

🟡 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.

Devin Review


Was this helpful? React with 👍 or 👎 to provide feedback.

Comment on lines +496 to +499
for directory in [
"tests/fixtures",
"crates/tracedecay-code-extraction/fixtures",
] {

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.

🟡 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.

Devin Review


Was this helpful? React with 👍 or 👎 to provide feedback.

@ScriptedAlchemy
ScriptedAlchemy merged commit fc80b39 into master Oct 7, 2026
12 of 15 checks passed
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