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

4.9 KiB

id status status_updated status_note component severity timestamp last_seen occurrences files tags related legacy
bug-1778 fixed 2026-09-24 scheduler unknown 2026-09-07T06:50:00Z 2026-09-19T09:00:00Z 1
src/scheduler/src/repl_info.c
job-lifecycle
stale-state
repl
snapshot
scheduler
related-to-bug-1776
assigned-jobs
persistence
needs-live-repro
related-to-bug-1764
interim-fix
sim
bug-1764
bug-1776
true

bug-1778: Live-observed (interactive sim session=29, replaying a real captured trace): three plain…

Description

Live-observed (interactive sim session=29, replaying a real captured trace): three plain (non-array) jobs already at JOB_STAT_DONE (0-14-1788751409-0, 0-14-1788751435-0, 0-14-1788751504-0) still appear in info worker pc13v's 'assigned jobs' listing (each annotated '*2' via bug-1776's slot-count fix), alongside the genuinely-running array job elements. User: 'The finished job(exit/done) should be de-assign from worker.'

Environment

Not recorded (migrated from buglog.json).

Reproduction

Not recorded (migrated from buglog.json).

Root cause

PARTIALLY CONFIRMED, NOT FULLY ROOT-CAUSED this session (user asked to record, not fix). Confirmed facts: (1) repl_info_worker_one()'s assigned-jobs loop (repl_info.c, ~line 686) gates solely on job->allocs != NULL, with no independent job->state check -- if allocs is non-NULL for a terminal job, it will always display, by design (mirrors the exact structural gap bug-1764 already documented for this same function). (2) The worker's ACTUAL resource accounting is NOT confused by this: the same session's 'resource slots: value=22.00 used=20.00' correctly reflects only the 5 genuinely-running 4-slot array elements (5*4=20), excluding the 3 DONE jobs entirely -- proving core.resmgr/resInUse (the real admission-control state) is fine; only the assigned-jobs DISPLAY loop (which walks job->allocs directly, a separate field) is stale for these 3 jobs. (3) This is NOT a regression of bug-1764's fix: verified sim_loop.c:713-714 still correctly excludes JOB_IS_DONE(job->state)||JOB_IS_EXIT(job->state) from sim_reconcile_unaccounted_jobs()'s resurrection path, so that specific documented mechanism is not what's reintroducing these allocs. (4) core_events.c's XCE_CORE_EVENT_JOB_STATE_CHANGE (line ~1155-1163) and XCE_CORE_EVENT_JOB_UPDATE (line ~1207-1215) handlers both correctly free_JobAlloc_ptr()+null job->allocs atomically alongside the state write whenever !JOB_IS_RUN(new_state) -- so if either of THESE handlers was what drove these 3 jobs to DONE, allocs should already be NULL; something else must be true. Leading unconfirmed hypothesis: snapshot_load.c:490-493 deep-copies wj->allocs from the .replay snapshot's wire payload UNCONDITIONALLY whenever wj->allocs != NULL, with no check against the snapshot's own wj->state -- if the live production recording's snapshot-capture moment (snapshot_wire.c's save side) captured these 3 jobs' allocs and state in a mutually inconsistent way (e.g. a save-time field-read ordering gap, or the snapshot genuinely predates their JOB_STATE_CHANGE/JOB_UPDATE terminal cleanup while a separately-recorded later event updated state to DONE without a matching allocs-clearing event reaching this replay stream), the replay would faithfully reproduce that inconsistency verbatim. NOT verified with a live trace/gdb this session -- flagged per this project's own persistence.md field-completeness concern (a state that 'recovers correctly' while an unrelated field goes stale is the exact class of bug that steering calls out), not asserted as confirmed.

Fix

INTERIM DISPLAY-LAYER MITIGATION APPLIED (2026-09-07, per user request). repl_info_worker_one()'s assigned-jobs loop (repl_info.c) now also excludes JOB_IS_DONE(job->state) || JOB_IS_EXIT(job->state), in addition to the existing job->allocs != NULL check -- mirrors the same 'terminal is permanent' guard job_state_regresses_terminal()/sim_reconcile_unaccounted_jobs() already apply elsewhere. TDD: new test test_info_worker_assigned_job_listing_excludes_terminal_job_despite_stale_allocs (test_repl_resource_table.c) directly assigns the SAME allocs struct to a DONE job and a RUN job on the same worker, forcing the DONE job's state after assigning allocs (reproducing the exact state/allocs inconsistency without needing to root-cause how it arises live) -- confirms the DONE job is excluded while the RUN job still shows. Full scheduler suite: 76/77 pass (same pre-existing unrelated test_core_events.c failure as before, bug-1762). NOTE: this masks the symptom at the display layer only -- the underlying question of WHY job->allocs stays non-NULL for a terminal job (suspected snapshot save/load inconsistency) remains unresolved; a real fix should also prevent/clear the stale allocs at its source.

Verification

Not recorded (migrated from buglog.json).

References

  • Related: bug-1764, bug-1776