PR #481 made target inputs positional and reserved keywords for
actor placement/runtime controls. PR #484 still forwarded target
kwargs, so tests and examples failed local signature binding after
the rebase.
Deats,
- bind named target inputs with `functools.partial()`
- keep placement, naming and runtime controls as direct keywords
- reject invalid target calls locally before actor startup
- require linked one-shots to raise one direct `RemoteActorError`
- doc linked context execution and per-child process reaping
Prompt-IO: ai/prompt-io/opencode/20260819T184640Z_481ba003_prompt_io.md
(this patch was generated in some part by `opencode` using `gpt-5.6-sol` (`openai`))
Convention sweep (user req): all `tractor.open_nursery()`
bindings in test + example code use `an: ActorNursery` (`n`,
`nursery` + several tractor-nurseries confusingly named `tn`
are renamed); `trio.open_nursery()` bindings stay `tn` (incl.
`concurrent_actors_primes.py`'s inner trio nursery, renamed
`n` -> `tn` to match).
Purely mechanical, function-scoped renames — prose "nursery"/
"an" in docstrings/comments untouched; func-arg kwargs like
`portal.run(func, n=value)` untouched.
Gate: renamed test modules green on `trio`; full debugger suite
(28p/6s) + example-runner (21p) green.
(this patch was generated in some part by [`claude-code`][claude-code-gh])
[claude-code-gh]: https://github.com/anthropics/claude-code
The `test_trynamic_trio` + `a_trynamic_first_scene.py` migration
to paired `to_actor.run()` one-shots carries a race the legacy
`run_in_actor()` shape never had: donny + gretchen each
`wait_for_actor()` (then DIAL) the *other*, but a one-shot is
reaped the instant its own hello returns — so the slower peer
can resolve the winner's registry entry and connect to an
already-dead sockaddr -> `ConnectionRefusedError` boxed as a
`RemoteActorError` (or a reg-wait `TooSlowError`), flaking
~1-in-3 standalone runs.
Mutual-rendezvous peers must OUTLIVE both dialogs, so pin the
lifetimes explicitly: `start_actor()` both as daemons, run both
hellos concurrently via bg `Portal.run()` tasks, then reap with
`an.cancel()` only after the task-nursery joins. (The legacy
teardown-reap provided this pinning implicitly — one of the
few places its semantics were ever actually relied upon.)
Gate: `-k trynamic` standalone x8 green (was flaking); full
`test_registrar` module + the example-runner green.
(this patch was generated in some part by [`claude-code`][claude-code-gh])
[claude-code-gh]: https://github.com/anthropics/claude-code
The final excision of #477: with zero in-repo callers left (all
tests/examples/docs migrated to `to_actor.run()` et al) the
entire legacy one-shot machinery drops out,
- `runtime/_supervise.py`: `ActorNursery.run_in_actor()`, the
`._cancel_after_result_on_exit` portal-set and the
`_reap_ria_portals()` teardown-reaper (both its happy-path
block-exit call AND the error-path snapshot + 0.5s-bounded
collection) are deleted — one-shot result-waiting now lives
entirely in the caller's task via `to_actor.run()`, whose
enclosing cancel-scope bounds the wait by construction (the
correct-scoping fix for the unbounded-reap hang class; the
`d1fb4a1a` guard test now passes structurally).
- `runtime/_portal.py`: `Portal._submit_for_result()`,
`._expect_result_ctx`, `._final_result_msg/_pld`,
`.wait_for_result()` + the deprecated `.result()` alias are
gone — a `Portal` no longer has any "main result" notion.
NB `Context.wait_for_result()` is a different (very alive)
API and is untouched.
- `spawn/_spawn.py`: `exhaust_portal()` +
`cancel_on_completion()` (the reaper tasks) deleted; backend
comment sweeps in `_trio.py`/`_mp.py`.
- `_exceptions.py`: the `NoResult` sentinel dies with its lone
reader.
- `tests/test_ringbuf.py`: drop a daemon-portal `.result()`
call that was already a warn + `NoResult` no-op (the ctx-acm
exit does the real result-wait); unshadow the 2nd `sctx` as
`rctx`.
- comment/docstring x-ref sweeps: `msg/types.py`,
`_context.py`, `to_actor/`, `tests/test_to_actor.py`.
Gate: `test_to_actor test_spawning test_cancellation
test_infected_asyncio test_local test_rpc` = 81 passed,
3 xfailed on `trio`; +`test_ringbuf` = 70 passed, 3 skipped,
3 xfailed on `mp_spawn`.
(this patch was generated in some part by [`claude-code`][claude-code-gh])
[claude-code-gh]: https://github.com/anthropics/claude-code
The 8 `examples/debugging/` scripts driven by the pexpect'd
`test_debugger.py` REPL-flows (#477 removal),
- blocking one-shots (`subactor_error`, `subactor_breakpoint`,
`shielded_pause`): straight `to_actor.run(fn, an=an)` — the
boxed error/`BdbQuit` raises in the root's task.
- `multi_subactors`: introduces the "collect all errors" pattern
— each one-shot catches + stashes its `RemoteActorError` (vs
raising) so no child's crash cancels its siblings before
they've had their own REPL sessions, then a
`BaseExceptionGroup` of the lot raises at the end; preserves
the legacy teardown-reap REPL flow exactly (28/28 debugger
suite unchanged).
- `multi_nested_subactors_error_up_through_nurseries` +
`root_cancelled_but_child_is_in_tty_lock`: recursive
`spawn_until` levels each block on their one-shot child; the
parallel spawner-trees run as bg task-nursery one-shots where
the first tree's error cancels the other.
- `multi_subactor_root_errors` +
`root_timeout_while_child_crashed`: `start_actor()` + bg
`Portal.run()` tasks so the root's own error/timeout races the
already-crashed children, same as before.
- `sync_bp`: TODO-comment x-ref update only.
`test_debugger.py`: the nested-nurseries test's final-output
patterns update to the new relay shape — the LAST-released leaf
REPL's error chain wins each level's relay-vs-cancel race and
relays as a `collapse_eg()`-annotated collapsed chain, while the
sibling tree is cancelled + absorbed. (The legacy teardown-reap
grouped BOTH the `name_error` and bp-quit chains — explaining
the previously-mysterious "extra" `src_uid`/`relay_uid` patterns
noted in the old TODO.)
Gate: `tests/devx/test_debugger.py` = 28 passed, 6 skipped —
identical to the pre-migration baseline.
(this patch was generated in some part by [`claude-code`][claude-code-gh])
[claude-code-gh]: https://github.com/anthropics/claude-code
The known-flaky dynamic pubsub test's 3 fire-and-forget spawn
sites (#477 removal),
- the forever-streaming `publisher` + N `consumer` one-shots now
bg-schedule as `to_actor.run(fn, an=n)` tasks in a local `trio`
task-nursery (`publisher`'s rendezvous name still derives from
`fn.__name__`).
- the simulated user-cancel raise (`KeyboardInterrupt` /
`TooSlowError` params) cancels the task-nursery, each one-shot
reaping its subactor via `to_actor.run()`'s shielded
`Portal.cancel_actor()`; `_run_and_match()`'s existing
`BaseExceptionGroup.split()` walk covers the (possibly nested)
relay shapes unchanged.
- spawns now issue concurrently rather than sequentially —
comment on the fork-backend budget updated to match.
Gate: both params x4 runs green on `trio` + x1 on `mp_spawn`;
full module green.
(this patch was generated in some part by [`claude-code`][claude-code-gh])
[claude-code-gh]: https://github.com/anthropics/claude-code
Final `test_cancellation.py` group of the `run_in_actor` removal
(#477) — cancel-mechanics tests, so clean conversions,
- `test_cancel_via_SIGINT_other_task`: the 3 keep-alive
`run_in_actor(sleep_forever)` one-shots become plain
`start_actor()` daemons (an idle daemon needs no "main" task,
and no longer shares a single dup'd `namesucka` name).
- `spawn_sub_with_sync_blocking_task`: the middle layer's spawn
becomes a blocking `to_actor.run(spin_for, an=an)` which parks
awaiting the sync-sleeping grandchild's result until cancelled
from above.
- `test_cancel_while_childs_child_in_sync_sleep`: the
fire-and-forget middle-actor spawn becomes a bg
`to_actor.run()` task in a local task-nursery; the root's
`assert 0` cancels it, driving the same
graceful-cancel-then-zombie-reap cascade on the sync-blocked
grandchild. The `man_cancel_outer` xfail param is unchanged.
Zero live `run_in_actor()` call-sites remain in this suite.
Gate: full `test_cancellation.py` module green on both `trio`
(18p/1xf) + `mp_spawn` (18p/1xf).
(this patch was generated in some part by [`claude-code`][claude-code-gh])
[claude-code-gh]: https://github.com/anthropics/claude-code
Third `test_cancellation.py` group of the `run_in_actor` removal
(#477),
- `spawn_and_error` fans out each level's erroring one-shots as
concurrent `to_actor.run(fn, an=an)` tasks in a local `trio`
task-nursery (recursing per spawner subactor), as does the
test-body's top-level spawner loop.
- the deterministic exact-breadth nested-BEG shape dies with the
legacy teardown-reap: each level now groups whatever subset of
sub-tree errors relay before the first one's cancel wins, and
a single-member group gets unwrapped by the runtime's own
`collapse_eg()` at every actor boundary — so a fully-raced
tree relays a bare `RemoteActorError` chain.
- loosen the shape walk accordingly: accept a lone
`RemoteActorError` or a 1..breadth group whose members box
`ExceptionGroup` (multi-relay), `AssertionError` (collapsed
leaf chain), `RemoteActorError` (re-boxed collapsed chain) or
`BaseExceptionGroup` (runtime reap-deadline `Cancelled`
upgrade); fold the windows-only tolerances into the same walk.
- raced sibling `trio.Cancelled`s are now ABSORBED by the
task-nursery instead of landing in the group, so the MTF
shape-mismatch xfail should consistently xpass — note added to
drop the marker once CI confirms.
- add an `else: pytest.fail()` so a silently-clean tree can no
longer pass.
Gate: both depths green on `trio` (10 consecutive runs) +
`mp_spawn`.
(this patch was generated in some part by [`claude-code`][claude-code-gh])
[claude-code-gh]: https://github.com/anthropics/claude-code
`test_nested_multierrors`'s backend/depth budget `match` only
carries arms for the `trio` + `main_thread_forkserver` spawn
backends, so running under any other (e.g. `mp_spawn`) leaves
`timeout` unbound and crashes with an `UnboundLocalError` at the
headroom-scaling below. Add default per-depth arms riding the MTF
budgets (same per-spawn round-trip cost class).
(this patch was generated in some part by [`claude-code`][claude-code-gh])
[claude-code-gh]: https://github.com/anthropics/claude-code
Second `test_cancellation.py` group of the `run_in_actor` removal
(#477),
- one-shot subactors now run as concurrent `to_actor.run(fn,
an=an)` tasks in a local `trio` task-nursery, so their errors
raise WHILE the actor-nursery block is open (vs the legacy
teardown-reap) and the first error cancels sibling one-shots.
- wrap the task-nursery in `collapse_eg()` so the deterministic
single-error cases still surface a bare `RemoteActorError`.
- loosen the group-shape assertion: the relay-vs-cancel race
populates anywhere from 1 to `num_actors` `RemoteActorError`s
(the exact-`num_actors` BEG was `run_in_actor`'s
reap-all-at-teardown); group members are always
`RemoteActorError` now since sibling `trio.Cancelled`s are
absorbed by the task-nursery.
- move the daemon-portal call loop inside the task-nursery body
so the sleep-forever one-shot case is cancelled by the daemon
error raise.
- rename the `*run_in_actor*` param ids to `*one_shot*`.
Gate: 6 passed on both `trio` + `mp_spawn` backends.
(this patch was generated in some part by [`claude-code`][claude-code-gh])
[claude-code-gh]: https://github.com/anthropics/claude-code
First group of the `test_cancellation.py` `run_in_actor` removal
(#477),
- `test_remote_error` -> blocking `to_actor.run()` (single erroring
one-shot; a bad-arg `TypeError` still relays as a
`RemoteActorError`).
- `test_multierror` -> concurrent fan-out via
`gather_contexts([p.open_context(assert_err_ctx) ...])` over
`start_actor()` portals. NB `gather_contexts` is cancel-on-first
so the 2nd errorer is usually cancelled before relaying its own
exc and the pair collapses to a single `RemoteActorError` (vs the
legacy reap-all-at-teardown `BEG`-of-N) — the assertion now
accepts either shape.
- delete `test_multierror_fast_nursery` — a 25-actor stress test of
`run_in_actor`'s teardown-reap; no analogous surface under the
`to_actor` fan-out.
- add an `assert_err_ctx` `@context` shim for the `open_context`
fan-out.
Remaining `test_cancellation` groups (some_cancels_all, nested,
SIGINT, sync-blocking) still on `run_in_actor` — ported next.
(this patch was generated in some part by [`claude-code`][claude-code-gh])
[claude-code-gh]: https://github.com/anthropics/claude-code
Two sites migrated (#477 removal),
- `test_trynamic_trio`: donny + gretchen each wait on the *other*
to register, so they must run CONCURRENTLY — was two
non-blocking `run_in_actor()`s awaited after; now two
`to_actor.run()` one-shots scheduled into a local `trio`
task-nursery.
- the unregister-on-cancel cluster test: its non-streaming branch
spawned `run_in_actor(trio.sleep_forever)` purely to keep each
subactor alive + registered — a `start_actor()` daemon does that
without a "main" task, so the spawn loop collapses to the same
`start_actor()` the streaming branch already used.
Suite: 16 passed.
(this patch was generated in some part by [`claude-code`][claude-code-gh])
[claude-code-gh]: https://github.com/anthropics/claude-code
`test_multi_actor_subs_arbiter_pub` used `run_in_actor()` to spawn
two forever-ish subscriber actors and hold their portals for a
later `cancel_actor()` (its `.result()` was commented out exactly
because `subs()` never cleanly returns). That deferred-spawn +
cancel shape isn't a blocking `to_actor.run()`, so convert to the
successor primitives (#477 removal),
- `start_actor()` per subscriber — keeps the portal for the
existing `cancel_actor()` teardown,
- run `subs()` on each via a background `Portal.run()` task in a
local `trio` nursery so both subscribe concurrently with the
test's `wait_for_actor` / topic checks,
- each bg runner swallows the `RemoteActorError`/`ContextCancelled`
that `cancel_actor()` relays; a trailing `tn.cancel_scope.cancel()`
drops any lingering runner.
Suite: 8 passed.
(this patch was generated in some part by [`claude-code`][claude-code-gh])
[claude-code-gh]: https://github.com/anthropics/claude-code
Migrate all 4 sites to blocking `tractor.to_actor.run()` (#477
removal),
- rename the two API-named tests to `test_to_actor_run_*`
(`same_func_in_child`, `can_skip_parent_main_inheritance`) —
they exercise the same spawn / `inherit_parent_main` path via
the successor API.
- the recursive `spawn()` helper drops its white-box
`an._children` / portal-`_peers` asserts (which probed
`run_in_actor`'s portal + nursery-tracking internals);
`to_actor.run()` returns the result and reaps internally, so
keep the user-facing `result == 10` check.
- `test_most_beautiful_word` drops the 2nd `wait_for_result()`
(the legacy result-cache re-fetch) — `to_actor.run()` delivers
the value once, no cache.
Suite: 9 passed.
(this patch was generated in some part by [`claude-code`][claude-code-gh])
[claude-code-gh]: https://github.com/anthropics/claude-code
Sole call-site: `run_in_actor(sleep_back_actor, ...)` ->
blocking `tractor.to_actor.run(..., an=n, ...)` (#477 removal).
The RPC-callback subactor is awaited in-caller instead of
reaped at nursery teardown; `name=`/`enable_modules=` map to
`to_actor.run()`'s same-named params, the rest to `**fn_kwargs`.
(this patch was generated in some part by [`claude-code`][claude-code-gh])
[claude-code-gh]: https://github.com/anthropics/claude-code
Sole call-site: an inlined `run_in_actor(...).result()` ->
blocking `tractor.to_actor.run(fn, an=an, ...)` (#477 removal).
Behaviour identical — the one-shot's result/error is awaited
in the caller's task rather than reaped at nursery teardown;
the enclosing `move_on_after` still cancels the sub in the
`error_in_child=False` case.
(this patch was generated in some part by [`claude-code`][claude-code-gh])
[claude-code-gh]: https://github.com/anthropics/claude-code
First test-file of the #477 `.run_in_actor()` removal (blocking
`to_actor.run()` is the successor; the legacy non-blocking one-shot
is dropped, not replaced). All 9 call-sites migrated,
- blocking result/error/streaming-result tests -> `to_actor.run(fn,
an=an, ...)`; the "streaming" ones stream aio<->trio INSIDE the
subactor so the caller only awaits the final result.
- forever-task + cancel tests (`test_tractor_cancels_aio`,
`test_trio_cancels_aio`) -> `start_actor()` +
`Portal.open_context()` + cancel — can't block on a
never-returning task. Adds a small `sleep_forever_aio_ctx`
`@context` shim.
- greens the red `test_tractor_cancels_aio` anti-hang guard from
the prior commit: under the correctly-scoped API the wait is
bounded by the caller's cancel scope, so the hang is structurally
gone — not patched.
Suite: 34 passed, 2 xfailed (trio backend).
(this patch was generated in some part by [`claude-code`][claude-code-gh])
[claude-code-gh]: https://github.com/anthropics/claude-code
Wrap `test_tractor_cancels_aio`'s `main()` in a
`trio.fail_after(9 * cpu_perf_headroom())` so a wedged remote
runtime can't hang the test forever. This is the blessed
anti-hang guard here bc `pytest-timeout`'s global cap is
intentionally off (it breaks `trio` under the fork backends,
per the `pyproject` NOTE).
The cap is generous + CPU-headroom-scaled bc it's an anti-hang
guard, not a perf assertion. Motivated by the
`._ria_nursery`-removal regression where a wedged ria-reaper
once hung this exact test indefinitely.
(this patch was generated in some part by [`claude-code`][claude-code-gh])
[claude-code-gh]: https://github.com/anthropics/claude-code
Treat `runtime_kwargs` as provided whenever it is not `None`.
Previously an empty dict bypassed placement validation and was
silently ignored when `an` or `portal` selected an existing runtime.
Reject both placement modes before actor startup for empty and
configured runtime kwargs while preserving empty-dict use when
`to_actor.run()` owns its private runtime.
Caught-during: review remediation
Found-via: `/code-review` P3 option-validation finding
Review: PR #481 (opencode)
https://github.com/goodboy/tractor/pull/481#pullrequestreview-4956692120
Prompt-IO: ai/prompt-io/opencode/20260819T020757Z_b38efed7_prompt_io.md
(this patch was generated in some part by `opencode` using `gpt-5.6-sol` (`openai`))
Pass target inputs positionally and normalize every retained
`functools.partial()` layer, including Python 3.14 Placeholder
binding. Validate the complete target signature before startup.
Route each ordinary async fn through a static `@context` endpoint
so remote results, errors and caller cancellation remain linked.
Send namespace and function components separately, then resolve
through `Actor._get_rpc_func()` so the RPC module allowlist remains
authoritative. Retain client-created `NamespacePath` refs so
`to_tuple()` does not re-import their callable.
Owned actors enable the endpoint's `__name__` directly. Keep
`to_actor.MODULE` as the importer-facing alias used by caller-owned
portals while retaining the target module's authorization boundary.
Cover all placement modes, nested partials, argument collisions,
linked cancellation, remote errors and authorization failures.
Caught-during: review remediation
Found-via: `/run-tests` portal cancellation regression
Review: PR #481 (opencode)
https://github.com/goodboy/tractor/pull/481#pullrequestreview-4956692120
Prompt-IO: ai/prompt-io/opencode/20260818T193005Z_bf06b4f8_prompt_io.md
(this patch was generated in some part by `opencode` using `gpt-5.6-sol` (`openai`))
`Actor.start_remote_task()` registers its caller context before
sending `Start`, but only cancellation cleaned that state.
Encoding, ack timeout, malformed ack and remote authorization
errors leaked it.
Protect the complete send, acknowledgement and validation phase.
Track successful publication, make a remote cancellation attempt
only when protocol-safe and always release the local context while
preserving the original startup error.
Cover both pre-publication serialization failure and a remote
`ModuleNotExposed` rejection without damaging a reused portal.
Caught-during: review remediation
Found-via: `/run-tests` startup-failure regressions
Review: PR #481 (opencode)
https://github.com/goodboy/tractor/pull/481#pullrequestreview-4956692120
Prompt-IO: ai/prompt-io/opencode/20260818T193004Z_bf06b4f8_prompt_io.md
(this patch was generated in some part by `opencode` using `gpt-5.6-sol` (`openai`))
Cancellation after `Start` publication but before `StartAck` can
strand the caller context and leave its remote task running.
Make one shielded, bounded task-cancel request before dropping
local startup state. Keep the private `cancel_on_startup` policy
outside public target kwargs and disable it for the `_cancel_task`
RPC itself so cleanup can not recursively cancel its own startup.
Release each private helper context on exit and prove the
caller-owned actor remains reusable after controlled startup
cancellation.
Caught-during: review remediation
Found-via: `/run-tests` test_cancel_during_context_startup
Review: PR #481 (opencode)
https://github.com/goodboy/tractor/pull/481#pullrequestreview-4956692120
Prompt-IO: ai/prompt-io/opencode/20260818T193003Z_bf06b4f8_prompt_io.md
(this patch was generated in some part by `opencode` using `gpt-5.6-sol` (`openai`))
`SendStream.send_all()` can raise `trio.Cancelled` after writing an
arbitrary prefix of the four-byte length header and payload. The
peer can no longer distinguish a following msg boundary.
Close the stream under a shield before propagating cancellation so
callers can not append another msg to an indeterminate byte stream.
Caught-during: review remediation
Found-via: prospective P2 cancellation review
Review: PR #481 (opencode)
https://github.com/goodboy/tractor/pull/481#pullrequestreview-4956692120
Prompt-IO: ai/prompt-io/opencode/20260818T193001Z_bf06b4f8_prompt_io.md
(this patch was generated in some part by `opencode` using `gpt-5.6-sol` (`openai`))
Give each `ActorNursery` child its own reap request and
completion event. Owned one-shots now wait for process joining and
bookkeeping removal before returning.
Escalate unacknowledged cancellation with `proc.kill()` after an
active debugger releases. Latch nursery-wide teardown for monitors
that finish startup late, and snapshot children before cancellation
checkpoints permit concurrent removal.
Cover immediate managed-nursery cleanup, failed cancel
acknowledgements and late monitor registration across Trio TCP/UDS
and `mp_spawn`.
Caught-during: review remediation
Found-via: `/run-tests` test_late_child_reap_registration_is_released
Review: PR #481 (copilot-pull-request-reviewer)
https://github.com/goodboy/tractor/pull/481#discussion_r3514759131
Prompt-IO: ai/prompt-io/opencode/20260818T031532Z_4151b956_prompt_io.md
(this patch was generated in some part by `opencode` using `gpt-5.6-sol` (`openai`))
Cover every placement variant + failure mode of the new
`to_actor.run()`,
- private-nursery one-shot + implicit runtime boot via pass-through
`runtime_kwargs`,
- remote-error relay to the caller's task (bare and inside a
caller-managed `an`) as boxed `RemoteActorError`s,
- caller-nursery spawn + portal-reuse w/o implicit reap,
- the concurrent "worker-pool-ish" pattern: a local `trio` task
nursery scheduling one-shots against a shared `an`,
- the 4 pre-spawn validation rejections (sync fn, async-gen fn,
`portal`+`an` combo, `runtime_kwargs`+placement combo).
(this patch was generated in some part by [`claude-code`][claude-code-gh])
[claude-code-gh]: https://github.com/anthropics/claude-code
`test_parent_writer_child_reader` deadlocks on Windows (the
parent/child shared-mem transfer hangs at the larger frame size),
so the `windows-latest` CI leg ran to the 16-min job cap instead
of completing. It's a genuine nascent-Windows shm bug, not a
clean "unsupported", so it's `skipif`'d (not removed) and tracked
under #404; `test_child_attaches_alot` still runs on Windows.
- `@pytest.mark.skipif(platform.system() == 'Windows', ...)` on
the parametrized `test_parent_writer_child_reader` so the leg
completes + reports. linux/macOS unaffected (all 6 variants
still run).
(this patch was generated in some part by [`claude-code`][claude-code-gh])
[claude-code-gh]: https://github.com/anthropics/claude-code
`tractor`'s infect-asyncio mode runs an `asyncio` loop under
`trio` guest-mode; on Windows the default `ProactorEventLoop` is
incompatible and the suite hangs/crashes mid-run (orphaned py
procs), so the `windows-latest` CI leg never finishes reporting.
- add a module-level `pytest.skip(allow_module_level=True)` gated
on `platform.system() == 'Windows'` to `test_infected_asyncio`
and `test_root_infect_asyncio`, before their asyncio-interop
imports. macOS/linux are unaffected (they run these fine).
(this patch was generated in some part by [`claude-code`][claude-code-gh])
[claude-code-gh]: https://github.com/anthropics/claude-code
`test_lifetime_stack_wipes_tmpfile` guards spawn+teardown with a
hard-coded `trio.move_on_after()` (1.6s / 1s) that isn't scaled
for slow CI. On a noisy macOS runner the `error_in_child=True`
case times out before the child error propagates, so the scope
cancels and `assert not cs.cancel_called` flips — reddening the
(required) macOS leg. Same unscaled-deadline class `main` already
fixed for `test_dynamic_pub_sub`.
- multiply the budget by `cpu_perf_headroom()` (`tests/conftest`),
the established deadline-headroom helper (3x on macOS CI, a
1.0 no-op locally / on un-throttled linux).
(this patch was generated in some part by [`claude-code`][claude-code-gh])
[claude-code-gh]: https://github.com/anthropics/claude-code
`tests/test_ringbuf.py` imports `tractor.ipc._ringbuf` at module
top, which pulls in `tractor.ipc._linux` whose module-level
`ffi.dlopen(None)` raises `OSError` on Windows (and any non-linux
host). That fires at COLLECTION, before the module's existing
`pytestmark = pytest.mark.skip` can apply, so it aborts the whole
pytest session — the new `windows-latest` CI leg never gets past
collection.
- guard the module with `pytest.skip(allow_module_level=True)`
gated on `platform.system() != 'Linux'`, placed before the
crashing import — same idiom as `tests/devx/test_debugger.py`.
- the `eventfd`-based ringbuf backend is linux-only by design, so
macOS skips cleanly too (previously it only skipped incidentally
via the absent `cffi` optional dep).
(this patch was generated in some part by [`claude-code`][claude-code-gh])
[claude-code-gh]: https://github.com/anthropics/claude-code
`_peer_closed_errno()` followed cause and context links but did not
descend through grouped exceptions. A reset below a group could
therefore escape as `trio.BrokenResourceError` instead of the
normalized `TransportClosed` boundary.
Walk the exception tree with cycle protection, requiring every
group branch to represent peer closure before normalization. Extend
the `MsgpackTransport.send()` regression to prove all-transport and
mixed-failure behavior.
Review: PR #480 (goodboy)
https://github.com/goodboy/tractor/pull/480
(this patch was generated in some part by `opencode` using
`gpt-5.6-sol` (`openai`))
Registrar election and multi-address probing are discovery-protocol
concerns, but their implementation lived in root-runtime ignition.
Move the bounded handshake probe and its concurrent address
classifier into `discovery._api`, leaving `open_root_actor()` to
consume the classified results.
Update probe tests for the canonical module and clarify that the
daemon-fixture regressions directly exercise their sibling
`conftest` plugin.
Review: PR #480 (goodboy)
https://github.com/goodboy/tractor/pull/480
(this patch was generated in some part by `opencode` using
`gpt-5.6-sol` (`openai`))
Linux previously accepted a pre-existing runtime bindspace without
checking its owner or mode, even though Darwin enforced both. Share
the POSIX directory guard so every managed root and subdir rejects
non-directories and foreign UIDs before changing permissions.
Normalize owner-controlled bindspaces to `0o700` and add Linux
regressions for mode repair, foreign ownership, and non-directory
paths.
Review: PR #480 (goodboy)
https://github.com/goodboy/tractor/pull/480
(this patch was generated in some part by `opencode` using
`gpt-5.6-sol` (`openai`))
The `@context` debugger E2E intentionally closes its channel.
Teardown can surface from either local error shipment or the peer
receive task.
The RPC response fix makes local close win under CI, while the test
required both scheduler-dependent diagnostics.
Keep the common debugger and cancellation assertions, then accept
either transport-close report. This preserves real actor-tree
teardown coverage without depending on task scheduling order.
Review: PR #480 (goodboy)
https://github.com/goodboy/tractor/pull/480
(this patch was generated in some part by `opencode` using
`gpt-5.6-sol` (`openai`))
An RPC caller can close its channel after the callee creates the
endpoint coro but before its `StartAck` or final response lands.
Treat those response-send failures as terminal delivery failures so
the accepted endpoint still runs and application errors stay local
instead of cancelling the shared service nursery.
Register each cancellable RPC in `Actor._rpc_tasks` before
publishing its `Context` through `TaskStatus.started()`.
This closes the checkpoint-free completion race without a
cross-task handoff and keeps `Actor._ongoing_rpc_tasks` balanced
through existing cleanup.
Add regressions for caller disconnects at `StartAck` and error
shipment, plus registration-before-execution and cleanup checks.
Review: PR #480 (goodboy)
https://github.com/goodboy/tractor/pull/480
(this patch was generated in some part by `opencode` using
`gpt-5.6-sol` (`openai`))
Darwin's pytest `tmp_path` can already exceed the 104-byte AF_UNIX
budget before appending either synthetic socket filename. That made
the new sentinel-policy regression fail identically in both macOS
matrix legs without exercising reaper behavior.
Allocate the test's real socket files under a short
`/tmp/tractor-reap-*` directory and retain scoped cleanup.
Review: PR #480 (goodboy)
https://github.com/goodboy/tractor/pull/480
(this patch was generated in some part by `opencode` using
`gpt-5.6-sol` (`openai`))
The remediation grew beyond the original no-autobind fix, leaving
public docs and nearby comments describing connect-only discovery,
XDG-only socket paths, raw readiness probes, and old cleanup naming.
Document typed registrar probing, occupied-address rejection, Darwin's
short runtime directory, platform-aware socket cleanup, sentinel
readiness, and subprocess output draining. Add the GH #473 bugfix
fragment and update regression rationale without changing task states.
Review: PR #480 (goodboy)
https://github.com/goodboy/tractor/pull/480
(this patch was generated in some part by `opencode` using
`gpt-5.6-sol` (`openai`))
Moving Darwin sockets to `/tmp/tractor-<uid>` left pytest and the
standalone reaper searching only `XDG_RUNTIME_DIR`. Enabling that
shared bindspace naively would also let automatic session cleanup
unlink an independent live `registry@1616.sock`.
Resolve the runtime's actual default UDS bindspace on every platform.
Exclude the pid-less registry sentinel from automatic cleanup while
retaining explicit CLI removal, and document that destructive choice.
Cover bindspace resolution and automatic-vs-explicit sentinel policy.
Review: PR #480 (goodboy)
https://github.com/goodboy/tractor/pull/480
(this patch was generated in some part by `opencode` using
`gpt-5.6-sol` (`openai`))
Registry probes need short retry deadlines, but applying their
one-second budget to every portal and child connection can terminate a
valid delayed actor with no client retry path.
Give ordinary pre-registration handshakes an independent ten-second
deadline. Normalize raw `msgspec.DecodeError` frames to
`TransportClosed` so malformed peers cannot cancel the shared IPC
nursery with decoder internals.
Cover malformed frames and the ordinary-vs-probe timeout distinction.
Review: PR #480 (goodboy)
https://github.com/goodboy/tractor/pull/480
(this patch was generated in some part by `opencode` using
`gpt-5.6-sol` (`openai`))
A loaded runner can accept a registry transport while delaying its
actor handshake beyond the first one-second attempt. Treating that
timeout as final classifies a healthy daemon as occupied and cascades
into unrelated discovery failures.
Retry connected handshake failures on fresh channels under a shared
three-second budget, while returning immediately for truly absent
listeners. Bound every connect-plus-handshake attempt, use incremental
backoff, and retain the one-second unauthenticated server limit.
Also bound shielded probe-channel cleanup to 200ms and cover the real
timeout, reconnection, backoff, fresh-channel, and stalled-close paths.
Review: PR #480 (goodboy)
https://github.com/goodboy/tractor/pull/480
(this patch was generated in some part by `opencode` using
`gpt-5.6-sol` (`openai`))
The discovery `daemon` fixture probed UDS readiness by connecting and
immediately closing. That entered Tractor's actor handshake without an
`Aid` payload and destabilized the remote registrar on macOS before
test roots attempted discovery.
Run the child through a small `open_root_actor()` wrapper and publish a
filesystem sentinel only after runtime startup completes. Poll that
sentinel with process-liveness checks and guaranteed setup-failure
cleanup, without touching the transport socket.
Cover transport-free readiness and deterministic polling backoff.
Caught-during: review remediation
Found-via: `/run-tests` discovery daemon fixture consumers
Cause: initial sentinel drafts mishandled `pytest.Testdir`, emitted
invalid `python -c` syntax, and misplaced return-code logging.
Review: PR #480 (goodboy)
https://github.com/goodboy/tractor/pull/480
(this patch was generated in some part by `opencode` using
`gpt-5.6-sol` (`openai`))
Transport connect alone can select a foreign, stalled, or ordinary
actor endpoint as the registry. On macOS UDS this also exercises a
fragile connect-and-bail path before every root election.
Extend `Aid` with backward-compatible probe and registrar capability
fields, require a bounded typed handshake, and classify addresses as
absent, occupied, or confirmed registrars. Reject occupied endpoints
instead of binding over them.
Also,
- close `_connect_chan()` in `finally`
- bypass normal peer tracking for election probes
- preserve legacy registrar handshakes with unknown capability
- cover foreign listeners and idle registrar peer state
Review: PR #480 (goodboy)
https://github.com/goodboy/tractor/pull/480
(this patch was generated in some part by `opencode` using
`gpt-5.6-sol` (`openai`))
`unlink_uds_bind_addrs()` reconstructs self-assigned socket paths
after a hard kill. An over-budget bindspace can make
`UDSAddress.get_sockname()` raise before the guarded `os.unlink()`,
replacing the original supervision outcome after the child is gone.
Catch and report reconstruction failures, then skip cleanup without
raising. Cover the overflow path and prove no unlink is attempted.
Review: PR #480 (goodboy)
https://github.com/goodboy/tractor/pull/480
(this patch was generated in some part by `opencode` using
`gpt-5.6-sol` (`openai`))
Best-effort subprocess teardown must not replace the exception raised
by the test body. Suppress cleanup errors only while propagating that
active failure; keep raising teardown errors on normal body exit.
Also close the Windows stdin pipe after its bounded leader reap.
Review: PR #480 (goodboy)
https://github.com/goodboy/tractor/pull/480
(this patch was generated in some part by `opencode` using
`gpt-5.6-sol` (`openai`))
Actor names and custom runtime subdirs can otherwise produce unsafe
or overlong pathname sockets after moving Darwin's bindspace to
`/tmp`.
Deats,
- hash unsafe or over-budget actor names while retaining `@pid.sock`
- share deterministic naming with post-kill socket cleanup
- enforce Linux and Darwin `sun_path` byte budgets
- restore non-Darwin dir validation and mode `0700`
- validate every nested Darwin runtime-dir component
Review: PR #480 (goodboy)
https://github.com/goodboy/tractor/pull/480
(this patch was generated in some part by `opencode` using
`gpt-5.6-sol` (`openai`))
A raw UDS readiness client can disconnect before the actor handshake.
Darwin reports the first server write as `ECONNRESET`, wrapped in
`trio.BrokenResourceError`; letting it escape cancels the daemon's
shared IPC nursery and makes later roots elect themselves registrar.
Walk the exception chain for `EPIPE` or `ECONNRESET` and translate
either into the existing `TransportClosed` boundary. Also handle
argument-less resource errors without raising `IndexError`.
Review: PR #480 (goodboy)
https://github.com/goodboy/tractor/pull/480
(this patch was generated in some part by `opencode` using
`gpt-5.6-sol` (`openai`))
Always drain docs-example pipes, even when the process exited before
the first status check, and decode malformed output with replacement
so diagnostics preserve the original failure.
Run POSIX examples in dedicated sessions and kill the full process
group on timeout. Reap the leader in every exit path, with bounded
Windows cleanup that cannot wait forever on descendant-held pipes.
Cover fast non-zero exits, invalid output bytes, process-group
termination, and post-timeout reaping.
Review: PR #480 (goodboy,copilot-pull-request-reviewer[bot])
https://github.com/goodboy/tractor/pull/480
(this patch was generated in some part by `opencode` using
`gpt-5.6-sol` (`openai`))
`platformdirs` can place the runtime dir deep below a pytest temp
home, pushing `registry@1616.sock` past Darwin's 104-byte
`AF_UNIX` limit.
Use a compact `/tmp/<app>-<uid>` root on Darwin and secure it
before allocating socket paths:
- require a real, current-user-owned dir via `lstat()`
- tighten existing roots to mode `0700`
- reject symlinks and unsafe nested dirs
Cover the path budget, mode, and symlink rejection.
Caught-during: review remediation
Found-via: `/run-tests` test_macos_rt_dir_fits_uds_path_limit
Review: PR #480 (goodboy,copilot-pull-request-reviewer[bot])
https://github.com/goodboy/tractor/pull/480
(this patch was generated in some part by `opencode` using
`gpt-5.6-sol` (`openai`))
UDS-on-macOS is otherwise un-exercised: the matrix
explicitly excludes the `macos-latest` + `uds` combo, so the
`uds_transport_actor_tree.py` example (skipped on macOS CI
since PR #460) is the only thing that ever touches that
path.
- drop the matrix `exclude` so the full suite runs with
`--tpt-proto=uds` on `macos-latest`.
- un-skip the example on macOS+CI; with example-stderr
surfacing in place a still-red run now yields the full
traceback GH #473 asks for instead of a bare returncode
assert.
Task-bullets 3 + 4 of GH #473.
Prompt-IO: ai/prompt-io/claude/20260702T155006Z_65bf9df5_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
The docs-example harness only re-raises captured subproc
stderr when the LAST line contains 'Error', but a `tractor`
root-actor crash always ends stderr with the strict-EG
collapse note `( ^^^ this exc was collapsed from a group ^^^ )`
— so every possible crash is swallowed down to a bare
`assert 1 == 0`, exactly what the macOS CI leg shows for the
UDS example in GH #473.
- raise with the FULL stderr (+stdout) whenever the example
subproc exits non-zero, regardless of stderr shape.
- keep the legacy last-line 'Error' check for zero-rc runs
which still emit error-ish output.
First task-bullet of GH #473.
Prompt-IO: ai/prompt-io/claude/20260702T155006Z_65bf9df5_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
Run seven fresh interpreters and gate the median `import tractor`
time below a conservative 0.35s ceiling. Provide an explicit
environment override for platforms with a different baseline.
Also assert the optional modules deferred by this patch remain
unloaded so a timing pass cannot hide an eager-import regression.
Review: PR #478 (goodboy)
https://github.com/goodboy/tractor/pull/478#pullrequestreview-4922213201
(this patch was generated in some part by `opencode` using
`gpt-5.6-sol` (`openai`))