flan/HANDOFF-devtest-noise.md

3.2 KiB

Handoff — the linker error dune test prints on every run

Branch: dev-loop, worktree agent-a4f73ab1c6726fdc2. Scope is the dev-robust fixture and the noise it makes; nothing else in the suite is touched.

The question

Every dune test --root . prints, twice:

/usr/bin/ld: cannot open output file /tmp/build_*_dune/flan-devtest-robust.cache/flan-macros-*.so.*: Permission denied
clang: error: linker command failed with exit code 1

HANDOFF-x86-rt.md section 4 says this is inside the dev-robust fixture and deliberate. That has been quoted forward without ever being checked. The brief names two possibilities: the failure is deliberate and only looks like a broken toolchain, or it is accidental and the fixture has been proving less than it claims.

The answer: deliberate, and the read is confirmed at the source

test/test_dev.ml, the block starting "A build that fails is a refusal, not the end of the session" (~line 2805):

  • The fixture gives the daemon a cache directory of its own — FLAN_CACHE_DIR=<tmp>/flan-devtest-robust.cache, with any inherited FLAN_CACHE_DIR filtered out of the environment first.
  • Before each step that must fail it runs Unix.chmod rcache 0o500, and afterwards 0o700. A read-only, non-writable cache directory is exactly why ld cannot open its output there.
  • The two failing steps are the eval-expr one (C-x C-e) and the eval one carrying a defmacro (C-c C-c) — which is why the message appears twice per run, and why the count matches.

So this is possibility 1. The property — a failed build leaves the session standing — is genuinely under test.

The actual defect, which is narrower

The daemon is spawned with Unix.stderr as its stderr, so it inherits the test binary's. Build.run shells out with Sys.command, which does not capture anything, so clang's and ld's own words go straight through to the terminal. Its stdout already goes to a log file nobody reads (rout), and the comment there says as much; its stderr was simply never given the same treatment.

The result is four lines of what reads like a broken toolchain on a passing run. A real linker failure in that spot today would be invisible.

The plan

  1. Give the robustness daemon its own stderr log rather than the suite's, the same way its stdout already has one.
  2. Report that log — and say so — only when something in the block actually failed, so a genuine linker error is still visible, and a deliberate one is not.
  3. Have the fixture announce the deliberate failure in its own voice before it causes it, so a reader of the output knows a build is about to be made to fail and why.

Baseline to not regress: dune test --root . exit 0, spike/x86/survey.sh 101 MATCH / 0 DIFFER / 0 refused, spike/x86/cells.sh 4/4.

Open questions

  • Build.run reports only (exit N) to the caller; clang's actual words reach the terminal and nothing else. The fixture asserts the reply "carries the compiler's message" but only checks for the substring (exit , so an editor attached to a real flan dev sees a bare exit status and no diagnostic. Widening Build.run to capture and forward stderr would fix that, and is out of this lane's scope — recorded here so it is not lost.