# API Server Backend Enhancement — Task Tracker ## Status Legend - [x] Completed - [ ] Pending - [>] In Progress (blocked) --- ## P0-1: Handle Cluster Config Push - [x] **#1 P0-1a: Add HostConfig + Cluster fields to models** - `models/clusters.go` — Added `HeartbeatInterval`, `Vcpus`, `InactiveHeartbeatCount` to `ClusterInfo` - `models/hosts.go` — Changed `HostStateStr(0)` from `"OK"` to `"UNKNOWN"` - `models/clusters_test.go` — New: verifies new fields serialize + existing fields unchanged - `models/hosts_test.go` — New: verifies `HostStateStr` for zero, flags, non-flag, combined - All 15 tests passing - [x] **#2 P0-1b: Add pure Go conversion functions + TDD tests** - `backend/convert_pure.go` — Added `rawHostConfigData` + `rawHostConfigToModel()`, `rawClusterData` + `rawClusterToModel()` - `backend/convert_test.go` — 8 new table-driven tests: zero-value ints, empty strings, normal values, worker/config-sourced field isolation - `TODO(dispatchwindow)` comment added per plan spec - `DisptWindow` guard test added to catch accidental future CGO layer changes - All 26 backend tests passing - [x] **#3 P0-1c: Add CGO conversion layer in convert.go** - `backend/convert.go` — Added `cHostConfigToModel()`, `cClusterConfHostConfigs()`, `cClusterConfToClusterInfo()` - `cResdefToModel` intentionally skipped — `xce_resdef_t` doesn't exist in C codebase and `ResourceInfo` has no store/consumer (YAGNI); existing `cResourceToModel` → `ResourceDefinitionInfo` already handles config push - `cClusterConfResources` already existed — no change needed - CGO compilation verified passing - All unit tests still passing - [x] **#4 P0-1d: Implement handlers with partial merge + TDD** - `backend/notification.go` — Added `OnClusterUpdated(cluster *models.ClusterInfo)` and `OnHostConfigUpdated(host *models.HostInfo)` to `NotificationHandler` interface - `backend/handler.go` — Implemented partial-merge `OnClusterUpdated` (preserves Version, Status, ControllerName, NumServers, Resources, HostTypes, HostModels, ClusterAdmins), `OnHostConfigUpdated` (preserves HostStatus, CPUFactor, NumCPUs, NumCores, NumThreads, MaxMem, MaxSwap, MaxTmp, NumDisk, NumRun, NumSSusp, NumUSusp, NumDone, Topology, SchedStop), fixed `OnHostUpdated` (preserves HostModel, HostType, MaxSlot, DisptWindow via two-value `Get()`) - `backend/handler_test.go` — 6 new tests: new host, partial merge for each of OnHostConfigUpdated, OnClusterUpdated, OnHostUpdated - All backend tests passing (32 total) - [x] **#5 P0-1e: Expand handleConfigPush** (unblocked) - `backend/clib.go` — Expand `handleConfigPush()` to call `cClusterConfHostConfigs()` and `cClusterConfToClusterInfo()` - Call `gHandler.OnHostConfigUpdated()` per host config, `gHandler.OnClusterUpdated()` for cluster settings --- ## P0-2: Host Control via Backend - [x] **#6 P0-2a: C-side plumbing for host control** - `schema/controller.capnp` — Added `HostControl` struct + `hostctrlmsg @21` - `include/typemap.h` — Added `xce_hostctrl_t* hostctrlmsg;` to body union (after @20 clrpreasonmsg, matching capnp ordinal) - `include/client.h` — Added `xcecli_hostctrl()` declaration - `include/xceapi.h` — Added `xce_hostctrl()` declaration - `include/request.h` — Added `xce_hostctrl_req()` declaration - `lib/client.c` — Implemented `xcecli_hostctrl()` - `lib/xceapi.c` — Implemented `xce_hostctrl()` with `cbuf_get()` wait - `lib/request.c` — Implemented `xce_hostctrl_req()` (validates HOST_OPEN/HOST_CLOSE only, rejects HOST_TAG/UNTAG) - `lib/helper.c` — Added `Request_body_hostctrlmsg` case to `xce_request_body()` + `xce_request_msg_name()` - `controller/cluster.c` — Added `OPCode_hostcontrol` case → `do_hostcontrol_op()` - `controller/worker.c` — Implemented `do_hostcontrol_op()` calling `worker_ctrl()`; fixed pre-existing `goto clean` bug that leaked write lock + reference count - `controller/include/cluster.h` — Declared `do_hostcontrol_op()` - `lib/testsuite/test_request.c` — 8 TDD validation tests (null ep/ctrl/hostname, empty hostname, HOST_DO_NONE no-op, HOST_TAG/UNTAG reject, negative action) - Regenerated capnp via `ninja -C build` - [ ] **#7 P0-2b: Implement Go-side ControlHost + fix allFlag race** (unblocked) - `backend/clib.go` — Replace `ControlHost()` stub with real implementation via `xcecli_hostctrl()` - `controllers/hosts.go` — Fix data race: remove `var allFlag bool`, return from `preCheckHostControlRequest` as local - `controllers/queues.go` — Same allFlag data race fix (lines 534-536 / 465) - [ ] **#8 P0-2c: Register routes + RBAC for host control/select** - `router/routers.go` — `POST /xce/v1/hosts/control` → RBAC `host:control` (cluster_admin) - `router/routers.go` — `POST /xce/v1/hosts/select` → RBAC `host:view` (all authenticated) - `config/rbac.yaml` — Add `host:control` and `host:view` action entries - [ ] **#9 P0-2d: Update stub.go with new function stubs** - `backend/stub.go` — Keep `ControlHost` stub (return `errNoCGO`), add `StartHeartbeat(ctx context.Context){}`, `SetHeartbeatInterval(secs int){}`, `EvalSelectForHost(...)` returning `errNoCGO`, `Cleanup(){}` - Add `"context"` to import block --- ## P1-1: Handle Resource Value Push - [ ] **#10 P1-1: Handle resource value push (OPCode_notify)** - `backend/clib.go` — Add CGO val helpers to preamble (`xce_val_which`, `xce_val_i32`, etc.) - `backend/clib.go` — Add `OPCode_notify` case at top-level switch in `go_push_handler()` (NOT inside `handlePush()`) - `backend/clib.go` — Add `handleNotifyData()` for `NotifyData_data_schedUpdateResvals` - `backend/convert.go` — Add `cResvalToDistribution()` — switch on `Value_u_*` constants, all 7 cases separate --- ## P1-2: Host Resource Requirement Select - [ ] **#11 P1-2a: C select expression parser (PEG + eval)** - `include/resreq.h` — Replace empty `xce_resreq_select_` with expression tree types, add enums, add extern declarations - `lib/xce_select.peg` — New PEG grammar for select boolean expressions (right-recursive) - `lib/resreq.c` — Implement `parse_select()`, `eval_select()`, `free_select()`; update `kill_primary_resreq()` - `lib/meson.build` — Add `xce_select.peg` to explicit source list - [>] **#12 P1-2b: Go EvalSelectForHost + FilterHostByResReqSelect** (blocked by #11) - `backend/clib.go` — Add `EvalSelectForHost()` using `cgo.Handle` for callback context, add `#include "resreq.h"`, extern declarations, `xce_select_destroy` helper - `controllers/hosts.go` — Implement `FilterHostByResReqSelect`, update `preCheckHostRequest()` limit from 4094→1024 bytes --- ## P1-3: Heartbeat Keep-Alive - [ ] **#13 P1-3: Heartbeat keep-alive implementation** - `backend/clib.go` — Add `heartbeatIntervalSecs int32` (default 30), `SetHeartbeatInterval()`, `heartbeatFn` var, `StartHeartbeat(ctx context.Context)` - `backend/handler.go` — `OnClusterUpdated` calls `SetHeartbeatInterval(cluster.HeartbeatInterval)` after store update - `cmd/main.go` — Call `backend.StartHeartbeat(ctx)` after `backend.RegisterToBackend(...)` --- ## Final - [>] **#14 Build and test verification** (blocked by all others) - `make test` in `src/frontend/` passes - `ninja -C build` for C components passes - RBAC verification - Input safety verification (1024-byte limit, 256-host limit)