極點宏觀|Financial Cloud Cloud · 建構文章
Kiro:實作 Lab — 從零建置具型別的 Factory Risk Portal
僅供教育工程用途。本內容是軟體架構演練,不屬於製程放行建議。
Lab 總覽
在這個 lab 中,開發人員會使用 Kiro 從空資料夾建置 TypeScript React single-page application。此 app 使用決定性的本機 fixtures、具型別領域邏輯、即時模擬更新、搜尋、排序與 SVG charts,將工廠機台風險視覺化。整個 lab 都使用 Kiro 作為單一 AI 工程服務:steering、specs、chat、hooks、code generation、refactoring 與 documentation。
先備條件
● 已安裝 Kiro,並開啟到空的 workspace。
● 建議使用 Node.js 20+。
● 可使用 npm。
● 熟悉 React 與 TypeScript。
步驟 1 — 建立 Vite TypeScript 專案
在 terminal 執行:
npm create vite@latest factory-risk-portal -- --template react-ts
cd factory-risk-portal
npm install
npm run dev
預期結果:預設 React + TypeScript application 會在本機 browser 中執行。
Kiro prompt 範例
Inspect this new React TypeScript workspace. Summarize the current structure and propose the minimal files we should add for a typed factory machinery risk portal. Do not write implementation code yet. Focus on architecture and type boundaries.
系統設計理由
● 從已知的建置系統開始。 Vite with React TypeScript 提供小而可預測的 workshop baseline。這讓開發人員能把課程時間花在 Kiro workflows 與工程設計,而不是 bundler configuration。穩定的 project scaffold 也讓 hooks 更容易,因為 npm run build、tsc 與 local dev commands 都有可預測的名稱。
● 將檢查與實作分離。 第一次 Kiro 互動要求的是 structure proposal,而不是 code。這很重要,因為當助理先讀取 workspace 並形成計畫時,agentic coding 品質會更好。對工廠工程軟體而言,從空資料夾急著跳到 UI,往往會把領域假設埋進 JSX,這正是造成 typed React false-start 的原因。
● 建立可重複的課堂 baseline。 每個人都從相同 project template 開始。這讓 troubleshooting 更容易,並確保教學中的 Kiro 行為與 specs、steering、implementation tasks 相關,而不是被各自本機 framework 差異影響。這也呼應現場工作:先建立穩定控制 baseline,再調整製程參數。
步驟 2 — 新增 Kiro steering files
建立 .kiro/steering/product.md:
# Product overview
Build a local-only factory machinery risk portal for professional engineers. The portal helps review machinery drift, sensor-health symptoms, and risk recommendations. The application is advisory-only and must not imply autonomous equipment control, machine state changes, or bypassing human approval.
建立 .kiro/steering/tech.md:
# Technology stack
- React with TypeScript.
- Strict TypeScript; no `any`.
- Vite build system.
- CSS modules are not required; use plain CSS with clear class names.
- Inline SVG is allowed for sparklines and bar charts.
- No charting dependencies unless explicitly approved.
- No backend calls in the lab.
- Use local deterministic fixture data.
建立 .kiro/steering/structure.md:
# Project structure
- `src/types.ts` contains shared domain types.
- `src/data/fixtures.ts` contains deterministic local data.
- `src/domain/risk.ts` contains pure risk and formatting functions.
- `src/components/` contains presentational React components.
- `src/App.tsx` owns top-level state and tab switching.
- Event handlers and props must be typed.
- Action labels, tab names, and sort keys must be union types.
Kiro prompt 範例
Read the steering files and confirm the constraints you will follow. Then identify the first three implementation tasks for the factory risk portal. Do not create files yet.
系統設計理由
● Steering 將部落知識轉換為持久情境。 現場團隊常有存在於資深工程師腦中的標準:絕不暗示自主控制、不要使用未具型別 state、保持 risk logic 可審查,並避免 demo 中的網路假設。Steering files 讓這些標準在每次互動中都可供 Kiro 使用,減少重複指示的需求。
● 型別政策放在 code generation 之前。 TypeScript failures 在 component tree 已存在後修正,成本遠高於事前預防。透過預先要求 actions、tabs 與 sort keys 使用 union types,Kiro 較不容易建立過度寬鬆的 string props 或不安全 indexing logic。這是防止 React 專案 drift 到 untyped code 的主要控制。
● 結構檔案建立可維護性壓力。 如果 Kiro 知道 domain logic 屬於 risk.ts、fixtures 屬於 fixtures.ts、UI 屬於 components,它就較不容易產生單體式 App.tsx。這對專業開發人員很重要,因為 maintainability 是系統屬性,而不是格式偏好。
步驟 3 — 產生 Kiro spec
要求 Kiro 建立包含 requirements、design 與 tasks 的 spec。
Kiro prompt 範例
Create a Kiro spec for a TypeScript React factory machinery risk portal.
The app must include:
- typed machinery fixtures
- top KPI strip
- tabs for Overview, Live Risk Board, Health Matrix, Dynamic Matching, Triage, and Runbook
- search by machine, layer, or symptom
- sorting by risk, blind-window age, matching gap, slope, fleet sigma, or residual
- expandable evidence panels
- SVG sparklines and a larger SVG trajectory chart
- simulated 5-second live updates
- advisory-only wording
Use strict TypeScript and include acceptance criteria that can be tested.
預期結果:Kiro 會建立或提出包含 requirements section、design section 與 task list 的 spec。
系統設計理由
● Spec 是工程合約。 專業開發人員不應讓 AI 助理單靠 UI labels 推斷控制行為。Spec 說明哪些 behavior 可用、哪些僅限本機、哪些必須維持 advisory,以及哪些 acceptance criteria 能證明正確性。這可降低歧義並提供 review artifact。
● Acceptance criteria 控制範圍。 如果沒有 acceptance criteria,Kiro 可能過度建置或建置不足。例如,「make a live dashboard」可能變成 backend architecture、WebSocket client 或 static table。Acceptance criteria 會把系統限縮到 deterministic fixtures、明確 search、明確 sort keys、accessible controls 與 simulated updates。
● Spec 保留可追溯性。 工廠機台應用程式常需要 software、equipment、process 與 safety owners 審查。Spec-driven workflow 會建立 artifacts,說明功能為何存在以及允許做什麼。這比包含零散決策的 chat transcript 更可靠。
步驟 4 — 建立 domain types
建立 src/types.ts:
export type Recommendation =
| 'RELEASE'
| 'WATCH'
| 'RUN SPC'
| 'GOLDEN WAFER'
| 'ROUTE LIMIT'
| 'HOLD REVIEW'
| 'APC GUARD';
export type RiskTone = 'pass' | 'warn' | 'danger';
export type SortKey = 'risk' | 'blindHours' | 'matchingGapNm' | 'slope' | 'fleetSigma' | 'residual3SigmaNm';
export type TabKey =
| 'OVERVIEW'
| 'LIVE RISK BOARD'
| 'HEALTH MATRIX'
| 'DYNAMIC MATCHING'
| 'TRIAGE'
| 'RUNBOOK';
export type MachineRecord = {
id: string;
layer: string;
symptom: string;
risk: number;
blindHours: number;
matchingGapNm: number;
slope: number;
fleetSigma: number;
residual3SigmaNm: number;
recommendation: Recommendation;
deltaMeanSeries: number[];
};
export type HealthLink = {
name: string;
sensor: string;
metrologyImpact: string;
yieldRisk: string;
advisoryRule: string;
};
商業邏輯說明
Domain types 定義 risk portal 的詞彙。MachineRecord 代表一個機台或量測工具狀態。Recommendation 是封閉的 review actions 集合,讓 UI 不會意外呈現未支援的命令。SortKey 保護排序不受任意字串影響。RiskTone 讓 UI 顯示顏色加上文字狀態。HealthLink 將 sensor symptoms 對應到工程審查邏輯。
程式碼邏輯說明
每個 type 都從中央檔案 export,讓 components 與 domain functions 共享相同合約。Recommendations、tabs 與 sorting 刻意使用 union types。這讓 TypeScript 能在 build time 偵測無效 labels、不安全 sort keys 與拼錯的 tabs。
預期結果
Kiro 與 TypeScript compiler 現在應理解核心 domain vocabulary。後續程式碼可 import 這些 types,而不是發明隱含 object shapes。
系統設計理由
● 封閉集合防止不安全語言 drift。 在工廠情境中,label 可能被解讀為操作。自由格式 action string 可能意外變成「STOP TOOL」或「AUTO ROUTE」。Union type 會將應用程式限制在已知、已審查且刻意措辭的 advisory recommendations。
● 具型別 sort keys 移除 runtime guesswork。 Sorting UI 常從 generic strings 與 dynamic object indexing 開始,這很脆弱。SortKey union 在 controls、data fields 與 sort logic 之間建立合約。如果欄位改變,TypeScript 會在 app 抵達使用者前標出每個受影響區域。
● Domain-first coding 引導 Kiro。 當 Kiro 看見 data contracts 後,就能產生更好的 components。這避免了常見 false-start:AI 助理產生吸引人的 JSX 與隱含形狀,然後難以在已寫好的 state 與 props 周圍補回 type declarations。
步驟 5 — 新增決定性 fixtures
建立 src/data/fixtures.ts:
import type { HealthLink, MachineRecord } from '../types';
export const initialMachines: MachineRecord[] = [
{
id: 'CDSEM-01',
layer: 'Gate ADI',
symptom: 'Stable master, production anchor',
risk: 18,
blindHours: 1.2,
matchingGapNm: 0.12,
slope: 1.0,
fleetSigma: 0.4,
residual3SigmaNm: 0.08,
recommendation: 'RELEASE',
deltaMeanSeries: [0.02, 0.01, 0.03, 0.01, 0.02, 0, 0.01, 0.02, 0.01, 0.02]
},
{
id: 'CDSEM-03',
layer: 'Fin dense CD',
symptom: 'Deflector DAC wobble, slope mismatch',
risk: 91,
blindHours: 7.4,
matchingGapNm: 0.23,
slope: 1.031,
fleetSigma: 3.2,
residual3SigmaNm: 0.16,
recommendation: 'HOLD REVIEW',
deltaMeanSeries: [0.02, 0.03, 0.06, 0.05, 0.08, 0.12, 0.16, 0.2, 0.22, 0.24]
}
];
export const healthLinks: HealthLink[] = [
{
name: 'Deflector DAC',
sensor: 'Deflector linearity / DAC stability',
metrologyImpact: 'Mandel Slope moves away from 1',
yieldRisk: 'Dense/isolated feature bias and process window narrowing',
advisoryRule: 'If slope <0.98 or >1.02, request engineering review before critical-layer routing.'
}
];
商業邏輯說明
Fixtures 代表真實感足夠但安全的學習資料集。一筆 record 穩定,另一筆 record 風險高。這讓開發人員不用連接工廠系統,就能立即驗證 pass/warn/danger 行為、排序、chart geometry 與 recommendation display。
程式碼邏輯說明
Arrays 明確指定為 MachineRecord[] 與 HealthLink[]。TypeScript 現在會驗證每個 field name、numeric metric 與 recommendation label。如果開發人員寫成 HOLD 而不是 HOLD REVIEW,compiler 會抓到。
預期結果
App 具備可 render、sort、search、simulate 與 test 的決定性資料。
系統設計理由
● Fixtures 比即時機台資料更安全。 工作坊重點是使用 Kiro 進行軟體工程,而不是營運整合。決定性的本機資料可避免憑證、網路失敗、敏感生產值與意外控制暗示。它也讓每位參與者看到相同預期結果。
● 代表性極端值支援驗證。 同時包含低風險穩定機台與高風險 drift case,有助於驗證 color thresholds、recommendation wording、expanded detail panels 與 chart scaling。只有正常值的資料集會把 risk-path defects 隱藏到更後面。
● 具型別 fixtures 作為 contract tests。 Fixture file 不只是 mock data;它是 domain model 可用性的 compile-time check。如果 model 過度複雜或缺少欄位,Kiro 與 TypeScript 會在撰寫 fixtures 時立即顯示不匹配。
步驟 6 — 新增純風險邏輯
建立 src/domain/risk.ts:
import type { MachineRecord, RiskTone, SortKey } from '../types';
export function getRiskTone(risk: number): RiskTone {
if (risk >= 80) return 'danger';
if (risk >= 55) return 'warn';
return 'pass';
}
export function getStatusLabel(machine: MachineRecord): string {
if (machine.risk >= 80) return 'HOLD CANDIDATE';
if (machine.risk >= 55) return 'WATCH / REVIEW';
return 'NORMAL RELEASE';
}
export function filterMachines(machines: MachineRecord[], query: string): MachineRecord[] {
const q = query.trim().toLowerCase();
if (!q) return machines;
return machines.filter(machine =>
`${machine.id} ${machine.layer} ${machine.symptom}`.toLowerCase().includes(q)
);
}
export function sortMachines(machines: MachineRecord[], sortKey: SortKey): MachineRecord[] {
return [...machines].sort((a, b) => Math.abs(b[sortKey]) - Math.abs(a[sortKey]));
}
商業邏輯說明
Risk thresholds 集中管理。Green/pass 低於 55,yellow/watch 為 55-79,red/danger 為 80+。Search 支援 machine ID、process layer 或 symptom。Sorting 會依工程師選擇的 metric,將最嚴重的值排在最前。
程式碼邏輯說明
這些 functions 是 pure:相同 inputs 永遠產生相同 outputs。sortMachines 在排序前複製 array,以避免 mutation React state。SortKey 確保 dynamic indexing 只使用有效 numeric fields。
預期結果
Kiro 現在可以在可測試 business logic 之上建置 UI components。開發人員稍後可新增 unit tests,而不需要 render 整個 application。
系統設計理由
● Pure functions 讓 AI-generated code 可審查。 當 business logic 直接混在 JSX 內,reviewers 必須在腦中分離 rendering、state、thresholds 與 formatting。Pure functions 會隔離重要決策:risk thresholds、filtering、sorting 與 status language。
● Immutability 保護 React state。 原地排序 arrays 是 dashboards 常見 bug。排序前複製可避免微妙 UI 問題,並讓 live update behavior 更容易推理。只要 Kiro 轉換 state,就應被引導保留 immutability。
● 集中 thresholds 支援治理。 Risk thresholds 是工程政策。將它們放在一個 domain file 中,可讓未來 review 與 change control 更容易。如果 process owner 改變 warning threshold,實作會有一個明確位置,且較少隱藏 UI dependencies。
步驟 7 — 建置 SVG sparkline component
建立 src/components/Sparkline.tsx:
type SparklineProps = {
data: number[];
width?: number;
height?: number;
color?: string;
};
export function Sparkline({ data, width = 130, height = 38, color = '#35d5ff' }: SparklineProps) {
const min = Math.min(...data, 0);
const max = Math.max(...data, 0.24);
const points = data.map((value, index) => {
const x = (index / Math.max(1, data.length - 1)) * width;
const y = height - ((value - min) / (max - min || 1)) * (height - 6) - 3;
return `${x.toFixed(1)},${y.toFixed(1)}`;
}).join(' ');
return (
<svg className="spark" viewBox={`0 0 ${width} ${height}`} role="img" aria-label="DeltaMean sparkline">
<polyline points={points} fill="none" stroke={color} strokeWidth="2" />
<line x1="0" y1={height / 2} x2={width} y2={height / 2} stroke="#49657c" strokeDasharray="4 4" />
</svg>
);
}
商業邏輯說明
Sparkline 不需加入第三方 chart package,就能提供近期 drift 的快速視覺情境。工程師可直接在 risk table 中辨識穩定、上升或雜訊行為。
程式碼邏輯說明
Component 會將 numeric values 對應到 SVG coordinates。它會在 minimum 與 maximum 之間 normalize values,建立 polyline string,並繪製中心參考線。Props 明確具型別,optional dimensions 也有 defaults。
預期結果
每個 machine row 都可顯示具 accessible image metadata 的 compact trend line。
系統設計理由
● Inline SVG 保持 dependency surface 小。 對工作坊而言,charting library 會增加 API 學習負擔與版本風險。SVG 是瀏覽器原生能力,對 React 而言型別支援足夠,且足以處理 sparklines、trajectory charts 與 sigma bars。
● Chart component 隔離 coordinate math。 Coordinate transformation 不是 UI 裝飾;它是邏輯。將它放在具型別 props 的 component 中,可讓 table 更簡潔,也讓 rendering decision 可在 compact 與 expanded views 間重用。
● Accessibility 保持明確。 Sparkline 使用 role="img" 與 aria-label。這可避免只用視覺傳達狀態。在工程工具中,即使無法使用顏色或視覺細節,使用者仍應能透過文字與 labels 解讀狀態。
步驟 8 — 要求 Kiro 實作 app shell
Kiro prompt 範例
Implement the app shell using the existing types, fixtures, risk functions, and Sparkline component.
Requirements:
- top header with title and live local clock
- KPI strip
- tab navigation controlled by typed TabKey state
- Live Risk Board with search, sort buttons, and expandable details
- simulated live updates every 5 seconds
- advisory-only footer
Keep App.tsx readable. If it becomes too large, propose component extraction before writing more code.
預期結果:Kiro 會建立可運作的 top-level app,具備 typed state 並渲染 core board。
系統設計理由
● Kiro 同時收到 constraints 與 assets。 到這一步,domain model、fixtures、pure functions 與 sparkline component 都已存在。Prompt 要求 Kiro 組合既有 pieces,而不是發明新東西。這會大幅改善 type consistency 並減少 duplicate logic。
● State ownership 是刻意設計的。 Top-level app 擁有 active tab、query、sort key、expanded row、live clock 與 machine records。這對小型 single-page lab 很合適。如果 app 成長,Kiro 應萃取 state 或 components,而不是把所有東西埋進深層 children。
● Simulated updates 安全地模擬真實遙測。 現場軟體常需要展示隨時間變化的 behavior。決定性的本機 update loop 比連接設備更安全。它可驗證 re-rendering、chart updates 與 risk changes,同時保留 advisory-only 邊界。
步驟 9 — 新增 hooks 以維持型別紀律
建立 .kiro/hooks/typecheck-on-save.json:
{
"version": "v1",
"hooks": [
{
"name": "typecheck-on-save",
"description": "Run TypeScript build checks when TS or TSX files are saved.",
"trigger": "PostFileSave",
"matcher": "\\.(ts|tsx)$",
"action": {
"type": "command",
"command": "npm run build"
},
"timeout": 60,
"enabled": true
}
]
}
建立 .kiro/hooks/review-react-change.json:
{
"version": "v1",
"hooks": [
{
"name": "review-react-change",
"description": "Ask Kiro to review React changes for typing, state mutation, accessibility, and advisory-only wording.",
"trigger": "PostFileSave",
"matcher": "src/.*\\.(ts|tsx)$",
"action": {
"type": "agent",
"prompt": "Review the changed TypeScript React file. Check for any, implicit object shapes, unsafe state mutation, untyped props, inaccessible controls, color-only status, and wording that implies autonomous machinery control. Suggest minimal fixes."
},
"timeout": 60,
"enabled": true
}
]
}
商業邏輯說明
Hooks 像工程品質閘門。它們不控制機台,也不核准 production behavior。它們協助開發人員及早捕捉不安全的 implementation patterns:type drift、mutation、UI-only status,以及可能被誤解為 operational command 的語言。
程式碼邏輯說明
第一個 hook 會在 TypeScript 或 TSX 檔案儲存後執行 shell command。第二個 hook 會在 TypeScript React source changes 後呼叫 Kiro 的 agent review。Matcher 會將自動化限制在相關檔案。
預期結果
儲存 TypeScript 檔案會觸發 checks 或 review automation,取決於本機環境中的 Kiro hook configuration。
系統設計理由
● Hooks 將標準轉換為 workflow。 Steering 告訴 Kiro 有哪些標準;hooks 讓這些標準在 development 期間運作。當開發人員快速迭代且不一定每次都手動要求 review 時,這很關鍵。
● Type checking 在變更點自動化。 TS 或 TSX 檔案一儲存,build 就能抓出 invalid labels、missing props 與 broken imports。這降低了直到 lab 結尾才發現 type errors 的機率。
● Agent review 捕捉語意問題。 TypeScript 無法偵測 button label 是否暗示自主設備控制。Agent hook 可以審查 language、accessibility 與 engineering intent。這是 compiler checks 的補充,而不是替代。
步驟 10 — 最終強化 prompt
Kiro prompt 範例
Perform a final technical review of the project.
Check:
- strict typing and no `any`
- no mutation of React state arrays
- all controls keyboard accessible
- search and sorting use typed contracts
- risk status includes text, not only color
- app wording remains advisory-only
- README explains how to run the app and states that fixture data must be replaced with validated data before operational use
Return a prioritized fix list, then implement only the safe low-risk fixes.
系統設計理由
● Final review 將偵測與行動分開。 要求 Kiro 先提供 prioritized list 再實作,可避免未審查的 sweeping changes。這在工程環境中很重要,因為大型 refactor 可能在 workshop 或 sprint 後期引入 regressions。
● Safety language 是技術品質的一部分。 在工廠應用程式中,措辭是系統的一部分。「Hold candidate」與「recommend route limit」不同於「hold tool」或「reroute lot」。Review 會確保軟體仍在 advisory scope 內。
● Low-risk fixes 保護 workshop 完成度。 最後幾分鐘應穩定專案,而不是開啟架構震盪。Kiro 有助於找出許多改善點,但專業開發人員必須決定哪些現在實作、哪些放入 backlog。
完成檢查清單
● [ ] 專案可在本機執行。
● [ ] Kiro steering files 存在。
● [ ] Kiro spec 存在或已完成審查。
● [ ] Domain types 先於 UI logic 存在。
● [ ] Source files 中沒有 any。
● [ ] Search 可依 machine、layer 與 symptom 運作。
● [ ] Sorting 可針對所有 configured keys 運作。
● [ ] Expand/collapse evidence panel 可運作。
● [ ] Live update loop 會改變 risk 與 trend values。
● [ ] SVG charts 可在沒有 chart libraries 的情況下 render。
● [ ] Advisory-only disclaimer 可見。
● [ ] Hooks 存在,用於 type checks 與 AI review。
Lab 區段:建立完整 fixture coverage
Main lab 中的短 fixture 是為了教學而刻意保持小型。若要更豐富的 demo,請以涵蓋低、中、高風險的較廣 records 取代它。這會讓 Kiro 與參與者在 sorting、filtering、display color 與 expanded evidence 上有更多案例。
import type { HealthLink, MachineRecord } from '../types';
export const initialMachines: MachineRecord[] = [
{
id: 'CDSEM-01',
layer: 'Gate ADI',
symptom: 'Stable master, production anchor',
risk: 18,
blindHours: 1.2,
matchingGapNm: 0.12,
slope: 1.0,
fleetSigma: 0.4,
residual3SigmaNm: 0.08,
recommendation: 'RELEASE',
deltaMeanSeries: [0.02, 0.01, 0.03, 0.01, 0.02, 0.0, 0.01, 0.02, 0.01, 0.02]
},
{
id: 'CDSEM-02',
layer: 'Gate AEI',
symptom: 'Emission current ramp, small mean offset',
risk: 64,
blindHours: 5.8,
matchingGapNm: 0.18,
slope: 1.012,
fleetSigma: 1.8,
residual3SigmaNm: 0.14,
recommendation: 'WATCH',
deltaMeanSeries: [0.02, 0.04, 0.05, 0.08, 0.10, 0.12, 0.14, 0.16, 0.17, 0.18]
},
{
id: 'CDSEM-03',
layer: 'Fin dense CD',
symptom: 'Deflector DAC wobble, slope mismatch',
risk: 91,
blindHours: 7.4,
matchingGapNm: 0.23,
slope: 1.031,
fleetSigma: 3.2,
residual3SigmaNm: 0.16,
recommendation: 'HOLD REVIEW',
deltaMeanSeries: [0.02, 0.03, 0.06, 0.05, 0.08, 0.12, 0.16, 0.20, 0.22, 0.24]
},
{
id: 'CDSEM-05',
layer: 'Contact/Via',
symptom: 'Vacuum slow degradation, residual rising',
risk: 76,
blindHours: 6.9,
matchingGapNm: 0.19,
slope: 1.006,
fleetSigma: 2.4,
residual3SigmaNm: 0.21,
recommendation: 'GOLDEN WAFER',
deltaMeanSeries: [0.02, 0.01, 0.04, 0.06, 0.08, 0.10, 0.12, 0.13, 0.16, 0.18]
},
{
id: 'CDSEM-10',
layer: 'Risk ramp product',
symptom: 'Yield-loss watch lots routed here',
risk: 82,
blindHours: 6.2,
matchingGapNm: 0.21,
slope: 0.976,
fleetSigma: 2.9,
residual3SigmaNm: 0.17,
recommendation: 'APC GUARD',
deltaMeanSeries: [0.02, 0.04, 0.07, 0.09, 0.11, 0.13, 0.15, 0.18, 0.20, 0.23]
}
];
export const healthLinks: HealthLink[] = [
{
name: 'Gun Vacuum',
sensor: 'Gun chamber vacuum / Torr',
metrologyImpact: 'Residual 3 sigma and TMP rise',
yieldRisk: 'False alarms and random CD noise',
advisoryRule: 'If vacuum degrades while residual 3 sigma rises, recommend golden-wafer review.'
},
{
name: 'Emission Current',
sensor: 'Extraction voltage / emission current',
metrologyImpact: 'Mean offset step jump',
yieldRisk: 'APC receives biased CD and may correct in the wrong direction',
advisoryRule: 'If emission slope changes for three lots, recommend virtual OOC review before next routine SPC.'
},
{
name: 'Deflector DAC',
sensor: 'Deflector linearity / DAC stability',
metrologyImpact: 'Mandel Slope moves away from 1',
yieldRisk: 'Dense/isolated feature bias and process-window narrowing',
advisoryRule: 'If slope <0.98 or >1.02, request engineering review before critical-layer routing.'
},
{
name: 'Stage Vibration',
sensor: 'Stage vibration / facility vibration mG',
metrologyImpact: 'Site-to-site Delta Max-Min expands',
yieldRisk: 'False local variation signatures can hide real yield patterns',
advisoryRule: 'If residual and site range rise after a vibration burst, recommend routing review for tight CD layers.'
}
];
商業邏輯說明
這個延伸 fixture set 包含多種 risk profiles:stable release、watch、high fleet deviation、residual rise、slope drift 與 yield-watch exposure。這種多樣性很重要,因為工程儀表板必須讓異常條件清楚可見,同時不失去正常 baseline context。
程式碼邏輯說明
Fields 與 MachineRecord 完全一致。Fixture names 使用工程友善 labels,同時保留 type-safe property names。每個 series 都有相同長度,因此 SVG scaling 與 live update logic 會保持可預測。
預期結果
Risk board 可展示所有主要 user paths:搜尋 tool、依不同 metrics 排序、展開 high-risk row、檢查 trend chart,並確認 advisory recommendation language。
系統設計理由
● Coverage 防止誤導性 demos。 只有一兩列的 dashboard,即使 sorting、threshold coloring 與 row expansion 有缺陷,也可能看起來正確。較廣的 fixture set 會產生足夠變化,讓參與者能跨多個 paths 驗證 behavior。
● Normal cases 保持可見。 Field engineering tools 不應只顯示 alarms。納入穩定 baseline tools,可協助使用者將 drift candidates 與健康 anchor 比較。這個 context 對視覺解讀與軟體驗證都很有價值。
● Fixture consistency 保護 chart logic。 保持 series length 一致,可避免在工作坊中處理分散注意力的 edge cases。Facilitator 可在核心 typed design 被理解後,再把 missing 或 uneven data 作為 advanced extension 引入。
延伸 Lab 區段:在 App.tsx 實作 top-level state
import { useEffect, useMemo, useState } from 'react';
import { initialMachines } from './data/fixtures';
import { filterMachines, sortMachines } from './domain/risk';
import type { MachineRecord, SortKey, TabKey } from './types';
const tabs: TabKey[] = ['OVERVIEW', 'LIVE RISK BOARD', 'HEALTH MATRIX', 'DYNAMIC MATCHING', 'TRIAGE', 'RUNBOOK'];
export function App() {
const [activeTab, setActiveTab] = useState<TabKey>('OVERVIEW');
const [query, setQuery] = useState('');
const [sortKey, setSortKey] = useState<SortKey>('risk');
const [expandedId, setExpandedId] = useState<string | null>(null);
const [machines, setMachines] = useState<MachineRecord[]>(initialMachines);
const visibleMachines = useMemo(() => {
return sortMachines(filterMachines(machines, query), sortKey);
}, [machines, query, sortKey]);
useEffect(() => {
let tick = 0;
const timer = window.setInterval(() => {
tick += 1;
setMachines(previous => previous.map((machine, index) => {
const wave = Math.sin((tick + index) * 0.7);
const risk = Math.max(5, Math.min(99, machine.risk + wave * 1.7));
const blindHours = Math.max(0.2, machine.blindHours + 0.05 + wave * 0.03);
const latest = machine.deltaMeanSeries[machine.deltaMeanSeries.length - 1] ?? 0;
const nextPoint = Math.max(0, Math.min(0.28, latest + wave * 0.006));
return {
...machine,
risk,
blindHours,
deltaMeanSeries: [...machine.deltaMeanSeries.slice(1), Number(nextPoint.toFixed(3))]
};
}));
}, 5000);
return () => window.clearInterval(timer);
}, []);
return (
<div className="app-shell">
<nav className="tabs" aria-label="Portal panels">
{tabs.map(tab => (
<button key={tab} className={activeTab === tab ? 'active' : ''} onClick={() => setActiveTab(tab)}>
{tab}
</button>
))}
</nav>
<pre>{JSON.stringify({ activeTab, query, sortKey, expandedId, visibleCount: visibleMachines.length }, null, 2)}</pre>
</div>
);
}
商業邏輯說明
Top-level state 捕捉主要 dashboard interactions:active panel、text search、selected sorting metric、expanded evidence row 與目前 machine records。Simulated updates 讓 portal 感覺即時,同時仍與真實系統斷開。
程式碼邏輯說明
visibleMachines 由 machines、query 與 sortKey 透過 useMemo 衍生。Update loop 會建立新的 machine objects,而不是 mutation previous state。Tab list 指定為 TabKey[],可防止不支援的 panels。
預期結果
App 應每五秒顯示變化中的 derived state。這是在建置完整 UI 前的 checkpoint。
系統設計理由
● State 明確且最小化。 App 只儲存使用者控制或會隨時間改變的值。Filtered 與 sorted data 由資料衍生。這可防止 duplicated state,這是 dashboard bugs 的常見來源。
● Derived views 被 memoized。 Search 與 sorting 可在 inputs 改變時重新計算。useMemo 讓邏輯保持可見,並在 app 成長時避免不必要 recomputation。
● Update loop 有邊界且可回復。 Simulated data changes 小幅、clamped 且在本機。此設計展示 live behavior,而不假裝自己是 production telemetry pipeline。
延伸 Lab 區段:建置具型別 metric card
建立 src/components/MetricCard.tsx:
type MetricCardProps = {
label: string;
value: string | number;
detail: string;
tone?: 'neutral' | 'pass' | 'warn' | 'danger';
};
export function MetricCard({ label, value, detail, tone = 'neutral' }: MetricCardProps) {
return (
<article className={`metric-card ${tone}`}>
<span>{label}</span>
<strong>{value}</strong>
<em>{detail}</em>
</article>
);
}
商業邏輯說明
Metric cards 會在使用者檢查 detail rows 前,摘要 operational state。範例包括 tools watched、blind-window average、fleet out-of-control count 與 yield-watch lots。
程式碼邏輯說明
Component 接受小型具型別 prop set。tone 是 optional,並被限制為已知 display styles。Component 不計算 metrics;它只負責 rendering。
預期結果
KPI cards 可在 header 或 summary rows 中重用,而不需重複 markup。
系統設計理由
● Presentational components 應保持簡單。 Metric card 應渲染 metric,而不是決定 fleet risk 如何計算。保持 presentation-only 可降低隱藏邏輯,並讓 Kiro-generated UI 更容易審查。
● Typed visual tone 防止 CSS drift。 Component 支援封閉的 tones 集合,而不是任意 class strings。這呼應 risk-tone pattern,讓未來 styling 更安全。
● Reusable layout 降低重複。 Dashboards 常跨 panels 重複 cards。小型具型別 component 可讓 Kiro 更快建立一致 UI,而不會每次建立不一致 markup。
延伸 Lab 區段:建立 risk board component
建立 src/components/RiskBoard.tsx:
import { getRiskTone, getStatusLabel } from '../domain/risk';
import type { MachineRecord, SortKey } from '../types';
import { Sparkline } from './Sparkline';
type RiskBoardProps = {
machines: MachineRecord[];
query: string;
sortKey: SortKey;
expandedId: string | null;
onQueryChange: (query: string) => void;
onSortChange: (key: SortKey) => void;
onExpandedChange: (id: string | null) => void;
};
const sortOptions: Array<[SortKey, string]> = [
['risk', 'Risk'],
['blindHours', 'Blind Window'],
['matchingGapNm', 'TMG'],
['slope', 'Slope'],
['fleetSigma', 'Fleet sigma'],
['residual3SigmaNm', 'Residual']
];
export function RiskBoard({ machines, query, sortKey, expandedId, onQueryChange, onSortChange, onExpandedChange }: RiskBoardProps) {
return (
<section className="risk-board">
<div className="toolbar">
<input
value={query}
onChange={event => onQueryChange(event.target.value)}
placeholder="Search by machine, layer, or symptom"
aria-label="Search by machine, layer, or symptom"
/>
<div className="sorts">
{sortOptions.map(([key, label]) => (
<button key={key} className={sortKey === key ? 'active' : ''} onClick={() => onSortChange(key)}>
Sort: {label}
</button>
))}
</div>
</div>
{machines.map(machine => {
const tone = getRiskTone(machine.risk);
const isExpanded = expandedId === machine.id;
return (
<article className="machine-row" key={machine.id}>
<div className="row-main">
<strong>{machine.id}</strong>
<span>{machine.layer}</span>
<span className={`risk ${tone}`}>{Math.round(machine.risk)} {tone.toUpperCase()}</span>
<Sparkline data={machine.deltaMeanSeries} />
<button onClick={() => onExpandedChange(isExpanded ? null : machine.id)} aria-expanded={isExpanded}>
{isExpanded ? 'Hide evidence' : machine.recommendation}
</button>
</div>
{isExpanded && (
<div className="evidence-panel">
<p><b>Status:</b> {getStatusLabel(machine)}</p>
<p><b>Evidence:</b> {machine.symptom}</p>
<p><b>Blind Window:</b> {machine.blindHours.toFixed(1)}h</p>
<p><b>Fleet Sigma:</b> {machine.fleetSigma.toFixed(1)}σ</p>
</div>
)}
</article>
);
})}
</section>
);
}
商業邏輯說明
Risk Board 是工程師搜尋、排序與檢查 machine evidence 的地方。它顯示 risk status text、layer context、trend evidence 與 advisory recommendations。
程式碼邏輯說明
Component 從 parent 接收所有 state,並透過 typed callbacks 發出變更。它不擁有 machine list。這讓 component 更容易測試,並將 state ownership 保留在 App.tsx。
預期結果
使用者可以搜尋、排序並展開 rows。每列都顯示 machine ID、layer、risk text、sparkline 與 recommendation button。
系統設計理由
● Controlled inputs 保留 state traceability。 Search 與 sort state 位於 parent,因此目前 board state 稍後可被 logged、tested 或傳給其他 panels。這比把 local state 埋在大型 table component 中更易維護。
● Callbacks 是具型別合約。 onSortChange 只接受 SortKey,因此 component 不能發出不支援的 field。這是另一個防止 Kiro 建立 stringly typed UI controls 的護欄。
● Evidence 按需顯示。 Expanded panels 讓預設 board 保持密集,同時仍使 reasoning 可見。工廠工程師同時需要快速 triage 與 drill-down evidence。
延伸 Lab 區段:新增較大的 SVG trajectory chart
建立 src/components/TrajectoryChart.tsx:
type TrajectoryChartProps = {
data: number[];
advisoryLimit?: number;
};
export function TrajectoryChart({ data, advisoryLimit = 0.18 }: TrajectoryChartProps) {
const width = 620;
const height = 190;
const pad = 24;
const min = Math.min(...data, -0.02);
const max = Math.max(...data, 0.26, advisoryLimit);
const xFor = (index: number) => pad + (index / Math.max(1, data.length - 1)) * (width - pad * 2);
const yFor = (value: number) => height - pad - ((value - min) / (max - min || 1)) * (height - pad * 2);
const points = data.map((value, index) => `${xFor(index)},${yFor(value)}`).join(' ');
return (
<svg className="trajectory-chart" viewBox={`0 0 ${width} ${height}`} role="img" aria-label="DeltaMean trajectory chart">
<line x1={pad} x2={width - pad} y1={yFor(advisoryLimit)} y2={yFor(advisoryLimit)} stroke="#ff5b6e" strokeDasharray="6 6" />
<polyline points={points} fill="none" stroke="#35d5ff" strokeWidth="4" strokeLinecap="round" strokeLinejoin="round" />
{data.map((value, index) => (
<circle key={index} cx={xFor(index)} cy={yFor(value)} r="4" fill={value > advisoryLimit ? '#ff5b6e' : '#35d5ff'} />
))}
</svg>
);
}
商業邏輯說明
Expanded chart 會顯示近期 DeltaMean values 是否接近或跨越 advisory threshold。它透過呈現 trend direction 與 threshold crossings,支援工程審查。
程式碼邏輯說明
Component 將 numeric series 轉換為 SVG coordinates。Advisory limit 是有 default value 的 prop,讓 chart 之後可重用於不同 layers 或 process windows。
預期結果
Evidence panel 可顯示較大的 trajectory chart,包含 dashed advisory limit line 與 colored points。
系統設計理由
● Thresholds 在 context 中可見。 單一數字無法呈現 trend velocity。帶有 advisory line 的 chart 有助於工程師區分穩定高值與快速上升的風險訊號。
● Chart 保持決定性。 不需要外部 rendering library 或 asynchronous data source。數學透明,參與者也容易檢查。
● Limit 可設定但有邊界。 將 advisoryLimit 做成 typed prop,可避免把每個 threshold 都硬編碼,同時仍防止隱藏 global behavior。
延伸 Lab 區段:新增可 unit-test 的 update logic
將 update function 移到 src/domain/risk.ts:
export function simulateMachineUpdate(machine: MachineRecord, tick: number, index: number): MachineRecord {
const wave = Math.sin((tick + index) * 0.7);
const risk = Math.max(5, Math.min(99, machine.risk + wave * 1.7));
const blindHours = Math.max(0.2, machine.blindHours + 0.05 + wave * 0.03);
const latest = machine.deltaMeanSeries[machine.deltaMeanSeries.length - 1] ?? 0;
const nextPoint = Math.max(0, Math.min(0.28, latest + wave * 0.006));
return {
...machine,
risk,
blindHours,
deltaMeanSeries: [...machine.deltaMeanSeries.slice(1), Number(nextPoint.toFixed(3))]
};
}
接著簡化 effect:
useEffect(() => {
let tick = 0;
const timer = window.setInterval(() => {
tick += 1;
setMachines(previous => previous.map((machine, index) => simulateMachineUpdate(machine, tick, index)));
}, 5000);
return () => window.clearInterval(timer);
}, []);
商業邏輯說明
Update function 會模擬小幅 risk 與 trend changes,讓 dashboard 保持活躍。它不代表實際設備遙測。
程式碼邏輯說明
將 function 移出 App.tsx 會讓它可測試。React effect 只負責排程 updates。
預期結果
Behavior 維持不變,但程式碼更容易驗證與維護。
系統設計理由
● Scheduling 與 calculation 是不同 concerns。 React effects 應管理時間與 lifecycle。Domain functions 應計算新值。這種分離讓兩部分都更容易推理。
● Pure simulation 支援測試。 Pure function 可用已知 inputs 測試。這很有用,因為 live update bugs 常以 gradual drift 或 state mutation 形式出現,若只靠 UI tests 很難診斷。
● Model 保持誠實。 將 function 命名為 simulateMachineUpdate,清楚表明 values 是 synthetic。這可避免將 demo behavior 與 validated factory data 混淆。
延伸 Lab 區段:README 內容
要求 Kiro 使用此 prompt 產生 README:
Create a README for this local-only TypeScript React factory risk portal. Include install commands, run commands, architecture summary, Kiro steering and hook notes, advisory-only disclaimer, and a warning that fixture data must be replaced with validated engineering data before operational use.
README 最低內容:
# Factory Risk Portal
Local-only TypeScript React workshop project built with Kiro.
## Run
npm install
npm run dev
## Build
npm run build
## Safety boundary
This application is advisory-only. It does not command equipment, change route state, write to dispatch systems, modify APC feedback, or bypass human approval. Fixture data is for learning only and must be replaced with validated engineering data before operational use.
系統設計理由
● Documentation 是 deliverable 的一部分。 工程軟體應說明如何執行,以及它不做什麼。當 UI 類似工廠控制儀表板時,這特別重要。
● Safety boundary 會跟著 code 移動。 README 可被未參加工作坊的未來開發人員閱讀。它在 live Kiro session 之外保留 advisory-only constraint。
● Kiro 可產生實用 maintenance artifacts。 Documentation generation 是實作後使用 Kiro 的實用方式,但它應根據實際 project structure 與 constraints,而不是 generic boilerplate。
參與者除錯指南
問題:TypeScript 表示 sort key 無法索引 machine record
可能原因:SortKey 包含了非 numeric,或不存在於 MachineRecord 的 field。
修正 prompt:
Inspect SortKey and MachineRecord. Make the sort key union include only numeric MachineRecord fields. Update sort buttons to match. Do not use type assertions to hide the problem.
問題:Risk colors 已顯示,但 screen readers 沒有 status
可能原因:status 只透過 CSS 傳達。
修正 prompt:
Update risk display so it includes visible text and an aria-label with the numeric risk and risk tone. Keep the existing colors.
問題:Kiro 產生了新的 action label
可能原因:模型把 recommendations 當成普通 strings。
修正 prompt:
The recommendation field must use the existing Recommendation union. Replace unsupported labels with the closest existing advisory recommendation or propose a union update before changing fixtures.
問題:最終程式碼全部在一個檔案中
可能原因:implementation prompt 沒有充分約束 structure。
修正 prompt:
Refactor the existing behavior into the agreed structure. Preserve runtime behavior. Move types, fixtures, pure domain functions, and reusable components into separate files. Explain each movement before editing.
額外 Lab 區段 — 建置更完整的 fixture coverage
前面的 lab 使用 minimal fixture 來快速教學概念。若要更豐富的 workshop demo,請用額外 records 擴充 src/data/fixtures.ts。這會讓 sorting、searching、color thresholds、charting 與 KPI calculations 擁有更真實的 behavior。
export const additionalMachines: MachineRecord[] = [
{
id: 'CDSEM-05',
layer: 'Contact/Via',
symptom: 'Vacuum slow degradation, residual rising',
risk: 76,
blindHours: 6.9,
matchingGapNm: 0.19,
slope: 1.006,
fleetSigma: 2.4,
residual3SigmaNm: 0.21,
recommendation: 'GOLDEN WAFER',
deltaMeanSeries: [0.02, 0.01, 0.04, 0.06, 0.08, 0.10, 0.12, 0.13, 0.16, 0.18]
},
{
id: 'CDSEM-06',
layer: 'Overlay support',
symptom: 'Vibration burst after facility event',
risk: 72,
blindHours: 4.3,
matchingGapNm: 0.17,
slope: 0.989,
fleetSigma: 2.2,
residual3SigmaNm: 0.19,
recommendation: 'ROUTE LIMIT',
deltaMeanSeries: [0.01, 0.00, 0.03, 0.02, 0.12, 0.03, 0.13, 0.05, 0.15, 0.06]
},
{
id: 'CDSEM-10',
layer: 'Risk ramp product',
symptom: 'Yield-loss watch lots routed here',
risk: 82,
blindHours: 6.2,
matchingGapNm: 0.21,
slope: 0.976,
fleetSigma: 2.9,
residual3SigmaNm: 0.17,
recommendation: 'APC GUARD',
deltaMeanSeries: [0.02, 0.04, 0.07, 0.09, 0.11, 0.13, 0.15, 0.18, 0.20, 0.23]
}
];
商業邏輯說明
Expanded records 引入三種不同 risk patterns:residual-driven review、vibration-related route advisory 與 APC guard evidence。這讓開發人員能看到不同 machine symptoms 的範例,而不需連接真實 telemetry。
程式碼邏輯說明
Records 使用相同的 MachineRecord type。它們可用 spread operator 合併到 initialMachines。TypeScript 會驗證每個 recommendation 與 metric field。
預期結果
Live Risk Board 更適合 demonstration。現在依 risk、fleet sigma、residual 或 blind-window age 排序時,visible order 會有明顯變化。
系統設計理由
● 更豐富的 dataset 揭露 UI edge cases。 只有兩筆 records 時,開發人員無法完整驗證 sort behavior、row wrapping、warning colors 或多種 recommendation labels。額外 records 會建立真實 layout pressure,同時仍保持 lab 安全且決定性。
● 不同 symptoms 測試 domain language。 Vacuum、vibration 與 APC guard 範例迫使 UI 顯示 evidence text,而不只是 risk number。這很重要,因為工程 reviewers 在採取行動前需要知道 recommendation 背後的理由。
● Typed fixture expansion 證明 scalability。 新增 records 不應需要修改 components。如果 app 在新增 records 時壞掉,表示設計耦合太緊。這會教導開發人員 data-driven rendering 是系統設計目標。
額外 Lab 區段 — 將 KPI calculations 新增為 pure functions
建立 src/domain/kpi.ts:
import type { MachineRecord } from '../types';
export type KpiSummary = {
toolsWatched: number;
averageBlindHours: number;
fleetOocRiskCount: number;
yieldWatchCount: number;
fdcHealthLinks: number;
dynamicLimitsEnabled: boolean;
};
export function calculateKpis(machines: MachineRecord[], healthLinkCount: number): KpiSummary {
const averageBlindHours = machines.reduce((sum, machine) => sum + machine.blindHours, 0) / machines.length;
const fleetOocRiskCount = machines.filter(machine => machine.fleetSigma > 2).length;
const yieldWatchCount = machines.filter(machine =>
machine.recommendation === 'HOLD REVIEW' ||
machine.recommendation === 'GOLDEN WAFER' ||
machine.recommendation === 'APC GUARD'
).length;
return {
toolsWatched: machines.length,
averageBlindHours,
fleetOocRiskCount,
yieldWatchCount,
fdcHealthLinks: healthLinkCount,
dynamicLimitsEnabled: true
};
}
商業邏輯說明
KPI cards 摘要 operational review pressure:watched tools 數量、average blind-window age、fleet out-of-control exposure、health-link coverage、dynamic limit status 與 yield-watch load。
程式碼邏輯說明
Function 接收 machine records 與 health-link count,然後回傳具型別 KpiSummary。它將 calculations 集中在 React rendering 之外,因此相同 values 可被測試與重用。
預期結果
App.tsx 可用一個 pure function call 計算 KPI values,並將 summary 傳給 KpiStrip component。
系統設計理由
● KPI calculation 屬於 JSX 之外。 Dashboards 常把 KPI math 埋進 render blocks。這會讓 review 與 testing 更困難。Pure KPI function 讓 calculation 可見且可重用。
● Summary type 記錄 business meaning。 KpiSummary 為 UI 中顯示的 values 命名。這比匿名 strings 與 numbers 的 array 更容易審查。
● KPI logic 支援未來替換 fixture data。 當日後引入 live validated data 時,KPI function 可保持穩定。只有 data source 會改變。
額外 Lab 區段 — 新增 trajectory chart component
建立 src/components/TrajectoryChart.tsx:
type TrajectoryChartProps = {
data: number[];
threshold?: number;
};
export function TrajectoryChart({ data, threshold = 0.18 }: TrajectoryChartProps) {
const width = 620;
const height = 188;
const pad = 22;
const min = Math.min(...data, -0.02);
const max = Math.max(...data, 0.26);
const xFor = (index: number) => pad + (index / Math.max(1, data.length - 1)) * (width - pad * 2);
const yFor = (value: number) => height - pad - ((value - min) / (max - min || 1)) * (height - pad * 2);
const points = data.map((value, index) => `${xFor(index)},${yFor(value)}`).join(' ');
return (
<svg className="big-chart" viewBox={`0 0 ${width} ${height}`} role="img" aria-label="DeltaMean trajectory chart">
<line x1={pad} x2={width - pad} y1={yFor(threshold)} y2={yFor(threshold)} stroke="#ff5b6e" strokeDasharray="6 6" />
<polyline points={points} fill="none" stroke="#35d5ff" strokeWidth="4" strokeLinejoin="round" strokeLinecap="round" />
{data.map((value, index) => (
<circle key={index} cx={xFor(index)} cy={yFor(value)} r="4" fill={value > threshold ? '#ff5b6e' : '#35d5ff'} />
))}
</svg>
);
}
商業邏輯說明
Trajectory chart 顯示近期 drift 是否接近或跨越 review threshold。它為 selected machine row 提供 expanded evidence view。
程式碼邏輯說明
Component 將 data points 轉換成 SVG coordinates、繪製 threshold line、畫出 series,並以不同顏色標示高於 threshold 的 points,同時仍依賴其他地方的文字 metrics 來傳達 status。
預期結果
Expanded rows 可顯示較大的 chart,以支援 engineering review。
系統設計理由
● Expanded evidence 將 summary 與 detail 分開。 Table row 應保持 compact。Detailed drift evidence 屬於 expandable panel,由使用者明確要求更多 context 時顯示。
● Threshold rendering 支援 traceability。 視覺化顯示 threshold 會讓 recommendation 為何存在更容易理解。不過 status 仍必須以文字顯示,以避免 color-only interpretation。
● Typed chart props 降低誤用。 Chart 只接受 numeric data 與 optional numeric threshold。這讓 component 可重用,並防止 Kiro 在預期 number array 的地方傳入整個 machine objects。
額外 Lab 區段 — 為 pure logic 新增 unit tests
如果 workshop environment 允許加入 Vitest,請要求 Kiro 新增 tests。如果不想安裝 package,請把此段視為 optional read-through exercise。
Kiro prompt 範例
Add Vitest tests for the pure domain functions only. Test risk tone thresholds, search filtering, sorting by risk, sorting by fleet sigma, KPI calculation, and simulated update bounds. Do not test CSS or implementation details of React components.
Example test file src/domain/risk.test.ts:
import { describe, expect, it } from 'vitest';
import { getRiskTone, sortMachines } from './risk';
import { initialMachines } from '../data/fixtures';
describe('risk domain logic', () => {
it('classifies risk thresholds', () => {
expect(getRiskTone(20)).toBe('pass');
expect(getRiskTone(55)).toBe('warn');
expect(getRiskTone(80)).toBe('danger');
});
it('sorts machines by highest risk first', () => {
const sorted = sortMachines(initialMachines, 'risk');
expect(sorted[0].risk).toBeGreaterThanOrEqual(sorted[1].risk);
});
});
商業邏輯說明
Tests 捕捉 dashboard 背後的工程規則。未來 refactor 不應意外改變 risk thresholds 或 sort order。
程式碼邏輯說明
Tests 直接呼叫 pure functions。它們不依賴 browser 或 rendered components。
預期結果
開發人員取得一個快速 safety net,用於保護最重要的 logic。
系統設計理由
● 先測試 pure logic,再測 UI。 Risk thresholds 與 sort order 比 CSS class 是否套用更關鍵。Unit tests 應先保護工程決策。
● 在短 workshop 中避免脆弱 visual tests。 Snapshot 或 pixel-style testing 可能分散參與者注意力。Pure function tests 更容易理解與維護。
● Kiro 可在 contracts 存在後產生 tests。 當 types 與 functions 已穩定時,AI-generated tests 會更好。太早要求 tests,常會產生圍繞稍後會改變的 implementation details 的測試。
額外 Lab 區段 — 新增包含 營運邊界 的 README 內容
要求 Kiro 建立或更新 README.md:
Update README.md with setup commands, project structure, Kiro workflow, type standards, hook descriptions, and advisory-only limitations. State clearly that fixture data is synthetic learning data and must be replaced with validated engineering data before any operational use.
建議 README section:
## Operational boundary
This project is a learning application. It does not connect to factory equipment, dispatch systems, APC systems, or production databases. All records are local fixtures. Recommendations are advisory labels for review workflows only and must not be interpreted as autonomous machine commands.
系統設計理由
● Documentation 是 safety boundary 的一部分。 開發人員可能理解 app 只在本機執行,但未來讀者不一定知道。README text 會保留預期用途。
● Run instructions 降低 workshop friction。 參與者常在稍後回到專案。清楚 setup commands 可讓他們不依賴記憶就重新開始。
● Kiro workflow notes 讓課程可移植。 記錄 steering、specs 與 hooks,有助於參與者在自己的 repositories 中重用此模式。
進階挑戰 — 讓 Kiro 在不改變 behavior 的情況下 refactor
Kiro prompt 範例
Refactor the Live Risk Board into `RiskBoard.tsx`, `MachineRow.tsx`, and `EvidencePanel.tsx`. Preserve behavior exactly. Keep all props typed. Do not change CSS class names. Show a summary of files changed before implementation.
系統設計理由
● Refactoring 搭配明確 invariants 更安全。 「Preserve behavior exactly」與「do not change CSS class names」是重要 constraints。沒有這些限制,Kiro 可能改善結構,卻意外破壞 styling 或 interactions。
● Typed props 揭露 component boundaries。 Refactoring 會迫使開發人員決定每個 component 擁有什麼。這是檢驗 domain model 是否乾淨的好方法。
● Change summary 改善 review。 在實作前,Kiro 應說明它計畫 touch 哪些 files。這可降低驚訝感,並示範專業 pull-request behavior。