Commit Graph

837 Commits (8fe574c1e447358dafd8ebf5ada3b0714c688f78)

Author SHA1 Message Date
Gud Boi 8fe574c1e4 Adjust to new `ctx._status._result` in disconnect suite 2026-11-06 13:04:35 -05:00
Gud Boi 51330af072 Test reaping of shielded peers with stalled IPC
Add a seventh stalled-IPC case whose peer task shields outer
cancellation while subactor control IPC remains usable. Require
shield entry before SIGINT and child-side pending-cancel evidence
afterward, then prove cancel-ack budget expiry, runtime hard-reap,
SIGKILL status and completed OS reaping before watchdog rescue.

Keep the six existing cases and include the reviewed provenance
pair with corrected human attribution. This models a shielded Trio
task, not an uninterruptible kernel call.

Prompt-IO: ai/prompt-io/opencode/20260929T222213Z_68e9c49f_shielded_peer.md

(this patch was generated in some part by `opencode` using
`gpt-6.1-sol` (`openai`))
2026-10-30 13:13:37 -04:00
Gud Boi 13ffb13be5 Test SIGINT teardown with stalled IPC transports
Add `examples.advanced_faults.ipc_stalls` and an external
`RootMonitor` so SIGINT delivery and shutdown evidence stay
independent of the stalled actor channels. Exercise six Linux
TCP cases with the `trio` spawner: healthy IPC, context receive,
send, both directions, context cancel send, and both directions
plus the subactor `Portal` cancel send.

Deats,
- Split cancellable send gates from buffered receive gates, and
  require actual parked operations before publishing readiness.
- Keep the OOB context `Channel` distinct from the child-control
  `Portal`; context-only faults retain cooperative child exit.
- Require finite cancel-send expiry, runtime hard-reap logs,
  SIGKILL status and completed `waitpid` for the control fault.
- Bound readiness and shutdown with an independent stdout
  watchdog; rescue cleanup cannot satisfy teardown assertions.
- Use `get_rando_addr()` for unreserved registry port selection,
  raise `TimeoutError` on watchdog expiry, and retain structured
  shutdown evidence through `record_property`.

These waits model cancellable runtime unresponsiveness, not
kernel TCP loss or an uninterruptible syscall. Preserve human
review decisions and historical working-tree verification in
the prompt provenance; exact-tree plan checks remain pending.

Prompt-IO: ai/prompt-io/opencode/20260923_ipc_fault_regressions.md

(this commit msg was generated in some part by `opencode` using
`gpt-6.1-sol` (`openai`))
2026-10-30 13:13:37 -04:00
Gud Boi b3e07bd569 Fix swallowed `TransportClosed` on ctx scope exit
EOF-driven cancellation can be absorbed by the ctx nursery before
teardown sees a disconnected channel and skips its recorded error.
Route an unfinished transport fault through `wait_for_result()` so
it raises the original error without waiting on broken IPC or
manufacturing a cancellation requester.

Share `ContextStatus._outcome_is_unfinished()` between EOF
notification and ctx teardown. Both buffered `Return` msgs and
decoded falsey results remain completed outcomes; notification
requires no existing error while teardown raises the recorded one.

Deats,
- Reconcile the close example and advanced-fault matrix with prompt
  fault propagation, preserving break order and pre-`Stop` cases.
- Check exception leaves, socket causes, cancellation provenance,
  and post-close send/receive error precedence and identity. Keep
  synthetic Ctrl-C for the healthy no-break cases.
- Cover buffered/consumed `None`, `False`, `0`, and empty-string
  results across EOF, including final value and type preservation.
- Add `assert_child_reaped()` for retained process handles. Check
  both backend completion and PID disappearance without cleanup.

(this patch was generated in some part by `opencode` using
`gpt-6-astra` (`openai`))
2026-10-30 13:13:37 -04:00
Gud Boi 299765508e Drop deprecated `Context.result()` wrapper
Use `Context.wait_for_result()` directly at the nine remaining
call sites, including the debugger's root stdio-lock handshake.
Preserve `hide_tb=False` in the payload-spec test and keep the
outcome-renaming TODO beside the supported API.

Update the API removal note, ctx-specific comments and drain
diagnostics, and include the API-removal Prompt-IO record.

Prompt-IO: ai/prompt-io/opencode/20260922_context_result_removal.md

(this patch was generated in some part by `opencode`
using `gpt-6-astra` (`openai`))
2026-10-30 13:13:37 -04:00
Gud Boi bcb58f2af5 Test `ContextStatus` ownership and cancel reasons
Add twenty cases around real ctx state, channels and Trio scopes,
with inert actor resources.

Cover independent status initialization and diagnostics, falsey
results, repeated waits and transport-vs-self-cancel detection.
Check first-fault delivery and shallow-frozen identity metadata.
Exercise self-cancel ack policy, existing and ancestor reasons,
and local validation provenance at the message-raising boundary.

Use `.wait_for_result()` so these tests also cover the subsequent
deprecated-wrapper removal. Test provenance is already recorded
with the preceding status implementation.

Prompt-IO: ai/prompt-io/opencode/20260919_context_fault_status.md

(this patch was generated in some part by `opencode`
using `gpt-6-astra` (`openai`))
2026-10-30 13:13:37 -04:00
Gud Boi e100d06732 Move ctx state into `ContextStatus`
Make `Context._status` the single store for outcome, cancellation
and handshake state. Migrate internal callers and existing test
assertions directly, without descriptors or duplicate fields.
Keep policy and live resources on `Context`.

Deats,
- Add shallow-frozen fault metadata in `ContextCancelReason`,
  delivered via Trio's public cancellation reason API. Preserve
  the first cancelling fault when the recorded error changes.
- Distinguish local transport/protocol faults from boxed remote
  failures; type-check self-cancel acknowledgements before reading
  remote-error metadata.
- Move `._final_result_is_set()` onto `ContextStatus` and migrate
  its five production callers plus the existing stream assertion.
- Retain the human's `._scope_tn` rename and add status-only reprs.

Keep public APIs, including the deprecated `.result()` wrapper.
New status regression tests follow in their own commit.

Prompt-IO: ai/prompt-io/opencode/20260919_context_fault_status.md

(this patch was generated in some part by `opencode`
using `gpt-6-astra` (`openai`))
2026-10-30 13:13:37 -04:00
Gud Boi 85f1bc1cb4 Keep `Actor.cancel_soon()` in the root nursery
A late self-cancel can arrive after the RPC service nursery has
closed. Starting `Actor.cancel()` there raises before teardown can
cancel the shielded parent-channel loop or run lifetime callbacks.

Schedule one root-owned task to await `Actor.cancel(None)` before
cancelling the root scope. Deduplicate pending requests, and cancel
the root scope directly once runtime cancellation has completed.

Add a fresh-child regression for open and closed service nurseries
under native Trio and asyncio infection. Check repeated requests
before completion, after completion and from a lifetime callback;
prove all three creator-owned shm segments disappear before parent
nursery teardown can mask a failure. Include the session raw record
referenced by the structured provenance already in the parent.

Prompt-IO:
 ai/prompt-io/opencode/20260916_ib_disconnect_cleanup.md

(this patch was generated in some part by `opencode`
using `gpt-6-astra` (`openai`))
2026-10-30 13:13:37 -04:00
Gud Boi db8dc0e27c Notify unfinished `Context` owners on IPC EOF
Save the proposed transport-fault handling for discovered peers.
`process_messages()` reports EOF then exits, leaving parent ctx
owners blocked until another send detects the loss. Wake unfinished
owners on that exact channel with `TransportClosed`, including
owners paused at application checkpoints unrelated to IPC.

This scope-cancellation policy remains provisional pending the
transport contract discussion. Saving the impl and regression cases
on this branch does not settle that policy or imply acceptance.

Deats,
- Match `Context.chan` by identity and notify all unfinished parent
  ctxs on the failed connection, leaving other channels alone.
- Preserve received outcomes and record buffered `Return` receipt
  using the `Unresolved` sentinel before EOF can replace success.
- Keep local transport loss distinct from a remote cancellation
  request: do not invent `Context._canceller` provenance.
- Preserve the original disconnect cause through owner cancellation
  and unwinding in `Context._maybe_raise_remote_err()`.

Also,
- Exercise two owners sharing a discovered sibling channel, blocked
  in stream receive or an application `Event`, plus a healthy peer
  that continues to echo after fault handling.
- Synchronize EOF injection after startup and task quiescence.
  Cover drained and buffered final results without timing sleeps.
- Regress cause preservation directly with a connection-reset cause
  and a distinct owner-unwinding exception.
- Carry the staged structured session provenance unchanged. Its
  broader cancellation observations do not expand this boundary;
  the root-owned `cancel_soon()` fix remains outside this save.

Exact-tree project tests remain pending in the commit plan. Earlier
session results in the provenance log are not passes for this tree.

Prompt-IO: ai/prompt-io/opencode/20260916_ib_disconnect_cleanup.md

(this patch was generated in some part by `opencode`
using `gpt-6-astra` (`openai`))
2026-10-30 13:13:37 -04:00
Gud Boi b6316ed776 Avoid `raise None` after successful `asyncio` exit
An asyncio-side task can handle the issued `TrioTaskExited` and
return normally. With `suppress_graceful_exits=False`, the strict
reporting branch in `translate_aio_errors()` nevertheless tried
to raise `trio_to_raise=None`, replacing successful completion
with TypeError('exceptions must derive from BaseException').

Require an actual pending exception before that strict-policy
raise. Existing errors retain their reporting and cause handling;
strict reporting does not manufacture an error after a successful
asyncio-side return with no remaining trio-side error.

Deats,
- run the channel-exit matrix directly in an infected root as well
  as through sequential TCP RPCs in an infected child;
- add direct/cached cases where the asyncio-side task suppresses
  the issued TTE and returns, under graceful and strict policies,
  with native and Greenback task execution;
- test a child suppressing TTE and staying blocked: check channel
  startup state, then require receipt of that channel's exact
  issued TTE with trio-side exit set and asyncio still running;
- publish the receipt marker only after those checks, before the
  responsive parent awaits `an.cancel()`; require nursery exit
  to join the retained child process handle;
- bound that probe from another process, failing if its watchdog
  must rescue the test group; successful child exit does not prove
  that hard-kill escalation was needed;
- explicitly `raise signal`, rename the caller-cancellation flag
  to `cancel_trio_side`, and format signal/callback diagnostics;
- retain the maintainer's test layout and helper documentation.

Qualification: expanded matrix before the guard: 8 failed / 33
passed; all eight failures are strict-policy successful returns.
After the guard, selected exit/root/infected-asyncio/formatter
suites: 70 passed / 18 deselected, with 24 fork deprecation
warnings. Strengthened blocked-child state checks: 1 passed.
Interactive SIGINT/debugger cases remain excluded.

(this patch was generated in some part by `opencode` using
`gpt-6.1-sol` (`openai`))
2026-10-30 13:10:53 -04:00
Gud Boi 540dd2bf73 Test `trio`-side channel exit in an infected child
Exercise the exit-signal handling fixed in 4dfcf6f6 using the real
`to_asyncio.open_channel_from()` API in an infected child actor.
An expected `TrioTaskExited` must not escape graceful teardown or
be raised again as an unhandled asyncio done-callback exception.

Deats,
- run six cases with native and Greenback-wrapped asyncio tasks:
  direct channel closure, last cached caller exit/cancellation,
  strict TTE reporting, and distinct RuntimeError/TTE cleanup faults;
- use `block_until_trio_side_exit()` to record task identity,
  complete startup, send one channel value and block until signalled;
- wrap the channel API in `maybe_open_context()` for cached cases,
  proving overlapping `trio.Task`s receive the same channel;
- synchronize the second subscription with `tn.start()` and an
  event, then join its task before checking asyncio is still alive;
- open two asyncio tasks sequentially, checking each finishes when
  its channel closes and retains the expected exception identity;
- raise a NEW TTE during cleanup to reject blanket type-based
  suppression, retaining the issued TTE as the new error's cause;
- drain a host-loop callback before checking unhandled exceptions
  for expected shutdown, separately from actor/RPC usability;
- await two sequential `Portal.run()` calls in the same child,
  then explicitly cancel the actor before leaving its nursery.

Keep the Piker reproduction as context rather than describing the
blocking asyncio task as a quote/feed implementation. Document case
selection, closure contracts and the assertions at their call sites.

Qualification: latest matrix 12 passed. Earlier combined asyncio
and task-formatter run: 41 passed / 18 deselected. This matrix does
not yet cover an infected root or an asyncio task suppressing TTE
and returning or remaining blocked; those are follow-up coverage.

(this patch was generated in some part by `opencode` using
`gpt-6.1-sol` (`openai`))
2026-10-30 11:37:16 -04:00
Gud Boi b64c89f05a Contain `@context` final-result disconnects
A caller can depart after its endpoint finishes but before the final
`Return` reaches it. Letting that `TransportClosed` escape `_invoke()`
cancels unrelated RPCs in the actor's shared service nursery.

Guard only the final result send and report the actor, context, scope,
channel and transport error using existing primitive renderers. Keep
endpoint execution and nursery teardown outside that suppression.

Extend the real-TCP reproducer with a genuine endpoint-raised
`TransportClosed` identity control. Snapshot the endpoint, sibling and
nursery owner through the shared task formatter when cancellation
exposes a collateral failure.

Prompt-IO: ai/prompt-io/20261019T145431Z_0553fcf8_ctx_return_fix_prompt_io.md
Prompt-IO: ai/prompt-io/20261020T193619Z_0553fcf8_review_prompt_io.md

(this patch was generated in some part by `opencode` using
`gpt-6.1-sol` (`openai`))
2026-10-21 14:23:33 -04:00
Gud Boi bf2bb1c9a6 Mv task UID formatting into `devx.pformat`
Add `pformat_task()` beside `pformat_task_uid()` so diagnostics can
inspect an explicit endpoint, sibling or nursery-owning `trio.Task`.
Keep the current-task default and decimal head/tail identity format.

Preserve the `tractor.log` entry point with lazy forwarding, avoiding
eager debugger initialization during logging imports. Move the
existing formatting TODOs with the implementation.

Exercise real parent/child tasks without an actor, including coroutine
identity, nursery ownership and equivalent log/tooling UID output.

Prompt-IO: ai/prompt-io/20261021T145344Z_0553fcf8_task_format_prompt_io.md
Prompt-IO: ai/prompt-io/20261021T151133Z_0553fcf8_uid_move_prompt_io.md

(this patch was generated in some part by `opencode` using
`gpt-6.1-sol` (`openai`))
2026-10-21 11:29:32 -04:00
Gud Boi 0553fcf88c Reproduce `@context` final-`Return` disconnect cascade
A chart disconnect can leave its context endpoint returning over
a closed channel. Exercise the real `_rpc._invoke()` dispatcher
with loopback TCP and a sibling service to isolate the resulting
`TransportClosed` nursery cancellation.

Deats,
- Order caller close, callee EOF and endpoint completion with
  events instead of timing sleeps or injected transport errors.
- Keep an ordinary-RPC control for existing response hardening.
- Capture the collateral task, actor/PID, nursery, cancel scope
  and cancellation source in a test-generated exception note.
- Record the live IB diagnosis and generated-test provenance.

The ordinary-RPC control passes; the context case remains red
with `ClosedResourceError` wrapped as `TransportClosed`. This
commit establishes the regression before runtime remediation.

Prompt-IO: ai/prompt-io/20261019T140536Z_9a0f220f_final_return_prompt_io.md

(this patch was generated in some part by `opencode` using
`gpt-6.1-sol` (`openai`))
2026-10-19 10:51:17 -04:00
Gud Boi 9a0f220fd5 Fix built-in level names in `at_least_level()`
The `CUSTOM_LEVELS`-only lookup raised `KeyError` for built-in
log levels such as 'INFO'. Resolve other registered names through
`logging.getLevelNamesMapping()` only when the custom lookup
misses, without eagerly evaluating the fallback on custom hits.

Deats,
- keep custom precedence, case-insensitive names and `NOTSET=0`;
- preserve integer inputs and unknown-name `KeyError` behavior;
- keep logger-local and global `isEnabledFor()` disable controls;
- advertise integer inputs on the adapter's `at_least_level()`;
- add 62 raw/adapted regressions with explicit threshold examples
  and fixture docs distinguishing instance and global log state.

The logging suite passed all 70 cases; the original Piker consumer
probe passed 8/8 with its string-level feed guard retained.

Prompt-IO: ai/prompt-io/20261011T053737Z_a966928d_prompt_io.md

(this patch was generated in some part by `opencode` using
`gpt-6.1-sol` (`openai`))
2026-10-16 20:31:49 -04:00
Gud Boi 2b4b65e717 Normalize `Registrar.delete_addr()` declarations
Match deletion inputs to the canonical addresses stored by
`register_actor()`. Legacy TCP/UDS pairs and the `uds` alias
otherwise miss their entry and leave it registered.

Cover legacy and tagged declarations as tuples and msgpack-style
lists, checking the returned uid and removal of the final entry.

Review: PR #505 (goodboy)
https://github.com/goodboy/tractor/pull/505#pullrequestreview-5408224830

(this patch was generated in some part by `opencode` using `gpt-6-astra` (`openai`))
2026-10-04 20:37:47 -04:00
Gud Boi 9d1e476d04 Canonicalize addrs in `Registrar.register_actor()`
Store the validated `waddr.unwrap()` result so legacy and tagged
declarations share one registry identity. This lets stale-entry
eviction replace an older actor which used the compatibility form.

Cover TCP and UDS registrations crossing from legacy to tagged addrs.

Review: PR #505 (goodboy)
https://github.com/goodboy/tractor/pull/505#pullrequestreview-5094473850

(this patch was generated in some part by `opencode` using `gpt-5.6-sol` (`openai`))
2026-09-02 16:57:43 -04:00
Gud Boi ca436aa9ec Wrap default `Server.listen_on()` addrs
Normalize implicit listener declarations through `wrap_address()`,
just like caller-supplied addrs. Tagged defaults otherwise reach
endpoint reflection as plain tuples and fail before socket startup.

Add a TCP/UDS regression which omits `accept_addrs` and proves
each generated ep holds the expected concrete `Address`.

(this patch was generated in some part by `opencode` using `gpt-5.6-sol` (`openai`))
2026-09-01 23:07:53 -04:00
Gud Boi 0d95a9e63c Emit canonical tagged addresses
- Make `TCPAddress.unwrap()` emit `('tcp', host, port)` and
  `UDSAddress.unwrap()` emit `('unix', path)` while retaining the
  compatibility readers from the preceding change.

- Pass concrete TCP fields to Trio, compose multiaddrs from tagged
  values, and let `SpawnSpec` carry protocol-specific tuple shapes
  for validation by `wrap_address()`.

- Compare runtime, registry, bind, and tunnel addresses through
  canonical serialized forms and cover both TCP and UDS operation.

Prompt-IO: ai/prompt-io/opencode/20260820T033108Z_ba07e09d_prompt_io.md

(this patch was generated in some part by `opencode` using `gpt-5.6-sol` (`openai`))
2026-09-01 15:47:28 -04:00
Gud Boi c857ad09f2 Decode tagged transport addresses
- Define canonical `tcp` and `unix` tuple shapes while retaining
  legacy pair aliases as the emitted `UnwrappedAddress`.

- Dispatch tagged tuple/list payloads explicitly, accept `uds` as a
  Unix input alias, and preserve legacy TCP, UDS, and native IPv6
  readers.

- Cover tag aliases, msgpack-style lists, legacy payloads, and IPv6
  socket addresses before switching writers.

Prompt-IO: ai/prompt-io/opencode/20260820T033107Z_ba07e09d_prompt_io.md

(this patch was generated in some part by `opencode` using `gpt-5.6-sol` (`openai`))
2026-09-01 15:47:28 -04:00
Gud Boi ffb87d351e Peel tunnels before `Endpoint` binding
Carry tunnel declarations through listener configuration, then strip
them immediately before constructing transport endpoints.

Also allocate random listener addresses from a contacted registry's
overlay, and prove a real TCP listener never stores the wrapper while
the source declaration retains its bindspace metadata.

Prompt-IO: ai/prompt-io/opencode/20260819T213145Z_f81fc5e5_prompt_io.md

(this patch was generated in some part by `opencode` using `gpt-5.6-sol` (`openai`))
2026-09-01 15:21:18 -04:00
Gud Boi b517f7f523 Peel tunnels before `Channel` connects
Retain tunnel annotations through address declaration, then hand only
the bindable overlay to exact-type transport lookup and dialing.

Broaden `Channel.from_addr()` and `_connect_chan()` inputs accordingly,
and cover plain plus tunnelled TCP dispatch arguments.

Prompt-IO: ai/prompt-io/opencode/20260819T213144Z_f81fc5e5_prompt_io.md

(this patch was generated in some part by `opencode` using `gpt-5.6-sol` (`openai`))
2026-09-01 15:21:18 -04:00
Gud Boi a26a0a48b6 Support nested `wg` maddrs
Teach discovery to preserve WireGuard bearer and identity metadata
around a bindable TCP overlay.

Deats,
- encode `wg(8)` keys as strict 32-byte multibase values
- peel nested stacks with `Multiaddr.decapsulate_code()` and compose
  them with `.encapsulate()` instead of splitting strings
- integrate wrappers with `parse_maddr()`, `mk_maddr()`,
  `wrap_address()` and `parse_endpoints()`
- pin the unreleased py-multiaddr#108 codec in package metadata
- cover exact round trips, nesting, bad grammar and missing codecs

Prompt-IO: ai/prompt-io/opencode/20260818T075031Z_dd02c7c0_prompt_io.md

(this patch was generated in some part by `opencode` using `gpt-5.6-sol` (`openai`))
2026-09-01 13:31:56 -04:00
Gud Boi cc1bb333e6 Add `TunnelledAddress` wrapper primitives
Introduce the first layer-A address type from the `wg` bindspace
plan without treating a transparent tunnel as a `MsgTransport`.

Deats,
- add frozen `WGTunnelSpec` and `TunnelledAddress` structs which
  delegate proto identity, bindspace, validity and wire
  serialization to their overlay
- add `strip_tunnels()` and `tunnels_of()` for nested wrappers
- recognize wrappers in `is_wrapped_addr()` while keeping them out
  of `_address_types`
- cover delegation, namespace fallback and nested peeling semantics

Also,
- widen `Address.namespace` ids for named netns
- export the new discovery API
- clarify that tractor's layer-C bindspace lifecycle may provision
  the kernel-owned bearer without making it a `MsgTransport`

Prompt-IO: ai/prompt-io/opencode/20260818T021729Z_d9a6e2e9_prompt_io.md

(this patch was generated in some part by `opencode` using `gpt-5.6-sol` (`openai`))
2026-09-01 13:31:56 -04:00
Gud Boi 6429e6a515 Reject concurrent `BroadcastReceiver.receive()` calls
Two tasks could receive through one `BroadcastReceiver` and race its
single `_wait_scope`. A later call could replace the scope, leaving
the first blocked through `.aclose()`.

Track the active `trio.lowlevel.Task` per receiver and reject an
overlapping call with `trio.BusyResourceError`. Include the owner's
task name and repr in the error, then clear ownership in `finally`.

Define each receiver as one logical subscription cursor. Concurrent
consumers must use separate `.subscribe()` receivers so each retains
an independent cursor and lag policy. Strict mode reads every retained
value or raises `Lagged`; `raise_on_lag=False` explicitly permits
drops.

Cover the regression with a checkpoint loop synchronized on
`recv_ready`; explain the interleaving inline, assert the error names
the active consumer, then release the source and prove the original
receive remains usable.

Prompt-IO: ai/prompt-io/opencode/20260828T014247Z_d06ee34b_prompt_io.md

(this patch was generated in some part by `opencode` using `gpt-5.6-sol` (`openai`))
2026-08-27 22:11:55 -04:00
Gud Boi d06ee34b83 Clean broadcast cancellation diagnostics
Bound `BroadcastState.cancelled` entries to receiver progress,
terminal state and resource lifetime instead of retaining completed
`Task`s indefinitely.

Deats,
- make EOC durable so awakened peers never re-enter a closed source.
- close root broadcasters during explicit `MsgStream` and
  `LinkedTaskChannel` teardown without re-entrant EOC closure or
  breaking `MsgStream.aclose()` overrides.
- reject non-positive fan-out retention capacity before constructing
  an unusable zero-length queue.
- cover child/root cancellation cleanup, terminal peer wakeups,
  wrapper teardown, subclass compatibility and zero-buffer rejection.

Prompt-IO: ai/prompt-io/opencode/20260813T181901Z_a2e0df4b_prompt_io.md

(this patch was generated in some part by `opencode` using `gpt-5.6-sol` (`openai`))
2026-08-27 21:18:00 -04:00
Gud Boi ff4391669b Expose stream subscriber lag policy
`MsgStream.subscribe()` and `LinkedTaskChannel.subscribe()` omitted
`BroadcastReceiver.raise_on_lag`, forcing downstream consumers to
mutate a private receiver attribute when overruns were acceptable.

Add `raise_on_lag` to both public wrappers. The first subscription
sets the irreversible root broadcaster's policy, while every child
selects its own strict or warn/drop/resume behavior independently.

Document both fan-out APIs. Cover policy forwarding plus real IPC
and infected-asyncio paths.

Prompt-IO: ai/prompt-io/opencode/20260812T213117Z_51185487_prompt_io.md

(this patch was generated in some part by `opencode` using `gpt-5.6-sol` (`openai`))
2026-08-27 21:18:00 -04:00
Gud Boi a5259a8ee4 Isolate `BroadcastReceiver.aclose()` wakeups
Closing any subscriber set the shared `recv_ready` event, even when
another receiver owned the source read. Waiting peers then looped
until an idle source produced another value.

Give each receiver private source-read and peer-wait cancellation
scopes. Closing a waiting peer interrupts only that peer. Closing
the source owner discards post-close source outcomes and then wakes
peers for a clean ownership handoff.

Keep outer task cancellation as `trio.Cancelled`; only explicit
receiver close maps either private scope's cancellation to
`ClosedResourceError`. Assert that scope cancellation implies the
receiver is closed and document the source-owner key check.

Prompt-IO: ai/prompt-io/opencode/20260812T150027Z_c2a6ccef_prompt_io.md

(this patch was generated in some part by `opencode` using `gpt-5.6-sol` (`openai`))
2026-08-27 21:18:00 -04:00
Gud Boi d6da0e7a22 Wake broadcast peers on shared receive failures
Only `EndOfChannel` and direct cancellation woke tasks waiting
behind the subscriber which owned the underlying receive. Any other
failure cleared `BroadcastState.recv_ready` while peers remained
blocked on its unreachable event.

Publish ordinary receive exceptions as terminal broadcast state.
The owner keeps the original failure while peers drain retained
values and then raise `BroadcastReceiveError` from that cause. Also
wake peers on process-control exits without retaining them as state.

Document the public owner/peer contract and cover current, late and
control-flow subscribers with deterministic bounded regressions.

Prompt-IO: ai/prompt-io/opencode/20260812T030608Z_1095e7f7_prompt_io.md

(this patch was generated in some part by `opencode` using `gpt-5.6-sol` (`openai`))
2026-08-27 21:18:00 -04:00
Gud Boi 5afd834732 Fix `BroadcastState.statistics()` queue counts
`BroadcastState.subs` stores each receiver's next unread deque
index, but `.statistics()` exposed that index as a queue length. A
caught-up receiver looked correct by accident while every queued
count was one short.

Convert cursors to retained, receivable counts and clamp lagged
receivers to the current queue length. Also avoid deprecated
`trio.Event` truthiness when reporting waiter counts.

Cover caught-up, queued, lagged and real-event states using actual
broadcast sends and receives.

Prompt-IO: ai/prompt-io/opencode/20260812T012324Z_06c4af17_prompt_io.md

(this patch was generated in some part by `opencode` using `gpt-5.6-sol` (`openai`))
2026-08-27 21:18:00 -04:00
Gud Boi 206b0354c5 Fix `BroadcastReceiver` lag counts
`BroadcastReceiver.receive_nowait()` treated `seq` as a deque index
but subtracted `BroadcastState.maxlen` without counting the first
invalid index. A one-slot queue thus claimed it dropped zero values
after its subscriber missed one.

Include that first displaced value in the count. Preserve the
existing Tokio-style reset to the oldest retained item.

Also, cover exact loss reporting and recovery for one- and
three-slot retention windows.

Prompt-IO: ai/prompt-io/opencode/20260811T233833Z_7cbd64ee_prompt_io.md

(this patch was generated in some part by `opencode` using `gpt-5.6-sol` (`openai`))
2026-08-27 21:18:00 -04:00
Gud Boi 1ad6281348 Clarify recursive one-shot spawning test
Handle the child RPC leaf as an early return, then leave the root-only
runtime and recursive one-shot flow unindented. Rename the helper for
that behavior and document why RPC namespace lookup requires it to
remain import-addressable at module scope.

Review: PR #484 (goodboy)
https://github.com/goodboy/tractor/pull/484#discussion_r3859956375

(this patch was generated in some part by `opencode` using `gpt-5.6-sol` (`openai`))
2026-08-26 18:27:11 -04:00
Gud Boi b3e8ed18d1 Link asyncio cancellation regression provenance
Point `test_tractor_cancels_aio()`'s anti-hang guard at the original
fix commit and the detailed ria-removal analysis plan.

Review: PR #484 (goodboy)
https://github.com/goodboy/tractor/pull/484#discussion_r3859956368

(this patch was generated in some part by `opencode` using `gpt-5.6-sol` (`openai`))
2026-08-26 17:00:54 -04:00
Gud Boi d5e66dffaa Restore high-fanout startup cancellation stress
Release 25 concurrent `to_actor.run()` callers through one local
barrier so their implicit child starts race the first remote error.
Exercise one, five and 25 deterministically placed errorers without
overloading actor-nursery internals.

Validate bounded cancel-on-first teardown, only boxed assertion
relays and empty actor-nursery child/reap maps across Trio and
multiprocessing backends. Clarify this successor's distinction from
`test_nested_multierrors()`, diagram flat-pool error propagation and
align the nearby expected-error comment with its handler.

Review: PR #484 (goodboy)
https://github.com/goodboy/tractor/pull/484#discussion_r3858891778

(this patch was generated in some part by `opencode` using `gpt-5.6-sol` (`openai`))
2026-08-26 17:00:44 -04:00
Gud Boi cf56f33be6 Correct cancellation and asyncio test prose
Fix five spelling errors in comments and failure text touched by the
one-shot migration: one `propagate`, one `Daemon` and three `directly`
corrections.

Review: PR #484 (GitHub Copilot)
https://github.com/goodboy/tractor/pull/484#pullrequestreview-5025348921

(this patch was generated in some part by `opencode` using `gpt-5.6-sol` (`openai`))
2026-08-25 23:35:43 -04:00
Gud Boi dfdaf2b1c1 Validate `test_multierror()` relay shapes
Inspect the exception emitted by the concurrent error fan-out instead
of accepting any `RemoteActorError` or `BaseExceptionGroup`.

Require each non-cancellation leaf to box `AssertionError`, allow one
or two relays for cancel-on-first timing and require both child relays
when no cancellation leaf accompanies the group.

Review: PR #484 (OpenCode)
https://github.com/goodboy/tractor/pull/484#pullrequestreview-5025383596

(this patch was generated in some part by `opencode` using `gpt-5.6-sol` (`openai`))
2026-08-25 23:00:13 -04:00
Gud Boi 37eeb7bab6 Exercise active RPC tasks in SIGINT test
Open one linked sleeping context in each daemon and wait for every
`StartAck` before `test_cancel_via_SIGINT_other_task()` reports
startup.

This restores the legacy test's active remote-task cancellation
target instead of proving SIGINT teardown only against idle actor
runtimes.

Review: PR #484 (OpenCode)
https://github.com/goodboy/tractor/pull/484#pullrequestreview-5025383596

(this patch was generated in some part by `opencode` using `gpt-5.6-sol` (`openai`))
2026-08-25 22:53:18 -04:00
Gud Boi 1d59f1963c Propagate unexpected `@pub` subscriber errors
Give each background subscriber runner its own teardown event and
suppress only `ContextCancelled` relayed by the root actor after that
portal's explicit cancellation begins.

Let generic remote errors, foreign cancellation and cancellation
before teardown escape the local task nursery so the test cannot pass
after a subscriber fails unexpectedly.

Review: PR #484 (GitHub Copilot and OpenCode)
https://github.com/goodboy/tractor/pull/484#discussion_r3858426546

(this patch was generated in some part by `opencode` using `gpt-5.6-sol` (`openai`))
2026-08-25 20:38:57 -04:00
Gud Boi dd3e7482bf Wait for active pub/sub cancellation targets
Replace `test_dynamic_pub_sub()`'s fixed startup sleep with an RPC
activity probe in the publisher actor. Track the publisher task and
wait until every launched consumer has installed its first
subscription before raising the user cancellation exception.

This keeps slow spawn backends from passing the regression by
cancelling actors which never reached the streaming workload.

Review: PR #484 (OpenCode)
https://github.com/goodboy/tractor/pull/484#pullrequestreview-5025383596

(this patch was generated in some part by `opencode` using `gpt-5.6-sol` (`openai`))
2026-08-25 20:23:53 -04:00
Gud Boi 09e78ad087 Bind named `to_actor.run()` inputs with partials
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`))
2026-08-25 19:20:43 -04:00
Gud Boi e7f5968850 Name every `ActorNursery` binding `an` in tests/examples
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
2026-08-25 19:19:28 -04:00
Gud Boi 551090d129 Fix mutual-rendezvous premature-reap race (#477)
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
2026-08-25 19:19:23 -04:00
Gud Boi bb0a9b3c93 Remove `run_in_actor()` + the ria reap cluster
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
2026-08-25 19:16:32 -04:00
Gud Boi 4eca8d7a10 Port `test_dynamic_pub_sub` off `run_in_actor`
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
2026-08-25 17:57:12 -04:00
Gud Boi 3fcc4ee713 Port SIGINT + sync-sleep cancel tests off `run_in_actor`
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
2026-08-25 17:57:12 -04:00
Gud Boi de3dc2ded0 Port `test_nested_multierrors` off `run_in_actor`
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
2026-08-25 17:57:12 -04:00
Gud Boi f81f7d40c3 Fix unbound `timeout` under non-trio/MTF backends
`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
2026-08-25 17:57:12 -04:00
Gud Boi f4f3034555 Port `test_some_cancels_all` off `run_in_actor`
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
2026-08-25 17:57:12 -04:00
Gud Boi e824e6b768 Port `test_cancellation` multierror cluster off `run_in_actor`
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
2026-08-25 17:57:12 -04:00
Gud Boi 0835963fa2 Port `test_registrar` off `run_in_actor`
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
2026-08-25 17:57:12 -04:00