myxce/.wolf/bugs/bug-1740.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

5.1 KiB

id status status_updated status_note component severity timestamp last_seen occurrences files tags related legacy
bug-1740 fixed 2026-09-24 scheduler unknown 2026-08-31T22:00:00Z 2026-09-19T09:00:00Z 1
src/scheduler/src/core_events.c (XCE_CORE_EVENT_JOB_RUSAGE handler -- live fix site)
src/scheduler/src/schedobj.c (sched_apply_job_rusage -- dead-code path
left in place)
src/scheduler/src/scheduler.c (sched_update -- unreachable caller)
src/scheduler/src/resource.c (sched_reconcile_job_resource_usage/sched_reconcile_host_resource_usage)
src/scheduler/src/driver.c (xce_sched_driver_translate_pending_events/xce_core_push_events -- the live path that supersedes sched_update)
src/scheduler/testsuite/test_core_events.c (new regression test) [file field reconstructed post-recovery from context in error_message/root_cause/fix -- not the verbatim original string
see recovery note]
scheduler
resource-accounting
rusage
dead-code
wiring-gap
test-coverage-gap
core-events
mem
duplicate-implementation
found-via-self-review
true

bug-1740: The bug-1735/resource-accounting.md reservation-vs-usage reconciliation feature…

Description

The bug-1735/resource-accounting.md reservation-vs-usage reconciliation feature (sched_reconcile_job_resource_usage()/sched_reconcile_host_resource_usage(), resource.c, committed this session) was wired into schedobj.c's sched_apply_job_rusage() -- but that function has exactly ONE caller anywhere in this codebase: scheduler.c's sched_update(), which itself has NO live caller at all (confirmed by exhaustive grep -- superseded by driver.c's xce_sched_driver_translate_pending_events()/xce_core_push_events() mechanism, per driver.c's own comments: "Replaces sched_update()'s inline handling"). The REAL, live production path for job rusage -- XCE_CORE_EVENT_JOB_RUSAGE in core_events.c's xce_core_push_events(), the SAME core-level event both the normal-mode driver (driver.c) and the sim/replay driver (sim_loop.c) translate their own rusage events into -- has its OWN separate, duplicate inline implementation of "store rusage on job" that never called sched_apply_job_rusage() or the new reconciliation logic at all. Result: the entire reconciliation feature was completely inert in both normal-mode (live cluster) and sim/replay mode -- exactly the context the user's original bug reports (phantom RUN jobs, EXIT/DONE divergence, mem always 0) were observed through -- despite passing all 11 of its own unit tests, which called the reconciliation functions directly, bypassing the real event-dispatch path entirely.

Environment

Not recorded (migrated from buglog.json).

Reproduction

Not recorded (migrated from buglog.json).

Root cause

Classic 'tested the function in isolation, never proved the real event path reaches it' gap. schedobj.c's sched_apply_job_rusage() and core_events.c's XCE_CORE_EVENT_JOB_RUSAGE handler are two independent, duplicate reimplementations of the same 'store rusage on job' bookkeeping (a pre-existing architectural duplication, likely left over from the scheduler-core rewrite that introduced the core/driver split -- sched_update() and its whole XCE_SCHED_OP_* dataq-dispatch mechanism appear to be the OLD, pre-rewrite path, fully superseded by driver.c's XCE_CORE_EVENT_* translation but never deleted). The two implementations had already drifted: they call DIFFERENT picker hooks (on_job_rusage vs plugin_job_rusage_update). Adding new logic to the dead one instead of the live one was an easy mistake to make without first confirming which path is actually reachable in production -- found only by directly grepping for every call site of sched_apply_job_rusage() and sched_update(), not by reading either function's own code or comments, which gave no hint of the duplication.

Fix

FIXED (2026-08-31). Added a call to sched_reconcile_job_resource_usage() directly inside core_events.c's XCE_CORE_EVENT_JOB_RUSAGE handler (the real, live path), right after its existing job->rusages bookkeeping. schedobj.c's sched_apply_job_rusage() keeps its own copy of the reconciliation call too (harmless dead code, but still exercised by its own existing unit tests, so left in place rather than removed as an unrelated cleanup). Added a genuine end-to-end regression test, test_job_rusage_reconciles_mem_reservation_against_usage (test_core_events.c), that drives the REAL xce_core_push_events() with a dispatched job carrying a real 4MB mem allocation, then a JOB_RUSAGE event claiming 7MB actual usage, and asserts the 3MB overage lands on both the account and the resource pool -- this is the test that would have caught the original wiring gap immediately, since it exercises the actual event dispatch instead of calling the reconciliation function directly. Required white-box #include-ing resource.c into test_core_events.c (previously only core.c/core_events.c) plus two link-satisfying stubs (plugin_prepare_allocation/sched_ctx_get_uncomit_host, matching test_resource_accounting.c's own established pattern).

Verification

Not recorded (migrated from buglog.json).

References

Not recorded (migrated from buglog.json).