Skip to content

Add chapter: subprocesses and external streams (structured teardown)#22

Open
adamw wants to merge 1 commit into
masterfrom
docs/structured-external-resources
Open

Add chapter: subprocesses and external streams (structured teardown)#22
adamw wants to merge 1 commit into
masterfrom
docs/structured-external-resources

Conversation

@adamw

@adamw adamw commented Jun 28, 2026

Copy link
Copy Markdown
Member

What

Adds guidance for the case structured concurrency is easiest to get subtly wrong: driving a long-lived subprocess (or socket / SSE) whose output is read line by line.

  • New chapter 170-subprocesses-and-external-streams.md:
    • own the work with a supervised scope; the reader's return value is the result (fork{…}.join()), not a cross-thread AtomicReference/CAS;
    • never return a live object that owns running forks to be driven later;
    • teardown: a blocking native read ignores interruption, and Ox joins forks before running releaseAfterScope finalizers — so destroy/close the resource in the scope body's finally, before the join;
    • kill the whole process tree (a launcher's forked child inherits the pipe fds; an orphan keeps the pipe open so the reader never EOFs);
    • drain/inherit stderr so an unread pipe can't stall the child.
  • SKILL.md: two always-loaded design rules (decide the owning scope first; result is the fork's return value; never return a fork-owning handle; destroy blocking resources in the body finally, not releaseAfterScope; tree-kill launchers) plus the index entry.

Why

The detailed how lives in the chapter; the decision triggers live in the always-loaded SKILL.md so the agent picks the right ownership and teardown shape before writing code — the failure mode here is a shutdown-time deadlock that doesn't show up until teardown.

Grounded in Ox's documented scope-teardown semantics (interruptAllAndJoinUntilCompleted runs in the scope body's finally; releaseAfterScope finalizers run after).

🤖 Generated with Claude Code

Adds guidance for the case structured concurrency is easiest to get subtly
wrong: driving a long-lived subprocess (or socket/SSE) whose output is read
line-by-line. The reader is a fork whose return value is the result; the killer
is teardown — a blocking native read ignores interruption, and Ox joins forks
before running `releaseAfterScope` finalizers, so the resource must be destroyed
in the scope body's `finally` (before the join), and a launcher subprocess needs
its whole tree killed or an orphan keeps the pipe open.

- New chapter 170-subprocesses-and-external-streams.md.
- SKILL.md: two always-loaded design rules so the agent decides the owning scope
  and the teardown point UP FRONT (model readers as forks with return-value
  results; never return a live fork-owning handle; destroy blocking resources in
  the body finally, not releaseAfterScope), plus the index entry.

Grounded in Ox's documented scope-teardown semantics (interruptAllAndJoin runs
in the body finally; finalizers after).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.

1 participant