v0.68.0

feder-cr/invisible_dotsv0.68.0Sep 15, 2026by github-actions[bot]

Upgrading?

AI Summary

This release implements an automated gate to enforce dependency declarations in pyproject.toml, specifically catching missed transitive imports like pydantic and httpx. Additionally, it improves the /live/frame route logic to correctly determine service availability based on tool schema constraints rather than error sentence matching.

Key Highlights

  • Implemented an automated gate to scan for undeclared imports in pyproject.toml.
  • Fixed false positives in the dependency scan regarding optional imports and local module names.
  • Updated the /live/frame route logic to choose between 204 and 503 status codes based on tool schema validation.
  • Verified that all runtime dependencies are actually imported and scripts are called by workflows.

New Features

  • Automated dependency declaration scanning (Gate) to catch transitive dependencies.
  • Dynamic resolution of local module names for import scanning.
  • Smart exemption for optional imports (e.g., packaging.markers) inside try/except blocks.
  • Improved /live/frame route availability detection using tool schema validation.

Full Release Notes

pyproject.toml states this rule twice, in its own words, about three packages:
python-dotenv is "declared rather than inherited ... a transitive dependency is
one somebody else can drop in a minor release without telling us", and starlette
and uvicorn are "declared even though mcp already pulls both in ... the day mcp
stops needing them, the failure would otherwise land here".

It was not applied to pydantic. src/aihawk/mcp/server.py says
`from pydantic import Field` and nothing declared it: it arrives because mcp
requires pydantic>=2.11,<3. The project knew the rule, wrote it down twice, and
missed the third - which is the shape that asks for a gate on the class rather
than a fourth careful sentence.

WHY NOTHING COULD HAVE FOUND IT. Every environment that has mcp has pydantic,
so the suite is green, the six matrix jobs are green, and a clean-environment
check installing from the index is green too. The wheel is broken only in a
future that has not happened yet, and when it does the traceback names pydantic
while the cause is in somebody else's pyproject. httpx is the same defect in the
test extra: the real-server test imports it and it was arriving through mcp.

THE GATE IS ON THE CLASS. Every third-party module the package imports must be
declared, and the same question is asked of the suite against the test extra. It
was written before the fix and went red on the real state, naming pydantic and
the file that imports it, which is the only way to know a gate can fail.

AND AN IMPORT THAT DECLARES ITSELF OPTIONAL IS EXEMPT, which the same scan
taught by being wrong. Run over the two sibling packages, it raised exactly one
thing in shipped code: `from packaging.markers import Marker` in invisible_core,
which is correct - inside a function, in a try that catches ImportError and
answers "cannot tell", with a docstring saying packaging is not one of that
package's runtime dependencies. The scan was wrong, not the line, and this gate
had the same blind spot waiting for the first optional import anybody writes
here. The exemption is the SHAPE of the code, not a name on a list, and both
sides are held: guarded is left alone, the same module imported at module level
is still caught.

The first version of the gate also carried a hand-written list of local module
names and was already wrong on its first run: it missed test_web_service, which
another test imports by bare name under this suite's convention. The list is
resolved from the tree now. A list of what a directory already says is a second
declaration, which is the thing this release is about.

What it does not see is written into it: an import through importlib with a
computed name, and whether a floor is high enough, which only a resolver can
answer. pytest-asyncio is correctly declared and correctly never imported,
because a plugin is loaded by pytest rather than by code.

THE 503 BRANCH OF /live/frame IS NOW CHOSEN BY THE SERVER. That route picks
between 204 and 503 by asking whether the sentence a tool raised IS the not-open
one, and only the yes was proven end to end: a comparison that answered "not
open" to EVERYTHING passed the whole suite, which turns every real breakage into
a silently idle pane. Reaching the other side without starting a browser costs
nothing once you notice that `browser` is declared Literal["main", "support"],
so a third value is refused by the tool's own schema and comes back as an error
result that is not that sentence. Known-bad applied to the real route: the new
test fails and the 204 one stays green, which is what should happen.

WHAT WAS SCANNED AND IS CLEAN, recorded as measured negatives rather than
assumptions. The six scripts in scripts/ are all invoked by workflows and carry
no unreferenced top-level definition and no uncalled method. All eight runtime
dependencies are genuinely imported by the package.

AND ONE THING THIS AUDIT REPORTED THAT WAS FALSE. It listed mixed line endings
as remaining debt. Measured on the index: 255 of 255 text files are LF only,
zero CRLF, zero mixed, zero lines a renormalisation would rewrite. The working
tree is CRLF because core.autocrlf is true on that machine, which is a property
of a checkout and not of this repository. A claim carried over from a sibling
repo without being measured, which is the error this audit exists to find, made
by the audit.

Suite 671 green from 664.

Related Repositories