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
|
|
@ -354,12 +354,13 @@
|
|||
|
||||
- [ ] 12. Core: Interactive REPL
|
||||
|
||||
- [ ] 12.1 Implement REPL execution control
|
||||
- [x] 12.1 Implement REPL execution control
|
||||
- Implement a single input-loop REPL (not a separate thread) that pauses before the first event and blocks the simulation loop while active, supporting step-N-sessions, continue-to-next-breakpoint-or-EOF, and quit
|
||||
- Observable: starting simulation with the REPL enabled pauses before any event is processed; issuing a step command advances exactly the requested number of sessions and returns control to the prompt
|
||||
- _Requirements: 5.22_
|
||||
- _Note: use readline() for the input loop's line editing (history, arrow keys) — xce_test's REPL already sets this precedent (src/controller/testsuite/test.c:3827) and readline is already a linked project dependency, zero new cost. Confirm readline's GPL licensing is acceptable for this build target before committing to it (vs. libedit/linenoise) if that hasn't already been settled elsewhere in the codebase._
|
||||
- _Note: use readline() for the input loop's line editing (history, arrow keys) — xce_test's REPL already sets this precedent (src/controller/testsuite/test.c:3827) and readline is already a linked project dependency, zero new cost. Confirm readline's GPL licensing is acceptable for this build target before committing to it (vs. libedit/linenoise) if that hasn't already been settled elsewhere in the codebase._ **Resolved: switched project-wide to libedit (BSD) instead — see Implementation Notes.**
|
||||
- _Depends: 11.2_
|
||||
- _Verified: RED-phase reproduction performed during review round 2 — temporarily changed `repl_should_pause()` (repl.c) from `return (rs->step_budget <= 0);` to `return false;`, rebuilt, ran `sched_repl_execution_control_test`: 2/4 tests failed exactly as expected (`test_should_pause_fires_on_exactly_the_nth_session`, `test_step_1_processes_exactly_one_session_then_quit_exits`), the 2 unrelated tests (`test_should_pause_never_fires_in_continue_mode`, `test_pre_replay_pause_blocks_before_any_event_is_processed`) stayed green. Reverted; rebuilt; full scheduler suite 34/34 GREEN._
|
||||
|
||||
- [ ] 12.2 Implement REPL breakpoints and state inspection
|
||||
- Support setting/listing/deleting breakpoints on event type with optional field filters, and inspection commands reporting current simulation position, and job/queue/worker/resource/summary state
|
||||
|
|
@ -402,3 +403,5 @@
|
|||
- Task 9.3's review found a genuine task-breakdown gap, not fixable within 9.3's own boundary: `record_snapshot_interval` (the field task 9.3's periodic-checkpoint mechanism reads) has no CLI or admin-request wiring anywhere in the codebase — `--record-snapshot-interval` appears in AC 11's own normative text but in zero task's actual scope. Neither task 9.4 (admin request handler — dispatches `SchedAdminRequest` actions, which don't carry an interval field either, per controller.capnp's `SchedAdminRequest{action, recordFile}`) nor group 10 (controller-side dispatch) owns scheduler startup CLI flags; only `main.c`'s argp table does, following task 9.1's own `--record-dir` precedent. As currently scoped, AC 11's mechanism is fully implemented and tested (via the struct field directly) but has no path to ever be configured non-zero in a real deployment. Needs a small follow-up task before this spec is considered complete: add a `--record-snapshot-interval N` CLI flag to `main.c`, wired to `sched.record_snapshot_interval`, mirroring `--record-dir`'s exact pattern (task 9.1).
|
||||
- Task 9.3's review also flagged `test_record_lifecycle.c`'s size (826→1232 lines across tasks 9.2+9.3) as past this suite's established over-800 precedent (prior max `test_driver_translate.c`≈867) — the content itself is genuine, non-duplicative coverage (not padding), and a natural split point exists along the AC10 (lifecycle/TOCTOU)/AC11 (periodic-checkpoint/error-handling) seam by extracting shared fixture helpers into a common header. Non-blocking; worth a future cleanup pass, not urgent.
|
||||
- Cross-task interaction, flagged before task 9.4 exists — resolution decided (not a fail/retry, a deferral): task 9.2's `sched_record_start()` calls `xce_sched_core_save_snapshot()` as its first action (AC 10 step 1) — the same function task 8.2 gated to return `-EBUSY` whenever `session_active != 0`. `main.c`'s driver loop is single-threaded (`while(1) { xce_sched_driver_run_normal_mode_round(&sched); ...; sleep(1); }`); incoming requests (including the future `SchedAdminRequest`) arrive asynchronously via `scheduler_req_handler` (handler.c) on the I/O side, same as job/queue/worker updates. **Decision**: task 9.4 must NOT call `sched_record_start()` directly from the request-handling thread (that both risks racing `session_active` and violates this codebase's async-I/O rule against blocking/file-I/O in a request-handler callback). Instead, `recordStart` must be enqueued the same way job/queue/worker updates already are (`pend_updates` dataq or an equivalent dedicated pending-admin-request slot) and picked up by `xce_sched_driver_translate_pending_events()` — which already runs at the top of every round, strictly before `begin_session()`, so it is guaranteed between-sessions by the same construction argument design.md uses for `core_push_events()`. This means recording "takes effect" at the next scheduling round, not synchronously on request arrival. Consequence for task 9.4: since AC 10 specifies a synchronous request-reply carrying the resolved filename, the actual `SchedAdminReply` send must be deferred until the driver thread performs the queued start (not sent eagerly from the handler thread) — needs a completion signal back to the waiting request context (e.g. a condition variable, or replying once `xce_sched_driver_translate_pending_events()` reports the deferred start applied). Given this deferral makes the direct `-EBUSY` race effectively unreachable from the admin path, the AC 10 error-codes table doesn't need a new "busy" entry for this case — `sched_record_start()` keeps its `-EBUSY` return as a defensive/fail-loud contract check (task 9.2's job, no torn state), it just should no longer be a path task 9.4's dispatch can actually trigger by construction.
|
||||
- Task 12.1's review found a licensing gap (fixed): the readline()-based REPL input loop originally linked GNU readline (GPL) directly into `xce_scheduler`, a shipped production daemon (unlike `xce_test`, a debug-only binary controller's own readline use was already confined to). Switched project-wide to libedit (BSD) — `nixSupport/xce.nix`, both `meson.build`s, `repl.c`, and `src/controller/testsuite/test.c` now use `editline/readline.h` (API-compatible: `readline()`/`add_history()`), no hand-written accessor changes needed anywhere.
|
||||
- Task 12.1's review also surfaced bug-1504 (fixed) and bug-1505 (deferred, out of boundary) — see `.wolf/buglog.json`. Verifying the licensing fix required a full clean rebuild (new nix dependency), which exposed a genuine, pre-existing, deterministic SIGSEGV in `write_SnapshotData` unrelated to task 12.1's own repl.c: `SnapshotData.internalState` was a capnp `Data` field, and this codebase's capnpc-c backend silently no-ops Data-field encoding in generated `encode_XXX()` helpers without zero-initializing the caller's stack struct first — a real, reproducible UB crash, not a flaky test. Fixed (bug-1504) by switching `internalState` to `List(UInt8)` (controller.capnp, typemap.h, test_e2e_capnp.c) — this same capnpc-c backend DOES correctly auto-generate real encode/decode for `List(UInt8)` (proven precedent: `FetchFileReply.content`/`bytesRead`), eliminating the whole no-op class for this field with no hand-written accessor code. Re-verified with a full clean rebuild: scheduler suite 34/34, no regressions. While chasing this, also hit a **second, unrelated instance of the same bug class** in the controller's HA/WAL-catchup subsystem (`WalCatchupEntry.payload`, bug-847's own territory) — confirmed systemic (affects any `:Data` field in `controller.capnp` fed through the generic codec, not a one-off) but deliberately **left unfixed** (bug-1505) as out of scheduler-core's boundary; needs its own project-wide `:Data`-field audit as a separate task.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue