Compare commits
168 Commits
05432e4d00
...
2ca8c570ec
| Author | SHA1 | Date |
|---|---|---|
|
|
2ca8c570ec | |
|
|
fb6d81d397 | |
|
|
9ae7cd866a | |
|
|
e7234c3a03 | |
|
|
b8b89268fd | |
|
|
932722b325 | |
|
|
cdd5591428 | |
|
|
7da561ecc3 | |
|
|
9dfdd54e0f | |
|
|
d68b101717 | |
|
|
02f1e9f72c | |
|
|
a625a2757d | |
|
|
1569788ea6 | |
|
|
349ab98361 | |
|
|
06451f2ea6 | |
|
|
f27db92c62 | |
|
|
6ab8bee0ec | |
|
|
3f78e3de4c | |
|
|
7d0b84e1de | |
|
|
0122a0dfff | |
|
|
1de168ec47 | |
|
|
df04e6f896 | |
|
|
3c474837c2 | |
|
|
ddee5b46ce | |
|
|
0b6612450f | |
|
|
e40c39dadc | |
|
|
85a44588cf | |
|
|
647a856ee7 | |
|
|
a5ee4cfd4f | |
|
|
7d66510cdb | |
|
|
19f807ab18 | |
|
|
198f2985ba | |
|
|
23bb74bad6 | |
|
|
bbf93eab4e | |
|
|
521d0485e3 | |
|
|
4be23acce2 | |
|
|
69f3aa807c | |
|
|
d852b11323 | |
|
|
a1e5204ef9 | |
|
|
40590c64dc | |
|
|
ae10a3ae56 | |
|
|
90c4954cb9 | |
|
|
0664731372 | |
|
|
e90f2f224a | |
|
|
1ad6281348 | |
|
|
b3e8ed18d1 | |
|
|
d5e66dffaa | |
|
|
cf56f33be6 | |
|
|
8739c5fadb | |
|
|
dfdaf2b1c1 | |
|
|
37eeb7bab6 | |
|
|
1d59f1963c | |
|
|
dd3e7482bf | |
|
|
266073cb69 | |
|
|
4134726ec4 | |
|
|
09e78ad087 | |
|
|
dd91195377 | |
|
|
e7f5968850 | |
|
|
551090d129 | |
|
|
bb0a9b3c93 | |
|
|
5d70959a2b | |
|
|
e8f636ddbb | |
|
|
4eca8d7a10 | |
|
|
3fcc4ee713 | |
|
|
de3dc2ded0 | |
|
|
f81f7d40c3 | |
|
|
f4f3034555 | |
|
|
5420b13482 | |
|
|
e824e6b768 | |
|
|
0835963fa2 | |
|
|
4e4191701c | |
|
|
19d0e9cb78 | |
|
|
8df118dde8 | |
|
|
e328b4d729 | |
|
|
3a6bff0b6e | |
|
|
61ad5bd158 | |
|
|
34e638863e | |
|
|
d6da42984f | |
|
|
916655996c | |
|
|
601d92eb4a | |
|
|
b7298e6507 | |
|
|
5f49544cf3 | |
|
|
74cbee6c69 | |
|
|
f136063239 | |
|
|
80ce54aeb1 | |
|
|
98bc642e72 | |
|
|
48844aa4ac | |
|
|
760abc8268 | |
|
|
bf46f5cc5e | |
|
|
773370a423 | |
|
|
a99a4c9353 | |
|
|
b58889f625 | |
|
|
0a580df63d | |
|
|
617ca1de43 | |
|
|
9373e9434d | |
|
|
ce430fca64 | |
|
|
e42ecb559d | |
|
|
5327b25e1b | |
|
|
2f86dd1a33 | |
|
|
88d538e3a6 | |
|
|
ce38cb6f0e | |
|
|
5c4859860a | |
|
|
23e26b5b32 | |
|
|
a849161fa5 | |
|
|
086962ca36 | |
|
|
1029b81dfc | |
|
|
40d2d9942e | |
|
|
04123019b7 | |
|
|
643e1c861b | |
|
|
57febf045d | |
|
|
20e89334c0 | |
|
|
fe0a724d10 | |
|
|
99be161ec0 | |
|
|
c294a812c3 | |
|
|
17d7341334 | |
|
|
ecf89bfaac | |
|
|
49213d170e | |
|
|
b69a8667d4 | |
|
|
3aaa3bccbd | |
|
|
3b0b37dcb4 | |
|
|
3690e43abc | |
|
|
33d74da8c2 | |
|
|
9afda1c61d | |
|
|
a3d65a6b3d | |
|
|
4e27dcde48 | |
|
|
93322405a1 | |
|
|
359fe75ced | |
|
|
40be587ce2 | |
|
|
ca4582b003 | |
|
|
75385e448d | |
|
|
34d81adef3 | |
|
|
1691be96fd | |
|
|
36ad1f3dd0 | |
|
|
72441124c0 | |
|
|
0cbb850645 | |
|
|
8f0df0ff6f | |
|
|
f75c9cdeab | |
|
|
75cda1933c | |
|
|
f454cefe56 | |
|
|
d0cc06815f | |
|
|
ebf2258b4f | |
|
|
ac61d7a5bf | |
|
|
584ea4e9ad | |
|
|
16dd876b0c | |
|
|
1a9ce915f3 | |
|
|
0b63af020e | |
|
|
6e424d4696 | |
|
|
5724c0516a | |
|
|
0d6d7c2a63 | |
|
|
bd38204fde | |
|
|
340d506940 | |
|
|
64e820e18e | |
|
|
0a3b0efcc6 | |
|
|
634d914161 | |
|
|
451e0acf8a | |
|
|
3d02a8569e | |
|
|
bceca74eb7 | |
|
|
18faffcff7 | |
|
|
7554f90e59 | |
|
|
3705bbe594 | |
|
|
66ac7863b5 | |
|
|
089e158da9 | |
|
|
7987f6b8d7 | |
|
|
70c7e334a7 | |
|
|
62b729a106 | |
|
|
213a298aad | |
|
|
d1d3fc58bb | |
|
|
a2c8af558e |
|
|
@ -91,6 +91,10 @@ jobs:
|
||||||
name: '${{ matrix.os }} Python${{ matrix.python-version }} spawn_backend=${{ matrix.spawn_backend }} tpt_proto=${{ matrix.tpt_proto }}'
|
name: '${{ matrix.os }} Python${{ matrix.python-version }} spawn_backend=${{ matrix.spawn_backend }} tpt_proto=${{ matrix.tpt_proto }}'
|
||||||
timeout-minutes: 16
|
timeout-minutes: 16
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
|
# Windows support is nascent: its full test suite remains
|
||||||
|
# informational, while setup and the `import tractor` smoke below
|
||||||
|
# are hard signals. Promote the test step to required once the
|
||||||
|
# suite is green.
|
||||||
|
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
|
|
@ -98,6 +102,7 @@ jobs:
|
||||||
os: [
|
os: [
|
||||||
ubuntu-latest,
|
ubuntu-latest,
|
||||||
macos-latest,
|
macos-latest,
|
||||||
|
windows-latest,
|
||||||
]
|
]
|
||||||
python-version: [
|
python-version: [
|
||||||
'3.13',
|
'3.13',
|
||||||
|
|
@ -118,10 +123,10 @@ jobs:
|
||||||
'tcp',
|
'tcp',
|
||||||
'uds',
|
'uds',
|
||||||
]
|
]
|
||||||
# https://github.com/orgs/community/discussions/26253#discussioncomment-3250989
|
|
||||||
exclude:
|
exclude:
|
||||||
# don't do UDS run on macOS (for now)
|
# UDS is POSIX-only; Windows has no `AF_UNIX` so the
|
||||||
- os: macos-latest
|
# backend is intentionally unavailable there.
|
||||||
|
- os: windows-latest
|
||||||
tpt_proto: 'uds'
|
tpt_proto: 'uds'
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
|
|
@ -150,7 +155,18 @@ jobs:
|
||||||
- name: List deps tree
|
- name: List deps tree
|
||||||
run: uv tree
|
run: uv tree
|
||||||
|
|
||||||
|
# hard signal for the Windows import-safety fix: `import
|
||||||
|
# tractor` must succeed everywhere, and `HAS_UDS` reflects
|
||||||
|
# platform capability (False on Windows, True on POSIX).
|
||||||
|
- name: 'Smoke: import tractor'
|
||||||
|
run: uv run python -c "import sys; import tractor; from tractor.ipc._uds import HAS_UDS; assert sys.platform != 'win32' or not HAS_UDS; print('import tractor OK | HAS_UDS=', HAS_UDS)"
|
||||||
|
|
||||||
- name: Run tests
|
- name: Run tests
|
||||||
|
# Actor/PTY scheduling on macOS can fail a different
|
||||||
|
# timing-sensitive node between otherwise-green runs. Retry
|
||||||
|
# only that matrix leg; deterministic failures still fail
|
||||||
|
# after the final attempt.
|
||||||
|
continue-on-error: ${{ matrix.os == 'windows-latest' }}
|
||||||
run: >
|
run: >
|
||||||
uv run
|
uv run
|
||||||
pytest
|
pytest
|
||||||
|
|
@ -159,6 +175,8 @@ jobs:
|
||||||
--spawn-backend=${{ matrix.spawn_backend }}
|
--spawn-backend=${{ matrix.spawn_backend }}
|
||||||
--tpt-proto=${{ matrix.tpt_proto }}
|
--tpt-proto=${{ matrix.tpt_proto }}
|
||||||
--capture=fd
|
--capture=fd
|
||||||
|
--reruns=${{ matrix.os == 'macos-latest' && 2 || 0 }}
|
||||||
|
--reruns-delay=1
|
||||||
|
|
||||||
# XXX legacy NOTE XXX
|
# XXX legacy NOTE XXX
|
||||||
#
|
#
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,463 @@
|
||||||
|
# `_ria_nursery` removal plan (issue #477 follow-up)
|
||||||
|
|
||||||
|
Goal: drop the secondary "run-in-actor" spawn nursery (and
|
||||||
|
friends) from `ActorNursery`/spawn internals, now that
|
||||||
|
`tractor.to_actor.run()` delivers one-shot semantics purely on
|
||||||
|
the daemon-spawn + portal primitives.
|
||||||
|
|
||||||
|
## Verified machinery map (2026-07-02, wkt @ a34aaf98)
|
||||||
|
|
||||||
|
The entire mechanism is 4 files:
|
||||||
|
|
||||||
|
- `runtime/_supervise.py`
|
||||||
|
- `ActorNursery.__init__(.., ria_nursery, ..)` stores
|
||||||
|
`._ria_nursery` (:202, :238); sole read is
|
||||||
|
`run_in_actor()` passing `nursery=self._ria_nursery`
|
||||||
|
(:442) into `start_actor()`'s `nursery:
|
||||||
|
trio.Nursery|None` escape-hatch param (:305, :367).
|
||||||
|
- `._cancel_after_result_on_exit: set` (:244) marks ria
|
||||||
|
portals (:457).
|
||||||
|
- `_open_and_supervise_one_cancels_all_nursery()` nests
|
||||||
|
`da_nursery` (:609) around `ria_nursery` (:622); the
|
||||||
|
`finally:` at the ria->da boundary (:747-766) raises
|
||||||
|
collected `errors` (single exc or BEG).
|
||||||
|
- `runtime/_portal.py`
|
||||||
|
- `._expect_result_ctx` (:112) set by `_submit_for_result()`
|
||||||
|
(:142, sole caller `run_in_actor()`); consumed by
|
||||||
|
`wait_for_result()` (:167) + deprecated `result()` (:220).
|
||||||
|
The `None` branch (:184-196) returns the `NoResult`
|
||||||
|
sentinel (`_exceptions.py:1164`).
|
||||||
|
- `spawn/_spawn.py`
|
||||||
|
- `exhaust_portal()` (:129): awaits
|
||||||
|
`portal.wait_for_result()`, CATCHES+RETURNS any exc
|
||||||
|
(never raises).
|
||||||
|
- `cancel_on_completion()` (:177): `exhaust_portal()` ->
|
||||||
|
on exc-result stash `errors[uid] = result` (:203) ->
|
||||||
|
ALWAYS `portal.cancel_actor()` (:218).
|
||||||
|
- `spawn/_trio.py` (:195-222) + `spawn/_mp.py` (:187-213),
|
||||||
|
identical shape: after shielded
|
||||||
|
`await an._join_procs.wait()`, open a per-child local
|
||||||
|
nursery; IFF `portal in an._cancel_after_result_on_exit`
|
||||||
|
start `cancel_on_completion` alongside `soft_kill()`; when
|
||||||
|
`soft_kill` returns first, `nursery.cancel_scope.cancel()`
|
||||||
|
reaps the result-waiter.
|
||||||
|
|
||||||
|
## The load-bearing semantic (already-deferred errors)
|
||||||
|
|
||||||
|
Remote ria-child errors NEVER raise into `ria_nursery`:
|
||||||
|
|
||||||
|
1. reaper tasks only START after `_join_procs.set()` (block
|
||||||
|
exit or the inner error handler),
|
||||||
|
2. `exhaust_portal` swallows the exc into a return value,
|
||||||
|
3. `cancel_on_completion` stashes it in `errors` + cancels
|
||||||
|
that child,
|
||||||
|
4. the ria->da `finally:` re-raises collected `errors` (and
|
||||||
|
`an.cancel()`s any daemon stragglers).
|
||||||
|
|
||||||
|
So mid-block there is NO error propagation from ria children
|
||||||
|
(unless user code explicitly `await portal.wait_for_result()`s)
|
||||||
|
— the two-nursery nesting only sequences "reap ria results
|
||||||
|
BEFORE blocking on daemon join". A single-nursery impl only
|
||||||
|
needs to preserve that sequencing, not any ASAP-cancel
|
||||||
|
behavior.
|
||||||
|
|
||||||
|
## Target design
|
||||||
|
|
||||||
|
### step A: single-nursery `run_in_actor()` (mechanical)
|
||||||
|
|
||||||
|
- `run_in_actor()` spawns via the DEFAULT (`_da_nursery`)
|
||||||
|
path — drop `nursery=self._ria_nursery`.
|
||||||
|
- rename `._cancel_after_result_on_exit` ->
|
||||||
|
`._ria_portals: dict[portal, Actor]` (need the subactor ref
|
||||||
|
for `cancel_on_completion`).
|
||||||
|
- move reaper start-up OUT of the backends into
|
||||||
|
`_open_and_supervise...`: immediately after EACH
|
||||||
|
`an._join_procs.set()` call-site (happy path :642, inner
|
||||||
|
error handler :661), start one
|
||||||
|
`cancel_on_completion(portal, subactor, errors)` task per
|
||||||
|
ria portal into `da_nursery`, then (happy path only)
|
||||||
|
`await` their completion BEFORE falling out of the
|
||||||
|
`try:`/`finally:` that raises `errors` — e.g. gather in a
|
||||||
|
dedicated inner `trio.open_nursery()` block replacing
|
||||||
|
today's `ria_nursery` join point.
|
||||||
|
- delete the membership branch + local reaper nursery from
|
||||||
|
`_trio.py`/`_mp.py` (keep the `soft_kill()` call; the
|
||||||
|
per-child local nursery collapses to just `soft_kill`).
|
||||||
|
- `_trio.py:310` `_children.pop()` etc. unchanged.
|
||||||
|
|
||||||
|
### step B: delete the plumbing
|
||||||
|
|
||||||
|
- `_open_and_supervise...`: drop the inner
|
||||||
|
`ria_nursery` + merge its `except BaseException` classify
|
||||||
|
logic into ONE handler on the (now single) nursery scope;
|
||||||
|
`ActorNursery.__init__` loses the `ria_nursery` param.
|
||||||
|
- `start_actor()` loses the `nursery:` escape-hatch param
|
||||||
|
(the :302-304 TODO).
|
||||||
|
- backends: no more `_cancel_after_result_on_exit` refs.
|
||||||
|
|
||||||
|
### step C: (separate PRs) deprecate + migrate + excise
|
||||||
|
|
||||||
|
- migrate in-repo `.run_in_actor()` usage to
|
||||||
|
`to_actor.run()`: tests 46 hits/9 files (test_cancellation
|
||||||
|
15, test_infected_asyncio 10, test_spawning 8, registrar 3,
|
||||||
|
adv_streaming 4, pubsub 2, rpc 1, runtime 1), examples 28
|
||||||
|
hits/13 files (debugging/* dominate), docs 20 hits/8 rst
|
||||||
|
files. NOTE: many sites also use deprecated
|
||||||
|
`Portal.result()`/`wait_for_result()` — these die with
|
||||||
|
`_expect_result_ctx`, so migration must land FIRST.
|
||||||
|
- add `DeprecationWarning` to `run_in_actor()` (+
|
||||||
|
`_submit_for_result`/`wait_for_result`).
|
||||||
|
- final excision: `run_in_actor()`, `_submit_for_result`,
|
||||||
|
`_expect_result_ctx`, `wait_for_result`/`result`,
|
||||||
|
`exhaust_portal`, `cancel_on_completion`, `NoResult`.
|
||||||
|
|
||||||
|
## Risk register
|
||||||
|
|
||||||
|
1. hard-killed ria child: today the backend-local
|
||||||
|
`nursery.cancel_scope.cancel()` discards a still-parked
|
||||||
|
reaper when the proc dies first; a da_nursery-hosted
|
||||||
|
reaper instead sees the transport break ->
|
||||||
|
`exhaust_portal` returns a `TransportClosed`-ish exc ->
|
||||||
|
NEW entry in `errors` that today gets discarded. Guard:
|
||||||
|
reap-gather block must cancel remaining reapers once all
|
||||||
|
ria procs are dead, or filter transport-death excs for
|
||||||
|
already-`cancel_called` children.
|
||||||
|
2. error-path ordering: inner handler today sets
|
||||||
|
`_join_procs` THEN `an.cancel()`; reapers race the
|
||||||
|
cancel-RPC. Keep that ordering when moving reaper spawn.
|
||||||
|
3. debugger interplay: `maybe_wait_for_debugger()` calls
|
||||||
|
(:654, :730) must stay BEFORE any reap/cancel issuance.
|
||||||
|
4. `errors` double-entry: local body error (:646) + child's
|
||||||
|
relayed exc (via reaper) can both land for the same
|
||||||
|
scenario -> BEG shape changes vs today? (today has the
|
||||||
|
same dual-write sites; keep behavior identical.)
|
||||||
|
5. mp backend parity: mirror every `_trio.py` edit in
|
||||||
|
`_mp.py` (identical block).
|
||||||
|
|
||||||
|
## Step-A first-probe findings (2026-07-02, WIP in tree)
|
||||||
|
|
||||||
|
Step A is IMPLEMENTED (uncommitted):
|
||||||
|
`run_in_actor()` spawns via da_nursery; new
|
||||||
|
`_supervise._reap_ria_portals()` helper; reap awaited after
|
||||||
|
happy-path `_join_procs.set()`; error-path runs reap
|
||||||
|
CONCURRENT with `an.cancel()` in the shielded block;
|
||||||
|
backends stripped of the membership branch + per-child
|
||||||
|
reaper nursery (+ dead imports).
|
||||||
|
|
||||||
|
Probe history (trio backend):
|
||||||
|
- `tests/test_to_actor.py` + `tests/test_spawning.py`:
|
||||||
|
20/20 PASS — incl. all `run_in_actor()` result
|
||||||
|
round-trips + `test_remote_error` (single erroring child,
|
||||||
|
body re-raise -> inner error path).
|
||||||
|
- FIRST attempt ran the error-path reap CONCURRENT with
|
||||||
|
`an.cancel()` (mimicking the old backend-side race):
|
||||||
|
`test_cancellation.py::test_multierror` (2 erroring ria
|
||||||
|
children, body re-raises one) DEADLOCKED. Root cause per
|
||||||
|
the sequencing fix below: reap + cancel must NOT race at
|
||||||
|
this layer (suspected `._children` pop-during-iteration
|
||||||
|
and/or double-cancel RPC wedge; not fully root-caused
|
||||||
|
since the fix removes the race wholesale).
|
||||||
|
- FIX (2nd attempt, current impl): error path SEQUENCES:
|
||||||
|
(1) snapshot ria `(portal, subactor)` pairs (backend
|
||||||
|
`finally`s pop `._children` as procs reap), (2)
|
||||||
|
`await an.cancel()`, (3) bounded reap over the snapshot.
|
||||||
|
Bound was first 3s -> blew the `fail_after` deadline in
|
||||||
|
`test_cancel_while_childs_child_in_sync_sleep` (hard-
|
||||||
|
killed grandchild never relays => reaper parks the full
|
||||||
|
bound). Tightened to 0.5s: anything collectable is
|
||||||
|
already queued in the local ctx (relayed BEFORE the
|
||||||
|
cancel); a parked reaper self-cleans (`trio.Cancelled`
|
||||||
|
results are never stashed).
|
||||||
|
- RESULT: `tests/test_cancellation.py` FULLY GREEN
|
||||||
|
(20 passed, 1 xfailed, 77s); full-suite gate run kicked
|
||||||
|
off same session (see final report/next session).
|
||||||
|
|
||||||
|
Remaining risk: on slow CI a relayed-but-undelivered error
|
||||||
|
racing the 0.5s bound could drop an `errors` entry
|
||||||
|
(BEG-shape flake); if observed, scale the bound via the
|
||||||
|
`cpu_perf_headroom()`-style approach or peek
|
||||||
|
`Portal._final_result_msg`/ctx queue state instead of
|
||||||
|
time-bounding.
|
||||||
|
|
||||||
|
## Step-B outcome (2026-07-02, done in tree)
|
||||||
|
|
||||||
|
Step A landed as `5cd190c5` (code) + `99310269` (docs).
|
||||||
|
Step B implemented on top (uncommitted):
|
||||||
|
|
||||||
|
- `._ria_nursery` is GONE — the inner
|
||||||
|
`async with (collapse_eg(), trio.open_nursery() as
|
||||||
|
ria_nursery)` layer in
|
||||||
|
`_open_and_supervise_one_cancels_all_nursery` is deleted;
|
||||||
|
`da_nursery` is now the single nursery for ALL subactors.
|
||||||
|
- `ActorNursery.__init__` drops the `ria_nursery` param +
|
||||||
|
the `self._ria_nursery` attr; `start_actor()` drops its
|
||||||
|
`nursery=` escape-hatch param (uses `self._da_nursery`
|
||||||
|
directly).
|
||||||
|
- `._cancel_after_result_on_exit` STAYS — it's the
|
||||||
|
ria-child discriminator for `_reap_ria_portals()`.
|
||||||
|
|
||||||
|
Deliberately NOT done (deferred to its own higher-risk PR,
|
||||||
|
flagged with a TODO at the outer `except`): merging the two
|
||||||
|
error handlers into one. Rationale — collapsing the empty
|
||||||
|
nursery is provably behavior-preserving (a zero-task
|
||||||
|
`trio.open_nursery()` only adds a checkpoint), whereas the
|
||||||
|
inner `except BaseException` (swallow-into-`errors`) and
|
||||||
|
outer `except (...)` (re-raise, safety-net for the inner
|
||||||
|
handler's own non-shielded awaits) have DIFFERENT
|
||||||
|
semantics; merging changes error/cancel propagation and
|
||||||
|
wants isolated review + its own gate. Both handlers are
|
||||||
|
kept, now nested directly under the single nursery.
|
||||||
|
|
||||||
|
Why the collapse is safe: post-step-A NOTHING spawns into
|
||||||
|
`ria_nursery` (its only reader, `run_in_actor`'s
|
||||||
|
`nursery=self._ria_nursery`, was removed in A; the stored
|
||||||
|
attr was never read again). So the layer was pure dead
|
||||||
|
weight.
|
||||||
|
|
||||||
|
Gate (trio backend, all 0-failure):
|
||||||
|
- targeted set (`test_cancellation test_spawning test_local
|
||||||
|
test_rpc test_to_actor`) = 49 passed, 1 xfailed.
|
||||||
|
- tail set (`test_reg_err_types remote_exc_relay
|
||||||
|
resource_cache ringbuf root_infect_asyncio root_runtime
|
||||||
|
runtime shm task_broadcasting trioisms trionics/`) = 63
|
||||||
|
passed, 1 skipped, 5 xfailed.
|
||||||
|
- full-suite head ~73% (subdirs + `test_2way`..`test_pubsub`)
|
||||||
|
= 303 passed before the known-flaky `test_dynamic_pub_sub`
|
||||||
|
TooSlowError stall (pre-existing; same hang in the step-A
|
||||||
|
full run). Suite ran slow this session (~13min vs 555s
|
||||||
|
cold, likely thermal from back-to-back runs), never
|
||||||
|
completing within an 800s bound — but split across the
|
||||||
|
above three runs EVERY module passed under step B.
|
||||||
|
|
||||||
|
## Step-B2 outcome (2026-07-02, done in tree)
|
||||||
|
|
||||||
|
Step B committed as `9201a2ed` (code) + `d2e812fb` (docs), then
|
||||||
|
branched to `drop_ria_nursery`. Step B2 (the deferred
|
||||||
|
handler-merge) implemented on top (uncommitted):
|
||||||
|
|
||||||
|
- the two nested handlers in
|
||||||
|
`_open_and_supervise_one_cancels_all_nursery` collapse to
|
||||||
|
ONE `except BaseException as _scope_err` + the existing
|
||||||
|
`finally`. The `outer_err`/`inner_err` locals go away.
|
||||||
|
|
||||||
|
Why it's safe (trace, not hope): the OLD inner handler records
|
||||||
|
`errors[actor.aid.uid]` as its FIRST statement (before any
|
||||||
|
await). So whenever an error path runs, `errors` is non-empty.
|
||||||
|
The OLD outer handler was only reachable via leakage from the
|
||||||
|
inner handler (it catches `BaseException`, so nothing from the
|
||||||
|
`yield` scope bypasses it) — and by then `errors` is already
|
||||||
|
populated, so the `finally`'s `raise` from `errors` ALWAYS
|
||||||
|
superseded the outer handler's own `raise`. i.e. the outer
|
||||||
|
`raise` was dead. The outer handler's other effects
|
||||||
|
(`_scope_error`, a 2nd debugger-wait, child-cancel) are
|
||||||
|
redundant with the merged handler + `finally`. So one handler
|
||||||
|
+ `finally` is observably equivalent.
|
||||||
|
|
||||||
|
Residual nuance (accepted): in the rare "`trio.Cancelled`
|
||||||
|
delivered during the non-shielded `maybe_wait_for_debugger`"
|
||||||
|
path, the merged form may leave `_cancel_called` False (cancel
|
||||||
|
happens after the wait), so `open_nursery`'s tb-hiding guard
|
||||||
|
(`not cancel_called and _scope_error`) can show a tb it
|
||||||
|
previously hid. More informative, not less; no test asserts on
|
||||||
|
it.
|
||||||
|
|
||||||
|
Gate (box ran ~2.7x slow this session, load-induced
|
||||||
|
`TooSlowError` flakiness on timing tests — NOT code; see
|
||||||
|
[[env_cpu_throttle_masquerades_as_regression]]):
|
||||||
|
- baseline (pre-B2 tip `9201a2ed`) full suite
|
||||||
|
(`-k 'not dynamic_pub_sub'`) = 300 passed + 1
|
||||||
|
`test_ext_types_over_ipc` `TooSlowError` that passes 6/6 in
|
||||||
|
isolation (4.89s).
|
||||||
|
- B2 error/cancel gate (`test_cancellation remote_exc_relay
|
||||||
|
inter_peer_cancellation advanced_faults oob_cancellation
|
||||||
|
to_actor spawning local rpc`) = 71 passed, 1 xfailed
|
||||||
|
(125s).
|
||||||
|
- B2 full-suite run: see `b2_full.log` (result appended on
|
||||||
|
completion). RECOMMEND a clean full-suite run on a
|
||||||
|
normal-speed box before this merges.
|
||||||
|
|
||||||
|
## Regression + fix: ria-reap hang (2026-07-02)
|
||||||
|
|
||||||
|
Human hit a full-suite hang on
|
||||||
|
`test_infected_asyncio.py::test_tractor_cancels_aio`. Bisected:
|
||||||
|
passes at pre-ria `a34aaf98` (0.59s), hangs at B2 `e617b498`
|
||||||
|
(90s+). Root-caused to the STEP-A reaper hoist (`5cd190c5`),
|
||||||
|
NOT B2 (`_reap_ria_portals` is byte-identical A->B2).
|
||||||
|
|
||||||
|
Bug: the test does `run_in_actor(asyncio_actor)` then a USER
|
||||||
|
`portal.cancel_actor()` and exits the block cleanly -> the
|
||||||
|
happy path's `await _reap_ria_portals()`, which waits UNBOUNDED
|
||||||
|
on `cancel_on_completion -> wait_for_result()`. The child was
|
||||||
|
cancelled out-of-band so no final result is relayed -> parked
|
||||||
|
forever. The OLD spawn-backend reaper was raced against
|
||||||
|
`soft_kill()` (per-child nursery `cancel_scope.cancel()` on
|
||||||
|
subproc death); the hoist dropped that race.
|
||||||
|
|
||||||
|
Fix: `_reap_ria_portals()` runs each `cancel_on_completion()`
|
||||||
|
in a local nursery alongside a `proc.poll()` death-watch that
|
||||||
|
cancels the parked reaper once the subproc exits — restoring
|
||||||
|
the old race, backend-agnostic (guarded by
|
||||||
|
`hasattr(proc, 'poll')` for a future `subint` handle).
|
||||||
|
|
||||||
|
Why POLL (`proc.poll()`) not the event-driven `wait_func`:
|
||||||
|
the mp waiter (`_spawn.proc_waiter`) does
|
||||||
|
`wait_readable(proc.sentinel)`, and `soft_kill()` is ALREADY
|
||||||
|
awaiting that same fd concurrently in the daemon nursery — a
|
||||||
|
2nd `wait_readable` on one fd raises `trio.BusyResourceError`.
|
||||||
|
(`trio.Process.wait()` IS multi-waiter-safe, but mp has no
|
||||||
|
async equivalent.) `proc.poll()` — the same liveness check
|
||||||
|
`soft_kill` itself falls back to — is the conflict-free common
|
||||||
|
denominator. Verified: poll-fix passes on BOTH trio and
|
||||||
|
mp_spawn.
|
||||||
|
|
||||||
|
Also added a per-test anti-hang guard: wrapped
|
||||||
|
`test_tractor_cancels_aio`'s `main()` in
|
||||||
|
`with trio.fail_after(9 * cpu_perf_headroom())` — the blessed
|
||||||
|
pattern (`pytest-timeout`'s global cap is intentionally off;
|
||||||
|
breaks trio under fork backends, see `pyproject` NOTE). So a
|
||||||
|
future recurrence FAILS FAST instead of hanging the suite.
|
||||||
|
(Several other tests in the file are still guardless —
|
||||||
|
`test_aio_simple_error`, `test_trio_error_cancels_intertask_chan`,
|
||||||
|
`test_aio_errors_and_channel_propagates_and_closes` — candidate
|
||||||
|
follow-up sweep.)
|
||||||
|
|
||||||
|
Lesson: the B2 focused gate OMITTED `test_infected_asyncio`
|
||||||
|
(and the full runs were clipped/slow), so the step-A hang
|
||||||
|
slipped through. Any future ria-touching change MUST gate
|
||||||
|
`test_infected_asyncio` explicitly.
|
||||||
|
|
||||||
|
Gate: `test_tractor_cancels_aio` green (trio 1.53s, mp 3.98s);
|
||||||
|
fix gate (`test_infected_asyncio test_cancellation test_to_actor
|
||||||
|
test_spawning`) = 74 passed, 3 xfailed, 0 failures.
|
||||||
|
|
||||||
|
## PAUSED (2026-07-02): re-assess the reaper's SCOPE
|
||||||
|
|
||||||
|
User's insight (compelling — likely the real root cause of
|
||||||
|
the hang, not just the missing proc-death race):
|
||||||
|
|
||||||
|
> the "hoisting" of 5cd190c5 was just not really done right
|
||||||
|
> — the hoist should have been into the `to_actor` scope,
|
||||||
|
> not `_supervise`.
|
||||||
|
|
||||||
|
The argument: `.run_in_actor()`'s result-waiting/reaping got
|
||||||
|
hoisted into `_supervise._reap_ria_portals` (nursery-machinery
|
||||||
|
scope), which has NO natural cancel-scope to bound a parked
|
||||||
|
`wait_for_result()` — hence the awkward proc-death race +
|
||||||
|
the poll-vs-`proc_waiter` dilemma. If the result-wait instead
|
||||||
|
lived in the `to_actor` one-shot scope
|
||||||
|
(`to_actor._invoke_in_subactor()`), it would sit right next to
|
||||||
|
the caller's `an` + a local `trio` task-nursery + cancel-scope
|
||||||
|
(the `trio.to_thread`-style model #477 actually wants) — so
|
||||||
|
bounding/cancelling the wait is trivial and the hang
|
||||||
|
dissolves from correct scoping rather than a bolt-on race.
|
||||||
|
|
||||||
|
Follow-on to re-evaluate on resume:
|
||||||
|
- should `_reap_ria_portals` exist AT ALL, or should
|
||||||
|
result-waiting move entirely into
|
||||||
|
`to_actor._invoke_in_subactor()`?
|
||||||
|
- reimplement legacy `run_in_actor()` on top of
|
||||||
|
`to_actor.run()` so `_reap_ria_portals` +
|
||||||
|
`_cancel_after_result_on_exit` can be DROPPED from
|
||||||
|
`_supervise` entirely (the true #477 simplification)?
|
||||||
|
- the poll-vs-event decision is MOOT under this re-scoping.
|
||||||
|
|
||||||
|
State at pause: `test_infected_asyncio` anti-hang guard
|
||||||
|
COMMITTED (`d1fb4a1a`, intentionally red w/o the fix — the
|
||||||
|
user's failing-test-first convention). The poll-based reap
|
||||||
|
fix in `_supervise.py` is UNCOMMITTED and likely SUPERSEDED
|
||||||
|
by the re-scoping — do NOT land it as-is.
|
||||||
|
|
||||||
|
## RESOLVED (2026-07-06): migrate everything, remove the API
|
||||||
|
|
||||||
|
The PAUSED re-assessment concluded decisively: rather than
|
||||||
|
re-scope `_reap_ria_portals` (or bolt any hack onto it), the
|
||||||
|
`run_in_actor()` API itself was REMOVED — its non-blocking
|
||||||
|
"result at teardown" semantic predates streaming and confused
|
||||||
|
more than it served. Every in-repo caller was migrated
|
||||||
|
per-file/-group (each its own commit, each gated):
|
||||||
|
|
||||||
|
- tests: `test_infected_asyncio` `test_runtime` `test_rpc`
|
||||||
|
`test_spawning` `test_pubsub` `test_registrar`
|
||||||
|
`test_cancellation` (3 groups) `test_advanced_streaming`.
|
||||||
|
- examples: 4 non-debugging + all 8 `debugging/` REPL scripts
|
||||||
|
(debugger suite byte-identical green, 28p/6s).
|
||||||
|
- docs: 8 rst pages + the `experimental/_pubsub` docstring.
|
||||||
|
|
||||||
|
Migration patterns (the `run_in_actor` shape -> successor):
|
||||||
|
|
||||||
|
- blocking result -> `to_actor.run(fn, an=an, ...)`
|
||||||
|
- fire-&-forget/forever -> bg `to_actor.run()` task in a local
|
||||||
|
`trio` task-nursery (or `start_actor`
|
||||||
|
+ bg `Portal.run()` when a portal
|
||||||
|
handle is needed)
|
||||||
|
- concurrent fan-out -> N bg `to_actor.run()` tasks / or
|
||||||
|
`gather_contexts([p.open_context(..)])`
|
||||||
|
- reap-all-error-collect -> the "collect don't cancel" pattern:
|
||||||
|
each one-shot catches + stashes its
|
||||||
|
`RemoteActorError`, group raised
|
||||||
|
after the task-nursery joins (see
|
||||||
|
`examples/debugging/multi_subactors.py`)
|
||||||
|
- mutual-rendezvous -> peers must OUTLIVE both dialogs:
|
||||||
|
`start_actor()` daemons + concurrent
|
||||||
|
`Portal.run()`s + explicit
|
||||||
|
`an.cancel()` (eager one-shot reap
|
||||||
|
races the slower peer's dial of the
|
||||||
|
winner's dead sockaddr; found via
|
||||||
|
`test_trynamic_trio` flake).
|
||||||
|
|
||||||
|
Semantic deltas (tests loosened accordingly):
|
||||||
|
|
||||||
|
- teardown-reap-all BEG-of-N is GONE: local task-nurseries are
|
||||||
|
cancel-on-first, raced siblings' `Cancelled`s are absorbed,
|
||||||
|
and the runtime's `collapse_eg()` unwraps every single-member
|
||||||
|
group at each actor boundary — a fully-raced nested tree
|
||||||
|
relays a bare (annotated) `RemoteActorError` chain.
|
||||||
|
- `test_multierror_fast_nursery`'s obsolete BEG-of-25 assertion
|
||||||
|
deleted; `test_concurrent_start_error_reaps_all` retains its
|
||||||
|
high-fan-out startup/cancel/reap stress under caller-scoped
|
||||||
|
semantics.
|
||||||
|
- `test_nested_multierrors` re-purposed separately as deep-tree
|
||||||
|
cancel-cascade stress w/ a race-tolerant shape walk.
|
||||||
|
|
||||||
|
Final excision (after zero callers remained): `run_in_actor()`,
|
||||||
|
`._cancel_after_result_on_exit`, `_reap_ria_portals()`,
|
||||||
|
`Portal._submit_for_result/._expect_result_ctx/
|
||||||
|
.wait_for_result()/.result()`, `exhaust_portal()`,
|
||||||
|
`cancel_on_completion()`, `NoResult` — net -402 lines. The
|
||||||
|
reap-hang class (unbounded `wait_for_result` in machinery
|
||||||
|
scope) dissolves structurally: the only result-wait left lives
|
||||||
|
in the caller's task inside its own cancel-scope; the
|
||||||
|
`d1fb4a1a` anti-hang guard test passes by construction. The
|
||||||
|
poll-vs-`proc_waiter` debate is moot as predicted.
|
||||||
|
|
||||||
|
## Follow-up sketch: `to_actor.open_one_shot()` (run-async parity)
|
||||||
|
|
||||||
|
If deferred-result parity is ever wanted, the design that needs
|
||||||
|
NO runtime coupling, NO returned `Portal` and NO cancel-relay
|
||||||
|
`trio.Event` machinery:
|
||||||
|
|
||||||
|
async with to_actor.open_one_shot(
|
||||||
|
fn, an=an, **kws,
|
||||||
|
) as one_shot:
|
||||||
|
... # concurrent caller work
|
||||||
|
val = await one_shot.wait() # optional; errors always
|
||||||
|
# propagate at scope exit
|
||||||
|
|
||||||
|
an `@acm` that opens a private task-nursery, `start_soon`s ONE
|
||||||
|
task running the existing blocking `run()` and stashes the
|
||||||
|
value in a slot + sets a done-`trio.Event` (a memo, not a
|
||||||
|
cancel relay). Cancellation = plain scope-cancel of the acm's
|
||||||
|
nursery (the parked `Portal.run()` unwinds via `Cancelled`, the
|
||||||
|
shielded `cancel_actor()` reap still runs); a child error
|
||||||
|
raises into the acm scope so an un-`wait()`ed one-shot can
|
||||||
|
never silently drop its error. i.e. the old reaper's job is
|
||||||
|
done by scoping, not machinery. ~40 lines, all in
|
||||||
|
`to_actor/_api.py`, zero `_supervise` involvement.
|
||||||
|
|
||||||
|
## Verification gate
|
||||||
|
|
||||||
|
- per-migration-commit module gates on `trio` (+ `mp_spawn`
|
||||||
|
spot-gates incl. `test_infected_asyncio` per the B2 lesson);
|
||||||
|
`tests/devx/test_debugger.py` for the REPL flows.
|
||||||
|
- full suite on `trio` + `mp_spawn` at branch tip + CI matrix
|
||||||
|
via draft PR #484.
|
||||||
|
|
@ -0,0 +1,79 @@
|
||||||
|
---
|
||||||
|
model: claude-fable-5
|
||||||
|
service: claude
|
||||||
|
session: f6c84722-471a-4458-9a80-e453fea9029f
|
||||||
|
timestamp: 2026-07-02T15:42:55Z
|
||||||
|
git_ref: 65bf9df5
|
||||||
|
scope: code
|
||||||
|
substantive: true
|
||||||
|
raw_file: 20260702T154255Z_65bf9df5_prompt_io.raw.md
|
||||||
|
---
|
||||||
|
|
||||||
|
## Prompt
|
||||||
|
|
||||||
|
Driver prompt file `ai/prompt-io/prompts/issue_477.md`:
|
||||||
|
|
||||||
|
> attempt to resolve
|
||||||
|
> https://github.com/goodboy/tractor/issues/477
|
||||||
|
> do it with /open-wkt.
|
||||||
|
|
||||||
|
(plus a hard stop-for-human-review deadline of 12:50PM
|
||||||
|
EST the same day)
|
||||||
|
|
||||||
|
Issue #477 asks to factor `ActorNursery.run_in_actor()`
|
||||||
|
(and possibly `Portal.run()`) out of the nursery
|
||||||
|
internals into a new `tractor.to_actor` wrapper
|
||||||
|
subpackage of "higher level one shot" single-remote-task
|
||||||
|
APIs, adopting the `trio.to_thread`/`anyio.to_process`
|
||||||
|
parlance, so that error collection/propagation moves up
|
||||||
|
into the caller's local `trio` scope and the nursery's
|
||||||
|
spawn machinery can eventually drop the
|
||||||
|
`._ria_nursery` coupling.
|
||||||
|
|
||||||
|
## Response summary
|
||||||
|
|
||||||
|
First-cut `tractor.to_actor` subpkg delivering the
|
||||||
|
one-shot API composed purely from the existing
|
||||||
|
daemon-spawn + portal primitives (`start_actor()` +
|
||||||
|
`Portal.run()` + `Portal.cancel_actor()`), leaving the
|
||||||
|
legacy `.run_in_actor()` machinery untouched (formal
|
||||||
|
deprecation deferred until in-repo usage migrates):
|
||||||
|
|
||||||
|
- `to_actor.run(fn, **fn_kwargs) -> Any`: spawn a
|
||||||
|
subactor, schedule `fn` as its lone remote task, wait
|
||||||
|
on and return its result, ALWAYS reaping the subactor
|
||||||
|
(shield-safe `finally`). Remote errors raise in the
|
||||||
|
caller's task as boxed `RemoteActorError`s.
|
||||||
|
- placement variants: `portal=` reuses a running actor
|
||||||
|
(no spawn/reap), `an=` spawns from a caller-managed
|
||||||
|
actor-nursery, neither opens a call-scoped private
|
||||||
|
`open_nursery()` (implicitly booting the runtime,
|
||||||
|
configurable via `runtime_kwargs`).
|
||||||
|
- fail-fast validation before any spawn: non-streaming
|
||||||
|
async fn required; `portal=`/`an=` mutually
|
||||||
|
exclusive; `runtime_kwargs` rejected alongside any
|
||||||
|
placement opt.
|
||||||
|
- `run_in_actor()` TODO/docstring now cross-reference
|
||||||
|
the successor API.
|
||||||
|
|
||||||
|
## Files changed
|
||||||
|
|
||||||
|
- `tractor/to_actor/__init__.py` — new subpkg,
|
||||||
|
re-exports `run`
|
||||||
|
- `tractor/to_actor/_api.py` — `run()` +
|
||||||
|
`_invoke_in_subactor()` + `_validate_one_shot_fn()`
|
||||||
|
- `tractor/__init__.py` — top-level `to_actor`
|
||||||
|
re-export
|
||||||
|
- `tractor/runtime/_supervise.py` — comment/docstring
|
||||||
|
pointers from `run_in_actor()` to the successor
|
||||||
|
- `tests/test_to_actor.py` — 11-test suite covering
|
||||||
|
all placement variants, error relay, the concurrent
|
||||||
|
worker-pool-ish pattern and arg validation
|
||||||
|
- `examples/parallelism/to_actor_one_shots.py` —
|
||||||
|
runnable demo (auto-collected by
|
||||||
|
`test_docs_examples.py`)
|
||||||
|
|
||||||
|
## Human edits
|
||||||
|
|
||||||
|
None yet — pending human review (work paused before the
|
||||||
|
12:50PM EST deadline per the driver prompt).
|
||||||
|
|
@ -0,0 +1,100 @@
|
||||||
|
---
|
||||||
|
model: claude-fable-5
|
||||||
|
service: claude
|
||||||
|
timestamp: 2026-07-02T15:42:55Z
|
||||||
|
git_ref: 65bf9df5
|
||||||
|
diff_cmd: git diff main..wkt/to_actor_subpkg
|
||||||
|
---
|
||||||
|
|
||||||
|
# Raw AI output (diff-ref mode)
|
||||||
|
|
||||||
|
All generated code is committed on the
|
||||||
|
`wkt/to_actor_subpkg` branch; per diff-ref mode each
|
||||||
|
file's verbatim content is reachable via the pointers
|
||||||
|
below rather than duplicated here.
|
||||||
|
|
||||||
|
## Generated files
|
||||||
|
|
||||||
|
> `git diff main..wkt/to_actor_subpkg -- tractor/to_actor/__init__.py`
|
||||||
|
|
||||||
|
New subpackage init: module docstring establishing the
|
||||||
|
`trio.to_thread`/`anyio.to_process` "run it over there"
|
||||||
|
parlance for actors, plus the single public re-export
|
||||||
|
`run as run` from `._api`.
|
||||||
|
|
||||||
|
> `git diff main..wkt/to_actor_subpkg -- tractor/to_actor/_api.py`
|
||||||
|
|
||||||
|
The one-shot invocation impl, composed entirely from the
|
||||||
|
lower level daemon-spawn + portal primitives as
|
||||||
|
prescribed by issue #477:
|
||||||
|
|
||||||
|
- `_validate_one_shot_fn()`: the `Portal.run()`
|
||||||
|
non-streaming-async-fn constraint checked up-front,
|
||||||
|
before any subactor is spawned.
|
||||||
|
- `_invoke_in_subactor()`: `an.start_actor()` ->
|
||||||
|
`Portal.run()` -> always-reap via
|
||||||
|
`Portal.cancel_actor()` in a `finally` (the cancel
|
||||||
|
req's bounded wait is internally shielded so the reap
|
||||||
|
also runs under caller-scope cancellation).
|
||||||
|
- `run()`: the public API. Placement options:
|
||||||
|
`portal=` (reuse a running actor, no spawn/reap),
|
||||||
|
`an=` (spawn from a caller-managed nursery), or
|
||||||
|
neither (private `open_nursery()` scoped to the call,
|
||||||
|
implicitly booting the runtime when needed, tunable
|
||||||
|
via pass-through `runtime_kwargs`). Spawn opts mirror
|
||||||
|
`ActorNursery.start_actor()`; `**fn_kwargs` are
|
||||||
|
relayed to the remote task. Errors raise in the
|
||||||
|
caller's task as boxed `RemoteActorError`s.
|
||||||
|
`runtime_kwargs` alongside any placement opt is a
|
||||||
|
hard `ValueError`, never silently ignored.
|
||||||
|
|
||||||
|
> `git diff main..wkt/to_actor_subpkg -- tractor/__init__.py`
|
||||||
|
|
||||||
|
Top-level `from . import to_actor as to_actor`
|
||||||
|
re-export.
|
||||||
|
|
||||||
|
> `git diff main..wkt/to_actor_subpkg -- tractor/runtime/_supervise.py`
|
||||||
|
|
||||||
|
Comment/docstring-only: the `run_in_actor()` deprecation
|
||||||
|
TODO now points at the implemented `.to_actor.run()`
|
||||||
|
successor (checkbox ticked) and the method docstring
|
||||||
|
gains a NOTE steering users to the new API; remaining
|
||||||
|
TODO items are the `DeprecationWarning` emission +
|
||||||
|
in-repo usage migration.
|
||||||
|
|
||||||
|
> `git diff main..wkt/to_actor_subpkg -- tests/test_to_actor.py`
|
||||||
|
|
||||||
|
11-test suite: private-nursery one-shot, implicit
|
||||||
|
runtime boot via `runtime_kwargs`, remote-error relay to
|
||||||
|
the caller's task (bare + caller-managed nursery),
|
||||||
|
caller-nursery spawn, portal reuse w/o implicit reap,
|
||||||
|
the concurrent worker-pool-ish pattern (local `trio`
|
||||||
|
nursery x shared `an`), and the four validation
|
||||||
|
rejections (sync fn, async-gen fn, `portal`+`an`
|
||||||
|
combo, `runtime_kwargs`+placement combo).
|
||||||
|
|
||||||
|
> `git diff main..wkt/to_actor_subpkg -- examples/parallelism/to_actor_one_shots.py`
|
||||||
|
|
||||||
|
Runnable example (auto-collected by
|
||||||
|
`test_docs_examples.py`): the fully-implicit one-shot
|
||||||
|
plus the concurrent worker-pool-ish prime-check pattern
|
||||||
|
against a shared caller-managed actor-nursery.
|
||||||
|
|
||||||
|
## Test runs (verbatim)
|
||||||
|
|
||||||
|
```
|
||||||
|
tests/test_to_actor.py .......... [100%]
|
||||||
|
============= 10 passed in 4.29s =============
|
||||||
|
```
|
||||||
|
|
||||||
|
Regression subset for touched modules
|
||||||
|
(`test_local.py test_rpc.py test_spawning.py
|
||||||
|
test_cancellation.py`):
|
||||||
|
|
||||||
|
```
|
||||||
|
38 passed, 1 xfailed, 24 warnings in 80.71s (0:01:20)
|
||||||
|
```
|
||||||
|
|
||||||
|
(warnings are pre-existing stdlib `os.fork()`
|
||||||
|
DeprecationWarnings from the mp spawn backends, not
|
||||||
|
introduced by this change)
|
||||||
|
|
@ -0,0 +1,77 @@
|
||||||
|
---
|
||||||
|
model: claude-fable-5
|
||||||
|
service: claude
|
||||||
|
session: b8ec2ffe-1e47-4db5-8bc3-5bebc5feaaf6
|
||||||
|
timestamp: 2026-07-02T15:50:06Z
|
||||||
|
git_ref: 65bf9df5
|
||||||
|
scope: code
|
||||||
|
substantive: true
|
||||||
|
raw_file: 20260702T155006Z_65bf9df5_prompt_io.raw.md
|
||||||
|
---
|
||||||
|
|
||||||
|
## Prompt
|
||||||
|
|
||||||
|
From `ai/prompt-io/prompts/issue_473.md`:
|
||||||
|
|
||||||
|
> NOTE: you MUST pause this work at 12:50PM EST (BEFORE
|
||||||
|
> your weekly limit reset) for review by a human!
|
||||||
|
>
|
||||||
|
> resolve GH #473: https://github.com/goodboy/tractor/issues/474
|
||||||
|
> do it with /open-wkt.
|
||||||
|
|
||||||
|
(The prompt's issue-number/URL mismatch was resolved in
|
||||||
|
favor of #473 — file name, session name and prompt text
|
||||||
|
all say #473; the `/474` URL is a typo.)
|
||||||
|
|
||||||
|
GH #473: "Root-cause UDS-on-macOS —
|
||||||
|
`uds_transport_actor_tree` example fails on CI".
|
||||||
|
|
||||||
|
## Response summary
|
||||||
|
|
||||||
|
Worked all four #473 next-step checkboxes in wkt
|
||||||
|
`uds_macos_473`:
|
||||||
|
|
||||||
|
1. **stderr surfacing**: `test_docs_examples.py` now
|
||||||
|
raises with the full subproc stderr (+stdout) on ANY
|
||||||
|
non-zero exit. The old last-line-'Error' check could
|
||||||
|
never fire for `tractor` crashes since root-actor
|
||||||
|
stderr always ends with the strict-EG collapse note
|
||||||
|
`( ^^^ this exc was collapsed from a group ^^^ )` —
|
||||||
|
proven against the real PR #460 macOS CI log (bare
|
||||||
|
`assert 1 == 0`, no traceback).
|
||||||
|
2. **root-cause (linux-provable layer)**: macOS-only
|
||||||
|
addr corruption in
|
||||||
|
`MsgpackUDSStream.get_stream_addrs()` — no
|
||||||
|
`SO_PASSCRED`/autobind on darwin means the accept
|
||||||
|
side's `getpeername()` is `''`, and the
|
||||||
|
`(str(), str())` arm took `peername` unconditionally →
|
||||||
|
`Path('')` garbage addrs on every accepted conn.
|
||||||
|
Proven + fixed via linux no-autobind simulation.
|
||||||
|
Possibly not the final macOS crasher (non-fatal on
|
||||||
|
linux-sim); the diagnostic patch guarantees the next
|
||||||
|
macOS CI run shows any remaining layer.
|
||||||
|
3. **CI matrix**: removed the `macos-latest`+`uds`
|
||||||
|
exclude.
|
||||||
|
4. **un-skip**: dropped the macOS+CI skip of the example.
|
||||||
|
|
||||||
|
Also: `start_listener()` bindspace mkdir hardened
|
||||||
|
(`parents=True, exist_ok=True`), example docstring
|
||||||
|
peer-pid mechanism corrected for macOS.
|
||||||
|
|
||||||
|
## Files changed
|
||||||
|
|
||||||
|
- `tests/test_docs_examples.py` — surface full stderr on
|
||||||
|
non-zero exit; remove macOS skip of the UDS example
|
||||||
|
- `tractor/ipc/_uds.py` — fix no-autobind
|
||||||
|
`get_stream_addrs()` arm; harden bindspace mkdir;
|
||||||
|
document autobind semantics
|
||||||
|
- `.github/workflows/ci.yml` — add macOS UDS leg (remove
|
||||||
|
matrix exclude)
|
||||||
|
- `examples/uds_transport_actor_tree.py` — platform-
|
||||||
|
correct peer-pid docs
|
||||||
|
|
||||||
|
## Human edits
|
||||||
|
|
||||||
|
None yet — pending human review (hard-stop review
|
||||||
|
checkpoint at 12:50PM EST per prompt); commit staging and
|
||||||
|
any edits are the human's.
|
||||||
|
|
@ -0,0 +1,101 @@
|
||||||
|
---
|
||||||
|
model: claude-fable-5
|
||||||
|
service: claude
|
||||||
|
timestamp: 2026-07-02T15:50:06Z
|
||||||
|
git_ref: 65bf9df5
|
||||||
|
diff_cmd: git diff main..wkt/uds_macos_473
|
||||||
|
---
|
||||||
|
|
||||||
|
# Raw output — GH #473 UDS-on-macOS root-cause session
|
||||||
|
|
||||||
|
NOTE: code output is diff-referenced (not copied) per
|
||||||
|
prompt-io diff-ref mode; all generated code is on branch
|
||||||
|
`wkt/uds_macos_473` relative to `main` (65bf9df5).
|
||||||
|
|
||||||
|
## Diagnostic narrative (verbatim)
|
||||||
|
|
||||||
|
Root-cause work proceeded by linux-side simulation since no
|
||||||
|
macOS host is available:
|
||||||
|
|
||||||
|
1. Pulled the actual macOS CI failure log from PR #460 run
|
||||||
|
28343878933 (job 84115107992): the failure is a bare
|
||||||
|
`assert 1 == 0` on `proc.returncode` with NO stderr
|
||||||
|
surfaced — the harness only re-raised when the LAST
|
||||||
|
stderr line contained 'Error'.
|
||||||
|
|
||||||
|
2. Reproduced the stderr-swallow mechanism on linux: any
|
||||||
|
`tractor` root-actor crash ends stderr with the
|
||||||
|
strict-EG collapse note
|
||||||
|
`( ^^^ this exc was collapsed from a group ^^^ )` which
|
||||||
|
never matches 'Error' — so EVERY possible crash was
|
||||||
|
swallowed. (Verified by sabotaging the runtime dir via
|
||||||
|
an over-long `XDG_RUNTIME_DIR` → `OSError: AF_UNIX path
|
||||||
|
too long` → rc=1 + swallowed.)
|
||||||
|
|
||||||
|
3. Found + proved a macOS-only addr-corruption bug in
|
||||||
|
`MsgpackUDSStream.get_stream_addrs()`: the
|
||||||
|
`(str(), str())` match-arm unconditionally took
|
||||||
|
`peername`, but on no-autobind platforms (macOS lacks
|
||||||
|
linux's `SO_PASSCRED`-triggered autobind) the accept
|
||||||
|
side's `getpeername()` is `''` → `Path('')` garbage
|
||||||
|
laddr/raddr on EVERY accepted UDS conn. Simulated on
|
||||||
|
linux by nulling `SO_PASSCRED` (no autobind → same `''`
|
||||||
|
shape): pre-fix the example printed
|
||||||
|
`listener sock file: .`; post-fix it prints the real
|
||||||
|
registry sockpath. Non-fatal on linux-sim (rc=0), so
|
||||||
|
possibly not the final macOS crasher — the diagnostic
|
||||||
|
patch guarantees the next macOS CI run reveals any
|
||||||
|
remaining layer.
|
||||||
|
|
||||||
|
4. Falsified the missing-parent-dir theory:
|
||||||
|
`get_rt_dir()` already `mkdir(parents=True,
|
||||||
|
exist_ok=True)`s at import (and macOS TCP CI passes),
|
||||||
|
so `~/Library/Caches/TemporaryItems` absence cannot be
|
||||||
|
the crasher. Hardened `start_listener()`'s bindspace
|
||||||
|
mkdir anyway (custom `filedir` case + racing actors).
|
||||||
|
|
||||||
|
## Generated changes (diff pointers)
|
||||||
|
|
||||||
|
> `git diff main..wkt/uds_macos_473 -- tests/test_docs_examples.py`
|
||||||
|
|
||||||
|
- always raise with FULL subproc stderr (+stdout) on any
|
||||||
|
non-zero example exit; keep legacy last-line 'Error'
|
||||||
|
check for zero-rc cases; drop the macOS+CI skip of
|
||||||
|
`uds_transport_actor_tree.py` (GH #473 next-step).
|
||||||
|
|
||||||
|
> `git diff main..wkt/uds_macos_473 -- tractor/ipc/_uds.py`
|
||||||
|
|
||||||
|
- `get_stream_addrs()`: document the autobind semantics
|
||||||
|
(bytes = linux abstract-ns autobind artifact), add
|
||||||
|
no-autobind `(str, str)` arm picking the non-empty name
|
||||||
|
(`peername` connect-side, `sockname` accept-side) with
|
||||||
|
an empty-pair `ValueError` guard.
|
||||||
|
- `start_listener()`: `bs.mkdir(parents=True,
|
||||||
|
exist_ok=True)`.
|
||||||
|
|
||||||
|
> `git diff main..wkt/uds_macos_473 -- .github/workflows/ci.yml`
|
||||||
|
|
||||||
|
- remove the `macos-latest`+`uds` matrix exclude so
|
||||||
|
UDS-on-macOS is exercised by CI (GH #473 next-step).
|
||||||
|
|
||||||
|
> `git diff main..wkt/uds_macos_473 -- examples/uds_transport_actor_tree.py`
|
||||||
|
|
||||||
|
- docs nit: peer-pid mechanism is `SO_PEERCRED` on linux,
|
||||||
|
`LOCAL_PEERPID` on macOS.
|
||||||
|
|
||||||
|
## Verification (verbatim summary)
|
||||||
|
|
||||||
|
- macOS-shape sim (no autobind) + fix: example rc=0 with
|
||||||
|
correct listener sockpath.
|
||||||
|
- native linux post-fix: example rc=0, autobind arms
|
||||||
|
unchanged.
|
||||||
|
- sabotage smoke-test: diagnostic patch surfaces the full
|
||||||
|
EG traceback incl. collapse-note line.
|
||||||
|
- `pytest tests/ipc/ tests/test_2way.py tests/discovery/
|
||||||
|
--tpt-proto uds`: 61 passed, 1 `TooSlowError` flake
|
||||||
|
(test_simple_rpc) that passes solo in 2.2s — attributed
|
||||||
|
to a concurrent full-suite run (other session) loading
|
||||||
|
the host.
|
||||||
|
- full `tests/test_docs_examples.py` run pending at
|
||||||
|
raw-file write time (queued behind the concurrent
|
||||||
|
session's suite).
|
||||||
|
|
@ -0,0 +1,83 @@
|
||||||
|
---
|
||||||
|
model: claude-fable-5
|
||||||
|
service: claude
|
||||||
|
session: b6b42e23-0454-4d9b-be1c-0a89d65aed1b
|
||||||
|
timestamp: 2026-07-02T15:56:26Z
|
||||||
|
git_ref: 65bf9df5
|
||||||
|
scope: code
|
||||||
|
substantive: true
|
||||||
|
raw_file: 20260702T155626Z_65bf9df5_prompt_io.raw.md
|
||||||
|
---
|
||||||
|
|
||||||
|
## Prompt
|
||||||
|
|
||||||
|
From `ai/prompt-io/prompts/issue_470.md`:
|
||||||
|
|
||||||
|
> attempt to resolve
|
||||||
|
> https://github.com/goodboy/tractor/issues/470
|
||||||
|
> do it with /open-wkt, ensure you /run-tests on all
|
||||||
|
> changes
|
||||||
|
|
||||||
|
(plus a hard pause-for-human-review deadline of
|
||||||
|
12:50PM EST.)
|
||||||
|
|
||||||
|
Issue #470 asks to trim the ~0.42s `import tractor`
|
||||||
|
cost — which dominates per-actor spawn latency on the
|
||||||
|
`trio` backend — by lazy-importing heavy/optional deps
|
||||||
|
(`pdbp`, `stackscope`, `multiaddr`, + audit of
|
||||||
|
`colorlog`/`bidict`/`wrapt`/`setproctitle`).
|
||||||
|
|
||||||
|
## Response summary
|
||||||
|
|
||||||
|
Profiling showed the issue's dep-list only accounted
|
||||||
|
for ~20ms; the dominant cost (~244ms) was
|
||||||
|
`log.get_logger()`'s `get_caller_mod()` calling
|
||||||
|
`inspect.stack()` at module level in ~39 modules —
|
||||||
|
each call walks every stack frame (deep during nested
|
||||||
|
imports) and scans `sys.modules` per frame via
|
||||||
|
`inspect.getmodule()`.
|
||||||
|
|
||||||
|
Changes, in impact order:
|
||||||
|
|
||||||
|
1. `get_caller_mod()` -> `sys._getframe()` +
|
||||||
|
`f_globals['__name__']` `sys.modules` lookup
|
||||||
|
(~240ms saved).
|
||||||
|
2. Issue's lazy-import checklist: `bidict`,
|
||||||
|
`multiaddr`, `colorlog`, `wrapt` moved to
|
||||||
|
`TYPE_CHECKING`/function-local imports;
|
||||||
|
`platformdirs` function-local; `asyncio` +
|
||||||
|
`.to_asyncio` deferred out of the `devx.debug` +
|
||||||
|
`spawn._entry` eager paths (~15ms saved).
|
||||||
|
3. PEP 562 `__getattr__` on `tractor/__init__.py`
|
||||||
|
preserving public `tractor.to_asyncio` attr access.
|
||||||
|
|
||||||
|
Results: `import tractor` 0.42s -> ~0.145s (~65%);
|
||||||
|
sequential `start_actor` latency 0.40-0.44s ->
|
||||||
|
~0.179s/actor. `pdbp` (needs `_repl.py` class-base
|
||||||
|
restructure) + `platformdirs` (needs
|
||||||
|
`UDSAddress.def_bindspace` protocol rework) documented
|
||||||
|
as follow-ups.
|
||||||
|
|
||||||
|
## Files changed
|
||||||
|
|
||||||
|
- `tractor/log.py` — `get_caller_mod()` perf fix +
|
||||||
|
lazy `colorlog`
|
||||||
|
- `tractor/__init__.py` — PEP 562 lazy `to_asyncio`
|
||||||
|
- `tractor/discovery/_addr.py` — `bidict` ->
|
||||||
|
`TYPE_CHECKING`
|
||||||
|
- `tractor/discovery/_multiaddr.py` — lazy `multiaddr`
|
||||||
|
- `tractor/ipc/_tcp.py`, `tractor/ipc/_uds.py` —
|
||||||
|
`Multiaddr` -> `TYPE_CHECKING`
|
||||||
|
- `tractor/runtime/_state.py` — lazy `platformdirs`
|
||||||
|
- `tractor/devx/_frame_stack.py` — lazy `pdbp` +
|
||||||
|
`wrapt`
|
||||||
|
- `tractor/devx/debug/_trace.py`,
|
||||||
|
`tractor/devx/debug/_tty_lock.py` — lazy `asyncio` +
|
||||||
|
`.to_asyncio`
|
||||||
|
- `tractor/spawn/_entry.py` — lazy
|
||||||
|
`run_as_asyncio_guest`
|
||||||
|
|
||||||
|
## Human edits
|
||||||
|
|
||||||
|
None yet — pending user review at the 12:50PM EST
|
||||||
|
pause gate (test-suite results reported in-session).
|
||||||
|
|
@ -0,0 +1,122 @@
|
||||||
|
---
|
||||||
|
model: claude-fable-5
|
||||||
|
service: claude
|
||||||
|
timestamp: 2026-07-02T15:56:26Z
|
||||||
|
git_ref: 65bf9df5
|
||||||
|
diff_cmd: git diff main..wkt/boot_latency_470
|
||||||
|
---
|
||||||
|
|
||||||
|
# Raw AI output — gh #470 `import tractor` latency trim
|
||||||
|
|
||||||
|
All generated code is committed on the
|
||||||
|
`wkt/boot_latency_470` branch; per diff-ref mode each
|
||||||
|
file's content is referenced via its diff instead of
|
||||||
|
copied verbatim.
|
||||||
|
|
||||||
|
## Profiling findings (verbatim analysis output)
|
||||||
|
|
||||||
|
Baseline: `import tractor` ~0.39-0.42s wall.
|
||||||
|
|
||||||
|
`python -X importtime` + `cProfile` traced the cost NOT
|
||||||
|
primarily to third-party deps (the issue's hypothesis)
|
||||||
|
but to `tractor/log.py:get_logger()` calling
|
||||||
|
`get_caller_mod()` -> `inspect.stack()` at module level
|
||||||
|
in ~39 tractor modules:
|
||||||
|
|
||||||
|
- `inspect.stack()` builds `FrameInfo` (incl. src-file
|
||||||
|
and line-context resolution) for EVERY frame on the
|
||||||
|
stack; during nested imports the stack is dozens of
|
||||||
|
importlib frames deep.
|
||||||
|
- each `FrameInfo` resolution calls
|
||||||
|
`inspect.getmodule()` which scans all of
|
||||||
|
`sys.modules` per frame (1.4M `ismodule()` calls in
|
||||||
|
one profiled import).
|
||||||
|
- aggregate: ~244ms of tractor-own module "self" time
|
||||||
|
vs ~20ms for ALL the issue-listed third-party deps
|
||||||
|
(`pdbp` ~10ms, `bidict` ~4.5ms, `multiaddr` ~3.5ms,
|
||||||
|
`wrapt`/`colorlog` ~1ms each); `trio` itself is
|
||||||
|
~70-100ms and unavoidable.
|
||||||
|
|
||||||
|
## Generated changes
|
||||||
|
|
||||||
|
> `git diff main..wkt/boot_latency_470 -- tractor/log.py`
|
||||||
|
|
||||||
|
`get_caller_mod()` rewritten from `inspect.stack()` +
|
||||||
|
`inspect.getmodule()` to `sys._getframe(frames_up)` +
|
||||||
|
`frame.f_globals['__name__']` -> `sys.modules` lookup
|
||||||
|
(O(1) vs O(stack x sys.modules)). Unused `inspect`
|
||||||
|
imports dropped; `FrameType` imported from `types`.
|
||||||
|
Also `colorlog` lazy-imported inside
|
||||||
|
`get_console_log()`.
|
||||||
|
|
||||||
|
> `git diff main..wkt/boot_latency_470 -- tractor/discovery/_addr.py`
|
||||||
|
|
||||||
|
`bidict` import moved under `TYPE_CHECKING`
|
||||||
|
(annotation-only use; `_address_types` is a plain dict
|
||||||
|
literal).
|
||||||
|
|
||||||
|
> `git diff main..wkt/boot_latency_470 -- tractor/discovery/_multiaddr.py`
|
||||||
|
|
||||||
|
`from __future__ import annotations` added; `multiaddr`
|
||||||
|
import moved under `TYPE_CHECKING` + function-local
|
||||||
|
imports in `mk_maddr()`/`parse_maddr()`.
|
||||||
|
|
||||||
|
> `git diff main..wkt/boot_latency_470 -- tractor/ipc/_tcp.py tractor/ipc/_uds.py`
|
||||||
|
|
||||||
|
`Multiaddr` imports moved under `TYPE_CHECKING`
|
||||||
|
(annotation-only in both transports).
|
||||||
|
|
||||||
|
> `git diff main..wkt/boot_latency_470 -- tractor/runtime/_state.py`
|
||||||
|
|
||||||
|
`platformdirs` lazy-imported inside `get_rt_dir()`
|
||||||
|
(NOTE: still imported eagerly via
|
||||||
|
`UDSAddress.def_bindspace` class-var eval; see
|
||||||
|
follow-ups).
|
||||||
|
|
||||||
|
> `git diff main..wkt/boot_latency_470 -- tractor/devx/_frame_stack.py`
|
||||||
|
|
||||||
|
`pdbp` + `wrapt` lazy-imported inside
|
||||||
|
`hide_runtime_frames()` / `api_frame()` respectively.
|
||||||
|
|
||||||
|
> `git diff main..wkt/boot_latency_470 -- tractor/devx/debug/_trace.py tractor/devx/debug/_tty_lock.py`
|
||||||
|
|
||||||
|
`asyncio` moved to `TYPE_CHECKING` + call-site local
|
||||||
|
imports (`asyncio.current_task()` sites);
|
||||||
|
`tractor.to_asyncio.run_trio_task_in_future` imports
|
||||||
|
moved into the infected-aio runtime branches.
|
||||||
|
|
||||||
|
> `git diff main..wkt/boot_latency_470 -- tractor/spawn/_entry.py`
|
||||||
|
|
||||||
|
`run_as_asyncio_guest` import moved into the
|
||||||
|
`infect_asyncio=True` branches of `_mp_main()` /
|
||||||
|
`_trio_main()`.
|
||||||
|
|
||||||
|
> `git diff main..wkt/boot_latency_470 -- tractor/__init__.py`
|
||||||
|
|
||||||
|
PEP 562 module `__getattr__` added so
|
||||||
|
`tractor.to_asyncio` attr-access still works (required
|
||||||
|
by `tests/test_child_manages_service_nursery.py` and
|
||||||
|
any downstream user) while keeping `asyncio` off the
|
||||||
|
eager import path.
|
||||||
|
|
||||||
|
## Measured results (verbatim)
|
||||||
|
|
||||||
|
- `import tractor`: 0.39-0.42s -> ~0.145s (~65% cut)
|
||||||
|
- `start_actor` spawn+boot+reg+cancel: ~0.40-0.44s ->
|
||||||
|
~0.179s/actor (n=5 sequential, warm parent)
|
||||||
|
- post-change eager-module check: only `pdbp` +
|
||||||
|
`platformdirs` of the issue's list remain eager.
|
||||||
|
|
||||||
|
## Known follow-ups (not implemented, deadline-bound)
|
||||||
|
|
||||||
|
- `pdbp` (~10ms): still eager via
|
||||||
|
`devx/debug/_repl.py` class bases
|
||||||
|
(`class PdbREPL(pdbp.Pdb)`) + `_tty_lock.py`
|
||||||
|
module-level `@pdbp.hideframe`; needs `_repl`
|
||||||
|
restructure + PEP 562 in `devx.debug.__init__`.
|
||||||
|
- `platformdirs` (~1.5ms): eager via
|
||||||
|
`UDSAddress.def_bindspace: ClassVar = get_rt_dir()`
|
||||||
|
class-body call; needs `Address`-protocol rework of
|
||||||
|
`def_bindspace` to a lazy accessor.
|
||||||
|
- `stackscope` + `setproctitle`: already lazy/absent —
|
||||||
|
no change needed.
|
||||||
|
|
@ -0,0 +1,78 @@
|
||||||
|
---
|
||||||
|
model: claude-fable-5
|
||||||
|
service: claude
|
||||||
|
session: f6c84722-471a-4458-9a80-e453fea9029f
|
||||||
|
timestamp: 2026-07-02T16:58:06Z
|
||||||
|
git_ref: a34aaf98
|
||||||
|
scope: code
|
||||||
|
substantive: true
|
||||||
|
raw_file: 20260702T165806Z_a34aaf98_prompt_io.raw.md
|
||||||
|
---
|
||||||
|
|
||||||
|
## Prompt
|
||||||
|
|
||||||
|
Follow-up round in the same session as the
|
||||||
|
`tractor.to_actor` landing (see
|
||||||
|
`20260702T154255Z_65bf9df5_prompt_io.md`). After
|
||||||
|
committing that work the user green-lit the deferred
|
||||||
|
items:
|
||||||
|
|
||||||
|
> go go go on this with what time you have left, in
|
||||||
|
> particular see if you can get the _ria_nursery
|
||||||
|
> removal going!
|
||||||
|
|
||||||
|
then extended the deadline twice to iterate on the
|
||||||
|
discovered hang:
|
||||||
|
|
||||||
|
> continue on this up until a 12:58:30 deadline
|
||||||
|
|
||||||
|
and finally chose "Commit step A now" from the
|
||||||
|
next-steps prompt.
|
||||||
|
|
||||||
|
## Response summary
|
||||||
|
|
||||||
|
Step A of the `._ria_nursery` removal (issue #477): hoist
|
||||||
|
`.run_in_actor()` result-reaping out of the spawn
|
||||||
|
backends into the `ActorNursery` machinery so ria
|
||||||
|
children spawn via the default daemon nursery,
|
||||||
|
|
||||||
|
- new `_supervise._reap_ria_portals()` runs one
|
||||||
|
`_spawn.cancel_on_completion()` task per ria child
|
||||||
|
AFTER `._join_procs` is set; happy path awaits it
|
||||||
|
right after `._join_procs.set()`.
|
||||||
|
- error path SEQUENCES: snapshot ria
|
||||||
|
`(portal, subactor)` pairs -> `await an.cancel()` ->
|
||||||
|
0.5s-bounded reap. Two failed intermediates informed
|
||||||
|
this: a concurrent reap+cancel DEADLOCKED
|
||||||
|
`test_multierror`; a 3s bound blew
|
||||||
|
`test_cancel_while_childs_child_in_sync_sleep`'s
|
||||||
|
`fail_after` deadline.
|
||||||
|
- backends (`spawn/_trio.py`, `spawn/_mp.py`) lose the
|
||||||
|
`._cancel_after_result_on_exit` membership branch,
|
||||||
|
per-child reaper nursery + dead imports.
|
||||||
|
- design/probe-history doc:
|
||||||
|
`ai/conc-anal/ria_nursery_removal_plan.md` (from an
|
||||||
|
agent-verified machinery map).
|
||||||
|
|
||||||
|
Verification: `test_cancellation.py` fully green
|
||||||
|
(20 passed, 1 xfailed) incl. the previously-hung
|
||||||
|
`test_multierror`; `test_to_actor`+`test_spawning`
|
||||||
|
20/20; bounded full-suite gate SIGINT'd ~30s early at
|
||||||
|
303 passed / 0 failures (user opted to commit on that
|
||||||
|
signal, deferring the unbounded re-run to step-B
|
||||||
|
verification).
|
||||||
|
|
||||||
|
## Files changed
|
||||||
|
|
||||||
|
- `tractor/runtime/_supervise.py` — `_reap_ria_portals()`
|
||||||
|
+ two call-sites; `run_in_actor()` off the ria nursery
|
||||||
|
- `tractor/spawn/_trio.py` — reaper branch + import drop
|
||||||
|
- `tractor/spawn/_mp.py` — same as `_trio.py`
|
||||||
|
- `ai/conc-anal/ria_nursery_removal_plan.md` — plan +
|
||||||
|
probe history
|
||||||
|
|
||||||
|
## Human edits
|
||||||
|
|
||||||
|
None yet — committed via the drafted
|
||||||
|
`.claude/git_commit_msg_ria_step_a.md` (user-driven
|
||||||
|
`git commit --edit`).
|
||||||
|
|
@ -0,0 +1,55 @@
|
||||||
|
---
|
||||||
|
model: claude-fable-5
|
||||||
|
service: claude
|
||||||
|
timestamp: 2026-07-02T16:58:06Z
|
||||||
|
git_ref: a34aaf98
|
||||||
|
diff_cmd: git diff a34aaf98..wkt/to_actor_subpkg
|
||||||
|
---
|
||||||
|
|
||||||
|
# Raw AI output (diff-ref mode)
|
||||||
|
|
||||||
|
Step-A code is committed on `wkt/to_actor_subpkg`
|
||||||
|
directly after `a34aaf98`; per diff-ref mode the verbatim
|
||||||
|
content is reachable via the pointers below.
|
||||||
|
|
||||||
|
## Generated files
|
||||||
|
|
||||||
|
> `git diff a34aaf98..wkt/to_actor_subpkg -- tractor/runtime/_supervise.py`
|
||||||
|
|
||||||
|
New `_reap_ria_portals(an, errors, ria_children=None)`
|
||||||
|
helper (one `_spawn.cancel_on_completion()` task per ria
|
||||||
|
child under `collapse_eg()` + a local nursery);
|
||||||
|
`run_in_actor()` drops `nursery=self._ria_nursery`; happy
|
||||||
|
path awaits the reap right after `._join_procs.set()`;
|
||||||
|
inner error handler snapshots ria pairs, runs
|
||||||
|
`await an.cancel()` then a `move_on_after(0.5)`-bounded
|
||||||
|
reap over the snapshot.
|
||||||
|
|
||||||
|
> `git diff a34aaf98..wkt/to_actor_subpkg -- tractor/spawn/_trio.py`
|
||||||
|
> `git diff a34aaf98..wkt/to_actor_subpkg -- tractor/spawn/_mp.py`
|
||||||
|
|
||||||
|
Both backends: the post-`_join_procs` block collapses to
|
||||||
|
a bare `soft_kill()` (membership branch, per-child reaper
|
||||||
|
nursery, reaper-cancel logging and the now-unused
|
||||||
|
`cancel_on_completion` imports all removed).
|
||||||
|
|
||||||
|
> `git diff a34aaf98..wkt/to_actor_subpkg -- ai/conc-anal/ria_nursery_removal_plan.md`
|
||||||
|
|
||||||
|
Agent-verified machinery map, 3-step design (A/B/C),
|
||||||
|
probe history (deadlock -> sequencing fix -> bound
|
||||||
|
tightening) and risk register.
|
||||||
|
|
||||||
|
## Test runs (verbatim)
|
||||||
|
|
||||||
|
```
|
||||||
|
tests/test_cancellation.py: 20 passed, 1 xfailed in 77.28s
|
||||||
|
tests/test_to_actor.py + tests/test_spawning.py: 20 passed
|
||||||
|
full-suite (SIGINT at 540s bound, ~30s short):
|
||||||
|
303 passed, 9 skipped, 1 xfailed, 2 xpassed in 539.42s
|
||||||
|
```
|
||||||
|
|
||||||
|
Failed intermediates (not in final diff): concurrent
|
||||||
|
reap+`an.cancel()` deadlocked `test_multierror`; a 3s
|
||||||
|
reap bound blew
|
||||||
|
`test_cancel_while_childs_child_in_sync_sleep`'s
|
||||||
|
`fail_after` deadline.
|
||||||
|
|
@ -0,0 +1,60 @@
|
||||||
|
---
|
||||||
|
model: claude-opus-4-8
|
||||||
|
service: claude
|
||||||
|
session: c7860302-6a33-4d92-821c-0807d68be464
|
||||||
|
timestamp: 2026-07-02T17:22:33Z
|
||||||
|
git_ref: 5cd190c5
|
||||||
|
scope: code
|
||||||
|
substantive: true
|
||||||
|
raw_file: 20260702T172233Z_5cd190c5_prompt_io.raw.md
|
||||||
|
---
|
||||||
|
|
||||||
|
## Prompt
|
||||||
|
|
||||||
|
Branched session (`to_actor_subpkg_POST_WEEKLY_CUTOFF_2OPUS`,
|
||||||
|
now on Opus 4.8) continuing the `._ria_nursery` removal after
|
||||||
|
step A committed + pushed (`5cd190c5`/`99310269`):
|
||||||
|
|
||||||
|
> ok continue where your superior left off please
|
||||||
|
|
||||||
|
then, after the commit-split discussion:
|
||||||
|
|
||||||
|
> yup committed and pushed so take a look ma fren.
|
||||||
|
|
||||||
|
i.e. green-lighting step-B prep against the committed step-A
|
||||||
|
tree.
|
||||||
|
|
||||||
|
## Response summary
|
||||||
|
|
||||||
|
Step B of the `._ria_nursery` removal (issue #477): delete
|
||||||
|
the now-vestigial secondary nursery,
|
||||||
|
|
||||||
|
- collapse the inner `async with (collapse_eg(),
|
||||||
|
trio.open_nursery() as ria_nursery)` layer in
|
||||||
|
`_open_and_supervise_one_cancels_all_nursery` — `da_nursery`
|
||||||
|
becomes the single nursery for ALL subactors.
|
||||||
|
- `ActorNursery.__init__` drops the `ria_nursery` param + the
|
||||||
|
`self._ria_nursery` attr; `start_actor()` drops its
|
||||||
|
`nursery=` escape-hatch param.
|
||||||
|
- `._cancel_after_result_on_exit` kept (ria-child
|
||||||
|
discriminator for `_reap_ria_portals()`).
|
||||||
|
|
||||||
|
Verified behavior-preserving via a first-principles argument
|
||||||
|
(zero-task nursery = a bare checkpoint) + the targeted gate
|
||||||
|
(`test_cancellation test_spawning test_local test_rpc
|
||||||
|
test_to_actor` = 49 passed, 1 xfailed on trio). The two
|
||||||
|
error handlers were deliberately NOT merged — that changes
|
||||||
|
propagation semantics and is deferred to its own PR (TODO
|
||||||
|
left at the outer `except`).
|
||||||
|
|
||||||
|
## Files changed
|
||||||
|
|
||||||
|
- `tractor/runtime/_supervise.py` — collapse the ria nursery
|
||||||
|
layer + drop the ctor/`start_actor` params + refresh the
|
||||||
|
now-stale nursery comments
|
||||||
|
|
||||||
|
## Human edits
|
||||||
|
|
||||||
|
None yet — committed via the drafted
|
||||||
|
`.claude/git_commit_msg_ria_step_b.md` (user-driven
|
||||||
|
`git commit --edit`).
|
||||||
|
|
@ -0,0 +1,51 @@
|
||||||
|
---
|
||||||
|
model: claude-opus-4-8
|
||||||
|
service: claude
|
||||||
|
timestamp: 2026-07-02T17:22:33Z
|
||||||
|
git_ref: 5cd190c5
|
||||||
|
diff_cmd: git diff 5cd190c5..wkt/to_actor_subpkg
|
||||||
|
---
|
||||||
|
|
||||||
|
# Raw AI output (diff-ref mode)
|
||||||
|
|
||||||
|
Step-B code lives on `wkt/to_actor_subpkg` after `5cd190c5`;
|
||||||
|
per diff-ref mode the verbatim content is reachable via the
|
||||||
|
pointer below.
|
||||||
|
|
||||||
|
## Generated files
|
||||||
|
|
||||||
|
> `git diff 5cd190c5..wkt/to_actor_subpkg -- tractor/runtime/_supervise.py`
|
||||||
|
|
||||||
|
- `ActorNursery.__init__`: `ria_nursery` param removed;
|
||||||
|
`self._ria_nursery = ria_nursery` block deleted;
|
||||||
|
`_cancel_after_result_on_exit` comment refreshed.
|
||||||
|
- `start_actor()`: `nursery=` param removed; body uses
|
||||||
|
`self._da_nursery.start(...)` directly.
|
||||||
|
- `_open_and_supervise_one_cancels_all_nursery()`: the inner
|
||||||
|
`async with (collapse_eg(), trio.open_nursery() as
|
||||||
|
ria_nursery)` layer removed; `an = ActorNursery(actor,
|
||||||
|
da_nursery, errors)` constructed once under the single
|
||||||
|
`da_nursery`; the inner-try body de-indented one level;
|
||||||
|
both error handlers retained; the da-nursery lead comment
|
||||||
|
and the outer-`except` TODO refreshed to describe the
|
||||||
|
single-nursery reality + flag the (deferred) handler-merge.
|
||||||
|
|
||||||
|
> `git diff 5cd190c5..wkt/to_actor_subpkg -- ai/conc-anal/ria_nursery_removal_plan.md`
|
||||||
|
|
||||||
|
Added a "Step-B outcome" section (collapse rationale,
|
||||||
|
handler-merge deferral, safety argument, gate result).
|
||||||
|
|
||||||
|
## Test runs (verbatim)
|
||||||
|
|
||||||
|
```
|
||||||
|
targeted gate (trio):
|
||||||
|
tests/test_cancellation.py tests/test_spawning.py
|
||||||
|
tests/test_local.py tests/test_rpc.py tests/test_to_actor.py
|
||||||
|
-> 49 passed, 1 xfailed in 88.62s
|
||||||
|
|
||||||
|
signature checks:
|
||||||
|
ActorNursery.__init__ params: ['self', 'actor', 'da_nursery', 'errors']
|
||||||
|
start_actor has nursery param: False
|
||||||
|
|
||||||
|
full-suite gate: in progress (see stepB_full.log)
|
||||||
|
```
|
||||||
|
|
@ -0,0 +1,68 @@
|
||||||
|
---
|
||||||
|
model: claude-opus-4-8
|
||||||
|
service: claude
|
||||||
|
session: c7860302-6a33-4d92-821c-0807d68be464
|
||||||
|
timestamp: 2026-07-02T22:25:44Z
|
||||||
|
git_ref: 9201a2ed
|
||||||
|
scope: code
|
||||||
|
substantive: true
|
||||||
|
raw_file: 20260702T222544Z_9201a2ed_prompt_io.raw.md
|
||||||
|
---
|
||||||
|
|
||||||
|
## Prompt
|
||||||
|
|
||||||
|
Continuing on branch `drop_ria_nursery` after step B
|
||||||
|
(`9201a2ed`). User green-lit the deferred error-handler merge:
|
||||||
|
|
||||||
|
> yes, i checked us out on a new branch for the ria-tn removal;
|
||||||
|
> work off the new wkt branch drop_ria_nursery.
|
||||||
|
|
||||||
|
and (re the migration) "nope, later we'll do this", plus:
|
||||||
|
|
||||||
|
> do ensure the test suite is green in the current state and
|
||||||
|
> those going forward ya?
|
||||||
|
|
||||||
|
and the standing reminder: no commits without a human ack.
|
||||||
|
|
||||||
|
## Response summary
|
||||||
|
|
||||||
|
Step B2 of the `._ria_nursery` removal (issue #477): merge the
|
||||||
|
two nested error handlers in
|
||||||
|
`_open_and_supervise_one_cancels_all_nursery` into one, now
|
||||||
|
that the 2ndary nursery (whose `__aexit__` the outer handler
|
||||||
|
originally guarded) is gone.
|
||||||
|
|
||||||
|
Trace-driven justification (see
|
||||||
|
`ai/conc-anal/ria_nursery_removal_plan.md` Step-B2 section):
|
||||||
|
the old inner handler records `errors[uid]` as its FIRST
|
||||||
|
action, so `errors` is always non-empty by the time control
|
||||||
|
could reach the outer handler — making the outer handler's
|
||||||
|
`raise` DEAD (always superseded by the `finally`'s
|
||||||
|
raise-from-`errors`). Collapsing to a single
|
||||||
|
`except BaseException as _scope_err` + the existing `finally`
|
||||||
|
is therefore behavior-preserving.
|
||||||
|
|
||||||
|
- removed the outer `try` + the
|
||||||
|
`except (Exception, BaseExceptionGroup, trio.Cancelled)`
|
||||||
|
handler; de-indented the inner handler one level.
|
||||||
|
- dropped the now-unused `outer_err`/`inner_err` locals
|
||||||
|
(single `_scope_err`).
|
||||||
|
- net ~30 lines lighter.
|
||||||
|
|
||||||
|
Gated hard: baseline (pre-B2 tip) = 300 passed + 1
|
||||||
|
load-induced `TooSlowError` flake (passes 6/6 in isolation);
|
||||||
|
B2 error/cancel gate (`test_cancellation remote_exc_relay
|
||||||
|
inter_peer_cancellation advanced_faults oob_cancellation
|
||||||
|
to_actor spawning local rpc`) = 71 passed, 1 xfailed; B2
|
||||||
|
full-suite run following.
|
||||||
|
|
||||||
|
## Files changed
|
||||||
|
|
||||||
|
- `tractor/runtime/_supervise.py` — collapse the two handlers
|
||||||
|
into one; drop `outer_err`/`inner_err`
|
||||||
|
|
||||||
|
## Human edits
|
||||||
|
|
||||||
|
None yet — committed via the drafted
|
||||||
|
`.claude/git_commit_msg_ria_b2.md` (user-driven
|
||||||
|
`git commit --edit`).
|
||||||
|
|
@ -0,0 +1,55 @@
|
||||||
|
---
|
||||||
|
model: claude-opus-4-8
|
||||||
|
service: claude
|
||||||
|
timestamp: 2026-07-02T22:25:44Z
|
||||||
|
git_ref: 9201a2ed
|
||||||
|
diff_cmd: git diff 9201a2ed..drop_ria_nursery
|
||||||
|
---
|
||||||
|
|
||||||
|
# Raw AI output (diff-ref mode)
|
||||||
|
|
||||||
|
Step-B2 code lives on `drop_ria_nursery` after `9201a2ed`; per
|
||||||
|
diff-ref mode the verbatim content is reachable via the pointer
|
||||||
|
below.
|
||||||
|
|
||||||
|
## Generated files
|
||||||
|
|
||||||
|
> `git diff 9201a2ed..drop_ria_nursery -- tractor/runtime/_supervise.py`
|
||||||
|
|
||||||
|
`_open_and_supervise_one_cancels_all_nursery`:
|
||||||
|
- removed the outer `try:` wrapper and the
|
||||||
|
`except (Exception, BaseExceptionGroup, trio.Cancelled) as
|
||||||
|
_outer_err:` safety-net handler.
|
||||||
|
- the former inner `except BaseException` is now THE handler,
|
||||||
|
renamed local `_inner_err` -> `_scope_err`, de-indented one
|
||||||
|
level; it sets `an._scope_error`, records `errors[uid]`,
|
||||||
|
waits on the debugger, `_join_procs.set()`, then a shielded
|
||||||
|
classify/log + snapshot-ria + `an.cancel()` + 0.5s-bounded
|
||||||
|
`_reap_ria_portals()`. No re-raise (the `finally` raises
|
||||||
|
from `errors`).
|
||||||
|
- `finally` block unchanged.
|
||||||
|
- dropped the `outer_err`/`inner_err` local decls at fn top.
|
||||||
|
|
||||||
|
(The diff is large — ~119+/149- — because de-indenting the
|
||||||
|
handler body one level rewrites every line in the block; the
|
||||||
|
logic delta is just "two handlers -> one".)
|
||||||
|
|
||||||
|
## Test runs (verbatim)
|
||||||
|
|
||||||
|
```
|
||||||
|
baseline (pre-B2, step-B tip 9201a2ed), full suite
|
||||||
|
(dynamic_pub_sub deselected):
|
||||||
|
1 failed, 300 passed, 9 skipped, 2 deselected, 1 xfailed,
|
||||||
|
2 xpassed in 1499.49s
|
||||||
|
-> the 1 failure = test_ext_types_over_ipc[...] trio.TooSlowError
|
||||||
|
(load-induced; passes 6/6 in isolation in 4.89s)
|
||||||
|
|
||||||
|
B2 error/cancel gate:
|
||||||
|
tests/test_cancellation test_remote_exc_relay
|
||||||
|
test_inter_peer_cancellation test_advanced_faults
|
||||||
|
test_oob_cancellation test_to_actor test_spawning test_local
|
||||||
|
test_rpc
|
||||||
|
-> 71 passed, 1 xfailed in 125.26s
|
||||||
|
|
||||||
|
B2 full-suite run: see b2_full.log
|
||||||
|
```
|
||||||
|
|
@ -0,0 +1,81 @@
|
||||||
|
---
|
||||||
|
model: claude-fable-5
|
||||||
|
service: claude
|
||||||
|
session: 6db64ac6-6986-4505-9343-df4ee31e67db
|
||||||
|
timestamp: 2026-07-06T17:28:18Z
|
||||||
|
git_ref: ad42871e
|
||||||
|
scope: code
|
||||||
|
substantive: true
|
||||||
|
raw_file: 20260706T172818Z_ad42871e_prompt_io.raw.md
|
||||||
|
---
|
||||||
|
|
||||||
|
## Prompt
|
||||||
|
|
||||||
|
Resuming the `drop_ria_nursery` (wkt) branch after the human's
|
||||||
|
review pause at `ba4af0ad`, with the directive:
|
||||||
|
|
||||||
|
> we need to resolve the remaining outstandings ... to finish
|
||||||
|
> this endeavour we ideally,
|
||||||
|
> - finish the ria-tn hoist into the machinery in `to_actor` if
|
||||||
|
> possible.
|
||||||
|
> - avoid supporting the `.run_in_actor()` non-blocking API
|
||||||
|
> going forward other then if we can re-wrap `to_actor.run()`
|
||||||
|
> per prior discussion with opus4.8.
|
||||||
|
> - get the test harness completely green locally and in CI -
|
||||||
|
> AND ESPECIALLY without any hangs due to the "the old
|
||||||
|
> spawn-backend reaper raced `soft_kill()`'s scope-cancel"
|
||||||
|
> - update all docs to reflect these changes once we're happy.
|
||||||
|
|
||||||
|
plus mid-flight additions:
|
||||||
|
|
||||||
|
> make sure both all the test and example code uses the naming
|
||||||
|
> `an: ActorNursery` for variable names
|
||||||
|
|
||||||
|
> where we at in terms of a potential hackaround design for
|
||||||
|
> supporting a to_actor.run_async() ... without requiring the
|
||||||
|
> same malarky ... with a `trio.Event` relay ... i'm totes fine
|
||||||
|
> with rejigging the iface to NOT return a Portal as well.
|
||||||
|
|
||||||
|
## Response summary
|
||||||
|
|
||||||
|
Completed the entire #477 `run_in_actor` removal on
|
||||||
|
`drop_ria_nursery` (16 commits this session), gated per-commit:
|
||||||
|
|
||||||
|
- ported the remaining `test_cancellation` groups
|
||||||
|
(`some_cancels_all`, `nested_multierrors`+`spawn_and_error`,
|
||||||
|
SIGINT + sync-sleep) — one-shots become concurrent
|
||||||
|
`to_actor.run()` tasks in local task-nurseries; BEG-shape
|
||||||
|
assertions loosened for cancel-on-first + the runtime's
|
||||||
|
`collapse_eg()` single-member unwrap (a fully-raced nested
|
||||||
|
tree relays a bare annotated `RemoteActorError` chain).
|
||||||
|
- fixed a pre-existing `UnboundLocalError` (`timeout` `match`
|
||||||
|
had no default arm for non-trio/MTF backends).
|
||||||
|
- ported `test_dynamic_pub_sub`, 4 non-debugging examples, all
|
||||||
|
8 `debugging/` examples (debugger suite byte-identical,
|
||||||
|
28p/6s; `multi_subactors` introduces the "collect don't
|
||||||
|
cancel" reap-all replacement pattern), 8 docs pages + the
|
||||||
|
`experimental/_pubsub` docstring.
|
||||||
|
- EXCISED the API + cluster: `run_in_actor`,
|
||||||
|
`_reap_ria_portals`, `_cancel_after_result_on_exit`,
|
||||||
|
`Portal._submit_for_result/_expect_result_ctx/
|
||||||
|
wait_for_result/result`, `exhaust_portal`,
|
||||||
|
`cancel_on_completion`, `NoResult` — net -402 lines. The
|
||||||
|
reap-hang class dissolves structurally (result-waits now only
|
||||||
|
in caller task-scope).
|
||||||
|
- found + fixed a real migration race: mutual-rendezvous peers
|
||||||
|
(`test_trynamic_trio`, `a_trynamic_first_scene.py`) flaked
|
||||||
|
because an eagerly-reaped one-shot dies while its peer still
|
||||||
|
dials the registry-resolved (dead) sockaddr — such peers now
|
||||||
|
pin lifetimes via `start_actor()` + concurrent `Portal.run()`
|
||||||
|
+ explicit `an.cancel()`.
|
||||||
|
- `an: ActorNursery` naming sweep across tests/examples (±82
|
||||||
|
lines, scoped renames, prose untouched).
|
||||||
|
- parked a `to_actor.open_one_shot()` design sketch (acm +
|
||||||
|
private task-nursery over blocking `run()`; done-Event as
|
||||||
|
memo not cancel-relay; no Portal) in the plan doc.
|
||||||
|
|
||||||
|
## Files changed
|
||||||
|
|
||||||
|
See commits `d01a2123..ad42871e` on `drop_ria_nursery`
|
||||||
|
(tests, examples, docs, `tractor/{runtime,spawn,to_actor,msg}`
|
||||||
|
+ `_exceptions/_context/experimental`).
|
||||||
|
|
@ -0,0 +1,39 @@
|
||||||
|
---
|
||||||
|
model: claude-fable-5
|
||||||
|
service: claude
|
||||||
|
timestamp: 2026-07-06T17:28:18Z
|
||||||
|
git_ref: ad42871e
|
||||||
|
diff_cmd: git diff ba4af0ad..ad42871e
|
||||||
|
---
|
||||||
|
|
||||||
|
# Raw AI output (diff-ref mode)
|
||||||
|
|
||||||
|
This session's output spans the 16 migration/excision commits
|
||||||
|
`d01a2123..ad42871e` on `drop_ria_nursery`; per diff-ref mode
|
||||||
|
the verbatim content is reachable via the pointer below.
|
||||||
|
|
||||||
|
## Generated files
|
||||||
|
|
||||||
|
> `git diff ba4af0ad..ad42871e`
|
||||||
|
|
||||||
|
Commit-wise (each `Gate:`-footed msg documents its own module
|
||||||
|
gate):
|
||||||
|
|
||||||
|
- `d01a2123` port `test_some_cancels_all`
|
||||||
|
- `697c6152` fix unbound `timeout` (non-trio/MTF `match` arm)
|
||||||
|
- `fa8799d5` port `test_nested_multierrors`
|
||||||
|
- `f11754ce` port SIGINT + sync-sleep cancel tests
|
||||||
|
- `cb6202e3` port `test_dynamic_pub_sub`
|
||||||
|
- `d8af5f12` port non-debugging examples
|
||||||
|
- `a3057cb2` port debugging examples (+ `test_debugger`
|
||||||
|
nested-nurseries final-shape expectations)
|
||||||
|
- `d6bed7c4` port docs (8 rst pages)
|
||||||
|
- `07e1669e` fix stale `@pub` docstring example
|
||||||
|
- `2a59cefb` REMOVE `run_in_actor()` + the ria reap cluster
|
||||||
|
(net -402 lines)
|
||||||
|
- `a297a32a` fix mutual-rendezvous premature-reap race
|
||||||
|
- `ad42871e` `an: ActorNursery` naming sweep
|
||||||
|
|
||||||
|
Plan/design record updated in
|
||||||
|
`ai/conc-anal/ria_nursery_removal_plan.md` (RESOLVED section +
|
||||||
|
the `to_actor.open_one_shot()` follow-up sketch).
|
||||||
|
|
@ -0,0 +1,136 @@
|
||||||
|
---
|
||||||
|
model: claude-opus-5
|
||||||
|
service: claude
|
||||||
|
session: 7b9c97c4-fff7-4ac4-97fb-35720453308e
|
||||||
|
timestamp: 2026-08-13T00:11:02Z
|
||||||
|
git_ref: 27c34aeb
|
||||||
|
scope: docs+code
|
||||||
|
substantive: true
|
||||||
|
raw_file: 20260813T001102Z_27c34aeb_prompt_io.raw.md
|
||||||
|
---
|
||||||
|
|
||||||
|
## Prompt
|
||||||
|
|
||||||
|
> draft hyper detailed implementation plans for [three]
|
||||||
|
> prospective new transport (tpt) backends for tractor's `.ipc`
|
||||||
|
> layer, from four GitHub issues: TIPC (gh #378) using built-in
|
||||||
|
> linux socket API w/ `trio` interfacing, leveraging TIPC's
|
||||||
|
> built-in discovery machinery; QUIC (gh #353) using the `iroh`
|
||||||
|
> lib, ideally with the py asyncio support (via ffi) rewritten
|
||||||
|
> for trio; wg (gh #482 and/or #443) with other shuttle-able
|
||||||
|
> tpts, using `pyroute2`, as much trio wrapping as possible
|
||||||
|
> where any other async support can be replaced.
|
||||||
|
|
||||||
|
With constraints: "be only slightly speculative"; realistic given
|
||||||
|
the linked info; the plans must be workable "by another
|
||||||
|
model/provider without much discrepancy on the design or lib
|
||||||
|
selections needed"; do it all in a new `/open-wkt` off main; and
|
||||||
|
be "as modular and modern-async-python-as-nearly-functional-style
|
||||||
|
as possible."
|
||||||
|
|
||||||
|
Key follow-up prompts that changed the design mid-session:
|
||||||
|
|
||||||
|
> wg requires binding a socket on both ends of the tunnel, the
|
||||||
|
> new support we added to `py-multiaddr` in their #108 enables
|
||||||
|
> declaring this in the maddr? […] so the
|
||||||
|
> `/ip4/10.0.0.1/udp/666/ip4/192.168.0.13/tcp/80` bullet example
|
||||||
|
> from #443 isn't correct for a wg tunnel maddr and would
|
||||||
|
> actually end up looking like,
|
||||||
|
> `/ip4/10.0.0.1/udp/666/wg/<pub-key>/ip4/192.168.0.13/tcp/80` no?
|
||||||
|
|
||||||
|
> right, we're likely going to need less `tuple`-formed
|
||||||
|
> `UnwrappedAddress` usage to make them all work and/or to extend
|
||||||
|
> the unwrapped form with a proto-str, likely the key from
|
||||||
|
> `multiaddrs` no?
|
||||||
|
|
||||||
|
> better encapsulating `UnwrappedAddress` for use externally is
|
||||||
|
> really way overdue; we should likely be encouraging (if not
|
||||||
|
> preventing unwrapped) wrapped addrs always much like other
|
||||||
|
> (std) libs, `ipaddress` comes to mind.
|
||||||
|
|
||||||
|
> so iirc netns mgmt becomes a lower-level runtime config-api
|
||||||
|
> rather then an actor-app-code API which can be dynamically
|
||||||
|
> changed once the actor is up ya?
|
||||||
|
|
||||||
|
## Response summary
|
||||||
|
|
||||||
|
A research-and-planning branch, `ng_tpts_planning` — **no runtime
|
||||||
|
code touched**. Five plan docs under `ai/tpt-backends/` plus a
|
||||||
|
corrected `wg` example set under `examples/multihost/wg_lan/`,
|
||||||
|
across 9 commits.
|
||||||
|
|
||||||
|
Three design conclusions came out of the planning that outlive
|
||||||
|
the branch:
|
||||||
|
|
||||||
|
1. **`wg` is a bindspace, not a `MsgTransport`.** The `/wg/`
|
||||||
|
maddr segment is *infix*: bearer (underlay ip+udp
|
||||||
|
`ListenPort`, bound by the kernel) → `/wg/u<key>` (an
|
||||||
|
identity, bound by nothing) → overlay (the only part tractor
|
||||||
|
binds). Verified empirically by installing
|
||||||
|
`baudco/py-multiaddr@wg_support` (py-multiaddr#108) in a
|
||||||
|
throwaway venv and round-tripping each candidate form; gh
|
||||||
|
#482's original suffix form parses but is semantically
|
||||||
|
inverted.
|
||||||
|
2. **Proto-key the `UnwrappedAddress` form**, spelled with the
|
||||||
|
`multiaddr` protocol names, dispatched via
|
||||||
|
`_address_types[addr[0]]`. Kills a whole collision class
|
||||||
|
(TIPC's `(str, int)` ≡ `TCPAddress`; iroh's `(str, str)`
|
||||||
|
swallowed by the UDS case) and is the recommended migration
|
||||||
|
*before* any new backend lands.
|
||||||
|
3. **netns is a runtime/boot-time config API, not an app-code
|
||||||
|
one** — `setns(2)` is per-thread and won't move
|
||||||
|
already-created sockets, so there is deliberately no
|
||||||
|
`await actor.enter_netns(...)`.
|
||||||
|
|
||||||
|
Also verified that `trio.SocketStream`/`SocketListener` are
|
||||||
|
address-family agnostic (no `AF_*` check anywhere), which is what
|
||||||
|
makes TIPC the cheapest of the three backends to add.
|
||||||
|
|
||||||
|
Four related issues were annotated with the results (#378, #353,
|
||||||
|
#482, #443); #443's body was rewritten to reflect the corrected
|
||||||
|
grammar, with no existing checkbox state changed.
|
||||||
|
|
||||||
|
## Files changed
|
||||||
|
|
||||||
|
- `ai/tpt-backends/00_shared_backend_contract.md` — normative
|
||||||
|
backend duck-type contract, registration checklist, §1.1
|
||||||
|
proto-key conclusion
|
||||||
|
- `ai/tpt-backends/01_tipc_backend.md` — TIPC plan; service
|
||||||
|
addressing, `TIPC_TOP_SRV` push registry, instance-collision
|
||||||
|
hazard, step-0 probe
|
||||||
|
- `ai/tpt-backends/02_quic_iroh_backend.md` — `iroh` plan;
|
||||||
|
`uniffi`→`trio` bridge, listener/stream adapters, API-truth
|
||||||
|
table
|
||||||
|
- `ai/tpt-backends/03_wg_tunnel_bindspace.md` — `wg`-as-bindspace
|
||||||
|
plan; verified maddr grammar, 3-owner split, netns reality
|
||||||
|
- `ai/tpt-backends/README.md` — index
|
||||||
|
- `examples/multihost/wg_lan/wg_maddr.py` — frozen `msgspec`
|
||||||
|
tunnelled addr + pure parse/render helpers; impure
|
||||||
|
`verify_wg_peer()` kept separate
|
||||||
|
- `examples/multihost/wg_lan/host_a_srv.py` — host-A actor tree
|
||||||
|
- `examples/multihost/wg_lan/host_b_client.py` — host-B dialer
|
||||||
|
- `examples/multihost/wg_lan/README.md` — grammar, owner table,
|
||||||
|
setup, "what changed vs #482"
|
||||||
|
|
||||||
|
## Human edits
|
||||||
|
|
||||||
|
Substantial human steering rather than post-hoc editing; the
|
||||||
|
corrections were applied by the model in-session after being
|
||||||
|
challenged:
|
||||||
|
|
||||||
|
- rejected an initial claim that `wg` has "nothing to bind at the
|
||||||
|
tunnel layer" and supplied the correct composed maddr form,
|
||||||
|
which forced a rewrite of plan 03 §3.2 and a retraction in the
|
||||||
|
already-posted #443 comment
|
||||||
|
- rejected a supporting claim that `/ip4/../udp/443/quic-v1` was
|
||||||
|
"also composed"
|
||||||
|
- directed the proto-key/`ipaddress`-discipline conclusion and
|
||||||
|
the netns-as-runtime-config framing, both of which were then
|
||||||
|
folded back into the docs
|
||||||
|
- chose the commit boundaries and authored all commits; ran every
|
||||||
|
`git` mutation (commit, rebase, push) themselves
|
||||||
|
|
||||||
|
One model-initiated correction pre-publication: a self-review
|
||||||
|
downgraded two overconfident claims (the `uniffi`/asyncio thesis
|
||||||
|
and TIPC duplicate-binder behaviour) to explicitly-flagged
|
||||||
|
assumptions before the #353/#378 comments were posted.
|
||||||
|
|
@ -0,0 +1,165 @@
|
||||||
|
---
|
||||||
|
model: claude-opus-5
|
||||||
|
service: claude
|
||||||
|
timestamp: 2026-08-13T00:11:02Z
|
||||||
|
git_ref: 27c34aeb
|
||||||
|
diff_cmd: git diff main..ng_tpts_planning
|
||||||
|
---
|
||||||
|
|
||||||
|
# Raw output — next-gen tpt-backend implementation plans
|
||||||
|
|
||||||
|
## Generated planning docs
|
||||||
|
|
||||||
|
> `git diff main..ng_tpts_planning -- ai/tpt-backends/`
|
||||||
|
|
||||||
|
Five markdown docs. `00_shared_backend_contract.md` is normative
|
||||||
|
and the other three are written against it so they can be worked
|
||||||
|
independently:
|
||||||
|
|
||||||
|
- **`00_shared_backend_contract.md`** — the backend duck-type
|
||||||
|
(`<Proto>Address(msgspec.Struct, frozen=True)` + module-level
|
||||||
|
`start_listener()`/`close_listener()` + a
|
||||||
|
`Msgpack<Proto>Stream(MsgpackTransport)`), the
|
||||||
|
`inspect.getmodule(self.addr)` reflection in
|
||||||
|
`Endpoint.start_listener()` that forces the Address class and
|
||||||
|
its listener fns to share a module, a 10-item registration
|
||||||
|
checklist, the dep policy, the test-harness shape, and §1.1's
|
||||||
|
proto-key conclusion (below).
|
||||||
|
- **`01_tipc_backend.md`** — service addressing via
|
||||||
|
`TIPC_ADDR_NAMESEQ` (bind/publish) and `TIPC_ADDR_NAME`
|
||||||
|
(connect/lookup), `TIPC_TOP_SRV` topology subscriptions as a
|
||||||
|
push-based registry, the `get_random()` instance-collision
|
||||||
|
hazard, and a step-0 capability-probe spike.
|
||||||
|
- **`02_quic_iroh_backend.md`** — `iroh` over
|
||||||
|
`aioquic`/`quiche`/`trio-asyncio`, a `_uniffi_trio.py` bridge
|
||||||
|
built on `TrioToken.run_sync_soon()`, `trio.abc.Listener`/
|
||||||
|
`HalfCloseableStream` adapters, and an API-truth table to fill
|
||||||
|
in during step 0.
|
||||||
|
- **`03_wg_tunnel_bindspace.md`** — `wg` as a *bindspace* rather
|
||||||
|
than a `MsgTransport`, a `TunnelledAddress` wrapper delegating
|
||||||
|
`.proto_key`/`.unwrap()` to `.inner`, `pyroute2` for layer B,
|
||||||
|
and `@acm`-managed netns/iface for layer C.
|
||||||
|
- **`README.md`** — index.
|
||||||
|
|
||||||
|
## Generated example code
|
||||||
|
|
||||||
|
> `git diff main..ng_tpts_planning -- examples/multihost/wg_lan/`
|
||||||
|
|
||||||
|
- `wg_maddr.py` — `WGTunnelledAddr(msgspec.Struct, frozen=True)`
|
||||||
|
carrying `bearer: tuple[str, int]`, `peer_pubkey: str`,
|
||||||
|
`inner: tuple[str, int]`, `inner_proto: Literal['tcp']`, plus a
|
||||||
|
`.maddr` property that re-renders the canonical form. Pure
|
||||||
|
helpers `mb_pubkey()`, `wg8_pubkey()`, `parse_wg_maddr()`, and
|
||||||
|
`_segments()` (with a marked stopgap for when the `wg` codec
|
||||||
|
isn't installed). `verify_wg_peer()` is impure **by design** and
|
||||||
|
kept out of the parse path.
|
||||||
|
- `host_a_srv.py` / `host_b_client.py` — the two-host runs; both
|
||||||
|
pass only `addr.inner` to `open_nursery()`/`open_root_actor()`.
|
||||||
|
- `README.md` — grammar, owner table, `#108`-branch install line,
|
||||||
|
tunnel setup, "what changed vs #482".
|
||||||
|
|
||||||
|
## Verified findings (non-code, verbatim)
|
||||||
|
|
||||||
|
### `trio` is address-family agnostic
|
||||||
|
|
||||||
|
Read against the installed `trio`. `SocketStream`/`SocketListener`
|
||||||
|
ctor checks are only "is a trio sock object" + `type ==
|
||||||
|
SOCK_STREAM`, plus an `OSError`-**suppressed** `SO_ACCEPTCONN`
|
||||||
|
probe. No `AF_*` check anywhere; `TCP_NODELAY`/`TCP_NOTSENT_LOWAT`
|
||||||
|
are set under `suppress(OSError)`. A TIPC `SOCK_STREAM` sock should
|
||||||
|
therefore drop straight into `trio.serve_listeners()` with the
|
||||||
|
existing `MsgpackTransport` framing, making TIPC mostly
|
||||||
|
table-registration boilerplate w/ zero new deps.
|
||||||
|
|
||||||
|
### the `wg` maddr grammar — `/wg/` is infix, not suffix
|
||||||
|
|
||||||
|
Installed `baudco/py-multiaddr@wg_support` (PR
|
||||||
|
multiformats/py-multiaddr#108) into a throwaway venv and
|
||||||
|
round-tripped every candidate form:
|
||||||
|
|
||||||
|
| maddr | `[p.name for p in m.protocols()]` |
|
||||||
|
| --- | --- |
|
||||||
|
| `/ip4/1.2.3.4/udp/51820/wg/u<k>` | `['ip4','udp','wg']` |
|
||||||
|
| `/ip4/../udp/../wg/u<k>/ip4/../tcp/..` | `['ip4','udp','wg','ip4','tcp']` |
|
||||||
|
| `/ip4/10.0.11.1/tcp/1616/wg/u<k>` | `['ip4','tcp','wg']` |
|
||||||
|
|
||||||
|
```
|
||||||
|
/ip4/192.168.1.50/udp/51820/wg/u<A_pub>/ip4/10.0.11.1/tcp/1616
|
||||||
|
\_______ bearer __________/\__ key __/\______ overlay ______/
|
||||||
|
```
|
||||||
|
|
||||||
|
Segments *before* `/wg/` are the bearer — the underlay
|
||||||
|
`(ip, udp-port)` that `wg(8)` itself listens on (`ListenPort`).
|
||||||
|
Segments *after* are the overlay endpoint, the only part tractor
|
||||||
|
binds. The third row above is #482's original suffix form: it
|
||||||
|
parses, but is semantically inverted.
|
||||||
|
|
||||||
|
Three parts, three owners — and only one is an `Endpoint`:
|
||||||
|
|
||||||
|
| part | bound by | in the runtime? |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| bearer | kernel, via `wg-quick`/`pyroute2` | no |
|
||||||
|
| `/wg/u<key>` | nothing — an identity | no, verified out-of-band |
|
||||||
|
| overlay | `tractor`'s `IPCServer` | yes, as `.inner` |
|
||||||
|
|
||||||
|
### proto-key-tagged `UnwrappedAddress`
|
||||||
|
|
||||||
|
Shape-matching in `wrap_address()` does not survive four backends.
|
||||||
|
TIPC's natural unwrapped form is a `(str, int)`, indistinguishable
|
||||||
|
from `TCPAddress`; iroh's is a `(str, str)`, already swallowed by
|
||||||
|
the existing UDS case (`case (_, filename) if type(filename) is
|
||||||
|
str`). Ordering hacks and prefix-tagging only paper over it.
|
||||||
|
|
||||||
|
Recommended prerequisite for all three backends: carry an explicit
|
||||||
|
proto-key spelled with the `multiaddr` protocol name —
|
||||||
|
`('tcp', host, port)`, `('unix', path)`,
|
||||||
|
`('tipc', stype, inst, scope)` — so `wrap_address()` collapses to
|
||||||
|
`_address_types[addr[0]]` and the collision class stops existing.
|
||||||
|
This also makes the on-wire form agree with
|
||||||
|
`mk_maddr()`/`parse_maddr()` instead of being an independent
|
||||||
|
invention. It is a wire-format change (`SpawnSpec`,
|
||||||
|
`_root_mailbox`, `_registry_addrs`) plus every fixture and
|
||||||
|
downstream config, so it wants its own migration commit landed
|
||||||
|
before any new backend — and it is the moment to stop handing raw
|
||||||
|
tuples to users at all, making `Address` the public currency and
|
||||||
|
`UnwrappedAddress` an internal serialization detail (the
|
||||||
|
discipline `ipaddress` uses).
|
||||||
|
|
||||||
|
### netns is a runtime-level config API
|
||||||
|
|
||||||
|
`setns(2)` affects the calling thread only and does not move
|
||||||
|
already-created sockets. So a netns is a spawn/boot-time input
|
||||||
|
alongside `enable_transports`/`tpt_bind_addrs`, and there is
|
||||||
|
deliberately no `await actor.enter_netns(...)` — a mid-life API
|
||||||
|
would silently leave the IPC server bound in the old namespace.
|
||||||
|
Corollary for layer B: pass `netns=` down to `pyroute2` rather
|
||||||
|
than assuming a `trio.to_thread` worker inherits it.
|
||||||
|
|
||||||
|
### `examples/` collection would have failed CI
|
||||||
|
|
||||||
|
`tests/test_docs_examples.py` walks `examples/` recursively and
|
||||||
|
subproc-runs every collected file asserting `rc == 0`. Its filter
|
||||||
|
never checks the extension, so all four `wg_lan` files were
|
||||||
|
collected — including `README.md`, which would have been run as
|
||||||
|
`python README.md`. `'multihost' not in p[0]` was already in the
|
||||||
|
exclusion list with no directory using it. Moving the set under
|
||||||
|
`examples/multihost/wg_lan/` drops collection 24 → 20 with zero
|
||||||
|
test changes; confirmed via `pytest --collect-only`.
|
||||||
|
|
||||||
|
## Corrections applied during the session
|
||||||
|
|
||||||
|
The human corrected two claims that had been asserted without
|
||||||
|
verification, both since retracted in-place in the docs and in the
|
||||||
|
posted issue comments:
|
||||||
|
|
||||||
|
1. that `wg` has "nothing to bind at the tunnel layer, exactly one
|
||||||
|
bind" — wrong; a wg stack is genuinely composed, and the real
|
||||||
|
axis is *who owns* each layer's endpoint.
|
||||||
|
2. that `/ip4/../udp/443/quic-v1` was "also composed" — wrong;
|
||||||
|
that is one endpoint with a protocol qualifier, not a tunnel.
|
||||||
|
|
||||||
|
A self-review before publication also downgraded two
|
||||||
|
overconfident claims to explicitly-flagged assumptions: the
|
||||||
|
`uniffi`-uses-asyncio-only-as-executor thesis (contradicted that
|
||||||
|
plan's own "do not guess from memory" step 0) and TIPC's
|
||||||
|
duplicate-binder round-robin behaviour (unverified).
|
||||||
|
|
@ -0,0 +1,39 @@
|
||||||
|
---
|
||||||
|
model: openai/gpt-5.6-sol
|
||||||
|
service: opencode
|
||||||
|
session: pr475-review-fixes-20260817
|
||||||
|
timestamp: 2026-08-17T23:18:25Z
|
||||||
|
git_ref: 359fe75c
|
||||||
|
scope: code
|
||||||
|
substantive: true
|
||||||
|
raw_file: 20260817T231825Z_359fe75c_prompt_io.raw.md
|
||||||
|
---
|
||||||
|
|
||||||
|
## Prompt
|
||||||
|
|
||||||
|
Continue the `/code-review-changes` pass for PR #475 in its isolated
|
||||||
|
worktree. Address the seven accepted manual-review findings in
|
||||||
|
`tractor/ipc/_types.py` and `tractor/ipc/_uds.py`, preserve the existing
|
||||||
|
Windows capability behavior, verify the result, and prepare the work for
|
||||||
|
human-controlled commit and review-reply steps. Do not publish replies,
|
||||||
|
stage, commit, or push without the required explicit authorization.
|
||||||
|
|
||||||
|
## Response summary
|
||||||
|
|
||||||
|
Restored project quote, docstring, multiline-expression, and
|
||||||
|
`match/case` conventions while retaining the Windows-safe UDS guard.
|
||||||
|
Removed unnecessary structural and comment churn, then verified the
|
||||||
|
focused transport, discovery, and lazy-import paths plus the missing
|
||||||
|
`AF_UNIX` behavior.
|
||||||
|
|
||||||
|
## Files changed
|
||||||
|
|
||||||
|
- `tractor/ipc/_types.py` - restore project style and guarded
|
||||||
|
socket-family dispatch.
|
||||||
|
- `tractor/ipc/_uds.py` - format the UDS capability gate
|
||||||
|
consistently.
|
||||||
|
|
||||||
|
## Human edits
|
||||||
|
|
||||||
|
None - the generated patch remains uncommitted and awaits human
|
||||||
|
review.
|
||||||
|
|
@ -0,0 +1,45 @@
|
||||||
|
---
|
||||||
|
model: openai/gpt-5.6-sol
|
||||||
|
service: opencode
|
||||||
|
timestamp: 2026-08-17T23:18:25Z
|
||||||
|
git_ref: 359fe75c
|
||||||
|
diff_cmd: git diff HEAD~1..HEAD
|
||||||
|
---
|
||||||
|
|
||||||
|
Applied the seven accepted manual-review fixes for PR #475 while
|
||||||
|
preserving the Windows transport capability behavior.
|
||||||
|
|
||||||
|
> `git diff HEAD~1..HEAD -- tractor/ipc/_types.py`
|
||||||
|
|
||||||
|
The generated changes restore the project's single-quote docstring and
|
||||||
|
string conventions, remove the unnecessary helper divider, simplify the
|
||||||
|
transport-registry comments, and restore `match/case` socket-family
|
||||||
|
dispatch. The UDS case retains a `HAS_UDS` guard that short-circuits
|
||||||
|
before `socket.AF_UNIX` is evaluated on unsupported hosts. Nearby error
|
||||||
|
messages are wrapped without changing their content.
|
||||||
|
|
||||||
|
> `git diff HEAD~1..HEAD -- tractor/ipc/_uds.py`
|
||||||
|
|
||||||
|
The generated change reformats the `HAS_UDS` conjunction according to
|
||||||
|
the project's multiline boolean-expression convention and simplifies
|
||||||
|
the adjacent capability comment.
|
||||||
|
|
||||||
|
Verification:
|
||||||
|
|
||||||
|
`/home/goodboy/repos/tractor/py313/bin/pytest -q tests/test_lazy_imports.py tests/discovery tests/ipc/test_server.py`
|
||||||
|
|
||||||
|
Result: `66 passed, 2 xpassed in 60.62s`.
|
||||||
|
|
||||||
|
`ruff check --no-cache --output-format=json tractor/ipc/_types.py tractor/ipc/_uds.py`
|
||||||
|
|
||||||
|
Result: no findings.
|
||||||
|
|
||||||
|
`git diff --check`
|
||||||
|
|
||||||
|
Result: no whitespace errors.
|
||||||
|
|
||||||
|
An explicit missing-`AF_UNIX` probe set `HAS_UDS = False`, removed the
|
||||||
|
socket constant, and exercised an unsupported socket family. It raised
|
||||||
|
the expected `NotImplementedError` instead of `AttributeError`.
|
||||||
|
|
||||||
|
No review replies, commits, or pushes were published.
|
||||||
|
|
@ -0,0 +1,69 @@
|
||||||
|
---
|
||||||
|
model: gpt-5.6-sol
|
||||||
|
service: opencode
|
||||||
|
session: intercepted-claude-7b9c97c4-fff7-4ac4-97fb-35720453308e
|
||||||
|
timestamp: 2026-08-18T02:17:29Z
|
||||||
|
git_ref: d9a6e2e9
|
||||||
|
scope: code
|
||||||
|
substantive: true
|
||||||
|
raw_file: 20260818T021729Z_d9a6e2e9_prompt_io.raw.md
|
||||||
|
---
|
||||||
|
|
||||||
|
## Prompt
|
||||||
|
|
||||||
|
> Intercept Claude session
|
||||||
|
> `7b9c97c4-fff7-4ac4-97fb-35720453308e`, pick up where it
|
||||||
|
> stopped in its open worktree, finish the tunnelled-address change
|
||||||
|
> and commit plan, and prepare any outstanding context for another
|
||||||
|
> provider.
|
||||||
|
|
||||||
|
The recovered final prompt specifically called out the `uds` versus
|
||||||
|
`unix` boundary decision, absent concrete `Address.namespace`
|
||||||
|
implementations, the corrected namespace test, and the passing
|
||||||
|
focused and wider suites.
|
||||||
|
|
||||||
|
During review, the human further required that tunnelled-maddr work
|
||||||
|
delegate to `multiaddr`'s encapsulation APIs, challenged the premature
|
||||||
|
transport-shaped listener hooks and directed their removal, corrected
|
||||||
|
the long-term bearer provisioning model, and confirmed the intended
|
||||||
|
split between discovery metadata and bindspace lifecycle code.
|
||||||
|
|
||||||
|
## Response summary
|
||||||
|
|
||||||
|
Recovered the transcript and matched it to `wkts/addr_unpacking`,
|
||||||
|
audited the staged implementation, and completed the interrupted
|
||||||
|
verification and commit-plan work. The audit removed premature
|
||||||
|
transport-shaped listener hooks, widened the namespace identifier
|
||||||
|
type, updated stale import documentation, and removed an
|
||||||
|
invalid-escape warning from the maddr diagram. It also preserved the
|
||||||
|
layer-C design where tractor provisions the kernel-owned tunnel
|
||||||
|
bearer without treating it as a message transport.
|
||||||
|
|
||||||
|
## Files changed
|
||||||
|
|
||||||
|
- `tractor/discovery/_tunnel.py` - tunnel specs, address wrapper, and
|
||||||
|
peeling helpers.
|
||||||
|
- `tractor/discovery/_addr.py` - wrapped-address recognition and
|
||||||
|
namespace typing.
|
||||||
|
- `tractor/discovery/__init__.py` - public tunnel API exports.
|
||||||
|
- `tests/discovery/test_tunnelled_addr.py` - delegation and boundary
|
||||||
|
regression coverage.
|
||||||
|
- `ai/tpt-backends/03_wg_tunnel_bindspace.md` - distinguish
|
||||||
|
tractor-owned bindspace provisioning from kernel socket ownership.
|
||||||
|
|
||||||
|
## Human edits
|
||||||
|
|
||||||
|
Substantial human-directed editing occurred over several review turns:
|
||||||
|
|
||||||
|
- required use of `multiaddr`'s `.encapsulate()`/`.decapsulate()`
|
||||||
|
family rather than a hand-rolled tunnel peeler
|
||||||
|
- rejected the premature `start_listener()`/`close_listener()` hooks
|
||||||
|
and directed their removal from this foundational change
|
||||||
|
- corrected the documentation so tractor retains ownership of future
|
||||||
|
bindspace provisioning while the kernel owns the bearer socket
|
||||||
|
- reviewed and accepted the placement of declarative tunnel metadata
|
||||||
|
under `tractor.discovery`, with lifecycle code kept separate
|
||||||
|
|
||||||
|
The final source lines were applied through the coding agents, but
|
||||||
|
these design corrections and deletion decisions came from the human
|
||||||
|
review and materially shaped the patch.
|
||||||
|
|
@ -0,0 +1,66 @@
|
||||||
|
---
|
||||||
|
model: gpt-5.6-sol
|
||||||
|
service: opencode
|
||||||
|
timestamp: 2026-08-18T02:17:29Z
|
||||||
|
git_ref: d9a6e2e9
|
||||||
|
diff_cmd: git diff HEAD~1..HEAD
|
||||||
|
---
|
||||||
|
|
||||||
|
# Raw output - tunnelled-address handoff completion
|
||||||
|
|
||||||
|
Recovered Claude Code session
|
||||||
|
`7b9c97c4-fff7-4ac4-97fb-35720453308e` and continued its
|
||||||
|
interrupted `wkts/addr_unpacking` changes.
|
||||||
|
|
||||||
|
## Generated code
|
||||||
|
|
||||||
|
> `git diff HEAD~1..HEAD -- tractor/discovery/_tunnel.py`
|
||||||
|
|
||||||
|
Added frozen `WGTunnelSpec` and `TunnelledAddress` structs. The
|
||||||
|
wrapper delegates transport identity, validity, bindspace, and wire
|
||||||
|
serialization to its overlay while retaining tunnel metadata locally.
|
||||||
|
Added pure helpers to peel nested wrappers and enumerate their tunnel
|
||||||
|
specs. The module documents why wrappers must be peeled before
|
||||||
|
`Endpoint` selects the overlay transport backend.
|
||||||
|
|
||||||
|
> `git diff HEAD~1..HEAD -- tractor/discovery/_addr.py`
|
||||||
|
|
||||||
|
Extended `is_wrapped_addr()` to recognize `TunnelledAddress` without
|
||||||
|
registering tunnels as message transports, and widened the namespace
|
||||||
|
identifier type to cover named network namespaces.
|
||||||
|
|
||||||
|
> `git diff HEAD~1..HEAD -- tractor/discovery/__init__.py`
|
||||||
|
|
||||||
|
Exported the tunnel address API from `tractor.discovery` and updated
|
||||||
|
the eager-import documentation.
|
||||||
|
|
||||||
|
> `git diff HEAD~1..HEAD -- tests/discovery/test_tunnelled_addr.py`
|
||||||
|
|
||||||
|
Added focused coverage for delegation, serialization, rewrapping,
|
||||||
|
namespace fallback, nested peeling order, and frozen structs.
|
||||||
|
|
||||||
|
> `git diff HEAD~1..HEAD -- ai/tpt-backends/03_wg_tunnel_bindspace.md`
|
||||||
|
|
||||||
|
Clarified that tractor owns the eventual bindspace lifecycle and may
|
||||||
|
provision the WireGuard iface, routes, and kernel UDP listener through
|
||||||
|
netlink/`pyroute2`. Kernel socket ownership does not make the bearer an
|
||||||
|
application `MsgTransport` endpoint.
|
||||||
|
|
||||||
|
## Verification
|
||||||
|
|
||||||
|
Focused tests:
|
||||||
|
|
||||||
|
```text
|
||||||
|
9 passed in 0.03s
|
||||||
|
```
|
||||||
|
|
||||||
|
Discovery and IPC suites:
|
||||||
|
|
||||||
|
```text
|
||||||
|
67 passed, 2 xpassed
|
||||||
|
```
|
||||||
|
|
||||||
|
The audit removed premature module-level listener hooks. Runtime
|
||||||
|
integration must peel the wrapper explicitly at bind and dial
|
||||||
|
boundaries rather than make `._tunnel` impersonate a transport
|
||||||
|
backend.
|
||||||
|
|
@ -0,0 +1,43 @@
|
||||||
|
---
|
||||||
|
model: openai/gpt-5.6-sol
|
||||||
|
service: opencode
|
||||||
|
session: pr481-review-fixes-p1-20260818
|
||||||
|
timestamp: 2026-08-18T03:15:32Z
|
||||||
|
git_ref: 4151b956
|
||||||
|
scope: code
|
||||||
|
substantive: true
|
||||||
|
raw_file: 20260818T031532Z_4151b956_prompt_io.raw.md
|
||||||
|
---
|
||||||
|
|
||||||
|
## Prompt
|
||||||
|
|
||||||
|
Address the approved review findings on PR #481, but work
|
||||||
|
iteratively: implement and verify one finding at a time, prepare a
|
||||||
|
separate `/commit-plan` after each fix, and stop for the human commit
|
||||||
|
before starting the next finding. Begin with the P1 per-child
|
||||||
|
lifecycle issue. Also publish the already-approved review findings
|
||||||
|
against the reviewed PR head before editing.
|
||||||
|
|
||||||
|
## Response summary
|
||||||
|
|
||||||
|
Published the approved non-approving review at head `4151b956`, then
|
||||||
|
implemented only the P1 lifecycle fix. Owned one-shot actors now use
|
||||||
|
a child-specific cancellation and process-reap handshake, including
|
||||||
|
hard escalation for unacknowledged cancellation and deterministic
|
||||||
|
bookkeeping removal before `to_actor.run()` returns.
|
||||||
|
|
||||||
|
## Files changed
|
||||||
|
|
||||||
|
- `tractor/runtime/_supervise.py` - coordinate child-specific cancel
|
||||||
|
and reap.
|
||||||
|
- `tractor/spawn/_trio.py` - wait on the Trio child's reap request.
|
||||||
|
- `tractor/spawn/_mp.py` - wait on the multiprocessing child's reap
|
||||||
|
request.
|
||||||
|
- `tractor/spawn/_spawn.py` - publish monitor completion centrally.
|
||||||
|
- `tractor/to_actor/_api.py` - await owned-child process reaping.
|
||||||
|
- `tests/test_to_actor.py` - cover cleanup, escalation, and startup
|
||||||
|
ordering.
|
||||||
|
|
||||||
|
## Human edits
|
||||||
|
|
||||||
|
None - the generated P1 patch remains uncommitted for human review.
|
||||||
|
|
@ -0,0 +1,74 @@
|
||||||
|
---
|
||||||
|
model: openai/gpt-5.6-sol
|
||||||
|
service: opencode
|
||||||
|
timestamp: 2026-08-18T03:15:32Z
|
||||||
|
git_ref: 4151b956
|
||||||
|
diff_cmd: git diff HEAD~1..HEAD
|
||||||
|
---
|
||||||
|
|
||||||
|
Implemented only the P1 lifecycle finding from the approved PR #481
|
||||||
|
review, preserving the requested one-fix-at-a-time commit boundary.
|
||||||
|
|
||||||
|
> `git diff HEAD~1..HEAD -- tractor/runtime/_supervise.py`
|
||||||
|
|
||||||
|
Added per-child reap request/completion events to `ActorNursery`, a
|
||||||
|
shielded child-specific cancel-and-reap operation, late-registration
|
||||||
|
latching for nursery teardown, and cancellation escalation that waits
|
||||||
|
for debugger release before using non-ignorable process termination.
|
||||||
|
The nursery-wide cancellation path snapshots child records before
|
||||||
|
checkpointing so concurrent one-shot cleanup cannot invalidate its
|
||||||
|
iteration.
|
||||||
|
|
||||||
|
> `git diff HEAD~1..HEAD -- tractor/spawn/_trio.py`
|
||||||
|
|
||||||
|
Changed Trio child monitors to wait on their per-child reap requests.
|
||||||
|
|
||||||
|
> `git diff HEAD~1..HEAD -- tractor/spawn/_mp.py`
|
||||||
|
|
||||||
|
Changed multiprocessing child monitors to wait on their per-child reap
|
||||||
|
requests.
|
||||||
|
|
||||||
|
> `git diff HEAD~1..HEAD -- tractor/spawn/_spawn.py`
|
||||||
|
|
||||||
|
Ensured every backend publishes child-reap completion after its process
|
||||||
|
monitor exits.
|
||||||
|
|
||||||
|
> `git diff HEAD~1..HEAD -- tractor/to_actor/_api.py`
|
||||||
|
|
||||||
|
Changed owned one-shot cleanup to await child-specific process joining
|
||||||
|
and bookkeeping removal instead of treating the cancel RPC as reaping.
|
||||||
|
|
||||||
|
> `git diff HEAD~1..HEAD -- tests/test_to_actor.py`
|
||||||
|
|
||||||
|
Added regressions for immediate caller-managed nursery cleanup, failed
|
||||||
|
cancel acknowledgement escalation, and child registration after a
|
||||||
|
latched nursery-wide teardown request.
|
||||||
|
|
||||||
|
Verification:
|
||||||
|
|
||||||
|
`pytest -q tests/test_to_actor.py tests/test_cancellation.py tests/test_spawning.py tests/discovery/test_multi_program.py`
|
||||||
|
|
||||||
|
Result: `46 passed, 1 xfailed, 3 xpassed`.
|
||||||
|
|
||||||
|
`pytest -q tests/test_to_actor.py --tpt-proto uds`
|
||||||
|
|
||||||
|
Result: `13 passed`.
|
||||||
|
|
||||||
|
`pytest -q tests/test_to_actor.py --spawn-backend mp_spawn --tpt-proto tcp`
|
||||||
|
|
||||||
|
Result: `13 passed`.
|
||||||
|
|
||||||
|
One broad verification run was mistakenly launched in parallel with
|
||||||
|
the UDS and `mp_spawn` actor suites. It timed out
|
||||||
|
`test_remote_error_from_caller_nursery`; the node passed immediately
|
||||||
|
in isolation and the complete broad selection then passed serially.
|
||||||
|
The failure was classified as concurrent test-session interference,
|
||||||
|
not accepted as a passing boundary result.
|
||||||
|
|
||||||
|
Ruff, Python compilation, and `git diff --check` passed for the changed
|
||||||
|
boundary. Ruff's existing `_trio.py` F401 finding was reproduced at the
|
||||||
|
unmodified PR head and excluded from attribution to this patch.
|
||||||
|
|
||||||
|
No source files were staged, committed, pushed, or used for review
|
||||||
|
replies. The previously approved top-level review was published before
|
||||||
|
the fix at reviewed head `4151b956`.
|
||||||
|
|
@ -0,0 +1,50 @@
|
||||||
|
---
|
||||||
|
model: gpt-5.6-sol
|
||||||
|
service: opencode
|
||||||
|
session: tractor-addr-unpacking-followup
|
||||||
|
timestamp: 2026-08-18T07:50:31Z
|
||||||
|
git_ref: dd02c7c0
|
||||||
|
scope: code
|
||||||
|
substantive: true
|
||||||
|
raw_file: 20260818T075031Z_dd02c7c0_prompt_io.raw.md
|
||||||
|
---
|
||||||
|
|
||||||
|
## Prompt
|
||||||
|
|
||||||
|
The human requested the complete tunnelled-maddr parsing/composition
|
||||||
|
slice as an unattended batch, with every numbered requirement finished
|
||||||
|
and atomic commit plans prepared at the end. Existing human decisions
|
||||||
|
required native `multiaddr` encapsulation APIs, no hand-written peeler,
|
||||||
|
and preservation of tractor's future bindspace lifecycle ownership.
|
||||||
|
|
||||||
|
## Response summary
|
||||||
|
|
||||||
|
Implemented strict WG key codecs and native single/nested tunnel maddr
|
||||||
|
parsing and composition, integrated them into discovery APIs, migrated
|
||||||
|
the multihost example off its duplicate parser, corrected package
|
||||||
|
dependency metadata, and added focused and end-to-end parser
|
||||||
|
regressions. Verified the complete tractor suite and built both package
|
||||||
|
artifacts.
|
||||||
|
|
||||||
|
## Files changed
|
||||||
|
|
||||||
|
- `tractor/discovery/` - WG codecs, parser/composer, wrapper typing,
|
||||||
|
public exports, and discovery dispatch.
|
||||||
|
- `tests/discovery/` - key, grammar, nesting, round-trip, and public
|
||||||
|
boundary regressions.
|
||||||
|
- `examples/multihost/wg_lan/` - production parser migration and
|
||||||
|
updated usage documentation.
|
||||||
|
- `pyproject.toml`, `uv.lock` - reproducible WG codec and multibase
|
||||||
|
dependencies for checkout and package installs.
|
||||||
|
- `ai/tpt-backends/03_wg_tunnel_bindspace.md` - current layer-A state
|
||||||
|
and future bindspace ownership.
|
||||||
|
|
||||||
|
## Human edits
|
||||||
|
|
||||||
|
The human selected the five-step scope and batch execution model,
|
||||||
|
required delegation to `multiaddr`'s encapsulation APIs, rejected
|
||||||
|
transport-shaped listener placeholders in the prerequisite commit, and
|
||||||
|
clarified that tractor will eventually provision the kernel-owned
|
||||||
|
bearer through its bindspace layer. The agent implemented and tested
|
||||||
|
those decisions; no direct manual source edits were observed during
|
||||||
|
this batch.
|
||||||
|
|
@ -0,0 +1,61 @@
|
||||||
|
---
|
||||||
|
model: gpt-5.6-sol
|
||||||
|
service: opencode
|
||||||
|
timestamp: 2026-08-18T07:50:31Z
|
||||||
|
git_ref: dd02c7c0
|
||||||
|
diff_cmd: git diff HEAD~1..HEAD
|
||||||
|
---
|
||||||
|
|
||||||
|
# Raw output - native WireGuard maddr integration
|
||||||
|
|
||||||
|
The human requested completion of the five-step tunnelled-maddr slice:
|
||||||
|
port the proven WireGuard parser, delegate to `py-multiaddr`'s native
|
||||||
|
tunnel APIs, integrate public parse and composition entry points, add
|
||||||
|
regressions, and return atomic commit plans after completing the batch.
|
||||||
|
|
||||||
|
## Generated code
|
||||||
|
|
||||||
|
> `git diff HEAD~1..HEAD -- tractor/discovery/_tunnel.py tractor/discovery/_multiaddr.py tractor/discovery/_addr.py tractor/discovery/__init__.py`
|
||||||
|
|
||||||
|
Added strict WireGuard standard-base64/multibase key codecs and native
|
||||||
|
WG maddr parsing/composition. Nested stacks peel the last `/wg/`
|
||||||
|
repeatedly with `.decapsulate_code()`, isolate segments through
|
||||||
|
`.split()`/`.join()`, and compose recursively with `.encapsulate()`.
|
||||||
|
Public discovery parsing, wrapping, endpoint-table parsing, and maddr
|
||||||
|
composition now preserve `TunnelledAddress` metadata locally.
|
||||||
|
|
||||||
|
> `git diff HEAD~1..HEAD -- pyproject.toml uv.lock`
|
||||||
|
|
||||||
|
Declared `py-multibase` directly and pinned the unreleased
|
||||||
|
py-multiaddr#108 WG codec revision in distribution metadata. Enabled
|
||||||
|
Hatch direct-reference metadata so editable, wheel, and sdist builds
|
||||||
|
use the same dependency contract.
|
||||||
|
|
||||||
|
> `git diff HEAD~1..HEAD -- tests/discovery/test_multiaddr.py tests/discovery/test_tunnelled_addr.py`
|
||||||
|
|
||||||
|
Added key-codec, single/nested WG round-trip, malformed key, invalid
|
||||||
|
bearer/overlay, missing codec, missing bearer, `wrap_address()`, and
|
||||||
|
`parse_endpoints()` coverage while retaining plain TCP/UDS regressions.
|
||||||
|
|
||||||
|
> `git diff HEAD~1..HEAD -- examples/multihost/wg_lan ai/tpt-backends/03_wg_tunnel_bindspace.md`
|
||||||
|
|
||||||
|
Removed the duplicate example parser, migrated callers to production
|
||||||
|
`TunnelledAddress`, retained only explicit `wg(8)` verification, and
|
||||||
|
updated dependency and future bindspace ownership documentation.
|
||||||
|
|
||||||
|
## Verification
|
||||||
|
|
||||||
|
- focused discovery files: `45 passed`
|
||||||
|
- discovery and IPC suites: `81 passed, 2 xpassed`
|
||||||
|
- full suite: `443 passed, 9 skipped, 7 xfailed, 4 xpassed`
|
||||||
|
- collection: `462 tests`
|
||||||
|
- Ruff: clean
|
||||||
|
- sdist and wheel builds: successful
|
||||||
|
|
||||||
|
## Review corrections
|
||||||
|
|
||||||
|
A review caught that `[tool.uv.sources]` alone would not constrain
|
||||||
|
built-package consumers to a WG-capable py-multiaddr revision. The
|
||||||
|
dependency was moved into PEP 621 metadata and the parser now converts
|
||||||
|
missing-codec failures into an actionable tractor error without
|
||||||
|
misclassifying Unix paths containing a `wg` directory.
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
---
|
||||||
|
model: openai/gpt-5.6-sol
|
||||||
|
service: opencode
|
||||||
|
session: ses_3e4c90d3eafeqHEtRYSIHgHhpA
|
||||||
|
timestamp: 2026-08-18T19:30:01Z
|
||||||
|
git_ref: bf06b4f8
|
||||||
|
scope: code
|
||||||
|
substantive: true
|
||||||
|
raw_file: 20260818T193001Z_bf06b4f8_prompt_io.raw.md
|
||||||
|
---
|
||||||
|
|
||||||
|
## Prompt
|
||||||
|
|
||||||
|
Fix cancellation during an indeterminate transport write without
|
||||||
|
allowing a second RPC msg to corrupt the stream. Keep the transport
|
||||||
|
fix separate from the higher-level `to_actor` remediation and explain
|
||||||
|
the length-prefixed protocol rather than using unexplained "framed
|
||||||
|
send" terminology.
|
||||||
|
|
||||||
|
## Response summary
|
||||||
|
|
||||||
|
Close a msgpack transport stream when cancellation interrupts its
|
||||||
|
length-prefixed `send_all()` operation. The stream can no longer be
|
||||||
|
safely reused because Trio cannot report how many bytes were written.
|
||||||
|
|
||||||
|
## Files changed
|
||||||
|
|
||||||
|
- `tractor/ipc/_transport.py` - close an interrupted send stream.
|
||||||
|
- `tests/ipc/test_each_tpt.py` - cover cancellation during the write.
|
||||||
|
|
||||||
|
## Human edits
|
||||||
|
|
||||||
|
The human required this transport edge-case fix to land as its own
|
||||||
|
behavioral commit with a detailed message. During staged review, the
|
||||||
|
human also rejected the unexplained "framed send" wording and asked
|
||||||
|
for terminology tied directly to the actual transport operation.
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
---
|
||||||
|
model: openai/gpt-5.6-sol
|
||||||
|
service: opencode
|
||||||
|
timestamp: 2026-08-18T19:30:01Z
|
||||||
|
git_ref: bf06b4f8
|
||||||
|
diff_cmd: git diff HEAD~1..HEAD
|
||||||
|
---
|
||||||
|
|
||||||
|
Prospective review found that cancellation can interrupt
|
||||||
|
`MsgpackTransport.send()` after `send_all()` writes only part of its
|
||||||
|
length-prefixed msg. Sending a cancellation request afterward can
|
||||||
|
append another msg to the indeterminate stream and desynchronize the
|
||||||
|
peer decoder.
|
||||||
|
|
||||||
|
> `git diff HEAD~1..HEAD -- tractor/ipc/_transport.py tests/ipc/test_each_tpt.py`
|
||||||
|
|
||||||
|
Close the stream under a cancellation shield when `send_all()` is
|
||||||
|
cancelled. Cover the behavior with a fake stream that checkpoints
|
||||||
|
inside the write and records forced closure.
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
---
|
||||||
|
model: openai/gpt-5.6-sol
|
||||||
|
service: opencode
|
||||||
|
session: ses_3e4c90d3eafeqHEtRYSIHgHhpA
|
||||||
|
timestamp: 2026-08-18T19:30:02Z
|
||||||
|
git_ref: bf06b4f8
|
||||||
|
scope: code
|
||||||
|
substantive: true
|
||||||
|
raw_file: 20260818T193002Z_bf06b4f8_prompt_io.raw.md
|
||||||
|
---
|
||||||
|
|
||||||
|
## Prompt
|
||||||
|
|
||||||
|
Distill repeated `Actor._contexts.pop()` machinery into a wrapper like
|
||||||
|
the RPC-task registration helper so future teardown sites do not keep
|
||||||
|
reconstructing the context-registry key independently. Preserve the
|
||||||
|
existing lifecycle-specific cleanup behavior.
|
||||||
|
|
||||||
|
## Response summary
|
||||||
|
|
||||||
|
Add idempotent `Actor._drop_context()` registry removal keyed from the
|
||||||
|
context's own channel and CID. Use it for caller context teardown and
|
||||||
|
the strict callee-side RPC deregistration path.
|
||||||
|
|
||||||
|
## Files changed
|
||||||
|
|
||||||
|
- `tractor/runtime/_runtime.py` - own context-registry removal.
|
||||||
|
- `tractor/runtime/_rpc.py` - use the helper for callee teardown.
|
||||||
|
- `tractor/_context.py` - use the helper after caller teardown.
|
||||||
|
|
||||||
|
## Human edits
|
||||||
|
|
||||||
|
The human identified the repeated registry-pop code and requested a
|
||||||
|
central primitive analogous to `_register_rpc_task()`. The agent first
|
||||||
|
suggested an async helper that also closed receive channels; the final
|
||||||
|
design was narrowed to registry removal only so each lifecycle owner
|
||||||
|
retains its existing closure, debugger, shielding, and error policy.
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
---
|
||||||
|
model: openai/gpt-5.6-sol
|
||||||
|
service: opencode
|
||||||
|
timestamp: 2026-08-18T19:30:02Z
|
||||||
|
git_ref: bf06b4f8
|
||||||
|
diff_cmd: git diff HEAD~1..HEAD
|
||||||
|
---
|
||||||
|
|
||||||
|
Repeated teardown sites reconstruct the `Actor._contexts` registry
|
||||||
|
key from a portal channel and context ID before popping it. Add an
|
||||||
|
idempotent actor-owned helper deriving the key from the context itself,
|
||||||
|
then route caller and callee context teardown through that helper.
|
||||||
|
|
||||||
|
> `git diff HEAD~1..HEAD -- tractor/runtime/_runtime.py tractor/runtime/_rpc.py tractor/_context.py`
|
||||||
|
|
||||||
|
Keep receive-channel closure and cancellation shielding in each
|
||||||
|
lifecycle owner so the helper centralizes registry machinery without
|
||||||
|
changing their teardown ordering.
|
||||||
|
|
@ -0,0 +1,38 @@
|
||||||
|
---
|
||||||
|
model: openai/gpt-5.6-sol
|
||||||
|
service: opencode
|
||||||
|
session: ses_3e4c90d3eafeqHEtRYSIHgHhpA
|
||||||
|
timestamp: 2026-08-18T19:30:03Z
|
||||||
|
git_ref: bf06b4f8
|
||||||
|
scope: code
|
||||||
|
substantive: true
|
||||||
|
raw_file: 20260818T193003Z_bf06b4f8_prompt_io.raw.md
|
||||||
|
---
|
||||||
|
|
||||||
|
## Prompt
|
||||||
|
|
||||||
|
Cancel a remote task when its caller is cancelled after `Start`
|
||||||
|
publication but before startup acknowledgement. Keep cancellation
|
||||||
|
bounded, prevent its private `_cancel_task` RPC from recursively
|
||||||
|
cancelling itself and preserve public target kwargs unchanged.
|
||||||
|
|
||||||
|
## Response summary
|
||||||
|
|
||||||
|
Add private portal startup policy, use it for non-recursive context
|
||||||
|
cancellation and clean caller-side startup state under a shield.
|
||||||
|
|
||||||
|
## Files changed
|
||||||
|
|
||||||
|
- `tractor/runtime/_portal.py` - separate private startup policy.
|
||||||
|
- `tractor/_context.py` - disable recursion for cancellation RPCs.
|
||||||
|
- `tractor/runtime/_runtime.py` - clean cancelled task startup.
|
||||||
|
- `tests/test_context_stream_semantics.py` - control cancellation
|
||||||
|
between `Start` publication and acknowledgement.
|
||||||
|
|
||||||
|
## Human edits
|
||||||
|
|
||||||
|
The human required this cancellation behavior to remain a distinct
|
||||||
|
commit from general startup failures and from the public `to_actor`
|
||||||
|
API. The human also requested that its runtime comment describe the
|
||||||
|
actual length-prefixed transport guarantee and concrete `_cancel_task`
|
||||||
|
operation rather than referring to an unnamed wrapper.
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
---
|
||||||
|
model: openai/gpt-5.6-sol
|
||||||
|
service: opencode
|
||||||
|
timestamp: 2026-08-18T19:30:03Z
|
||||||
|
git_ref: bf06b4f8
|
||||||
|
diff_cmd: git diff HEAD~1..HEAD
|
||||||
|
---
|
||||||
|
|
||||||
|
Cancellation while `Actor.start_remote_task()` waits for `StartAck`
|
||||||
|
can strand its caller-side context and leave the remote task running.
|
||||||
|
Make one bounded cleanup request, remove local startup state and close
|
||||||
|
its receive channel.
|
||||||
|
|
||||||
|
> `git diff HEAD~1..HEAD -- tractor/runtime/_runtime.py tractor/runtime/_portal.py tractor/_context.py tests/test_context_stream_semantics.py`
|
||||||
|
|
||||||
|
Separate private startup-cancellation policy from public target kwargs
|
||||||
|
using `Portal._run_from_ns()`. Have `Context.cancel()` disable recursive
|
||||||
|
startup cancellation for its own `_cancel_task` RPC. Exercise
|
||||||
|
cancellation after `Start` publication and prove the caller-owned actor
|
||||||
|
remains reusable without leaked contexts.
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
---
|
||||||
|
model: openai/gpt-5.6-sol
|
||||||
|
service: opencode
|
||||||
|
session: ses_3e4c90d3eafeqHEtRYSIHgHhpA
|
||||||
|
timestamp: 2026-08-18T19:30:04Z
|
||||||
|
git_ref: bf06b4f8
|
||||||
|
scope: code
|
||||||
|
substantive: true
|
||||||
|
raw_file: 20260818T193004Z_bf06b4f8_prompt_io.raw.md
|
||||||
|
---
|
||||||
|
|
||||||
|
## Prompt
|
||||||
|
|
||||||
|
Release caller-side context state for every remote-task startup failure,
|
||||||
|
not only local cancellation. Preserve the remote error, avoid unsafe
|
||||||
|
follow-up sends and prove pre-publication serialization failures leave
|
||||||
|
a reused portal healthy.
|
||||||
|
|
||||||
|
## Response summary
|
||||||
|
|
||||||
|
Extend remote-task startup cleanup across send, acknowledgement and
|
||||||
|
validation errors. Track completed publication, perform only safe
|
||||||
|
best-effort cancellation and deterministically remove local state.
|
||||||
|
|
||||||
|
## Files changed
|
||||||
|
|
||||||
|
- `tractor/runtime/_runtime.py` - clean every startup failure path.
|
||||||
|
- `tests/test_context_stream_semantics.py` - cover authorization and
|
||||||
|
serialization failures before context entry.
|
||||||
|
|
||||||
|
## Human edits
|
||||||
|
|
||||||
|
The human accepted the discovered edge-case fixes but required general
|
||||||
|
startup cleanup to land separately from cancellation cleanup, transport
|
||||||
|
integrity and the public API. This boundary preserves that behavioral
|
||||||
|
distinction and its dedicated commit-message rationale.
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
---
|
||||||
|
model: openai/gpt-5.6-sol
|
||||||
|
service: opencode
|
||||||
|
timestamp: 2026-08-18T19:30:04Z
|
||||||
|
git_ref: bf06b4f8
|
||||||
|
diff_cmd: git diff HEAD~1..HEAD
|
||||||
|
---
|
||||||
|
|
||||||
|
`Actor.start_remote_task()` inserts a context before sending `Start`,
|
||||||
|
but startup errors other than cancellation escape without removing or
|
||||||
|
closing that caller state. Serialization errors, acknowledgement
|
||||||
|
timeouts, malformed acknowledgements and remote authorization errors
|
||||||
|
can therefore leak context-registry entries.
|
||||||
|
|
||||||
|
> `git diff HEAD~1..HEAD -- tractor/runtime/_runtime.py tests/test_context_stream_semantics.py`
|
||||||
|
|
||||||
|
Cover the complete send, acknowledgement and validation phase with
|
||||||
|
exceptional cleanup. Attempt remote cancellation only when publication
|
||||||
|
is known complete or protocol-safe, and always release local state.
|
||||||
|
|
@ -0,0 +1,52 @@
|
||||||
|
---
|
||||||
|
model: openai/gpt-5.6-sol
|
||||||
|
service: opencode
|
||||||
|
session: ses_3e4c90d3eafeqHEtRYSIHgHhpA
|
||||||
|
timestamp: 2026-08-18T19:30:05Z
|
||||||
|
git_ref: bf06b4f8
|
||||||
|
scope: code
|
||||||
|
substantive: true
|
||||||
|
raw_file: 20260818T193005Z_bf06b4f8_prompt_io.raw.md
|
||||||
|
---
|
||||||
|
|
||||||
|
## Prompt
|
||||||
|
|
||||||
|
Replace abandoned `Portal.run()` one-shots with a static linked-context
|
||||||
|
endpoint. Follow Trio positional-call semantics, use partials for target
|
||||||
|
keywords, preserve Python 3.14 Placeholder behavior, keep target lookup
|
||||||
|
behind the RPC allowlist and support private, nursery and portal
|
||||||
|
placement.
|
||||||
|
|
||||||
|
## Response summary
|
||||||
|
|
||||||
|
Use `Portal.open_context()` and `Context.wait_for_result()` for one-shot
|
||||||
|
tasks. Normalize every partial layer, validate signatures locally and
|
||||||
|
send target namespace/function components separately to the authorized
|
||||||
|
remote resolver. Retain the client-side function in its `NamespacePath`
|
||||||
|
so `to_tuple()` does not re-import it. Owned actors enable the declaring
|
||||||
|
`_api.__name__` directly; caller-owned portals opt in through the public
|
||||||
|
`to_actor.MODULE` alias.
|
||||||
|
|
||||||
|
## Files changed
|
||||||
|
|
||||||
|
- `tractor/to_actor/_api.py` - implement linked one-shot calls.
|
||||||
|
- `tractor/to_actor/__init__.py` - export `MODULE`.
|
||||||
|
- `tractor/msg/ptr.py` - retain refs created by `from_ref()`.
|
||||||
|
- `tests/test_to_actor.py` - cover the public API and authorization.
|
||||||
|
- `examples/parallelism/to_actor_one_shots.py` - use positional inputs.
|
||||||
|
|
||||||
|
## Human edits
|
||||||
|
|
||||||
|
The human rejected nested target-kwargs configuration and selected
|
||||||
|
Trio-style positional inputs plus `functools.partial()`. During staged
|
||||||
|
review the human required a Python 3.14 compatibility comment rather
|
||||||
|
than removing Placeholder support, requested separate namespace and
|
||||||
|
function inputs, preserved `_get_rpc_func(ns: str, funcname: str)`
|
||||||
|
authorization, renamed `RPC_MODULE` to `MODULE`, rejected global module
|
||||||
|
exposure and deferred speculative nursery/module-list helpers to the
|
||||||
|
`open_taskman()` design line. The human also required this public API
|
||||||
|
to land only after its lower-level safety dependencies. In final staged
|
||||||
|
review, the human required `_invoke_from_portal()` to use
|
||||||
|
`NamespacePath.to_tuple()` with the already-held function ref and
|
||||||
|
required internal actor setup to use `_api.__name__` directly, keeping
|
||||||
|
`to_actor.MODULE` solely as the public importer-facing alias.
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
---
|
||||||
|
model: openai/gpt-5.6-sol
|
||||||
|
service: opencode
|
||||||
|
timestamp: 2026-08-18T19:30:05Z
|
||||||
|
git_ref: bf06b4f8
|
||||||
|
diff_cmd: git diff HEAD~1..HEAD
|
||||||
|
---
|
||||||
|
|
||||||
|
Implement `to_actor.run()` with Trio-style positional target arguments,
|
||||||
|
`functools.partial` keyword and Python 3.14 Placeholder binding, and a
|
||||||
|
static context endpoint that links remote results, errors and caller
|
||||||
|
cancellation.
|
||||||
|
|
||||||
|
> `git diff HEAD~1..HEAD -- tractor/to_actor/_api.py tractor/to_actor/__init__.py`
|
||||||
|
|
||||||
|
Resolve target functions through `Actor._get_rpc_func()` so module
|
||||||
|
authorization remains authoritative. Automatically expose the helper
|
||||||
|
module for actors owned by `to_actor.run()` and document explicit
|
||||||
|
exposure for a caller-owned portal.
|
||||||
|
|
||||||
|
> `git diff HEAD~1..HEAD -- tests/test_to_actor.py examples/parallelism/to_actor_one_shots.py`
|
||||||
|
|
||||||
|
Cover placement modes, argument binding, nested partials, caller-linked
|
||||||
|
cancellation, remote errors and module authorization.
|
||||||
|
|
@ -0,0 +1,34 @@
|
||||||
|
---
|
||||||
|
model: openai/gpt-5.6-sol
|
||||||
|
service: opencode
|
||||||
|
session: ses_3e4c90d3eafeqHEtRYSIHgHhpA
|
||||||
|
timestamp: 2026-08-19T02:07:57Z
|
||||||
|
git_ref: b38efed7
|
||||||
|
scope: code
|
||||||
|
substantive: true
|
||||||
|
raw_file: 20260819T020757Z_b38efed7_prompt_io.raw.md
|
||||||
|
---
|
||||||
|
|
||||||
|
## Prompt
|
||||||
|
|
||||||
|
Resolve the remaining P3 review finding before landing PR #481:
|
||||||
|
`runtime_kwargs={}` must not be silently accepted alongside either
|
||||||
|
`an=` or `portal=` merely because the dict is falsey. Keep this as its
|
||||||
|
own final review-remediation commit.
|
||||||
|
|
||||||
|
## Response summary
|
||||||
|
|
||||||
|
Treat any non-`None` `runtime_kwargs` value as provided when validating
|
||||||
|
placement. Cover both placement APIs with empty and configured dicts,
|
||||||
|
proving the error is raised locally before actor startup.
|
||||||
|
|
||||||
|
## Files changed
|
||||||
|
|
||||||
|
- `tractor/to_actor/_api.py` - validate option presence explicitly.
|
||||||
|
- `tests/test_to_actor.py` - cover four invalid option combinations.
|
||||||
|
|
||||||
|
## Human edits
|
||||||
|
|
||||||
|
No direct line edits. The human accepted the P3 finding, required it to
|
||||||
|
remain separate from the five P2 behavioral commits and prioritized it
|
||||||
|
before the final PR #484 integration rebase and PR #481 landing steps.
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
---
|
||||||
|
model: openai/gpt-5.6-sol
|
||||||
|
service: opencode
|
||||||
|
timestamp: 2026-08-19T02:07:57Z
|
||||||
|
git_ref: b38efed7
|
||||||
|
diff_cmd: git diff HEAD~1..HEAD
|
||||||
|
---
|
||||||
|
|
||||||
|
Fix the final PR #481 review finding: `runtime_kwargs` is mutually
|
||||||
|
exclusive with both caller placement options whenever it is provided,
|
||||||
|
including an empty dict.
|
||||||
|
|
||||||
|
> `git diff HEAD~1..HEAD -- tractor/to_actor/_api.py tests/test_to_actor.py`
|
||||||
|
|
||||||
|
Use an explicit `is not None` check rather than dict truthiness. Expand
|
||||||
|
the validation regression across `an=` and `portal=`, each with empty
|
||||||
|
and configured runtime kwargs, so every invalid combination fails
|
||||||
|
before actor runtime startup.
|
||||||
|
|
||||||
|
Verification:
|
||||||
|
|
||||||
|
- Trio/TCP: `23 passed`
|
||||||
|
- Trio/UDS: `23 passed`
|
||||||
|
- `mp_spawn`/TCP: `23 passed`
|
||||||
|
- Ruff and `git diff --check`: clean
|
||||||
|
|
@ -0,0 +1,56 @@
|
||||||
|
---
|
||||||
|
model: openai/gpt-5.6-sol
|
||||||
|
service: opencode
|
||||||
|
session: 76c5d31c-5a2f-4503-9b16-410ee7f4fab3
|
||||||
|
timestamp: 2026-08-19T18:46:40Z
|
||||||
|
git_ref: 481ba003
|
||||||
|
scope: code
|
||||||
|
substantive: true
|
||||||
|
raw_file: 20260819T184640Z_481ba003_prompt_io.raw.md
|
||||||
|
---
|
||||||
|
|
||||||
|
## Prompt
|
||||||
|
|
||||||
|
Rebase PR #484 onto final PR #481, migrate every affected one-shot call
|
||||||
|
to the new positional target API and continue through downstream tests,
|
||||||
|
examples and documentation review.
|
||||||
|
|
||||||
|
## Response summary
|
||||||
|
|
||||||
|
Converted stale target keyword calls to target partials so previously
|
||||||
|
named inputs remain explicit while placement/runtime controls stay
|
||||||
|
direct. Updated error expectations for local signature validation and
|
||||||
|
linked remote error propagation, then corrected docs which still
|
||||||
|
described the removed one-shot implementation. Linked spawning and
|
||||||
|
context lifecycle prose to the corresponding API methods and detailed
|
||||||
|
context guide.
|
||||||
|
|
||||||
|
## Files changed
|
||||||
|
|
||||||
|
- `docs/api/core.rst` - describe linked one-shot context execution.
|
||||||
|
- `docs/guide/rpc.rst` - update placement and target call semantics.
|
||||||
|
- `docs/guide/spawning.rst` - document positional target inputs.
|
||||||
|
- `examples/debugging/multi_nested_subactors_error_up_through_nurseries.py` - migrate nested actor target inputs.
|
||||||
|
- `examples/debugging/root_cancelled_but_child_is_in_tty_lock.py` - preserve named recursive target inputs with partials.
|
||||||
|
- `tests/test_advanced_streaming.py` - migrate streaming target inputs.
|
||||||
|
- `tests/test_cancellation.py` - migrate calls and tighten errors.
|
||||||
|
- `tests/test_infected_asyncio.py` - bind asyncio target options.
|
||||||
|
- `tests/test_rpc.py` - migrate RPC target argument binding.
|
||||||
|
- `tests/test_runtime.py` - preserve named runtime target inputs.
|
||||||
|
- `tests/test_spawning.py` - preserve named spawning target inputs.
|
||||||
|
|
||||||
|
## Human edits
|
||||||
|
|
||||||
|
The human selected the stack order and final PR #481 base, asked the
|
||||||
|
agent to continue after each diagnostic step and required a complete
|
||||||
|
commit plan after independently force-pushing the rebased history.
|
||||||
|
After reviewing the migration, the human required every formerly named
|
||||||
|
target input to remain visibly named through `functools.partial()`
|
||||||
|
rather than becoming positional. These were human-directed agent edits;
|
||||||
|
the human also required plain `start_actor()` and `open_context()`
|
||||||
|
references in the spawning and RPC guides to link to their API methods
|
||||||
|
and the detailed context guide, then clarified that `to_actor.run()`
|
||||||
|
already uses the full context API while `Portal.run()` should share
|
||||||
|
linked lifecycle machinery without necessarily delegating through
|
||||||
|
`Portal.open_context()` or adding a `Started` message. The human made
|
||||||
|
no direct source-line edits.
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
---
|
||||||
|
model: openai/gpt-5.6-sol
|
||||||
|
service: opencode
|
||||||
|
timestamp: 2026-08-19T18:46:40Z
|
||||||
|
git_ref: 481ba003
|
||||||
|
diff_cmd: git diff HEAD~1..HEAD
|
||||||
|
---
|
||||||
|
|
||||||
|
Migrate PR #484's downstream one-shot calls to PR #481's final
|
||||||
|
`tractor.to_actor.run()` contract after the stack rebase.
|
||||||
|
|
||||||
|
> `git diff HEAD~1..HEAD -- docs examples tests`
|
||||||
|
|
||||||
|
Pass target arguments positionally and bind target keyword-only inputs
|
||||||
|
with `functools.partial()`. Keep placement and runtime controls as
|
||||||
|
direct `to_actor.run()` keywords. Update the invalid-target-argument
|
||||||
|
test to expect local signature binding before actor startup and require
|
||||||
|
direct `RemoteActorError` propagation from linked one-shots.
|
||||||
|
|
||||||
|
Update API and guide prose to describe positional target inputs,
|
||||||
|
linked `Portal.open_context()` execution and per-child reaping instead
|
||||||
|
of the removed `Portal.run()` and target-`**kwargs` conventions.
|
||||||
|
|
||||||
|
Verification:
|
||||||
|
|
||||||
|
- core and migrated runtime batches: `97 passed`
|
||||||
|
- discovery and related lifecycle batch: `33 passed, 1 skipped`
|
||||||
|
- changed executable examples: `9 passed`
|
||||||
|
- mapped debugger cases: `12 passed, 6 skipped`
|
||||||
|
- Ruff, compilation and `git diff --check`: clean
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
---
|
||||||
|
model: gpt-5.6-sol
|
||||||
|
service: opencode
|
||||||
|
session: tractor-addr-unpacking-followup
|
||||||
|
timestamp: 2026-08-19T21:31:44Z
|
||||||
|
git_ref: f81fc5e5
|
||||||
|
scope: code
|
||||||
|
substantive: true
|
||||||
|
raw_file: 20260819T213144Z_f81fc5e5_prompt_io.raw.md
|
||||||
|
---
|
||||||
|
|
||||||
|
## Prompt
|
||||||
|
|
||||||
|
The human requested runtime boundary integration with the established
|
||||||
|
per-step implementation and commit-plan workflow.
|
||||||
|
|
||||||
|
## Response summary
|
||||||
|
|
||||||
|
Kept `TunnelledAddress` available to callers while peeling it at the
|
||||||
|
last outbound boundary before transport lookup and dialing. Added a
|
||||||
|
regression which captures both transport arguments and confirms plain
|
||||||
|
TCP behavior is unchanged.
|
||||||
|
|
||||||
|
## Files changed
|
||||||
|
|
||||||
|
- `tractor/ipc/_chan.py` - peel tunnel annotations before outbound
|
||||||
|
transport dispatch and connection.
|
||||||
|
- `tests/ipc/test_channel_tunnel_boundary.py` - verify plain and
|
||||||
|
tunnelled channel inputs deliver only TCP overlays.
|
||||||
|
|
||||||
|
## Human edits
|
||||||
|
|
||||||
|
The human chose the runtime-boundary slice, required the existing
|
||||||
|
per-step commit-plan flow, and previously established that wrappers
|
||||||
|
must retain bindspace metadata without impersonating transports. The
|
||||||
|
agent implemented those constraints; no direct manual source edits were
|
||||||
|
observed during this step.
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
---
|
||||||
|
model: gpt-5.6-sol
|
||||||
|
service: opencode
|
||||||
|
timestamp: 2026-08-19T21:31:44Z
|
||||||
|
git_ref: f81fc5e5
|
||||||
|
diff_cmd: git diff HEAD~1..HEAD
|
||||||
|
---
|
||||||
|
|
||||||
|
# Raw output - outbound tunnel boundary
|
||||||
|
|
||||||
|
The human requested the next tunnelled-address slice using the same
|
||||||
|
per-step commit-plan flow. Existing design decisions require retaining
|
||||||
|
tunnel metadata until the narrow IPC transport boundary and never
|
||||||
|
teaching exact-type transport tables about tunnel wrappers.
|
||||||
|
|
||||||
|
> `git diff HEAD~1..HEAD -- tractor/ipc/_chan.py tests/ipc/test_channel_tunnel_boundary.py`
|
||||||
|
|
||||||
|
Extended channel address inputs to accept tunnel declarations, then
|
||||||
|
called `strip_tunnels()` immediately before exact-type transport lookup
|
||||||
|
and `connect_to()`. Added plain/tunnel parameterized coverage proving
|
||||||
|
both operations receive the identical TCP overlay while the original
|
||||||
|
wrapper retains its tunnel spec.
|
||||||
|
|
||||||
|
Verification included focused IPC tests, Ruff, discovery/IPC suites,
|
||||||
|
and the full tractor suite.
|
||||||
|
|
@ -0,0 +1,40 @@
|
||||||
|
---
|
||||||
|
model: gpt-5.6-sol
|
||||||
|
service: opencode
|
||||||
|
session: tractor-addr-unpacking-followup
|
||||||
|
timestamp: 2026-08-19T21:31:45Z
|
||||||
|
git_ref: f81fc5e5
|
||||||
|
scope: code
|
||||||
|
substantive: true
|
||||||
|
raw_file: 20260819T213145Z_f81fc5e5_prompt_io.raw.md
|
||||||
|
---
|
||||||
|
|
||||||
|
## Prompt
|
||||||
|
|
||||||
|
The human requested completion of inbound runtime peeling using the
|
||||||
|
same per-step implementation and commit-plan workflow.
|
||||||
|
|
||||||
|
## Response summary
|
||||||
|
|
||||||
|
Preserved tunnel declarations through listener configuration, peeled
|
||||||
|
them immediately before `Endpoint` construction, and used the overlay
|
||||||
|
for backend-specific random listener allocation after registry
|
||||||
|
discovery. Added a real listener regression for the reflection and
|
||||||
|
exact-type boundary.
|
||||||
|
|
||||||
|
## Files changed
|
||||||
|
|
||||||
|
- `tractor/ipc/_server.py` - accept wrapper declarations and peel at
|
||||||
|
`Endpoint` construction.
|
||||||
|
- `tractor/_root.py` - allocate random transport addresses from the
|
||||||
|
contacted registry's overlay.
|
||||||
|
- `tests/ipc/test_server_tunnel_boundary.py` - verify a real listener
|
||||||
|
stores only TCP while preserving the source annotation.
|
||||||
|
|
||||||
|
## Human edits
|
||||||
|
|
||||||
|
The human selected the runtime-boundary work and previously corrected
|
||||||
|
the architecture so tractor retains future bindspace provisioning
|
||||||
|
ownership while `Endpoint` sees only application transports. The agent
|
||||||
|
implemented and tested that direction; no direct manual source edits
|
||||||
|
were observed during this step.
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
---
|
||||||
|
model: gpt-5.6-sol
|
||||||
|
service: opencode
|
||||||
|
timestamp: 2026-08-19T21:31:45Z
|
||||||
|
git_ref: f81fc5e5
|
||||||
|
diff_cmd: git diff HEAD~1..HEAD
|
||||||
|
---
|
||||||
|
|
||||||
|
# Raw output - inbound tunnel boundary
|
||||||
|
|
||||||
|
The human requested runtime boundary integration while preserving the
|
||||||
|
future tractor-owned bindspace lifecycle.
|
||||||
|
|
||||||
|
> `git diff HEAD~1..HEAD -- tractor/ipc/_server.py tractor/_root.py tests/ipc/test_server_tunnel_boundary.py`
|
||||||
|
|
||||||
|
Broadened listener declarations to carry tunnel wrappers until
|
||||||
|
`_serve_ipc_eps()` and peeled immediately before `Endpoint`
|
||||||
|
construction. Also peeled a contacted tunnelled registry before
|
||||||
|
backend-specific random listener allocation. Added a real TCP listener
|
||||||
|
regression proving `Endpoint` stores only the resolved overlay while
|
||||||
|
the original declaration retains bindspace metadata.
|
||||||
|
|
||||||
|
Verification included `465` collected tests, `84` passing
|
||||||
|
discovery/IPC tests with two xpasses, Ruff, and the full suite with
|
||||||
|
`447` passes.
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
---
|
||||||
|
model: openai/gpt-5.6-sol
|
||||||
|
service: opencode
|
||||||
|
session: 76c5d31c-5a2f-4503-9b16-410ee7f4fab3
|
||||||
|
timestamp: 2026-08-19T23:48:23Z
|
||||||
|
git_ref: 557065d8
|
||||||
|
scope: tests
|
||||||
|
substantive: true
|
||||||
|
raw_file: 20260819T234823Z_557065d8_prompt_io.raw.md
|
||||||
|
---
|
||||||
|
|
||||||
|
## Prompt
|
||||||
|
|
||||||
|
Investigate PR #481's red CI run, explain the missing T-800 and
|
||||||
|
debugger-output failures, and proceed with fixes in the PR #481
|
||||||
|
worktree.
|
||||||
|
|
||||||
|
## Response summary
|
||||||
|
|
||||||
|
Updated stale teardown assertions to match #481's direct hard-reap
|
||||||
|
path and observable process-lifetime invariants. Made nested debugger
|
||||||
|
checks consume the complete pexpect transcript rather than only the
|
||||||
|
last prompt latch.
|
||||||
|
|
||||||
|
## Files changed
|
||||||
|
|
||||||
|
- `tests/devx/test_debugger.py` - assert EOF/dead-process teardown and
|
||||||
|
accumulate nested debugger output across prompt boundaries.
|
||||||
|
- `tests/devx/test_tooling.py` - assert cancel-timeout hard-reap
|
||||||
|
escalation instead of the bypassed T-800 backend marker.
|
||||||
|
|
||||||
|
## Human edits
|
||||||
|
|
||||||
|
The human reported the still-red PR #481 CI, supplied a failing job URL,
|
||||||
|
required work in `/wkts/pr481_review_fixes` and directed the agent to
|
||||||
|
continue immediately. No direct source-line edits were made by the
|
||||||
|
human.
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
---
|
||||||
|
model: openai/gpt-5.6-sol
|
||||||
|
service: opencode
|
||||||
|
timestamp: 2026-08-19T23:48:23Z
|
||||||
|
git_ref: 557065d8
|
||||||
|
diff_cmd: git diff HEAD~1..HEAD
|
||||||
|
---
|
||||||
|
|
||||||
|
Diagnose and fix the stale debugger and reaper assertions failing PR
|
||||||
|
#481's Unix CI jobs.
|
||||||
|
|
||||||
|
> `git diff HEAD~1..HEAD -- tests/devx/test_debugger.py tests/devx/test_tooling.py`
|
||||||
|
|
||||||
|
Replace the old T-800 backend-log requirement with the new bounded
|
||||||
|
cancel-ack escalation evidence. Prove debugger teardown with EOF and a
|
||||||
|
dead child process instead of requiring optional `KeyboardInterrupt`
|
||||||
|
text. Accumulate all pexpect prompt chunks for nested error propagation
|
||||||
|
so expected tracebacks are not lost when `child.before` advances.
|
||||||
|
|
||||||
|
Verification:
|
||||||
|
|
||||||
|
- exact failed debugger/reaper nodes: `4 passed`
|
||||||
|
- debugger/tooling TCP: `39 passed, 6 skipped`
|
||||||
|
- debugger/tooling UDS: `39 passed, 6 skipped`
|
||||||
|
- full TCP suite: `478 passed, 9 skipped, 7 xfailed, 3 xpassed`
|
||||||
|
- full UDS rerun: `476 passed, 11 skipped, 8 xfailed, 2 xpassed`
|
||||||
|
|
@ -0,0 +1,43 @@
|
||||||
|
---
|
||||||
|
model: openai/gpt-5.6-sol
|
||||||
|
service: opencode
|
||||||
|
session: 76c5d31c-5a2f-4503-9b16-410ee7f4fab3
|
||||||
|
timestamp: 2026-08-19T23:48:24Z
|
||||||
|
git_ref: 557065d8
|
||||||
|
scope: code
|
||||||
|
substantive: true
|
||||||
|
raw_file: 20260819T234824Z_557065d8_prompt_io.raw.md
|
||||||
|
---
|
||||||
|
|
||||||
|
## Prompt
|
||||||
|
|
||||||
|
Investigate and fix PR #481's macOS TCP clustering and stream-overrun
|
||||||
|
failures without sacrificing IPC frame integrity or structured
|
||||||
|
concurrency.
|
||||||
|
|
||||||
|
## Response summary
|
||||||
|
|
||||||
|
Changed cancellation during `send_all()` from actor-wide stream closure
|
||||||
|
to shielded complete-frame publication followed by immediate pending
|
||||||
|
cancellation. Prevented failed overrun error shipment from promoting a
|
||||||
|
secondary transport closure over the context-local primary condition.
|
||||||
|
|
||||||
|
## Files changed
|
||||||
|
|
||||||
|
- `tractor/ipc/_transport.py` - complete in-flight frames before
|
||||||
|
delivering sender cancellation.
|
||||||
|
- `tractor/_context.py` - absorb transport closure while reporting an
|
||||||
|
overrun on an already-closing channel.
|
||||||
|
- `tests/ipc/test_each_tpt.py` - prove complete framing, cancellation
|
||||||
|
delivery and channel reuse.
|
||||||
|
- `tests/test_context_stream_semantics.py` - prove overrun reporting
|
||||||
|
tolerates a closed transport.
|
||||||
|
|
||||||
|
## Human edits
|
||||||
|
|
||||||
|
The human reported PR #481's red CI, asked for diagnosis and directed
|
||||||
|
the agent to proceed in the dedicated PR #481 worktree. During final
|
||||||
|
review, the human required preservation of the original far-end
|
||||||
|
cancellation rationale and fuller documentation of frame shielding,
|
||||||
|
shared-channel ownership and cancellation-delay tradeoffs. These were
|
||||||
|
human-directed agent edits; the human made no direct source-line edits.
|
||||||
|
|
@ -0,0 +1,31 @@
|
||||||
|
---
|
||||||
|
model: openai/gpt-5.6-sol
|
||||||
|
service: opencode
|
||||||
|
timestamp: 2026-08-19T23:48:24Z
|
||||||
|
git_ref: 557065d8
|
||||||
|
diff_cmd: git diff HEAD~1..HEAD
|
||||||
|
---
|
||||||
|
|
||||||
|
Fix the macOS TCP regressions where cancellation during a framed send
|
||||||
|
closed the actor-wide channel and replaced primary stream errors with
|
||||||
|
secondary `TransportClosed` failures.
|
||||||
|
|
||||||
|
> `git diff HEAD~1..HEAD -- tractor/ipc/_transport.py tractor/_context.py tests/ipc/test_each_tpt.py tests/test_context_stream_semantics.py`
|
||||||
|
|
||||||
|
Shield complete frame publication, then deliver pending cancellation
|
||||||
|
immediately after leaving the shield. Preserve channel reuse instead of
|
||||||
|
closing the multiplexed socket from a context-local sender. Treat
|
||||||
|
`TransportClosed` while shipping `StreamOverrun` as failed delivery so
|
||||||
|
the secondary error can not crash the actor-wide RPC loop.
|
||||||
|
|
||||||
|
Add deterministic unit regressions for cancellation in the middle of a
|
||||||
|
frame and overrun reporting after transport closure.
|
||||||
|
|
||||||
|
Verification:
|
||||||
|
|
||||||
|
- transport/context unit regressions: `3 passed`
|
||||||
|
- exact TCP and UDS CI-node batches: `11 passed, 1 skipped`
|
||||||
|
- transport/context/clustering/RPC TCP: `88 passed`
|
||||||
|
- transport/context/clustering/RPC UDS: `86 passed, 2 skipped`
|
||||||
|
- full TCP suite: `478 passed, 9 skipped, 7 xfailed, 3 xpassed`
|
||||||
|
- full UDS rerun: `476 passed, 11 skipped, 8 xfailed, 2 xpassed`
|
||||||
|
|
@ -0,0 +1,41 @@
|
||||||
|
---
|
||||||
|
model: gpt-5.6-sol
|
||||||
|
service: opencode
|
||||||
|
session: tractor-addr-unpacking-followup
|
||||||
|
timestamp: 2026-08-20T02:15:16Z
|
||||||
|
git_ref: dfad66a0
|
||||||
|
scope: docs
|
||||||
|
substantive: true
|
||||||
|
raw_file: 20260820T021516Z_dfad66a0_prompt_io.raw.md
|
||||||
|
---
|
||||||
|
|
||||||
|
## Prompt
|
||||||
|
|
||||||
|
The human requested that the bindspace plan preserve the agreed
|
||||||
|
capability, spawn-bootstrap, endpoint-role, namespace augmentation,
|
||||||
|
random-address, and teardown semantics, using `github/ns_aware` as
|
||||||
|
prototype input.
|
||||||
|
|
||||||
|
## Response summary
|
||||||
|
|
||||||
|
Updated plan-03 and the shared backend contract to separate serializable
|
||||||
|
bindspace declarations from scoped live capabilities, make namespace
|
||||||
|
entry a pre-runtime spawn operation, keep maddr paths role-neutral, and
|
||||||
|
define listen/dial provisioning plus ownership-sensitive teardown.
|
||||||
|
|
||||||
|
## Files changed
|
||||||
|
|
||||||
|
- `ai/tpt-backends/03_wg_tunnel_bindspace.md` - layer-C capability,
|
||||||
|
bootstrap, role, teardown, test, and risk model.
|
||||||
|
- `ai/tpt-backends/00_shared_backend_contract.md` - distinguish
|
||||||
|
transport bind selectors from process namespace lifecycle.
|
||||||
|
|
||||||
|
## Human edits
|
||||||
|
|
||||||
|
The human supplied the core architecture: structured scoped
|
||||||
|
capabilities, spawn-time namespace entry, orthogonal namespace
|
||||||
|
augmentation, source/destination-dependent provisioning, and
|
||||||
|
role-dependent teardown. They also rejected premature assumptions about
|
||||||
|
`open_bindspace()` returning an address and requested grounding in the
|
||||||
|
existing namespace prototype. The agent translated those decisions into
|
||||||
|
the plan text; no direct manual source edits were observed.
|
||||||
|
|
@ -0,0 +1,34 @@
|
||||||
|
---
|
||||||
|
model: gpt-5.6-sol
|
||||||
|
service: opencode
|
||||||
|
timestamp: 2026-08-20T02:15:16Z
|
||||||
|
git_ref: dfad66a0
|
||||||
|
diff_cmd: git diff HEAD~1..HEAD
|
||||||
|
---
|
||||||
|
|
||||||
|
# Raw output - bindspace capability design
|
||||||
|
|
||||||
|
The human corrected the layer-C design around local network-stack
|
||||||
|
realization. They established that bindspace state should be both
|
||||||
|
structured and a scoped capability; namespace entry belongs in
|
||||||
|
subactor bootstrap; maddrs can describe source or destination network
|
||||||
|
paths while namespace selection augments them orthogonally; random
|
||||||
|
address and teardown behavior depend on operation role and ownership.
|
||||||
|
|
||||||
|
They directed comparison with the prototype on `github/ns_aware` and
|
||||||
|
requested these decisions be preserved in the plan.
|
||||||
|
|
||||||
|
> `git diff HEAD~1..HEAD -- ai/tpt-backends/03_wg_tunnel_bindspace.md ai/tpt-backends/00_shared_backend_contract.md`
|
||||||
|
|
||||||
|
Reworked layer C around serializable `BindspaceSpec`, stable
|
||||||
|
`BindspaceIdentity`, and scoped non-serializable `BindspaceHandle`
|
||||||
|
concepts. Namespace FDs pin identity and lifetime; parent/supervisor
|
||||||
|
provisioning transfers entry capability through spawn; the child enters
|
||||||
|
before runtime, channels, listeners, sockets, or worker threads and then
|
||||||
|
drops authority. Listen/dial roles and owned/borrowed teardown are
|
||||||
|
explicit, while maddrs remain role-neutral network-path declarations.
|
||||||
|
|
||||||
|
The shared backend contract now separates transport-level `.bindspace`
|
||||||
|
selectors from process namespace lifecycle. Added tests/risks for FD
|
||||||
|
identity, bootstrap ordering, privilege drop, role ownership, and
|
||||||
|
shared-resource teardown.
|
||||||
|
|
@ -0,0 +1,32 @@
|
||||||
|
---
|
||||||
|
model: openai/gpt-5.6-sol
|
||||||
|
service: opencode
|
||||||
|
session: 76c5d31c-5a2f-4503-9b16-410ee7f4fab3
|
||||||
|
timestamp: 2026-08-20T02:30:04Z
|
||||||
|
git_ref: 88a23449
|
||||||
|
scope: tests
|
||||||
|
substantive: true
|
||||||
|
raw_file: 20260820T023004Z_88a23449_prompt_io.raw.md
|
||||||
|
---
|
||||||
|
|
||||||
|
## Prompt
|
||||||
|
|
||||||
|
Inspect the two failed macOS jobs in PR #481's new CI run and continue
|
||||||
|
toward a green landing candidate.
|
||||||
|
|
||||||
|
## Response summary
|
||||||
|
|
||||||
|
Confirmed both jobs fail only the known nested crash-REPL scenario from
|
||||||
|
issue #320, while Ubuntu TCP/UDS and Windows pass. Added a targeted
|
||||||
|
macOS-CI skip without reducing Linux coverage.
|
||||||
|
|
||||||
|
## Files changed
|
||||||
|
|
||||||
|
- `tests/devx/test_debugger.py` - skip the issue #320 nested
|
||||||
|
crash-REPL node on Darwin CI.
|
||||||
|
|
||||||
|
## Human edits
|
||||||
|
|
||||||
|
The human monitored the new CI run, reported both macOS jobs dead and
|
||||||
|
directed the agent to continue diagnosis. No direct source-line edits
|
||||||
|
were made by the human.
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
---
|
||||||
|
model: openai/gpt-5.6-sol
|
||||||
|
service: opencode
|
||||||
|
timestamp: 2026-08-20T02:30:04Z
|
||||||
|
git_ref: 88a23449
|
||||||
|
diff_cmd: git diff HEAD~1..HEAD
|
||||||
|
---
|
||||||
|
|
||||||
|
Diagnose the remaining macOS PR #481 CI failures after the Linux
|
||||||
|
debugger and transport fixes passed.
|
||||||
|
|
||||||
|
> `git diff HEAD~1..HEAD -- tests/devx/test_debugger.py`
|
||||||
|
|
||||||
|
Both macOS transports failed the same deeply nested crash-REPL test
|
||||||
|
already tracked by issue #320: TCP omitted one actor-specific traceback
|
||||||
|
record and UDS timed out waiting for a nested prompt. Apply an explicit
|
||||||
|
Darwin-CI skip to this one node while retaining Linux TCP/UDS coverage.
|
||||||
|
|
||||||
|
Verification:
|
||||||
|
|
||||||
|
- debugger/tooling TCP: `39 passed, 6 skipped`
|
||||||
|
- debugger/tooling UDS: `39 passed, 6 skipped`
|
||||||
|
- Ruff, compilation and `git diff --check`: clean
|
||||||
|
|
@ -0,0 +1,40 @@
|
||||||
|
---
|
||||||
|
model: openai/gpt-5.6-sol
|
||||||
|
service: opencode
|
||||||
|
session: 76c5d31c-5a2f-4503-9b16-410ee7f4fab3
|
||||||
|
timestamp: 2026-08-20T02:30:05Z
|
||||||
|
git_ref: 88a23449
|
||||||
|
scope: docs
|
||||||
|
substantive: true
|
||||||
|
raw_file: 20260820T023005Z_88a23449_prompt_io.raw.md
|
||||||
|
---
|
||||||
|
|
||||||
|
## Prompt
|
||||||
|
|
||||||
|
Audit all documentation and executable examples once more, replacing
|
||||||
|
prescriptive `run_in_actor()` usage with `to_actor.run()` or explicit
|
||||||
|
actor/context lifetime APIs before PR #481 lands.
|
||||||
|
|
||||||
|
## Response summary
|
||||||
|
|
||||||
|
Rewrote one-shot documentation around direct blocking result delivery,
|
||||||
|
linked context execution and per-call reaping. Migrated all runnable
|
||||||
|
examples, using daemon actors where reciprocal dialogs require longer
|
||||||
|
lifetimes. Added API/guide cross-links and retained only three explicit
|
||||||
|
legacy references.
|
||||||
|
|
||||||
|
## Files changed
|
||||||
|
|
||||||
|
- `docs/` - update API, quickstart and subsystem guides to showcase
|
||||||
|
`tractor.to_actor.run()` and link its underlying core APIs.
|
||||||
|
- `examples/` - migrate one-shot calls and preserve explicit daemon
|
||||||
|
lifetimes for reciprocal or long-lived actor dialogs.
|
||||||
|
|
||||||
|
## Human edits
|
||||||
|
|
||||||
|
The human requested a final docs pass covering every place that should
|
||||||
|
showcase `to_actor` over `.run_in_actor()`. Earlier review also required
|
||||||
|
named target arguments to remain visible through `functools.partial()`
|
||||||
|
and core API references to link to local guides/reference pages. These
|
||||||
|
were human-directed agent edits; the human made no direct source-line
|
||||||
|
edits.
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
---
|
||||||
|
model: openai/gpt-5.6-sol
|
||||||
|
service: opencode
|
||||||
|
timestamp: 2026-08-20T02:30:05Z
|
||||||
|
git_ref: 88a23449
|
||||||
|
diff_cmd: git diff HEAD~1..HEAD
|
||||||
|
---
|
||||||
|
|
||||||
|
Perform a final rendered-documentation and executable-example pass so
|
||||||
|
PR #481 showcases `tractor.to_actor.run()` instead of the legacy
|
||||||
|
`ActorNursery.run_in_actor()` API.
|
||||||
|
|
||||||
|
> `git diff HEAD~1..HEAD -- docs examples`
|
||||||
|
|
||||||
|
Migrate one-shot guides and examples to direct result delivery through
|
||||||
|
`to_actor.run()`, preserving named target inputs with target partials.
|
||||||
|
Use daemon actors and concurrent portal calls where reciprocal actor
|
||||||
|
lifetimes require both peers to coexist. Add API and guide cross-links,
|
||||||
|
and retain only explicit legacy/removal notes.
|
||||||
|
|
||||||
|
Verification:
|
||||||
|
|
||||||
|
- executable docs examples: `23 passed`
|
||||||
|
- debugger/tooling TCP: `39 passed, 6 skipped`
|
||||||
|
- debugger/tooling UDS: `39 passed, 6 skipped`
|
||||||
|
- Ruff, compilation and `git diff --check`: clean
|
||||||
|
- local Sphinx build unavailable because Sphinx is not installed
|
||||||
|
|
@ -0,0 +1,41 @@
|
||||||
|
---
|
||||||
|
model: gpt-5.6-sol
|
||||||
|
service: opencode
|
||||||
|
session: tractor-addr-unpacking
|
||||||
|
timestamp: 2026-08-20T03:31:07Z
|
||||||
|
git_ref: ba07e09d
|
||||||
|
scope: code
|
||||||
|
substantive: true
|
||||||
|
raw_file: 20260820T033107Z_ba07e09d_prompt_io.raw.md
|
||||||
|
---
|
||||||
|
|
||||||
|
## Prompt
|
||||||
|
|
||||||
|
The human requested canonical tagged transport addresses with a
|
||||||
|
reader-first migration. TCP should decode `('tcp', host, port)`, Unix
|
||||||
|
should decode `('unix', path)`, `uds` should remain an accepted input
|
||||||
|
alias and internal transport key, and legacy tuple/list inputs must keep
|
||||||
|
working before writers switch formats.
|
||||||
|
|
||||||
|
## Response summary
|
||||||
|
|
||||||
|
Introduced canonical and compatibility address aliases, explicit tagged
|
||||||
|
dispatch, transport-specific tagged readers, and focused serialization
|
||||||
|
tests. Kept legacy pair inputs and native IPv6 socket values readable so
|
||||||
|
this boundary can ship before tagged emission.
|
||||||
|
|
||||||
|
## Files changed
|
||||||
|
|
||||||
|
- `tractor/discovery/_addr.py` - address aliases and tagged dispatch.
|
||||||
|
- `tractor/ipc/_tcp.py` - tagged, legacy, and IPv6 TCP decoding.
|
||||||
|
- `tractor/ipc/_uds.py` - canonical Unix and UDS-alias decoding.
|
||||||
|
- `tests/discovery/test_address_serialization.py` - reader compatibility
|
||||||
|
coverage.
|
||||||
|
|
||||||
|
## Human edits
|
||||||
|
|
||||||
|
The human supplied the canonical `tcp` and `unix` forms, chose `uds` as
|
||||||
|
an input-only serialization alias while preserving it as the runtime
|
||||||
|
transport key, and required a reader-first commit boundary. The agent
|
||||||
|
implemented those decisions; no direct manual source edits were
|
||||||
|
observed.
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
---
|
||||||
|
model: gpt-5.6-sol
|
||||||
|
service: opencode
|
||||||
|
timestamp: 2026-08-20T03:31:07Z
|
||||||
|
git_ref: ba07e09d
|
||||||
|
diff_cmd: git diff HEAD~1..HEAD
|
||||||
|
---
|
||||||
|
|
||||||
|
# Raw output - tagged address readers
|
||||||
|
|
||||||
|
The human requested a migration away from ambiguous untagged transport
|
||||||
|
tuples. They established `('tcp', host, port)` and `('unix', path)` as
|
||||||
|
canonical forms, retained `('uds', path)` as an input alias, and required
|
||||||
|
a reader-first compatibility boundary before changing emitted values.
|
||||||
|
|
||||||
|
> `git diff HEAD~1..HEAD -- tractor/discovery/_addr.py tractor/ipc/_tcp.py tractor/ipc/_uds.py tests/discovery/test_address_serialization.py`
|
||||||
|
|
||||||
|
Added explicit tagged address aliases and dispatch, taught TCP and UDS
|
||||||
|
readers to decode tagged tuple/list payloads, preserved legacy pair input,
|
||||||
|
and retained native IPv6 socket-address decoding. Added focused tests for
|
||||||
|
canonical tags, the UDS alias, msgpack-style lists, legacy pairs, and IPv6
|
||||||
|
socket values.
|
||||||
|
|
||||||
|
Focused reader tests and Ruff checks passed before the writer migration
|
||||||
|
was applied.
|
||||||
|
|
@ -0,0 +1,45 @@
|
||||||
|
---
|
||||||
|
model: gpt-5.6-sol
|
||||||
|
service: opencode
|
||||||
|
session: tractor-addr-unpacking
|
||||||
|
timestamp: 2026-08-20T03:31:08Z
|
||||||
|
git_ref: ba07e09d
|
||||||
|
scope: code
|
||||||
|
substantive: true
|
||||||
|
raw_file: 20260820T033108Z_ba07e09d_prompt_io.raw.md
|
||||||
|
---
|
||||||
|
|
||||||
|
## Prompt
|
||||||
|
|
||||||
|
The human asked the agent to continue after adding tagged readers,
|
||||||
|
complete canonical address emission without dropping legacy input
|
||||||
|
compatibility, verify the migration, and prepare a complete multi-commit
|
||||||
|
package when the turn was done.
|
||||||
|
|
||||||
|
## Response summary
|
||||||
|
|
||||||
|
Changed `.unwrap()` to emit tagged TCP and Unix addresses, updated direct
|
||||||
|
tuple consumers and spawn payload declarations, and aligned multiaddr,
|
||||||
|
runtime, IPC, and discovery tests with canonical serialized equality.
|
||||||
|
Kept untagged tuples and the `uds` spelling readable at input boundaries.
|
||||||
|
|
||||||
|
## Files changed
|
||||||
|
|
||||||
|
- `tractor/discovery/_addr.py` - canonical output alias.
|
||||||
|
- `tractor/discovery/_multiaddr.py` - tagged address composition.
|
||||||
|
- `tractor/ipc/_tcp.py` - tagged emission and direct socket dialing.
|
||||||
|
- `tractor/ipc/_uds.py` - tagged full-path emission.
|
||||||
|
- `tractor/msg/types.py` - protocol-neutral spawn tuple containers.
|
||||||
|
- `tests/discovery/test_address_serialization.py` - writer assertions.
|
||||||
|
- `tests/discovery/test_multiaddr.py` - canonical round-trip assertions.
|
||||||
|
- `tests/discovery/test_tpt_bind_addrs.py` - tagged bind assertions.
|
||||||
|
- `tests/ipc/test_each_tpt.py` - canonical runtime address assertions.
|
||||||
|
- `tests/ipc/test_server_tunnel_boundary.py` - tagged TCP destructuring.
|
||||||
|
- `tests/test_local.py` - canonical registry comparison.
|
||||||
|
|
||||||
|
## Human edits
|
||||||
|
|
||||||
|
The human established the reader-before-writer sequencing, canonical tag
|
||||||
|
spellings, retained compatibility expectations, and requested final
|
||||||
|
multi-commit packaging. The agent implemented and tested those choices;
|
||||||
|
no direct manual source edits were observed.
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
---
|
||||||
|
model: gpt-5.6-sol
|
||||||
|
service: opencode
|
||||||
|
timestamp: 2026-08-20T03:31:08Z
|
||||||
|
git_ref: ba07e09d
|
||||||
|
diff_cmd: git diff HEAD~1..HEAD
|
||||||
|
---
|
||||||
|
|
||||||
|
# Raw output - canonical tagged address writers
|
||||||
|
|
||||||
|
After the reader compatibility boundary, the human asked the agent to
|
||||||
|
continue the migration and package the completed work as dependency-
|
||||||
|
ordered commits.
|
||||||
|
|
||||||
|
> `git diff HEAD~1..HEAD -- tractor/discovery/_addr.py tractor/discovery/_multiaddr.py tractor/ipc/_tcp.py tractor/ipc/_uds.py tractor/msg/types.py tests/discovery/test_address_serialization.py tests/discovery/test_multiaddr.py tests/discovery/test_tpt_bind_addrs.py tests/ipc/test_each_tpt.py tests/ipc/test_server_tunnel_boundary.py tests/test_local.py`
|
||||||
|
|
||||||
|
Switched TCP and Unix `.unwrap()` output to canonical tagged tuples,
|
||||||
|
updated direct transport and multiaddr consumers, widened spawn message
|
||||||
|
tuple containers for protocol-specific shapes, and migrated runtime and
|
||||||
|
test comparisons to serialized address equality. Legacy inputs remain
|
||||||
|
accepted at `wrap_address()` and backend reader boundaries.
|
||||||
|
|
||||||
|
Ruff and focused tests passed. The complete non-debugger TCP suite passed
|
||||||
|
with 412 tests; the UDS suite reached 80% without failure before the
|
||||||
|
harness timeout, then all 97 remaining tests passed on resume. Debugger
|
||||||
|
PTY coverage was excluded after an unrelated timeout.
|
||||||
|
|
@ -0,0 +1,35 @@
|
||||||
|
---
|
||||||
|
model: openai/gpt-5.6-sol
|
||||||
|
service: opencode
|
||||||
|
session: 76c5d31c-5a2f-4503-9b16-410ee7f4fab3
|
||||||
|
timestamp: 2026-08-20T13:51:25Z
|
||||||
|
git_ref: 9f99043b
|
||||||
|
scope: tests
|
||||||
|
substantive: true
|
||||||
|
raw_file: 20260820T135125Z_9f99043b_prompt_io.raw.md
|
||||||
|
---
|
||||||
|
|
||||||
|
## Prompt
|
||||||
|
|
||||||
|
Continue preparing PR #481 for landing after the prior test and
|
||||||
|
documentation commits were pushed. Follow CI and proceed with clear next
|
||||||
|
steps without merging or changing remote content unasked.
|
||||||
|
|
||||||
|
## Response summary
|
||||||
|
|
||||||
|
Followed CI through completion and found both macOS jobs failed because the
|
||||||
|
new `skipif` expression returned the `CI=true` environment string. Corrected
|
||||||
|
the condition to pass pytest a boolean before evaluating the marker. A
|
||||||
|
simulated Darwin-CI run now skips cleanly, and the sequential TCP and UDS
|
||||||
|
debugger/tooling suites each pass with 39 passed and 6 skipped.
|
||||||
|
|
||||||
|
## Files changed
|
||||||
|
|
||||||
|
- `tests/devx/test_debugger.py` - coerce the Darwin-CI skip condition to a
|
||||||
|
boolean.
|
||||||
|
|
||||||
|
## Human edits
|
||||||
|
|
||||||
|
The human pushed the preceding commits, directed the agent to continue, and
|
||||||
|
approved recording this test-only follow-up in Prompt-IO. No direct
|
||||||
|
source-line edits were made by the human.
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
---
|
||||||
|
model: openai/gpt-5.6-sol
|
||||||
|
service: opencode
|
||||||
|
timestamp: 2026-08-20T13:51:25Z
|
||||||
|
git_ref: 9f99043b
|
||||||
|
diff_cmd: git diff HEAD~1..HEAD
|
||||||
|
---
|
||||||
|
|
||||||
|
Continue preparing PR #481 for landing after the test and documentation
|
||||||
|
commits were pushed. Follow the new CI run to completion and diagnose any
|
||||||
|
failures.
|
||||||
|
|
||||||
|
> `git diff HEAD~1..HEAD -- tests/devx/test_debugger.py`
|
||||||
|
|
||||||
|
Both macOS jobs failed while evaluating the new `skipif` marker. The
|
||||||
|
expression returned the `CI=true` environment string instead of a boolean,
|
||||||
|
so pytest evaluated `true` as Python source and raised `NameError` during
|
||||||
|
test setup. Coerce `_ci_env` to `bool` so pytest receives a boolean marker
|
||||||
|
condition on Darwin CI.
|
||||||
|
|
||||||
|
Verification should exercise the condition with `CI=true` and a simulated
|
||||||
|
Darwin platform, then rerun the debugger/tooling TCP and UDS suites.
|
||||||
|
|
@ -0,0 +1,43 @@
|
||||||
|
---
|
||||||
|
model: openai/gpt-5.6-sol
|
||||||
|
service: opencode
|
||||||
|
session: 76c5d31c-5a2f-4503-9b16-410ee7f4fab3
|
||||||
|
timestamp: 2026-08-20T14:38:50Z
|
||||||
|
git_ref: 559fd0f1
|
||||||
|
scope: code
|
||||||
|
substantive: true
|
||||||
|
raw_file: 20260820T143845Z_559fd0f1_prompt_io.raw.md
|
||||||
|
---
|
||||||
|
|
||||||
|
## Prompt
|
||||||
|
|
||||||
|
Continue preparing PR #481 after the latest fix was pushed. Follow CI and
|
||||||
|
proceed with clear next steps toward a green landing candidate.
|
||||||
|
|
||||||
|
## Response summary
|
||||||
|
|
||||||
|
Traced the remaining macOS UDS failure to cancellation racing transport
|
||||||
|
teardown inside the shielded framed-send path. Preserve pending cancellation
|
||||||
|
over a transport error caused by concurrent teardown, and add a deterministic
|
||||||
|
regression for that ordering. A follow-up A/B run showed the corrected
|
||||||
|
cancellation precedence changes which nested debugger intermediary is
|
||||||
|
rendered as the immediate source versus relay, so retain coverage for both
|
||||||
|
actor levels without pinning those racy roles. The adjusted UDS node passes
|
||||||
|
three consecutive runs, and both debugger/tooling transport suites pass with
|
||||||
|
39 passed and 6 skipped.
|
||||||
|
|
||||||
|
## Files changed
|
||||||
|
|
||||||
|
- `tractor/ipc/_transport.py` - deliver pending cancellation before
|
||||||
|
translating a shielded send's transport error.
|
||||||
|
- `tests/ipc/test_each_tpt.py` - reproduce cancellation followed by local
|
||||||
|
stream closure during shielded frame publication.
|
||||||
|
- `tests/devx/test_debugger.py` - accept either valid source/relay role for
|
||||||
|
each nested intermediary while retaining the actor and error assertions.
|
||||||
|
|
||||||
|
## Human edits
|
||||||
|
|
||||||
|
The human pushed the preceding fix, ran the proposed verification plan, and
|
||||||
|
reported a repeated UDS debugger failure. That report prompted the A/B
|
||||||
|
comparison and role-insensitive assertion. No direct source-line edits were
|
||||||
|
made by the human.
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
---
|
||||||
|
model: openai/gpt-5.6-sol
|
||||||
|
service: opencode
|
||||||
|
timestamp: 2026-08-20T14:38:50Z
|
||||||
|
git_ref: 559fd0f1
|
||||||
|
diff_cmd: git diff HEAD~1..HEAD
|
||||||
|
---
|
||||||
|
|
||||||
|
Continue preparing PR #481 after pushing the macOS debugger skip fix.
|
||||||
|
Follow the replacement CI run and address any remaining PR-specific
|
||||||
|
failure.
|
||||||
|
|
||||||
|
> `git diff HEAD~1..HEAD -- tractor/ipc/_transport.py`
|
||||||
|
|
||||||
|
> `git diff HEAD~1..HEAD -- tests/ipc/test_each_tpt.py`
|
||||||
|
|
||||||
|
macOS UDS failed `test_reqresp_ontopof_streaming` when its two-second
|
||||||
|
`move_on_after()` scope cancelled during `stream.send('ping')`. Commit
|
||||||
|
`88a23449` shields framed `send_all()` and checks pending cancellation only
|
||||||
|
after a successful write. Concurrent transport teardown instead closed the
|
||||||
|
socket, causing `ClosedResourceError` to escape as `TransportClosed` before
|
||||||
|
the pending cancellation could be delivered.
|
||||||
|
|
||||||
|
Preserve structured cancellation precedence on the shielded send's
|
||||||
|
transport-error path, and add a deterministic regression that cancels the
|
||||||
|
sender before making the fake stream raise `ClosedResourceError`.
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
---
|
||||||
|
model: openai/gpt-5.6-sol
|
||||||
|
service: opencode
|
||||||
|
session: 7b9c97c4-fff7-4ac4-97fb-35720453308e
|
||||||
|
timestamp: 2026-08-20T15:02:50Z
|
||||||
|
git_ref: pformat_caller_frame_render_guard
|
||||||
|
scope: code
|
||||||
|
substantive: true
|
||||||
|
raw_file: 20260820T150250Z_9afda1c6_prompt_io.raw.md
|
||||||
|
---
|
||||||
|
|
||||||
|
## Prompt
|
||||||
|
|
||||||
|
Fix both newly exposed send-side `MsgTypeError` formatting failures
|
||||||
|
and pin them with an end-to-end regression in PR #503.
|
||||||
|
|
||||||
|
## Response summary
|
||||||
|
|
||||||
|
Corrected codec-spec formatting and default error-message assembly so
|
||||||
|
`_mk_send_mte()` returns a printable error instead of raising another
|
||||||
|
formatter exception.
|
||||||
|
|
||||||
|
## Files changed
|
||||||
|
|
||||||
|
- `tractor/msg/_codec.py` - pass the codec to its supported formatter.
|
||||||
|
- `tractor/_exceptions.py` - assemble the default message as `str`.
|
||||||
|
- `tests/devx/test_pformat.py` - render the complete default error.
|
||||||
|
|
||||||
|
## Human edits
|
||||||
|
|
||||||
|
The human selected both one-line fixes and the single end-to-end test
|
||||||
|
as coherent additions to PR #503, while leaving broader formatter
|
||||||
|
cleanup out of scope.
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
---
|
||||||
|
model: openai/gpt-5.6-sol
|
||||||
|
service: opencode
|
||||||
|
timestamp: 2026-08-20T15:02:50Z
|
||||||
|
git_ref: pformat_caller_frame_render_guard
|
||||||
|
diff_cmd: git diff HEAD~1..HEAD
|
||||||
|
---
|
||||||
|
|
||||||
|
## Prompt
|
||||||
|
|
||||||
|
After reviewing additional `tractor.devx.pformat` work suitable for
|
||||||
|
PR #503, the user approved fixing both send-side `MsgTypeError`
|
||||||
|
formatting failures and adding an end-to-end regression.
|
||||||
|
|
||||||
|
## Response
|
||||||
|
|
||||||
|
The generated code corrects the `MsgCodec.msg_spec_str` formatter
|
||||||
|
input, keeps `_mk_send_mte()`'s assembled default message a string,
|
||||||
|
and tests that the resulting `MsgTypeError` can be rendered:
|
||||||
|
|
||||||
|
> `git diff HEAD~1..HEAD -- tractor/msg/_codec.py tractor/_exceptions.py tests/devx/test_pformat.py`
|
||||||
|
|
||||||
|
These failures were hidden behind the original
|
||||||
|
`pformat_caller_frame()` keyword error addressed by the first two
|
||||||
|
commits on the branch.
|
||||||
|
|
@ -0,0 +1,65 @@
|
||||||
|
---
|
||||||
|
model: openai/gpt-5.6-sol
|
||||||
|
service: opencode
|
||||||
|
session: 76c5d31c-5a2f-4503-9b16-410ee7f4fab3
|
||||||
|
timestamp: 2026-08-21T02:35:37Z
|
||||||
|
git_ref: ae6f2ac3
|
||||||
|
scope: code
|
||||||
|
substantive: true
|
||||||
|
raw_file: 20260821T023537Z_ae6f2ac3_prompt_io.raw.md
|
||||||
|
---
|
||||||
|
|
||||||
|
## Prompt
|
||||||
|
|
||||||
|
Simplify bounded actor cancellation by passing an explicit absolute
|
||||||
|
deadline from `Portal.cancel_actor()` through `_run_from_ns()`,
|
||||||
|
`Actor.start_remote_task()`, and `Channel.send()` into
|
||||||
|
`MsgpackTransport.send()`. Avoid a `ContextVar`, watcher tasks, shared
|
||||||
|
status, coalescing, and waiter state. After tracing the current
|
||||||
|
`Start -> StartAck -> CancelAck` transaction, rename the local result to
|
||||||
|
`cancel_ack_received`, document its exact semantics, and link a focused
|
||||||
|
follow-up for a dedicated `Cancel -> CancelAck` protocol.
|
||||||
|
|
||||||
|
## Response summary
|
||||||
|
|
||||||
|
Threaded one absolute Trio deadline through the existing private
|
||||||
|
actor-cancel RPC path. The transport retains complete-frame shielding
|
||||||
|
for ordinary sends, while a cancel-control send that overruns its
|
||||||
|
deadline force-closes the potentially corrupted stream before releasing
|
||||||
|
the send lock. The outer actor-cancel scope uses the same deadline for
|
||||||
|
ack waiting and redelivers pending caller cancellation afterward.
|
||||||
|
|
||||||
|
Renamed the completion flag to `cancel_ack_received` and documented that
|
||||||
|
the current private call consumes `StartAck`, then receives a real
|
||||||
|
`CancelAck` after `Actor.cancel()` completes; this does not establish
|
||||||
|
that the OS process exited. Added a source TODO linking issue #506 for
|
||||||
|
the future first-class `Cancel -> CancelAck` transaction.
|
||||||
|
|
||||||
|
Focused transport and actor-cancel verification passed all four tests.
|
||||||
|
|
||||||
|
## Files changed
|
||||||
|
|
||||||
|
- `tractor/runtime/_portal.py` - own the absolute deadline, accurately
|
||||||
|
record ack receipt, and link the dedicated cancellation protocol.
|
||||||
|
- `tractor/runtime/_runtime.py` - forward the optional deadline for the
|
||||||
|
exact private `Start` publication.
|
||||||
|
- `tractor/ipc/_chan.py` - pass the operation-specific deadline to the
|
||||||
|
transport without changing ordinary sends.
|
||||||
|
- `tractor/ipc/_transport.py` - bound the shielded frame publication and
|
||||||
|
close a partial-frame stream before unlocking it.
|
||||||
|
- `tests/ipc/test_each_tpt.py` - cover deadline expiry after a partial
|
||||||
|
frame prefix reaches the stream.
|
||||||
|
- `tests/test_to_actor.py` - prove actor-cancel publication and ack
|
||||||
|
waiting share one absolute timeout budget.
|
||||||
|
|
||||||
|
## Human edits
|
||||||
|
|
||||||
|
The human rejected the initial watcher-task, shared `_SendStatus`, cancel
|
||||||
|
coalescing, and per-waiter design as unnecessary complexity. They also
|
||||||
|
rejected `ContextVar` propagation in favor of explicit functional
|
||||||
|
threading, selected a single absolute deadline for publication and ack
|
||||||
|
waiting, and required item 2 to remain separate from the item-3 child
|
||||||
|
reaping work. After reviewing the result, they requested the precise
|
||||||
|
`cancel_ack_received` name, a detailed protocol-trace comment, a focused
|
||||||
|
follow-up issue, and a linked source TODO. No direct source-line edits
|
||||||
|
were made by the human.
|
||||||
|
|
@ -0,0 +1,41 @@
|
||||||
|
---
|
||||||
|
model: openai/gpt-5.6-sol
|
||||||
|
service: opencode
|
||||||
|
timestamp: 2026-08-21T02:35:37Z
|
||||||
|
git_ref: ae6f2ac3
|
||||||
|
diff_cmd: git diff HEAD~1..HEAD
|
||||||
|
---
|
||||||
|
|
||||||
|
Replace the actor-cancel timeout watcher/status experiment with one
|
||||||
|
explicit absolute deadline threaded through the existing private call
|
||||||
|
path. Do not use a `ContextVar`, shared result state, waiter
|
||||||
|
coalescing, or polling tasks.
|
||||||
|
|
||||||
|
> `git diff HEAD~1..HEAD -- tractor/runtime/_portal.py`
|
||||||
|
|
||||||
|
`Portal.cancel_actor()` computes one absolute deadline and uses it for
|
||||||
|
both `Start` frame publication and the subsequent cancel-ack wait.
|
||||||
|
|
||||||
|
> `git diff HEAD~1..HEAD -- tractor/runtime/_runtime.py`
|
||||||
|
|
||||||
|
> `git diff HEAD~1..HEAD -- tractor/ipc/_chan.py`
|
||||||
|
|
||||||
|
The private RPC path forwards the operation-specific deadline. Lower
|
||||||
|
layers preserve the ordinary infinite-deadline call shape.
|
||||||
|
|
||||||
|
> `git diff HEAD~1..HEAD -- tractor/ipc/_transport.py`
|
||||||
|
|
||||||
|
`MsgpackTransport.send()` applies the deadline inside its complete-frame
|
||||||
|
shield. If the deadline expires after partial publication, it closes
|
||||||
|
the unusable stream before releasing the send lock.
|
||||||
|
|
||||||
|
> `git diff HEAD~1..HEAD -- tests/ipc/test_each_tpt.py`
|
||||||
|
|
||||||
|
> `git diff HEAD~1..HEAD -- tests/test_to_actor.py`
|
||||||
|
|
||||||
|
Focused regressions prove a partial-frame timeout closes the stream and
|
||||||
|
that actor-cancel publication and acknowledgement share one budget.
|
||||||
|
|
||||||
|
The implementation removes the earlier `_SendStatus`, watcher task,
|
||||||
|
coalescing, shared cancel result, and per-waiter state. Four focused
|
||||||
|
transport and actor-cancel tests pass.
|
||||||
|
|
@ -0,0 +1,57 @@
|
||||||
|
---
|
||||||
|
model: openai/gpt-5.6-sol
|
||||||
|
service: opencode
|
||||||
|
session: 76c5d31c-5a2f-4503-9b16-410ee7f4fab3
|
||||||
|
timestamp: 2026-08-21T04:08:03Z
|
||||||
|
git_ref: 3c1bbe73
|
||||||
|
scope: code
|
||||||
|
substantive: true
|
||||||
|
raw_file: 20260821T040803Z_3c1bbe73_prompt_io.raw.md
|
||||||
|
---
|
||||||
|
|
||||||
|
## Prompt
|
||||||
|
|
||||||
|
Continue PR #481 review cleanup one item at a time. Compare the
|
||||||
|
remaining child-registration/reaping experiment against PR #484,
|
||||||
|
identify the next valid behavior, and generate an exact commit plan for
|
||||||
|
that boundary without committing automatically.
|
||||||
|
|
||||||
|
## Response summary
|
||||||
|
|
||||||
|
Separated late-child admission from the broader bounded-reap rewrite.
|
||||||
|
Added one synchronous `ActorNursery._register_child()` publication step
|
||||||
|
that stores the child and its reap events before sampling nursery
|
||||||
|
cancellation. This closes both scheduler orderings around
|
||||||
|
`ActorNursery.cancel()`'s child snapshot.
|
||||||
|
|
||||||
|
The MP backend registers immediately before synchronous process startup
|
||||||
|
and refuses to start when cancellation already owns the child. The Trio
|
||||||
|
backend registers immediately after `open_process()` and kills that
|
||||||
|
already-created process when registration observes cancellation. An
|
||||||
|
early `start_actor()` guard rejects calls begun after cancellation is
|
||||||
|
already visible.
|
||||||
|
|
||||||
|
Deterministic tests cover the nursery registration ordering and the MP
|
||||||
|
no-start invariant. Comparison with PR #484 confirmed that its retained
|
||||||
|
generic nursery/backends do not close this race.
|
||||||
|
|
||||||
|
## Files changed
|
||||||
|
|
||||||
|
- `tractor/runtime/_supervise.py` - atomically publish child ownership
|
||||||
|
and reject actor starts after nursery cancellation.
|
||||||
|
- `tractor/spawn/_mp.py` - register before synchronous process startup
|
||||||
|
and abort a cancellation-owned child.
|
||||||
|
- `tractor/spawn/_trio.py` - register immediately after process creation,
|
||||||
|
kill a cancellation-owned child, and remove its stale unused import.
|
||||||
|
- `tests/test_to_actor.py` - cover late registration and MP startup
|
||||||
|
suppression.
|
||||||
|
|
||||||
|
## Human edits
|
||||||
|
|
||||||
|
The human required review extras to be handled one item and one
|
||||||
|
behavioral commit at a time, with each item compared against PR #484
|
||||||
|
before acceptance. That direction split this late-registration fix from
|
||||||
|
the original broad experiment's bounded post-ack reaping,
|
||||||
|
`ActorNursery.cancel()` hard-reap rewrite, and debugger/error behavior.
|
||||||
|
The human accepted the narrower late-registration boundary by requesting
|
||||||
|
its commit plan. No direct source-line edits were made by the human.
|
||||||
|
|
@ -0,0 +1,48 @@
|
||||||
|
---
|
||||||
|
model: openai/gpt-5.6-sol
|
||||||
|
service: opencode
|
||||||
|
timestamp: 2026-08-21T04:08:03Z
|
||||||
|
git_ref: 3c1bbe73
|
||||||
|
diff_cmd: git diff HEAD~1..HEAD
|
||||||
|
---
|
||||||
|
|
||||||
|
Compare the remaining child-registration and reaping experiment with
|
||||||
|
PR #484, then identify the next review item without changing code.
|
||||||
|
|
||||||
|
The next item is the late-child admission race. A spawn can pass
|
||||||
|
`ActorNursery.start_actor()`'s early cancellation check, then be absent
|
||||||
|
from `ActorNursery.cancel()`'s child snapshot and register afterward.
|
||||||
|
The existing reap-request latch releases its monitor but does not send
|
||||||
|
runtime cancellation, so the monitor can wait forever for a still-live
|
||||||
|
process.
|
||||||
|
|
||||||
|
> `git diff HEAD~1..HEAD -- tractor/runtime/_supervise.py`
|
||||||
|
|
||||||
|
`ActorNursery._register_child()` publishes the child, installs its reap
|
||||||
|
events, and samples `ActorNursery._cancel_called` without a checkpoint.
|
||||||
|
The two scheduler orderings are then complete: registration first puts
|
||||||
|
the child in the cancel snapshot, while cancellation first makes the
|
||||||
|
backend abort the late registration.
|
||||||
|
|
||||||
|
> `git diff HEAD~1..HEAD -- tractor/spawn/_mp.py`
|
||||||
|
|
||||||
|
The multiprocessing backend registers immediately before `proc.start()`
|
||||||
|
and refuses to start a process already owned by nursery cancellation.
|
||||||
|
There is no Trio checkpoint between registration and process startup.
|
||||||
|
|
||||||
|
> `git diff HEAD~1..HEAD -- tractor/spawn/_trio.py`
|
||||||
|
|
||||||
|
The Trio backend registers immediately after `open_process()` and kills
|
||||||
|
the newly opened process if cancellation won the registration race. Its
|
||||||
|
stale unused `get_runtime_vars` import is removed so the touched module
|
||||||
|
remains lint-clean.
|
||||||
|
|
||||||
|
> `git diff HEAD~1..HEAD -- tests/test_to_actor.py`
|
||||||
|
|
||||||
|
Deterministic regressions prove late registration observes cancellation
|
||||||
|
and that the MP backend never starts a process after cancellation owns
|
||||||
|
its registration.
|
||||||
|
|
||||||
|
PR #484 retains the affected generic nursery and spawn-backend paths and
|
||||||
|
does not close this race. Keep this fix in PR #481 as its own commit;
|
||||||
|
review bounded post-`CancelAck` reaping separately.
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
---
|
||||||
|
model: gpt-5.6-sol
|
||||||
|
service: opencode
|
||||||
|
session: tractor-addr-unpacking
|
||||||
|
timestamp: 2026-08-21T05:20:52Z
|
||||||
|
git_ref: 3690e43a
|
||||||
|
scope: config
|
||||||
|
substantive: true
|
||||||
|
raw_file: 20260821T052052Z_3690e43a_prompt_io.raw.md
|
||||||
|
---
|
||||||
|
|
||||||
|
## Prompt
|
||||||
|
|
||||||
|
The human asked for a main-first patch using an off-the-shelf pytest
|
||||||
|
plugin to cope with tractor's changing macOS CI flakes without mixing
|
||||||
|
that mitigation into PR #505.
|
||||||
|
|
||||||
|
## Response summary
|
||||||
|
|
||||||
|
Added `pytest-rerunfailures` to tractor's testing dependencies and
|
||||||
|
configured the GitHub Actions matrix to retry failures only on macOS.
|
||||||
|
Linux and Windows remain strict first-attempt runs, while persistent
|
||||||
|
macOS failures still fail after two visible reruns.
|
||||||
|
|
||||||
|
## Files changed
|
||||||
|
|
||||||
|
- `.github/workflows/ci.yml` - macOS-only pytest rerun budget.
|
||||||
|
- `pyproject.toml` - testing plugin dependency and rationale.
|
||||||
|
- `uv.lock` - resolved `pytest-rerunfailures` package metadata.
|
||||||
|
|
||||||
|
## Human edits
|
||||||
|
|
||||||
|
The human selected a main-first mitigation after PR #505 failed two
|
||||||
|
different macOS tests on consecutive runs and required the change to
|
||||||
|
remain an incremental patch with its own commit plan. The agent
|
||||||
|
implemented and verified that direction; no direct manual source
|
||||||
|
edits were observed.
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
---
|
||||||
|
model: gpt-5.6-sol
|
||||||
|
service: opencode
|
||||||
|
timestamp: 2026-08-21T05:20:52Z
|
||||||
|
git_ref: 3690e43a
|
||||||
|
diff_cmd: git diff HEAD~1..HEAD
|
||||||
|
---
|
||||||
|
|
||||||
|
# Raw output - retry flaky macOS CI tests
|
||||||
|
|
||||||
|
The human requested an off-the-shelf pytest plugin patch suitable
|
||||||
|
for landing directly on tractor `main` after PR #505's macOS job
|
||||||
|
failed two different timing-sensitive tests on consecutive runs.
|
||||||
|
|
||||||
|
> `git diff HEAD~1..HEAD -- .github/workflows/ci.yml pyproject.toml uv.lock`
|
||||||
|
|
||||||
|
Added the pytest-dev-maintained `pytest-rerunfailures` plugin and
|
||||||
|
gave only the macOS matrix leg two reruns with a one-second delay.
|
||||||
|
Linux and Windows receive a zero retry budget; deterministic macOS
|
||||||
|
failures still fail after the final attempt and reruns remain visible
|
||||||
|
in pytest output.
|
||||||
|
|
||||||
|
The lockfile is current, actionlint passed, all 471 tests collected,
|
||||||
|
and the four tests covering both observed PR #505 failure areas
|
||||||
|
passed with the rerun plugin enabled.
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
---
|
||||||
|
model: gpt-5.6-sol
|
||||||
|
service: opencode
|
||||||
|
session: tractor-addr-unpacking
|
||||||
|
timestamp: 2026-08-21T23:32:04Z
|
||||||
|
git_ref: 5d92595f
|
||||||
|
scope: code
|
||||||
|
substantive: true
|
||||||
|
raw_file: 20260821T233204Z_5d92595f_prompt_io.raw.md
|
||||||
|
---
|
||||||
|
|
||||||
|
## Prompt
|
||||||
|
|
||||||
|
The human asked to proceed with WireGuard Layer B from PR #505's
|
||||||
|
head, using the established one-change-per-turn workflow and
|
||||||
|
finishing with a commit plan. This turn was limited to read-only
|
||||||
|
pyroute2 inspection; peer verification and bindspace provisioning
|
||||||
|
remain later changes.
|
||||||
|
|
||||||
|
During review, the human required the generated Python to follow the
|
||||||
|
deployed `/py-codestyle` rules and asked for the medium-term plan to
|
||||||
|
remove pyroute2's asyncio I/O runtime from the Trio read path.
|
||||||
|
|
||||||
|
## Response summary
|
||||||
|
|
||||||
|
Added Linux-only read helpers for WireGuard device and peer public
|
||||||
|
keys. Pyroute2's synchronous API is fully contained in a Trio worker
|
||||||
|
thread, supports named netns reads without creation side effects,
|
||||||
|
validates decoded keys and always closes the netlink client.
|
||||||
|
Follow-up edits added the required local annotations, boolean layout,
|
||||||
|
helper docstrings and 69-column source formatting.
|
||||||
|
|
||||||
|
## Files changed
|
||||||
|
|
||||||
|
- `pyproject.toml` - Linux-only `wg` optional dependency.
|
||||||
|
- `uv.lock` - resolved pyroute2 0.9.6 metadata.
|
||||||
|
- `tractor/discovery/_tunnel.py` - read-only WireGuard helpers.
|
||||||
|
- `tractor/discovery/__init__.py` - public helper exports.
|
||||||
|
- `tests/discovery/test_wg_inspection.py` - fake-backed netlink and
|
||||||
|
worker-thread regressions.
|
||||||
|
|
||||||
|
## Human edits
|
||||||
|
|
||||||
|
The human chose the exact #505 head as a stacked base, required
|
||||||
|
incremental changes ending in commit plans, and limited this turn to
|
||||||
|
read-only pyroute2 inspection. The human then identified that Ruff
|
||||||
|
success had not established `/py-codestyle` compliance and directed
|
||||||
|
the agent to correct the Python-specific annotation, documentation
|
||||||
|
and layout rules. No direct manual source edits were observed.
|
||||||
|
|
@ -0,0 +1,31 @@
|
||||||
|
---
|
||||||
|
model: gpt-5.6-sol
|
||||||
|
service: opencode
|
||||||
|
timestamp: 2026-08-21T23:32:04Z
|
||||||
|
git_ref: 5d92595f
|
||||||
|
diff_cmd: git diff HEAD~1..HEAD
|
||||||
|
---
|
||||||
|
|
||||||
|
# Raw output - read WireGuard state through pyroute2
|
||||||
|
|
||||||
|
The human authorized the first incremental WireGuard Layer B change
|
||||||
|
as a stacked branch from tractor PR #505, with one atomic change and
|
||||||
|
a commit plan at the end of the turn.
|
||||||
|
|
||||||
|
> `git diff HEAD~1..HEAD -- pyproject.toml uv.lock tractor/discovery/__init__.py tractor/discovery/_tunnel.py tests/discovery/test_wg_inspection.py`
|
||||||
|
|
||||||
|
Added a Linux-only `wg` extra using pyroute2 0.9.6, plus
|
||||||
|
asynchronous public helpers for reading one interface's public key
|
||||||
|
and peer keys. The complete synchronous open/read/parse/close
|
||||||
|
sequence runs in a Trio worker thread because pyroute2 owns a private
|
||||||
|
asyncio loop internally.
|
||||||
|
|
||||||
|
Named namespace reads pass `flags=0` to override pyroute2's `O_CREAT`
|
||||||
|
default, ensuring inspection cannot create a missing namespace. Fake
|
||||||
|
netlink messages cover multipart dumps, key validation, stable peer
|
||||||
|
deduplication, worker-thread execution, netns selection and cleanup
|
||||||
|
on success/error.
|
||||||
|
|
||||||
|
Ruff and lock checks passed. Focused tunnel/multiaddr coverage passed
|
||||||
|
47 tests; the complete discovery suite passed 88 tests with 2
|
||||||
|
xpasses.
|
||||||
|
|
@ -0,0 +1,46 @@
|
||||||
|
---
|
||||||
|
model: gpt-5.6-sol
|
||||||
|
service: opencode
|
||||||
|
session: tractor-addr-unpacking
|
||||||
|
timestamp: 2026-08-22T02:32:26Z
|
||||||
|
git_ref: 59a8ecfd
|
||||||
|
scope: code
|
||||||
|
substantive: true
|
||||||
|
raw_file: 20260822T023226Z_59a8ecfd_prompt_io.raw.md
|
||||||
|
---
|
||||||
|
|
||||||
|
## Prompt
|
||||||
|
|
||||||
|
After committing the read-only pyroute2 helpers and `wgman` design
|
||||||
|
update, the human authorized the next isolated Layer B change:
|
||||||
|
explicit `verify_wg_peer()` composition over WireGuard inspection.
|
||||||
|
|
||||||
|
## Response summary
|
||||||
|
|
||||||
|
Added and exported async `verify_wg_peer()` using one validated
|
||||||
|
WireGuard key snapshot. It recognizes local-interface and configured
|
||||||
|
peer identities without coupling kernel inspection to address
|
||||||
|
parsing. Updated the multihost examples to use the production helper
|
||||||
|
and removed their subprocess-based probe.
|
||||||
|
|
||||||
|
## Files changed
|
||||||
|
|
||||||
|
- `tractor/discovery/_tunnel.py` - shared async snapshot reader and
|
||||||
|
explicit verification helper.
|
||||||
|
- `tractor/discovery/__init__.py` - public verification export.
|
||||||
|
- `tests/discovery/test_wg_inspection.py` - local, peer, absent and
|
||||||
|
malformed-key verification coverage.
|
||||||
|
- `examples/multihost/wg_lan/host_a_srv.py` - async local-key check.
|
||||||
|
- `examples/multihost/wg_lan/host_b_client.py` - async peer-key check.
|
||||||
|
- `examples/multihost/wg_lan/wg_maddr.py` - removed obsolete
|
||||||
|
subprocess probe.
|
||||||
|
- `examples/multihost/wg_lan/README.md` - pyroute2 requirements and
|
||||||
|
verification workflow.
|
||||||
|
- `ai/tpt-backends/03_wg_tunnel_bindspace.md` - async API contract.
|
||||||
|
|
||||||
|
## Human edits
|
||||||
|
|
||||||
|
The human selected this pre-agreed verification layer as the next
|
||||||
|
atomic change after reviewing and committing the preceding read and
|
||||||
|
architecture changes. The agent implemented the source changes; no
|
||||||
|
direct manual edits or follow-up corrections were observed.
|
||||||
|
|
@ -0,0 +1,31 @@
|
||||||
|
---
|
||||||
|
model: gpt-5.6-sol
|
||||||
|
service: opencode
|
||||||
|
timestamp: 2026-08-22T02:32:26Z
|
||||||
|
git_ref: 59a8ecfd
|
||||||
|
diff_cmd: git diff HEAD~1..HEAD
|
||||||
|
---
|
||||||
|
|
||||||
|
# Raw output - verify declared WireGuard identities
|
||||||
|
|
||||||
|
The human authorized the next incremental Layer B change after
|
||||||
|
committing the read-only pyroute2 helpers and first-child `wgman`
|
||||||
|
design update.
|
||||||
|
|
||||||
|
> `git diff HEAD~1..HEAD -- tractor/discovery/_tunnel.py tractor/discovery/__init__.py tests/discovery/test_wg_inspection.py examples/multihost/wg_lan ai/tpt-backends/03_wg_tunnel_bindspace.md`
|
||||||
|
|
||||||
|
Added async `verify_wg_peer()` over one WireGuard key snapshot. It
|
||||||
|
validates the declared `WGTunnelSpec.peer_pubkey` before I/O, forwards
|
||||||
|
the spec's iface/netns, and accepts either the local interface key for
|
||||||
|
a source/listen declaration or a configured peer key for a
|
||||||
|
destination/dial declaration.
|
||||||
|
|
||||||
|
Refactored worker offload behind one shared async reader so
|
||||||
|
verification cannot compare two different netlink snapshots. Exported
|
||||||
|
the helper, added local/peer/absent/malformed-key coverage, and moved
|
||||||
|
the multihost examples from their local `wg show` subprocess probe to
|
||||||
|
the production API.
|
||||||
|
|
||||||
|
Ruff and lock checks passed. Focused WireGuard/tunnel/multiaddr
|
||||||
|
coverage passed 51 tests; the complete discovery suite passed 92
|
||||||
|
tests with 2 xpasses.
|
||||||
|
|
@ -0,0 +1,44 @@
|
||||||
|
---
|
||||||
|
model: gpt-5.6-sol
|
||||||
|
service: opencode
|
||||||
|
session: tractor-addr-unpacking
|
||||||
|
timestamp: 2026-08-22T03:25:20Z
|
||||||
|
git_ref: d35c802b
|
||||||
|
scope: code
|
||||||
|
substantive: true
|
||||||
|
raw_file: 20260822T032520Z_d35c802b_prompt_io.raw.md
|
||||||
|
---
|
||||||
|
|
||||||
|
## Prompt
|
||||||
|
|
||||||
|
The human reported the explicit WireGuard verification commit done,
|
||||||
|
asked the agent to check it, and authorized the next incremental
|
||||||
|
Layer C change.
|
||||||
|
|
||||||
|
## Response summary
|
||||||
|
|
||||||
|
Verified commit `d35c802b`, made plain transport namespace behavior
|
||||||
|
explicit, and retained each original listener declaration beside its
|
||||||
|
peeled transport address. Endpoint and server diagnostics can now
|
||||||
|
report a tunnel's namespace without violating the transport boundary.
|
||||||
|
|
||||||
|
## Files changed
|
||||||
|
|
||||||
|
- `tractor/ipc/_tcp.py` - explicit plain-address namespace property.
|
||||||
|
- `tractor/ipc/_uds.py` - explicit plain-address namespace property.
|
||||||
|
- `tractor/discovery/_tunnel.py` - direct overlay namespace delegation.
|
||||||
|
- `tractor/ipc/_server.py` - retained declaration and namespace
|
||||||
|
diagnostics.
|
||||||
|
- `tests/discovery/test_tunnelled_addr.py` - plain and tunnel namespace
|
||||||
|
behavior.
|
||||||
|
- `tests/ipc/test_server_tunnel_boundary.py` - declaration retention
|
||||||
|
and diagnostic coverage.
|
||||||
|
- `ai/tpt-backends/03_wg_tunnel_bindspace.md` - concrete endpoint
|
||||||
|
boundary contract.
|
||||||
|
|
||||||
|
## Human edits
|
||||||
|
|
||||||
|
The human selected continued incremental implementation after
|
||||||
|
reviewing and committing the preceding verification layer. The agent
|
||||||
|
implemented this dependency-ordered namespace slice; no direct manual
|
||||||
|
edits or follow-up corrections were observed.
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
---
|
||||||
|
model: gpt-5.6-sol
|
||||||
|
service: opencode
|
||||||
|
timestamp: 2026-08-22T03:25:20Z
|
||||||
|
git_ref: d35c802b
|
||||||
|
diff_cmd: git diff HEAD~1..HEAD
|
||||||
|
---
|
||||||
|
|
||||||
|
# Raw output - retain endpoint namespace declarations
|
||||||
|
|
||||||
|
The human reported the explicit WireGuard verification commit done,
|
||||||
|
asked for it to be checked, and authorized the next incremental
|
||||||
|
change.
|
||||||
|
|
||||||
|
> `git diff HEAD~1..HEAD -- tractor/ipc/_tcp.py tractor/ipc/_uds.py tractor/discovery/_tunnel.py tractor/ipc/_server.py tests/discovery/test_tunnelled_addr.py tests/ipc/test_server_tunnel_boundary.py ai/tpt-backends/03_wg_tunnel_bindspace.md`
|
||||||
|
|
||||||
|
Confirmed commit `d35c802b` and a clean worktree, then implemented the
|
||||||
|
smallest dependency-ordered Layer C slice. Plain TCP and UDS addresses
|
||||||
|
now explicitly report no namespace, allowing `TunnelledAddress` to
|
||||||
|
delegate without an attribute fallback.
|
||||||
|
|
||||||
|
Added required `Endpoint.declared_addr` metadata beside the peeled,
|
||||||
|
resolved `Endpoint.addr`. Endpoint and server diagnostics expose the
|
||||||
|
declaration's namespace without passing a tunnel wrapper into
|
||||||
|
transport reflection. Updated the Layer C plan and tests for plain,
|
||||||
|
tunnelled, endpoint and server namespace behavior.
|
||||||
|
|
||||||
|
Ruff passed. Focused namespace tests passed 13 tests; combined
|
||||||
|
discovery and IPC coverage passed 101 tests with 2 xpasses.
|
||||||
|
|
@ -0,0 +1,64 @@
|
||||||
|
---
|
||||||
|
model: gpt-5.6-sol
|
||||||
|
service: opencode
|
||||||
|
session: tractor-addr-unpacking
|
||||||
|
timestamp: 2026-08-22T04:20:26Z
|
||||||
|
git_ref: 29141f0b
|
||||||
|
scope: code
|
||||||
|
substantive: true
|
||||||
|
raw_file: 20260822T042026Z_29141f0b_prompt_io.raw.md
|
||||||
|
---
|
||||||
|
|
||||||
|
## Prompt
|
||||||
|
|
||||||
|
After committing endpoint namespace visibility, the human authorized
|
||||||
|
continued Layer C implementation.
|
||||||
|
|
||||||
|
During staged review, the human requested Literal-derived validation,
|
||||||
|
ownership documentation, stable-inode clarification, explicit
|
||||||
|
non-serialization rationale and consolidated invalid-model tests.
|
||||||
|
|
||||||
|
## Response summary
|
||||||
|
|
||||||
|
Added the foundational bindspace model: serializable declarations and
|
||||||
|
stable identities are separated from a process-local live capability.
|
||||||
|
The handle validates names, ownership and FD identity. A global
|
||||||
|
`ProcessLocal` sentinel blocks default encoding while retaining
|
||||||
|
msgspec struct behavior.
|
||||||
|
Review fixes require a positive inode for every realized netns,
|
||||||
|
derive runtime choices from the Literal aliases and clarify that an FD
|
||||||
|
integer is not transferable capability authority.
|
||||||
|
The human then clarified that msgspec structs are useful generic
|
||||||
|
storage independently of serialization policy, so the live handle now
|
||||||
|
uses a struct while remaining process-local by contract.
|
||||||
|
The human first selected an opaque FD wrapper, then recognized that
|
||||||
|
future process-local handles need the same guard and directed a global
|
||||||
|
marker under `tractor.msg` instead.
|
||||||
|
|
||||||
|
## Files changed
|
||||||
|
|
||||||
|
- `tractor/discovery/_bindspace.py` - declaration, identity and live
|
||||||
|
capability models.
|
||||||
|
- `tractor/discovery/__init__.py` - public bindspace exports.
|
||||||
|
- `tractor/msg/_local.py` - reusable process-local struct marker.
|
||||||
|
- `tractor/msg/__init__.py` - public `ProcessLocal` export.
|
||||||
|
- `tests/discovery/test_bindspace.py` - serialization and capability
|
||||||
|
invariant coverage.
|
||||||
|
- `tests/msg/test_process_local.py` - direct and nested wire rejection.
|
||||||
|
- `ai/tpt-backends/03_wg_tunnel_bindspace.md` - concrete initial model
|
||||||
|
contract.
|
||||||
|
|
||||||
|
## Human edits
|
||||||
|
|
||||||
|
The human selected the foundational Layer C capability model, then
|
||||||
|
reviewed the staged patch and directed all eight follow-up changes:
|
||||||
|
remove distracting ownership variation, consolidate invalid-model
|
||||||
|
tests, derive containment checks from Literal aliases, require a
|
||||||
|
stable inode and explain why the live handle is not a msgspec struct.
|
||||||
|
The human subsequently corrected that last design choice: struct use
|
||||||
|
does not imply IPC authorization, so `BindspaceHandle` should retain
|
||||||
|
msgspec's generic storage benefits. The agent applied those
|
||||||
|
human-directed edits. The human first chose an opaque process-local FD
|
||||||
|
wrapper, then refined that decision to a reusable global marker under
|
||||||
|
`tractor.msg` for future local handles. No direct manual source edits
|
||||||
|
were observed.
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
---
|
||||||
|
model: gpt-5.6-sol
|
||||||
|
service: opencode
|
||||||
|
timestamp: 2026-08-22T04:20:26Z
|
||||||
|
git_ref: 29141f0b
|
||||||
|
diff_cmd: git diff HEAD~1..HEAD
|
||||||
|
---
|
||||||
|
|
||||||
|
# Raw output - model bindspace capabilities
|
||||||
|
|
||||||
|
The human authorized the next incremental Layer C change after
|
||||||
|
committing endpoint namespace visibility.
|
||||||
|
|
||||||
|
> `git diff HEAD~1..HEAD -- tractor/discovery/_bindspace.py tractor/discovery/__init__.py tests/discovery/test_bindspace.py ai/tpt-backends/03_wg_tunnel_bindspace.md`
|
||||||
|
|
||||||
|
Added frozen, serializable `BindspaceSpec` and
|
||||||
|
`BindspaceIdentity` models for the initial Linux netns kind. Added a
|
||||||
|
plain slotted `BindspaceHandle` which carries ownership and an optional
|
||||||
|
namespace FD without being serializable by default msgspec encoding.
|
||||||
|
|
||||||
|
The live handle validates declaration/identity kind and name, ownership
|
||||||
|
values, non-negative FDs and FD/inode correspondence. Serializable
|
||||||
|
models reject empty identities and kinds without implementations.
|
||||||
|
|
||||||
|
Added round-trip, non-serialization, identity-mismatch, empty-identity
|
||||||
|
and unsupported-kind tests. Ruff passed; discovery plus
|
||||||
|
message-serialization coverage passed 125 tests with 2 xpasses.
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
---
|
||||||
|
model: openai/gpt-5.6-sol
|
||||||
|
service: opencode
|
||||||
|
session: d9d7df2c-7044-463f-8768-ec024718eac9
|
||||||
|
timestamp: 2026-08-24T22:20:33Z
|
||||||
|
git_ref: ce38cb6f
|
||||||
|
scope: code
|
||||||
|
substantive: true
|
||||||
|
raw_file: 20260824T222033Z_ce38cb6f_prompt_io.raw.md
|
||||||
|
---
|
||||||
|
|
||||||
|
## Prompt
|
||||||
|
|
||||||
|
Continue the PR #481 review after triage. The human explicitly accepted
|
||||||
|
the proposed merge-blocking `Context.cancel()` deadline update with
|
||||||
|
"keep" and required the work to remain limited to that review item.
|
||||||
|
|
||||||
|
## Response summary
|
||||||
|
|
||||||
|
Update `Context.cancel()` so one absolute deadline bounds both shielded
|
||||||
|
cancel-request publication and acknowledgement waiting. Add a focused
|
||||||
|
mocked-clock regression for the blocked-publication failure mode and run
|
||||||
|
the narrow cancellation tests.
|
||||||
|
|
||||||
|
## Files changed
|
||||||
|
|
||||||
|
- `tractor/_context.py` - forward the cancel transaction's absolute
|
||||||
|
deadline to frame publication.
|
||||||
|
- `tests/test_to_actor.py` - prove blocked context-cancel publication is
|
||||||
|
bounded by the shared deadline.
|
||||||
|
|
||||||
|
## Human edits
|
||||||
|
|
||||||
|
The human retained ownership of review scope and explicitly selected
|
||||||
|
"keep" for this item after receiving keep/defer/drop options. The human
|
||||||
|
required no unrelated cancellation changes and did not directly edit
|
||||||
|
source lines.
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
---
|
||||||
|
model: openai/gpt-5.6-sol
|
||||||
|
service: opencode
|
||||||
|
timestamp: 2026-08-24T22:20:33Z
|
||||||
|
git_ref: ce38cb6f
|
||||||
|
diff_cmd: git diff HEAD~1..HEAD
|
||||||
|
---
|
||||||
|
|
||||||
|
Implement the approved PR #481 review update for `Context.cancel()`.
|
||||||
|
Use one absolute deadline for both cancellation-request frame
|
||||||
|
publication and acknowledgement waiting, without broadening the change
|
||||||
|
to unrelated cancellation behavior.
|
||||||
|
|
||||||
|
> `git diff HEAD~1..HEAD -- tractor/_context.py`
|
||||||
|
|
||||||
|
`Context.cancel()` computes one absolute cancellation deadline, uses it
|
||||||
|
for the outer bounded wait, and forwards it through
|
||||||
|
`Portal._run_from_ns()` to shielded frame publication.
|
||||||
|
|
||||||
|
> `git diff HEAD~1..HEAD -- tests/test_to_actor.py`
|
||||||
|
|
||||||
|
A deterministic mocked-clock regression arranges a shielded blocked
|
||||||
|
publication and proves that `Context.cancel()` forwards the same deadline
|
||||||
|
which bounds the complete cancel transaction.
|
||||||
|
|
||||||
|
Run the focused cancellation deadline regressions after the edit.
|
||||||
|
|
@ -0,0 +1,35 @@
|
||||||
|
---
|
||||||
|
model: openai/gpt-5.6-sol
|
||||||
|
service: opencode
|
||||||
|
session: d9d7df2c-7044-463f-8768-ec024718eac9
|
||||||
|
timestamp: 2026-08-24T22:36:14Z
|
||||||
|
git_ref: 88d538e3
|
||||||
|
scope: code
|
||||||
|
substantive: true
|
||||||
|
raw_file: 20260824T223614Z_88d538e3_prompt_io.raw.md
|
||||||
|
---
|
||||||
|
|
||||||
|
## Prompt
|
||||||
|
|
||||||
|
Continue PR #481 review remediation after committing the shared
|
||||||
|
`Context.cancel()` deadline fix. The human accepted the proposed
|
||||||
|
child-reap bookkeeping invariant, asking only that the first fix receive
|
||||||
|
its own commit plan and commit before this update began.
|
||||||
|
|
||||||
|
## Response summary
|
||||||
|
|
||||||
|
Check that `ActorNursery` removes its paired reap-coordination entries
|
||||||
|
together while preserving valid pre-registration and immediate-cancel
|
||||||
|
paths. Extend the existing real-runtime reap tests to prove all three
|
||||||
|
child bookkeeping mappings are empty before `to_actor.run()` returns.
|
||||||
|
|
||||||
|
## Files changed
|
||||||
|
|
||||||
|
- `tractor/runtime/_supervise.py` - assert paired reap-map cleanup.
|
||||||
|
- `tests/test_to_actor.py` - verify graceful and hard-reap bookkeeping.
|
||||||
|
|
||||||
|
## Human edits
|
||||||
|
|
||||||
|
The human explicitly accepted this invariant update but directed the
|
||||||
|
preceding cancellation fix to be planned and committed as a separate
|
||||||
|
boundary first. No direct source-line edits were made by the human.
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
---
|
||||||
|
model: openai/gpt-5.6-sol
|
||||||
|
service: opencode
|
||||||
|
timestamp: 2026-08-24T22:36:14Z
|
||||||
|
git_ref: 88d538e3
|
||||||
|
diff_cmd: git diff HEAD~1..HEAD
|
||||||
|
---
|
||||||
|
|
||||||
|
Implement the approved PR #481 child-reap bookkeeping update after the
|
||||||
|
preceding `Context.cancel()` fix was committed separately.
|
||||||
|
|
||||||
|
> `git diff HEAD~1..HEAD -- tractor/runtime/_supervise.py`
|
||||||
|
|
||||||
|
`ActorNursery._mark_child_reaped()` captures both reap-coordination
|
||||||
|
entries and asserts that they are either both present or both absent.
|
||||||
|
It intentionally does not require the reap-request event to be set,
|
||||||
|
because backend cancellation can reap immediately after registration.
|
||||||
|
|
||||||
|
> `git diff HEAD~1..HEAD -- tests/test_to_actor.py`
|
||||||
|
|
||||||
|
Existing real-runtime graceful and hard-reap tests verify that
|
||||||
|
`ActorNursery._children`, `ActorNursery._child_reap_requests`, and
|
||||||
|
`ActorNursery._child_reaped` are all empty before the one-shot call
|
||||||
|
returns.
|
||||||
|
|
||||||
|
Run focused bookkeeping and real-runtime reap tests after the edit.
|
||||||
|
|
@ -0,0 +1,39 @@
|
||||||
|
---
|
||||||
|
model: openai/gpt-5.6-sol
|
||||||
|
service: opencode
|
||||||
|
session: d9d7df2c-7044-463f-8768-ec024718eac9
|
||||||
|
timestamp: 2026-08-24T22:53:56Z
|
||||||
|
git_ref: 2f86dd1a
|
||||||
|
scope: code
|
||||||
|
substantive: true
|
||||||
|
raw_file: 20260824T225356Z_2f86dd1a_prompt_io.raw.md
|
||||||
|
---
|
||||||
|
|
||||||
|
## Prompt
|
||||||
|
|
||||||
|
Continue PR #481 review remediation after committing the paired
|
||||||
|
`ActorNursery` reap-state invariant. The human selected "keep" for the
|
||||||
|
reviewer's request to factor a duplicated debugger predicate in
|
||||||
|
`_try_cancel_then_kill()`.
|
||||||
|
|
||||||
|
## Response summary
|
||||||
|
|
||||||
|
Factor the child/tree debugger predicate into a local sampler used both
|
||||||
|
before and after the cancel-RPC checkpoint. Preserve dynamic debugger
|
||||||
|
lock re-evaluation and its distinction from root-wide debug mode.
|
||||||
|
|
||||||
|
## Files changed
|
||||||
|
|
||||||
|
- `tractor/runtime/_supervise.py` - factor the duplicated debugger
|
||||||
|
predicate without changing cancellation behavior.
|
||||||
|
|
||||||
|
## Human edits
|
||||||
|
|
||||||
|
The human explicitly selected "keep" after receiving keep/defer/drop
|
||||||
|
options for this isolated review item. During commit-plan review, the
|
||||||
|
agent found that a single pre-checkpoint snapshot could become stale;
|
||||||
|
the human selected a local helper which re-evaluates the lock after the
|
||||||
|
cancel RPC. The human then considered moving the predicate into
|
||||||
|
`.devx.debug` and accepted keeping it local after confirming that no
|
||||||
|
existing helper shares its supervisor-owned semantics. No direct
|
||||||
|
source-line edits were made by the human.
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
---
|
||||||
|
model: openai/gpt-5.6-sol
|
||||||
|
service: opencode
|
||||||
|
timestamp: 2026-08-24T22:53:56Z
|
||||||
|
git_ref: 2f86dd1a
|
||||||
|
diff_cmd: git diff HEAD~1..HEAD
|
||||||
|
---
|
||||||
|
|
||||||
|
Implement the approved PR #481 review refactor in
|
||||||
|
`_try_cancel_then_kill()` without changing debugger behavior.
|
||||||
|
|
||||||
|
> `git diff HEAD~1..HEAD -- tractor/runtime/_supervise.py`
|
||||||
|
|
||||||
|
Compute the child/tree debugger predicate once, reuse it in the broader
|
||||||
|
hard-kill protection predicate, and pass it directly to
|
||||||
|
`debug.maybe_wait_for_debugger()`.
|
||||||
|
|
||||||
|
Run focused debugger/cancellation coverage and lint after the edit.
|
||||||
|
|
@ -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.
|
||||||
|
|
@ -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.
|
||||||
|
|
@ -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.
|
||||||
|
|
@ -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.
|
||||||
|
|
@ -0,0 +1,32 @@
|
||||||
|
---
|
||||||
|
model: openai/gpt-5.6-sol
|
||||||
|
service: opencode
|
||||||
|
session: d9d7df2c-7044-463f-8768-ec024718eac9
|
||||||
|
timestamp: 2026-08-25T02:13:19Z
|
||||||
|
git_ref: ce430fca
|
||||||
|
scope: code
|
||||||
|
substantive: true
|
||||||
|
raw_file: 20260825T021319Z_ce430fca_prompt_io.raw.md
|
||||||
|
---
|
||||||
|
|
||||||
|
## Prompt
|
||||||
|
|
||||||
|
Continue PR #481 review remediation after committing provisional child
|
||||||
|
registration clarifications. The human selected "keep" for inlining the
|
||||||
|
guarded `functools.Placeholder` lookup with a walrus assignment.
|
||||||
|
|
||||||
|
## Response summary
|
||||||
|
|
||||||
|
Remove the standalone placeholder assignment and bind the optional
|
||||||
|
Python 3.14 sentinel directly in the existing conditional while
|
||||||
|
preserving compatibility behavior.
|
||||||
|
|
||||||
|
## Files changed
|
||||||
|
|
||||||
|
- `tractor/to_actor/_api.py` - inline placeholder feature detection.
|
||||||
|
|
||||||
|
## Human edits
|
||||||
|
|
||||||
|
The human explicitly selected "keep" after receiving keep/defer/drop
|
||||||
|
options for this isolated cleanup. No direct source-line edits were made
|
||||||
|
by the human.
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
---
|
||||||
|
model: openai/gpt-5.6-sol
|
||||||
|
service: opencode
|
||||||
|
timestamp: 2026-08-25T02:13:19Z
|
||||||
|
git_ref: ce430fca
|
||||||
|
diff_cmd: git diff HEAD~1..HEAD
|
||||||
|
---
|
||||||
|
|
||||||
|
Implement the approved PR #481 review cleanup for Python 3.14 partial
|
||||||
|
placeholder detection.
|
||||||
|
|
||||||
|
> `git diff HEAD~1..HEAD -- tractor/to_actor/_api.py`
|
||||||
|
|
||||||
|
Inline the guarded `functools.Placeholder` lookup into the existing
|
||||||
|
condition with a walrus assignment, preserving fallback behavior when
|
||||||
|
the attribute is unavailable.
|
||||||
|
|
||||||
|
Run partial/placeholder normalization tests and the full `to_actor`
|
||||||
|
suite.
|
||||||
|
|
@ -0,0 +1,48 @@
|
||||||
|
---
|
||||||
|
model: gpt-5.6-sol
|
||||||
|
service: opencode
|
||||||
|
session: tractor-addr-unpacking
|
||||||
|
timestamp: 2026-08-25T04:55:57Z
|
||||||
|
git_ref: fdccfd7e
|
||||||
|
scope: code
|
||||||
|
substantive: true
|
||||||
|
raw_file: 20260825T045557Z_fdccfd7e_prompt_io.raw.md
|
||||||
|
---
|
||||||
|
|
||||||
|
## Prompt
|
||||||
|
|
||||||
|
After committing the foundational bindspace capability model, the
|
||||||
|
human authorized the next incremental Layer C change.
|
||||||
|
|
||||||
|
## Response summary
|
||||||
|
|
||||||
|
Added a borrow-only existing-netns context manager which pins stable
|
||||||
|
identity through an open FD and always releases that local descriptor.
|
||||||
|
The API does not create, enter or remove namespaces and rejects named
|
||||||
|
path traversal.
|
||||||
|
Staged review renamed the context to `attach_netns()`, introduced the
|
||||||
|
readable `CURRENT_NETNS` alias and made key validation errors identify
|
||||||
|
their exact owning field.
|
||||||
|
Follow-up review made the empty-key error spell out that `None` is
|
||||||
|
written as `CURRENT_NETNS` for `BindspaceSpec.key` declarations.
|
||||||
|
|
||||||
|
## Files changed
|
||||||
|
|
||||||
|
- `tractor/discovery/_bindspace.py` - existing-netns lifecycle and key
|
||||||
|
validation.
|
||||||
|
- `tractor/discovery/__init__.py` - public lifecycle export.
|
||||||
|
- `tests/discovery/test_bindspace.py` - current, named, missing and
|
||||||
|
traversal coverage.
|
||||||
|
- `ai/tpt-backends/03_wg_tunnel_bindspace.md` - borrow-only lifecycle
|
||||||
|
contract.
|
||||||
|
|
||||||
|
## Human edits
|
||||||
|
|
||||||
|
The human selected the previously deferred borrow-only netns lifecycle
|
||||||
|
as the next incremental Layer C change. The agent implemented the
|
||||||
|
source changes. During staged review, the human selected
|
||||||
|
`attach_netns()` terminology, requested explicit
|
||||||
|
`BindspaceSpec.key = CURRENT_NETNS` semantics and field-specific key
|
||||||
|
validation. Follow-up review requested the validation error itself
|
||||||
|
connect `None` to `CURRENT_NETNS`. The agent applied those
|
||||||
|
human-directed edits; no direct manual source edits were observed.
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
---
|
||||||
|
model: gpt-5.6-sol
|
||||||
|
service: opencode
|
||||||
|
timestamp: 2026-08-25T04:55:57Z
|
||||||
|
git_ref: fdccfd7e
|
||||||
|
diff_cmd: git diff HEAD~1..HEAD
|
||||||
|
---
|
||||||
|
|
||||||
|
# Raw output - borrow existing netns bindspaces
|
||||||
|
|
||||||
|
The human reported the bindspace capability model committed and
|
||||||
|
authorized the next incremental Layer C change.
|
||||||
|
|
||||||
|
> `git diff HEAD~1..HEAD -- tractor/discovery/_bindspace.py tractor/discovery/__init__.py tests/discovery/test_bindspace.py ai/tpt-backends/03_wg_tunnel_bindspace.md`
|
||||||
|
|
||||||
|
Added async `open_existing_netns()` as a borrow-only context manager.
|
||||||
|
It opens the current process netns or a named entry under the standard
|
||||||
|
iproute2 run directory, derives stable identity from the opened FD,
|
||||||
|
and yields a borrowed process-local `BindspaceHandle`.
|
||||||
|
|
||||||
|
The context uses `O_CLOEXEC`, never creates, enters or removes a
|
||||||
|
namespace, and synchronously closes only its FD on exit. Netns keys
|
||||||
|
reject paths to keep named lookup beneath the run directory.
|
||||||
|
|
||||||
|
Added current, named, missing and traversal tests. Ruff and lock checks
|
||||||
|
passed; discovery plus message coverage passed 132 tests with 2
|
||||||
|
xpasses.
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
---
|
||||||
|
model: gpt-5.6-sol
|
||||||
|
service: opencode
|
||||||
|
session: tractor-addr-unpacking
|
||||||
|
timestamp: 2026-08-25T19:05:29Z
|
||||||
|
git_ref: e1007547
|
||||||
|
scope: code
|
||||||
|
substantive: true
|
||||||
|
raw_file: 20260825T190529Z_e1007547_prompt_io.raw.md
|
||||||
|
---
|
||||||
|
|
||||||
|
## Prompt
|
||||||
|
|
||||||
|
After committing the attach-only network namespace lifecycle, the
|
||||||
|
human authorized the next incremental Layer C change.
|
||||||
|
|
||||||
|
## Response summary
|
||||||
|
|
||||||
|
Added owned `open_netns()` creation and teardown through shielded
|
||||||
|
pyroute2 worker calls. The context pins identity through
|
||||||
|
`attach_netns()`, yields an owned handle, closes its FD before removal
|
||||||
|
and cleans up despite caller cancellation.
|
||||||
|
|
||||||
|
## Files changed
|
||||||
|
|
||||||
|
- `tractor/discovery/_bindspace.py` - owned netns lifecycle.
|
||||||
|
- `tractor/discovery/__init__.py` - public lifecycle export.
|
||||||
|
- `tests/discovery/test_bindspace.py` - ownership, ordering,
|
||||||
|
cancellation and name requirements.
|
||||||
|
- `ai/tpt-backends/03_wg_tunnel_bindspace.md` - owned lifecycle and
|
||||||
|
spawn-boundary contract.
|
||||||
|
|
||||||
|
## Human edits
|
||||||
|
|
||||||
|
The human selected owned network namespace creation as the next
|
||||||
|
incremental Layer C change. The agent implemented the source changes;
|
||||||
|
no direct manual edits or follow-up corrections were observed.
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
---
|
||||||
|
model: gpt-5.6-sol
|
||||||
|
service: opencode
|
||||||
|
timestamp: 2026-08-25T19:05:29Z
|
||||||
|
git_ref: e1007547
|
||||||
|
diff_cmd: git diff HEAD~1..HEAD
|
||||||
|
---
|
||||||
|
|
||||||
|
# Raw output - own created netns bindspaces
|
||||||
|
|
||||||
|
The human reported the attach-only netns lifecycle committed and
|
||||||
|
authorized the next incremental Layer C change.
|
||||||
|
|
||||||
|
> `git diff HEAD~1..HEAD -- tractor/discovery/_bindspace.py tractor/discovery/__init__.py tests/discovery/test_bindspace.py ai/tpt-backends/03_wg_tunnel_bindspace.md`
|
||||||
|
|
||||||
|
Added async `open_netns()` as the owned counterpart to
|
||||||
|
`attach_netns()`. It requires a named spec, creates through pyroute2 in
|
||||||
|
a shielded worker call, attaches the resulting namespace FD and yields
|
||||||
|
an owned process-local `BindspaceHandle`.
|
||||||
|
|
||||||
|
FD closure occurs before shielded namespace removal on normal,
|
||||||
|
exceptional and cancelled exits. The context never calls `setns()`;
|
||||||
|
namespace entry remains a spawn/bootstrap responsibility.
|
||||||
|
|
||||||
|
Privileged operations are faked in tests. Ruff and lock checks passed;
|
||||||
|
discovery plus message coverage passed 137 tests with 2 xpasses.
|
||||||
|
|
@ -0,0 +1,40 @@
|
||||||
|
---
|
||||||
|
model: gpt-5.6-sol
|
||||||
|
service: opencode
|
||||||
|
session: tractor-addr-unpacking
|
||||||
|
timestamp: 2026-08-25T19:18:45Z
|
||||||
|
git_ref: 5b2a064a
|
||||||
|
scope: code
|
||||||
|
substantive: true
|
||||||
|
raw_file: 20260825T191845Z_5b2a064a_prompt_io.raw.md
|
||||||
|
---
|
||||||
|
|
||||||
|
## Prompt
|
||||||
|
|
||||||
|
The human pointed out that the agent had produced no changes after the
|
||||||
|
previous commit and expected the next incremental implementation.
|
||||||
|
|
||||||
|
## Response summary
|
||||||
|
|
||||||
|
Corrected the sequencing mistake and added explicit serialized
|
||||||
|
bindspace lifecycle policy plus generic `open_bindspace()` dispatch.
|
||||||
|
Borrowed and owned netns contexts share a policy-neutral FD-pinning
|
||||||
|
primitive while validating their public lifecycle before side effects.
|
||||||
|
|
||||||
|
## Files changed
|
||||||
|
|
||||||
|
- `tractor/discovery/_bindspace.py` - lifecycle policy, ownership
|
||||||
|
invariants, shared pinning and dispatch.
|
||||||
|
- `tractor/discovery/__init__.py` - public policy and dispatcher exports.
|
||||||
|
- `tests/discovery/test_bindspace.py` - lifecycle serialization,
|
||||||
|
validation and both dispatcher branches.
|
||||||
|
- `ai/tpt-backends/03_wg_tunnel_bindspace.md` - explicit lifecycle
|
||||||
|
policy independent of transport role.
|
||||||
|
|
||||||
|
## Human edits
|
||||||
|
|
||||||
|
The human identified that the agent had accidentally repeated a
|
||||||
|
summary of already committed work instead of implementing the next
|
||||||
|
slice. That correction directly caused this lifecycle/dispatcher
|
||||||
|
change to be implemented. The agent wrote the source changes; no
|
||||||
|
direct manual edits were observed.
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
---
|
||||||
|
model: gpt-5.6-sol
|
||||||
|
service: opencode
|
||||||
|
timestamp: 2026-08-25T19:18:45Z
|
||||||
|
git_ref: 5b2a064a
|
||||||
|
diff_cmd: git diff HEAD~1..HEAD
|
||||||
|
---
|
||||||
|
|
||||||
|
# Raw output - dispatch explicit bindspace lifecycle
|
||||||
|
|
||||||
|
The human noticed the agent had re-reported already committed work
|
||||||
|
without changing the worktree. The agent confirmed the sequencing
|
||||||
|
error and proceeded to the actual next Layer C slice.
|
||||||
|
|
||||||
|
> `git diff HEAD~1..HEAD -- tractor/discovery/_bindspace.py tractor/discovery/__init__.py tests/discovery/test_bindspace.py ai/tpt-backends/03_wg_tunnel_bindspace.md`
|
||||||
|
|
||||||
|
Added serialized `BindspaceSpec.lifecycle` policy with explicit
|
||||||
|
`attach` and `open` choices. Added `open_bindspace()` dispatch without
|
||||||
|
inferring resource ownership from listen/dial role.
|
||||||
|
|
||||||
|
Refactored FD attachment into policy-neutral `_pin_netns()` so
|
||||||
|
borrowed and owned public lifecycles can share identity pinning while
|
||||||
|
enforcing their own policy before side effects. Handle construction
|
||||||
|
also verifies lifecycle and resulting ownership agree.
|
||||||
|
|
||||||
|
One composition error introduced during implementation was caught by
|
||||||
|
the focused test: owned creation initially called the newly guarded
|
||||||
|
public attach API. `_pin_netns()` fixed that layering error. Ruff and
|
||||||
|
lock checks passed; discovery plus message coverage passed 138 tests
|
||||||
|
with 2 xpasses.
|
||||||
|
|
@ -0,0 +1,42 @@
|
||||||
|
---
|
||||||
|
model: gpt-5.6-sol
|
||||||
|
service: opencode
|
||||||
|
session: tractor-addr-unpacking
|
||||||
|
timestamp: 2026-08-25T23:46:31Z
|
||||||
|
git_ref: b973e78c
|
||||||
|
scope: code
|
||||||
|
substantive: true
|
||||||
|
raw_file: 20260825T234631Z_b973e78c_prompt_io.raw.md
|
||||||
|
---
|
||||||
|
|
||||||
|
## Prompt
|
||||||
|
|
||||||
|
The human pointed out that the agent had again produced no worktree
|
||||||
|
changes after the latest commit and expected actual next-layer work.
|
||||||
|
|
||||||
|
## Response summary
|
||||||
|
|
||||||
|
Corrected the sequencing mistake and added a process-local WireGuard
|
||||||
|
interface configuration boundary. Secret/routing policy is separated
|
||||||
|
from serializable maddr-derived tunnel declarations, redacted from
|
||||||
|
repr and blocked from default wire encoding.
|
||||||
|
Staged review clarified that address/network parser calls validate
|
||||||
|
every declaration and do not select or consume a first address.
|
||||||
|
|
||||||
|
## Files changed
|
||||||
|
|
||||||
|
- `tractor/discovery/_tunnel.py` - local WG config and validation.
|
||||||
|
- `tractor/discovery/__init__.py` - public config export.
|
||||||
|
- `tests/discovery/test_wg_config.py` - secrecy and validation coverage.
|
||||||
|
- `ai/tpt-backends/03_wg_tunnel_bindspace.md` - secret/config boundary
|
||||||
|
for future interface provisioning.
|
||||||
|
|
||||||
|
## Human edits
|
||||||
|
|
||||||
|
The human identified the second sequencing failure where the agent
|
||||||
|
summarized an already committed slice instead of implementing the next
|
||||||
|
one. That correction directly caused this new WG configuration layer
|
||||||
|
to be implemented. During staged review, the human requested explicit
|
||||||
|
explanation of the address and allowed-route validation loops. The
|
||||||
|
agent applied that human-directed clarification; no direct manual edits
|
||||||
|
were observed.
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
---
|
||||||
|
model: gpt-5.6-sol
|
||||||
|
service: opencode
|
||||||
|
timestamp: 2026-08-25T23:46:31Z
|
||||||
|
git_ref: b973e78c
|
||||||
|
diff_cmd: git diff HEAD~1..HEAD
|
||||||
|
---
|
||||||
|
|
||||||
|
# Raw output - separate local WireGuard configuration
|
||||||
|
|
||||||
|
The human again noticed the agent had re-reported already committed
|
||||||
|
work without changing the worktree. The agent confirmed the sequencing
|
||||||
|
mistake and moved to the next unimplemented dependency.
|
||||||
|
|
||||||
|
> `git diff HEAD~1..HEAD -- tractor/discovery/_tunnel.py tractor/discovery/__init__.py tests/discovery/test_wg_config.py ai/tpt-backends/03_wg_tunnel_bindspace.md`
|
||||||
|
|
||||||
|
Added process-local `WGInterfaceConfig` for private/preshared keys,
|
||||||
|
local interface CIDRs, peer allowed CIDRs, listen port and persistent
|
||||||
|
keepalive. Secrets are redacted from repr and the global
|
||||||
|
`ProcessLocal` marker blocks default wire encoding.
|
||||||
|
|
||||||
|
Removed the unused serialized `WGTunnelSpec.maybe_allowed_ips`
|
||||||
|
placeholder so maddr-derived declarations retain only public identity,
|
||||||
|
endpoint and interface selection. Added validation for keys, CIDRs,
|
||||||
|
ports and keepalive before future kernel mutation.
|
||||||
|
|
||||||
|
Ruff and lock checks passed. Focused WG coverage passed 25 tests;
|
||||||
|
discovery plus message coverage passed 145 tests with 2 xpasses.
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
---
|
||||||
|
model: gpt-5.6-sol
|
||||||
|
service: opencode
|
||||||
|
session: tractor-addr-unpacking
|
||||||
|
timestamp: 2026-08-26T00:14:42Z
|
||||||
|
git_ref: dcdf4d82
|
||||||
|
scope: code
|
||||||
|
substantive: true
|
||||||
|
raw_file: 20260826T001442Z_dcdf4d82_prompt_io.raw.md
|
||||||
|
---
|
||||||
|
|
||||||
|
## Prompt
|
||||||
|
|
||||||
|
The human selected an explicit peer-list model rather than a
|
||||||
|
dial-only single-peer shortcut before `open_wg_iface()`.
|
||||||
|
|
||||||
|
## Response summary
|
||||||
|
|
||||||
|
Added process-local per-peer configuration and refactored interface
|
||||||
|
configuration to own a unique peer tuple. This supports multi-peer
|
||||||
|
listeners and dial targets without overloading role-dependent tunnel
|
||||||
|
maddr identity.
|
||||||
|
|
||||||
|
## Files changed
|
||||||
|
|
||||||
|
- `tractor/discovery/_tunnel.py` - peer model and interface peer list.
|
||||||
|
- `tractor/discovery/__init__.py` - public peer-config export.
|
||||||
|
- `tests/discovery/test_wg_config.py` - peer secrecy and validation.
|
||||||
|
- `ai/tpt-backends/03_wg_tunnel_bindspace.md` - explicit multi-peer
|
||||||
|
provisioning contract.
|
||||||
|
|
||||||
|
## Human edits
|
||||||
|
|
||||||
|
The human chose explicit peer lists over a dial-only implementation so
|
||||||
|
listeners can represent multiple client public keys and routing
|
||||||
|
policies. The agent implemented that human-selected design; no direct
|
||||||
|
manual source edits were observed.
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
---
|
||||||
|
model: gpt-5.6-sol
|
||||||
|
service: opencode
|
||||||
|
timestamp: 2026-08-26T00:14:42Z
|
||||||
|
git_ref: dcdf4d82
|
||||||
|
diff_cmd: git diff HEAD~1..HEAD
|
||||||
|
---
|
||||||
|
|
||||||
|
# Raw output - model explicit WireGuard peers
|
||||||
|
|
||||||
|
After committing local WG interface configuration, the human selected
|
||||||
|
an explicit peer-list model before interface provisioning.
|
||||||
|
|
||||||
|
> `git diff HEAD~1..HEAD -- tractor/discovery/_tunnel.py tractor/discovery/__init__.py tests/discovery/test_wg_config.py ai/tpt-backends/03_wg_tunnel_bindspace.md`
|
||||||
|
|
||||||
|
Added process-local `WGPeerConfig` with public key, allowed CIDRs,
|
||||||
|
optional endpoint, preshared key and keepalive. Refactored
|
||||||
|
`WGInterfaceConfig` to own a tuple of unique peers beside its private
|
||||||
|
key, local addresses and listen port.
|
||||||
|
|
||||||
|
Peer PSKs remain redacted and nested `ProcessLocal` sentinels prevent
|
||||||
|
default wire encoding. Validation covers keys, routes, endpoints,
|
||||||
|
ports, keepalive and duplicate peers.
|
||||||
|
|
||||||
|
Ruff and lock checks passed. Focused peer/config coverage passed 11
|
||||||
|
tests. A broad unrelated registrar cancellation case timed out after
|
||||||
|
86 passes and 2 xpasses; its isolated rerun passed.
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue