Skip to content

Put the last 14 files under the type gate, and fix what they were hiding - #418

Merged
dovvnloading merged 1 commit into
mainfrom
types/close-the-gate-gap
Sep 5, 2026
Merged

Put the last 14 files under the type gate, and fix what they were hiding#418
dovvnloading merged 1 commit into
mainfrom
types/close-the-gate-gap

Conversation

@dovvnloading

Copy link
Copy Markdown
Owner

Problem

The gate said "339 source files" and meant it — but "all of them" was never
verified. Checking [tool.mypy].files against the tree found three directories
with Python in them that nothing was checking:

directory files state
plugins/ 8 6 errors
mutation_tests/ 5 clean
tests_e2e/ 1 clean

What plugins/ was hiding

plugins/system_prompt/plugin.py, two kinds of error:

root = document.get_branch_root(parent_node_id)
... edge.target == root.id

get_branch_root returns SceneNode | NoneNone for an id it does not
know. It cannot be None here: the guard fifteen lines up already returns
early for a parent_node_id that is not in document.nodes. Rather than assert
that with an ignore, the None branch now returns _create_pending() — the
answer this plugin already has for "no usable parent", and the difference
between an AttributeError and a sensible fallback if that guard ever changes.

The other four were the familiar unnarrowed node state, in a generator that read
document.nodes[edge.source] three separate times. A checker cannot tie a kind
check on one subscript expression to a field read on another — and neither can a
reader. It is a plain loop over one bound candidate now, with the same result:
first match in edge order, or None.

Two settings come with plugins/

namespace_packages and explicit_package_bases. plugins/ is eight sibling
directories each holding a plugin.py with no __init__.py anywhere — the
layout the loader discovers by path — and without those two, mypy maps all eight
onto one module name and refuses to check any of them ("Duplicate module named
'plugin'"
). Verified they change nothing for the existing files: the run is
clean before and after.

Result

  • The gate covers 353 source files.
  • The coverage check now reports no uncovered directory and no uncovered root
    module
    .
  • Verified it still bites by injecting a deliberate type error into a plugin, a
    mutation test and api_providercaught in all three.

Test plan

  • Full suite: 3,285 passed, 20 skipped.
  • The System Prompt plugin's own 23 tests pass.
  • ruff check . clean. mypy clean across 353 files.

🤖 Generated with Claude Code

A coverage check of [tool.mypy].files against the tree found three
directories with Python in them that nothing was checking: plugins/ (8
files), mutation_tests/ (5) and tests_e2e/ (1). The gate said "339 source
files" and meant it, but "all of them" was never verified - so this checks.

mutation_tests/ and tests_e2e/ were already clean. plugins/ was not.

plugins/system_prompt/plugin.py had six errors, of two kinds:

  root = document.get_branch_root(parent_node_id)
  ... edge.target == root.id

get_branch_root returns SceneNode | None - None for an id it does not
know. It cannot be None here, because the guard fifteen lines up already
returned early for a parent_node_id that is not in document.nodes. Rather
than assert that with an ignore, the None branch now returns
_create_pending() - the answer this plugin already has for "no usable
parent", and the difference between an AttributeError and a sensible
fallback if that guard ever changes.

The other four were the familiar unnarrowed node state, in a generator
that read `document.nodes[edge.source]` three separate times. A checker
cannot tie a kind check on one subscript expression to a field read on
another, and neither can a reader. It is a plain loop over one bound
candidate now, with the same result: first match in edge order, or None.

Two mypy settings come with plugins/: namespace_packages and
explicit_package_bases. plugins/ is eight sibling directories each holding
a plugin.py with no __init__.py anywhere - the layout the loader discovers
by path - and without those two mypy maps all eight onto one module name
and refuses to check any of them. Verified they change nothing for the
existing files: the run is clean before and after.

The gate now covers 353 source files and the coverage check reports no
uncovered directory and no uncovered root module. Verified it still bites
by injecting a deliberate type error into a plugin, a mutation test and
api_provider - caught in all three.

Test plan: full suite 3,285 passed / 20 skipped; the System Prompt plugin's
own 23 tests pass. ruff clean. mypy clean across 353 files.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@dovvnloading
dovvnloading merged commit f81e378 into main Sep 5, 2026
5 checks passed
@dovvnloading
dovvnloading deleted the types/close-the-gate-gap branch September 5, 2026 04:12
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.

1 participant