myxce/.wolf/bugs/bug-1770.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.8 KiB

id status status_updated status_note component severity timestamp last_seen occurrences files tags related fix_commits legacy
bug-1770 fixed 2026-09-24 scheduler unknown 2026-09-04T00:00:00Z 2026-09-19T09:00:00Z 1
scheduler
resreq-parsing
job-submission
parjob-allocator
pre-existing
severity-high
scheduler-allocation-discovery
task-5.3
bug-1766
bug-1768
bug-1769
799a71ec
5c87f1cc
true

bug-1770: FIXED (2026-09-06, resreq-maxtasks-wiring feature, commits 799a71ec..5c87f1cc)

Description

FIXED (2026-09-06, resreq-maxtasks-wiring feature, commits 799a71ec..5c87f1cc). Originally: pre-existing, out-of-scope gap discovered during scheduler-allocation task 5.3's end-to-end integration test. The REAL job-ingest path could NEVER produce a primary resource request with maxTasks > 1, regardless of what task count a real job submission specified -- parjob_allocator.c's my_alloc() (maxTasks > 1) was therefore UNREACHABLE from any real production job submission.

Environment

Not recorded (migrated from buglog.json).

Reproduction

Not recorded (migrated from buglog.json).

Root cause

CONFIRMED via direct code read and a genuine RED-phase run of test_parjob_e2e_multislot.c (src/scheduler/testsuite/): parse_resreq_primary() (src/lib/resreq.c:431-432) unconditionally hardcodes primary->minTasks = 1; primary->maxTasks = 1; after parsing a resreq string's select/rusage/span/same/affinity clauses, regardless of their content -- there is no resreq-string syntax for task count at all (LSF-style task count is a job-submission-level parameter, job_submit_data_t.mintasks/maxtasks, sibling to the resreq string, not nested inside it). Neither of this codebase's two job-ingest constructors -- core_build_job()/core_build_primary_schedobj() (src/scheduler/src/core_events.c, the real production JOBS_PUSH path) nor make_sched_job() (src/scheduler/src/schedobj.c, the sibling non-event construction path) -- ever copies the job's own submitted job_submit_data_t.mintasks/maxtasks (correctly propagated onto the TOP-LEVEL xce_sched_job_t.mintasks/maxtasks by both functions, confirmed working) onto the NESTED xce_primary_resreq_t.minTasks/maxTasks that sched_make_primary_alloc_()'s maxTasks>1 dispatch check (scheduler.c:290) and parjob_allocator.c's my_alloc() actually read. Net effect: every job's primary resreq that parses successfully has maxTasks pinned to 1, so sched_make_primary_alloc_() always routes it to seqjob_allocator instead of parjob_allocator (scheduler.c:290-294) -- parjob_allocator's real combinatorial multi-host allocation (scheduler-allocation spec, tasks 4.1-4.3) is fully implemented and unit-tested but cannot be exercised by any real job submission as the system stands today. Live-confirmed: test_parjob_e2e_multislot.c's own RED-phase run (maxTasks patch temporarily disabled) showed the job routing to seqjob and dispatching onto exactly 1 host instead of the intended 2 (Expected 2 Was 1).

Fix

FIXED (2026-09-06, resreq-maxtasks-wiring feature, commits 799a71ec..5c87f1cc, 12 tasks). Added apply_job_task_count(xce_resreq_t*, uint32_t mintasks, uint32_t maxtasks) (src/lib/resreq.c/resreq.h) -- overwrites a standalone top-level primary's minTasks/maxTasks with the job's real requested count (no-op for COMPOUND/ALTER nested primaries, deliberately left out of scope per bug-1768; defensive min>max clamp). Wired at both live ingest call sites: core_build_job() (core_events.c, fresh submission) using job->submit->mintasks/maxtasks, and the snapshot-restore path (snapshot_load.c, --sim/replay driver) using the job's already-restored mintasks/maxtasks -- both called strictly before their respective schedobj/resreq_fp builders read the task count. make_sched_job() (schedobj.c) intentionally NOT wired -- confirmed dead code (unreachable via sched_update(), which has zero live callers). Verified end-to-end via a new xce-test-multihost skill: a real multi-task job (and a multi-task array-job element) submitted via plain jobsub is genuinely split across two real xcecd_virtual hosts by the scheduler's own decision and reaches DONE on both. Full regression clean (scheduler/controller/worker suites, only pre-existing unrelated bug-1758/bug-1723 remain) and HA soak clean (sequential + chaotic, zero job loss/divergence). test_parjob_e2e_multislot.c's PATCH_MAXTASKS_FOR_BUG_1770 workaround is removed; that test now drives its scenario entirely through unmodified real ingest. Sibling gaps deliberately NOT fixed by this feature, tracked separately: bug-1772 (snapshot-restore resreq_fp gap), bug-1773 (resource-request update never taking effect for scheduling), bug-1774 (make_sched_job()'s dead-code resreq_fp gap).

Verification

Not recorded (migrated from buglog.json).

References

  • Related: bug-1766, bug-1768, bug-1769