Validation task 4.3 found a real, deterministic conflict with the original requirement 2.2 wording, not a flake: /xce-build controller alone's default asan profile transitively rebuilds harness/xce_test, because Meson's test-runner rebuilds every registered test's build- order dependencies project-wide before filtering by --suite, and harness_unit_test (task 2.1) depends on it. Confirmed via build-log step-count analysis (explicit compile: 209 steps, clean; subsequent meson test: 844 steps, resurrects it) and independently reproduced twice, with an exact step-count match both times. User decision (2026-09-14): accept as a documented Meson limitation rather than auditing every component's test-target list (controller's own suite already relies on this same mechanism for 4 of its own test binaries) or weakening harness_unit_test's build-order guarantee (verified load-bearing in task 4.2). Requirement 2.2 narrowed to what the build system directly controls: its explicit compile-target list never names xce_test. requirements.md/design.md/tasks.md updated to match and cross-reference each other; logged as bug-1806 (accepted, not fixed) for the record. Also commits the xce-harness-component spec's remaining docs (requirements.md, brief.md, research.md, spec.json) and the roadmap.md promotion entry from discovery, none of which had been committed yet. Task 4.3 of xce-harness-component. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RLSNJhNoULvVcUhXEFafQC
62 lines
10 KiB
Markdown
62 lines
10 KiB
Markdown
# 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.
|