Skip to content

refactor(cli): build the SSE event body from flatten_row_event - #1146

Merged
thekevinscott merged 3 commits into
mainfrom
claude/1139-sse-flatten
Sep 18, 2026
Merged

thekevinscott merged 3 commits into
mainfrom
claude/1139-sse-flatten

Conversation

@thekevinbot

Copy link
Copy Markdown
Collaborator

Fixes #1139. Part of #1072.

event_to_value in packages/rust/src/cli/serialize.rs was the third
hand-written RowEvent flattening: an arm-for-arm match repeating the
lowercase action strings, the Update new_row/old_row split, and the Error
variant's nullable table and to_string_lossy path. #1131 moved those
decisions into dirsql::flatten_row_event and cut both bindings over; this is
the missed call site.

The SSE shape is not the flat struct — the error arm omits row/old_row
entirely and the row arms carry no error key — so the serializer consumes
flatten_row_event and adds only that transport detail: a presence check on
flat.error. row_to_json still does the cell marshaling, blob-to-hex
included.

Key order is unchanged because serde_json::Map is a BTreeMap here
(cargo tree -p dirsql -i serde_json -e features | grep -c preserve_order →
0), so the hand-built map renders alphabetically exactly as the json!
literals did.

Two assertions were added, neither weakening an existing one: the insert test
now asserts no error key, and a new test covers the error arm's table: null
plus the absence of row/old_row.

PARITY.md gains a line noting the SSE serializer shares the same source.
This restores parity rather than introducing drift; no SDK API or CLI flag
changed.

Byte-identical SSE proof

Built the CLI twice from the same worktree — once at origin/main, once with
the change — and ran the identical scenario against the same fixture path
(insert, update, error, delete) with curl -sN http://localhost:7139/events.

Fixture (/tmp/dirsql-1139-fixture): a posts table over posts/*/*.json
whose on-file hook emits basename/size, and prints non-JSON for any file
named bad* so the error arm fires. Driven by:

printf '{"a":1}'     > posts/alice/Brand-New.json   # insert
printf '{"a":22222}' > posts/alice/Brand-New.json   # update
printf '{}'          > posts/alice/bad-one.json     # error
rm                     posts/alice/Brand-New.json   # delete

Both binaries produced, byte for byte:

data: {}
data: {"action":"insert","file_path":"posts/alice/Brand-New.json","old_row":null,"row":{"basename":"Brand-New.json","size":7},"table":"posts"}
data: {"action":"update","file_path":"posts/alice/Brand-New.json","old_row":{"basename":"Brand-New.json","size":7},"row":{"basename":"Brand-New.json","size":11},"table":"posts"}
data: {"action":"error","error":"on-file output was not a JSON array of rows: invalid JSON: expected ident at line 1 column 2","file_path":"posts/alice/bad-one.json","table":"posts"}
data: {"action":"delete","file_path":"posts/alice/Brand-New.json","old_row":null,"row":{"basename":"Brand-New.json","size":11},"table":"posts"}
$ diff <(grep '^data:' main.sse) <(grep '^data:' new.sse); echo $?
0

(The captures also contain a duplicate error line from the hook running twice
on the create+write pair; it is present identically in both.)

Coverage

cargo +nightly llvm-cov --lib --features cli --branch in packages/rust:
regions 93.67%, functions 93.16%, lines 94.54%, branch 79.49% — all above the
[rust.coverage] floors (lines 92 / functions 91 / regions 91 / branch 72).
No floor moved. unit mutation (rust): 1 mutant tested, 0 survivors.

E2E Verification

  • Ran e2e suites locally for every affected SDK
  • Python SDK e2e: pass
  • TypeScript SDK e2e: pass
  • Rust core e2e (if applicable): pass
  • packages/python/e2e-attestations/<branch>.json receipt written if packages/python changed (just e2e-attest-python)
  • packages/ts/e2e-attestations/<branch>.json receipt written if packages/ts changed (just e2e-attest-ts)
  • Command(s) run: cargo test --workspace --features cli; just e2e-attest-python; just e2e-attest-ts; just preflight
  • Result summary: rust workspace green (887 lib tests + integration/e2e suites, 0 failed); python e2e 37 passed; ts e2e 17 passed across 5 files; just preflight → 0 failing pair(s), 3 skipped.

Changelog / Migrations

  • Changelog fragment added under <root>/<pkg>/changelog.d/ for each changed package (or: skip-changelog trailer on a commit with reason)
  • Migration fragment added under <root>/<pkg>/migrations.d/ (or: not required -- additive/bugfix only)

Changelog: packages/rust/changelog.d/2026-09-18-sse-shares-flatten-row-event.md
(Changed). No migration fragment: the refactor is behavior-preserving and
the wire body is unchanged.

The HTTP /events serializer was the third hand-written flattening of
RowEvent, repeating the action strings, the Update new_row/old_row split
and the Error variant's nullable table and lossy path. It now consumes
the core's flatten_row_event and adds only the transport's own shape: a
presence check on the flat error that picks between the row keys and the
error key.

The wire body is unchanged byte for byte. serde_json's Map is a BTreeMap
here, so the hand-built map renders in the same alphabetical key order as
the json! literals it replaces.
@thekevinscott
thekevinscott merged commit 59474c3 into main Sep 18, 2026
77 checks passed
@thekevinscott
thekevinscott deleted the claude/1139-sse-flatten branch September 18, 2026 17:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

core: the CLI's SSE serializer is a third hand-written RowEvent flattening

2 participants