diff --git a/.kiro/specs/xce-harness-component/tasks.md b/.kiro/specs/xce-harness-component/tasks.md index 6efbe841..34df23ab 100644 --- a/.kiro/specs/xce-harness-component/tasks.md +++ b/.kiro/specs/xce-harness-component/tasks.md @@ -61,7 +61,7 @@ - _Requirements: 5.3, 5.4_ - _Boundary: xce-test-resources skill_ - _Depends: 3.1_ -- [ ] 3.6 (P) Update root guide and build/deployment steering for the harness component +- [x] 3.6 (P) Update root guide and build/deployment steering for the harness component - Add `harness` to root `CLAUDE.md`'s `/xce-build` component list and Subdirectory Guidelines; remove `build-deployment.md`'s "controller always builds xce_test" claim and add harness to its `/xce-build` command table and Meson Structure tree - Observable: `CLAUDE.md` lists `harness` among buildable components; `build-deployment.md` no longer claims the controller build produces `xce_test` - _Requirements: 6.1, 6.2_ diff --git a/.kiro/steering/build-deployment.md b/.kiro/steering/build-deployment.md index 03a60d23..b4cc80e9 100644 --- a/.kiro/steering/build-deployment.md +++ b/.kiro/steering/build-deployment.md @@ -11,7 +11,8 @@ All builds use `/xce-build` (skill at `.claude/skills/xce-build/`): ```bash /xce-build # all components + all tests /xce-build lib # libxce + lib tests -/xce-build controller # lib + controller + controller tests + xce_test driver +/xce-build controller # lib + controller + controller tests +/xce-build harness # lib + harness + harness tests + xce_test driver /xce-build scheduler # lib + scheduler + scheduler tests /xce-build worker # lib + worker (daemon only, no unit tests) /xce-build apiserver # lib + apiserver + xcecli + Go unit tests @@ -21,15 +22,15 @@ All builds use `/xce-build` (skill at `.claude/skills/xce-build/`): `xceadmin` ignores the profile arg (always the same pinned, non-editable, out-of-tree install + test, regardless of `asan`/`debug`/`release`). Beyond the build-dir venv, it also produces a real wheel at `/xceadmin/dist/xceadmin--py3-none-any.whl` (built via `pip wheel` from the same schema-synced tree the tests just ran against, only after they pass) — `pip install` it anywhere a compatible Python is available, no nix/meson needed at install time. `pyproject.toml` declares all 4 real runtime deps (`click`, `pycapnp`, `PyYAML`, `jsonschema` — verified against every actual `import` in `src/xceadmin`/`src/xcecommon`); the one caveat is `pycapnp`, whose PyPI-prebuilt wheels are usually fine outside the nix shell but aren't the same guaranteed-consistent link against the capnproto C++ runtime `python3Packages.pycapnp` gets. -`controller` always builds `xce_test` alongside `xce_controller` — the driver `/xce-test-ha` needs, in every profile below. There is no separate build step for it. +`harness` builds the `xce_test` driver that `/xce-test-ha` and the multi-host/resource-value verification workflows need, independently of `controller` — building `controller` alone no longer produces `xce_test` as a side effect. Run `/xce-build harness` (or `/xce-build all`) explicitly to get it. Each invocation: 1. `nix develop` into the hermetic shell 2. `meson setup --buildtype= -Db_sanitize=` (fresh clean build — `rm -rf ` then reconfigure, unconditionally, for EVERY component including single-component invocations) -3. `meson compile -C [targets...]` — per-component invocations pass an explicit target list scoped to just that component (e.g. `controller` compiles only `xce_controller controller_unit_test controller/xce_test`); only `/xce-build all` compiles with no target args, building everything +3. `meson compile -C [targets...]` — per-component invocations pass an explicit target list scoped to just that component (e.g. `controller` compiles only `xce_controller controller_unit_test`; `harness` compiles `xce_test harness_unit_test` plus its four standalone test binaries); only `/xce-build all` compiles with no target args, building everything 4. Run registered meson tests (skipped for non-`asan` profiles — see below) -**Consequence (bug-1565) and fix:** because step 2 wipes the whole build dir but step 3 only rebuilds the requested component, running `/xce-build xceadmin` (or any other single component) after `/xce-build controller` in the SAME build dir silently deletes the already-built `xce_controller`/`xce_test`/etc — they're gone until something explicitly rebuilds them too. Two ways to get multiple binaries from the same dir together: run `/xce-build all` once instead of separate single-component invocations, **or** pass `--incremental` to a single-component invocation (`/xce-build xceadmin --incremental`) to reconfigure the existing dir in place (`meson setup --reconfigure`) instead of wiping it — preserves whatever else was already built there. `--incremental` is opt-in, not the default: this project's capnp-generation `custom_target` has documented, unfixed missing-dependency-edge races (buglog.json, "retry once before debugging") that a full wipe papers over as a loud, retry-fixable clean-build failure; skipping the wipe could instead let the same gap cause silent staleness (a schema edit not triggering regeneration, so the build reports success against stale generated code) — rebuild without the flag if anything looks wrong afterward. +**Consequence (bug-1565) and fix:** because step 2 wipes the whole build dir but step 3 only rebuilds the requested component, running `/xce-build xceadmin` (or any other single component) after `/xce-build controller` in the SAME build dir silently deletes the already-built binaries for that component (e.g. `xce_controller`, `controller_unit_test`) — they're gone until something explicitly rebuilds them too. Two ways to get multiple binaries from the same dir together: run `/xce-build all` once instead of separate single-component invocations, **or** pass `--incremental` to a single-component invocation (`/xce-build xceadmin --incremental`) to reconfigure the existing dir in place (`meson setup --reconfigure`) instead of wiping it — preserves whatever else was already built there. `--incremental` is opt-in, not the default: this project's capnp-generation `custom_target` has documented, unfixed missing-dependency-edge races (buglog.json, "retry once before debugging") that a full wipe papers over as a loud, retry-fixable clean-build failure; skipping the wipe could instead let the same gap cause silent staleness (a schema edit not triggering regeneration, so the build reports success against stale generated code) — rebuild without the flag if anything looks wrong afterward. **Three profiles, three build dirs** (`/xce-build [component] [profile]`, second arg optional): `asan` (default, debug+ASAN, `xce.build/`, runs the meson test suite) for everyday development and CI-style verification; `debug` (`-O0 -g3` no sanitizer, `build.dbg/`, build-only) for gdb/core-dump root-causing of heisenbugs that ASAN's allocator perturbs away; `release` (`-O3` no sanitizer, `build.release/`, build-only) for soak/perf runs against production-shaped binaries. Each profile's own dir means the three never clobber each other — a `debug` rebuild doesn't invalidate an `asan` binary already in use. @@ -44,6 +45,7 @@ src/ ├── meson.build # top-level: subdir() order, project() ├── lib/meson.build # libxce shared library + libxce_test ├── controller/meson.build # xce_controller daemon + controller_unit_test +├── harness/meson.build # xce_test driver + harness_unit_test + 4 standalone test binaries ├── worker/meson.build # xcecd daemon ├── scheduler/meson.build # scheduler daemon + test ├── autoscaler/meson.build # libscaler static lib + provider tools (no daemon binary) @@ -52,7 +54,7 @@ src/ └── xceadmin/meson.build # xceadmin config-build tool (used by /xce-deploy-ha) ``` -**Dependency flow**: `lib → {controller, worker, scheduler, apiserver}` +**Dependency flow**: `lib → {controller, harness, worker, scheduler, apiserver}` **Test registration**: ```python diff --git a/CLAUDE.md b/CLAUDE.md index c785eb36..09132af1 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -35,7 +35,7 @@ For details on any topic below, read the corresponding steering file in `.kiro/s - **Connections are long-lived**: closing signals RDHUP failover. Inbound connections are vote-only; outbound carry all state push - **Middleware chain** (non-negotiable): WhitelistURL → WhitelistIP → RateLimit → JWT → CORS → RBAC - **Error pattern**: `ReturnError(code, msg, nil); return` — early return on every error -- **Build**: `/xce-build [component]` — debug + ASAN + tests. See `build-deployment.md` for full commands +- **Build**: `/xce-build [component]` — debug + ASAN + tests. Components: `all` (default), `lib`, `controller`, `harness`, `scheduler`, `worker`, `apiserver`, `xcecli`, `xceadmin`. See `build-deployment.md` for full commands - **Save/restore field-completeness**: any persistence mechanism (WAL fact, scheduler snapshot, checkpoint) must capture every field of the entity it claims to persist — a state that "recovers correctly" while silently missing a field it depends on (e.g. `job.state` correct but `job.allocs` lost) is a bug, not an acceptable simplification, unless the omission is explicitly documented as a scope decision (see `persistence.md`'s "Snapshot Save/Load Field-Completeness" + `APPLY_CLEAN`/bug-1678 sections). When adding or extending save/restore logic, verify the field round-trips through BOTH the write side and every read/apply seam, and run `/xce-audit-invariants` (controller dimension 7 / scheduler dimension 4) rather than assuming a passing test suite already proves it — `APPLY_CLEAN`-style verdicts check row counts and cursor position, not per-field completeness ## Key Files @@ -49,6 +49,7 @@ For details on any topic below, read the corresponding steering file in `.kiro/s ## Subdirectory Guidelines - `src/frontend/` — See `src/frontend/CLAUDE.md` - `src/controller/` — See `src/controller/CLAUDE.md` +- `src/harness/` — See `src/harness/CLAUDE.md` - `src/worker/` — See `src/worker/CLAUDE.md` - `src/scheduler/` — See `src/scheduler/CLAUDE.md` - `src/lib/` — See `src/lib/CLAUDE.md`