a73x

attach refusals are neither counted nor logged: five causes, one byte, no line

open   by a73x

`resolveSession` turns an attach away for four distinct reasons — bad name,
session table full, too small to create at, failed spawn — and its own doc
comment says only the failed spawn logs. Add the refusal that happens before
it (no free client slot, `max_clients`) and five different failures produce
one byte and zero lines.

Both refusal sites answer identically and record nothing:

- the `.attach` arm on an established connection: `queueFrame(i, .exit_status, &.{1})`
- the observer-promotion arm: `writeFrame(fd, .exit_status, &.{1})` + `dropObserver`

`Stats` counts `attaches` and has no `refusals` field.

The precedent is four lines above `attaches` in the same struct — the reason
it is a counter and not a gauge:

> a client that attaches and leaves between two samples is invisible to it

That argument transfers verbatim. `muxd stats` gauges (`clients=`,
`sessions=`) already answer "is it full RIGHT NOW"; nothing answers "was
anyone turned away since I last looked". Note this is NOT the agent-refusal
case: `agent_refused_no_offer`/`agent_refused_full` exist because an agent
refusal is invisible on the wire entirely (ssh sees a closed socket). An
attach refusal does reach the client. Only the reason and the history are
missing.

Wants: a `refusals` counter beside `attaches` (by cause, as the agent path
does), and one log line per refusal naming the cause.

Depends on 2195870d for the log half — an untimestamped refusal line cannot
be correlated with the incident that prompted the search.

Found 2026-08-23 triaging a "lost session": `mux` had printed
"attach refused or no state received (session full?)" at 12:28 and
`muxd.log` held nothing about it across the daemon's five-hour life. The
session table had to be reconstructed from /proc.