feat(cl2k): show which save locations the Poster Healer keeps up to date - #467
Conversation
The healer follows the CL2K maker's save locations automatically — it stores no drive config of its own — but nothing surfaced that, so the only way to know what was covered was to read config.yml. Add GET /api/poster-self-heal/coverage and a read-only panel on the review page listing every scanned folder and Drive with the types it handles. Coverage is derived from the module's own local_dirs_for/drive_twins rather than re-deriving it in the API layer, because the heal's scope is WIDER than the maker's routing: a location claiming no types saves nothing but is still scanned. Filtering on types (as the maker's own summary does) would under-report. Drives show the types that actually resolve to them, so a fallback target reports what it really receives rather than what it claims. Also warns when no Drive receives a type, since those files heal locally only and anyone syncing the Drive keeps the stale names. local_dirs_for is extracted from run() so the endpoint and the scan cannot drift.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (2)
🚧 Files skipped from review as they are similar to previous changes (1)
📝 WalkthroughWalkthroughAdds a live poster self-heal coverage endpoint and displays folder, Drive, routed-type, and unrouted-type data on the review page. Refactors local-folder extraction into a reusable helper and adds backend and resolver tests. ChangesPoster self-heal coverage
Estimated code review effort: 3 (Moderate) | ~20 minutes Sequence Diagram(s)sequenceDiagram
participant PosterHealReviewPage
participant posterSelfHealAPI
participant CoverageEndpoint
participant CL2KConfiguration
PosterHealReviewPage->>posterSelfHealAPI: Request coverage
posterSelfHealAPI->>CoverageEndpoint: GET /poster-self-heal/coverage
CoverageEndpoint->>CL2KConfiguration: Read live configuration
CL2KConfiguration-->>CoverageEndpoint: Folders and Drive routes
CoverageEndpoint-->>posterSelfHealAPI: Coverage JSON
posterSelfHealAPI-->>PosterHealReviewPage: Display coverage data
Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@backend/api/poster_self_heal.py`:
- Around line 83-91: Update local_dirs_for() so folders contains at most one row
per path, matching the deduplicated scanned set and scanned_count. Track emitted
paths while iterating local_folders or otherwise construct folders from unique
scanned paths, preserving the existing normalized name, path, and types fields.
- Around line 68-71: Update the configuration-loading path in the endpoint
around load_config() to catch ConfigError and return a configuration-unavailable
response with HTTP status 503, instead of allowing the global handler to produce
500. Preserve the existing cl2k_maker availability response for successfully
loaded configurations.
In `@frontend/src/pages/poster/PosterHealReviewPage.jsx`:
- Around line 77-90: Update the PosterHealReviewPage coverage effect to create
an AbortController, pass its signal through posterSelfHealAPI.coverage(options),
and abort it during cleanup while retaining the active state-update guard.
Extend apiCore.makeRequest to combine the caller-provided signal with its
timeout signal instead of replacing it, and ensure posterSelfHealAPI.coverage
forwards its options.
In `@tests/test_api_poster_self_heal_coverage.py`:
- Around line 130-138: Update test_coverage_rereads_config_each_request to have
load_config return a replaceable holder, then assign a new _Cfg instance
containing the added BGS drive before the second request instead of mutating
cfg.cl2k_maker.gdrive_uploads. Keep the existing first-request and
second-request assertions to verify the handler reads the replacement
configuration.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 20734b36-bb81-4748-910a-635820b51fe2
📒 Files selected for processing (6)
backend/api/poster_self_heal.pybackend/modules/poster_self_heal.pyfrontend/src/pages/poster/PosterHealReviewPage.jsxfrontend/src/utils/api/posterSelfHeal.jstests/test_api_poster_self_heal_coverage.pytests/test_poster_self_heal_resolver.py
Two local_folders rows sharing a path are ONE scanned location, but the response emitted a row each — so folders could disagree with scanned_count, and the panel keys on path. Emit the first claimer only. Also replace the config object rather than mutating it in the reload test: a handler that captured the config once would have passed the mutation check, since it would be holding the very list under mutation.
Summary
The Poster Healer already follows the CL2K maker's save locations automatically —
PosterSelfHealConfigdeliberately has no drive fields, andrun()readscl2k_maker.local_folders/.gdrive_uploadslive on every run. But nothing surfaced that, so the only way to know what was actually covered was to readconfig.yml.This adds
GET /api/poster-self-heal/coverageand a read-only panel on the review page listing every scanned folder and Drive with the types it handles, plus a warning when a type reaches no Drive at all.Related issue
N/A
Type of change
Purely additive: one new GET, one new panel, no config schema change, no change to what the heal does.
The subtlety worth reviewing
The heal's scope is wider than the maker's routing, and the panel reports the former:
local_dirs_for()takes everylocal_foldersentry with a non-emptypathand ignorestypes. A row claiming no types saves nothing but is still scanned — and still healed.drive_twins()collects everygdrive_uploadsentry with a non-emptyfolder_id;typesonly decide which Drive receives which type, with a poster-Drive then first-Drive fallback.cl2k_maker's ownupload-statussummary filters onand f.types. Copying that predicate here would have hidden a folder the healer really scans, so the panel would have lied. Instead the endpoint calls the module's own helpers, andlocal_dirs_foris extracted out ofrun()so the two can't drift.Drives report
heals_types— the types that actually resolve to them viatwin_of— not the claimedtypes, so a fallback target shows what it genuinely receives.Testing
ruff check .— cleanpython -m pytest— 1571 passed (9 new)npm run lint,npx prettier --check "src/**/*.{js,jsx,css,json,md}"(whole tree),npx stylelint "src/**/*.css"— all cleanNew backend coverage in
tests/test_api_poster_self_heal_coverage.py: the folder/Drive listing, that atypes: []folder is still listed, Drive dedupe + blank-id skipping,heals_typesreflecting fallback rather than claims,unrouted_typeswhen no Drive exists, an absentcl2k_makerreturning an empty payload instead of a 500, and that config is re-read per request so a settings save needs no restart. Plus two unit tests pinninglocal_dirs_for's ordering/dedupe and its deliberate ignoring oftypes.Rendered against a real four-way routed config, the payload is:
Screenshots
Not captured — the panel is a plain surface card in the existing page column (title + count pill + "Edit in Module Settings →", then one row per location with a mono path/ID and type pills). It follows the existing
ReviewRow/ConfigBanneridiom rather than introducing new primitives.Checklist
backend/util/config.py) — N/A, reads existingcl2k_makerfieldscd frontend && npm run build)DAPSreferences introducedmainonlyNotes for review: the endpoint is config-only and makes no rclone calls —
list_files()was deliberately avoided so a transient Drive/token failure can't make the panel silently report zero coverage.load_config()is called inside the handler, never bound at import, so coverage is never stale. All six touched files are develop-only, so the branch-isolation invariant holds.Summary by CodeRabbit
New Features
Bug Fixes