post-#472 docs follow ups - #512
Conversation
The "Toward capability-based msging" section only pointed at the `#196`/`#36` epics. Fold in the concrete recent state, - `#365` as the most recent step: driving the whole `pld_spec` off plain type-annotations (e.g. annotating a context's `open_stream()` with `msgspec.Struct` subtypes) rather than explicit `pld_spec=` kwargs. - clarify that the decorator-level `@tractor.context(pld_spec=...)` is already the higher-level path (vs the lower-level `tractor.msg._ops.limit_plds()` escape hatch), pointing at `tests/msg/test_pldrx_limiting.py` + `test_ext_types_msgspec.py` which exercise both. - `#376` (from @guilledk, `auto_codecs` branch) as the drafted public factory API for the `enc_hook`/`dec_hook` pair (today only reachable via `tractor.msg._ops`). Addresses the caps-based-msging bullet in #472. (this patch was generated in some part by [`claude-code`][claude-code-gh]) [claude-code-gh]: https://github.com/anthropics/claude-code
Add a runnable `examples/dedicated_registrar.py` + a "A dedicated registrar" subsection in `guide/discovery.rst` demoing the registrar decoupled from any app tree's root: boot a bare `tractor.run_daemon([], registry_addrs=[...])` as its own process (a root actor that does nothing but hold the registry), point the app tree at the same `registry_addrs`, and discover a service *through* that external registrar. This is the buildable-today form of the #472 "Registrar-as-subsystem (not the root actor)" bullet. Two constraints are called out inline as follow-ups: `enable_transports` is single-proto per runtime (no multi-backend registrar yet), and a registrar can only be a root (no `actor_cls` hook on `start_actor()` to spawn one as a subactor). (this patch was generated in some part by [`claude-code`][claude-code-gh]) [claude-code-gh]: https://github.com/anthropics/claude-code
Sweep the `examples/debugging/` set for basic typing: add `-> None` to all 16 bare `async def main()`s and annotate the clean single-line `open_nursery()` bindings as `tractor.ActorNursery`. Kept to the unambiguous, runtime-safe cases (these breakpoint/crash demos can't be run headless); the heterogeneous multi-line/paren-group nursery bindings + `current_actor()` returns are left for a later pass. Continues the examples-typing bullet in (this patch was generated in some part by [`claude-code`][claude-code-gh]) [claude-code-gh]: https://github.com/anthropics/claude-code
Type the runtime objects (`ActorNursery`, `Portal`, `Context`, `trio.Nursery`) + fn signatures across the 16 highest-visibility, `literalinclude`-d `examples/` scripts, matching the front-page `we_are_processes.py` style — so the rendered guides show typed usage throughout, not just on the landing snippet. Spans the 3 quickstart-backing scripts + `single_func`, `remote_error_propagation`, `multiple_streams_one_portal`, `quick_cluster`, `service_discovery`, `service_daemon_discovery`, `asynchronous_generators`, `nested_actor_tree`, `concurrent_actors_primes`, `streaming_broadcast_fanout`, `rpc_bidir_streaming`, `infected_asyncio_echo_server`, `typed_payloads`. Annotation-only (no renames/logic changes); each runs green and the docs build stays warning-free. Part of the examples-typing bullet in #472. (this patch was generated in some part by [`claude-code`][claude-code-gh]) [claude-code-gh]: https://github.com/anthropics/claude-code
Finish the examples-typing sweep with the last non-docs-visible scripts: `-> None` on the two `trio/` behavior-demo mains (plus a `trio.TaskStatus` on `hold_lock_forever`) and nursery/portal typing on `integration/mpi4py/inherit_parent_main.py`. Leaves `concurrent_futures_primes` (a verbatim stdlib baseline) and `integration/open_context_and_sleep` (its tractor nursery is commented out) as-is, and the paren-group `trio.open_nursery()` bindings unannotated (no clean spot for a preceding annotation). Completes the examples-typing bullet in #472. (this patch was generated in some part by [`claude-code`][claude-code-gh]) [claude-code-gh]: https://github.com/anthropics/claude-code
There was a problem hiding this comment.
🟡 Changes recommended
The new examples/dedicated_registrar.py uses a fixed port and has incomplete subprocess cleanup, which can make the examples-as-tests run flaky and potentially leak a background registrar process.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
This PR updates the documentation and example scripts as follow-ups to the #472 work, primarily by tightening type annotations across examples and extending the discovery guide with a dedicated-registrar pattern example.
Changes:
- Add/expand type annotations across many
examples/scripts to make the docs/examples more type-explicit. - Add a new
examples/dedicated_registrar.pyand document the “dedicated registrar” pattern indocs/guide/discovery.rst. - Expand messaging guide discussion around typed payload contracts and codec hooks, with additional issue/PR references.
File summaries
| File | Description |
|---|---|
| examples/uds_transport_actor_tree.py | Add explicit ActorNursery/Portal annotations in UDS transport example. |
| examples/typed_payloads.py | Add explicit ActorNursery/Portal annotations in typed payloads example. |
| examples/trio/send_chan_aclose_masks_beg.py | Add explicit return type to main() signature. |
| examples/trio/lockacquire_not_unmasked.py | Add TaskStatus typing and return annotations for async entry points. |
| examples/streaming_broadcast_fanout.py | Add ActorNursery/Portal/Nursery annotations in broadcast fanout example. |
| examples/service_discovery.py | Add typed main() signature and typed portal variables for discovery calls. |
| examples/service_daemon_discovery.py | Add ActorNursery/Portal/Nursery annotations in daemon discovery example. |
| examples/rpc_bidir_streaming.py | Add explicit Portal type annotation for spawned RPC server portal. |
| examples/remote_error_propagation.py | Add return annotations and typed real_actors list. |
| examples/quick_cluster.py | Add return annotation for main() and annotate nursery variable. |
| examples/parallelism/single_func.py | Add return annotations and type pid from to_actor.run. |
| examples/parallelism/concurrent_actors_primes.py | Add argument/variable typing throughout primes worker-pool example. |
| examples/nested_actor_tree.py | Add ActorNursery/Nursery/Portal annotations in nested-tree example. |
| examples/multiple_streams_one_portal.py | Add typing to function params/locals and reformat start_actor call. |
| examples/integration/mpi4py/inherit_parent_main.py | Add ActorNursery/Portal/result annotations in mpi4py integration example. |
| examples/infected_asyncio_echo_server.py | Add return annotations and typed Portal in asyncio infection example. |
| examples/full_fledged_streaming_service.py | Add/extend type annotations in streaming service example. |
| examples/dedicated_registrar.py | New example demonstrating a dedicated external registrar process and discovery through it. |
| examples/debugging/subactor_error.py | Add return annotation for main() in debugging example. |
| examples/debugging/subactor_breakpoint.py | Add return annotation for main() in debugging example. |
| examples/debugging/subactor_bp_in_ctx.py | Add return annotation for main() in debugging example. |
| examples/debugging/shielded_pause.py | Add return annotation for main() in debugging example. |
| examples/debugging/root_timeout_while_child_crashed.py | Add return annotation for main() in debugging example. |
| examples/debugging/root_self_cancelled_w_error.py | Add return annotation for main() in debugging example. |
| examples/debugging/root_cancelled_but_child_is_in_tty_lock.py | Add return annotation for main() in debugging example. |
| examples/debugging/root_actor_error.py | Add return annotation for main() in debugging example. |
| examples/debugging/root_actor_breakpoint.py | Add return annotation for main() in debugging example. |
| examples/debugging/pm_in_subactor.py | Add return annotation for main() in debugging example. |
| examples/debugging/per_actor_debug.py | Add return annotation for main() in debugging example. |
| examples/debugging/multi_subactors.py | Add return annotation for main() in debugging example. |
| examples/debugging/multi_subactor_root_errors.py | Add return annotation for main() in debugging example. |
| examples/debugging/multi_nested_subactors_error_up_through_nurseries.py | Add return annotation for main() in debugging example. |
| examples/debugging/multi_daemon_subactors.py | Add return annotation for main() in debugging example. |
| examples/debugging/fast_error_in_root_after_spawn.py | Add return annotation for main() in debugging example. |
| examples/asynchronous_generators.py | Add return annotation and type key locals in async generator example. |
| examples/actor_spawning_and_causality.py | Add return annotations for example functions. |
| examples/actor_spawning_and_causality_with_daemon.py | Add return annotations and type the spawned portal. |
| examples/a_trynamic_first_scene.py | Add return annotations and type the portal in discovery call. |
| docs/guide/msging.rst | Expand typed messaging roadmap/details and add references (#365, #376). |
| docs/guide/discovery.rst | Document the dedicated registrar pattern and include the new example. |
Review details
Suppressed comments (1)
examples/dedicated_registrar.py:118
- The registrar subprocess cleanup suppresses TimeoutExpired but does not kill the process on timeout, which can leak a background daemon and cause later example runs to fail. Also, send_signal() can raise if the process already exited; it’s safer to suppress that and force-kill on timeout.
# graceful SIGINT teardown of the standalone registrar.
registrar.send_signal(signal.SIGINT)
with suppress(subprocess.TimeoutExpired):
registrar.wait(timeout=10)
print('dedicated registrar shut down')
- Files reviewed: 40/40 changed files
- Comments generated: 2
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Finish the Python style, typing and docstring pass across the ordinary, parallelism, Trio and integration examples. Preserve each demo's runtime behavior while tightening callable, portal, stream and nursery annotations. Use the modern `.chan` portal attr and current actor-lifecycle terminology. (this patch was generated in some part by `opencode` using `gpt-5.6-sol` (`openai`))
Finish the Python style, typing and docstring pass across the debugger examples without changing their intentional breakpoints, failures, cancellation races or timeout reproducers. Restore full child command lines in the documented process trees and keep the examples within the 69-column source limit. (this patch was generated in some part by `opencode` using `gpt-5.6-sol` (`openai`))
Run the registrar in its own process and prove that a sibling client discovers the service through registry lookup instead of an existing peer channel. Retry ephemeral bind collisions, publish readiness atomically and validate bounded cross-platform shutdown. Document actual duplicate name and multi-registrar ordering semantics alongside the example. Move the demo under the discovery examples and wrap process ownership in an `@acm`. Record the future public subsystem, Piker service and pytest isolation follow-ups. (this patch was generated in some part by `opencode` using `gpt-5.6-sol` (`openai`))
Document `Started` as the eager sender-side payload check and `Yield` plus `Return` as receiver-side decoding boundaries without promising a symmetric error relay. Separate the working task-scoped codec encoder from the private per-dialog decoder and the incomplete `@context` hook params. Link the planned typed `Start` contract and sender-side argument validation follow-up in #514. (this patch was generated in some part by `opencode` using `gpt-5.6-sol` (`openai`))
Capture the human direction, generated changes, review findings and validation results for the docs-example landing pass. Point generated-code references at the complete commit range from the pre-remediation branch head. Prompt-IO: ai/prompt-io/opencode/20260828T200822Z_0be872ff_prompt_io.md (this patch was generated in some part by `opencode` using `gpt-5.6-sol` (`openai`))
Print the discovered service's remote channel address instead of the default `Portal` object repr. Review: #512 (comment) (this patch was generated in some part by `opencode` using `gpt-5.6-sol` (`openai`))
There was a problem hiding this comment.
🟡 Changes recommended
At least one updated example introduces a concrete static-typing mismatch (accept_addr annotated too narrowly) that should be corrected to keep the examples type-consistent.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Review details
- Files reviewed: 42/42 changed files
- Comments generated: 1
- Review effort level: Lite
Match `Actor.accept_addr`'s declared `tuple[str, int|str]` contract instead of narrowing its second item to the UDS-specific string path. Review: #512 (comment) (this patch was generated in some part by `opencode` using `gpt-5.6-sol` (`openai`))
Refine typed messaging and discovery examples
Motivation
PR #460 established the modern docs scaffold, while #472 retained the
content work needed to make its guides and examples accurately teach
current APIs. Typed messaging and discovery still needed deeper
coverage, and many docs-visible examples lacked consistent typing and
source style.
This pass documents the runtime's actual payload-validation and
discovery contracts, adds a real dedicated-registrar topology, and
makes the examples easier to read without changing their intentional
failure and debugging behavior.
Summary of changes
Expand the typed-messaging guide around
pld_spec, sender- versusreceiver-side validation, custom codecs, and the current boundaries
of per-task and per-dialog hooks.
Add a standalone dedicated-registrar example under
examples/discovery/. It uses an ephemeral address, boundedcollision retries, atomic readiness publication, external sibling
discovery, and validated cross-platform teardown.
Apply the repository's typing, docstring, string, and 69-column
conventions across 38 changed examples while preserving intentional
debugger failures, cancellation races, and Trio reproducers.
Future follow up
Promote dedicated-registrar lifecycle management into a public
tractor.discoverysubsystem, informed by Piker's attach-or-createservice ownership and usable by pytest registry isolation.
Complete typed
Startcontracts and sender-side RPC argumentvalidation under Type-check RPC
Startparams from endpoint signatures #514.Continue the remaining content and real-world-example work
under Docs content refinement pass (follow-up to #460) #472.
Links
Context.open_stream()(probably withmsgspec.Structsubtype(s)) 😎 #365 and PRenc_hook&dec_hookfactory (+ CIuvupdate) #376.Startvalidation follow-up in Type-check RPCStartparams from endpoint signatures #514.(this pr content was generated in some part by
opencodeusinggpt-5.6-sol(openai))