Feature-level validation (/kiro-validate-impl) found src/harness/CLAUDE.md (written by task 1.3, before tasks 2.1/2.2/3.1 landed) still claimed no /xce-build harness entry point and no unit-test binaries existed -- both false by the time all 16 tasks completed. Updated to describe the real, final state: /xce-build harness as the build command, and all 5 test binaries (harness_unit_test + 4 standalone) under the harness suite. Also logs one more out-of-boundary stale-path finding (src/tools/test-commit-durability.sh:97) for a future owner. This closes out feature-level validation for xce-harness-component -- mechanical (173+1213 tests, 0 failures), design/boundary alignment (zero controller-private dependency, both sanctioned exceptions exact match), and requirements coverage (22/22 mapped) all passed cleanly. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RLSNJhNoULvVcUhXEFafQC
20 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 confirm its explicit compile step never targetsxce_test(a full default-profile test run may still transitively rebuild it viaharness_unit_test's dependency -- a documented, accepted Meson limitation, not a defect; see requirements.md's "Known limitation" boundary note); run/xce-build alland confirm both exist - Observable: each of the three build invocations produces exactly the binaries described above, and the controller-only build's explicit
meson compilestep is confirmed (via build-log inspection) to never namexce_test - 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. - Feature-level validation (
/kiro-validate-impl, post-4.4): three parallel independent checks (mechanical+smoke, design/boundary alignment, requirements+cross-task) all passed cleanly (173+1213 tests 0 failures; harness has zero controller-private dependency; both sanctioned exceptions exact-match; 22/22 requirements mapped; zero stalecontroller/xce_testreferences within this spec's own files). Two dimensions independently found the same gap:src/harness/CLAUDE.md(written by task 1.3, genuinely in-boundary per design.md's File Structure Plan) was never revisited after tasks 2.1/2.2/3.1 landed, so it still claimed "no/xce-build harnessentry point yet" and "no unit-test binaries yet" -- both false. Fixed directly post-validation (doc-only correction, not a new task). Lesson for future specs: a component-guide task written early (task 1.3) should either land LAST, or tasks.md should explicitly assign a later task to circle back and refresh it once the facts it describes stabilize. Also found one more out-of-boundary stale-path script beyond the two already known:src/tools/test-commit-durability.sh:97hardcodes the oldcontroller/xce_testpath -- noted for whoever owns that script next, not fixed here. - Task 4.4 (final task): verified all four downstream workflows against a genuinely harness-absent, otherwise-fully-built dir, including a real 3-node HA cluster deployment for
xce-deploy-ha. All pass. Extended task 4.3's finding:worker)andscheduler)'sxce-build.shcases also runmeson test, so they too transitively resurrectharness/xce_testvia the same mechanism (apiserver)does not, since it only runsgo test) -- same accepted-limitation status as task 4.3, no new decision needed. Found one more pre-existing, out-of-boundary stale doc claim worth a quick follow-up sometime:.claude/skills/xce-build/SKILL.md:37still says "controller always builds xce_test too... in every profile" (false since task 3.1). - Task 4.3: found a real, deterministic Requirement 2.2 conflict, not a flake — under the default
asanprofile,/xce-build controller'smeson test --suite controllerstep transitively rebuildsharness/xce_teston disk, because Meson's test-runner rebuilds every registered test's build-order dependencies project-wide before filtering by--suite, andharness_unit_test(task 2.1) depends onxce_test. Traced precisely via build-log line analysis (explicitmeson compilestep: 209 steps, clean; subsequentmeson teststep: 844 steps, resurrects it) -- this is why task 3.1's own validation missed it: it checked the oldcontroller/xce_testpath (correctly absent), notharness/xce_test(the path that actually gets touched). User decision 2026-09-14: accept as a documented Meson limitation and narrow Requirement 2.2's wording to what the build system directly controls (its own explicit compile-target list), rather than auditing every component's test-target list (non-trivial -- controller's own suite already has 4 small test binaries not in its own explicit compile list, relying on this exact same auto-rebuild) or weakeningharness_unit_test's build-order dependency (task 4.2 verified load-bearing). requirements.md/design.md updated accordingly; logged as bug (not-fixed, accepted) for the record. - 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.