Files
sic/cmd
Claude (noether) 5d2ee5cdc9 sicd: implement v1/v2 dual-read (migration) — first-byte dispatch + v1-legacy parser
The v2-only daemon rejected v1 frames, so deploying it would brick every v1 client in the
field. main() now dispatches on the FIRST byte: 0x00 = v2 (path unchanged); an ASCII digit =
a new STREAMING v1-legacy parser (that digit is the first byte of the first netstring length,
pushed back via io.MultiReader before parsing); anything else = exit 1 with a diagnostic.

v1 wire (what the deployed cmd/sic sends, no preamble): netstring(mode "exec"|"sh") +
argv/command netstrings + the empty-netstring terminator 0:, ; everything after the terminator
is the child's stdin. exec runs argv directly (each netstring is ONE argv element, never
space-split); sh runs via sh -c. Reuses pumpStdin so EPIPE / reap / exit-status semantics
match the v2 path exactly.

Removed the sicd_test.go "old-v1-frame-without-preamble expects exit 1" subtest: it pinned the
v2-only REJECTION of v1, which dual-read deliberately reverses. v1 acceptance is now covered by
dualread_test.go (9 cases: exec/sh, argv boundaries, trailing stdin, exit status, and the four
reject paths).

Implemented by noether (rich agent) after the room hit a genuine wall — DeepSeek couldn't build
the parser in 10 attempts, Kimi-k2.6 soliloquy-stalled past the 300s per-call timeout. All green:
go test ./... ok, 27/27 Python reference.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-23 10:42:32 +02:00
..