feat(scheduler-core): implement what-if branching and simulation output recording (task 11.5)

Add sim_record.c (scheduler_t-free .replay writer: ReplayHeader + loaded
SnapshotEntry once, then per-event pass-through and per-session
ReplaySession finalization) and wire --sim-alt-events event-source
selection into sim_loop.c's drain loop, coexisting with task 11.2's
stdout default and task 11.4's --sim-verify. Extract scheduler_t-free
halves out of recording_session.c's begin/end so sim-side code can reuse
the decision-encoding path without gaining an xce_scheduler_t* dependency,
preserving Req 5 AC 17 isolation. Per Req 5 AC 16/19.
This commit is contained in:
Rongsong Shen 2026-08-07 02:42:50 +00:00
parent a6083d803e
commit 564408e967
Signed by: rshen
SSH key fingerprint: SHA256:8Vf2pZ3h6rJa7EhGK+WaAQ9Mta/apZvteW6g9U/WKKk
14 changed files with 1750 additions and 91 deletions

View file

@ -505,16 +505,18 @@ sched_sim_event_loop_test = executable(
# 17, "Simulation isolation"): "a build-time or structural check confirms no
# controller-facing symbol is reachable from the simulation driver's compiled
# object", plus the AC's own "confirmed by the struct definition itself, not
# runtime checks alone" clause. Same source-text-scan + nm(1) link-time-proof
# genre as sched_core_isolation_test above, PLUS a third, narrower structural
# layer scanning ONLY xce_sched_sim_driver_t's own struct body text for
# forbidden field types -- see the test's own header comment for why a
# comment-stripping pass is required first (sim.h's own boundary doc comment
# legitimately names xcecli_ctx_t/xce_cluster_config_t in prose). link_with
# establishes the build-order dependency on sched_sim_isolation_lib (parent
# meson.build -- sim.c AND sim_loop.c compiled together, isolated from the
# rest of the scheduler); XCE_SIM_*_PATH bake in absolute paths the same way
# XCE_CORE_*_PATH does above.
# runtime checks alone" clause. Extended by task 11.5 to also cover the new
# sim_record.c (--sim-output writer) sibling file. Same source-text-scan +
# nm(1) link-time-proof genre as sched_core_isolation_test above, PLUS a
# third, narrower structural layer scanning ONLY xce_sched_sim_driver_t's own
# struct body text for forbidden field types -- see the test's own header
# comment for why a comment-stripping pass is required first (sim.h's own
# boundary doc comment legitimately names xcecli_ctx_t/xce_cluster_config_t
# in prose). link_with establishes the build-order dependency on
# sched_sim_isolation_lib (parent meson.build -- sim.c + sim_loop.c +
# sim_record.c compiled together, isolated from the rest of the scheduler);
# XCE_SIM_*_PATH bake in absolute paths the same way XCE_CORE_*_PATH does
# above.
sched_sim_isolation_test = executable(
'sched_sim_isolation_test',
['test_sim_isolation.c'],
@ -530,6 +532,9 @@ sched_sim_isolation_test = executable(
'-DXCE_SIM_LOOP_C_PATH="'
+ (meson.current_source_dir() / '..' / 'src' / 'sim_loop.c')
+ '"',
'-DXCE_SIM_RECORD_C_PATH="'
+ (meson.current_source_dir() / '..' / 'src' / 'sim_record.c')
+ '"',
'-DXCE_SIM_LIB_PATH="'
+ sched_sim_isolation_lib.full_path()
+ '"'],
@ -566,6 +571,45 @@ sched_sim_verify_test = executable(
dependencies: [scheduler_test_deps],
install: false)
# Contract test for what-if event-source branching (scheduler-core spec,
# task 11.5, Req 5 AC 16): running with --sim-alt-events produces decisions
# driven by the alternate events, not the base file's original events. A
# baseline run (no --sim-alt-events) proves the base file's own events drive
# scheduling by default; a --sim-alt-events run against the SAME base file's
# snapshot proves the alternate file's events drive it instead, and that the
# base file's own events are never read once --sim-alt-events is set. Same
# white-box #include pattern as sched_sim_verify_test (core.c, core_clock.c,
# core_events.c, core_session.c, decision.c, plugin.c, plugins/builtin_fcfs.c,
# recording.c, sim.c, sim_loop.c, sim_verify.c, snapshot.c/snapshot_wire.c/
# snapshot_load.c) -- sim_verify.c is required at link time even though this
# file never sets --sim-verify (sim_loop.c externs its two entry points
# unconditionally, same reason sched_sim_event_loop_test needs it).
sched_sim_alt_events_test = executable(
'sched_sim_alt_events_test',
['test_sim_alt_events.c'],
include_directories:[incdir],
dependencies: [scheduler_test_deps],
install: false)
# Contract test for the --sim-output `.replay` writer (scheduler-core spec,
# task 11.5, Req 5 AC 19): the output file, when requested, is decodable by
# the same reader used for normal-mode recordings (test_record_lifecycle.c's
# own capn_getp(capn_root(&c), 0, 1) + read_X()/decode_X() technique, reused
# verbatim here), with full ReplayHeader -> SnapshotEntry -> ReplayEvent* ->
# ReplaySession -> ReplayEvent{endSession} stream symmetry (Req 5 AC 19's own
# symmetry table). A second test proves --sim-output composes with
# --sim-alt-events: the output reflects the alternate run's own events and
# decision, not the base file's. Same white-box #include pattern as
# sched_sim_verify_test PLUS ../src/recording_session.c (the scheduler_t-free
# session-accumulator entry points sim_loop.c now calls) PLUS the new
# ../src/sim_record.c (the code under test).
sched_sim_record_output_test = executable(
'sched_sim_record_output_test',
['test_sim_record_output.c'],
include_directories:[incdir],
dependencies: [scheduler_test_deps],
install: false)
# Register as meson tests so `meson test --suite scheduler` runs them all.
# detect_leaks=0 matches the suite convention (fixtures are not torn down).
test('scheduler test', scheduler_test, suite: 'scheduler',
@ -647,3 +691,9 @@ test('scheduler sim isolation test',
test('scheduler sim verify test',
sched_sim_verify_test, suite: 'scheduler',
env: ['ASAN_OPTIONS=detect_leaks=0'])
test('scheduler sim alt events test',
sched_sim_alt_events_test, suite: 'scheduler',
env: ['ASAN_OPTIONS=detect_leaks=0'])
test('scheduler sim record output test',
sched_sim_record_output_test, suite: 'scheduler',
env: ['ASAN_OPTIONS=detect_leaks=0'])