Clarify provisional child registration

Child monitors register before their IPC handshake so cancellation
owns every started process, but the bare `None` portal arg obscured
that `Portal(chan)` replaces the provisional entry after connection.

Document that transition and name all `_register_child()` args in both
spawn backends. Replace the MP test's positional-only lambda with a
signature-accurate fake which asserts the provisional portal state.

Caught-during: review remediation
Found-via: `/run-tests` test_mp_late_registration_never_starts_process

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

Prompt-IO: ai/prompt-io/opencode/20260825T015742Z_e42ecb55_prompt_io.md

(this patch was generated in some part by `opencode` using `gpt-5.6-sol` (`openai`))
wkt/to_actor_subpkg
Gud Boi 2026-08-24 22:11:15 -04:00
parent e42ecb559d
commit ce430fca64
5 changed files with 89 additions and 11 deletions

View File

@ -0,0 +1,38 @@
---
model: openai/gpt-5.6-sol
service: opencode
session: d9d7df2c-7044-463f-8768-ec024718eac9
timestamp: 2026-08-25T01:57:42Z
git_ref: e42ecb55
scope: code
substantive: true
raw_file: 20260825T015742Z_e42ecb55_prompt_io.raw.md
---
## Prompt
Continue PR #481 review remediation after committing the `Aid` reap-map
migration. The human selected "keep" for comments explaining why both
spawn backends provisionally register children with `portal=None`.
## Response summary
Document that a child has no `Portal` until its IPC handshake yields a
`Channel`, and make `portal=None` explicit at both registration calls.
Identify the later replacement of each provisional entry with
`Portal(chan)`. Update the MP registration test double to accept and
assert the explicit provisional portal state. Name every registration
argument consistently in both backends.
## Files changed
- `tractor/spawn/_mp.py` - clarify provisional MP registration.
- `tractor/spawn/_trio.py` - clarify provisional Trio registration.
- `tests/test_to_actor.py` - model explicit provisional registration.
## Human edits
The human explicitly selected "keep" after receiving keep/defer/drop
options for this paired clarification. During local review, the human
then requested that `subactor` and `proc` also be passed by name in both
backend calls. No direct source-line edits were made by the human.

View File

@ -0,0 +1,21 @@
---
model: openai/gpt-5.6-sol
service: opencode
timestamp: 2026-08-25T01:57:42Z
git_ref: e42ecb55
diff_cmd: git diff HEAD~1..HEAD
---
Implement the approved PR #481 clarification for provisional child
registration in both process-spawn backends.
> `git diff HEAD~1..HEAD -- tractor/spawn/_mp.py`
> `git diff HEAD~1..HEAD -- tractor/spawn/_trio.py`
Explain that `portal=None` is provisional because no `Portal` can exist
until the child completes its IPC handshake and returns a `Channel`.
Use an explicit keyword argument and identify the later replacement with
`Portal(chan)`.
Run lint and the full `to_actor` runtime suite.

View File

@ -456,6 +456,24 @@ def test_mp_late_registration_never_starts_process(
process = FakeProcess()
def register_child(
subactor: object,
proc: object,
portal: object|None,
) -> tuple[trio.Event, trio.Event, bool]:
'''
Simulate provisional MP registration before child startup.
'''
assert subactor
assert proc is process
assert portal is None
return (
trio.Event(),
trio.Event(),
True,
)
class FakeContext:
def get_start_method(self) -> str:
return 'spawn'
@ -465,11 +483,7 @@ def test_mp_late_registration_never_starts_process(
return process
nursery = SimpleNamespace(
_register_child=lambda *args: (
trio.Event(),
trio.Event(),
True,
),
_register_child=register_child,
)
subactor = SimpleNamespace(
aid=tractor.msg.Aid(

View File

@ -141,14 +141,16 @@ async def mp_proc(
# `multiprocessing` only (since no async interface): publish the
# process and its reap coordination before start so cancellation
# can own every subsequently started child.
# No `Portal` exists until the IPC handshake returns `chan`.
# Replace this provisional entry with `Portal(chan)` below.
(
reap_request,
_,
cancel_during_registration,
) = actor_nursery._register_child(
subactor,
proc,
None,
subactor=subactor,
proc=proc,
portal=None,
)
if cancel_during_registration:
raise RuntimeError(

View File

@ -130,14 +130,17 @@ async def trio_proc(
f' |_{proc}\n'
)
# No `Portal` exists until the IPC handshake returns
# `chan`. Replace this provisional entry with
# `Portal(chan)` below.
(
reap_request,
_,
cancel_during_registration,
) = actor_nursery._register_child(
subactor,
proc,
None,
subactor=subactor,
proc=proc,
portal=None,
)
if cancel_during_registration:
cancelled_during_spawn = True