Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions ai/prompt-io/opencode/20260820T150250Z_9afda1c6_prompt_io.md
Original file line number Diff line number Diff line change
@@ -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.
25 changes: 25 additions & 0 deletions ai/prompt-io/opencode/20260820T150250Z_9afda1c6_prompt_io.raw.md
Original file line number Diff line number Diff line change
@@ -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.
84 changes: 84 additions & 0 deletions tests/devx/test_pformat.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
'''
Unit tests for the `tractor.devx.pformat` render helpers.

'''
from __future__ import annotations

import pytest

from tractor._exceptions import _mk_send_mte
from tractor.devx.pformat import (
pformat_boxed_tb,
pformat_caller_frame,
)
from tractor.msg._codec import _def_tractor_codec


@pytest.mark.parametrize(
'box_tb',
[True, False],
ids=['boxed', 'bare'],
)
def test_pformat_caller_frame_renders(box_tb: bool):
'''
`pformat_caller_frame()` must render, not raise.

XXX the `box_tb=True` branch was passing an `indent=''` kwarg
that `pformat_boxed_tb()` never accepted, so it blew up with
a `TypeError`. Nothing in the test suite covered it, and the
only caller is `_mk_send_mte()` — i.e. EVERY send-side
`MsgTypeError` died while formatting itself, masking the real
msg-spec violation behind a bogus `TypeError`.

'''
report: str = pformat_caller_frame(
stack_limit=3,
box_tb=box_tb,
)
assert isinstance(report, str)
assert 'test_pformat_caller_frame_renders' in report


def test_pformat_boxed_tb_rejects_unknown_kwargs():
'''
Pin the signature so a future typo'd kwarg fails loudly at the
call site rather than only when some rare error path runs.

'''
assert pformat_boxed_tb(tb_str='doggy\n')

with pytest.raises(TypeError):
pformat_boxed_tb(
tb_str='doggy\n',
indent='',
)


def test_send_mte_default_message_renders():
'''
The default send-side `MsgTypeError` must remain printable.

Once `pformat_caller_frame()` stopped failing first, this path
exposed two more formatter errors: `MsgCodec.msg_spec_str` passed
a type union where `pformat_msgspec()` requires a codec/decoder,
then `_mk_send_mte()` wrapped its message in a one-element tuple.

Construct the error without an override message to execute that
complete default path. Requiring a `str` message with the bad
value and valid spec, then rendering the exception, proves the
original IPC violation survives every formatter layer.

'''
bad_msg: dict[str, bool] = {'bad': True}
mte = _mk_send_mte(
msg=bad_msg,
codec=_def_tractor_codec,
)

assert isinstance(mte.message, str)
assert f'invalid msg -> {bad_msg}' in mte.message
assert 'Valid IPC msgs are:' in mte.message

report: str = repr(mte)
assert 'MsgTypeError' in report
assert f'invalid msg -> {bad_msg}' in report
2 changes: 1 addition & 1 deletion tractor/_exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -1509,7 +1509,7 @@ def _mk_send_mte(
f'invalid msg -> {msg}: {type(msg)}\n\n'
f'{tb_fmt}\n'
f'Valid IPC msgs are:\n\n'
f'{codec.msg_spec_str}\n',
f'{codec.msg_spec_str}\n'
)
elif src_type_error:
src_message: str = str(src_type_error)
Expand Down
1 change: 0 additions & 1 deletion tractor/devx/pformat.py
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,6 @@ def pformat_caller_frame(
tb_str: str = pformat_boxed_tb(
tb_str=tb_str,
field_prefix=' ',
indent='',
)
return tb_str

Expand Down
2 changes: 1 addition & 1 deletion tractor/msg/_codec.py
Original file line number Diff line number Diff line change
Expand Up @@ -430,7 +430,7 @@ def msg_spec(self) -> Union[Type[Struct]]:
# wrapped field over the `.msg_spec` one?
@property
def msg_spec_str(self) -> str:
return pformat_msgspec(self.msg_spec)
return pformat_msgspec(self)

lib: ModuleType = msgspec

Expand Down
Loading