---
model: claude-opus-4-7[1m]
service: claude
timestamp: 2026-06-01T23:14:29Z
git_ref: 0e3e008b
diff_cmd: git diff HEAD~1..HEAD
---

# RETROACTIVE — original model output not preserved

This `.raw.md` would normally contain the verbatim
pre-human-edit response from the design session that
produced the staged `_subproc.py` module + tests. That
session's transcript is not available, so this file
serves as a diff-pointer placeholder + transparency
note.

## Authoritative artifact

The committed code IS the artifact of record. Once the
companion commit lands, the unified diff is:

> `git diff HEAD~1..HEAD -- tractor/trionics/_subproc.py`
> `git diff HEAD~1..HEAD -- tests/trionics/test_subproc.py`
> `git diff HEAD~1..HEAD -- tractor/trionics/__init__.py`

Before committing, substitute `--cached` for the
pre-commit form.

## What is NOT here

Because this is retroactive:
- No verbatim chain-of-thought / discussion prose from
  the design session.
- No rejected alternatives the model considered before
  arriving at the final shape (e.g. whether the
  rc-check should live inside `own_tn` vs after it; the
  `_UNSET` sentinel vs a `None`-means-DEVNULL
  convention; `io` vs `info` as the default relay
  level).
- No pre-edit code blocks as the model first emitted
  them, separable from any user cleanup applied before
  the diff was staged.

## Inferred design choices visible in the final code

(Documented here because they're the kind of decision
detail an unedited raw transcript would have captured.)

1. **Post-drain rc-check in the supervisor coro body,
   AFTER `own_tn.__aexit__`.** Placing the
   `CalledProcessError` raise here (not inside
   `own_tn`) means the EG-unwrap happens at the OUTER
   `tn.start()` boundary — callers do `collapse_eg()`
   if they want bare. Doing the raise INSIDE `own_tn`
   would cancel the still-draining relay reader
   mid-flight and lose stderr lines.

2. **`_UNSET` sentinel for `stdout`.** A plain default
   of `None` couldn't distinguish "use the safe
   `DEVNULL` default" from "caller explicitly passed
   `None` (inherit, presumably knowingly)". The
   sentinel keeps the SAFE default while letting power
   users opt into inherit.

3. **`relay_level='io'` (custom level 21).** Chosen to
   sort just above stdlib `INFO`=20 so a default
   `--ll info` shows the relay, but it remains a
   distinct level so users can filter
   `tractor.trionics:io` separately. Picking
   `runtime`=15 would have made the relay invisible at
   default verbosity (a footgun for daemon supervisors
   whose whole point is "I want to see this output").

4. **Reader is MANDATORY, not opt-in cosmetic.** With
   `stdout=PIPE` / `stderr=PIPE` we OWN the drain
   responsibility — there's no `trio.capture_*` running
   under the hood here. The ~64KiB OS pipe buffer
   means a child writing more than that without us
   reading hangs at `write()` — a deadlock that won't
   show up in small-output tests, which is why the
   200KiB-no-newline test is in the suite.

5. **`task_status.started(trio_proc)` BEFORE the
   `own_tn` exits.** Without this, `tn.start()` would
   block until the child exits — losing the "start a
   long-lived daemon and continue with parent work"
   use case. With it, the parent gets the live process
   handle immediately and the supervise+relay tasks
   run in the supervisor coro until the child exits.

6. **`__notes__` via `add_note()` for the CPE
   `.stderr`.** The `.stderr` attribute is what
   `subprocess` callers expect; the `add_note()` is
   what trio's exception-rendering shows. Both wired so
   programmatic AND human consumers see the stderr at
   teardown.

## Honesty statement

This file's content is RECONSTRUCTED from the staged
code, not extracted from a verbatim model transcript.
The prompt-io skill's intent is for the `.raw.md` to
be a pre-edit fossil; that's not possible here. Future
work should write the prompt-io entry DURING the
design session.
