docs(user-delight) ADR-0026 User Delight Frontend and Streaming + 5-layer enforcement scaffolding#146
Merged
Conversation
…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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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:.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
text/event-stream) — NOT SignalR, NOT WebSocketIAiRoutercontract (AnswerAsync+AnswerStreamingAsync)AnswerChunkdiscriminated union (TextDelta / ToolCallStarted / ToolCallCompleted / CitationArrived / Refusal / Final)AgentResponseExtensions.ToAgentResponseAsync; refusal-supersedes-text UX rule/api/wizard/landing+ Cosmosfeatured_machinesWizardAgentWarmupHostedService(mirrors PR #140)pinwiz.ai.first_token_msHistogram instrumentTest Plan
dotnet build PinballWizard.slnx -p:TreatWarningsAsErrors=true→ 0/0 (clean)AnswerChunkContractTests,RefusalDetailContractTests,CitationContractTests,RefusalPanelPluralityTests) land in Wave 1 PRs per ADR § 5-layer enforcement/local-reviewqualitative: 0 🔴 / 5SKILL.mdbody refs "7-item" → updated to "9-item"CLAUDE.mdDocumentation map said "18 ADRs" (stale through ADR-0025) → refreshed to "26 ADRs"CLAUDE.mditem 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.mdinvariant 14 "SSE-over-SignalR" misleading → reworded to "SSE (text/event-stream) — NOT SignalR, NOT WebSocket"git log -1 --format='%an <%ae>'shows personal noreplyOut of Scope
The plan file at
~/.claude/plans/lets-come-up-with-functional-badger.mdis 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):
Microsoft.Agents.AIGA SDK migration (gated on Microsoft GA announcement)🤖 Generated with Claude Code