Validation-only task, no source changes. Confirmed via: file-identity spot-checks (7 of 15 production files across two independent passes, all zero content drift since relocation beyond the one documented ctrlstatus_fmt.h include-path edit); a fresh harness suite run reproducing known-good counts (4/20/5/12/132 tests, 0 failures); byte-diffing a genuine pre-relocation binary (src/build.dbg/controller/ xce_test, predates the move by 3 days) against the harness-built binary for --help/--usage/piped REPL commands -- identical output, confirmed independently by two separate passes. Found a real, non-blocking follow-up (pre-scoped as out of this spec's boundary): two integration scripts outside the 15 relocated files (test_rcfile_integration.sh, clean_failover_preservation.sh) hardcode a stale default xce_test path. Logged in Implementation Notes for whoever owns those scripts next. Task 4.1 of xce-harness-component. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RLSNJhNoULvVcUhXEFafQC
16 KiB
Implementation Plan: xce-harness-component
-
1. Foundation: relocate xce_test itself and establish the component
-
1.1 Relocate xce_test's production sources, wire its standalone build, and strip every controller-side reference to the moved files
- Move all 15 xce_test source files (
test.c,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) and their headers out ofsrc/controller/testsuite/intosrc/harness/, content unchanged - Create
src/harness/meson.builddefining only thexce_testexecutable so far (same sources/dependencies as today, paths updated); addsubdir('harness')tosrc/meson.build - In the same task, from
src/controller/meson.buildremove: thexce_test_src/xce_test_deps/xce_testexecutable block; everycontroller_unit_test_srcentry that references one of the 15 moved files, which is 10 production+test pairs (waldump,walcompare,snapverify,snapdiff,configcompare,rcfile,flagspec,interruptcall,confirm,listfmt_json, each with itstest_*.c) plustest_xcetest_interrupt.c(extrainterruptcall.ccoverage) — 11 test files total; and the four entire standaloneexecutable()/test()blocks (resval_typeresolve_unit_test,joblist_display_unit_test,join_worker_args_unit_test,listrow_json_unit_test), since each references one of the 15 moved production files directly - Also remove those same 11 test files'
extern/RUN_TESTentries fromcontroller_test.c. Leavetest_test_cli.c's andtest_xcetest_process_entry.c's entries in place — neither links any of the 15 moved files (they spawn the builtxce_testbinary as a subprocess instead), so they stay valid incontroller_unit_testuntil task 2.1 relocates them - The 11 removed test files'
.csources are left on disk at their currentsrc/controller/testsuite/path for now (referenced by no build target) — task 2.1 physically relocates them together withtest_test_cli.c/test_xcetest_process_entry.c - Observable: a harness-scoped build produces an
xce_testbinary; a controller-scoped build run immediately after this single task (no later tasks applied) still succeeds,controller_unit_test's reported test count has dropped by exactly the 11 removed entries, and none of the four standalone binaries are defined anymore - Requirements: 1.1, 1.2, 1.3, 2.1, 2.2, 2.4
- Move all 15 xce_test source files (
-
1.2 Promote the shared ctrlstatus_fmt.h header and repoint its current consumers
- Move
ctrlstatus_fmt.htosrc/include/xce/; update the include path in harness'stest.candjoblist_display.c(already relocated in 1.1) and in controller'stest_ctrlstatus.c joblist_display.c's own test file,test_joblist_display.c, still lives insrc/controller/testsuite/at this point and is intentionally left untouched here — its include-path fix happens in task 2.2, when that file relocates- Observable: a repo-wide search finds no remaining relative
#include "ctrlstatus_fmt.h"; all three current consumers include it fromxce/ctrlstatus_fmt.h - Requirements: 1.4
- Move
-
1.3 Write the harness component guide
- New
src/harness/CLAUDE.mddescribing the component's purpose, build command, and structure, matching the style of existing component guides (e.g.src/controller/CLAUDE.md) - Observable:
src/harness/CLAUDE.mdexists and documents the build command and file layout - Requirements: 6.1
- New
-
2. Core: migrate the five harness test binaries
-
2.1 Consolidate the controller-embedded harness tests into a new binary and finish clearing them from the controller's own test binary
- Physically relocate the 13 test files for this group into
src/harness/testsuite/: the 11 whosecontroller_unit_test_src/controller_test.creferences were already removed in task 1.1 (test_waldump.cthroughtest_xcetest_interrupt.c, left on disk at their old path until now), plustest_test_cli.candtest_xcetest_process_entry.c(still validly part ofcontroller_unit_testuntil this task) - Write a new Unity runner
harness_test.c(modeled oncontroller_test.c) carrying all 13extern/RUN_TESTentries; register the newharness_unit_testexecutable and itsmeson test()under suiteharnessinsrc/harness/meson.build; declare its build-order dependency on thexce_testexecutable target (two of its tests spawn that binary over a pty) - In the same task, remove
test_test_cli.c's andtest_xcetest_process_entry.c's entries fromcontroller_unit_test_srcand theirextern/RUN_TESTlines fromcontroller_test.c— the only two of the 13 not already removed in task 1.1 (test_ctrlstatus's entry stays — it tests controller's ownctrlstatus.c) - Observable:
meson test --suite harnessrunsharness_unit_testwith the same pass count these 13 files reported before the move;meson test --suite controllerrun immediately after this same task still runscontroller_unit_testsuccessfully, with its test count reduced by exactly these final 2 entries (11 were already reflected in task 1.1's count drop) - Requirements: 3.1, 3.2, 3.3, 3.4
- Physically relocate the 13 test files for this group into
-
2.2 Relocate the four already-standalone harness test binaries
- Physically relocate the
test_resval_typeresolve.c,test_joblist_display.c,test_join_worker_args.c, andtest_listrow_json.cfiles intosrc/harness/testsuite/(left on disk at their old path since task 1.1 removed their controller-sideexecutable()/test()blocks); re-create each of the fourexecutable()/test()blocks, unchanged in shape, insrc/harness/meson.buildunder suiteharness, referencing their production files already relocated tosrc/harness/in task 1.1 - Update
test_joblist_display.c'sctrlstatus_fmt.hinclude path toxce/ctrlstatus_fmt.has part of this same move - Observable:
meson test --suite harnesslists all four binaries alongsideharness_unit_test, each reporting the same pass count as before the move - Requirements: 1.4, 3.1, 3.2, 3.4
- Physically relocate the
-
3. Integration: decouple build orchestration, downstream workflows, and documentation
-
3.1 Add the harness build entry and decouple it from the controller build
- In
src/tools/xce-build.sh, dropcontroller/xce_testfrom thecontroller)case's compile target list; add a newharness)case following the identical three-partsetup_build/meson compile/ conditionalmeson test --suite harnesstemplate already used byscheduler)/worker) - Observable:
/xce-build harnesson a fresh build dir (no prior controller build) produces thexce_testbinary and runs theharnesssuite;/xce-build controlleralone does not producexce_test - Requirements: 2.1, 2.2, 2.3, 2.4
- In
-
3.2 (P) Update the HA soak workflow's harness prerequisite
- Update
.claude/skills/xce-test-ha/run-ha-soak.shandSKILL.mdso the soak ensures the harness is built (or fails with a clear message) instead of assuming a controller build produced it - Observable: running the workflow from a build dir where only controller was built either builds the harness itself or reports a clear, actionable message instead of a raw spawn/connect failure
- Requirements: 5.1, 5.4
- Boundary: xce-test-ha skill
- Depends: 3.1
- Update
-
3.3 (P) Extend the local HA deploy workflow's soft-skip handling to the harness
- Update
.claude/skills/xce-deploy-ha/SKILL.mdto cover the harness binary under its existing worker/scheduler/xceadmin soft-skip pattern - Observable: deploying from a build dir without the harness built reports the same clear soft-skip style already used for a missing worker/scheduler/xceadmin binary
- Requirements: 5.2, 5.4
- Boundary: xce-deploy-ha skill
- Depends: 3.1
- Update
-
3.4 (P) Update the multi-host verification workflow's harness prerequisite
- Update
.claude/skills/xce-test-multihost/run-multihost-verify.shandSKILL.md - Observable: running the workflow from a build dir where only controller was built either builds the harness itself or reports a clear, actionable message
- Requirements: 5.3, 5.4
- Boundary: xce-test-multihost skill
- Depends: 3.1
- Update
-
3.5 (P) Update the resource-value verification workflow's harness prerequisite
- Update
.claude/skills/xce-test-resources/run-resource-verify.shandSKILL.md - Observable: running the workflow from a build dir where only controller was built either builds the harness itself or reports a clear, actionable message
- Requirements: 5.3, 5.4
- Boundary: xce-test-resources skill
- Depends: 3.1
- Update
-
3.6 (P) Update root guide and build/deployment steering for the harness component
- Add
harnessto rootCLAUDE.md's/xce-buildcomponent list and Subdirectory Guidelines; removebuild-deployment.md's "controller always builds xce_test" claim and add harness to its/xce-buildcommand table and Meson Structure tree - Observable:
CLAUDE.mdlistsharnessamong buildable components;build-deployment.mdno longer claims the controller build producesxce_test - Requirements: 6.1, 6.2
- Boundary: CLAUDE.md, build-deployment.md
- Depends: 3.1
- Add
-
3.7 (P) Update structure steering to list the harness component
- Add
harnesstostructure.md's component table and to its flat-root Exceptions list alongside controller - Observable:
structure.md's component tree and Exceptions section both mention harness - Requirements: 6.3
- Boundary: structure.md
- Depends: 1.1
- Add
-
4. Validation: prove behavioral parity and end-to-end correctness
-
4.1 Verify xce_test's behavioral parity after relocation
- Rerun the existing
xce-test-cli-ux/xce-test-e2e-primitives/xce-test-rc-filetest coverage against the harness-builtxce_testbinary and diff its command/flag/output behavior against a pre-move capture - Observable: no behavioral difference is found; every previously-passing scenario in that coverage still passes unchanged
- Requirements: 4.1, 4.2, 4.3
- Depends: 3.1
- Rerun the existing
-
4.2 Verify the harness_unit_test build-order dependency actually holds
- Deliberately remove the built
xce_testbinary, runharness_unit_test, and confirmtest_test_cli.c/test_xcetest_process_entry.cfail loudly rather than silently pass; rebuild normally and confirm the suite passes - Observable: the deliberate-removal run reports a clear failure from the two pty-spawning tests; the normal run reports the full suite green
- Requirements: 3.1, 3.2
- Depends: 2.1, 3.1
- Deliberately remove the built
-
4.3 Verify independent and combined build behavior
- Run
/xce-build harnessalone on a clean build dir and confirmxce_testis produced; run/xce-build controlleralone and confirmxce_testis absent; run/xce-build alland confirm both exist - Observable: each of the three build invocations produces exactly the binaries described above
- Requirements: 2.1, 2.2, 2.4
- Depends: 3.1
- Run
-
4.4 Verify every downstream workflow from a clean build state
- Run the HA soak, local HA deploy, multi-host verification, and resource-value verification workflows starting from a build directory where the harness has never been built
- Observable: none of the four workflows fails with a raw/unrelated error; each either succeeds after building the harness itself or reports the expected clear, actionable message
- Requirements: 5.1, 5.2, 5.3, 5.4
- Depends: 3.2, 3.3, 3.4, 3.5
Implementation Notes
- Task 1.1: discovered a real gap in the discovery/design audit —
test_config_repl.c,test_ha_core_safety.c,test_upgrade_snapshot_apply.c,test_bug857_regression.c(controller's own domain tests, out of this spec's boundary) call functions directly fromwaldump.c/walcompare.c/snapverify.c/snapdiff.c. Fixed by havingcontroller_unit_test_srcrecompile those four files a second time from../harness/. This is a permanent, sanctioned controller→harness dependency, not a migration artifact — recorded in design.md's Allowed Dependencies. Later tasks: do not assume harness has zero inbound dependency from controller; only assume harness has zero outbound dependency on controller (Requirement 1.4). - Task 1.1:
test_test_cli.c/test_xcetest_process_entry.clocatexce_testas a build-dir sibling viaresolve_xce_test_path(). Since they still live insrc/controller/testsuite/until task 2.1, a temporaryxce_test_controller_linkcustom_target symlink bridge was added tosrc/controller/meson.build(documented in-code as removable once task 2.1 relocates both files). Task 2.1 must remove this custom_target and thetest()'sdepends:on it as part of that relocation. - Task 1.1 gotcha: a meson
custom_targetcommand's@INPUT0@/@OUTPUT0@tokens expand to build-root-relative paths, but a symlink's target string is resolved relative to the symlink's own directory at dereference time, not the invocation cwd or the string's own reference frame — using@INPUT0@directly as anln -sftarget from a different subdirectory produces a broken link. Use an explicit../-relative (or absolute) path instead. - Build dir note: this repo's default build dir resolves to
src/xce.build/(relative tosrc/tools/xce-build.sh's own cwd), not a barexce.build/at the repo root. - Task 4.1: Confirmed behavioral parity conclusively — found a genuine pre-relocation binary (
src/build.dbg/controller/xce_test, predates commitcf55d691by 3 days) and byte-diffed its--help/--usage/piped-REPL output against the harness-built binary: identical (only--version's embedded build-hash/timestamp differs, as expected). Real, non-blocking follow-up found (out of this spec's boundary, pre-scoped by requirements.md's Adjacent Expectations for thexce-test-rc-file-owned one):src/controller/testsuite/test_rcfile_integration.shandclean_failover_preservation.sh(the latter actually belongs toha-term-safetytask 5.2, not one of the three named xce-test-* specs) both hardcode a stale default binary path (src/xce.build/controller/xce_test) and the latter's failure message still says "run /xce-build controller" — should be repointed tosrc/xce.build/harness/xce_testby whoever owns each script next. - Task 3.7: Major Task 3 (Integration) is now fully complete. Task 3.6's review round caught a real doc-consistency miss (a partial fix left a contradicting stale claim elsewhere in the same file); the lesson (full end-to-end file re-read, not just the two named spots) was applied successfully in 3.7 and independently confirmed by review. Non-blocking follow-up noted for a future pass:
structure.mdline 92's generic "test.cprovides the test runner harness" line is doubly stale (controller's runner iscontroller_test.c, harness's isharness_test.c) and also coincidentally collides withsrc/harness/test.c, which is real production code (xce_test's REPL entry point), not a test runner — worth a one-line correction sometime, out of this spec's boundary. - Task 2.2: Major Task 2 (Core) is now fully complete — all 5 harness test binaries exist under
suite: 'harness'(4/20/5/12/132 tests, all green), controller's suite is fully clean at 1199 tests. Minor non-blocking cleanup found for a future pass:src/controller/meson.build:211'sstruct2json_depcomment still citestest_listfmt_json.c(relocated to harness in task 2.1) as a consumer, and its own dependency may now be unused incontroller_unit_test_deps— worth checking whether it can be dropped entirely once someone audits it. - Task 2.1: confirmed
resolve_xce_test_path()(/proc/self/exedirname + sibling lookup) needed zero code change once bothxce_testandharness_unit_testbuild under the samesrc/harness/meson.build— they land in the same build-dir subdir automatically. Test counts cross-checked and consistent end to end: 1331 (pre-move) → 1228 (after task 1.1's 11-entry removal) → 1199 controller + 132 harness = 1331 (after task 2.1). Minor non-blocking nit for a future pass:test_xcetest_process_entry.c's header comment still describes the old controller-embedded build layout — stale prose only, not a functional issue.