Silence routine diagnostic noise; fix a real CRLF double-submit bug
Two separate fixes, found and closed together after task 3.9/prompt-
format landed (Captain Bob: "finish the work first then we'll do
cleanup before 3.10 begins").
1. Logging noise (confirmed live via QMP screendump): three sources
were cluttering ordinary interactive console sessions.
- INFERENCE "Output validation failed, ignoring results"
(vm_runtime.c, kernel; vm_time.c, hosted mirror) and xhci "CSW
status = FAILED"/"unit not ready -- retrying" (xhci.c) were
already log_message(LOG_WARN/LOG_ERROR, ...) calls, just visible
at the default runtime LOG_WARN level -- downgraded to LOG_INFO,
all three fire routinely and self-resolve (xhci.c's own existing
comment already documents the retry as expected SCSI UNIT
ATTENTION behavior, not a driver defect).
- Stadium: dispatch cell=... (stadium.c's stadium_dispatch()) was a
genuine defect: an unconditional console_puts()/console_println()
sequence with no level gating at all, printing on every single
dispatch. Rewritten through log_message(LOG_DEBUG, ...).
2. CRLF double-submit (found while investigating why the cleaned-up
noise still didn't look like a normal single-VM session):
sk_console_readline() (repl.c) breaks on '\r' OR '\n' as independent
terminators, so a line sent as both bytes submits twice -- the real
line, then an immediate empty-line submit on the second byte, each
printing its own " ok". Pre-dates Stage D entirely, not an async-
relay artifact. Fixed with a single non-blocking peek-and-discard
for the paired byte right where the line terminates.
Verified live on all three architectures for both fixes: zero UNKNOWN
WORD, dict_hash identical to every prior acceptance run in this
document (both changes are display/interaction-only).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
5c07745c18
commit
d4f8a568ee
@@ -1405,6 +1405,73 @@ not-yet-started item (memory `project_production_logging_cleanup_needed`:
|
||||
`console_println()` overuse, much of it should be DEBUG-level). Captain Bob explicitly deferred
|
||||
this to after the prompt-format fix landed — genuinely not started, no task number assigned yet.
|
||||
|
||||
**2026-09-22 — CLOSED**, after the task 3.8 payload-aliasing fix landed. The noise turned out to
|
||||
be exactly three sources, found by tracing each visible line back to its actual call site rather
|
||||
than auditing `console_println()` broadly:
|
||||
|
||||
1. **`INFERENCE: Output validation failed, ignoring results`** (`vm_runtime.c`, kernel; `vm_time.c`,
|
||||
hosted mirror) — already `log_message(LOG_WARN, ...)`, level-gated, just visible at the default
|
||||
runtime `LOG_WARN` level (`kernel_main.c` sets it there after POST). Fires routinely during
|
||||
normal operation with the runtime self-recovering every time (the message says so). Downgraded
|
||||
to `LOG_INFO` in both files (kept in sync, same duplicated-logic pattern this codebase already
|
||||
uses elsewhere for the hosted/kernel split).
|
||||
2. **`xhci: CSW status = FAILED`** (was `LOG_ERROR`) and **`xhci: unit not ready -- retrying TEST
|
||||
UNIT READY`** (was `LOG_WARN`), both `xhci.c` — also already level-gated, same visibility
|
||||
reason. `xhci.c`'s own existing comment right there confirms these are "a fresh SCSI target's
|
||||
standard first-command UNIT ATTENTION behavior, not a driver defect" — the bounded retry
|
||||
resolves it routinely. Downgraded both to `LOG_INFO`. The genuine terminal failure ("`xhci: unit
|
||||
still not ready -- giving up`", after `XHCI_BOT_TUR_MAX_RETRIES`) was left at `LOG_ERROR`,
|
||||
unchanged — that one is a real failure, not routine noise.
|
||||
3. **`Stadium: dispatch cell=... behaviour=...`** (`stadium.c`'s `stadium_dispatch()`) — the one
|
||||
genuine defect, not just a severity judgment call: an unconditional `console_puts()`/
|
||||
`console_println()` sequence with **no level gating at all**, printing on every single Stadium
|
||||
dispatch regardless of log level — confirmed live, interleaving visibly with ordinary
|
||||
interactive console sessions (a live QMP `screendump` Captain Bob asked for caught it directly,
|
||||
`logs/20260922-134049/aarch64/`'s own screendump before this fix). Rewritten to route through
|
||||
`log_message(LOG_DEBUG, ...)`, one call per `StadiumBehaviour` case, matching every other
|
||||
diagnostic trace in this codebase — silent at the default level, available with
|
||||
`--log-level=debug`.
|
||||
|
||||
No sender/reader/call-site changes needed anywhere else — this was purely a severity/level
|
||||
question for two sources and a genuine gating gap for the third, not an API or behavior change.
|
||||
Verified live: a fresh screendump after the fix (`logs/20260922-142449/amd64/`) shows a clean
|
||||
interactive session with none of the three noise sources present, confirmed against the exact
|
||||
same mint→WIREBIND→`USE`→relay sequence that produced the noisy screendump earlier. Standard
|
||||
three-ISA acceptance clean: `logs/20260922-142449/amd64/`, `logs/20260922-142835/aarch64/`,
|
||||
`logs/20260922-143229/riscv64/` — zero `UNKNOWN WORD`, `dict_hash` identical across all three and
|
||||
matching every prior acceptance run in this document (unaffected — display-only change). Also
|
||||
confirmed the hosted build (`make`, no `ARCH=`) still compiles clean after the `vm_time.c` change.
|
||||
|
||||
**2026-09-22, real defect found and fixed while investigating a separate complaint (Captain Bob:
|
||||
the interactive Stage D exchange "looks nothing like a normal single-VM session" — extra `ok`/
|
||||
`ok>` cycles with no command typed in between).** Root cause traced via a live QMP `screendump`,
|
||||
not guessed at: `sk_console_readline()` (`repl.c`) breaks its read loop on **either** `'\r'` or
|
||||
`'\n'` as an independent terminator — a line sent as both bytes (a real terminal in CRLF mode, or
|
||||
any scripted sender writing `"\r\n"`, which every QMP/socat-driven verification session in this
|
||||
whole document has been doing) submits **twice**: once for the real line on the first byte, then
|
||||
an immediate empty-line submit on the second byte the very next time the function is called, each
|
||||
producing its own `" ok"`. Confirmed this pre-dates Stage D entirely — the very first `ok`/`ok>`
|
||||
pair after `MINT` (Hera's own console, not `rajames`'s, not an async relay at all) already showed
|
||||
doubled. **Not a Stage D async-relay artifact as first suspected** — every prior screendump and
|
||||
every prior acceptance log in this document that shows doubled `ok`s is this same bug, present
|
||||
since before this document opened.
|
||||
|
||||
Fixed at the source (`sk_console_readline()`, `repl.c`): after breaking on `'\r'` or `'\n'`, a
|
||||
single non-blocking peek for the paired byte (the other one of the two) — discard it if present,
|
||||
push it back via the existing `g_console_pending_key` single-slot buffer (the same mechanism
|
||||
`sk_console_key_available()` already uses for "peeked but not this call's to consume") if it
|
||||
turns out to be unrelated input for the next line. A byte that hasn't arrived yet is not waited
|
||||
for, matching every other non-blocking read in this function.
|
||||
|
||||
Verified live: a fresh screendump after the fix (`logs/20260922-162219/amd64/`) shows exactly one
|
||||
`ok`/`ok>` pair per typed line, matching normal single-VM interaction, with Stage D's own async
|
||||
delivery timing unchanged (Captain Bob's own explicit choice: clean up the prompt noise, keep the
|
||||
real delivery latency — the evidence line and result still land on a later idle-pump beat, just
|
||||
without the doubled `ok` cluttering the wait). Standard three-ISA acceptance also clean on all
|
||||
three architectures: `logs/20260922-162219/amd64/`, `logs/20260922-162531/aarch64/`,
|
||||
`logs/20260922-162932/riscv64/` — zero `UNKNOWN WORD`, `dict_hash` identical to every prior run in
|
||||
this document (unaffected).
|
||||
|
||||
---
|
||||
|
||||
## Out of scope, carried for visibility
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1306,6 +1306,26 @@ int sk_console_readline(char* buf, int size, VM* active_vm, int reanchor_prompt)
|
||||
if (c == '\r' || c == '\n') {
|
||||
console_fb_erase_cursor(); /* leaving this cell without drawing a char over it */
|
||||
console_putc('\n');
|
||||
/* CRLF pairing, found live 2026-09-22: a terminator sent as
|
||||
* both bytes (a real terminal in CRLF mode, or any scripted
|
||||
* sender writing "\r\n") used to submit TWICE -- this line
|
||||
* on the '\r' (or '\n'), then an immediate empty line on the
|
||||
* OTHER byte the next time this function is called, each
|
||||
* producing its own " ok" -- confirmed live as the doubled
|
||||
* "ok" this project's own session interaction showed, not an
|
||||
* async-relay artifact as first suspected. Non-blocking peek
|
||||
* for the paired byte right here, before returning -- discard
|
||||
* it if present, push it back via g_console_pending_key
|
||||
* (already the mechanism sk_console_key_available() uses for
|
||||
* exactly this "peeked but not this call's to consume" case)
|
||||
* if it's unrelated input for the NEXT line. A byte that
|
||||
* hasn't arrived yet by this point is not waited for --
|
||||
* matches every other non-blocking read in this function. */
|
||||
{
|
||||
int pair = (c == '\r') ? '\n' : '\r';
|
||||
int next = sk_console_getc_raw();
|
||||
if (next >= 0 && next != pair) g_console_pending_key = next;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -2111,7 +2111,16 @@ void xhci_poll_events(void)
|
||||
if (csw_pass) {
|
||||
log_message(LOG_DEBUG, "xhci: CSW status = PASS");
|
||||
} else if (dev->bot_csw.bCSWStatus == USB_BOT_CSW_STATUS_FAILED) {
|
||||
log_message(LOG_ERROR, "xhci: CSW status = FAILED");
|
||||
/* 2026-09-22: downgraded from LOG_ERROR --
|
||||
* a fresh SCSI target's standard first-
|
||||
* command UNIT ATTENTION behavior (see this
|
||||
* block's own comment a few lines down),
|
||||
* not a driver defect; the bounded retry
|
||||
* below resolves it routinely. The genuine
|
||||
* terminal failure ("giving up" below,
|
||||
* after XHCI_BOT_TUR_MAX_RETRIES) stays at
|
||||
* LOG_ERROR. */
|
||||
log_message(LOG_INFO, "xhci: CSW status = FAILED");
|
||||
} else {
|
||||
log_message(LOG_ERROR, "xhci: CSW status = PHASE ERROR");
|
||||
}
|
||||
@@ -2142,7 +2151,11 @@ void xhci_poll_events(void)
|
||||
}
|
||||
} else if (dev->bot_tur_retries < XHCI_BOT_TUR_MAX_RETRIES) {
|
||||
dev->bot_tur_retries++;
|
||||
log_message(LOG_WARN, "xhci: unit not ready -- retrying TEST UNIT READY");
|
||||
/* 2026-09-22: downgraded from LOG_WARN,
|
||||
* same reasoning as the CSW FAILED
|
||||
* downgrade above -- expected, routinely
|
||||
* self-resolving. */
|
||||
log_message(LOG_INFO, "xhci: unit not ready -- retrying TEST UNIT READY");
|
||||
xms->next_action = XHCI_NEXT_ACTION_BOT_SEND_TUR;
|
||||
} else {
|
||||
log_message(LOG_ERROR, "xhci: unit still not ready -- giving up");
|
||||
|
||||
+17
-13
@@ -37,6 +37,7 @@
|
||||
#include "starkernel/kmalloc.h"
|
||||
#include "starkernel/pmm.h"
|
||||
#include "starkernel/console.h"
|
||||
#include "starkernel/log.h" /* log_message() -- stadium_dispatch()'s own trace, LOG_DEBUG */
|
||||
#include "starkernel/hal/hal.h"
|
||||
#include "starkernel/q48_16.h" /* Q48_ONE -- item 4.1's reservoir starts each VM's quota at 1.0 */
|
||||
#include "vm.h" /* VM_MEMORY_SIZE -- the per-VM footprint stadium_max_vm_count() budgets against */
|
||||
@@ -366,33 +367,36 @@ uint8_t *stadium_header_bitmap(void) {
|
||||
* integration of any kind exists yet (FABRIC-2.md §B).
|
||||
*/
|
||||
void stadium_dispatch(size_t cell_index, StadiumBehaviour behaviour) {
|
||||
console_puts("Stadium: dispatch cell=");
|
||||
console_put_u64((uint64_t)cell_index);
|
||||
console_puts(" behaviour=");
|
||||
/* 2026-09-22: was an unconditional console_puts()/console_println()
|
||||
* sequence -- the only Stadium trace with no level gating at all,
|
||||
* printing on every single dispatch regardless of log level
|
||||
* (confirmed live: interleaves visibly with ordinary interactive
|
||||
* console sessions). Routed through log_message(LOG_DEBUG, ...)
|
||||
* instead, matching every other diagnostic trace in this codebase --
|
||||
* silent at the default LOG_WARN runtime level, available with
|
||||
* --log-level=debug. */
|
||||
switch (behaviour) {
|
||||
case STADIUM_BEHAVIOUR_MIGRATE: {
|
||||
uint32_t lbn = (uint32_t)stadium_cell_array[cell_index].header.identity;
|
||||
console_puts("MIGRATE lbn=");
|
||||
console_put_u64((uint64_t)lbn);
|
||||
console_println("");
|
||||
log_message(LOG_DEBUG, "Stadium: dispatch cell=%zu behaviour=MIGRATE lbn=%u",
|
||||
cell_index, (unsigned)lbn);
|
||||
blk_flush(lbn);
|
||||
break;
|
||||
}
|
||||
case STADIUM_BEHAVIOUR_DELIVER: {
|
||||
uint64_t msg_idx = stadium_cell_array[cell_index].header.identity;
|
||||
console_puts("DELIVER msg_idx=");
|
||||
console_put_u64(msg_idx);
|
||||
console_println("");
|
||||
log_message(LOG_DEBUG, "Stadium: dispatch cell=%zu behaviour=DELIVER msg_idx=%llu",
|
||||
cell_index, (unsigned long long)msg_idx);
|
||||
break;
|
||||
}
|
||||
case STADIUM_BEHAVIOUR_EXPIRE:
|
||||
console_println("EXPIRE (stub)");
|
||||
log_message(LOG_DEBUG, "Stadium: dispatch cell=%zu behaviour=EXPIRE (stub)",
|
||||
cell_index);
|
||||
break;
|
||||
case STADIUM_BEHAVIOUR_COOL: {
|
||||
uint64_t identity = stadium_cell_array[cell_index].header.identity;
|
||||
console_puts("COOL identity=");
|
||||
console_put_u64(identity);
|
||||
console_println("");
|
||||
log_message(LOG_DEBUG, "Stadium: dispatch cell=%zu behaviour=COOL identity=%llu",
|
||||
cell_index, (unsigned long long)identity);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -677,7 +677,12 @@ void vm_tick_inference_engine(VM* vm)
|
||||
/* 3. Validate outputs */
|
||||
if (!inference_outputs_validate(vm->last_inference_outputs))
|
||||
{
|
||||
log_message(LOG_WARN,
|
||||
/* 2026-09-22: downgraded from LOG_WARN -- fires routinely
|
||||
* during normal operation and the runtime self-recovers
|
||||
* every time (the whole point of "ignoring results"), so it
|
||||
* does not warrant default-visible attention. Still
|
||||
* available with --log-level=info/debug. */
|
||||
log_message(LOG_INFO,
|
||||
"INFERENCE: Output validation failed, ignoring results");
|
||||
}
|
||||
|
||||
|
||||
+7
-1
@@ -674,7 +674,13 @@ void vm_tick_inference_engine(VM* vm)
|
||||
/* 3. Validate outputs */
|
||||
if (!inference_outputs_validate(vm->last_inference_outputs))
|
||||
{
|
||||
log_message(LOG_WARN,
|
||||
/* 2026-09-22: downgraded from LOG_WARN, matching the kernel
|
||||
* copy of this logic (src/starkernel/vm/vm_runtime.c) --
|
||||
* fires routinely during normal operation and the runtime
|
||||
* self-recovers every time (the whole point of "ignoring
|
||||
* results"), so it does not warrant default-visible
|
||||
* attention. Still available with --log-level=info/debug. */
|
||||
log_message(LOG_INFO,
|
||||
"INFERENCE: Output validation failed, ignoring results");
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user