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>
6.4 KiB
| id | status | status_updated | status_note | component | severity | timestamp | last_seen | occurrences | files | tags | related | legacy | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| bug-1681 | fixed | 2026-09-24 | scheduler | unknown | 2026-08-24T21:30:00Z | 2026-09-19T09:00:00Z | 2 |
|
|
|
true |
bug-1681: xce-audit-invariants scheduler-driver-equivalence audit finding (not user-reported, no live…
Description
xce-audit-invariants scheduler-driver-equivalence audit finding (not user-reported, no live symptom today): driver.c's normal-mode round (xce_sched_driver_run_normal_mode_round, driver.c:750,802) calls plugin_begin_session(sched)/plugin_end_session(sched) -- iterating every registered plugin and invoking its begin_session_fn/end_session_fn hook, called 'real, load-bearing per-session bookkeeping for EVERY registered plugin' per driver.c's own comment (lines 688-693). sim_loop.c has zero equivalent call anywhere (grep-confirmed across sim*.c/repl*.c). This is structurally the same shape of gap as bug-1613's original root cause (a driver.c-only per-session call with no sim_loop.c mirror), but under the codebase's only currently-supported configuration it is inert: sim mode's sim_bootstrap_picker() (sim_picker.c:60-70) only ever registers builtin_fcfs, which explicitly leaves begin_session_fn/end_session_fn NULL (plugins/builtin_fcfs.c:973-981), and the one plugin that does set a hook (legacy fcfs, plugins/fcfs.c:352, end_session_fn=fcfs_end_session, only sets privdata->new_session=1, no reach into resInUse/sharedInUse/job->accounts) is unreachable in sim mode either way (sim mode refuses any picker name other than builtin_fcfs).
Environment
Not recorded (migrated from buglog.json).
Reproduction
Not recorded (migrated from buglog.json).
Root cause
plugin_begin_session()/plugin_end_session() (plugin.c:370,407) are a distinct hook category from xce_job_picker_iface_t's begin_session/end_session (which both drivers DO call symmetrically -- scheduler.c:111,131 vs sim_loop.c:467,484). Nothing in sim_loop.c or sim.c acknowledges skipping the plugin-level begin/end_session hooks -- unlike the wire/capnp-only and REPL-only differences the audit also found, which are documented deliberate exclusions.
Fix
FIXED 2026-08-24. plugin_begin_session()/plugin_end_session() (src/scheduler/src/plugin.c) relocated from an implicit xce_scheduler_t* (void* arg cast to xce_scheduler_t*, only ever touching sched->core.plugins) to a direct xce_sched_core_t* parameter -- mirroring sched_account_jobs()'s identical scheduler-sim-decision-parity task 1.1 relocation, and confirming driver.c's own header comment claim that plugin_begin_session() 'needs the full xce_scheduler_t*' was stale/incorrect (its body never touched anything beyond core->plugins). src/scheduler/src/sim_loop.c's sched_sim_driver_run() round loop now calls plugin_begin_session(&(sim->core)) immediately before sched_account_jobs(&(sim->core)) (same relative order driver.c uses), and plugin_end_session(&(sim->core)) immediately before xce_core_end_session() -- exactly mirroring driver.c's xce_sched_driver_run_normal_mode_round() call sites, which were updated to pass &(sched->core) instead of sched. src/scheduler/src/scheduler.c's legacy sched_begin_session()/sched_end_session() (a third, pre-existing call site not part of the original driver-equivalence audit's scope, found only when the signature change broke its compile) updated the same way. plugin.h's declarations relocated to xce_sched_core_t* (a new forward-declare added earlier in the file so the type is visible at the declaration site). Stale comments describing the old xce_scheduler_t* coupling corrected in driver.c, core.h, and core_session.c (two locations).
Test: extended testsuite/test_sim_session_ordering.c's existing bug-1613 spy-based regression tests (test_sim_replay_calls_account_jobs_once_per_round_within_session_window, test_sim_replay_batch_mode_uses_the_same_round_boundary) with new plugin_begin_session()/plugin_end_session() spies (same call-count + core->session_active-window proof technique as the existing sched_account_jobs spy) -- proving 2 calls/round (recording mode) and 1 call (batch mode), each with session_active==1 at call time. Deliberately does NOT #include the real ../src/plugin.c in this file (unlike test_driver_session_ordering.c, which proves real per-plugin hook firing via a genuinely-registered observer plugin) -- sim.core.plugins does not exist until sched_sim_driver_run()'s own internal sched_sim_driver_start()/sim_bootstrap_picker() calls run, with no hook point for a test to register an observer plugin in between, so this file spies the call site directly instead, plus a verbatim real reimplementation of plugin_register_core() (needed for real by both this file's fixture and sim_picker.c's sim_bootstrap_picker()) and no-op link-satisfying stubs for plugin_register()/plugin_unregister() (both genuinely unreachable in this file's fixtures). Four other white-box test files (test_resval_ownership.c, test_admin_request.c, test_driver_decision_cb.c, test_driver_translate.c) each defined their own plugin_begin_session/plugin_end_session stub against the old void* signature -- all four updated to xce_sched_core_t* to match.
RED/GREEN independently verified (not just narrated): temporarily commented out both new sim_loop.c call sites, rebuilt, confirmed test_sim_replay_calls_account_jobs_once_per_round_within_session_window and test_sim_replay_batch_mode_uses_the_same_round_boundary both FAIL (Expected 2 Was 0 / Expected 1 Was 0) -- proving the assertions genuinely exercise the missing calls, not a tautology. Restored (git diff confirmed byte-identical to the fixed state), rebuilt, reconfirmed both PASS.
Full scheduler suite: 51/52 suites green; the one failure (test_recordstart_symlink_escape_replies_error) is the pre-existing, unrelated bug-1560 sandbox-environment flake (already documented as such in scheduler-core's own task 12.5 verification notes), not a regression from this fix.
Deliberately NOT fixed as part of this change (separate, unrelated pre-existing defect found while touching the same function bodies): plugin_end_session()'s per-plugin guard checks the wrong field (begin_session_fn instead of end_session_fn) -- logged separately as bug-1682.
Verification
Not recorded (migrated from buglog.json).
References
- Related: bug-1611, bug-1613