Skip to content

Declare caller-owned types for terminal, turn and rendering inputs - #1154

Merged
trissim merged 8 commits into
mainfrom
next/toad-types-07
Oct 23, 2026
Merged

trissim merged 8 commits into
mainfrom
next/toad-types-07

Conversation

@trissim

@trissim trissim commented Oct 23, 2026 •

Copy link
Copy Markdown

170 parameters now name the types their callers pass. The eight selected modules go 224 → 54 bare parameters; all src/toad goes 1328 → 1158. Explicit Any remains 10. Parameter deletions: 0.

Base: eee930eadd3f4c0383b8116242112a5659fc53b7. Head: ae601d27904f00ad4e30b349bb09cf63867a1ee1. These are the next eight untouched modules by count; earlier slices’ documented missing owners are not counted again as new work.

Module Main bare parameters Head bare parameters
markdown_links.py 32 4
acp/session_updates.py 31 5
terminal_execution.py 30 7
conversation_turn.py 28 7
agent.py 27 10
block_navigation.py 27 4
widgets/transcript_fragments.py 25 9
widgets/transcript_lines.py 24 8
Eight total 224 54
All src/toad 1328 1158

All 287 production modules parse. All eight changed modules import under python -I and compile. AST comparison confirms unchanged bodies, defaults and inheritance; Mypy adds no diagnostics in these eight files. Debt ratchet passed on this head: https://github.com/OpenHCSDev/toad/actions/runs/39991600220. No tests were run. No guards, dependency locks or checker baselines changed. No Any, object, cast, Protocol, type ignores or invented nullable annotations were added.

The 54 remaining bare parameters are listed below. Each row names the broken declaration and the class that should own it. Proposed parent classes are named explicitly; they are not implemented by this typing PR.

File:line What is wrong Owning class
markdown_links.py:44 Link readers receive Widget, but Widget.app does not declare the coordination_access they read. LinkContext
markdown_links.py:62 Live messages return None for history_source; this argument has no class representing a live source. MessageHistorySource (parent of live and saved sources)
markdown_links.py:175 markdown_preparation.py:38 and :57 omit env, leaving None to mean the parser should create an environment. MessageMarkdownParser
markdown_links.py:187 markdown_preparation.py:38 and :57 omit env, leaving None to mean the parser should create an environment. MessageMarkdownParser
acp/session_updates.py:120 CommsUpdateConsumer.route begins as None at comms_updates.py:49 and passes that value to content handlers. ResponseDelivery
acp/session_updates.py:158 Mypy calls ValidateSessionUpdateTask abstract, although RenderExecution creates its result-checking method during class creation and the runtime class is concrete. RenderExecution
acp/session_updates.py:158 Mypy calls ValidateSessionUpdateTask abstract, although RenderExecution creates its result-checking method during class creation and the runtime class is concrete. RenderExecution
acp/session_updates.py:170 Rejected updates arrive as raw JSON from sdk_boundary.py:56 or decoded SessionNotification from this reader; one argument is doing two jobs. RejectedSessionUpdateValidation
acp/session_updates.py:170 SessionNotification.field_meta can be None, and receive forwards it unchanged. SessionNotificationOwner
terminal_execution.py:204 action_modal.py:89 and command_pane.py:99 omit Command.cwd; TerminalExecution.run forwards its None value into process creation. Command
terminal_execution.py:300 TerminalExecution and ShellOperationalSource both implement run(ready), but the operation accepts either without a shared parent declaring that method. TerminalRunner (new parent of TerminalExecution and ShellOperationalSource)
terminal_execution.py:310 TerminalExecution and ShellOperationalSource both implement run(ready), but the operation accepts either without a shared parent declaring that method. TerminalRunner (new parent of TerminalExecution and ShellOperationalSource)
terminal_execution.py:322 TerminalExecution and ShellOperationalSource both implement run(ready), but the operation accepts either without a shared parent declaring that method. TerminalRunner (new parent of TerminalExecution and ShellOperationalSource)
terminal_execution.py:331 TerminalExecution and ShellOperationalSource both implement run(ready), but the operation accepts either without a shared parent declaring that method. TerminalRunner (new parent of TerminalExecution and ShellOperationalSource)
terminal_execution.py:421 TerminalTool is passed here, but _view was inferred as a function returning only None and cannot truthfully hold its weak reference. TerminalExecution
terminal_execution.py:427 TerminalExecution.close and TerminalController.detach call this without a terminal, leaving None to select detach-all behavior. TerminalExecution
conversation_turn.py:60 AgentCommsUpdate and AttachmentPresentation each implement matches_turn, but their shared declaration does not name that operation. TurnUpdate (new parent of AgentCommsUpdate and AttachmentPresentation)
conversation_turn.py:63 No caller exists in the searched Toad/Core sources or tests, so there is no producer declaring the settlement identifier this unused method accepts. TurnOwner
conversation_turn.py:115 No caller exists in the searched Toad/Core sources or tests, so there is no producer declaring the settlement identifier this unused method accepts. TurnOwner
conversation_turn.py:169 AgentCommsUpdate and AttachmentPresentation each implement matches_turn, but their shared declaration does not name that operation. TurnUpdate (new parent of AgentCommsUpdate and AttachmentPresentation)
conversation_turn.py:172 No caller exists in the searched Toad/Core sources or tests, so there is no producer declaring the settlement identifier this unused method accepts. TurnOwner
conversation_turn.py:218 The constructor defaults agent to None, while presentation constructors pass Agent or NoAgentConnection. AgentConnection
conversation_turn.py:266 The reader needs CommsUpdated.sequence, but CoreEventMessage declares its event only as CoreEvent. CoreEventMessage
agent.py:38 ConversationSessionBinding passes itself, while AttachedSurfaceBinding requires concrete Conversation; the parent does not declare the behavior the binding uses. ConversationSessionBinding
agent.py:45 CoreEventMessage declares publisher as object, so the agent identity carried to this comparison loses its type. CoreEventMessage
agent.py:51 AgentBase reads self.session_id, but only ACP Agent declares that session field. AgentBase
agent.py:66 slash_command.py:204–218 and conversation.py:1653–1657 pass None to mean no compaction instructions. CompactRequest
agent.py:69 ConversationSubmissions requires ACP Agent, but these methods pass their AgentBase receiver, which does not declare the queue and session operations being used. QueuedAgent (capability parent composed into ACP Agent)
agent.py:69 ConversationSubmissions requires ACP Agent, but these methods pass their AgentBase receiver, which does not declare the queue and session operations being used. QueuedAgent (capability parent composed into ACP Agent)
agent.py:72 These readers use session_id, recover_draft and queue_scope from CommsUpdated, while the carrier declares only CoreEvent. CoreEventMessage
agent.py:81 ConversationSubmissions requires ACP Agent, but these methods pass their AgentBase receiver, which does not declare the queue and session operations being used. QueuedAgent (capability parent composed into ACP Agent)
agent.py:101 slash_command.py:204–218 and conversation.py:1653–1657 pass None to mean no compaction instructions. CompactRequest
agent.py:107 These readers use session_id, recover_draft and queue_scope from CommsUpdated, while the carrier declares only CoreEvent. CoreEventMessage
block_navigation.py:42 BlockContent creates this cursor but does not declare the Widget geometry that the cursor reads. ConversationEntry
block_navigation.py:88 No production caller or override exists in the searched Toad/Core/Textual sources, so this unused method has no producer to declare its input. BlockCursor
block_navigation.py:247 AgentResponse.displayed_children yields Widget, but this cursor calls the children’s BlockContent methods. AgentResponse
block_navigation.py:358 Contents declares its children as Widget, while navigation calls ConversationEntry methods on them. Contents
widgets/transcript_fragments.py:246 BusMessageFragment passes an event whose common TranscriptEvent parent allows source=None; the reader requires an actual message reference. ReferencedTranscript (reference-bearing TranscriptEvent parent)
widgets/transcript_fragments.py:308 TextTranscript does not declare a replaceable text field, and IncomingTranscript derives text instead of storing it; replacing text on the parent is false. TextTranscript
widgets/transcript_fragments.py:308 Fragment construction forwards shared fields plus row or publication through unnamed keyword arguments. TranscriptFragment
widgets/transcript_fragments.py:434 Fragment construction forwards shared fields plus row or publication through unnamed keyword arguments. TranscriptFragment
widgets/transcript_fragments.py:469 LineHistory passes Shown, but bus fragments read style and status declared only by MessageShown. TranscriptFragment (preserve its Shown member type)
widgets/transcript_fragments.py:472 LineHistory passes Shown, but bus fragments read style and status declared only by MessageShown. TranscriptFragment (preserve its Shown member type)
widgets/transcript_fragments.py:475 LineHistory passes Shown, but bus fragments read style and status declared only by MessageShown. TranscriptFragment (preserve its Shown member type)
widgets/transcript_fragments.py:496 This method reads events[0].text, but inherited events is declared as TranscriptEvent, which has no text member. ContextTranscriptFragment
widgets/transcript_fragments.py:536 Fragment construction forwards shared fields plus row or publication through unnamed keyword arguments. TranscriptFragment
widgets/transcript_lines.py:259 LineBlock promises any RowsT result, while these overrides return only PreparedLines and cannot accept that entire callback type. LineBlock
widgets/transcript_lines.py:308 LineBlock promises any RowsT result, while these overrides return only PreparedLines and cannot accept that entire callback type. LineBlock
widgets/transcript_lines.py:700 TranscriptPageView, LineHistory and Contents each provide line width and palette access without a shared parent declaring them. LineViewport (capability parent of those three views)
widgets/transcript_lines.py:703 TranscriptPageView, LineHistory and Contents each provide line width and palette access without a shared parent declaring them. LineViewport (capability parent of those three views)
widgets/transcript_lines.py:709 LineHistory.app is declared as generic App, which does not declare the colours read here. LineHistory
widgets/transcript_lines.py:709 LineHistory._show passes ready.get(), which returns None before prepared rows arrive. RowAcquisition
widgets/transcript_lines.py:791 TranscriptPageView, LineHistory and Contents each provide line width and palette access without a shared parent declaring them. LineViewport (capability parent of those three views)
widgets/transcript_lines.py:791 Mypy calls TranscriptLinesRenderTask abstract, although RenderExecution generates its result-checking method and the runtime class is concrete. RenderExecution

Assumed:

  • “Next modules” means the next untouched modules by current-main count, excluding previous slices’ documented missing owners.
  • Existing callers determine parameter types; real None values and missing class declarations retain existing behavior.
  • This PR changes annotations, not installed UI behavior. MI and play-testing remain the priority.

Independent review on the head is required. The author does not merge this PR.

@trissim
trissim marked this pull request as ready for review October 23, 2026 17:04
Copilot AI balanced review requested due to automatic review settings October 23, 2026 17:04

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has insufficient remaining quota.

An organization owner can choose the repository organization as the billing source for reviews requested by members with a paid Copilot license. Learn about Copilot code review billing.

@trissim
trissim merged commit 06c611d into main Oct 23, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants