Skip to content

Stop the endpoint page reporting things that are not true - #41

Merged
botre merged 6 commits into
masterfrom
fix/endpoint-page-behaviour
Aug 8, 2026
Merged

Stop the endpoint page reporting things that are not true#41
botre merged 6 commits into
masterfrom
fix/endpoint-page-behaviour

Conversation

@botre

@botre botre commented Aug 8, 2026

Copy link
Copy Markdown
Collaborator

Acts on the critique of the endpoint page, which scored 13/40. The visual system held up well; the problems were in behaviour and in the same control existing in eight spellings.

Three defects that each broke the task

Timestamps froze. formatTimeAgo was evaluated once when a card rendered and never again, with no timer anywhere. Measured on a tab open nineteen minutes: the card read "2 minutes ago" for a request 1132 seconds old. On the most-read field of a page designed to sit open for four hours. Absolute clock time is now the value, the relative age is the aside, and it refreshes on an interval.

A dropped WebSocket was silent and terminal. close and error went to console.debug and nothing else: no reconnect, no state in the UI. A four-hour tab will lose its socket to sleep or a proxy timeout, and the page then looked identical to an idle endpoint, so the user concludes their integration is broken and debugs the wrong system. It now reconnects with backoff to a 30s ceiling, refetches on reopen so traffic sent while it was down is not missed, and shows Live / Reconnecting / Disconnected.

One panel served four situations and three of them were lies. The empty state keyed on visibleRequests.length === 0 regardless of cause, so 38 captured requests plus a non-matching search rendered "Waiting for requests... will appear here in real time". It also broke the product's own Dashed-Means-Waiting Rule. There are now three states, and the dashed border is reserved for genuinely empty.

Plus the send panel, which displayed GET and sent POST because x-for options reset the select after the model bound, and which dropped header lines without a colon in silence while reporting success. In a tool whose value is telling you the truth about HTTP, both are worse than having no control.

Components are extracted

Ten classes in src/styles/components.css now carry roughly 86 call sites. Two things this fixes beyond tidiness:

  • Two nominally identical secondary buttons rendered 38px and 34px because one carried py-2 and the other py-1.5. That drift is what produced visibly misaligned controls.
  • Focus was two incompatible systems: 11 sites on :focus-visible with a 2px ring, 8 on :focus (which also fires on a mouse click) with a 1px ring, and 6 interactive elements with no focus style at all, every one of them inside the card that repeats once per request. It is one system now, on :focus-visible throughout, keeping the two documented ring weights.

Accessibility

The page had zero headings and zero live regions, so heading navigation yielded nothing and arrivals were announced to nobody (SC 4.1.3). It now has an h1, real heading structure, and a polite live region for arrivals, copies, sends and deletions. The method filter and search input had no programmatic name; seven buttons shared the name "Copy". Four controls failed the 24x24 target minimum at 16x16, all inside the repeating card. The single contrast failure, the italic "None" at 2.63:1, is now 4.76:1.

Performance

The list renders 25 cards with a Show more control rather than every captured request (38 requests is now ~2,200 DOM nodes; 128 was 13,558 with a 336ms stall on every filter change). Highlighting caps at 40k characters: a 322KB body previously cost ~164ms of highlight plus ~372ms of insert on the main thread and produced 63,805 spans, to colour text inside a window showing 0.14% of it.

Also

Delete all confirms inline and names its scope, since it clears the endpoint while its neighbour copies only the filtered view and both said "all". Copy feedback lands on the button that was clicked. Query string gained the Copy button its siblings had. The send panel gained the missing path and query field.

Design record

Fixed an unquoted colon in the frontmatter description that made the whole YAML block unparseable, introduced when em dashes were removed from prose in the previous branch. Corrected the claims this branch invalidated, and recorded that the component system is implemented rather than only described.

Verification

go vet, gofmt, go build, unit tests and prettier --check . clean. Playwright 39/39.

Not addressed

The card is still a 523px dossier per request, which was a P1 in the critique: four repeating labelled regions per card, ~110 of them containing only the word "None" at the 128 cap, and 1.7 cards visible at once against a North Star that promises an arrivals board. Deliberately left alone for now.

Also still open, from the landing-page review: POST /endpoint has no CSRF protection, and / ships with no security headers while every other route has them.

🤖 Generated with Claude Code

https://claude.ai/code/session_01X8JeP7AQwpd2coSfwbMopt

botre added 5 commits August 8, 2026 13:43
Eight button strings across one template shared no definitions, so two
nominally identical secondary buttons rendered 4px apart because one carried
py-2 and the other py-1.5. Ten field call sites had four spellings; the label
fragment had three different bottom margins.

Focus was two systems: eleven sites used focus-visible with a 2px ring, eight
used focus, which also fires on a mouse click, with a 1px ring, and six
interactive elements had no focus style at all and fell through to the browser
default. All of them were inside the card that repeats once per request. It is
one system now, on focus-visible throughout, keeping the two documented ring
weights.

The value is not brevity. It is that a token can no longer drift in one call
site and not the others.

Claude-Session: https://claude.ai/code/session_01X8JeP7AQwpd2coSfwbMopt
Three defects each broke the task outright.

Relative timestamps were evaluated once when a card rendered and never again,
so a tab open for nineteen minutes still read "2 minutes ago" for a request
that was 1132 seconds old, across the whole four-hour retention window. The
absolute clock time is now the value and the relative age is the aside, and it
refreshes on an interval.

A dropped WebSocket was handled with console.debug and nothing else: no
reconnect, no state anywhere in the UI. A page designed to sit open for hours
will lose its socket, and the user then sees a page identical to an idle
endpoint and concludes their integration is broken. It reconnects with backoff
now, refetches on reopen so traffic sent while it was down is not missed, and
says which of the three states it is in.

The method select was populated with x-for, so the browser reset it to index 0
after the model bound and the panel displayed GET while sending POST. Header
lines without a colon were dropped silently and still reported success, so a
mistyped Authorization header vanished without a word.

Copy feedback also landed on the endpoint URL button no matter which of the
hundred-odd copy buttons was clicked, and a body large enough to matter was
highlighted synchronously with no cap, costing roughly half a second of blocked
main thread and tens of thousands of nodes to colour text nobody reads.

Claude-Session: https://claude.ai/code/session_01X8JeP7AQwpd2coSfwbMopt
Four new tests: the delete confirmation and its cancel path, a filtered-empty
stream not claiming to be waiting, a malformed header line being reported
rather than dropped, and the landing page shipping no scripts.

One existing assertion changed because it encoded the defect. It filled the
search box with a non-matching term and expected "Waiting for requests", which
is exactly the state that lies about the user's own traffic.

Claude-Session: https://claude.ai/code/session_01X8JeP7AQwpd2coSfwbMopt
The frontmatter description carried an unquoted colon, left behind when em
dashes were removed from prose, which made the whole YAML block unparseable.
That block is the machine-readable layer of the file, so nothing could read it.

Prose claims this branch invalidated are corrected: the card header carries the
requested path rather than a UUID, an absent value uses label ink because the
lighter step measured 2.63:1, and text-only buttons carry their own padding and
focus ring. Components now records that the system is implemented in
src/styles/components.css rather than only described.

Claude-Session: https://claude.ai/code/session_01X8JeP7AQwpd2coSfwbMopt
Critique snapshots are local design-tooling output with no bearing on the
build, so they are ignored rather than versioned; the one already committed is
untracked here and stays on disk.

public/app.css is generated and minified onto a single line, which renders as a
33KB wall in every diff that touches a class name. Marking it generated
collapses it in review and keeps it out of language statistics. It stays
committed: that is what lets the binary, the container and `go run` work
without Node.

Claude-Session: https://claude.ai/code/session_01X8JeP7AQwpd2coSfwbMopt
@botre
botre force-pushed the fix/endpoint-page-behaviour branch from 3443793 to 0466540 Compare August 8, 2026 11:47
Tailwind v4 walks the whole repository by default, on top of any explicit
@source directives, and mines every non-ignored file for class-like strings.
Prose that merely discusses a class name therefore shipped it: the stylesheet
carried rules harvested from a design write-up and from DESIGN.md, and CI
failed the freshness check because ignoring one directory silently changed the
build output.

source(none) turns the automatic pass off, so the sheet is built from the
templates and the scripts that hold the method-badge palette and nothing else.
It is deterministic now: the same sources produce the same file regardless of
what else is in the working tree.

Output drops from 33,875 to 31,480 bytes. Verified in a browser that every
badge colour, the connection dot, and the button and panel geometry still
resolve.

Claude-Session: https://claude.ai/code/session_01X8JeP7AQwpd2coSfwbMopt
@botre
botre merged commit c0c2313 into master Aug 8, 2026
3 checks passed
@botre
botre deleted the fix/endpoint-page-behaviour branch August 8, 2026 11:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant