docs(openspec): archive coordinator-auto-poll-streaming-conversion 並同步 specs + roadmap - #99
Conversation
…步 specs + roadmap PR #98(implementation,squash `2103bf7`)merged 後的 OpenSpec sync/archive cleanup。 - `git mv openspec/changes/coordinator-auto-poll-streaming-conversion/` → `openspec/changes/archive/2026-05-22-coordinator-auto-poll-streaming-conversion/` - `openspec/specs/conversion-webhook-lifecycle/spec.md` MODIFIED `Coordinator ingests host-native conversion result into callback outbox` requirement:加 SHALL auto-poll 子條款(env config 三項可調)+ 3 新 Scenario(dispatch auto-schedules / manual de-dup / poll timeout failed-equivalent)+ implementation status note - `docs/plans/AI-BIM-governance-saas-roadmap-2026-05.md` 加 archive 摘要(2026-05-22 fast-mvp loop zero-touch automation 完成) `openspec validate --specs --strict` = **26 passed / 0 failed** ## fast-mvp loop zero-touch 收尾完成 4 段 OpenSpec change archive(`remove-conflict-review-from-fast-mvp` + `fast-ifc-link-demo-loop` + `streaming-server-prefer-local-ifc-path` + `coordinator-auto-poll-streaming-conversion`)+ 4 段 hotfix bundle(PR #94 / #95 / #96 / #98)整體把外部 IFC Worker → coordinator → streaming-server → viewer 連結這條 happy path 從 spec 到 code、從 manual 到 zero-touch 完整對齊並驗證。 L4 真實 runtime 第一次 zero-touch end-to-end 跑通(2026-05-22):dispatch → 40 秒內 viewer_url 自動出現,無 manual ingest trigger。 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
Warning Rate limit exceeded
You’ve run out of usage credits. Purchase more in the billing tab. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the 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 have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (7)
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: d33345d7e9
ℹ️ 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".
| > | ||
| > **2026-05-19 更新(B 方案 intake smoke 現行證據)**:`scripts/smoke-bscheme-intake.ps1` 已從 contract-stub API-only smoke 擴成「contract checks + optional real `storage/*.ifc` → coordinator intake → streaming conversion polling → coordinator callback outbox」的最小整合驗證入口。本 8caa worktree 的 `storage/*.ifc` 目前為 **0 檔**,因此 `real_ifc_fixture=blocked`、`real_ifc_intake_conversion=blocked`;contract/API 層仍通過(repo-root pytest 7、coordinator verify 140、streaming conversion pytest 10、callback outbox passed)。GPU/Kit 層本次 `runtime_image_kit_launcher=deferred`,實際 blocker 為 **Docker engine not available**;single Kit render 為 `deferred`、multi-viewer 與 USD stage composition 為 `not_observed`。證據:`docs/verification/evidence/2026-05-18-bscheme-intake-smoke/bscheme-readiness.json` 與 `docs/verification/evidence/2026-05-18-t0-kit-launcher/kit-launcher-readiness.json`。 | ||
| > | ||
| > **2026-05-22 更新(`coordinator-auto-poll-streaming-conversion` archive 對齊;fast-mvp loop zero-touch automation 完成)**:本 change(implementation PR #98,2026-05-22 merged,squash `2103bf7`)已完成 **OpenSpec sync/archive**(archive folder `openspec/changes/archive/2026-05-22-coordinator-auto-poll-streaming-conversion/`;`openspec/specs/` 26 保持 **26**,本次為 `conversion-webhook-lifecycle` capability **MODIFIED** 1 個 requirement(`Coordinator ingests host-native conversion result into callback outbox` 加 SHALL auto-poll 子條款 + 3 新 Scenario + implementation status note);totals + 0 added / ~ 1 modified / - 0 removed)。`openspec validate --specs --strict` = **26 passed / 0 failed**。本 change 補實該 requirement 早已寫的「ingest through polling, an internal result loop, or an equivalent internal callback」但 polling / internal result loop **皆未實作**的 gap:`bim-review-coordinator/src/services/streamingConversionClient.ts` 加 module-level `isTerminalConversionResult` helper + class method `pollConversionResult(jobId, options): PollerHandle`(setTimeout chain 避免 setInterval overlap、cancel()、max attempts → poll_timeout fake failed result);`bim-review-coordinator/src/config.ts` 加 `conversionPollEnabled`(default true)/ `conversionPollIntervalSeconds`(default 5)/ `conversionPollMaxAttempts`(default 60 = 5 分鐘 ceiling)+ `parseBooleanEnv`;`bim-review-coordinator/src/app.ts` 加 `pollerRegistry: Map<conversion_job_id, PollerHandle>`、refactor 既有 manual ingest 邏輯抽成共用 helper `ingestStreamingConversionResult`、dispatch 成功後 `schedulePollerForConversion`、manual endpoint 開頭 cancel + delete registry(去重)、`CoordinatorApp` 加 `dispose()` shutdown hook。**Verification(本 change L1-L4)**:coordinator `npm run verify` = 12 files / **173 tests passed**(168 既有 + 5 新 `auto-poll-conversion.test.ts` cover happy path / failed / duplicate dispatch budget / manual cancel auto / disabled config);streaming-server `pytest tests -q` = **31 passed**(不動,regression OK);root pytest 9 passed;GitNexus pre-impact LOW for `fetchConversionResult` / `createCoordinatorApp`;**L4 真實 runtime end-to-end 第一次 zero-touch 跑通**:docker compose recreate coordinator(讀新 code,帶 absolute `RUNTIME_STORAGE_ROOT`)→ `POST /api/external/ifc-ready` → fixture trick 確保 IFC 落地 → **無任何 manual POST ingest** → 40 秒內 coordinator `GET /api/external/ifc-ready/<job>` 返回 `conversion_status: ready` + `viewer_url: http://127.0.0.1:8004/ui/open?session=review_session_<id>`(2026-05-22 timestamp)。**注意**:本 archive 不持久化 poller state(coordinator restart 後 in-memory timers lost,手動 endpoint 仍可救);不解雲端 callback outbox retry / dead-letter(另一條既有 requirement 不動);不引入 streaming-server push callback(本 change 走 coordinator pull)。**fast-mvp loop 從 spec 到 code 從 zero-touch 自動化的角度完整收尾(架構完整對齊 + 自動跑通)**:OpenSpec changes archived = `remove-conflict-review-from-fast-mvp` + `fast-ifc-link-demo-loop` + `streaming-server-prefer-local-ifc-path` + `coordinator-auto-poll-streaming-conversion`(4 段);hotfix bundle = PR #94 / #95 / #96 / #98。 |
There was a problem hiding this comment.
Update roadmap HTML after syncing roadmap markdown
This archive-sync entry updates the roadmap markdown, but the paired HTML view was not regenerated in the same commit. Per AGENTS.md workflow rules, roadmap sync must also refresh docs/plans/AI-BIM-governance-saas-roadmap-2026-05.html; otherwise the human-facing HTML drifts from the markdown source of truth and can hide the new archive/evidence status from reviewers who read the HTML view.
Useful? React with 👍 / 👎.
| ### Requirement: Coordinator ingests host-native conversion result into callback outbox | ||
|
|
||
| Coordinator SHALL ingest the host-native conversion result through polling, an internal result loop, or an equivalent internal callback. A ready conversion result SHALL be transformed into the existing metadata-only `conversion_result_ready` callback outbox entry; a failed result SHALL become `conversion_failed`. Callback delivery state SHALL remain separate from conversion success. | ||
| Coordinator SHALL ingest the host-native conversion result through polling, an internal result loop, or an equivalent internal callback. **A successful dispatch to `bim-streaming-server` SHALL automatically schedule an in-process polling task that periodically fetches `GET /api/conversions/<conversion_job_id>/result` until the result reaches a terminal state(`status` ∈ {`succeeded`, `succeeded_with_warnings`, `failed`, `cancelled`} or `model_status` ∈ {`ready`, `failed`}),then runs the same ingestion path as the manual `POST /api/internal/conversions/<id>/ingest` endpoint(callback outbox enqueue + local review session handoff per existing requirements).** Polling cadence and max attempts SHALL be configurable via env(`CONVERSION_POLL_INTERVAL_SECONDS` default `5`,`CONVERSION_POLL_MAX_ATTEMPTS` default `60`= 5 分鐘 ceiling);env `CONVERSION_POLL_ENABLED=false` MAY disable auto-poll for test fixtures while keeping the manual endpoint functional. A ready conversion result SHALL be transformed into the existing metadata-only `conversion_result_ready` callback outbox entry; a failed result SHALL become `conversion_failed`. Callback delivery state SHALL remain separate from conversion success. The same conversion_job_id MUST NOT spawn duplicate concurrent pollers; the manual ingest endpoint MUST cancel any active poller for that conversion_job_id before running ingestion to prevent double-delivery. |
There was a problem hiding this comment.
Define failed callback mapping for cancelled terminal status
The updated requirement makes status="cancelled" a terminal poll outcome, but the same paragraph only specifies callback mapping for ready and failed results, leaving cancelled behavior implicit. This ambiguity can lead an implementation to stop polling on cancelled without enqueuing conversion_failed, causing inconsistent outbox behavior across implementations that follow this spec literally.
Useful? React with 👍 / 👎.
| ### Requirement: Coordinator ingests host-native conversion result into callback outbox | ||
|
|
||
| Coordinator SHALL ingest the host-native conversion result through polling, an internal result loop, or an equivalent internal callback. A ready conversion result SHALL be transformed into the existing metadata-only `conversion_result_ready` callback outbox entry; a failed result SHALL become `conversion_failed`. Callback delivery state SHALL remain separate from conversion success. | ||
| Coordinator SHALL ingest the host-native conversion result through polling, an internal result loop, or an equivalent internal callback. **A successful dispatch to `bim-streaming-server` SHALL automatically schedule an in-process polling task that periodically fetches `GET /api/conversions/<conversion_job_id>/result` until the result reaches a terminal state(`status` ∈ {`succeeded`, `succeeded_with_warnings`, `failed`, `cancelled`} or `model_status` ∈ {`ready`, `failed`}),then runs the same ingestion path as the manual `POST /api/internal/conversions/<id>/ingest` endpoint(callback outbox enqueue + local review session handoff per existing requirements).** Polling cadence and max attempts SHALL be configurable via env(`CONVERSION_POLL_INTERVAL_SECONDS` default `5`,`CONVERSION_POLL_MAX_ATTEMPTS` default `60`= 5 分鐘 ceiling);env `CONVERSION_POLL_ENABLED=false` MAY disable auto-poll for test fixtures while keeping the manual endpoint functional. A ready conversion result SHALL be transformed into the existing metadata-only `conversion_result_ready` callback outbox entry; a failed result SHALL become `conversion_failed`. Callback delivery state SHALL remain separate from conversion success. The same conversion_job_id MUST NOT spawn duplicate concurrent pollers; the manual ingest endpoint MUST cancel any active poller for that conversion_job_id before running ingestion to prevent double-delivery. |
There was a problem hiding this comment.
Use API terminal field name consistently in polling rule
The new terminal-state clause says polling should stop when model_status is terminal, but this same spec defines the upstream result payload using model.status (see the ready scenario). Because this sentence governs how callers interpret GET /api/conversions/<id>/result, using the internal flattened name here can cause implementers to watch a non-existent API field and poll until timeout even after upstream has reached terminal.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Pull request overview
Archives the OpenSpec change for coordinator auto-poll streaming conversion ingestion and syncs the resulting requirement/scenario updates into the main conversion-webhook-lifecycle spec, plus a roadmap entry documenting the archive/verification.
Changes:
- Synced
conversion-webhook-lifecyclewith an expanded “Coordinator ingests …” requirement, implementation-status note, and 3 new scenarios for auto-poll behavior. - Added the archived OpenSpec change bundle under
openspec/changes/archive/2026-05-22-coordinator-auto-poll-streaming-conversion/(proposal/design/tasks/acceptance + spec delta). - Updated the May 2026 SaaS roadmap with an archive summary and verification notes.
Reviewed changes
Copilot reviewed 2 out of 7 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| openspec/specs/conversion-webhook-lifecycle/spec.md | Syncs the main spec requirement + scenarios + implementation status note for coordinator auto-poll ingestion. |
| openspec/changes/archive/2026-05-22-coordinator-auto-poll-streaming-conversion/tasks.md | Archived tasks checklist/reference path for the change. |
| openspec/changes/archive/2026-05-22-coordinator-auto-poll-streaming-conversion/specs/conversion-webhook-lifecycle/spec.md | Archived spec delta capturing the requirement/scenario modifications. |
| openspec/changes/archive/2026-05-22-coordinator-auto-poll-streaming-conversion/proposal.md | Archived rationale and intended implementation outline for auto-poll. |
| openspec/changes/archive/2026-05-22-coordinator-auto-poll-streaming-conversion/design.md | Archived design notes describing poller behavior, idempotency, and failure modes. |
| openspec/changes/archive/2026-05-22-coordinator-auto-poll-streaming-conversion/acceptance.md | Archived acceptance criteria and verification checklist. |
| docs/plans/AI-BIM-governance-saas-roadmap-2026-05.md | Adds a roadmap archive summary entry with verification notes and references. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| ### Requirement: Coordinator ingests host-native conversion result into callback outbox | ||
|
|
||
| Coordinator SHALL ingest the host-native conversion result through polling, an internal result loop, or an equivalent internal callback. A ready conversion result SHALL be transformed into the existing metadata-only `conversion_result_ready` callback outbox entry; a failed result SHALL become `conversion_failed`. Callback delivery state SHALL remain separate from conversion success. | ||
| Coordinator SHALL ingest the host-native conversion result through polling, an internal result loop, or an equivalent internal callback. **A successful dispatch to `bim-streaming-server` SHALL automatically schedule an in-process polling task that periodically fetches `GET /api/conversions/<conversion_job_id>/result` until the result reaches a terminal state(`status` ∈ {`succeeded`, `succeeded_with_warnings`, `failed`, `cancelled`} or `model_status` ∈ {`ready`, `failed`}),then runs the same ingestion path as the manual `POST /api/internal/conversions/<id>/ingest` endpoint(callback outbox enqueue + local review session handoff per existing requirements).** Polling cadence and max attempts SHALL be configurable via env(`CONVERSION_POLL_INTERVAL_SECONDS` default `5`,`CONVERSION_POLL_MAX_ATTEMPTS` default `60`= 5 分鐘 ceiling);env `CONVERSION_POLL_ENABLED=false` MAY disable auto-poll for test fixtures while keeping the manual endpoint functional. A ready conversion result SHALL be transformed into the existing metadata-only `conversion_result_ready` callback outbox entry; a failed result SHALL become `conversion_failed`. Callback delivery state SHALL remain separate from conversion success. The same conversion_job_id MUST NOT spawn duplicate concurrent pollers; the manual ingest endpoint MUST cancel any active poller for that conversion_job_id before running ingestion to prevent double-delivery. |
|
|
||
| #### Scenario: Auto-poll de-duplicates with manual ingest endpoint | ||
|
|
||
| - **WHEN** a poller is active for `conversion_job_id` and the operator(or other internal caller)POSTs `/api/internal/conversions/<conversion_job_id>/ingest` with a valid internal token |
…error 帶 log path (#100) * streaming-server capture Kit conversion logs:async redirect + result.error 帶 log path(streaming-server-capture-kit-conversion-logs) 完整 OpenSpec change(scaffold + apply 同 commit)。 ## Why 2026-05-22 user 用 341MB 真實 IFC 跑 fast-mvp loop,coordinator pipeline 全 work (下載 / dispatch / sandbox / auto-poll / ingest),但 Kit subprocess `exit 0` 沒寫 model.usdc,error message 只有 ps1 line 316 throw 字串(`"output was not created"`)。 根因:`bim-streaming-server/scripts/convert-ifc-to-usdc.ps1` 用 `Process.Start` 沒設 `RedirectStandardOutput` / `RedirectStandardError`,Kit subprocess 所有 stdout/stderr 直接寫到 PowerShell host 主控台(被吞)沒進 conversion result。任何「Kit silently exit 0 但沒寫 output」debug 線索皆無。 ## What changed(9 files / +575 / -15) ### ps1(+77 / -15) - `convert-ifc-to-usdc.ps1::Invoke-KitConversion`: - 加 `$startInfo.RedirectStandardOutput = $true` / `RedirectStandardError = $true` - artifact dir 內開 `kit-stdout.log` / `kit-stderr.log`(AutoFlush UTF-8 StreamWriter) - async `Register-ObjectEvent` + `BeginOutputReadLine` / `BeginErrorReadLine` 避免 sync ReadToEnd + WaitForExit 大量 output 經典 deadlock - finally:second WaitForExit(drain pending events)→ Unregister-Event → close writer - 失敗 throw 改 multi-line message,含:reason、kit_stdout_log 路徑、kit_stderr_log 路徑、 stderr last 100 行 tail、stdout last 50 行 tail - 成功時 log file 仍保留(baseline 對比用) ### Python adapter(+15) - `conversion_authority.ConversionAuthorityError`:加 optional `metadata: dict | None` attribute(backward compat:既有 raise 不影響) - `ifc2usdc_powershell_adapter._run_powershell_conversion`:subprocess fail 時 regex 從 combined stderr/stdout 抓 `kit_stdout_log:` / `kit_stderr_log:` 兩行 host absolute path, 附到 ConversionAuthorityError.metadata - `conversion_authority.StreamingConversionStore._fail_job`:加 optional `metadata` 參數, merge 進 `result.error` dict;catch ConversionAuthorityError 那條 path 傳入 exc.metadata ### Pytest(+58) - `tests/test_host_native_conversion_service.py` 加 2 case: - failed conversion with metadata → result.error 含 kit_stdout_log / kit_stderr_log - regression:沒 metadata 不該硬塞 keys(`FakeFailedConverter` 既有 path 不破) ### OpenSpec(5 files / +398) - proposal / design / tasks / acceptance / `streaming-ifc-usdc-conversion-authority` MODIFIED requirement(`Conversion failures expose actionable diagnostic`)+ 4 新 Scenario ## GitNexus blast radius = LOW | symbol | risk | d=1 | |---|---|---| | `_run_powershell_conversion` | LOW | `convert`(同 class) | | `Ifc2UsdcPowershellConverterAdapter` | LOW | `host_native_conversion_service.py` import | ## Verification | Level | Result | |---|---| | L1 streaming-server `pytest tests -q` | **33 passed**(31 既有 + 2 新) | | L1 coordinator `npm run verify` | **173 passed**(不動,regression OK) | | L1 root pytest | **9 passed** | | L2 `openspec validate streaming-server-capture-kit-conversion-logs --strict` | **valid** | | L2 `openspec validate --specs --strict` | **26 passed / 0 failed** | | L3 GitNexus pre-impact | LOW | | L4 真實 runtime | **跳過** — 留 merge 後重啟 streaming-server 讀新 code + user 重 trigger 341MB IFC,看 GET /result error 含 kit_stdout_log / kit_stderr_log 並 tail 看 Kit 真實錯誤訊息 | ## Backward compatibility - 既有 ConversionAuthorityError 不帶 metadata raise 點(>10 處)全部不變(metadata 預設 None) - result.error 加 keys 是純 additive(`code` / `message` 仍在) - callback outbox payload 不變(metadata-only,log paths 屬 host-local diagnostic, 不轉發雲端,per `conversion-webhook-lifecycle` 原則) - 既有 11+168+9 tests 全綠 ## Predecessor / Scope ✓ Predecessor:`coordinator-auto-poll-streaming-conversion`(archive PR #99,2026-05-22) 本 change 只加觀察性,**不嘗試 fix Kit 自己為什麼 silent exit 0 沒寫 USDC**(那是 Kit / HOOPS 本身的問題);下一個 change 可以拿本 change 收集的 Kit log 做 root cause analysis。 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix(streaming-server): code review:message 顯式 prepend log paths + 加 regex unit test Review feedback on PR #100 from senior code reviewer: - **Important #1**:`_run_powershell_conversion` 內 `[-800:]` tail truncation 可能 把 spec scenario 1 required substring `"---- stderr tail (last 100 lines) ----"` 砍掉。改成 message 開頭顯式 prepend `kit_stdout_log: ` + `kit_stderr_log: ` 兩行 +tail(額度提到 3000 chars,容納 header 與 tail 內容),保證 spec 要的 substring 仍在。 - **Important #2**:加 `test_run_powershell_conversion_regex_extracts_log_paths_from_ps1_throw` unit test,monkeypatch `subprocess.run` 返回真實 ps1 throw heredoc shape(含 Windows path 的 `C:\` drive-letter colon 與 backslash),assert regex 抓兩個 path 對 + ConversionAuthorityError.message 含 spec substring。鎖定 ps1 throw shape ↔ Python regex 契約,L1 直接 cover。 - **Minor #3**:`import re` 移到 module top(consistent with other stdlib imports), 移除 inline `import re as _re`。 Verification: - streaming-server pytest:**34 passed**(33 + 1 新 regex test) - 既有 33 case 不破 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Summary
PR #98(implementation,squash
2103bf7)merged 後的 OpenSpec sync/archive cleanup。git mv openspec/changes/coordinator-auto-poll-streaming-conversion/→openspec/changes/archive/2026-05-22-coordinator-auto-poll-streaming-conversion/openspec/specs/conversion-webhook-lifecycle/spec.mdMODIFIEDCoordinator ingests host-native conversion result into callback outboxrequirement:CONVERSION_POLL_ENABLED/CONVERSION_POLL_INTERVAL_SECONDS/CONVERSION_POLL_MAX_ATTEMPTS)docs/plans/AI-BIM-governance-saas-roadmap-2026-05.md加 archive 摘要openspec validate --specs --strict= 26 passed / 0 failedfast-mvp loop zero-touch automation 完成 ✨
remove-conflict-review-from-fast-mvp+fast-ifc-link-demo-loop+streaming-server-prefer-local-ifc-path+coordinator-auto-poll-streaming-conversion整條 fast-mvp loop 從 spec 到 code 完整對齊並真實跑通。
🤖 Generated with Claude Code