Both CodeRabbit findings on the merge commit, verified against the code first.
Major, plugins.py: the stale-findings filter derived its own notion of "in
config" and "on disk", and both were looser than the reconciliation module's.
set(load_config()) also contains system keys, the secrets-file keys load_config()
merges in, and non-dict values; and any directory holding a manifest.json
counted as installed even when that manifest does not parse. Either looseness
clears a finding that is still true -- and a secrets key read as a plugin is the
precise bug the filter exists to stop reporting, so reintroducing that asymmetry
while re-checking was the wrong way round.
The two extractions now live in state_reconciliation.py as config_plugin_ids()
and disk_plugin_ids(), with ignored_config_keys() and secrets_top_level_keys()
alongside. _get_config_state() and _get_disk_state() use them too, so there is
one definition rather than two that can drift. _get_disk_state() re-reads each
manifest for version/name after taking membership from the shared extractor;
that costs one extra small read per plugin on a path that runs once per boot.
Minor, the new test: the fixture assigned api_v3.config_manager and
api_v3.plugin_manager directly. Those live on a module-level blueprint
singleton, so the mocks leaked into every later test that imports api_v3 --
pointing at a tmp_path already deleted. Both now go through monkeypatch.setattr,
which restores them. This is the same pollution class that made an earlier test
in this session break seven unrelated ones, so it is worth getting right.
Five cases added for the parity itself: a secrets key, a system key and a
non-dict value must not clear an "installed but missing from config" finding,
and neither an unparseable manifest nor a .standalone-backup- directory may
count as installed. All five fail against the looser version.
Linux CI on the preceding commit: Core unit tests, plugin harness, CodeQL and
CodeRabbit all pass. Codacy reads action_required on every commit of this
branch including the first, so it is pre-existing and not from this work.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
main modified web_interface/blueprints/api_v3.py while this branch deletes it,
so git reports a modify/delete conflict. Resolving that by accepting the
deletion - the obvious move - would silently discard every api_v3.py change
merged since this branch forked. Three were:
#560 the sudo diagnosis on /system/action -> api_v3/system.py
#557 the stale-reconciliation-findings filter -> api_v3/plugins.py
#550 the deletion of dead _validate_time_range -> api_v3/__init__.py
All three were verified absent from the split beforehand: _sudo_hint_for,
_SUDO_NEEDS_PASSWORD and _drop_stale_reconciliation_findings appeared in zero
files, while _validate_time_range was still sitting at __init__.py:343 after
#550 removed it from main. Each is now ported to the module that owns the
affected route, and the dead function is gone.
Two further changes this surfaced:
- api_v3/__init__.py imported fcntl at module scope. fcntl is POSIX-only, the
monolithic blueprint never imported it, and on this branch it made the whole
api_v3 package unimportable off POSIX: the pristine branch cannot run its
test suite on Windows at all - 14 collection errors interrupt the run. The
import now happens inside _starlark_manifest_lock(), the only thing that uses
it. Deliberately NOT degraded to a no-op lock off POSIX; that function's
docstring describes a real lost-update race, and silently not locking would
be worse than failing loudly on a platform that cannot drive the display.
- test/test_reconciliation_status_endpoint.py is new. #557's endpoint wiring had
no test - only the pure still_unresolved() helper in src/ was covered, and
that lives outside web_interface so it survived the split untouched. Losing
the filter during this merge would therefore have been completely silent.
Removing the filter call now fails three of its seven cases.
Suite on this merge: 115 failed / 4317 passed. main today: 105 / 4252. All 11
extra failures are in test_starlark_pixlet_routes.py and need POSIX file locks,
so they are Windows-only; on that same file the pristine branch shows 19 failed
plus 39 errors, so this is a large net improvement rather than a regression.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
web_interface/blueprints/api_v3.py held 111 routes, 56 helpers and 181
functions in one module -- 9% of the core by line count and three times the
next largest file. It becomes a package of nine route modules grouped by path
segment, plus __init__.py for the shared imports, constants, Blueprint and
helpers.
Every route module decorates the SAME api_v3 Blueprint object, so endpoint
names stay api_v3.<function>, the URL map is unchanged and app.py is untouched.
Verified: 111 routes before, 111 after, byte-identical rules, endpoints and
methods, and every endpoint still on the one blueprint.
plugins 3,867 config 1,178 starlark 692 system 619
fonts 452 misc 398 wifi 361 display 326 backup 212
__init__ 1,787 (imports, constants, Blueprint, 56 helpers)
Two things the URL-map check could not catch, both found by running the suite:
1. PROJECT_ROOT = Path(__file__).parent.parent.parent. Moving the code one
directory deeper made that resolve to web_interface/ instead of the project
root. Nothing failed at import; it surfaced as ~110 tests failing with 404s
and "installation script not found", because every path built from it was
one level too shallow. Now parents[3], and test_api_v3_url_map.py asserts
PROJECT_ROOT/run.py exists so the next move cannot repeat it.
2. Module-attribute patching. Tests do
monkeypatch.setattr(api_v3_module, "_BACKUP_EXPORT_DIR", ...) and a route
module that binds such a name by value never sees the patch. The shared code
therefore stays in __init__.py rather than moving to a _common submodule --
it has to live on the module the tests patch -- and the eleven names tests
patch are read back through the package (_pkg.X) instead of bound by value.
Those eleven were found by AST-scanning every setattr in the test tree, not
by guessing; "time" is among them, used to drive a fake clock through the
second-resolution credential-backup filenames.
Test changes are confined to what genuinely moved: patch targets that now name
the owning route module, imports of helpers, and six tests that scan the api_v3
source as a file and now read the package directory.
Full suite: 4,278 passed, 68 skipped, 0 failed.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014RRtqXDCnvnY6EQwhT5CV9