Spec 0010: HQ Chat Interface
Agent Roles
Section titled “Agent Roles”This specification is the single source of truth for what to build, how to verify it, and who does what. Each agent reads its role below and follows the instructions exactly. Agents do not communicate directly — they communicate through the provenance document.
Builder Agent
Section titled “Builder Agent”Purpose: Read this specification and produce working software with full provenance.
Reads:
- This specification
- ADR-021 (
docs/adr/adr-021-auth0-authentication-hq.md) sites/hq-kevinryan-io/app/page.tsx— current placeholder page being replacedsites/hq-kevinryan-io/app/api/— existing API routeshttps://brand.kevinryan.io— brand guidelines
Produces:
- Working chat interface at
hq.kevinryan.io - A provenance record at
.sdd/provenance/spec-0010-hq-chat-interface.provenance.md
Instructions:
- Save this spec to
.sdd/specification/spec-0010-hq-chat-interface.mdin the repo. - Read the full specification and all files listed under “Current state” before writing any code.
- Build the software as specified. Where the specification is silent on an implementation detail, make a reasonable decision and record it in the provenance.
- Write provenance as you build, not after.
- Do not write tests. Testing is not your role.
- When the build is complete, add a “Build Status” entry to the provenance.
- Commit the spec, implementation, and provenance together.
Testing Agent
Section titled “Testing Agent”Purpose: Read this specification and the builder’s provenance, then generate prose scenarios and executable tests.
Reads:
- This specification
- The provenance document at
.sdd/provenance/spec-0010-hq-chat-interface.provenance.md
Produces:
- Prose scenarios at
.sdd/scenarios/spec-0010-hq-chat-interface.scenarios.md - Executable test code in the
tests/directory - Updates to the provenance document
- Save this spec to
.sdd/specification/spec-0010-hq-chat-interface.mdin the repo. - Implement all changes described below.
- After completing all work, create a provenance record at
.sdd/provenance/spec-0010-hq-chat-interface.provenance.md.
Prerequisites
Section titled “Prerequisites”- Spec-0009 deployed:
hq.kevinryan.iois live with Auth0 authentication working. - Read ADR-021 for Auth0 context.
- The
hq-auth0-secretsK8s secret exists and is synced via ESO.
Context
Section titled “Context”HQ (hq.kevinryan.io) currently shows a placeholder page post-login. This spec replaces the placeholder with a working chat interface backed by the Anthropic API.
This is Claude — a general-purpose AI assistant — with a system prompt that establishes its identity as HQ, the operational assistant for Kevin Ryan & Associates. No tools or integrations are added in this spec. Those come in subsequent specs (GitHub in spec-0011, Azure in spec-0012, platform monitoring in spec-0013).
The Anthropic API key is stored in Azure Key Vault and delivered to the pod via the platform’s established ESO pattern — the same pattern used for Auth0 secrets in spec-0009.
The chat interface is a single-page application. The authenticated user types a message, it is sent to a Next.js API route server-side, the API route calls the Anthropic API, and the response streams back to the client. The UI maintains conversation history in React state for the duration of the session. There is no persistent conversation storage — history is lost on page refresh. That is intentional for this iteration.
A demo mode toggle is included. When enabled, HQ adds a instruction to the system prompt instructing it not to reveal sensitive information (rates, personal finances, HMRC matters, client details not already public). When demo mode is active a visible DEMO badge appears in the header. This allows HQ to be used safely in front of clients or at conferences.
Current state (read these files before making changes)
Section titled “Current state (read these files before making changes)”| File / Directory | What it does |
|---|---|
sites/hq-kevinryan-io/app/page.tsx | Current placeholder — replace entirely |
sites/hq-kevinryan-io/app/api/auth/[auth0]/route.ts | Auth0 handler — do not touch |
sites/hq-kevinryan-io/app/api/healthz/route.ts | Health check — do not touch |
sites/hq-kevinryan-io/app/layout.tsx | Root layout with Google Fonts — do not touch |
sites/hq-kevinryan-io/lib/auth0.ts | Auth0 client — do not touch |
k8s/hq-kevinryan-io/externalsecret.yaml | ESO manifest — add Anthropic key mapping |
infra/variables.tf | Terraform variables — add Anthropic key variable |
infra/main.tf | Terraform resources — add Anthropic key vault secret |
.github/workflows/terraform.yml | CI — add Anthropic key TF_VAR |
Key facts
Section titled “Key facts”- Anthropic model:
claude-sonnet-4-20250514 - Max tokens:
8192 - Streaming: yes — use Anthropic streaming API, stream response to client via
ReadableStream - API key env var:
ANTHROPIC_API_KEY - Key Vault secret name:
anthropic-api-key - Terraform variable name:
anthropic_api_key - GitHub secret name:
TF_VAR_ANTHROPIC_API_KEY - Container port:
3000(unchanged) - Fonts: Bebas Neue (display), Archivo (body), JetBrains Mono (monospaced accents)
- Colours:
#0A0A0Abackground,#F5F3EFtext,#A8E10Clime accent,#111111message bubbles,#1a1a1ainput background
1. Anthropic API key — secret management
Section titled “1. Anthropic API key — secret management”Follow the exact pattern established in spec-0009 for Auth0 secrets.
infra/variables.tf — add
Section titled “infra/variables.tf — add”variable "anthropic_api_key" { description = "Anthropic API key for HQ chat interface" type = string sensitive = true}infra/main.tf — add alongside existing hq_auth0 secrets
Section titled “infra/main.tf — add alongside existing hq_auth0 secrets”resource "azurerm_key_vault_secret" "anthropic_api_key" { name = "anthropic-api-key" value = var.anthropic_api_key key_vault_id = module.keyvault.key_vault_id}.github/workflows/terraform.yml — add to both plan and apply job env sections
Section titled “.github/workflows/terraform.yml — add to both plan and apply job env sections”TF_VAR_anthropic_api_key: ${{ secrets.TF_VAR_ANTHROPIC_API_KEY }}k8s/hq-kevinryan-io/externalsecret.yaml — add mapping
Section titled “k8s/hq-kevinryan-io/externalsecret.yaml — add mapping” - secretKey: ANTHROPIC_API_KEY remoteRef: key: anthropic-api-keyDesign notes:
- The Anthropic API key is a runtime secret — it must never appear in the Dockerfile or GitHub Actions workflow.
- The
hq-auth0-secretsK8s secret already exists. Adding a new key to theExternalSecretwill cause ESO to update the existing secret with the new key on its next sync. The secret name does not change.
2. Chat API route
Section titled “2. Chat API route”Create sites/hq-kevinryan-io/app/api/chat/route.ts.
This route:
- Accepts
POSTrequests with JSON body{ messages: Message[], demoMode: boolean } - Is protected by Auth0 — returns
401if no session - Calls the Anthropic API with streaming enabled
- Returns a
ReadableStreamresponse that the client consumes
Message type
Section titled “Message type”interface Message { role: 'user' | 'assistant' content: string}System prompt
Section titled “System prompt”const BASE_SYSTEM_PROMPT = `You are HQ — the operational AI assistant for Kevin Ryan & Associates, a boutique AI-Native engineering consultancy.
You have deep knowledge of:- AI-Native Software Engineering and Spec Driven Development (SDD)- DevOps, Platform Engineering, MLOps- The Kevin Ryan & Associates client portfolio (CERN, Nestlé, NatWest, BBC Worldwide, Financial Times, Vodafone, HelloFresh, Dematic, McKinsey, Barclays)- The platform infrastructure (K3s on Azure, Flux CD, Terraform, GitHub Actions)- Active workstreams and business development
You are direct, concise, and operationally focused. You think like an engineering leader.You assist with: strategy, writing, technical decisions, platform operations, business development, and general reasoning.You have access to general knowledge and can search the web when needed.`
const DEMO_SYSTEM_PROMPT = `${BASE_SYSTEM_PROMPT}
DEMO MODE IS ACTIVE. Do not reveal, reference, or quote any sensitive information including:- Day rates, contract fees, or financial details- HMRC, tax, or legal matters- Personal health or financial circumstances- Specific client contract terms not already publicly known- Any information that could be commercially sensitive
If asked about these topics, acknowledge they exist but state they are redacted in demo mode.`Route implementation pattern
Section titled “Route implementation pattern”import { auth0 } from '@/lib/auth0'import Anthropic from '@anthropic-ai/sdk'
const client = new Anthropic()
export async function POST(request: Request) { const session = await auth0.getSession() if (!session) { return new Response('Unauthorized', { status: 401 }) }
const { messages, demoMode } = await request.json() const systemPrompt = demoMode ? DEMO_SYSTEM_PROMPT : BASE_SYSTEM_PROMPT
const stream = await client.messages.stream({ model: 'claude-sonnet-4-20250514', max_tokens: 8192, system: systemPrompt, messages, })
const readable = new ReadableStream({ async start(controller) { for await (const chunk of stream) { if ( chunk.type === 'content_block_delta' && chunk.delta.type === 'text_delta' ) { controller.enqueue(new TextEncoder().encode(chunk.delta.text)) } } controller.close() }, })
return new Response(readable, { headers: { 'Content-Type': 'text/plain; charset=utf-8', 'Transfer-Encoding': 'chunked', }, })}3. Chat page
Section titled “3. Chat page”Replace sites/hq-kevinryan-io/app/page.tsx entirely.
This is a React client component ('use client') — the chat interface requires browser state. The Auth0 session check moves to a server component wrapper.
File structure
Section titled “File structure”app/page.tsx— thin server component, checks session, passes user to client componentapp/components/ChatInterface.tsx— full chat UI as a client component
app/page.tsx
Section titled “app/page.tsx”import { redirect } from 'next/navigation'import { auth0 } from '../lib/auth0'import ChatInterface from './components/ChatInterface'
export default async function HomePage() { const session = await auth0.getSession() if (!session) redirect('/auth/login') return <ChatInterface user={session.user} />}app/components/ChatInterface.tsx — the UI
Section titled “app/components/ChatInterface.tsx — the UI”The component must implement:
Header:
- HQ wordmark (Bebas Neue, left aligned) with lime period
- Demo mode toggle (right side) — label
demo mode, toggle switch,DEMObadge when active - Logged-in user avatar (circle, from
user.picture) and username - No logout button in header — move logout to a subtle link in the footer
Message area:
- Scrollable, fills available height
- User messages right-aligned, dark bubble (
#1a2a05background, lime border) - Assistant messages left-aligned, dark bubble (
#111background,#222border) - HQ messages prefixed with a small
HQlabel in Bebas Neue lime - Streaming text renders as it arrives — do not wait for full response
- Auto-scrolls to bottom on new content
- Empty state: centred
ask HQ anythingin JetBrains Mono, muted
Input area:
- Full-width textarea, dark background, lime focus border
- Send button — lime background, dark arrow icon
- Hint pills for common queries:
what's in flight,last deploy status,open specs,platform health - Disabled during streaming — re-enables when response completes
Footer:
- Commit SHA:
build: {NEXT_PUBLIC_COMMIT_SHA}in JetBrains Mono, lime, right-aligned, very small - Logout link: subtle, left-aligned, small
Demo mode behaviour:
- Toggle sends
demoMode: truein the API request body - When active: header shows amber
DEMObadge, input placeholder changes toask HQ anything (demo mode) - State persists for the session (React state, not localStorage)
Dependencies to add to package.json
Section titled “Dependencies to add to package.json”"@anthropic-ai/sdk": "^0.39.0"4. Update pnpm lockfile
Section titled “4. Update pnpm lockfile”After adding @anthropic-ai/sdk to package.json, run pnpm install from the monorepo root to update pnpm-lock.yaml. Commit the updated lockfile alongside all other changes.
Constraints and Assumptions
Section titled “Constraints and Assumptions”- Constraint: Conversation history is in-memory React state only. No persistence. History is lost on page refresh. This is intentional — persistence is a future spec.
- Constraint: The Anthropic API key is a runtime secret. It must never appear in the Dockerfile, GitHub Actions workflow, or any committed file.
- Constraint: Streaming must work end-to-end. Do not buffer the full response before sending to the client.
- Constraint: The chat API route must be excluded from Auth0 middleware — it handles its own auth check via
getSession(). Update the middleware/proxy matcher to excludeapi/chatif needed. - Assumption:
ANTHROPIC_API_KEYwill be added to thehq-auth0-secretsK8s secret via ESO after Terraform applies the new Key Vault secret. The secret name remainshq-auth0-secrets— ESO updates it in place. - Assumption:
TF_VAR_ANTHROPIC_API_KEYGitHub secret will be added by the human before running Terraform.
Out of Scope
Section titled “Out of Scope”- Persistent conversation history — future spec
- GitHub MCP integration — spec-0011
- Azure integration — spec-0012
- Platform monitoring integration — spec-0013
- Mobile responsive layout — future iteration
- Markdown rendering in messages — future iteration
Manual steps (not performed by the agent)
Section titled “Manual steps (not performed by the agent)”Before merging the PR:
- Add GitHub Actions secret
TF_VAR_ANTHROPIC_API_KEYwith your Anthropic API key value in repository Settings → Secrets and variables → Actions.
After merging the PR:
-
Run
terraform applyto write the Anthropic API key to Key Vault. -
Force ESO sync to update the K8s secret immediately:
Terminal window az vm run-command invoke \--resource-group rg-kevinryan-io \--name vm-kevinryan-node1 \--command-id RunShellScript \--scripts "kubectl annotate externalsecret hq-auth0-secrets -n hq-kevinryan-io force-sync=$(date +%s) --overwrite && kubectl rollout restart deployment/hq-kevinryan-io -n hq-kevinryan-io 2>&1" -
Visit
https://hq.kevinryan.ioand confirm the chat interface loads. -
Send a message and confirm streaming response appears.
-
Toggle demo mode and confirm the
DEMObadge appears and sensitive topics are redacted.
Provenance Record
Section titled “Provenance Record”After completing the work, create .sdd/provenance/spec-0010-hq-chat-interface.provenance.md using the provenance template at .sdd/provenance/template.md.
Validation steps
Section titled “Validation steps”- This spec has been saved to
.sdd/specification/spec-0010-hq-chat-interface.md infra/variables.tfcontainsanthropic_api_keyvariable withsensitive = trueinfra/main.tfcontainsazurerm_key_vault_secret.anthropic_api_keyresource.github/workflows/terraform.ymlpassesTF_VAR_anthropic_api_keyin both plan and apply jobsk8s/hq-kevinryan-io/externalsecret.yamlcontains mapping forANTHROPIC_API_KEY→anthropic-api-keysites/hq-kevinryan-io/app/api/chat/route.tsexistssites/hq-kevinryan-io/app/api/chat/route.tsreturns401when no sessionsites/hq-kevinryan-io/app/components/ChatInterface.tsxexistssites/hq-kevinryan-io/app/page.tsximports and rendersChatInterfacesites/hq-kevinryan-io/package.jsoncontains@anthropic-ai/sdkpnpm-lock.yamlis updated to include@anthropic-ai/sdk- Demo mode toggle exists in the UI and passes
demoModeboolean to the API route - Base system prompt references Kevin Ryan & Associates and the operational context
- Demo system prompt extends base prompt with redaction instructions
pnpm buildpasses insidesites/hq-kevinryan-io/pnpm lintpasses- The provenance record exists at
.sdd/provenance/spec-0010-hq-chat-interface.provenance.md - All files are committed together in a single commit