Commit Graph
3 Commits
Author SHA1 Message Date
Joseph MattielloandClaude Fable 5 7df363aa49 fix(trace): make trace loss and dump failure impossible to miss
Final-review findings. A 300-frame yarc run emits ~2,578 events/frame, so
the default 1M ring holds ~390 frames while both worked examples showed
--frames 1800: the ring wrapped and threw away most of the run while
trace_probe reported success and trace_dump printed nothing unusual. The
loss was always recoverable from the data — the oldest surviving record's
seq IS the number evicted — so trace_dump and trace_diff now say so on
stderr, and the examples carry a measured VJ_TRACE_RING with a note that
the event rate is ROM-dependent.

vjtrace_dump() checked no fwrite and not fclose, so a truncated dump
returned success — the opposite of the invariant vjtrace_snapshot()
enforces 200 lines below it. It now mirrors that: any short write removes
the partial file and returns -1, so a VJTR file that exists is complete.

A --watch whose ADDR+LEN wrapped past $FFFFFFFF produced lo > hi, a watch
that could never fire, and a run that reported success. Rejected now.

The vj-debug skill's menu_step_probe build line predated that probe
gaining trace_probe_attach and no longer linked; it does now.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-08-12 01:49:11 -04:00
Joseph MattielloandClaude Fable 5 0230c9e676 fix(trace): analyzer CLI fixes -- overflow-safe trace_diff alloc, REGSGPU/DSP FLAGS label, field_diff blank-line desync
Task 6 review found three Critical issues in the analyzer CLIs added
in 93c7f26; fixes all three:

- trace_diff.c: load_filtered() trusted the VJTR header's hdr.count
  for a malloc(count * sizeof(vjtrace_ev)) with no overflow check. A
  crafted header claiming ~2^59 records wrapped that size_t multiply
  into a tiny allocation, and the read loop then wrote real trailing
  records past its end (reviewer reproduced SIGABRT/exit 133 instead
  of exit 2). Now derives the real record count from the file's
  actual length (ftell to end, minus header size, divided by
  ev_size), requires it to match hdr.count exactly before allocating
  anything sized by it, plus a defense-in-depth SIZE_MAX guard on the
  multiply itself. trace_dump.c was already unaffected (streams
  record-by-record).

- trace_memdiff.c / vjtrace.h: REGSGPU/REGSDSP slot 33 was labeled
  CTRL per a literal reading of the task-6 brief's ambiguity
  resolution, but the writer (GPUGetFlags()/DSPGetFlags() in
  vjtrace_snapshot()) and vjtrace.h's own format comment both say
  it's the flags register, not G_CTRL/D_CTRL (a different register at
  a different address, not captured by this format at all). Relabeled
  to FLAGS in trace_memdiff.c; tightened vjtrace.h's REGSDSP wording
  to match REGSGPU's already-clear "+ GPU_FLAGS" and added an
  explicit note on the CTRL/FLAGS distinction.

- field_diff.c: the row loop skipped any row where EITHER side's line
  was blank, not just a trailing blank on both -- silently dropping
  the comparison when one file has a blank line and the other has
  real data at that position (reviewer reproduced two genuinely
  different CSVs reported identical, exit 0). Now: blank on both
  sides is tolerated (never desyncs alignment, since both cursors
  always advance together); blank on exactly one side is compared as
  an all-empty-fields row against the real row, surfacing as an
  ordinary per-column mismatch with exit 1.

Re-verified the full malformed-input matrix (bad magic, truncated
header, mid-record/mid-section truncation, crafted huge count,
blank-line-desync CSV) on all four tools plus real-data behavior on
t1/t2 rings, f1/f2 CSVs, and the snapshot pairs from the original
report -- all unchanged from before the fix except the three bugs
themselves. c89-lint clean. Fix report appended to
.superpowers/sdd/2026-08-11-vjtrace-flight-recorder/task-6-report.md.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-08-11 22:33:07 -04:00
Joseph MattielloandClaude Fable 5 93c7f26463 feat(trace): analyzer CLIs -- trace_dump/trace_diff/field_diff/trace_memdiff
Task 6 of the vjtrace flight-recorder plan: four standalone C99 CLI
tools in test/tools/ that read the formats Tasks 1/4/5 already write
(VJTR event ring, VJSN state snapshot, per-field CSV), with no harness
dependency -- only src/core/vjtrace.h for the shared vjtrace_ev struct
and event enum.

- trace_dump: text/JSONL dump of a VJTR ring, filterable by
  --type/--who/--frame; small register-symbol table for the addr
  field verified against docs/jtrm-register-map.md (corrects the
  brief's example: B_CMD is $F02238, not $F0223C -- that's B_COUNT).
- trace_diff: structural diff of two VJTR rings on (type, who, addr)
  in filtered stream order, default filter excludes per-object
  OP_OBJECT noise, --types overrides; reports first divergence with
  +/-5 records of context from each side.
- field_diff: row/column diff of two trace_probe --field-csv files;
  refuses to align mismatched headers rather than guessing.
- trace_memdiff: byte-run diff of two VJSN snapshots with gap<=4
  coalescing, MAINRAM region annotation from the vjag_memory.c
  address-map comment, and named per-register lines for
  REGS68K/REGSGPU/REGSDSP instead of raw offsets.

All four: exit 0 no-difference/success, 1 difference found, 2 usage
or malformed/truncated input (verified: bad magic, truncated headers,
mid-stream/mid-section truncation, ragged CSV rows, header mismatch --
all exit 2 with a clear message, no crash). c89-lint clean on all four
(the repo's pre-commit hook checks all staged .c files, including
test/tools/, not just src/). Verification details in
.superpowers/sdd/2026-08-11-vjtrace-flight-recorder/task-6-report.md.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-08-11 22:20:09 -04:00