Skip to content

docs(user-delight) ADR-0026 User Delight Frontend and Streaming + 5-layer enforcement scaffolding#146

Merged
jkeeley2073 merged 1 commit into
mainfrom
Dev-Phase5Adr0026UserDelightScaffolding
May 9, 2026
Merged

docs(user-delight) ADR-0026 User Delight Frontend and Streaming + 5-layer enforcement scaffolding#146
jkeeley2073 merged 1 commit into
mainfrom
Dev-Phase5Adr0026UserDelightScaffolding

Conversation

@jkeeley2073
Copy link
Copy Markdown
Contributor

Summary

PR 1 of 24 in the Phase 5 User Delight track per the master plan at ~/.claude/plans/lets-come-up-with-functional-badger.md. Mirrors the just-shipped Cosmos for User Delight track (ADR-0025, PRs #139-#145) exactly in structure: a docs-only ADR that locks the architectural posture and weaves 5-layer enforcement so future PRs in the track don't re-litigate already-locked decisions.

What lands

  • docs/adr/0026-user-delight-frontend-and-streaming.md (NEW) — canonical ADR locking 12 architectural decisions covering Phase 5's frontend + streaming surface. Plus revisit-triggers table (10 deferred-with-trigger items), Explicitly NOT adopted (8 rejected alternatives), 26-row trade-off matrix, Positive + Negative consequences, alternatives considered, and 5-layer enforcement weave declaration.
  • docs/adr/README.md — adds ADR-0026 row.
  • docs/guardrails.md § Locked decisions — 3 new bullets (SSE streaming transport, MudBlazor-strict + custom split, refusal-supersedes-text UX rule).
  • CLAUDE.md:
    • § Locked invariants — bullet 14 covers full Phase 5 User Delight posture.
    • § PR self-audit Step 1 — item 9 "User-Delight surface conformance" with 7 sub-rules.
    • § Documentation map — refreshes "18 ADRs" → "26 ADRs".
  • .claude/skills/local-review/SKILL.md — new "User-Delight surface conformance" review category (12) with 9 check rules and verdict tags.

The 12 locked architectural decisions

§ Decision
1 Blazor Web App auto-render mode + separate Web API project
2 Server-Sent Events (text/event-stream) — NOT SignalR, NOT WebSocket
3 Dual IAiRouter contract (AnswerAsync + AnswerStreamingAsync)
4 AnswerChunk discriminated union (TextDelta / ToolCallStarted / ToolCallCompleted / CitationArrived / Refusal / Final)
5 Streaming-with-guardrails via AgentResponseExtensions.ToAgentResponseAsync; refusal-supersedes-text UX rule
6 MudBlazor strict for chrome + custom only for the 4 delight surfaces
7 Refusal recovery — plural by construction (≥3 marketplace, ≥2 machine-ref)
8 Citation enrichment — DTO widening; no retrieval changes
9 Graceful degradation — RFC 9457 ProblemDetails + DegradationContext
10 Empty state — /api/wizard/landing + Cosmos featured_machines
11 WizardAgentWarmupHostedService (mirrors PR #140)
12 pinwiz.ai.first_token_ms Histogram instrument

Test Plan

  • dotnet build PinballWizard.slnx -p:TreatWarningsAsErrors=true → 0/0 (clean)
  • No new tests this PR (docs-only); contract tests (AnswerChunkContractTests, RefusalDetailContractTests, CitationContractTests, RefusalPanelPluralityTests) land in Wave 1 PRs per ADR § 5-layer enforcement
  • Pre-push self-audit:
    • Step 0 /local-review qualitative: 0 🔴 / 5 ⚠️ / 7 categories ✅
      • ⚠️ SKILL.md body refs "7-item" → updated to "9-item"
      • ⚠️ Trade-off matrix latency column had non-latency values on rows 2+18 → reworded
      • ⚠️ CLAUDE.md Documentation map said "18 ADRs" (stale through ADR-0025) → refreshed to "26 ADRs"
      • ⚠️ CLAUDE.md item 9 missing 2 sub-rules vs local-review category 12 (SSE wire-format + audio mute-by-default 🔴 checks) → added sub-rules (f) and (g)
      • ⚠️ CLAUDE.md invariant 14 "SSE-over-SignalR" misleading → reworded to "SSE (text/event-stream) — NOT SignalR, NOT WebSocket"
    • Step 1 9-item mechanical: ✅ all 9 pass; items 1-5 + 8 are N/A for docs-only; item 6 build zero-warning; item 7 identity check; item 9 verifies the rule itself is internally consistent
  • Identity: git log -1 --format='%an <%ae>' shows personal noreply

Out of Scope

The plan file at ~/.claude/plans/lets-come-up-with-functional-badger.md is the master document for the 24-PR sequence. This PR is Wave 0 only — pure spec/scaffolding, no code shipped. Wave 1 (4 backend foundational PRs in parallel + 3 sequential frontend foundational PRs) follows once this lands.

Deferred-with-trigger items documented in ADR-0026 § Revisit triggers (so they don't get re-proposed PR-by-PR):

  • Multi-turn conversation memory (gated on Entra External ID passport)
  • Multimodal image upload (gated on vision-agent eval baseline)
  • Audio design beyond mute-by-default (gated on customer signal)
  • Strategy Tracker / Dream Game UI (separate Phase 5+ design conversations)
  • Per-component WASM opt-in (gated on Lighthouse data)
  • SignalR upgrade (gated on bidirectional UX need)
  • Redis-backed semantic cache (gated on multi-instance scale-event cache crater)
  • Microsoft.Agents.AI GA SDK migration (gated on Microsoft GA announcement)

🤖 Generated with Claude Code

…ayer enforcement scaffolding

PR 1 of 24 in the Phase 5 User Delight track. Mirrors the just-shipped
Cosmos for User Delight track (ADR-0025, PRs #139-#145) exactly in
structure: a docs-only ADR that locks the architectural posture and
weaves 5-layer enforcement so future PRs in the track don't re-litigate
already-locked decisions.

What lands (5 files):

- docs/adr/0026-user-delight-frontend-and-streaming.md (new) — canonical
  ADR locking 12 architectural decisions covering Phase 5's frontend +
  streaming surface:
    1. Architectural style — Blazor Web App auto-render mode + separate
       Web API project
    2. Streaming transport — Server-Sent Events (text/event-stream),
       NOT SignalR, NOT WebSocket
    3. Dual IAiRouter contract — AnswerAsync (whole-response) AND
       AnswerStreamingAsync (IAsyncEnumerable<AnswerChunk>)
    4. AnswerChunk discriminated-union shape (TextDelta |
       ToolCallStarted | ToolCallCompleted | CitationArrived | Refusal
       | Final)
    5. Streaming-with-guardrails — cache + cost-ceiling + confidence-
       threshold + citation-required all stay one-shot via
       AgentResponseExtensions.ToAgentResponseAsync post-stream
       reconstruction; refusal-supersedes-text streaming UX rule
    6. Component strategy — MudBlazor strict for chrome + custom
       components ONLY for the four delight surfaces
       (WizardAnswerStream, RefusalPanel, CitationStrip family,
       TiltPage/TiltErrorBoundary)
    7. Refusal recovery — plural by construction (>=3 marketplace,
       >=2 machine-ref community resources; ConfidenceBreakdown +
       RelatedMachines)
    8. Citation enrichment — DTO widening to surface fields the
       retrieval pipeline already computes (PageStart/PageEnd,
       SectionHeading, RelevanceScore, LastScrapedUtc, SourceType);
       no retrieval changes
    9. Graceful degradation — RFC 9457 ProblemDetails + DegradationContext
       on WizardAnswer + 5-arm decision tree
    10. Empty state — /api/wizard/landing endpoint + Cosmos
        featured_machines lookup container (mirrors PR #145 point-read
        pattern)
    11. Wizard host warmup — BackgroundService analogous to
        CosmosClientWarmupHostedService (PR #140)
    12. Observability — pinwiz.ai.first_token_ms instrument
  Plus revisit-triggers table (10 deferred-with-trigger items),
  Explicitly NOT adopted (8 rejected alternatives), 26-row trade-off
  matrix, Positive + Negative consequences, alternatives considered,
  and 5-layer enforcement weave declaration.

- docs/adr/README.md — adds ADR-0026 row to the index.

- docs/guardrails.md § Locked decisions — three new bullets pointing at
  ADR-0026: (i) SSE streaming transport posture; (ii) MudBlazor-strict
  + custom-component split for the four delight surfaces;
  (iii) refusal-supersedes-text streaming UX rule.

- CLAUDE.md changes:
    * § Locked invariants — adds bullet 14 covering the full Phase 5
      User Delight posture in one bullet.
    * § PR self-audit Step 1 — adds item 9 "User-Delight surface
      conformance" with 7 sub-rules covering plural recovery, citation
      enrichment, Final-chunk-always-emitted invariant, bUnit + axe-core
      coverage, custom-component scope limit, AnswerChunk-shaped JSON
      wire format, and audio mute-by-default.
    * § PR self-audit Step 0 — "eleven categories" -> "twelve categories"
      reflecting the new local-review category 12.
    * § Documentation map — refreshes "18 ADRs" -> "26 ADRs" (caught
      pre-existing stale ref during ADR-0025 review; fixed in this PR
      per guardrails.md § Spec maintenance trigger for ADR additions).

- .claude/skills/local-review/SKILL.md — new "User-Delight surface
  conformance" review category (12) with 9 specific check rules:
    * Refusal plurality threshold (>=3 marketplace, >=2 machine-ref) - 🔴
    * Citation writer not populating LastScrapedUtc + RelevanceScore - ⚠️
    * Streaming endpoint that can return without emitting Final - 🔴
    * Refusal chunk emitted without subsequent Final - 🔴
    * Custom component outside the four locked delight surfaces - ⚠️
    * Razor component without bUnit smoke test - ⚠️
    * SSE event payload not AnswerChunk-shaped JSON - 🔴
    * Auto-playing audio / non-muted-by-default SoundController - 🔴
  Plus description "7-item" -> "9-item" reflecting items 8 and 9 that
  joined the self-audit (item 8 was added in PR #139; item 9 lands here).

Pre-push self-audit:

- /local-review qualitative: 0 🔴 / 5 ⚠️ / 7 categories ✅
    * ⚠️ SKILL.md body refs "7-item" - fixed inline in same PR
    * ⚠️ Trade-off matrix latency column had non-latency values on rows
      2 + 18 - reworded inline
    * ⚠️ CLAUDE.md Documentation map "18 ADRs" stale - refreshed to
      "26 ADRs" inline
    * ⚠️ CLAUDE.md item 9 missing 2 sub-rules vs local-review category
      12 (SSE wire-format + audio mute-by-default 🔴 checks) - added
      sub-rules (f) and (g) inline
    * ⚠️ CLAUDE.md invariant 14 "SSE-over-SignalR" misleading - reworded
      to "SSE (text/event-stream) - NOT SignalR, NOT WebSocket"
- 9-item mechanical: ✅ all 9 pass; items 1-5 + 8 are N/A for docs-only;
  item 6 build zero-warning; item 7 identity check passes; item 9
  verifies the rule itself is internally consistent (passes after the
  inline fixes above).
- Build: 0/0 zero warnings as errors.
- Identity: personal noreply.

The plan file at C:/Users/JimKeeley/.claude/plans/lets-come-up-with-functional-badger.md
is the master document for the 24-PR sequence; this ADR is the canonical
spec it references. Wave 1 PRs (4 backend foundational PRs in parallel
with 3 sequential frontend foundational PRs) follow once this lands.
@jkeeley2073 jkeeley2073 added the claude-code Generated with Claude Code label May 9, 2026
@jkeeley2073 jkeeley2073 enabled auto-merge May 9, 2026 17:26
@jkeeley2073 jkeeley2073 merged commit 0acb52e into main May 9, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

claude-code Generated with Claude Code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant