docs(spec): promote eliminate-frontend-makefile backlog item to a brief

/kiro-discovery investigation of removing src/frontend/Makefile in favor
of Meson as the sole build system. Records the quick investigation's
findings (5 boundary seams, current-state evidence that build-cli/
build-server are already dead from /xce-build's perspective and
build-server is already broken today) and the user's confirmed decision
to fully remove the Makefile rather than keep a compat wrapper.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AgYRU3zDTqNGV5Jk8aiKLg
This commit is contained in:
Rongsong Shen 2026-08-27 08:27:55 +00:00
parent d8bf8fb2e1
commit 5e68fcc0d8
No known key found for this signature in database
GPG key ID: 5BCDAAE0123E9FE2
3 changed files with 83 additions and 1 deletions

View file

@ -0,0 +1,58 @@
# Brief: eliminate-frontend-makefile
## Problem
`src/frontend/Makefile` and Meson's `tools/go_build` wrapper are two independent build systems for the same two Go binaries (apiserver, xcecli), each computing its own copy of the same version-fingerprint logic (`commit_id`/`build_time`/`dirty_fingerprint`). This is an already-realized, not theoretical, drift risk: the Makefile's independent fingerprint computation needed three adversarial review rounds to fix a shell exit-status bug during `dirty-content-fingerprint` task 4.3 (`roadmap.md:82`), and `build-server` — the Makefile's OWN apiserver build target — is independently confirmed *currently broken* (bug-1597, `.wolf/cerebrum.md:48`): a bare `go build` with no `CGO_CFLAGS`/`CGO_LDFLAGS`, failing outright with `fatal error: client.h: No such file or directory`. `src/docs/deployment.md:243-247` already documents this exact failure as a "build gotcha" and tells developers to use `/xce-build apiserver` instead — i.e., the project's own docs already steer people away from the target that's supposed to be the Makefile's main value proposition.
## Current State
- `src/frontend/Makefile` (195 lines) has 5 targets: `build-cli`, `build-server`, `test`, `test-backend`, `test-integration` (+ `test-all`).
- `build-cli`/`build-server` are each 2 lines of actual build logic; the surrounding ~130 lines exist entirely to independently re-derive version-fingerprint `-ldflags`, duplicating logic `src/tools/xce-build.sh` already owns.
- Meson already builds both binaries today via `tools/go_build` (`apiserver/meson.build`'s `custom_target`, `xcecli/meson.build`'s equivalent) — confirmed working, lands in the per-profile build dir (e.g. `build.dbg/frontend/apiserver/xce_apiserver`).
- `/xce-build apiserver`/`/xce-build all` (`xce-build.sh`) already build via meson exclusively (`meson compile ... apiserver xcecli`, line 237) — the Makefile's `build-cli`/`build-server` are not in that path at all today.
- `xce-build.sh` DOES still shell out to `cd frontend && make test` for running Go unit tests (lines 240, 287) — this is the one live automation dependency on the Makefile from the project's own canonical build tool.
- No meson `test()` exists anywhere for the CGO-backed backend test suite (`apiserver/meson.build` has a build-only `custom_target`, zero test registrations) — `make test-backend` is the ONLY way to run these tests today.
- One other live script (`run-resource-verify.sh:350`) checks for a binary at the Makefile's hardcoded `/tmp/apiserver` output path and prints a `make build-server`-referencing error if absent.
- `deploy-ha.sh` used to hard-depend on the same `/tmp/apiserver` path; this was just fixed (this session, uncommitted) to prefer the meson build-dir path first, matching the pattern already used for xceadmin/worker/scheduler, falling back to `/tmp/apiserver` only if the meson one is absent.
## Desired Outcome
`src/frontend/Makefile` is deleted. All Go builds for apiserver/xcecli happen exclusively via Meson (`/xce-build apiserver`/`/xce-build xcecli`/`/xce-build all`). Go test execution (unit + CGO backend + integration) is either wired into meson as real `test()` targets, or reduced to plain, documented `go test`/shell invocations with no Makefile indirection — matching the pattern `xce-build.sh`'s own `xcecli` case (line 249) already uses today for xcecli's tests, bypassing Make entirely. No developer or script depends on a Makefile-specific fixed output path (`/tmp/apiserver`, `/tmp/xce_static`) after this lands.
## Approach
**Decision confirmed with user (2026-08-27): keep a single build system — Meson survives, `src/frontend/Makefile` is removed entirely, not kept as a thin/compat wrapper.** No alternative approach was considered or offered; this was a direct instruction, not a selection among options.
**Phased replacement, test-coverage-preserving order** (see Boundary Candidates below for the split). Not a single mechanical deletion — the CGO backend test suite has no meson equivalent today, so that gap must be closed BEFORE the Makefile can be safely removed, or backend CGO regression coverage silently disappears.
Two sub-approaches were considered for the CGO backend test gap specifically:
1. **Add a meson `test()` wired with the same CGO env `apiserver/meson.build`'s `custom_target` already derives** (cgo_cflags/cgo_ldflags via the same `apiserver_cgo_deps` pkg-config loop, lines 20-38) — single source of truth, no duplicate derivation. **Recommended.**
2. Keep a minimal, build-free shell script (not a Makefile) that just runs `go test ./backend/...` with hand-derived CGO flags — avoids touching `apiserver/meson.build`, but reintroduces exactly the duplicate-derivation risk this whole effort exists to eliminate.
## Scope
- **In**: deleting `src/frontend/Makefile`; migrating `xce-build.sh`'s 2 `make test` call sites to direct `go test` invocations (mirroring the existing `xcecli` case); adding a meson `test()` (or equivalent) for the CGO backend suite so that coverage isn't lost; migrating `test-integration`'s `XCE_BIN`/`SRV_URL` convention to point at the meson-built binary; updating the ~9 files with live-relevant doc references (`tech.md`, `build-deployment.md`, `testing.md`, `structure.md`, `frontend/CLAUDE.md`, `src/docs/deployment.md`, `xce-deploy-ha/SKILL.md`); updating `run-resource-verify.sh`'s apiserver-binary discovery to the same dual-path pattern `deploy-ha.sh` now uses.
- **Out**: rewriting historical spec docs (`tasks.md`/`research.md`/`design.md` under already-completed specs like `ha-config-bootstrap`, `component-failover-adoption`, etc.) that mention `make test`/`make build-server` as acceptance criteria for work already shipped — those are frozen records, not live automation, and editing them provides no value. Any change to the underlying version-fingerprint computation itself (`xce-build.sh`'s own logic) — this effort only removes the SECOND, duplicate computation site, not the first.
## Boundary Candidates
- **Seam 1 — Build targets** (`build-cli`/`build-server`): already effectively dead from `/xce-build`'s perspective; lowest risk, smallest diff. Can land independently of the other seams.
- **Seam 2 — Unit test target** (`test`): 2 call sites in `xce-build.sh`, already has a proven direct-`go test` replacement pattern in the same file. Low risk.
- **Seam 3 — CGO backend test target** (`test-backend`): needs a genuinely new meson `test()` registration (no existing equivalent) before the Makefile target can be removed without losing coverage. This is the one seam that's real net-new work, not just deletion — highest-effort seam.
- **Seam 4 — Integration test target** (`test-integration`): depends on Seam 1 (needs a `build-cli` replacement first) plus its own `XCE_BIN` path update.
- **Seam 5 — Doc/script updates**: mechanical once seams 1-4 land; the actual reference list is already enumerated (see this investigation's own findings).
Given all 5 seams touch the same small `src/frontend/` + `src/tools/xce-build.sh` area, share one dependency graph (Seam 4 depends on Seam 1; nothing else cross-depends), and total scope is well under 20 tasks, this is a **single spec**, not a multi-spec decomposition — Path C.
## Out of Boundary
- Any change to `apiserver/meson.build`'s existing CGO flag derivation logic itself (only *reusing* it for the new test() target, not modifying it).
- Any change to `xce-build.sh`'s version-fingerprint computation (the "source of truth" side of the duplication, not the Makefile side being removed).
- Rewriting frozen historical spec documents.
- The `set -euo pipefail`-causes-CGO-tests-to-be-structurally-skipped-in-`all` gap noted in `component-failover-adoption/tasks.md:201` — real, adjacent, but a separate `xce-build.sh` control-flow bug, not something this removal effort should silently fix as a side effect.
## Upstream / Downstream
- **Upstream**: `apiserver/meson.build`'s existing CGO dependency list (`apiserver_cgo_deps`) is the thing Seam 3's new test() target must reuse, not re-derive.
- **Downstream**: `deploy-ha.sh` (already migrated this session), `run-resource-verify.sh` (needs the same dual-path treatment), any future skill/script that currently assumes `/tmp/apiserver`/`/tmp/xce_static` as a fixed discovery path.
## Existing Spec Touchpoints
- **Extends**: none directly — this is genuinely new spec-shaped work, not an extension.
- **Adjacent**: `dirty-content-fingerprint` (the spec whose task 4.3 review first surfaced the duplication risk this effort resolves — provenance only, not reopened); `build-time-versioning` (owns the version-fingerprint computation `xce-build.sh` is the single source of truth for, post this change).
## Constraints
- Must not reduce CGO backend test coverage at any point during the migration (Seam 3 must land before Seam 3's Makefile target is removed, not after).
- Nix devShell already bundles the full C/Go/Python toolchain uniformly — there is no "avoid installing the C toolchain" argument for keeping a Make-only path, unlike projects where Go-only contributors might want to skip C dependencies.
- `go test -race` is confirmed unusable in this ASAN-linked CGO environment (independently hit 3 times per `component-failover-adoption/tasks.md:197`) — any new test() wiring must not attempt to add `-race`.

View file

@ -0,0 +1,24 @@
{
"feature_name": "eliminate-frontend-makefile",
"created_at": "2026-08-27T00:00:00Z",
"updated_at": "2026-08-27T00:00:00Z",
"language": "en",
"phase": "brief",
"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
},
"design": {
"generated": false,
"approved": false
},
"tasks": {
"generated": false,
"approved": false
}
},
"ready_for_implementation": false,
"blocked_on": []
}

View file

@ -79,7 +79,7 @@ The protocol **drive** is NOT fully event-driven. Root cause: `peer_ctrl_msg_han
Smaller items that likely don't warrant a full spec; pick up opportunistically or bundle into adjacent spec work.
- [ ] **eliminate src/frontend/Makefile, consolidate Go builds onto meson** — **recorded 2026-08-21, from `dirty-content-fingerprint` spec's task 4.3.** The Makefile's `build-cli`/`build-server` targets exist as a "no meson involved" fast dev-loop path for apiserver/xcecli, but they achieve this by INDEPENDENTLY REIMPLEMENTING the same `commit_id`/`build_time`/`dirty_fingerprint` computation `src/tools/xce-build.sh` already owns (two separate computation sites for the identical values, a pattern `build-time-versioning`'s own research.md already flagged as an accepted-but-real drift risk). That risk materialized concretely: task 4.3's Makefile-side fingerprint computation needed **three adversarial review rounds** to fix a shell multi-stage-pipe exit-status bug (`{ cmd1; cmd2; } >file`'s exit status only reflects the last command, silently swallowing an earlier failure and fabricating output from incomplete data) — a bug class that could not have existed if there were only one computation site. Since XCE's nix devShell already bundles the full C/Go/Python toolchain uniformly (no dependency-avoidance benefit to a separate meson-free path the way there might be in a project where Go contributors don't want the C toolchain installed), the Makefile's marginal value may just be "skip the initial `meson setup` step" — `meson compile -C <builddir> apiserver`/`xcecli` should already give an equivalent single-target incremental rebuild once a build directory exists. **Not started — investigation first**: measure actual dev-loop friction of `meson compile` vs. `make build-cli`/`build-server` (first-configure cost vs. per-rebuild cost) before committing to removing the Makefile; if removed, `build-deployment.md` and any CI/doc references to `make build-cli`/`build-server` need updating too. Promote via `/kiro-discovery` or direct investigation when ready.
- [x] ~~eliminate src/frontend/Makefile, consolidate Go builds onto meson~~ — **PROMOTED to spec 2026-08-27** (`/kiro-discovery`; brief at `.kiro/specs/eliminate-frontend-makefile/brief.md`). Investigation (recorded 2026-08-21, from `dirty-content-fingerprint` spec's task 4.3) confirmed the risk is real and independently corroborated: `build-server` is separately confirmed BROKEN today (bug-1597, bare `go build` with no CGO flags, `fatal error: client.h: No such file or directory`; `src/docs/deployment.md:243-247` already tells developers to use `/xce-build apiserver` instead). Blast radius is narrower than originally framed: `/xce-build` already builds both binaries via meson exclusively (`build-cli`/`build-server` are dead weight from that path today); only 2 live call sites (`xce-build.sh:240,287`) depend on `make test`, with an already-proven direct-`go test` replacement 3 lines away in the same file. One genuine gap found: no meson `test()` exists for the CGO backend suite (`test-backend`) — that must be added before the Makefile can be safely removed, or backend CGO coverage is lost, not just inconvenienced. Next: `/kiro-spec-init eliminate-frontend-makefile`.
- [ ] **job_ctrl direct-mutate path — migrate to commit-then-apply** — **recorded 2026-07-13, investigated 2026-08-19 (finding: real risk, wider scope than originally framed).** `raft-apply-decoupling` deliberately scoped `job_ctrl`'s `mark_job_update`/`mark_job_fail` (admin SIGCONT/SIGSTOP) OUT-OF-BOUNDARY (task 4.1/6.1 Implementation Notes). The 2026-08-19 investigation found the original framing wrong in two ways: (1) **NOT the only remaining call site** — `on_job_stop`/`on_job_resume`/`on_job_ckpt` (`job.c:2067-2222`) share the identical unconverted mutate-then-notify-then-WAL-append pattern, fed by ordinary worker/scheduler traffic, not "rare, admin-only" as originally described; (2) **blast radius (a) is confirmed real, not theoretical** — the pre-commit mutation is synchronously pushed live to BOTH the scheduler (`sched_job_update_fun`) AND every connected apiserver/client (`apiserver_job_update_fun`) before the commit barrier resolves; on barrier timeout the admin gets a failure reply but `job->job.state` is never rolled back and no retraction is ever sent — matching the bug-795/828/851/1060 shape. Live-apply vs replay-apply (b) is narrower than feared for `job_ctrl` specifically (the `allocs != NULL` gate mutually excludes it from the converted deferred-apply fact types), but the investigation surfaced a SEPARATE bug underpinning that safety argument: `job_ctrl` (`job.c:999`) holds only a read lock while mutating job state — every other mutator in this file takes a write lock — so two concurrent admin commands on the same job aren't mutually excluded, undermining the "same lock, no staleness window" claim the replay-safety argument depends on (already flagged by a `raft-apply-decoupling` task 2.1 reviewer, never fixed). **Recommendation**: promote to a small follow-up spec covering all four call sites together (bounded, well-precedented fix — same shape as the fact-receipt conversion `raft-apply-decoupling` already built for job submit); the lock-type bug is independently fixable and low-risk regardless of when/whether the larger migration happens. Not yet promoted — user decision 2026-08-19 to correct scope/framing here first, promote later.
- [x] **WAL-trim cursor-bound guard (bug-1147; surfaced 2026-06-25 by raft-drive-async design review)** — **IMPLEMENTED** as part of `raft-drive-async` task 3.2 (`raft-drive-async` is IMPLEMENTED, validate-impl GO 2026-07-18; see above). `trim_wal_segments` (`persist.c` ~2597-2644) now computes `floor = repl_trim_lsn(&p->repl, active_seg_lsn, p->config.snap_interval_s)` — folding in active-standby `acked_lsn` and disconnected-slot `last_disconnected_lsn` — and only unlinks segments below that floor; the code comment at `persist.c:2634` explicitly labels this the "(bug-1147 guard)". Far-behind/empty join (cursor=0) is additionally covered now that `entity-bearing snapshots` (strategy B) has landed as `ha-entity-snapshots` (see above), with a live decision path (`ha.c:4413`, "< oldest_wal_lsn → requesting snapshot transfer"). Related: bug-795, bug-828, bug-1060.
- [ ] OP_heartbeat peers cross-validation — include a hash of the local `--peers` string in heartbeat requests; log ERROR on mismatch between candidates. Detects misconfigured nodes at election time instead of silently risking dual-primary split-brain. _(FUTURE_WORK.md §3.)_