snapshot_load.c's job-restore path now calls apply_job_task_count()
with the job's already-restored mintasks/maxtasks, right after its
resreq parse succeeds and before snapshot_build_schedobj() reads the
resreq's task count for its fingerprint. This is the simulation/replay
driver's only live call site (xce_sched_core_load_snapshot(), reached
via --sim), independent of and parallel to task 2.1's fresh-ingest
wiring in core_events.c.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XYdtrEWWZfKRRGED7raEYg
core_build_job() now calls apply_job_task_count() right after its
resreq parse succeeds and before core_build_schedobj() reads the
resreq's task count for resreq_fp — so a freshly-submitted multi-task
job's dispatched primary resreq reflects its real requested min/max
task count instead of the previous always-one-task hardcode.
Tracked follow-up (not blocking, see tasks.md Implementation Notes):
test_repl_breakpoints_inspection.c:735 hardcodes an incidental
"Tasks: min = 0, max = 0" byproduct of a fixture's parse_select()
failure; now correctly reports (1,1) per Requirement 1.2. Fix folded
into task 5.1's full-suite regression pass.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XYdtrEWWZfKRRGED7raEYg
Adds apply_job_task_count() to overwrite a standalone primary resreq's
minTasks/maxTasks with a job's real requested task count, no-op for
COMPOUND/ALTER nested primaries, with a defensive min>max clamp.
Closes the gap that lets scheduler-allocation's multi-host allocator
ever be reached (bug-1770). Wiring at live ingest call sites follows
in subsequent tasks.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XYdtrEWWZfKRRGED7raEYg
Pure-atexit cleanup (27e81738) regressed the scheduler repl/sim suites: their
real-stdout-capture helpers fork() a child that runs the REPL and exits via
exit(0), which runs the INHERITED atexit handler and deletes every dir the
PARENT registered -- including the dir the child just wrote its capture file
into, so the parent's fopen() sees ENOENT ("Expected Non-NULL" cascade; 8
repl/sim tests failed).
Fix: tag each registered dir with the pid that created it and have the atexit
sweep remove only entries whose owner == getpid(). A forked child inherits the
parent's entries tagged with the parent's pid, so the child's inherited atexit
removes nothing the parent owns; dirs the child itself mkdtemp()s are tagged
with the child pid and cleaned at its own exit. No pthread_atfork hooks are
registered -- a global atfork (prepare-lock or child-only) was tried twice and
regressed the controller suite's fork-heavy HA/commit tests (~98-119
failures), because a fork hook runs on every fork those tests do. The
parent-process path is byte-for-byte the behavior validated earlier
(controller full PASS, 0 failures); only fork-child cleanup semantics change.
Validated: scheduler repl/sim snapshot/step/rollup/event/breakpoints/exec
tests now pass 8/8 (was 8 fails under pure-atexit). Controller full
controller_unit_test re-run is pending a quieter machine -- this 7.8GB-RAM
box OOM-kills the 1269-test ASAN run before it finishes; no failures were
observed in the partial runs.
Co-Authored-By: Claude Code <noreply@anthropic.com>
Validation-only: both sequential and chaotic HA soaks pass cleanly on
the final commit (zero job loss/divergence/stall), independently
re-run and confirmed by the reviewer. Worker-rejoin-resync coverage in
the soak is live evidence task 5.2's cache_clear_cb fix holds under
real primary failover. Standing caveat: bug-1770 means this soak (like
the rest of the feature) can only exercise seqjob's rewrap and the
cache dispatch/clear wiring -- parjob_allocator has no live production
path until that separate gap is fixed (Req 9.4).
This completes all 17 sub-tasks of the scheduler-allocation feature.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XYdtrEWWZfKRRGED7raEYg
controller_connector_test (worker-rejoin-resync, THEIRS) fixtures a
zeroed cc_ctx with worker_list == NULL and drives rejoin success; our
bug-1509 resend-on-rejoin call dereferenced *cc_ctx->worker_list and
SEGV'd (ASan null read at controller_connector.c:536). worker_list is NULL
until the first job dispatch on a worker, so guard the re-report on it.
Co-Authored-By: Claude Code <noreply@anthropic.com>
Unit tests create /tmp/xce_* scratch dirs via mkdtemp() and never remove
them; a full ASAN run leaks 70+ dirs and fills /tmp to ENOSPC, which then
surfaces as unrelated "Expected Non-NULL"-style cascade failures in whatever
test next calls mkdtemp()/persist_init() (precedent: 16238 dirs / 696G /
disk 100%). xce-build.sh's age-gated prune_stale_tmp_fixtures() only sweeps
the debris hours later -- a mask, not a fix.
Fix at the source: a self-contained mkdtemp() replacement
(src/testsuite_support/mkdtemp_clean.c) linked into every unit-test
executable via a declare_dependency() appended to the shared deps lists
(controller_unit_test_deps, scheduler_test_deps). Each scratch dir it creates
is registered and recursively removed by an atexit() handler, so a test
binary leaves /tmp clean. Cleanup runs at process exit -- after all TEST_
functions -- so tests that legitimately inspect a fixture's on-disk contents
across multiple TEST functions in the same file are unaffected (the property
that made per-file teardown retrofitting invasive).
Verified: full controller_unit_test (1269 tests) passes with 0 failures and
zero /tmp/xce_* left at exit (was: intermittent ENOSPC cascade). Scheduler
suite build+run still to be validated in the merge verification pass.
Design notes (rejected alternatives, to avoid re-litigating):
- A mid-run "keep newest N dirs" eviction was tried and dropped: it slowed a
monolithic run dramatically and one variant hung the suite (rmrf of large
WAL dirs in the hot path). atexit-only bounds nothing mid-run, but a single
suite fits in /tmp and cleanup is prompt; parallel meson suites are the
residual risk, addressed by running meson test with reduced parallelism if
needed.
- A pthread_atfork registry reset was tried (to stop a forked child's
inherited atexit from deleting the parent's dirs) and dropped: it
regressed the controller suite (~98 failures), so the plain atexit version
is what ships. If the scheduler repl/sim fork suites need it, revisit there
with a scheduler-scoped mechanism, not a global one.
Co-Authored-By: Claude Code <noreply@anthropic.com>
Audit-only: confirmed all 4 pre-existing scheduler test files touched by
this feature (meson.build, test_core_events.c, test_job_update.c,
test_resource_accounting.c) contain only pure additions or link-scaffold
replacement, zero altered assertions; confirmed every existing
COMPOUND/ALTER-dispatch test is byte-identical to its pre-feature state
despite task 5.1's shared sched_make_primary_alloc_() change. Full suite:
74/75 OK, sole failure the pre-existing, unrelated bug-1758 (Req 9.2, 9.3).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XYdtrEWWZfKRRGED7raEYg
Drive a real multi-slot parallel job through the actual production
dispatch path -- real sched_init(), real queue/worker/job ingest via
xce_core_push_events(), a real scheduling session (exercising task 5.2's
cache-clear wiring), the real sched_allocator() thread, cache lookup,
parjob_allocator's real my_alloc(), host_iter, and commit -- and confirm
the resulting job allocation genuinely spans 2 hosts with correct
per-host slot counts (Req 4.1, 4.2, 4.3).
Discovered and logged bug-1770: parse_resreq_primary() unconditionally
hardcodes primary->minTasks/maxTasks to 1 regardless of what a job
actually requests, and nothing in the real ingest pipeline ever copies
the job's own submitted task count (correctly threaded from the
apiserver's num_processors/maxnum_processors all the way to
xce_sched_job_t.mintasks/maxtasks) onto the nested xce_primary_resreq_t
fields the allocator dispatch actually reads. This predates the feature
by ~7 months and is out of this spec's scope to fix, but it means
parjob_allocator has zero live production path until a dedicated
follow-up lands. The test compensates with one clearly-labeled patch of
the real, already-ingested resreq's task-count fields immediately after
real parsing; everything else in the pipeline is unmodified production
code.
Also logged bug-1771 (xce_sched_fcfs's fcfs_del_job() dereferences
job->queue->name with no NULL guard) discovered while building this
task's fixture; worked around by registering the job's queue before
push, mirroring the apiserver's own pre-submission queue validation.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XYdtrEWWZfKRRGED7raEYg
Clear the cache inside core_begin_session_internal() so a job cached as
unsatisfiable in one session is re-evaluated fresh in the next (Req 7.2).
An initial weak-symbol cast-based approach was rejected: it corrupted
xce_sched_sim_driver_t.alt_events_file, since core_begin_session_internal()
is shared between driver.c (real xce_scheduler_t) and sim_loop.c (a
structurally different, deliberately isolated sim driver struct with no
cache field at the same byte offset) -- the strong cache_clear() symbol
won unconditionally for both, invisible to ASAN and untested by any
test_sim_*.c file.
Fixed via a driver-registered callback: xce_sched_core_t gains a
cache_clear_cb hook (NULL by default, per xce_sched_core_init()'s
full-struct memset), set only in main.c's driver-mode branch --
mirroring the existing on_decision/xce_core_set_decision_cb() pattern.
--sim mode returns before that registration is ever reached, so its hook
stays NULL and core_begin_session_internal()'s guarded call is a no-op.
The real xce_scheduler_t-casting adapter lives in alloc_cache.c rather
than driver.c, since ~11 tests white-box-include driver.c without
linking alloc_cache.c.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XYdtrEWWZfKRRGED7raEYg
Wrap sched_make_primary_alloc_() -- the single choke point all three
primary-resreq shapes (standalone, compound-nested, alter-nested via
compound delegation) already funnel through -- with a cache lookup
before allocation and a cache insert on exhaustion. On a hit, pend the
job immediately via the same make_pendreason()/add_job_preason() path
every exhaustion case already uses, without invoking either allocator
(Req 7.3, 7.4, 7.5).
A nested primary has no real per-primary schedobj, so
sched_primary_cache_obj_() synthesizes one on the stack per dispatch for
cache-key purposes; verified memory-safe since alloc_cache.c's
build_key_() copies the fingerprint/identity by value synchronously
before the stack frame unwinds.
Log bug-1768 (compound/alter-nested primaries never get real slot/rusage
availability checks -- seqjob/parjob's prepare_allocation hooks no-op
whenever ctx->schedobj->tag != XCE_RESREQ_PRIMARY) and bug-1769
(sched_make_alter_allocation() type-confuses xce_compound_schedobj_t*
as xce_sched_obj_t*, a ~112-byte heap over-read, currently untested).
Both are pre-existing and out of this feature's scope; discovered and
confirmed while building this task's dispatch tests.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XYdtrEWWZfKRRGED7raEYg
Task 4.3's multi-host merge itself was already implemented and verified
by task 4.2. The one unproven clause -- job-level shared-resource
allocation computed once regardless of host count -- is now covered by
test_shared_resource_debited_once_regardless_of_host_count, using a
PER_JOB (not PER_TASK) global resource so a per-bucket-repeated
computation can't coincidentally sum to the correct total. Mutation
testing (a deliberately injected duplicate per-host shared debit)
confirms the test fails exactly as expected; production code required
no change.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XYdtrEWWZfKRRGED7raEYg
Replace parjob's return-0 stub with a real multi-host allocator: for each
candidate plan, walk buckets in the given (descending) order, claiming a
distinct host per bucket via a fresh host_iter excluding hosts already
claimed elsewhere in the same plan attempt. On a bucket failure, reverse
every earlier successful claim's provisional debit via
sched_ctx_uncommit_host() before advancing to the next plan (Req 4.2, 4.4,
4.5, 4.6, 4.7).
Fix parjob_prepare_allocation() to compute each bucket's want from that
bucket's own slot count (ctx->nslots) instead of a fixed value, and reset
ctx->alloc_req.host at the top of each bucket's scan so host_iter's
pre-filter can't inherit a stale want from a differently-sized prior
bucket (safe: sched_try_alloc_on_host_() always repopulates it from the
real per-bucket want before its own authoritative check runs).
Add a maxTasks<=1 guard matching Req 4 AC1's precondition, needed because
a snapshot-restored job with unpopulated maxTasks==0 (pre-existing,
tracked separately as bug-1767) would otherwise reach parjob's now-real
allocator instead of being silently ignored as before.
On full success, merges every claimed bucket into one xce_job_alloc_t via
make_job_alloc()'s existing runtime-sized host array, satisfying task
4.3's multi-host commit scope as well.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XYdtrEWWZfKRRGED7raEYg
Add the real xce_alloc_plan_iter_t for parjob: for host-count k=1..maxTasks
(ascending, most-preferred-first), generate one balanced partition of
maxTasks into k buckets (floor/ceil split, remainder to the first rem
buckets), which is descending by construction. Pure function of maxTasks
alone -- never dereferences sched -- with a real pthread_testcancel()
cancellation point and stable post-exhaustion NULL (Req 2.1-2.6, 4.1).
my_alloc()/parjob_prepare_allocation() remain untouched stubs; wiring the
iterator into real allocation is task 4.2.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XYdtrEWWZfKRRGED7raEYg
Add a dedicated equivalence suite (test_seqjob_rewrap_equivalence.c)
covering multi-host fixtures the trivial single-host test doesn't reach:
select-filter-only rejection, resource-only rejection, a combined
select-then-resource-then-success ordering, and per-host debit
correctness. The iteration-order test is grounded in the deleted
pre-rewrap sched_collect_workers() code path (verified via git show
against the parent commit) and the unmodified xlist.c primitive, not
merely in host_iter.c's own traversal, so it proves equivalence to the
old behavior rather than internal self-consistency.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XYdtrEWWZfKRRGED7raEYg
Restructure seqjob's my_alloc() as a trivial single-bucket case over the
shared xce_alloc_plan_iter_t/xce_host_iter_t contracts, replacing its own
inline worker-collection loop. Guard seqjob_prepare_allocation() to
maxTasks == 1, closing the fan-out gap for the parallel allocator's later
fix (Req 9.1, 4.6).
Export sched_try_alloc_on_host_() from resource.c (drop static/va_list,
plain (worker, sched, ctx) signature) so seqjob calls the real per-host
try-alloc leaf instead of a private duplicate; delete the now-dead
sched_try_alloc_on_hosts() variadic wrapper. Also wires host_iter.c into
the production scheduler_srcs list (task 2.1 only added it to testsuite).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XYdtrEWWZfKRRGED7raEYg
Adds alloc_cache.c: xce_cache_key_t, cache_key_ops (the corrected
three-way, 0-means-equal .cmp -- an inverted-boolean version was a
real bug found during design review), and cache_lookup/insert/clear
against a real kvtab_t (constructed matching xce_select.c's
sel_node_table_init() precedent). Cache values are a boxed reason-
category int, not a bare hit/miss sentinel, satisfying Req 7 AC5's
requirement that a hit reproduce the original pend-reason category.
unsatisfiable_cache lives on xce_scheduler_t per Req 7 AC1.
Task 2.2 (scheduler-allocation spec). Requirements 7.1, 7.3, 7.4, 7.5.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XYdtrEWWZfKRRGED7raEYg
Adds host_iter.c: the one shared xce_host_iter_t implementation,
lazily filtering sched->core.workers by select expression
(sched_meet_host_selection, unchanged) and resource availability
(sched_check_host_satisfy, unchanged), with a real cancellation
point per candidate visited. xce_alloc_plan_t gains a ctx field
(xce_sched_ctx_t*) so the iterator can reach the scheduler/select
expression/staged resource-want -- additive to task 1.1's approved
interface, NULL-guarded at init(). Test proves the core design
property this spec exists to establish: a host too small for one
search remains a valid candidate for a later, smaller search via a
fresh iterator instance over the same worker set.
Task 2.1 (scheduler-allocation spec). Requirements 3.1-3.6.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XYdtrEWWZfKRRGED7raEYg
Adds sched_ctx_uncommit_host() (resource.c), the structural inverse
of sched_try_alloc_on_host_()'s existing XVEC_ADD_FLOAT commit call,
scoped to ctx->uncomitted only per requirements.md's Rollback scope
discussion. Test exercises the real sched_ctx_get_uncomit_host()
(schedobj.c) via white-box #include rather than a private
reimplementation, matching test_picker_config_fields.c's established
precedent.
Task 1.4 (scheduler-allocation spec). Requirement 4.2.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XYdtrEWWZfKRRGED7raEYg
Adds xce_alloc_mode_t (XCE_ALLOC_NORMAL=0, XCE_ALLOC_PREEMPT, with
commented-out RESERVE/BACKFILL placeholders) and alloc_mode on
xce_sched_ctx_t, zero-defaulted by sched_ctx_init()'s existing
memset. Purely additive: nothing branches on the field yet -- this
spec only declares the extension point scheduler-preemption needs.
Task 1.3 (scheduler-allocation spec). Requirement 9.5.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XYdtrEWWZfKRRGED7raEYg
Adds rusage_hash to xce_primary_resreq_t (computed once by
parse_resreq_primary() via a new hash_rusages() helper, hashing
rusage content rather than raw struct bytes since resname is a
non-interned strdup'd pointer), the xce_resreq_fingerprint_t value
type, and resreq_fp on xce_primary_schedobj_t, populated once at real
job-ingest time in core_build_primary_schedobj().
Task 1.2 (scheduler-allocation spec). Requirement 7.1.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XYdtrEWWZfKRRGED7raEYg
Defines xce_alloc_plan_t (partition of a job's task count into
per-host slot buckets), xce_alloc_plan_iter_t, and xce_host_iter_t
(init/next/fini vtables, stack-allocate-then-init-in-place convention
matching sched_ctx_init) as reusable abstractions any allocator can
adopt. Proven usable by a trivial no-op reference implementation and
a fresh-per-bucket host_iter composition test, no allocator wired in
yet.
Task 1.1 (scheduler-allocation spec). Requirements 2.1, 3.1.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XYdtrEWWZfKRRGED7raEYg
Found during task 1.1 implementation: the Data Structure Changes
Summary declared next() returning xce_worker_t* (the wire/config
type), but the Composition Algorithm pseudocode in the same document
already used xce_sched_worker_t* (the scheduler-internal type with
resvals/resInUse/host_eval_ctx) for the identical call -- these are
genuinely different types in this codebase, confirmed via
make_sched_worker()'s xce_worker_t* -> xce_sched_worker_t* conversion.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XYdtrEWWZfKRRGED7raEYg
Extensive requirements review found and fixed a real correctness bug
(host_iter shared across plan buckets instead of re-scanned fresh per
bucket, which would silently drop solvable multi-host allocations),
a self-contradiction around parjob_prepare_allocation's scope, a
plan/host-iterator init calling-convention mismatch, and an inverted
kvtab .cmp polarity that would have made the unsatisfiable-resreq
cache permanently non-functional. brief.md brought back into sync
with requirements.md's corrected architecture.
design.md and tasks.md generated and validated (kiro-validate-design
GO after two fixes; independent task-graph sanity review PASS).
Two pre-existing, out-of-boundary gaps found along the way logged as
bug-1766/bug-1767.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XYdtrEWWZfKRRGED7raEYg
on_scheduler_join()'s full job resync and sched_job_new_fun()'s
incremental per-commit push both hold c->sched.lock for their entire
body, making concurrent delivery of the same jobid structurally
impossible -- confirmed by direct code reading, no live repro needed.
This also fully explains the previously-unexplained "redelivery
storm" (up to 18x per jobid): every scheduler reconnect triggers a
full resync of the whole job set, and the scheduler's connection
already flaps repeatedly under heavy load (found earlier this
session), so N flaps produce N redundant resyncs with no race
involved. All scheduler-side and controller-side push-path candidates
are now exhausted; the actual stale-reader for j->submit remains
unfound after 9 sessions.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XYdtrEWWZfKRRGED7raEYg
Static tracing confirms the resreq/select-expression parser fully
copies/interns every string (no aliasing into the short-lived
job_submit_data_t), and the pend_updates dataq's enqueue/dequeue path
cannot return the same item twice. Neither explains the UAF. Updates
the running tally of ruled-out candidates so future sessions don't
re-chase them; the controller-side HOOK_DETAIL_JOB_NEW trace remains
the sharpest open lead.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XYdtrEWWZfKRRGED7raEYg
An idle scheduler (nothing queued to publish) never noticed a dead
controller connection: sched_needs_rejoin was only ever set as a side
effect of an active client-library call, and an idle round makes none.
After any controller failover with nothing already in flight, jobs
submitted to the new primary stayed PEND indefinitely.
Adds xcecli_is_connection_broken() (libxce) -- a pure, socket-free read
of the transport's already-async-maintained dead/alive flag, set for
free by the existing RDHUP disconnect handler with zero application
calls. sched_check_pending_rejoin() now retries xcecli_join() on either
the existing reactive flag or this new passive signal, so a healthy
idle scheduler still makes zero extra network calls while a genuinely
dead connection self-heals within one ~1s round -- live-verified
against a 3-node HA cluster (rejoin fired ~300ms after new-primary
election with nothing submitted in between).
Also closes out bug-1735 (job memory/CPU usage reporting): confirmed
already fixed on this branch since 2026-08-30, just never marked in
the bug log.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XYdtrEWWZfKRRGED7raEYg
Checkbox update for commit 6fabdba9 (deleted the file, but its
tasks.md diff wasn't included in that commit due to a git-add
pathspec issue on the already-git-rm'd path). Marks 4/4.1/4.2 done
and records the two out-of-boundary doc mentions found during
task 4.2's post-deletion review. All 12 tasks / 4 major groups are
now [x] -- the eliminate-frontend-makefile feature is complete.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XYdtrEWWZfKRRGED7raEYg
The terminal step: src/frontend/Makefile (194 lines, the second,
independently-maintained build/test path this whole spec exists to
eliminate) is deleted. Re-ran the full task 4.1 validation suite with
the file genuinely absent (build, meson test --suite apiserver, and
-- going beyond citing task 3.5 -- a real live run-resource-verify.sh
end-to-end run: VERDICT result=PASS, closing design.md's Testing
Strategy "Discovery-path regression" check specifically). Full-repo
sweep: 181 hits, zero unaccounted, byte-identical to task 4.1's
non-Makefile hits (the only delta is the Makefile's own 8
self-referential lines, now gone since the file doesn't exist).
Independently re-verified by the task reviewer.
Meson is now the single build/test system for apiserver and xcecli.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XYdtrEWWZfKRRGED7raEYg
Pure verification, no code changes. Confirmed all four checks against
one fresh build: /xce-build apiserver (migrated Go-test invocation,
zero make calls), meson test --suite apiserver (new backend CGO test
genuinely registered and passing), task 3.5's regression parity
(cited, same-session), and a fresh live-reference sweep (190 hits,
zero unaccounted -- corrects task 3.6's commit message, which said
188; verified byte-identical at both commits, a message-only miscount
not drift). Also independently reconfirmed the two failures blocking
a literal /xce-build all are exactly bug-1723/bug-1758 and nothing
else. Reviewer independently reproduced every check. Clear to proceed
to Makefile deletion (task 4.2).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XYdtrEWWZfKRRGED7raEYg
Pure audit, no code changes. Full repo-wide grep (188 hits) fully
classified: every hit is either already fixed by tasks 3.1-3.4 or
falls into one of design.md's named exclusion classes (.kiro/specs/**
historical docs, vendor/**, dated historical records, roadmap.md's
own backlog entry, OpenWolf's logs, run.sh:12's retained placeholder).
Zero unaccounted hits. Independently re-run and confirmed exact match
by the reviewer. Also flips the "1." and "3." container-header
checkboxes now that all their children are done.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XYdtrEWWZfKRRGED7raEYg
Pure verification, no code changes. Deployed a live single-node
cluster and ran tests/regression/run.sh against a Meson-built xcecli
binary, then again via the still-present Makefile path -- both
produced identical 28 passed/1 failed/1 skipped results. Reviewer
independently reproduced this three times and traced the one
pre-existing failure (jobs submit's job-ID format regex) through
source and git history to a 2026-05-07 commit, four months before
this spec existed -- logged as bug-1765 (not fixed, out of scope).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XYdtrEWWZfKRRGED7raEYg
Comment-only edits in 4 files: test_ha_bootstrap.c/test_ha_ctrlhello.c
rename their "make test-integration" pointers to reference the
regression suite (tests/regression/run.sh) directly; apiserver/xcecli
cmd/main.go drop the Makefile clause from their ldflags-injection
comments, keeping only src/tools/go_build. Zero production/test
logic changed -- verified via full controller unit-test suite rerun
(1261 tests, 0 failures) and xcecli build.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XYdtrEWWZfKRRGED7raEYg
Removes every live current-fact reference to src/frontend/Makefile
targets or Makefile-specific fixed paths across 11 docs (steering,
frontend/CLAUDE.md, root CLAUDE.md, deployment docs, and two skill
docs), replacing with the Meson-based equivalents established by
tasks 1-3.2: /xce-build apiserver, meson test --suite apiserver,
direct tests/regression/run.sh invocation.
Round-1 review rejected the first pass: xce-deploy-ha/SKILL.md had
over-corrected by deleting all mention of deploy-ha.sh's still-real
/tmp/apiserver legacy fallback (that script's discovery logic is
unchanged, unlike run-resource-verify.sh's, which genuinely lost its
fallback in task 3.1 -- xce-test-resources/SKILL.md's identical-
looking edit was correct for that reason). Fixed by restoring an
accurate, Makefile-free description of the real fallback at all four
touched sites. Re-reviewed and approved.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XYdtrEWWZfKRRGED7raEYg
The two same-anchor insertions in job.c left one shared closing brace on
theirs' side (record_job_reconcile_assign_fact); our mark_worker_jobs_exit
(and the reconcile functions that followed it) were swallowed into an
unclosed function body -- "invalid storage class" / "expected declaration
at end of input" on xce_controller build. Added the missing '}' that closes
mark_worker_jobs_exit after its unlock(c->jobmgr).
Co-Authored-By: Claude Code <noreply@anthropic.com>
hosts.go conflict-free auto-merge left both host counter implementations:
ours countRunningJobsPerHost() (bug-1490, per-RUN-job count) and theirs
computeHostSlotUsage() (bug-1743, per-host occupied-SLOT count bucketed
USUSP>SSUSP>RUN) wired into processHostData(), which runs after GetHostsInfo
and overwrote our num_run, leaving our helper dead code.
Resolution: keep theirs (slot-bucketed). Rationale: it matches the codebase's
canonical JobStateStr mapping (a RUN|USUSP job is USUSP, not RUN -- our
RUN-bit-only check mislabeled suspended jobs as running and left
num_ssusp/num_ususp permanently 0), sums num_slots so values are comparable to
max_slot, and is applied uniformly at the processHostData choke point for all
host endpoints. Deleted countRunningJobsPerHost + GetHostsInfo fill-loop and
rewrote hosts_numrun_test.go to cover computeHostSlotUsage's bucket/multi-slot
semantics plus our retained robustness cases (PEND/DONE and empty/bad
exec_hosts ignored). Theirs' host_control_test.go
TestProcessHostData_ComputesSlotUsageFromJobs provides the processHostData
integration coverage.
Co-Authored-By: Claude Code <noreply@anthropic.com>
Rewords deploy-ha.sh's block comment and two fallback echo lines, and
run.sh's XCE-not-found error, to stop naming the now-deleted
Makefile's build-server/build-cli targets, pointing at /xce-build
apiserver instead. Discovery/control-flow logic in both files is
unchanged (verified line-by-line: no if/elif/else lines touched);
run.sh's XCE default placeholder is untouched by design.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XYdtrEWWZfKRRGED7raEYg