feat(scratchnode): degraded badge on provider-fallback /ask answers#447
Conversation
…nswers
Completes the honest-degraded-UX half of the /ask observability work. When the AI
provider is unavailable, askAgent answers from public sources only and records
agentMode=provider_fallback. renderAnswer already LABELLED that ("AI fallback ·
deterministic") but rendered it as neutral text, so a reader could mistake a
degraded answer for a full AI one.
Adds an amber "degraded · sources only" pill (icon + text, never colour alone for
a11y; role=status so screen readers announce it; title tooltip explains it may be
less complete) in the answer head, shown ONLY for provider_fallback answers.
Verified: all 4 inline <script> blocks pass node --check (the live-room block is
type=module / strict). Renders only on the rare fallback path; Tier-A live-DOM
check post-deploy confirms the code shipped.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
🤖 Augment PR SummarySummary: Adds an amber “⚠ degraded · sources only” badge to ScratchNode 🤖 Was this summary useful? React with 👍 or 👎 |
| // text, never colour alone — a11y) with role=status so it's announced, so a | ||
| // reader never mistakes a degraded answer for a full AI one. | ||
| const degradedBadge = answer.agentMode === 'provider_fallback' | ||
| ? '<span class="sep">·</span><span class="ans-degraded" role="status" style="color:#e0a160;background:rgba(224,161,96,.14);border:1px solid rgba(224,161,96,.32);padding:0 7px;border-radius:999px;font-weight:600;white-space:nowrap" title="The AI provider was unavailable, so this answer was generated from the public event sources only. It may be less complete than a full AI answer.">⚠ degraded · sources only</span>' |
There was a problem hiding this comment.
degradedBadge sets role="status" but doesn’t include an explicit aria-live attribute; elsewhere in this file status messages use role="status" aria-live="polite". Consider adding aria-live="polite" here too for more consistent screen reader announcement behavior across AT/browser combos.
Severity: low
🤖 Was this useful? React with 👍 or 👎, or 🚀 if it prevented an incident/outage.
… restore + scenario tests The first /ask sent in the sub-second cold-load window (before the liveEventMembers row commits) can reject not_joined. PR #445 already added the pre-init send queue + idempotent join+resend retry; this closes the remaining gap: - home-v5.html: the final-catch draft restore now guards `if (!input.value)` like its sibling _sn_failPendingSends, so a total-failure restore never clobbers a newer draft typed while the send was in flight; the toast stays honest about whether it actually repopulated. - scratchnode.events.test.ts: two scenario tests pin the recovery contract — a pre-join send rejects not_joined and persists nothing, then join+resend lands exactly one message (not lost, not duplicated); and the idempotent re-join during recovery never forks a second member row. Additive only — no sendMessage/joinEvent contract changes. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Demo: walkthrough of the surfaces this PR changed is available as a workflow artifact ( |
Completes the honest-degraded-UX half of the /ask observability work (pairs with the
getAskTelemetryquery in #446).Why
When the AI provider is unavailable,
askAgentanswers from public sources only and recordsagentMode=provider_fallback.renderAnsweralready labelled it (\AI fallback · deterministic) but as neutral text — a reader could mistake a degraded answer for a full AI one.What
An amber *\⚠ degraded · sources only* pill in the answer head, shown ONLY for
provider_fallbackanswers:role=statusso screen readers announce ittitletooltip explains it may be less complete than a full AI answerVerification
All 4 inline
<script>blocks passnode --check(live-room block is type=module/strict). Renders only on the rare fallback path; Tier-A live-DOM check post-deploy confirms it shipped.🤖 Generated with Claude Code