Commit graph

291 commits

Author SHA1 Message Date
955efecd19
fix(controller): WAL_JOB_START loses pidinfo/start_time on restart replay (bug-1687)
Same class as bug-1678: apply_job_start_transition() sets the real
job->job.pidinfo (worker-reported PIDs/PGIDs/exdata) and start_time, but
the WAL fact for WAL_JOB_START only carried the shared wal_jobstate_t
{jobid, state} header -- both apply seams (apply_committed_entry() for
primary boot replay, apply_wal_entry() for standby catch-up) restored
state only. pidinfo stayed permanently NULL after a restart; start_time
was fabricated with time(NULL) at replay instead of being carried,
drifting from the true value.

Mirrors bug-1678's exact fix shape: capnp_encode_pidinfo()/decode_pidinfo()
(persist.c, using the pre-existing JobPIDInfo capnp struct -- no schema
change needed) serialize job_pidinfo_t into the WAL payload, trailing a
new uint64_t start_time field appended after wal_jobstate_t.
persist_submit_job_start_fact() replaces the old state-only
persist_submit_job_fact() call for this type. Both apply seams updated;
a payload of exactly sizeof(wal_jobstate_t) (any entry written before
this fix) decodes with pidinfo left NULL and start_time fabricated via
time(NULL), exactly matching pre-fix behavior -- full backward
compatibility, verified by every pre-existing WAL_JOB_START test using
the old bare-struct shape.

Also fixed a genuine, unrelated deadlock this change's larger payload
exposed in test_commit_barrier_reply.c: cbrp_count_jobstate_entries_ex()
matched WAL entries by exact payload size (==), so once WAL_JOB_START
legitimately grew it silently undercounted to 0 and a downstream test
spun forever polling an uncounted target. Fixed the match to >= (jobid/
state always live at a fixed leading offset) -- bisected via /proc/*/wchan
futex inspection (no ptrace in this sandbox), confirmed as the actual
cause, not the teardown-ordering red herring first suspected.

Found by an /xce-audit-invariants controller pass (field-complete apply
parity dimension, added after bug-1678). 6 new tests in
test_wal_job_start_fact.c; RED/GREEN independently verified (4 tests
genuinely failed pre-fix with exact expected-vs-fabricated value
mismatches, 2 backward-compat tests correctly passed both before and
after). Full controller suite: 1146 tests, 2 failures -- both the
pre-existing, unrelated bug-1561 overlayfs sandbox flake.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AgYRU3zDTqNGV5Jk8aiKLg
2026-08-25 09:23:00 +00:00
bc3f76b42e
fix(scheduler): invalidate stale builtin_fcfs picker cursors across calls (bug-1686)
User-reported live crash: xce_scheduler SIGSEGV in fcfs_cursor_enter_queue
(builtin_fcfs.c), called from builtin_fcfs_next_job <- sched_job_picker
(picker thread).

priv->cursor/priv->job_cursor (fcfs_plugin_data_t) are raw ordq_item_t*
pointers that persist across SEPARATE next_job() calls, each with its own
independent priv->lock acquisition/release. The existing mutual-exclusion
fix (task 2.4) correctly protects the cursor walk from corruption DURING
one locked call, but does nothing to stop a LATER call from dereferencing
a node a properly-locked, INTERVENING call already freed.
on_queue_update()'s re-sort (remove old ordq_item_t node, insert a fresh
one via ordq_put) is exactly such a free -- with only one queue in the
deployment, the picker's cursor is essentially always parked on it
between calls (sched_queue_is_ready/sched_job_is_ready are unconditional
true stubs today, so the cursor never advances away while jobs remain),
making a queue config update land in that window trivially reachable.

Fixed by invalidating (NULLing) whichever cursor points at a node right
before that node is freed, at all three sites that can free a node the
cursors might reference: on_queue_update's reuse-and-resort path
(priv->cursor + priv->job_cursor in lockstep), builtin_fcfs_on_job_state_change's
!JOB_IS_PEND removal, and fcfs_remove_job_everywhere's cross-queue removal
loop (both job_cursor only, via an ordq_peek-then-invalidate-then-delete
pattern). Always safe: next_job() treats a NULL cursor as "no more
candidates this round", and begin_session() resets both cursors fresh
every round regardless -- worst case is a bounded one-round delay, never
a crash or a permanently lost job.

New test_builtin_fcfs_cursor_uaf.c reproduces the bug fully single-threaded
(next_job() releases the lock between calls, so no real concurrency is
needed to hit the window). RED-phase independently confirmed a real ASAN
heap-use-after-free with the exact same call chain as the live crash
(freed by on_queue_update, read by next_job()); GREEN confirms 0 failures
and no job permanently lost across the invalidation.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AgYRU3zDTqNGV5Jk8aiKLg
2026-08-25 08:49:43 +00:00
b9f13355df
chore(wolf): checkpoint buglog.json (1027 bugs) 2026-08-25 08:35:40 +00:00
b040dfc068
fix(scheduler): remove double-free in limit plugin config update (bug-1685)
User-reported live crash: xce_scheduler SIGABRT via glibc malloc_printerr
(heap corruption) inside kill_limit(), called from limit_update_config()
during a live config reload.

limit_update_config() called kill_limit(limit) in its error path after a
failed key_add(privdata->limits, ...). But privdata->limits is a kvmgr
created with .datakiller = kill_limit, so on a kvtab-level failure (e.g.
-EEXIST for a duplicate limit name -- exactly the rc=-17 captured in the
crash's own backtrace), xkvmgr_add() already rolls back via vclear() ->
xzarray_clear(), which fires kill_limit(limit) once automatically. The
caller's explicit second call was a double free.

Removed the redundant call; the kvmgr's own configured datakiller already
owns cleanup on this failure path (mirrors the ownership asymmetry
job.c's restore_job_array() already documents for the opposite case,
where its kvmgr has no datakiller configured and the caller must free
failed items itself). One accepted, documented residual gap: a vstore()-
level OOM failure (never reaching the kvtab step) would leak `limit`
instead -- vanishingly rare, and a leak is strictly better than the
guaranteed crash on the common duplicate-name case; xkvmgr_add's return
code doesn't distinguish which sub-case occurred, so there's no clean way
to free conditionally without a kvmgr API change (out of scope).

New test_limit_plugin.c (this plugin had zero test coverage before) --
RED-phase independently reproduced a genuine ASAN heap-use-after-free
matching the live crash's exact call chain; GREEN confirms 0 failures.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AgYRU3zDTqNGV5Jk8aiKLg
2026-08-25 08:18:34 +00:00
307f5b82e1
chore(wolf): checkpoint buglog.json (1026 bugs) 2026-08-25 08:00:03 +00:00
6e4f25c464
chore(wolf): checkpoint buglog.json (1025 bugs) 2026-08-25 07:48:53 +00:00
b48364f006
chore(wolf): checkpoint buglog.json (1024 bugs) 2026-08-25 07:17:06 +00:00
56c658dec8
chore(wolf): checkpoint buglog.json (1024 bugs) 2026-08-25 07:05:47 +00:00
eadf88a975
chore(wolf): sync anatomy/cerebrum/memory/hooks/token-ledger bookkeeping
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AgYRU3zDTqNGV5Jk8aiKLg
2026-08-25 06:40:25 +00:00
6d2668ced3
chore(wolf): checkpoint buglog.json (1023 bugs) 2026-08-25 04:50:30 +00:00
64fb19183a
chore(wolf): checkpoint buglog.json (1023 bugs) 2026-08-25 04:43:10 +00:00
0dccb3a9be
chore(wolf): checkpoint buglog.json (1022 bugs) 2026-08-25 04:31:36 +00:00
070c7869f4
chore(wolf): backfill gitea_issue linkage for bug-1674/1675/1676
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AgYRU3zDTqNGV5Jk8aiKLg
2026-08-25 04:07:31 +00:00
dd14553566
chore(wolf): sync anatomy/memory/hooks/token-ledger bookkeeping
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AgYRU3zDTqNGV5Jk8aiKLg
2026-08-25 04:04:19 +00:00
1b654ba1ee
chore(wolf): mark bug-1680 fixed
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AgYRU3zDTqNGV5Jk8aiKLg
2026-08-25 04:04:13 +00:00
0b04ab89b1
chore(wolf): sync anatomy/memory/hooks/token-ledger bookkeeping
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AgYRU3zDTqNGV5Jk8aiKLg
2026-08-25 03:44:31 +00:00
e787cead73
chore(wolf): mark bug-1679 fixed; log bug-1680 (xcecli jobs list -a returns nothing, discovered via bug-1679 verification)
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AgYRU3zDTqNGV5Jk8aiKLg
2026-08-25 03:44:31 +00:00
3aaba2d323
chore(wolf): sync anatomy/memory/hooks/token-ledger bookkeeping
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AgYRU3zDTqNGV5Jk8aiKLg
2026-08-25 03:13:06 +00:00
8f2e684f21
chore(wolf): mark bug-1678 fixed; log bug-1679 (live job-status pushes never fire, discovered via bug-1678 verification)
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AgYRU3zDTqNGV5Jk8aiKLg
2026-08-25 03:13:06 +00:00
2a24551542
chore(wolf): sync anatomy/memory/hooks/token-ledger bookkeeping
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AgYRU3zDTqNGV5Jk8aiKLg
2026-08-24 15:14:56 +00:00
c5aa64da71
chore(wolf): mark bug-1676 fixed (full round-trip)
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AgYRU3zDTqNGV5Jk8aiKLg
2026-08-24 15:14:56 +00:00
590e2a726e
chore(wolf): sync anatomy/memory/hooks/token-ledger bookkeeping
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AgYRU3zDTqNGV5Jk8aiKLg
2026-08-24 14:23:51 +00:00
8174186948
chore(wolf): mark bug-1673/1674/1675/1677 fixed
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AgYRU3zDTqNGV5Jk8aiKLg
2026-08-24 14:23:51 +00:00
e83d207afd
fix(wolf): log bug-1678, gitea #43 IS reproducible -- corrects earlier false negative
The prior verification pass (commit 1c9cfc1, same session) restarted
controller+scheduler+worker but left the apiserver running, so it only
ever saw the apiserver's stale, still-correct in-memory job cache from
before the restart -- never the controller's actual post-recovery
state. Repeating the test with the apiserver also restarted (matching
the original report's "restart all daemons" exactly) reproduces the
bug immediately: EXEC_HOST stays permanently blank across 15 polls/30s
while STATUS correctly shows RUN throughout.

Root cause fully traced: dispatch_a_job_() (job.c:1343-1372) sets
job.allocs in memory at dispatch-commit time but the durable
WAL_JOB_DISPATCH fact (persist_submit_job_fact, wal_jobstate_t
{jobid,state}) never carries the allocation. On a full restart,
restore_job() replays only the pre-dispatch WAL_JOB_SUBMIT payload
(allocs empty by construction) and apply_committed_entry()'s
WAL_JOB_DISPATCH case routes through apply_job_update_transition(),
which by design "only ever touches state" -- job.allocs is never
reconstructed. Controller log corroborates: recovery trace shows the
WAL_JOB_DISPATCH replay applying silently (no log on the APPLIED
path), followed by each job's WAL_JOB_START fact correctly logging a
now-redundant "duplicate UPDATE ... no-op".

Fix direction confirmed with the user: WAL_JOB_DISPATCH's payload
needs to carry the scheduler's resource allocation, not just
{jobid, state}, with a corresponding decode/apply path in
apply_committed_entry() -- not implemented here, this is a
reproduce-and-root-cause pass only.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AgYRU3zDTqNGV5Jk8aiKLg
2026-08-24 13:52:07 +00:00
1c9cfc1f10
fix(wolf): log 4 reproduced Gitea issues (#40-42, #44) + 1 discovered gitea-sync bug
Live-reproduced and root-caused on branch scheduler-framework via a
single-node deploy:

- bug-1673 (gitea #40): apiserver hosts-list MAX_SLOT stuck at 0 for
  workers not exact-name-matched in hosts.yaml -- display-only, C-side
  scheduling correctly uses the wildcard 'default' entry's slot count.
  Reporter's secondary "hard 8-job cap" claim did NOT reproduce.
- bug-1674 (gitea #41): `xcecli jobs list -l` fails outright (not just
  missing timestamp fields as reported) -- two call sites validate
  field lists against JobSubmitRequest instead of JobInfo.
- bug-1675 (gitea #42): connection-refused right after apiserver
  startup surfaces a raw OS error string instead of a friendly message.
- bug-1676 (gitea #44): FROM_HOST always blank -- capnp schema has no
  from_host field, and OnJobUpdated's blind store overwrite wipes the
  apiserver-local value set at creation on the first controller push.

Gitea issue #43 (EXEC_HOST lost after daemon restart) was NOT
reproduced after two restart cycles with WAL-preserving state --
EXEC_HOST/state recovered correctly in every poll. Not logged as a bug;
a related design gap (WAL_JOB_DISPATCH only carries {jobid, state}, not
allocs) was flagged as an unconfirmed latent risk, not a live finding.

bug-1677: scripts/gitea-sync.py's sync_bugs() reads buglog.json as a
bare list but the real format is the {version, bugs: [...]} wrapper --
would crash on any real run. Found via static review while checking
the gitea_issue cross-reference field convention; not executed live.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AgYRU3zDTqNGV5Jk8aiKLg
2026-08-24 13:40:47 +00:00
0f2ca9ebb5
chore(wolf): sync anatomy/memory/hooks bookkeeping
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AgYRU3zDTqNGV5Jk8aiKLg
2026-08-24 13:04:24 +00:00
755976fcf2
fix(wolf): renumber 40 buglog entries that collided after Aug 13-17 counter restart
39 duplicate IDs (one tripled: bug-909) in the bug-875-bug-949 range,
where a restarted counter re-used IDs already held by June 21-22
entries. Kept each original (earliest timestamp) untouched and
renumbered the 40 later collisions to bug-1633..bug-1672 (new global
max, up from 1632), preserving array order. Verified no related_bugs
reference pointed at a renumbered entry -- the 10 existing references
to bug-924 all predate the Aug 17 collision and correctly resolve to
the original ENOSPC/overlayfs entry.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AgYRU3zDTqNGV5Jk8aiKLg
2026-08-24 13:04:04 +00:00
f2b5f9c916
chore: untrack .wolf/dashboard-token, gitignore it going forward
Committed at 90be04bd. Dashboard is LAN-only and never exposed
externally, so no history scrub -- just stop tracking the token file
so it's not re-added on the next wolf bookkeeping commit.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AgYRU3zDTqNGV5Jk8aiKLg
2026-08-24 13:00:55 +00:00
5573ae22c1
chore(wolf): sync anatomy/cerebrum/memory/hooks/token-ledger bookkeeping
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AgYRU3zDTqNGV5Jk8aiKLg
2026-08-24 12:56:39 +00:00
548ad9b2e2
chore(wolf): checkpoint buglog.json (1013 bugs) 2026-08-23 22:04:06 +00:00
5591f3603f
chore(wolf): checkpoint buglog.json (1013 bugs) 2026-08-23 21:17:20 +00:00
d4ada4474b
chore(wolf): checkpoint buglog.json (1012 bugs) 2026-08-23 19:35:33 +00:00
65390fb111
chore(wolf): checkpoint buglog.json (1011 bugs) 2026-08-23 18:02:38 +00:00
f756c4b09f
chore(wolf): checkpoint buglog.json (1011 bugs) 2026-08-23 17:21:12 +00:00
d87f4bb333
chore(wolf): checkpoint buglog.json (1011 bugs) 2026-08-23 10:13:25 +00:00
006b1b6aea
chore(wolf): checkpoint buglog.json (1011 bugs) 2026-08-23 08:36:15 +00:00
0649050ae8
chore(wolf): checkpoint buglog.json (1010 bugs) 2026-08-23 08:16:37 +00:00
018c1a818c
chore(wolf): checkpoint buglog.json (1009 bugs) 2026-08-23 08:04:48 +00:00
5e34f1f571
chore(wolf): checkpoint buglog.json (1008 bugs) 2026-08-23 04:50:57 +00:00
f8b24a993f
chore(wolf): checkpoint buglog.json (1007 bugs) 2026-08-23 04:10:48 +00:00
45acce4184
chore(wolf): checkpoint buglog.json (1005 bugs) 2026-08-23 02:56:14 +00:00
5d194db9b1
chore(wolf): checkpoint buglog.json (1004 bugs) 2026-08-22 12:48:23 +00:00
75edb7a329
chore(wolf): checkpoint buglog.json (1003 bugs) 2026-08-22 09:50:28 +00:00
c38207458f
chore(wolf): checkpoint buglog.json (1002 bugs) 2026-08-22 08:24:41 +00:00
76ca61547e
chore(wolf): checkpoint buglog.json (1001 bugs) 2026-08-22 07:13:00 +00:00
230083ae5d
chore(wolf): checkpoint buglog.json (1001 bugs) 2026-08-22 03:24:37 +00:00
5f7e1e98be
docs(scheduler): document driver-equivalence invariant, scope decision-parity fix spec
Elevates the normal-mode/simulation-mode driver equivalence guarantee
from a one-line architectural description in CLAUDE.md to an enforced
steering constraint (scheduler-driver-equivalence.md), backed by
bug-1611/bug-1613 as concrete evidence of violation. Scopes the fix as
its own spec (scheduler-sim-decision-parity, requirements + design)
rather than reopening the closed scheduler-core spec or folding it
into scheduler-sim-repl-verbosity's explicitly narrower boundary.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-22 03:07:05 +00:00
5d25925a01
merge working 2026-08-22 02:57:19 +00:00
20dbf35b8c
chore(wolf): sync anatomy/memory/hooks/token-ledger bookkeeping
Routine OpenWolf/harness bookkeeping drift since the last sync (94fd150) --
no source changes.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-22 02:53:38 +00:00
25e35bacaf
docs(scheduler): document driver-equivalence invariant, scope decision-parity fix spec
Elevates the normal-mode/simulation-mode driver equivalence guarantee
from a one-line architectural description in CLAUDE.md to an enforced
steering constraint (scheduler-driver-equivalence.md), backed by
bug-1611/bug-1613 as concrete evidence of violation. Scopes the fix as
its own spec (scheduler-sim-decision-parity, requirements + design)
rather than reopening the closed scheduler-core spec or folding it
into scheduler-sim-repl-verbosity's explicitly narrower boundary.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-22 02:52:00 +00:00