myxce/.wolf/bugs/bug-1799.md
Rongsong Shen d26ff8870d
feat(buglog): one Markdown record per bug, checked against a record standard
The buglog was one 27K-line JSON file whose fields covered only symptom,
root cause and fix, as free text: 2% of entries named the code version a
bug was found on, 12% the build profile, 36% a way to reproduce, and 58% of
the file was OpenWolf hook auto-detections ("Missing guard clause",
"Function not marked async" on .md files).

- .claude/skills/buglog/bug-record.md: the record standard. Eight
  sections (Summary as the title, Description, Environment, Reproduction;
  Root cause, Fix, Verification, References once fixed), each with the
  content it must contain -- e.g. Environment needs the OS, the libc the
  failing binary loads (from its ELF interpreter: glibc 2.42 in the nix
  shell vs 2.40 on the host), toolchain, build profile and commit.
- .wolf/bugs/<id>.md: 500 records migrated from buglog.json; every field
  verified to round-trip. They carry legacy: true and an explicit "not
  recorded" where the old log had no such field; component derived from
  file paths, severity from keywords (else unknown), fix_commits from
  hashes in status_note/fix that git resolves (91 of 463 fixed).
- .wolf/buglog-archive.json: the 710 hook auto-detections, verbatim.
- .wolf/buglog.json is now an index generated from the records, keeping
  the fields OpenWolf's hooks and `openwolf bug search` read.
- scripts/buglog.py rewritten: new (template + Environment collected from
  the machine and --binary), check, status (refuses a status the record
  doesn't support; fixed needs Root cause, Fix, Verification and
  --commit hashes git resolves), note, recur, set, show, list, triage,
  env, index. 29 unittest cases in scripts/tests/test_buglog.py.
- scripts/git-hooks/pre-commit runs `check --staged`: incomplete staged
  records, a legacy record hand-changed to fixed without the fix-time
  sections, a new record claiming legacy: true, or a stale index refuse
  the commit.
- scripts/gitea-sync.py reads the records: issue body = the record's
  sections plus linked fix commits (origin remote, GITEA_COMMIT_REPO to
  override), and a body hash so an issue's text is refreshed when the
  record changes.
- bug-1866 rewritten to the full standard as the worked example.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-28 03:40:11 +00:00

6 KiB

id status status_updated status_note component severity timestamp last_seen occurrences files tags related fix_commits legacy gitea_issue gitea_synced_status
bug-1799 fixed 2026-09-24 25fb5c11 scheduler unknown 2026-09-11T07:32:00Z 2026-09-19T09:00:00Z 1
src/scheduler/src/scheduler.c
src/scheduler/src/core_session.c
scheduler
sim
replay
resource-accounting
resInUse
sched_account_jobs
worker-rejoin-resync
IS_WORKER_RECONCILING
over-commit
slots
max-capacity-exceeded
core_reset_workers
bug-1751-interaction
user-reported
found-live
bug-1751
bug-1739
bug-1734
bug-1624
bug-1727
bug-1613
25fb5c11
true 268 fixed

bug-1799: User-reported (xce_scheduler -i -s , live cluster-captured replay…

Description

User-reported (xce_scheduler -i -s , live cluster-captured replay /tmp/scheduler-replay-20260911-064117.replay, worker pc13v, 22 real slots): info worker pc13v at session=98 shows resource slots: value=22.00 used=22.00 while the printed assigned job: list for that same worker sums to 26 slots (jobs [5]-[9] each *4 = 20, plus six single-slot jobs from a second batch = 6); the very next session (99, no new DISPATCH/PEND events, same assigned-jobs list) shows used=26.00 -- exceeding the worker's own value=22.00 capacity, an admission-invariant violation (max slots exceeded). Session 98's own event stream shows two back-to-back WORKERS_PUSH+JOB_PUSH (njobs=20) events for the same worker/jobids, consistent with a live scheduler-connection resync/reconnect being faithfully replayed (matching bug-1734/bug-1739's documented resync scenario), plus sched_add_job failed ... rc=-17, merging state into existing tracked job instead of discarding warnings for nearly every job in the resync batch, and one resync claims RUN for jobid ...[7]/[8] with no allocation data -- refusing to merge a phantom RUN (bug-1739's guard firing as designed).

Environment

Not recorded (migrated from buglog.json).

Reproduction

Not recorded (migrated from buglog.json).

Root cause

Interaction between two independently-reasonable pieces of code that were never reconciled with each other. (1) bug-1751 (2026-09-01, worker-rejoin-resync task 5) deliberately added an IS_WORKER_RECONCILING(w->status) skip to core_reset_workers() (core_session.c:136-142, the sim/normal-mode-shared per-round resInUse-reset helper called from xce_core_begin_session()): a worker mid-resync has its w->resInUse zeroing SKIPPED that round, on purpose, so its in-flight usage isn't wiped while reconciliation is still in progress. (2) sched_account_jobs() (scheduler.c:1434-1461) is the ONLY thing that re-derives resInUse/sharedInUse each round -- it walks every job in core->jobs and, for each one with JOB_IS_RUN(job->state) && job->accounts != NULL, calls sched_charge_account() (an unconditional ADD via sched_update_res_accounts(..., charge=1), resource.c:608-633) -- with NO equivalent RECONCILING check and NO reset of its own. Its own documented contract (core.h:311-322) is explicit that callers 'must call this strictly after the session's resval reset' -- a precondition bug-1751's reset-skip silently violates for exactly the RECONCILING window it targets. Consequence: for every round a worker stays RECONCILING (entered via a WORKERS_PUSH re-announce during a scheduler reconnect/resync, exactly what session 98's two WORKERS_PUSH events reflect), every currently-RUN job's slot charge on that worker gets RE-ADDED on top of the NOT-reset resInUse from the prior round, instead of the intended zero-then-resum. This produces exactly the observed shape: used= (accumulating additively) trails one round behind the true assigned jobs sum at first (22 vs 26 at session 98, since only some of the newly-RUN jobs' charges had been double-counted-in by that round's pass), then overtakes real capacity outright (26 at session 99). This is NOT the same mechanism as bug-1739 (JOBS_PUSH resync-merge phantom-RUN guard, which governs whether job->allocs/state get attached at all) nor bug-1624 (already-fixed dispatch-to-RUN synchronization gap) nor bug-1727 (already-fixed info worker display double-count of value=) -- all four live in the same resource-accounting neighborhood but are structurally distinct bugs. Root cause identified via static trace only (codegraph_explore + direct read of core_session.c/scheduler.c/core.h); not yet reproduced with an isolated minimal repro or instrumented trace confirming the exact round-by-round resInUse progression.

Fix

NOT YET FIXED -- investigation only, per user request (this session's live replay-driven Q&A, not yet escalated to an implementation task). Likely fix direction: give sched_account_jobs() the SAME IS_WORKER_RECONCILING awareness core_reset_workers()/sched_reset_worker_() already have (bug-1751) -- either (a) skip charging jobs whose host is currently RECONCILING (mirroring the reset-skip so the pair stays consistent: neither reset nor resum touches a RECONCILING worker's resInUse until it clears), or (b) have core_reset_workers() NOT skip the reset for a RECONCILING worker but instead have sched_account_jobs() re-derive from job state alone regardless of RECONCILING (i.e. resInUse is always a clean per-round sum, and RECONCILING-safety is enforced elsewhere, e.g. admission control refusing new dispatches to a RECONCILING host, which per bug-1751/bug-1745 already exists as a separate candidate-filter check). Needs the worker-rejoin-resync spec's own design intent re-read (.kiro/specs/worker-rejoin-resync/) before picking (a) vs (b), plus a live repro (re-run this exact replay file with an instrumented sched_account_jobs() logging resInUse before/after each round) to confirm the round-by-round mechanism before implementing, and full TDD (scheduler-driver-equivalence steering requires normal-mode/sim-mode parity for any fix here).

Verification

Not recorded (migrated from buglog.json).

References

  • Related: bug-1751, bug-1739, bug-1734, bug-1624, bug-1727, bug-1613
  • Gitea: #268