feat(shell): pipelines + Spawner seam, increment 2 (#34)#39
Merged
Conversation
WHAT: The safe-subset engine now supports pipelines (a | b | c). parse.rs classify() returns a Pipeline (Vec of stages) — an unquoted single | splits stages; || and every other operator stay refused. shell_tool.rs gains an injectable Spawner seam: the real OsSpawner wires stages with OS pipes (deadlock-free: per-pipe reader threads), captures last-stage stdout + all stderr, exit = last stage (no pipefail), and kills already-spawned stages on a mid-pipeline spawn failure. Atomic admission: EVERY stage's program is exec-checked before any stage spawns. WHY: Pipelines are the highest-value engine increment (agents pipe constantly), and the Spawner seam establishes the fully-mocked testing strategy for all of Track A: unit tests inject a mock spawner and assert the leash blocks BEFORE any spawn (the spawner is never called on a refusal/denial — incl. atomic pipeline admission), with no real subprocesses. The real std::process path moves to tests/real_spawn.rs (integration), per the no-real-subprocess-in-unit-tests norm. TEST: 28 unit (pure parser + mock-spawner) + 5 real-spawn integration. Keystones: quoted | stays a single stage; a dynamic stage downstream of a pipe is refused; an out-of-scope stage aborts the whole pipeline before any spawn; real echo|cat passes data; pipeline exit = last stage. just check green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HMGPEApE4XfwgMhgFbRn6c
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Increment 2 of #34 (ADR 0005 D3): the safe-subset engine now runs pipelines (
a | b | c), and introduces theSpawnerseam that establishes the fully-mocked testing strategy for the rest of Track A.What
parse.rs—classify()returns aPipeline(Vec<Vec<String>>). An unquoted single|splits stages;||and every other operator/construct stay refused (Unsupported/Dynamic). Empty stages →Malformed.shell_tool.rs— injectableSpawnertrait. RealOsSpawnerwires stages with OS pipes, deadlock-free (a reader thread per stderr + the last stdout), exit = last stage (nopipefail), and kills already-spawned stages on a mid-pipeline spawn error. Atomic admission: every stage isexec-checked before any stage spawns.Testing (fully mocked + deep)
Spawnerasserts the leash blocks before any spawn — out-of-scope exec, a refused construct, and an out-of-scope pipeline stage all leave the spawner uncalled; quoted|stays one stage; correct stages reach the spawner; timeout via a blocking mock.tests/real_spawn.rs, realstd::process):echo|catpasses data; pipeline exit = last stage (true|false→1,false|true→0); stderr/non-zero captured; out-of-scopermdenied and never spawns.L3 note
The
Spawnerseam is also where L3 will plug in (Landlock on Linux, #35) and where it's simulated in tests — brush is not an L3 mechanism (it can't see a spawned binary's syscalls).Test plan
just checkgreen (fmt + clippy all-features & no-default-features + workspace tests). Part of #34.🤖 Generated with Claude Code