feat(scheduler-core): implement REPL execution control (task 12.1)

Adds the interactive simulation REPL's execution-control slice (Req 5
AC 22): a single-threaded input loop (readline-based) supporting
step [N], continue, quit, and help, pausing before the first replay
event and blocking the driver loop while active.

Review round 1 raised two findings, both resolved here:

- GNU readline (GPL) was linked directly into xce_scheduler, a shipped
  production daemon. Switched the whole project to libedit (BSD) for
  line editing -- scheduler and controller (xce_test) both now use
  editline/readline.h, API-compatible (readline()/add_history()).

- Getting a clean regression run after the libedit swap required a
  full clean rebuild, which exposed a genuine pre-existing SIGSEGV
  (bug-1504): SnapshotData.internalState was a capnp Data field, and
  this codebase's capnpc-c backend silently no-ops Data-field
  encoding without zero-initializing the caller's stack struct,
  leaving an uninitialized capn_ptr written onto the wire. Fixed by
  switching internalState to List(UInt8) (with a new internalStateLen
  wire field), matching the working FetchFileReply.content/bytesRead
  precedent -- no hand-written accessor code needed.

A second, unrelated instance of the same bug class was found in the
controller's WAL-catchup path (WalCatchupEntry.payload) and
deliberately left unfixed as bug-1505, out of scheduler-core's
boundary; needs its own project-wide Data-field audit.

Scheduler suite: 34/34 clean. RED-phase reproduction recorded in
tasks.md (repl_should_pause() step-budget check disabled -> 2/4
tests fail as expected -> reverted -> 34/34 green).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JPPXeKSUXUqcqvzNXY1rzJ
This commit is contained in:
Rongsong Shen 2026-08-07 08:17:35 +00:00
parent de00ccff37
commit 02f7f48ff9
Signed by: rshen
SSH key fingerprint: SHA256:8Vf2pZ3h6rJa7EhGK+WaAQ9Mta/apZvteW6g9U/WKKk
17 changed files with 1491 additions and 81 deletions

View file

@ -2,7 +2,17 @@ scheduler_test_src = [
'test.c',
]
scheduler_test_deps = [libxce_deps, librtti_dep, unity_dep, libxce_dep]
# libedit_dep (defined in the parent meson.build, task 12.1, Req 5 AC 22):
# every white-box test TU that #includes ../src/sim_loop.c now transitively
# pulls in repl.h's inline wrappers, which reference repl.c's real
# readline()/add_history() calls the moment ANY such TU also #includes
# ../src/repl.c (already required at link time regardless -- sim_loop.c's
# own repl.h wrappers reference xce_repl_start()/xce_repl_post_session_check()
# unconditionally). Added to the shared deps list rather than per-test so
# every current and future sim-family white-box test picks it up
# automatically.
scheduler_test_deps = [libxce_deps, librtti_dep, unity_dep, libxce_dep,
libedit_dep]
incdir = include_directories('../include', '../../include', '.', '../src', '../../lib')
@ -535,6 +545,12 @@ sched_sim_isolation_test = executable(
'-DXCE_SIM_RECORD_C_PATH="'
+ (meson.current_source_dir() / '..' / 'src' / 'sim_record.c')
+ '"',
'-DXCE_SIM_REPL_C_PATH="'
+ (meson.current_source_dir() / '..' / 'src' / 'repl.c')
+ '"',
'-DXCE_SIM_REPL_H_PATH="'
+ (meson.current_source_dir() / '..' / 'include' / 'repl.h')
+ '"',
'-DXCE_SIM_LIB_PATH="'
+ sched_sim_isolation_lib.full_path()
+ '"'],
@ -636,6 +652,24 @@ sched_sim_equivalence_test = executable(
dependencies: [scheduler_test_deps],
install: false)
# Contract test for the interactive simulation REPL's execution-control slice
# (scheduler-core spec, task 12.1, Req 5 AC 22): starting simulation with
# --interactive pauses before any event is processed, and a `step N` command
# advances exactly N sessions before returning control to the prompt. Same
# white-box #include pattern as sched_sim_alt_events_test/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, recording_session.c, sim.c, sim_loop.c,
# sim_record.c, sim_verify.c, snapshot.c/snapshot_wire.c/snapshot_load.c) PLUS
# the new ../src/repl.c under test. Needs libedit_dep (parent meson.build)
# at link time -- repl.c's real readline()/add_history() calls are pulled
# into this TU by the white-box #include.
sched_repl_execution_control_test = executable(
'sched_repl_execution_control_test',
['test_repl_execution_control.c'],
include_directories:[incdir],
dependencies: [scheduler_test_deps, libedit_dep],
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',
@ -726,3 +760,6 @@ test('scheduler sim record output test',
test('scheduler sim equivalence test',
sched_sim_equivalence_test, suite: 'scheduler',
env: ['ASAN_OPTIONS=detect_leaks=0'])
test('scheduler repl execution control test',
sched_repl_execution_control_test, suite: 'scheduler',
env: ['ASAN_OPTIONS=detect_leaks=0'])