Skip to content

The keymap guard is zero everywhere; links click as themselves - #1555

Merged
trissim merged 3 commits into
mainfrom
keymap-guard-zero
Nov 6, 2026
Merged

trissim merged 3 commits into
mainfrom
keymap-guard-zero

Conversation

@trissim

@trissim trissim commented Nov 6, 2026

Copy link
Copy Markdown

1 commit, 8 files, +82/-54. Lands after OpenHCSDev/textual#244 (CI installs Textual from main).

Why

tests/guards/test_keymap_owner.py gave every measure a ceiling, a tolerated count of keys written outside their owner. ActionMethods allowed 9, main had 11, and main went red. Raising the ceiling would have been the wrong fix, and so is the ceiling: it's a ratchet on slop. Paper1 §1: a fact supplied outside its declaration is slop, and the count of copies we agree to keep is still that count. Every measure now asserts zero. RawBindings (4), HandBindings (5) and KeyHandlers (1) already had 0 sites under their slack.

The 11 sites

Site Before After
history_anchor.py ×5 overrode action_scroll_* and action_page_*, reached by ScrollableContainer's string BINDINGS overrides ScrollsView operations, whose keys are the ScrollCommand family (textual#244)
sidebar_tree.py ×2 action_cursor_left/right, already the MovesHorizontalCursor contract. The guard exempted only MovesCursor. exempt as declared operations
prompt.py ×2 overrode TextArea's delete_left/cursor_line_end, the first reached by a string binding TextArea declares both with MethodBinding.declare (textual#244)
linked_rows.py ×2 action_open(link) reached by the meta ("open", (link,)), and action_link(href) reached by Markdown's "link('…')" deleted

ActionMethods exempts exactly the operations Textual declares: methods of a Navigable contract and methods carrying a MethodBinding. The set is read from the classes, not a hand-kept list.

Links

  • A drawn link's style meta carries OpenLineLink(link), a StyleAction in the new toad/link_click.py whose run dispatches the link on the widget that drew it.
  • LineLink.click and its open_action = "open" leave line_blocks.py. That module's docstring declares that nothing in it imports a frontend, yet it was holding a Textual action name.
  • OpensLinks inherits Textual's OpensMarkdownLinks, and open_link(href) decodes a toad-link href once.
  • Right-click reads OpenLineLink.link_of(handler.action), the single boundary decode, and the app's function-local import is gone. The keyboard path dispatches the link directly.

Proof

  • test_no_key_is_written_outside_the_action_family passes against textual#244.
  • Link, file-link (including right-click), scroll, reader-position, history-geometry and widget-action tests: see the comment once the run finishes.

🤖 Generated with Claude Code

Copilot AI balanced review requested due to automatic review settings November 6, 2026 05:43

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. Try requesting a review at a lower effort level.

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.

@github-actions

github-actions Bot commented Nov 6, 2026 •

Copy link
Copy Markdown

debt-src at 8a38d6e passes.

trissim and others added 3 commits November 6, 2026 00:52
Every KeymapMeasure asserts zero and `ceiling` is gone: a tolerated count of keys written
outside their owner was a ratchet on slop. ActionMethods exempts exactly the operations
Textual declares (a navigation contract's methods, a MethodBinding's), read from the classes.

With OpenHCSDev/textual#244, the eleven `def action_*` sites are operations, not names:
HistoryWindow overrides ScrollsView, TargetTree implements MovesHorizontalCursor, the prompt
overrides TextArea's declared editing methods. The two link actions are deleted:
- a drawn link's style meta carries OpenLineLink(link), a StyleAction in the new
  toad/link_click.py; LineLink.click and its "open" action name leave line_blocks, which
  declares that nothing in it imports a frontend;
- OpensLinks inherits Textual's OpensMarkdownLinks, and open_link decodes a toad-link href
  once; the right-click menu reads OpenLineLink.link_of, and the app's inline import is gone.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… open through

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…or hidden imports

The NRA gate on this PR found five: link_of matched OpenLineLink by type (OwnTypeTest) on a
StyleAction | None (NoneAbsence), two TYPE_CHECKING imports with no cycle, the app's nested
right-click if (SIM102), and the new file unformatted. With textual#245, OpenLineLink.secondary
opens the link's menu and OpensLinks.link_menu decodes a toad-link href into its link's
menu; the app asks handler.action.secondary once. link_of is deleted.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@trissim
trissim merged commit 5ea88ac into main Nov 6, 2026
2 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