myxce/.kiro/specs/eliminate-frontend-makefile/design.md
Rongsong Shen be9a03a3e9
docs(eliminate-frontend-makefile): add approved spec (requirements/design/tasks)
Requirements: 6 requirement areas covering single build path, unit/
backend/integration test migration, binary discovery, and doc
consistency. Design: 6 rounds of independent adversarial review to
convergence (2 real technical bugs caught -- unresolved Meson
placeholder tokens in CGO flags, invalid unquoted dict-literal keys --
plus a long tail of stale Makefile references across docs/scripts/
source comments, closed by a scope-rule restructure rather than
continued line-by-line patching). Tasks: 4 major / 12 sub-tasks,
task-graph sanity review passed after one repair round.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XYdtrEWWZfKRRGED7raEYg
2026-09-03 13:03:00 +00:00

31 KiB

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/<component>/<binary> 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

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/<component>/<binary> 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 (<build_dir>/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 <dir> --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 <dir>)
  • 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 <dir> --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 <dir> --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=<meson-built xcecli path> URL=<live apiserver> 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.

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.