From bf2bb1c9a612f12dc53bca1d395986433c7a252c Mon Sep 17 00:00:00 2001 From: goodboy Date: Wed, 21 Oct 2026 11:29:32 -0400 Subject: [PATCH] Mv task UID formatting into `devx.pformat` Add `pformat_task()` beside `pformat_task_uid()` so diagnostics can inspect an explicit endpoint, sibling or nursery-owning `trio.Task`. Keep the current-task default and decimal head/tail identity format. Preserve the `tractor.log` entry point with lazy forwarding, avoiding eager debugger initialization during logging imports. Move the existing formatting TODOs with the implementation. Exercise real parent/child tasks without an actor, including coroutine identity, nursery ownership and equivalent log/tooling UID output. Prompt-IO: ai/prompt-io/20261021T145344Z_0553fcf8_task_format_prompt_io.md Prompt-IO: ai/prompt-io/20261021T151133Z_0553fcf8_uid_move_prompt_io.md (this patch was generated in some part by `opencode` using `gpt-6.1-sol` (`openai`)) --- ...T145344Z_0553fcf8_task_format_prompt_io.md | 56 ++++++++++++++ ...344Z_0553fcf8_task_format_prompt_io.raw.md | 49 +++++++++++++ ...021T151133Z_0553fcf8_uid_move_prompt_io.md | 41 +++++++++++ ...151133Z_0553fcf8_uid_move_prompt_io.raw.md | 33 +++++++++ tests/devx/test_pformat.py | 67 +++++++++++++++++ tractor/devx/pformat.py | 73 ++++++++++++++++++- tractor/log.py | 24 +++--- 7 files changed, 327 insertions(+), 16 deletions(-) create mode 100644 ai/prompt-io/20261021T145344Z_0553fcf8_task_format_prompt_io.md create mode 100644 ai/prompt-io/20261021T145344Z_0553fcf8_task_format_prompt_io.raw.md create mode 100644 ai/prompt-io/20261021T151133Z_0553fcf8_uid_move_prompt_io.md create mode 100644 ai/prompt-io/20261021T151133Z_0553fcf8_uid_move_prompt_io.raw.md diff --git a/ai/prompt-io/20261021T145344Z_0553fcf8_task_format_prompt_io.md b/ai/prompt-io/20261021T145344Z_0553fcf8_task_format_prompt_io.md new file mode 100644 index 00000000..b623d486 --- /dev/null +++ b/ai/prompt-io/20261021T145344Z_0553fcf8_task_format_prompt_io.md @@ -0,0 +1,56 @@ +--- +created_at: 2026-10-21T14:53:44Z +generated_by: + harness: opencode + provider: openai + model: openai/gpt-6.1-sol +git_ref: 0553fcf8 +scope: code +substantive: true +raw_file: 20261021T145344Z_0553fcf8_task_format_prompt_io.raw.md +--- + +## Prompt + +The human requested moving the new regression-local Trio task formatter +into devx.pformat, scanning branches for earlier equivalents first, and +noted that log.pformat_task_uid already extracts the task ID. Follow-up: +the reviewed change was good enough apart from the function move; the +QA agent had already been alerted to the new order-bootstrap race. + +## Response summary + +Found no earlier detailed task formatter across 810 local refs and 18 +worktrees. Extracted pformat_task into tooling and made the existing UID +helper accept an explicit task, preserving its established log output. +Added focused coverage that inspects a real child from its parent and +updated the cancellation regression to use the shared renderer. + +## Files changed + +- `tractor/devx/pformat.py`: reusable pformat_task. +- `tractor/log.py`: optional explicit task for pformat_task_uid. +- `tests/devx/test_pformat.py`: real task-selection/ownership coverage. +- `tests/test_context_final_return_disconnect.py`: shared-tool callers. + +## Review boundary and verification + +Pre-move tree: `27e75d8b1cdfec1d96284d993e55ad3c51c3804c`. +HEAD remains `0553fcf88cd37b4d487eafb992071d4d1cc6e06f`. +The real index was unchanged at extraction start. Existing final-send +work and provenance are retained, including the earlier review record +that documents the then-local helper. Original Tuicr replies were not +rewritten; this extraction supersedes their local-helper description. +See the raw partner for environment details and focused verification. + +Final verification repeated after import/line-wrap adjustments: 75 +formatter/logger tests and 10 final-Return/RPC tests passed; Ruff, +whitespace and added-source 69-column checks passed. A read-only Tuicr +comparison found the saved session now contains only the first original +comment/reply pair, unlike the earlier three-pair receipt. This extraction +did not write session state or recreate the absent comments. + +## Human edits + +This extraction follows the human's requested final placement. Existing +logging TODOs, pre-review changes and task/review states are preserved. diff --git a/ai/prompt-io/20261021T145344Z_0553fcf8_task_format_prompt_io.raw.md b/ai/prompt-io/20261021T145344Z_0553fcf8_task_format_prompt_io.raw.md new file mode 100644 index 00000000..5e39f854 --- /dev/null +++ b/ai/prompt-io/20261021T145344Z_0553fcf8_task_format_prompt_io.raw.md @@ -0,0 +1,49 @@ +--- +created_at: 2026-10-21T14:53:44Z +generated_by: + harness: opencode + provider: openai + model: openai/gpt-6.1-sol +git_ref: 0553fcf8 +diff_cmd: git diff HEAD~1..HEAD +--- + +## Generated output + +> `git diff HEAD~1..HEAD -- tractor/devx/pformat.py tractor/log.py` + +Moved the regression-local task renderer into devx.pformat.pformat_task. +It accepts an explicit Trio task or defaults to the current task, and +reports identity, coroutine, parent nursery/owner and child nurseries. +The new renderer reuses log.pformat_task_uid for the short identity, +rather than independently extracting and slicing id(task). +Added a keyword-only optional task argument to that existing UID helper; +its current-task default, positional id_part and decimal head/tail output +remain unchanged. The full Task repr retains the full native identity. +Kept the UID import local to the richer renderer so log headers do not +gain a dependency on devx initialization. Existing UID/color TODOs remain. + +> `git diff HEAD~1..HEAD -- tests/test_context_final_return_disconnect.py tests/devx/test_pformat.py` + +Removed the local formatter and switched its three consumers to the +shared tooling function. Added an actor-free live parent/child task test +that checks explicit-task selection, default current-task selection, +nursery ownership, coroutine identity, and unchanged head/tail log IDs. +The accepted final-result send guard is untouched by this extraction. + +## Existing-work discovery + +Read-only scan: 810 local branch/remote-tracking refs, 18 worktree path +inventories and task-formatting definitions, plus relevant all-ref history. +Only log.pformat_task_uid and this session's regression helper were found. +The dirty pformat.py in wkt/tipc_backend_wg_stack contains an unrelated +comment spelling correction; no other worktree was modified. + +## Verification + +Worktree Python 3.13.13 / pytest 9.1.0 / Trio 0.29.0: formatter and logger +suites, 75 passed. Downstream Python 3.13.14 / pytest 9.0.2 / Trio 0.32.0: +final-Return regression and RPC suite, 10 passed. The older pytest in the +downstream environment remains below the current harness reference's +version bound; neither environment was changed. Ruff passed. A source +line-length check prompted wrapping before final verification. diff --git a/ai/prompt-io/20261021T151133Z_0553fcf8_uid_move_prompt_io.md b/ai/prompt-io/20261021T151133Z_0553fcf8_uid_move_prompt_io.md new file mode 100644 index 00000000..680839ba --- /dev/null +++ b/ai/prompt-io/20261021T151133Z_0553fcf8_uid_move_prompt_io.md @@ -0,0 +1,41 @@ +--- +created_at: 2026-10-21T15:11:33Z +generated_by: + harness: opencode + provider: openai + model: openai/gpt-6.1-sol +git_ref: 0553fcf8 +scope: code +substantive: true +raw_file: 20261021T151133Z_0553fcf8_uid_move_prompt_io.raw.md +--- + +## Prompt + +The human proposed moving pformat_task_uid into devx.pformat with the +new pformat_task and its unit test in a precursor commit, then placing +the final-result disconnect fix and matching tests in the next commit. +The request authorizes implementation and commit planning, not commits. + +## Response summary + +Moved the identity implementation into the formatting module, removed +the detailed formatter's reverse logging dependency, and preserved the +existing logging entry point through lazy forwarding. Retained the +existing TODO group and task-state markers. Extended focused identity +tests, verified the relocation and selected two dependency-ordered +whole-file commit boundaries with separate provenance. + +## Files changed + +- `tractor/devx/pformat.py`: canonical short and detailed task formatters. +- `tractor/log.py`: lazy forwarding entry point for existing log users. +- `tests/devx/test_pformat.py`: canonical/log entry-point equivalence. + +## Verification and human edits + +75 formatter/logger tests and 10 downstream final-Return/RPC tests passed. +Ruff and whitespace checks passed. Both existing environments are kept; +the downstream pytest-version caveat is disclosed in prior records. +Earlier raw prompt records are retained unchanged as generation history. +No staging, commits, history changes or Tuicr mutations were performed. diff --git a/ai/prompt-io/20261021T151133Z_0553fcf8_uid_move_prompt_io.raw.md b/ai/prompt-io/20261021T151133Z_0553fcf8_uid_move_prompt_io.raw.md new file mode 100644 index 00000000..b13bf1a0 --- /dev/null +++ b/ai/prompt-io/20261021T151133Z_0553fcf8_uid_move_prompt_io.raw.md @@ -0,0 +1,33 @@ +--- +created_at: 2026-10-21T15:11:33Z +generated_by: + harness: opencode + provider: openai + model: openai/gpt-6.1-sol +git_ref: 0553fcf8 +diff_cmd: git diff HEAD~1..HEAD +--- + +> `git diff HEAD~1..HEAD -- tractor/devx/pformat.py tractor/log.py tests/devx/test_pformat.py` + +Moved pformat_task_uid's implementation into devx.pformat alongside +pformat_task. The detailed renderer now calls the UID renderer directly; +it no longer imports logging. Kept the tractor.log entry point as a +lazy forwarding function to avoid eagerly initializing devx/debugger +modules while logging itself is being imported. Preserved the existing +current-task default and decimal head/tail ID output, plus explicit-task +selection added in the preceding extraction. Moved the existing TODO +group verbatim and did not change its checklist markers. + +Extended the task test to compare the logging entry point and canonical +tooling renderer for current and selected child tasks in both ID modes. +The existing tests/devx/test_pformat.py module is reused. + +Verification: formatter/logger suites 75 passed in 0.16s on the worktree +environment; final-Return/RPC suites 10 passed in 1.76s on the downstream +evaluation environment. Ruff and git diff --check passed. The downstream +pytest version remains 9.0.2; its current harness-version gap is recorded +in the earlier review/extraction provenance. No environment was changed. + +Prepared the requested tooling-first, disconnect-fix-second boundary +selection. Planning itself does not execute checks, stage or commit. diff --git a/tests/devx/test_pformat.py b/tests/devx/test_pformat.py index 401fae45..14a58f30 100644 --- a/tests/devx/test_pformat.py +++ b/tests/devx/test_pformat.py @@ -5,15 +5,82 @@ Unit tests for the `tractor.devx.pformat` render helpers. from __future__ import annotations import pytest +import trio from tractor._exceptions import _mk_send_mte +from tractor.log import pformat_task_uid as log_task_uid from tractor.devx.pformat import ( pformat_boxed_tb, pformat_caller_frame, + pformat_task, + pformat_task_uid, ) from tractor.msg._codec import _def_tractor_codec +def test_pformat_task_selects_another_tasks_identity() -> None: + ''' + Describe the selected task rather than the diagnostic caller. + + The final-Return regression needs to snapshot an endpoint and its + nursery owner from another task. Hold a real child in an active + nursery and inspect it from its parent, checking both identities + and public nursery relationships without requiring an actor. + Preserve the log header's decimal head/tail ID convention while + allowing that existing helper to select an explicit task too. + + ''' + async def main() -> None: + ''' + Keep the inspected child alive through all report assertions. + + ''' + async def child( + task_status: trio.TaskStatus[trio.lowlevel.Task], + ) -> None: + ''' + Publish the real task before waiting for cancellation. + + ''' + task_status.started(trio.lowlevel.current_task()) + await trio.sleep_forever() + + owner: trio.lowlevel.Task = trio.lowlevel.current_task() + owner_id: str = str(id(owner)) + assert pformat_task_uid() == f'{owner.name}[{owner_id[-6:]}]' + assert pformat_task_uid('head') == ( + f'{owner.name}[{owner_id[:6]}]' + ) + assert log_task_uid() == pformat_task_uid() + assert log_task_uid('head') == pformat_task_uid('head') + async with trio.open_nursery() as nursery: + task: trio.lowlevel.Task = await nursery.start(child) + task_id: str = str(id(task)) + task_uid: str = f'{task.name}[{task_id[-6:]}]' + report: str = pformat_task(task) + assert pformat_task_uid(task=task) == task_uid + assert pformat_task_uid('head', task=task) == ( + f'{task.name}[{task_id[:6]}]' + ) + assert log_task_uid(task=task) == task_uid + assert log_task_uid('head', task=task) == ( + pformat_task_uid('head', task=task) + ) + assert f'uid={task_uid!r}' in report + assert f'task={task!r}' in report + assert f'coro={task.coro!r}' in report + assert f'parent_nursery: {nursery!r}' in report + assert f'parent_task={owner!r}' in report + assert 'child_nurseries: ()' in report + assert f'child_nurseries: ({nursery!r},)' in ( + pformat_task(owner) + ) + assert pformat_task() == pformat_task(owner) + nursery.cancel_scope.cancel() + + trio.run(main) + + @pytest.mark.parametrize( 'box_tb', [True, False], diff --git a/tractor/devx/pformat.py b/tractor/devx/pformat.py index 9350ea47..495d4e42 100644 --- a/tractor/devx/pformat.py +++ b/tractor/devx/pformat.py @@ -25,7 +25,14 @@ import sys import textwrap import traceback -from trio import CancelScope +from trio import ( + CancelScope, + Nursery, +) +from trio.lowlevel import ( + Task, + current_task, +) def add_div( @@ -219,6 +226,70 @@ def pformat_caller_frame( return tb_str +# TODO IDEAs: +# -[ ] move to `.devx.pformat`? +# -[ ] do per task-name and actor-name color coding +# -[ ] unique color per task-id and actor-uuid +def pformat_task_uid( + id_part: str = 'tail', + *, + task: Task|None = None, + +) -> str: + ''' + Render a task's name and a decimal head/tail slice of its ID. + + Default to the current task, or select an explicit `task` when + rendering another task's identity from diagnostic tooling. + + ''' + if task is None: + task = current_task() + tid: str = str(id(task)) + if id_part == 'tail': + tid_part: str = tid[-6:] + else: + tid_part: str = tid[:6] + + return f'{task.name}[{tid_part}]' + + +def pformat_task( + task: Task|None = None, + +) -> str: + ''' + Render a Trio task's identity, execution and nursery ownership. + + Default to the current task. An explicit `task` lets a caller + inspect a sibling or nursery owner without selecting itself. + Capture the report while that task is alive to retain its active + `Task.parent_nursery` and `Task.child_nurseries` relationships. + + ''' + if task is None: + task = current_task() + task_uid: str = pformat_task_uid(task=task) + nursery: Nursery|None = task.parent_nursery + parent_task: Task|None = ( + nursery.parent_task if nursery is not None else None + ) + child_nurseries: tuple[Nursery, ...] = tuple( + task.child_nurseries, + ) + return ( + f'\n' + ) + + def pformat_cs( cs: CancelScope, var_name: str = 'cs', diff --git a/tractor/log.py b/tractor/log.py index 50dabaf7..e6042c75 100644 --- a/tractor/log.py +++ b/tractor/log.py @@ -332,26 +332,20 @@ def add_log_level( add_log_level('IO', 21, 'purple') -# TODO IDEAs: -# -[ ] move to `.devx.pformat`? -# -[ ] do per task-name and actor-name color coding -# -[ ] unique color per task-id and actor-uuid def pformat_task_uid( - id_part: str = 'tail' -): + id_part: str = 'tail', + *, + task: trio.lowlevel.Task|None = None, + +) -> str: ''' - Return `str`-ified unique for a `trio.Task` via a combo of its - `.name: str` and `id()` truncated output. + Forward the logging entry point to devx.pformat's UID renderer. ''' - task: trio.Task = trio.lowlevel.current_task() - tid: str = str(id(task)) - if id_part == 'tail': - tid_part: str = tid[-6:] - else: - tid_part: str = tid[:6] + # Import lazily: devx initialization itself imports tractor.log. + from .devx.pformat import pformat_task_uid as render_task_uid - return f'{task.name}[{tid_part}]' + return render_task_uid(id_part, task=task) _curr_actor_no_exc = partial(