Verifier: bundle index, Prev/Next, status tracking, keyboard shortcuts#55
Merged
Conversation
Adds a workflow on top of the per-bundle editor that surfaces what's been done and steps through what hasn't. INDEX page at /verifier/ (no ?bundle=). Lists every bundle in data/verifier/ with its three-state status badge (incomplete / partial / complete), page_date_raw, and last-saved timestamp. Click a row to open. Header has an "Open next page that needs work" button that jumps to the first non-complete bundle. Three-state tracking via a new top-level "status" field on corrections.json. /api/save accepts an optional "status" body parameter: complete from the client wins, but an existing on-disk complete is preserved across plain Saves (refining details on a done page doesn't downgrade it). Legacy corrections.json without status is treated as partial — they were saved, just not done. New GET /api/bundles enumerates bundles + their state for the index page and the Prev/Next nav in the editor. Malformed bundles still appear (with null metadata) so the user can spot the breakage. Editor header gets two buttons (Save, Mark complete), a status pill reflecting the current state, position indicator (3 / 5), Prev/Next buttons, and a "← All pages" back link. Save and Mark complete share the same POST path; they only differ in the status body field. Keyboard shortcuts: ⌘S save, ⌘⇧S mark complete, j/k row nav (also ⌘↓/⌘↑), ⌘D toggle delete on focused row, n/p prev/next bundle, ? toggles the overlay listing them all. Single-letter shortcuts are ignored when an input has focus so typing works normally. The bundle-input file picker is dropped — opening a bundle is now done via the index list or a direct ?bundle= URL. 10 new tests cover the status preservation rule, /api/bundles state classification (incomplete/partial/complete, legacy corrections.json, malformed bundle, verified_at surfacing, empty dir). Total 494, ruff/mypy clean.
MEDIUM findings from the review fixed: - verifier/serve.py: hoisted the function-local `from datetime import UTC, datetime` out of _bundle_state to the module-level imports, matching the convention everywhere else in the module. - verifier/app.js: moved the `open-next-incomplete` click handler attachment from showIndex (which is the rendering path) to the one-time DOMContentLoaded wiring. showIndex now just toggles the disabled state on the button. Prevents duplicate listeners if showIndex is ever re-invoked in a future refresh-without-reload flow. LOW polish: - verifier/README.md: added a "local-dev tool" warning up front. /api/save and /api/bundles have no auth or CSRF protection, server binds to 127.0.0.1 only — explicit so nobody forwards it to a shared host accidentally. - verifier/app.js: simplified `e.key === "?" || (e.shiftKey && e.key === "/")` to just `e.key === "?"`. Modern browsers emit `?` for Shift+/ consistently; the OR fallback was defensive but unnecessary. - verifier/app.js: trust the immediate /api/save response for the status pill. refreshNavFromBundleList now takes a `pillFromList` option (default true for initial load, false on the post-save refresh path) so the pill isn't overwritten from a potentially-stale cached list. Test coverage: - New test_save_then_bundles_reflects_status_round_trip exercises the save → list round trip in one test: a draft save and a complete save flip the right entries in /api/bundles, the third bundle stays incomplete. Closes the integration gap between the write path (corrections.json) and the read path (state classification). 495 tests pass, ruff/mypy clean.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
/verifier/(no?bundle=) lists every bundle indata/verifier/with a status badge (incomplete / partial / complete),page_date_raw, and last-saved timestamp. "Open next page that needs work" jumps to the first non-complete bundle."status"field oncorrections.json./api/saveaccepts optional"status"("draft" | "complete");completefrom the client wins, an existing on-diskcompleteis preserved across plain Saves so refining a done page doesn't downgrade it. Legacycorrections.jsonfiles (nostatus) are classified aspartial.GET /api/bundlesenumerates bundles + their state, used by the index page and the editor's Prev/Next nav. Malformed bundles still appear (with null metadata).3 / 5), Prev / Next buttons, and a "← All pages" link.⌘Ssave,⌘⇧Smark complete,j/krow nav,⌘Dtoggle delete,n/pprev/next bundle,?overlay. Single-letter shortcuts ignored when an input is focused.Closes #54.
Test plan
pytest -q— 494 pass (10 new for save status + /api/bundles), ruff/mypy clean./api/bundlesreturns the 5 local goldens classified correctly. After a save withstatus=complete, the relevant entry flips tocomplete./api/saveround-tripsstatusand applies the preservation rule (plain Save on a complete page keeps it complete).Notes for review
verifier/index.htmlnow has two header configurations (index vs edit) hidden/shown by JS based on?bundle=. Same file handles both modes — keeps the single-page-app feel.?bundle=URL. Drops the file-picker fallback that nobody used after the FastAPI server became the recommended path._bundle_statereads each<stem>.corrections.jsonto compute the state. For a corpus with thousands of pages, the directory walk + per-file JSON parse could get slow — at that scale we'd want a cache or sidecar index. Out of scope here.location.href = next.urlfor navigation rather than SPA-style swap. Page reload is fine for this size and avoids state-management complexity.