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>
8.1 KiB
| id | status | status_updated | status_note | component | severity | timestamp | last_seen | occurrences | files | tags | related | legacy | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| bug-1610 | fixed | 2026-09-24 | scheduler | unknown | 2026-08-20T21:10:00Z | 2026-09-19T09:00:00Z | 1 |
|
|
|
true |
bug-1610: User-reported (same live REPL session): under 'continue', an unchanged PEND decision for the same…
Description
User-reported (same live REPL session): under 'continue', an unchanged PEND decision for the same jobid prints an identical '[sim] PEND jobid=...' line every scheduling round it remains pending -- e.g. jobs [22]-[29] (8 jobs) print their 8 byte-identical PEND lines 5+ times in a row across consecutive rounds with zero new information each time.
Environment
Not recorded (migrated from buglog.json).
Reproduction
Not recorded (migrated from buglog.json).
Root cause
sim_print_decision() (sim_loop.c:388-411) unconditionally prints one DISPATCH/PEND/PREEMPT line per drained xce_decision_t with no de-duplication and no session/round context attached to the line itself -- called every round a job's allocation is (re-)attempted, so a job that stays PEND for N consecutive rounds produces N identical lines. xce_repl_on_decision() (repl_break.c:776-811), called alongside it, already tallies last_n_pend/last_n_dispatch/last_n_preempt per round for 'info session' -- that rollup exists but is never surfaced automatically, only on explicit 'info session'.
Fix
FIXED (2026-09-02, scheduler-sim-repl-verbosity spec, task 4.2 -- design.md's sim_print_decision Gate component, option (b) of this bug's own original proposal, now that the Round Rollup -- task 2.2, repl_rollup.c -- already provides the per-round summary line to fall back on). sim_loop.c's decision-drain loop (sched_sim_driver_run()) now calls a new file-local sim_print_decision_gate(sim, d) instead of calling sim_print_decision(d) directly; the gate skips the bare per-decision print only when sim->interactive && sim->repl_state->mode == XCE_REPL_MODE_CONTINUE, leaving step mode and non-interactive/headless runs completely unaffected (Req 5.1, 5.4) -- the Round Rollup's own per-round summary becomes the sole source of decision output under continue. Implementation wrinkle discovered and resolved during this task: design.md describes this as an 'inline-wrapper-style gate ... consistent with repl.h's established convention', but repl.h's existing wrappers (xce_repl_check_decision() and siblings) only ever test the PUBLIC sim->interactive field inline and delegate all repl-internal-state logic to an extern function implemented in repl.c/repl_break.c (the only files with repl_internal.h access, where xce_sched_repl_state_t's full definition -- including mode -- actually lives; sim.h/repl.h deliberately keep repl_state an opaque forward-declared pointer). That pattern cannot be reused verbatim here because the function being gated, sim_print_decision(), is itself static (private to sim_loop.c) -- repl.h cannot wrap a conditional call to a symbol it cannot see, and moving the check into repl.h would require repl.h to include repl_internal.h, leaking the full repl_state struct into every other repl.h consumer. Resolved by having sim_loop.c include repl_internal.h directly (a private header already scoped to src/scheduler/src/, so this stays an intra-directory dependency, not a new external one) and implementing sim_print_decision_gate() as a file-local static inline wrapper co-located with sim_print_decision() -- same 'wrapper hides the condition, call site stays one bare call expression' style repl.h's wrappers use, just not physically in that shared header since the wrapped function isn't extern. No new extern symbol crosses out of sim_loop.c for this purpose, so test_sim_isolation.c's nm(1) undefined-symbol layer and source-text-scan layer are both unaffected. repl.h itself was left untouched, matching design.md's own File Structure Plan line for it ('no functional change expected'). TDD: task 4.1's RED-phase test_sim_print_decision_gate.c (3 tests, real forked-child/direct-call stdout capture) pinned this exact contract before the fix landed; all 3 pass after (continue-mode suppression, step-mode-unaffected regression guard, headless-batch-unaffected regression guard). sched_repl_round_rollup_test (7/7) and sched_repl_event_echo_test (4/4) reverified green post-fix. Full-suite verification via /xce-build scheduler --incremental surfaced 3 UNEXPECTED additional failures beyond the pre-existing bug-1758, confirmed (via a controlled before/after rebuild swapping only sim_loop.c) to be caused by this fix itself, not a defect in it: test_repl_execution_control.c's test_pre_replay_pause_blocks_before_any_event_is_processed, and test_repl_breakpoints_inspection.c's test_event_breakpoint_pauses_repl_at_matching_event and test_decision_breakpoint_pauses_after_decision_output -- all three pre-date this spec and assert on the bare '[sim] PEND jobid=...' line's presence in a continue-mode capture, i.e. they pin exactly the bug-1610 behavior this fix intentionally removes. Left unmodified per this task's explicit no-test-file-editing boundary; flagged to the parent controller/reviewer as a real, reproducible side effect requiring a follow-up task to update those 3 assertions (e.g. swap their continue scripts for step, or assert on the Round Rollup's summary line instead) rather than a regression in this fix's own logic. Remediation (2026-09-02, task 4.2 remediation, post-review): the fix above only covered ONE of sim_loop.c's two direct sim_print_decision() call sites -- sched_sim_driver_run()'s main decision-drain loop. Review found a second, direct, unguarded call inside sim_reconcile_unaccounted_jobs() (added by the sibling scheduler-sim-decision-parity/scheduler-sim-eager-reconciliation specs after this bug's original fix landed, unrelated in authorship but real production code reporting DISPATCH decisions through this exact same channel) -- and that reconciliation path is actually a PRIMARY mechanism by which DISPATCH decisions surface in replay mode at all (most live-drain-loop activity is PEND), so a round where reconciliation fired was still showing both the bare per-decision line AND the Round Rollup's summary counting the same dispatch: this bug's own duplication, reproduced via the second call site design.md's original Boundary Commitments never accounted for (it only ever named "the existing call site", singular). Fixed by routing that call site through the identical sim_print_decision_gate(sim, d) too (same function, same condition, no new symbol) -- sim_loop.c:760's bare sim_print_decision(d) became sim_print_decision_gate(sim, d). TDD: two new tests added to test_sim_reconcile_unaccounted.c (test_reconcile_continue_mode_with_repl_state_suppresses_bare_decision_line / test_reconcile_step_mode_with_repl_state_still_prints_bare_decision_line), using the repl_state_ensure()-based direct-call fixture technique test_repl_round_rollup.c's own fixtures establish; the pre-existing test_reconcile_reports_reconstructed_decision_through_existing_channel (which deliberately leaves sim->repl_state NULL) was empirically re-verified to still pass unmodified, confirming the gate's NULL-safety default ("never load-bearing, degrade to print") covers it. Full-suite verification via /xce-build scheduler --incremental: sched_sim_reconcile_unaccounted_test now 16/16 (was 14/14), sched_sim_print_decision_gate_test 3/3, sched_repl_round_rollup_test 7/7, sched_repl_event_echo_test 4/4, sched_repl_execution_control_test 6/6, sched_repl_breakpoints_inspection_test 30/30 -- all green, confirming this bug's own previously-flagged test_repl_execution_control.c/test_repl_breakpoints_inspection.c follow-up (3 tests pinning the original bug-1610 behavior) was already resolved by an earlier task before this remediation. The only other full-suite failure remains the pre-existing, unrelated bug-1758 (sched_core_events_test). This closes the duplication gap completely -- both production call sites now gated.
Verification
Not recorded (migrated from buglog.json).
References
- Related: bug-1609