CR round 3 on #212: the detail page never started polling - #218
Merged
Merged
Conversation
The section reaches a browser by four routes, not three: the deactivate swap, the Replicate now swap, the poll - and the detail page itself. Only the three swaps went through the renderer that supplies `poll`, so a full page load rendered the section with no polling context at all. That is archiver#212's own defect surviving on the most ordinary path to this screen. Click Replicate now, press F5 before it settles, and the outcome is back to requiring a manual reload. Arriving at the item from the list while a command is in flight behaves the same way. The RepSpec twin routed all four of its sites from the start, so the two screens disagreed - and the one that was wrong is the one the operator actually used. One context builder now serves every site here too. Two tests pin it: the page polls with a command open, and does not with nothing open - a page that always polled would be worse than one that never did. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Both templates read `{% if poll is defined and poll.active %}`. Under Jinja's
default undefined a missing `poll` is falsy, so a render site that forgot the
key produced a section which silently never polled - and that is exactly how CR
12 shipped green: every test exercised a path that passed the key, and the one
path without it degraded instead of failing.
A defensive guard that turns a missing contract into the original defect is
worse than no guard. Every site supplies the keys now, so the templates say so.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…needs Both headers declared their required context and stopped short of `poll` and `poll_interval_seconds`. That comment is the only statement of what a caller must supply, and it is the document CR 12 would have been checked against - the detail page omitted both keys and nothing said it was wrong. The hub's note also records why the `is defined` guard is gone, since that is the part a future reader is most likely to want to reinstate. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Both poll fragments returned a plain 200 with no cache headers. Heuristic caching has little to work with here - no Last-Modified, no Expires - so the present risk is low, but a fragment fetched every two seconds is the one response in this app that must never be served from a cache: a stale body would freeze the section on a state that reads as authoritative, which is #212's defect wearing a different hat. One header removes the class. On the GETs only. The swap responses are POST and DELETE, which are not cacheable by method. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The paragraph was duplicated verbatim across both partials, with the comment explaining why it says what it says in only one of them. The two screens render the same section from the same live_poll decision, so they have to describe "open, no longer watching" identically - and the copy without the reasoning beside it is the one a later edit would reword. Lives in _macros.html beside replication_state, which the same two screens already share for the same reason. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Every caller passes a mapping keyed by assignment ULID; `object` accepted anything and documented nothing, in a module whose other signatures are exact. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This was referenced Sep 11, 2026
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.
Round-3 review of
88d3dfd..a8ab532(the #212 work). Six findings applied, one held.poll— a full page load with an open command was inert508711b{% if poll is defined %}turned the missing key into silent non-polling620a54cpoll/poll_interval_seconds5f63556Cache-Controlon a fragment fetched every 2s66723c6df85c72Mapping[object, ReplicationCommand]where keys are ULIDs1aea43bFinding 12: #212's own defect survived on the most ordinary path
The section reaches a browser by four routes, not three: the deactivate swap, the Replicate now swap, the poll — and the detail page itself. Only the three swaps went through the renderer that supplies
poll, so a full page load rendered the section with no polling context at all.Click Replicate now, press F5 before it settles, and the outcome is back to requiring a manual reload. Arriving at the item from the list while a command is in flight behaves the same. The RepSpec twin routed all four of its sites from the start, so the two screens disagreed — and the broken one is the screen the operator actually used.
Two tests pin it: the page polls with a command open, and does not with nothing open.
Finding 13 is why it shipped green
{% if poll is defined and poll.active %}— under Jinja's default undefined, a missingpollis falsy. Every test exercised a path that passed the key, and the one path without it degraded silently instead of failing. A defensive guard that converts a missing contract into the original defect is worse than no guard.Finding 18, held
A poll firing while a Replicate now POST is in flight gives two swaps racing for one target; a poll response landing after the POST's would briefly show pre-issuance state. It self-corrects within one interval and both renders are server-authoritative, so the impact is a flicker.
hx-syncis the obvious lever, but which element declares it and which strategy gives the right precedence — the poll must lose to the click, never the reverse — needs verifying in a browser rather than reasoning about. Not applied under a blanket directive.Verification
ruff checkandruff format --checkclean over 440 files; 1791 passed, 2 skipped, 3 deselected. Five net-new tests.🤖 Generated with Claude Code