6 Commits

Author SHA1 Message Date
Markus Fritsche a0f93c16cf sicd: add SIGPIPE/SIG_IGN porting note at EPIPE catch site
CPython sets SIGPIPE to SIG_IGN so EPIPE surfaces as BrokenPipeError.
A Go port must replicate this explicitly or the EPIPE path silently
becomes a SIGPIPE death.

24/24 green.
2026-07-23 01:05:39 +02:00
Markus Fritsche 833a0432b8 sicd: harden pump loop, EPIPE/diagnostic handling, signal fidelity
Review round 3 fixes:
- write_all helper: handle partial writes from signals
- EPIPE on write side: catch BrokenPipeError (child dead), not all OSError
- Read-side OSError: diagnose on stderr + exit 1 (not silent truncation)
- parse_netstring: sanity cap 1<<24, return content only (not tuple)
- 128+WTERMSIG for signal-killed children (shell convention)
- 24/24 pytest green
2026-07-23 01:01:59 +02:00
Claude (noether) 7f7f0c2ecb sicd: handle EPIPE and short writes at both write sites
write_all() loops until the full buffer is transferred — os.write's return value was
ignored at the payload write and in the pump loop, so a short write silently truncated
the stream. EPIPE at the payload write (child already dead) now reaps the child and
inherits its status instead of dying with a BrokenPipeError traceback; EPIPE in the pump
loop falls through to the same waitpid, which is standard `cmd | head` semantics.

Tests use deterministic fault injection rather than racing signals: a fork wrapper that
waits with waitid(WNOWAIT) so the child is provably dead but still reapable before the
parent writes, and an os.write cap of 7 bytes per call as the stand-in for a
signal-interrupted partial write.

Reviewed three times. Known gaps, tracked, NOT fixed here: signal-killed children still
collapse to exit 1 (no 128+WTERMSIG), and the pump loop's `except OSError: pass` is
broader than it should be.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-23 00:39:58 +02:00
Markus Fritsche 70103792df sicd: fix pipe-buffer deadlock and review findings
- Pump loop after fork: parent streams stdin to child through pipe
- netstring: enforce .isdigit() on length field
- netstring: require exact consumption (no trailing data)
- argv split: use .split space not .split
- Non-ASCII command: fail loudly
- Child exec failure: os._exit(1)

17/17 pytest green
2026-07-23 00:00:55 +02:00
Markus Fritsche b5e7e2b632 sicd: implement wire protocol (preamble + nested netstring peeling)
- Read preamble: magic byte \x00 + 4-byte BE length + netstring
- Split netstring at first NUL: command\0<payload>
- Exec command with payload + trailing stdin forwarded
- Nested payloads are netstrings for the next hop (one layer peeled per sicd)
- Bare -- survives as opaque data
- Malformed input: exit 1 with stderr diagnostic
- 14/14 pytest green
2026-07-22 23:44:00 +02:00
marfrit 35a8a07152 sic — quoting-proof remote command execution
Client (sic) frames argv as netstrings and pipes it over ssh to a daemon
(sicd) that execvp's it, so no shell re-parses the arguments. exec mode by
default; opt-in sh mode for pipes/redirects/globs. Go client + daemon, Python
reference daemon, design doc, quoting-hell demo, and foreground/background
agent skills.

Renamed from the climcp prototype.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EWpfhDgYNA21tETDP9ueBE
2026-07-19 12:10:51 +02:00