a31cc5d3 widened the jwt-only census to app/plugins and shipped a ceiling
of 611 measured on a dev box. CI measured 591 and went red: most
app/plugins/ live copies are GITIGNORED, so the count depended on which
extensions a machine happened to have installed. That is the same
machine-dependence the builtin-copy drift census exists to prevent, and
this walked straight into it.
Count builtin-extensions/ instead -- the source, pinned equal to the live
copy by test_builtin_extension_drift.py, and tracked. Filter to
git-tracked files as well, because extractions leave stale ignored
directories behind (cloudflare-ops, git, localkit after plan 52) that
would otherwise be counted on some working copies but not others.
Stable population: 608 (35 of them extension routes across
cloud-provision, remote-access and status). Also regenerates
docs/MIGRATION_INVENTORY.md, whose row is derived from this ceiling, and
points the CLAUDE.md / AGENTS.md notes at the scope actually scanned.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
PR #122 review suggested converting the serverkit-status routes from
@jwt_required() + an inline is_admin check to rbac decorators. Declined:
plan 76's recorded decision (2026-08-19) is that JWT-only is the
deliberate default, because every rbac decorator authenticates through
auth_required() and therefore GRANTS API-key callers access -- an
expansion of the authenticated surface, never a mechanical sweep.
Two gaps let that suggestion look uncontroversial:
1. jwt_only_census.py scanned only app/api, so the extension's 15 bare
@jwt_required() routes were invisible to the ratchet. It now scans
app/plugins as well; ceiling 577 -> 611 for the newly visible
population (a scope widening, not a regression). Only the live copy
is counted -- builtin-extensions/ is the same code twice and the
drift guard pins them equal.
2. Neither CLAUDE.md nor AGENTS.md said the inline shape is already a
valid explicit decision (test_route_authz_static counts .is_admin via
ATTRIBUTE_GATES), or that ~91 routes across 21 files use it. Both now
do, with the plan 76 quote and a note that automated reviewers raise
this routinely.
Audited 2026-08-31 by AST over app/api + app/plugins + builtin-extensions.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Milestone A's open question is decided: JWT-only stays the deliberate
default. Converting a route to auth_required() grants API-key access, so
it happens one route at a time when a use case exists, in a commit that
says so — never as a sweep. The new census/ceiling (605) enforces both
directions: no new bare @jwt_required() routes, and no silent
conversions. The identity ratchet's third assertion keeps each deliberate
conversion safe.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>