git.lucas.co / cce-compositor
Wayland compositor (wlroots)
git clone https://git.lucas.co/cce-compositor.git

commit2695dbce6d46f082c6860138ed280c193db6e2c9
parent28d83a54fa
authorLucas Galante <[email protected]>
date2026-08-28 11:20
docs: the scenefx build dir a moved workspace leaves behind, and what strings cannot tell you

Two traps that each cost a session an afternoon this week, from
cce-20's debugging rather than mine.

`build.rs` guards `meson setup` on `!Path::new("scenefx/build")
.exists()`, so once that dir exists setup never runs again and every
later build is `meson compile -C build` alone. Meson bakes absolute
paths into a configured build dir, which makes relocating the workspace
root fatal to it: the dir points at the old tree forever, `cargo clean`
and `make clean` only clear `../target/`, and a fresh clone is fine
because the dir is gitignored — so only a MOVED tree carries the dead
one. It surfaces in compile rather than setup, and the old path in
meson's "Neither source directory ... contain a build file meson.build"
is the whole diagnosis. Recovery is deleting the directory.

WORKSPACE.md's staleness check is the other. It says to look inside the
installed binary for something the change introduced, which is right in
one direction only: a hit proves freshness, a miss proves nothing. Not
every literal survives into the binary. Verified here against a current
cce-fx — `mode_next_shared` and `toggle_overview` appear, while
`overlay_right`, `brightness_down` and `focus_up` report zero from the
same file and the same kind of match arm. That reads exactly like a
build that did not take, and it is what sent a session chasing one that
had worked. Left as observed rather than explained, since neither of us
chased the linker. The behavioral alternative that did work — plant a
bogus value and watch for the code path that rejects it — is recorded
alongside it.

Co-Authored-By: Claude Opus 5 <[email protected]>

 CLAUDE.md    | 23 +++++++++++++++++++++++
 WORKSPACE.md | 17 +++++++++++++++++
 2 files changed, 40 insertions(+)

diff --git a/CLAUDE.md b/CLAUDE.md
index d907319..a690ba5 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -232,6 +232,29 @@ cargo test --lib config::         # tests in the config module
 4. Runs `bindgen` over `wrapper.h` → `$OUT_DIR/bindings.rs`, blocklisting a handful of
    types that are hand-defined `#[repr(C)]` in Rust instead.
 
+**"First time" means the guard is `!Path::new("scenefx/build").exists()`** (step
+1, `build.rs`) — so once that directory exists `setup` never runs again, and
+every later build is `meson compile -C build` alone, which cannot reconfigure.
+Meson bakes absolute paths into a configured build dir, so **relocating the
+workspace root kills it permanently**: the dir still points at where the tree
+used to be, and nothing in the build recovers it. `cargo clean` and `make
+clean` both only clear `../target/`; `scenefx/build/` is gitignored
+(`.gitignore:6`, `scenefx/.gitignore:2`), so a fresh clone never has one and is
+fine, while a *moved* tree carries the dead one along.
+
+It surfaces in `meson compile`, not `meson setup`, which is what makes it
+confusing — ninja goes to regenerate `build.ninja` and meson dies with
+
+```
+ERROR: Neither source directory '<old absolute path>' nor build directory '.' contain a build file meson.build
+```
+
+The old path in that message is the entire diagnosis: it names where the
+workspace used to live. Recovery is `rm -rf cce-compositor/scenefx/build` and
+one more build to reconfigure, about a minute. Cost an afternoon on
+2026-08-28, when a build dir configured under the workspace's former
+`~/Dropbox/cce` path survived the move to `~/projects/cce`.
+
 ## Architecture
 
 Everything lives under `src/server/` and is re-exported flat from `src/lib.rs` via
diff --git a/WORKSPACE.md b/WORKSPACE.md
index 1a55608..cc921c9 100644
--- a/WORKSPACE.md
+++ b/WORKSPACE.md
@@ -164,6 +164,23 @@ more than the rest of the workspace combined, so it keeps whatever toolkit
 version it was last built against until someone decides that trade is worth
 making.
 
+**A hit proves freshness; a miss proves nothing.** Not every string literal in
+the source survives into the binary, and the two cases are not distinguishable
+from the outside. Measured against a current `cce-fx` on 2026-08-28: the live
+`Action` names `mode_next_shared` and `toggle_overview` appear (twice and once),
+while `overlay_right`, `brightness_down` and `focus_up` — same file, same kind
+of literal, all reachable in `cce-window-manager/src/api.rs` — report zero.
+Probably link-time constant merging; recorded as observed, not explained. So
+prefer a long distinctive log string over a short match-arm literal, and never
+read a zero as "the build didn't take" — that false negative has already cost a
+session an afternoon of chasing a build that had worked.
+
+When the answer actually matters, test the behavior instead of a proxy for it:
+put a deliberately bogus value where the real one goes (a made-up action name in
+a shadow session's `input.kdl`) and watch for the code path that rejects it —
+the compositor's "unknown window-manager action" warning firing for the bogus
+name and staying quiet for yours proves the running binary knows yours.
+
 For plain cargo work:
 
 ```sh