# Brief: xce-harness-component ## Problem `xce_test` — the REPL-driven wire-protocol test client used to drive/verify controller, scheduler, and worker (HA soak, resource-value verification, multi-host allocation proof, WAL/snapshot inspection) — is physically and organizationally welded to the controller component, even though it has no real dependency on it. Its 15 source files live in `src/controller/testsuite/` and its build target is defined inside `src/controller/meson.build`, so it is invisible as a first-class thing in the project's own component vocabulary (`CLAUDE.md`'s `/xce-build` component list: `all, lib, controller, scheduler, worker, apiserver, xcecli, xceadmin` has no entry for it) despite being load-bearing infrastructure for at least four other skills (`/xce-test-ha`, `/xce-deploy-ha`, `/xce-test-multihost`, `/xce-test-resources`). ## Current State - **Binary**: `xce_test`, defined at `src/controller/meson.build:58-85` (`xce_test_src`, `xce_test_deps`, `xce_test = executable(...)`). - **Sources** (15 files, all in `src/controller/testsuite/`): `test.c` (REPL/dispatch entry), `waldump.c`, `walcompare.c`, `snapverify.c`, `snapdiff.c`, `configcompare.c`, `resval_typeresolve.c`, `joblist_display.c`, `join_worker_args.c`, `rcfile.c`, `flagspec.c`, `interruptcall.c`, `confirm.c`, `listfmt_json.c`, `listrow_json.c` — plus their matching `.h` headers. - **Verified finding (de-risks the move)**: none of these 15 files `#include` any controller-private header (`ha.h`, `repl.h`, `config_repl.h`, `myconf.h`, etc.). They depend only on libxce public headers (`src/include/*.h`, e.g. `client.h`, `config.h`, `delay.h`, `idgen.h`, `persist.h`, `preason.h`, `utils.h`, `wal_types.h`, `xce/cluster.h`, `xce/types.h`), the generated `controller.capnp.h` schema header, and xce_test's own component-local headers. `xce_test_deps` is `libxce_dep, librtti_dep, struct2json_dep, libedit_dep, tinylog_dep, zlib_dep, c_capnproto_dep, ssl_dep, thread_dep` — no `controller_deps`-only dependency. It is a pure network client, not a controller-internals harness. - **Hidden coupling to untangle**: `src/controller/meson.build`'s `controller_unit_test_src` (lines ~160-466) currently recompiles ~14 of these same 15 production files a second time, paired with their own white-box unit tests, directly into the controller's own `controller_unit_test` binary: `test_waldump.c`+`waldump.c`, `test_walcompare.c`+`walcompare.c`, `test_snapverify.c`+`snapverify.c`, `test_snapdiff.c`+`snapdiff.c`, `test_configcompare.c`+`configcompare.c`, `test_rcfile.c`+`rcfile.c`, `test_flagspec.c`+`flagspec.c`, `test_interruptcall.c`+`interruptcall.c` (+`test_xcetest_interrupt.c`), `test_confirm.c`+`confirm.c`, `test_listfmt_json.c`+`listfmt_json.c`, `test_resval_typeresolve.c`+`resval_typeresolve.c`, `test_joblist_display.c`+`joblist_display.c`, `test_join_worker_args.c`+`join_worker_args.c`, `test_listrow_json.c`+`listrow_json.c` — plus `test_xcetest_process_entry.c` and `test_test_cli.c`, which white-box-test `test.c`'s own REPL dispatch. This pairing is the real reason a naive "just move the 15 files" plan breaks: those ~16 test files have no production code left to compile against once their counterpart moves. - Not part of any nix/AppImage production package (`src/flake.nix` and `src/meson.build` have zero references to `xce_test`/`testsuite`) — it is dev/test-only tooling, so there is no release-artifact impact. - Existing behavior specs (`xce-test-cli-ux` — tasks generated, ready for implementation; `xce-test-e2e-primitives` — implemented; `xce-test-rc-file` — implementation-complete) all govern xce_test's *behavior* (CLI UX, REPL primitives, rc-file config) and reference these same files by their current `src/controller/testsuite/` paths. None of them address where the code lives or how it's built. ## Desired Outcome `xce_test` (built at) becomes a genuine standalone component under `src/harness/`, following the project's existing C Daemon Layout Pattern (own `meson.build`, `CLAUDE.md`, `include/`, `testsuite/`) exactly like `worker/` and `scheduler/`. It has its own explicit `/xce-build harness` entry, its own unit-test binary covering the ~16 white-box tests currently trapped inside `controller_unit_test`, and building `controller` no longer implicitly produces it. Every consumer that currently assumes "build controller → xce_test exists" is updated to build it explicitly. ## Approach Pure structural relocation + build-graph split — no behavioral/logic changes to xce_test itself: 1. **Move** the 15 production files + their headers from `src/controller/testsuite/` into `src/harness/` (layout to be finalized in design — e.g. flat under `src/harness/` mirroring controller's own flat-root exception, or `src/harness/src/` + `src/harness/include/` per the standard pattern; `test.c` stays the REPL entry point). 2. **Split the unit-test binary**: carve the ~16 paired white-box test files out of `controller_unit_test_src` into a new `harness_unit_test` binary defined in `src/harness/meson.build`, registered as its own `meson test()` (new suite, e.g. `harness`, parallel to the existing `suite: 'controller'`). `controller_unit_test` keeps every other file untouched — this is confirmed to be `test_*.c` domain tests for `ha`/`persist`/`commit`/`repl`/`job`/etc., which are genuinely controller-internal and stay exactly where they are. 3. **Rewire the build**: new `src/harness/meson.build`, `subdir('harness')` added to `src/meson.build`, remove `xce_test_src`/`xce_test_deps`/`xce_test` executable and the moved entries out of `controller_unit_test_src` in `src/controller/meson.build`. 4. **Fully decouple** (per user decision): `/xce-build controller` no longer transitively builds the harness. Add `harness` as a new first-class `/xce-build` component (`.claude/skills/xce-build/`), update its component list and the `Key Files`/`Components` line in root `CLAUDE.md`. 5. **Fix every consumer** that currently assumes xce_test exists right after building controller: `/xce-test-ha`, `/xce-deploy-ha`, `/xce-test-multihost` (`.claude/skills/xce-test-multihost/run-multihost-verify.sh`), `/xce-test-resources` — each needs an explicit `/xce-build harness` step added (or a documented prerequisite) wherever it currently piggybacks on `/xce-build controller`. 6. **Update steering**: `build-deployment.md` (line 24's "`controller` always builds `xce_test`..." claim, the Meson Structure tree, the `/xce-build` command table), `structure.md` (component table + C Daemon Layout Pattern exceptions list), and a new `src/harness/CLAUDE.md`. ## Scope - **In**: relocating xce_test's 15 source files + headers into `src/harness/`; splitting the paired ~16 white-box tests into a new `harness_unit_test` binary; new `src/harness/meson.build` + `src/harness/CLAUDE.md`; adding `harness` to `/xce-build`; decoupling it from `/xce-build controller`; updating every downstream skill/script and steering doc that assumes the old coupling. - **Out**: any behavioral change to xce_test itself (owned by `xce-test-cli-ux`/`xce-test-e2e-primitives`/`xce-test-rc-file` and future xce_test feature work — e.g. the still-open `xce_test WAL/snapshot inspection tooling` roadmap item); packaging xce_test into the nix/AppImage production artifacts (it is dev/test tooling today and stays that way); reorganizing `controller`'s own domain unit tests (ha/persist/commit/repl/job/etc. — confirmed to stay in `src/controller/testsuite/`). ## Boundary Candidates - The production-binary move (files + meson target) is independent of the unit-test-binary split, but both must land together — an intermediate state where `xce_test.c`'s sources live in `src/harness/` while their unit tests still reference the old `src/controller/testsuite/` paths would not build. - The build/skill decoupling (item 4-5 above) is separable follow-up work from the pure file move (items 1-3) and could in principle be its own task group within the same spec, but is NOT a separate spec — it's required for the "standalone component" outcome to actually hold (a component whose build is still silently triggered by another component's build isn't standalone). ## Out of Boundary - Renaming the `xce_test` binary itself (stays `xce_test`; only the *directory*/component is named `harness`). - Any new xce_test functionality (commands, REPL features, output formats). - Touching `xceadmin`, `xcecli`, or any Go frontend build wiring. - Re-litigating which controller unit tests belong where beyond the ~16 files whose production counterpart is physically moving. ## Upstream / Downstream - **Upstream**: none — this is a pure internal restructuring with no external dependency. - **Downstream**: `/xce-test-ha`, `/xce-deploy-ha`, `/xce-test-multihost`, `/xce-test-resources` skills (must be updated to build `harness` explicitly); the three existing `xce-test-*` behavior specs (their task/design docs reference file paths under `src/controller/testsuite/` that will need a path-only update, not a re-review of their approved requirements/design); the still-open roadmap backlog item "xce_test WAL/snapshot inspection tooling" (new `snapdump`/`walcompare` work should target the post-move `src/harness/` location). ## Existing Spec Touchpoints - **Extends**: none directly — this is a new boundary, not an extension of an approved spec. - **Adjacent**: `xce-test-cli-ux`, `xce-test-e2e-primitives`, `xce-test-rc-file` — same binary, disjoint concern (behavior vs. location/build). File-path references in their design/tasks docs will go stale after this spec lands; worth a quick path-only sync pass once implemented, not a re-open of their approved content. ## Constraints - No behavioral regression in xce_test — every existing command/REPL feature must work identically post-move (verify via the existing `xce-test-e2e-primitives`/`xce-test-cli-ux` test coverage rerun against the new binary location). - Meson/build hygiene: must respect the project's existing `--incremental` vs. full-wipe build-dir semantics (`build-deployment.md` bug-1565 note) — a two-component build (`controller` + `harness`) in the same dir needs either `/xce-build all` or `--incremental`, same as any other two-component combo today. - User decisions locked in for this spec (2026-09-13, `/kiro-discovery`): component directory name is `src/harness/` (binary name unchanged, `xce_test`); build coupling is **fully decoupled** (not transitional-compatible) — downstream skills must be fixed as part of this spec, not left broken.