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:
parent
de00ccff37
commit
02f7f48ff9
17 changed files with 1491 additions and 81 deletions
|
|
@ -1109,10 +1109,23 @@ struct SchedProject $C.mapname("sched_snapshot_project_t") {
|
|||
# session/iterator state) with no capnp-visible structure -- the core owns
|
||||
# its byte layout; internalVersion is a format tag so a future incompatible
|
||||
# internal layout is rejected rather than misinterpreted (this task's
|
||||
# Observable). Like every other Data field in this schema (see
|
||||
# ReplayHeader.configHash above), internalState's generated encode_/decode_
|
||||
# codec is a documented no-op -- real extraction is hand-written
|
||||
# low-level-accessor code, task 9+'s job, same pattern.
|
||||
# Observable).
|
||||
#
|
||||
# internalState is List(UInt8), NOT Data (task 12.1 review finding, fixed):
|
||||
# every OTHER Data field in this schema (e.g. ReplayHeader.configHash) has
|
||||
# its generated encode_/decode_ codec silently no-op'd by this codebase's
|
||||
# capnpc-c setup -- the generated encode_SnapshotData_ptr()'s local `struct
|
||||
# SnapshotData d;` is never zero-initialized, so a no-op'd Data field left
|
||||
# capn_setp() writing an UNINITIALIZED stack capn_ptr onto the wire (real,
|
||||
# reproducible SIGSEGV in write_SnapshotData, deterministic per call site's
|
||||
# stack layout -- configHash happened to get lucky garbage, internalState
|
||||
# did not). List(UInt8) sidesteps the whole no-op class: this same capnpc-c
|
||||
# backend DOES correctly auto-generate real encode/decode for List(UInt8)
|
||||
# (proven precedent: FetchFileReply.content/bytesRead above -- a genuine
|
||||
# capn_new_list8()/capn_set8() loop, not a comment placeholder), so no
|
||||
# hand-written low-level accessor code is needed here at all. Same
|
||||
# maplistcount convention as content/bytesRead: internalStateLen (@11,
|
||||
# below) carries the byte count as its own real wire scalar.
|
||||
struct SnapshotData $C.mapname("sched_snapshot_data_t") {
|
||||
config @0: Conf4Sched;
|
||||
jobs @1: SchedJobData;
|
||||
|
|
@ -1123,8 +1136,9 @@ struct SnapshotData $C.mapname("sched_snapshot_data_t") {
|
|||
users @6: List(SchedUser) $C.maplistcount("n_users");
|
||||
projects @7: List(SchedProject) $C.maplistcount("n_projects");
|
||||
prngSeed @8: UInt64 $C.mapname("prng_seed");
|
||||
internalState @9: Data $C.mapname("internal_state");
|
||||
internalState @9: List(UInt8) $C.mapname("internal_state") $C.maplistcount("internal_state_len");
|
||||
internalVersion @10: UInt32 $C.mapname("internal_version");
|
||||
internalStateLen @11: UInt32 $C.mapname("internal_state_len");
|
||||
}
|
||||
|
||||
struct SnapshotEntry $C.mapname("replay_snapshot_entry_t") {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue