The gh-pages deploy workflow already exists (build on PR + main,
deploy on main push via `actions/deploy-pages`); wire up the sphinx
side,
- `sphinx.ext.githubpages`: emits a `.nojekyll` so Pages serves the
`_static/` + `_images/` dirs (Jekyll otherwise drops `_`-prefixed
paths, breaking every asset),
- `html_baseurl`: the canonical site root
(`https://goodboy.github.io/tractor/`) for the `<link
rel="canonical">` + `og:url` tags.
The one remaining (one-time, manual) step is the repo setting:
Settings -> Pages -> Source = "GitHub Actions".
(this patch was generated in some part by [`claude-code`][claude-code-gh])
[claude-code-gh]: https://github.com/anthropics/claude-code
Rework the landing top into a left-anchored hero row and match the
navbar branding to it.
- `index.rst` + new `tractor_hero.html`: an inline-svg hero
(`[logo] distributed structured concurrency`) with a full-width
sub-tagline, plus a single screen-reader-only `<h1>` ("tractor")
so the section headings become proper `<h2>` (was 4x `<h1>`), and
the intro prose reflowed into 3 bullets,
- `conf.py`: a `logo.text` ("tractor") beside the navbar wireframe,
polars-style,
- `custom.css`: the hero-row layout (logo + tagline + sub-line,
single row on desktop / stack on mobile), the navbar brand styled
like the centered nav tabs with `display:inline-block` to kill
the parent link's propagated hover-underline, and smaller landing
section headings w/ their `tl;dr` margin-note pulled level.
The hero linework stays `fill: currentColor` so the whole thing
flips with the light/dark toggle.
(this patch was generated in some part by [`claude-code`][claude-code-gh])
[claude-code-gh]: https://github.com/anthropics/claude-code
Capture the (easy-to-forget) logo recolour + theme-preview flow,
- new `notes_to_self/svgtool.py`: a headless-firefox svg helper
(`colors`/`recolor`/`preview`/`page`); its `page --theme
dark|light` injects a `localStorage` pin so a pydata `auto`-mode
build renders its dark variant in a screenshot — the only way to
verify theme-adaptive bits headlessly,
- `howtodocs.md`: a "Tweaking the logo" table (which file +
`color:` knob per context) and an `svgtool` section that breaks
down the `--theme` trick for the sphinx-rusty.
(this patch was generated in some part by [`claude-code`][claude-code-gh])
[claude-code-gh]: https://github.com/anthropics/claude-code
Carry the landing hero's transparent-faces wireframe look to the
two other logo contexts. An `<img>`-embedded svg can't read the
page CSS (no `currentColor`), so the colours are baked per target,
- navbar: 2 variants (`tractor_logo_nav_light.svg` near-black
lines, `…_nav_dark.svg` near-white) wired via the pydata
`logo.image_light`/`image_dark` opts so it swaps with the theme
toggle, matching the hero,
- README: `tractor_logo_wire.svg` — one neutral-grey, with INLINE
fills (no `<style>`) so it reads on both GitHub light + dark and
survives GitHub's svg sanitiser.
Faces are `fill: none` throughout; the filled `tractor_logo_side`
stays the recolour source + favicon.
(this patch was generated in some part by [`claude-code`][claude-code-gh])
[claude-code-gh]: https://github.com/anthropics/claude-code
Swap the landing-page hero from a static `<img>` (white-faced —
which glared as a light box on the dark theme) to an INLINE svg
whose linework uses `fill: currentColor` and whose faces are
transparent. The wireframe now inherits the theme text colour
(`svg.hero-logo { color: var(--pst-color-text-base); }`) and the
page background shows through, so it flips correctly with the
light/dark toggle.
- `tractor_logo_hero.html`: the inline svg partial (`.st0` ->
`currentColor`, `.st1` -> `none`), `.. raw:: html :file:`-d into
`index.rst`,
- the shared `tractor_logo_side.svg` is untouched here (navbar +
favicon still ride it).
(this patch was generated in some part by [`claude-code`][claude-code-gh])
[claude-code-gh]: https://github.com/anthropics/claude-code
Add a "Share on your LAN" blurb to both docs how-to homes
(`notes_to_self/howtodocs.md` + the dev-tips "Building these docs"
section): bind the server to all ifaces — via `sphinx-autobuild
--host 0.0.0.0` (live-reload) or `python -m http.server --bind
0.0.0.0` (static) — so a peer on your subnet can hit
`http://<lan-ip>:8000` (`hostname -I` prints the ip).
Verified a LAN-ip `curl` returns 200 + the socket binds `0.0.0.0`;
includes a trusted-LAN-only caveat since it's an unauthenticated
server on every interface.
(this patch was generated in some part by [`claude-code`][claude-code-gh])
[claude-code-gh]: https://github.com/anthropics/claude-code
Collect the sphinx build + live-preview one-liners (incl. the nix
`.#docs` opt-in shell and the `d2` diagram specifics) so
contributors don't have to reverse-engineer them,
- new `notes_to_self/howtodocs.md` terse cheat-sheet (force-added
past the `notes_to_self/` ignore, same as `howtorelease.md`),
- a rendered "Building these docs" section in the dev-tips guide
(`docs/project/dev-tips.rst`),
- a README pointer to the note.
(this patch was generated in some part by [`claude-code`][claude-code-gh])
[claude-code-gh]: https://github.com/anthropics/claude-code
The `docs` group (sphinx 9 + `pydata-sphinx-theme` stack) was added
to `pyproject.toml` but its lockfile half never landed — the
committed `uv.lock` still matched `main` and `uv lock --check`
flagged it out-of-sync. Relock to add the (additions-only) docs
deps so `uv sync --locked` and any CI lock-check pass.
(this commit msg was generated in some part by [`claude-code`][claude-code-gh])
[claude-code-gh]: https://github.com/anthropics/claude-code
The new `api/` reference pages surfaced 22 docutils warnings from
informal reST in public docstrings; fix the markup so the docs
build is warning-free (24 -> 0), clearing the path to a future
`-W`/`nitpicky` flip in CI.
Deats (docstring content only; no code/signature changes),
- give bullet lists a blank line + base-column indent (`Context`,
`Context.cancel_called`/`.cancelled_caught`/ `.outcome`,
`ActorNursery.cancel_called`, `query_actor`,
`open_crash_handler`),
- demote the under-short `Behaviour:` underline in `Context.cancel`
to a `**bold**` label,
- close an unbalanced backtick in the `wait_for_actor` summary and
use the `` `role`\ s `` escaped-plural idiom where a role was
pluralized (`gather_contexts`, `mk_pdb`, `MsgCodec`, msg `Error`,
`open_context_from_portal`),
- make the `|_` method-tree in `ContextCancelled.canceller` a
literal block (the bare `|` was read as a substitution ref),
- same blank-line fix for the `#318` entry in `NEWS.rst`.
(this patch was generated in some part by [`claude-code`][claude-code-gh])
[claude-code-gh]: https://github.com/anthropics/claude-code
Three robustness fixes surfaced by the PR #460 self-review,
- a render that's *attempted and fails* (a `d2` bin is present but
errors on the source) now raises a docutils error instead of
silently serving the stale committed SVG — so `sphinx-build -W`
fails on a broken `.d2` edit. The no-binary case stays a quiet
committed-SVG fallback.
- render into a sibling temp-file then `os.replace()` so a
failed/torn render can never clobber a good committed SVG.
- guard against 2 distinct `.d2` sources colliding on a single
`_diagrams/<stem>.svg` within a build.
`render_svg()` now returns a `RenderResult` tristate that `run()`
maps to error / raw-source-literal / image.
(this patch was generated in some part by [`claude-code`][claude-code-gh])
[claude-code-gh]: https://github.com/anthropics/claude-code
Factor the shared dev-shell toolchain into `basePkgs` + `baseHook`
so extra-tooling shells can extend it, then add a `docs` shell =
base + `pkgs.d2` (the diagram renderer our `.. d2::` sphinx
directive shells out to). Keeps `d2` OUT of the `default` shell so
casual dev envs stay lean.
Enter with `nix develop .#docs`, then build via `uv run --group
docs make -C docs html`.
(this patch was generated in some part by [`claude-code`][claude-code-gh])
[claude-code-gh]: https://github.com/anthropics/claude-code
Address the actionable findings from the `/code-review` pass
(#1-6); the d2-ext + docstring nits (#7-10) are left for a
follow-up.
Deats,
- `uds_transport_actor_tree.py`: `portal.chan.raddr.sockpath` is
the *shared listener* socket (named for the root registrar), NOT
the child's path — relabel it + lead with the per-child peer pid,
and stop claiming it's the child addr in the docstring,
- `docs.yml`: scope the `pages` `concurrency` group to the `deploy`
job w/ `cancel-in-progress: false` so a PR build can't cancel an
in-flight production deploy,
- `architecture.rst`: `'subint'` is not a selectable `start_method`
on this branch (`SpawnMethodKey` lacks it) — reframe as
in-development/roadmap,
- `context.rst`: `StreamOverrun` isn't re-exported from `tractor`;
point at `tractor._exceptions`,
- `debugging/`: sweep the 3 literalinclude'd examples off the
deprecated `.result()` -> `.wait_for_result()`,
- `quickstart.rst`: proc-title is `name@pid` (per `Aid.reprol`),
not `@uuid`.
NOTE, the `debugging/` examples are pexpect-tested; re-run
`tests/devx/test_debugger.py` for them.
(this patch was generated in some part by [`claude-code`][claude-code-gh])
[claude-code-gh]: https://github.com/anthropics/claude-code
`docs.yml`: sphinx build on PRs + main pushes (via `uv sync
--no-dev --group docs`), deploying to gh-pages only from main
pushes using the official
`actions/{upload-pages-artifact,deploy-pages}` pair - the
`msgspec`-style flow wished for in #123. No `d2` bin in CI (yet) so
the committed SVGs serve as-is.
(this patch was generated in some part by [`claude-code`][claude-code-gh])
[claude-code-gh]: https://github.com/anthropics/claude-code
Replace the ancient `docs/index.rst` (still teaching
`tractor.run()`, `@stream` + arbiters..) with a full ~32 page tree
teaching ONLY the current api (`.wait_for_result()`, registrar
naming, `@context` + `open_context()` as the core model),
- landing: hero example, feature cards + canon links,
- `start/`: install + a 4-example quickstart on-ramp,
- `explain/`: an "SC across processes" essay distilling the essence
per #157's orig ask + a runtime architecture tour,
- `guide/`: 12 task-focused pages incl the flagship multi-process
debugging walkthrough, `Context` + `MsgStream` deep-dives,
cancellation semantics (self-vs-cross cancel rules), discovery,
infected `asyncio`, typed msging + the #126 testing-tips page,
- `api/`: 10 curated autodoc pages (all targets import-verified vs
the reorg'd subpkg tree),
- `project/`: changelog include, ported dev-tips (drops old
`docs/dev_tips.rst`) + roadmap.
Every code block is a `literalinclude` from `examples/`
- zero duplication, all CI-run - w/ `d2` figs floated
into the RHS margin per the 3-col design. Build is green; the 24
remaining warnings all source from lib docstring rst-isms or legacy
`NEWS.rst` content.
Substantially resolves#157 (refine round pending); chips at #175 +
#126.
Prompt-IO: ai/prompt-io/claude/20260611T175152Z_8526985c_prompt_io.md
(this patch was generated in some part by [`claude-code`][claude-code-gh])
[claude-code-gh]: https://github.com/anthropics/claude-code
7 hand-authored `docs/diagrams/*.d2` sources (sketch-mode,
grayscale theme, `elk` layout) w/ their pre-rendered fallback SVGs
committed under `docs/_diagrams/` (served as-is when no `d2` bin is
found, eg. in CI),
- `actor_tree`: the hero supervision-tree,
- `context_handshake`: seq diagram of the
`Start`/`StartAck`/`Started`/`Yield`/`Stop`/`Return` ctx dialog,
- `streaming_pipeline`: 4-actor fan-in topology,
- `runtime_stack`: the per-actor layer cake,
- `debug_lock`: root-tty-lock REPL serialization,
- `error_propagation`: one-cancels-all boxed-err flow,
- `infected_aio`: guest-mode loop nesting.
Rendered w/ `d2` v0.7.1 via `nix run nixpkgs#d2`.
(this patch was generated in some part by [`claude-code`][claude-code-gh])
[claude-code-gh]: https://github.com/anthropics/claude-code
Full `docs/conf.py` rewrite targeting `pydata-sphinx-theme` 0.18
(3-col layout: nav-index left, content middle, page-toc right)
skinned to a minimal b&w look via `_static/css/custom.css`, keeping
the `algol_nu` pygments style.
Also add 2 local sphinx exts under `docs/_ext/`,
- `d2diagrams.py`: a `.. d2::` directive rendering `d2lang` sources
at build time when a `d2` bin is found (`D2_BIN` env supports
argful values eg. `'nix run nixpkgs#d2 --'`), falling back to any
git-committed SVG, else emitting the raw source as a literal
block; no pypi ext was usable (all stubs or returncode-swallowing
per the #157 research),
- `marginalia.py`: a theme-agnostic `.. margin::` directive
(`sphinx-book-theme` style `Sidebar` subclass) for prose-anchored
RHS asides; the custom css floats these (and `:margin:` d2 figs)
into the right gutter on wide screens.
(this patch was generated in some part by [`claude-code`][claude-code-gh])
[claude-code-gh]: https://github.com/anthropics/claude-code
Fill in the long-TODO'd `[dependency-groups]` entry: `sphinx>=9.1`
+ `pydata-sphinx-theme>=0.18` +
design/copybutton/opengraph/togglebutton exts; relock.
Build via, `uv run --group docs make -C docs html`
Theme choice per the #157 research: `numpy`, `ray` + `polars` all
ride `pydata-sphinx-theme` these days (`ray` migrated off
`sphinx-book-theme`, which now hard-pins a stale pydata 0.16.1
anyway).
(this patch was generated in some part by [`claude-code`][claude-code-gh])
[claude-code-gh]: https://github.com/anthropics/claude-code
New runnable (and so CI-auto-tested) scripts backing the new docs
guides, each following the `test_docs_examples.py` runner
conventions,
- `typed_payloads.py`: `@tractor.context(pld_spec=)` typed
`started()`/stream roundtrip + a deliberate `MsgTypeError` catch
demoing send-side validation,
- `nested_actor_tree.py`: 3-level tree w/ fan-out rpc through a
mid-tier supervisor actor,
- `service_daemon_discovery.py`: registered daemon located via
`find_actor()`/`wait_for_actor()` sans any spawn-portal ref,
- `uds_transport_actor_tree.py`: `enable_transports=['uds']` tree
printing the filesystem sockaddr + kernel peer-pid creds,
- `streaming_broadcast_fanout.py`: one ipc stream fanned out to N
local tasks via `MsgStream.subscribe()`.
All gaps were mined from in-code TODOs + the docs recon pass; see
#175 for the orig tutorial wishlist.
(this patch was generated in some part by [`claude-code`][claude-code-gh])
[claude-code-gh]: https://github.com/anthropics/claude-code
Rename `parallelism/_concurrent_futures_primes.py` ->
`concurrent_futures_primes.py` so the example-runner
(`test_docs_examples.py`) stops skipping it (leading `_` =
excluded) and CI finally exercises the `concurrent.futures`
baseline we compare against in the new parallelism guide.
Deats,
- keep the original executor code verbatim in a sync
`check_primes()` fn for clean docs excerpting,
- add module docstring + zero-arg `async def main()` +
`trio.run(main)` guard per the runner conventions.
(this patch was generated in some part by [`claude-code`][claude-code-gh])
[claude-code-gh]: https://github.com/anthropics/claude-code
Swap the deprecated `portal.result()` calls for the modern
`.wait_for_result()` spelling in,
- `a_trynamic_first_scene.py` (x2)
- `actor_spawning_and_causality.py`
- `parallelism/single_func.py`
These 3 are literalinclude'd by the new docs tree so the rendered
code must teach the current api; the `debugging/` set still calls
`.result()` (pexpect pattern-matched tests, left for a follow-up
sweep).
(this patch was generated in some part by [`claude-code`][claude-code-gh])
[claude-code-gh]: https://github.com/anthropics/claude-code
The `.uid`->`.aid` migration (prior commits) re-assembled the
legacy uid pair by hand as `(x.aid.name, x.aid.uuid)`.
`Aid.uid` (`msg/types.py`) is the non-deprecated canonical
property returning exactly that pair, so swap to `x.aid.uid`
everywhere — byte-identical, just DRY + consistent.
Covers the 5 review-flagged source sites (`msg._ops`,
`devx.debug._sync`, `experimental._pubsub`, `_exceptions`)
plus the ~22 unflagged identical sites (`_streaming` f-string
logs + 6 test modules); also tightens the `_exceptions`
`our_uid` annotation to `tuple[str, str]`.
Review: PR #469 (copilot-pull-request-reviewer)
https://github.com/goodboy/tractor/pull/469#pullrequestreview-4575979601
(this patch was generated in some part by [`claude-code`][claude-code-gh])
[claude-code-gh]: https://github.com/anthropics/claude-code
Ports below 1024 are privileged on linux
(`net.ipv4.ip_unprivileged_port_start = 1024`), so a non-root
`.bind()` on one raises `PermissionError`. The old `1000 +` floor
could roll into the 1000-1023 range and trip flaky `[Errno 13]
Permission denied` registry-listener binds; bump the base to 1024
so every generated port is non-privileged.
(this commit msg was generated in some part by [`claude-code`][claude-code-gh])
[claude-code-gh]: https://github.com/anthropics/claude-code
`experimental._pubsub`'s `fan_out_to_ctxs()` pushed each packet
via the now-deprecated `Context.send_yield()`, tripping its own
`DeprecationWarning` on every published msg.
Inline that method's exact body —
`await ctx.chan.send(Yield(cid=ctx.cid, pld=payload))` — so the
wire msg stays byte-identical (still a `Yield`) yet no warning
fires. The "proper" fix (swap to `MsgStream.send()` via
`Context.open_stream()`) needs BOTH sides migrated: the
subscriber still uses the legacy `Portal.open_stream_from()`
which never calls `.started()`, so a publisher-side
`open_stream()` raises a `RuntimeError` ("`started()` must be
called before opening a stream") — verified. Left as the
module's standing rework TODO.
(this patch was generated in some part by [`claude-code`][claude-code-gh])
[claude-code-gh]: https://github.com/anthropics/claude-code
Two test-suite warnings can't be fixed at source, so register
documented `[tool.pytest.ini_options] filterwarnings` ignores
(real library users still see both):
- `forkpty()` multi-threaded `DeprecationWarning` — emitted by
`pexpect` (stdlib `pty.py`) for the `devx` debugger REPL tests;
we never call it. UPSTREAM fix = pexpect avoiding `forkpty()`
under multithreading.
- `@tractor.stream` `DeprecationWarning` — the legacy
`test_legacy_one_way_streaming.py` exists to test that
DEPRECATED API; the warning fires at import (module-level
decorators) so a per-test mark can't catch it, hence the ini
filter.
(this patch was generated in some part by [`claude-code`][claude-code-gh])
[claude-code-gh]: https://github.com/anthropics/claude-code
`test_cancel_via_SIGINT_other_task` opened its nursery with the
now-deprecated `strict_exception_groups=False`. Under strict EGs
(trio's default since 0.25) the SIGINT-induced `KeyboardInterrupt`
surfaces wrapped in a `BaseExceptionGroup` — alongside the child
tasks' `Cancelled`s, so it's MULTI-exc and `collapse_eg()` can't
fold it back to a bare KI (the `# why no work!?` the author hit).
Drop the deprecated flag, use a default (strict) nursery, and
assert the result is a bare `KeyboardInterrupt` OR a
`BaseExceptionGroup` whose `.subgroup(KeyboardInterrupt)` is
non-empty.
(this patch was generated in some part by [`claude-code`][claude-code-gh])
[claude-code-gh]: https://github.com/anthropics/claude-code
Mirror the core swap on the test side: every `Channel`/`Actor`
`.uid` access now reads the non-deprecated `.aid` struct's
`(.name, .uuid)` (or `.aid.name` for a `.uid[0]`), keeping the
compared / printed value byte-identical while silencing the
self-inflicted `DeprecationWarning`. Touches
`test_context_stream_semantics`, `test_spawning`, `test_local`,
`test_advanced_streaming`, `msg.test_ext_types_msgspec`,
`discovery.test_multi_program`, `test_infected_asyncio`.
(this patch was generated in some part by [`claude-code`][claude-code-gh])
[claude-code-gh]: https://github.com/anthropics/claude-code
`tractor`'s own `Channel.uid`/`Actor.uid` now warn + redirect to
the `.aid: msg.Aid` struct, yet the runtime kept calling the
deprecated tuple property internally — tripping its own
`DeprecationWarning` across many tests.
Swap each internal `.uid` for the behavior-preserving
`(x.aid.name, x.aid.uuid)` (and `.uid[0]` -> `.aid.name`) so the
emitted value / log output stays byte-identical, just sourced
from the non-deprecated struct. Touches `msg._ops`,
`_streaming`, `_exceptions`, `devx.debug._sync`,
`experimental._pubsub`.
(this patch was generated in some part by [`claude-code`][claude-code-gh])
[claude-code-gh]: https://github.com/anthropics/claude-code
`tests.test_cancellation` asserted on `ActorNursery.cancelled`,
which now warns + redirects to `.cancel_called`. Swap to the
non-deprecated property so the suite stops tripping its own
`DeprecationWarning`.
(this patch was generated in some part by [`claude-code`][claude-code-gh])
[claude-code-gh]: https://github.com/anthropics/claude-code
`tests.devx.test_tooling`'s end-of-tree `expect()` patterns use
a literal `\(` (pexpect regex), which Python 3.12+ flags as an
invalid escape sequence. Make them raw strings so the `\(` is
preserved verbatim and no `SyntaxWarning` fires.
(this patch was generated in some part by [`claude-code`][claude-code-gh])
[claude-code-gh]: https://github.com/anthropics/claude-code
`config.addinivalue_line('markers', ...)` the two custom marks
the suite uses but never registered, silencing
`PytestUnknownMarkWarning` (e.g. `test_debugger.py`'s
`has_nested_actors`). Registering in-code (vs a `pyproject`
`markers=` block) keeps the mark + its doc next to the rest of
the harness config.
(this patch was generated in some part by [`claude-code`][claude-code-gh])
[claude-code-gh]: https://github.com/anthropics/claude-code
Snapshot diagnostic state for a hung `pytest`/`tractor`
process tree: for each pid (+ `pgrep -P` descendants) dump
the `ps` forest, `/proc/<pid>/wchan` + `stack` (kernel-side
blocked syscall), and `py-spy dump` (python-side stack).
Salvaged from the retired `wkt/warnings_cleanup` branch
(untracked, not upstream); homed here as standalone tooling.
(this commit msg was generated in some part by [`claude-code`][claude-code-gh])
[claude-code-gh]: https://github.com/anthropics/claude-code
Doc/type accuracy in `supervise_run_process()`:
- the docstring claimed the rc!=0 `CalledProcessError` had
"no nursery-eg-wrapped CPE to catch/`collapse_eg`", but it
IS raised as a task into the parent `tn`, so under
`trio>=0.33` it surfaces `ExceptionGroup`-wrapped like any
`tn.start()`-task raise. Say so + point at `except*` /
`collapse_eg()` (matching the inline rc-check comment).
- widen `stdout: int` to `int|None` — the param accepts an
explicit `None` (inherit) per its own docstring.
- note that stdout is NOT captured, so the CPE note carries
only stderr (a cmd logging errors to stdout should
`relay_stdout=True`).
Review: PR #465 (copilot-pull-request-reviewer)
https://github.com/goodboy/tractor/pull/465#pullrequestreview-4548191641
(this patch was generated in some part by [`claude-code`][claude-code-gh])
[claude-code-gh]: https://github.com/anthropics/claude-code
`_relay_stream_lines()` kept ALL captured `stderr` for the
rc!=0 `CalledProcessError` note, so a long-lived `check=True`
child could buffer its entire stderr history in parent mem.
Add an `accum_cap` (the new `_STDERR_NOTE_TAIL_BYTES`=16KiB)
that trims the buffer to its TAIL — the bytes nearest the
failure, which are what the note wants anyway.
While here, factor the per-line decode/`rstrip` into a
`_decode_line()` helper so the in-loop + trailing-flush
emits no longer duplicate it.
Review: PR #465 (copilot-pull-request-reviewer)
https://github.com/goodboy/tractor/pull/465#pullrequestreview-4548191641
(this patch was generated in some part by [`claude-code`][claude-code-gh])
[claude-code-gh]: https://github.com/anthropics/claude-code
`supervise_run_process()` bound the relay `emit` method
(`getattr(log, relay_level)`) unconditionally at entry, so a
bad/typo'd `relay_level` raised `AttributeError` even for a
call that requested NO relay (the common silent path). Bind
it lazily behind `relay_stdout or relay_stderr` so only an
actual relay request validates the level.
Review: PR #465 (copilot-pull-request-reviewer)
https://github.com/goodboy/tractor/pull/465#pullrequestreview-4548191641
(this patch was generated in some part by [`claude-code`][claude-code-gh])
[claude-code-gh]: https://github.com/anthropics/claude-code
Two `supervise_run_process()` caller-kwarg footguns that
previously failed obscurely now raise a clear, early
`ValueError`:
- a bare `stdout=subprocess.PIPE` override spawned NO drain
reader (only `relay_stdout` does), so a child emitting
>64KiB would deadlock on a full ~64KiB pipe buffer.
- `trio`'s `capture_stdout`/`capture_stderr` alias our
MANAGED `stdout`/`stderr` keys; forwarding them made
`trio.run_process` raise an opaque "can't specify both"
error, so reject them up-front pointing at `relay_*` /
the `stdout=` override.
Review: PR #465 (copilot-pull-request-reviewer)
https://github.com/goodboy/tractor/pull/465#pullrequestreview-4548191641
(this patch was generated in some part by [`claude-code`][claude-code-gh])
[claude-code-gh]: https://github.com/anthropics/claude-code
Under `trio>=0.33`'s strict exception-groups a lone
`error_parent` arrives wrapped in a 1-exc `ExceptionGroup`
instead of collapsing to the bare exc, so it skips the
`except error_parent` arm and lands in the group arm — where
`is_multi_cancelled()` is (correctly) `False`, tripping the
assert. Also accept `beg.subgroup(error_parent) is not None`.
Review: PR #465 (copilot + self CI-triage)
https://github.com/goodboy/tractor/pull/465#pullrequestreview-4548191641
(this patch was generated in some part by [`claude-code`][claude-code-gh])
[claude-code-gh]: https://github.com/anthropics/claude-code
Flat `trio.fail_after(1)` wraps a whole actor spawn + IPC
round-trip in `test_most_beautiful_word` — sub-second on a
warm box, but slow/noisy CI runners (esp. macOS) blow the
1s deadline with a `trio.TooSlowError`. Scale the budget by
`cpu_scaling_factor()`: a strict `1s` locally (factor
`1.0`), CI/CPU-throttle headroom (~3x) on macOS.
Review: PR #465 (copilot + self CI-triage)
https://github.com/goodboy/tractor/pull/465#pullrequestreview-4548191641
(this patch was generated in some part by [`claude-code`][claude-code-gh])
[claude-code-gh]: https://github.com/anthropics/claude-code
Two version-compat fixes for the `devx` debugger
test-suite, both about matching upstream output that
got more verbose w/ recent lib releases.
ANSI stripping (`tests/devx/conftest.py`),
- Add `ansi_strip(text)` helper + `_ansi_re` pattern
(regex per https://stackoverflow.com/a/14693789).
- Apply inside `in_prompt_msg()` + `assert_before()` so
substring matches against REPL/traceback output stay
robust to color leakage.
- Motivated by py3.13's colored tracebacks +
`pdbp`/pygments highlighting leaking ANSI even when
`PYTHON_COLORS=0` is set in the `spawn` fixture (not
every renderer in the spawned subproc honors it).
- Replaces the longstanding inline TODO that linked
the SO answer w/o impl'ing.
trio 0.30+ `Cancelled._create(` match (`test_debugger`),
- In `test_shield_pause` swap the two
`"raise Cancelled._create()"` assertion patterns →
`"raise Cancelled._create("` (open-paren form, no
closing).
- trio >=0.30 raises a multi-line
`raise Cancelled._create(source=.., reason=..,
source_task=..)` w/ cancel-reason metadata, so the
legacy bare-`()` form no longer matches. Inline
comment documents the trio-version pivot.
(this commit msg was generated in some part by [`claude-code`][claude-code-gh])
[claude-code-gh]: https://github.com/anthropics/claude-code
(cherry picked from commit 3854cf5ecb)
(cherry picked from commit c07cf2546b)
A `trio.Nursery.start()`-style wrapper around
`trio.run_process()` that surfaces rc!=0 errors
deterministically, ALWAYS isolates the parent
controlling-tty, and optionally live-relays the child's
std-streams to `log.<level>` per-line. Suits both
short-lived test-runners + long-lived daemons.
`supervise_run_process()`,
- Deterministic rc!=0: pass `check=False` to `trio`
and do our OWN post-drain rc-check from the
supervisor coro body AFTER `own_tn.__aexit__` — NOT
inside the internal nursery, since that would
race-cancel the still-draining relay reader and lose
stderr lines. (Re)build + raise a BARE
`subprocess.CalledProcessError`: `.stderr=` for
programmatic callers + an `add_note()`'d
`|_.stderr:` block for human teardown logs. No
nursery-eg-wrapped CPE to `collapse_eg` around.
- Parent controlling-tty isolation: `stdin=DEVNULL`
always, `stdout=DEVNULL` unless relayed/overridden
(via `stdout=` kwarg w/ `_UNSET` sentinel so explicit
`None` = inherit still works). Prevents a spawned
program from clobbering the launching tty's scrollback
w/ control-seqs.
- Live per-line relay: `relay_stdout=True`/
`relay_stderr=True` → relayed to `log.<relay_level>`
(default `'io'`, our custom level 21). Picked to sort
just above stdlib `INFO`=20 so it shows at usual
`info`/`devx` levels yet stays separately filterable;
`runtime`=15 was REJECTED as a default since it'd be
silently filtered at usual verbosity — footgun for
daemon supervisors whose whole point is visibility.
STREAMED, not buffered-until-exit.
- Non-blocking `tn.start()` semantics: live
`trio.Process` handed up via
`task_status.started()` immediately (else
`tn.start()` would block till child exit, losing
the long-lived-daemon use case). Supervise/relay bg
tasks run to completion in this coro.
- `**run_process_kwargs` forwarded verbatim (env, shell,
cwd, start_new_session, executable, ...); MANAGED keys
(`stdin`/`stdout`/`stderr`/`check`) win on conflict.
- Crash-handling layer intentionally NOT baked in —
compose `maybe_open_crash_handler()` ON TOP at the
call-site.
`_relay_stream_lines()` helper,
- Concurrent pipe-drain reader. MANDATORY whenever piping
w/o `capture_*` since nothing else drains the OS pipe —
child blocks on `write()` once kernel buf (~64KiB) fills
→ deadlock.
- Modes (combine freely): `emit`-only live relay,
`accum`-only silent drain+capture (for the CPE note),
or both. Per-line splitting handles cross-chunk
residuals + flushes any trailing un-newline-term'd line
at EOF.
`_add_stderr_note()` helper,
- Attaches an indented `|_.stderr:` note to a CPE via
`add_note()` for legible rc!=0 reporting at teardown.
Tests (`tests/trionics/test_subproc.py`),
- Hermetic `trio`-only (no actor-runtime).
- `test_stdout_relayed_per_line`: per-line stdout relay.
- `test_parent_tty_isolated`: child fd1 is OUR pipe (no
`/dev/pts/*`), fd0 pinned to `/dev/null`.
- `test_no_deadlock_on_big_unnewlined_output`: 200KiB
no-newline output completes under `fail_after(2)` —
exercises the concurrent drain (without it, the child
blocks at ~64KiB).
- `test_stderr_relay_and_cpe_rebuild`: rc!=0 w/
`relay_stderr=True` → bare `CalledProcessError` w/ the
`.stderr` note + per-line live relay.
- `test_nonrelay_cpe_note`: rc!=0 w/o relay → same
deterministic post-drain CPE w/ `.stderr` note (silent
drain+capture path).
Re-export `supervise_run_process` from `tractor.trionics`.
Prompt-IO: ai/prompt-io/claude/20260601T231429Z_0e3e008b_prompt_io.md
(this patch was generated in some part by [`claude-code`][claude-code-gh])
[claude-code-gh]: https://github.com/anthropics/claude-code
(cherry picked from commit f595acc76c)
(cherry picked from commit 6df9ee11bc)
Matches the explicit `dict.pop(uid, None)` contract one
line above; same semantics as the prior truthy check.
(this commit msg was generated in some part by [`claude-code`][claude-code-gh])
[claude-code-gh]: https://github.com/anthropics/claude-code
(cherry picked from commit 0e3e008b0c)
(cherry picked from commit 13ed668512)
The bound `StackLevelAdapter.<name>` emit method captured its
level via a `_level: int = value` default-arg at bind time, so
a later `add_log_level(name, <new-value>)` re-registration left
the method emitting at the *stale* original level. Read
`CUSTOM_LEVELS[name_up]` at call time instead — the method binds
once but always tracks the current registered value.
Also,
- correct the `add_log_level()` docstring's "Idempotent" note
to describe the call-time value lookup (was the misleading
"no-op-ish refresh (won't clobber an already-bound method)").
- sync stale `_reap.py` doc/comment markers `tractor[` ->
`_subactor[` to match the actual `_proctitle._def_prefix`
proc-title prefix (doc-only drift; the code markers already
referenced `_proctitle._def_prefix`).
Review: PR #467 (copilot-pull-request-reviewer)
https://github.com/goodboy/tractor/pull/467#pullrequestreview-4562945515
(this patch was generated in some part by [`claude-code`][claude-code-gh])
[claude-code-gh]: https://github.com/anthropics/claude-code
Make the sub-actor proc-title prefix a single
authoritative constant (`_proctitle._def_prefix`) so
the reap-recognition markers and `xontrib` banner pick
it up automatically — one place to flip the prefix
shape going fwd.
Deats,
- `_proctitle._def_prefix: str = '_subactor'`. New
module-level const consumed by everything that needs
to know the prefix.
- `set_actor_proctitle(actor, prefix=_def_prefix)`:
takes an explicit `prefix` arg (default = the const)
so callers can override per-spawn if they want.
- Default proc-title format:
`'tractor[<reprol>]'` → `f'{prefix}[<reprol>]'`
i.e. `_subactor[<reprol>]` by default.
- `_testing/_reap.py`: cmdline + comm markers source
the prefix from `_proctitle._def_prefix` instead of
the hardcoded `'tractor['`. So
`_is_tractor_subactor()` tracks the const
automatically.
- `xontrib/tractor_diag.xsh`: `acli.reap` orphan-mode
banner now interpolates the
`_TRACTOR_PROC_CMDLINE_MARKERS` tuple directly so
the human-readable mode line stays in sync if the
prefix shape changes again.
(this commit msg was generated in some part by [`claude-code`][claude-code-gh])
[claude-code-gh]: https://github.com/anthropics/claude-code
(cherry picked from commit 3a45dbd503)
(cherry picked from commit fd8d39c0ce)