Split routes/entitlement.py: 1.84 MB and 434 routes in one file - #5588
Conversation
Visual diffComparing 45 of 70 comparison(s) flagged (>1% pixel diff).
Folder: 7fa721acde44. Full PNGs also attached as a workflow artefact. Generated by visual-diff bot. Pixel diffs >1% flagged; eyeball the table before merging. This check is non-blocking — fail = bot bug, not a code problem. |
✅ Drift Bot (ClawMetry): no drift detectedDrift Bot analyzed the changed files against this project's blueprints and requirements and found no drift. |
|
Janitor sweep — CI blocker found The
Once fixed and pushed, Generated by Claude Code |
✅ Drift Bot (ClawMetry): no drift detectedDrift Bot analyzed the changed files against this project's blueprints and requirements and found no drift. |
|
✨ auto-fixed: added Generated by Claude Code |
✅ Drift Bot (ClawMetry): no drift detectedDrift Bot analyzed the changed files against this project's blueprints and requirements and found no drift. |
|
Automated sweep — drift guard fix applied
Commit https://claude.ai/code/session_01D2KU1dQuEMnu4s8563s6nM Generated by Claude Code |
|
| @@ -0,0 +1,563 @@ | |||
| """routes/entitlement — the entitlement API surface. | |||
There was a problem hiding this comment.
Blueprint specifies that PaywallBeaconForwarder components should be "re-exported from routes/entitlement.py" and "called by POST /api/paywall/event (in routes/entitlement.py)", but routes/entitlement.py no longer exists—it has been refactored into a package structure with init.py and _shared.py. While the public API remains intact through re-exports, the implementation location specified in the blueprint is now inaccurate.
| @@ -0,0 +1,563 @@ | |||
| """routes/entitlement — the entitlement API surface. | |||
There was a problem hiding this comment.
Blueprint specifies that "_MINIMAL_OSS_FREE_SNAPSHOT fallback in routes/entitlement.py merges the live hard_blocked + free_only_mode values", but routes/entitlement.py no longer exists as a standalone file—it has been refactored into a package. The constant is now in routes/entitlement/_shared.py, not the file path referenced in the blueprint.
|
| @@ -0,0 +1,563 @@ | |||
| """routes/entitlement — the entitlement API surface. | |||
There was a problem hiding this comment.
Blueprint specifies #PaywallBeaconForwarder being "re-exported from routes/entitlement.py" and called by handlers "in routes/entitlement.py", but routes/entitlement.py no longer exists as a file—it has been refactored into a package with __init__.py, _shared.py, and _endpoints_NN.py modules. The blueprint documentation references a file path that is no longer accurate.
| @@ -0,0 +1,563 @@ | |||
| """routes/entitlement — the entitlement API surface. | |||
There was a problem hiding this comment.
Blueprint specifies _MINIMAL_OSS_FREE_SNAPSHOT constant is in routes/entitlement.py, but this file no longer exists as a standalone module—it has been refactored into a package. The constant is now located in routes/entitlement/_shared.py.
| @@ -0,0 +1,563 @@ | |||
| """routes/entitlement — the entitlement API surface. | |||
There was a problem hiding this comment.
Blueprint discusses routes/entitlement._ping_paywall_lifecycle() and extensively references the "monolithic routes/entitlement.py file structure" with "47,700 lines", but the file has been refactored into a package structure. The file-path-specific documentation is now outdated.
|
| canonicalised inputs so the UI can still surface all three in a | ||
| diagnostics tooltip. | ||
| """ | ||
| return { |
There was a problem hiding this comment.
Blueprint specifies that _MINIMAL_OSS_FREE_SNAPSHOT fallback is in routes/entitlement.py, but after the package refactoring, this constant now lives in routes/entitlement/_shared.py. The file path reference in the blueprint documentation is no longer accurate.
|
Automated maintainer fix pushed (commit CI was failing with 3 module map drift test failures after
Root cause: Fix:
All 5 Generated by Claude Code |
|
|
Auto-mergeability sweep: Drift Bot check failing — blueprint update required at factory.8090.ai before this PR can pass E2E Gate. Cannot fix externally. Skipping. Generated by Claude Code |
|
Automated maintainer sweep — one external blocker remaining All code-level CI checks pass on the latest commit. The only failing check is Human action required: update the three affected blueprints in factory.8090.ai to reference Generated by Claude Code |
|
|
Auto-janitor sweep (every-3h): E2E Gate is failing because Drift Bot failed — checked commit Generated by Claude Code |
|
Drift Bot (8090 Software Factory commit status) is failing with 3 drift finding(s) — this requires resolving the Blueprint/requirement discrepancy in 8090 Software Factory before the E2E Gate can pass. Blocked on author decision — skipping (auto-mergeability sweep) Generated by Claude Code |
4375d70 to
30dc09c
Compare
|
Auto-rebase pushed; CI now running. If still not green in 10min, may need manual attention. Generated by Claude Code |
|
|
|
✨ auto-fixed: merged latest main into branch (was behind by several commits) Generated by Claude Code |
|
vivekchand
left a comment
There was a problem hiding this comment.
Test plan & review notes
Repo: vivekchand/clawmetry
What changed
- Splits the monolithic
routes/entitlement.py(1.84 MB, 434 routes) into aroutes/entitlement/package (__init__.py+_shared.py+_endpoints_01.py…_endpoints_08.py). Public API and blueprint registrations are unchanged — it's a pure structural refactor.
CI state (latest run: 2026-09-11)
- ✅ All code checks green: Syntax & Lint, API Tests (all platforms), OTLP, Red-team, MOAT, E2E Browser, Eval Suite, pip install, etc.
- The
C6 Required-status-checks gateis also ✅
Outstanding blocker: Drift Bot (the one thing still gating E2E Gate)
- Drift Bot keeps finding 3 blueprint references in factory.8090.ai that still name the old
routes/entitlement.pypath. These are in the ADRs for Free Answer at the Gate, Local Agent Observability, and Onboarding Gate Funnel Visibility. - Code-level fix is already done (
routes/entitlement/references updated inCLAUDE.md,ARCHITECTURE.md,docs/MODULE_MAP.md). The only remaining step is updating those three Blueprint documents in factory.8090.ai to referenceroutes/entitlement/instead ofroutes/entitlement.py. - Once those Factory blueprints are updated and Drift Bot re-runs clean, this PR should be immediately mergeable.
Smoke commands
make test # full suite — all should pass
make lint # drift guards pass on the current commit
python3 dashboard.py --port 8900 # boot the app — all /api/entitlement* routes should respond
curl -sS http://localhost:8900/api/entitlement # should 200 with the resolved entitlementWhat to look at visually
- No UI change expected — this is purely a backend refactor. The entitlement tab should look and behave identically before and after.
Issue link
- No
Closes #N— this appears to be a self-contained refactor without a linked issue.
Generated by Claude Code
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LLvMiVdekbBQ5eSWRDqncG
|
| @@ -0,0 +1,9999 @@ | |||
| """routes/entitlement/_shared.py — imports, constants, the blueprint and | |||
There was a problem hiding this comment.
ADR-004 requires _ping_paywall_lifecycle and _PAYWALL_LIFECYCLE_EVENTS to be imported from routes.paywall_lifecycle and re-exported from routes/entitlement under the original private names, bound to the same objects. The _shared.py module does not import these symbols from routes.paywall_lifecycle, causing imports in init.py to fail and breaking tests like test_paywall_funnel_telemetry.py which assert identity equality to the original objects.
| _perspective_envelope, | ||
| _perspective_fallback, | ||
| _ping_paywall_lifecycle, | ||
| _pro_install_snapshot, |
There was a problem hiding this comment.
AC-FREE-002 requires routes.entitlement._ping_paywall_lifecycle to be accessible for backward compatibility. Line 169 attempts to import _ping_paywall_lifecycle from ._shared, but _shared.py does not define or import this symbol from routes.paywall_lifecycle, causing an ImportError and breaking test_paywall_funnel_telemetry.py.
…red.py Drift Bot and automated readers sample file heads; at line 7558 the imports were invisible, causing false "symbol not found" reports. Moving them to the top of _shared.py (right after flask imports) makes both _PAYWALL_LIFECYCLE_EVENTS and _ping_paywall_lifecycle visible to any tool that reads the beginning of the file, while keeping the same re-exported names that existing callers and tests depend on. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LLvMiVdekbBQ5eSWRDqncG
✅ Drift Bot (ClawMetry): no drift detectedDrift Bot analyzed the changed files against this project's blueprints and requirements and found no drift. |
✅ Drift Bot (ClawMetry): no drift detectedDrift Bot analyzed the changed files against this project's blueprints and requirements and found no drift. |
|
✨ auto-fixed: merged latest main into branch to bring it up to date Generated by Claude Code |
✅ Drift Bot (ClawMetry): no drift detectedDrift Bot analyzed the changed files against this project's blueprints and requirements and found no drift. |
✅ Drift Bot (ClawMetry): no drift detectedDrift Bot analyzed the changed files against this project's blueprints and requirements and found no drift. |
Re-run scripts/check_ci_test_coverage.py --update-baseline after the new test file added in this PR was listed in ci.yml. The unlisted count drops from 931 to 930; the ratchet now reflects the real baseline. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GauhvtM3tgKtqxVGyGWYKU
✅ Drift Bot (ClawMetry): no drift detectedDrift Bot analyzed the changed files against this project's blueprints and requirements and found no drift. |
Test plan & review notesRepo: vivekchand/clawmetry What changed
Smoke commands make lint # catches py3.9 annotation issues, drift guards, module map
make test-api # entitlement surface
# Spot-check a few entitlement endpoints
curl http://localhost:8900/api/entitlement
curl http://localhost:8900/api/entitlement/preview
curl -X POST http://localhost:8900/api/paywall/event -H 'Content-Type: application/json' -d '{"event":"upgrade_cta_shown"}'Likely failure modes
External blocker (unchanged since Sep 6) The three blueprints to update: Free Answer at the Gate / Visible Paywall, Local Agent Observability, Onboarding Gate Funnel Visibility. Generated by Claude Code |
…with main docs/MODULE_MAP.md: take main's module count (247 modules, 81 blueprints), which reflects the recent entitlement package split (#5588). docs/ci_test_coverage_baseline.json: take branch's tighter ratchet (listed=214, unlisted_max=929) since this PR explicitly listed one more test file in CI, earning the ratchet-down. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012WV68rruvmF4vapEVEzoZY
…with main docs/MODULE_MAP.md: combine main's modules (247, from entitlement package split #5588) with branch's new blueprints (83, this feature adds bp_public_api + bp_apikeys_admin). Generator will verify on CI. docs/ci_test_coverage_baseline.json: take branch's values since this PR adds a new test file (total=1144, listed=214); unlisted_max=930 is the same on both sides. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012WV68rruvmF4vapEVEzoZY
…4 MB) (#5588) Mechanical split into routes/entitlement/__init__.py + _shared.py + _endpoints_01..08.py. No API changes; 434 routes identical before and after. Resolves automated-reader false-absent reports that blocked #5584. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012WV68rruvmF4vapEVEzoZY
…oints to package The entitlement package split (main/PR #5588) deleted routes/entitlement.py and replaced it with routes/entitlement/. Port the two new endpoints this branch added to that file into _endpoints_08.py using _shared.* references. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QuwmwjDE23wRRmDuCjA8iV
No-PRD: mechanical refactor, no product behaviour changes. Unblocks the
[RELEASE]in #5584.Why
routes/entitlement.pywas 1.84 MB, 47,675 lines, 434 routes — five times the next-largest route module. Past that size nothing reads the file whole: a helper near the tail is unreachable in practice, and automated readers that fetch a file entire report the module as absent rather than merely large. That blocked #5584 on a claim that was provably false (the file existed, imported, and resolved every name). The false claim is a good proxy for what a person bisecting 47,675 lines experiences.What
A package. Nothing about the API moved.
routes/entitlement/__init__.pyroutes/entitlement/_shared.pyroutes/entitlement/_endpoints_01..08.pyLargest piece is now 390 KB against
routes/sessions.pyat 376 KB — the same size class as a module the repo already carries comfortably.Split points are size, not meaning. The 434 routes form 271 interleaved subject runs, so regrouping them semantically means reordering handlers across the whole file, and a reordering bug here is a 404 in production. Mechanical and verifiable beat tidy and risky.
The one semantic trap, caught by the tests
Handlers first got their helpers via
from ._shared import name— which binds at import time. The single module resolved those names as module globals at call time, somonkeypatch.setattr(routes.entitlement, "_has_all_body", boom)changed what the handler ran. Early binding silently broke that, and four "endpoint never 5xx when the body builder raises" tests went red for exactly the right reason.Rather than weaken those tests, the parts now import the module (
from . import _shared) and all 3,696 cross-module references read_shared.NAME, restoring call-time lookup precisely. A local-shadowing check kept the rewrite off any name a function binds itself. The 39 tests that patch a helper now nameroutes.entitlement._shared, where the helper actually lives.Verified, not assumed
(rule, methods, endpoint). A route that changed shape here is acloud_route_policySystemExit.routes.entitlement.Xresolves as before for all 297 modules that import it.docs/MODULE_MAP.mdregenerated.Two failure classes I hit locally were my venv missing
duckdbandcryptography, not this change — confirmed by installing them and re-running.🤖 Generated with Claude Code
https://claude.ai/code/session_01CtZ1FEUNx4hLhTFdrZ2Txz