14 iterations over 5 phases. Each version solved a specific problem but revealed the next one. This is the reasoning behind every change.
| Version | What We Did | Result | Why We Moved On |
|---|---|---|---|
| v1 | First per-KID PnL computation. Dark theme prototype. Basic tables. | Could see which lockers were negative for the first time. | No prediction capability. Just a snapshot of current state. |
| v2 | Light theme. Scientific language. AND-based filters. | Usable dashboard with age segmentation. | Still descriptive, not predictive. |
| v3 | Reactive charts. Select2 searchable dropdown (10K partners). CSV downloads. | Operational exploration tool. | "Which lockers WILL become negative?" remained unanswered. |
| v4 | Multi-tab redesign. Purpose-driven views. | Clean architecture ready for predictive layer. | Foundation complete. Time to predict. |
| Version | What We Did | Result | Why We Moved On |
|---|---|---|---|
| v5-v7 | Model Misses tab. Partner Problem View. Data integration fixes. Bug: JS in Python f-strings broke rendering (v7). | Infrastructure for predictive layer. Lesson learned on code architecture. | Needed actual prediction algorithm. |
| v8 | First predictive model: 2-window delta velocity (current 3mo vs prior 3mo). 6-window walk-forward validation. | 66% CRITICAL precision. | Too noisy. Summer dips triggered false positives because holiday volume "rescued" lockers in the validation window. Seasonal patterns were confounding the signal. |
| Version | What We Did | Result | Why We Moved On |
|---|---|---|---|
| v9 | Replaced delta with OLS regression. 6m + 12m dual-horizon slopes. R² significance filter (removes low-confidence trends). Extended data to 27 months. | 78% precision (+12pp). Dual-slope concordance eliminated single-window noise. | Using flat $1,057/mo ad revenue assumption for all commercial lockers. Reality: only 15% earn ad revenue. |
| v10 | Replaced flat ad assumption with actual per-KID attribution from Kiosk Summary (2,581 KIDs with real campaign data). | 84% precision (+6pp). Discovery: without ads 75% are negative, with ads only 40%. Loss estimate corrected from $7.6M to $29.5M. | 46% of model misses (false negatives) were caused by maintenance cost spikes that slopes structurally cannot detect. A $500 one-month repair doesn't show as a "slope." |
| v11 | Added per-KID maintenance from PRUNE (annual/12). Severity scoring (40% net + 35% slope + 25% R²). | ~80% precision. Identified 49% have $0 maintenance, 6% have >$100/mo (bimodal distribution). | Jul25 validation window: only 56.6% precision. Holiday volume (Oct-Dec) rescues summer-declining lockers. Annual/12 maintenance smooths out the monthly spikes we need to see. |
| Version | What We Did | Result | Why We Moved On |
|---|---|---|---|
| v12 | Three innovations: (1) Per-locker multiplicative seasonal detrending removes holiday cycles from slope estimation. (2) Ticket frequency as leading indicator. (3) Dual-path classification: slope OR maintenance — covers both failure modes. | 88% precision (+8pp). Jul25 improved 56.6%→71.1% (+14.5pp). Maintenance path alone: 93.3% precision. | Needed real MONTHLY maintenance costs (not annual/12). Needed to handle "fixable" lockers (just need ads activated) differently from structurally broken ones. |
| v13 | Five innovations: (1) Emerging Risk tier for still-positive degrading lockers. (2) Quality Gate: good DPMO + good tput + has ad rights → "Run Ads" not CRITICAL. (3) Actual vendor invoice maintenance (B&H + Velociti, 3-month rolling). (4) Seasonal-aware projections. (5) Comprehensive 5-window quarterly validation. | 95.6% CRITICAL, 93.8% HIGH, 92.8% combined. 70.9% remain negative every single month. Ad wrap cost fix: only charge $209 when campaign runs (not when wrap installed) — reduced loss estimate from ~$32M to ~$17M (real number). | Current production model. |
v14-AI extends the v13 prediction model with a conversational AI layer powered by Claude Sonnet 4.6 (tool-use architecture). The prediction model remains unchanged — v14-AI adds intelligence on top.
| Tool | Description |
|---|---|
| filter_lockers | Any combination of 16+ filters across the full 26K dataset (vertical, subtype, state, city, partner, tier, PnL range, ad status, tickets, maintenance, age, shipped volume, DPMO, offline, generation, EWS). |
| get_locker | Full single-locker deep-dive: PnL breakdown, model classification, time-series, EWS, tickets, offline history. |
| get_network_summary | High-level network health stats: tier distribution, loss exposure, ad opportunity, top/bottom performers. |
| compare_groups | Side-by-side comparison of two groups (e.g., "Commercial vs Residential", "With Ads vs Without Ads"). |
| search_locker_by_name | Fuzzy name/partner/address search with disambiguation (2-5 matches prompts user to specify). |
| simulate_scenario | What-if simulations: close lockers, activate ads, remove a partner, threshold-based closures. Before/after financial impact. |
| generate_partner_scorecard | QBR-ready partner analysis: portfolio breakdown, worst performers, strategic recommendations. Top 50 cached. |
| detect_anomalies | Statistical anomaly detection: sudden drops, maintenance spikes, volume collapse, DPMO explosions. |
| query_clusters | Geographic cluster analysis: identify hot spots of underperformance by area. |
| get_trend | Month-over-month PnL trend for any locker or group. Powers the inline sparkline charts. |
| add_note | Save collaborative team notes to DynamoDB (per KID or partner). Persistent across sessions. |
| get_notes | Retrieve team notes for context when discussing a KID/partner. Notes surface automatically. |
| Feature | Description |
|---|---|
| Progressive Rendering | Typewriter effect during AI responses. Uses textContent during stream, single renderMd() at completion to prevent layout thrash. |
| Inline Charts | When AI discusses 1-3 KIDs, Chart.js sparklines render inline showing PnL trend (green=positive, red=negative). Fixed 70px container. |
| Scheduled Digests | "What Changed" amber card on chat open. Compares current network snapshot to localStorage. Shows new CRITICAL/HIGH lockers, PnL shifts, profitability transitions. 1-hour debounce. |
| Collaborative Notes | 📝 button next to every KID in responses. Click to add notes persisted in DynamoDB. Auto-retrieved when KID discussed again. |
| Watchlist / Pins | Pin lockers for quick access. DynamoDB-backed (nerve-ai-pins). Available from chat header. |
| Multi-Session Chat | Chat history panel shows previous conversations. Start new chats, switch between sessions. History persisted in localStorage. |
| PDF Export | Print/PDF button on every page. Optimized print stylesheet hides interactive elements. |
| Map Integration | Leaflet + MarkerCluster network map. Radius search by zip/coordinates/click. Pin lockers. Area summary pop-ups. |
| Quick Actions | Pre-built prompts for common queries (worst lockers, ad opportunity, partner analysis) shown in chat panel. |
| Page Context | AI automatically knows which page you are on and includes relevant context (e.g., KID search passes locker data to AI). |
| Metric | Value | Notes |
|---|---|---|
| Cold Start | ~6-8 seconds | Lambda loads 30-40MB CSV from S3, parses into memory. First query after idle period. |
| Warm Query | 3-15 seconds | Depends on query complexity. Simple KID lookup ~3s, network-wide simulation ~15s. |
| Provisioned Concurrency | 0 (on-demand) | Cost optimization: $0 idle. Cold starts acceptable for internal tool. |
| Lambda Memory | 5 GB | Required for full dataset in memory + Bedrock response buffering. |
| Lambda Timeout | 5 minutes | Complex simulations (50K+ comparisons) can take 2-3 minutes. |
| Monthly Cost | ~$60-220 | Mostly Bedrock token costs. Well under $1K budget. Scales with usage. |
| Dashboard Generation | ~120 seconds | 20 HTML pages + chat data + full CSV export from Python generator. |
| Full Pipeline | ~8-10 minutes | Data load → model → EWS → clusters → dashboards → deploy. |
| Component | Detail |
|---|---|
| Flow | Page JS → Cognito Hosted UI → Federate OIDC → Midway → Token → Page Loads |
| Cognito User Pool | us-east-2_VPrE6QUOJ (nerve-sso-pool) |
| App Client | 5rr04qgb7ooab9i58unb7bcnoa |
| Federate Provider | FederateOIDC (idp-integ.federate.amazon.com) |
| Token Storage | sessionStorage (cleared on tab close — re-auth per session) |
| Self-Registration | Disabled (AdminCreateUserOnly) — Midway-authenticated Amazonians only |
| Component | Detail |
|---|---|
| AI Lambda | nerve-ai-summary (5GB, 5min, Python 3.12, tool-use architecture) |
| AI Model | Claude Sonnet 4.6 (us.anthropic.claude-sonnet-4-6) — cross-region inference via Bedrock |
| Alerts Lambda | nerve-daily-alerts (1GB, 2min) — daily deterioration scanning |
| Reports Lambda | nerve-weekly-report (2GB, 3min) — Monday 9AM ET via EventBridge |
| S3 | project-nerve (us-east-2) — dashboard files + nerve_full_data.csv + scorecards |
| DynamoDB | nerve-ai-memory (conversations), nerve-ai-pins (watchlist), nerve-ai-notes (team notes) |
| API Gateway | HTTPS endpoint for AI queries (cxoy20lw6j.execute-api.us-east-2.amazonaws.com) |
| Amplify | dpgfi1rgq3n2n / staging — static hosting with managed CloudFront |
| SNS | Email notifications for daily alerts and weekly reports |
| Component | Detail |
|---|---|
| Domain | nerve.parp.amazon.dev (SuperNova delegation) |
| Route 53 | Hosted Zone Z00940353I8G0GXWRGVWX |
| ALIAS Record | d3ixerfm6up1q2.cloudfront.net (Amplify-managed CloudFront) |
| Anti-Spoofing | SPF (v=spf1 -all) + DMARC (p=reject, sp=reject) |
| IAM Role | Nova-DO-NOT-DELETE (Route53FullAccess + SecurityAudit for domain audits) |
flowchart LR
subgraph Browser["Browser — nerve.parp.amazon.dev"]
direction TB
SSO["SSO Check
Cognito + Federate
→ Midway"]
UI["20 HTML Pages
DataTables, Chart.js
Leaflet Maps"]
CHAT["AI Chat Panel
Progressive render
Sparklines, Notes"]
end
subgraph Hosting["Static Hosting"]
AMP["AWS Amplify
dpgfi1rgq3n2n
staging branch"]
S3H["S3: project-nerve
dashboard files
nerve_chat_data.js"]
end
subgraph AI["AI Engine (us-east-2)"]
direction TB
APIG["API Gateway
HTTPS POST
/summary"]
LAM["Lambda
nerve-ai-summary
5GB, 5min"]
BED["Bedrock
Claude Sonnet 4.6
Tool-Use API"]
S3D["S3
nerve_full_data.csv
26,167 × 60+ cols"]
DDB["DynamoDB
nerve-ai-memory
nerve-ai-pins
nerve-ai-notes"]
end
subgraph Automation["Scheduled Jobs"]
ALERTS["nerve-daily-alerts
Lambda 1GB, 2min
→ SNS Email"]
REPORT["nerve-weekly-report
Lambda 2GB, 3min
Monday 9AM ET"]
end
Browser -->|"Static files"| Hosting
CHAT -->|"HTTPS POST"| APIG
APIG --> LAM
LAM -->|"Load data"| S3D
LAM -->|"12 tools"| BED
LAM -->|"Read/Write"| DDB
LAM -.->|"Trigger"| ALERTS
LAM -.->|"Trigger"| REPORT
style Browser fill:#f0f9ff,stroke:#3b82f6
style Hosting fill:#f0fdf4,stroke:#059669
style AI fill:#fff7ed,stroke:#f97316
style Automation fill:#faf5ff,stroke:#7c3aed
flowchart LR
A["User visits
nerve.parp.amazon.dev"] --> B{"Token in
sessionStorage?"}
B -->|No| C["Redirect →
Cognito Hosted UI"]
C --> D["Federate OIDC
idp-integ.federate"]
D --> E["Midway
Authentication"]
E --> F["Auth Code
returned to page"]
F --> G["Exchange code
for id_token"]
G --> H["Store token
sessionStorage"]
H --> I["Page loads
✅ Authenticated"]
B -->|Yes| I
style A fill:#f0f9ff,stroke:#3b82f6
style I fill:#f0fdf4,stroke:#059669
style E fill:#fef3c7,stroke:#d97706
flowchart TD
Q["User asks question"] --> EX["Extract KIDs/names
from question text"]
EX --> CTX["Build context:
page, locker data,
conversation history"]
CTX --> SEND["POST to Lambda
via API Gateway"]
SEND --> LOAD["Load nerve_full_data.csv
(cached on warm start)"]
LOAD --> TOOLS["Claude selects tool:
filter_lockers, get_locker,
simulate_scenario, etc."]
TOOLS --> EXEC["Execute tool against
26K dataset in memory"]
EXEC --> RESP["Format response
with real data"]
RESP --> RENDER["Progressive rendering
(typewriter effect)"]
RENDER --> CHART{"Response has
1-3 KIDs?"}
CHART -->|Yes| SPARK["Render inline
sparkline chart"]
CHART -->|No| DONE["Display final
markdown response"]
SPARK --> DONE
style Q fill:#f0f9ff,stroke:#3b82f6
style TOOLS fill:#fff7ed,stroke:#f97316
style DONE fill:#f0fdf4,stroke:#059669
flowchart LR
A["python
refresh_all_v14.py"] --> B["Merlon Auth
+ STS verify"]
B --> C["Steps 1-6
Data Pipeline
~6 min"]
C --> D["Step 7
Generate Dashboard
20 HTML pages"]
D --> E["Step 8
S3 sync +
CSV upload"]
E --> F["Step 9
Amplify Deploy
(auto-stop stuck)"]
F --> G["Step 10
Lambda Deploy
code + config"]
G --> H["Step 11
Trigger Alerts
+ Weekly Report"]
H --> I["✅ LIVE
nerve.parp.amazon.dev"]
style A fill:#1e3a5f,stroke:#1e3a5f,color:#fff
style I fill:#059669,stroke:#059669,color:#fff