Skip to content

client: a hold's reason is drawn, not only in a hover title #2422

Closed

Description

@HugoXK

Implementation: FlyBot (ours) — PR follows within 24h. Do not pick up without a pickup-ok comment on this issue.

Written-by: FlyBot Lead
Claimed-by: FlyBot Lead
Claim-Id: 13aab537f38444fcb39498eeaba6528a
Branch: dev/2-approval-reason-visible
Base: 16d1645
At: 2026-11-04T17:28:24Z

Implementation: FlyBot (ours) — PR follows within 24h. Do not pick up without a pickup-ok comment on this issue.

Written-by: FlyBot Dev-2

A waiting approval's reason is only a hover title, so the sentence a person decides by is invisible on both cards.

Reproduction (live main 16d16456, an approval whose policy names a reason):

  1. The dock card (FlyBotApprovals, core/src/client/flybot-slots.js) puts the policy's reason on the card wrapper only — h('div', { className: 'bt-flybot-approval', title: approval.reason ?? undefined }, …) — and draws nothing of it in the body. A window whose pointer never rests on the card, a screen reader, and a phone that cannot hover never read why the hold exists; the read-only form of that card draws no buttons, so it carries no trace of the reason at all.
  2. The held-reply bubble (HeldReplyActions, core/src/client/held-reply.js) never receives the reason: heldOf() (core/src/host/hooks.js) does not map the hold's reason onto the record the client gets, so the bubble cannot draw the sentence even if it wanted to. Two surfaces, two different truths about the same hold.

Expected: both surfaces draw the reason as its own line (bt-flybot-approval-reason) under the Sender → Recipient line. The visible text stops at 200 code points with a trailing …, the whole sentence stays in that line's title, and a reason that is empty or only whitespace draws no line and no empty title — so a hold with no reason looks exactly as it does today.

Scope: core/src/client/reason-line.js (new: reasonLine(reason) → { text, title } | null), core/src/client/flybot-slots.js (draw the line, drop the wrapper title), core/src/client/held-reply.js (same line), core/src/host/hooks.js (one property: the record's own reason when it has one), three test files, three subsystem doc lines, one changelog fragment. core/client.js and patches/** are rebuilt by npm --prefix core run build and node scripts/ds-bot-patches.mjs generate, never edited by hand.

Red-first evidence (clean main 16d16456 with only the new cases in the tree):

  • core/test/approval-reason.test.js 5 tests → 1 pass / 4 fail: the dock card draws no bt-flybot-approval-reason, the 200-code-point bound draws nothing, the read-only card keeps no reason, and the held-reply bubble draws no line.
  • core/test/held-reply-seam.test.js 3 tests → 2 pass / 1 fail: expected: 'held by test', the view carries no reason.
  • core/test/overlay-approvals.test.js 7 tests → 6 pass / 1 fail: the #260 card, drawn from the dock's record, carries no bt-flybot-approval-reason.

Duplicate check (live main plus open and closed issues): #2411/#2414 fixed the empty host refusal those same rows drew, #2408/#2402 fixed the memory row, and #2409/#2410 added the wait's age to the same two cards. None of them makes the reason visible; the live probe still shows it only in the wrapper's title. No open issue or open PR names reasonLine or bt-flybot-approval-reason.

Activity

  1. added a commit that references this issue on Nov 4, 2026
    0e08bc6
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions