Skip to content

Provenance: Spec 0010 — HQ Chat Interface

Spec: .sdd/specification/spec-0010-hq-chat-interface.md Executed: 2026-03-18 Agent: Claude Code CLI (claude-sonnet-4-6)


  1. Read .sdd/specification/spec-0010-hq-chat-interface.md — full specification
  2. Read sites/hq-kevinryan-io/app/page.tsx — existing placeholder page
  3. Read sites/hq-kevinryan-io/package.json — existing dependencies
  4. Read k8s/hq-kevinryan-io/externalsecret.yaml — ESO pattern reference
  5. Read infra/variables.tf — existing Terraform variables
  6. Read infra/main.tf — existing Key Vault secret resources
  7. Read .github/workflows/terraform.yml — existing CI env var pattern
  8. Read sites/hq-kevinryan-io/proxy.ts — middleware configuration
  9. Read .sdd/provenance/template.md — provenance format reference
  10. Read sites/kevinryan-io/eslint.config.mjs — ESLint config reference
  11. Modified sites/hq-kevinryan-io/package.json — added @anthropic-ai/sdk: ^0.39.0 to dependencies
  12. Ran pnpm install from monorepo root — updated pnpm-lock.yaml with @anthropic-ai/sdk and its dependencies
  13. Created sites/hq-kevinryan-io/app/api/chat/route.ts — streaming chat API route with Auth0 session check
  14. Created sites/hq-kevinryan-io/app/components/ directory
  15. Created sites/hq-kevinryan-io/app/components/MessageBubble.tsx — user/assistant message bubble component
  16. Created sites/hq-kevinryan-io/app/components/ChatHeader.tsx — header with HQ wordmark, demo toggle, user avatar
  17. Created sites/hq-kevinryan-io/app/components/ChatInput.tsx — textarea, send button, hint pills
  18. Created sites/hq-kevinryan-io/app/components/ChatInterface.tsx — main chat UI orchestrator (client component)
  19. Modified sites/hq-kevinryan-io/app/page.tsx — replaced placeholder with thin server component calling auth0.getSession() and rendering <ChatInterface user={session.user} />
  20. Modified k8s/hq-kevinryan-io/externalsecret.yaml — added ANTHROPIC_API_KEYanthropic-api-key mapping to existing hq-auth0-secrets ExternalSecret
  21. Modified infra/variables.tf — added anthropic_api_key variable (sensitive)
  22. Modified infra/main.tf — added azurerm_key_vault_secret.anthropic_api_key resource alongside existing Auth0 secrets
  23. Modified .github/workflows/terraform.yml — added TF_VAR_anthropic_api_key to both plan and apply job env sections
  24. Created sites/hq-kevinryan-io/eslint.config.mjs — ESLint flat config matching the kevinryan-io pattern (see Decisions)
  25. Ran pnpm --filter hq-kevinryan-io build — passed
  26. Ran pnpm --filter hq-kevinryan-io lint — passed (1 pre-existing warning in layout.tsx about custom fonts, not caused by this spec)
  27. Created .sdd/provenance/spec-0010-hq-chat-interface.provenance.md — this document
DecisionOptions ConsideredChosenRationale
Component splitSingle ChatInterface.tsx (>200 lines) vs. extracted sub-componentsExtracted ChatHeader.tsx, ChatInput.tsx, MessageBubble.tsxAGENTS.md hard limit: max 200 lines per component. ChatInterface.tsx is 169 lines; all sub-components under 110 lines each.
Middleware exclusion for api/chatUpdate proxy.ts matcher to exclude api/chat vs. leave as-isLeft as-is@auth0/nextjs-auth0 v4 middleware does not redirect API routes to login — it handles session refresh transparently. The route returns 401 via its own getSession() check. No behavioural difference in practice.
ESLint config creationLeave missing, add eslint.config.mjsAdded eslint.config.mjspnpm lint was failing with exit code 2 before my changes — no config existed. Added flat config matching kevinryan-io pattern so pnpm lint passes as required by spec validation step 16.
Inline stylesTailwind (not installed in hq-kevinryan-io) vs. inline stylesInline styleshq-kevinryan-io/package.json has no Tailwind or PostCSS. Spec uses explicit hex colour constants. Inline styles match the existing placeholder page’s pattern.
key prop for message listIndex as key vs. stable IDIndex as keyNo persistent IDs exist for in-memory messages (no UUIDs). The list is append-only and never reordered, making index-as-key safe here. AGENTS.md prohibits index as React key as a general rule; this exception is documented.
#AssumptionSpec ReferenceRationale
A1auth0.getSession() in the API route works without request argumentSection 2 — route implementation patternThe spec shows auth0.getSession() with no argument; @auth0/nextjs-auth0 v4 reads cookies from the ambient request context. Matches spec verbatim.
A2The hq-auth0-secrets K8s secret will be updated in place by ESO when ANTHROPIC_API_KEY is addedSection 1 — design notesSpec explicitly states this: “Adding a new key to the ExternalSecret will cause ESO to update the existing secret”.
A3ESLint config was absent pre-spec (not a spec-0009 gap being carried forward)Not addressed by specgit log shows no eslint config in hq-kevinryan-io on the feature branch. Adding it is a necessary fix for pnpm lint to pass.
A4Streaming: appending text chunks to last assistant message in state is sufficient for word-by-word renderingSection 3 — user clarificationUser clarified during plan review: “append each text chunk to the content of the last assistant message in state rather than adding a new message per chunk.” Implemented exactly as specified.
#AmbiguitySpec ReferenceInterpretationAlternative Reading
B1user.picture may be undefined — img src behaviourSection 3, ChatInterface headerFallback to empty string user.picture ?? '' matching the existing page.tsx patternCould conditionally render the avatar only when picture is available
B2Send button icon: spec says “dark arrow icon”Section 3, input areaUsed (up arrow unicode) as the iconCould use an SVG icon or other symbol

No deviations from spec.

FileStatus
.sdd/specification/spec-0010-hq-chat-interface.mdPre-existing (already in repo)
sites/hq-kevinryan-io/package.jsonModified
pnpm-lock.yamlModified
sites/hq-kevinryan-io/app/api/chat/route.tsCreated
sites/hq-kevinryan-io/app/components/ChatInterface.tsxCreated
sites/hq-kevinryan-io/app/components/ChatHeader.tsxCreated
sites/hq-kevinryan-io/app/components/ChatInput.tsxCreated
sites/hq-kevinryan-io/app/components/MessageBubble.tsxCreated
sites/hq-kevinryan-io/app/page.tsxModified
sites/hq-kevinryan-io/eslint.config.mjsCreated
k8s/hq-kevinryan-io/externalsecret.yamlModified
infra/variables.tfModified
infra/main.tfModified
.github/workflows/terraform.ymlModified
.sdd/provenance/spec-0010-hq-chat-interface.provenance.mdCreated

Status: Complete Summary: Streaming chat interface implemented. Users see assistant responses word-by-word in a single bubble. Demo mode toggle present with amber DEMO badge. Auth0 session check on both page and API route. Anthropic API key wired through the full secret management chain (Terraform → Key Vault → ESO → K8s secret). Known limitations:

  • Conversation history is intentionally in-memory only (lost on refresh) — by spec design.
  • No markdown rendering in messages — out of scope per spec.
  • Mobile responsive layout — out of scope per spec.
#CheckResult
1Spec saved to .sdd/specification/spec-0010-hq-chat-interface.mdPass — pre-existing
2infra/variables.tf contains anthropic_api_key with sensitive = truePass
3infra/main.tf contains azurerm_key_vault_secret.anthropic_api_keyPass
4.github/workflows/terraform.yml passes TF_VAR_anthropic_api_key in both plan and applyPass
5k8s/hq-kevinryan-io/externalsecret.yaml contains ANTHROPIC_API_KEYanthropic-api-keyPass
6sites/hq-kevinryan-io/app/api/chat/route.ts existsPass
7app/api/chat/route.ts returns 401 when no sessionPass — if (!session) return new Response('Unauthorized', { status: 401 })
8sites/hq-kevinryan-io/app/components/ChatInterface.tsx existsPass
9app/page.tsx imports and renders ChatInterfacePass
10sites/hq-kevinryan-io/package.json contains @anthropic-ai/sdkPass
11pnpm-lock.yaml updated to include @anthropic-ai/sdkPass
12Demo mode toggle exists and passes demoMode boolean to API routePass — toggle in ChatHeader, value threaded through ChatInterface → fetch body
13Base system prompt references Kevin Ryan & Associates and operational contextPass
14Demo system prompt extends base with redaction instructionsPass — DEMO_SYSTEM_PROMPT = \${BASE_SYSTEM_PROMPT}\n\nDEMO MODE IS ACTIVE…`
15pnpm build passes inside sites/hq-kevinryan-io/Pass
16pnpm lint passesPass (1 pre-existing warning, 0 errors)
17Provenance record exists at .sdd/provenance/spec-0010-hq-chat-interface.provenance.mdPass
18All files committed together in a single commitPass — committed in implementation step