← All resources

A real Bazel build fix with Codex and Hermetiq.

A script reads a file that its BUILD rule never declared. The remote action fails. Hermetiq supplies the failed-action evidence, Codex corrects the rule, and Bazel verifies the result.

This is an intentionally broken local sample, recorded on . The failure, MCP responses, patch and remote reruns are real. The source and evidence are available below.

1. A local file is missing from the remote action.

The requested target is //:verify_release. Its first genrule, //:release_manifest, runs a script that reads version.txt and build-mode.txt. Both files exist in the sample directory, but the original rule lists only version.txt in srcs.

generate_manifest.sh — complete source
#!/bin/sh
set -eu
version="$(cat "$1")"
mode="$(cat build-mode.txt)"
printf 'version=%s\nmode=%s\n' "$version" "$mode" > "$2"

Buildbarn runs the remote action with the inputs Bazel supplies. The script reaches cat build-mode.txt, cannot find the undeclared file, and exits with code 1.

failure.log — selected error lines
Executing genrule //:release_manifest failed: (Exit 1)
cat: build-mode.txt: No such file or directory
ERROR: Build did NOT complete successfully

The recorded invocation is 47ebfae1-6932-4e6f-8077-50ad3c20f5f2. Its execution log records runner: remote, cacheHit: false and exitCode: 1.

2. Hermetiq finds the action. Codex checks its inputs.

Codex uses the Hermetiq skill and the actual local Hermetiq MCP server. It queries find_actions for failed actions in the invocation, then retrieves the returned action with get_action_execution. Hermetiq returns its command, stderr and failure details.

get_action_execution — selected fields from the MCP response
{
  "label": "//:release_manifest",
  "type": "Genrule",
  "exitCode": 1,
  "failureDetail": "Genrule returned a non-zero exit code when running remotely"
}

The stderr identifies the missing filename. Codex inspects the source and Bazel’s execution log to check why it is absent. The failed action’s complete list of input paths contains the tool and version.txt, but no build-mode.txt:

failure-execution.json — remote input paths
external/bazel_tools/tools/genrule/genrule-setup.sh
generate_manifest.sh
version.txt

The diagnosis is an undeclared input in the BUILD rule. The recorded evidence does not call for adding a file to the worker image or increasing execution capacity.

3. Declare the missing input.

Codex changes one line in BUILD.bazel, adding build-mode.txt to the genrule’s srcs. The script stays unchanged. Bazel can now send the file with the remote action.

fix.diff — exact recorded patch
--- a/BUILD.bazel
+++ b/BUILD.bazel
@@ -1,6 +1,6 @@
 genrule(
     name = "release_manifest",
-    srcs = ["version.txt"],
+    srcs = ["version.txt", "build-mode.txt"],
     tools = ["generate_manifest.sh"],
     outs = ["release-manifest.txt"],
     cmd = "$(location generate_manifest.sh) $(location version.txt) $@",

Compare the original BUILD file with the fixed BUILD file. The fixed execution log now includes build-mode.txt in the action’s inputs.

4. Rebuild, then verify remote cache reuse.

Codex reruns the same //:verify_release target with remote cache reads disabled. Both genrules execute remotely and succeed. The second genrule checks that the generated manifest contains exactly version=1.0.0 and mode=release.

fixed.log — selected verification lines
Target //:verify_release up-to-date:
  bazel-bin/verified.txt
INFO: 3 processes: 1 internal, 2 remote.
INFO: Build completed successfully, 3 total actions

The Hermetiq get_invocation response reports success, exit code 0, two remote executions and zero remote cache hits. This verifies the correction by executing the affected work.

Codex then reruns unchanged with remote cache reads enabled. Local action reuse and the disk cache remain disabled. This time both action results come from Buildbarn’s remote cache:

cache.log — selected verification lines
Target //:verify_release up-to-date:
  bazel-bin/verified.txt
INFO: 3 processes: 2 remote cache hit, 1 internal.
INFO: Build completed successfully, 3 total actions
Recorded runExit codeRemote executionsRemote cache hits
Fixed BUILD020
Unchanged rerun002

Both runs also include one internal Bazel action. The fixed and cached execution records preserve the same action digest for each genrule, and the cached records mark cacheHit: true. No speedup percentage is inferred from this small sample.

Reproduce it in a local Buildbarn environment.

The recording used Bazel 9.1.0 on macOS, a local Buildbarn Linux ARM64 worker, a local Hermetiq BEP receiver with Postgres, and the Hermetiq MCP server. You need those services running before using the example; the download contains the sample and evidence, not a provisioned build environment.

  1. Download and extract the complete example ZIP into a new disposable directory. Its sample/ folder includes the source and .bazelversion. From the extracted directory, prepare the broken version:

    Prepare the sample
    cd sample
    cp BUILD.before.bazel BUILD.bazel
  2. Run the following command from sample/. These endpoints, instance names and identity header belong to the recorded local lab. For another environment, use its configured endpoints and authentication. The expected failure is the missing build-mode.txt error shown above.

    Recorded reproduction command — remote cache reads disabled
    bazel --ignore_all_rc_files --batch build //:verify_release \
      --jobs=2 --nouse_action_cache --disk_cache= \
      --remote_executor=grpc://127.0.0.1:8980 \
      --remote_cache=grpc://127.0.0.1:8980 \
      --remote_instance_name=hardlinking \
      --remote_default_exec_properties=OSFamily=linux \
      --remote_default_exec_properties=container-image=docker://ghcr.io/catthehacker/ubuntu:act-22.04@sha256:dd7654ffb01d5b7b54b23b9ce928a1f7f2d08c7b3d7e320b6574b55d7ccde78b \
      --bes_backend=grpc://127.0.0.1:50091 \
      --bes_header=X-Forwarded-User=public-example-agent \
      --bes_instance_name=local-dev \
      --bes_upload_mode=wait_for_upload_complete \
      --build_event_json_file=build.bep.json \
      --execution_log_json_file=build.execution.json \
      --remote_accept_cached=false
  3. Use the invocation ID printed by your run with Hermetiq’s find_actions, filtering for result="failed". Pass the returned action ID to get_action_execution. Compare its stderr with the local script, BUILD rule and your build.execution.json.

  4. From sample/, apply the recorded patch. Then run the same Bazel command again, keeping --remote_accept_cached=false. Check the successful build and Hermetiq’s get_invocation result.

    Apply the recorded one-line correction
    patch -p1 < ../fix.diff
  5. Run unchanged once more, replacing only --remote_accept_cached=false with --remote_accept_cached=true. Keep --batch --nouse_action_cache --disk_cache= to separate remote cache reuse from local reuse. Check both the invocation and execution records for remote cache hits. Save each run’s JSON logs separately if you want to compare them; the command reuses the same output filenames.

The captured evidence is available even without a local stack. Local dashboard and Buildbarn URLs inside the original responses refer to the recording environment and are not public demo endpoints.

What this recording demonstrates.

This is a complete client-assisted correction: Hermetiq supplied the failed-action evidence; Codex inspected source and execution inputs, applied a targeted change, and ran Bazel to validate it. Codex had repository and terminal access. It is a recorded local example rather than a customer benchmark or an unattended service run.

The diagnosis used BEP action data and stderr retrieved through Hermetiq, together with local source and Bazel’s execution log. This particular run did not correlate worker logs, infrastructure telemetry or completed-action-log records. Project CAS enrichment was unavailable.

The execution records identify the Linux remote platform and remote runner. The darwin_arm64 path in Bazel output describes the macOS client configuration; it does not mean the genrule ran on a macOS worker.

Recorded
September 8, 2026 UTC
Bazel
9.1.0
Backend source revision
efbd9de19d5cfccf46039d3431ccbda7b33df2b0, compiled unchanged
Hermetiq skill revision
cddcaa83354e3d1dfc0ddc8c019cbab683f2fea5

Timings in the logs apply only to this tiny sample. Bazel elapsed time and Hermetiq’s invocation duration use different boundaries. The export preserves MCP structured responses and normalizes execution-log records into JSON arrays; usernames and absolute recording paths were replaced with generic public paths. Full methodology and export details are in the README.

Bring your build evidence into the agent’s workflow.

Hermetiq’s AI works across managed builds and intelligence for existing Buildbarn, with cloud and on-prem options. The available diagnosis and changes depend on connected data and the access you grant.