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

@ -18,6 +18,7 @@ scheduler_srcs = [
'src/sim_loop.c',
'src/sim_verify.c',
'src/sim_record.c',
'src/repl.c',
'src/schedobj.c',
'src/plugin.c',
'src/resource.c',
@ -33,12 +34,23 @@ incdir = include_directories('src',
'../include',
'../lib')
# Task 12.1 (scheduler-core spec, Req 5 AC 22): the interactive REPL's
# line-editing input loop (src/repl.c) needs readline()/add_history().
# Uses libedit (BSD), not GNU readline (GPL) -- xce_scheduler is a shipped
# production daemon (unlike xce_test, a debug tool), so a GPL dependency
# would encumber the whole linked binary. libedit's editline/readline.h
# emulation header provides the same two calls under a permissive license;
# src/controller/meson.build (xce_test's own REPL) uses the identical dep
# for the same reason, kept consistent project-wide.
libedit_dep = dependency('libedit')
scheduler_deps = [
libxce_dep,
librtti_dep,
math_dep,
tinylog_dep,
thread_dep
thread_dep,
libedit_dep,
]
scheduler = executable('xce_scheduler',
@ -66,17 +78,18 @@ sched_core_isolation_lib = static_library(
# Task 11.3 (scheduler-core spec, Req 5 AC 17): isolated compile target for
# the simulation driver's own files, consumed by
# testsuite/test_sim_isolation.c to prove at build time that
# sim.c/sim_loop.c/sim_record.c reference zero controller-facing symbols
# (client/netio/xceapi/delay/idgen). All files are compiled together into ONE
# archive -- task 11.2 added sim_loop.c as a sibling file to task 11.1's
# sim.c, and task 11.5 adds sim_record.c as a third; covering only a subset
# of them would repeat core.c's own siblings' (core_events.c/core_session.c/
# core_clock.c) never-mechanically-covered gap for the sim driver. Same
# "compiling to an object is enough, no real linking needed" rationale as
# sim.c/sim_loop.c/sim_record.c/repl.c reference zero controller-facing
# symbols (client/netio/xceapi/delay/idgen). All files are compiled together
# into ONE archive -- task 11.2 added sim_loop.c as a sibling file to task
# 11.1's sim.c, task 11.5 added sim_record.c as a third, and task 12.1 adds
# repl.c as a fourth; covering only a subset of them would repeat core.c's
# own siblings' (core_events.c/core_session.c/core_clock.c)
# never-mechanically-covered gap for the sim driver. Same "compiling to an
# object is enough, no real linking needed" rationale as
# sched_core_isolation_lib above.
sched_sim_isolation_lib = static_library(
'sched_sim_isolation',
['src/sim.c', 'src/sim_loop.c', 'src/sim_record.c'],
['src/sim.c', 'src/sim_loop.c', 'src/sim_record.c', 'src/repl.c'],
include_directories: incdir,
dependencies: scheduler_deps,
install: false)