3.9 KiB
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.pygit diff HEAD~1..HEAD -- tests/trionics/test_subproc.pygit 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.)
Post-drain rc-check in the supervisor coro body, AFTER
own_tn.__aexit__. Placing theCalledProcessErrorraise here (not insideown_tn) means the EG-unwrap happens at the OUTERtn.start()boundary — callers docollapse_eg()if they want bare. Doing the raise INSIDEown_tnwould cancel the still-draining relay reader mid-flight and lose stderr lines._UNSETsentinel forstdout. A plain default ofNonecouldn’t distinguish “use the safeDEVNULLdefault” from “caller explicitly passedNone(inherit, presumably knowingly)”. The sentinel keeps the SAFE default while letting power users opt into inherit.relay_level='io'(custom level 21). Chosen to sort just above stdlibINFO=20 so a default--ll infoshows the relay, but it remains a distinct level so users can filtertractor.trionics:ioseparately. Pickingruntime=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”).Reader is MANDATORY, not opt-in cosmetic. With
stdout=PIPE/stderr=PIPEwe OWN the drain responsibility — there’s notrio.capture_*running under the hood here. The ~64KiB OS pipe buffer means a child writing more than that without us reading hangs atwrite()— a deadlock that won’t show up in small-output tests, which is why the 200KiB-no-newline test is in the suite.task_status.started(trio_proc)BEFORE theown_tnexits. 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.__notes__viaadd_note()for the CPE.stderr. The.stderrattribute is whatsubprocesscallers expect; theadd_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.