diff --git a/.kiro/specs/eliminate-frontend-makefile/design.md b/.kiro/specs/eliminate-frontend-makefile/design.md new file mode 100644 index 00000000..6baa6b86 --- /dev/null +++ b/.kiro/specs/eliminate-frontend-makefile/design.md @@ -0,0 +1,232 @@ +# Design Document + +## Overview + +**Purpose**: Delete `src/frontend/Makefile` and consolidate apiserver/xcecli build and test execution onto Meson, eliminating a real, already-realized duplicate version-fingerprint-computation risk. + +**Users**: XCE developers building/testing apiserver and xcecli, and the project's own canonical build tooling (`xce-build.sh`, `/xce-build`) and dependent scripts (`run-resource-verify.sh`). + +**Impact**: `src/frontend/Makefile` (194 lines, 5 targets) is removed entirely. Its `build-cli`/`build-server` targets are already dead from `/xce-build`'s perspective (Meson already builds both binaries). Its `test` target's two live callers migrate to direct `go test` invocations. Its `test-backend` target's coverage moves into a genuine Meson `test()` registration. Its `test-integration` target's one line of logic (an env-var rename) is absorbed by direct invocation of the already-standalone `tests/regression/run.sh`. + +### Goals +- Exactly one build/test orchestration path for apiserver/xcecli exists after this lands. +- No CGO-backed backend test coverage is lost at any point during the migration. +- No script or document is left depending on a Makefile-specific fixed path or target. + +### Non-Goals +- Redesigning `apiserver/meson.build`'s existing CGO flag derivation (`apiserver_cgo_deps`) — reused as-is. +- Changing `xce-build.sh`'s version-fingerprint computation — the single surviving source of truth is untouched. +- Rewriting historical/frozen spec documents. +- Fixing `component-failover-adoption`'s unrelated `set -euo pipefail` CGO-test-skip issue. + +## Boundary Commitments + +### This Spec Owns +- Deletion of `src/frontend/Makefile`. +- The two `xce-build.sh` call sites that invoke `cd frontend && make test` (`apiserver` and `all` cases). +- A new Meson `test()` registration for the CGO-backed backend suite in `apiserver/meson.build`, reusing the existing `apiserver_cgo_deps` derivation. +- `run-resource-verify.sh`'s apiserver-binary discovery (currently the one script in the resource-verify skill still hardcoded to `/tmp/apiserver`; its siblings `BINARY`/`SCHED_BIN`/`XT` already resolve from the Meson build dir). +- The documented invocation convention for `tests/regression/run.sh` (unchanged script; only which binary path callers point `XCE` at). +- `deploy-ha.sh`'s three `make build-server`/`/tmp/apiserver`-referencing message strings (its binary-discovery logic itself is out of boundary — see below). +- Live-documentation updates: `tech.md`, `build-deployment.md`, `testing.md`, `structure.md`, `frontend/CLAUDE.md`, `src/docs/deployment.md`, `xce-deploy-ha/SKILL.md`, `src/frontend/docs/status.md`, `src/docs/apiserver-backend-tasks.md`, `xce-test-resources/SKILL.md`. +- `tests/regression/run.sh`'s own `XCE` binary-not-found error message (currently reproduces the deleted `build-cli` target's exact manual recipe). +- Two C testsuite files' comments (`test_ha_bootstrap.c`, `test_ha_ctrlhello.c`) pointing readers at `make test-integration` by name. +- The root `CLAUDE.md`'s "Key Files" entry naming `src/frontend/Makefile` as "primary build/test orchestration" — the single most-visible live reference in the repo (loaded into every session). +- Both Go modules' `cmd/main.go` ldflags-injection comments (`apiserver/cmd/main.go`, `xcecli/cmd/main.go`) naming the Makefile's `build-server`/`build-cli` targets alongside `src/tools/go_build` as where GitCommit/BuildTime injection happens. + +### Out of Boundary +- `apiserver/meson.build`'s CGO flag *values* — only the existing derivation is reused, not redesigned. +- `xce-build.sh`'s version-fingerprint computation (commit id / build time / dirty fingerprint) — unchanged, remains the single source of truth `go_build` already reads via meson options. +- `tools/go_build` itself — unchanged, reused as-is. +- `deploy-ha.sh`'s binary-discovery *logic* (the meson-first / `/tmp/apiserver`-fallback branching itself) — already migrated in a prior session; not re-touched here. Its three `make build-server`/`/tmp/apiserver`-referencing message strings ARE in boundary — see File Structure Plan. +- Historical/frozen spec documents (`tasks.md`/`research.md`/`design.md` of already-completed specs) referencing Makefile targets as acceptance criteria for shipped work. +- `component-failover-adoption`'s `set -euo pipefail` CGO-test-skip gap. +- Test *content* — this is a pure execution-path migration; no test assertions change. + +### Allowed Dependencies +- `apiserver/meson.build`'s existing `apiserver_cgo_deps` list and the top-level `src/meson.build` program objects (`go_compiler = find_program('go')`, `pkgconfig`) — read/reused, not modified. +- `xce-build.sh`'s existing `commit_id`/`build_time`/`dirty_fingerprint` meson options — read-only dependency, contract unchanged. +- `run-resource-verify.sh`'s own established `$BUILD_DIR//` discovery convention (already used for `BINARY`/`SCHED_BIN`/`XT`) — the apiserver fix follows this existing in-file pattern, not a new one. + +### Invariant Cross-Check +This is a pure build-tooling migration — no async I/O, HA/consensus, persistence, or driver/simulation state-equivalence surface is touched. `xce-audit-invariants`' controller/scheduler dimension lists do not apply; no invariant checklist was skipped. + +### Revalidation Triggers +- If `apiserver_cgo_deps` (the CGO dependency list in `apiserver/meson.build`) changes, both its consumers — the existing `custom_target` and the new backend `test()` — must be updated together; they now share one derivation, so this should no longer be possible to miss, but any refactor of that block should verify both call sites still compile/run. +- If `xce-build.sh`'s version-fingerprint env var contract (`XCE_GIT_COMMIT`/`XCE_BUILD_TIME`/`XCE_DIRTY_FINGERPRINT`) changes shape, `go_build` and every `custom_target` depending on it need re-verification — pre-existing contract, not introduced here, but worth flagging since Requirement 1 asserts it as the single source of truth. +- Any future script needing to discover a built apiserver/xcecli binary should follow the Meson-build-dir-first pattern established here and in `deploy-ha.sh`, not reintroduce a fixed legacy path. + +## Architecture + +### Existing Architecture Analysis +Two independent orchestration paths build the same two Go binaries today: +1. **Meson** (`apiserver/meson.build`, `xcecli/meson.build`) — via `tools/go_build`, already the path `/xce-build apiserver`/`/xce-build all` use for the actual binaries. +2. **`src/frontend/Makefile`** — a second, independently-maintained path. Its `build-cli`/`build-server` targets duplicate the version-fingerprint computation `xce-build.sh` already owns; only its `test` target has a live caller (`xce-build.sh`'s `apiserver`/`all` cases, 2 call sites, `cd frontend && make test`). `test-backend`/`test-integration` have no live caller today — they are developer-manual-only, and `build-server` is independently confirmed broken (bug-1597). + +The sibling `xcecli` case in `xce-build.sh` already bypasses Make entirely (`cd frontend/xcecli && CGO_ENABLED=0 go test ./... -count=1`) — this is the precedent the apiserver unit-test migration mirrors directly. + +### Architecture Pattern & Boundary Map + +```mermaid +flowchart TB + subgraph Before[Before] + XB1[xce-build.sh apiserver/all] --> MT1[make test] + MT1 --> UT1[apiserver + xcecli unit tests] + Dev1[Developer, manual] --> MTB[make test-backend] + MTB --> DerivA[Makefile CGO flag derivation] + Dev2[Developer, manual] --> MTI[make test-integration] + MTI --> BC[build-cli] + MTI --> RunSh1[tests/regression/run.sh] + RVS1[run-resource-verify.sh] --> TmpPath[fixed path /tmp/apiserver] + end + + subgraph After[After] + XB2[xce-build.sh apiserver/all] --> GT2[direct go test invocations] + GT2 --> UT2[apiserver + xcecli unit tests] + MesonTest[meson test] --> BackendTest[new backend test target] + BackendTest --> DerivB[apiserver_cgo_deps, shared] + Dev3[Developer, manual] --> RunSh2[tests/regression/run.sh] + RunSh2 --> XCEBin[XCE env pointed at meson build dir binary] + RVS2[run-resource-verify.sh] --> BuildDirPath[BUILD_DIR/frontend/apiserver/xce_apiserver] + CustomTarget[apiserver custom_target] --> DerivB + end +``` + +**Architecture Integration**: +- **Selected pattern**: Consolidation onto the existing Meson build graph; no new orchestration layer introduced. +- **Domain/feature boundaries**: build (Meson `custom_target`, unchanged) stays separate from test execution (Meson `test()` for the backend suite; direct shell/`go test` invocation for unit and integration tests) — matching the existing project convention where C tests are `meson test()` targets but the E2E regression suite (`tests/regression/run.sh`) is not. +- **Existing patterns preserved**: the `xcecli` case's direct-`go test`-invocation style (mirrored for apiserver's unit tests); the Meson `test()` registration pattern already used for controller/scheduler (`build-deployment.md`'s Test Registration example); `run-resource-verify.sh`'s own `$BUILD_DIR//` discovery convention (mirrored for the one binary — apiserver — not yet following it). +- **New components rationale**: exactly one new component — the backend-test `test()` registration — since no Meson-testable equivalent exists today for CGO backend coverage. Everything else is migration of an existing call site, not new architecture. +- **Steering compliance**: `build-deployment.md`'s "single hermetic build system" philosophy is the explicit target state this spec completes; no async-I/O, HA, or persistence invariants are touched (pure build-tooling change). + +### Technology Stack + +| Layer | Choice / Version | Role in Feature | Notes | +|-------|------------------|------------------|-------| +| Build orchestration | Meson ≥ 1.5 (existing) | Sole build/test path for apiserver, xcecli, and the new backend test target | No version change; reuses `go_compiler = find_program('go')` already declared in `src/meson.build` | +| Backend test execution | `go test` invoked directly by Meson `test()` | Runs `./backend/...` under real CGO linkage | No wrapper script — `test()` invokes the `go_compiler` program object directly with `args`/`env`/`workdir` | +| Shell tooling | Bash (existing) | `xce-build.sh` call-site migration, `run-resource-verify.sh` discovery fix | No new script introduced | + +## File Structure Plan + +### Modified Files +- `src/tools/xce-build.sh` — the `apiserver` case (~line 275) and `all` case (~line 322) replace `cd frontend && make test` with direct invocations: `cd frontend/apiserver && CGO_ENABLED=0 go test ./store/... ./utils/... ./controllers/... ./backend/... -count=1 && cd ../xcecli && CGO_ENABLED=0 go test ./... -count=1` — the exact same package set and flags `make test` runs today, just inlined, mirroring the `xcecli` case's own existing style at line 284. +- `src/frontend/apiserver/meson.build` — no refactor needed to compute the flags: `cgo_cflags`/`cgo_ldflags` are already file-scope Meson variables from the existing `apiserver_cgo_deps` foreach loop (lines 20-38). **They are not directly usable as-is**, though: lines 42-43 append literal `@CURRENT_SOURCE_DIR@`/`@BUILD_ROOT@` placeholder text, which today is resolved only by the existing `custom_target`'s two-step path (meson substitutes those tokens in the `command:` argument list at lines 57-58 into real `CURRENT_SOURCE_DIR`/`BUILD_ROOT` env vars, which `tools/go_build`'s own `fix_env`/`fix_input_envs` shell functions then sed-substitute into `$CGO_CFLAGS`/`$CGO_LDFLAGS` at runtime). The new test bypasses `go_build` entirely (see Non-Goals — `go_build` is hardcoded to a single `go build` invocation, not reusable for `go test`, and is out of boundary to modify), so it must resolve these tokens itself at Meson-configure time: `resolved_cgo_cflags = cgo_cflags.replace('@CURRENT_SOURCE_DIR@', meson.current_source_dir()).replace('@BUILD_ROOT@', meson.project_build_root())` (same pattern for `cgo_ldflags`) — Meson's own `.replace()` string method, evaluated once when `meson.build` is processed, no shell/sed step needed. Add a new `test('apiserver backend', go_compiler, args: ['test', './backend/...', '-count=1'], workdir: meson.current_source_dir(), env: {'CGO_ENABLED': '1', 'CGO_CFLAGS': resolved_cgo_cflags, 'CGO_LDFLAGS': resolved_cgo_ldflags, 'GOFLAGS': '-mod=vendor', 'GOPROXY': 'off', 'GOCACHE': meson.current_build_dir() / 'go-cache', 'GOPATH': meson.current_build_dir() / 'go-path'}, suite: 'apiserver', depends: [libxce])` — dict keys are quoted string literals (Meson dict-literal syntax requires this; an unquoted key parses as a variable reference and fails configure) — `GOFLAGS`/`GOPROXY` mirror `tools/go_build`'s own offline/hermetic conventions (confirmed `apiserver/vendor/` is committed). `GOCACHE`/`GOPATH` are likewise set explicitly (pinned under `meson.current_build_dir()`, not ambient `$TMPDIR`) since, same as the CGO flags, this test gets none of `go_build`'s internal exports for free. +- `.claude/skills/xce-test-resources/run-resource-verify.sh` — line 203: `APISERVER_BIN` changes from `"${XCE_RESVERIFY_APISERVER_BIN:-/tmp/apiserver}"` to `"${XCE_RESVERIFY_APISERVER_BIN:-$BUILD_DIR/frontend/apiserver/xce_apiserver}"`, matching the file's own existing `BINARY`/`SCHED_BIN`/`XT` convention exactly (no dual-path fallback needed — none of its siblings have one, and `/tmp/apiserver` is never populated by anything once the Makefile is gone). Line 350's failure message updates from `"cd src/frontend && make build-server, or the CGO-flags workaround"` to `"run /xce-build apiserver"`, matching the sibling messages' phrasing. +**Scope rule for every file below** (this rule, not any individual line number, is the actual acceptance boundary — three successive review rounds each found a live `make`/fixed-path reference the previous enumeration missed in an already-in-scope file, so the line numbers below are a verified starting inventory, not the boundary itself): update **every** live, current-fact reference to a `src/frontend/Makefile` target (`build-cli`/`build-server`/`test`/`test-backend`/`test-integration`/`test-all`) or a Makefile-specific fixed path (`/tmp/apiserver`, `/tmp/xce_static`) in each named file — including ones in a different section, bullet, or comment block than the ones enumerated here. The Testing Strategy's "Live-reference sweep" is the authoritative completeness check; a task implementing one of these files is not done until that sweep, scoped to that one file, comes back clean. + +- `.kiro/steering/tech.md` — the Build Systems table's `Make` row and its `make build-server`/`make build-cli`/`make test`/`make test-integration` code block; **also** lines 81-82 under "Testing Requirements" (`` `make test` must pass without a live server. `` / `` `make test-integration` requires... ``), found only in review round 3 — same file, different section. +- `.kiro/steering/build-deployment.md` — the "Go Frontend Build" section's `make` command block, replaced with the direct invocations this design introduces (unit tests via `xce-build.sh`, backend tests via `meson test --suite apiserver`, integration tests via direct `tests/regression/run.sh` invocation with `XCE`/`URL`). +- `.kiro/steering/testing.md` — the three `make test*` bullet points under "Build targets"; the Philosophy line ("A change is not complete until `make test`... passes"); **also** line 95 under "What Not to Mock" (`` CGO bridge (`backend/`) is covered by `make test-backend`... ``), found only in review round 3. +- `.kiro/steering/structure.md` — the line referencing `make test`/`make test-all`. +- `src/frontend/CLAUDE.md` — the "Build & Test" code block's four `make` commands. +- `src/docs/deployment.md` — the "Build gotcha" section (lines 243-247), already steering developers away from `make build-server`; reword now that the target no longer exists at all (from "avoid this, use X instead" to "this Makefile no longer exists, use X"). +- `.claude/skills/xce-deploy-ha/SKILL.md` — references at lines 40, 165, 453 to `make build-server`/`/tmp/apiserver` as a manual pre-step or troubleshooting hint; **also** the Step 4d intro (~line 294, "a pre-built apiserver binary exists at `/tmp/apiserver`"), found only in review round 3 — update all to `/xce-build apiserver` and the Meson build-dir path. +- `src/tools/deploy-ha.sh` — the two `echo` strings in its `/tmp/apiserver` fallback branch (~162, 164) referencing `make build-server`; **also** the preceding block comment (~lines 138-155, "The apiserver is buildable TWO ways... (2) the standalone Go workflow (`cd src/frontend && make build-server`)..."), found only in review round 3 — same defect, one file over. The meson-first/`/tmp/apiserver`-fallback branching *logic* itself is unchanged (out of boundary — already correct); only text describing a now-deleted second build path changes. +- `src/frontend/docs/status.md` (line 67) — a status checklist line stating `Root Makefile: make test, make test-integration, make test-all` as current infrastructure; update to reflect Meson-only tooling. +- `src/docs/apiserver-backend-tasks.md` (lines ~70, ~120) — two `make test` references presented as current fact, not as a frozen spec record (this file lives outside `.kiro/specs/`, so Requirement 6.3's historical-spec exemption does not apply). +- `.claude/skills/xce-test-resources/SKILL.md` (line 108) — documents `$XCE_RESVERIFY_APISERVER_BIN`'s default as `/tmp/apiserver`; update alongside the script's own default change (5.1's discovery fix and its documentation must not disagree). +- `src/controller/testsuite/test_ha_bootstrap.c` (comments at ~lines 290, 338, 388) and `test_ha_ctrlhello.c` (comment at ~line 13) — each points a reader to "`make test-integration`" as where certain HA retry/tag/call-chain behavior is validated end-to-end. A 3-line rename to reference the regression suite directly (`tests/regression/run.sh`) rather than the deleted target; no test logic changes. +- Root `CLAUDE.md` (line 42) — the "Key Files" entry `` `src/frontend/Makefile` — primary build/test orchestration `` is deleted or replaced with the Meson-based equivalent (e.g. `src/tools/xce-build.sh` — already listed elsewhere in the same file's Build Commands). +- `src/frontend/apiserver/cmd/main.go` (~lines 25-27) and `src/frontend/xcecli/cmd/main.go` (~lines 39-41) — each has a comment reading "injected at link time via `-ldflags`... (see `src/tools/go_build` and `src/frontend/Makefile`'s `build-server`/`build-cli` target)"; drop the Makefile clause, keeping only `src/tools/go_build`. +- `src/frontend/tests/regression/run.sh` (line 69) — the `XCE` binary-not-found error currently says `"Build with: cd xcecli && CGO_ENABLED=0 go build -o /tmp/xce_static ./cmd/main.go"`, reproducing the deleted `build-cli` target's exact manual recipe (5.2's own literal example of what must not remain). Update to name `/xce-build apiserver` (which also builds `xcecli`) instead. The `XCE="${XCE:-/tmp/xce_static}"` default (line 12) is left as an inert placeholder — callers are still expected to pass `XCE=` explicitly (testing.md already documents this as a required env var), so only the error-message *content* needs to stop pointing at a deleted target. + +### Deleted Files +- `src/frontend/Makefile` — removed only after the above are landed and verified (see Migration Strategy). + +### Unchanged (verified, not modified) +- `src/frontend/tests/regression/run.sh`'s core logic (still a standalone, parameterized script driven by `XCE`/`URL` env vars) — only its not-found error message changes, per above. The Makefile's `test-integration` target added no logic beyond renaming `XCE_BIN`→`XCE`/`SRV_URL`→`URL`; once removed, callers invoke `run.sh` directly with those two env vars, pointed at a Meson-built `xcecli` binary (`/frontend/xcecli/xcecli`) instead of the Makefile's `build-cli` output. +- `src/tools/go_build`, `apiserver/meson.build`'s existing `custom_target`, `xcecli/meson.build` — unchanged. + +## System Flows + +The Before/After flow is fully captured in the Architecture diagram above; no additional sequence diagram is needed — this is a call-site substitution, not a new multi-party interaction. + +## Requirements Traceability + +| Requirement | Summary | Components | Flows | +|-------------|---------|------------|-------| +| 1.1, 1.2, 1.3 | Single build path; one fingerprint source; Makefile deleted once 2-4 covered | `xce-build.sh` (build already Meson-only), Makefile deletion | Migration Strategy Phase 4 | +| 2.1, 2.2, 2.3 | Unit tests run without Makefile, same behavior, clear failure reporting | `xce-build.sh` direct `go test` call sites | Migration Strategy Phase 2 | +| 3.1, 3.2, 3.3 | CGO backend suite runnable without Makefile, single CGO derivation, no coverage regression | `apiserver/meson.build` new `test()` | Migration Strategy Phase 1 | +| 4.1, 4.2, 4.3 | Integration suite targets canonical-build binary, same scope/pass-fail criteria | `tests/regression/run.sh` invocation convention (unchanged script) | Migration Strategy Phase 3 | +| 5.1, 5.2, 5.3 | Binary discovery from canonical build dir; clear error naming the surviving command; no sole dependency on fixed legacy path | `run-resource-verify.sh` `APISERVER_BIN` fix, `deploy-ha.sh` message strings | Migration Strategy Phase 3 | +| 6.1, 6.2, 6.3 | Live docs reference only the surviving system; historical docs untouched | 11 doc files (plus 2 C testsuite and 2 Go source comment fixes) listed in File Structure Plan | Migration Strategy Phase 3 | + +## Components and Interfaces + +| Component | Domain/Layer | Intent | Req Coverage | Key Dependencies (P0/P1) | Contracts | +|-----------|--------------|--------|---------------|---------------------------|-----------| +| `xce-build.sh` unit-test invocation | Build tooling | Run apiserver/xcecli unit tests without Make | 2.1, 2.2, 2.3 | `go` toolchain (P0) | Batch | +| `apiserver/meson.build` backend test() | Build tooling | Run CGO backend suite via Meson, reusing existing CGO derivation | 3.1, 3.2, 3.3, 1.2 | `apiserver_cgo_deps` (P0), `go_compiler` (P0), `libxce` (P0) | Batch | +| `run-resource-verify.sh` binary discovery | Verification tooling | Locate the apiserver binary from the canonical build dir | 5.1, 5.2, 5.3 | Meson build dir layout (P0) | Batch | +| `tests/regression/run.sh` invocation convention | Verification tooling | Run the E2E regression suite against a canonical-build binary | 4.1, 4.2, 4.3 | Meson-built `xcecli` binary (P0), live apiserver (P0) | Batch | +| `deploy-ha.sh` message strings | Verification tooling | Stop naming a deleted Makefile target in fallback/warning text | 5.2, 5.3, 6.2 | — | — | +| Documentation set (11 files) | Docs | Reference only the surviving build system | 6.1, 6.2, 6.3 | — | — | + +Only the backend `test()` registration introduces a genuinely new boundary (a new Meson test target with its own env/dependency contract); the rest are call-site substitutions in already-existing scripts, so they are covered by the summary table plus the File Structure Plan detail above rather than full component blocks. + +### Build Tooling + +#### apiserver backend test() target + +| Field | Detail | +|-------|--------| +| Intent | Run the CGO-backed `apiserver/backend/...` test suite under Meson, with zero independent CGO-flag derivation | +| Requirements | 3.1, 3.2, 3.3, 1.2 | + +**Responsibilities & Constraints** +- Owns: registering the test with Meson's test runner (`suite: 'apiserver'`) so `meson test -C --suite apiserver` (and full `meson test`) picks it up. +- Does not own: the CGO flag values themselves — reads `apiserver_cgo_deps`'s already-computed `cgo_cflags`/`cgo_ldflags`, the same values the existing `custom_target('apiserver', ...)` in the same file already uses. +- Must not add `-race` — confirmed unusable in this project's ASAN-linked CGO environment (`component-failover-adoption/tasks.md:197`, hit 3 times independently). + +**Dependencies** +- Inbound: `meson test` / `/xce-build apiserver`'s test-running step (P0) +- Outbound: `apiserver_cgo_deps` derivation, `libxce` build target (P0 — must build before this test can link) +- External: `go` toolchain via `go_compiler` program object (P0) + +**Contracts**: Batch [x] + +##### Batch / Job Contract +- Trigger: `meson test` (any invocation covering the `apiserver` suite, including bare `meson test -C `) +- Input / validation: requires `libxce.a` and its generated headers already built in the same build dir (`depends: [libxce]` enforces build ordering) +- Output / destination: standard Meson test log; non-zero exit on any `go test` failure +- Idempotency & recovery: `go test -count=1` (no test caching across runs, matching every other Go test invocation in this codebase) + +**Implementation Notes** +- Integration: env additionally sets `GOFLAGS=-mod=vendor`, `GOPROXY=off`, and writable `GOCACHE`/`GOPATH` under the build dir's own tmp space — mirroring `tools/go_build`'s existing hermetic/offline conventions, since this is the first place `apiserver/backend`'s real CGO path is exercised from inside the Meson/Nix sandbox rather than a developer's ambient shell. +- Validation: confirm `meson test -C --suite apiserver` (both standalone and as part of `/xce-build apiserver`'s existing test step) reports this new test, and that it fails for the expected reason if `apiserver_cgo_deps` is temporarily broken (proving it isn't silently skipped). +- Risks: this is the one genuinely new capability in the migration — its correctness (does it actually build+run `backend/clib.go`'s real CGO path, not the `!cgo` stub) is the primary thing Testing Strategy below must prove before Migration Strategy Phase 4 (Makefile deletion) is safe. + +## Testing Strategy + +- **Unit-level (invocation correctness)**: after migrating `xce-build.sh`'s two call sites, confirm `/xce-build apiserver` and `/xce-build all` still run exactly the same apiserver/xcecli test package set (`./store/... ./utils/... ./controllers/... ./backend/...` for apiserver, `./...` for xcecli) with the same pass/fail counts as the pre-migration `make test`-driven baseline. +- **New capability verification**: run `meson test -C --suite apiserver` (and full `/xce-build apiserver`) and confirm the new backend test target appears, links against real `libxce` (not the `//go:build !cgo` stub), and passes with no compiler/linker error referencing a literal `@CURRENT_SOURCE_DIR@`/`@BUILD_ROOT@` string (the specific failure mode of an unresolved placeholder token reaching `go test`); then deliberately break `apiserver_cgo_deps` (e.g. comment out `librtti_dep`) and confirm the new test fails for the expected linker/undefined-symbol reason — proving it exercises the real CGO path, not a vacuous pass. +- **Discovery-path regression**: with `src/frontend/Makefile` deleted, run `run-resource-verify.sh` end-to-end and confirm `APISERVER_BIN` resolves to the Meson build dir and the apiserver layer is reachable; confirm the failure message (binary artificially removed) names `/xce-build apiserver`, not a deleted Makefile target. +- **E2E regression parity**: run `tests/regression/run.sh` directly (`XCE= URL= bash tests/regression/run.sh`) against a live apiserver and confirm the same PASS/FAIL/SKIP counts as the pre-migration `make test-integration` baseline. +- **Live-reference sweep**: run BEFORE Phase 4 (Makefile deletion), scoped to each file named in File Structure Plan, and again as a full-repository pass after — grep for `make (build-server|build-cli|test|test-backend|test-integration|test-all)` AND `/tmp/apiserver`/`/tmp/xce_static`. Running it per-file before deletion (not only repo-wide after) is what makes the File Structure Plan's "every live reference, not just the enumerated ones" scope rule actually enforceable, rather than relying on the enumeration being exhaustive. The full-repository pass will legitimately still hit files that are not violations — name these exclusion classes explicitly rather than leaving "zero hits" as an unachievable, ambiguous bar an implementer would otherwise have to rediscover by judgment call: + - `.kiro/specs/**` — historical spec documents (Requirement 6.3's own exemption), including this spec's own `brief.md`/`research.md`/`requirements.md`/`design.md`, which necessarily discuss the very targets being removed. + - `**/vendor/**` — third-party vendored dependency files (e.g. `go.uber.org/zap`'s own `CONTRIBUTING.md`) whose `make test` refers to that upstream project, not XCE's. + - Dated historical records: `CHANGES.md`, `reports/*.md`, `docs/superpowers/plans/*.md`, `src/docs/archive/**` — frozen snapshots of past state, not live guidance. + - `.kiro/steering/roadmap.md`'s entry for this very spec (~line 82) — describes this spec's own backlog/discovery history, not a live instruction. + - `.wolf/memory.md`, `.wolf/cerebrum.md`, `.wolf/buglog.json` — OpenWolf's own append-only session/bug logs, not authoritative project documentation. + - `src/frontend/tests/regression/run.sh:12` — the `XCE="${XCE:-/tmp/xce_static}"` default is a deliberately-retained inert placeholder (see File Structure Plan); only its *error-message content* (line 69) is required to change, not this default value itself. + + Any hit NOT falling into one of the above classes is a real gap and must be fixed before Phase 4. + +## Migration Strategy + +Phased, coverage-preserving order — the CGO backend test gap (Phase 1) must close before the Makefile can be safely removed (Phase 4), matching Requirement 3.3's explicit non-regression constraint. + +```mermaid +flowchart LR + P1[Phase 1: add Meson backend test target] --> P3[Phase 3: fix run-resource-verify.sh discovery + update docs + document run.sh invocation] + P2[Phase 2: migrate xce-build.sh unit-test call sites] --> P3 + P3 --> P4[Phase 4: delete src/frontend/Makefile] +``` + +- **Phase 1** (Requirement 3) and **Phase 2** (Requirement 2) have no dependency on each other — different files, no shared state — and may be implemented and landed in parallel. Phase 1 is net-new capability, zero risk to existing behavior. Phase 2 mirrors the already-proven `xcecli` pattern; low risk. +- **Phase 3** (Requirements 4, 5, 6): depends on both Phase 1 and Phase 2, since the documentation this phase writes describes the final Makefile-free state of both the build and the backend test target. Mechanical once Phases 1-2 land — the real reference list and exact line numbers are already enumerated in File Structure Plan. +- **Phase 4** (Requirement 1): the only phase that is hard to reverse cheaply — gated on Phases 1-3 having landed and their own verification (Testing Strategy above) having passed. +- **Rollback trigger**: if Phase 1's backend test reveals `apiserver_cgo_deps` cannot be cleanly shared between the `custom_target` and the new `test()` (e.g. a Meson version limitation on reusing computed variables across targets in the same file), stop before Phase 4 and re-evaluate Requirement 3.2 rather than forcing a second, duplicate CGO-flag derivation into existence — that would defeat this spec's own purpose. +- **Validation checkpoint**: run the full Testing Strategy section after Phase 3, before executing Phase 4. diff --git a/.kiro/specs/eliminate-frontend-makefile/requirements.md b/.kiro/specs/eliminate-frontend-makefile/requirements.md new file mode 100644 index 00000000..5a4c11f9 --- /dev/null +++ b/.kiro/specs/eliminate-frontend-makefile/requirements.md @@ -0,0 +1,63 @@ +# Requirements Document + +## Introduction + +`src/frontend/Makefile` and Meson's `tools/go_build` wrapper are two independent, parallel build orchestration paths for the same two Go binaries (apiserver, xcecli), each independently computing the same version-fingerprint logic (`commit_id`/`build_time`/`dirty_fingerprint`). This duplication has already caused a real defect (a shell exit-status bug in the Makefile's independent fingerprint computation, `dirty-content-fingerprint` task 4.3) and the Makefile's own `build-server` target is independently confirmed currently broken (bug-1597) — the project's own documentation already tells developers to avoid it in favor of `/xce-build apiserver`. + +This feature removes `src/frontend/Makefile` entirely and consolidates all apiserver/xcecli build and test execution onto Meson, so exactly one build system exists for these components going forward. This is a confirmed decision, not an option to weigh during design: Meson is retained, the Makefile is deleted outright, not kept as a thin or compatibility wrapper. + +## Boundary Context + +- **In scope**: removing `src/frontend/Makefile`; ensuring apiserver/xcecli builds, unit tests, CGO-backed backend tests, and the integration/regression test suite are all runnable through Meson or direct, documented, Makefile-free invocations; updating any project script or documentation that discovers a built binary via a Makefile-specific fixed path or instructs a developer to run a Makefile target. +- **Out of scope**: changing the underlying version-fingerprint computation itself (the single surviving source of truth is unchanged, only the duplicate second computation is removed); changing the CGO build-flag derivation Meson's apiserver build already performs (this feature reuses it, it does not redesign it); rewriting historical/frozen spec documents (e.g. `tasks.md`/`research.md`/`design.md` of already-completed specs) that reference Makefile targets as acceptance criteria for work already shipped; fixing unrelated, adjacent build-tooling defects noted during investigation (e.g. `component-failover-adoption`'s `set -euo pipefail` CGO-test-skip issue). +- **Adjacent expectations**: this feature depends on Meson's existing apiserver build already deriving correct CGO flags — that derivation is reused, not duplicated, for backend test execution. Downstream scripts that discover apiserver/xcecli binaries (e.g. HA deploy scripts, resource-verification scripts) must continue to find a usable binary after this change; none of them may be left depending on a Makefile-specific fixed output path once the Makefile is gone. + +## Requirements + +### Requirement 1: Single Build Path for Go Binaries +**Objective:** As a developer, I want apiserver and xcecli to be built through exactly one system, so that build logic and version-fingerprint computation cannot silently drift out of sync between two independent implementations. + +#### Acceptance Criteria +1. When a developer builds apiserver or xcecli through the project's canonical build tooling, the system shall produce the binary without invoking any `src/frontend/Makefile` target. +2. The system shall compute each binary's version-fingerprint information (commit id, build time, dirty-content fingerprint) from exactly one implementation, not from two independently maintained computations. +3. The system shall not include a `src/frontend/Makefile` file once the build, unit-test, backend-test, and integration-test capabilities described in Requirements 1-4 are all available without it. + +### Requirement 2: Go Unit Test Execution Without the Makefile +**Objective:** As a developer, I want apiserver/xcecli unit tests runnable without `src/frontend/Makefile`, so that test execution is not split across two orchestration systems. + +#### Acceptance Criteria +1. When the project's canonical build tooling runs apiserver or xcecli unit tests, the system shall do so without invoking `src/frontend/Makefile`'s `test` target. +2. The system shall preserve existing unit-test behavior (no external dependencies, no live server required) after this change. +3. If a unit test fails, then the system shall report the failure in a form at least as clear and actionable as the current Makefile-driven output (test name, failure reason). + +### Requirement 3: CGO-Backed Backend Test Coverage Preserved +**Objective:** As a developer, I want the CGO-backed backend test suite runnable without `src/frontend/Makefile`, so that removing the Makefile does not silently drop test coverage. + +#### Acceptance Criteria +1. The system shall provide a way to run the full CGO-backed backend test suite that does not depend on `src/frontend/Makefile`. +2. When the CGO-backed backend test suite runs, the system shall derive its CGO build flags from the same single source Meson's apiserver build already uses, not from a second, independently maintained derivation. +3. The system shall not reduce CGO-backed backend test coverage at any point during this migration — an equivalent, Makefile-free way to run this suite shall exist before `src/frontend/Makefile`'s `test-backend` target is removed. + +### Requirement 4: Integration/Regression Test Suite Targets a Canonical Build +**Objective:** As a developer, I want to run the apiserver integration/regression test suite against a binary built by the project's canonical tooling, so that verification does not require a Makefile-built artifact. + +#### Acceptance Criteria +1. When a developer runs the integration/regression test suite, the system shall accept a binary built by the project's canonical (Makefile-free) build tooling as the target under test. +2. The system shall not require `src/frontend/Makefile`'s `build-cli` or `build-server` targets to produce the binary under test. +3. The system shall preserve the existing regression suite's scope and pass/fail criteria after this change. + +### Requirement 5: Binary Discovery Independent of Makefile-Specific Paths +**Objective:** As an operator or script maintainer, I want project scripts that locate a built apiserver or xcecli binary to do so without depending on a Makefile-specific fixed output path, so that removing the Makefile does not break deployment or verification tooling. + +#### Acceptance Criteria +1. When a project script needs to locate a built apiserver or xcecli binary, the system shall discover it from the canonical build tooling's output location as the primary source. +2. If no binary is found at the canonical build tooling's output location, then the system shall report an error or warning that names the canonical (Makefile-free) build command to run, and shall not instruct the operator to run a `src/frontend/Makefile` target that no longer exists. +3. The system shall not require any project script to depend on a `src/frontend/Makefile`-specific fixed output path (e.g. `/tmp/apiserver`, `/tmp/xce_static`) as its only means of locating a built binary. + +### Requirement 6: Documentation Reflects a Single Build System +**Objective:** As a developer or operator reading project documentation, I want build and test instructions to reference only the surviving build system, so that I am never directed to run a target that no longer exists. + +#### Acceptance Criteria +1. When live (non-historical) project documentation describes how to build or test apiserver/xcecli, the system's documentation shall reference only the canonical Makefile-free build/test commands. +2. The system shall not retain any live documentation instructing a developer to run a `src/frontend/Makefile` target. +3. Where a historical, already-completed spec document references a Makefile target as a frozen record of past acceptance criteria, the system shall leave that document unchanged — this requirement applies only to live, currently-authoritative documentation. diff --git a/.kiro/specs/eliminate-frontend-makefile/research.md b/.kiro/specs/eliminate-frontend-makefile/research.md new file mode 100644 index 00000000..fcd853db --- /dev/null +++ b/.kiro/specs/eliminate-frontend-makefile/research.md @@ -0,0 +1,84 @@ +## Summary +- **Feature**: `eliminate-frontend-makefile` +- **Discovery Scope**: Extension (existing build system, light discovery — brief.md's own Path C investigation already covered most of the ground; this pass verified currency and filled architecture-level gaps) +- **Key Findings**: + - Only one live automated caller of `src/frontend/Makefile` exists today: `xce-build.sh`'s two `cd frontend && make test` call sites (`apiserver` and `all` cases). `build-cli`/`build-server`/`test-backend`/`test-integration` have no live automated caller — they are developer-manual-only. + - `apiserver/meson.build` already computes the exact CGO flags (`apiserver_cgo_deps`: librtti, c-capnproto, openssl, tinylog, math, rt) the Makefile's `test-backend` independently re-derives via pkg-config. A shared derivation is directly reusable for a new `test()` target — no new mechanism needed. + - `xcecli`'s case in `xce-build.sh` already bypasses Make entirely (direct `go test` invocation) — this is a live, working precedent for the apiserver unit-test migration, not a hypothetical pattern. + - `run-resource-verify.sh` already has an established `$BUILD_DIR//` discovery convention for its other three binaries (`BINARY`, `SCHED_BIN`, `XT`); only `APISERVER_BIN` is the odd one out still hardcoded to `/tmp/apiserver`. + - `tests/regression/run.sh` is already a standalone, fully-parameterized script (`XCE`/`URL` env vars). The Makefile's `test-integration` target added zero logic beyond an env-var rename (`XCE_BIN`→`XCE`, `SRV_URL`→`URL`) plus a `build-cli` prerequisite. No new script is needed for Requirement 4 — only documentation of the direct invocation. + +## Research Log + +### Live automation dependency surface +- **Context**: requirements.md's Boundary Context needed to state precisely what currently depends on the Makefile, to scope the coverage-preservation requirements accurately. +- **Sources Consulted**: `grep -rn --include="*.sh" -E '\bmake\b' src/tools/ .claude/skills/`, direct read of `xce-build.sh` lines 255-328. +- **Findings**: Exactly 2 live `make` invocations exist in the whole repo's live automation (`xce-build.sh:275`, `:322`, both `cd frontend && make test`). `deploy-ha.sh` and `run-resource-verify.sh` reference the Makefile only in human-facing strings (error/log messages) or by checking for its *output path*, never by executing `make`. +- **Implications**: The coverage-preservation requirements (2, 3, 4) protect manual developer workflows and documentation accuracy, not any existing automated pipeline — this made scope smaller and safer than it might first appear, and confirmed as a direct answer to a user clarifying question during the requirements phase. + +### CGO backend test flag derivation reuse +- **Context**: requirements.md 3.2 requires the new backend test path to derive CGO flags from "the same single source" as Meson's existing apiserver build, not a second independent derivation (the exact failure mode this whole spec exists to eliminate). +- **Sources Consulted**: `apiserver/meson.build` (full read), `src/meson.build` (`go_compiler = find_program('go')`, `pkgconfig = find_program('pkg-config')`), `src/frontend/Makefile`'s own `test-backend` recipe (`FRONTEND_CGO_PKGS := librtti c-capnproto openssl tinylog`, `-lm -lrt`). +- **Findings**: `apiserver/meson.build`'s `apiserver_cgo_deps` loop already produces `cgo_cflags`/`cgo_ldflags` covering the identical dependency set the Makefile independently re-derives via pkg-config. `go_compiler` (the raw `go` program object) is already available at top-level scope — no new `find_program()` call needed. +- **Implications**: A new `test()` in the same file can read the same `cgo_cflags`/`cgo_ldflags` variables the existing `custom_target` already computes — genuinely one derivation, not two, satisfying 3.2 directly rather than by convention/discipline. + +### `run-resource-verify.sh`'s own established discovery pattern +- **Context**: requirements.md 5.1-5.3 needed a concrete replacement for `run-resource-verify.sh`'s `/tmp/apiserver` default. +- **Sources Consulted**: full grep of `_BIN=`/`BUILD_DIR` in `run-resource-verify.sh`. +- **Findings**: `BINARY`, `SCHED_BIN`, `XT` already resolve as `"${ENV_VAR_OVERRIDE:-$BUILD_DIR//}"`, each independently overridable, with `$BUILD_DIR` defaulting to `$REPO/src/xce.build`. Only `APISERVER_BIN` deviates, hardcoded to `/tmp/apiserver` with no `$BUILD_DIR`-relative default at all. +- **Implications**: the correct fix is not "mirror `deploy-ha.sh`'s dual-path pattern" as brief.md's Downstream section literally suggested, but "match this script's own sibling convention exactly" — see Design Decision below. + +## Architecture Pattern Evaluation + +| Option | Description | Strengths | Risks / Limitations | Notes | +|--------|-------------|-----------|----------------------|-------| +| Meson `test()` reusing `apiserver_cgo_deps` | New backend test target shares the existing `custom_target`'s CGO derivation | Single source of truth (satisfies 3.2 directly); no wrapper script | Requires the derivation to be readable by both a `custom_target` and a `test()` in the same `meson.build` — confirmed straightforward, both are just Meson variables in scope | **Selected** | +| Standalone shell script re-deriving CGO flags via pkg-config | Keep a Makefile-free script mirroring the Makefile's own `test-backend` pkg-config derivation | Avoids touching `apiserver/meson.build` | Reintroduces exactly the duplicate-derivation risk this spec exists to eliminate | Rejected (brief.md's own "not recommended" option 2) | + +## Design Decisions + +### Decision: `run-resource-verify.sh`'s `APISERVER_BIN` follows its own sibling convention, not `deploy-ha.sh`'s dual-path fallback +- **Context**: brief.md's Downstream section suggested mirroring `deploy-ha.sh`'s meson-first / `/tmp/apiserver`-fallback pattern. +- **Alternatives Considered**: + 1. Mirror `deploy-ha.sh`'s dual-path pattern (meson path first, `/tmp/apiserver` fallback). + 2. Match `run-resource-verify.sh`'s own existing `BINARY`/`SCHED_BIN`/`XT` convention (single `$BUILD_DIR`-relative default, no fallback). +- **Selected Approach**: Option 2. +- **Rationale**: once `src/frontend/Makefile` is deleted, nothing in the project's own tooling will ever populate `/tmp/apiserver` again — a fallback to it is a fallback to a path only a developer's own hand-run `go build` could produce, which is not a real safety net worth the inconsistency of being the only binary in this script with different default-resolution logic than its three siblings. +- **Trade-offs**: slightly diverges from the brief's literal suggestion, but produces a more internally consistent script and satisfies requirements.md 5.3 ("shall not require... as its only means") without needing a fallback at all, since the primary path is now proven reliable (Meson always builds apiserver via `/xce-build apiserver`/`all`). +- **Follow-up**: `deploy-ha.sh`'s own `/tmp/apiserver` fallback becomes similarly vestigial once the Makefile is gone — out of boundary for this spec (already migrated in a prior session per brief.md), but worth flagging as a small future cleanup, not a blocker here. + +### Decision: No wrapper script for the new backend test target +- **Context**: Meson `test()` needs an executable; `go test` with CGO env and a specific working directory could be wrapped in a small shell script for clarity. +- **Alternatives Considered**: + 1. A thin wrapper shell script invoking `go test` with the right env, registered as the `test()`'s executable. + 2. `test()` invokes the `go_compiler` program object directly, with `args`, `env`, and `workdir` kwargs. +- **Selected Approach**: Option 2. +- **Rationale**: Meson's `test()` natively supports `workdir` and `env`; no wrapper is needed, and one fewer file avoids exactly the kind of incidental indirection this spec exists to remove elsewhere. +- **Trade-offs**: revised after an independent adversarial review round (2026-09-03) caught a real one: `cgo_cflags`/`cgo_ldflags` contain literal `@CURRENT_SOURCE_DIR@`/`@BUILD_ROOT@` placeholder text that is normally resolved by `tools/go_build`'s own `fix_env` sed machinery — bypassing `go_build` means bypassing that resolution too. Fixed by resolving both tokens directly with Meson's own `.replace()` string method at configure time (`cgo_cflags.replace('@CURRENT_SOURCE_DIR@', meson.current_source_dir()).replace('@BUILD_ROOT@', meson.project_build_root())`) rather than reintroducing a wrapper script — still net simpler than Option 1, but not the zero-cost choice the first design pass assumed. + +### Decision: `deploy-ha.sh`'s dead-target message strings are in boundary even though its discovery logic is not +- **Context**: the first design draft excluded `deploy-ha.sh` entirely as "already migrated in a prior session," reasoning from the binary-discovery *logic* (genuinely already meson-first). An independent adversarial review round caught that its three `make build-server`/`/tmp/apiserver`-referencing `echo` strings (lines ~157-164) were not part of that prior migration and would still tell a developer to run a target that no longer exists, once `src/frontend/Makefile` is deleted. +- **Selected Approach**: split the file's two concerns — discovery logic (out of boundary, unchanged) vs. message text (in boundary, added to File Structure Plan) — rather than treating "already touched once" as "fully out of scope." +- **Follow-up**: none needed beyond the File Structure Plan entry; this is a 2-string text edit, not a logic change. + +### Decision: `status.md` and `apiserver-backend-tasks.md` are live documentation, not historical spec records +- **Context**: requirements.md 6.3 exempts historical, already-completed *spec* documents from doc updates. An independent adversarial review round found two files outside `.kiro/specs/` (`src/frontend/docs/status.md:67`, `src/docs/apiserver-backend-tasks.md:70,120`) stating `make test`/`make test-integration` as current fact — missed by the original brief's doc inventory, which only enumerated files already known to be live-authoritative. +- **Selected Approach**: treat both as live documentation in scope for update, since 6.3's exemption is worded specifically around ".kiro/specs" historical records, not arbitrary status/changelog files elsewhere in the repo. +- **Follow-up**: added both to File Structure Plan. + +### Decision: doc-file File Structure Plan entries state a scope rule, not a closed line-number list +- **Context**: three successive independent adversarial review rounds each found at least one live `make`-target/fixed-path reference in a file the design already had in scope, in a section/line the enumeration hadn't named (round 1: two new doc files entirely; round 2: `xce-test-resources/SKILL.md`, `run.sh`'s error message; round 3: `tech.md`:81-82, `testing.md`:95, `deploy-ha.sh`:138-155's comment block, `xce-deploy-ha/SKILL.md`:294 — all in files already listed). A closed line-number enumeration kept being treated as if it were the completeness boundary, when it was really just a snapshot of what one investigation pass happened to find. +- **Selected Approach**: reworded every doc/script File Structure Plan entry around a stated scope rule ("every live reference in this file," not just the enumerated lines) and moved the Testing Strategy's live-reference sweep to run per-file BEFORE Phase 4, not only repo-wide after — so a task implementing one file is verifiably complete against that file's own grep, independent of whether this design's own enumeration was exhaustive. +- **Rationale**: the recurring pattern strongly suggested the enumeration approach itself, not any individual miss, was the actual defect — fixing instances one at a time without changing the underlying acceptance criterion risked a fourth, fifth round finding the same class of gap indefinitely. +- **Follow-up**: a fourth review round found the sweep's own "zero hits" bar was itself unachievable as worded (several legitimate, deliberately-excluded classes — vendored files, dated historical records, an inert placeholder default, OpenWolf's own logs — all match the same grep). Named each exclusion class explicitly in the Testing Strategy rather than leaving "zero hits" ambiguous; also found two C testsuite files (`test_ha_bootstrap.c`, `test_ha_ctrlhello.c`) with comments naming `make test-integration` by name, outside any doc/script file category considered so far — added to File Structure Plan (a 3-line rename, not a logic change). A fifth review round, specifically scanning `.go` source-file comments (a category rounds 1-4 never checked), found two more: the ROOT `CLAUDE.md`'s "Key Files" entry (line 42, the single most-visible live reference in the repo — loaded into every session, distinct from the already-covered `src/frontend/CLAUDE.md`) and both Go modules' `cmd/main.go` ldflags-injection comments naming the Makefile's `build-server`/`build-cli` targets. Both added to File Structure Plan. The recurring lesson across rounds 1, 4, and 5: each round's "fresh eyes, no assumptions" instruction and switch to an unchecked file *category* (docs → scripts → C test comments → Go source comments) is what surfaces genuinely new gaps — re-reading the same already-covered files more carefully stopped finding anything past round 2. + +## Risks & Mitigations +- **Risk**: the new backend `test()` silently links against `backend/stub.go` (the `//go:build !cgo` fallback) instead of the real CGO path, producing a vacuous pass. **Mitigation**: Testing Strategy's "New capability verification" step deliberately breaks `apiserver_cgo_deps` and confirms the test fails for the expected linker reason before trusting a green run. +- **Risk**: deleting the Makefile before all manual-developer-only targets (`test-backend`, `test-integration`) have a proven equivalent silently drops coverage nobody notices until someone needs it. **Mitigation**: Migration Strategy's explicit Phase ordering (1-3 must land and be verified before Phase 4). +- **Risk**: `run-resource-verify.sh`'s new discovery default assumes `$BUILD_DIR` (`$REPO/src/xce.build`, the `asan` profile dir) always contains a built apiserver — true only after `/xce-build apiserver`/`all` has actually been run. **Mitigation**: this matches the existing behavior/risk profile of the script's other three binaries (`BINARY`/`SCHED_BIN`/`XT`), which already assume the same; no new risk class introduced. + +## References +- `.kiro/specs/eliminate-frontend-makefile/brief.md` — original Path C discovery investigation (2026-08-27), the primary source for scope/seam decomposition. +- `src/tools/xce-build.sh` (lines 255-328) — live call-site evidence. +- `src/frontend/apiserver/meson.build`, `src/meson.build` — CGO derivation and program-object reuse evidence. +- `.claude/skills/xce-test-resources/run-resource-verify.sh` (lines 200-207, 348-351) — existing sibling discovery convention. diff --git a/.kiro/specs/eliminate-frontend-makefile/spec.json b/.kiro/specs/eliminate-frontend-makefile/spec.json index c9d50be4..fbe1d015 100644 --- a/.kiro/specs/eliminate-frontend-makefile/spec.json +++ b/.kiro/specs/eliminate-frontend-makefile/spec.json @@ -1,22 +1,24 @@ { "feature_name": "eliminate-frontend-makefile", "created_at": "2026-08-27T00:00:00Z", - "updated_at": "2026-08-27T00:00:00Z", + "updated_at": "2026-09-03T13:01:25Z", "language": "en", - "phase": "brief", + "phase": "tasks-generated", "description": "Remove src/frontend/Makefile entirely, consolidating Go builds (apiserver, xcecli) and their tests onto Meson's tools/go_build wrapper. Closes a real, already-realized duplicate version-fingerprint-computation risk (roadmap.md backlog item, recorded 2026-08-21). Path C single-scope investigation via /kiro-discovery, 2026-08-27.", "approvals": { "requirements": { - "generated": false, - "approved": false + "generated": true, + "approved": true }, "design": { - "generated": false, - "approved": false + "generated": true, + "approved": true, + "validation_note": "6 review rounds (1 self-review + 5 independent adversarial subagent reviews, 2026-09-03), run to convergence per explicit user goal ('fix issues found and review again until no new issue found'). Round 6 came back clean (GO, zero new issues) after a category sweep of Python/Nix/YAML/JSON/Cap'n Proto/every other meson.build/remaining skill scripts, a semantic pass beyond literal-keyword grep, and a full cross-artifact consistency check -- self-approved on that basis. Findings across rounds 1-5, all fixed in design.md/research.md: (1) CRITICAL -- the new apiserver-backend Meson test() target's CGO flags contained unresolved @CURRENT_SOURCE_DIR@/@BUILD_ROOT@ placeholder tokens that tools/go_build normally resolves via sed, bypassed entirely by this new test; fixed via Meson's own .replace() at configure time. (2) CRITICAL -- a follow-up fix itself used invalid unquoted Meson dict-literal keys in the test()'s env: argument; fixed to quoted string keys (verified live against a real `meson setup` run). (3) A long tail of live documentation/comment references to the deleted Makefile's targets or fixed paths, discovered by switching file CATEGORY each round rather than re-reading known files (steering docs -> skill docs/scripts -> C testsuite comments -> Go source comments -> root CLAUDE.md): tech.md, testing.md, structure.md, build-deployment.md, frontend/CLAUDE.md, src/docs/deployment.md, xce-deploy-ha/SKILL.md, xce-test-resources/SKILL.md, src/frontend/docs/status.md, src/docs/apiserver-backend-tasks.md (11 doc files total), deploy-ha.sh, run-resource-verify.sh, tests/regression/run.sh (3 scripts), test_ha_bootstrap.c/test_ha_ctrlhello.c (2 C testsuite files), apiserver/xcecli cmd/main.go (2 Go source files), and root CLAUDE.md's Key Files entry. In response to the recurring pattern, the File Structure Plan's doc/script entries were restructured around an explicit SCOPE RULE ('every live reference in this file', not a closed line-number list) and the Testing Strategy's live-reference sweep now runs per-file before Makefile deletion (not only repo-wide after) with named exclusion classes (historical .kiro/specs docs, vendored third-party files, dated historical records, OpenWolf's own logs, one deliberately-retained inert placeholder default)." }, "tasks": { - "generated": false, - "approved": false + "generated": true, + "approved": true, + "validation_note": "kiro-spec-tasks 2026-09-03: 4 major tasks / 12 sub-tasks drafted covering all 18 acceptance criteria (1.1-6.3) and every design.md File Structure Plan entry (11 doc files, 4 scripts, 2 C testsuite files, 2 Go source files, 1 meson.build). Task-graph sanity review (fresh subagent, pass 1): NEEDS_FIXES -- task 4.2's completion criteria didn't explicitly re-verify design.md's Testing Strategy 'Discovery-path regression' item after the Makefile is actually deleted, only before. Fixed. Sanity review pass 2 (fresh subagent): PASS -- fix confirmed adequate, no other issues; noted some Phase-3 (P) tasks carry a slightly over-conservative _Depends: 1.1, 2_ (not strictly required by file-locality alone) but this is faithful to design.md's own stated phase gating, not a correctness bug. Auto-approved per -y flag." } }, "ready_for_implementation": false, diff --git a/.kiro/specs/eliminate-frontend-makefile/tasks.md b/.kiro/specs/eliminate-frontend-makefile/tasks.md new file mode 100644 index 00000000..2155ffa6 --- /dev/null +++ b/.kiro/specs/eliminate-frontend-makefile/tasks.md @@ -0,0 +1,82 @@ +# Implementation Plan + +- [ ] 1. Core: CGO-backed backend test coverage via Meson +- [ ] 1.1 (P) Add a Meson test target for the apiserver CGO-backed backend suite, reusing the existing CGO flag derivation + - Resolve the `@CURRENT_SOURCE_DIR@`/`@BUILD_ROOT@` placeholder tokens already present in `apiserver_cgo_deps`'s computed CGO flags at Meson-configure time (they are otherwise only resolved by `tools/go_build`'s own runtime sed machinery, which this new target does not go through) + - Register the target with Meson's test runner (`suite: 'apiserver'`), scoped to the apiserver source directory, depending on `libxce` having already been built + - Set the same offline/hermetic Go environment `tools/go_build` already uses (`GOFLAGS=-mod=vendor`, `GOPROXY=off`) plus an explicit, build-dir-scoped `GOCACHE`/`GOPATH` (this target invokes the Go toolchain directly, not through `go_build`, so it inherits none of `go_build`'s own internal exports) + - Observable: `meson test -C --suite apiserver` lists and runs the new backend test target, distinct from the existing apiserver build's `custom_target`, and exits 0 against the current (unmodified) `backend/` source + - _Requirements: 3.1, 3.2_ + - _Boundary: apiserver/meson.build_ + +- [ ] 1.2 Prove the new backend test exercises the real CGO path rather than the `//go:build !cgo` stub + - Deliberately break `apiserver_cgo_deps` (e.g. temporarily drop one dependency) and confirm the new test fails with a genuine linker/undefined-symbol error, not a silent pass + - Restore the dependency list and confirm the test is green again + - Observable: a captured failing run shows a real link-time error tied to the removed dependency; the immediately following restored run passes with no other change + - _Requirements: 3.2, 3.3_ + +- [ ] 2. (P) Core: migrate xce-build.sh's unit-test invocation off Make + - Replace both `cd frontend && make test` call sites (the `apiserver` case and the `all` case) with direct `go test` invocations covering the exact same package set and flags the Makefile's `test` target runs today (apiserver: `store`/`utils`/`controllers`/`backend` under `CGO_ENABLED=0`; xcecli: full module), mirroring the `xcecli` case's own already-Makefile-free style in the same file + - Observable: `/xce-build apiserver` and `/xce-build all` run unit tests with zero invocation of `make`, reporting the identical test package set and pass/fail counts as the pre-migration baseline + - _Requirements: 2.1, 2.2, 2.3_ + - _Boundary: xce-build.sh_ + +- [ ] 3. Integration: eliminate every remaining live reference to the Makefile +- [ ] 3.1 (P) Fix run-resource-verify.sh's apiserver-binary discovery + - Change `APISERVER_BIN`'s default from the fixed `/tmp/apiserver` path to the same `$BUILD_DIR//`-relative convention its sibling binaries (`BINARY`, `SCHED_BIN`, `XT`) already use, and update its failure message to name `/xce-build apiserver` instead of the Makefile target + - Observable: with a binary present only in the Meson build dir (no `/tmp/apiserver`), the script locates and uses it; with neither present, the failure message names `/xce-build apiserver`, not `make build-server` + - _Requirements: 5.1, 5.2, 5.3_ + - _Boundary: run-resource-verify.sh_ + - _Depends: 1.1, 2_ + +- [ ] 3.2 (P) Update deploy-ha.sh's and run.sh's stale message strings + - `deploy-ha.sh`: update its `/tmp/apiserver`-fallback branch's two log lines and the preceding block comment describing "two ways" to build apiserver, dropping the `make build-server` mention; its meson-first/`/tmp/apiserver`-fallback discovery logic itself is unchanged + - `tests/regression/run.sh`: update the `XCE` binary-not-found error to name `/xce-build apiserver` instead of reproducing the deleted `build-cli` target's manual `go build` recipe; its `XCE="${XCE:-/tmp/xce_static}"` default is left as-is (an inert placeholder — callers are still expected to pass `XCE=` explicitly) + - Observable: neither file's live text names a `make` target anywhere; deleting `src/frontend/Makefile` and re-running each script's own failure path shows only Meson-based guidance + - _Requirements: 5.2, 5.3, 6.2_ + - _Boundary: deploy-ha.sh, tests/regression/run.sh_ + - _Depends: 1.1, 2_ + +- [ ] 3.3 (P) Update live steering, skill, and status documentation + - Update every current-fact reference to a `src/frontend/Makefile` target or a Makefile-specific fixed path in: `tech.md` (Build Systems table and code block, plus the "Testing Requirements" lines), `build-deployment.md` (Go Frontend Build section), `testing.md` (Build targets bullets, Philosophy line, and the "What Not to Mock" CGO-bridge line), `structure.md`, `frontend/CLAUDE.md`, root `CLAUDE.md`'s Key Files entry, `src/docs/deployment.md`'s Build gotcha section, `xce-deploy-ha/SKILL.md` (all four references, including the Step 4d intro), `xce-test-resources/SKILL.md`'s `$XCE_RESVERIFY_APISERVER_BIN` default description, `src/frontend/docs/status.md`, and `src/docs/apiserver-backend-tasks.md` + - Replace each with the equivalent Meson-based guidance (`/xce-build apiserver`, `meson test --suite apiserver`, direct `tests/regression/run.sh` invocation) already established by tasks 1-3.2 + - Observable: none of the 11 files contains a live `make (build-server|build-cli|test|test-backend|test-integration|test-all)` or `/tmp/apiserver`/`/tmp/xce_static` reference presented as current fact + - _Requirements: 6.1, 6.2, 6.3_ + - _Boundary: Documentation set (11 files)_ + - _Depends: 1.1, 2, 3.1_ + +- [ ] 3.4 (P) Update source-code comments referencing the Makefile + - `src/controller/testsuite/test_ha_bootstrap.c` and `test_ha_ctrlhello.c`: rename their `make test-integration` pointers to reference the regression suite (`tests/regression/run.sh`) directly, with no change to test logic + - `src/frontend/apiserver/cmd/main.go` and `src/frontend/xcecli/cmd/main.go`: drop the Makefile clause from each ldflags-injection comment, keeping only `src/tools/go_build` + - Observable: none of the four files' comments name a `make` target; no production or test logic changes + - _Requirements: 1.2, 6.1, 6.2_ + - _Boundary: test_ha_bootstrap.c, test_ha_ctrlhello.c, apiserver/cmd/main.go, xcecli/cmd/main.go_ + - _Depends: 1.1, 2_ + +- [ ] 3.5 (P) Verify the regression suite runs against a Meson-built binary with parity to the pre-migration baseline + - Run `tests/regression/run.sh` directly (`XCE= URL=`) against a live apiserver built via `/xce-build apiserver` + - Observable: the run reports the same PASS/FAIL/SKIP counts as the documented pre-migration `make test-integration` baseline, using no Makefile-built artifact + - _Requirements: 4.1, 4.2, 4.3_ + - _Boundary: tests/regression/run.sh (verification only, no file changes)_ + - _Depends: 1.1, 2_ + +- [ ] 3.6 Run the live-reference sweep across the whole repository and confirm zero hits outside the named exclusion classes + - Grep the full repository for `make (build-server|build-cli|test|test-backend|test-integration|test-all)` and `/tmp/apiserver`/`/tmp/xce_static`; classify every hit as either fixed by 3.1-3.4 or falling into one of design.md's named exclusion classes (`.kiro/specs/**` historical spec documents, `**/vendor/**` third-party files, dated historical records, `roadmap.md`'s own backlog entry for this spec, OpenWolf's own logs, `run.sh:12`'s retained placeholder default) + - Observable: the sweep's full output is captured and every hit is accounted for by name — either as an already-completed fix or a cited exclusion class — with none left unexplained + - _Requirements: 5.3, 6.1, 6.2_ + - _Depends: 3.1, 3.2, 3.3, 3.4_ + +- [ ] 4. Validation: final regression gate and Makefile removal +- [ ] 4.1 Run the full coverage-preservation validation suite as the final go/no-go gate before deletion + - Confirm: `/xce-build apiserver` and `/xce-build all` pass with the migrated unit-test invocation (task 2); `meson test -C --suite apiserver` passes including the new backend test target (task 1); the regression suite parity check (task 3.5) and the live-reference sweep (task 3.6) both already passed + - Observable: a single consolidated run confirms all four checks pass together against the same build, immediately before the Makefile is deleted + - _Requirements: 3.3, 4.3_ + - _Depends: 1.2, 2, 3.5, 3.6_ + +- [ ] 4.2 Delete src/frontend/Makefile and confirm no functional path still depends on it + - Delete the file; re-run the full coverage-preservation validation from 4.1 unchanged, confirming no regression from the deletion itself + - Re-run task 3.1's discovery scenario (`run-resource-verify.sh` locating the apiserver binary from the Meson build dir) with the Makefile now genuinely absent from disk — this closes design.md's Testing Strategy "Discovery-path regression" check, which specifically requires this to be re-verified post-deletion, not just pre-deletion + - Re-run the live-reference sweep (task 3.6's grep) one more time, full-repository, now that the file genuinely no longer exists + - Observable: `src/frontend/Makefile` no longer exists in the tree; the full validation suite, the post-deletion discovery-path re-check, and the post-deletion sweep all pass with no unaccounted hits + - _Requirements: 1.1, 1.2, 1.3, 5.1_ + - _Depends: 4.1_