docs(spec): scheduler-sim-discrete-clock requirements, design and tasks approved
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
51736fa44f
commit
4b6872d053
5 changed files with 546 additions and 14 deletions
318
.kiro/specs/scheduler-sim-discrete-clock/design.md
Normal file
318
.kiro/specs/scheduler-sim-discrete-clock/design.md
Normal file
|
|
@ -0,0 +1,318 @@
|
|||
# Design Document
|
||||
|
||||
## Overview
|
||||
|
||||
**Purpose**: This feature makes the scheduler simulator's logical clock depend only on the recording, so a replay reproduces every clock-derived value (session start, written `beginRound`, decision timestamps, `SessionSummary.durationNs`, and values derived from the session start such as `xce_alloc_t.from`) exactly, on any host.
|
||||
|
||||
**Users**: Operators and engineers who replay recordings with `xce_scheduler --sim` (with `--sim-verify`, `--sim-output`, `--sim-alt-events`, or `--interactive`), and spec libxce-alloc-preason-equality, whose task 5.3 needs a replayed session to start at exactly the recorded `beginRound`.
|
||||
|
||||
**Impact**: The simulator already jumps its clock to each recorded timestamp; today the default `--sim-speed 1` lets it drift with real time between jumps. This design pins simulation mode to the core's existing frozen-clock setting (`speed_num = 0`), turns `--sim-speed` into a deprecated option that only logs a warning, removes the REPL `speed` parameter, and updates the operator docs and the scheduler-core spec. The core clock formula and normal mode are unchanged.
|
||||
|
||||
### Goals
|
||||
- In simulation mode the logical clock changes only at the start snapshot, at each recorded event, and at each recorded session (Req 1).
|
||||
- `--sim-speed` keeps existing command lines working in both modes, with one warning naming its value (Req 2).
|
||||
- The REPL no longer offers a speed setting (Req 3).
|
||||
- Docs and the scheduler-core spec describe the simulator as it behaves (Req 5).
|
||||
|
||||
### Non-Goals
|
||||
- Pacing a replay against the wall clock (not provided before or after).
|
||||
- Removing `--sim-speed` from the command line (a later release).
|
||||
- Changing the `.replay` format, the core clock formula, or normal-mode clock behavior (Req 4).
|
||||
- How `from` is computed and compared (spec libxce-alloc-preason-equality).
|
||||
|
||||
## Boundary Commitments
|
||||
|
||||
### This Spec Owns
|
||||
- The simulation-mode clock setting: `sim_set_virtual_clock()` stores `speed_num = 0`, `speed_den = 1`, and no simulation or REPL path sets any other speed.
|
||||
- The contract that, in simulation mode, a session's `core->session_start` equals its recorded `beginRound` exactly, including when that is earlier than the previous clock value (Req 1.3, 1.4).
|
||||
- The `--sim-speed` command-line behavior: accepted with any value in both modes, no effect, one startup warning (`sched_cli_warn_deprecated_options()`).
|
||||
- The REPL `set speed` removal message, and the `help`, `set` usage and `info config` text without speed.
|
||||
- The simulation sections of `src/docs/deployment.md`, and the speed-mode passages of `.kiro/specs/scheduler-core/requirements.md` and `design.md` listed in requirements.md 5.2.
|
||||
|
||||
### Out of Boundary
|
||||
- `xce_core_now()`'s formula and the four clock atoms in `core.h` (kept; only their documentation of which values are used changes).
|
||||
- Normal mode: `xce_core_set_wallclock_anchor()`, `driver.c`, recording (`recording*.c`).
|
||||
- The `.replay` schema (`src/schema/controller.capnp`).
|
||||
- `from` stamping and allocation equality (spec libxce-alloc-preason-equality).
|
||||
- The superseded `scheduler-framework` spec, and scheduler-core `brief.md`/`tasks.md` history.
|
||||
|
||||
### Allowed Dependencies
|
||||
- The core clock atoms and `xce_core_now()` (`include/core.h`, `core_clock.c`), read and written only through the existing sim driver functions.
|
||||
- The logging macros and `mylog_init()`/`mylog_fini()` in `src/include/utils.h` (`WARN`; the drain in `main.c`).
|
||||
- argp as already used by `main.c`.
|
||||
|
||||
### Revalidation Triggers
|
||||
- Any change to `sim_set_virtual_clock()`, `sim_advance_virtual_clock()`, the order of the clock jump and `xce_core_begin_session()` in `sim_loop.c`, or `xce_core_now()`: re-run libxce-alloc-preason-equality task 5.3 and this spec's clock tests.
|
||||
- Any new code in simulation mode that writes the clock atoms or reads real time.
|
||||
- Removing `--sim-speed` entirely (the later release): update `sched_cli_warn_deprecated_options()`, the help text, the soak script and `deployment.md`.
|
||||
|
||||
## Architecture
|
||||
|
||||
### Existing Architecture Analysis
|
||||
- One core, two drivers (`.kiro/steering/scheduler-driver-equivalence.md`): `driver.c` (normal) and `sim_loop.c` (simulation) drive the same core session protocol. This change is confined to the simulation driver's clock setting, the CLI entry point and the REPL.
|
||||
- `xce_core_now() = virtual_clock_base + (CLOCK_MONOTONIC_now - real_clock_base) * speed_num / speed_den` (`core_clock.c:80-119`). Core init seeds a neutral 1/1 (`core.c:142`) and normal mode re-seeds 1/1 in `xce_core_set_wallclock_anchor()` (`core_clock.c:180`); `speed_num = 0` returns `virtual_clock_base` exactly.
|
||||
- Simulation-mode clock writers: `sim_set_virtual_clock()` (`sim.c:309`, called once at `sim.c:594`), `sim_advance_virtual_clock()` at each event (`sim_loop.c:417`, after the `endSession` return at 412-415) and at each session (`sim_loop.c:1528`, immediately before the session record opens at 1538 and `xce_core_begin_session()` sets `session_start`). `repl_set_speed()` (`repl_params.c:202`) is the only other writer and is removed.
|
||||
|
||||
### Architecture Pattern & Boundary Map
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
subgraph CLI
|
||||
Main[main.c argp]
|
||||
Compat[cli_compat.c warn helper]
|
||||
end
|
||||
subgraph SimDriver
|
||||
SimStart[sim.c sim_set_virtual_clock]
|
||||
SimLoop[sim_loop.c sim_advance_virtual_clock]
|
||||
Repl[repl_params.c repl.c repl_info.c]
|
||||
end
|
||||
subgraph Core
|
||||
Clock[core_clock.c xce_core_now]
|
||||
Session[core_session.c session_start]
|
||||
end
|
||||
Main --> Compat
|
||||
Main --> SimStart
|
||||
SimStart --> Clock
|
||||
SimLoop --> Clock
|
||||
Session --> Clock
|
||||
Repl --> Clock
|
||||
```
|
||||
|
||||
- Selected pattern: reuse the core's frozen-clock setting; no new abstraction (research.md, Option A).
|
||||
- Existing patterns preserved: one core, two drivers; the core module stays free of new I/O (`test_core_isolation.c`).
|
||||
- New component: `cli_compat.c`, one function, so the deprecation warning is unit-testable and has one place to remove later.
|
||||
- The REPL only reads the clock after this change.
|
||||
|
||||
### Technology Stack
|
||||
|
||||
| Layer | Choice / Version | Role in Feature | Notes |
|
||||
|-------|------------------|-----------------|-------|
|
||||
| CLI | argp (glibc), existing | `--sim-speed` stays an option taking a value | No validation of the value |
|
||||
| Scheduler | C11, existing sources | Sim driver, REPL, CLI helper | No new dependency |
|
||||
| Logging | tlog via `WARN` (`utils.h`) | Deprecation warning | Emitted after `mylog_init()` |
|
||||
| Build/Test | Meson, Unity | New test binary, updated tests | `src/scheduler/testsuite/meson.build` |
|
||||
|
||||
## File Structure Plan
|
||||
|
||||
### Directory Structure
|
||||
```
|
||||
src/scheduler/
|
||||
├── include/
|
||||
│ ├── cli_compat.h # NEW: sched_cli_warn_deprecated_options() declaration
|
||||
│ ├── core.h # doc only: speed atoms hold 1/1 (normal) or 0/1 (simulation)
|
||||
│ └── sim.h # remove sched_sim_args_t.sim_speed and the sim_speed_num/den shadow copy; doc updates
|
||||
├── src/
|
||||
│ ├── cli_compat.c # NEW: the deprecation warning for --sim-speed
|
||||
│ ├── main.c # help text; no validation; call the warning after mylog_init(); stop passing a speed
|
||||
│ ├── sim.c # sim_set_virtual_clock(sim, snapshot_ts) stores speed 0/1
|
||||
│ ├── sim_loop.c # comments on sim_advance_virtual_clock (no drift, exact unclamped set)
|
||||
│ ├── sim_record.c # comment ~88-89 (no "non-frozen --sim-speed")
|
||||
│ ├── repl_params.c # delete repl_set_speed(); set speed removal message before the ntok check; usage text
|
||||
│ ├── repl_info.c # info config line without speed=; doc comment
|
||||
│ ├── repl.c # help without "set speed <N>"
|
||||
│ └── repl_internal.h # comment references only
|
||||
├── meson.build # add src/cli_compat.c to scheduler_srcs only
|
||||
└── testsuite/
|
||||
├── test_sim_discrete_clock.c # NEW: Req 1 clock tests and Req 2 warning tests
|
||||
├── meson.build # register the new test (links ../src/cli_compat.c, depends on the scheduler binary)
|
||||
└── (existing tests, see Modified Files)
|
||||
src/docs/deployment.md # simulation section
|
||||
.kiro/specs/scheduler-core/requirements.md, design.md # speed-mode passages (Req 5.2)
|
||||
```
|
||||
|
||||
### Modified Files
|
||||
- `src/scheduler/include/sim.h` — remove `sched_sim_args_t.sim_speed` (155-157) and `xce_sched_sim_driver_t.sim_speed_num`/`sim_speed_den` (90-91); update the comments at 44-50, 60-68, 136, 214-223.
|
||||
- `src/scheduler/src/sim.c` — `sim_set_virtual_clock(sim, snapshot_ts)` (drop the third parameter; store `speed_num = 0`, `speed_den = 1`; no shadow copy); caller at 594.
|
||||
- `src/scheduler/src/main.c` — option help text (92-94); remove the `sim_speed` default, validation and `sim_args.sim_speed` assignment (339, 356-368, 373) and the comment at 296-299 that cites it; update the comments at 67-72 (flag meanings live in `sched_sim_args_t`, which no longer has a speed field) and 74 (the `sim_speed` field); call `sched_cli_warn_deprecated_options(arguments.sim_speed)` once after `mylog_init()` (325) and before the `--sim` branch (335); when `arguments.sim_speed != NULL`, immediately after that call (still single-threaded: no picker, allocator or driver thread exists before `sched_sim_driver_run()` or `sched_init()`), call `mylog_fini()` (`utils.c:303`, `tlog_exit()`) and then `mylog_init()` again with the same arguments. tlog writes the log file from its own thread and registers no exit hook (the tlog-1.0.0 Nix package, `tinylog_dep` at `src/scheduler/meson.build:62`: no `atexit` or destructor in tlog.c); `tlog_exit()` joins that writer only after every buffered line is written (tlog.c:1725-1728, join at 2210), and re-initialising is supported (`tlog_exit()` clears `root_format`, tlog.c:2222, which `tlog_init()` checks at 2135; the file is reopened with `O_APPEND`, tlog.c:1371). Side effects of the drain: when gzip is missing, tlog's "can not find gzip command" notice is printed to stderr twice (tlog.c:2161-2163), and if the first write rotated an oversized log the drain waits for that compression to finish (tlog.c:1484-1486); both are harmless while single-threaded. This puts the warning on disk before any mode-specific startup, so it survives every exit path: the `--sim` branch's returns (main.c:352, 391, 396, 399; 366 is removed), the REPL's `exit(0)` on `quit` or end of input (repl.c:350), and normal-mode shutdown. `tlog_exit()` is deliberately not called at the `--sim` returns or in the REPL's quit: the picker and allocator threads are never joined (sim_loop.c:1374-1385), and `tlog_exit()` is unsafe while another thread logs (tlog.c:833-841 vs 2213-2219). The unreachable `-ENOSYS` branch (main.c:380-392; no simulation path returns `-ENOSYS`) is left untouched.
|
||||
- `src/scheduler/src/repl_params.c` — delete `repl_set_speed()` and its doc comment (174-227; keep and edit the section banner at 170-172); in `repl_cmd_set()` handle `speed` first; remove `speed` from the usage string (400-402); comment references (22, 44-48, 348).
|
||||
- `src/scheduler/src/repl_info.c` — `info config` line without `speed=` (1801-1806); comments (23, 1770-1784).
|
||||
- `src/scheduler/src/repl.c` — delete the help line at 314; comment at 253-254.
|
||||
- `src/scheduler/src/sim_loop.c`, `sim_record.c`, `repl_internal.h`, `include/core.h` — comments only; also `repl_params.c:171` (section banner) and `:388-394` (the `repl_cmd_set()` doc comment naming `set speed <N>`); `sim.c:227-230` (says `--sim-output` timestamps come from a clock that "never runs backwards", which Req 1.3 contradicts); `testsuite/meson.build` comments at 318-319, 566, 1178 and 1211.
|
||||
- `src/scheduler/meson.build` — add `'src/cli_compat.c'` to `scheduler_srcs` (the list starting at line 1) only; not to the sim isolation library list (~163), since it is not a sim file. The 22 test targets built with `foreach f : scheduler_srcs` pick it up harmlessly.
|
||||
- `src/scheduler/testsuite/meson.build` — register `sched_sim_discrete_clock_test` from `['test_sim_discrete_clock.c', '../src/cli_compat.c']` plus the white-box sim `#include`s the other sim tests use (it must not link `scheduler_srcs`, whose `sched_election.c` would clash with the test's stand-in picker/allocator); pass `-DXCE_SCHEDULER_BIN="<path>"` from `scheduler.full_path()` (`src/scheduler/meson.build:67`, defined before `subdir('testsuite')` at `:171`) and register it with `test('scheduler sim discrete clock test', …, suite: 'scheduler', env: ['ASAN_OPTIONS=detect_leaks=0'], depends: scheduler)` like the other scheduler tests.
|
||||
- **Build-order constraint for tasks**: removing the `sim.h` fields breaks compilation of `main.c`, `sim.c`, `repl_params.c`, `repl_info.c` and every test file that assigns `sim_speed` (plus the assignments in `test_repl_snapshot_params.c:1164-1165`, its shadow-field assertions at 896-914, and `test_sim_startup.c:442-443`). These edits must land in one task.
|
||||
- Tests (mechanical, remove `args.sim_speed = 0;`/`sim_args.sim_speed = …;`): `test_repl_breakpoints_inspection.c`, `test_repl_equivalence.c`, `test_repl_event_echo.c`, `test_repl_execution_control.c`, `test_repl_limit_table.c`, `test_repl_prompt_context.c`, `test_repl_round_rollup.c`, `test_repl_step_verbosity.c`, `test_sim_alt_events.c`, `test_sim_contention_parity.c`, `test_sim_e2e_limit_visibility.c`, `test_sim_e2e_original_motivation.c`, `test_sim_equivalence.c`, `test_sim_event_loop.c` (its direct core-atom writes at 703-704 stay), `test_sim_live_allocation.c`, `test_sim_print_decision_gate.c`, `test_sim_record_output.c`, `test_sim_resource_contention.c`, `test_sim_round_boundary_reconciliation.c`, `test_sim_session_ordering.c`, `test_sim_snapshot_reconcile_gap.c`, `test_sim_verify.c`, `test_repl_snapshot_params.c` (678, 814), `test_sim_startup.c` (422, 484, 521, 549, 571).
|
||||
- Tests (behavioral): `test_sim_startup.c` — keep the `core.speed_num == 0`/`speed_den == 1` assertions (now unconditional), drop the shadow-copy assertions (442-443); `test_repl_snapshot_params.c` — replace `test_set_speed_updates_shadow_and_core_atomics` with a removal-message test, update `test_info_config_reports_current_parameters` (no `speed=`; assert it is absent) and the help assertion (1212); `test_repl_help.c` — drop `set speed <N>` from the lists at 655 and 711 and the comments at 8, 555, 649; comment-only updates in `test_repl_snapshot_params.c` (18-28, 1044-1052, 1125-1135, 1144), `test_repl_step_verbosity.c` (30-32, 637) and `test_repl_resource_formatting.c` (16); `test_clock.c` — the three `…_simulation_…` speed tests (163-229) become formula tests named for the formula, not for product speed modes (including their `RUN_TEST` lines at 496-499) and the header comment at 5-18; the normal (1/1) and frozen (0/1) tests stay.
|
||||
- Unchanged: `test_maxtasks_wiring_sim_driver_parity.c` and `test_sched_alloc_from_stamp.c` (they write the core atoms, which remain). (requirements.md's "27" is the 24 files above that assign `sim_speed` plus three that write the core speed atoms directly: these two and `test_clock.c`.)
|
||||
- `src/docs/deployment.md` — 214 (example without `--sim-speed`), 220 (deprecated, no effect), 223 (no pacing difference between REPL and batch); describe the clock as moving only at recorded timestamps.
|
||||
- `.kiro/specs/scheduler-core/requirements.md` — flag table (359), AC 3 (383-405), AC 12 (750), AC 13 (786-791), AC 14 (797-798, including "strictly monotonically increasing"), AC 22 (1053, 1057, 1059, 1070-1072, 1102), Data Structure Changes Summary (1244).
|
||||
- `.kiro/specs/scheduler-core/design.md` — 5.14 row (299), sim-driver fields (529), `test_clock.c` mode rows (760), "Simulation fast mode" (783).
|
||||
|
||||
## System Flows
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Main as main.c
|
||||
participant Sim as sim.c
|
||||
participant Loop as sim_loop.c
|
||||
participant Core as core
|
||||
Main->>Main: argp parse, mylog_init
|
||||
Main->>Main: sched_cli_warn_deprecated_options(sim_speed)
|
||||
Main->>Main: if sim_speed given: mylog_fini, mylog_init (drain)
|
||||
Main->>Sim: sched_sim_driver_run(args)
|
||||
Sim->>Core: sim_set_virtual_clock(snapshot_ts): vbase=snapshot_ts, speed 0/1
|
||||
loop each record
|
||||
Loop->>Core: event: vbase = event.ts
|
||||
Loop->>Core: session: vbase = beginRound
|
||||
Loop->>Core: xce_core_begin_session: session_start = xce_core_now = beginRound
|
||||
Loop->>Loop: decisions and output use xce_core_now = beginRound
|
||||
end
|
||||
```
|
||||
|
||||
- Every clock set is exact and unconditional; a value earlier than the current clock is set as is (Req 1.3). `endSession` does not move the clock (Req 1.2).
|
||||
- Because `speed_num = 0`, nothing between two sets moves the clock: REPL waits, allocator work, output writing, the post-session pause and the end-of-replay park all read the last set value (Req 1.5).
|
||||
|
||||
## Requirements Traceability
|
||||
|
||||
| Requirement | Summary | Components | Interfaces | Flows |
|
||||
|-------------|---------|------------|------------|-------|
|
||||
| 1.1 | Clock = start snapshot's timestamp | SimClock | `sim_set_virtual_clock()` | Startup |
|
||||
| 1.2 | Exact set at each event/session | SimClock | `sim_advance_virtual_clock()` | Replay loop |
|
||||
| 1.3 | Earlier timestamps set exactly, no error | SimClock | `sim_advance_virtual_clock()` | Replay loop |
|
||||
| 1.4 | `session_start == beginRound` | SimClock (reads `core_session.c:429`, unchanged) | `xce_core_begin_session()` | Replay loop |
|
||||
| 1.5 | No other clock movement | SimClock, ReplSet | speed 0/1; `repl_set_speed()` removed, REPL read-only | Replay loop |
|
||||
| 1.6 | Identical logical timestamps and decisions across replays | SimClock | all of the above | Replay loop |
|
||||
| 1.7 | Written `beginRound` = start time | SimClock (`sim_loop.c` output writes) | `sched_record_session_ctx_new(xce_core_now())` | Replay loop |
|
||||
| 1.8 | `durationNs` 0, decision `ts` = `beginRound` | SimClock (`sim_loop.c` output writes) | finalize and decision writes | Replay loop |
|
||||
| 2.1 | Accepted in both modes | CliCompat, main | argp option | Startup |
|
||||
| 2.2 | No effect | main, SimClock | no speed plumbing | Startup |
|
||||
| 2.3 | One warning naming the last value | CliCompat, main | `sched_cli_warn_deprecated_options()` | Startup |
|
||||
| 2.4 | Non-numeric/negative/empty accepted | main, CliCompat | no validation | Startup |
|
||||
| 2.5 | No warning without the option | CliCompat | NULL argument | Startup |
|
||||
| 2.6 | Help text | main | argp table | — |
|
||||
| 3.1 | `set speed` message, no state change | ReplSet | `repl_cmd_set()` | REPL |
|
||||
| 3.2 | Returns to prompt, keeps reading | ReplSet | returns false | REPL |
|
||||
| 3.3 | Help and usage without speed | ReplSet, ReplHelp | text | REPL |
|
||||
| 3.4 | `info config` without speed | ReplInfo | text | REPL |
|
||||
| 3.5 | Other `set` parameters unchanged | ReplSet | `repl_cmd_set()` | REPL |
|
||||
| 4.1 | Normal mode unchanged | (none changed) | `xce_core_set_wallclock_anchor()` untouched | — |
|
||||
| 4.2 | Old recordings replay | SimClock | reader unchanged | Replay loop |
|
||||
| 4.3 | `.replay` format unchanged | (none changed) | schema untouched | — |
|
||||
| 5.1 | Operator docs | Docs | `deployment.md` | — |
|
||||
| 5.2 | scheduler-core spec | Docs | requirements.md, design.md | — |
|
||||
| 5.3 | In-repo callers keep passing | CliCompat, main | soak script unchanged | Startup |
|
||||
|
||||
## Components and Interfaces
|
||||
|
||||
| Component | Domain/Layer | Intent | Req Coverage | Key Dependencies | Contracts |
|
||||
|-----------|--------------|--------|--------------|------------------|-----------|
|
||||
| SimClock (`sim.c`, `sim_loop.c`) | Sim driver | Pin speed 0/1; exact sets | 1.1-1.8, 2.2, 4.2 | core clock atoms (P0) | State |
|
||||
| CliCompat (`cli_compat.c`) | CLI | Deprecation warning | 2.1, 2.3, 2.4, 2.5, 5.3 | `WARN` (P0) | Service |
|
||||
| main (`main.c`) | CLI | Option table, call order, log drain | 2.1, 2.2, 2.3, 2.4, 2.6, 5.3 | argp, CliCompat (P0) | — |
|
||||
| ReplSet / ReplHelp / ReplInfo (`repl_params.c`, `repl.c`, `repl_info.c`) | REPL | Remove speed | 1.5, 3.1-3.5 | — | — |
|
||||
| Docs (`deployment.md`, scheduler-core spec) | Docs | Describe actual behavior | 5.1, 5.2 | — | — |
|
||||
|
||||
### Sim driver
|
||||
|
||||
#### SimClock
|
||||
|
||||
| Field | Detail |
|
||||
|-------|--------|
|
||||
| Intent | Keep the simulation-mode logical clock at the last recorded timestamp set |
|
||||
| Requirements | 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 2.2, 4.2 |
|
||||
|
||||
**Responsibilities & Constraints**
|
||||
- `sim_set_virtual_clock()` is the only place that sets the speed in simulation mode, and it sets 0/1.
|
||||
- `sim_advance_virtual_clock()` sets `virtual_clock_base` (and `real_clock_base`) unconditionally: no clamping, no warning, no error for a value earlier than the current clock.
|
||||
- The clock jump to `beginRound` (`sim_loop.c:1528`) stays before both the output session record opening (1538) and `xce_core_begin_session()` (1548); with speed 0 that is what makes 1.4, 1.7 and 1.8 hold (the relative order of the last two does not matter). The record is opened from `xce_core_now()`, not `core.session_start`, because it opens before `xce_core_begin_session()` sets that field.
|
||||
|
||||
**Contracts**: State [x]
|
||||
|
||||
##### State Management
|
||||
```c
|
||||
/* sim.c — replaces sim_set_virtual_clock(sim, snapshot_ts, sim_speed) */
|
||||
static void sim_set_virtual_clock(xce_sched_sim_driver_t* sim,
|
||||
uint64_t snapshot_ts);
|
||||
```
|
||||
- Postconditions: `core.virtual_clock_base == snapshot_ts`, `core.speed_num == 0`, `core.speed_den == 1`, so `xce_core_now(&sim->core) == snapshot_ts` until the next `sim_advance_virtual_clock()`.
|
||||
- Invariant (simulation mode): `xce_core_now()` returns the most recent value passed to `sim_set_virtual_clock()` or `sim_advance_virtual_clock()`, regardless of elapsed real time.
|
||||
|
||||
`sched_sim_args_t` loses `sim_speed`; `xce_sched_sim_driver_t` loses `sim_speed_num` and `sim_speed_den`. The core's `speed_num`/`speed_den` remain; `core.h` documents their two used values (1/1 normal, 0/1 simulation).
|
||||
|
||||
### CLI
|
||||
|
||||
#### CliCompat
|
||||
|
||||
| Field | Detail |
|
||||
|-------|--------|
|
||||
| Intent | Log the one deprecation warning for `--sim-speed` |
|
||||
| Requirements | 2.1, 2.3, 2.4, 2.5, 5.3 |
|
||||
|
||||
**Contracts**: Service [x]
|
||||
|
||||
##### Service Interface
|
||||
```c
|
||||
/* include/cli_compat.h */
|
||||
|
||||
/*
|
||||
* sched_cli_warn_deprecated_options - logs one WARN per deprecated option
|
||||
* that was given. sim_speed is the raw --sim-speed value as argp stored it
|
||||
* (the last occurrence), or NULL when the option was not given.
|
||||
*/
|
||||
void sched_cli_warn_deprecated_options(const char* sim_speed);
|
||||
```
|
||||
- Preconditions: called once, after `mylog_init()`, before any mode-specific startup.
|
||||
- Postconditions: `sim_speed == NULL` logs nothing; otherwise exactly one `WARN` line containing `--sim-speed`, the words "deprecated" and "no effect", "will be removed", and the value in quotes (so an empty value is visible as `""`).
|
||||
- Never fails, never exits, never parses the value.
|
||||
|
||||
**Implementation Notes**
|
||||
- `main.c` keeps `OPT_SIM_SPEED` with argument name `"N"` so a trailing `--sim-speed` stays an argp error (Req 2.4); help text: "Deprecated, has no effect: the simulator clock moves only at recorded timestamps. Will be removed in a future release."
|
||||
|
||||
### REPL
|
||||
|
||||
#### ReplSet / ReplHelp / ReplInfo
|
||||
|
||||
| Field | Detail |
|
||||
|-------|--------|
|
||||
| Intent | Remove the speed parameter from the REPL |
|
||||
| Requirements | 1.5, 3.1, 3.2, 3.3, 3.4, 3.5 |
|
||||
|
||||
**Implementation Notes**
|
||||
- `repl_cmd_set()`: if `ntok >= 2` and `tokens[1]` is `"speed"`, print `[sim] set speed: the speed parameter was removed; the simulator clock moves only at recorded timestamps` and return false (re-prompt, same as `help`); nothing else changes. Then the existing `ntok < 3` usage check, whose text becomes `set output <FILE>|off | set verify on|off | set units raw|human|both`.
|
||||
- `repl_info_config()` prints `[sim] config: output=%s verify=%s events_processed=%llu current_ts=%llu (%s) sessions=%llu`.
|
||||
- `repl.c` help drops the `set speed <N>` line; `set output`, `set verify`, `set units` lines are unchanged.
|
||||
|
||||
## Data Models
|
||||
|
||||
No persisted or wire data changes. The `.replay` schema is untouched (Req 4.3). In-memory changes: `sched_sim_args_t.sim_speed` and `xce_sched_sim_driver_t.sim_speed_num`/`sim_speed_den` are removed; the core's four clock atoms are unchanged. Simulation output values change only in content: `SessionSummary.durationNs` becomes 0 and `ReplayDecision.ts` equals the session's `beginRound`; in simulation mode the snapshot blob's diagnostic `session_duration_ns` is 0 after any replayed session, while a snapshot written before the first replayed session ends (the `--sim-output` initial SnapshotEntry, or a REPL `snapshot`/`set output` at the pre-replay pause) carries the value loaded from the recorded snapshot (`snapshot_load.c:448-449`).
|
||||
|
||||
## Error Handling
|
||||
|
||||
- `--sim-speed` never produces an error or a non-zero exit, except the argp error for a missing value, which is unchanged.
|
||||
- A recorded timestamp earlier than the current clock is not an error (Req 1.3).
|
||||
- `set speed` is not an error; it returns to the prompt.
|
||||
- When `--sim-speed` is given, `main()` drains the logger right after logging the warning (`mylog_fini()` then `mylog_init()`, while still single-threaded), so the warning is on disk before any mode-specific startup, whether the process later returns from `main()` or exits through the REPL's `exit(0)`. The drain's only other observable effect, a repeated tlog "can not find gzip command" notice on stderr when gzip is absent, is accepted as outside Req 2.2's "only difference" (tlog's own diagnostic, not scheduler behavior; stdout, log content and simulation output are unaffected).
|
||||
- Monitoring: the startup `WARN` is the only new log line; it goes to `--logfile` when given, otherwise to the default `/tmp/xce_scheduler.log` (`main.c:216`), in both modes.
|
||||
|
||||
## Testing Strategy
|
||||
|
||||
New test binary `test_sim_discrete_clock.c` (registered as `scheduler sim discrete clock test`), using the in-process fixtures and stand-in picker/allocator pattern of the existing sim tests (e.g. `test_sim_record_output.c`, `test_sim_session_ordering.c`):
|
||||
|
||||
- **Unit (SimClock)**:
|
||||
- After `sched_sim_driver_start()`, `xce_core_now()` equals the chosen snapshot's timestamp, stays equal after a real sleep (≥ 20 ms), and the core holds `speed_num == 0`, `speed_den == 1` (1.1, 1.5).
|
||||
- A replay whose second session's `beginRound` is earlier than the preceding event's timestamp: that session's `session_start` equals its `beginRound`, and the replay completes without error. Second case, as the normal-mode recorder produces: events whose `ts` is earlier than the start snapshot's timestamp; after each such event `xce_core_now()` equals that event's `ts` (observed by calling the `static` `sim_process_replay_event_message()` directly, reachable through the white-box `#include` of `sim_loop.c`), and the replay completes (1.3). A fixture whose last recorded event (`ts = T`) is followed by a second SnapshotEntry with `ts = S` and then the `endSession` marker (whose `ts` differs from both `T` and `S`, e.g. 0 as `test_sim_record_output.c`'s `append_end_of_stream_marker()` writes), where `S` differs from `T` and is greater than the initial snapshot's `ts` (a SnapshotEntry can be hand-built as `test_sim_startup.c:369-390` does): run it through `sched_sim_driver_run()` in batch mode with the stand-in picker/allocator and `args.sim_start_ts` set to the initial snapshot's `ts`, so `sim.c:218` does not pick `S` as the start snapshot and the real skip branch at `sim_loop.c:1684-1692` runs; after the run returns 0, and again after a real sleep (≥ 20 ms), `xce_core_now()` equals `T` (1.5: mid-replay snapshot skip, and after the end of the recording).
|
||||
- **Integration (replay loop)**:
|
||||
- With a stand-in allocator that sleeps (≥ 10 ms) before deciding and a recorded `beginRound` that is not a whole second: `core->session_start`, recorded by the stand-in allocator on every `cbuf_get` return (including the `jobid == 0` terminator, so a session with no pending jobs is still observed), equals `beginRound` exactly (1.2, 1.4), and simulation output (`sim->record_file` to a temp file) has each session's `beginRound` equal to the recorded one, `summary.durationNs == 0`, and every `ReplayDecision.ts == beginRound` (1.7, 1.8).
|
||||
- The same recording replayed twice to two output files: identical session start times, `ReplayDecision.ts`, `durationNs`, snapshot timestamps and `recordingStartTs`, and identical decision sequences; `recordingStartWall` is not compared (1.6). The REPL's reported position (prompt and `info session`) reads `xce_core_now()`, so its equality across runs follows from the SimClock invariant proven above.
|
||||
- Fixtures written by the unchanged recorder and fixture writers (the format is untouched, 4.3) replay without error across the whole sim suite, as do the `/xce-test-ha` soak's recordings (4.2).
|
||||
- **Unit (CliCompat)**, with a local copy of the `capture_stdout_around()` helper (each test file carries its own, e.g. `test_repl_help.c`); the test binary must not call `mylog_init()`, so `WARN` takes tlog's early-print path to stdout (tlog.c:838-839): `"0"`, `"abc"`, `"-1"` and `""` each produce exactly one line naming the value and containing `--sim-speed`, "deprecated", "no effect" and "will be removed"; `NULL` produces no output (2.3, 2.4, 2.5).
|
||||
- **REPL** (update `test_repl_snapshot_params.c`, `test_repl_help.c`): `set speed 7` and bare `set speed` print the removal message, return false, and leave `xce_core_now()` and the core atoms unchanged; `set` usage and `help` do not contain `speed`; `info config` does not contain `speed=` and still shows `output=`, `verify=`, `events_processed=`, `current_ts=`, `sessions=`; `set output`, `set verify`, `set units` tests pass unchanged; `set speed 7` followed by `info config` and `quit` in a script file that a forked child `freopen`s as stdin (the `child_step_snapshot_quit()` pattern, `test_repl_snapshot_params.c:656-690`; stdout `freopen`ed to a capture file; the parent `waitpid`s): the child exits 0 and the capture contains both the removal message and the `[sim] config:` line (a non-tty stdin exercises the same `readline()` path as a pipe) (3.1-3.5).
|
||||
- **Regression**: the full scheduler suite (every sim and REPL test now runs at the pinned speed with no speed field), `test_clock.c` normal-mode 1/1 test and `test_sched_alloc_from_stamp.c` normal-mode test unchanged (4.1); `test_core_isolation.c` passes (core untouched).
|
||||
- **Manual/soak (5.3)**: `/xce-test-ha` runs its `--sim-verify --sim-speed=0` step unmodified and passes; the step's log contains the deprecation warning.
|
||||
- **CLI help (2.6)**: in `test_sim_discrete_clock.c`, `popen(XCE_SCHEDULER_BIN " --help")` and assert the `--sim-speed` entry contains "Deprecated" and the whole output contains neither "real-time" nor "as-fast-as-possible".
|
||||
- **CLI end-to-end (1.4, 1.7, 1.8 on the default command line; 2.1-2.5)**, in `test_sim_discrete_clock.c`, via `popen()` of `XCE_SCHEDULER_BIN` (the child inherits the test's `ASAN_OPTIONS=detect_leaks=0`) against a fixture the test writes, whose `beginRound` is not a whole second. The in-process tests above cannot fail on the pre-change code, because a zero-initialized `sched_sim_args_t` already selected speed 0; this test is the one that does (default speed 1 at `main.c:339`, `strtoull` rejection at `main.c:356-368`).
|
||||
- (a) `--sim FIXTURE --sim-output OUT0 --logfile LOG0` with no `--sim-speed` exits 0; OUT0's sessions have `beginRound` equal to the recorded value, `summary.durationNs == 0`, and every `ReplayDecision.ts == beginRound`; LOG0 is absent or has no `--sim-speed` line (1.7, 1.8, 2.5; 1.4 on the default command line follows because `session_start` (`core_session.c:429`) and the written `beginRound` (`sim_loop.c:1538`) are both `xce_core_now()` reads with no clock write between them, and 1.4 itself is proven in-process). The fixture contains a queue update and a pending job before each session (as `test_sim_record_output.c`'s `append_queue_update_event()`/`append_jobs_push_event()` do), and the test asserts at least one decision per session, so the `ts` check is not vacuous.
|
||||
- (b) The same command, but with a fresh `--sim-output OUTn` and `--logfile LOGn` for each run, plus each of `--sim-speed 7`, `--sim-speed abc`, `--sim-speed -1` and `--sim-speed=` exits 0, produces output identical to OUT0 in every field except `recordingStartWall` (compared byte-for-byte after the first message; the `ReplayHeader` is decoded with `decode_ReplayHeader` and compared field by field except `recordingStartWall`) (2.2), and logs exactly one line containing `--sim-speed` and the quoted value (2.1, 2.3, 2.4).
|
||||
- (c) `--sim-speed 1 --sim-speed 9`, with its own fresh `--sim-output` and `--logfile`, logs exactly one such line, naming `"9"` (2.3).
|
||||
- (d) `--sim FIXTURE --sim-speed` with no value exits non-zero (argp usage error) (2.4).
|
||||
- (e) `--sim FIXTURE -i --sim-speed 3 --logfile LOGI < /dev/null` (end of input → `quit` → `exit(0)`, repl.c:350) exits 0, and LOGI contains exactly one line containing `--sim-speed` and `"3"` (2.3 on the REPL exit path).
|
||||
- Mechanics: the test reads the child's stdout to EOF before `pclose()` (or redirects it to `/dev/null` in the command), since the child prints decision lines, and takes the exit code from `WEXITSTATUS(pclose(...))`.
|
||||
- Normal mode (2.1, 2.3) is covered by the CliCompat unit test plus the call site's placement before the `--sim` branch in `main.c`, because normal mode needs a controller.
|
||||
|
||||
## Project Invariant Cross-Check
|
||||
|
||||
Scheduler dimensions from `.claude/skills/xce-audit-invariants/SKILL.md`:
|
||||
1. Driver state-equivalence: only the simulation driver's clock setting changes; the session protocol calls in `sim_loop.c` and `driver.c` are untouched. A replayed `session_start` and written `beginRound` now equal the recorded values exactly; `durationNs = 0` and decision `ts = beginRound` are intentional simulation-output differences (requirements, Adjacent expectations); decisions and accounting are unaffected. Pass.
|
||||
2. Core module I/O isolation: no core-family source changes except comments in `core.h`. Pass.
|
||||
3. Picker→allocator `jbuf` capacity-1 rendezvous: untouched. Pass.
|
||||
4. Snapshot save/load field completeness: no persistence change; `session_start` and `session_duration_ns` still round-trip unchanged, and after a replayed session their values are `beginRound` and 0. Pass.
|
||||
|
|
@ -3,9 +3,70 @@
|
|||
## Project Description (Input)
|
||||
**Who has the problem:** operators and engineers who replay scheduler recordings with `xce_scheduler --sim` / `--sim-verify` or the simulation REPL, and the libxce-alloc-preason-equality work, which needs a replay to reproduce values derived from a session's start time bit-for-bit.
|
||||
|
||||
**Current situation:** the simulator is already event-driven: `sim_loop.c` never sleeps, and it jumps the virtual clock with `sim_advance_virtual_clock()` to each recorded event timestamp and to each recorded session's `beginRound`. `--sim-speed` (default 1, help text "1=real-time") does not pace anything. It only scales how far the clock drifts with real elapsed time between those jumps (`xce_core_now() = virtual_clock_base + elapsed_real * speed_num / speed_den`). So at the default speed, every clock reading in a replay (`core->session_start`, the `--sim-record` `beginRound`, decision timestamps, REPL position) depends on how fast the host happens to run. For example, a replayed session's `session_start` lands slightly after the recorded `beginRound`. Only `--sim-speed 0` is deterministic. The REPL exposes the same knob as its `speed` parameter.
|
||||
**Current situation:** the simulator is already event-driven: `sim_loop.c` never sleeps, and it jumps the virtual clock with `sim_advance_virtual_clock()` to each recorded event timestamp and to each recorded session's `beginRound`. `--sim-speed` (default 1, help text "1=real-time") does not pace anything. It only scales how far the clock drifts with real elapsed time between those jumps (`xce_core_now() = virtual_clock_base + elapsed_real * speed_num / speed_den`). So at the default speed, every clock reading in a replay (`core->session_start`, the `beginRound` written to simulation output (`--sim-output FILE` or the REPL `set output FILE`), decision timestamps, REPL position) depends on how fast the host happens to run. For example, a replayed session's `session_start` lands slightly after the recorded `beginRound`. Only `--sim-speed 0` is deterministic. The REPL exposes the same knob as its `speed` parameter.
|
||||
|
||||
**What should change:** make the simulator a pure discrete-event simulation. Remove `--sim-speed`, the REPL `speed` parameter, and the simulator's use of `speed_num`/`speed_den`, so the virtual clock moves only when the replay reaches a recorded event or round timestamp and never drifts with real time. Guarantee that a replayed session's `core->session_start` equals the recorded ReplaySession `beginRound` exactly, so values derived from it (for example `xce_alloc_t.from`, spec libxce-alloc-preason-equality Requirement 4.3) reproduce under `--sim-verify`. Amend the scheduler-core spec where it defines the speed modes (Req 5 AC 3 mode table, AC 13 simulation startup). Expected touch points: `main.c` CLI, `sim.c` `sim_set_virtual_clock()`, `sim_loop.c`, `repl_params.c`/`repl_info.c`, `core_clock.c`/`core.h`, about 20 scheduler tests that set a speed, and `src/docs/deployment.md`. Normal-mode (live) scheduling is unchanged.
|
||||
**What should change:** make the simulator a pure discrete-event simulation. Retire `--sim-speed` (see Decided below), remove the REPL `speed` parameter, and remove the simulator's use of `speed_num`/`speed_den`, so the virtual clock moves only when the replay reaches a recorded event or round timestamp and never drifts with real time. Guarantee that a replayed session's `core->session_start` equals the recorded ReplaySession `beginRound` exactly, so values derived from it (for example `xce_alloc_t.from`, spec libxce-alloc-preason-equality Requirement 4.3) reproduce under `--sim-verify`. Amend the scheduler-core spec wherever it describes speed modes or wall-clock pacing (Requirement 5.2 lists the places). Expected touch points: `main.c` CLI, `sim.c` `sim_set_virtual_clock()`, `include/sim.h` (`sched_sim_args_t.sim_speed`, the `sim_speed_num`/`sim_speed_den` shadow copy), `sim_loop.c`, `repl.c` (help text), `repl_params.c`/`repl_info.c`, `core_clock.c`/`core.h`, the 27 scheduler test files that set a speed, plus `test_repl_help.c`, which asserts the help text lists `set speed <N>`, and `src/docs/deployment.md`. Normal-mode (live) scheduling is unchanged.
|
||||
|
||||
**Decided (2026-09-28):** `--sim-speed` is kept for one release as an accepted, deprecated no-op that logs one warning, so existing scripts and command lines keep working; the REPL `speed` parameter is removed.
|
||||
|
||||
## Introduction
|
||||
|
||||
The scheduler simulator (`xce_scheduler --sim`, with or without `--sim-verify`, `--sim-output`, `--sim-alt-events`, `--interactive`) replays a recording by moving its logical clock from one recorded timestamp to the next. The `--sim-speed` option and the REPL `set speed` command were specified (scheduler-core spec, Requirement 5's mode-selection flag table, AC 3/13/14 and the REPL parameter table) to pace a replay against the wall clock, but no pacing was ever implemented: the simulator never waits. Their only effect is that the logical clock drifts with real elapsed time between recorded timestamps, which makes every time value observed during a replay depend on host speed and load. Only speed 0 is deterministic, and every simulator test that runs a replay already uses it. This feature makes the simulator's logical clock purely event-driven in every run, keeps `--sim-speed` accepted as a deprecated option with no effect, removes the REPL `speed` parameter, and records the change in the documentation and in the scheduler-core spec.
|
||||
|
||||
## Boundary Context
|
||||
|
||||
- **In scope**: the simulator's logical-clock behavior in simulation mode (batch replay, `--sim-verify`, simulation output (`--sim-output FILE` or the REPL `set output FILE`), `--sim-alt-events`, and the interactive REPL); the `--sim-speed` command-line option becoming a deprecated no-op; removal of the REPL `speed` parameter from `set`, `help`, and `info config`; the operator documentation of simulation mode; the scheduler-core spec's description of simulation speed modes.
|
||||
- **Out of scope**: normal-mode (live, controller-connected) scheduling and its wall-clock-anchored clock; the `.replay` file format; pacing a replay against the wall clock (not provided today and not added here; a future feature that wants it must not change logical time); removing `--sim-speed` from the command line entirely (a later release); how `from` is computed and compared (spec libxce-alloc-preason-equality); the decomposed `scheduler-framework` spec, which is kept only as discovery context and whose speed-mode text is superseded by scheduler-core.
|
||||
- **Adjacent expectations**: spec libxce-alloc-preason-equality relies on Requirements 1.2 and 1.4 (a replayed session starts at exactly the recorded `beginRound`), and 1.3 for the case where that moves the clock backwards. Recordings made before this change remain replayable unchanged. Logical time no longer passes inside a replayed session, so a session's elapsed time reported by the simulator (`SessionSummary.durationNs` in simulation output) is zero, because the replay reaches no recorded timestamp while a session is in progress; this is expected, and is what speed 0 already reports today.
|
||||
|
||||
## Requirements
|
||||
<!-- Will be generated in /kiro-spec-requirements phase -->
|
||||
|
||||
### Requirement 1: Event-driven logical clock in simulation mode
|
||||
**Objective:** As an operator replaying a recording, I want the simulator's logical time to depend only on the recording, so that every replay of the same recording shows the same times and decisions on any host.
|
||||
|
||||
#### Acceptance Criteria
|
||||
1. When a simulation run starts, the scheduler simulator shall set its logical clock to the timestamp of the snapshot it starts from (the last snapshot in the file whose timestamp is at or before `--sim-start-ts`, or the last snapshot in the file when `--sim-start-ts` is not given).
|
||||
2. When the replay reads a recorded event (other than the end-of-stream `endSession` marker) or a recorded session, the scheduler simulator shall set its logical clock to exactly that event's timestamp or that session's `beginRound`.
|
||||
3. If a recorded event timestamp or `beginRound` is earlier than the current logical clock, the scheduler simulator shall still set its logical clock to exactly that recorded value, without clamping it and without treating it as an error. This happens with well-formed recordings (the normal-mode recorder stamps a drained event batch with a time read before `record start` stamps the initial snapshot, so the first events after that snapshot can be earlier than it) as well as with a `--sim-alt-events` file or recording whose timestamps are out of order.
|
||||
4. When the scheduler simulator starts a replayed session, the scheduler simulator shall set the session's start time (`core->session_start`) to exactly the recorded session's `beginRound`.
|
||||
5. The scheduler simulator shall change its logical clock only as described in 1.1, 1.2 and 1.3, and shall keep it unchanged at all other times, however much real time passes: before the first recorded event, between recorded events and sessions, when it skips a mid-replay snapshot, after the replay reaches the end of the recording, and while the interactive REPL waits for operator input.
|
||||
6. When the same recording is replayed twice with the same options and, for interactive runs, the same sequence of REPL commands, the scheduler simulator shall produce identical logical timestamps in both runs (session start times; `ReplayDecision.ts`, `SessionSummary.durationNs`, snapshot timestamps and `ReplayHeader.recordingStartTs` in simulation output; and the REPL's reported position) and identical decision sequences, regardless of host speed or load. `ReplayHeader.recordingStartWall`, a wall-clock field, is excluded.
|
||||
7. Where simulation output (`--sim-output FILE` or the REPL `set output FILE`) is enabled, the scheduler simulator shall write each session it writes to that output with `beginRound` equal to that session's start time.
|
||||
8. Where simulation output (`--sim-output FILE` or the REPL `set output FILE`) is enabled, the scheduler simulator shall write each session it writes to that output with `summary.durationNs` as 0 and each of its `ReplayDecision.ts` values equal to that session's `beginRound`.
|
||||
|
||||
### Requirement 2: `--sim-speed` accepted as a deprecated no-op
|
||||
**Objective:** As an operator with existing scripts that pass `--sim-speed`, I want those command lines to keep working, so that the change does not break my tooling while I remove the option.
|
||||
|
||||
#### Acceptance Criteria
|
||||
1. When `xce_scheduler` is started with `--sim-speed` and any value, in simulation mode or in normal mode, the scheduler shall accept the option and start normally.
|
||||
2. When `xce_scheduler` is started with `--sim-speed`, the scheduler shall behave exactly as it would without the option, including the logical-clock behavior of Requirement 1; the only difference shall be the warning of 2.3.
|
||||
3. When `xce_scheduler` is started with `--sim-speed`, in simulation mode or in normal mode, the scheduler shall log exactly one warning at startup stating that `--sim-speed` is deprecated, has no effect, and will be removed in a future release, and naming the value it received (the last value, if the option is given more than once).
|
||||
4. If the value given to `--sim-speed` is not a valid number (including a non-numeric, negative, or empty value), the scheduler shall still start normally and log the same single warning, instead of rejecting the command line. (`--sim-speed` as the last command-line argument, with no value after it, remains a command-line error, as for any option that takes a value.)
|
||||
5. When `xce_scheduler` is started without `--sim-speed`, the scheduler shall not log the deprecation warning.
|
||||
6. The scheduler's command-line help shall describe `--sim-speed` as deprecated with no effect, and shall not describe any speed or real-time replay mode.
|
||||
|
||||
### Requirement 3: REPL `speed` parameter removed
|
||||
**Objective:** As an operator using the interactive REPL, I want the REPL to stop offering a speed setting that does nothing, so that its commands and output reflect how the simulator actually behaves.
|
||||
|
||||
#### Acceptance Criteria
|
||||
1. When the operator enters `set speed`, with or without an argument, the REPL shall change no simulation state and shall print a message stating that the speed parameter was removed because the simulator's clock moves only at recorded timestamps.
|
||||
2. When the REPL has handled `set speed`, the REPL shall return to its prompt as it does after `help`, and shall not end the simulation or stop reading further commands, whether they are typed or piped in.
|
||||
3. The REPL's `help` output and its `set` usage message shall not list a `speed` parameter.
|
||||
4. When the operator enters `info config`, the REPL shall show the remaining configuration fields (output, verify, events processed, current timestamp, sessions) and shall not show a speed field (the `[sim] config:` line loses its `speed=` item).
|
||||
5. The REPL's other `set` parameters (`output`, `verify`, `units`) shall behave as before this change.
|
||||
|
||||
### Requirement 4: Normal mode and recordings unchanged
|
||||
**Objective:** As an operator running a live cluster, I want this change confined to simulation, so that live scheduling and existing recordings are unaffected.
|
||||
|
||||
#### Acceptance Criteria
|
||||
1. While the scheduler runs in normal mode, the scheduler shall keep its existing wall-clock-anchored time behavior unchanged.
|
||||
2. When the scheduler simulator replays a recording made before this change, the scheduler simulator shall read and replay it without error.
|
||||
3. The scheduler shall keep the `.replay` file format unchanged, so recordings made after this change remain readable by the same tools as before.
|
||||
|
||||
### Requirement 5: Documentation and specification alignment
|
||||
**Objective:** As an engineer reading the docs or the scheduler-core spec, I want them to describe the simulator as it actually behaves, so that nobody relies on a pacing mode that does not exist.
|
||||
|
||||
#### Acceptance Criteria
|
||||
1. The operator documentation (`src/docs/deployment.md`) shall describe the simulator's logical clock as moving only at recorded timestamps, shall describe `--sim-speed` as a deprecated option with no effect, shall not describe any pacing difference between REPL and batch runs, and shall not use `--sim-speed` in its example command lines.
|
||||
2. The scheduler-core specification shall record that simulation speed modes and wall-clock pacing are not provided, that `--sim-speed` is a deprecated no-op, and that the REPL `speed` parameter was removed. It shall replace the speed-mode text in Requirement 5's mode-selection flag table, AC 3 (clock-virtualization mode table), AC 12 (simulation-mode session-duration bullet), AC 13 (simulation startup), AC 14 (the `--sim-speed` bullet, and its "strictly monotonically increasing" clause restated to match Requirement 1.3 of this spec), AC 22 (REPL parameter table, the speed step of the replay pseudocode, the "Why interactive parameters matter" rationale, and the out-of-scope `set` note), and the `sim_speed_num, sim_speed_den` real-time/fast comment in its Data Structure Changes Summary. It shall likewise replace the speed-mode text in the scheduler-core design (the 5.14 traceability row, the `sim_speed_num`/`sim_speed_den` sim-driver fields, the clock-virtualization test's mode rows, and the "Simulation fast mode" note).
|
||||
3. The scheduler shall let in-repository callers that pass `--sim-speed` (for example the HA soak script's `--sim-verify --sim-speed=0` step, `.claude/skills/xce-test-ha/run-ha-soak.sh`) run and pass without modification, with the deprecation warning in their logs.
|
||||
|
|
|
|||
88
.kiro/specs/scheduler-sim-discrete-clock/research.md
Normal file
88
.kiro/specs/scheduler-sim-discrete-clock/research.md
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
# Research & Design Decisions
|
||||
|
||||
## Summary
|
||||
- **Feature**: `scheduler-sim-discrete-clock`
|
||||
- **Discovery Scope**: Extension (light discovery): the simulator is already event-driven; the work is to pin its clock, retire a CLI option, remove a REPL parameter, and align docs/specs.
|
||||
- **Key Findings**:
|
||||
- The core already has a frozen-clock mode: `xce_core_now() = virtual_clock_base + (monotonic_now - real_clock_base) * speed_num / speed_den` (`core_clock.c:80-119`), and `speed_num = 0` makes it return `virtual_clock_base` exactly. Every simulator test that runs a replay already uses it. Pinning simulation mode to 0/1 delivers Requirement 1 without touching the core formula.
|
||||
- In simulation mode exactly three places write the clock: `sim_set_virtual_clock()` at start (`sim.c:309-329`), `sim_advance_virtual_clock()` per event (`sim_loop.c:417`, after the `endSession` return) and per session `beginRound` (`sim_loop.c:1528`, before `xce_core_begin_session()` sets `session_start`). REPL `set output`/`snapshot` only read it. With speed 0 these are the only clock movements (Req 1.5), and `session_start`, the written `beginRound`, decision `ts` and `durationNs` all derive from `xce_core_now()` at `beginRound` (Req 1.4, 1.7, 1.8).
|
||||
- `parse_opt` runs before `mylog_init()` (`main.c:265` vs `:325`); a `WARN` issued before `mylog_init()` goes to stdout, not `--logfile`. The deprecation warning therefore belongs right after `mylog_init()` and before the `--sim` branch, which covers both modes.
|
||||
- `repl_cmd_set()` checks `ntok < 3` (`repl_params.c:400`) before dispatching on the parameter name, so bare `set speed` never reaches the speed branch today (Req 3.1 needs the name check first).
|
||||
|
||||
## Research Log
|
||||
|
||||
### Clock mechanics and who writes the clock atoms
|
||||
- **Context**: Requirement 1 (event-driven clock) and whether `speed_num`/`speed_den` can be dropped from the core.
|
||||
- **Sources Consulted**: `src/scheduler/src/core_clock.c`, `include/core.h:264-302,706-716`, `core.c:130-143`, `sim.c:309-329,594`, `sim_loop.c:175-202,411-417,1520-1545`, `repl_params.c:202-226`, `scheduler.c:757`.
|
||||
- **Findings**: Writers of the four atoms: core init (speed 1/1), `xce_core_set_wallclock_anchor()` (normal mode, 1/1), `sim_set_virtual_clock()` (sim start, `speed_num = args->sim_speed`), `sim_advance_virtual_clock()` (bases only), `repl_set_speed()` (speed). No production code reads `speed_num`/`speed_den` except `xce_core_now()`. Normal mode is always 1/1.
|
||||
- **Implications**: Removing the fields from the core would need a replacement freeze flag and would churn 28 test files and the core-isolation surface for no behavioral gain. Keep them; simulation mode sets 0/1 unconditionally; nothing else ever sets another value.
|
||||
|
||||
### CLI parsing and logging order
|
||||
- **Context**: Requirement 2 (deprecated no-op in both modes, one warning naming the last value).
|
||||
- **Sources Consulted**: `src/scheduler/src/main.c:53,74,92-94,149-151,265-400`; `src/include/utils.h:154-160`; vendored tlog early-print path.
|
||||
- **Findings**: `--sim-speed` is stored as a raw string; argp keeps the last occurrence; a trailing `--sim-speed` with no value is an argp error. Validation (`strtoull`, exit 1) happens only in the `--sim` branch; normal mode ignores the option silently. Logging before `mylog_init()` goes to stdout.
|
||||
- **Implications**: Drop the numeric validation and the `sim_args.sim_speed` plumbing; warn once after `mylog_init()`. `main()` is not unit-testable, so the warning goes in a small function a test can call with the existing `capture_stdout_around()` pattern (`test_repl_help.c:437-460`; test binaries never call `mylog_init()`, so `WARN` reaches stdout — `test_scheduler_decision_publish_retry.c:61-70`).
|
||||
|
||||
### REPL surface
|
||||
- **Context**: Requirement 3.
|
||||
- **Sources Consulted**: `repl_params.c:171-226,388-435`, `repl_info.c:1770-1806`, `repl.c:253-254,314,401-463`, `repl_internal.h`.
|
||||
- **Findings**: `repl_dispatch()` returns true (resume) or false (re-prompt); every `set` path returns false. `info config` prints `speed=%llu` from the shadow copy `sim->sim_speed_num`, its only production reader. Help line at `repl.c:314`.
|
||||
- **Implications**: Handle `speed` before the argument-count check, print a fixed removal message, return false. Delete `repl_set_speed()` and the shadow copy.
|
||||
|
||||
### Tests, docs and specs touched
|
||||
- **Context**: File Structure Plan and Requirement 5.
|
||||
- **Findings**: 22 test files only assign `args.sim_speed = 0` (mechanical removal); `test_sim_startup.c` (which also assigns 1 in its failure-path tests) and `test_repl_snapshot_params.c` also carry behavioral assertions; `test_clock.c`, `test_sim_startup.c`, `test_repl_snapshot_params.c` and `test_repl_help.c` test speed behavior, the help text or the `info config` line; `test_sim_event_loop.c`, `test_maxtasks_wiring_sim_driver_parity.c` and `test_sched_alloc_from_stamp.c` write the core atoms directly (unaffected, since the core keeps them). No test runs the `xce_scheduler` binary as a subprocess. Docs: `deployment.md:214,220,223`. scheduler-core requirements/design passages listed in requirements.md 5.2. The only in-repo command line passing `--sim-speed` is `run-ha-soak.sh:738`, which counts only `decision verification MISMATCH` lines.
|
||||
|
||||
## Architecture Pattern Evaluation
|
||||
|
||||
| Option | Description | Strengths | Risks / Limitations | Notes |
|
||||
|--------|-------------|-----------|---------------------|-------|
|
||||
| A. Pin sim to speed 0/1 (selected) | Keep the core formula; `sim_set_virtual_clock()` always stores 0/1; remove every sim/CLI/REPL path that sets another speed | Smallest change; reuses the exact mode all replay tests already exercise; core untouched | Core formula still supports N/M values no product path uses | Document the two values in `core.h` |
|
||||
| B. Replace speed with a `clock_frozen` flag | Core formula becomes `vbase + (frozen ? 0 : elapsed)` | Removes unused generality | Touches core, core isolation surface, 28 test files; no behavioral gain | Rejected (simplification lens) |
|
||||
| C. Simulator-private clock | Sim stops using the core clock | None over A | Breaks the "one core, two consumers" model and driver equivalence | Rejected |
|
||||
|
||||
## Design Decisions
|
||||
|
||||
### Decision: Frozen clock via existing `speed_num = 0`
|
||||
- **Context**: Req 1.1-1.8.
|
||||
- **Alternatives Considered**: Options B and C above.
|
||||
- **Selected Approach**: `sim_set_virtual_clock(sim, snapshot_ts)` sets `virtual_clock_base = snapshot_ts`, `speed_num = 0`, `speed_den = 1`; `sim_advance_virtual_clock()` unchanged in behavior (exact, unclamped set).
|
||||
- **Rationale**: Delivers exactness by construction; zero change to the core module.
|
||||
- **Trade-offs**: The core formula keeps a generality nothing uses; documented as "normal mode 1/1, simulation mode 0/1".
|
||||
- **Follow-up**: Tests prove that real time elapsing inside a replay does not move the clock.
|
||||
|
||||
### Decision: Deprecation warning in a testable helper, emitted after `mylog_init()`
|
||||
- **Context**: Req 2.1-2.6.
|
||||
- **Selected Approach**: New `sched_cli_warn_deprecated_options(const char* sim_speed)` in `src/scheduler/src/cli_compat.c`, called once from `main()` after `mylog_init()` and before the `--sim` branch. It logs one `WARN` naming the value when the pointer is non-NULL, and nothing otherwise.
|
||||
- **Rationale**: Goes to `--logfile` in both modes; unit-testable; argp already gives last-value and missing-value semantics.
|
||||
- **Trade-offs**: One small new file for one function; justified by testability and by giving the later removal a single place.
|
||||
|
||||
### Decision: `set speed` handled before the argument-count check
|
||||
- **Context**: Req 3.1, 3.2.
|
||||
- **Selected Approach**: In `repl_cmd_set()`, if `ntok >= 2` and `tokens[1]` is `speed`, print the removal message and return false; then the usual `ntok < 3` usage check.
|
||||
|
||||
### Generalization / Build vs. Adopt / Simplification
|
||||
- **Generalization**: Requirements 1.1-1.8 are one property ("the sim clock moves only at recorded timestamps"); a single mechanism (speed 0/1 plus the three existing writers) satisfies all of them.
|
||||
- **Build vs. Adopt**: Adopt the core's existing frozen mode; no new dependency.
|
||||
- **Simplification**: Remove `sched_sim_args_t.sim_speed`, the `sim_speed_num`/`sim_speed_den` shadow copy, `repl_set_speed()` and the numeric validation in `main.c`; keep the core fields (Option B rejected).
|
||||
|
||||
## Review Gate
|
||||
- Pass 1 (independent reviewer): GATE PASS, no must-fix. Ten nits applied: `--help` test mechanism (`popen` of the built binary via `-DXCE_SCHEDULER_BIN`), meson source-list placement, default log file path, line ranges, stale comments, a stronger warning test, a realistic 1.3 case, 4.2 wording, invariant-1 wording, and the test-file count here.
|
||||
|
||||
- Design review round 1: two must-fix issues fixed. (1) The in-process clock tests cannot fail on the pre-change code (zero-initialized args already meant speed 0), so an end-to-end `popen` test of the binary with default options and with every `--sim-speed` variant was added. (2) The snapshot's `session_duration_ns` claim was corrected: snapshots written before the first replayed session carry the recorded value. Nine nits applied (test registration, line ranges, stale comments, observation points, build-order constraint, `RUN_TEST` lines, SimClock rationale).
|
||||
- Design review round 2: one must-fix issue fixed. The `--sim` branch returns without `mylog_fini()`, and tlog writes asynchronously with no exit hook, so a startup warning could be lost; superseded in round 3. Seven nits applied (1.4 proof attribution, non-vacuous decision check, popen pipe handling, absent log file, output comparison method, line references, tests for a trailing `--sim-speed` and piped `set speed`).
|
||||
- Design review round 3: one must-fix issue fixed. Interactive runs exit through the REPL's `exit(0)` and never return from `main()`, and calling `tlog_exit()` at the returns or at quit is unsafe because the picker/allocator threads are never joined. The design now drains the logger (`mylog_fini()` + `mylog_init()`) right after the warning while `main()` is still single-threaded, covering every exit path; an interactive end-to-end case (e) was added. Nits applied: tlog citation, the real scripted-REPL test pattern (`freopen` of a script file), the unreachable `-ENOSYS` branch noted, a mid-replay snapshot observation for 1.5.
|
||||
- Design review round 4: one must-fix issue fixed. The mid-replay snapshot check could not fail (wrong code path, masked by the next event, and the start-snapshot scan would select the later snapshot); it now runs the real skip branch with `sim_start_ts` pinned and checks the clock after the run. The end-to-end cases now use a fresh output and log file per run. Nits applied (tlog line reference, drain in the sequence diagram, 5.3 coverage in the components table, drain side effects, REPL position under 1.6, test-local capture helper).
|
||||
- Design review round 5: one must-fix issue fixed (1.5 coverage disagreed between the Traceability and Components tables; ReplSet now covers 1.5 because removing `repl_set_speed()` removes the only other clock writer). Nits applied: line ranges, a pinned `endSession` timestamp in the mid-replay test, fresh output file in E2E case (c), stale four-mode comments, component names in Traceability, drain calls in Allowed Dependencies.
|
||||
- Design review round 6: no must-fix issues (40+ line references verified). Four nits applied: a line range, the neutral 1/1 core-init citation, the drain's repeated gzip notice noted against Req 2.2, and the test-file count reconciled with requirements.md.
|
||||
|
||||
## Risks & Mitigations
|
||||
- A test relies on drift at speed 1 (the three `test_sim_startup.c` failure-path tests set speed 1): they fail before the clock matters; mechanical removal of the assignment.
|
||||
- Code outside the scheduler parses `speed=` from `info config`: none found (grep of xceadmin, harness, tools, frontend, .claude).
|
||||
- The soak script's log now contains a warning: it counts only `decision verification MISMATCH` lines (`run-ha-soak.sh:737-739`); verified by 5.3's check.
|
||||
- Old recordings whose events precede their first snapshot's timestamp move the clock backwards: accepted by Req 1.3; exact set, no error.
|
||||
|
||||
## References
|
||||
- `.kiro/steering/scheduler-driver-equivalence.md` — one core, two drivers; the change stays in the sim driver.
|
||||
- `.claude/skills/xce-audit-invariants/SKILL.md` — scheduler dimensions 1-4 (cross-checked in design.md).
|
||||
- `.kiro/specs/libxce-alloc-preason-equality` — task 5.3 depends on Req 1.2-1.4.
|
||||
|
|
@ -1,24 +1,22 @@
|
|||
{
|
||||
"feature_name": "scheduler-sim-discrete-clock",
|
||||
"created_at": "2026-09-28T08:46:06Z",
|
||||
"updated_at": "2026-09-28T08:46:06Z",
|
||||
"updated_at": "2026-09-29T04:11:51Z",
|
||||
"language": "en",
|
||||
"phase": "initialized",
|
||||
"phase": "tasks-generated",
|
||||
"approvals": {
|
||||
"requirements": {
|
||||
"generated": false,
|
||||
"approved": false
|
||||
"generated": true,
|
||||
"approved": true
|
||||
},
|
||||
"design": {
|
||||
"generated": false,
|
||||
"approved": false
|
||||
"generated": true,
|
||||
"approved": true
|
||||
},
|
||||
"tasks": {
|
||||
"generated": false,
|
||||
"approved": false
|
||||
"generated": true,
|
||||
"approved": true
|
||||
}
|
||||
},
|
||||
"ready_for_implementation": false
|
||||
"ready_for_implementation": true
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
67
.kiro/specs/scheduler-sim-discrete-clock/tasks.md
Normal file
67
.kiro/specs/scheduler-sim-discrete-clock/tasks.md
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
# Implementation Plan
|
||||
|
||||
Test-first throughout (red → green → refactor). Every sub-task leaves the build and the scheduler suite green. The simulator's speed field and its shadow copy stay in place, unused, until the last core task removes them together with every test that sets them (design.md, File Structure Plan: build-order constraint).
|
||||
|
||||
- [ ] 1. Foundation: deprecation-warning helper and the new test binary
|
||||
- [ ] 1.1 Add the `--sim-speed` deprecation-warning helper and register the new discrete-clock test binary
|
||||
- Write the tests first, with a test-local stdout-capture helper and without initialising the logger (so warnings reach stdout): the values `0`, `abc`, `-1` and the empty string each produce exactly one line naming the value in quotes and containing `--sim-speed`, "deprecated", "no effect" and "will be removed"; no value produces no output.
|
||||
- Add the helper as its own small source (design.md, Components: CliCompat) and add it to the scheduler's source list only, not to the simulator-isolation library.
|
||||
- Register the new test binary in the scheduler suite with the same leak-detection setting as the other scheduler tests, a compile-time path to the built scheduler binary, and a build dependency on that binary. Its build sources are the new test file and the helper only, with the usual scheduler test dependencies; the white-box simulator `#include`s and the stand-in picker/allocator go into the test file itself in 2.1, as the other simulator tests do, not into the build file (listing the simulator sources there as well would define every symbol twice).
|
||||
- Done when the new test binary runs as part of the scheduler suite and its warning tests pass, and the scheduler binary still builds.
|
||||
- _Requirements: 2.3, 2.4, 2.5_
|
||||
- _Boundary: CliCompat, scheduler test build_
|
||||
|
||||
- [ ] 2. Core: pin the simulator clock, remove the REPL speed parameter, retire the CLI option
|
||||
- [ ] 2.1 (P) Pin the simulation-mode clock so it moves only at recorded timestamps
|
||||
- Write the tests first in the new test binary, each passing a non-zero speed through the still-present simulator argument so they fail on the current code: after start the clock equals the chosen snapshot's timestamp and stays equal after a real sleep, with the core holding the frozen speed setting; with a stand-in allocator that sleeps and a recorded session start that is not a whole second, the session start seen inside the session equals the recorded one exactly, and simulation output has that session start, a zero session duration and every decision timestamp equal to it, with at least one decision per session; a session start earlier than the preceding event, and events earlier than the start snapshot, set the clock to exactly those values and the replay completes; a mid-replay snapshot followed by the end marker leaves the clock at the last event's timestamp after the run and after a sleep, with the start timestamp pinned so the real skip branch runs; the same recording replayed twice gives identical logical timestamps and decisions in both outputs, ignoring only the wall-clock header field (design.md, Testing Strategy: Unit (SimClock), Integration (replay loop)).
|
||||
- Make simulator startup always select the frozen clock setting and drop the speed parameter from the start-clock function. The start-clock function keeps writing the shadow copy, now always the frozen setting, because `info config` and the startup test still read it; the simulator's speed argument is no longer read. Both are removed in 3.1.
|
||||
- Update the speed-drift and never-runs-backwards comments in the simulator driver, replay loop and simulation-output sources; the simulator header's comments are left to 3.1 (design.md, Modified Files).
|
||||
- Done when the new clock tests pass, fail with the change reverted, and every existing simulator and REPL test still passes.
|
||||
- _Requirements: 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 4.2_
|
||||
- _Boundary: SimClock_
|
||||
- _Depends: 1.1_
|
||||
- [ ] 2.2 (P) Remove the REPL speed parameter
|
||||
- Write the tests first: `set speed 7` and bare `set speed` print the removal message, return to the prompt and leave the clock and core speed setting unchanged; `help` and the `set` usage message do not mention speed; `info config` has no speed item and still shows output, verify, events processed, current timestamp and sessions; a scripted forked-child REPL run with `set speed 7`, `info config` and `quit` exits 0 and prints both the removal message and the config line; the other `set` parameters' existing tests still pass.
|
||||
- Handle `set speed` before the argument-count check, remove the speed setter, and drop speed from the usage, help and `info config` text (design.md, Components: ReplSet / ReplHelp / ReplInfo).
|
||||
- Replace the existing speed-setter test and update the help and `info config` tests, the comments about the `set speed` command in the REPL parameter, help and resource-formatting tests, and the REPL internal header's comments that mention `set speed` (comments about the simulator speed argument belong to 3.1).
|
||||
- Done when the REPL tests pass and no REPL output or help text offers a speed setting.
|
||||
- _Requirements: 1.5, 3.1, 3.2, 3.3, 3.4, 3.5_
|
||||
- _Boundary: ReplSet, ReplHelp, ReplInfo_
|
||||
- [ ] 2.3 Retire `--sim-speed` on the command line and prove the default command line end to end
|
||||
- Write the tests first in the new test binary, running the built scheduler binary as a child process on a fixture the test writes (session start not a whole second, a pending job before each session), draining its stdout before collecting the exit status: without the option it exits 0, its output has exact session starts, zero durations and decision timestamps equal to the session start, and its log has no warning; with each of `7`, `abc`, `-1` and an empty value (fresh output and log files per run) it exits 0, its output matches the default run except the wall-clock header field, and its log has exactly one warning naming the value; a repeated option logs one warning naming the last value; a trailing option with no value exits non-zero; an interactive run at end of input logs exactly one warning; `--help` describes the option as deprecated and mentions no real-time or as-fast-as-possible mode.
|
||||
- Stop defaulting, validating and passing a simulation speed; call the warning helper once right after the logger starts and, when the option was given, drain and restart the logger there while still single-threaded; update the option's help text and the comment that cites its numeric validation; the flag-meaning comments that describe the simulator speed argument are left to 3.1 (design.md, Modified Files: `main.c`; Error Handling).
|
||||
- Done when the end-to-end and help tests pass; the runs with `abc`, `-1`, an empty value and `7`, the repeated-option run, the interactive run and the help test fail against the pre-change command-line handling, while the run without the option is a regression guard that 2.1 already makes pass.
|
||||
- _Requirements: 1.4, 1.7, 1.8, 2.1, 2.2, 2.3, 2.4, 2.5, 2.6_
|
||||
- _Boundary: main, CliCompat_
|
||||
- _Depends: 1.1, 2.1_ (shares the new test file with 2.1, and its output assertions need the pinned clock)
|
||||
|
||||
- [ ] 3. Integration: remove the simulator speed fields
|
||||
- [ ] 3.1 Remove the simulator speed argument and shadow copy, and update every test that set them
|
||||
- Remove the simulator's speed argument and its shadow copy, and in the same change update every test that assigns or asserts them: the 22 tests that only assign a speed, the startup test (keep its core frozen-setting assertions, drop the shadow-copy ones), the REPL parameter test's remaining assignments, and the non-zero speed assignments that 2.1 added to the new discrete-clock tests to prove RED (those tests keep asserting the pinned clock) (design.md, Modified Files and the build-order constraint).
|
||||
- Rename the three core clock tests that describe simulation speed modes as formula tests, including their test registrations and the file header; update the stale speed comments in the scheduler test build file, in the step-verbosity REPL test (which describe the removed speed argument), and in the command-line flag-meaning comments that describe the simulator speed argument, the simulator header's comments, and the core header's documentation of the speed setting (1/1 in normal mode, 0/1 in simulation mode).
|
||||
- Done when a search of the scheduler sources and tests finds no simulator speed argument or shadow copy, only the core speed setting, and the full scheduler suite passes.
|
||||
- _Requirements: 1.5, 2.2, 4.1_
|
||||
- _Depends: 2.1, 2.2, 2.3_
|
||||
|
||||
- [ ] 4. Documentation and specification alignment
|
||||
- [ ] 4.1 (P) Update the operator documentation of simulation mode
|
||||
- Describe the simulator clock as moving only at recorded timestamps, describe `--sim-speed` as deprecated with no effect, remove it from the example command line, and remove the claim that REPL and batch runs differ in pacing.
|
||||
- Done when the simulation section of the deployment guide contains none of real-time, speed modes or pacing, and names `--sim-speed` only as deprecated.
|
||||
- _Requirements: 5.1_
|
||||
- _Boundary: Docs (deployment guide)_
|
||||
- [ ] 4.2 (P) Update the scheduler-core specification
|
||||
- Replace every speed-mode passage listed in requirements.md 5.2 in the scheduler-core requirements (flag table, AC 3, 12, 13, 14 including the monotonic-clock clause, AC 22 parameter table, pseudocode, rationale and out-of-scope note, data-structure summary) and design (traceability row, simulator fields, clock-test mode rows, fast-mode note).
|
||||
- Done when those passages state that no speed modes or wall-clock pacing exist, that `--sim-speed` is a deprecated no-op and that the REPL speed parameter was removed, and AC 14 permits the clock to move backwards to a recorded value.
|
||||
- _Requirements: 5.2_
|
||||
- _Boundary: Docs (scheduler-core spec)_
|
||||
|
||||
- [ ] 5. Validation
|
||||
- [ ] 5.1 Full build and regression
|
||||
- Run the full ASAN build with the libxce, scheduler and controller suites, including the core-isolation test, and scan the test log for hidden Unity failures and sanitizer reports.
|
||||
- Confirm the replay schema and the normal-mode clock anchoring are unchanged by this feature's commits.
|
||||
- Done when every suite passes with no hidden failures, and the core clock source, the normal-mode wall-clock anchoring and the replay schema show no diff from this feature (the core header changes only in comments).
|
||||
- _Requirements: 4.1, 4.2, 4.3_
|
||||
- [ ] 5.2 HA soak with the existing `--sim-speed` caller
|
||||
- Run the HA soak skill, whose replay-verification step still passes `--sim-speed=0`, without modifying it.
|
||||
- Done when that step passes with zero mismatches and its log contains exactly one deprecation warning.
|
||||
- _Requirements: 5.3_
|
||||
Loading…
Add table
Add a link
Reference in a new issue