Skip to content

feat(web): the deployment card — the badge's tooltip, reachable without a pointer (#755) - #758

Merged
eaitbrahim merged 1 commit into
mainfrom
fix-755-deployment-card
Sep 7, 2026
Merged

feat(web): the deployment card — the badge's tooltip, reachable without a pointer (#755)#758
eaitbrahim merged 1 commit into
mainfrom
fix-755-deployment-card

Conversation

@eaitbrahim

Copy link
Copy Markdown
Contributor

Closes #755.

modeBadge writes the identifying --db/--config pair into a title, and title is a hover tooltip. There is no hover on a phone — the device #648/#656 exist for — so the half of the badge that actually identifies a deployment was unreachable exactly where the terminal is least available.

The mode word identifies nothing on its own: two paper deployments both say paper, and a live sandbox and a paper-equities profile can both say confirm.

┌──────────────────────────────────────────────┐
│  deployment   keel-live                      │
│  mode         confirm                        │
│  config       config.live-sandbox.yaml       │
│  database     keel-live.db                   │
│  origin       http://127.0.0.1:8765          │
│  ──────────────────────────────────────────  │
│  Mode and profile switching are terminal     │
│  ceremonies with a runbook. This console     │
│  reports the deployment it was pointed at    │
│  and cannot change it.                       │
└──────────────────────────────────────────────┘

A native disclosure, which is what makes it safe

The badge is now the <summary> of a <details> — keyboard-operable, touch-operable and announced, with no handler to bind and no button to build. That is what lets deploymentCard join sessionChip and paperBanner in test_neither_the_chip_nor_the_banner_builds_anything_clickable rather than be exempted from it. venueCard already uses the element the same way.

That matters more than convenience: this is the surface where a reader looks for the control, which makes it the likeliest place for one to appear later. A "switch to live" affordance arriving through a door marked accessibility is still the growth funnel the chip's docstring refuses.

The card says so in words too, and the test pins the sentence's content — refusing four invitation wordings — because a reference-only assertion would pass over a note reworded into a CTA.

Tags changed, layout did not

<span class="right"> and <span id="session-chip"> are <div>s now: <details> is flow content and a <span> may hold only phrasing content. Both already set their own display: inline-flex, so nothing moves. The popover is positioned out of flow — opening it must not reflow the header, which on a narrow screen would push the nav under the reader's finger mid-tap.

title is kept, per the issue: it works for keyboard and pointer and costs nothing.

Two existing scans caught me

  • SWITCHING_NOTE was spelled with +, which test_render_contains_no_arithmetic forbids in render.js — concatenation is .concat throughout that file so the derivation-free scan can stay simple.
  • The header test pins the badge's tag, so <summary> failed it. Updated with the reason: what it protects — the badge exists carrying that id and both classes, which modeBadge and header .mode:empty key on — is unchanged.

Registered in _CONFIG_READERS in the same commit that adds it, which is the rule that table states.

Checks

6,319 passed / 3 skipped, ruff clean. Three mutants killed: card built but never mounted, refusal reworded as an invitation, db path dropped.

Not proven here: that the popover is legible on a real phone, or that a tap opens it. Hand checks against a running keel serve.

🤖 Generated with Claude Code

https://claude.ai/code/session_01KZZxmspQXe5qJ9FAsG13s6

…out a pointer (#755)

`modeBadge` writes the identifying `--db`/`--config` pair into a `title`, and
`title` is a HOVER tooltip. There is no hover on a phone, which is the device
#648/#656 exist for -- so the half of the badge that actually identifies a
deployment was unreachable exactly where the terminal is least available. The
mode WORD identifies nothing on its own: two paper deployments both say `paper`,
and a live sandbox and a paper-equities profile can both say `confirm`.

A NATIVE DISCLOSURE, WHICH IS WHAT MAKES IT SAFE

The badge is now the `<summary>` of a `<details>`. Keyboard-operable,
touch-operable and announced, with no handler to bind and no button to build --
so `deploymentCard` JOINS `sessionChip` and `paperBanner` in
`test_neither_the_chip_nor_the_banner_builds_anything_clickable` rather than
being exempted from it. `venueCard` already uses the element the same way.

That matters more than convenience. This is the surface where a reader looks for
the control, which makes it the likeliest place for one to appear later. A
"switch to live" affordance arriving through a door marked accessibility is
still the growth funnel `sessionChip`'s docstring refuses, so the card is held to
the stricter standard on the way in.

The card says so in words, too. Without the sentence it reads as an unfinished
settings panel; with it the refusal is legible where someone would look for the
control. The test pins the constant's CONTENT, not just its placement, and
refuses four invitation wordings -- a reference-only assertion would pass over a
note reworded into a CTA.

TAGS CHANGED, LAYOUT DID NOT

`<span class="right">` and `<span id="session-chip">` are DIVs now: `<details>`
is flow content and a `<span>` may hold only phrasing content. Both already set
their own `display: inline-flex` in `keel.css`, so nothing moves. The card is
positioned out of flow -- opening it must not reflow the header, which on a
narrow screen would push the nav under the reader's finger mid-tap.

`title` is KEPT, per the issue: it works for keyboard and pointer and costs
nothing. This supplements it rather than replacing it.

TWO SCANS CAUGHT ME

`SWITCHING_NOTE` was spelled with `+`, which `test_render_contains_no_arithmetic`
forbids in `render.js` -- string concatenation is `.concat` throughout that file
precisely so the derivation-free scan can stay simple. And the header test pins
the badge's TAG, so changing it to `<summary>` failed there; updated with the
reason, since what it protects (the badge exists, carrying that id and both
classes, which `modeBadge` and `header .mode:empty` key on) is unchanged.

Registered in `_CONFIG_READERS` in the same commit that adds it, which is the
rule that table states: a new consumer arrives with the check covering it. Four
config keys, and a rename on either side would otherwise leave the card
rendering em-dashes forever.

6,319 passed / 3 skipped, ruff clean. Three mutants killed: the card built but
never mounted, the refusal reworded as an invitation, and the db path dropped.
Not proven here and unchanged: that the popover is legible on a real phone, or
that a tap opens it. Hand checks against a running `keel serve`.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KZZxmspQXe5qJ9FAsG13s6
@eaitbrahim
eaitbrahim merged commit 4f552ee into main Sep 7, 2026
4 checks passed
@eaitbrahim
eaitbrahim deleted the fix-755-deployment-card branch September 7, 2026 17:40
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.

[web] The mode badge's db/config identity is a hover tooltip, so it is unreachable on touch — the mobile wedge's target device

1 participant