v0.4.2
Annotator reliability fixes and security-hardened local agent
Annotator (in-app PDF preview)
- Page renders no longer get stuck blank. A race between the resize observer and the page-render task could leave a freshly-displayed page completely transparent, while the viewer still thought it was rendered. Toggling fullscreen or split view, then jumping to a specific page (e.g. page 5 of a 30-page submission) was the most common way to hit it. The render result is now discarded when the underlying canvas has been detached or replaced during the render, and the next observer pass paints the live canvas correctly.
- Sidebar Delete always asks first. Clicking the red Delete button on an annotation now always swaps the action row to a
Delete?/Cancelconfirm pair before any destructive request goes out. The previous build only mounted the confirm when an older.btn-groupancestor was present; on the current stacked sidebar markup a single accidental click was destroying annotations with no warning. - Sidebar Revert-to-AI now talks to the server with the right id. The orange “revert” button on annotations that started as AI and were edited by a human now resolves the annotation’s stable server identifier before sending the request. Previously it sent a composite token that the server rejected silently, so the button looked broken even though the endpoint itself was healthy.
- Drag-to-move on the PDF now transfers ownership AI → Human in-place — the marker recolours, the sidebar card jumps to the Comments column, and a Revert-to-AI affordance appears on the row. No modal reopen required.
- Split view is fullscreen-only. Exiting fullscreen drops split mode immediately; reopening the modal defaults to single-panel.
- Narrow Comments column no longer overlaps actions. Meta-row (verdict pill, source badge, author, priority dots) wraps cleanly above the Edit / Revert / Delete actions at every reachable sidebar width.
Local agent v0.3.7
- Loopback Host header enforcement on every route — closes the DNS-rebinding gap where a hostile origin could reach the local agent through an attacker-controlled hostname that resolves to
127.0.0.1. - Pairing PIN no longer leaks to non-interactive stdout. Tray notification + OS clipboard still hand the PIN to the operator; daemonised launches and captured stdout logs do not receive it any more.
- Pairing flow hardening.
POST /pair/initiaterefuses to overwrite an active challenge;POST /pair/completekeeps the challenge alive on pre-PIN validation failures; failure detail collapses to a singlePairing failed; repeated bad-PIN attempts trigger a temporary lockout. - Request-body cap on JSON endpoints prevents authenticated memory-exhaustion against the results, assignment, grading-bundle, and annotation CRUD routes.
- File-IO reliability fixes — randomized temp filenames with cleanup on failure, incremental SHA-256 hashing on PDF downloads, nanosecond-mtime cache freshness for annotated PDFs, and PDF-space round-trip of annotation rects.
The agent download is available from the docs page; existing paired tokens stay valid across the 0.3.6 → 0.3.7 upgrade.