Skip to content

Every CLI result writes itself; watch and tool honour Answer: CliResult - #2774

Merged
trissim merged 3 commits into
mainfrom
next/remaining-any-families
Oct 23, 2026
Merged

trissim merged 3 commits into
mainfrom
next/remaining-any-families

Conversation

@trissim

@trissim trissim commented Oct 23, 2026 •

Copy link
Copy Markdown
Contributor

What changed

Two commands broke the CLI family's Answer: CliResult bound and hid it with their own run overrides:

  • WatchCliCommand.apply returned an async iterator.
  • InvokeCliCommand.apply returned a dict.

Both wrote output by hand beside CliCommand.written. Writing now belongs to the result:

  • CliResult.write(output) is the one writer. CliCommand.run and the three installer-facing execute overrides (read-back, store-reconcile, replace-route) call it. CliCommand.written is deleted.
  • WatchStream(CliResult) owns the JSON-lines stream. WatchCliCommand declares CliCommand[WatchStream], and its run override is deleted.
  • ToolReplyResult(CliResult) carries the tool protocol reply, unwrapped, as its wire form. InvokeCliCommand declares CliCommand[ToolReplyResult], and its run override is deleted.

The ratchet already exists

debt-src already runs AnyName and UnannotatedParameter in tools/debt_ratchet.py. Both are AST-based and counted per file, and Measure.blocks fails any growth in any touched file, so no new measure is needed.

Proof that AnyName tracks real edits, measured across #2762's merge (f6fad69^1 → f6fad69):

Measure Before After
AnyName 41 39
UnannotatedParameter 1642 1642

The whole −2 is command.py:17 (def apply(self, ctx: Any) -> Any), which is exactly what the diff removed. #2762's report of "42 → 38" counted import lines as uses.

Not drained here, with owners

  • registry_document.py:508: mypy's replace check types each keyword against its field, so **reused only typechecks as Any. The section names come from FieldCodec.field_plans. The owner of dynamic field replacement is FieldCodec, which Give DM peers one typed encoding, fix stale tests, repin native build #2763 holds.
  • child_process.py:1968: SpawnedProcess.launch(**options: Any) forwards to launching. Deriving its signature with a ParamSpec fails under mypy (launching in the class body has no __func__, and launch turns into Any). The real owner is a declared launch-options record, which touches 31 call sites; that's a slice of its own.

This PR leaves both measures unchanged: AnyName stays at 39 and UnannotatedParameter at 1642. It fixed two bound violations, not Any sites.

Checks run locally

  • mypy and ruff on the touched files, head against main with the project config: no new mypy messages, one fewer error each in cli_commands.py and tools.py, ruff counts equal.
  • Touched tests compared against main: see the comment below.

🤖 Generated with Claude Code

Owner list for what #2762 left: every remaining Any site by file:line and the
unannotated-parameter scan, in the PR body. No code yet.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Citation fixture and others added 2 commits October 23, 2026 09:16
WatchCliCommand.apply returned an async iterator and InvokeCliCommand.apply a
dict, both outside the family's `Answer: CliResult` bound, and each covered the
gap with its own `run` override writing output by hand beside
CliCommand.written. Writing belongs to the result:

- CliResult.write(output) is the one writer. CliCommand.run and the three
  installer-facing execute overrides call it; CliCommand.written is deleted.
- WatchStream(CliResult) owns the JSON-lines stream. WatchCliCommand declares
  CliCommand[WatchStream]; its run override is deleted.
- ToolReplyResult(CliResult) carries the tool protocol reply, unwrapped, as its
  wire form. InvokeCliCommand declares CliCommand[ToolReplyResult]; its run
  override is deleted.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@trissim trissim changed the title Type the remaining Any families and unannotated parameters Every CLI result writes itself; watch and tool honour Answer: CliResult Oct 23, 2026
@trissim

trissim commented Oct 23, 2026

Copy link
Copy Markdown
Contributor Author

Touched tests (test_cli, test_watch_claims, test_tools, test_child_process, test_command_families, test_codec_capabilities, test_runtime, test_mcp_acceptance), -p no:xdist: head 7f0b38e 156 passed / 4 failed; origin/main 156 passed / 4 failed; identical failing set (test_command_families normal_history[DmHistoryPageResult-1], test_runtime new_session_metadata_has_a_display_title_before_first_switch, test_runtime subscription_replay_uses_requested_alias[True/False]). None new.

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

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 reached their quota limit.

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 eaaff2b into main Oct 23, 2026
12 of 14 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