Repository navigation
fix: P3 follow-ups from the #203/#204/#206 reviews (#208) - #214
Conversation
A failed lookupMissingNode (schema drift on inactive_nodes) answers the bare 404 without a trace in the log. Pin that it is logged exactly once for repeated requests, carries the error but not the requested key, and that a request cancelled by its client is not logged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
writeNodeNotFound still answers the bare 404 when lookupMissingNode fails, but now logs the error: the first failure at once, later ones at most every 10 minutes with the number suppressed in between (the log-first-then-interval shape of schema_wait.go). A request its client cancelled is not logged, and the requested key stays out of the line. Read-only: no new query, no new write. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The last advert date also appears in the explanation sentence, so a card without the "Last advert" row (mutant M8) passed both the unit test and the E2E. Both now read the card's <dt>/<dd> rows and assert the "Last advert" row shows the inactive row's last_seen. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…208) An observer can be uploading right now while its node row is still in inactive_nodes (retired before #203, or while the observer was quiet), so "this device is inactive" can contradict the "Last upload as observer" row next to it. Pin the wording to the record instead. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…208) "No advert heard since <date>; this device is inactive" becomes "...; this node is listed as inactive", which stays true while the observer of the same key is uploading. The observer's current status is already on the card as the "Last upload as observer" row. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…#208) No test focused an element inside the modal, so dropping the overlay.contains(el) check in focusInLayerAbove (mutant M4) survived: the position:fixed .modal-overlay is then taken for a layer drawn over the modal and Escape on its close button does nothing. Add a unit case and an E2E step for the close and copy-link buttons. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
#208) A tab switch away from Hash Issues now drops its byte-size selector (bytes=) and section link (section=) keys, like the other tabs' keys. Hash Issues itself is unchanged: it reads and writes both as before. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…208) The #206 audit missed the All / Confirmed / Suspected / Unknown filter of the Multi-Byte Hash Adopters card. Pin the #194/#205 pattern for it as mbf=: a URL value selects and filters, an unknown or hostile value is All and is dropped from the URL, All keeps the URL as it is, nothing goes to sessionStorage, and leaving the tab drops the key. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…bf= (#208) The All / Confirmed / Suspected / Unknown filter of the Multi-Byte Hash Adopters card now follows the #194/#205 pattern: renderHashSizes reads mbf= on every render through restoreViewParams, a click writes it with setViewParam, a value is only compared with === against the allowed list (never put in a selector or markup), an unknown one is All, and All keeps the URL as it was. Leaving the tab drops mbf=. URL only: a spec without a storageKey now skips sessionStorage, so a plain visit still opens on All as before. The column sort of the same table stays local (see the PR): it is a one-shot DOM reorder with no state to link. The test now derives each fixture adopter's status the way the card does (capability row by pubkey, else unknown) instead of assuming it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
In Chromium against the fixture server: a cold load of ?tab=hashsizes&mbf=confirmed selects Confirmed; a click writes mbf=, a reload keeps it, All drops it and nothing is stored; a tab switch drops it; a hostile value is All without a page error. The click path is not reachable from the vm unit test (the card wires its handler in a setTimeout), so this is what kills a click that does not write the URL. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…iew (#208) A default view leaves its key out of the URL and a missing key falls back to sessionStorage, so Back to #/analytics?tab=scopes after a later entry stored sub=regions opens Regions, not that entry's Overview (and the same for Wardriving's window). The vm's fake history now keeps a state, and new entries and Back/Forward are modelled. Also pinned: a new entry without the key still opens the stored value, a tab switch inside an entry still brings it back, a garbled entry state never reaches the view, and other history.state keys are kept. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…one (#208) restoreViewParams leaves a default out of the URL and fell back to sessionStorage for a missing key, so Back to #/analytics?tab=scopes after a later entry stored sub=regions opened Regions and rewrote that entry's URL. _writeViewParams now also records the resolved values in the entry's history.state (analyticsView, other state keys kept), and restoreViewParams uses that record before sessionStorage when the URL has no value. A new entry has no record, so a plain visit still opens the stored view; a tab switch writes a null state, so clicking a tab back in the same entry also still does. URLs are unchanged, and replaceState only runs when the URL or the record changes. E2E: real Back/Forward in Chromium for Scopes and Wardriving; Regions has no window buttons, and expectScopes now waits for a given window. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…1914), drops section= (#208) CI's Kpa-clawbot#1914 E2E (test-issue-1306-collisions-terminology-e2e.js) pins that Hash Issues -> Hash Stats -> Hash Issues keeps the chosen byte size: bytes= has no stored fallback, so the URL is where the tab remembers it. Dropping it on a tab switch (249351f) broke that. Only section=, a one-shot scroll anchor, belongs to the tab's dropped keys. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Narrows 249351f: TAB_URL_PARAMS.collisions is ['section']. bytes= stays in the URL across a tab switch, as Kpa-clawbot#1914 intends (no stored fallback), and the comment now says why it is not listed, which was the mismatch the #208 review flagged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Rapport — CS-pve-agent3 PR#214 #208 — head 0f77c97Status: All 7 items are handled. Items 1, 3 and 5 are fixed, items 2 and 4 close their test gaps, item 6 deep-links the filter and keeps the sort local with a recorded reason, and item 7 drops Evidence tags: [T] test or run output, [K] checked in code or diff, [A] assessment or inference. Items
Note on item 7. The first fix ( Tests
CI (run 37194646048, head
|
| Job | Result |
|---|---|
| Go Build & Test | pass [T] |
| Playwright E2E Tests | pass [T] |
| Build & Publish Docker Image | pass [T]; a local image build only, no push |
| Release Artifacts / Deploy Staging / Publish Badges & Summary | skipped (PR) [T] |
The previous run on 0022501b (37191941496) failed on the Kpa-clawbot#1914 E2E step described above.
Remaining
- [A] The Hash Stats adopters column sort has the
colIdxoff-by-one and no state. Fixing it is a behaviour change outside follow-ups from #203, #204 and #206 reviews (P3: test gaps, inactive-card wording, history defaults, Hash Stats audit) #208 and fits a separate issue. - [A] Item 5 relies on
_updateAnalyticsUrlwriting anullstate on a tab switch, so a tab clicked back within the same entry keeps the stored fallback. This is pinned by the unit test "a tab switch inside an entry still brings back the stored view". - [A] The branch has 15 commits, test then fix per item, including the item-7 narrowing as follow-up commits (no amend or force-push).
Review — CS-Minimax PR#214 review-followups — head 0f77c97Dom: APPROVE med nits Independent, read-only review of head
Evidence tags: [T] run here, [A] analysis of the source, [K] taken from the author's report or CI, not re-run. Findings
Item by item1.
2.
3. Card wording
4. Escape inside the View Path modal
5. Back/forward in Analytics
6. Hash Stats
7.
Proposed follow-up issue (not created)
Cross-cutting checks
Tests
MutantsEach mutant was applied to a copy of the merged tree. The E2Es ran against a fresh server on that copy, stopped by its port-owner pid after each mutant.
The author's M1a–M7b were not re-run. [K] Not verified
The head was Generated by Claude Code |
Relates to #208
P3 follow-ups from the reviews of #203, #204 and #206. Each item is fixed with a test that fails before and passes after (or pins a test gap with a mutant that survived before), except the Hash Stats column sort in item 6, which stays local for the reason below. One test commit followed by one fix commit per item; items 2 and 4 are test-only gaps.
Items
writeNodeNotFoundlogs a failedlookupMissingNode: the first at once, then at most once per 10 min with the count suppressed in between (log-first-then-interval, as inschema_wait.go). A client-cancelled request is not logged; the requested key is not in the line. Still the bare 404.TestNodeDetail404LookupErrorIsLoggedOnce(schema drift:inactive_nodeswithoutrole; 3 requests → 1 line),TestNodeDetail404CancelledLookupIsNotLogged,TestMissingNodeLookupLogThrottle<→<=: all killed<dt>/<dd>rows and assert that theLast advertrow shows the inactivelast_seen.test-issue-199-missing-node.js,test-issue-199-inactive-observer-e2e.jsLast upload as observerrow.#packetPathCloseand#packetPathCopyLink, then presses Escape.test-packet-path-map.js,test-issue-180-packets-url-modal-e2e.jsoverlay.contains(el)removed) survived before; killed now by unit and E2E_writeViewParamsalso records the resolved values in the entry'shistory.state(analyticsView, other keys kept). Without a URL value,restoreViewParamsuses that record beforesessionStorage. URLs are unchanged.test-analytics-subtab-deeplinks-205.js(Back/Forward modelled in the vm, Scopes and Wardriving), E2E with realgoBack/goForwardin Chromiummbf=(all/confirmed/suspected/unknown), URL only. The column sort stays local (reason below).TAB_URL_PARAMS, M6c URL ignored, M6d raw URL value used unvalidated: all killedsection);bytesreasonedTAB_URL_PARAMS.collisions = ['section'], and the comment now says whybytesis not listed (see below)bytes=2§ion=…gives#/analytics?tab=topology&bytes=2&window=24h; a Hash Issues → Hash Stats → Hash Issues round-trip keepsbytes=2sectionnot listed), M7b (byteslisted again): both killed by unit; M7b also by the Kpa-clawbot#1914 E2EItem 5: how history entries are told apart
A default view leaves its key out of the URL, so the URL alone cannot tell "this entry was the default" from "a plain visit of the tab". The entry's
history.statecan:location.hash = …) has no state, so it still opens the stored value, as analytics: deep-link inner views (Scopes sub-tabs + window, audit other tabs' local view state) #205 intends._updateAnalyticsUrl) already writes anullstate, so clicking a tab back within the same entry still brings the stored view back.resolveViewParam: a value is compared with===against the allowed list, and an unknown one gives the default. A garbled or foreign state never reaches the view.replaceStateruns only when the URL or the record changes, so re-renders do not add calls (Safari throttles them).Item 7: why
bytes=stays across a tab switchMy first fix (
249351f1) listed both keys, and CI'stest-issue-1306-collisions-terminology-e2e.jsfailed: step "Kpa-clawbot#1914: time-window, tab and theme refreshes retain the chosen byte size" pins that Hash Issues → Hash Stats → Hash Issues keepsbytes=2. Hash Issues has no stored fallback for the byte size, so the URL is where it is remembered across a tab switch.0f77c974narrows the fix:section=is a one-shot scroll anchor and is now dropped when leaving the tab.bytes=stays in the URL, as Allow to link to Mesh Analytics with a path hash byte size set via url Kpa-clawbot/CoreScope#1914 intends.TAB_URL_PARAMScomment says whybytesis not listed, which was the mismatch the review flagged.Item 6: why the column sort stays local
colIdxmap is off by one against the six columns: Role is missing, Status sorts the Role cells, Hash Size sorts the Status cells, Adverts sorts the Hash Size cells, and Last Seen sorts the Adverts cells. Linking it now would put a broken order into shared links. Listed under follow-ups below.The filter follows the #194/#205 pattern:
renderHashSizesreadsmbf=throughrestoreViewParamson every render, and a click writes it withsetViewParam. The value is never put into a selector or markup, an unknown value gives All, and All keeps the URL as it was. The filter is not stored insessionStorage, because it had no stored state before; a spec without astorageKeynow skips storage, so a plain visit still opens on All.Performance
time.Now(). There are no new queries.history.stateobject (a few keys).replaceStateis only called on a change.Invariants
cmd/serverstays read-only: no new query and no write.map[string]interface{}; the throttle is a named struct.scripts/check-xss-sinks.sh --diffis clean.var(--x, #fallback)values; onlyclasschanged.Tests run locally
cd cmd/server && go test -timeout 20m ./...: ok (1172 s, run alone)cd cmd/ingestor && go test -timeout 60m ./...: ok (883 s; untouched by this PR. Two earlier local runs hit the 10 and 20 min timeouts while sharing the CPU, with no failed test)sh test-all.sh: 214 files passed, 0 failednode test-frontend-helpers.js: 707 passed, 0 failedtest-fixtures/e2e-fixture.db, prepared as in CI (freshen, inline seed,corescope-migrate,seed-2073,seed-199):test-issue-199-inactive-observer-e2e.js: 3/3test-issue-180-packets-url-modal-e2e.js: 12/12test-issue-205-analytics-subtab-deeplinks-e2e.js: 15/15, three runsanalytics.js, the two new Back/Forward steps fail.test-issue-1306-collisions-terminology-e2e.js: 23/23 after0f77c974(it failed on249351f1locally and in CI)test-channel-proposals-e2e.js, which starts its own server and ingestor): 122 passed. The 5 failures were "no packets row" and timeouts from a fixture freshened two hours earlier. With a re-freshened fixture all 5 pass, on this branch and onorigin/master.Follow-ups (not in this PR)
colIdxoff-by-one above, and its lack of state. Fixing it is a behaviour change, so it should get its own issue.🤖 Generated with Claude Code