Skip to content

fix: 中文 model_version_id 轉檔派工 400 根治(全欄位 sanitize + 回拋對帳 + #/conv dispatch_error 可見) - #206

Merged
monkey1sai merged 13 commits into
mainfrom
fix/conversion-artifact-id-sanitize
Jun 11, 2026
Merged

monkey1sai merged 13 commits into
mainfrom
fix/conversion-artifact-id-sanitize

Conversation

@monkey1sai

@monkey1sai monkey1sai commented Jun 11, 2026 •

Copy link
Copy Markdown
Owner

Why(Fixes #205 的 sanitize 部分)

真實 MinIO intake 帶中文 external_model_version_id(271_pieple_管線)的 job 下載成功但派工 400:coordinator 把外部 id 原樣嵌進 conversion 內部識別欄位,被 conversion authority SAFE_ID_RE = ^[A-Za-z0-9_.-]+$ 擋下。對抗複驗以真 conversion_authority.py 模組實打再挖出兩顆連環雷(model_version_id、worker 派生含冒號的 event_id fallback),並抓到第一版修復自己引入的 correlation 回拋對帳斷裂 — 全部在 merge 前修復並以真模組 ACCEPT/REJECT 鑑別力 probe 驗證。

Spec:docs/superpowers/specs/2026-06-11-conversion-artifact-id-sanitize-design.md(隨 PR 入庫)
Plan:docs/superpowers/plans/2026-06-11-conversion-artifact-id-sanitize.md
OpenSpec change:openspec/changes/conversion-artifact-id-sanitize/

What Changes

  • sanitizeArtifactIdPart 純函式:safe 字元 identity 回傳(純英文/uuid id 輸出零變化,逐欄回歸鎖測試);含非 safe → ${safe}_${sha256[:8]}(確定性+防碰撞);全非 safe → mv_<hash8>。
  • 套用於送往 conversion 的全部 _safe_id 欄位:ifc_artifact.artifact_id、model_version_id、correlation_id、event_id(外部給定與 fallback 兩路)。external_model_version_id 等對外對帳欄位保留原始值。
  • correlationIndex 雙鍵登記(原始+sanitized):conversion result 以 sanitize 值回拋仍命中原 job;callback outbox 對外用原始 correlation_id(端到端對帳測試:worker 冒號 correlation dispatch → sanitized 回拋 → 非 404 正常推進)。
  • 整合測試 stub 逐欄鏡像真 API 驗證面(event_id、idempotency_key fallback 順序、optional 欄位 _safe_optional_id 語意)— 對抗複驗以 counterfactual 突變證明 stub 真會咬 regression。
  • #/conv:dispatch_error 明細可見(>80 字截斷 + title 完整字串;無錯不渲染錯誤節點)。

Frontend Verification Table(product-operability §4)

Item Result
Frontend route /ui/#/conv
Main button(s) Refresh queue(GET /api/external/ifc-ready);表格 conv-dispatch-error-<jobId> 明細
Fixture used E2E intake:中文 external_model_version_id="271_pieple_管線"(POST /api/external/ifc-ready,X-Webhook-Secret header)+ 一筆 forcefail job
Backend API called POST /api/external/ifc-ready(202)→ dispatch → GET /api/external/ifc-ready(list 含 dispatch_error)
Runtime action 中文 id job:ifcready_1781153867837_5f76456c → dispatched(dispatch_error=null);必失敗 job:ifcready_1781153867868_53f20883 → dispatch_failed 且明細可見
Visible success state #/conv 列表:中文 job conversion=queued/dispatched;forcefail job 顯示 400 明細(title 含完整字串)
E2E command npx playwright test e2e/conversion-artifact-id-sanitize.spec.ts(引擎:Playwright;1 passed,自起隔離 coordinator + STUB CONVERSION API)
Screenshot / trace tracked:docs/evidence/conversion-artifact-id-sanitize/{conv-list.png, summary.json};local trace:artifacts/e2e/conversion-artifact-id-sanitize-trace/
Manual test steps 起 coordinator → POST 一筆中文 model_version_id 的 ifc-ready(帶 X-Webhook-Secret)→ #/conv 看 job 進 dispatched;停掉 conversion API 再 POST 一筆 → 看 dispatch_error 明細
Known gaps (1) E2E conversion 端為 STUB(與真 API 同款 SAFE_ID_RE 規則;檔頭與 evidence 已標註)— 真 API 閉合通道 = merge 後部署區以真 conversion authority 重打中文 intake(P7 部署驗證);(2) E2E beforeAll 為 conditional skip(檔頭 12 行明文揭露限制;repo 無 e2e CI job 不會 false-green);(3) dispatch_failed 重派端點不在本輪(#205 留 follow-up)

Deploy Path Verification Table(product-operability §7)

Item Result
Affects runtime / docker / Kit / viewer / ports / env? yes(coordinator dispatch 邏輯 + console dist;不動 ports/Kit/env)
Canonical deploy path updated? not needed(既有 deploy 路徑涵蓋:coordinator web-plane image rebuild 烘入)
New root script added? no
Deploy dry-run command 不適用(repo 規範禁 -DryRun;merge 後走 .\scripts\dev\rebuild-test-deploy.ps1 -Build)
Full deploy tested merge 後執行 rebuild-test-deploy + 部署區真 conversion API 中文 intake 驗證
Verify command coordinator vitest 24 files / 315 tests 全綠;前端 vitest 全綠;Playwright 1 passed
Frontend URL verified E2E 自起隔離 coordinator /ui/#/conv(:8004 待 merge 後重建驗證)
Evidence path docs/evidence/conversion-artifact-id-sanitize/(tracked)+ artifacts/e2e/conversion-artifact-id-sanitize-*(local)

Impact / Review 揭露

  • GitNexus impact:overallRisk=MEDIUM(IfcReadyListItem 6 importers — 僅新增欄位,零修改既有欄位);toInternalIfcReadyEvent 模組內部 mapper(0 callers)。detect_changes 於 linked worktree 走已知 fallback(git diff --cached 自查),task3 與兩輪 fix cycle 標 fallback,其餘 pass。
  • 對抗複驗(兩輪,真模組實證):r1 抓出 event_id 第三顆雷(host py312 直載真 conversion_authority.py REJECT 實證)+ 第一版修復引入的 correlation 回拋 404 regression(critic high);fix 67518f5 後 r2 全 5 項 truly_closed(counterfactual 突變驗證 stub 咬合力、ACCEPT/REJECT 鑑別力 probe、證據 blob hash 三方一致)、critic overall_safe=true。
  • 殘餘已揭露(follow-up 候選):correlationIndex 雙鍵理論撞鍵面(後到 caller 可控 correlation 等於先前 job 的 sanitized alias,意外命中 ~2^-32;建議碰撞防護或 tenant-scoped index);stub 對 tenant/project 空值偏嚴(fail-safe 方向,實際路徑不可達);in-memory store 無過期清理為既有行為。
  • stub vs 真服務:整合層 stub 為真 API 規則的逐欄鏡像(驗證面一一對應已親驗),但仍屬字面複製非呼叫真服務碼 — merge 後部署區真 conversion API 驗證為最終閉合。

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added visible dispatch error reporting in the conversion jobs table with truncated error details and full error message tooltips.
  • Bug Fixes

    • Fixed handling of non-ASCII characters (e.g., Chinese) in external model version IDs—these are now internally sanitized to meet conversion API constraints, preventing dispatch failures.
  • Documentation

    • Added implementation plans, design specifications, and E2E evidence documentation for artifact ID sanitization feature.
  • Tests

    • Added comprehensive test coverage for sanitization behavior, including Chinese ID handling and dispatch error visibility.

monkey1sai and others added 11 commits June 11, 2026 11:37
…error 可見)(#205)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…us + import 清單補 IfcReadyListItem(四軸 review 修正落地)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… artifact_id)

純函式 + 五個回歸鎖測試(vitest),尚未接線 dispatch 路徑(Task 1)。
規則逐字鎖 conversion_authority.py SAFE_ID_RE = ^[A-Za-z0-9_.-]+$。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…rsion_id 不再 400 (#205)

toInternalIfcReadyEvent 內 ifc_artifact.artifact_id 改用 sanitizeArtifactIdPart
組裝,使含中文的 external_model_version_id 通過 conversion 端 SAFE_ID_RE。
補單元回歸鎖(純英文 id 輸出不變)與端到端 dispatch 整合測試(stub conversion
以同款 SAFE_ID_RE 驗收,證明中文 id dispatch 不再被 400 擋下)。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…nator summarize)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
job 有 dispatch_error 時於該列「dispatch」欄附註截斷明細(完整字串走 title),
沿用既有 ec-warn-note 樣式;無錯誤不渲染(顯示 —)。新增 mount 測試覆蓋
有錯/無錯兩種 job 的渲染行為,無 mock 假資料。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
task#2 把 dispatch_error 加成 IfcReadyListItem 必填欄位,但同目錄 sibling
fixture(#182 起既有)未同步補欄位,tsc --noEmit 報 TS2741。vite/vitest
不跑 tsc 故 runtime 全綠,屬 merge 前型別清潔度問題。修法:fixture 補
dispatch_error: null。tsc 該錯已清,IntakeSelectPage 6/6 + console 37/37 綠。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… E2E + evidence (#205)

#/conv → Refresh queue → 真 coordinator GET /api/external/ifc-ready →
真實 ifcready_* ID 的 user-facing vertical slice。

- 中文 external_model_version_id(271_pieple_管線)POST ifc-ready → sanitize 後
  artifact_id 為 safe,conversion 端真 SAFE_ID_RE 不再 400 → dispatched(非 dispatch_failed)。
- 另造必失敗 job(forcefail 哨兵 → stub 回 400)→ dispatch_failed,
  #/conv 顯 conv-dispatch-error-<jobId> 節點,title 含完整錯誤字串(明細可見)。

採 (B) STUB CONVERSION API(誠實鐵律):spec 自起本 branch coordinator(tsx)
+ 同 SAFE_ID_RE 規則的 Node http stub conversion server;真規則已在單元/整合層
(external-ifc-ready.test.ts)鎖死。evidence README 標 STUB CONVERSION API。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ion 不再 400 (#205)

mv1/mv2 對抗複驗根治:conversion_authority.py:261-264 對 model_version_id /
correlation_id / tenant_id / project_id 全跑 _safe_id(SAFE_ID_RE)。原修復只
sanitize ifc_artifact.artifact_id,中文 model_version_id 與 worker 派生含冒號的
correlation_id 仍會被真 API 擋成 400;整合測試 stub 只驗 artifact_id 故假綠。

- toInternalIfcReadyEvent:model_version_id / correlation_id 走 sanitizeArtifactIdPart,
  tenant_id / project_id 走 sanitizeSafeIdField(非字串透傳由 authority 套 server-side 預設);
  external_model_version_id 保留原始供 callback/binding 對帳,外部契約不變。
- 整合 stub 升級為對齊真 API 驗證面:對 artifact_id + 上述四欄全跑 SAFE_ID_RE,任一非 safe → 400。
- 補測試:中文 model_version_id 內部事件兩欄(sanitize 後 / 原始)+ 含冒號 correlation_id sanitize
  單元測試;worker 派生 correlation_id(worker:project::version::task)整合測試走嚴格 stub dispatched。
- 審計結論:tenant_id/project_id 在 coordinator 端無 SAFE_ID_RE 輸入驗證(requiredIdentity 只擋空值),
  中文 project/tenant 命名會踩同一條 400,故一併 sanitize。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
)

- mv2b(critical): toInternalIfcReadyEvent 的 event_id fallback 不再用原始 correlationId,
  改 evt_${sanitizeArtifactIdPart(correlationId)};外部帶 event_id 也過 sanitize。
  conversion_authority.py:238 對 event_id 跑 _safe_id,worker 派生冒號 correlation
  未 sanitize 會先炸 400。加 worker-compat 無 event_id 的整合回歸測試。
- cr1(high regression): correlationIndex 同時登記 sanitize 後鍵(sanitized !== 原始時
  雙鍵指向同一 job),讓 conversion result 以 sanitize 後 correlation_id 回拋仍命中
  原 job(非 404)。加端到端對帳測試(worker 冒號 correlation → result callback 命中)。
- st1(honesty): startSafeIdValidatingStub 補驗 event_id / idempotency_key(缺省 fallback
  到 event_id)/ export_job_id / source_rvt_artifact_id(optional None/空字串放行),
  對齊真 conversion_authority create_conversion_job 全 _safe_id 驗證面。
- f1b(揭露): conversion-artifact-id-sanitize.spec.ts 補 conditional-skip 限制明文
  (skip≠fail、靜默全 skip=假信心、本機/指揮官 gate、無 CI e2e job、升級條件)。
- ch1(措辭): console.test.tsx describe 標題改為如實(欄位形狀對齊真後端 schema,
  渲染層驗證;真後端值由 E2E 驗),不再宣稱「真實後端欄位無 mock 假資料」。
- ce1(evidence): commit run2 最終碼 summary.json + conv-list.png。
- rl1(回歸鎖): streaming-conversion-client.test.ts 補全 safe 輸入逐欄 identity 斷言
  (model_version_id/correlation_id/tenant_id/project_id/event_id === 原始值)。

驗證:bim-review-coordinator npm run build 通過、vitest 全量 314 passed;
web-viewer-sample console.test.tsx 37 passed。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings June 11, 2026 05:33
@coderabbitai

coderabbitai Bot commented Jun 11, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@monkey1sai, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 30 minutes and 30 seconds. Learn how PR review limits work.

Your organization has reached its usage spending cap. Adjust your spending cap in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: b9f1649d-e6b6-4b74-8c4a-b3250db4628a

📥 Commits

Reviewing files that changed from the base of the PR and between 7d9dfcf and 0cf150a.

📒 Files selected for processing (9)
  • bim-review-coordinator/src/app.ts
  • bim-review-coordinator/src/services/externalIfcReadyStore.ts
  • bim-review-coordinator/tests/external-ifc-ready.test.ts
  • bim-review-coordinator/tests/host-native-conversion-ingest.test.ts
  • docs/evidence/conversion-artifact-id-sanitize/README.md
  • docs/superpowers/plans/2026-06-11-conversion-artifact-id-sanitize.md
  • docs/superpowers/specs/2026-06-11-conversion-artifact-id-sanitize-design.md
  • openspec/changes/conversion-artifact-id-sanitize/specs/conversion-artifact-id-sanitize/spec.md
  • web-viewer-sample/e2e/conversion-artifact-id-sanitize.spec.ts
📝 Walkthrough

Walkthrough

This PR fixes issue #205 by sanitizing external model version IDs before conversion dispatch to prevent 400 errors when IDs contain Chinese or special characters. The coordinator converts unsafe characters to deterministic safe form (allowlist + SHA-256 hash), sends sanitized IDs to conversion, stores dispatch errors in job state, displays them in the UI, and validates the end-to-end flow with integration and E2E tests.

Changes

Artifact ID sanitization and dispatch-error handling

Layer / File(s) Summary
ID sanitization helper and unit tests
bim-review-coordinator/src/services/streamingConversionClient.ts, bim-review-coordinator/tests/streaming-conversion-client.test.ts
Introduces sanitizeArtifactIdPart(raw: string) that preserves safe characters and appends SHA-256 suffix for unsafe inputs (with mv_ fallback when no safe chars remain), and sanitizeSafeIdField() for type-safe wrapping. Comprehensive unit tests verify backward compatibility, deterministic hashing, collision safety, and SAFE_ID_RE compliance.
Coordinator payload sanitization and dispatch wiring
bim-review-coordinator/src/services/externalIfcReadyStore.ts, bim-review-coordinator/src/services/streamingConversionClient.ts, bim-review-coordinator/tests/external-ifc-ready.test.ts
Updates toInternalIfcReadyEvent() to sanitize event_id, correlation_id, tenant_id, project_id, and model_version_id before sending to conversion API; ifc_artifact.artifact_id is built from sanitized external model version ID with ifc_ prefix while preserving original external_model_version_id for callback reconciliation. Introduces startSafeIdValidatingStub() test stub that enforces SAFE_ID_RE on required/optional fields; adds worker-compatibility dispatch tests for Chinese external IDs and derived colon-containing correlation IDs.
Correlation index registration and reconciliation
bim-review-coordinator/src/services/externalIfcReadyStore.ts, bim-review-coordinator/tests/host-native-conversion-ingest.test.ts
Updates externalIfcReadyStore to register both original and sanitized correlation IDs in correlation index so getByCorrelation can resolve jobs when callback arrives with either form. Adds integration test verifying conversion result with sanitized correlation_id reconciles correctly to original job.
Frontend types, dispatch-error rendering, and UI tests
web-viewer-sample/src/console/coordinatorClient.ts, web-viewer-sample/src/console/pages.tsx, web-viewer-sample/src/console/console.test.tsx, web-viewer-sample/src/console/IntakeSelectPage.test.tsx
Adds dispatch_error: string | null field to IfcReadyListItem type. Updates ConversionSchedulingPage table to display dispatch, session, and stage columns; renders per-job dispatch error summary (80-char truncated with full error in title tooltip) when non-null, otherwise shows —. Includes vitest mount tests verifying conditional error-node rendering for failed vs. non-failed jobs and test fixture updates.
Playwright E2E test harness and validation
web-viewer-sample/e2e/conversion-artifact-id-sanitize.spec.ts
Complete E2E vertical slice: spawns local coordinator with environment overrides pointing to in-test HTTP stubs for conversion API and IFC source, conditional dist-ui gate, helper functions for job submission/polling/DOM queries. Main test submits a Chinese external_model_version_id job (expected to dispatch successfully with dispatch_error null) and a forced-failure sentinel job (expected dispatch_failed with dispatch_error containing 400). Validates UI renders both outcomes correctly and captures screenshot artifact.
Design specs, plans, proposal, and E2E evidence documentation
docs/superpowers/specs/2026-06-11-conversion-artifact-id-sanitize-design.md, docs/superpowers/plans/2026-06-11-conversion-artifact-id-sanitize.md, openspec/changes/conversion-artifact-id-sanitize/proposal.md, openspec/changes/conversion-artifact-id-sanitize/tasks.md, docs/evidence/conversion-artifact-id-sanitize/README.md, docs/evidence/conversion-artifact-id-sanitize/conversion-artifact-id-sanitize-summary.json
Comprehensive documentation: design spec defines sanitization rules and UI behavior, implementation plan covers Tasks 0–4 with detailed checkpoint descriptions, openspec proposal outlines scope and non-goals, task checklist tracks completion with commit references. E2E evidence README documents stub setup, execution mode, observed state transitions, and artifact paths; JSON summary records Playwright metadata, job IDs, state expectations, assertions, and evidence artifact locations.

🎯 4 (Complex) | ⏱️ ~60 minutes

🐰 A coordinator with a keen eye,
Takes Chinese IDs and lets them fly,
With SHA-hash magic, safe and sound,
No more dispatch fails—progress found!
The UI now whispers errors clear,
When conversion stumbles, we see what's near. 🌟

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 64.29% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title directly addresses the root cause and solution from issue #205: fixing Chinese external_model_version_id causing conversion dispatch 400 through comprehensive field sanitization, correlation matching, and UI visibility.
Linked Issues check ✅ Passed All objectives from issue #205 are implemented: sanitizeArtifactIdPart function created [#206], applied to all conversion-bound _safe_id fields, correlationIndex dual-key registration for idempotency, stub conversion API validation upgraded, dispatch_error surfaced in UI with truncation/tooltip, unit/integration/E2E tests covering Chinese IDs and dispatch failures, and documentation with risk assessment provided.
Out of Scope Changes check ✅ Passed All changes directly support the stated objectives: ID sanitization logic, dispatch-error UI display, test fixtures, documentation, and E2E evidence are all in scope; no unrelated refactoring, dependency upgrades, or feature creep detected.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/conversion-artifact-id-sanitize

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes issue #205 where Chinese characters in external_model_version_id (e.g., 271_pieple_管線) caused the coordinator's conversion dispatch to fail with a 400 error because the conversion authority's SAFE_ID_RE regex rejects non-ASCII characters. The fix introduces a deterministic sanitize function on the coordinator side, applies it to all fields validated by the conversion API's _safe_id checks, fixes correlation index reconciliation for sanitized keys, and makes dispatch_error details visible in the #/conv UI.

Changes:

  • Added sanitizeArtifactIdPart pure function that preserves safe-character-only IDs unchanged (backward compatible) and appends a SHA256 hash suffix for IDs containing non-safe characters; applied to all fields sent through _safe_id validation (artifact_id, model_version_id, correlation_id, event_id, tenant_id, project_id).
  • Added dual-key registration in ExternalIfcReadyStore.correlationIndex (original + sanitized key) so conversion results can reconcile back to the originating job even when the correlation ID was sanitized.
  • Frontend #/conv page now shows dispatch_error details in a new "dispatch" column with truncation at 80 characters and full text in the title tooltip.

Reviewed changes

Copilot reviewed 16 out of 17 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
bim-review-coordinator/src/services/streamingConversionClient.ts Core sanitize function + apply to all _safe_id fields in toInternalIfcReadyEvent
bim-review-coordinator/src/services/externalIfcReadyStore.ts Dual-key correlation index registration for sanitized correlation IDs
bim-review-coordinator/tests/streaming-conversion-client.test.ts Unit tests for sanitizeArtifactIdPart + toInternalIfcReadyEvent field-level assertions
bim-review-coordinator/tests/external-ifc-ready.test.ts Integration tests with SAFE_ID_RE-validating stub for CJK and worker-colon scenarios
bim-review-coordinator/tests/host-native-conversion-ingest.test.ts Correlation reconciliation test (sanitized correlation roundtrip)
web-viewer-sample/src/console/coordinatorClient.ts dispatch_error field added to IfcReadyListItem type
web-viewer-sample/src/console/pages.tsx #/conv table renders dispatch_error column with truncation + title tooltip
web-viewer-sample/src/console/console.test.tsx Vitest for dispatch_error rendering (present + absent cases)
web-viewer-sample/src/console/IntakeSelectPage.test.tsx Test fixture updated with dispatch_error: null
web-viewer-sample/e2e/conversion-artifact-id-sanitize.spec.ts Playwright E2E: self-contained coordinator + stub conversion, CJK dispatch success + forcefail error visibility
openspec/changes/conversion-artifact-id-sanitize/* OpenSpec change artifacts (proposal, tasks)
docs/superpowers/specs/2026-06-11-conversion-artifact-id-sanitize-design.md Spec design document
docs/superpowers/plans/2026-06-11-conversion-artifact-id-sanitize.md Implementation plan
docs/evidence/conversion-artifact-id-sanitize/* Evidence README, summary JSON, screenshot

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

res.end(JSON.stringify({ detail: `Invalid ifc_artifact_id: ${artifactId}` }));
return;
}
res.writeHead(200, { "Content-Type": "application/json" });
// STUB CONVERSION API:與 conversion 端 SAFE_ID_RE 同款規則驗 ifc_artifact.artifact_id。
// 哨兵 forcefail → 400(製造 dispatch_failed);非 safe → 400(防回歸:sanitize 失效時會紅);
// 其餘 → 200 queued。
async function startConversionStub(): Promise<void> {
return;
}
}
res.writeHead(200, { "Content-Type": "application/json" });
Comment thread docs/superpowers/specs/2026-06-11-conversion-artifact-id-sanitize-design.md Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 7d9dfcf55c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

this.correlationIndex.set(correlationId, jobId);
const sanitized = sanitizeArtifactIdPart(correlationId);
if (sanitized !== correlationId) {
this.correlationIndex.set(sanitized, jobId);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep sanitized correlation keys out of replay lookup

When a new job has an unsafe correlation ID, this writes the sanitized value into the same correlationIndex used by findExisting() for incoming intake deduplication. If another request uses that sanitized string as its real X-Correlation-Id with a different idempotency key, it will be treated as a replay of the first job; in the reverse order, this assignment overwrites the existing safe job's key so later conversion-result ingestion for that safe correlation updates the wrong job. The sanitized lookup should be isolated to conversion result reconciliation, or at least collision-checked, rather than sharing the raw correlation index used for intake identity.

Useful? React with 👍 / 👎.


### New Capabilities

- `conversion-artifact-id-sanitize`: 外部(含中文/特殊字元)model_version_id 經確定性 sanitize 後可通過 conversion authority 驗證完成派工;派工失敗原因於 `#/conv` 可見。

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Add the OpenSpec spec delta for this capability

This declares a new OpenSpec capability, but the change directory only adds proposal.md and tasks.md; there is no openspec/changes/conversion-artifact-id-sanitize/specs/conversion-artifact-id-sanitize/spec.md requirement/scenario delta. In this state the completed change cannot be archived into openspec/specs/, so the externally observable behavior and acceptance criteria for the sanitize contract will be lost after the PR even though the proposal says this is a new capability.

Useful? React with 👍 / 👎.

@github-actions

Copy link
Copy Markdown
Contributor

PR Review Agent Summary

Field Value
Status failed
Risk high
PR 206
Head fix/conversion-artifact-id-sanitize / 7d9dfcf55c25cf79f6ba102d711aabec144adeda
Base main / 9cc2d6670f12eef4c78632e28e60b589a3484d3a

Blockers

  • [high] D:\a\AI-BIM-governance\AI-BIM-governance Required validation failed: openspec validate conversion-artifact-id-sanitize.

Warnings

  • [medium] GitNexus detect changes did not pass: warning.

Validation Commands

  • openspec validate conversion-artifact-id-sanitize
  • npm run verify
  • npm run verify

Checks

  • failed openspec validate conversion-artifact-id-sanitize (openspec)
  • passed bim-review-coordinator verify (bim-review-coordinator)
  • passed web-viewer-sample verify (web-viewer-sample)

Human Review Notes

  • OpenSpec changes detected: conversion-artifact-id-sanitize
  • Optional AI adapter is not required by policy and was skipped.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (1)
web-viewer-sample/e2e/conversion-artifact-id-sanitize.spec.ts (1)

79-111: 💤 Low value

Consider validating additional _safe_id fields in the conversion stub to mirror real API behavior.

The stub currently validates only ifc_artifact.artifact_id, but per context snippet 1 (streamingConversionClient.ts:115-138), the real conversion authority also validates event_id, correlation_id, tenant_id, project_id, and model_version_id with _safe_id. While the current stub suffices for the primary test goal (Chinese external_model_version_id → sanitized artifact_id), validating additional fields would:

  • Catch regressions if coordinator fails to sanitize other fields
  • Better mirror the E2E's stated goal ("stub 與 conversion 端 SAFE_ID_RE 同規則")
Proposed extension for multi-field validation
       let artifactId = "";
+      let eventId = "";
+      let correlationId = "";
       try {
-        artifactId = (JSON.parse(body || "{}")?.ifc_artifact?.artifact_id as string) ?? "";
+        const parsed = JSON.parse(body || "{}");
+        artifactId = (parsed?.ifc_artifact?.artifact_id as string) ?? "";
+        eventId = (parsed?.event_id as string) ?? "";
+        correlationId = (parsed?.correlation_id as string) ?? "";
       } catch {
         artifactId = "";
       }
       if (artifactId.includes(FORCE_FAIL_MARKER)) {
         res.writeHead(400, { "Content-Type": "application/json" });
         res.end(JSON.stringify({ detail: `STUB forced failure for artifact_id: ${artifactId}` }));
         return;
       }
-      if (!SAFE_ID_RE.test(artifactId)) {
+      const invalidFields = [];
+      if (!SAFE_ID_RE.test(artifactId)) invalidFields.push("artifact_id");
+      if (eventId && !SAFE_ID_RE.test(eventId)) invalidFields.push("event_id");
+      if (correlationId && !SAFE_ID_RE.test(correlationId)) invalidFields.push("correlation_id");
+      if (invalidFields.length > 0) {
         res.writeHead(400, { "Content-Type": "application/json" });
-        res.end(JSON.stringify({ detail: `Invalid ifc_artifact_id: ${artifactId}` }));
+        res.end(JSON.stringify({ detail: `Invalid fields: ${invalidFields.join(", ")}` }));
         return;
       }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web-viewer-sample/e2e/conversion-artifact-id-sanitize.spec.ts` around lines
79 - 111, The conversion stub in startConversionStub currently only applies
SAFE_ID_RE to ifc_artifact.artifact_id; extend its validation to also parse and
validate event_id, correlation_id, tenant_id, project_id, and model_version_id
from the incoming JSON using the same SAFE_ID_RE and existing FORCE_FAIL_MARKER
check (mirror the real behavior in streamingConversionClient), returning the
same 400 error messages when any of these fields are invalid or contain the
FORCE_FAIL_MARKER; keep the existing response flow and error formats, reusing
the artifact_id error wording pattern for each failing field so tests detect
sanitization regressions across all fields.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@bim-review-coordinator/src/services/externalIfcReadyStore.ts`:
- Around line 74-75: The correlationIndex currently collapses raw and sanitized
correlation keys causing aliasing; split into two maps (e.g.,
rawCorrelationIndex and sanitizedCorrelationIndex) and update
registerCorrelationKeys to register the original raw key in rawCorrelationIndex
and the sanitized key in sanitizedCorrelationIndex, then modify findExisting()
and getByCorrelation() to consult the appropriate index depending on whether
they expect raw vs sanitized keys (and update any callback reconciliation code
to use sanitizedCorrelationIndex). Ensure all call sites that read/write
correlationIndex (including registerCorrelationKeys, findExisting,
getByCorrelation, and callback reconciliation paths) are updated to the new
indices, add tests covering both raw->sanitized collisions, and note blast
radius: HIGH.

In `@docs/evidence/conversion-artifact-id-sanitize/README.md`:
- Around line 1-2: The README "Evidence — conversion-artifact-id-sanitize(中文
model_version_id 派工修復 + dispatch_error 可見)" is missing a document nature marker;
update the top of that README to include a single-line nature tag (e.g.,
"Document nature: evidence / runbook") so it conforms to docs/**/*.md
guidelines; ensure the marker appears near the main heading and uses the phrase
"Document nature:" followed by the chosen category (evidence or runbook) to make
intent explicit.

In `@docs/superpowers/plans/2026-06-11-conversion-artifact-id-sanitize.md`:
- Around line 1-8: Add the required document nature marker to this markdown (the
"Conversion artifact_id sanitize(中文 model_version_id 派工修復)Implementation Plan"
doc) by inserting a single-line nature tag immediately under the title (e.g.,
"Document nature: spec design" or the appropriate category from the docs
guidelines) so the file explicitly declares its nature per docs/**/*.md rules;
ensure the marker is plain text on its own line and not wrapped in code blocks
or HTML so automated checks will detect it.

---

Nitpick comments:
In `@web-viewer-sample/e2e/conversion-artifact-id-sanitize.spec.ts`:
- Around line 79-111: The conversion stub in startConversionStub currently only
applies SAFE_ID_RE to ifc_artifact.artifact_id; extend its validation to also
parse and validate event_id, correlation_id, tenant_id, project_id, and
model_version_id from the incoming JSON using the same SAFE_ID_RE and existing
FORCE_FAIL_MARKER check (mirror the real behavior in streamingConversionClient),
returning the same 400 error messages when any of these fields are invalid or
contain the FORCE_FAIL_MARKER; keep the existing response flow and error
formats, reusing the artifact_id error wording pattern for each failing field so
tests detect sanitization regressions across all fields.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: c8ab58ab-e9ae-40e7-b280-6eddc0f053b6

📥 Commits

Reviewing files that changed from the base of the PR and between 9cc2d66 and 7d9dfcf.

⛔ Files ignored due to path filters (1)
  • docs/evidence/conversion-artifact-id-sanitize/conv-list.png is excluded by !**/*.png
📒 Files selected for processing (16)
  • bim-review-coordinator/src/services/externalIfcReadyStore.ts
  • bim-review-coordinator/src/services/streamingConversionClient.ts
  • bim-review-coordinator/tests/external-ifc-ready.test.ts
  • bim-review-coordinator/tests/host-native-conversion-ingest.test.ts
  • bim-review-coordinator/tests/streaming-conversion-client.test.ts
  • docs/evidence/conversion-artifact-id-sanitize/README.md
  • docs/evidence/conversion-artifact-id-sanitize/conversion-artifact-id-sanitize-summary.json
  • docs/superpowers/plans/2026-06-11-conversion-artifact-id-sanitize.md
  • docs/superpowers/specs/2026-06-11-conversion-artifact-id-sanitize-design.md
  • openspec/changes/conversion-artifact-id-sanitize/proposal.md
  • openspec/changes/conversion-artifact-id-sanitize/tasks.md
  • web-viewer-sample/e2e/conversion-artifact-id-sanitize.spec.ts
  • web-viewer-sample/src/console/IntakeSelectPage.test.tsx
  • web-viewer-sample/src/console/console.test.tsx
  • web-viewer-sample/src/console/coordinatorClient.ts
  • web-viewer-sample/src/console/pages.tsx

Comment thread bim-review-coordinator/src/services/externalIfcReadyStore.ts
Comment thread docs/evidence/conversion-artifact-id-sanitize/README.md
…ta + 文件標記/stub 202)

- [P2/Major] externalIfcReadyStore:sanitize 後 correlation 鍵移入獨立
  sanitizedCorrelationIndex,只供 conversion 結果回拋(getByCorrelation fallback)
  查詢;intake 去重(findExisting)只看原始鍵,杜絕「真實 correlation 恰等於
  他 job sanitize 值」被誤判 idempotent replay 的 aliasing;新增 intake aliasing
  回歸測試(host-native-conversion-ingest.test.ts)
- [Blocker] 補 openspec/changes/conversion-artifact-id-sanitize/specs/.../spec.md
  ADDED requirements/scenarios(sanitize 規則、回拋對帳 + aliasing 防護、
  #/conv dispatch_error 可見),openspec validate 轉綠
- [Major] evidence README 與 implementation plan 補「文件性質」標記
- [nit] conversion dispatch stub 受理回應 200 → 202(對齊真 API);設計文件
  操作者一詞混入西里爾字母修正為 operator

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

PR Review Agent Summary

Field Value
Status warning
Risk medium
PR 206
Head fix/conversion-artifact-id-sanitize / 0fbd5314998241a836b13426ce721dbd36be5f85
Base main / 9cc2d6670f12eef4c78632e28e60b589a3484d3a

Blockers

  • None

Warnings

  • [medium] GitNexus detect changes did not pass: warning.

Validation Commands

  • openspec validate conversion-artifact-id-sanitize
  • npm run verify
  • npm run verify

Checks

  • passed openspec validate conversion-artifact-id-sanitize (openspec)
  • passed bim-review-coordinator verify (bim-review-coordinator)
  • passed web-viewer-sample verify (web-viewer-sample)

Human Review Notes

  • OpenSpec changes detected: conversion-artifact-id-sanitize
  • Optional AI adapter is not required by policy and was skipped.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 0fbd531499

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

// 先查原始鍵;查不到再查 sanitize 鍵(conversion 回拋帶的是 sanitize 後值)。
// 原始鍵優先:若另一 job 的真實 correlation 恰等於某 sanitize 值,回拋對帳
// 仍以「持有該原始鍵」的 job 為準,不被 sanitize 鍵搶走。
const id = this.correlationIndex.get(correlationId) ?? this.sanitizedCorrelationIndex.get(correlationId);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Disambiguate sanitized correlation collisions before ingest

When an unsafe correlation ID sanitizes to a string that another job already uses as its real X-Correlation-Id, conversion results for the unsafe job arrive with that sanitized value but this lookup now prefers correlationIndex first, so the result is applied to the safe job instead. The fresh evidence is this new raw-first fallback: it avoids intake replay pollution, but still leaves result reconciliation ambiguous for the exact aliasing case; use conversion_job_id or collision rejection rather than resolving solely by the sanitized correlation string.

Useful? React with 👍 / 👎.

… r2)

job A(unsafe correlation,sanitize 後為 S)與 job B(真實 correlation 恰為 S)
並存時,A 的 conversion 結果以 S 回拋,單靠 correlation 字串無法裁決。
getByCorrelation 改收 optional conversionJobId:原始鍵與 sanitize 鍵的候選
job 中優先回傳 conversion_job_id 吻合者,無法消歧時維持原始鍵優先(向後
相容);ingestConversionReport 帶入 report.conversion_job_id。新增 store
層 collision 消歧回歸測試。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

PR Review Agent Summary

Field Value
Status warning
Risk medium
PR 206
Head fix/conversion-artifact-id-sanitize / 0cf150adb6991bb198dbefc01474904298c1153b
Base main / 9cc2d6670f12eef4c78632e28e60b589a3484d3a

Blockers

  • None

Warnings

  • [medium] GitNexus detect changes did not pass: warning.

Validation Commands

  • openspec validate conversion-artifact-id-sanitize
  • npm run verify
  • npm run verify

Checks

  • passed openspec validate conversion-artifact-id-sanitize (openspec)
  • passed bim-review-coordinator verify (bim-review-coordinator)
  • passed web-viewer-sample verify (web-viewer-sample)

Human Review Notes

  • OpenSpec changes detected: conversion-artifact-id-sanitize
  • Optional AI adapter is not required by policy and was skipped.

@monkey1sai
monkey1sai merged commit 6b50c12 into main Jun 11, 2026
2 checks passed
@monkey1sai
monkey1sai deleted the fix/conversion-artifact-id-sanitize branch June 11, 2026 06:14
monkey1sai added a commit that referenced this pull request Jun 18, 2026
#229)

把 13 個產品碼已 merge 進 main 的 active change 歸檔為不可變快照,
並將其 spec delta 併入 canonical specs。對齊 #197 收斂規約。

歸檔(archive/<merge-date>-<id>,git 偵測為 R100 純改名、零內容漂移):
  a1-m1-closeout(#213) a2-version-diff-selector(#207) conv-coverage-report(#218/#220)
  conv-prioritize-retry(#221) conv-watch-toggle(#225) conversion-artifact-id-sanitize(#206)
  governance-service-deploy(#215) minio-fileserver-source(#204) minio-watch-auto-intake(#210)
  raise-claude-md-line-budget(#199) sessions-terminate(#226) stop-all-single-pid-cleanup(#217)
  test-deploy-rebuild-workflow(#198)

canonical 併入:
- 9 個新 capability(純 ADDED → 新建 spec):a1-m1-closeout、a2-version-diff-selector、
  conv-coverage-report、conv-prioritize-retry、conversion-control、conversion-artifact-id-sanitize、
  minio-fileserver-source、minio-watch-auto-intake、test-deploy-rebuild-workflow。
- review-session-request-lifecycle:append sessions-terminate 的 ADDED requirement
  「Operator 結束 session controlled action」(5 scenario),既有 7 requirement 不動。
- one-click-deploy-hybrid:併入 governance-service-deploy 與 stop-all-single-pid-cleanup
  兩 delta,採「合併不取代」保全既有更豐富內容。依 deploy.ps1 現況權威
  (4a=governance/4b=conversion/4c=Kit/4d=docker)調和 Phase 4 編號,並修正
  canonical 其他兩處陳舊的舊 3 段編號(Mode C 入口 scenario、退出碼 stage 清單補 4d)。
- agent-doc-context-budget:raise-claude-md-line-budget 的 130 行預算已於 #199 併入,
  本次為 archive-only。

驗證:
- 結構檢查無殘留 ## ADDED/MODIFIED header、每 requirement 皆有 scenario、
  43 archive 檔全 R100、git diff --cached --check 無 whitespace。
- 雙 agent 對抗驗證:完整性 PASS(無規範遺失);一致性初判 FAIL 抓到 2 處
  Phase 4 編號矛盾,已修正後複驗。
- 本機 openspec CLI 不可用(結構驗證代替);openspec validate --strict 由 CI pr-review-agent 執行。


Claude-Session: https://claude.ai/code/session_01JEyNWhEmb3x8oinY3B2v9V

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bug: 中文 external_model_version_id 使 conversion 派工 400(ifc_artifact_id 驗證),job 卡 dispatch_failed

2 participants