Vertex Macro | Financial Cloud Cloud · Builder Articles
Build with Kiro: Create the Automation Analytics Engine Behind a Factory Automation Portal
Educational engineering workshop only. This is a software architecture exercise and not process-release advice.
Summary: This standalone workshop teaches developers to rebuild the JavaScript analytics and interaction logic from the latest Factory Automation Portal using Kiro, TypeScript, pure process-window functions, SVG chart generators, tests, and controlled live simulation. Developers implement process deltas, drift, signal volatility, efficiency ratio, P05 delta, best delta, worst delta, window rate, sorting, search filtering, sparkline paths, process-window index curves, drift waterlines, histograms, status-tile charts, tab interaction preservation, and update-loop safety with complete prototype coverage and review tasks.
Workshop purpose
This 2-hour workshop focuses on the analytics and interaction engine behind the Factory Automation Portal. Instead of emphasizing UI styling, it extracts the prototype JavaScript behavior into testable TypeScript modules. Developers learn how to use Kiro to design deterministic process-window functions, test edge cases, explain metric meaning, preserve status-tile and leaderboard behavior, and keep live-update simulation clearly bounded as behavior.
The learning goal mirrors the original analytics-engine workshop structure: capture every JavaScript helper, define professional domain names for short prototype functions, write deterministic tests, and protect the chart geometry from regressions. The content is fully updated to the latest portal.
Coverage map
This workshop covers these portal implementation details:
● runtimeCards array with six runtime workbench entries: Synchronous Runtime, Streaming Runtime, Large Payload Runtime, Payload Validation, Smoke Tests, Session Cleanup.
● tools array with four tools: throughput_throughput, metrology_drift_widening, process_window_drift, and tool_to_tool_mismatch.
● specialists array with four governed agents: throughput, metrology, overlay, and etch-process.
● runbook array with eight timeline steps from 0-10 through 115-120.
● Process-window row array P with 8 automation cells, process patterns, Index %, Delta %, Stability, PF, WR, Skew, Max Drift, and indexed series.
● Status tile array M with Runtime, Tools, Policy, and Eval lanes, movement values, readiness state, and mini-series.
● Formatting helpers: sg, cl, and id for signed values, semantic classes, and detail-panel IDs.
● Calculation helpers: ret, dd, avg, sd, and perc, mapped to professional TypeScript functions.
● SVG helpers: path, spark, gridlines, eqChart, ddChart, and hist.
● Detail-panel builder panel, including derived advanced metrics and architecture automation notes.
● Search and sort behavior in data.
● Row rendering and open-panel preservation in render.
● Analysis toggle behavior in tog.
● Status tile rendering in markets.
● Live HKT clock in clock.
● Five-second simulated process-window update loop.
Target developers
● TypeScript developers converting dashboard math into tested modules.
● Developers learning deterministic calculation patterns for automation portals.
● Frontend developers who need reproducible chart path generation.
● Platform engineers learning how Kiro can review math, edge cases, simulation behavior, and interactive dashboard parity.
Two-hour agenda
| Time | Module | Developer output |
|---|---|---|
| 0:00-0:10 | Extract requirements | analytics inventory from portal HTML |
| 0:10-0:25 | Kiro steering | metric sign, units, test, and simulation rules |
| 0:25-0:45 | Metric engine | deltas, drift, volatility, percentile, efficiency ratio |
| 0:45-1:05 | Aggregation engine | search, sort, panel-state preservation, status stats |
| 1:05-1:25 | SVG chart engine | sparkline, index curve, drift waterline, histogram paths |
| 1:25-1:45 | Live simulation | controlled update loop and state safety |
| 1:45-1:55 | Tests | unit, fixture, and property-style tests |
| 1:55-2:00 | Kiro review | edge-case and production-hardening backlog |
Architecture
src/domain/
├─ types.ts # AutomationCell, StatusTile, RuntimeCard, ToolCard, SpecialistCard, RunbookStep, Metrics, SortKey
├─ fixtures.ts # P, M, runtimeCards, tools, specialists, runbook copied from the portal
├─ formatting.ts # signed percentages, semantic classes, slug IDs
├─ calculations.ts # deltas, drift, volatility, percentile, automation metrics
├─ portalAnalytics.ts # search, sort, stats, row models, panel models
├─ charts.ts # SVG path and chart model functions
├─ simulator.ts # deterministic update loop helpers
├─ chartAudit.ts # chart metadata for review
└─ __tests__/
├─ calculations.test.ts
├─ portalAnalytics.test.ts
├─ charts.test.ts
├─ simulator.test.ts
├─ fixtures.test.ts
└─ chartAudit.test.ts
Automation metric definitions and review usage
| Metric | formula or interpretation | Engineering review usage |
|---|---|---|
| Window Index | (last index / first index - 1) * 100. |
Measures cumulative movement across the displayed process-window sample. |
| Daily Delta | (today index / previous index - 1) * 100. |
Shows short-term movement and drives histogram bars. |
| Signal Volatility | Standard deviation of daily deltas multiplied by sqrt(252). |
Compares signal intensity across automation cells. |
| Drift | (current index / running peak - 1) * 100. |
Detects how far a cell is below its local high-water mark. |
| Max Drift | Minimum drift over the series. | Used as a process-window review pressure indicator. |
| Efficiency Ratio | Window Index divided by absolute Max Drift. | Compares index movement versus drift pressure in the. |
| P05 Delta | Fifth percentile of daily deltas in the simplified. | Highlights lower-tail movement for review discussion. |
| Best Delta | Maximum daily delta. | Identifies largest upward single-period move. |
| Worst Delta | Minimum daily delta. | Identifies largest downward single-period move. |
| Window Rate | Positive daily deltas divided by total deltas. | Measures consistency, not causal quality. |
| Process Factor | row quality metric from automation-cell data, displayed as PF. | Supports row comparison in the portal, but does not imply automated action. |
| Stability | row quality score, displayed as Stability. | Supports ranking and comparison across cells. |
| Skew | row asymmetry indicator. | Flags asymmetry that reviewers may inspect with chart metadata. |
Step 1 — Add Kiro steering for process-window calculations
Create .kiro/steering/process-math.md:
# Process math steering
- Treat all displayed deltas and index movements as percentage points, not decimals.
- Drift must be zero or negative.
- P05 Delta is the fifth percentile of daily deltas for this.
- Signal volatility uses sqrt(252) annualization to mirror the prototype calculation style.
- Keep all calculation functions pure and side-effect free.
- Do not invent live equipment data or process-release claims.
- simulation must be clearly labeled as simulated behavior.
- Preserve original portal values and series unless the workshop explicitly asks to create synthetic fixtures.
Create .kiro/steering/testing.md:
# Testing steering
Every process-window formula needs deterministic unit tests.
Use property-style tests for invariants such as drift <= 0.
Test empty arrays, one-point arrays, flat index, all-up index, all-down index, invalid index, and extreme drift.
Use injected random values for simulation tests.
Validate all eight automation-cell series and all four status-tile series from the portal.
Prompt sample for Kiro
Create a spec for extracting the latest factory automation portal JavaScript into TypeScript analytics modules. Include process-window formula definitions, sort and search behavior, SVG path generation, live simulation behavior, fixture parity, status-tile analytics, tests, edge cases, and production-hardening tasks.
Business logic: The steering defines metric meaning before code generation. Developers need consistent units and sign conventions to avoid misleading rankings.
Code logic: Kiro uses the steering rules to generate pure functions and tests rather than embedding calculations inside UI rendering.
Expected result: Kiro produces a requirements/design/tasks flow for the automation analytics engine.
System design rationale:
● Process math steering is mandatory because the prototype includes short helper names such as ret, dd, and perc; production developers need explicit meaning.
● Testing steering is separated from math steering because test coverage is an engineering policy, not a formula.
● The no-live-data-invention rule keeps the workshop honest. Simulated data can be used only when clearly labeled as behavior.
Step 2 — Define types
Create src/domain/types.ts:
export type SortKey = 'indexPct' | 'deltaPct' | 'stability' | 'processFactor' | 'windowRatePct' | 'maxDriftPct';
export type RuntimeCard = {
title: string;
body: string;
tags: string[];
};
export type ToolCard = {
name: string;
desc: string;
signals: string[];
};
export type SpecialistCard = {
name: string;
focus: string;
tools: string;
};
export type RunbookStep = [string, string, string];
export type AutomationCell = {
name: string;
strategy: string;
indexPct: number;
deltaPct: number;
stability: number;
processFactor: number;
windowRatePct: number;
skew: number;
maxDriftPct: number;
series: number[];
};
export type AutomationMetrics = {
windowIndexPct: number;
signalVolPct: number;
efficiencyRatio: number;
p05DeltaPct: number;
bestDeltaPct: number;
worstDeltaPct: number;
windowRatePct: number;
maxDriftPct: number;
};
export type StatusTile = {
key: 'RUNTIME' | 'GATEWAY' | 'POLICY' | 'EVAL';
move: string;
state: 'READY' | 'WATCH';
up: boolean;
series: number[];
};
Business logic: Types document which values are raw input and which values are derived analytics. They also preserve the non-row portal data needed by Runtime, Tools, Governed, and Runbook panels.
Code logic: SortKey models the 's sort buttons. AutomationCell mirrors the process-window row object and AutomationMetrics mirrors the detail panel boxes.
Expected result: TypeScript catches invalid sort keys, missing fixture fields, or incomplete portal panel data during development.
System design rationale:
● Explicit types replace implicit JavaScript object shapes.
● Sort keys use domain names instead of UI labels like INDEX, PF, or DRIFT, making code easier to understand while preserving labels at the presentation layer.
● Derived metrics are separated from automation-cell input because values such as P05 Delta and efficiency ratio are computed from series, not manually maintained.
Step 3 — Copy portal fixtures exactly
Create src/domain/fixtures.ts:
import type { AutomationCell, RuntimeCard, RunbookStep, SpecialistCard, StatusTile, ToolCard } from './types';
export const runtimeCards: RuntimeCard[] = [
{ title: 'Synchronous Runtime', body: 'runtime entrypoint in app.py validates prompt, injects request and session context, runs an AI assistant backed by Amazon Bedrock, and returns structured engineering analysis.', tags: ['app.py', 'FactoryAutomationApp', 'boto3 invoke'] },
{ title: 'Streaming Runtime', body: 'Async entrypoint yields incremental chunks from agent.stream_async so portals and chat interfaces can render analysis progressively.', tags: ['app_streaming.py', 'async', 'partial output'] },
{ title: 'Large Payload Runtime', body: 'Base64 Excel and image fields are decoded into typed document and image content payloads for combined factory engineering analysis.', tags: ['xlsx', 'png', 'base64'] },
{ title: 'Payload Validation', body: 'Local validators check required prompt field, optional metadata, and unknown top-level fields before runtime invocation.', tags: ['JSON Schema', 'fail fast', 'client contract'] },
{ title: 'Smoke Tests', body: 'Deterministic tests validate payload contract and Python calculation tools without model calls, credentials, or latency.', tags: ['pytest', 'deterministic', 'local'] },
{ title: 'Session Cleanup', body: 'Runtime sessions are treated as managed resources and stopped explicitly after workflow completion.', tags: ['session ID', 'cleanup', 'operations'] },
];
export const tools: ToolCard[] = [
{ name: 'throughput_throughput', desc: 'Assess WIP queue stress, tool availability pressure, wafer throughput, and hot-lot preference.', signals: ['queue depth', 'tool availability', 'wafer throughput', 'hot-lot preference'] },
{ name: 'metrology_drift_widening', desc: 'Assess inline and lot-level drift widening, CD-SEM pressure, yield-loss watch, and measurement capacity.', signals: ['inline drift', 'lot drift', 'CD-SEM index', 'yield-loss watch'] },
{ name: 'process_window_drift', desc: 'Assess process capability, overlay error, recipe divergence, lot flow, and process-window drift.', signals: ['overlay error', 'process capability', 'recipe divergence', 'lot flow'] },
{ name: 'tool_to_tool_mismatch', desc: 'Assess overlay mismatch, backlog pressure, baseline offsets, and control context.', signals: ['overlay mismatch', 'baseline offsets', 'control context', 'tool matching'] },
];
export const specialists: SpecialistCard[] = [
{ name: 'throughput', focus: 'throughput stability, WIP queue stress, hot-lot preference, and tool capacity depth', tools: 'bpu_change, throughput_buffer' },
{ name: 'metrology', focus: 'CD-SEM drift widening, yield-loss pressure, defect-risk drift, and measurement capacity', tools: 'bpu_change' },
{ name: 'overlay', focus: 'tool-to-tool mismatch, lot flow, overlay drift, baseline offsets, and control context', tools: 'control_amount' },
{ name: 'etch-process', focus: 'overlay error, process capability, recipe divergence, controller reaction, and process-window drift', tools: 'bpu_change' },
];
export const runbook: RunbookStep[] = [
['0-10', 'Environment and architecture check', 'Confirm AWS identity, region, Python environment, project resources, and role boundaries.'],
['10-25', 'Prompt and schema contracts', 'Create runtime system prompt, orchestrator prompt, canonical tool schema, and payload contract.'],
['25-45', 'Local agent and tooling build', 'Implement AI assistance tools, local runtime logic, local FastAPI tool server server, and discovery client.'],
['45-65', 'Validation, policy, and smoke tests', 'Run payload validators, deterministic tool smoke tests, policy tests, and tool schema linting.'],
['65-85', 'Managed deployment', 'Launch runtime, package Lambda target, create AWS tool gateway, and register tooling target.'],
['85-105', 'Invocation and debugging', 'Invoke runtime with boto3, run Tools semantic search, direct tools/call, and tools/list debugging.'],
['105-115', 'Governed orchestration', 'Run specialists, bounded memory, response policy checks, and structured observability events.'],
['115-120', 'Evaluation and handoff', 'Run safe and blocked evaluation fixtures, capture session ID, cleanup, and backlog follow-up tasks.'],
];
export const automationCells: AutomationCell[] = [
{ name: 'Sofia Garcia', strategy: 'Etch Endpoint Depth Multi-Step Recipe Control', indexPct: 18.4, deltaPct: 0.42, stability: 0.73, processFactor: 1.8, windowRatePct: 58, skew: 0.44, maxDriftPct: 18, series: [100,101,100.7,102.2,104,103.2,105.7,106.1,108,109.8,111,112.4,114.9,116.2,118.4] },
{ name: 'Lucia Fernandez', strategy: 'Photolithography Overlay Drift Detection', indexPct: 16.9, deltaPct: 0.88, stability: 0.91, processFactor: 1.7, windowRatePct: 61, skew: 0.31, maxDriftPct: 22, series: [100,102.1,101.5,103.8,102.9,106.4,108.2,107.5,110.8,112.2,111.6,114.1,115.2,116,116.9] },
{ name: 'Carmen Lopez', strategy: 'Chamber Matching RF Power Pressure Stability', indexPct: 14.2, deltaPct: -0.31, stability: 0.68, processFactor: 1.6, windowRatePct: 56, skew: 0.22, maxDriftPct: 25, series: [100,99.4,101.7,103.2,104.8,103.7,106.8,108.9,110.4,109.2,112.6,113.8,115.1,114.8,114.2] },
{ name: 'Elena Martin', strategy: 'Factory Line Yield Trend Automation', indexPct: 11.8, deltaPct: 0.17, stability: 0.62, processFactor: 1.5, windowRatePct: 54, skew: 0.18, maxDriftPct: 17, series: [100,100.8,101.1,102.5,103.2,104,103.8,105.4,106.2,107,108.9,109.3,110.2,111.1,111.8] },
{ name: 'Marta Sanchez', strategy: 'Recipe Parameter Relative Stability', indexPct: 9.6, deltaPct: 0.09, stability: 0.57, processFactor: 1.4, windowRatePct: 53, skew: 0.09, maxDriftPct: 15, series: [100,100.2,99.9,101,101.8,102.5,102.2,103.6,104.1,105.4,106,106.8,108.2,109,109.6] },
{ name: 'Paula Romero', strategy: 'Metrology Feature Ensemble Scoring', indexPct: 7.1, deltaPct: -0.12, stability: 0.49, processFactor: 1.3, windowRatePct: 52, skew: -0.04, maxDriftPct: 14, series: [100,100.5,101.2,100.8,102.1,102.7,103.4,104.2,103.8,105,105.4,106.2,106.8,107.3,107.1] },
{ name: 'Ana Torres', strategy: 'Endpoint Signal Breakout Alarm System', indexPct: 5.4, deltaPct: 0.28, stability: 0.42, processFactor: 1.2, windowRatePct: 51, skew: 0.12, maxDriftPct: 19, series: [100,99.1,100.4,101.6,100.8,102.2,101.5,103.4,102.8,104.2,103.8,104.7,105.1,105.2,105.4] },
{ name: 'Laura Navarro', strategy: 'Multi-Tool Mean Reversion Control', indexPct: 3.8, deltaPct: -0.06, stability: 0.35, processFactor: 1.1, windowRatePct: 49, skew: -0.11, maxDriftPct: 16, series: [100,100.4,99.8,100.9,101.4,100.6,101.8,102.2,101.7,102.8,103.1,102.9,103.6,103.9,103.8] },
];
export const statusTiles: StatusTile[] = [
{ key: 'RUNTIME', move: '+0.38%', state: 'READY', up: true, series: [20,21,20,22,23,23,24,25,24,26] },
{ key: 'GATEWAY', move: '-0.22%', state: 'WATCH', up: false, series: [30,29,31,28,27,26,25,24,23,22] },
{ key: 'POLICY', move: '+0.62%', state: 'READY', up: true, series: [18,18.5,19,18.7,20,21,20.5,22,23,23.5] },
{ key: 'EVAL', move: '+2.18%', state: 'READY', up: true, series: [20,22,21,24,26,25,29,28,32,34] },
];
Business logic: Fixtures preserve the portal as the source of truth. Developers should be able to prove that every visible row, tile, card, and runbook step is represented.
Code logic: This file mirrors the original JavaScript arrays and gives React, tests, analytics modules, and documentation one typed import path.
Expected result: A fixture test can assert counts: 6 runtime cards, 4 tools, 4 specialists, 8 runbook steps, 8 automation cells, and 4 status tiles.
Step 4 — Implement calculations
Create src/domain/calculations.ts:
import type { AutomationMetrics } from './types';
export function deltasPct(series: number[]): number[] {
if (series.length < 2) return [];
return series.slice(1).map((value, index) => ((value / series[index]) - 1) * 100);
}
export function driftPct(series: number[]): number[] {
if (!series.length) return [];
let peak = series[0];
return series.map(value => {
peak = Math.max(peak, value);
return peak === 0 ? 0 : ((value / peak) - 1) * 100;
});
}
export function average(values: number[]): number {
return values.length ? values.reduce((a, b) => a + b, 0) / values.length : 0;
}
export function standardDeviation(values: number[]): number {
const m = average(values);
return values.length ? Math.sqrt(average(values.map(v => (v - m) ** 2))) : 0;
}
export function percentile(values: number[], p: number): number {
if (!values.length) return 0;
const sorted = [...values].sort((a, b) => a - b);
const index = Math.min(sorted.length - 1, Math.max(0, Math.floor((p / 100) * sorted.length)));
return sorted[index];
}
export function automationMetrics(series: number[]): AutomationMetrics {
const deltas = deltasPct(series);
const drift = driftPct(series);
const windowIndexPct = series.length >= 2 ? ((series.at(-1)! / series[0]) - 1) * 100 : 0;
const maxDriftPct = drift.length ? Math.min(...drift) : 0;
return {
windowIndexPct,
signalVolPct: standardDeviation(deltas) * Math.sqrt(252),
efficiencyRatio: maxDriftPct === 0 ? 0 : windowIndexPct / Math.abs(maxDriftPct),
p05DeltaPct: percentile(deltas, 5),
bestDeltaPct: deltas.length ? Math.max(...deltas) : 0,
worstDeltaPct: deltas.length ? Math.min(...deltas) : 0,
windowRatePct: deltas.length ? deltas.filter(x => x > 0).length / deltas.length * 100 : 0,
maxDriftPct,
};
}
Business logic: These functions reproduce the analytics panel logic using professional names and safe empty-array behavior.
Code logic: The code converts prototype helpers into composable functions. automationMetrics aggregates the specific box values shown in the detail panel.
Expected result: Passing Sofia's index series returns a window index around 18.4%, a negative max drift, and advanced analytics values for the panel.
System design rationale:
● Pure functions make the analytics engine usable from UI, API, tests, or batch jobs.
● automationMetrics centralizes panel calculations so the React detail panel does not duplicate formulas.
● The percentile method intentionally mirrors the simplified. Production systems need stronger data governance before using lower-tail summaries for operational decisions.
Step 5 — Implement sorting, filtering, summary stats, and panel models
Create src/domain/portalAnalytics.ts:
import type { AutomationCell, SortKey, StatusTile } from './types';
import { automationMetrics } from './calculations';
export function signed(value: number, digits = 1, suffix = ''): string {
return `${value >= 0 ? '+' : ''}${value.toFixed(digits)}${suffix}`;
}
export function semanticClass(value: number): 'pos' | 'neg' {
return value >= 0 ? 'pos' : 'neg';
}
export function slugId(value: string): string {
return value.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/(^-|-$)/g, '');
}
export function searchAndSort(cells: AutomationCell[], query: string, sortKey: SortKey): AutomationCell[] {
const q = query.trim().toLowerCase();
return cells
.filter(cell => !q || cell.name.toLowerCase().includes(q) || cell.strategy.toLowerCase().includes(q))
.sort((a, b) => sortKey === 'maxDriftPct' ? a.maxDriftPct - b.maxDriftPct : b[sortKey] - a[sortKey]);
}
export function portalLeaderboardStats(cells: AutomationCell[]) {
return {
cellCount: cells.length,
bestStability: Math.max(...cells.map(cell => cell.stability)),
avgWindowRatePct: cells.reduce((sum, cell) => sum + cell.windowRatePct, 0) / cells.length,
bestIndexPct: Math.max(...cells.map(cell => cell.indexPct)),
};
}
export function statusTileSummary(tiles: StatusTile[]) {
return {
tileCount: tiles.length,
readyCount: tiles.filter(tile => tile.state === 'READY').length,
watchCount: tiles.filter(tile => tile.state === 'WATCH').length,
};
}
export function analysisPanelModel(cell: AutomationCell) {
return {
id: slugId(cell.name),
owner: cell.name,
strategy: cell.strategy,
metrics: automationMetrics(cell.series),
automationNote: `Automation path: runtime validates payload and session context; Tools discovers or calls domain tools; Lambda returns structured evidence; specialists synthesize throughput, metrology, overlay, and etch-process findings; policy checks confirm boundary terms; telemetry is emitted with request ID and trace ID. Domain context: ${cell.strategy}.`,
};
}
Business logic: Users can rank by index, delta, stability, process factor, window rate, or drift and search by owner or process pattern.
Code logic: The function reproduces the 's data, panel, architecture, and status summary behavior. Max Drift sorts ascending because lower drift pressure is better in the original logic.
Expected result: Sorting by Index puts Sofia first. Searching overlay returns Lucia Fernandez. Searching metrology returns Paula Romero. Searching tool returns Laura Navarro.
System design rationale:
● Sorting is extracted from rendering so tests can prove ranking behavior independent of UI.
● Search uses both owner and strategy because the portal lets users discover rows by process pattern.
● Max Drift sort is special-cased because pressure metrics often have opposite directionality from positive metrics.
Step 6 — Implement SVG path helpers
Create src/domain/charts.ts:
import { deltasPct, driftPct } from './calculations';
export function svgPath(values: number[], width: number, height: number, padding = 18): string {
if (!values.length) return '';
const min = Math.min(...values);
const max = Math.max(...values);
const range = max - min || 1;
const innerWidth = width - padding * 2;
const innerHeight = height - padding * 2;
return values.map((value, index) => {
const x = padding + index * innerWidth / Math.max(1, values.length - 1);
const y = padding + innerHeight - ((value - min) / range) * innerHeight;
return `${index ? 'L' : 'M'}${x.toFixed(1)} ${y.toFixed(1)}`;
}).join(' ');
}
export function histogramBars(deltas: number[], width: number, height: number, padding = 18) {
const maxAbs = Math.max(...deltas.map(Math.abs), 1);
const barWidth = (width - padding * 2) / Math.max(1, deltas.length);
const mid = height / 2;
return deltas.map((value, index) => {
const barHeight = Math.abs(value) / maxAbs * (height / 2 - padding);
return {
x: padding + index * barWidth + 1,
y: value >= 0 ? mid - barHeight : mid,
width: Math.max(2, barWidth - 2),
height: barHeight,
positive: value >= 0,
};
});
}
export function sparklineModel(values: number[]) {
return { viewBox: '0 0 120 32', path: svgPath(values, 120, 32, 2) };
}
export function indexChartModel(series: number[]) {
return {
viewBox: '0 0 680 190',
path: svgPath(series, 680, 190, 20),
label: `Index ${series[0]?.toFixed(1) ?? '0.0'} to ${series.at(-1)?.toFixed(1) ?? '0.0'}`,
};
}
export function driftChartModel(series: number[]) {
const drift = driftPct(series);
return {
viewBox: '0 0 680 120',
drift,
maxDriftPct: drift.length ? Math.min(...drift) : 0,
};
}
export function deltaHistogramModel(series: number[]) {
const deltas = deltasPct(series);
return {
viewBox: '0 0 680 120',
deltas,
bars: histogramBars(deltas, 680, 120, 18),
};
}
Business logic: Charts help developers understand trend, drift pressure, and daily process-delta distribution faster than numbers alone.
Code logic: svgPath mirrors the prototype path generator. histogramBars converts deltas into rectangle geometry for positive/negative bars. Chart model functions decouple data preparation from React/SVG rendering.
Expected result: The same index series can generate a sparkline, process-window index chart, drift model, and delta histogram model.
System design rationale:
● Chart geometry is calculated in pure functions so it can be tested without browser rendering.
● The path function normalizes values to a view box. This allows the same helper to power small sparklines and larger charts.
● Histogram bars carry a positive flag so the UI can apply green or red styling without recalculating sign logic.
Step 7 — Implement controlled live simulation
Create src/domain/simulator.ts:
import type { AutomationCell } from './types';
export function nextTick(cells: AutomationCell[], random = Math.random): AutomationCell[] {
return cells.map(cell => {
const bump = (random() - 0.48) * 0.18;
const nextDeltaPct = Number((cell.deltaPct + bump).toFixed(2));
const nextIndexPct = Number((cell.indexPct + bump * 0.25).toFixed(2));
return {
...cell,
deltaPct: nextDeltaPct,
indexPct: nextIndexPct,
series: [...cell.series.slice(1), 100 + nextIndexPct],
};
});
}
Business logic: The updates every five seconds to simulate a live automation board. Developers must clearly label this as simulated, not equipment data.
Code logic: The function mirrors the update loop but makes randomness injectable for deterministic tests.
Expected result: Each tick adjusts delta, adjusts index slightly, shifts the series window, and appends a new synthetic index point.
System design rationale:
● Randomness is injected because tests need deterministic behavior.
● The series uses a rolling window by dropping the oldest point. This preserves chart length and avoids unbounded memory growth.
● The function returns new objects rather than mutating the original array, matching React state management expectations.
Step 8 — Add tests
Create src/domain/__tests__/calculations.test.ts:
import { describe, expect, it } from 'vitest';
import { automationMetrics, driftPct, deltasPct } from '../calculations';
describe('process-window calculations', () => {
it('calculates deltas from indexed series', () => {
expect(deltasPct([100, 110, 99]).map(x => Number(x.toFixed(2)))).toEqual([10, -10]);
});
it('drift is never positive', () => {
expect(driftPct([100, 120, 90, 130]).every(x => x <= 0)).toBe(true);
});
it('creates automation panel metrics', () => {
const metrics = automationMetrics([100, 105, 99.75]);
expect(Number(metrics.windowIndexPct.toFixed(2))).toBe(-0.25);
expect(Number(metrics.maxDriftPct.toFixed(2))).toBe(-5.00);
});
});
Create src/domain/__tests__/fixtures.test.ts:
import { describe, expect, it } from 'vitest';
import { automationCells, runtimeCards, runbook, specialists, statusTiles, tools } from '../fixtures';
describe('portal fixtures', () => {
it('preserves portal fixture counts', () => {
expect(runtimeCards).toHaveLength(6);
expect(tools).toHaveLength(4);
expect(specialists).toHaveLength(4);
expect(runbook).toHaveLength(8);
expect(automationCells).toHaveLength(8);
expect(statusTiles).toHaveLength(4);
});
it('preserves required tool names', () => {
expect(tools.map(tool => tool.name)).toEqual([
'throughput_throughput',
'metrology_drift_widening',
'process_window_drift',
'tool_to_tool_mismatch',
]);
});
});
Create src/domain/__tests__/portalAnalytics.test.ts:
import { describe, expect, it } from 'vitest';
import { automationCells } from '../fixtures';
import { searchAndSort, signed, slugId } from '../portalAnalytics';
describe('portal analytics', () => {
it('formats signed values', () => {
expect(signed(0.42, 2, '%')).toBe('+0.42%');
expect(signed(-0.31, 2, '%')).toBe('-0.31%');
});
it('creates stable detail ids', () => {
expect(slugId('Sofia Garcia')).toBe('sofia-garcia');
});
it('sorts by index descending', () => {
expect(searchAndSort(automationCells, '', 'indexPct')[0].name).toBe('Sofia Garcia');
});
it('searches by process pattern', () => {
expect(searchAndSort(automationCells, 'photolithography', 'indexPct').map(cell => cell.name)).toEqual(['Lucia Fernandez']);
expect(searchAndSort(automationCells, 'metrology', 'indexPct').map(cell => cell.name)).toEqual(['Paula Romero']);
});
});
Prompt sample for Kiro
Review the automation analytics modules and tests. Add test cases for every automation-cell series, every status tile series, sort behavior for Index and Max Drift, search by each process pattern, SVG path boundaries, histogram positive/negative bars, and deterministic simulation with injected random values.
Business logic: Tests protect calculations that drive engineering interpretation and prove the TypeScript port still matches the portal.
Code logic: Unit tests validate known numeric examples and can be expanded to cover all automation cells and status tiles.
Expected result: npx vitest run passes and Kiro proposes additional coverage.
System design rationale:
● Known-value tests catch formula regressions quickly.
● Kiro is asked to add automation-cell-specific regression tests so changes to data or formulas do not unexpectedly change displayed analytics.
● Chart tests focus on geometry boundaries because SVG visual tests are expensive and unnecessary for this workshop.
Final lab challenge
Ask Kiro:
Generate a complete analytics gap report comparing the TypeScript modules against the latest portal JavaScript functions and data structures: runtimeCards, tools, specialists, runbook, P, M, renderStatic, tab click handling, sg, cl, id, ret, dd, avg, sd, perc, path, spark, gridlines, eqChart, ddChart, hist, architecture, panel, data, render, tog, markets, clock, and the five-second update loop. Identify what is implemented, what is intentionally moved to React, and what still needs tests.
Completion checklist
● [ ] Runtime cards, tools, specialists, runbook steps, automation cells, and status tiles are typed.
● [ ] Deltas, drift, signal volatility, percentile, efficiency ratio, P05 delta, best/worst delta, and window rate are implemented.
● [ ] Search and sort reproduce behavior.
● [ ] SVG path and histogram geometry are pure functions.
● [ ] Chart model helpers cover sparkline, index chart, drift chart, and delta histogram.
● [ ] Live simulation is clearly labeled and deterministic in tests.
● [ ] Kiro has reviewed math edge cases.
● [ ] Tests cover fixtures, calculation, sorting, charts, and simulation.
Appendix — complete automation-cell and status-tile coverage for analytics tests
Use this checklist to ensure the analytics engine covers the full portal, not only sample rows:
● Sofia Garcia — Etch Endpoint Depth Multi-Step Recipe Control: Index +18.4%, Delta +0.42%, Stability 0.73, Process Factor 1.8, Window Rate 58%, Skew +0.44, Max Drift 18.
● Lucia Fernandez — Photolithography Overlay Drift Detection: Index +16.9%, Delta +0.88%, Stability 0.91, Process Factor 1.7, Window Rate 61%, Skew +0.31, Max Drift 22.
● Carmen Lopez — Chamber Matching RF Power Pressure Stability: Index +14.2%, Delta -0.31%, Stability 0.68, Process Factor 1.6, Window Rate 56%, Skew +0.22, Max Drift 25.
● Elena Martin — Factory Line Yield Trend Automation: Index +11.8%, Delta +0.17%, Stability 0.62, Process Factor 1.5, Window Rate 54%, Skew +0.18, Max Drift 17.
● Marta Sanchez — Recipe Parameter Relative Stability: Index +9.6%, Delta +0.09%, Stability 0.57, Process Factor 1.4, Window Rate 53%, Skew +0.09, Max Drift 15.
● Paula Romero — Metrology Feature Ensemble Scoring: Index +7.1%, Delta -0.12%, Stability 0.49, Process Factor 1.3, Window Rate 52%, Skew -0.04, Max Drift 14.
● Ana Torres — Endpoint Signal Breakout Alarm System: Index +5.4%, Delta +0.28%, Stability 0.42, Process Factor 1.2, Window Rate 51%, Skew +0.12, Max Drift 19.
● Laura Navarro — Multi-Tool Mean Reversion Control: Index +3.8%, Delta -0.06%, Stability 0.35, Process Factor 1.1, Window Rate 49%, Skew -0.11, Max Drift 16.
Status tile regression cases:
● RUNTIME: +0.38%, READY, series [20,21,20,22,23,23,24,25,24,26].
● GATEWAY: -0.22%, WATCH, series [30,29,31,28,27,26,25,24,23,22].
● POLICY: +0.62%, READY, series [18,18.5,19,18.7,20,21,20.5,22,23,23.5].
● EVAL: +2.18%, READY, series [20,22,21,24,26,25,29,28,32,34].
Kiro prompt for full coverage:
Generate a regression test suite that loads all six runtime cards, four tools, four specialists, eight runbook steps, eight automation-cell rows, and four status tiles. Validate summary stats, sort order for every sort key, search by each process-pattern keyword, SVG paths for each series, and automation panel metrics for every row.
Additional Hands-on Developer Labs for Advanced Developers — HTML Graphic Analysis
These labs extend the automation analytics workshop by analyzing the portal 's SVG chart graphics. They follow the same structure as the original quant analytics workshop, but every concept is updated to the factory automation portal.
Advanced graphic-analysis goals
By the end of this section, advanced developers will be able to:
● Explain how the HTML converts process-window series into SVG paths, areas, lines, labels, and bars.
● Validate chart geometry with deterministic tests.
● Separate visual encoding logic from process-window calculations.
● Build chart-audit metadata that helps reviewers understand graphic meaning.
● Detect misleading chart output caused by scale, padding, short samples, or edge-case data.
Chart graphics inventory from the portal HTML file
The portal HTML uses several JavaScript functions to produce SVG graphics:
● path(a,w,h,p) normalizes a sequence into SVG M and L commands.
● spark(a) renders compact row and status-tile sparklines.
● gridlines(w,h,p) creates horizontal grid lines and a bottom axis.
● eqChart(a) renders a Process Window Index Path with filled area and start/end label.
● ddChart(a) renders drift as a red waterline shape below a zero axis.
● hist(a) renders daily process-delta bars around a midline, using separate positive and negative classes.
Advanced Lab 1 — Chart geometry contract tests
Objective: Create tests that prove SVG geometry stays inside chart bounds and handles flat, short, and volatile series safely.
Create src/domain/__tests__/chartGeometry.test.ts:
import { describe, expect, it } from 'vitest';
import { histogramBars, svgPath } from '../charts';
function extractNumbers(path: string): number[] {
return path.match(/-?\d+(\.\d+)?/g)?.map(Number) ?? [];
}
describe('SVG chart geometry contracts', () => {
it('keeps path coordinates inside the viewbox padding boundaries', () => {
const d = svgPath([100, 102, 101, 104], 120, 32, 2);
const numbers = extractNumbers(d);
const xs = numbers.filter((_, index) => index % 2 === 0);
const ys = numbers.filter((_, index) => index % 2 === 1);
expect(Math.min(...xs)).toBeGreaterThanOrEqual(2);
expect(Math.max(...xs)).toBeLessThanOrEqual(118);
expect(Math.min(...ys)).toBeGreaterThanOrEqual(2);
expect(Math.max(...ys)).toBeLessThanOrEqual(30);
});
it('renders flat series without division-by-zero geometry failures', () => {
const d = svgPath([100, 100, 100], 120, 32, 2);
expect(d).toContain('M');
expect(d).toContain('L');
expect(d).not.toContain('NaN');
expect(d).not.toContain('Infinity');
});
it('creates positive and negative histogram bars around a midline', () => {
const bars = histogramBars([1, -2, 0.5], 680, 120, 18);
expect(bars.some(bar => bar.positive)).toBe(true);
expect(bars.some(bar => !bar.positive)).toBe(true);
});
});
Kiro prompt:
Generate chart geometry tests for the portal HTML path, sparkline, Process Window Index Path, Drift Waterline, and Daily Process Delta Distribution behavior. Verify coordinate bounds, no NaN or Infinity, flat series behavior, short series behavior, positive/negative histogram flags, and drawing consistency across all automation-cell and status-tile series.
Expected result: Developers can refactor chart code without accidentally producing broken SVG.
Advanced Lab 2 — SVG visual encoding documentation
Objective: Document the relationship between process-window concepts and graphic marks so chart behavior is reviewable.
Create docs/svg-visual-encoding.md:
# Factory Automation Portal SVG Visual Encoding Notes
## Sparkline
- Data input: automation-cell index series or status-tile mini-series.
- Mark type: single green line.
- Purpose: compact trend preview.
- Risk: no y-axis scale shown, so it should not be treated as precise measurement.
## Process Window Index Path
- Data input: indexed process-window series.
- Mark type: green line plus translucent filled area.
- Purpose: visible cumulative index path.
- Label: first index value and final index value.
## Drift Waterline
- Data input: drift percentage series derived from the index series.
- Mark type: red line and red filled area below zero axis.
- Purpose: show peak-to-trough drift pressure and recovery.
## Daily Process Delta Distribution
- Data input: daily process-delta series.
- Mark type: vertical bars around a horizontal midline.
- Positive encoding: green bar above midline.
- Negative encoding: red bar below midline.
Kiro prompt:
Create visual encoding documentation for the portal charts. Explain sparkline, Process Window Index Path, Drift Waterline, and Daily Process Delta Distribution using data input, SVG mark type, color encoding, scale limitations, automation-boundary notes, and reviewer cautions.
Expected result: Chart graphics become explainable to developers, designers, and reviewers.
Advanced Lab 3 — Chart-audit metadata generator
Objective: Generate metadata for each chart so reviewers can inspect scale, min/max, range, positive/negative bar counts, and label text.
Create src/domain/chartAudit.ts:
import { deltasPct, driftPct } from './calculations';
export type SeriesAudit = {
pointCount: number;
min: number;
max: number;
range: number;
first: number;
last: number;
};
export type ChartAudit = {
index: SeriesAudit;
drift: SeriesAudit;
deltas: SeriesAudit & {
positiveCount: number;
negativeCount: number;
zeroCount: number;
};
};
function auditSeries(values: number[]): SeriesAudit {
if (!values.length) {
return { pointCount: 0, min: 0, max: 0, range: 0, first: 0, last: 0 };
}
const min = Math.min(...values);
const max = Math.max(...values);
return {
pointCount: values.length,
min,
max,
range: max - min,
first: values[0],
last: values.at(-1)!,
};
}
export function auditChartSeries(indexSeries: number[]): ChartAudit {
const drift = driftPct(indexSeries);
const deltas = deltasPct(indexSeries);
const deltasAudit = auditSeries(deltas);
return {
index: auditSeries(indexSeries),
drift: auditSeries(drift),
deltas: {
...deltasAudit,
positiveCount: deltas.filter(value => value > 0).length,
negativeCount: deltas.filter(value => value < 0).length,
zeroCount: deltas.filter(value => value === 0).length,
},
};
}
Kiro prompt:
Add chart-audit metadata for every process-window index series and every status-tile mini-series. Include point count, min, max, range, first/last values, drift range, delta range, and positive/negative/zero delta counts. Add tests for all eight automation-cell series and four status-tile series.
Expected result: Analytics reviewers can inspect chart inputs and scale risks without opening the browser.
Advanced Lab 4 — Misleading-graphic edge-case lab
Objective: Teach developers to identify edge cases where a graphic can be technically correct but visually misleading.
Create docs/misleading-chart-edge-cases.md:
# Factory Automation Portal Misleading Chart Edge Cases
## Flat series
A flat index series produces a line, but the visual range fallback can make tiny movements appear larger than they are if the label is ignored.
## Single-point series
A single point cannot represent a trend. The chart should render a safe placeholder or show an insufficient-data message.
## Extreme outlier
One large jump can compress all other variation, making normal signal movement look invisible.
## Short delta window
A 14-delta histogram is educational but not enough for robust distribution conclusions.
## Missing scale labels
Sparklines are useful for quick shape recognition but should not be used as precise process evidence.
## Automation boundary
No chart should be interpreted as an equipment command, process-release decision, or bypass signal.
Kiro prompt:
Create a misleading-graphic edge-case guide for the portal chart functions. Cover flat series, one-point series, extreme outliers, short delta windows, missing y-axis scale, histogram interpretation limits, and automation-boundary cautions.
Advanced Lab 5 — Chart rendering acceptance criteria
Objective: Define acceptance criteria for chart graphics before production refactor acceptance.
Create docs/chart-rendering-acceptance-criteria.md:
# Factory Automation Portal Chart Rendering Acceptance Criteria
## Process Window Index Path
- Uses the same normalized coordinate logic as the HTML prototype.
- Includes visible line and filled area.
- Labels first and final index values.
- Does not render NaN or Infinity.
## Drift Waterline
- Drift values are zero or negative before rendering.
- Zero axis is visible.
- Red area grows downward as drift deepens.
- Max drift label matches calculated minimum drift.
## Daily Process Delta Distribution
- Positive bars appear above the midline.
- Negative bars appear below the midline.
- Zero bars do not create visual errors.
- Bar width remains visible at all supported chart sizes.
## Sparkline
- Compact chart does not include unsupported scale claims.
- Preserves trend shape for all automation-cell and status-tile mini-series.
Kiro prompt:
Generate chart rendering acceptance criteria for React/SVG refactoring. Include Process Window Index Path, Drift Waterline, Daily Process Delta Distribution, and Sparkline checks. Tie every visual assertion to a deterministic test where possible.
Advanced final challenge — SVG chart fidelity review
Ask Kiro:
Perform an SVG chart fidelity review against the latest portal HTML file. Compare path normalization, padding, index-area closure, grid lines, drift zero axis, histogram midline, positive/negative bar placement, labels, status-tile sparklines, and edge-case behavior. Produce a prioritized remediation backlog.
Advanced graphic-analysis completion checklist
● [ ] Chart geometry tests verify bounds and invalid-number protection.
● [ ] Visual encoding documentation explains every portal SVG chart type.
● [ ] Chart-audit metadata summarizes index, drift, and delta series.
● [ ] Misleading-graphic edge cases are documented.
● [ ] Rendering acceptance criteria protect chart refactors.
● [ ] Automation-boundary cautions remain visible in chart documentation.