| name | data-sanity |
| description | Layer C of spectra pre-handoff quality gates. Static audit for the data-shape failure class where a client query param literal violates the server zod schema bound (e.g. perPage: 200 vs max(100)) → silent 4xx → lookup map empties → admin list column collapses to a uniform fallback. Use when running /spectra-apply Design Review, before /spectra-archive, or ad-hoc on any change that touches paginated queries + lookup-resolved columns. |
| effort | medium |
| metadata | {"author":"clade","version":"1.0"} |
/data-sanity — Layer C static data-shape audit.
Born from perno app-status-badge-extraction(2026-05-24): attendance/amendments.vue 用 useEmployeeListQuery({ perPage: 200 }),但 server schema 是 perPage: z....max(100) → API 400 → employeeNameMap empty → 員工 column 整列「-」。typecheck / lint / design review / verify:ui 全沒抓到,因為它是資料形狀問題,不是型別或視覺問題。
這是 clade 自有 skill(不是 /impeccable 的 sub-command — impeccable 是第三方 npx-managed skill,clade 不擁有)。
何時跑
/spectra-apply Section 7 Design Review 期間:對本 change 觸及的 paginated query + lookup-resolved column 跑一次。
/spectra-archive Step 0 之前:archive gate 的 data-shape 防線。
- Ad-hoc:任何改到
useXxxQuery({ <param>: <literal> }) 或 lookup map 的工作。
怎麼跑
從 clade central 呼叫(<clade-vendor> = ~/offline/clade/vendor,與其他 vendor script 同慣例):
node <clade-vendor>/scripts/audit-data-sanity.mjs \
--consumer-path . \
[--files <comma-separated-touched-files>] \
--json
- 不給
--files → walk 整個 consumer(skip node_modules/.nuxt/dist)。
- 給
--files → 只掃指定檔(spectra-apply 時傳本 phase touched files 較快)。
偵測項
- PARAM_BOUNDARY(Critical — blocks archive):client 端 pagination-ish param literal(
perPage / per_page / pageSize / limit / take / first / count / top / size)超過(max)或低於(min)同名 server zod bound(.max(N) / .min(N) / .length(N))。對應 UI-INV-2(lookup-resolved column 解析率 100%)。
- LOOKUP_MAP_RISK(advisory / warn):偵測
xxxMap / xxxLookup / xxxById 從 query data(.reduce( / Object.fromEntries( / new Map()建的 lookup map。提醒:來源 query 若失敗,此 map empty → 對應 column uniform fallback。確認來源 query param 在 schema bound 內。
限制(heuristic,非真 AST):clade home 無 oxc-parser / @vue/compiler-sfc,本 audit 是 dependency-free regex 掃描(與 d-pattern-audit.mjs / manual-review-audit.mjs 同慣例),靠 param 名跨檔對應 連結 client literal ↔ server bound。同名 param 出現在不相關 schema 時可能 false-link;PARAM_BOUNDARY 命中後請人工確認該 client query 真打到該 server endpoint。
結果處置
- exit 0
status: "pass" → 通過。advisory lookupRisks 印到 stderr 供參考,不 block。
- exit 1
status: "fail"(含 PARAM_BOUNDARY)→ Critical,MUST block:root-cause 修 client literal 到 bound 內(典型:perPage: 200 → 100),或若新 bound 是有意則調 server schema。NEVER 標 phase / archive done、NEVER 留給 user 在 manual review 抓。
- JSON schema:
{ status: "pass"|"fail", violations: [{param, clientValue, kind, bound, clientFile, clientLine, serverFile, serverLine}], lookupRisks: [{name, file, line}], scanned: N }。
與其他 Layer 的關係
- Layer B(
refactor-invariant-check.mjs):runtime 偵測「column 整欄 fallback」(症狀)。Layer C 是 static 偵測「param 超界」(root cause)。兩者互補。
- Layer D(
UI-INVARIANTS.md):UI-INV-2 是本 skill 的契約來源。
- Layer E.1(pre-handoff self-analysis):D4 維度(API contract boundary)可直接引用本 audit 結果。