Skip to content

Inbox unread on the nav row, under a narrow dead-man exception - #281

Merged
pixexid merged 4 commits into
mainfrom
bb/gh-258-item-5-inbox-unread-nav-indicator-thr_2rbixgj33q
Aug 19, 2026
Merged

pixexid merged 4 commits into
mainfrom
bb/gh-258-item-5-inbox-unread-nav-indicator-thr_2rbixgj33q

Conversation

@pixexid

@pixexid pixexid commented Aug 19, 2026 •

Copy link
Copy Markdown
Owner

Two changes, both GH-258 item 5.

1. The inbox unread indicator. PluginNavPanelRegistration has no badge,
count, or attention field (get-bb/bb#1852), so the Inbox row is painted directly
under the narrow exception the operator granted to the standing refusal of host
data-testid plus visible-text row matching. The exception's mandatory
condition is that the coupling announces its own death, and it has two deaths,
not one.

Zero-match — the selector or the text coupling finds no row:

RELABELLED ROW: {"matched":false,"reason":"0 of the 2 rows in [data-testid=\"plugin-nav-sidebar-items\"] are titled \"Inbox\", expected exactly 1"}
RENAMED TESTID: {"matched":false,"reason":"no element matches [data-testid=\"plugin-nav-sidebar-items\"]"}

Valid but wrong — something plausible is drawn and signals nothing:

AMBIGUOUS ROW:   {"matched":false,"reason":"2 of the 3 rows in [data-testid=\"plugin-nav-sidebar-items\"] are titled \"Inbox\", expected exactly 1"}
COLLAPSED GLYPH: {"matched":false,"reason":"the Inbox and Lanes rows draw the same glyph (path[d=M7 19h6]) though they declare different icons"}
NO GEOMETRY:     null   (no verdict, not a failure)
HEALTHY:         {"matched":true} / {"matched":true}

inspectInboxNavGlyph compares drawn geometry rather than class names, and
reads the Lanes row as the control — only a control row tells a precedence
collapse apart from an unknown name falling back to the host default.

Undetectable from inside the plugin, stated rather than faked: whether the
glyph beside Inbox is the one Inbox declared. The host owns the icon
registry, exposes no reading of which glyph it drew, and renders an unknown name
as a default with no error; hardcoding an expected path would only trade the
blind spot for a coupling that breaks falsely on the next host icon-set change.
So a single row drawing a wrong-but-unique glyph is not caught, and the check
returns no verdict rather than a false one when the control is unreadable.

Every reported break surfaces as a visible Inbox unread indicator broken alert
in the replacement thread list plus a recorded console.error. Scope (this
indicator only) and retirement (get-bb/bb#1852) are in
docs/sidebar-plugin-nav-collapse.md.

2. branding.icon dropped. A manifest icon overrides every contribution
icon of that plugin, form-independently, which is why Lanes and Inbox render the
same glyph today. Measured on a throwaway plugin: absent icon renders Mail and
GitBranch distinctly, "Toolbox" collapses both. pluginBrandingSchema
requires at least one of icon and logo.light, so a real assets/logo.svg
ships in the same change and scripts/build.mjs now stages assets/. The
finding itself is recorded in #277.

Observed vs derived: the collapse and its absence were observed on a probe
plugin whose manifest shape and icon names match this one; that bb-collab's own
two rows follow is derived, since the only differing input is the plugin id.
The collapsed-glyph switch above is what would catch it if that derivation is
wrong.

npm run verify exit 0 — 359 passed, 2 skipped, 22 files passed, 2 skipped.

Related GH-258

Review tier: A

GH-258 item 5 wants unread state visible before the Inbox panel is opened.
PluginNavPanelRegistration still has no badge, count, or attention field
(get-bb/bb#1852), so the operator granted a narrow exception to the standing
refusal of host data-testid plus visible-text row matching.

The exception is conditional on the coupling being observable when it dies.
paintInboxNavUnread returns a reason on zero-match; the sidebar poll turns that
into a visible "Inbox unread indicator broken" alert in the replacement thread
list plus a recorded console.error. Never silently nothing.

Scope and retirement are recorded beside the refusals they except, in
docs/sidebar-plugin-nav-collapse.md.
A manifest branding.icon overrides every contribution icon of that plugin,
whatever form it takes, so declaring "GitBranch" collapsed Lanes and Inbox onto
one glyph. Measured on a throwaway plugin: with branding.icon absent the two
rows render Mail and GitBranch distinctly; with it set they collapse.

pluginBrandingSchema requires at least one of branding.icon and
branding.logo.light, so removing the icon ships a real logo asset in the same
change. scripts/build.mjs stages assets/ or the manifest would point at a file
the packaged plugin does not carry.
A missing row is one way the indicator dies. The other is the one the icon
probe hit: something plausible is still drawn and signals nothing. Two shapes
of that are detectable and now report broken — a second row also titled Inbox,
where the dot lands on a row that is not ours, and this plugin's two rows
drawing the same glyph though they declare different icons.

inspectInboxNavGlyph compares drawn geometry, not class names, and reads Lanes
as the control, because only a control row tells a precedence collapse apart
from an unknown name falling back to the host default.

What it cannot do is stated in the exception text rather than faked: the host
exposes no reading of which glyph it drew, so a single row drawing a
wrong-but-unique glyph is undetectable from inside the plugin, and the check
returns no verdict rather than a false one when the control is unreadable.
Four graded findings from the tier A review at f628a2b.

The switch fingerprinted `d` alone, so identical path data under a 90-degree
transform read as a collapse. A false alarm is the one failure this switch
cannot have: it is the retirement signal for get-bb/bb#1852, and a signal that
fires on a legitimate re-theme gets muted. The fingerprint now covers what
actually moves pixels — transform, transform-origin, style, the root viewBox
and preserveAspectRatio, href — and the root svg element is read alongside its
children. Both directions are tested: same-d-plus-transform must be healthy,
the collapse must still be broken.

A rejected project read was reduced to zero unread, so one transient failure
erased a count already proven. Each project's last proven count is retained
across a failed read instead; a project with no inbox registered is a proven
zero, not a failure, and #278's exact-message check is what tells them apart.

A continuous break logged an identical line every 30 seconds. The transition is
recorded now; the visible alert was already the standing report.

Rebased onto 5e16dc7. The app.tsx conflict was mechanical — #278's
unregistered-project helper and this branch's poll constant landed adjacent —
and dist was regenerated by the builder rather than reconciled by hand.
@pixexid
pixexid force-pushed the bb/gh-258-item-5-inbox-unread-nav-indicator-thr_2rbixgj33q branch from f628a2b to a0b0451 Compare August 19, 2026 02:13
@pixexid
pixexid marked this pull request as ready for review August 19, 2026 02:16
@pixexid
pixexid merged commit 5d674a8 into main Aug 19, 2026
1 check passed
@pixexid
pixexid deleted the bb/gh-258-item-5-inbox-unread-nav-indicator-thr_2rbixgj33q branch August 19, 2026 02:16
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