feat: add canonical profile content schema and access helpers

This commit is contained in:
2026-02-16 23:32:25 +00:00
parent 8178d03cb2
commit 6605966fab
6 changed files with 338 additions and 0 deletions
+12
View File
@@ -2,6 +2,10 @@
## Patterns ## Patterns
### mem-1771284742-cc7f
> Stage 1 canonical descriptive text foundation now exists in src/data/profile-content.ts with strict schema in src/types/profile-content.ts and typed selectors in src/lib/profile-content.ts; this is additive and preserves existing consumer behavior until migration checkpoints.
<!-- tags: data, content, refactor | created: 2026-02-16 -->
### mem-1771245168-48e8 ### mem-1771245168-48e8
> Canonical timeline data now lives in src/data/timeline.ts and legacy consultations/constellation exports are compatibility layers derived from it, removing duplicated date/year maintenance. > Canonical timeline data now lives in src/data/timeline.ts and legacy consultations/constellation exports are compatibility layers derived from it, removing duplicated date/year maintenance.
<!-- tags: data, timeline, consistency | created: 2026-02-16 --> <!-- tags: data, timeline, consistency | created: 2026-02-16 -->
@@ -30,6 +34,14 @@
## Fixes ## Fixes
### mem-1771284167-9e36
> failure: cmd=sed -n '1,260p' ralph/prompt.md, exit=2, error=No such file or directory, next=locate actual prompt path with rg --files and use correct casing/location
<!-- tags: tooling, error-handling, ralph | created: 2026-02-16 -->
### mem-1771284072-053c
> failure: cmd=sed -n '1,260p' Ralph/PROMPT.md, exit=2, error=No such file or directory (path case mismatch), next=use lowercase path ralph/prompt.md
<!-- tags: tooling, error-handling, ralph | created: 2026-02-16 -->
### mem-1771246458-9388 ### mem-1771246458-9388
> failure: cmd=rg -n "--font-mono\b|font-mono-dashboard|font-geist-mono|timeline-intervention|chronology|pathway" src/index.css, exit=2, error=pattern beginning with -- parsed as flag, next=use rg -n -- '<pattern>' <file> > failure: cmd=rg -n "--font-mono\b|font-mono-dashboard|font-geist-mono|timeline-intervention|chronology|pathway" src/index.css, exit=2, error=pattern beginning with -- parsed as flag, next=use rg -n -- '<pattern>' <file>
<!-- tags: tooling, error-handling, search | created: 2026-02-16 --> <!-- tags: tooling, error-handling, search | created: 2026-02-16 -->
+18
View File
@@ -0,0 +1,18 @@
# Scratchpad
## 2026-02-16T23:27:00Z — Planner Stage 1 framing
- Confirmed next unchecked rollout stage is Stage 1 in `Ralph/PROMPT.md`.
- Inventory completed across `src/data`, `src/components`, and search/chat builders. Major duplication centers are component literals (`PatientSummaryTile`, `Sidebar`) and builder-layer hardcoded text (`search.ts`, `llm.ts`) versus existing canonical domain data.
- Planned Stage 1 as schema + canonical module + typed access helpers + thin adapters only, with no component migration yet, to minimize regression risk.
- Confidence: 92/100. High confidence because scope is additive and preserves existing export contracts while preparing Stage 2/3 migrations.
## 2026-02-16T23:33:00Z — Builder Stage 1 Checkpoint A complete
- Implemented additive canonical text foundation only (no consumer rewires):
- `src/types/profile-content.ts` defines typed schema for profile, sidebar copy, results, education entries, quick actions, and LLM prompt content.
- `src/data/profile-content.ts` is now the single editable canonical module seeded with current descriptive literals from `PatientSummaryTile`, `Sidebar`, `search.ts`, and `llm.ts`.
- `src/lib/profile-content.ts` adds typed selectors (`getProfileContent`, `getProfileSummaryText`, `getSidebarCopy`, `getSearchQuickActions`, `getLLMCopy`).
- Quality gates passed:
- `npm run lint` (warnings only, no errors)
- `npm run typecheck`
- `npm run build`
- Confidence: 91/100. Safe additive checkpoint with no runtime behavior changes.
+2
View File
@@ -33,3 +33,5 @@
{"id":"task-1771251477-81a2","title":"Visual: Entry animation reveal effects","description":"Role nodes scale from 0 with ease-out-back. New skill nodes scale from 0 with ease-out. Links draw on via stroke-dashoffset. Year indicator top-left monospace. Replace Phase 2 entry animation with timeline-driven animation.","status":"closed","priority":2,"blocked_by":[],"loop_id":"primary-20260216-135722","created":"2026-02-16T14:17:57.098724221+00:00","closed":"2026-02-16T14:31:18.740781337+00:00"} {"id":"task-1771251477-81a2","title":"Visual: Entry animation reveal effects","description":"Role nodes scale from 0 with ease-out-back. New skill nodes scale from 0 with ease-out. Links draw on via stroke-dashoffset. Year indicator top-left monospace. Replace Phase 2 entry animation with timeline-driven animation.","status":"closed","priority":2,"blocked_by":[],"loop_id":"primary-20260216-135722","created":"2026-02-16T14:17:57.098724221+00:00","closed":"2026-02-16T14:31:18.740781337+00:00"}
{"id":"task-1771251479-1473","title":"Integration: Wire animation to highlight system (Phase 4)","description":"Multiplicative opacity: animation(0/target) × highlight(1.0/0.15). Hover/tap pauses animation. Highlight only on revealed nodes. Resume 800ms after interaction ends. Explicit pause via button stays paused. Play/pause toggle bottom-right. Mobile accordion + keyboard nav + detail panel work during pause. prefers-reduced-motion shows final state.","status":"closed","priority":2,"blocked_by":[],"loop_id":"primary-20260216-135722","created":"2026-02-16T14:17:59.857204916+00:00","closed":"2026-02-16T14:31:18.838001047+00:00"} {"id":"task-1771251479-1473","title":"Integration: Wire animation to highlight system (Phase 4)","description":"Multiplicative opacity: animation(0/target) × highlight(1.0/0.15). Hover/tap pauses animation. Highlight only on revealed nodes. Resume 800ms after interaction ends. Explicit pause via button stays paused. Play/pause toggle bottom-right. Mobile accordion + keyboard nav + detail panel work during pause. prefers-reduced-motion shows final state.","status":"closed","priority":2,"blocked_by":[],"loop_id":"primary-20260216-135722","created":"2026-02-16T14:17:59.857204916+00:00","closed":"2026-02-16T14:31:18.838001047+00:00"}
{"id":"task-1771251482-f0e9","title":"Accessibility: reduced-motion + play/pause button","description":"prefers-reduced-motion skips animation entirely, shows final state. Play/pause button with aria-label, subtle styling, larger touch target on mobile.","status":"closed","priority":2,"blocked_by":[],"loop_id":"primary-20260216-135722","created":"2026-02-16T14:18:02.061675075+00:00","closed":"2026-02-16T14:31:18.930889962+00:00"} {"id":"task-1771251482-f0e9","title":"Accessibility: reduced-motion + play/pause button","description":"prefers-reduced-motion skips animation entirely, shows final state. Play/pause button with aria-label, subtle styling, larger touch target on mobile.","status":"closed","priority":2,"blocked_by":[],"loop_id":"primary-20260216-135722","created":"2026-02-16T14:18:02.061675075+00:00","closed":"2026-02-16T14:31:18.930889962+00:00"}
{"id":"task-1771284229-34e9","title":"Plan Stage 1 canonical content schema","description":"Planner hat: identify next unchecked stage and update .ralph/plan.md with scoped file-level migration plan","status":"closed","priority":1,"blocked_by":[],"loop_id":"primary-20260216-232330","created":"2026-02-16T23:23:49.734442590+00:00","closed":"2026-02-16T23:29:43.798920375+00:00"}
{"id":"task-1771284608-2942","title":"Stage1 Checkpoint A: add canonical profile content schema/module/helpers","description":"Create src/types/profile-content.ts, src/data/profile-content.ts, and src/lib/profile-content.ts with typed, centralized descriptive text and selectors; no consumer migration in this task.","status":"closed","priority":1,"blocked_by":[],"loop_id":"primary-20260216-232330","created":"2026-02-16T23:30:08.665923960+00:00","closed":"2026-02-16T23:32:25.469667619+00:00"}
+211
View File
@@ -0,0 +1,211 @@
import type { ProfileContent } from '@/types/profile-content'
export const profileContent = {
profile: {
patientSummaryNarrative: 'Healthcare leader combining clinical pharmacy expertise with proficiency in Python, SQL, and data analytics, self-taught over the past decade through a drive to find root causes in data and build the most efficient solutions to complex problems. Currently leading population health analytics for NHS Norfolk & Waveney ICB, serving a population of 1.2 million. Experienced in working with messy, real-world prescribing data at scale to deliver actionable insights, from financial scenario modelling and pharmaceutical rebate negotiation to algorithm design and population-level pathway development. Proven track record of identifying and prioritising efficiency programmes worth £14.6M+ through automated, data-driven analysis. Skilled at translating complex clinical, financial, and analytical requirements into clear recommendations for executive stakeholders.',
latestResults: {
title: 'LATEST RESULTS (CLICK TO VIEW FULL REFERENCE RANGE)',
rightText: 'Updated May 2025',
helperText: 'Select a metric to inspect methodology, impact, and outcomes.',
evidenceCta: 'Click to view evidence',
},
sidebar: {
sectionTitle: 'Patient Data',
roleTitle: 'Pharmacy Data Technologist',
gphcLabel: 'GPhC No.',
educationLabel: 'Education',
locationLabel: 'Location',
phoneLabel: 'Phone',
emailLabel: 'Email',
registeredLabel: 'Registered',
navigationTitle: 'Navigation',
tagsTitle: 'Tags',
alertsTitle: 'Alerts / Highlights',
searchLabel: 'Search',
searchAriaLabel: 'Search. Press Control plus K',
searchShortcut: 'Ctrl+K',
menuLabel: 'Menu',
},
},
experienceEducation: {
educationEntries: [
{
title: 'NHS Leadership Academy — Mary Seacole Programme',
subtitle: 'NHS Leadership Academy · 2018',
keywords: 'nhs leadership academy mary seacole programme 2018 qualification management',
},
{
title: 'MPharm (Hons) — 2:1',
subtitle: 'University of East Anglia · 20112015',
keywords: 'mpharm hons 2:1 university east anglia uea 2011 2015 pharmacy degree',
},
{
title: 'A-Levels',
subtitle: 'Highworth Grammar School · 20092011',
keywords: 'a-levels mathematics chemistry politics highworth grammar school 2009 2011',
},
{
title: 'GPhC Registration',
subtitle: 'General Pharmaceutical Council · August 2016',
keywords: 'gphc registration general pharmaceutical council 2016 registered pharmacist',
},
],
},
skillsNarrative: {
summary: 'Technical, domain, and leadership capabilities spanning data analysis, medicines optimisation, and executive communication with practical delivery across population-scale NHS prescribing programmes.',
},
resultsNarrative: {
achievements: [
{
title: '£14.6M Efficiency Savings Identified',
subtitle: 'Data-driven prescribing interventions',
keywords: '14.6m efficiency savings identified data-driven prescribing interventions money cost',
kpiId: 'savings',
},
{
title: '£220M Budget Oversight',
subtitle: 'Full analytical accountability to ICB board',
keywords: '220m budget oversight analytical accountability icb board',
kpiId: 'budget',
},
{
title: 'Power BI Dashboards for 200+ Users',
subtitle: 'Clinicians & commissioners across ICB',
keywords: 'power bi dashboards 200 users clinicians commissioners',
kpiId: 'years',
},
{
title: '1.2M Population Served',
subtitle: 'Norfolk & Waveney Integrated Care System',
keywords: '1.2m population served norfolk waveney ics integrated care system',
kpiId: 'population',
},
],
},
searchChat: {
quickActions: [
{
title: 'Download CV',
subtitle: 'Export as PDF',
keywords: 'download cv export pdf resume',
type: 'download',
},
{
title: 'Send Email',
subtitle: 'andy@charlwood.xyz',
keywords: 'send email contact andy charlwood',
type: 'link',
url: 'mailto:andy@charlwood.xyz',
},
{
title: 'View LinkedIn',
subtitle: 'Professional profile',
keywords: 'view linkedin professional profile social',
type: 'link',
url: 'https://linkedin.com/in/andycharlwood',
},
{
title: 'View Projects',
subtitle: 'GitHub & portfolio',
keywords: 'view projects github portfolio code repositories',
type: 'link',
url: 'https://github.com/andycharlwood',
},
],
llm: {
systemPrompt: `You are a helpful assistant on Andy Charlwood's portfolio website. Answer questions about Andy's professional background using ONLY the information below.
## Profile
Andy Charlwood — MPharm, GPhC Registered Pharmacist. Norwich, UK.
Healthcare leader combining clinical pharmacy with Python, SQL, and data analytics (self-taught). Leading population health analytics for NHS Norfolk & Waveney ICB, serving 1.2M people. Specialises in prescribing data at scale — financial modelling, algorithm design, pathway development. Identified efficiency programmes worth £14.6M+ through automated analysis.
## Employment Timeline (IMPORTANT)
- **NHS employment**: May 2022present (all roles at NHS Norfolk & Waveney ICB). Total NHS service: ~4 years.
- **Private sector**: Nov 2017May 2022 at Tesco PLC (community pharmacy). This was NOT NHS employment.
- GPhC registration (Aug 2016) is a professional licence, NOT an employer or NHS role.
## Career History
### [exp-interim-head-2025] Interim Head, Population Health & Data Analysis
NHS Norfolk & Waveney ICB | MayNov 2025
Led population health initiatives and data-driven medicines optimisation, reporting to Associate Director of Pharmacy with accountability to CMO.
- Identified £14.6M efficiency programme; achieved over-target performance by October 2025
- Built Python switching algorithm: real-world GP prescribing data, 14,000 patients, £2.6M annual savings (£2M on target), compressed months into 3 days
- Novel GP payment system linking rewards to savings; 50% prescribing reduction within 2 months
- Presented to CMO bimonthly; led transformation to patient-level SQL analytics
### [exp-deputy-head-2024] Deputy Head, Population Health & Data Analysis
NHS Norfolk & Waveney ICB | Jul 2024Present (substantive role)
Data analytics strategy for medicines optimisation from real-world GP prescribing data.
- Managed £220M prescribing budget with forecasting models for proactive financial planning
- Created comprehensive dm+d medicines data table: standardised strengths, morphine equivalents, Anticholinergic Burden scoring — single source of truth for all medicines analytics
- Led DOAC switching financial modelling: interactive dashboard with rebate mechanics, patent expiry timelines
- Renegotiated pharmaceutical rebate terms ahead of patent expiry
- Tirzepatide commissioning (NICE TA1026): financial projections, cohort identification; authored executive paper advocating primary care model, driving system shift to GP-led delivery
- Built Python controlled drug monitoring: oral morphine equivalents across all opioid prescriptions, patient-level tracking, high-risk identification, diversion detection
- Improved team data fluency through training and self-serve tools
### [exp-high-cost-drugs-2022] High-Cost Drugs & Interface Pharmacist
NHS Norfolk & Waveney ICB | May 2022Jul 2024
Led NICE TA implementation and high-cost drug pathways across the ICS. Pathways spanning: rheumatology, ophthalmology (wet AMD, DMO, RVO), dermatology, gastroenterology, neurology, migraine.
- Blueteq automation: 70% form reduction, 200 hours immediate savings, 78 hours ongoing weekly gains
- Integrated Blueteq with secondary care databases for accurate high-cost drug spend tracking
- Python Sankey chart tool for patient pathway visualisation and trust compliance auditing
### [exp-pharmacy-manager-2017] Pharmacy Manager
Tesco PLC (private sector, NOT NHS) | Nov 2017May 2022
Community pharmacy with full operational autonomy (100-hour contract). LPC representative for Norfolk.
- Asthma screening process adopted nationally (~300 branches): reduced pharmacist time 60→6 hours/store/month, ~£1M revenue
- Leadership training: Created national induction training plan and eLearning modules for Tesco pharmacy staff
- Leadership development: Supervised two staff through NVQ3 to pharmacy technician registration; full HR responsibilities
## Projects
### [proj-inv-pharmetrics] PharMetrics Interactive Platform (2024, Live)
Real-time medicines expenditure dashboard for NHS decision-makers. Tech: Power BI, SQL, DAX. Tracks £220M prescribing budget.
### [proj-inv-switching-algorithm] Patient Switching Algorithm (2025, Complete)
Python algorithm using GP prescribing data to auto-identify patients for cost-effective alternatives. Tech: Python, Pandas, SQL. 14,000 patients, £2.6M annual savings, novel GP payment system.
### [proj-inv-blueteq-gen] Blueteq Generator (2023, Complete)
Automated Blueteq prior approval form creation. Tech: Python, SQL. 70% form reduction, 200 hours immediate savings, 78 hours ongoing weekly gains.
### [proj-inv-cd-monitoring] CD Monitoring System (2024, Complete)
Controlled drug monitoring calculating oral morphine equivalents (OME) across all opioid prescriptions. Tech: Python, SQL. Patient-level tracking, high-risk identification, diversion detection.
### [proj-inv-sankey-tool] Sankey Chart Analysis Tool (2023, Complete)
Patient journey visualisation through high-cost drug pathways. Tech: Python, Matplotlib, SQL. Trust compliance auditing.
## Education
### [edu-0] NHS Mary Seacole Programme (2018)
NHS Leadership Academy. Score: 78%. Covers change management, healthcare leadership, system-level thinking.
### [edu-1] MPharm (Hons) 2:1 — University of East Anglia (20112015)
4-year integrated Master's degree. Research project on drug delivery and cocrystals: 75.1% (Distinction).
### [edu-2] A-Levels — Highworth Grammar School (20092011)
Mathematics A*, Chemistry B, Politics C.
### [edu-3] GPhC Registration — General Pharmaceutical Council (August 2016Present)
Professional registration required to practise as a pharmacist in Great Britain.
## Skills
Technical: [skill-data-analysis] Data Analysis (9yr, 95%), [skill-python] Python (6yr, 90%), [skill-sql] SQL (7yr, 88%), [skill-power-bi] Power BI (5yr, 92%), [skill-javascript-typescript] JavaScript/TypeScript (3yr, 70%), [skill-excel] Excel (9yr, 85%), [skill-algorithm-design] Algorithm Design (3yr, 82%), [skill-data-pipelines] Data Pipelines (2yr, 75%)
Domain: [skill-medicines-optimisation] Medicines Optimisation (9yr, 95%), [skill-population-health] Population Health (3yr, 90%), [skill-nice-ta] NICE TA Implementation (3yr, 92%), [skill-health-economics] Health Economics (3yr, 80%), [skill-clinical-pathways] Clinical Pathways (3yr, 88%), [skill-controlled-drugs] Controlled Drugs (1yr, 85%)
Leadership: [skill-budget-management] Budget Management (1yr, 90%), [skill-stakeholder-engagement] Stakeholder Engagement (3yr, 88%), [skill-pharma-negotiation] Pharmaceutical Negotiation (1yr, 82%), [skill-team-development] Team Development (8yr, 85%), [skill-change-management] Change Management (7yr, 80%), [skill-financial-modelling] Financial Modelling (1yr, 78%), [skill-executive-comms] Executive Communication (1yr, 85%)
## Response Rules
1. Answer ONLY from the data above. If the answer is not in the data, say "I don't have that information" — never invent facts, roles, dates, achievements, URLs, or contact details.
2. Distinguish NHS employment (May 2022present, ~4 years, all at Norfolk & Waveney ICB) from private sector (Tesco PLC, Nov 2017May 2022, community pharmacy). Never conflate the two. GPhC registration is a professional licence, not NHS employment.
3. When asked broad questions about tools, skills, projects, or achievements across Andy's career, aggregate from ALL roles — do not limit your answer to one position.
4. Cite exact numbers, dates, percentages, and outcomes. Never say "approximately" or "around" when exact figures exist in the data.
5. For detailed or list-based questions, give a thorough answer covering all relevant items. For simple questions, be concise (2-4 sentences).
## Item References
End your response with a single line listing relevant item IDs from the square-bracketed IDs above:
[ITEMS: exp-deputy-head-2024, skill-python]
Only include IDs that directly support your answer. Omit the line if none are relevant.`,
},
},
} as const satisfies ProfileContent
+27
View File
@@ -0,0 +1,27 @@
import { profileContent } from '@/data/profile-content'
import type {
LLMCopy,
ProfileContent,
QuickActionCopyEntry,
SidebarCopy,
} from '@/types/profile-content'
export function getProfileContent(): ProfileContent {
return profileContent
}
export function getProfileSummaryText(): string {
return profileContent.profile.patientSummaryNarrative
}
export function getSidebarCopy(): SidebarCopy {
return profileContent.profile.sidebar
}
export function getSearchQuickActions(): ReadonlyArray<QuickActionCopyEntry> {
return profileContent.searchChat.quickActions
}
export function getLLMCopy(): LLMCopy {
return profileContent.searchChat.llm
}
+68
View File
@@ -0,0 +1,68 @@
export interface AchievementCopyEntry {
title: string
subtitle: string
keywords: string
kpiId: string
}
export interface EducationCopyEntry {
title: string
subtitle: string
keywords: string
}
export interface QuickActionCopyEntry {
title: string
subtitle: string
keywords: string
type: 'download' | 'link'
url?: string
}
export interface SidebarCopy {
sectionTitle: string
roleTitle: string
gphcLabel: string
educationLabel: string
locationLabel: string
phoneLabel: string
emailLabel: string
registeredLabel: string
navigationTitle: string
tagsTitle: string
alertsTitle: string
searchLabel: string
searchAriaLabel: string
searchShortcut: string
menuLabel: string
}
export interface LLMCopy {
systemPrompt: string
}
export interface ProfileContent {
profile: {
patientSummaryNarrative: string
latestResults: {
title: string
rightText: string
helperText: string
evidenceCta: string
}
sidebar: SidebarCopy
}
experienceEducation: {
educationEntries: ReadonlyArray<EducationCopyEntry>
}
skillsNarrative: {
summary: string
}
resultsNarrative: {
achievements: ReadonlyArray<AchievementCopyEntry>
}
searchChat: {
quickActions: ReadonlyArray<QuickActionCopyEntry>
llm: LLMCopy
}
}