Provider problem — where does it show?

Three conditions (key rejected, can’t reach the provider, out of credit) and three places they could surface. The Mac app, Tahoe chrome, light/dark follows the system. The title-bar pill also exists as a DEBUG-only native prototype (Diagnostics ▸ Provider problem pill), because toolbar placement and resizing can only be judged native.

Artefact — what the app would draw Commentary — not the product Frame bars flag proposed wording that is not a shipped locale key

AOne pill in the title bar

The existing out-of-credit pill, generalised. Same envelope, same .status zone; the dot colour and label change with the condition.

key rejectedlabel proposed
project-ikea
Claude · invalid key
Your Claude key was rejected.

Add a new key in Settings.

Switch provider…Open Settings…
network blocks itwording proposed
project-ikea
Network · can’t reach Claude
This network can’t reach Claude.

Try a different network.

out of creditshipped
project-ikea
Claude · no credit
Claude is out of credit

Add credit to run new analysis or coding.

Switch provider…Add funds…

The person who tags by hand for months — same project, same dead key:

A · key rejected, nothing has asked for it
project-ikea163 Quotes
Projects
project-ikea
fishkeeping
pricing
trust
A′ · after an edit needed a call and it was refusedlabel proposed
project-ikea163 Quotes
Claude · invalid key
Projects
project-ikea
fishkeeping
pricing
trust
Commentary · A
  • The trigger decides whether this is useful or a nag. Today the out-of-credit pill lights from a sticky verdict (Settings validation or a failed run) and stays until a top-up and a successful run. For the hand-tagger that is a pill present for months over work that never needed the provider. A′ is the alternative: the pill appears when a call was refused, not when a verdict is merely known.
  • The glyph is the existing MessageKind, not a new one. Rejected key is error (xmark.circle.fill, red; .invalid is red everywhere else); credit and unreachable are warning (exclamationmark.triangle.fill, amber). The words say which. This replaces the amber dot the shipped out-of-credit pill uses, a documented decision to reverse.
  • The diagnosis decides who the sentence is about. Offline: no pill. The internet answers and this provider doesn’t: the subject is the network (“This network can’t reach Claude”), and the remedy is a different network. The provider answers badly: the subject is Claude (“Claude is unavailable. Try again shortly.”, the shipped apiServer wording split in two). “Check your connection” is right for none of the last two. If the app can’t tell which, it shows no pill.
  • “Unavailable” has no source of truth. It is a failed observation: the sticky cache the pill reads deliberately doesn’t store it. A pill for it needs new state, and it would sit lit for anyone on a train.
  • Visible on Welcome and in Settings; one fix clears every project at once.

BSidebar footer, bottom left decided against · owner, 2 Nov 2026

Kept for the record. Next to the version and the bug. The footer is plain secondary text by design (SidebarFooter.swift: not a capsule, not selectable).

key rejectedwording proposed
project-ikea
fishkeeping
0.44.0 betaClaude key rejected🐞︎
can’t reachwording proposed
project-ikea
fishkeeping
0.44.0 betaClaude unavailable🐞︎
out of creditwording proposed
project-ikea
fishkeeping
0.44.0 betaClaude · no credit🐞︎
B · sidebar collapsed (⌥⌘S)
project-ikea163 Quotes
pricing
trust
B · App Store build, beta word gonewording proposed
project-ikea
fishkeeping
Claude key rejected🐞︎
Commentary · B
  • It is the quiet standing marker. Small, secondary, never competing with the work. For a dead key that is allowed to sit for months, that is arguably the right volume.
  • It disappears with the sidebar. Hide the sidebar and the condition is gone; the first frame in the second row. Fine for a quiet marker, poor for something blocking.
  • The footer is a sidebar-width strip. At the 200 pt minimum, version + status + bug leaves about 100 pt, so the status truncates (the first row is already tight).
  • Neighbours. It sits beside Send Feedback and the beta word. The latter is compile-time and goes before the App Store build, which leaves status + bug alone. The code comments also say the plain-text footer was chosen because a capsule read as one of the app’s clickable status pills; a status here has no popover to open, so the fix path is Settings only.

CNothing global — the consequence stays where the call was made

Shipped today for runs: the row says “Run failed”, its popover says why. Three conditions, same row.

key rejectedshipped
✕
project-ikeaRun failed
fishkeeping
Run failed

Your Claude key was rejected.

Category: Provider key issue

can’t reachshipped
✕
project-ikeaRun failed
fishkeeping
Run failed

Couldn’t reach Claude. Check your connection.

Category: Network error

out of creditshipped
✕
project-ikeaRun failed
fishkeeping
Run failed

Claude is out of credit.

Category: Out of credit

The incidental call has no row to land on. The Signals lens, one card whose refresh was refused:

C′ · in-place consequence at the point of neednew element · wording proposed
Not updated — Claude key rejected
Commentary · C
  • Zero nag, and it matches the hand-tagger. A dead key shows nothing until something actually needs it. No chrome to maintain, no state to clear.
  • Coverage stops at the analyse row. The row exists for runs. A signal-card update or a transcript edit that needs “a few cents” has no row, so a refused call is silent unless it grows its own in-place note (C′). That is a new element on each surface that makes such calls, and I haven’t audited how many there are or which report a refusal over the bridge. The only failure the app currently turns into state is a billing one.
  • One fix, many failures. With N projects a dead key is N “Run failed” rows, each found by trying. The row also says only “Run failed”; the cause is one click away.
  • It finds out late. Someone who drops in eight recordings learns their key is dead when the run starts, not before.

DThe forms the pill can take — loud once, then it recedes

Out of credit, drawn with the shipped wording. The same pill in four stages, each a title bar with the toolbar's own spacing. The proposed treatments are flagged in each bar.

1 · first refusaltreatment proposed
ikea
Claude · no credit
Claude is out of credit
2 · settled, ✕ hides it✕ proposed
ikea
Claude · no credit✕
3 · a day later, bare glyphno capsule · proposed
ikea
Claude · no credit
4 · clicked, expandedtwo buttons, as shipped
ikea
Claude is out of credit

Add credit to run new analysis or coding.

Switch provider…Add funds…

Same glyph at every stage, and for every condition of the same kind. Only the words drop away:

expanded → collapsed, per conditionno-credit wording shipped · the others proposed
Claude · Invalid key→ Claude · Unavailable→ Claude · no credit→

The traffic-light question, at toolbar scale. The collapsed pill sits where .status puts it (the centre, on macOS 26), with the window controls at the leading edge:

collapsed warning
project-ikea
collapsed error
project-ikea
Commentary · what the HIG says (fetched 2 Nov 2026 from Apple’s DocC JSON)
IdeaHIGReads as
Loud once, quiet afterFeedback: match the significance of the information to the way it’s delivered; “display status information in a passive way so that people can view it when they need it”.Supports a passive pill. Prominence belongs to the first occurrence and in place, not to a permanent loud chrome.
Where the first telling goesWriting: show an error “as close to the problem as possible”, avoid blame. Alerts: avoid an alert for information that isn’t actionable; Mail’s answer to an unavailable server is “an indicator that people can choose to learn more”.Stage 1 is the in-place note plus the full-label pill. Not an alert, not a modal. An alert only if a researcher-started action was blocked outright.
Popover as the resolve surfacePopovers: “Avoid using a popover to show a warning. People can miss a popover or accidentally close it.” A Close button only “for confirmation and guidance”.A mismatch with what ships. The out-of-credit popover carries a status and two actions, not a data-loss warning, which is the HIG’s case for an alert. Fine if the first telling is in place (stage 1); not fine if the popover is the only telling.
Expanding a condensed viewPopovers: for “condensed and expanded views of the same information”, animate the change so it doesn’t read as a new popover.Stages 3 → 4 is that case. Animate it, and honour Reduce Motion.
A day, as a timerAccessibility: “Minimize use of time-boxed interface elements… Prefer dismissing views with an explicit action.” Offering help: tips at “once every 24 hours” (a cadence, not a collapse).A live timer changing the toolbar while someone looks at it is the thing to avoid. Evaluate the 24 hours at launch and on window activation, never mid-glance. Nothing is removed by collapsing, so the content stays one click away.
Glyph-onlyButtons: macOS shows a tooltip on hover; tooltips 60–75 characters, “context-sensitive… different text for a control’s different states”.The collapsed pill needs a state-specific tooltip; its accessibility label stays the full text. The glyph is the kind’s own and doesn’t change on collapse, so a collapsed pill is recognisably yesterday’s.
Same colour, different meaningColor: “Avoid using the same color to mean different things.”An amber or red disc in the title bar echoes the minimise and close controls, which is why the lamp works in a Settings list and not here. A triangle isn’t a disc. The red error glyph is a disc, but inside a capsule it isn’t the bare window control; check it at real size.
Don’t look like a countNotifications: “Avoid creating a custom image or component that mimics the appearance or behavior of a badge.”The coloured dot sits on the glyph; no number, ever.
Reachable when hiddenToolbars: “Make every toolbar item available as a command in the menu bar… people can customize or hide” the toolbar. Reduce tinted toolbar controls.The HIG-conformant answer is a menu twin so the condition can’t be lost to a hidden toolbar or the » overflow. Declined by the owner (micro-interaction on a small flag); the cost is accepted, see the decision below. The stage-1 tint is the one place this runs against the grain.
Footer, option BSidebars: “Avoid putting critical information or actions at the bottom of a sidebar. People often relocate a window in a way that hides its bottom edge.”Counts against B directly.
Commentary · how it behaves, and the calls that are yours
  • No ✕ convention for a toolbar pill in the HIG. The closest are system notification banners and tip popovers, which both give people a dismiss and say to “make it easy to dismiss”. A trailing ✕ is a reasonable invention; it is not a precedent.
  • Decided (owner, 2 Nov 2026): ✕ is the dismiss and hides the notice entirely; no Hide button in the popover; no menu-bar twin. The ✕ is a well-known affordance (Safari’s tab close button is the comparison), and a micro-interaction on a small flag belongs on the surface, not in the menu bar, which is for discovering general commands. Both reviewers asked for a menu twin so a hidden toolbar or the » overflow can’t lose the condition. That cost is accepted: the Settings dot still says it, and a new refused call or a changed condition brings the pill back. The VoiceOver “Hide” action on the main button stays, so VoiceOver users don’t need to find the ✕. The collapsed glyph is bare, with no capsule, and has no ✕ of its own yet.
  • Decided (owner, 2 Nov 2026): the ✕ shows on any mouseover into the pill, like Safari’s tabs, and on keyboard focus; its space is reserved. Reserving it means the pill never resizes under the pointer, which in the centred .status zone would also move the glyph and its neighbours. Two layouts are in the native prototype for comparison: slot on the right only (lopsided at rest) and slot balanced on both sides (about 20 pt wider). Two crosses in the error state are accepted (“just unfortunate”: there is no stronger red failure mark and no better-understood close mark). With hover-reveal they are on screen together only while hovering or focused.
  • ✕ has to be a separate hit target. StatusPill is one Button that toggles the popover, so a nested ✕ needs its own target beside it. Right-click “Dismiss” isn’t available on a toolbar item (macOS swallows it for Customize Toolbar; already a gotcha in desktop/CLAUDE.md). So the accessible dismiss is the Hide button in the popover (fourth frame), with ✕ as the pointer shortcut. A hover-only ✕ would be unreachable by keyboard and VoiceOver.
  • Dismissed doesn’t mean forgotten. ✕ removes the pill; the Settings dot still says “Out of credit”, and the next refused call re-lights it.
  • When does it come back loud? The risk in your own rule: a month of transcript edits on empty credit means a month of refused calls, and if each one re-lights the full pill, that is the nag again. Suggested rule: a refusal re-expands the pill only when the condition changed (credit → key rejected) or the call was an explicit AI action such as Analyse or Autocode. An incidental refusal on an already-acknowledged condition gets its in-place note and nothing in the title bar.
  • Clearing. A successful call, a re-validated key, or a top-up removes it at any stage. App-wide state, so hiding it in one window hides it in all.
  • Not checked. The exact SF Symbols (the glyphs here are drawn to suggest them), the ✕’s hit-target size, and how a glyph-only item behaves in macOS 26 toolbar overflow.

EEverything we can measure, and what each failure says

The rule from before: the pill needs a diagnosis, the diagnosis needs measurements, and the sentence’s subject follows the diagnosis. Measured on this Mac on 2 Nov 2026 with a read-only probe (URLSession, Network framework), healthy network only. Everything not marked measured is expected behaviour and untested.

Commentary · what we can measure
SignalHowWhat it tells apartMeasured hereLimits
Network pathNWPathMonitor: status, supportsDNS, interfaces, expensive/constrained. No traffic, always on.Offline vs “some route”.satisfied · dns true · en0/wifi“Satisfied” means a route exists, not that the internet answers (router up, WAN down still reads satisfied). The offline and Wi-Fi-off states are unmeasured.
VPN interfaceThe same path’s interface list (utun, “other”).Whether a VPN is up; a dropped one.none presentNo VPN to test with. A dropped VPN looks like offline or a block, so it falls through to “can’t tell”.
System proxyCFNetworkCopySystemProxySettingsA managed network (proxy or PAC configured).emptySays what the network is likely to do, not what it did.
URLError codeThe failed call itself.DNS vs refused vs timeout vs TLS.−1003 DNS · −1004 refused · −1001 timeout (4.0 s, blackhole address) · −1202 untrusted and expired certificateA black-holed route and a slow provider are both −1001. Expired and untrusted certificates are the same code. −1009 and −1005 (offline, connection lost) unmeasured.
“Reached the provider” signatureAn unauthenticated GET to the provider’s models endpoint; no key and no user data is sent.Reached the provider vs reached something else (a proxy block page).Anthropic: 401, application/json, headers request-id server cf-ray, body {"type":"error","error":{"type":"authentication_error"…, 140 msProves reach, says nothing about the key (401 either way). Only Anthropic measured; ChatGPT, Gemini and Azure signatures unmeasured. A proxy block page is unmeasured.
Control probeOne HTTPS GET to Apple’s captive-portal check, only after a failure.Internet works, versus captive portal (a login page instead), versus nothing.captive.apple.com and www.apple.com/library/test/success.html: 200, body “Success”, 38–48 ms, over HTTPSOne request to Apple, carrying an IP address and nothing else. Over HTTPS it can’t see a captive portal: a portal can’t present a valid certificate, so the reply arrives as a TLS error, the same as a wrong clock or an inspecting proxy. Apple’s own check is plain HTTP, which the app’s default transport rules block; the project has no exception for it.
DNS answersResolve the provider host and the control host, compare.Sinkholed (null or private address) vs no DNS at all.not measuredWorth adding; a filtering DNS usually answers with an address that isn’t the provider’s.
Provider status and error typeHTTP status and the JSON error.type.Key rejected, credit, rate limit, outage.401 / authentication_error only402, 429 and 5xx/529 are unmeasured here. Today a failed run is classified by matching stderr, which turns every network failure into one category.
HistoryWhen the provider last answered (every LLM call is already logged).A blip vs a standing block.not measured“Worked until 14:20” is also better copy than a bare state.
Task metrics (phase times)URLSessionTaskMetricsWhere a request died (DNS, connect, TLS).empty for all four failures, even with a waitDropped. It would have separated a slow provider from a black-holed route; it can’t.
Commentary · every version of failure, and the two lines
#VersionRecognised byPill?Line 1 (bold) · line 2 (regular)
1OfflinePath unsatisfiedNo. In place only, if a call was refused.No network connection.Try again when you’re back online.line 1 shipped phrase · line 2 proposed
2Router up, internet downPath satisfied; control probe fails (DNS or timeout)No. In place only.No internet connection.Check your network.proposed
3Captive portal only on evidenceControl probe over HTTP answers, but not with “Success” (login page or redirect). Needs a transport-rules exception for Apple’s host; without it this row can’t be told from row 4 and falls to 13No. In place only.This network needs you to sign in.Open a web page to sign in, then try again.proposed
4Wrong date or time on the MacControl probe and provider both fail TLS (−1202)No. In place only.Your Mac’s date or time looks wrong.Set it automatically in System Settings.proposed; a certificate check fails for everything
5Network blocks the provider (filtered DNS, firewall)Control OK; provider DNS fails or resolves to a null/private address, or connection refused/reset, or no answerYes, warningThis network can’t reach Claude.Try a different network.proposed
6Network inspects secure trafficControl OK; provider fails TLS (−1202 or −1200)Yes, warningThis network can’t make a secure connection to Claude.Try a different network.proposed
7A proxy answers instead of the providerControl OK; HTTP reply that isn’t the provider’s (HTML, 403/407/502, no request-id)Yes, warningThis network is blocking Claude.Try a different network.proposed
8Provider outageProvider signature present, status 5xx or overloadedIn place; pill only if it persists (your call)Claude is unavailable.Try again shortly.shipped sentence, split in two
9Rate limitedProvider signature, 429No. In place only.Claude rate limit reached.Try again soon.shipped sentence, split in two
10No answer, nothing else knownControl OK; timeout (−1001)No, until it repeats (then 5)Claude didn’t respond.Try again shortly.proposed; the code can’t tell this from a black-holed route
11Key rejectedProvider signature, 401/403, authentication_error on a call that sent a keyYes, errorYour Claude key was rejected.Add a new key in Settings.line 1 shipped · line 2 proposed
12Out of creditProvider signature, 402 or the credit-balance bodyYes, warningClaude is out of creditAdd credit to run new analysis or coding.shipped
13Can’t tellAnything not matched aboveNo. In place only.Couldn’t reach Claude.Try again in a moment.proposed

Reading it. The pill is for 5, 6, 7, 11 and 12, plus 8 if you want a persistent outage shown. Rows 5–7 are three ways to detect one condition and say the same words, so they differ only in the details the log keeps. Rows 1–4, 9, 10 and 13 are about the person’s own situation, a transient limit or an unknown, so they are said where it happened and never become a pill. A VPN that dropped can’t be told from rows 2 or 5, so it lands in 13 and says nothing it can’t support.

Remedies assume nothing about who the person is. No line says “ask your administrator”: this could be a café, a plane or a hotel, and for them a different network or a different provider is the only lever. Where another provider is known to work, the pull-down (“Switch to…”) is the action under line 2. For someone who does have an administrator, the popover can still offer a quiet Copy details, as the run-failure popover already does.

Not yet measured, and how to get it. Rows 1, 2 and 3 need the real states: Wi-Fi off, a router with the WAN unplugged, a captive-portal network. Rows 4–7 can be induced locally: a wrong clock (revert it after), a blocking rule in a firewall tool or a null address in the hosts file for the provider, and a local proxy that re-signs TLS or returns a 403 page. Rows 8 and 9 need a stub that answers 529 and 429 with the provider’s JSON. For each, record the URLError code, the HTTP status and headers, the control-probe result and the path state. I can write that as one script.

FThe ladder — which layer is it?

Two lines say the first layer that fails. Under them, one click down, the layers: what passes, what fails, and what was never checked because something beneath it failed. Each frame says whether it earns a pill. The same message kinds as everywhere else (✓ passes, ⚠ or ✗ fails, — not checked).

offline · no pill, said in placewording proposed
No network connection.

Try again when you’re back online.

Connected to network
Internet
Claude reachable
Claude responding
Your account
connected, no internet · no pill (my reading)wording proposed
No internet connection.

Check your network.

Connected to network
Internet
Claude reachable
Claude responding
Your account
a sign-in page was seen · extra rung only now · pill: your callwording proposed
This network needs you to sign in.

Open a web page to sign in, then try again.

Connected to network
Signed in to network
Internet
Claude reachable
Claude responding
Your account
sees the internet, not Claude · pillwording proposed
This network can’t reach Claude.

Try a different network.

Connected to network
Internet
Claude reachable
Claude responding
Your account
reaches Claude, Claude is failing · pill if it persistsshipped sentence, split
Claude is unavailable.

Try again shortly.

Connected to network
Internet
Claude reachable
Claude responding
Your account
Claude replied: your key was rejected · pillline 2 proposed
Your Claude key was rejected.

Add a new key in Settings.

Connected to network
Internet
Claude reachable
Claude responding
Your account
Claude replied: no credit · pillshipped
Claude is out of credit

Add credit to run new analysis or coding.

Connected to network
Internet
Claude reachable
Claude responding
Your account
Commentary · the ladder
RungWhat measures itMeasured hereHonest limitDeliverable?
Connected to networkNetwork path status and interfacessatisfied · en0/wifiSays a link exists. Unsatisfied is measured nowhere yet (Wi-Fi off needs a real toggle).Yes. Core of the API; the offline state itself is untested.
Internet (not “Router and internet”)The control probe answers at all; DNS resolves a neutral hostanswers; DNS okThe router can’t be shown separately. A sandboxed app has no ping or traceroute, so “router fine, ISP down” and “router down” both read as one rung. A gateway check (connect to the router on a port it answers) might split them; unmeasured, and I wouldn’t promise it.Internet: yes. Router: no. Draw one rung, and don’t claim the router is fine or broken.
Claude reachableThe keyless request to the provider returns its JSON error signature401 + request-id, 140 msProves a path to the provider, not that the key works. Anthropic only; the others are unmeasured.Yes for Claude. Each other provider needs its own signature measured; Azure’s host is the user’s own, and Ollama is local.
Claude respondingA normal reply in the provider’s own format. A 401, 402 or 429 with its JSON body is a normal reply; a 5xx, an overloaded error or no reply is not.401 JSON reply (no key)5xx and overloaded replies are unmeasured. Fails only when Claude itself is failing, which is why it is separate from the next rung.Yes, from the refused call’s own status. No extra probe.
Your accountWhether Claude accepted who is asking: key valid, credit left, not rate-limited.401 only (no key sent)402, 429 and a valid-key call are unmeasured. Credit exhaustion shows only on a billed call, so it comes from the failure and is never probed. Claude answered in all of these; it said no.Yes, from the refused call. Not checked when a rung beneath it failed.
  • The last rung was mislabelled and is now two. “Claude answering” read as “is Claude alive?”, so a ⚠ on it said Claude was silent, when in the credit and key cases Claude replied and refused. “Claude responding” fails only when Claude itself is failing (an outage); “Your account” fails when Claude replied and said no (key, credit, rate limit). The ladder grows to five rows, six when a sign-in page was seen; if that is too tall for something meant to be subtle, “Connected” and “Internet” can merge, since line 1 already says which of the two it is.
  • The sign-in rung exists only when a sign-in page was actually seen. At home or at work there is no such page, so nobody’s ladder has that row; it is inserted for the café case (five bars, nothing loads, the sign-in screen hidden somewhere). macOS’s own assistant handles the first sign-in; this is for when it didn’t appear or the portal wants you again. What counts as “seen”: a plain-HTTP request to Apple’s check page that returns anything but “Success” (a login page or a redirect). Over HTTPS a portal can’t be told from a wrong clock or an inspecting proxy (all one TLS error), so it is no evidence, and with no evidence the row stays out. That means a two-step probe: HTTPS first, then HTTP only if the first fails with a TLS error, so cleartext traffic is rare. It needs a narrow transport-rules exception for Apple’s host in the app’s Info.plist (the project already merges a partial plist for the OAuth keys). Untested on a real portal.
  • It answers “whose side is it on?” without asking anyone. Everything up to “Internet” is the person’s situation, “Claude reachable” is the network between them and the provider, “Claude responding” is whether the provider itself is working, and “Your account” is whether it will take this request. That’s the distinction you’re asking for; the two lines name the rung, the ladder shows the evidence for it.
  • Below a failure is “not checked”, never “fine”. If the router and internet fail, claiming Claude is reachable would be a guess. This is what the skipped kind is for.
  • Default closed. The popover opens on the two lines. The ladder sits behind a disclosure (“Connection details”, proposed wording), so the quick case stays quick and nobody is asked to read a diagnostic they didn’t want. Popovers should hold “a small amount of information”.
  • It can live where checks already live. Diagnostics ▸ Check Health is a native list of pass/fail checks, and a “Connection” section there would be the same ladder run on demand. I haven’t read that window’s code for network checks.
  • It costs requests the pill doesn’t. The pill needs a diagnosis only after a refused call. The ladder adds the control probe, a DNS check and the keyless provider request, run once at that moment, never on a timer.

GWhere in the title bar — the pill as the first trailing item proposal · not built

The owner’s layout, reading from the right edge: Search, then the actions group, then the status pill(s) on their own. Drawn here only to agree the order; whether it holds is a native question (see the commentary underneath). Nothing in the toolbar code has been changed.

Quotes · wide window · macOS 26artefact
Ikea study16 Sessions Claude · no credit ☰▤⬆ ⌕ Search
same, project lens (no left-panel toggle, no Search)
Ikea study Claude · no credit ⬆
Welcome window (no project: no actions group, no Search)
Bristlenose Claude · key rejected
two pills (rare; the plan is to merge them later)
Ikea study Claude · key rejected Whisper model… ☰▤⬆⌕
narrowing: today every equal-priority item folds from the right, so the order of loss is Search, then the actions, then the pill, unless priorities are set
Ikea study Claude · no credit»
The same row on macOS 15 · Sequoiaartefact
Ikea study Claude · no credit ☰  ▤  ⬆⌕ Search

No shared glass on 15: items are plain buttons in a row, so there is no capsule to fuse into and no spacer type to separate them.

Commentary · what is known, what is not
QuestionAnswerSource
Where does .status put an item on macOS?The centre of the toolbar. Informational, “not an action”. It folds into » like anything else.Apple DocC ToolbarItemPlacement.status; measured 3 Oct 2026 (project notes)
Where does .primaryAction put one?Apple’s page says the leading edge on macOS. The app uses it for the trailing actions and that is what is measured on 26. This disagrees with the docs and I have not resolved it. I would not rely on either text for 15; it needs looking at on a Sequoia guest.DocC primaryAction vs desktop/CLAUDE.md (Toolbar)
Can a SwiftUI item say “fold me last”?No. visibilityPriority exists on NSToolbarItem only; the app sets it through the ToolbarItemPriority probe, re-applied on resize and every rebuild. Search already uses it (.user).desktop/CLAUDE.md gotcha; QuotesToolbarControls.swift
Separating the pill from the actions capsuleOn 26, ToolbarSpacer(.fixed) between them (the app already does this before Search, searchSeparator), plus withoutSharedBackground() on the pill so the glass does not draw a second capsule round it. On 15 the spacer type does not exist and the question does not arise.DocC ToolbarSpacer (26.0+); house helper in StatusPill.swift
HIG limits that bear on thisAim for at most three groups (pill, actions, search is exactly three); trailing edge “remain visible at all window sizes”; the system adds the overflow menu, do not add one; avoid layouts that overflow by default.HIG Toolbars
A pill that hides itselfWhen there is no problem the item is empty. An empty ToolbarItem can still leave a gap or a stray capsule, so “only there when there is a problem” may need the item to be added and removed from the builder rather than draw nothing. That is a toolbar rebuild, and a rebuild is what loses the priority probe.Not measured. Reasoned from the rebuild gotcha
Does the pill fuse with the actions capsule?Unknown. Adjacent items share a glass capsule unless split; whether a hidden-background item next to a capsule stays out of it, I have not seen.Not measured
Will the pill survive a narrow window?Not in the draw above: it folds first-from-the-right, so it would outlast Search and the actions only by sitting left of them, which is the opposite of “important enough to keep”. If it should outlast them, it needs a priority above theirs; if it should go first, it is already right.Reasoned; my read of the fold rule

What I have not done. No native measurement: I could not open a window from this shell, and the guests were not up. Nothing here has been seen on a Sequoia or a Tahoe toolbar. Before any build I would want, on the 15.7.3 and 26.6.2 guests and on 27: (1) where .primaryAction and .status land, (2) the order items fold at 940 pt and below, (3) the empty-pill gap, (4) fuse or no fuse beside the actions capsule.

Decision for you. The centre (today) is safe and measured, but it is the place that folds into » and sits under the title. The trailing slot is where you want it and is the more fragile one. A DEBUG switch in Diagnostics that moves only the pill between the two would let you judge it on this machine without changing the shipping toolbar.

HThe network-blocks pill — when it lights, when it clears, when it never does approved by owner, 7 Nov 2026 · built

Key rejected and out of credit are facts about the account, so they stay until the account changes. “This network can’t reach Claude” is a fact about where you are: it is true in the café and false at home. This section draws that difference. It adds no new control; the pill, popover, ✕ and bare glyph are the ones in A and D.

1 · a call was refused, herewording from E
project-ikea
Network · can’t reach Claude
This network can’t reach Claude.

Try a different network.

Switch provider…
2 · the Mac joins a different networkclears by itself
project-ikea

No pill, no “resolved” message. The network path changed, so the old finding is not evidence about this one. Nothing is asked of you.

3 · or the next call to Claude worksclears by itself
project-ikea

Same: a successful call removes it, whether or not the network changed. The ✕ is not needed to get rid of it, only to hide it sooner.

Settings ▸ LLM Provider, while 1 is litunchanged
Claude ● Online

The key is fine and stays green. A network block says nothing about the key, so it is never written to the key’s saved verdict.

Ollama refused a callno network pill
project-ikea

Ollama is on this Mac, so the network is not the cause. It says what it says today (“Ollama isn’t running…”, llmSettings.validation.ollamaUnreachable), in place.

Azure OpenAI: the endpoint doesn’t resolveno network pill · wording proposed
project-ikea

Couldn’t reach Azure OpenAI. Check the endpoint in Settings. Apple’s host answered, and a made-up Azure name fails the same way (measured), so a mistyped endpoint is likelier than a block. In place only.

Commentary · H
QuestionProposalWhy
What lights itA refused call, then one HTTPS check against Apple’s connectivity address: it answers, Claude didn’t, and the failed call died in connect, TLS or timeout, or came back from something that wasn’t Claude.Rows 5–7 of E share these words, so the pill never says which of the three it was; the details keep that.
What clears itA change of network path, or any successful call to that provider. Not a timer.A timer is a guess about a place; a path change is the Mac saying the place changed. Matches D’s rule against a clock changing the toolbar under a glance.
Where it is keptIn memory only. Gone on quit.It would hold where you were. It also must not become a key verdict: Settings would show a good key as bad, and the saved record would follow you home.
Whose refusalOnly the provider you currently use. Switch provider and it goes.Otherwise the pill names a product you are no longer using.
Never forOllama; an Azure endpoint that doesn’t resolve; a refusal that carried the provider’s own reply (that is key, credit or rate limit, and says so).None of those is “this network”. Saying so would be wrong, which is worse than saying nothing.
Behind a proxy that answers instead of ClaudeLights this pill, not “key rejected”.A block page’s 403 reads as a rejected key today (measured on the stub). That is a fix to the classifier, not to the pill.
Already decided, reused as isWarning glyph and flat envelope (A); ✕ on hover, hides it, and a new refused call or a changed condition brings it back (D); the bare glyph after a day; no menu twin.Owner, 2 Nov 2026.
Not drawnThe ladder behind “Connection details”; the sign-in (captive portal) line.Both deferred in the build plan. F still shows what they would look like.

Where this could be wrong. A managed Mac with an inspecting proxy whose root certificate is installed passes the Apple check and may pass Claude’s TLS in the system’s store while the app’s own Python connection (which uses its own bundle of roots) fails. Then the pill must come from the failed call’s own TLS error, not from the check, or it will not light at all. The wording is the same; only what triggers it differs. Nothing here has been seen on a real hostile network: every state in the list above is expected, not measured.

Decision for you. Is “clears when the network changes, or a call works” the right persistence for this pill, with key and credit unchanged (they stay until the account is fixed)? And is the in-place Azure line acceptable as proposed wording?

·Side by side

Commentary · comparison
A · title-bar pillB · footer outC · consequence in place
Hand-tagger, dead key for monthsNags if verdict-triggered; fine if blocked-call-triggered (A′)Quiet, correct volumeSilent until needed; incidental calls need C′
About to analyseSeen before they startSeen if the sidebar is openSeen only after the run fails
Many projectsOne fix clears allOne fix clears allOne failure per project
Reachable with sidebar hiddenYesNoYes, in place
New engineeringVerdict for invalid (small); none for unreachableNew view in a deliberately plain footerA note on each non-run surface
Strings proposed2 pill labels, 1 button3 status phrases1–2 (per surface)

Decided (owner, 2 Nov 2026): the pill route, as subtle as possible. The footer is out. In-place consequence (C′) stays as the first telling, since a refused call has no row to land on. The pill is the one global surface, and it is for the sticky conditions (key rejected, out of credit) and the network-blocks-the-provider case; everything else is said in place. “Subtle” I read as: the flat envelope with no glass of its own, kind glyph and short label, one tinted moment on first sight, then the ✕ and the glyph-only form. If you meant subtler even than that first sighting, the tint in stage 1 is the thing to take out.

The open call is the persistence rule for the pill that already ships: should out-of-credit also stop showing on a stored verdict alone?

Commentary · strings
TextStatus
Claude · no credit, Claude is out of credit, Add credit to run new analysis or coding., Add funds…, Switch provider…shipped (desktop.outOfCredit.*)
Your Claude key was rejected., Couldn’t reach Claude. Check your connection., Run failed, Category: …shipped (pipeline.failure.*, pipeline.diagnostic.*)
Claude · invalid key, Claude · unavailableproposed — built from llmSettings.status.invalid / unavailable
Open Settings…proposed
Claude key rejected, Claude unavailable (footer)proposed
Not updated — Claude key rejectedproposed, wording not drafted
Couldn’t reach Azure OpenAI. Check the endpoint in Settings.proposed (section H), in place only
Network · can’t reach Claude, This network can’t reach Claude., Try a different network.proposed (A, E); the pill’s clearing rule is section H