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>
4 KiB
| id | status | status_updated | status_note | component | severity | timestamp | last_seen | occurrences | files | tags | related | legacy | |||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| bug-1826 | fixed | 2026-09-24 | scheduler | crash | 2026-09-17T21:50:00Z | 2026-09-19T09:00:00Z | 1 |
|
|
true |
bug-1826: deliberately out-of-scope follow-up discovered while fixing bug-1825 (sim-mode limit plugin…
Description
NOT A CRASH -- known, deliberately out-of-scope follow-up discovered while fixing bug-1825 (sim-mode limit plugin bootstrap, scheduler-sim-diagnostics task 6). Bug-1825's fix makes the limit plugin's CONFIGURED SETTING visible in sim mode (info limit now shows a real replay's actual limit configuration). But limit matching -- the mechanism that actually populates live usage instances (xce_vres_t) as jobs get evaluated -- is still never invoked during replay. So info limit's live-usage display will correctly show 'no active usage instance yet' for every limit against any real replay, even one whose original live session actively enforced and charged against that limit.
Environment
Not recorded (migrated from buglog.json).
Reproduction
Not recorded (migrated from buglog.json).
Root cause
limit_job_election() (plugins/limit.c, the function that calls match_job_global_limit_()/match_job_host_limit_() and populates xce_vres_t usage instances) is only ever invoked via plugin_job_election() (plugin.c:554), which -- like plugin_config_update()/plugin_prepare_allocation() -- takes a void* arg cast unconditionally to xce_scheduler_t* and reads sched->core.plugins. plugin_job_election() has exactly ONE call site in the entire codebase: scheduler.c (normal-mode only). Nothing in sim_loop.c's replay decision path calls plugin_job_election() or any core-safe equivalent, so no limit ever gets matched against any job during simulation/replay, regardless of whether the limit plugin is registered (bug-1825 fixes registration+config, not this).
Fix
FULLY FIXED (2026-09-19, scheduler-sim-live-allocation tasks 1.1-5.2). The core-safe plugin_job_election() variant this entry called for was built in task 1.2 (retyped to take xce_sched_core_t* directly, iterating core->plugins); it is now genuinely invoked in sim mode via two paths: (a) the unified sched_job_picker()/sched_allocator() thread pair (task 3.1, wired into sim mode in task 4.1) calls the full core-safe election/allocation chain for every job the live picker reaches, which includes limit matching via limit_job_election() (task 2.3, also fixed bug-1828, a pre-existing host-scoped-limit enforcement defect blocking this same path); (b) sim_reconcile_unaccounted_jobs() (task 4.2) additionally calls plugin_job_election() directly for any job only recovered via recorded-decision reconciliation, so a job never misses limit registration regardless of which path resolves it. Host-scoped limit fan-out during live seqjob/parjob dispatch specifically required a further fix (task 4.3, closing a throwaway-wrapper-registry seam gap) before it worked end-to-end. Proven via the full TDD + contention-fixture discipline this entry called for: bare-core contention fixtures (task 5.1, test_sim_contention_limit_driver_parity.c) and this entry concrete acceptance test -- a genuine real capnp-encoded .replay driven through the real, unmodified sched_sim_driver_run(), with both a global and a host-scoped limit configured and matched, both showing real non-zero usage via the same limit_get_usage_instances() accessor info limit reads (task 5.2, test_sim_e2e_limit_visibility.c) -- independently reviewed with a targeted revert-to-pre-task-4.3 RED-phase reproduction confirming the host-scoped half genuinely regresses without the fix.
Verification
Not recorded (migrated from buglog.json).
References
Not recorded (migrated from buglog.json).