Use `Aid` keys for child reap state

The fresh reap-coordination maps still used legacy `.uid` tuples even
though process monitors and channels carry complete `Aid` identities.
This extended the legacy key format into new private state.

Key both reap maps by `Aid` and derive `.uid` only when accessing the
existing `_children` map. UUID-based `Aid` hashing lets the subactor
and decoded channel identities resolve the same synchronization state.

Update registration tests to exercise the full identity keys.

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

Prompt-IO: ai/prompt-io/opencode/20260824T233957Z_5327b25e_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 21:56:00 -04:00
parent 5327b25e1b
commit e42ecb559d
5 changed files with 87 additions and 18 deletions

View File

@ -0,0 +1,36 @@
---
model: openai/gpt-5.6-sol
service: opencode
session: d9d7df2c-7044-463f-8768-ec024718eac9
timestamp: 2026-08-24T23:39:57Z
git_ref: 5327b25e
scope: code
substantive: true
raw_file: 20260824T233957Z_5327b25e_prompt_io.raw.md
---
## Prompt
Continue PR #481 review remediation after committing the debugger-state
sampler. The human selected "keep" for the paired review request to use
`Aid` objects as keys in the newly added reap-coordination maps.
## Response summary
Migrate only `ActorNursery._child_reap_requests` and
`ActorNursery._child_reaped` to `Aid` keys. Preserve the legacy
`ActorNursery._children` `.uid` key and pass full actor identities
through the narrow process-monitor bookkeeping path.
## Files changed
- `tractor/runtime/_supervise.py` - key fresh reap maps by `Aid`.
- `tractor/spawn/_spawn.py` - pass `Aid` into completed-reap cleanup.
- `tests/test_to_actor.py` - exercise `Aid` registration keys.
## Human edits
The human explicitly selected "keep" after reviewing the scope,
performance, and mutability tradeoffs. The human retained the legacy
tuple key for `_children` and accepted `Aid` for only the two fresh
private mappings. No direct source-line edits were made by the human.

View File

@ -0,0 +1,27 @@
---
model: openai/gpt-5.6-sol
service: opencode
timestamp: 2026-08-24T23:39:57Z
git_ref: 5327b25e
diff_cmd: git diff HEAD~1..HEAD
---
Implement the approved PR #481 review update which uses `Aid` keys for
the two fresh `ActorNursery` reap-coordination maps while preserving the
legacy `.uid` key for `ActorNursery._children`.
> `git diff HEAD~1..HEAD -- tractor/runtime/_supervise.py`
Type and access `_child_reap_requests` and `_child_reaped` by `Aid`.
Pass full actor identities through registration, cancellation, and
completed-reap bookkeeping, deriving `.uid` only for `_children`.
> `git diff HEAD~1..HEAD -- tractor/spawn/_spawn.py`
Forward `subactor.aid` when publishing completed process teardown.
> `git diff HEAD~1..HEAD -- tests/test_to_actor.py`
Update deterministic registration tests to exercise `Aid` map keys.
Run focused registration/reaping tests and the full `to_actor` suite.

View File

@ -431,8 +431,8 @@ def test_late_child_registration_observes_cancel():
proc, proc,
None, None,
) )
assert an._child_reap_requests[aid.uid] is reap_request assert an._child_reap_requests[aid] is reap_request
assert an._child_reaped[aid.uid] is reaped assert an._child_reaped[aid] is reaped
def test_mp_late_registration_never_starts_process( def test_mp_late_registration_never_starts_process(
@ -521,9 +521,11 @@ def test_late_child_reap_registration_is_released():
an._child_reaped = {} an._child_reaped = {}
an._join_procs.set() an._join_procs.set()
reap_request, _ = an._register_child_reap( aid = tractor.msg.Aid(
('late_child', 'uid'), name='late_child',
uuid='uid',
) )
reap_request, _ = an._register_child_reap(aid)
assert reap_request.is_set() assert reap_request.is_set()

View File

@ -46,6 +46,7 @@ from ..log import (
get_logger, get_logger,
get_loglevel, get_loglevel,
) )
from ..msg import Aid
from ._runtime import Actor from ._runtime import Actor
from ._portal import Portal from ._portal import Portal
from ..trionics import ( from ..trionics import (
@ -246,11 +247,11 @@ class ActorNursery:
self._join_procs = trio.Event() self._join_procs = trio.Event()
self._child_reap_requests: dict[ self._child_reap_requests: dict[
tuple[str, str], Aid,
trio.Event, trio.Event,
] = {} ] = {}
self._child_reaped: dict[ self._child_reaped: dict[
tuple[str, str], Aid,
trio.Event, trio.Event,
] = {} ] = {}
self._at_least_one_child_in_debug: bool = False self._at_least_one_child_in_debug: bool = False
@ -319,7 +320,7 @@ class ActorNursery:
def _register_child_reap( def _register_child_reap(
self, self,
uid: tuple[str, str], aid: Aid,
) -> tuple[trio.Event, trio.Event]: ) -> tuple[trio.Event, trio.Event]:
''' '''
Register a child monitor's process-reap events. Register a child monitor's process-reap events.
@ -327,8 +328,8 @@ class ActorNursery:
''' '''
reap_request = trio.Event() reap_request = trio.Event()
reaped = trio.Event() reaped = trio.Event()
self._child_reap_requests[uid] = reap_request self._child_reap_requests[aid] = reap_request
self._child_reaped[uid] = reaped self._child_reaped[aid] = reaped
if self._join_procs.is_set(): if self._join_procs.is_set():
reap_request.set() reap_request.set()
return reap_request, reaped return reap_request, reaped
@ -343,13 +344,14 @@ class ActorNursery:
Atomically publish one child and its reap coordination. Atomically publish one child and its reap coordination.
''' '''
uid: tuple[str, str] = subactor.aid.uid aid: Aid = subactor.aid
uid: tuple[str, str] = aid.uid
self._children[uid] = ( self._children[uid] = (
subactor, subactor,
proc, proc,
portal, portal,
) )
reap_request, reaped = self._register_child_reap(uid) reap_request, reaped = self._register_child_reap(aid)
return ( return (
reap_request, reap_request,
reaped, reaped,
@ -369,18 +371,19 @@ class ActorNursery:
def _mark_child_reaped( def _mark_child_reaped(
self, self,
uid: tuple[str, str], aid: Aid,
) -> None: ) -> None:
''' '''
Publish completed child-process teardown to its waiter. Publish completed child-process teardown to its waiter.
''' '''
uid: tuple[str, str] = aid.uid
self._children.pop(uid, None) self._children.pop(uid, None)
reap_request: trio.Event|None = ( reap_request: trio.Event|None = (
self._child_reap_requests.pop(uid, None) self._child_reap_requests.pop(aid, None)
) )
reaped: trio.Event|None = self._child_reaped.pop( reaped: trio.Event|None = self._child_reaped.pop(
uid, aid,
None, None,
) )
assert ( assert (
@ -399,14 +402,15 @@ class ActorNursery:
Cancel, join and unregister one nursery-owned child. Cancel, join and unregister one nursery-owned child.
''' '''
uid: tuple[str, str] = portal.channel.aid.uid aid: Aid = portal.channel.aid
uid: tuple[str, str] = aid.uid
child_entry = self._children.get(uid) child_entry = self._children.get(uid)
if child_entry is None: if child_entry is None:
return return
subactor, proc, _ = child_entry subactor, proc, _ = child_entry
reap_request: trio.Event = self._child_reap_requests[uid] reap_request: trio.Event = self._child_reap_requests[aid]
reaped: trio.Event = self._child_reaped[uid] reaped: trio.Event = self._child_reaped[aid]
with trio.CancelScope(shield=True): with trio.CancelScope(shield=True):
try: try:

View File

@ -460,7 +460,7 @@ async def new_proc(
proc_kwargs=proc_kwargs proc_kwargs=proc_kwargs
) )
finally: finally:
actor_nursery._mark_child_reaped(subactor.aid.uid) actor_nursery._mark_child_reaped(subactor.aid)
# NOTE: bottom-of-module to avoid a circular import since the # NOTE: bottom-of-module to avoid a circular import since the