極點宏觀|Financial Cloud Cloud · 建構文章
Kiro:從零建置 Fab SPC Drift Synchronization Portal
僅供教育工程用途。本內容是軟體架構演練,不屬於製程放行建議。
步驟 7 — Render 入口網站
Kiro 提示詞
Create src/ui/render.ts and src/main.ts to render a dark engineering portal. Include summary cards, search, sort buttons, a risk board, FDC health-link cards, dynamic matching notes, yield triage notes, and runbook steps. Use assessFleet from the domain layer.
程式碼範例 — src/main.ts
import { cdSemTools, fdcHealthLinks, runbookSteps } from "./data/sampleData";
import { assessFleet } from "./domain/risk";
import { renderApp } from "./ui/render";
import "./styles.css";
const root = document.querySelector<HTMLDivElement>("#app");
if (!root) {
throw new Error("Missing #app root element");
}
renderApp(root, {
tools: cdSemTools,
assessments: assessFleet(cdSemTools),
fdcHealthLinks,
runbookSteps
});
說明
商業邏輯: 應用程式從已知的 CD-SEM 記錄開始,計算風險評估,並將所有可供顯示的資料傳遞給 render 層。
程式碼邏輯: main.ts 刻意保持精簡。它匯入資料、呼叫領域引擎、檢查 DOM 根元素,並將 UI 建構委派給 renderApp。
預期結果: 執行 npm run dev 會顯示入口網站。如果缺少 #app,應用程式會以清楚的錯誤快速失敗。
系統設計決策
- 進入點只負責協調。 它不應包含業務規則或複雜的 HTML。這讓產生的程式碼更容易審查,也讓應用程式的啟動路徑清楚。
- 快速失敗的根元素驗證可避免無聲的空白畫面。 如果 HTML shell 損壞,開發人員會取得明確錯誤,而不是除錯空白頁面。這能改善工作坊的疑難排解。
- 先計算評估,再進行 render。 UI 收到的已是解讀過的領域資料。這種分離讓風險規則能獨立於視覺版面與互動程式碼演進。
步驟 8 — 測試
Kiro 提示詞
Create tests/risk.test.ts using Vitest. Test Release, ApcGuard, HoldReview for slope, HoldReview for fleet deviation, RunGoldenWafer for residual noise, and Watch for stale blind-window conditions.
程式碼範例 — tests/risk.test.ts
import { describe, expect, it } from "vitest";
import { assessTool } from "../src/domain/risk";
import type { CdSemTool } from "../src/domain/types";
function tool(overrides: Partial<CdSemTool> = {}): CdSemTool {
return {
id: "CDSEM-T",
layer: "Gate ADI",
symptom: "baseline",
blindWindowHours: 1,
tmgNm: 0.1,
mandelSlope: 1,
fleetDeviationSigma: 0.5,
residualThreeSigmaNm: 0.08,
deltaMeanSeries: [0.01, 0.02],
...overrides
};
}
describe("assessTool", () => {
it("releases a healthy tool", () => {
expect(assessTool(tool()).action).toBe("Release");
});
it("guards APC when TMG exceeds limit", () => {
expect(assessTool(tool({ tmgNm: 0.25 })).action).toBe("ApcGuard");
});
it("requires hold review when Mandel Slope is out of range", () => {
expect(assessTool(tool({ mandelSlope: 1.031 })).action).toBe("HoldReview");
});
it("requires hold review when Fleet deviation exceeds 3 sigma", () => {
expect(assessTool(tool({ fleetDeviationSigma: 3.2 })).action).toBe("HoldReview");
});
it("runs golden wafer when residual noise exceeds limit", () => {
expect(assessTool(tool({ residualThreeSigmaNm: 0.21 })).action).toBe("RunGoldenWafer");
});
it("watches a stale blind window", () => {
expect(assessTool(tool({ blindWindowHours: 8 })).action).toBe("Watch");
});
});
說明
商業邏輯: 測試將每種量測條件預期得到的建議結果編碼下來。
程式碼邏輯: Fixture factory 建立健康的預設機台,並在每個測試中覆寫一個欄位。每個斷言驗證一項特定規則。
預期結果: 當風險引擎遵循 spec 時,npm test 會通過。未來若規則變更而破壞建議行為,測試會失敗。
系統設計決策
- 每個測試只變更一個欄位。 這能隔離因果關係,讓失敗容易理解。除非明確測試優先順序行為,否則 Kiro 產生的測試應避免混合許多無關的風險因素。
- Fixture factory 減少重複。 開發人員可以快速新增測試,不必複製完整的機台物件。這能改善可維護性並鼓勵涵蓋更多邊界案例。
- 測試斷言動作,而不是實作細節。 業務結果比精確的評分算術更重要。這讓內部評分能持續演進,同時保留操作行為。
步驟 9 — Hooks
Kiro 提示詞
Create .kiro/hooks/test-domain-on-save.json. Run npm test when files under src/domain or tests are saved. Also create a documentation agent hook that updates docs/decision-log.md after spec task execution.
程式碼範例 — .kiro/hooks/test-domain-on-save.json
{
"version": "v1",
"hooks": [
{
"name": "test-domain-on-save",
"description": "Run the deterministic risk tests when domain or test files are saved.",
"trigger": "PostFileSave",
"matcher": "^(src/domain/.*\\.ts|tests/.*\\.ts)$",
"action": {
"type": "command",
"command": "npm test"
},
"timeout": 60,
"enabled": true
}
]
}
說明
商業邏輯: 如果風險邏輯或測試變更,專案會立即檢查建議行為是否仍然有效。
程式碼邏輯: JSON hook 監聽符合領域或測試 TypeScript 檔案的檔案儲存事件,並執行 npm test。
預期結果: 儲存 src/domain/risk.ts 會觸發測試套件,快速顯示回歸問題。
系統設計決策
- Hook 只鎖定高價值檔案。 每次 CSS 編輯都執行測試會浪費時間。只比對領域與測試檔案,能讓自動化保持相關且快速。
- 動作具備決定性。
npm test有可預期的成功或失敗輸出,因此比可能意外修改檔案的廣泛 agent action 更安全。 - 逾時保護開發循環。 Hook 不應卡住 IDE。60 秒足以執行工作坊套件,也能教導參與者為自動化設定界限。
步驟 10 — Kiro 最終審查
Kiro 提示詞
Review the implementation against the fab-spc-portal spec. Check domain correctness, safety language, test coverage, accessibility, project structure, and hook safety. Produce a prioritized review report without modifying files.
預期的審查主題
- 建議動作是否安全且非自主?
- 閾值是否集中管理?
- 風險原因是否可解釋?
- 每項關鍵規則是否都有測試?
- 新開發人員能否從 steering 與 spec 檔案理解專案?
- Hooks 的範圍是否夠窄?
系統設計決策
- 最終審查讓 Kiro 成為工程夥伴,而不是未經檢查的程式碼產生器。 開發人員要求結構化批評,再決定接受哪些發現。這能強化專業責任。
- 不修改檔案的審查可避免意外變更。 目標是在繼續編輯前檢查涵蓋範圍與風險。這類似 pull request 審查,將分析與實作分開。
- 審查會完成 spec 循環。 工作坊從意圖開始,最後以比較程式碼與意圖結束。這正是專業團隊應透過 Kiro 建立的核心習慣。
# 擴充 Lab:使用 Kiro 從 HTML Demo 撰寫程式碼
此擴充 Lab 保留原始建置指南,並加入來自 HTML Demo 的更多實作細節。目標是訓練開發人員使用 Kiro,將可運作的單檔 prototype 轉換為可維護的 TypeScript,同時盡可能保留 Demo 行為。
步驟 11 — 要求 Kiro 分析單體式 HTML Demo
Kiro 提示詞
Analyze the uploaded Fab SPC Drift Synchronization HTML demo. Identify all major UI sections, JavaScript data structures, functions, event handlers, CSS design tokens, and state transitions. Return a module extraction plan for a TypeScript/Vite implementation. Do not modify files yet.
預期的 Kiro 分析輸出
Kiro 應辨識出以下 Demo 元件:
- Header 與 hero 區段。
- 摘要統計卡片。
- 使用
.tab按鈕與.portal面板的分頁導覽。 - 包含 CD-SEM 記錄的
tools陣列。 - 包含 FDC health-link 記錄的
fdc陣列。 - 包含專案實作步驟的
runbook陣列。 - 用於 FDC 卡片、runbook 步驟與 market 卡片的
renderStatic()。 - 用於風險看板資料列的
render()。 - 用於可展開機台詳細資訊證據面板的
panel()。 - 用於 inline SVG 視覺化的
path()、spark()與fleetSvg()。 - 用於即時時間顯示的
clock()。 - 會變更盲窗與風險值的
setInterval()模擬。
系統設計決策
- 在重寫之前先做分析。 專業開發人員應要求 Kiro 先解釋並分類 prototype,再允許它產生替代程式碼。這能避免遺失原始 JavaScript 中隱藏的重要行為。
- 輸出會成為遷移檢查清單。 每個已辨識的函式都會對應到目標模組與測試策略。這能清楚說明哪些 Demo 行為已保留,哪些行為被刻意重新設計。
- 將模擬與 production 邏輯分離。 Demo 使用
setInterval()變更值。在專業實作中,模擬應屬於 Demo adapter,而不是領域模型。Kiro 應將它隔離,讓測試保持決定性。
步驟 12 — 將 Demo 資料擷取為具型別記錄
Kiro 提示詞
Convert the demo's tools, fdc, and runbook arrays into typed TypeScript fixture modules. Preserve the values and labels from the HTML demo, but use camelCase fields and explicit interfaces. Put tools in src/data/tools.ts, FDC links in src/data/fdc.ts, and runbook steps in src/data/runbook.ts.
程式碼範例 — src/data/tools.ts
import type { CdSemTool } from "../domain/types";
export const cdSemTools: CdSemTool[] = [
{
id: "CDSEM-01",
layer: "Gate ADI",
symptom: "Stable master, production anchor",
risk: 18,
blindWindowHours: 1.2,
tmgNm: 0.12,
mandelSlope: 1.0,
fleetDeviationSigma: 0.4,
residualThreeSigmaNm: 0.08,
action: "Release",
deltaMeanSeries: [0.02, 0.01, 0.03, 0.01, 0.02, 0.0, 0.01, 0.02, 0.01, 0.02]
},
{
id: "CDSEM-03",
layer: "Fin dense CD",
symptom: "Deflector DAC wobble, slope mismatch",
risk: 91,
blindWindowHours: 7.4,
tmgNm: 0.23,
mandelSlope: 1.031,
fleetDeviationSigma: 3.2,
residualThreeSigmaNm: 0.16,
action: "HoldReview",
deltaMeanSeries: [0.02, 0.03, 0.06, 0.05, 0.08, 0.12, 0.16, 0.2, 0.22, 0.24]
},
{
id: "CDSEM-05",
layer: "Contact/Via",
symptom: "Vacuum slow degradation, residual rising",
risk: 76,
blindWindowHours: 6.9,
tmgNm: 0.19,
mandelSlope: 1.006,
fleetDeviationSigma: 2.4,
residualThreeSigmaNm: 0.21,
action: "RunGoldenWafer",
deltaMeanSeries: [0.02, 0.01, 0.04, 0.06, 0.08, 0.1, 0.12, 0.13, 0.16, 0.18]
}
];
說明
商業邏輯: 這些記錄保留 Demo 中近似生產環境的案例:健康的母機、斜率不匹配、真空退化、殘差雜訊、Fleet 偏差與盲窗老化。
程式碼邏輯: 單體式 Demo 使用 blind、tmg、slope、fleet 與 resid 等短欄位名稱。TypeScript 版本改用 blindWindowHours、tmgNm、mandelSlope 與 residualThreeSigmaNm 等明確名稱。
預期結果: 應用程式可以 render 與 HTML Demo 相同的情境,同時讓 TypeScript 驗證每筆記錄的形狀。
系統設計決策
- 明確欄位名稱降低認知負擔。 原始 Demo 為了簡潔使用緊湊欄位名稱。在 production-style 程式碼中,領域名稱應能自我說明,因為多位開發人員與審查者都會接觸它。
- Fixture 資料保持接近 prototype。 保留原始值,讓開發人員能比較 TypeScript 應用程式與 HTML Demo。這使視覺與行為 parity 更容易驗證。
- 動作以具型別值正規化。 Demo 標籤是
HOLD REVIEW與GOLDEN WAFER等顯示字串。TypeScript 模型使用穩定的類似 enum 的動作值,並讓 UI formatting 處理標籤。
步驟 13 — 從 Demo 標籤新增動作標籤對應
Kiro 提示詞
Create a display mapping for advisory actions. Domain actions must be stable TypeScript values, while UI labels should match the HTML demo labels such as RELEASE, WATCH, HOLD REVIEW, GOLDEN WAFER, ROUTE LIMIT, RUN SPC, and APC GUARD.
程式碼範例 — src/domain/actions.ts
import type { AdvisoryAction } from "./types";
export const actionLabels: Record<AdvisoryAction, string> = {
Release: "RELEASE",
Watch: "WATCH",
RunGoldenWafer: "GOLDEN WAFER",
RouteLimit: "ROUTE LIMIT",
RunSpc: "RUN SPC",
ApcGuard: "APC GUARD",
HoldReview: "HOLD REVIEW"
};
export function isHighAttentionAction(action: AdvisoryAction): boolean {
return action === "ApcGuard" || action === "HoldReview";
}
export function isWatchAction(action: AdvisoryAction): boolean {
return action === "Watch" || action === "RunSpc" || action === "RunGoldenWafer" || action === "RouteLimit";
}
說明
商業邏輯: Fab 工程師可以看到 Demo 中熟悉的標籤,而內部程式碼維持穩定的值供測試與邏輯使用。
程式碼邏輯: actionLabels 將領域動作轉換為顯示標籤。輔助函式將動作分類以供樣式使用。
預期結果: UI 輸出符合 Demo,測試也能斷言穩定的動作值,而不依賴標籤大小寫。
系統設計決策
- 顯示文字不是領域邏輯。 標籤可能因在地化或 UX 而變更。領域值應保持穩定,讓 UI 文字變更時測試與業務規則不會中斷。
- 樣式輔助程式避免散落字串。 沒有輔助函式時,每個 render 器可能檢查不同的標籤字串。集中管理能讓色彩與嚴重程度邏輯一致。
- 對應表支援未來在地化。 相同的動作值可以對應到英文、繁體中文或內部 fab 術語,而不必重寫風險引擎。
步驟 14 — 將 Demo 搜尋與排序邏輯轉換為純輔助函式
Kiro 提示詞
Extract search and sort behavior from the HTML demo into pure TypeScript functions. Search should match tool id, layer, or symptom. Sort should support risk, blindWindowHours, tmgNm, mandelSlope, fleetDeviationSigma, and residualThreeSigmaNm. Add unit tests.
程式碼範例 — src/domain/filters.ts
import type { CdSemTool } from "./types";
export type ToolSortKey =
| "risk"
| "blindWindowHours"
| "tmgNm"
| "mandelSlope"
| "fleetDeviationSigma"
| "residualThreeSigmaNm";
export function filterTools(tools: CdSemTool[], query: string): CdSemTool[] {
const normalized = query.trim().toLowerCase();
if (!normalized) {
return tools;
}
return tools.filter((tool) => {
return [tool.id, tool.layer, tool.symptom]
.join(" ")
.toLowerCase()
.includes(normalized);
});
}
export function sortTools(tools: CdSemTool[], sortKey: ToolSortKey): CdSemTool[] {
return [...tools].sort((a, b) => b[sortKey] - a[sortKey]);
}
export function filterAndSortTools(
tools: CdSemTool[],
query: string,
sortKey: ToolSortKey
): CdSemTool[] {
return sortTools(filterTools(tools, query), sortKey);
}
說明
商業邏輯: 工程師需要快速找到特定 CD-SEM、圖層或症狀,並依風險、過時的 SPC 盲窗、TMG、斜率、Fleet 偏差或殘差雜訊排列優先順序。
程式碼邏輯: filterTools 對可搜尋欄位執行不分大小寫的比對。sortTools 回傳副本而不是修改原始陣列。filterAndSortTools 組合這兩種行為。
預期結果: TypeScript 應用程式能重現 HTML Demo 的搜尋與排序行為,同時讓它們可進行單元測試。
系統設計決策
- 篩選與排序是純領域/UI 支援函式。 它們不需要 DOM。擷取出來後可以進行單元測試,也能避免事件處理常式變成業務邏輯容器。
- 排序回傳新陣列。 修改共用 fixture 資料可能造成令人困惑的 UI 錯誤,尤其與重複 render 結合時更是如此。不可變輸出讓狀態轉換更容易預測。
- 搜尋欄位刻意受到限制。 比對每個屬性可能產生令人意外的結果。本工作坊選擇 id、layer 與 symptom,因為這些是工程師在 Demo 中尋找機台時會使用的欄位。
步驟 15 — 將 Demo SVG 輔助程式轉換為圖表工具
Kiro 提示詞
Extract the demo SVG path, sparkline, and fleet bar chart logic into src/ui/chart.ts. Keep the functions deterministic and return strings. Add tests for path generation with flat and increasing series.
程式碼範例 — src/ui/chart.ts
export function svgPath(values: number[], width: number, height: number, padding = 6): string {
if (values.length === 0) {
return "";
}
if (values.length === 1) {
const x = width / 2;
const y = height / 2;
return `M${x.toFixed(1)} ${y.toFixed(1)}`;
}
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) / (values.length - 1);
const y = padding + innerHeight - ((value - min) / range) * innerHeight;
return `${index === 0 ? "M" : "L"}${x.toFixed(1)} ${y.toFixed(1)}`;
})
.join(" ");
}
export function sparkline(values: number[], className = "line"): string {
return `
<svg class="spark" viewBox="0 0 120 32" preserveAspectRatio="none" aria-hidden="true">
<path class="${className}" d="${svgPath(values, 120, 32, 2)}"></path>
</svg>
`;
}
說明
商業邏輯: 小型圖表不需要完整的圖表函式庫,就能讓漂移趨勢可見。工程師可以看出 ΔMean 是穩定、趨勢性變化還是充滿雜訊。
程式碼邏輯: svgPath 將數值序列縮放為 SVG path 座標。sparkline 將 path 包裝在 SVG 標記中,並以 aria-hidden 標示為裝飾內容。
預期結果: 產生的 TypeScript 應用程式能重現 Demo 的 inline spark 圖表與詳細資訊面板中的大型軌跡圖。
系統設計決策
- 圖表輔助程式回傳字串,因為應用程式不使用框架。 這讓 render 模型與 Demo 一致,也避免在兩小時工作坊中引入圖表相依套件。
- 明確處理邊界案例。 空序列與單一點序列在天真的 path 函式中會產生無效數學運算。Kiro 應產生安全的輔助程式,因為 production 資料可能不完整。
- 隔離 SVG 輔助程式以便測試。 重構時很容易破壞 render 數學。將它留在
chart.ts可讓開發人員不啟動瀏覽器,也能精確測試。
步驟 16 — 將分頁切換轉換為可重用的控制器
Kiro 提示詞
Extract the demo tab switching behavior into src/ui/tabs.ts. Implement activateTab(tabId), bindTabs(container), and getActiveTab(container). Use data-tab attributes like the HTML demo. Keep it framework-free and accessible.
程式碼範例 — src/ui/tabs.ts
export function activateTab(tabId: string, root: ParentNode = document): void {
root.querySelectorAll<HTMLElement>(".tab").forEach((button) => {
const isActive = button.dataset.tab === tabId;
button.classList.toggle("active", isActive);
button.setAttribute("aria-selected", String(isActive));
});
root.querySelectorAll<HTMLElement>(".portal").forEach((panel) => {
const isActive = panel.id === tabId;
panel.classList.toggle("active", isActive);
panel.toggleAttribute("hidden", !isActive);
});
}
export function bindTabs(root: ParentNode = document): void {
root.querySelectorAll<HTMLButtonElement>(".tab[data-tab]").forEach((button) => {
button.addEventListener("click", () => {
const tabId = button.dataset.tab;
if (tabId) {
activateTab(tabId, root);
}
});
});
}
export function getActiveTab(root: ParentNode = document): string | undefined {
return root.querySelector<HTMLElement>(".tab.active")?.dataset.tab;
}
說明
商業邏輯: 入口網站使用分頁區隔 overview、live risk board、FDC、matching、triage 與 runbook 工作流程。
程式碼邏輯: activateTab 切換按鈕與面板 class、設定 aria-selected,並使用 hidden 將未啟用面板從輔助技術中隱藏。bindTabs 只附加一次點擊處理常式。
預期結果: TypeScript 應用程式的行為與 Demo 相似,同時比只切換 class 更具無障礙性。
系統設計決策
- 集中管理分頁狀態。 原始 Demo 直接在 query selector 迴圈中附加 inline 行為。控制器函式讓分頁行為可重用且可測試。
- 同步更新 ARIA 狀態與視覺狀態。 專業 UI 程式碼不應只切換 CSS class。螢幕閱讀器需要明確的啟用狀態,未啟用面板也應隱藏。
- 控制器維持無框架。 工作坊保留原始 Demo 的簡潔性,同時加入更好的結構與無障礙性。
步驟 17 — 轉換詳細資訊面板 render
Kiro 提示詞
Convert the demo panel(p) function into a TypeScript renderer that receives a CdSemTool and RiskAssessment. It should render status boxes, TMG vs UCL, slope, Fleet σ, residual 3σ, blind window, recommendation, evidence, SVG trajectory, and root-cause hint.
程式碼範例 — src/ui/board.ts
import type { CdSemTool, RiskAssessment } from "../domain/types";
import { actionLabels } from "../domain/actions";
import { svgPath } from "./chart";
export function renderDetailPanel(tool: CdSemTool, assessment: RiskAssessment): string {
const trajectory = svgPath(tool.deltaMeanSeries, 680, 190, 20);
return `
<div class="detail" id="detail-${tool.id}">
<div class="detail-in">
<div class="grid2">
<div class="chart-card">
<div class="chart-title">
<span>ΔMean trajectory between scheduled SPC</span>
<span>${escapeHtml(tool.symptom)}</span>
</div>
<svg class="svg eq" viewBox="0 0 680 190" preserveAspectRatio="none" role="img" aria-label="${tool.id} drift trajectory">
<line class="axis" x1="20" y1="150" x2="660" y2="150"></line>
<line class="gridline" x1="20" y1="80" x2="660" y2="80"></line>
<path class="fill" d="${trajectory} L 660 150 L 20 150 Z"></path>
<path class="${assessment.score > 70 ? "line2" : "line"}" d="${trajectory}"></path>
</svg>
</div>
<div class="metrics">
<div class="chart-title"><span>Tool Health Evidence</span><span>human review required</span></div>
<div class="metricgrid">
${metricBox("Status", assessment.level.toUpperCase(), assessment.level === "critical" ? "neg" : "yellow")}
${metricBox("Action", actionLabels[assessment.action], assessment.action === "Release" ? "pos" : "yellow")}
${metricBox("Score", String(assessment.score), assessment.score >= 65 ? "neg" : "pos")}
${metricBox("Reasons", String(assessment.reasons.length), "cyan")}
</div>
</div>
</div>
<div class="box" style="margin-top:12px">
<div class="boxl">Root-cause hint</div>
<div class="strategy">${escapeHtml(tool.id)} on ${escapeHtml(tool.layer)}: ${escapeHtml(tool.symptom)}.</div>
</div>
</div>
</div>
`;
}
function metricBox(label: string, value: string, tone: "pos" | "neg" | "yellow" | "cyan"): string {
return `<div class="box"><div class="boxl">${label}</div><div class="boxv ${tone}">${value}</div></div>`;
}
function escapeHtml(value: string): string {
return value
.replaceAll("&", "&")
.replaceAll("<", "<")
.replaceAll(">", ">")
.replaceAll('"', """)
.replaceAll("'", "'");
}
說明
商業邏輯: 詳細資訊面板提供建議背後的證據:漂移軌跡、分數、動作、嚴重程度與根本原因提示。
程式碼邏輯: Render 器以具型別輸入組合 HTML。它使用 svgPath 產生圖表幾何、使用 actionLabels 產生顯示字串,並使用 escapeHtml 防止 fixture 文字變成不安全標記。
預期結果: 點擊或展開機台時,可以顯示與 HTML Demo 相似、但以模組化 TypeScript 實作的面板。
系統設計決策
- 證據面板能提高信任。 只有風險分數並不足夠。開發人員應保留 Demo 的詳細資訊面板概念,因為它能解釋建議的原因。
- 在遷移期間加入 escaping。 原始 Demo 控制所有字串,但專業程式碼不應假設未來的資料永遠安全。Escaping 能防止意外的 HTML injection。
- 圖表 render 保持輕量。 面板以不使用外部相依套件的方式重現 prototype 的 SVG 方法。這適合本機 Kiro 工作坊,也讓程式碼審查保持可控。
步驟 18 — 以決定性 Demo 模式取代即時亂數變更
Kiro 提示詞
The HTML demo uses setInterval and Math.random to mutate risk values. Replace this with an optional deterministic demo ticker that accepts a seeded sequence of deltas. Keep production state immutable and tests deterministic.
程式碼範例 — src/domain/demoTicker.ts
import type { CdSemTool } from "./types";
export function applyDemoTick(tools: CdSemTool[], deltas: number[]): CdSemTool[] {
return tools.map((tool, index) => {
const delta = deltas[index % deltas.length] ?? 0;
const nextBlindWindowHours = Math.max(0, round(tool.blindWindowHours + 0.02));
const nextRisk = Math.max(0, Math.min(100, Math.round(tool.risk + delta)));
const lastDeltaMean = tool.deltaMeanSeries.at(-1) ?? 0;
return {
...tool,
blindWindowHours: nextBlindWindowHours,
risk: nextRisk,
deltaMeanSeries: [...tool.deltaMeanSeries.slice(1), round(Math.max(0, lastDeltaMean + delta / 1000))]
};
});
}
function round(value: number): number {
return Number(value.toFixed(3));
}
說明
商業邏輯: Demo 仍可以顯示隨時間變動的值,但變更對測試與示範而言是可預測的。
程式碼邏輯: applyDemoTick 回傳含有更新後盲窗持續時間、風險與時間序列資料的新陣列。它不會修改原始機台。
預期結果: 開發人員可以示範類似即時的行為,同時讓單元測試保持穩定。
系統設計決策
- 從核心邏輯移除亂數。 原始 Demo 使用
Math.random()製造視覺變動。專業程式碼應隔離亂數,讓測試與審查不依賴非決定性行為。 - 不可變更新簡化 render。 回傳新物件讓狀態變更更容易推理,也能防止模組之間的隱藏修改。
- 明確標示 Demo 模式。 Ticker 對工作坊很有用,但不應看起來像真正的遙測資料。命名為
demoTicker可以避免與 production ingestion 混淆。
步驟 19 — 為 HTML Demo 衍生的輔助程式新增測試
Kiro 提示詞
Generate tests for the helper functions extracted from the HTML demo: filterTools, sortTools, svgPath, applyDemoTick, action label mapping, and risk priority. Include edge cases for empty query, unknown query, flat series, single-point series, and immutable updates.
程式碼範例 — tests/demoHelpers.test.ts
import { describe, expect, it } from "vitest";
import { filterTools, sortTools } from "../src/domain/filters";
import { applyDemoTick } from "../src/domain/demoTicker";
import { svgPath } from "../src/ui/chart";
import { cdSemTools } from "../src/data/tools";
describe("demo-derived helpers", () => {
it("filters by layer or symptom", () => {
expect(filterTools(cdSemTools, "deflector")).toHaveLength(1);
expect(filterTools(cdSemTools, "gate").length).toBeGreaterThan(0);
});
it("returns all tools for empty search", () => {
expect(filterTools(cdSemTools, "")).toHaveLength(cdSemTools.length);
});
it("sorts without mutating the original array", () => {
const before = cdSemTools.map((tool) => tool.id).join(",");
const sorted = sortTools(cdSemTools, "risk");
const after = cdSemTools.map((tool) => tool.id).join(",");
expect(sorted[0].risk).toBeGreaterThanOrEqual(sorted.at(-1)?.risk ?? 0);
expect(after).toBe(before);
});
it("creates an SVG path for a flat series", () => {
expect(svgPath([1, 1, 1], 120, 32, 2)).toContain("M");
});
it("applies deterministic demo ticks immutably", () => {
const updated = applyDemoTick(cdSemTools, [1, -1]);
expect(updated).not.toBe(cdSemTools);
expect(updated[0]).not.toBe(cdSemTools[0]);
expect(updated[0].blindWindowHours).toBeGreaterThan(cdSemTools[0].blindWindowHours);
});
});
說明
商業邏輯: 測試驗證 HTML Demo 衍生的行為,在變得更安全且更可維護的同時仍被保留。
程式碼邏輯: 測試套件檢查篩選、排序、SVG path 建立與決定性狀態更新,也驗證不可變性。
預期結果: Kiro 重構 render 或狀態邏輯時,這些測試會捕捉意外的行為回歸。
系統設計決策
- 輔助程式測試保護擷取出的行為。 從 HTML 遷移至 TypeScript 可能細微破壞排序、篩選或圖表。這些測試保護 Demo 的核心互動。
- 不可變性測試防止隱藏耦合。 在精簡 Demo 中,修改資料或許可以接受,但在較大型的應用程式中會產生缺陷。測試不可變性會迫使狀態管理更乾淨。
- 圖表測試關注有效性,而不是像素。 SVG path 測試應確保邊界案例的輸出格式正確。像素級 render 應由視覺審查處理,而非單元測試。
步驟 20 — 要求 Kiro 產生 Demo parity 檢查清單
Kiro 提示詞
Create a demo parity checklist comparing the TypeScript implementation against the original HTML demo. Include sections for layout, navigation, data, risk board, detail panel, FDC cards, runbook, charts, responsive behavior, and safety language.
預期的檢查清單
# Demo Parity Checklist
## Layout
- [ ] Header contains AWS logo block and live status.
- [ ] Hero contains SPC Frequency / Mass Production Sync message.
- [ ] Summary cards show tools watched, blind window, Fleet OOC risk, FDC links, dynamic limits, yield watch lots.
## Navigation
- [ ] Tabs switch active panel without page reload.
- [ ] Active tab state is visually clear.
- [ ] Inactive panels are hidden from assistive technology.
## Risk Board
- [ ] Search matches tool id, layer, and symptom.
- [ ] Sort works for risk, blind window, TMG, slope, Fleet σ, and residual.
- [ ] Detail panels show evidence and trajectory.
## Safety
- [ ] No button issues real equipment commands.
- [ ] Footer states advisory-only behavior.
- [ ] APC Guard is represented as review guidance, not an automated APC write.
系統設計決策
- Parity 檢查清單防止意外縮減範圍。 Kiro 將程式碼模組化時,可能遺漏 prototype 中小而重要的行為。檢查清單提供簡明的驗證工具。
- 安全有自己的檢查清單區段。 此入口網站的領域需要明確的僅供建議語言。將安全視為獨立審查區域,能更難漏掉它。
- 無障礙功能改善原始 Demo。 Parity 不代表複製每項限制。TypeScript 實作應保留行為,同時改善 ARIA 狀態與鍵盤操作性。