--- id: "bug-1929" status: "fixed" status_updated: "2026-10-09" status_note: "" component: "scheduler" severity: "wrong-result" timestamp: "2026-10-09T03:21:26Z" last_seen: "2026-10-09T03:21:26Z" occurrences: 1 files: ["src/scheduler/src/repl_rollup.c", "src/scheduler/src/repl_break.c", "src/scheduler/src/repl_internal.h", "src/scheduler/testsuite/test_repl_round_rollup.c", "src/scheduler/testsuite/test_repl_trace_emit.c", "src/scheduler/testsuite/test_repl_trace_wiring.c", "src/scheduler/testsuite/fixtures/repl_trace_baseline/transcript.txt"] tags: ["repl", "sim", "rollup", "continue", "output"] related: ["bug-1610", "bug-1611"] fix_commits: ["ba6f42cd"] gitea_issue: 354 gitea_synced_status: "fixed" gitea_synced_hash: "32f3f26a5565" --- # bug-1929: xce_scheduler --sim REPL continue round summary prints empty jobids= for a dispatch-only round ## Description **Observed:** in `continue` mode a round whose only decision is a dispatch prints a round summary with an empty job list, with tracing off as well as on. From the 6.3 trace-equivalence harness (`XCE_TRACE_EQUIV_DUMP=1`, extra replay, 8 occurrences): ``` [sim] round 1-1 (1 round(s), t=1970-01-01T00:00:00Z..1970-01-01T00:00:00Z): 4 dispatch, 1 pend, 0 preempt -- jobids=0-0-0-202 [sim] round 2-2 (1 round(s), t=1970-01-01T00:00:00Z..1970-01-01T00:00:00Z): 1 dispatch, 0 pend, 0 preempt -- jobids= ``` With the 1.1 contention fixture the dispatched job is missing even when a pend job is listed (round 8 dispatched 0-0-0-108): ``` [sim] round 8-8 (1 round(s), t=1970-01-01T00:00:00Z..1970-01-01T00:00:00Z): 1 dispatch, 1 pend, 0 preempt -- jobids=0-0-0-107 ``` **Expected:** "jobids=" names the jobs the round's decisions affected (scheduler-sim-repl-verbosity Requirement 2.1: "the count of each decision type and the identifiers of the jobs affected"), so the dispatched (and preempted) jobs are listed: `jobids=0-0-0-202` (the job pending in round 1 and dispatched in round 2) and `jobids=0-0-0-107,0-0-0-108`. **Impact:** wrong/misleading REPL output only: the operator cannot see which job a round dispatched or preempted. A second effect of the same cause: rounds with the same counts and pend set but different dispatched jobs collapse into one range (Requirement 3.2 says a range needs the same affected job identifiers). Scheduling decisions are not affected. **Found by:** scheduler-sim-decision-trace validation (rollup output in the 6.1/6.3 e2e and equivalence harness dumps). ## Environment - OS: no /etc/os-release; Docker container; NixOS kernel; kernel Linux 6.18.33 x86_64 - libc: glibc 2.42-47 (interpreter /nix/store/wb6rhpznjfczwlwx23zmdrrw74bayxw4-glibc-2.42-47/lib/ld-linux-x86-64.so.2) - Toolchain: gcc (GCC) 15.2.0, meson 1.9.1 - Build: asan (xce.build), tests run with `ASAN_OPTIONS=detect_leaks=0` (suite convention for these binaries); the symptom does not depend on sanitizer options or on tracing - Code: scheduler-framework @ 5ca59dc9-dirty - Deployment: standalone simulator only (`xce_scheduler --sim ... --interactive`), no controller, no workers; single dev host ## Reproduction 1. Build: `bash src/tools/xce-build.sh scheduler --jobs 12 --incremental` at the Environment commit. 2. `cd src/xce.build/scheduler/testsuite && XCE_TRACE_EQUIV_DUMP=1 ASAN_OPTIONS=detect_leaks=0 ./sched_trace_equivalence_test 2>&1 | grep 'jobids=$'` -- 8 lines `... 1 dispatch, 0 pend, 0 preempt -- jobids=`. 3. Contention fixture: `printf 'continue\nquit\n' | TERM=dumb src/xce.build/scheduler/xce_scheduler --sim src/scheduler/testsuite/fixtures/repl_trace_baseline/contention.replay --interactive --logfile /sched.log` (repo root; with bug-1928 unfixed add `ASAN_OPTIONS=detect_leaks=0`) -- round 8-8 lists only 0-0-0-107; rounds 1-1 and 5-5 list only the pend set. 4. Smallest: `test_round_summary_lists_dispatched_jobids_golden` in test_repl_round_rollup.c (drives xce_repl_on_begin_round/xce_repl_on_decision/repl_round_rollup_process directly). - Frequency: deterministic (every run, 2026-10-09) - Evidence: /home/claude/tmp/bug-1929/ -- equiv-dump-red.txt / equiv-dump-green.txt, e2e-dump-red.txt, contention-continue-red.txt / contention-continue-green.txt, rollup-red.txt, transcript.diff, fixture-backup/ (the baseline fixtures before regeneration) ## Root cause `repl_round_emit()` (src/scheduler/src/repl_rollup.c:178, listing built at :201-202 before the fix) built the "jobids=" listing from `pend_jobids` only, and `repl_round_rollup_process()` (repl_rollup.c:285) only ever collected the pend set (`repl_query_pend_jobids()`, a walk of core->jobs for JOB_IS_PEND jobs). The dispatch and preempt decisions were counted in `xce_repl_on_decision()` (src/scheduler/src/repl_break.c:953-959, `last_n_dispatch`/`last_n_preempt`) but their job ids were never recorded anywhere. A dispatched job is no longer pending after the round, so a dispatch-only round has an empty pend set and printed `jobids=` with nothing after it; in mixed rounds the dispatched jobs were silently left out. The same pend-only fingerprint made rounds that dispatched different jobs compare equal and collapse. Since: 921c72bb (scheduler-sim-repl-verbosity task 2.2, Round Rollup), written while bug-1611 meant the simulator produced no DISPATCH decisions, so the pend set was the only affected-job information then. Scope: the traced-round path (scheduler-sim-decision-trace 7.3/7.4) and the allocation-failure fallback go through the same `repl_round_emit()`; step mode prints per-decision lines and was not affected. ## Fix - src/scheduler/src/repl_internal.h: new state in `xce_sched_repl_state_s` -- `round_decided_jobids`/`round_n_decided`/`round_cap_decided`/`round_decided_lost` (this round's dispatch/preempt jobids) and `collapse_decided_jobids`/`collapse_n_decided_jobids` (the active range's); declarations of `repl_round_note_decided()`/`repl_round_reset_decided()`. - src/scheduler/src/repl_break.c: `xce_repl_on_decision()` records the jobid of each DISPATCH and PREEMPT decision (a decision with no body, jobid 0, is skipped); `xce_repl_on_begin_round()` resets the per-round list. - src/scheduler/src/repl_rollup.c: `repl_round_rollup_process()` takes over the round's list (sorted, de-duplicated) and makes it part of the fingerprint next to the counts and the pend set, so rounds collapse only when the affected jobs match; `repl_round_emit()` lists the union of the pend set and the dispatch/preempt set (`repl_fmt_affected()`, through the existing `repl_fmt_jobid_range()` compressor) and takes the pend count separately; `repl_round_collapse_flush()` frees the new list. If the per-round list cannot grow (out of memory) or the pend set cannot be built, the round is reported on its own with `jobids=(unavailable)` instead of an empty or partial list. - Tests: test_repl_trace_emit.c and test_repl_trace_wiring.c teardowns free the new lists (both run with leak detection on). - src/scheduler/testsuite/fixtures/repl_trace_baseline/transcript.txt regenerated (XCE_REPL_BASELINE_REGEN=1, .replay fixtures restored from the backup): exactly two lines change, both `jobids=` lines -- round 5-5 `jobids=0-0-0-107` -> `jobids=0-0-0-104,0-0-0-105,0-0-0-106,0-0-0-107` and round 8-8 `jobids=0-0-0-107` -> `jobids=0-0-0-107,0-0-0-108`. Output format change: "jobids=" now means every job the range's decisions affected (Requirement 2.1), not only the jobs left pending; the counts are unchanged. ## Verification - Test: `test_round_summary_lists_dispatched_jobids_golden` in src/scheduler/testsuite/test_repl_round_rollup.c (exact golden lines for two dispatch-only rounds of different jobs, and a dispatch+preempt round): RED before the fix -- `Expected '[sim] round 1-1 ...: 1 dispatch, 0 pend, 0 preempt -- jobids=0-0-0-5101\n[sim] round 2-2 ...: 1 dispatch, 0 pend, 0 preempt -- jobids=0-0-0-5102\n' Was '[sim] round 1-2 (2 round(s), ...): 1 dispatch, 0 pend, 0 preempt -- jobids=\n'` -- GREEN after (8 Tests 0 Failures). `scheduler repl trace baseline test` with the regenerated transcript: GREEN (4 Tests 0 Failures, leak detection on). - Harness: equivalence dump (`XCE_TRACE_EQUIV_DUMP=1`) has 8 empty `jobids=` lines before and 0 after; all 8 dispatch-only rounds now read `round 2-2 ... 1 dispatch, 0 pend, 0 preempt -- jobids=0-0-0-202` (equiv-dump-green.txt). - Suites: `bash src/tools/xce-build.sh scheduler --jobs 12 --incremental`: scheduler suite 118/118 OK, asan (log: /home/claude/tmp/bug-1929/gate-scheduler-suite.log). - Live: standalone contention run (Reproduction step 3) after the fix: round 8-8 `jobids=0-0-0-107,0-0-0-108`, round 1-1 lists 101-107 (contention-continue-green.txt). ## References - Related: bug-1611 (no DISPATCH decisions in sim at the time the rollup was written), bug-1610 (rollup introduced to replace per-decision lines) - Docs: .kiro/specs/scheduler-sim-repl-verbosity/requirements.md (Requirement 2.1, 3.2); src/scheduler/testsuite/fixtures/repl_trace_baseline/transcript.txt