diff --git a/.kiro/specs/scheduler-sim-step-verbosity/tasks.md b/.kiro/specs/scheduler-sim-step-verbosity/tasks.md index e2f65a26..076b3430 100644 --- a/.kiro/specs/scheduler-sim-step-verbosity/tasks.md +++ b/.kiro/specs/scheduler-sim-step-verbosity/tasks.md @@ -8,7 +8,7 @@ - _Requirements: 1.1, 1.2, 1.3, 1.4, 2.2_ - _Boundary: Event Echo Preamble Deduplicator_ -- [ ] 1.2 Implement the preamble deduplication cache and print-skip logic +- [x] 1.2 Implement the preamble deduplication cache and print-skip logic - Add the last-printed rendered timestamp and session cache to the REPL session state, zero-initialized alongside the state's existing fields; update the event echo path to compare the candidate rendered timestamp and session against the cache, printing and updating it only when either has changed (or no preamble has been printed yet this session), while always still printing the event's own content line unconditionally - Observable: all tests from 1.1 pass; a captured transcript shows exactly one preamble line per distinct rendered (timestamp, session) run of consecutive echoed events, with every event's own content line still present - After landing the implementation change, re-check `repl_break.c`'s line count against its already-accepted 949-line residual (design.md's Architecture Integration); if it meaningfully exceeds that residual, evaluate extracting `repl_print_event_summary()` and its Event Echo Gate neighbors into a new file rather than pre-splitting speculatively diff --git a/src/scheduler/src/repl_break.c b/src/scheduler/src/repl_break.c index 42c9c019..9bc947a0 100644 --- a/src/scheduler/src/repl_break.c +++ b/src/scheduler/src/repl_break.c @@ -674,13 +674,44 @@ static void repl_print_event_summary(xce_sched_sim_driver_t* sim, { xce_sched_repl_state_t* rs = sim->repl_state; char ts_human[32]; + uint64_t session_num; repl_format_ts_human(xce_core_now(&(sim->core)), ts_human, sizeof(ts_human)); - fprintf(stdout, "[sim] t=%llu (%s) session=%llu\n", - (unsigned long long)xce_core_now(&(sim->core)), ts_human, - (unsigned long long)((rs != NULL) ? rs->session_count + 1 : 1)); + session_num = (rs != NULL) ? rs->session_count + 1 : 1; + /* + * Event Echo Preamble Deduplicator (scheduler-sim-step-verbosity spec, + * task 1.2, design.md's "Event Echo Preamble Deduplicator" component, + * Req 1.1-1.4/2.2): print the preamble only when the candidate rendered + * timestamp or displayed session differs from the last preamble THIS + * function actually printed this session (rs->last_echo_ts_human/ + * rs->last_echo_session), or when that cache is still at its + * zero-initialized "never printed" default (empty string / session 0 -- + * values no real echoed event can produce, repl_internal.h's own doc + * comment on these fields). The comparison is on the RENDERED pair only, + * never the raw xce_core_now() value, which changes on every call (Req + * 1.3). `rs == NULL` cannot happen in practice (xce_repl_on_event() only + * reaches here when sim->repl_state != NULL), but is guarded the same + * defensive way session_num above already is, by printing unconditionally + * rather than dereferencing rs->last_echo_*. + */ + if ((rs == NULL) || (strcmp(rs->last_echo_ts_human, ts_human) != 0) + || (rs->last_echo_session != session_num)) { + fprintf(stdout, "[sim] t=%llu (%s) session=%llu\n", + (unsigned long long)xce_core_now(&(sim->core)), ts_human, + (unsigned long long)session_num); + + if (rs != NULL) { + snprintf(rs->last_echo_ts_human, sizeof(rs->last_echo_ts_human), + "%s", ts_human); + rs->last_echo_session = session_num; + } + } + + /* The event's own content line below is printed UNCONDITIONALLY + * regardless of the preamble decision above -- this component only ever + * suppresses the preamble, never the content (Req 1.4, 2.2). */ switch (ev->type) { case XCE_CORE_EVENT_CONFIG_PUSH: fprintf(stdout, "[sim] event: CONFIG_PUSH\n"); diff --git a/src/scheduler/src/repl_internal.h b/src/scheduler/src/repl_internal.h index 1b32d826..40d484d4 100644 --- a/src/scheduler/src/repl_internal.h +++ b/src/scheduler/src/repl_internal.h @@ -260,6 +260,36 @@ struct xce_sched_repl_state_s { * exception. */ xce_repl_units_mode_t units_mode; + + /* + * Event Echo Preamble Deduplicator fields (scheduler-sim-step-verbosity + * spec, task 1.2, design.md's "Event Echo Preamble Deduplicator" + * component, Req 1.1-1.4/2.2) - cache the rendered (second-granularity) + * human timestamp and displayed session number of the last preamble + * ACTUALLY PRINTED by repl_print_event_summary() (repl_break.c), so a + * consecutive echoed event sharing the same rendered context can skip + * reprinting it instead of restating the same "[sim] t=... session=..." + * line before every single event. Like every field in this struct + * except `units_mode` above, both legitimately rely on calloc's + * zero-initialization being a valid default -- no explicit post-calloc + * assignment is needed here: an empty string / session 0 can never equal + * a real echoed event's rendered timestamp/session (session numbers + * start at 1, session_count + 1), so repl_print_event_summary()'s "no + * preceding echoed event this session" case (AC 1.2) is satisfied by the + * zero default alone. Written only by repl_print_event_summary(), only + * when it actually prints a preamble; read only there. + */ + char last_echo_ts_human[32]; /* last rendered human timestamp + * actually printed in a preamble; "" + * (calloc zero) until the first + * preamble prints this session. Same + * buffer size as repl_format_ts_human() + * output buffers elsewhere in this + * file family. */ + uint64_t last_echo_session; /* last displayed session number + * actually printed in a preamble; 0 + * (calloc zero) until the first + * preamble prints this session. */ }; /* repl_split_tokens/XCE_REPL_MAX_TOKENS/XCE_REPL_TOKEN_LEN (repl.c) -