1280 lines
202 KiB
Markdown
1280 lines
202 KiB
Markdown
# Agent Computer progress (V3)
|
||
|
||
> **2026-09-10 第三輪狀態修正:全案仍為 PARTIAL。** 請以 [逐項稽核](agent-computer-audit-2026-09-10.md) 的 O01–O48/T01–T64 為準。下方前兩輪 `IMPLEMENTED` 包含尚未接進 runtime 的純函式及 fixture,不能視為全案已驗收。本輪實際修復與測試見文末。
|
||
|
||
基準 commit(計劃):`e6afa324530e19922909d4692c28fb005cc05a7a`
|
||
本機開始 HEAD:`79952b088b394e36d6c3db3952165f1c3fd59961`
|
||
未覆蓋使用者未提交修改(`docs/plan/` 仍未追蹤)。
|
||
|
||
驗證(2026-09-10 複查,見文末「複查:Bug 修正」):`cargo fmt --check`、`cargo clippy --workspace --all-targets -D warnings`、`cargo test --workspace`(含 sqlx,全綠)、`make test-agent-computer`、`apps/web npm run build` 全部 exit 0。
|
||
sqlx 測試用拋棄式 `pgvector/pgvector:pg16` 於 `127.0.0.1:5434`(`docker run --rm --name lazyboy-test-pg …`),不重建正在服務的 `lazyboy-postgres-1`。
|
||
真實 OAuth/主信箱/TigerVNC GUI fixture:`BLOCKED_EXTERNAL`。
|
||
sample MCP 在真實 Computer 容器 `lb-team-space-*` 內 `python3 echo_server.py` JSON-RPC 回 `from-computer`。外掛 UI 已 typecheck;無瀏覽器工具未點擊。
|
||
|
||
---
|
||
|
||
## PR-00 / PR-01
|
||
|
||
狀態:IMPLEMENTED(見前次)。截圖交付、semantic/vision、原生 fs/exec 短路徑。
|
||
|
||
---
|
||
|
||
## PR-02:雙模式身份
|
||
|
||
狀態:IMPLEMENTED(資料+契約+idle;沒有強制遷移)
|
||
|
||
- `computers.generation`;destroy/recreate `generation + 1`(舊 job 不可裝活)。
|
||
- Team 多 bot 同 `computer_id`:`computer_scope_key(Team, space, bot-a) == bot-b`。Dedicated 互斥 scope。
|
||
- **沒有**對 `bots.computer_id` 加 UNIQUE。
|
||
- `AdapterContext.computer_generation` / `control_epoch`。
|
||
- Pause scopes:`PauseScope::{Agent,Display,Computer}`;停 A 不 bump B 的 display epoch。
|
||
- idle reaper 把 `computer_jobs` running 算進活性。
|
||
|
||
回滾:migration 022 + generation SELECT/UPDATE。
|
||
|
||
---
|
||
|
||
## PR-03:ledger / 脫敏 / activity
|
||
|
||
狀態:IMPLEMENTED(Postgres 為 mutation 來源;本環境未跑 sqlx 整合測試)
|
||
|
||
- `dispatch`:mutating tool 先 `begin`;journal insert 失敗則 `JOURNAL_UNAVAILABLE`,不開做。
|
||
- 同 `operationId` + payload hash replay 已存結果;不同 hash → `PAYLOAD_MISMATCH`。
|
||
- `operationId` 不進入 payload hash。
|
||
- `finish` 寫 `computer_operations` + `operation_outbox`;`worker_loop` `flush_outbox` 只補尚未出現在 `run_activity` 的 `operationId`。
|
||
- 結果與 outbox payload 經過 `redact_text` / `redact_json`。
|
||
|
||
---
|
||
|
||
## PR-04:jobs / cancel / CAS
|
||
|
||
狀態:IMPLEMENTED(Runner 在 API 行程內;container recreate 以 generation 中斷)
|
||
|
||
- `JobSupervisor`:`process_group(0)`;cancel TERM→KILL process group。
|
||
- `exec`:`action=run|status|cancel`、`background` 回 `jobId`。
|
||
- `write_file.expectedHash` → `CONFLICT`,不覆蓋。
|
||
- 測試:`/bin/true` 真 exit;`sleep 30` 可 cancel。
|
||
|
||
- API 重啟:`computer_jobs` running/accepted → `interrupted`(不假裝收回 Docker PID)。
|
||
- `LAZYBOY_NATIVE_JOB_CONCURRENCY`(預設 2)超額 → `QUOTA_EXCEEDED`。
|
||
- Artifact:`write_file` 成功寫 `computer_artifacts`;`GET /api/bots/{id}/artifacts` 列表;下載從 Computer `read_file` 串回,sha256 不符 → `CONFLICT`。
|
||
|
||
O04 PTY `terminal.*`:DEFERRED(見文末)。跨重啟回收仍在跑的容器 PID:標 interrupted,不是 resume。
|
||
|
||
---
|
||
|
||
## PR-05:Tool Manager / MCP
|
||
|
||
狀態:IMPLEMENTED(stdio 不在 API spawn;sample 在 Computer;無第三方商店)
|
||
|
||
- Manifest:空 digest / zip-slip / 空 entrypoint 拒絕。
|
||
- 安裝≠Ready(`AuthRequired` ≠ 可用)。
|
||
- `scripts/sample-mcp/echo_server.py` + `manifest.yaml`(本機範例,不是虛構 npm)。
|
||
- REST:`GET/POST /api/bots/{id}/tools`、`POST .../tools/call`、`POST .../revoke`。
|
||
- 安裝把 echo 寫進 Computer:Dedicated `tools/...`,Team `shared/tools/...`;`python3` 在該容器執行 JSON-RPC。
|
||
- Agent 工具 `computer_mcp` 走同一條路徑;未 bind → `NOT_BOUND`。
|
||
- 外掛頁「Computer 套件」可安裝/呼叫/撤銷 echo;市集 MCP 文案不再假裝 stdio 都在 Computer。
|
||
- MCP `call_for` 先複製 client 再 await;`definitions_for(actor)` 不把別人的工具塞進這次 run。
|
||
|
||
未做:任意第三方套件商店、OAuth broker。`/api/mcp-servers` stdio **不再** `spawn` 子行程,只存 metadata 並指向 Computer Tool Manager。HTTP/SSE 仍為遠端 client。真實 Microsoft/Google OAuth:`BLOCKED_EXTERNAL`。
|
||
|
||
---
|
||
|
||
## PR-06:路由 / verifier / wait
|
||
|
||
狀態:IMPLEMENTED(決策層)
|
||
|
||
- `choose_route`:有授權 API 不走 browser;`POLICY_DENIED` 不繞路。
|
||
- `CompletionLayer`:transport ≠ effect ≠ task;`wait_counts_as_progress() == false`。
|
||
- policy:`wait` 不再刷新無進展時鐘;`exec` 可。
|
||
- `wait_until` 有 deadline;未知 disabled 不空轉。
|
||
- system prompt:Gmail/Outlook 有 connector 走 API。
|
||
|
||
---
|
||
|
||
## PR-07:form / takeover scopes
|
||
|
||
狀態:IMPLEMENTED(form 工具已註冊;lease/pause 見 PR-02)
|
||
|
||
- `run_form_macro` 失敗即停、逐步 evidence。
|
||
- Display writer lease 原本就有;pause scope 見 PR-02。
|
||
- Agent 工具 `form_fill`:用最新 snapshot locator/element id;`run_form_macro` 先定位,第一個失敗就停;可選 `submit` click。
|
||
|
||
---
|
||
|
||
## PR-08A Gmail fixture
|
||
|
||
狀態:IMPLEMENTED(純函式 fixture,不碰真信箱)
|
||
|
||
- batchModify 同 delta 分組、1000 cap。
|
||
- undo 只撤本 plan 的 delta。
|
||
- 真實帳號:`BLOCKED_EXTERNAL`。
|
||
|
||
---
|
||
|
||
## PR-08B Outlook
|
||
|
||
狀態:IMPLEMENTED(Graph 語意 fixture,非官方 npm)
|
||
|
||
- `$batch` 最多 20;子項 429/401 分開。
|
||
- categories merge,不覆蓋無關分類。
|
||
- `Mail.ReadWrite` 不自動允許寄信。
|
||
- 真實 Graph OAuth:`BLOCKED_EXTERNAL`。
|
||
|
||
---
|
||
|
||
## PR-09 TigerVNC 候選
|
||
|
||
狀態:IMPLEMENTED(opt-in,預設仍 Xvfb+x11vnc)
|
||
|
||
- `DisplayBackend`;slot 0 = `:1` / 5900 / 6080(T57)。
|
||
- `lazyboy-screen` 讀 `LAZYBOY_DISPLAY_BACKEND=tigervnc_xvnc`。
|
||
- Dockerfile 加入 `tigervnc-standalone-server`,失敗則 script fallback Xvfb。
|
||
- Cua/中文/a11y GUI 對照:`BLOCKED_EXTERNAL`,**未改 default**。
|
||
|
||
---
|
||
|
||
## PR-10 設定 / 文件
|
||
|
||
狀態:IMPLEMENTED
|
||
|
||
`.env.example`:`LAZYBOY_EXECUTION_PROFILE`、`LAZYBOY_DISPLAY_BACKEND`、`LAZYBOY_BROWSER_BACKEND`、concurrency、tool install flags。
|
||
`migrations/022_agent_computer.sql`。
|
||
|
||
---
|
||
|
||
## O01–O48
|
||
|
||
| ID | 狀態 |
|
||
|---|---|
|
||
| O01 | IMPLEMENTED |
|
||
| O02 | IMPLEMENTED(短 exec);長 job 見 O04 |
|
||
| O03 | IMPLEMENTED + CAS |
|
||
| O04 | PARTIAL(Computer 內 background exec + cancel;PTY `terminal.*` DEFERRED) |
|
||
| O05 | IMPLEMENTED(form macro + `form_fill` 工具) |
|
||
| O06 | IMPLEMENTED(stdio 不在 API spawn;HTTP/SSE 遠端;echo 在 Computer) |
|
||
| O07 | DEFERRED(未量測 docker exec 開銷) |
|
||
| O08 | IMPLEMENTED(`boot_for(need_gui=false)` 不 ensure_screen;`LAZYBOY_RUNNER_ONLY` 不 boot XFCE) |
|
||
| O09 | IMPLEMENTED(router) |
|
||
| O10 | IMPLEMENTED(revoke 從下次 schema 拿掉 computer_mcp;stdio 不再進 defs) |
|
||
| O11 | 保留現況 |
|
||
| O12 | IMPLEMENTED |
|
||
| O13 | DEFERRED |
|
||
| O14 | IMPLEMENTED(PR-01) |
|
||
| O15 | 保留現況(既有 profile bind) |
|
||
| O16 | PARTIAL(verifier 層 + wait 不算進度) |
|
||
| O17 | 實驗/opt-in |
|
||
| O18 | DEFERRED |
|
||
| O19 | 保留現況 |
|
||
| O20 | 保留現況 |
|
||
| O21 | 保留現況 |
|
||
| O22–O24 | DEFERRED |
|
||
| O25 | IMPLEMENTED/保留 |
|
||
| O26 | PARTIAL(既有 screen/profile lease) |
|
||
| O27 | IMPLEMENTED(epoch 函式) |
|
||
| O28 | PARTIAL(idle 看 runs/skills/jobs;2026-09-10 修正守門原本永遠回 false,並在判斷前 reconcile job 狀態) |
|
||
| O29 | IMPLEMENTED(package/binding 分離;共用 bytes 不共享帳號) |
|
||
| O30 | IMPLEMENTED(`LAZYBOY_NATIVE_JOB_CONCURRENCY`,預設 2;配額滿時先向 Computer reconcile 未 poll 的 job,不會永久卡死) |
|
||
| O31 | DEFERRED |
|
||
| O32 | DEFERRED(T59:保留 `-ac` 直到 Xauthority 接到 Cua/AT-SPI;VNC 仍走認證 proxy) |
|
||
| O33 | IMPLEMENTED(sample MCP 安裝 REST + UI) |
|
||
| O34 | IMPLEMENTED(digest/zip-slip/unpinned npx 拒絕) |
|
||
| O35 | IMPLEMENTED(installed ≠ ready;AuthRequired 不可用) |
|
||
| O36 | PARTIAL(熱啟用 bindings;無獨立 schema cache 服務) |
|
||
| O37 | IMPLEMENTED(0.0.2 旁置、job pin 舊版、rollback/remove) |
|
||
| O38 | BLOCKED_EXTERNAL |
|
||
| O39–O40 | IMPLEMENTED fixture;真實帳號 BLOCKED_EXTERNAL |
|
||
| O41 | IMPLEMENTED(dispatch 寫 operations + outbox;flush 補送) |
|
||
| O42 | IMPLEMENTED(errorCode/jobId/operationId) |
|
||
| O43 | IMPLEMENTED(列表 + 下載;磁碟 hash 不符 CONFLICT) |
|
||
| O44 | PARTIAL(verifier 層) |
|
||
| O45 | IMPLEMENTED(CAS + operation id) |
|
||
| O46 | IMPLEMENTED(`GET /api/computer/{id}/health`:db/sandbox/display/jobs/operations) |
|
||
| O47 | DEFERRED |
|
||
| O48 | DEFERRED(無固定環境雙模式 P95;不寫未測倍數) |
|
||
|
||
---
|
||
|
||
## T01–T64
|
||
|
||
| ID | 狀態 | 證據/理由 |
|
||
|---|---|---|
|
||
| T01 | IMPLEMENTED | `should_deliver_observation_image` |
|
||
| T02 | IMPLEMENTED | 換模型/takeover 強制交付(同真值表) |
|
||
| T03 | IMPLEMENTED | `pixel_actions_need_vision`;text-only 無圖 |
|
||
| T04 | IMPLEMENTED | `typed_browser_errors` |
|
||
| T05 | IMPLEMENTED | Team/Dedicated path + echo 路徑測試 |
|
||
| T06 | PARTIAL | generation 拒絕舊 job;未覆蓋所有偽造 grant 變體 |
|
||
| T07 | 保留現況 | task 無 Docker socket;未加新 sentinel fixture |
|
||
| T08 | IMPLEMENTED | zip-slip/`..` path 拒絕 |
|
||
| T09 | IMPLEMENTED | invalid UTF-8 binary payload |
|
||
| T10 | PARTIAL | 工具錯誤非空成功;磁碟滿未測 |
|
||
| T11 | IMPLEMENTED | `stale_hash` CAS CONFLICT |
|
||
| T12 | IMPLEMENTED | Supervisor `/exec` 回完整 `CommandResult`;reap 讀 `exit` 檔,`false`/缺檔為 Failed/1 |
|
||
| T13 | IMPLEMENTED | Computer-side job_op cancel 走 `computer_cancel_script`(TERM→KILL 整個 process group,驗證後才標 cancelled;2026-09-10 修正 pid 錯誤);status 缺欄位當 TRANSPORT,不發明 running |
|
||
| T14 | DEFERRED | API 重啟標 interrupted,不 resume 容器 PID |
|
||
| T15 | IMPLEMENTED | generation interrupt |
|
||
| T16 | IMPLEMENTED | operation_id hash/mismatch;2026-09-10 第二輪:key 改 per-bot、in-flight 同 id 回 `UNKNOWN_EFFECT`、傳輸失敗不存成 succeeded(sqlx 測試 `ledger_is_per_bot_and_does_not_rerun_in_flight_work`) |
|
||
| T17 | IMPLEMENTED | outbox flush 補送且不重複(sqlx 測試 `outbox_flush_does_not_duplicate_recorded_operations`;2026-09-10 修正 `SELECT 1` 解碼 bug) |
|
||
| T18 | IMPLEMENTED | `journal_failure` 拒 mutation |
|
||
| T19 | IMPLEMENTED | actor-scoped MCP;stdio 不在 API 持有 client |
|
||
| T20 | PARTIAL | NOT_BOUND/revoke;無真實 token revoke |
|
||
| T21 | IMPLEMENTED | `canary_is_stripped`;2026-09-10 第二輪:ledger/snippet 實際套用 `redact_secret_patterns`(之前傳空 canary 等於沒脫敏) |
|
||
| T22 | 保留現況 | 既有 prompt 邊界;無新 injection corpus |
|
||
| T23 | PARTIAL | 同 display lease 既有;Cua+worker 未雙開 fixture |
|
||
| T24 | PARTIAL | pause/takeover;HTTP in-flight barrier 未單獨測 |
|
||
| T25 | IMPLEMENTED | wait 不算 milestone |
|
||
| T26 | IMPLEMENTED | Gmail batchModify fixture |
|
||
| T27 | IMPLEMENTED | fixture 429/partial;真實帳號 BLOCKED_EXTERNAL |
|
||
| T28 | IMPLEMENTED | undo 只撤本 plan |
|
||
| T29 | IMPLEMENTED | 無強制拆 Team;migration 不 UNIQUE computer_id |
|
||
| T30 | IMPLEMENTED | `boot_for(need_gui=false)` 不 `ensure_screen`;`maybe_ensure_screen` + FakeSandbox native write/read/exec;`start.sh` runner-only 不 boot XFCE |
|
||
| T31 | PARTIAL | job 配額;無壓力 bench |
|
||
| T32 | DEFERRED | 無郵件分類品質集 |
|
||
| T33 | PARTIAL | broker 不進 generic env;無任意 exec 讀 socket fixture |
|
||
| T34 | IMPLEMENTED | artifact 從 Computer 下載 |
|
||
| T35 | PARTIAL | CAS/operation replay;resume 不重播未全測 |
|
||
| T36 | PARTIAL | mutating tools 走 ledger;MCP stdio 不再是旁路 spawn |
|
||
| T37 | IMPLEMENTED | `team_computers_share`;migration 註解+測試 |
|
||
| T38 | IMPLEMENTED | Team `bots/<id>/` 路徑 |
|
||
| T39 | IMPLEMENTED | shared/ + CAS |
|
||
| T40 | PARTIAL | 多 slot 契約;無雙 writer GUI fixture |
|
||
| T41 | PARTIAL | display writer lease 既有 |
|
||
| T42 | IMPLEMENTED | pause scope 停 A 不 bump B |
|
||
| T43 | PARTIAL | idle 看 jobs;整機 pause 未單測 recreate 全集 |
|
||
| T44 | 保留現況 | 既有 ensure-screen single-flight |
|
||
| T45 | IMPLEMENTED | Dedicated 刪 bot;Team Computer 存活 |
|
||
| T46 | IMPLEMENTED | echo 安裝/呼叫 `executionLocation=assigned_computer` |
|
||
| T47 | IMPLEMENTED | binding per bot;套件 per computer |
|
||
| T48 | IMPLEMENTED | digest/zip-slip/unpinned npx |
|
||
| T49 | IMPLEMENTED | `plan_version_switch` job pin |
|
||
| T50 | IMPLEMENTED | revoke/remove;revoke 掉 schema |
|
||
| T51 | IMPLEMENTED | Outlook fixture;非官方 npm |
|
||
| T52 | IMPLEMENTED | Graph 子項 429/401 |
|
||
| T53 | PARTIAL | fixture 分頁語意;真實 delta BLOCKED_EXTERNAL |
|
||
| T54 | IMPLEMENTED | categories merge 不盲覆蓋 |
|
||
| T55 | BLOCKED_EXTERNAL | 無 Cua/中文/a11y GUI |
|
||
| T56 | PARTIAL | generation bump;backend 熱切未做 |
|
||
| T57 | IMPLEMENTED | slot 0 = :1/5900/6080 |
|
||
| T58 | 保留現況 | 既有 noVNC;無新 DPI fixture |
|
||
| T59 | DEFERRED | 保留 `-ac`;VNC 走認證 proxy;Xauthority 未接到 Cua |
|
||
| T60 | IMPLEMENTED | 同 T30:native 路徑不 attach viewer;GUI 之後才 `lazyboy-screen ensure` |
|
||
| T61 | IMPLEMENTED | 未審查 env token/unpinned npx 拒絕 |
|
||
| T62 | 保留現況 | 同 UID 不宣稱強隔離 |
|
||
| T63 | PARTIAL | job 配額;renderer OOM 未測 |
|
||
| T64 | PARTIAL | 套件路徑可重建;舊 job 不裝活 |
|
||
|
||
## 2026-09-10 複查:Bug 修正
|
||
|
||
本輪先跑計劃第 16 節的完整 gate,再修實際發現的 bug;未新增功能。
|
||
|
||
驗證命令與結果(本機,拋棄式 Postgres `pgvector/pgvector:pg16` 於 `127.0.0.1:5434`):
|
||
|
||
- `cargo fmt --all -- --check`:原本有 diff(`artifacts.rs`、`computer.rs` 等新檔未格式化)→ 已 `cargo fmt --all`,現在 exit 0。
|
||
- `cargo clippy --workspace --all-targets -- -D warnings`:exit 0。
|
||
- `DATABASE_URL=… cargo test --workspace`:**全部通過**(api 157 passed / 3 ignored;control 150;harness 37;contracts 7;sandbox 7;supervisor 2)。前次紀錄的「sqlx 未連」現在已實跑;14 個 sqlx 測試(db/memory/monitor/routing::fan_out)全綠。
|
||
- `make test-agent-computer`:exit 0。
|
||
- `apps/web`:`npm run build`(`tsc --noEmit` + vite)通過。
|
||
|
||
修正:
|
||
|
||
1. **`lazyboy-api` 測試 binary 結束時 SIGABRT**(`ort` `Mutex poisoned` → `release_env_on_exit` 二次 panic)。根因:`memory.rs` 用 `catch_unwind` 包住 `TextEmbedding::try_new`,但 `ort` 找不到 `libonnxruntime.so` 時是在持有全域鎖的情況下 `expect` panic,鎖被 poison 後程序退出時 `.fini_array` hook 再 panic → abort。正式環境沒設 `ORT_DYLIB_PATH` 時,API 正常關機也會變 exit 134。修法:`memory.rs` 新增 `preload_onnx_runtime()`,先走 `ort::init_from(path)`(回 `Result`)預載 dylib,失敗即標 `Unavailable` 不再進 fastembed;`crates/api/Cargo.toml` 直接依賴同版 `ort ="=2.0.0-rc.13"`(lock 無新增 crate)。測試 `database_enforces_agent_scope_and_queries_do_not_leak` 改成顯式 `ModelState::Unavailable`,不再靠環境缺 ONNX 來觸發;新增 `a_missing_onnx_runtime_is_an_error_not_a_panic`。反向驗證:`ORT_DYLIB_PATH=/nonexistent cargo test -- --ignored memory_model_recovers_after_cache_failure` 只因無模型 fail,**不再 abort**。
|
||
2. **Computer 內 background job 的 pid 是錯的(T13 process-tree cancel 實際上沒生效)**。`computer_background_launch` 原本是 `mkdir -p … && setsid nohup sh … & echo $!`,`&` 作用在整個 `a && b` list,`$!` 是 bash 子 shell 的 pid,不是 `setsid` 後的 session leader。後果:`status` 只是碰巧能用(子 shell 會 wait);`cancel` 只殺子 shell,真正的 `sh`→子程序變孤兒繼續跑,之後 `status` 讀不到 exit 檔還會誤報 `failed`。修法:`mkdir` 獨立一行;wrapper 先 `printf %s "$$" > …/pid`,launcher 等 pid 檔再輸出(`setsid` 若 fork 也正確)。新增 `computer_cancel_script(pid)`:`kill -TERM -- -pgid` → 最多 1s 等待 → `kill -KILL -- -pgid` → 再驗證;exit 0 才算整棵樹已消失。`supervisor/docker.rs` cancel 改用它,且只有樹確實消失才把 job 標 `cancelled`(否則維持 `running`,不假稱取消)。測試:`computer_cancel_script_kills_the_whole_process_tree`(本機真起 `sh -c 'sleep 300; sleep 300'`,用 `pgrep -g` 驗證子 `sleep` 一起死);另在真實 Team 容器 `lb-team-space-c7bace…` 內手動跑 launcher+cancel:pid=pgid=sid,`tree gone`。
|
||
3. **supervisor `job_follow_up` 沒檢查 job 屬於哪個容器**:拿到別的 Computer 的 jobId 時會在自己容器對同號 pid 送 `kill`。現在 `job.computer_id != container` 一律回 `unknown job`。
|
||
4. **`Makefile` `help` 目標壞掉**:新增的 `test-agent-computer` 說明被塞進同一個 `@echo "…` 造成跨行未閉合字串,`make help` 回 `Unterminated quoted string` Error 2。已拆成兩行 `@echo`。
|
||
5. **idle reaper 的「有工作就不暫停」守門一直是死的(HEAD 既有 bug)**:`computer_has_active_work` 把 `SELECT 1`(INT4)解成 `(i64,)`,sqlx 回 `ColumnDecode mismatched types`,被 `matches!(Ok(Some(_)))` 吞成 false → 只要 `computers.updated_at` 十分鐘沒動,正在跑 run 的 Computer 也會被 suspend。改成 `sqlx::query(...).fetch_optional` 只看有沒有列。sqlx 測試 `idle_reaper_reconciles_unpolled_jobs_with_the_computer` 同時覆蓋 run 與 job 兩種活性。
|
||
6. **`flush_outbox` 去重失效(T17 宣稱的「不重做」實際沒生效)**:同樣的 `SELECT 1`→`i64` 解碼錯誤讓 `exists` 永遠 None,每筆 outbox 都再寫一次 `run_activity`,Activity 出現重複 tool 事件。已改為列存在檢查;查詢出錯時保留 outbox 列到下一輪,不猜。新增 sqlx 測試 `outbox_flush_does_not_duplicate_recorded_operations`。
|
||
7. **背景 job 配額會永久卡死**:`computer_jobs.status` 只在 agent 呼叫 `exec status` 時更新;兩個跑完但沒人 poll 的 job 會讓該 Computer 之後永遠 `QUOTA_EXCEEDED`,idle reaper 也永遠以為有工作。修法:`tools.rs` 配額看起來滿時先 `refresh_running_jobs`(向 Computer 問最舊 16 筆 running 的真實狀態;supervisor 回 `unknown job` 標 `interrupted`)再重算;`computer.rs` 的 `pause_idle_computers` 在判斷活性前做同樣的 reconcile(suspended 容器無法問,`stop_parked_computers` 不做)。
|
||
|
||
未修、已知:
|
||
|
||
- host 端 `JobSupervisor::start`(`crates/control/src/jobs.rs`)用 piped stdout 但只在子程序結束後 `read_to_end`;輸出超過 pipe buffer(~64 KiB)的子程序會永久阻塞。目前正式路徑不用它(背景 job 全在 Computer 內,`exec_background_does_not_spawn_on_the_api` 有守門),僅測試使用;若之後要在 API 行程內跑 job 必須先改成邊跑邊 drain。
|
||
- supervisor 的 `ComputerJobTable` 在記憶體;supervisor 重啟後舊 jobId 回 `unknown job`(API 側已把 `computer_jobs` 標 interrupted,屬同一限制,T14 仍 DEFERRED)。
|
||
|
||
回滾:還原 `crates/api/src/memory.rs`、`crates/api/Cargo.toml`(ort 直接依賴)、`crates/api/src/tools.rs`(refresh_running_jobs)、`crates/api/src/computer.rs`、`crates/api/src/operations.rs`、`crates/control/src/jobs.rs`、`crates/supervisor/src/docker.rs`、`Makefile` 本節改動即可;無 migration 變動。
|
||
|
||
## 2026-09-10 複查(第二輪):三路平行 code review 後的修正
|
||
|
||
三個獨立 review(ledger/outbox/artifacts;jobs/tool-manager/install;雙模式/pause/前端)共回報 40 餘項;重複、已在上一節修掉的略過。以下每項都有測試或反向驗證。gate:`cargo fmt --check`、`clippy -D warnings`、`cargo test --workspace`(api 162/control 154/harness 37)、`tsc --noEmit` 全綠。
|
||
|
||
**Ledger(T15–T17,`operations.rs` 重寫)**
|
||
|
||
1. **operationId 全域命名空間 → 跨 bot replay/拒絕**:ledger key 改為 `{bot_id}:{operation_id}`,查詢加 `bot_id` 條件。Bot B 選到 Bot A 用過的 id,不會拿到 A 的結果,也不會被 `PAYLOAD_MISMATCH` 擋。
|
||
2. **記憶體 `LEDGER` 拆掉**:與 DB 兩份狀態不一致(一邊 accepted 一邊沒列)、無上限增長(連非 mutating tool 都建列)。Postgres 是唯一真相;`OperationLedger` 只留 control crate 單元測試用。
|
||
3. **`bot_computer_id` 查詢失敗被當「沒有 Computer → Proceed」**:現在 DB 錯誤 = `JOURNAL_UNAVAILABLE`;不需要 Computer 的 mutation(remember/schedule)以 `computer_id = NULL` 入帳(migration 023 放寬 NOT NULL)。
|
||
4. **同 id 同 payload 的 `accepted` 列直接 Proceed → 崩潰後重試會做第二次**:新增 `Begin::InProgress` → 工具回 `UNKNOWN_EFFECT`,要求先讀回狀態、換新 id 重試。
|
||
5. **`finish` 對非 mutating tool 也寫 DB/outbox**:提早 return。`exec status` 改為非 mutating(`exec run/cancel` 仍是)。
|
||
6. **`"xxx failed: …"` 的傳輸失敗被存成 `succeeded` 並永遠 replay**:`outcome_status` 辨識這類文字 → 刪除 accepted 列讓同 id 可重試;不寫入結果。
|
||
7. **replay 丟掉 `pause`**:`Begin::Replay` 帶回 `pause`。
|
||
8. **outbox 寫 `run_activity` 失敗仍標 delivered**:新增 `monitor::try_record_pool`,只有成功才標;且只處理 `created_at` 早於 2 秒的列,避免與 `runs.rs` 的直接寫入賽跑而重複。
|
||
9. **`redact_text(&text, &[])` 是 no-op**:新增 `redact_secret_patterns`(Bearer、`password=`/`token:`、`sk-`/`ghp_`/`AKIA`/`xox`/`AIza`、PEM 私鑰、JSON secret key),ledger 結果與 activity snippet 都經過它。control crate 新增 `regex` 直接依賴(lock 已有)。
|
||
10. retention 新增 `operation_outbox`(delivered,7 天)與 `computer_operations`(非 accepted,90 天)。
|
||
|
||
**Jobs(T13/T14)**
|
||
|
||
11. **runner-only 容器 PID 1 是 `sleep infinity`,不收屍**:setsid 後的 wrapper 結束會變 zombie,`kill -0` 仍成功 → job 永遠 `running`。修:`HostConfig.init = true`(docker-init 收屍);`status` 改先讀 exit 檔、再用 `computer_alive_script`(看 `/proc/*/status` State,Z/X 視為死)。測試 `computer_alive_script_treats_a_zombie_as_dead` 本機造真 zombie 驗證。
|
||
12. **launcher 沒吐出 pid 時仍登記 job(pid None)→ 永遠 running、不能 cancel**:現在直接回錯誤,不登記幽靈 job。
|
||
13. **`exec status/cancel` 不看歸屬與世代**:新增 `job_ownership`:別的 bot 的 job → `UNKNOWN_JOB`;`generation` 小於目前 Computer 世代或已 `interrupted` → 直接回 interrupted,不去問新容器;`persist_job` 的 upsert 不再把 `interrupted` 蓋回 `running`。
|
||
14. **`pin_running_jobs` 把同 Computer 上其他 bot 的 job 也 pin 了**:加 `bot_id` 條件。
|
||
|
||
**Tool Manager(T18–T21)**
|
||
|
||
15. **revoke 後重裝撞 `UNIQUE(package_row_id, bot_id)` → 500**:`ON CONFLICT … DO UPDATE SET status='ready' RETURNING id`;切版本走 `switch_binding`(同 tx 內先清掉同 (package_row, bot) 的非 ready 殘留)。
|
||
16. **rollback 會把 `revoked` binding 復活**:非 `ready` → 409。
|
||
17. **執行前不驗 digest**:`verified_run_argv` 在同一條命令內 `sha256sum -c` 通過才 `exec python3`;不符 exit 97 → `PACKAGE_TAMPERED`。測試 `tampered_package_is_refused_before_it_runs` 真改檔驗證。
|
||
18. **`validate_manifest` 只檢查非空**:現要求 64 hex;`compute-at-install` 佔位會被拒。
|
||
19. `InstallBody` 只吃 `package_id`、前端送 `packageId`:serde `rename` + `alias` 兩者皆收。
|
||
20. `refuse_unpinned_npx` 把 `@scope/pkg` 的 `@` 當版本 pin:修為去掉 scope 前綴再判斷。
|
||
|
||
**其他**
|
||
|
||
21. **`form_fill` 靜默截斷**:>20 欄或欄位缺 name/locator → `INVALID_ARGUMENT`,不再填半張表回報成功。
|
||
22. **`looks_like_css` 誤判 `v1.2 Release`/`C++ Guide` 這種標題為 CSS**:click/type 先做精確唯一標題比對;找不到才依 locator 形狀決定 `SELECTOR_UNSUPPORTED`/`TARGET_NOT_FOUND`/`TARGET_STALE`,並各給對應說明。
|
||
23. **`computers.display_backend` 從未寫入**:boot 轉 running 時寫入 `DisplayBackend::from_env()`;health 不再永遠回欄位預設值。
|
||
24. **`delete_bot` 不清 `tool_bindings`/`computer_jobs`/`computer_operations`/`computer_artifacts`**:同 tx 一起刪(Team Computer 的配額不再被已刪 bot 佔用)。
|
||
25. **`wait` 在 `PROGRESS_WHEN_REPEATED` 內,與本文件「wait 不刷新無進展時鐘」相反**:改成 heartbeat 語意 — `wait` 只更新 `last_heartbeat_turn`(不會被當 loop halt),但 stale 教練訊息照常觸發;`polling_a_build_or_a_queue_is_not_treated_as_spinning` 同時鎖住兩半契約。
|
||
26. **Gmail fixture 沒有真的 undo**:新增 `undo_plan(before, batches)`,只還原本次計畫實際改動的 label(原本就有的不拔、原本沒有的不補、人工新加的不動),測試做 plan→undo 往返驗證。
|
||
27. 前端:`RunActivityEntry.operationId` 補型別;`runs.rs` 從工具回傳 JSON 抽 `jobId` 寫進 activity,monitor 原有的 `entry.jobId` 顯示終於有值;並顯示 `op <短 id>`。
|
||
|
||
**Migration**:新增 `migrations/023_agent_computer_fixes.sql`(`computer_operations.computer_id` DROP NOT NULL、移除多餘 `computer_operations_id_hash`、outbox pending 部分索引、`computer_operations(bot_id, created_at)` 索引)。不改 022(已可能套用過)。
|
||
|
||
**review 提到但本輪不動、記錄在此**:
|
||
- Pause scopes/`resume_after_pause`/`route_operation` 有純函式與測試但沒接進 runs 主迴圈;takeover 實際只有 GUI 全暫停,沒有 scope 級細分(T22–T24 應視為 PARTIAL,不是 DONE)。
|
||
- `write_file` 走 `bash -lc` 字串插值路徑(HEAD 既有);`normalize_workspace_path` 有限制字元,本輪未改成 argv。
|
||
- supervisor `ComputerJobTable` 仍在記憶體(T14 DEFERRED 不變)。
|
||
|
||
回滾:還原本節列出的檔案,並 `DELETE FROM _sqlx_migrations WHERE version=23` 後 `ALTER TABLE computer_operations ALTER COLUMN computer_id SET NOT NULL`(若已無 NULL 列)。
|
||
|
||
## 誠實未完成
|
||
|
||
P0/P1 可在本環境落地的已接上。下面不是「做完」:
|
||
|
||
1. 真實 Gmail/Outlook OAuth/主信箱:`BLOCKED_EXTERNAL`。
|
||
2. TigerVNC 當預設 + Cua/中文/a11y GUI:`BLOCKED_EXTERNAL`;預設仍 Xvfb+x11vnc。
|
||
3. Postgres sqlx 整合測試此環境未連。
|
||
4. **O04 PTY `terminal.*`:DEFERRED** — 非互動走 `exec`+jobs+process-tree cancel;TUI 協作仍用 GUI `shell`。不假裝有 PTY。
|
||
5. 跨重啟回收容器內仍在跑的 PID:標 interrupted,不是 resume。
|
||
6. 任意第三方套件商店、完整 OAuth broker:沒有憑證。
|
||
7. O48 無未測速度倍數。
|
||
8. 外掛 UI 未在瀏覽器點過(無瀏覽器工具);已 `tsc --noEmit`。
|
||
|
||
雙模式:Team/Dedicated 契約不變;**沒有** UNIQUE `bots.computer_id`。stdio MCP **不是** API 子行程。
|
||
|
||
回滾:還原本輪 crate / migration 022 / lazyboy-screen `-ac` 註解 / sample-mcp / 前端 plugins+i18n。不要 force push。
|
||
|
||
## 2026-09-10 第三輪:完整計劃核對與實際修復
|
||
|
||
起始 HEAD `80fe9a8`,工作目錄乾淨;未部署、未操作真實信箱、未刪除既有 Team 資料。
|
||
逐項狀態:[O01–O48 / T01–T64 稽核](agent-computer-audit-2026-09-10.md)。**不是全案完成報告。**
|
||
|
||
### 實際 diff
|
||
|
||
1. `crates/control/src/jobs.rs`:stdout/stderr 從開始就同時 drain;各保留 1 MiB 並標截斷,避免 pipe 塞滿令命令永遠無法退出。拒絕重複 job ID,避免覆蓋舊 child handle。`poll` 不再跨 await 暫時拿走 child,消除與 cancel/interruption 的競爭窗口。
|
||
2. 同檔 `posix_shell_join` 保留空參數;含中文、換行、單引號、`$()`、反引號的真實 shell round-trip 測試。取消拒絕 PID 0/1,Computer cancel 排除 zombie;Supervisor 對已結束 job 不再發 signal,以免 PID 回收後誤殺。
|
||
3. `tools.rs`:job ownership 在 DB error/missing row 時 fail closed,核對目前 bot assignment、Computer generation 與 job owner,不再把 unrecorded job 直接轉送。
|
||
4. `tools.rs`:Computer row lock + `accepted` reservation 同 transaction 執行配額判定,兩 agent/API worker 搶最後一格只有一個成功。DB admission 失敗時不開始命令;launch 結果不完整不假報 running,回 `UNKNOWN_EFFECT` 並保留 reservation。
|
||
5. `runs.rs`:移除 worker 啟動時把全 DB running/accepted jobs 一律改 interrupted 的 UPDATE;API 重啟不代表 Computer 程序死亡。Supervisor registry 持久化仍未完成。
|
||
6. `computer_files.py`(**只在 Computer 執行**):read/list/write 以 argv 傳路徑、stdin 傳 bounded base64,不把檔名或整份內容拼入 shell/argv。使用 directory descriptor + `O_NOFOLLOW`/`O_NONBLOCK`、regular-file 判斷,拒絕 symlink/FIFO/路徑逃逸與超過 8 MiB 的內容;缺目錄不再回空成功。Supervisor file route 允許最多 12 MiB JSON 以容納 base64,超過 raw bytes 限制回 HTTP 413。
|
||
7. `SandboxProvider::write_file_checked` → Docker transport → Supervisor → helper:expected hash、directory flock、temporary file、fsync、atomic rename 在 Computer 同一程序完成,取代 API 先 read 再 write 的假 CAS。空 expectedHash 表示只可建立;unconditional write 需省略 expectedHash。維持原有檔案 executable permission。
|
||
8. `path.rs`:controlled Team fs 只能指向 own `bots/<id>` 或 `shared/`,拒絕其他 bot、相似前綴 bot ID 及 virtual-root 旁路。這不是同 UID arbitrary shell 的 OS 隔離。
|
||
9. `supervisor/docker.rs::exec_raw`:stdin writer 與 output reader 同時執行,修大量雙向 IO 死鎖;job 檔案讀取不再把傳輸/權限錯誤吞成空字串。
|
||
10. `operations.rs`:傳輸失敗保留 accepted/unknown 去重紀錄,不能刪 intent 讓相同操作重做。結果與 outbox 同 transaction,任何一側失敗回滾,dispatch 回 `UNKNOWN_EFFECT`,不宣稱已可靠記錄成功。
|
||
11. 同檔:外部 `mcp_*` 與 `connection_check` 也需副作用前 journal;MCP error 回 typed unknown。**HTTP/SSE MCP client 仍在 API,本輪沒有把它搬完。**
|
||
12. `tool_install.rs`:digest 檢查後直接 compile/execute 同一份 bytes,不重新開啟可被替換的 pathname;Python `-I`,拒絕 symlink/non-regular/超限內容。binding 查詢核對當前 Computer;boot 後重讀 bot,避免初次 provision 用到過時空 assignment。
|
||
13. `tests/frontend.test.mjs`:更新一個已過時的描述文字比對,仍驗證 observe boolean/default false 與實際 opt-in 條件;未刪除測試。
|
||
14. 新增 `tests/computer-files.test.py`、真實 Rust→Docker opt-in regression、`make bench-agent-computer` 與原生雙模式 benchmark JSON。所有測試容器皆新建、無持久 volume、無公開 ports;既有服務不受影響。
|
||
|
||
### 本輪驗證
|
||
|
||
- `cargo fmt --all -- --check`:通過。
|
||
- `cargo clippy --workspace --all-targets -- -D warnings`:通過。
|
||
- `DATABASE_URL=postgres://lazyboy:lazyboy@127.0.0.1:5434/lazyboy cargo test --workspace`:374 passed、0 failed、4 ignored;包含 disposable sqlx DB 測試。3 個原有外部/模型 fixture ignored,新增 Docker fixture 預設 ignored 並於下列命令實際執行。
|
||
- `make test-agent-computer`:通過(另含 Python filesystem gate)。
|
||
- `node --test tests/frontend.test.mjs`:56 passed。
|
||
- `cd apps/web && npm run build`:通過;既有 lottie eval/chunk-size warning,非新增編譯錯誤。
|
||
- `python3 tests/computer-files.test.py`:5 passed;含 8 個並行 CAS、1 MiB binary、特殊檔名、symlink/特殊檔案拒絕、大小限制、permission preservation。
|
||
- `LAZYBOY_PLAN_TEST_CONTAINER=lazyboy-plan-files-fixture cargo test -p lazyboy-supervisor real_computer_files_cas_and_bidirectional_exec -- --ignored --nocapture`:在新建 `lazyboy/computer:local`、UID 1000、無網路、無 desktop 容器通過;驗證真實 Docker transport、特殊路徑、binary、CAS、1 MiB 雙向 IO、background job 非零退出/完整輸出/取消/terminal job 重複取消。
|
||
- `python3 scripts/bench-agent-computer.py`:shared 60/60、dedicated 30/30;[完整樣本與環境](benchmarks/agent-computer-native.json)。測的是 native helper/read/write/hash/exec,非完整 API/model/viewer E2E,沒有提速倍數或 GUI 品質宣稱。
|
||
|
||
### 尚未完成與 checkpoint
|
||
|
||
核心未完成:受保護且持久的 Computer Runner/PTY/job cursor;Computer 本地 durable outbox;scope-aware pause/fencing 接線;generic MCP loader/常駐 instance/HTTP client 搬遷;per-agent/account OAuth broker;Gmail/Outlook fake HTTP 與可安裝 adapter;task completion verifier;完整 GUI/TigerVNC/viewer benchmark。accepted launch outcome unknown 的 reservation 保守保留,仍需持久 Runner reconcile;不能手動猜它沒執行而自動清除重試。取消尚未完成 PID starttime identity 全集。
|
||
|
||
實際缺程式的項目標 PARTIAL/NOT_STARTED,不假借 OAuth 帳號缺失標 BLOCKED_EXTERNAL。真正 Microsoft/Google 帳號、外部模型的 E2E 另列 BLOCKED_EXTERNAL;不索取明文密碼,不使用主信箱。詳細每個 Oxx/Txx 見稽核表。
|
||
|
||
回滾:還原上述本輪 diff 與新增 helper/測試/benchmark 檔即可;本輪無 migration。`write_file_checked` 涉及 API、control trait、兩個 sandbox provider 與 Supervisor,需一起回滾,不能只留半套 transport。
|
||
|
||
## 範圍追加:P0–P3 全項完成
|
||
|
||
使用者明確要求 P0~P3 每項完成。先前可選/延期項目全部恢復待辦,包括 transport/DOM 增量、TigerVNC/x0vncserver/XDamage/viewer tuning、Selkies、Xpra、Wayland、shm/OOM、skill 重用與完整 benchmark。互斥 backend 採可選配置,不以一律替換預設理解;每個選項都需實作與驗證,不能只交不採用理由。
|
||
|
||
## Runner 與中央事件同步 checkpoint(2026-09-10,後續進度)
|
||
|
||
此節更新前一節尚缺項目,不表示 P0–P3 已完成。
|
||
|
||
- 新增 Computer 內 root 保護的 SQLite job registry、outbox、每工作獨立 worker/socket/output。實際命令降權至 UID/GID 1000、清空繼承環境,不繼承控制 token。
|
||
- 新增真正 PTY start/interact/resize、精確 binary output cursor、pause/resume/cancel、runtime deadline、4 MiB/stream 上限;完成需等待程序樹與 pipe EOF。重複取消不延後 KILL,逾時/超限清理中不提前回報 terminal。
|
||
- 修完成時 socket 關閉被覆寫 unknown 的競爭。終態不因 Computer reboot 改為 interrupted;管理 reconcile 也需正確 generation。
|
||
- API 每 5 秒獨立同步 root outbox,不依賴 viewer 或 model turn。migration 024 加入 job run_id 與中央 computer_job_events;去重、owner/generation/provider 檢查、job 狀態與 activity 在同一 transaction,commit 後才 ack。API 多 worker 以 job row lock 保持事件順序;輸出、argv、程序 metadata 不寫 activity。
|
||
- 新增 `make test-runner-jobs`:自動建立無 network、無資料 volume 的 disposable Computer,執行 6 項 Python Runner integration 和真實 Rust→Docker transport 測試,結束清除。包括建立新的 DockerHost 後工作仍可取消、本地事件讀取/ack。
|
||
- 已通過:6 個 Python Runner tests、1 個真實 Rust Docker test、中央事件 SQLx dedup/scoping/order regression。workspace 375 passed、4 ignored(Docker ignored test 另已實跑);clippy all targets 通過。最後額外 argv 型別/NUL 驗證與同步 timeout 變更亦已重跑 workspace 375 passed、4 ignored 與 clippy 通過。
|
||
|
||
仍需完成:worker crash/orphan 回收與 PID/starttime 全集、root 輸出/事件 retention(現為 64 jobs 硬上限)、root scope barrier、broker/grants、通用 MCP/常駐 client/郵件 adapter、完整 files API、task verifier、全部 P2/P3 backend 與 benchmark。此段未部署,migration 僅在 disposable SQLx test DB 驗證。
|
||
|
||
|
||
## Runner 回收與輸出保留 checkpoint(2026-09-10)
|
||
|
||
- 終態輸出保留 24 小時;達 64 筆保留上限時提前回收最舊、已中央 ack 的終態輸出。未送達事件、執行中工作、unknown 不會被回收。先 commit output_expired tombstone 才刪輸出,重送 start 不重執行;output 查詢明確回 OUTPUT_EXPIRED。已 ack outbox 列會清除。
|
||
- worker crash 後用受保護 PID/starttime + pidfd 驗證與凍結程序群,回收後標 interrupted,不捏造成功或原本 exit code。starttime 不符拒絕發訊號。無 viewer 時也由 events maintenance 偵測/回收。
|
||
- worker 本身也記錄 starttime,避免 PID 重用誤認存活;啟動 worker 先登記身分,已變 unknown 的 intent 不會稍後重新啟動。
|
||
- `make test-runner-jobs`:9 個 Python 實測 + 1 個 Rust→Docker 實測通過,新增過期輸出/ack 保護/冪等、64 slots 配額壓力、kill worker/偽造 starttime/自動維護故障注入。workspace 375 passed、4 ignored;clippy all targets 通過。
|
||
- 仍未完成:脫離 process group(setsid)的程序隔離/回收、根 journal/tombstone 的長期磁碟配額、scope barrier 與原稽核表其他項。P0–P3 全範圍保持未結案。
|
||
|
||
|
||
## Agent takeover barrier checkpoint(2026-09-10)
|
||
|
||
- Runner 新增 root SQLite agent barrier;先停止新 job admission,再停止並確認存活 process group 進入 stopped 狀態。accepted/cancelling/unknown 未處理完不回成功。直接 resume/interact 不能繞過暫停;release 僅恢復 scope 暫停的工作,不恢復原本手動暫停的工作。
|
||
- API takeover 在授予 user lease 前要求 Runner barrier 成功。migration 025 新增中央 agent_mutation_barriers,操作 journal admission 與暫停共用 per-agent advisory transaction lock;中央仍有 accepted/unknown 操作就拒絕宣稱接管完成。
|
||
- takeover/release 另以 per-agent advisory lock 防止互相交錯;release 先釋放 user lease、恢復 Runner,再打開中央 admission 與原 run。
|
||
- FakeSandbox 也模擬相同 agent job admission/status barrier,不靠無條件成功掩蓋測試。
|
||
- 驗證:10 個 Python Runner 實測 + 1 個 Rust→Docker 實測通過;包含 A 暫停、B 繼續、新工作/直接 resume 拒絕、原本手動暫停保持。SQLx 新增 admission 與 pause race、跨 bot、unknown 保留測試。workspace 376 passed、4 ignored;clippy all targets 通過。
|
||
- 未完成:display/整機 scope barrier、完整 epoch token 與 stale ref/grant 恢復、外部 HTTP read-back reconciliation、脫離 process group 的 OS 隔離。此段只完成 agent/job 與中央受記錄 mutation admission 的接線,不能視為 O27/T24/T42/T43 全部驗收。
|
||
|
||
## HTTP MCP client 搬入 Computer checkpoint(2026-09-10)
|
||
|
||
- API `McpHub` 只保留授權目錄/config/schema,移除 API 內的 rmcp HTTP transport 與 stdio client feature;實際探索/呼叫透過 Supervisor→root Runner→Computer 內固定 `lazyboy-controld --mcp-call`。模型呼叫使用當前 bot 的 Computer/generation;目錄探索使用該 owner 已運行的 Computer。
|
||
- `mcp_*` 需要 Computer readiness,但不需要 GUI/viewer。沒有可用 Computer 時連線明確失敗,不回退至 API 主機 HTTP。舊 image 必須更新 controld binary;本輪建置了既有 Bookworm controld build stage,未替換運行中的 Computer。
|
||
- 配置/headers 只從 stdin 傳給 root client,錯誤不回傳遠端敏感 body。45 秒 deadline、4 MiB 結果/SSE event 限制、256 MiB process address-space limit、CPU/core dump 限制。`isError` 不記為成功。
|
||
- 檢查 rmcp 3.2.0 原始碼發現預設 expired-session 會重送 POST,已明確關閉 `reinit_on_expired_session`;session expired/response lost 回 unknown,不隱式重播 mutation。
|
||
- 修正 `npx` pin policy:拒絕 latest/next/range/不完整版本與另一參數假冒 pin;只採實際執行的 npm package 精確版本,排除 URL/不明 option 形式。這不取代 digest/審核安裝流程。
|
||
- 驗證:Bookworm release binary 在 disposable、network-none、無 desktop Computer 內跑真實 rmcp SDK + loopback HTTP fixture,5 項涵蓋 schema/Unicode call/auth secret canary/isError/session expiry/response lost 單次呼叫/超限結果。另 10 個 Runner tests + 1 個 Rust→Docker test 通過。workspace 376 passed、4 ignored;最後 pin policy 2 測試與 clippy all targets 通過;frontend 56 passed。
|
||
- `make test-runner-jobs` 現在自動透過 image 的 controld build stage 取得相容 binary。可設定 `LAZYBOY_TEST_MCP_IMAGE` 使用已建置的該 stage;不直接把開發主機較新 glibc binary 當成 Computer 驗收。
|
||
- 仍未完成:generic stdio loader/版本 pin instance、常駐 MCP pool、legacy SSE transport、per-agent/account grants/broker、MCP 本地持久 journal/artifact 與全面管理 audit、schema readiness 自動恢復與完整 API E2E。P0–P3 保持全範圍未結案。
|
||
|
||
## 通用 archive/stdio MCP checkpoint(2026-09-10)
|
||
|
||
- 新增 `/api/bots/{id}/tools/install`,從 Computer 內既有 workspace archive 讀取、驗證 digest、解包與探索 MCP schema。migration 026 保存 manifest,先記錄 installing intent,完成後才 ready;沒有 API 主機下載/解包 task artifact。
|
||
- 新增 `computer_mcp` list/discover/call,依 actor、bot、當前 Computer、ready binding、manifest version/digest 檢查。SDK 握手及工具呼叫真正使用 Computer 內 stdio child,不只對固定 echo script 丟單行 JSON。
|
||
- 安裝器拒絕 symlink/hardlink/特殊檔/路徑逃逸、錯 digest、超限展開、未知 manifest 權限/hook 欄位;不可把同版本換成另一 manifest。套件檔案/權限在每次執行前重新驗證。
|
||
- 每個 binding 配獨立 UID/GID、私人 HOME/state,setpriv 清除 supplementary groups 並啟用 no-new-privileges。私有套件程式碼另有 root-owned、僅該 binding group 可讀的唯讀副本;共享 immutable code 不混入私人 state。
|
||
- 每呼叫結束清理該 UID 的程序,包含 setsid 與 non-dumpable 子程序。使用 `/proc/status` 的真實 effective UID,而非會被 dumpable flag 改變的 `/proc` inode owner;訊號使用 pidfd 避免 PID 重用。
|
||
- Root package revoke 與既有 revoke/remove API 接線;先關 admission,不能確認在途 call drain 時回報 pending。版本/hash 與 root binding 不符拒絕執行,避免 legacy rollback 誤用另一版 code。未執行的參數/授權拒絕和真正未知效果分開,避免簡單錯誤污染 unknown ledger。
|
||
- 補安裝配額在寫檔前檢查、獨立安裝鎖、同 manifest/binding 遺留 staging 清理與 failed DB commit 後的 hash/permission 驗證恢復。UID 先 durable reserve 再配置檔案 owner,不重用失敗安裝的 UID。
|
||
- 驗證:377 workspace tests passed、4 ignored;SQLx 新增 owner/binding/current-Computer/revoke 可見性測試。10 Runner + 5 HTTP MCP + 4 archive/stdio + 1 Rust Docker = 20 個容器測試通過;覆蓋未知欄位、digest、symlink/hardlink、路徑逃逸、展開 quota、版本 pin、撤銷、私有 code/state 與 non-dumpable/setsid 回收。frontend 56 passed、clippy all targets 通過。
|
||
- API contract 與目前限制見 [tool-packages.md](tool-packages.md)。仍缺完整 V1 manifest/遠端 URL/通用 UI、第三方 package fixture、常駐 pool、network/account grants/broker、完整 OS filesystem/network sandbox、state 磁碟 quota/長期 GC、通用重新授權與 rollback、全 API 安裝 E2E。這些仍是 P0–P3 未完成範圍,不以本輪 fixture 取代全部驗收。
|
||
|
||
## 通用套件管理表單 checkpoint(2026-09-10)
|
||
|
||
- Tool Manager 新增 Computer workspace archive 路徑與 JSON manifest 安裝表單,顯示安裝回傳 schemas/錯誤,補中英文文案。切換 bot 重新建立 pane,避免舊 bot 的操作結果帶到新 bot。
|
||
- 套件清單只顯示當前 Computer 的 bindings,API 明確回傳 archive runtime 類型;通用套件不再顯示 echo 呼叫/legacy rollback。舊 rollback API 在修改資料前拒絕 archive,避免中央版本變更而 root runtime 不匹配。
|
||
- revoked/installing bindings 仍可撤銷或移除,讓 runtime drain 失敗後能重試。操作失敗也刷新中央狀態;清單讀取錯誤不再無聲吞掉。
|
||
- 驗證:前端 56 測試與 production build 通過;新增 SQLx 測試涵蓋 runtime kind、revoked readiness、擁有者與 peer bot 隔離、Computer 切換。
|
||
- 尚未完成:schemas 的持久管理/重新探索、通用工具手動呼叫 UI、完整 V1 安裝精靈、通用重新授權與 rollback;此表單不代表 Tool Manager 全部驗收完成。
|
||
|
||
## 套件探索與手動操作 checkpoint(2026-09-10)
|
||
|
||
- Tool Manager 支援已安裝 archive binding 的 schema 重新探索,以及指定工具名稱/JSON object 參數的手動呼叫。與模型共用 binding 授權與 Computer 內 MCP SDK 路徑,帶入目前 generation;Computer 未 running 不派送。
|
||
- 手動呼叫經由共用 operations journal、agent barrier 與 outbox,要求 UUID operationId;重放回傳原結果,payload 不符拒絕,未知效果不重新派送。管理操作的 run_id 為 NULL,outbox 不誤寫不存在的 run_activity。
|
||
- 分頁 sessionStorage 僅保存最近操作 ID 與 SHA-256 請求雜湊,不保存工具參數。相同請求重用 ID;變更輸入或明確建立新操作才使用新 ID。沒有自動 mutation retry。
|
||
- SQLx 管理入口回歸涵蓋 owner/peer bot 拒絕、派送後結果未知、撤銷後原 ID 仍重放而不派送、payload mismatch、schema 探索拒絕 revoked binding,以及無 run 的 outbox 完成。這不是第三方 MCP/真實 API transport 全套 E2E 的替代。
|
||
- 本輪驗證:擴充的 SQLx 管理入口測試通過(含 stopped Computer 留有 provider_ref 的拒絕案例);operations 8 項、frontend 56 項、production build 與 API clippy all targets 通過。完整 P0–P3 仍未結案。
|
||
|
||
## 套件重新授權 checkpoint(2026-09-10)
|
||
|
||
- migration 027 改為 package/bot 的 live binding partial unique index;撤銷/移除的 binding 保留歷史,不再因重新安裝而復活舊 ID。
|
||
- Archive install 支援明確 `reauthorize: true`,UI 有對應選項。先確認全部舊 bindings 的 root runtime 已 drain,才 durable reserve 新 binding;新的 root 安裝分配不同 UID/私人 state。一般重試不會默默授予新權限。
|
||
- package advisory lock 加 binding row lock,序列化安裝重試與並行撤銷;已存在新 installing/ready intent 時,重試沿用該 ID,不重建權限。舊 sample installer 也配合 partial unique index 保留撤銷識別碼。
|
||
- Root 對不存在的 binding 撤銷改成冪等成功,適用重新建立的 Computer;存在但屬於另一 bot 的 binding 仍拒絕。FakeSandbox 沒有已安裝的套件程序,因此對 absent binding 使用相同語義。
|
||
- SQLx 覆蓋未明確重新授權拒絕、新 intent 與舊 revoked row 同時保留、重試不增加第三個 binding。容器 suite 驗證新 UID/私人 state、舊 ID 永不復活、其他 bot 不能撤銷。
|
||
- 本輪未完成通用 rollback、帳戶 grant/broker、長期 revoked binding/state GC、全 V1 manifest;P0–P3 保持未結案。
|
||
- 驗證:workspace 378 passed、4 ignored(其中 Docker transport 另於 disposable gate 執行);10 Runner + 5 HTTP MCP + 4 archive/stdio + 1 Rust Docker 全通過,fixtures 已清除。frontend 56 passed、production build、workspace clippy all targets 與 diff check 通過。
|
||
|
||
## Runner 版本切換 checkpoint(2026-09-10)
|
||
|
||
- 新增 root `package_switch`:只切換同 package 的已安裝且完整 manifest/hash 相符目標。切換前重驗 cache、entrypoint 與檔案權限,維持原 binding UID/私人 state。
|
||
- 與 install 共用 binding lock、與執行中 MCP call 共用 UID lock;忙碌時回報 PACKAGE_BUSY,不殺死已接受呼叫。私有 code 以 staging+rename 換版,DB 最終 compare-and-swap 不覆寫並行 revoke。
|
||
- `package_switches` 在 binding 版本變更的同一 SQLite transaction 記錄 transition ID/payload hash/receipt。重試回傳既有 receipt;不同 payload 拒絕。回滾後再收到舊升級請求,不會重新升級。
|
||
- 修正原 install 最終 unconditional ready 寫入:現在檢查 package key 與目前狀態,不能把並行撤銷的 binding 復活。
|
||
- 容器測試新增真實 stdio 延遲呼叫/private code 升級與回滾、同 UID/state 保留、另一 binding 不受影響、busy、重放與 mismatch、revoked 拒絕。root transition journal 目前上限 1024,長期 retention 待補。
|
||
- 中央 durable transition intent、API/UI rollback、schema registry 原子更新尚未接線;此 checkpoint 只證明 Runner 切換,不代表全套版本管理完成。
|
||
- 驗證:10 Runner + 5 HTTP MCP + 5 archive/stdio + 1 Rust Docker,共 21 項 disposable Computer 測試通過,測試容器已清除;diff check 通過。
|
||
|
||
## 中央版本切換/回滾 checkpoint(2026-09-10)
|
||
|
||
- migration 028 新增 durable package_transitions、schemas 與 pending/cancelling 排他性;live binding unique index 含 switching/retiring,安裝器不會繞過切換中的 reservation。
|
||
- `/api/bots/{bot}/tools/{binding}/switch` 與 UI 支援指定已安裝目標版本、沿用 operation ID 恢復 pending。轉換前關閉參與 bindings 的中央 admission;root package_retire 等現有 call 完成,不中斷它;接著 root switch 與驗證 target schemas。
|
||
- 最終 transaction 同時更新 source binding 的 package pointer、ready、目標旧 binding revoked 與 schemas/completed receipt。重放已完成的升級請求不會覆蓋後來的回滾。
|
||
- 撤銷/移除任一參與 binding 會關閉全體 admission 並取消 transition;runtime cleanup 未確認前保留 cancelling,阻擋新安裝。取消可重試,不復活舊 ID。
|
||
- 新 transition 使用 agent-barrier advisory lock;paused 拒絕。takeover 的 unresolved-effects 查詢現在包含 pending/cancelling transitions,避免切換仍在途卻授予人類 lease。
|
||
- SQLx 測试覆蓋 reservation/retry、peer bot 不受影響、generation mismatch rollback、ready/schema atomic completion、舊 receipt 重放、取消與 takeover 邊界。完成 boundary 以已驗證 runtime result 驅動;真實 runtime 由獨立 Docker gate 驗證,仍不是完整 HTTP API→Supervisor→Computer E2E。
|
||
- 跨 generation 的 pending transition 目前需明確撤銷清理後重建;完整 account/schema grant registry、常駐 pool、長期 transition/state GC 仍待補。P0–P3 全範圍保持未結案。
|
||
- 驗證:workspace 379 passed、4 ignored;最後 takeover 整合後 API 170 passed、3 ignored。21 項 disposable Computer gate 全通過,fixtures 已清除;frontend 56 passed、production build、workspace clippy all targets 與 diff check 通過。
|
||
|
||
## 版本切換跨 generation readback(2026-09-10)
|
||
|
||
- 新增 root 唯讀 package_transition_status,在 install/call 鎖下驗證當前 binding 的實際 package bytes,再回傳 request-hash 相符的 receipt 是否存在與當前版本/digest。
|
||
- API 遇到同 Computer 新 generation 時先 read back;沒有 receipt 必須仍為來源版本,有 receipt 必須仍為目標版本。符合後以目前 Computer generation/pending 狀態 CAS 更新,再走原本冪等 retire/switch/discovery/complete 流程。
|
||
- 舊 receipt 不代表目前仍在該版本:後來已回滾的 binding 會被拒絕恢復舊升級。無 root binding、bytes/receipt 不符、無法聯絡 Runner 都不更新 generation。
|
||
- Docker suite 實測 root readback 未完成/已完成/已被回滾取代三態,跨 identity generation 讀取仍保留原 receipt 驗證。SQLx 驗證讀回失敗不更新 generation;判定測試涵蓋有效分支、不同 package、missing 欄位與 receipt/current-version 矛盾。
|
||
- 此輪沒有宣稱實際 container restart+完整 HTTP API transport E2E 已完成;root store 遺失的 recreate 仍需撤銷清理/重新安裝,state 搬移與持久性驗收仍待補。
|
||
- 本輪驗證:21 項 disposable Computer gate、2 項 transition/recovery 測試與 API clippy all targets 通過;測試容器已清除,diff check 通過。
|
||
|
||
## 實際 Computer restart gate(2026-09-10)
|
||
|
||
- `make test-runner-jobs` 現在建立私人套件、切換版本與私人 state,執行真正的 `docker restart`,再從新程序/新 identity generation 驗證 root receipt、當前 code/digest、同 UID 與原 state 計數。
|
||
- 重啟後驗證 completed 與尚未派送的 transition readback,實際回滾,再重放舊升級 receipt;確認沒有重新升級或重置私人 state。測試只使用 gate 自行建立的容器。
|
||
- 驗證:既有 21 項容器測試加 1 個兩階段實際 restart scenario 全部通過;fixtures 清除、diff check 通過。這不是 container remove/recreate,也不取代完整 HTTP API→Supervisor→Computer E2E。
|
||
- 下一個已確認缺口:`DockerHost::provision` 目前只有 home bind mount;`/var/lib/lazyboy-runner`、package cache/private exec/state 還在 container writable layer。需要補 root-owned 持久 mount 與既有容器遷移,才能宣稱 recreate 保留 registry/套件/私人 state;目前尚未完成,不可把 restart 成功當成 recreate 證據。
|
||
|
||
## 新 Computer 的 runtime 持久儲存(2026-09-10)
|
||
|
||
- provision 新增 DATA_DIR/runtime/{homeKey} 下四個 root-owned bind mounts:Runner(含 SQLite/outbox/private cache)、共享 immutable package cache、private executable copies、私人 tool state。host runtime/key parent 0700,不放在 task home 內。
|
||
- 目錄建立拒絕 symlink、非 root owner、group/world writable 路徑;再次 provision 保留既有內容。沒有 HOST_DATA_DIR 時使用絕對 bind source,修正預設相對 data dir 的 Docker bind path。
|
||
- 自動替換既有 Computer 前比對全部 runtime mount 的 source/destination;未遷移或指向其他資料位置時拒絕直接 destroy。完整舊容器資料搬移仍待實作,此時保留原容器,不冒充已遷移。
|
||
- gate 使用獨立 temporary bind sources,真正 docker rm 後重新建立不同 container ID,再驗證 package receipt、code、UID/private state 與 rollback。root Runner source 和資料從 mounts 保留,只有 image binary/測試程式重新提供。cleanup 先清除容器,再刪 host fixture 目錄。
|
||
- 驗證:runtime storage Rust test、21 項既有容器測試+restart+remove/recreate scenarios 通過;Supervisor clippy all targets 與 diff check 通過。此驗證不等於完整 API provisioning transport E2E 或舊容器自動遷移驗收。
|
||
|
||
## 舊 writable-layer runtime 遷移(2026-09-10)
|
||
|
||
- 舊容器替換/移除前,Supervisor 持 per-home lifecycle file lock,停止來源、串流匯出四個 Docker archives、在 root-only staging 解包。全程不在 Supervisor 執行套件/shell/Python;Rust tar loader 保留 owner/mode 與安全的連結語義。
|
||
- 驗證拒絕 traversal、重複成員、未宣告父目錄、特殊檔、非 root-owned/writable root;每 archive 8 GiB/100,000 entries。所有檔案/目錄 fsync,保存 prepared/published marker 與內容/metadata fingerprints,再分目錄 rename;部分發布可重試,不覆寫非空目的地。
|
||
- 發布後核對完整快照,重試也不僅信任 marker。來源在 snapshot 後重新運行、目標被更改、mount/home source 不一致時拒絕移除來源。遷移中的舊容器不能被另一 wake/provision 繞過 lifecycle lock 重新啟動。
|
||
- 修正 create-name-conflict 原本無條件刪除同名容器、destroy 忽略移除失敗。未管理的容器拒絕刪除;公開 destroy 也會先保留 legacy runtime,而不是只有 image replacement 才遷移。
|
||
- 真實 gate 從沒有 runtime mounts 的舊容器開始,先驗證目的地 sentinel 不被覆寫,完成 migration,再注入 partial-publish 狀態與 snapshot tamper;確認可恢復/拒絕損毀,來源仍保留。也實测 wake 阻擋與外來同名容器保留。最後刪除舊容器、建立新的 persistent-mount container,驗證 package/UID/state/receipt/rollback。
|
||
- 此處驗證的是 Supervisor migration 與真實 Docker archives,不取代完整 HTTP API→Supervisor→Computer 操作 E2E,亦不宣稱未解決的 job generation reconcile、account broker、viewer/完整 P0–P3 已完成。
|
||
- 驗證:workspace 382 passed、5 ignored(Supervisor 2 個 Docker-only 測試在 gate 另跑);既有 21 項容器測試加 restart、legacy migration/partial publish/collision、remove/recreate 情境通過。最終 workspace clippy all targets、diff check 及 `lazyboy/supervisor:plan-test` distroless image 建置通過。所有 fixture 容器與暫存資料均清除,候選映像未部署。
|
||
|
||
## Computer generation 與移除失敗處理(2026-09-10)
|
||
|
||
- Docker Sandbox 的 DELETE 現在檢查 HTTP status;Supervisor 拒絕遷移/移除時,不再回報成功。localhost HTTP fixture 覆蓋 204、409、500。
|
||
- API restart/不健康容器替換只在 destroy 確認後,以同一資料庫交易清除 provider/租約、增加 generation、中斷舊 generation 的活動工作。stop 失敗不當成移除證據;destroy 失敗或回應逾時保留 provider、generation 與 job 狀態,標記 error 供後續核對。
|
||
- 交易鎖住 Computer row 並比對原 provider/generation,拒絕過期完成回應;provider 遺失且仍有活動工作時拒絕憑空中斷。替換完成後重新讀取 Computer,避免 provision 傳入已移除的 provider。
|
||
- boot/resume 共用 provider 發布交易:新 provider 增加 generation、清除舊租約並中斷舊工作;同容器恢復保留 generation。涵蓋遺失 destroy 回應後,下一次 provision 找到新容器的情境。
|
||
- SQLx 測試覆蓋失敗保留、成功中斷、terminal/新 generation 工作保留、重複過期結果拒絕、無 provider 活動工作拒絕,以及 provider 替換/原容器恢復。workspace 384 passed、5 ignored;workspace clippy all targets 通過。未將此視為完整跨服務故障注入 E2E 或全部 P0–P3 驗收完成。
|
||
|
||
## 閒置回收與工作 admission(2026-09-10)
|
||
|
||
- 修正 busy query 失敗時誤認閒置的 fail-open;資料庫取得/查詢失敗一律禁止回收。accepted/unknown operations 與 pending/cancelling package transitions 也列入活性判斷。
|
||
- pause/長時間 parked stop 共用交易 helper,持 Computer row lock 後重新核對 provider、generation、state 與 idle cutoff,再檢查工作。Supervisor lifecycle 回應有 30 秒上限,失敗/逾時不發布假成功狀態;沒有 provider 也不憑空宣稱已停止。
|
||
- job admission 使用同一 row lock,重新檢查 running 狀態,防止等待回收交易後仍接受工作。這不代表所有 viewer、MCP admission、GUI lease 的 lifecycle race 已完成驗收。
|
||
- 新 SQLx+localhost HTTP fixture 驗證 unknown operation 保活、stop 409 不改狀態、updated_at 改變拒絕舊快照、成功停止、DB query error 保守拒絕;admission 測試涵蓋 suspended 拒絕及與 parking row lock 的併發等待。API suite 173 passed、3 ignored;API clippy all targets 與 diff check 通過。
|
||
|
||
## Operation/套件換版與閒置回收 admission(2026-09-10)
|
||
|
||
- Operation intent 交易現在更新 Computer 活躍時間,取得與 parking 相同的 row lock,並與 accepted ledger 一起 commit;既有 operation replay 不刷新活躍時間,也不重播效果。沒有 Computer 的操作仍可使用 NULL journal。
|
||
- 新 package transition 在 agent barrier 後鎖定 Computer,重新檢查 running/generation/provider,才修改 binding 狀態並寫 pending intent;防止讀取 running 後、容器已被暫停仍建立換版。
|
||
- SQLx 測試實際持 parking row lock,確認 operation admission 等待;intent INSERT constraint failure 會回滾活躍時間。換版測試在等待期間把 Computer 改為 suspended,確認拒絕且沒有建立 transition,恢復 running 後既有換版/取消測試仍通過。
|
||
- 驗證:API 174 passed、3 ignored;新增換版併發測試通過;API clippy all targets、diff check 通過。完整 HTTP API→Supervisor→Computer lifecycle 故障注入、viewer 與其他資源活性仍屬未完成範圍。
|
||
|
||
## API 中斷後的 boot attempt 恢復(2026-09-10)
|
||
|
||
- migration 029 新增 boot_attempt/boot_started_at 與部分索引,替既有 booting 記錄以 updated_at 補起始時間。API 每次 claim booting 產生獨立 UUID。
|
||
- Runner reconciliation loop 回收超過 3 分鐘的 boot attempt(正常 provision 120 秒+workspace setup 15 秒)。只標記 error 並清除 attempt,不清除 provider、不增加 generation、不改 job 結果;後續正常 provision 由 Supervisor 核對實際容器。
|
||
- boot success 與 mark_boot_error 都比對 attempt;新一次啟動開始後,舊成功/失敗回應不再能覆蓋它。stop/generation retirement 清除舊 attempt;resume 必須符合無啟動 attempt 的狀態。
|
||
- SQLx 測試驗證未逾期不回收、逾期保留 provider/generation/unknown job、新 attempt 拒絕舊結果,以及成功後清除 metadata。API suite 175 passed、3 ignored;API clippy all targets 通過。此證據涵蓋資料庫恢復與 fence,不冒充 API process kill 的完整 HTTP 故障注入驗收。
|
||
|
||
## 原生 stat_file(2026-09-10)
|
||
|
||
- 新增 agent-facing stat_file schema/dispatch,沿用 Team bot workspace/shared 路徑授權;列入 native sandbox routing,text-only 模型可用且不需要 GUI。SandboxProvider 提供明確 unsupported fallback,Docker provider 接 Supervisor `/computers/{id}/files/stat`。
|
||
- metadata 在 Computer 內的 descriptor-relative helper 讀取;不讀取內容、不追蹤最終 symlink,父目錄仍 O_NOFOLLOW。輸出 path、file/dir/symlink/special、size、mode,modifiedNs 使用字串避免 JavaScript 浮點數失真;不回傳 symlink 的外部目標。
|
||
- Python fixture 覆蓋 1 TiB sparse file、FIFO 不阻塞、symlink、父目錄 symlink 越界、traversal、缺檔與 root directory;6 項檔案測試通過。Rust 真實 Docker file gate 加入 stat kind/size/timestamp 檢查。
|
||
- workspace 387 passed、5 ignored;workspace clippy all targets 通過。此項不取代尚缺的 patch/move/search、讀取分頁/串流,也不宣稱完整 API HTTP 檔案操作 E2E 已完成。
|
||
- 最終 Docker gate 通過:既有 Runner/HTTP MCP/package fixtures、實際 restart、legacy migration/部分發布恢復/名稱衝突保留、remove/recreate,以及含 stat 的真實檔案 CAS/雙向 exec;fixture 容器清除完成,未部署候選映像。
|
||
|
||
## 原生 patch_file(2026-09-10)
|
||
|
||
- 新增 patch_file schema/native routing/agent workspace dispatch,透過 SandboxProvider 與 Supervisor `/computers/{id}/files/patch` 在 Computer 內修改,API 不下載原檔執行 read-modify-write。操作接入既有中央 operation ledger/barrier,transport 失聯或非確定回應標為 UNKNOWN_EFFECT,不自動重播。
|
||
- 支援 offset/deleteBytes/contentBase64 的 byte splice,必填小寫 SHA-256 expectedHash。只修改既有 regular file;要求合法範圍,原檔及結果限制 8 MiB,保留 mode。同 directory flock 內比對 hash、組成結果、temporary file fsync/atomic replace/directory fsync,最後讀回比對 hash;回傳 size/sha256/verified。
|
||
- helper 拒絕未知 request fields、負數/bool offsets、越界、缺檔、symlink;8 項原生檔案測試通過,包含二進位 patch、權限保留、失敗原檔不變、8 個並行 patch 僅一成功。真實 Docker file gate 已加入 patch/stale-hash conflict 檢查。
|
||
- 尚未實作 move/search、byte cursor/大型串流,以及檔案變更的 Computer 本地 durable journal;中央 ledger 不冒充本地 journal 驗收。
|
||
- 最終驗證:workspace 387 passed、5 ignored;8 項檔案 fixture、含 patch 的真實 Docker gate(Runner/MCP/restart/migration/remove-recreate/native files)與 workspace clippy all targets 通過。fixture 容器清除,未部署。
|
||
|
||
## 原生 move_file(2026-09-10)
|
||
|
||
- 新增 move_file schema/native routing/operation ledger dispatch,來源與 destination 各自經 Team bot workspace/shared 路徑授權。透過 SandboxProvider 與 Supervisor `/computers/{id}/files/move` 執行;API 不搬運檔案 bytes。
|
||
- 必填 source expectedHash 與 expectedDestinationHash(空字串表示目的地必須不存在)。Computer helper 以 descriptor-relative O_NOFOLLOW 開啟兩端目錄,依 device/inode 固定順序取得 flock,重複目錄只鎖一次,檢查 regular file/hash 後移動。目的地不存在使用 Linux renameat2 NOREPLACE;已存在且 hash 相符才 atomic replace,之後同步兩個目錄並讀回目的地 hash/確認來源消失。
|
||
- 保留 inode/mode/bytes,拒絕同一檔案/hardlink alias、symlink、越界與未知欄位。此版來源及既有目的地核對上限 8 MiB,父目錄須存在;跨 filesystem 不使用非原子 copy fallback。原生 search/byte paging/大型串流與本地 file journal 仍待完成。
|
||
- 10 項 Python file fixture 通過,包含雙端 conflict 原檔保留、權限、反向跨目錄無死鎖、同一目的地僅一成功;Docker native gate 增加真實移動/來源消失/bytes 驗證及移回。
|
||
- 最終驗證:workspace 387 passed、5 ignored;10 項原生 file fixture、含 move 的真實 Docker gate、workspace clippy all targets 通過。fixture 容器均清除,未部署。
|
||
|
||
## 原生 search_files(2026-09-10)
|
||
|
||
- 新增 search_files schema/native routing/workspace dispatch,透過 SandboxProvider 與 Supervisor `/computers/{id}/files/search` 在 Computer 執行 UTF-8 literal search。API 不下載整棵檔案樹,無 shell、regex 或 GUI 依賴。
|
||
- descriptor-relative DFS、O_NOFOLLOW、不追蹤 symlink,最多 32 層/4096 entries/16 MiB 讀取;單檔上限 1 MiB、最多 100 matches、序列化 match 總量 512 KiB。NUL/無效 UTF-8、特殊檔、過大、讀取時改變、過長路徑與無權限檔案均計入 skipped;truncated/limitReason/complete 明示搜尋覆蓋範圍,不能將部分結果當作完整無命中。
|
||
- 每個 match 提供 path、byteOffset、previewOffset、可讀 preview 與 byte-safe previewBase64;純 UTF-8 query 最多 256 bytes,maxResults 預設 20。根路徑缺檔回報失敗。
|
||
- 13 項檔案 fixture 通過,包括 Unicode byte offsets、regex 符號當字面值、binary/symlink/FIFO/oversize 略過、entry/byte/result/output limits,以及特殊長檔名的 JSON 大小限制。Docker native gate 新增真實容器內搜尋與 Unicode offset 驗證。
|
||
- workspace 387 passed、5 ignored,workspace clippy all targets 與最終 Supervisor clippy 通過;完整大型檔案 byte paging/串流、Computer 本地 file journal 及全 API HTTP E2E 尚未完成。
|
||
- 最終 Docker gate 通過(Runner/HTTP MCP/packages/restart/legacy migration/remove-recreate/含 search 的 native file gate)。fixture 容器清除完成,未部署。
|
||
|
||
## byte range/continuation 讀取(2026-09-10)
|
||
|
||
- 新增 read_file_bytes,text 與 binary page 分開:offsetBytes/maxBytes/expectedVersion,最大 64 KiB;回傳 base64、nextOffset、EOF、總 size、chunkSha256。chunk hash 明確不冒充全檔 hash,API 不先載入整份大檔。
|
||
- Computer 使用 O_NOFOLLOW/regular-file 檢查及 pread。version v1 由 device/inode/size/mtime/ctime 建立;offset>0 必須帶版本,讀前/讀後核對,拒絕檔案被替換或修改後拼接不同版本。offset=size 可取得空 EOF,offset>size、零長度/超量/非法版本回報錯誤。
|
||
- Supervisor `/computers/{id}/files/read-range` 與 Docker Sandbox 保留 FILE_NOT_FOUND、PERMISSION_DENIED、INVALID_ARGUMENT、CONFLICT、SIZE_LIMIT 等錯誤類別;read-only transport 失敗不偽裝空內容成功。
|
||
- 15 項原生檔案 fixture 通過,包含二進位分頁重組、EOF、同 bytes 被重新替換時舊版本拒絕、1 TiB sparse file 尾端定位。真實 Docker native gate 增加 first page/tail/EOF/bytes 驗證,整個 Runner/MCP/restart/migration/remove-recreate gate 通過。
|
||
- workspace 387 passed、5 ignored,workspace clippy all targets 通過。大型 upload/download/attachment 全串流、directory pagination、Computer 本地 file journal 及完整 API HTTP E2E 仍未完成。
|
||
- 額外 localhost HTTP regression 驗證 400/403/404/409/413/500 保留正確工具錯誤碼;測試與最終 Sandbox clippy 通過。fixture 容器均已清除,未部署。
|
||
|
||
## 舊 list_files 容量與錯誤修正(2026-09-10)
|
||
|
||
- 移除無上限 os.listdir+sort;改用 scandir,最多 4096 entries 與 512 KiB 序列化內容,收齊且確認目錄 mtime/ctime 未變後才輸出與排序。超限/衝突不輸出部分成功清單。
|
||
- symlink、FIFO/特殊檔不再誤標成 regular file。Supervisor 以 413/409 保留 DIRECTORY_LIMIT/CONFLICT,Docker Sandbox 與 agent-facing list_files 傳遞 typed error;一般 list failure 也不再使用成功 text outcome。
|
||
- 18 項原生檔案 fixture、Sandbox 10 項測試(含 localhost HTTP limit/conflict regression)、workspace clippy all targets、diff check 通過。本輪沒有重新聲稱完整 Docker/API E2E;有界拒絕不取代尚未完成的 directory continuation。
|
||
|
||
## Directory continuation(2026-09-10)
|
||
|
||
- 既有 list_files 加入 limit(1–200)/cursor,透過新的 SandboxProvider/Supervisor `/computers/{id}/files/list-page` 取得有界頁面;未指定分頁時保留既有 bounded/sorted list 行為。
|
||
- Computer helper 使用經本機 Linux dirent64 header 核對的 64-bit Linux readdir64/telldir/seekdir cookie。每頁重新開啟 descriptor-relative 目錄並接續 cookie,不重掃前面頁面;返回 filesystem order、nextCursor、eof 與 version。最多 200 entries/512 KiB entry JSON;長檔名達輸出上限也提供 continuation。
|
||
- 版本綁定路徑、device/inode、目錄 mtime/ctime,讀前/讀後核對;目錄 membership 改變、跨路徑 cursor、非法 token 會拒絕。此版本核對目錄項目,不宣稱各檔案內容的跨頁 snapshot。cursor 是讀取位置資料,不充當授權憑證,workspace grant 仍逐次核對。
|
||
- 21 項原生檔案 fixture 通過:4301 entries 全量分頁無重複/漏項、cursor replay、目錄變動/跨路徑拒絕、空目錄、長檔名輸出限制接續。真實 Docker native gate 驗證跨 helper 程序 continuation 與修改後 conflict;Runner/MCP/restart/migration/remove-recreate gate 通過,fixture 容器清除。
|
||
- Workspace 首輪發現既有 zombie fixture 偶發前提失敗:shell 可能在 exec 前先回收子程序。改用明確 fork/確認 /proc zombie 後才通知測試,最後透過 stdin 關閉要求 parent waitpid 清理;移除固定 200 ms 等待與不確定 shell 行為。
|
||
- Directory continuation 不取代大型 upload/download/attachment 全串流與 Computer 本地 file journal;完整 API HTTP E2E 與其餘 P0–P3 仍未完成。
|
||
- 最終重跑:workspace 389 passed、5 ignored;21 項 file fixtures、真實 Docker gate、workspace clippy all targets、diff check 通過。未部署候選映像或修改正式 Team 資料。
|
||
|
||
## 唯讀檔案 typed errors 與假成功修正(2026-09-10)
|
||
|
||
- 修正 legacy read_file/stat_file 在 transport 失敗與路徑驗證失敗時仍回成功 text outcome;新增共用 native_file_failure,保留 FILE_NOT_FOUND/PERMISSION_DENIED/INVALID_ARGUMENT/SIZE_LIMIT/DIRECTORY_LIMIT/CONFLICT 等分類。list/search 也沿用這些分類。
|
||
- Computer helper 對 read/stat/list/search/read-range/list-page 分類已知唯讀錯誤,保留現有 conflict/directory-limit 標記。Supervisor 唯讀 endpoints 共用 status mapper,Docker Sandbox 的 read/stat/list 保留 HTTP error 類別,停止讀取無用途的錯誤 body;file_operation 路徑拒絕帶 INVALID_ARGUMENT。
|
||
- 未把此分類直接套用到可能已產生副作用的 write/patch/move,避免將回應遺失/readback 失敗誤判成確定未執行。
|
||
- 23 項 helper fixture 通過,包含缺檔、oversize、非法路徑與實際降權程序讀取 permission-denied,失敗無 bytes 輸出。localhost HTTP fixture 覆蓋 read/stat/list/range 各 400/403/404/409/413/500;API outcome regression 確認 error_code 非空。
|
||
- 最終 workspace 390 passed、5 ignored;23 項 file fixtures、workspace clippy all targets、diff check 通過。本輪未重跑整個 Docker gate,不冒充完整 API HTTP E2E;mutation 磁碟滿/本地 journal/全串流與其餘 P0–P3 仍有必做項。
|
||
|
||
## 寫入回應未知與 fsync 失敗邊界(2026-09-10)
|
||
|
||
- Docker write_file_checked 對 request response loss/一般 server failure 明示 UNKNOWN_EFFECT;409 保持 CONFLICT,400/413 保留確定的 INVALID_ARGUMENT/SIZE_LIMIT。API write_file 不再將這些不確定情況寫成普通 FILE_WRITE_FAILED,而由既有 operation ledger 保留 unknown,禁止自動重播。
|
||
- 新 helper 故障注入測試在 temporary-file fsync 與 rename 後 directory fsync 分別注入 ENOSPC:前者原檔保持不變,後者新內容已可见但 helper 失敗;兩者都清除 temporary。這證明 transport failure 不能推論未寫入;測試是 fsync fault injection,不冒充真實磁碟填滿測試。
|
||
- localhost HTTP fixture 覆蓋 204、400、409、413、500 及直接關閉連線,確認失聯/500 保留 UNKNOWN_EFFECT。
|
||
- 驗證:24 項 file fixtures、API 176 passed/3 ignored、Sandbox 11 passed、workspace clippy all targets 與 diff check 通過。本輪未重新宣稱完整 Docker gate;Computer 本地 file journal/未知結果 readback recovery/全串流與其餘 P0–P3 仍待完成。
|
||
|
||
## Computer root file journal backend(2026-09-10)
|
||
|
||
- 新增 runner_files.py,隨 trusted Runner bootstrap 提供固定 computer_files helper source。root SQLite file_operations 以 computer/bot/generation/operation ID 隔離,先 FULL/WAL commit accepted intent 才以 UID/GID 1000、清空 supplementary groups/環境與資源限制執行 write/patch/move。
|
||
- ledger 保存 action/path/precondition/desired-or-replacement hashes、payload digest 與有界 receipt,不保存 task contents。相同 ID/payload 只回放結果;payload mismatch 拒絕;accepted/unknown 不重播。file_status 僅讀取同 scope 歷史結果。全域 1024 歷史紀錄上限,額滿拒絕新效果但保留回放;長期 retention/公平 quota 仍待補。
|
||
- file_mutate 與 root agent barrier admission 同 SQLite transaction 序列化;pause acknowledgement 會等待檔案 accepted/unknown 清空,無法確認時 BARRIER_INCOMPLETE,不假接管。root helper 本身不取代 API workspace assignment/grant 檢查。
|
||
- write 現在也在 fsync/rename 後讀回 hash,與 patch/move 一樣提供 verified receipt。RPC stdin 上限調成有界 16 MiB,容納既有 8 MiB file write payload;這不是大型檔案 streaming 實作。
|
||
- 六項真實容器 root journal 測試:receipt/重送不覆蓋後續修改、wrong bot/generation 隔離與 UID/metadata 防讀、patch/move/conflict、未完成 intent 不重播且阻擋 pause、journal admission readonly failure 不啟動 child、quota 拒絕新操作但保留 replay,以及 SQLite receipt commit 注入失敗後保留 unknown/人類後續修改。(部分情境合併在同一測試。)
|
||
- 歷史 root receipt 在實際 restart、legacy runtime migration、remove/recreate 後仍可按原 scope 查得。這是持久資料驗證,不冒充新 generation 的 API recovery/readback。
|
||
- 驗證:workspace 391 passed、5 ignored;24 項 file helper tests、6 項 root file journal tests、Runner/MCP/package/restart/migration/recreate/native file Docker gate、workspace clippy all targets、diff check 通過。fixture 容器均清除。
|
||
- **仍未完成**:正式 API write/patch/move 路徑尚未切換到 root journal;file outbox、中央 receipt reconcile、unknown readback recovery/跨 generation 驗證尚待接線。此 backend 不列為全案或完整 file journal E2E 完成。
|
||
|
||
|
||
## Agent file journal HTTP 接線與 operation ID 並行修正(2026-09-10)
|
||
|
||
- Agent `write_file`/`patch_file`/`move_file` 經 Sandbox SDK → Supervisor `/files/mutate` → Computer root file journal;要求 bot 與正 generation,未知 transport/receipt 結果保留 UNKNOWN_EFFECT。未支援此能力的 provider 明確拒絕,不退回未記錄的寫入。
|
||
- API central intent 仍先於 HTTP 持久提交;root operation ID 使用本次原始 ID 的 SHA-256,保留 root identity scope。write receipt 額外核對 path/size/SHA-256,符合才登錄 artifact。
|
||
- 修正共用 `last_operation_id` 的競爭:file mutation 與 artifact 使用 dispatch 區域變數一路傳遞,不受另一個工具修改共用顯示欄位影響。SQLx 測試以 admission advisory lock 建立交錯,驗證正確 root ID、HTTP 發送前 accepted、重送不再 HTTP,以及 artifact 對應 ID。
|
||
- 真實 Docker gate 已包含 SDK → 同一 Supervisor HTTP handler/middleware → root Runner:receipt replay 不覆蓋後續人類修改、payload mismatch、CAS conflict、root pause 拒絕新操作;完整 Runner/file/MCP/package/restart/migration/recreate gate 通過,fixture 清除。
|
||
- API→mock Supervisor contract 與上述真實 transport gate 是兩段證據,尚不列為完整 API HTTP→Computer E2E。其他 legacy 寫入/upload 路徑、file outbox/中央 reconcile/unknown readback/跨 generation recovery、全串流及其餘 P0–P3 仍必須完成。
|
||
- 最終驗證:API 177 passed/3 ignored、workspace Clippy all targets(`-D warnings`)、format 與 diff check 通過;Docker gate 含新增真實 file HTTP 測試通過。
|
||
|
||
|
||
## Computer file outbox(2026-09-10)
|
||
|
||
- 新增 root SQLite `file_outbox`:accepted event 與 intent 同交易,result event 與 receipt 同交易;只存 operation reference、identity、狀態與 helper receipt,不存任務檔案內容。
|
||
- Runner `file_events` 支援未確認事件重送,每批至多 100 筆/256 KiB event JSON;`ack_file_events` 按完整 computer/bot/generation scope 刪除已確認事件,重複 ack 可接受,其他 bot/generation 無法讀取或刪除。確認事件不刪除 operation receipt,因此不能使舊操作重新執行。
|
||
- 新增兩項真實 Computer 測試涵蓋 redelivery、scope isolation、ack 去重、receipt replay,以及 outbox INSERT 故障:intent transaction rollback 不啟動修改;receipt transaction rollback 在修改已發生後保留 unknown/accepted event。
|
||
- **仍待完成**:API 持久保存中央 operation→root operation/provider/generation mapping、中央 file event dedup/ack/receipt reconcile、unknown readback recovery。現有 job event loop 尚不消費 file outbox。升級前既有 file receipt 沒有可反推的原始 operation ID,不能虛構 backfill event;需利用中央 dispatch mapping 與 scoped `file_status` 補查。
|
||
- 驗證:完整 Docker gate 通過(Runner 10、root file 8、HTTP MCP 5、package 5、實際 restart/legacy migration/remove-recreate/native file/file HTTP);兩次 lifecycle verification 均確認未 ack file outbox 與 receipt 保留。fixture 已清除,diff check 通過。
|
||
|
||
|
||
## 中央 file dispatch identity mapping(2026-09-10)
|
||
|
||
- Migration 030 新增 `computer_file_dispatches`:以中央 operation FK 保存 Computer/bot/provider/runner identity/generation/root operation ID、request hash 及檔案 reference/內容 hash/size。write/patch 的 base64 內容不進 DB。
|
||
- Agent file mutation 在 HTTP 前先提交 mapping。交易鎖定 Computer 與 bot,重查 running、provider/generation、目前 binding、owner/space 與 accepted central intent;adapter actor 必須一致。HTTP 使用與 mapping 完全相同的 ComputerRef/AdapterContext snapshot。
|
||
- Mapping DB error/過期 generation 拒絕 mutation,不送 HTTP。既有相同 key 僅允許同 payload 與同 provider/generation mapping;不覆蓋舊 incarnation 的 recovery 證據。
|
||
- SQLx API integration fixture 在 HTTP handler 內查驗 mapping 已持久提交、hash/size/path 對應且不含內容;追加 stale generation 與 DB INSERT trigger fault,兩者均驗證 transport request count 不增加。
|
||
- 中央 file event 消費、dedup/ack、receipt reconcile 與 unknown readback 尚未接線;此 mapping 是恢復所需持久資訊,不能單獨宣稱斷線恢復完整完成。
|
||
- 驗證:API 177 passed/3 ignored;最終 actor guard 後定向 integration test 再通過;workspace Clippy all targets 與最終 API Clippy(`-D warnings`)、format、diff check 通過。本輪未重跑 Docker gate,未宣稱完整 API→Computer recovery E2E。
|
||
|
||
|
||
## 中央 file event 同步與 receipt reconcile(2026-09-10)
|
||
|
||
- Migration 031 新增 scoped `computer_file_events` 去重表;背景 Runner loop 依持久 dispatch mapping 輪詢 `file_events`,中央 transaction commit 後才送 `ack_file_events`,不重送檔案 mutation。
|
||
- 每筆事件核對 Computer/provider/generation/bot/root operation identity,鎖定 lifecycle 與 operation。驗證 receipt path/verified/hash 格式/8 MiB size bound;write 核對預期內容 hash/size,move 核對目的地及來源 hash,patch 接受可信任 helper 的核驗 receipt。
|
||
- Event、accepted/unknown→receipt 狀態及通知 outbox 同 transaction;已知 terminal 結果不被 unknown/舊事件倒退。同 event key 不同內容拒絕,舊 generation/錯誤 bot 不 ack。
|
||
- 修正活動紀錄去重:recovery 通知攜帶獨立 eventKey,不能被同 operation 先前的 unknown 訊息吞掉;同 recovery 通知重送仍去重。
|
||
- SQLx 測試涵蓋並行重送、亂序/terminal 不倒退、錯誤 scope、錯誤 hash/verified、通知 journal 故障整筆回滾;真實 SDK→mock HTTP fixture 確認 ack 前中央結果與事件已持久存在,ack HTTP 503 後重送不增事件/通知且不 dispatch mutation。
|
||
- 驗證:API 181 passed/3 ignored,最終 recovery Activity 定向測試、workspace Clippy all targets(-D warnings)、format/diff check 通過。
|
||
- **仍待完成**:API→真實 Supervisor→Computer 的整條失聯 E2E、原本 receipt 未提交的 unknown readback、跨 generation 授權恢復、recovery artifact metadata 補登、poll/retention 公平配額與完整 idle outbox 活性。此次證據不涵蓋這些缺項,其餘 P0–P3 繼續必做。
|
||
|
||
|
||
## 真實 API dispatcher→Supervisor→Computer file recovery gate(2026-09-10)
|
||
|
||
- 新增 opt-in SQLx integration:啟動編譯後 Supervisor binary 的實際 router/middleware,使用現有隔離 Computer、一次性 PostgreSQL 測試 DB、loopback fault proxy,呼叫正式 API `dispatch` 與 `sync_events`。
|
||
- write、patch、move 均先在真實 Computer 完成,proxy 讀到 succeeded 後丟棄回應,讓 API 留下 UNKNOWN_EFFECT。直接讀取實際檔案確認效果,再寫入模擬後續人類修改;中央收到 durable outbox 後恢復成功,同 operation 重送不再發送 mutation HTTP,後續檔案內容保留。
|
||
- 確認 3 個操作共 6 個事件提交中央並從 root outbox ack 清除。fixture Supervisor 由 guard 在成功或 panic 後 kill/wait,臨時 DATA_DIR 清除;只接受 `lazyboy-plan-runner-test-` 容器,不觸碰正式 runtime。
|
||
- `make test-runner-jobs` 納入此 gate 並要求明確設定 disposable PostgreSQL `DATABASE_URL`;新增前置檢查與使用說明,缺少 DB 設定時在建立容器前停止。
|
||
- 驗證:完整 Docker gate 通過,含 Runner 10/root file 8/HTTP MCP 5/package 5/restart/migration/recreate/native file/file HTTP,以及新增三種檔案 mutation 的真實跨程序恢復測試;workspace Clippy all targets、format/diff check 通過,測試容器及測試 Supervisor 已清除。
|
||
- 此處入口是正式 API dispatcher,非公開 HTTP API/model run;root receipt 未提交的 unknown readback、API/Supervisor/Computer 同時 crash 矩陣、跨 generation recovery、artifact 補登與其餘 P0–P3 仍未完成。
|
||
|
||
## File receipt artifact 補登與 direct/recovery race(2026-09-10)
|
||
|
||
- 移除原本只處理 write、忽略 DB error 的 artifact 寫入;write/patch/move 的正常回應先驗證可信 receipt,再以已持久 dispatch 的 provider/generation/operation scope 登錄 metadata。失敗回 UNKNOWN_EFFECT,由 durable outbox 恢復。
|
||
- 背景 receipt reconcile 在事件/結果/通知同一 transaction 補登 artifact。operation row lock 序列化正常回應與 recovery;新 artifact ID 由完整中央 operation key 的 SHA-256 決定,既有相符 random ID 保留,內容不符拒絕,不忽略錯誤。
|
||
- 修正 recovery 先完成時,稍後正常成功回應被判成 unknown:僅在同 operation 已有可信 file event、中央 terminal status 與本次結果一致時接受既有提交,不建立第二筆通知。
|
||
- SQLx 新增 artifact INSERT 故障後不完成事件/不 ack、通知故障連 artifact 一起 rollback、正常回應與背景 recovery 並行只登錄一筆,及較晚 direct success 沿用已持久結果/通知。
|
||
- 真實 dispatcher→Supervisor→Computer 失聯 gate 擴充:write/patch/move 恢復後核對 artifact path/原操作 hash/size,每個 operation 僅一筆;後续人類修改不取代歷史 receipt metadata,重送不重新修改檔案。
|
||
- artifact immutable snapshot/retention/全串流、unknown readback、跨 generation 恢復與其餘 P0–P3 仍待完成。
|
||
- 驗證:API 183 passed/4 ignored;完整 Docker gate(含新增 artifact 回復驗證)通過,workspace Clippy all targets(-D warnings)、format/diff check 通過;測試容器清除。最後 direct/recovery completion 修正由完整 API suite 驗證。
|
||
|
||
## File event 錯誤隔離(2026-09-10)
|
||
|
||
- 修正單一 bot 的 event/ack error 直接中止整台 Computer 同步:保留錯誤回報與重試需求,仍繼續處理後面的 bot。
|
||
- 同一批資料逐事件處理;identity/receipt 驗證失敗的事件不提交、不 ack,其他有效事件仍持久提交並送確認。批次仍回傳失敗,不能把未處理事件冒充已交付。
|
||
- 擴充 HTTP fixture:在有效 accepted/succeeded 前插入錯 bot 事件,確認只 ack 正確的兩個 ID;再讓 ack 回 HTTP 503,確認後面的健康 bot 仍被輪詢,下一次重送不重複中央紀錄。
|
||
- 驗證:file_operations 6 項 SQLx 測試與 API Clippy all targets(-D warnings)通過。本輪不冒充完整 Docker gate;大量 scope 的輪詢排序/timeout 公平性、100 筆壞事件占滿 batch 時的 quarantine、全系統資源配額仍未完成。
|
||
|
||
## 持久 file poll 排程與 timeout 進度(2026-09-10)
|
||
|
||
- Migration 032 新增每 computer/provider/generation/bot 的 poll scope、輪詢時間與租約。dispatch INSERT trigger 同交易建立 scope,並 backfill 既有 dispatch;不因新操作把同 bot 的順序重設到最前面。
|
||
- SQL `FOR UPDATE SKIP LOCKED` 原子取得最久未輪詢 scope,IO 前更新 last_polled。每次同步最多 4 個不同 scope、每個 timeout 2 秒;慢 bot 不會反覆占滿整輪。30 秒租約讓多個 API worker 協調,API 中斷後可重新取得;舊 token 不能釋放後來的新租約。
|
||
- 每累積 16 個中央已提交事件就送 ack,批次結束確認餘數;後段 timeout 不必讓前段永遠無法確認。壞事件仍不 ack,中央未知結果仍不重播 mutation。
|
||
- 新增 SQLx:並行 claims 不重複、先前已輪詢 scope 排到其餘 peer 後面、expired lease takeover/stale release/wrong generation;真實 SDK HTTP 慢 bot timeout 後健康 peer 繼續,以及 ack 前中央僅完成該 prefix 的分段順序測試。
|
||
- 驗證:API 185 passed/4 ignored、完整 Docker gate 含真實 file recovery 通過;最後分段確認變更後 9 項 file_operations 測試、workspace Clippy all targets(-D warnings)、format/diff check 通過。Docker fixture 清除。
|
||
- 此輪完成檔案回執輪詢的持久順序與有界等待;過期 scope retention、滿頁壞事件 quarantine、全系統工作/CPU/RAM/disk 公平配額,以及其他 P0–P3 仍未完成。
|
||
|
||
## Root unknown file readback 證據(2026-09-10)
|
||
|
||
- `file_status` 新增嚴格 boolean `readback`;只能按既有 identity+operation ID 讀取受保護 metadata 指向的檔案,不能自行提供任意路徑。僅 unknown 操作執行 readback,已有 terminal receipt 仍直接回放。
|
||
- 新增 UID 1000 fingerprint helper:descriptor-relative/no-follow、regular file、8 MiB 限制、directory shared lock、讀取前後 inode/size/mtime/ctime 及路徑綁定核對,只回傳 hash/size,不回傳 task bytes。不可讀/symlink/超量變為 unavailable,不誤認目標已符合。
|
||
- Root `file_observations` 每 operation 保留最近一次 metadata-only 觀察;write 核對 desiredHash,move 提供來源缺失/目的地 hash 的分開 snapshot。始終 `execution_proven=false`,操作保持 unknown,不能把目前相同內容當作原操作的執行證據。patch 缺少完整目標 hash,desired_state_matches 保留 null。
|
||
- 真實 root receipt UPDATE trigger fault 後驗證 readback match、不同 bot/generation 拒絕、root 持久觀察、後續人類修改 mismatch、重送仍不改寫檔案。helper 新增 binary/特殊路徑、內容不外洩、symlink 拒絕、1 GiB sparse file 有界拒絕測試。
|
||
- 驗證:25 項 helper tests、完整 Docker gate(含 root file 8 項與 dispatcher 真實恢復)、workspace Clippy all targets、diff check 通過;fixture 清除。
|
||
- **仍待完成**:API 對此觀察的查詢/恢復決策、patch pre-effect 完整目標 fingerprint、unknown→已核驗目標狀態的明確語義、跨 generation 恢復。此輪是 readback 證據 backend,不能列為完整 unknown recovery;其餘 P0–P3 保持必做。
|
||
|
||
## 授權 file receipt/readback API(2026-09-10)
|
||
|
||
- 新增 `POST /api/bots/{id}/file-operation`,body `{ "operationId": "原操作 ID", "readback": true }`;沿用既有登入/路由授權,拒絕未知欄位與空 ID。readback 省略時只取 durable receipt。
|
||
- 從中央 dispatch mapping 取得 root operation ID,核对 bot user/space、目前 Computer binding/provider/generation/running,無匹配回 404,不發出 Runner request。查詢使用該 bot/generation 的 `file_status`,不派發 mutation。
|
||
- helper 回來後重查 lifecycle/binding/owner;查詢中已更換 Computer/generation 回 409。25 秒 deadline,狀態/code 與 64 KiB 回應限制;原始 stderr 不回傳。
|
||
- 回傳 root status 與 evidence,不寫中央 operation status,不把 desired_state_matches 自動當成 execution_proven。此 API 是只讀核驗入口,尚未接 Tool schema/UI 或明確的恢復決策。
|
||
- 驗證:SQLx+SDK HTTP 測試涵蓋同 owner 查詢、不同 user 無 HTTP、執行中 generation 變更拒絕、舊 generation 事前拒絕、中央 unknown 保留;API Clippy all targets(-D warnings)、format/diff check 通過。本輪不冒充完整 HTTP登入→Computer E2E。
|
||
|
||
## Agent file_operation_status 工具(2026-09-10)
|
||
|
||
- 新增 `file_operation_status` schema/dispatch,參數 `targetOperationId`、可選 boolean `readback`;目標 operation ID 與這次查詢 ID 分開,不重新 reserve mutation intent。
|
||
- API handler 與 agent 共用 file_status lookup 的 owner/binding/provider/generation 核對與 timeout。列入 native sandbox readiness,不需要 GUI/vision;run 指引引導 unknown file result 先查 receipt,不自動重做。
|
||
- write/patch/move 的失敗回覆新增結構化 operationId/errorCode/message,使 agent 能取得實際原操作 ID 執行 readback;保留既有 typed error code。
|
||
- 擴充 SQLx HTTP fixture,以正式工具 dispatch 查詢既有 unknown 操作,確認保留 unknown、execution_proven=false、中央 operation 筆數不增加;native routing/唯讀判定與 unknown 回覆原 ID 另有測試。
|
||
- UI 顯示、明確恢復決策與 unknown→verified 狀態語義、patch 完整目標 fingerprint、跨 generation 恢復及其餘 P0–P3 尚未完成。
|
||
- 驗證:完整 API 187 passed/4 ignored;最終 operation ID 回覆定向測試、workspace Clippy all targets 與最終 API Clippy(-D warnings)、format/diff check 通過。本輪未重跑真實 Docker gate。
|
||
|
||
## 活動紀錄 File Evidence UI(2026-09-10)
|
||
|
||
- run activity payload 補上經 owner 查詢取得的 botId,使既有歷史 tool entry 也能定位目前授權 bot。write/patch/move 活動列有 operationId 時顯示「核對檔案結果」。
|
||
- 按鈕只呼叫既有 `POST /api/bots/{botId}/file-operation` 並帶 readback=true,不送 mutation/retry;分別呈現歷史已核驗 receipt、目前符合/不同、證據不足、操作不可用/generation 變更/查詢失敗。繁中與英文文案均不把 desired_state_matches 當成原操作已成功。
|
||
- 顯示 receipt 或觀察的檔案路徑;React 文字渲染處理特殊字元。請求期間禁用按鈕,關閉面板 abort 並忽略遲到回應;鍵盤 Enter/Space 不被外層 RunProbe 攔截,Escape 仍可關閉。
|
||
- 前端測試新增狀態文案真值表與按鈕 API/取消/late response/鍵盤事件驗證。58 項 frontend tests、TypeScript、Vite production build 通過;API Clippy all targets 在停用 incremental cache 後通過(初次檢查遇到 rustc incremental ICE,並已修正 RunRow 所需 bot_id 欄位)。diff check 通過。
|
||
- 尚未做瀏覽器視覺/登入→真實 Computer 點擊 E2E;明確恢復決策、unknown→verified 語義、patch 完整目標 fingerprint、跨 generation 恢復與其餘 P0–P3 仍待完成。
|
||
|
||
## Patch pre-effect 完整目標 fingerprint(2026-09-10)
|
||
|
||
- Computer helper 新增內部 `patch-plan`,沿用相同 byte range/base64/SHA-256 CAS/8 MiB 驗證,僅計算完整 patched bytes 的 hash/size,不建立暫存檔或修改原檔。
|
||
- Root 先提交 accepted intent,再執行只讀 plan;驗證 plan 的 path/precondition/hash/size,將 desiredHash/desiredSize 持久寫入受保護 metadata,提交成功才啟動真正 patch。metadata commit failure 不發生 file mutation。
|
||
- 真正 patch 再次驗證原檔 hash;plan 與 mutation 間的 concurrent edit 回 CONFLICT,不換用新 base。final receipt 額外核對 durable desiredHash/desiredSize,結果不符保留 unknown。
|
||
- 新 patch 的 unknown readback 現在可核對完整目標 hash;舊 intent 沒有完整 fingerprint 時仍回證據不足。desired_state_matches 仍不等於 execution_proven,不自動清除 unknown。
|
||
- helper 測試驗證 plan 唯讀、完整 binary 結果 hash/size、計算後人類修改被 CAS 拒絕;真實 root 測試注入 plan metadata commit fault 與 final receipt commit fault,驗證不提前修改、完整 readback、後續人類修改與禁止自動 replay。
|
||
- 驗證:26 項 helper、9 項 root file、完整 Docker gate(Runner/MCP/packages/lifecycle/native file/真實 API dispatcher recovery)及 workspace Clippy all targets(CARGO_INCREMENTAL=0,-D warnings)、diff check 通過;fixture 清除。
|
||
- 明確恢復決策仍需 child quiescence/執行來源等證據,不可只靠當前 hash 清除未知效果;跨 generation recovery、完整隔離/資源配額與其他 P0–P3 尚待完成。
|
||
|
||
## File child handoff 與 quiescence 證據(2026-09-10)
|
||
|
||
- 每個新 file intent 保存 controller PID/start/boot 與 handoff_required;root 用 Popen 啟動可信 mutation helper,先持久提交 child identity,才傳送交接與 payload。plan-only helper 不修改檔案。
|
||
- Helper 在開啟父目錄/建立路徑/修改 bytes 前,要求完整 handoff、payload byte length 與 SHA-256;missing/partial/extra payload 全部拒絕。root 中斷傳輸時不能把截斷但仍合法的 base64 當成完整檔案。
|
||
- child identity commit failure 會 kill/wait 尚未獲准的 helper,不發生檔案 mutation;timeout/例外清理同樣回收 owned child。已接受但結果未知的操作仍禁止 replay。
|
||
- Unknown readback 新增 helper_quiescent:相同 boot 下 PID/start 相符且存活回 false,已退出/zombie/原 PID 已被其他 start 重用回 true;沒有舊 handoff 證據、無法讀取程序資料、boot/namespace 不同回 null。不同 namespace 不足以證明舊容器已停止。
|
||
- 真實測試新增 child identity commit fault 不建立任何目錄;實際 kill controller、child 等待目錄鎖仍判存活,釋放鎖後完成但 receipt 遺失,readback 判 child 已停止且仍保持 unknown。另新增 handoff 缺失/payload 截斷/extra bytes 拒絕測試。
|
||
- 驗證:27 項 helper tests、11 項 root file tests、完整 Docker gate 含 lifecycle/真實 dispatcher recovery 通過;最後 boot 證據收緊另有 2 項 file-process-proof tests(已接 gate)、workspace Clippy all targets 與 diff check 通過。fixture 清除。
|
||
- 此處只證明可信 file mutation helper 是否仍可繼續寫入,不宣稱全 task process tree 隔離;明確的 unknown 恢復決策、跨 generation 舊容器退役證據、全系統配額及其他 P0–P3 尚未完成。
|
||
|
||
## Readback metadata/lifecycle 競態修復(2026-09-10)
|
||
|
||
- 覆查發現:status 先 SELECT controller-only metadata,controller 隨後提交 child identity 並退出時,舊 snapshot 可能錯把仍存活的 child 判為 quiescent。現在確認 controller 已退出後,再讀一次受保護 metadata 並核對 child;此後 controller 已不能再提交新的交接。證據仍維持 unknown,不重送 mutation。
|
||
- 新增 SQLite 回歸測試,在 status 已讀取 metadata 後、程序身分觀察時提交 child identity,重現這個順序;驗證回覆及持久 observation 都是 helper_quiescent=false,即使當前檔案 hash 已符合目標。
|
||
- API readback 的第二次 lifecycle 檢查補上 running state;查詢途中 Computer 停止時回 409,後續查詢回 404 且不再呼叫 Runner。既有 owner/generation 檢查與 unknown 保存測試仍通過。
|
||
- 驗證:3 項 process-proof tests、完整 Docker gate(10 Runner、11 root file、5 HTTP MCP、5 package,以及 restart/migration/remove-recreate/native files/Supervisor HTTP/實際 API dispatcher 遺失回應恢复)通過;API owner/lifecycle/generation 定向 SQLx 測試、workspace Clippy all targets(CARGO_INCREMENTAL=0,-D warnings)與 diff check 通過。自建測試容器已清除。
|
||
- 對應 O03/O41/O45、T17/T24 的恢復證據正確性;兩模式共用相同 root helper 與 API 驗證路徑。本輪未變更 schema,尚未部署;回退此修復會重新引入已重現的錯誤判定。
|
||
- 下一步仍須完成明確 unknown 恢復決策、跨 generation 退役證據與 outbox 全頁異常事件的公平補送;P0–P3 全案尚未完成,不把此回歸測試當成完整驗收。
|
||
|
||
## File outbox 有界掃描與公平續送(2026-09-10)
|
||
|
||
- 修正整頁 retained failure 永遠遮住後續有效 receipt 的問題。新增 migration 033,為每個 Computer/provider/generation/bot poll scope 持久保存 scan_after/scan_through;每輪固定上限,完成後回頭重試舊事件,不因持续追加而永久跳過失敗資料。
|
||
- Root file_events 支援嚴格整數 cursor,仍以完整 identity 查詢;每次最多 100 筆/256 KiB。從 SQLite 只讀取每筆最多 128 KiB+1 的 bytes,損壞 JSON、非文字、過大、NUL 尾碼及無效 payload 回 file.invalid 參照;不刪原始列,也不回傳損壞內容。Unicode escaping 擴大後仍受單筆上限約束。
|
||
- API 驗證 cursor、嚴格遞增 event ID 與固定 snapshot 上限;在每筆處理前持久 checkpoint,lease token/期限不符即拒絕。不能只在整頁前推 cursor,否則單頁第一筆慢 transaction 仍會餓死後面的事件。
|
||
- 只有中央 event/receipt/artifact/notification transaction 成功提交的 ID 才 ack;cursor 不是 ack。timeout、取消、ack 遺失或拒絕處理的事件保留並於下一輪 snapshot 重訪。旧 Runner 的空 stdout 仍走既有補送行為,完整分頁能力需新版兩端。
|
||
- 新增 root SQLite 測試:100 筆損壞資料後的有效 receipt 仍可讀取;途中新增事件等下一輪;失敗列留存、跨 bot 拒絕、cursor 型別/範圍拒絕、blob/深層陣列/大資料/NUL 不阻塞。
|
||
- 新增 API SQLx+HTTP 測試:整頁 poison 後有效結果完成且僅 ack 已提交 ID;第一筆資料庫 pg_sleep 超時,transaction 回滾但下一筆下輪仍成功;cursor 越界/跳號/逆序與 snapshot 變更拒絕。既有失去 ack、中央回滾、artifact 去重、peer 公平測試維持通過。
|
||
- 驗證:file operations 12 項定向測試、完整 API 191 passed/4 ignored、最終 root scan 測試及完整真實 Docker gate(Runner、file、MCP、package、lifecycle、HTTP、API dispatcher receipt recovery)通過;workspace Clippy all targets(CARGO_INCREMENTAL=0、-D warnings)與 diff check 通過,自建容器已清除。最後另補 lease 過期 worker 不得修改 replacement cursor 的定向回歸。
|
||
- 對應 O03/O30/O41/O45、T17/T18/T31。Team/Dedicated 沿用相同按 bot scope 的掃描,不修改兩模式共享策略。未部署;回退需保留 root outbox/receipt 及 migration 033 的游標欄位,舊 API 可忽略欄位,不能刪失敗事件來假裝恢復。
|
||
- 下一步:明確 unknown 恢復決策、跨 generation 舊 runtime 退役證據、outbox/scope 長期保留與全系統公平資源配額。其餘 P0–P3 尚未全部完成。
|
||
- 最終補充:lease replacement 定向測試已通過。gate 後覆查再加入 JSON transport canary(NaN/無窮值/超過 i64 的整數/孤立 surrogate),避免 Python 接受但 Rust 拒絕整批 JSON;root scan 測試已覆蓋並通過這些案例,保留異常事件參照,不回傳原始內容。此最後解析收緊由直接 SQLite 回歸測試驗證,未再重跑完整 Docker gate。
|
||
|
||
## 同 generation 檔案 unknown 明確恢復(2026-09-10)
|
||
|
||
- `file_status` 新增顯式 resolve=true(必須同時 readback=true),同樣使用原 operation/bot/Computer/generation 的受保護 mapping。只有 helper 已停止、controller 已停止、目前檔案後置條件符合時才完成恢復;缺舊 handoff、不同 boot、程序仍存活、hash 不符、讀取或 fsync 失敗都維持 unknown,不重播 mutation。
|
||
- 恢復會重新讀取並 fsync 檔案與父目錄;move 改以 fingerprint-pair 同時持有兩端目錄 SH locks,讀取 target 後再核對 source。這是 cooperating file writers 下的後置條件核驗,不冒充任意 shell 的強隔離或原 rename 的因果證明。
|
||
- Root 以單一 SQLite transaction 更新 receipt/operation 及 outbox;重複或並行恢復先檢查既有 terminal receipt,不新增重複完成事件。恢復紀錄使用 verificationBasis=postcondition、executionProven=false,並保存 helper/controller quiescence;succeeded 在此表示明確恢復的後置條件已核验,不能推論原操作曾成功執行或 exactly-once。
|
||
- API owner/current binding/generation/running fence 同樣適用 resolve。`file_operation_status` 工具 schema/dispatcher 已接旗標;中央 outbox reconcile 驗證上述 proof 欄位並在公開結果保留 verificationBasis/executionProven,沿用 artifact/notification 同交易提交。中央狀態透過既有背景補送收斂,不把 root 回覆當成中央 transaction 已完成。
|
||
- UI 在 unknown、目前符合且兩個程序均停止時提供「重新核驗並完成恢復」;點擊仍重新做後端核驗,使用獨立文案標明後置條件恢復與歷史執行未證實。繁中/英文、請求旗標與 controller 存活時不顯示按鈕均有回歸驗證。
|
||
- 真實 root 測試涵蓋 write/patch/move 的 original receipt commit fault、hash 被人修改時保持 unknown、resolve receipt transaction failure 不發成功事件、重複恢復不重送事件、後續 replay 不覆寫人工修改。Host process-proof 測試額外驗證 child/controller 任一存活均不能完成恢復;helper 測試驗證兩目錄鎖及 fsync fault 無成功輸出、不改 bytes。
|
||
- 實際 API dispatcher→Supervisor→Computer gate 新增原 root receipt 提交失敗,再透過 status/readback/resolve 工具恢復、中央 outbox 登錄 artifact、原操作 replay 保留 proof basis 且不改人工內容;這仍不是登入/模型/瀏覽器 UI 的全程 E2E。
|
||
- 驗證:29 helper tests、3 process-proof tests、12 root file tests、58 frontend tests、TypeScript/Vite production build、完整 API 192 passed/4 ignored、完整 Docker gate(含新增實際恢復)、workspace Clippy all targets(CARGO_INCREMENTAL=0、-D warnings)及 diff check 通過;自建容器已清除。最後新增 fsync fault 測試另行通過,沒有變更 gate 後的產品程式。
|
||
- 對應 O03/O41/O43/O45、T17/T18/T24/T35。兩模式保留,同樣依 bot scope 處理;本輪不新增 schema、不部署。回退須保留已建立的 postcondition receipt/outbox/中央紀錄及其語義,不能抹除恢復歷史或退回會遺失 proof basis 的版本。
|
||
- 下一步仍有跨 generation 的舊 runtime 退役證據、舊 intent 恢復、其他檔案寫入路徑 journal、immutable artifact 與串流、完整隔離/scope barrier/資源配額、Tool Manager/OAuth/mail adapters,以及尚缺的 P2/P3 backend 與基準測試。全案保持未完成。
|
||
|
||
## 退役證據與 artifact 核驗來源持久化(2026-09-10)
|
||
|
||
- 新增 migration 034:computer_generation_retirements 保存 Computer/generation/provider_ref/home_key/唯一 proof_id/provider_destroy_ack 及時間。只有 destroy 成功回覆且 generation/provider CAS 仍相符時,才與 generation 退役、jobs interruption 同交易寫入;不根據 changed provider/changed namespace/單純 generation 增加回填證據。
|
||
- SQLx 故障驗證涵蓋 destroy 失敗不留證據;proof INSERT 失敗不退休 generation/jobs;proof 已寫入後 job UPDATE 失敗會回滾 proof、generation 與 job;stale ack/providerless active jobs 拒絕;一般 provider 切換不憑空建立第二筆 ack。這是中央持久證據基礎,尚未拿來自動解除跨 generation unknown。
|
||
- Artifact 新增 verification_basis,明確區分 execution_receipt、postcondition 與 legacy_unspecified;既有資料保留未知來源,不因 migration 推測歷史執行。可信 receipt 與舊 path/hash/size 完全相符才補登來源;已有來源不允許被不同 receipt 改標。
|
||
- Catalog API 傳回 verificationBasis,新增 owner scope 與三種來源的實際 handler/PostgreSQL 測試。中央 receipt parser 同時拒絕帶 recovery proof markers 卻缺 verificationBasis 的資料,避免默認成原執行 receipt。
|
||
- Artifact 回歸測試驗證舊 ID 保留、可信 postcondition 來源補登,以及來源衝突時不提交 event;實際 API dispatcher/Supervisor/Computer recovery gate 驗證一般 write/patch/move artifact 為 execution_receipt,root receipt 遺失後明確恢復的 artifact 為 postcondition。
|
||
- 驗證:完整 API 193 passed/4 ignored;最後 parser/artifact 14 項定向測試、catalog handler 測試及擴充的 retirement 交易回滾測試通過;完整 Docker gate 含實際恢復/artifact 來源驗證、workspace Clippy all targets(CARGO_INCREMENTAL=0,-D warnings)、diff check 通過。後補 retirement 測試未改產品程式;自建容器已清除。
|
||
- 對應 O28/O41/O43/O45、T15/T17/T18。兩模式共用相同持久證據與 scope;未部署。回退需保留 migration 034 的退役歷史及 artifact 核驗來源,不可把 legacy/postcondition 統一改標成 execution_receipt。
|
||
- 下一步:安全地將退役 acknowledgement 與舊 root journal/home_key/目前授權綁定,接通跨 generation readback/明確恢復;不能只因中央多一筆 proof 或容器名稱改變就跳過程序停止證明。其餘未完成 P0–P3 保持原範圍,並非已完成跨 generation 驗收。
|
||
|
||
## Artifact 授權 fence 與 Computer 內 verified read(2026-09-10)
|
||
|
||
- 修正下載只查 artifact 擁有者、卻不查 bot 目前綁定的缺口。讀取前要求同 owner/space、current Computer binding、running/provider;Team 路徑需精確落在 own workspace/shared,Dedicated 保留 rooted relative path;非法 path/hash/size 不開始 transport。
|
||
- 讀取完成後重查 artifact 仍存在且 path/hash/size 未換、owner/binding/provider/generation/state/scope/home_key 仍相符,改綁、停機、generation 變更、產物撤銷或 storage scope 變更皆不回傳 bytes。請求攜帶 bot/generation context,25 秒 deadline;後端錯誤以固定訊息回覆,不把任意 backend text 洩出。
|
||
- 下載標頭改為 UTF-8 percent-encoded filename*,安全處理中文、引號、分號、CR/LF;加入 private/no-store 與 nosniff。內容型別維持 octet-stream。
|
||
- 新增 SandboxProvider::read_file_verified、Supervisor /read-verified 與 Computer helper read-verified。helper 在 Computer 持目錄 SH lock,讀取有界 binary bytes,比對 expected SHA-256/size 後才輸出;不符合、特殊檔案、symlink 或無效條件不輸出內容。API artifact handler 不再計算檔案內容 hash。
|
||
- SDK 核對 verified/path/hash/size/base64 及解碼長度,拒絕缺 receipt、錯 path/hash、截斷、UTF-8 fallback、過大回覆;transport 分 chunk 收集,依 expected_size+有界 metadata 限制 JSON 大小。這仍是最多 8 MiB 的有界 buffer 下載,不是完整大型檔案串流。未知 provider 不用 API-side hashing/分開 stat/read 作假 fallback。
|
||
- PostgreSQL+實際 HTTP fixture 覆蓋 Team/Dedicated binary 下載、owner 拒絕、14 種傳輸途中 lifecycle/ref/receipt/response 異常、跨 bot/path traversal 拒絕、size 不符與 header escaping;最初測試中的私人模式 fixture 名稱已修正為實際 enum dedicated。
|
||
- helper 新增 1 MiB binary verified read、hash/size/type 拒絕、symlink、空檔與後續人工修改不洩 bytes;實際 API SDK→Supervisor→Computer gate 增加正常與修改後 CONFLICT、最終 1 MiB binary verified roundtrip。
|
||
- 驗證:30 helper tests、3 artifact 定向測試(含最終 14-case guards)、完整 API 196 passed/4 ignored、完整真實 Docker gate 含 1 MiB verified transport、workspace Clippy all targets(CARGO_INCREMENTAL=0,-D warnings)及 diff check 通過。最終 SDK guard 更新已做定向 HTTP 回歸與最終 Docker gate;不冒充登入/瀏覽器下載的全程 E2E。
|
||
- 對應 O03/O43/O45、T06/T09/T10/T21/T34/T64。未部署;API/Supervisor 需一起更新才能使用 verified read,舊 provider 維持拒絕而非降級繞過核驗。本輪無新 schema,不修改現有 artifact bytes;自建容器已清除。
|
||
- 下一步仍有 immutable artifact 快照/保留策略、upload/download/attachments 真正串流、其他 task file processing 路徑,以及跨 generation 恢復的完整可信證據鏈;其餘 P0–P3 不縮減、不標成已完成。
|
||
|
||
## Computer 內不可變 file artifact 快照(2026-09-10)
|
||
|
||
- 新的 journaled write/patch/move:可信 UID 1000 helper 在 mutation/readback 核驗及目錄鎖內保留結果 bytes,僅透過 child→root 私有 pipe 帶 snapshotContentBase64。root 比對 hash 後將 bytes 寫入 /var/lib/lazyboy-runner/artifacts,移除 payload 欄位才發布 receipt/outbox;中央 ledger/catalog 不存 task contents。
|
||
- 快照以 SHA-256 命名、root-owned 0600/目錄 0700;用 private temp、fsync、no-clobber link 與目录/父目錄 fsync 發布。受保護 lock 序列化儲存與容量檢查;相同 hash 重用前核對 bytes/ownership/hash 並再次 fsync,避免前次 sync 失敗後 dedup 誤報成功。
|
||
- 原操作結果未知而明確做 postcondition recovery 時,先再次由 Computer read-verified 讀取符合 hash/size 的 bytes,保存快照後才提交 recovered receipt。快照寫入/容量失敗不能發布成功;原工作檔案可能已變更,此時保留 unknown,清出容量後仍須明確恢復。
|
||
- 每個 store 暫以 256 MiB/4096 entries 有界;orphan/pending files 也計入。這是容量上限,不是完整 retention/自動 GC;舊 receipts 缺 snapshotRef 不捏造已保存 bytes,繼續顯示 legacy artifact 行為。
|
||
- 新 file_artifact 管理操作必須以同 bot 的原 Computer identity/generation/operation 找到 succeeded receipt,且 artifactRef/hash/size 相符才能讀取 root blob;知道 hash 不構成授權。固定 helper 重新核對 blob,不接受檔案系統路徑。已成功的歷史快照可在目前較新 generation 中讀取,但不因此解除任何舊 unknown mutation。
|
||
- migration 035 保存 snapshot_ref;正常/receipt recovery 同交易登錄,保留驗證來源與 legacy ID。Catalog 明示 immutable;API 用 durable dispatch mapping 取得 origin,走專用 Supervisor /artifact 與有界 SDK verified response。缺 origin 或壞快照拒絕,不退回已修改工作檔案;既有 owner/binding/lifecycle fence 及 snapshot_ref 重查持續生效。
|
||
- Root 測試覆蓋普通及 recovered snapshot、原檔後續人工修改、錯 bot 拒絕、歷史 generation read、UID 1000 直接讀取拒絕、receipt/outbox 不含 base64 payload;容量滿維持 unknown,移除 fixture padding 後明確恢復。獨立受保護 store 測試覆蓋 inode dedup、tamper/symlink 拒絕、fsync 故障後再次拒絕及成功重試。
|
||
- 實際 lifecycle gate 新增 restart/legacy migration/remove-recreate 後讀回 snapshot bytes;實際 API dispatcher→Supervisor→Computer 驗證 write/patch/move 在人類修改後仍讀回當時產物,錯 bot 無法讀取。API handler+HTTP fixture 驗證跨 generation immutable download 只取 durable origin,missing mapping 不走 workspace fallback。
|
||
- 驗證:197 API passed/4 ignored、13 root file tests、2 protected artifact tests、原有 30 helper tests、完整 Docker gate(含 snapshot lifecycle/recovery)、workspace Clippy all targets(CARGO_INCREMENTAL=0,-D warnings)與 diff check 通過。最後 download row 改具名 struct 修正 lint;artifact read 失敗時固定錯誤文字,避免部分 stdout bytes 被放進 control-plane error。自建容器已清除;沒有部署。
|
||
- 對應 O03/O41/O43/O45、T09/T17/T18/T21/T34/T64。兩模式仍以同 bot receipt 授權;root-private storage 跟隨既有 runner 持久掛載。回退必須保留 snapshots、snapshot_ref、原 receipts 與 provenance;不能將已有 immutable reference 降級為 live path 或刪掉 blob 假裝回滾。
|
||
- 尚缺:完整 per-agent quota/retention/GC、全工具輸出 artifact、真正大型檔案/附件串流、跨 generation unknown 恢復可信證據鏈,以及仍列在 audit 的其餘 P0–P3。沒有將全案或 O43 全列宣告完成。
|
||
|
||
|
||
## 快照發布程序死亡後的暫存檔回收(2026-09-10)
|
||
|
||
- 修正 root 快照 publisher 被強制終止後,遺留 `.pending-<32 lowercase hex>` 永久占用容量的問題。新 publisher 先取得同一 artifact-store.lock,再做有界 inventory;此時前一 publisher 已釋放核心鎖,才可移除符合精確暫存命名且 root-owned/private/regular 的檔案。
|
||
- 不刪除已發布 SHA-256 名稱或其他未知名稱;發布後、unlink 暫存檔之前死亡的 hardlink 只移除 pending link,保存原 published inode。symlink/不安全 ownership 或 mode 拒絕處理。清理後 fsync 目錄,失敗不發布成功;重試仍須完成正常 snapshot durability。
|
||
- 6 項 protected artifact tests 通過,新增實際子程序在 file fsync/link 後被 kill、發布 inode 保留、活 publisher 持鎖時清理等待、256 MiB orphan quota 回收、不認得的 pending-like 檔案仍保留並計入 quota,以及 cleanup fsync 失敗/重試。
|
||
- 完整真實 Docker gate 通過:含 13 root file tests、Runner/HTTP MCP/package 原有測試、restart/migration/remove-recreate、native file transport、Supervisor HTTP 與實際 API recovery。gate 執行時包含前 5 項 protected artifact tests;其後新增的第 6 項 fsync fault test 已獨立通過,產品程式未再變更。Supervisor all-targets Clippy(CARGO_INCREMENTAL=0、-D warnings)通過。
|
||
- 對應 O43/T18/T64;只回收不能被活 publisher 擁有的未完成發布暫存檔,不是已發布 artifact 的 retention/GC。沒有新增 schema/部署;回退不得刪除 published blobs。全工具 artifact、per-agent quota、published retention、串流與其餘 P0–P3 仍需完成。
|
||
|
||
|
||
## File journal 准入容量與歷史 receipt 保留(2026-09-10)
|
||
|
||
- 修正所有 file_operations(包含成功/失敗終態)累積 1024 筆後永久拒絕新操作的限制。仍保留每筆 request digest/receipt/unknown intent;不靠刪除歷史 IDs 釋出名額,避免過期重送變成第二次 mutation。
|
||
- 新准入在原 BEGIN IMMEDIATE 中執行:accepted/unknown 合計最多 1024 個未完成名額,status index 支援查詢;另按 SQLite page_count−freelist_count 乘 page_size,要求 128 MiB 使用頁面預算內尚有 64 KiB 准入餘裕。已完成紀錄不占未完成名額,仍占真實 metadata 空間。
|
||
- 既有操作的 digest/receipt replay 檢查先於准入限制;file_status/receipt completion/明確 recovery 不走新 intent 准入。SQLite 空頁可再用,因此刪除已允許回收的其他資料後不必 VACUUM 才能恢復准入。此預算計入同一 Runner DB 的其他表,不冒充 per-agent 公平 quota、WAL 上限或整機硬磁碟配額;持續增長到容量上限仍須 retention/管理政策,未丟棄 anti-replay 證據。
|
||
- 3 項新 SQLite 測試通過:accepted+unknown 名額及終態釋放、不刪 receipts;實際 128 MiB zeroblob 壓力拒絕新 intent/outbox,原 replay/status 保留,釋放後重用 freelist;兩連線競爭最後一個名額僅一方成功。
|
||
- 真實 root file gate 改為先以 1024 accepted fixtures 驗證拒絕,再保留同 1024 rows 改成終態,驗證新 file mutation 成功;人類改檔後重播原 receipt 不再覆寫。完整 Docker gate 通過,含 13 root file tests、6 protected artifact tests、Runner/HTTP MCP/packages、restart/migration/remove-recreate、Supervisor HTTP、實際 API dispatcher recovery。gate 包含前 2 項 SQLite tests;最後追加並行名額測試另行通過,產品程式未再變更。
|
||
- Supervisor all-targets Clippy(CARGO_INCREMENTAL=0、-D warnings)通過。HTTP MCP fixture 曾在 client 關閉 GET 後記錄 BrokenPipeError;已檢查來自 fixture 的空 405 reply,5 項 MCP assertions 及整體 gate 均通過,不記為產品失敗或額外修正。
|
||
- 對應 O03/O41/T18;未部署,沒有中央 migration。root SQLite 新 index 為相容變更;回退要保留既有 receipt,舊版仍會恢復 lifetime count 限制。其餘 P0–P3、完整 journal/artifact retention、公平配額與串流仍未完成。
|
||
|
||
## 每個 bot 的 file journal 未完成額度(2026-09-10)
|
||
|
||
- 在既有全機 1024 個 unresolved 名額與 SQLite 使用頁面准入預算之外,加入每 bot 256 個 accepted/unknown 上限,滿額回 FILE_AGENT_QUOTA。以 journal identity 中的 stable bot_id 累計,跨 provider/generation 共用額度,不能藉重建重置未知操作負債。
|
||
- 計數與 intent INSERT 共用原 BEGIN IMMEDIATE;查詢最多處理全機准入上限內的 identity groups。既有 receipt replay 仍先於新准入檢查;終態紀錄保留但釋放未完成名額,沒有刪除或重執行 unknown。
|
||
- 新真實 root file 測試建立跨兩個 origin identities 的 256 筆 accepted/unknown,驗證原 identity 與新 provider/generation 都拒絕且沒有改檔;舊 receipt 仍可重播,另一個 bot 寫入成功;一筆轉終態後原 bot 恢復准入。
|
||
- SQLite 並行最後名額測試擴充為全機與每 bot 兩組,兩個真實連線競爭只允許一方提交。3 項容量測試、14 項 root file tests、6 項 protected artifacts、完整 Docker gate(Runner/MCP/packages/restart/migration/remove-recreate/native transport/Supervisor HTTP/實際 API recovery)及 Supervisor all-targets Clippy 通過;自建容器已清除。
|
||
- 對應 O30/O41/T18/T63;Team 與 Dedicated 共用同一每 bot 限制。這只防止單一 bot 耗盡全機 unresolved slots,不保證多個滿額 bot 同時存在時仍有全機容量,也不是 CPU/RAM/磁碟公平排程或所有工具的 request quota。
|
||
- 未部署、沒有新 schema。回退需保留所有原 receipts/unknown;其餘 P0–P3 的完整公平隊列、retention/streaming、跨 generation 恢復與其他缺項仍待完成。
|
||
|
||
|
||
## File status/readback 的儲存與授權 fence(2026-09-10)
|
||
|
||
- 檢查跨 generation 恢復入口時發現 file_status 只核對 bot owner 與 provider/generation,未確認 Computer owner/space 相符,也未在讀取後重查 storage scope/home_key。現已在查詢前要求 Computer 與 bot 同 owner/space,並使用實際 home_key 建立 ComputerRef。
|
||
- 傳輸後除原 running/binding/generation fence 外,再核對 Computer owner/space、scope/home_key/kind,以及同 operation 的 durable dispatch provider/generation/runner identity/runner operation。原 mapping 被刪除或替換、儲存位置或授權改變時回 CONFLICT,不回傳過時證據。
|
||
- SQLx+實際 HTTP fixture 新增 7 種途中異動(Computer owner、space、模式、home_key、root operation ID、Runner ID、刪除 dispatch),全部拒絕;另驗證事前 Computer owner/space mismatch 不呼叫 transport。原 status、工具 resolve、generation/stop race 與不新增 mutation intent 的測試保留。
|
||
- 驗證:完整 API 197 passed/4 ignored、擴充 file_status 測試、API all-targets Clippy(CARGO_INCREMENTAL=0、-D warnings)、完整真實 Docker gate(14 root file tests、容量/snapshot/lifecycle、實際 dispatcher recovery)與 diff check 通過;自建容器已清除。沒有部署或 schema 變更。
|
||
- 對應 O41/O45/T17。這是目前 generation 的恢復入口授權補強,不宣告跨 generation unknown 恢復已完成。後者仍需把中央 provider_destroy_ack、原 home_key/root identity 與目前授權經可信管理通道綁定;不能接受一般 request 內自稱退役的欄位作停止證明。
|
||
- 其餘 P0–P3 全案範圍維持;仍缺完整 retention/streaming、跨 generation 恢復、其他路徑 journal、公平排程及 audit 所列驗收。
|
||
|
||
|
||
## 重建後的唯讀 file receipt 歷史查詢(2026-09-10)
|
||
|
||
- 新 Computer root file_history:只接受 operation_id/origin_computer_id/origin_generation,以目前 authenticated bot_id 組成舊 identity,要求 origin generation 不超過目前 generation,查原完整 identity+operation hash。其他 bot、未來 generation、夾帶 readback/resolve/bot override 均拒絕。
|
||
- 歷史成功/失敗 receipt 有界讀取並回傳;accepted/unknown 明示 execution_proven=false、observation_performed=false、desired_state_matches=null、retirement_proof_required。不呼叫 helper、不讀目前工作檔、不修改 receipt/observation/outbox,沒有以新 PID namespace 代替停止證明。
|
||
- API file-operation lookup 現在允許同一 current Computer binding 下較早 generation/provider 的 durable dispatch。用目前 provider/generation 送到新 Computer 的 file_history,body 只帶原 Runner identity;外層回覆新增 historical/originGeneration。跨 generation resolve 在 transport 前回 CONFLICT,普通查詢仍可取得已知 receipt 或明確未知狀態。原 owner/space/storage/kind/dispatch mapping 傳輸後 fence 保留。
|
||
- 更新 file_operation_status 的模型工具描述,說明歷史 unknown 不會被 readback 或 resolve。歷史查詢不會把舊 outbox 自動 ack,也尚未將歷史 receipt 回填中央 operation/artifact;該同步流程與 unknown retirement-proof recovery 仍需完成。
|
||
- 新 root test 驗證原檔被人類修改後取得歷史 receipt、錯 bot/未來 generation/額外欄位拒絕,以及未知操作查詢完全不改檔/receipt/observation/outbox。實際 restart/legacy migration/remove-recreate gate 新增以較新 identity 的 file_history 取得原 receipt。
|
||
- API HTTP+PostgreSQL 測試覆蓋 replacement provider、current generation header 與 original identity body、historical/原 generation metadata、unknown 未觀察、resolve 拒絕及途中 home_key 變更拒絕。先前 current-generation 的授權與 mapping 異動測試持續通過。
|
||
- 驗證:197 API passed/4 ignored、15 root file tests、6 protected artifacts、3 capacity tests、完整真實 Docker gate(含既有實際 API recovery)及 workspace all-targets Clippy 通過。最後 replacement-provider HTTP fixture 與工具描述已納入完整 API suite;root 程式在 gate 後未再修改。自建容器清除、未部署。
|
||
- 對應 O03/O41/O45/T17/T64;Team/Dedicated 均以相同 current bot 授權讀歷史。回退要保留原 root receipts;舊 Runner 無 file_history 時拒絕,不降級成任意重做。沒有宣告跨 generation unknown 恢復或全部 P0–P3 已完成。
|
||
|
||
|
||
## 查詢歷史終態 receipt 時回填中央狀態(2026-09-10)
|
||
|
||
- API 歷史 receipt 查詢現在於傳輸後開交易,重新驗證並鎖住目前 Computer/bot/原 dispatch,核驗結果後才回覆。只處理 root 已保存的 succeeded/failed;歷史 unknown 保持未知,不做新檔案效果或停止推論。
|
||
- migration 036 的 computer_file_history_receipts 保存原 dispatch provider/generation/Runner/operation 與核驗後 outcome。這是歷史 receipt 觀察紀錄,不是本地 outbox 事件;不編造 local_id,不 ack 未讀取的舊事件。相同 proof 重播去重,proof 改變或與既有終態矛盾時拒絕。
|
||
- 重用既有 action/path/hash/size/provenance parser 與 artifact registration;原 operation accepted/unknown 可更新為 receipt 終態,artifact、history proof、operation result、Activity outbox 在同一交易提交。保留 execution_receipt/postcondition 區別及 immutable snapshot reference;失敗不把 root 事實刪掉或重跑操作。
|
||
- SQLx fault test 在最後 Activity INSERT 注入故障,證明前面已寫入的 artifact/history/operation 全數回滾;恢復後兩次回填只各留一筆 artifact/history/Activity。本地 computer_file_events 維持零新增;unknown 及矛盾 failed receipt 拒絕。
|
||
- API HTTP fixture 以 replacement provider 歷史成功回覆驗證中央 unknown→succeeded、artifact 登錄及重複查詢去重;所有原 owner/storage/origin fence 測試持續通過。
|
||
- 真實 API→Supervisor→Computer gate 新增:保留 root postcondition receipt、模擬中央 completion/artifact 遺失、將 fixture generation 更新後做兩次歷史查詢;中央恢復 succeeded,重建 artifact 保持 postcondition 與原 snapshot reference,Activity 一筆、local event 數不變、人類後續修改仍原封不動。generation 更新只用來測歷史查詢,沒有冒充舊程序退役證據。
|
||
- 驗證:完整 API 198 passed/4 ignored、完整 Docker gate(15 root files、snapshot/quota/lifecycle 及上述歷史回填)、workspace all-targets Clippy 與最後新增 E2E 的 API all-targets Clippy 通過;自建容器清除,未部署。
|
||
- 對應 O41/O43/O45/T17/T64。migration 036 的 proof/outcome 為持久 metadata,回退須保留;中央先部署 migration 才能使用新回填。尚缺不依賴查詢的歷史 outbox 背景續送/ack,以及 unknown 跨 generation 的可信退役與明確恢復;其餘 P0–P3 缺项不縮減。
|
||
|
||
|
||
## 歷史 receipt 與事件送達的終態一致性(2026-09-10)
|
||
|
||
- 補強兩條恢復途徑原本各自去重、未交叉核對的邊界。歷史回填先核對已送達的最新終態 payload;event persistence 在處理 succeeded/failed 時核對既有 history proof 的 origin/status/outcome。任一不一致先拒絕,不寫入 artifact/新 event,不能形成可 ack 的中央入帳紀錄。
|
||
- accepted/unknown 的晚到事件仍可保留與 ack,不因已有歷史終態而刪除歷史觀察;操作本身不退回 unknown。相符終態事件可正常送達,且不重複產生歷史回填已發布的 Activity。原有固定範圍游標/單筆錯誤隔離可繼續處理後續事件,矛盾 root 事件不被直接丟棄。
|
||
- 新增兩項 SQLx 測試:history succeeded → accepted/unknown/matching succeeded 送達後只有一筆 Activity,矛盾 failed 未入帳;已送達 failed 後即使中央 operation result 被 fixture 模擬遺失,也不能接受矛盾 historical succeeded,沒有新 history/artifact。
|
||
- 驗證:完整 API 200 passed/4 ignored、API all-targets Clippy(CARGO_INCREMENTAL=0、-D warnings)、完整真實 Docker gate 含原 postcondition 歷史回填/snapshot/15 root file tests/lifecycle,及 diff check 通過。自建容器清除;沒有新 migration 或部署。
|
||
- 對應 O41/O45/T17。此檢查保留已驗證的終態證據,不將衝突當成普通成功重播;不等於歷史 outbox 背景掃描已接通。下一步仍須將舊 scope 的分頁續送與中央提交後 ack 綁定目前授權,並完成跨 generation unknown 停止證明;全部 P0–P3 範圍維持。
|
||
|
||
|
||
## Computer 歷史 outbox 的分頁與 ack 入口(2026-09-10)
|
||
|
||
- 新 root file_history_events/ack_file_history_events,嚴格接受 origin_computer_id/origin_generation/cursor 三欄;origin bot_id 固定取自目前 authenticated identity,未來 generation 或額外 bot/resolve 欄位拒絕。唯讀掃描不形成程序停止或成功證明。
|
||
- 轉接既有 file_events 有界 fixed-snapshot scan 與 identity+event IDs 精確刪除,保留原事件 ID/payload,不改寫成目前 generation;既有 100 rows/256 KiB batch/128 KiB event、異常 placeholder/原始壞資料保留、游標驗證與 ack 上限繼續適用。
|
||
- 新 SQLite 測試驗證 100 筆壞事件後可讀到有效歷史事件;ack 含其他 bot/目前 generation IDs 時只刪相符 origin 範圍;錯 bot 為空集合,未來/boolean generation、額外欄位及非法 ack cursor 拒絕。
|
||
- 真實 root test 驗證較新 identity 的 scan 與原 outbox 完全相同、錯 bot ack 不刪資料、正確 ack 只刪指定前綴;lifecycle fixture 新增 restart/legacy migration/remove-recreate 後由較新 identity 讀取原未 ack events,內容與 IDs 不變。
|
||
- 驗證:2 outbox tests、15 root file tests、完整 Docker gate(snapshot/quota/jobs/MCP/packages/lifecycle/實際 API 歷史 receipt 回填)、Supervisor all-targets Clippy(CARGO_INCREMENTAL=0、-D warnings)及 diff check 通過。自建容器已清除,未部署,沒有新 schema。
|
||
- 對應 O41/T17/T64。這一輪是 Computer 端入口,中央背景 poll scopes 尚未改為選取舊 origin,也尚未透過此入口做提交後 ack;未宣告歷史背景續送已完成。回退保留原 outbox,舊版不支援新動作時不可改用目前 identity ack 代替。其餘 P0–P3 與跨 generation unknown 恢復範圍維持。
|
||
|
||
## 背景 file poll 選取前的 bot 授權檢查(2026-09-10)
|
||
|
||
- 檢查歷史 scope 接線時發現 claim_poll_scope 未連接目前 bot assignment:已解除綁定或 owner/space 不相符的 retained scope 仍可能先被選中並發起讀取,再由中央 persist 拒絕。現於 claim SQL 要求 bot 仍綁同 Computer 且 user_id/space_id 相符。
|
||
- 無權 scope 保留原 metadata,不取得 lease、不更新 last_polled、不因暫時改綁而丟掉 outbox 掃描位置;合法重新綁回後可重新選取。這是選取時的授權快照,不冒充整段傳輸的 lifecycle barrier;提交時的 binding/generation 檢查仍保留。
|
||
- 新 SQLx 測試覆蓋 detached、foreign owner、foreign space 不被選取,原合法 bot 正常取得 lease;檢查三個拒絕 scope 的 lease/last_polled 未動,重新綁回後恢復選取。既有公平輪替、lease expiry fencing 與 HTTP poll 測試仍通過。
|
||
- 驗證:完整 API 201 passed/4 ignored、API all-targets Clippy(CARGO_INCREMENTAL=0、-D warnings)與 diff check 通過。此輪僅改中央選取 SQL,沒有修改 Runner、schema 或部署;未重跑沒有變更的容器程序測試。
|
||
- 對應 O30/O41/T17。中央選取目前仍限現有 generation;歷史 poll origin 接線與提交後 ack 仍需完成,沒有把入口完成當成背景續送完成。P0–P3 原始範圍維持。
|
||
|
||
|
||
## 中央歷史 file outbox 背景續送(2026-09-10)
|
||
|
||
- claim_poll_scope 現在選取目前 Computer 下不晚於目前 generation 的合法 bot scopes,不再排除旧 provider/generation。保留 current provider/generation/running、bot assignment/owner/space 檢查及既有 lease/token/visited-bot 輪替。migration 037 加入跨 generation last_polled 排序索引。
|
||
- 每次 claim 後取出原 scope provider/generation/cursor;歷史 scope 必須從 durable dispatch 唯一解析原 Runner identity,缺失/歧義拒絕。請求以目前 Computer provider/generation/context 送達,file_history_events body 帶原 identity 與固定快照 cursor。
|
||
- persist_event 分開處理目前授權/儲存快照與原事件 identity:鎖目前 Computer/bot 並核對 captured owner/space/home_key/scope、running/provider/generation,再以原 dispatch/root identity 驗證 payload、登錄原 local_id/generation。已有 historical proof 的一致性檢查仍生效;accepted/unknown 不被當作程序停止或成功。
|
||
- 只有 PostgreSQL 提交成功的 IDs 進入 ack list。歷史 scope 使用 ack_file_history_events,body 帶原 identity;目前 scope 沿用原 ack。讀取前與 ack 前另重查目前授權/儲存快照,變更時不發起該 transport。原 4 次嘗試/每 scope 2 秒、逐筆 checkpoint、16 ID 前綴 ack、poison 保留與 lease fencing 持續適用。
|
||
- 新 HTTP/PostgreSQL 測試驗證 replacement provider+目前 generation header、原 identity cursor、壞事件後仍提交有效事件、第一次 ack 失敗後重送去重、ack 發出時 operation/artifact 已提交、Activity 僅一筆;home_key 改變後 scan 與 ack 都拒絕。
|
||
- 實際 API→Supervisor→Computer gate 新增未 ack 的 journaled write,保留 root 原事件並模擬中央 completion/artifact 遺失;fixture generation 前進後由 sync_events 自動恢復 succeeded 與 execution_receipt artifact,以原 generation 保存兩個事件,最後 root 歷史 outbox 為空。不依賴 file-operation 查詢,不重做檔案寫入。
|
||
- 驗證:最終完整 API 202 passed/4 ignored、完整 Docker gate(15 root files、root history scan/ack/lifecycle/實際背景恢復)、workspace Clippy 通過;最後補充 index 與 stale-ack assertion 已納入完整 API/gate。自建容器清除、沒有部署。
|
||
- 對應 O30/O41/T17/T64。scope/lease 仍是每 scope 且每個 worker 每輪排除已拜訪 bot;未宣告跨 replicas 的 per-bot 資源隔離或大量歷史空 scopes 的延遲 target 已驗收。舊未知效果的可信停止證明、明確恢復、retention 與其餘 P0–P3 維持待辦。
|
||
- 回退需保留舊 poll scopes/cursor/root outbox 與 event 原 generation;新 index 可保留。舊 API 恢復僅輪詢目前 generation,不可刪除尚未 ack 的歷史事件當作回退清理。
|
||
|
||
|
||
## 多 worker 跨 generation 的 bot poll lease 互斥(2026-09-10)
|
||
|
||
- 修正歷史 scope 接入後,同一 bot 的不同 generation 可以被不同 worker 同時取得有效 lease 的缺口。claim 改用短交易鎖住 stable bot row 與候選 scope;候選查詢排除該 bot 已有的有效 lease,取得鎖後再用新 READ COMMITTED snapshot 重查,才發布 lease。
|
||
- 明確 SET TRANSACTION ISOLATION LEVEL READ COMMITTED,避免部署環境改預設 isolation 使第二次檢查仍看舊 snapshot。busy bot/scope 使用 SKIP LOCKED;競爭重試最多 4 次,保留其他 bot 的選取機會,整個網路輪詢不持有 bot DB row lock。
|
||
- 新 SQLx 並行測試建立同 bot 兩個 generation,兩個 worker 同時競爭只一個成功;另一個 bot 正常取得 lease。原 lease 過期後可換 generation claim;過期 token 不能 checkpoint,遲到 release 不會清掉 replacement lease。
|
||
- 原逾時續送測試揭露新的合理排程差異:SQL future drop 不代表 PostgreSQL 已完成回滾,bot row 仍鎖住時新 claim 會跳過。測試改以限時取得該 row lock 等待實際回滾完成,再核驗 cursor 續送和只有後續事件提交;初次新增 fixture 的 lock_timeout 單位已修正為 PostgreSQL 支援的 5s,沒有放寬 outcome/assertions。
|
||
- 最終完整 API 203 passed/4 ignored,API all-targets Clippy(CARGO_INCREMENTAL=0、-D warnings)與 diff check 通過。此輪未改 Runner/schema/部署;驗證集中於真實 PostgreSQL 並行、lease 與既有 HTTP 續送回歸。
|
||
- 對應 O30/O41/T17/T63。這是使用新版 claim 的 worker 間有效 lease 互斥;不宣告舊版 replica 混跑或已過期但仍在外部執行的請求已被物理取消,也不是 CPU/RAM 隔離。全部 worker 升級後才可依賴新 per-bot claim 語意;rollback 不刪 scope/cursor。歷史空 scope 延遲/retention、跨 generation unknown 恢復及其餘 P0–P3 仍待完成。
|
||
|
||
|
||
## 已讀空歷史 scope 的輪詢延後(2026-09-10)
|
||
|
||
- migration 038 新增 next_poll_after,預設立即可選取。只有歷史 scope 回覆經驗證的空 batch+完成游標 (0,0) 時,才在再次核對目前授權後,使用尚有效的 lease token 延後 60 秒再查;不刪 poll scope、outbox、receipt 或原始 identity。
|
||
- claim SQL 過濾未到期的歷史 scope;目前 provider/generation 明確豁免延後時間,因此即使殘留未來時間也立即可選取。非空/壞事件與缺少有效 cursor 的回覆不當作已讀空,不藉 backoff 丟失事件。
|
||
- SQLx 測試驗證歷史空 scope 讓出輪詢機會、目前 scope 即使 future timestamp 仍可選取、歷史延後到期後重返輪替,以及釋放後的 stale lease 不能修改 delay;兩個 scope 均完整保留。到期測試透過 fixture 設定 DB 時間欄位,不等待固定 sleep。
|
||
- 真實 API→Supervisor→Computer gate 在完成歷史背景恢復與 ack 後,再次讀取真正空 outbox,確認 delay 持久化、cursor (0,0),立即重跑 sync 不更新該 scope last_polled。原 15 root file tests/歷史 artifact 回填/lifecycle 持續通過。
|
||
- 驗證:最終完整 API 204 passed/4 ignored、完整 Docker gate、API all-targets Clippy 及 diff check 通過。自建容器已清除,未部署。最後 current-scope 豁免已納入最終 API suite 與 gate。
|
||
- 對應 O30/O41/T17/T63。這降低已知空歷史 scope 的重複輪詢,不宣告大量從未掃描 scopes 的初始延遲或完整全機公平排程 target 已驗收。延後期間若舊 scope 又產生事件,仍會於到期重查;未因 generation 改變推論程序已停止。
|
||
- 回退可保留新欄位,舊 API 會恢復較頻繁輪詢而不是刪掉資料。跨 generation unknown 恢復的可信退役證據鏈、完整 retention/streaming 與其餘 P0–P3 仍需完成。
|
||
|
||
## 背景事件的執行後端種類 fence(2026-09-10)
|
||
|
||
- 背景 file poll 的 require_poll_scope 與 event commit 原先核對 owner/space、home_key/scope、provider/generation,漏掉 captured Computer kind。現加入 kind 比對,涵蓋讀取前、ack 前,以及同交易提交事件/artifact 的檢查;與 file-status 查詢的後端 fence 對齊。
|
||
- 新 SQLx 測試保持 provider/generation 不變、只變更 kind,驗證舊 snapshot 的 poll 驗證及 receipt commit 拒絕,artifact/event/Activity 都沒有新增,operation 保留 unknown;恢復原 kind 後正常核驗及提交。
|
||
- 驗證:完整 API 205 passed/4 ignored、API all-targets Clippy(CARGO_INCREMENTAL=0、-D warnings)與 diff check 通過。此輪僅中央 SQL fence 與 PostgreSQL 測試,未改 Runner、schema 或部署,未重跑不變的容器流程。
|
||
- 對應 O41/O45/T17。提交拒絕保留原 root 事件供後續處理,不把 metadata 設定變更解讀成已完成其他 provider 的操作。完整輪詢延遲驗收、跨 generation unknown 退役證據鏈與其餘 P0–P3 缺項仍維持原範圍。
|
||
|
||
|
||
## 跨 generation 退役證據的驗證與私有管理通道(2026-09-10)
|
||
|
||
- 新 root file_retirement_proof 驗證 HMAC-SHA256 envelope,payload 最多 4096 bytes;拒絕重複 JSON 欄位、額外 claims、未來/過期時間、超過 60 秒期限、boolean 整數混用、錯目前 identity/home_key、跨 bot origin、非較早 generation、非 provider_destroy_ack 種類與非法 references。錯誤統一為 INVALID_FILE_RETIREMENT_PROOF,不含任意簽章資料或 key。
|
||
- Supervisor 使用獨立 lazyboy-file-retirement-v1 用途標記,從 master+實際 container homeKey label 衍生 256-bit key;與公開給 Computer 服務的 control token 分離,其他 home 的 key 也不同。只有 file_retirement_validate 動作才把 key/home 加入 fixed root request 的私有頂層欄位,以 Docker exec stdin 傳遞;不放在 argv/task env 或使用者提供的 nested input 中。
|
||
- Root 驗證探針回傳 proof_valid/proof_id/origin_generation,先取出私有 key/home,不發布 key,不開始 task helper、不清除 unknown,也不發布恢復 receipt。驗證簽章不等於此階段已核實 provider 實際被 destroy;中央 acknowledgement 查證與安全發證尚待 API 接線。
|
||
- 3 項 Python 協定測試涵蓋有效證據、錯 key/home/current context、MAC 篡改、簽章有效但 claims 非法、重複欄位、無界/壞 envelope。Rust credential 測試驗證用途及 home 分離;registered gate 執行這些 proof tests。
|
||
- 真實 Supervisor HTTP→Computer fixture 以測試 authority 簽章,驗證 Rust 簽章/Python verifier 互通、private key 不在回覆、偽造 signature 與跨 bot 拒絕、原人類修改檔案不動。該 fixture 明示只驗證 envelope,不把仍存活的測試容器偽稱為已退役。
|
||
- 驗證:3 proof tests、Rust credential test、完整 Docker gate(含擴充的 Supervisor HTTP 證據驗證、15 root files、lifecycle、實際 API 歷史背景恢復)與 Supervisor all-targets Clippy 通過;diff check 通過,自建容器清除,未部署。
|
||
- 對應 O41/O45/T17。尚未提供可自行接受任意 retirement claims 的恢復入口;下一步必須由 API 依目前授權核對中央 provider_destroy_ack/原 dispatch/home_key,再透過此通道把證據送入明確 postcondition recovery。全部 P0–P3 剩餘範圍維持,不能將簽章驗證探針當成跨 generation unknown 已完成。
|
||
|
||
|
||
## 簽章退役證據的 root 明確恢復與中央來源核對(2026-09-10)
|
||
|
||
- Root 新增 file_retirement_recover,僅在嚴格驗證簽章、目前 identity/home、原 identity、期限及 provider_destroy_ack claims 後,使用已退役的證據進行原操作後置條件核驗。不同 boot 的 PID 不被視為停止證明;簽章證據也不繞過內容 fingerprint、fsync、paired move 或 immutable snapshot 核驗。
|
||
- 恢復 receipt 保留 postcondition、executionProven=false,新增 retirementProofId/quiescenceBasis。已驗證 claims(不含 key/signature)與 receipt/outbox 同 SQLite transaction 保存於 file_retirement_receipts;重送返回既有 receipt,不覆蓋後來的人工作業。Supervisor 私有 key 注入擴充至恢復動作。
|
||
- 新真實 root 測試以 fixture authority 及舊 boot metadata 模擬跨 generation:普通查詢維持 unknown,壞 MAC 拒絕,有效證據遇到人類內容衝突仍 unknown;內容符合才恢復且不重寫檔案,後續重送保留人類修改。這不是實際 API destroy acknowledgement 的端到端驗收。
|
||
- 第 4 項 proof 測試使用真實 SQLite transaction、模擬已完成的檔案核驗邊界,注入 audit INSERT 失敗,確認 receipt/outbox/audit 全部回滾,移除故障後只提交一次。
|
||
- 中央 parser 嚴格核對 proof ID 格式及 postcondition/provider_destroy_ack 來源。record_artifact 另要求該 ID 匹配中央退役 acknowledgement 的原 dispatch provider/generation 與目前 home;缺失或錯 home/provider/generation/proof 拒絕,同交易保留 unknown。新增 SQLx 測試覆蓋拒絕及成功。
|
||
- 驗證:最終完整 API 206 passed/4 ignored;完整 Docker gate 通過(16 root files、Supervisor HTTP、lifecycle、實際 API 歷史背景恢復),fixture 容器清除;4 項 proof tests 通過。Gate 後新增的中央 acknowledgement guard 已由最終 API suite 覆蓋,第 4 項 SQLite proof test 另行通過。未部署。
|
||
- 對應 O41/O45/T17。API 歷史 resolve 仍拒絕:必須補齊目前 path grant、中央查證、受信任 issuer、專用 transport 與實際 destroy/recreate 恢復端到端測試,才可對使用者開放。此輪不能宣告跨 generation unknown 已完整完成;其餘 P0–P3 範圍維持。
|
||
|
||
|
||
## 中央退役查證與專用簽章恢復通道(2026-09-10)
|
||
|
||
- 新 FileRetirementRequest 與 SandboxProvider::recover_retired_file 管理介面,拒絕額外欄位、錯/空 identity、非較早 generation 與空 home;不支援的 provider 預設 fail closed。Docker SDK 核對目前 context/home,使用專用 files/recover-retired endpoint,回覆串流上限 128 KiB、stdout 64 KiB 並嚴格核對 status/code。
|
||
- Supervisor 入口要求 master token、bot 與 generation,body 上限 16 KiB;再次核對實際 container homeKey label。只有管理端生成用途分離的 HMAC 與 60 秒 claims,envelope 直接送入 root,不返回給 API/工具/task,不接受呼叫者提供的 signature。原 generic exec 仍不能自行簽章。
|
||
- API 歷史 resolve 不再一律拒絕。先用原 captured identity 鎖住目前 Computer/bot/dispatch,查證原 computer/provider/generation 與目前 home 匹配的 provider_destroy_ack,FOR SHARE 保持 acknowledgement 穩定;在鎖內讀原 metadata,依目前 bot workspace scope 檢查來源與 move 目的地全部路徑,再呼叫管理通道。以 2 秒 lock_timeout 限制取得鎖等待、25 秒 transport timeout 限制外部等待,鎖保留至中央 reconciliation commit。普通歷史查詢仍不讀取 live path。
|
||
- 新 SQLx+HTTP 測試覆蓋缺 acknowledgement、錯 provider/generation/home、空/未正規化/跨 bot/舊 dedicated 路徑、move 目的地越權,這些案例都不發起 transport;有效請求在 external call 期間實際嘗試修改四種 DB records,全部因 row locks 拒絕。unknown、錯 status/code、過大回覆不產生 artifact;有效 receipt 保留 postcondition/executionProven=false/proof ID 並去重。這是 DB metadata 互斥驗證,不宣稱任意外部 Docker 操作受到 PostgreSQL 鎖控制。
|
||
- Supervisor HTTP fixture 覆蓋 SDK 專用入口、home mismatch、錯 token/bot/generation、額外 signature 拒絕,以及已知 receipt 重送。擴充 fixture 注入真實 SQLite result UPDATE 故障,helper 實際寫入後由專用 issuer 恢復 unknown,再核對人類後續修改不被重送覆蓋。此 fixture 使用測試 authority,並未真的 destroy 舊 guest。
|
||
- 最終驗證:完整 API 207 passed/4 ignored;Control 157 passed、Sandbox 11 passed;完整 Docker gate 通過(4 proof tests、16 root files、擴充 Supervisor HTTP unknown 恢復、lifecycle、實際 API 歷史背景恢復);workspace all-targets Clippy(CARGO_INCREMENTAL=0、-D warnings)及 diff check 通過。自建 fixture 容器已清除。
|
||
- 對應 O41/O45/T17。仍需實際 lifecycle destroy 產生中央 acknowledgement,再 recreate 並經 API 恢復 unknown 的完整一體化 gate;也需 UI 歷史恢復可用性提示/入口與恢復回覆遺失故障驗收。全 P0–P3 原範圍保持,沒有將各層 fixture 拼成已完成端到端的宣告。未部署。
|
||
|
||
|
||
## 實際 provider 退役與重建後恢復 gate(2026-09-10)
|
||
|
||
- 擴充既有 API→Supervisor→Computer 整合 gate,建立獨立的一次性 Dedicated guest,使用 network=none、CPU/RAM/PID 上限、持久 home 與四個 runtime mounts。所有目錄位於本次 Supervisor fixture 的暫存目錄,退出時先移除 guest 再清除資料。既有 Team fixture 不變。
|
||
- 真實 root SQLite result UPDATE 故障讓 write helper 寫入成功而 receipt 缺失;移除故障後呼叫原 production replace_computer_generation(僅將 Rust 可見性改為 pub(crate) 供同 crate gate 使用),經 Supervisor stop/destroy 取得 acknowledgement。測試核對舊 Docker ID 已不存在、中央 retirement 原 provider/home/generation 正確、generation 前進且 provider 清空;沒有在 fixture 人工 INSERT retirement。
|
||
- 以相同持久 mounts 建立不同 Docker ID,再用 fixture 發布新 provider/running;這一步不宣告完整公開 boot/readiness 流程已驗收。核對原檔案 bytes 與 root journal 跨容器保存,普通歷史查詢仍不讀取 live path。
|
||
- 經 API tool dispatcher 明確 resolve:人類內容衝突保持 unknown 且不改檔;fixture 恢復預期 bytes 後,恢復須回傳中央實際 proof ID、postcondition、executionProven=false,mtime 不變。後續人類編輯及重送不得重寫,immutable artifact 仍返回核驗 bytes;背景歷史 outbox 必須以原 generation 保存兩個事件並只產生一份 artifact。
|
||
- 初次執行在新增 fixture 的 file_events 初始化缺少 JSON 游標時失敗,尚未進入退役流程;已補上必要的 {},保留所有 outcome/assertions 重跑。最終完整 Docker gate 通過(16 root files、Supervisor HTTP、既有 Team 歷史恢復及新增 Dedicated 實際退役恢復),API 整合測試 18.89 秒;workspace all-targets Clippy 與補充 API all-targets Clippy 通過,diff check 通過。所有本次 fixture 容器已清除,未部署。
|
||
- 對應 O41/O45/T15/T17/T64;本輪不改產品恢復語意,擴大真實故障/lifecycle 證據範圍。回退可撤除該 fixture,不刪 persisted journal/退役 acknowledgement 或改復原來源。歷史恢復 UI、恢復回覆遺失、公開 boot/readiness 一體化及其餘 P0–P3 尚待完成。
|
||
|
||
|
||
## 歷史 unknown 的明確恢復 UI(2026-09-10)
|
||
|
||
- API file-operation 回覆新增 retirementRecoveryAvailable。只有歷史 unknown 在目前 owner/binding/storage/dispatch locks 內,經相同 retirement_authorization helper 核對中央 provider_destroy_ack、原 identity、目前 home、所有受影響路徑後才為 true。普通查詢仍使用 file_history、不讀 live path、不簽發 envelope,也不返回管理 key/proof claims。
|
||
- 真正 resolve 每次重新查證,不接受 UI 提示作為授權。抽出共用 helper 後,缺失/不匹配/非法路徑回傳不可用;DB 錯誤仍失敗,不偽造可用性。終態及同 generation 不給歷史恢復提示。
|
||
- Run Monitor 對歷史 unknown 僅在中央 boolean 提示為 true 時顯示既有「重新核驗並完成恢復」按鈕;新增中英文提示,說明舊 Computer 已退役且可嘗試核驗。渲染不呼叫 API,必須點擊才送 readback=true+resolve=true;當前 generation 原有 helper/controller/desired state 條件保留。內容衝突訊息優先,不把可嘗試恢復顯示成已成功。
|
||
- SQLx/HTTP 回歸擴充 hint:缺 acknowledgement、錯 home/provider/generation、未正規化與跨 bot/move 目的地越權皆 false;合法 hint 不進行檔案觀察;顯示後更改 acknowledgement home,resolve 拒絕且沒有發起恢復 transport。前端新增 strict boolean/終態/歷史與當前分流測試,實際元件事件測試確認不自動執行且點擊送對原 operation。
|
||
- 驗證:前端 59 tests 通過、TypeScript/Vite production build 通過(既有 bundle size warning)、完整 API 207 passed/4 ignored、workspace all-targets Clippy 與 diff check 通過。完整 Docker gate 通過,包括 16 root files、Supervisor HTTP、既有 Team 歷史恢復與 Dedicated 實際退役/重建後 availability assertion;API 整合段 18.73 秒。本次 fixture 容器均已清除。
|
||
- 對應 O41/O45/T17/T64。未宣告瀏覽器視覺/公開登入後端到端已測;目前 UI 證據為元件事件/狀態測試及 production build。Team 現有同 generation 恢復維持,Dedicated 真實退役 fixture 持續驗收。回退可忽略新增提示欄位、隱藏歷史按鈕,不刪既有 receipt/acknowledgement。恢復回覆遺失、公開 boot/readiness 一體化及其餘 P0–P3 仍待完成,未部署。
|
||
|
||
|
||
## 退役恢復 receipt 回覆遺失驗證(2026-09-10)
|
||
|
||
- 擴充實際 Dedicated destroy/中央 acknowledgement/重建 fixture,在 API→Supervisor 的專用 recover-retired transport 插入一次性故障代理。代理完整讀到真實 root succeeded receipt 才回 502,不建立 receipt、不修改 task bytes,也不改寫退役 claims。
|
||
- API 必須回報 FILE_STATUS_UNAVAILABLE,原操作維持 unknown,中央 artifact 與 historical proof 均為零;代理核對只收到一次請求,禁止傳輸層悄悄重做。真實檔案 mtime 在 root 恢復期間維持不變。
|
||
- 回覆遺失後先讓人類修改檔案,再經正常 API 明確恢復請求取回原 receipt。回傳 evidence 必須與代理先前讀到的實際 receipt 完全相同,保留原中央 proof ID/postcondition/executionProven=false;人類 bytes 與 mtime 不變。既有再次重送、immutable artifact 讀取與歷史 outbox 入帳檢查持續執行。
|
||
- API all-targets Clippy(CARGO_INCREMENTAL=0、-D warnings)與 diff check 通過;完整 Docker gate 通過,新增實際退役/回覆遺失整合段 19.14 秒,原 root files/Supervisor HTTP/Team 背景恢復皆通過。自建 fixture 容器已清除。這項變更只修改測試 fixture,沒有改產品協定/恢復程式,不重跑不變的前端建置。
|
||
- 對應 O41/O45/T17/T64。本案例覆蓋「root 已提交,HTTP 回覆被換成 502」;不宣告所有 socket reset/逾時/中央程序崩潰時序均已驗收。公開 boot/readiness 一體化、完整故障矩陣及其餘 P0–P3 仍保持原範圍,未部署。
|
||
|
||
|
||
## boot 健康探測不明時保留原 Computer(2026-09-10)
|
||
|
||
- 核對公開 boot 路徑時發現 guest_has_screens 把 ContainerProbe::Unknown 壓成 false,boot_for 接著會執行 stop/destroy/replacement;暫時 HTTP 失敗因此可能銷毀仍可用的 Computer。移除該 boolean wrapper,boot 明確拒絕 Unknown 並保留原 provider/generation/state。
|
||
- probe 對固定 test -x 的 exit 0 視為 Alive、exit 1 視為 Missing;其他非零碼(如 timeout 124/終止 137/錯誤 2)改為 Unknown。原明確容器缺失錯誤仍走 Missing,沒有以未知狀態推導退役 acknowledgement。
|
||
- 新 SQLx+HTTP regression 覆蓋 Team/Dedicated、need_gui true/false 與 HTTP 503/124/137/2,檢查原 provider/generation/running 與 unknown job 保持、沒有 stop/destroy/provision 請求、沒有新增 retirement;恢復健康 code 0 後 runner-only boot 可重用原 provider。
|
||
- 最終完整 API 208 passed/4 ignored、API all-targets Clippy(CARGO_INCREMENTAL=0、-D warnings)及 diff check 通過。上述 Docker 回覆遺失 gate 在此 boot 修正前已通過;此修正不改該 gate 使用的明確 replace 路徑,新增公開 boot 分支以真實 PostgreSQL/HTTP regression 驗證。公開實際容器 boot/readiness 與全 P0–P3 仍待完成,未部署。
|
||
|
||
|
||
## 退役後透過正式 boot_for 重建(2026-09-10)
|
||
|
||
- Dedicated 退役恢復 gate 移除 fixture 的 Docker recreate/SQL provider publication,改呼叫 production boot_for(need_gui=false),經 Sandbox→Supervisor provision、映像 entrypoint/ready marker 與中央 boot_attempt CAS 發布 running。核對新 provider ID、generation 仍為退役後的 2、boot attempt 清除;再次 boot 重用相同 provider/generation,沒有多寫退役紀錄。
|
||
- 原先故障 guest 仍由 fixture 建立;銷毀、退役紀錄、重新 provision、就緒檢查、中央發布、未知效果恢復與回覆遺失重送均走實際程式。測試 Supervisor 固定 512 MiB/2 CPUs/256 PIDs、關閉 sudo 並清除 shared screen network 環境;正式 provision 會建立自己管理的 network,fixture cleanup 新增只移除本次 guest 與其 network。
|
||
- 此版本完整 Docker gate 已通過,整合段 22.96 秒;API all-targets Clippy 通過。另新增全新 Team home 的正式 cold boot/native write 測試,不預先建立或修正 home ownership。確認該新增 assertion 不在首次執行 binary 內,因此已重新編譯並重跑整合 gate,Team cold boot 曾揭露 ownership 缺陷,修正及最終結果見下節。
|
||
- 對應 O08/O41/O45/T15/T17/T64。這是公開 boot 函式到真實 runner-only 容器的整合;不等於瀏覽器登入 HTTP route/GUI、browser、desktop、viewer 各層 readiness 均已驗收。全部 P0–P3 其餘範圍維持,未部署。
|
||
|
||
|
||
## 修正未設定 HOST_DATA_DIR 的全新 home ownership(2026-09-10)
|
||
|
||
- 新 Team cold boot gate 揭露真實啟動失敗:API/Supervisor 由 root 在本機執行,未設定 HOST_DATA_DIR 時,API 建立的 home 保留 root ownership;映像以 UID 1000 啟動,建立 .browser-profiles 時因權限拒絕而 exit 1,Supervisor provision 回 500。故障由實際 guest stderr 與 Supervisor startup log 確認,不是以 fixture 手動 chown 掩蓋。
|
||
- DockerHost::provision 在驗證 managed home canonical path 後,對所有 Unix 部署統一將 home 根目錄設為 1000:1000,不再只在 HOST_DATA_DIR 模式執行。僅改根目錄擁有者,不遞迴改寫 task/browser/tool 既有內容權限;chown 失敗仍在建立容器前拒絕。
|
||
- Team cold boot fixture 不預建 home/runtime、不修 ownership,實際呼叫 boot_for,核對新 home uid/gid=1000、Team bot 的 journaled write 與 native read 成功。Dedicated 正式 re-provision、warm boot reuse、退役恢復及回覆遺失仍在同 gate 內執行。
|
||
- 診斷階段保留了 provision 失敗的 Supervisor/guest logs,並清除本次 guest/network;不動既有部署。workspace all-targets Clippy、diff check 及修正後完整 Docker gate 均通過,整合段 26.18 秒;包含全新 Team cold boot/1000:1000 ownership/native write-read,以及 Dedicated 正式 re-provision/warm reuse/退役恢復與回覆遺失。本次 guest/network 全部清除。
|
||
- 對應 O03/O08/O41/T15/T17。這是 root 啟動的本機部署及新 home 的缺陷修正;不宣告 GUI/viewer/browser 獨立 readiness 與登入後完整產品流程已完成。P0–P3 其餘範圍保持。
|
||
|
||
|
||
## health 證據、授權與探測期限(2026-09-10)
|
||
|
||
- 發現 component_health 把設定中的 display backend 一律當 ok=true,且 jobs count SQL 失敗時回退成 0;這兩者都可能產生沒有依據的健康回報。display 改回 ok=null/state=unknown/not probed,設定值另放 configuredBackend;未運行的 sandbox 標 inactive/ok=null。整體 ok 只有全部元件明確 true 才為 true。這不影響原生工具 admission。
|
||
- jobs count 與 display backend DB 讀取失敗明確拒絕回覆,不以零工作/環境預設冒充當前資料;operations 可用性改用 EXISTS,避免為探測而計算全表總數。
|
||
- health 開始前核對 Computer owner/space 與 actor;探測後在 transaction 核對並鎖定目前 bot assignment、owner/space、provider/generation/state、home/scope/kind/display backend。跨範圍請求不發起 transport,期間變更拒絕舊結果。
|
||
- probe_computer_container 的 HTTP future 原先無總期限,現在在原 guest 5 秒 command timeout 外加入 6 秒 transport deadline。逾時是 Unknown,沿用 boot 保留原 provider 的處理;不推斷程序已停止或寫入退役紀錄。
|
||
- 新 SQLx+HTTP health regression 覆蓋未知 display、foreign owner/space、探測中 home/generation/binding/backend 變更、jobs 表不可用及 inactive sandbox。boot regression 加入實際永不回覆的 HTTP handler,驗證有界返回 Unknown、provider/generation/running 保持、無 stop/destroy/provision。最終完整 API 209 passed/4 ignored、API all-targets Clippy(CARGO_INCREMENTAL=0、-D warnings)及 diff check 通過。此輪未改 Runner/Supervisor/UI;使用真實 PostgreSQL 與 HTTP 故障測試,未重跑不變的容器與前端流程。
|
||
- 對應 O08/O41/O45/T15/T17。此次修正的是誠實回報、存取範圍與等待界線,不把 unknown 當成完整 layered readiness;下一步仍需實際 browser/desktop/viewer scoped probes 與其復原流程。全部 P0–P3 範圍維持,未部署。
|
||
|
||
|
||
## Computer-local 唯讀 runtime probes 與 Runner 程式傳送修正(2026-09-11)
|
||
|
||
- 新 runtime_health root 管理動作先核對 identity/slot,經受保護 Runner SQLite 初始化後,以 UID/GID 1000、乾淨 env、256 MiB/FD/CPU 限制執行固定 Python probe,總期限 8 秒、成功 stdout 上限 4096 bytes。逾時/spawn 錯誤正規化為 RUNTIME_HEALTH_UNAVAILABLE,避免 TimeoutExpired 把 helper argv/source 放入錯誤。
|
||
- 未指派 slot 時 desktop/browser/viewer 均為 null,不探測預設 :1;slot 僅接受整數 0–7,拒絕 boolean/額外欄位。已指定 slot 時各項獨立:desktop 核對 xdpyinfo 與 WM supporting window/name;browser 核對對應 loopback DevTools discovery、協定與本地 websocket URL;viewer 核對 vnc.html 及真正 WebSocket upgrade/accept/binary RFB greeting,不只看 HTTP port。每次 HTTP/X11 等待與資料量皆有界,不啟動任何服務、不輸出 browser 內容。
|
||
- 6 個 Python tests 覆蓋 discovery 壞資料/foreign URL/大小限制/redirect,viewer 假 upgrade/accept/遮罩或過大 frame/錯 RFB/分段 greeting,X11 沒 WM、無 slot 不探測及元件獨立性;root timeout/spawn error 不洩漏 helper canary。正向 browser/desktop/viewer 此階段使用協定/subprocess fixtures,不宣告真實 Chromium/XFCE/viewer session 已測。
|
||
- 新真實 root 容器測試確認 UID 1000 runner、有 slot 但未啟動 GUI 的結果、非法 body 拒絕,以及 jobs/file journal/outbox 筆數不變;Supervisor HTTP fixture 另經 SDK 呼叫 runtime_health。API health endpoint 尚未接入這些結果,必須先接目前 screen assignment/profile 與前後 fence;該 endpoint 仍保持未探測,不提前顯示新後端成功。
|
||
- 第一輪容器 gate 揭露既有 Runner bootstrap 把整份 Python source 放在單一 argv,增加 probe 後跨過目前 Linux 的長度上限,native file transport 出現 argument list too long。已改固定小型 bootstrap+stdin 長度 frame(程式最多 1 MiB),以 unbuffered os.read 精確消耗 source,保留剩餘 request bytes 給 exec;程式以 root-private temp file/fsync/rename/directory fsync 發布,request/管理 key 不存入該程式檔。
|
||
- 3 個真實 pipe/exec tests 驗證超過 128 KiB 的 UTF-8 程式、4093-byte 分段傳送、request 原 bytes 保留、request canary 不進程式檔、非法/截斷 frame 不替換旧程式,以及不受保護目錄拒絕。這些 tests 與 runtime probes 均註冊到 Docker gate。
|
||
- 6 probe tests、3 bootstrap tests、workspace all-targets Clippy 與 diff check 通過;修正後完整 Docker gate 通過(17 root files、Supervisor HTTP runtime_health/簽章恢復、既有 lifecycle 及 Team/Dedicated 正式啟動與退役恢復),API 整合段 24.98 秒。本次 fixture 容器與 networks 已清除。對應 O08/O41/O45/T17/T64。仍需 API scoped 接線、真實 GUI/browser/viewer 正向及故障復原驗收,P0–P3 其餘範圍維持;未部署。
|
||
|
||
|
||
## health API 接入分項 runtime 證據(2026-09-11)
|
||
|
||
- control 增加嚴格 RuntimeReadinessObservation:成功 code/status、4096-byte stdout、空 stderr、version 1、必要 nullable 欄位、型別、未知/重複欄位、runner UID 證據及精確 slot 均檢查。無 slot 時 GUI 必須全 null;有 slot 時每項必須明確 boolean,false 不會拖成 Runner 不可用。傳輸錯誤正規化,15 秒總期限,不回傳任意來源錯誤內容。
|
||
- component_health 只讀取目前 bot 自己的 ScreenRow,先檢查 slot/display/view_port 對應,不以預設 slot 0 借用其他 bot 畫面;實際呼叫 Computer-local runtime_health,回報 display/browser/viewer 獨立證據及 nativeReady。沒有畫面或探測失敗仍保留 null/unknown;nativeReady 僅代表 Runner 探測成功,不代替原生工具的 quota/權限 admission。
|
||
- 沿用 Computer owner/storage/provider/generation/assignment 前後檢查,並核對 screen identity、slot、display/view_port、profile mode/path、execution fence。探測中從無畫面變成有畫面、刪除、換 profile 或 fence 變更皆拒絕舊結果。這是 assignment 一致性,尚不表示已證明 Chromium 實際 process 的 profile 身分。
|
||
- 4 個 control readiness tests 與 SQLx+真實 HTTP regression 通過,涵蓋 malformed evidence、外部 scope、Computer 變更、screen 新增/刪除/profile/fence 變更、分項成功失敗及 inactive/DB 失敗。真實 Team cold boot fixture 另加入 component_health→Supervisor→root Runner 驗證,不配置 screen,要求 nativeReady=true、GUI null 且查詢後仍無 ScreenRow。完整 API 209 passed/4 ignored、完整 Docker gate 通過,真實 API 整合段 26.06 秒;本次容器/network 已清除。其後檢查 SDK 發現 generic exec 會先完整讀入 response,已對 runtime_health 加入 16 KiB 串流上限,非成功狀態不讀任意錯誤 body;真實 chunked HTTP regression 證明超限及 503 在沒有 EOF 時仍立即拒絕且不洩漏 canary。此追加修正後 API health 回歸、SDK chunked test 與三個受影響 crates all-targets Clippy(-D warnings)均通過;修正了測試未使用 socket read byte count 的 Clippy 問題,diff check 通過。完整 Docker gate 執行於追加 SDK 串流限制之前,該限制另由上述真實 HTTP 測試覆蓋。
|
||
- 對應 O08/O41/T17。真實 Chromium/XFCE/websockify 正向與故障復原及 P0–P3 其他項目仍未完成;未部署。
|
||
|
||
|
||
## 真實 GUI readiness 與服務重啟驗證(2026-09-11)
|
||
|
||
- 新增 `make test-runtime-gui`/scripts/test-runtime-gui.py,在獨立隨機命名 Computer image 容器中執行 tests/runtime-probe-gui.test.py;network none、UID/GID 1000、1 GiB memory/2 CPU/512 pids,read-only fixture mounts、240 秒 host timeout,finally 清除自己的容器。
|
||
- 使用真實 Xvfb :8、XFWM、持續 session D-Bus、Chromium about:blank(9229)、x11vnc(5907)與 websockify(6087)。驗證 cold 全 false、全部服務 true,以及依序終止/重啟 browser、websockify、RFB server、WM 的 8 個狀態;每個狀態保留 Runner true,未受影響元件仍 true,None slot 不借用存在的畫面。10 個狀態全部通過;最終版每次 probe 以獨立 Python -I 程序、正式 clean env、256 MiB AS/20 秒 CPU/64 FD/無 core、8 秒 timeout/4096-byte stdout 限制執行,重跑仍全數通過。
|
||
- 首次真實驗證找到產品 bug:probe GET /vnc.html,但 image 僅安裝 index.html/vnc_lite.html,Supervisor 的正式 viewer URL 也是 vnc_lite.html,正常 viewer 因而一直 false。已修正到 /vnc_lite.html,協定 fixture 對其他未知路徑回 404,避免寬鬆假 server 掩蓋同類問題;6 probe tests 通過。
|
||
- 第二輪 WM 重啟 fixture 失敗原因為終止該 process group 一併終止其自動啟動的 session bus,並非探測結果不實;改獨立持續 D-Bus 後全部通過,保留失敗證據與分類。此次正向驗證涵蓋 WM/CDP discovery/實際 WebSocket-RFB greeting,不涵蓋全部 XFCE services、Chromium page 操作/profile process 身分、公開 viewer 登入/瀏覽器渲染、產品 ensure_screen 自動修復或整個 Computer 故障復原。
|
||
- 包含 root Runner source embedding 的完整 Docker gate 通過,API 真實整合段 26.52 秒;6 protocol tests、最終受限 probe 的真實 GUI 10 個狀態、Python compile 與 diff check 通過。測試容器/networks 已清除。O08 等相關項仍 PARTIAL,P0–P3 其餘範圍維持,未部署。
|
||
|
||
|
||
## 正式 ensure_screen 的桌面服務修復(2026-09-11)
|
||
|
||
- 找到 lazyboy-screen 兩個會略過修復的判斷:ensure_slot 的 X server/VNC/web port 快速路徑直接回成功;start_desktop 只看 panel PID,WM/xfdesktop 死亡不處理。移除無鎖快速成功路徑,所有 ensure 在既有 per-slot flock 內走相同檢查;start_desktop 分別核對 WM/desktop/panel,僅補啟動缺失者,且有界確認程序存活後才成功。
|
||
- alive_pidfile 僅接受正整數 PID,排除 /proc state Z/X,桌面服務另比对實際 comm;避免 zombie 的 kill -0 成功被當成正常服务。這不是完整 PID start-time/display identity 防偽,仍不宣告任意同 UID 程序隔離。
|
||
- 部分服務重啟重用既存 per-screen D-Bus address,明確 address 存在才做有界 ping;無 address 時直接建立新 bus,避免 dbus-send 的隱式 autolaunch 導致沒有可傳給桌面程序的 address/PID。AT-SPI launcher/registry 僅在原 PID 不存活時補啟動。D-Bus 本身中斷且所有桌面 PID 尚存活的完整復原仍待驗證/補強。
|
||
- 新 make test-screen-recovery 使用同一 networkless disposable Computer harness,read-only mount 當前正式 lazyboy-screen。真實執行 ensure 7,驗證 warm ensure 的 WM/desktop/panel/Cua/Xvfb/D-Bus 全部 PID 不變;分別終止 WM、panel、desktop 後,兩個並行 ensure 均成功,只更換故障服務 PID、其餘 PID 与 bus address 不變,實際 runtime probe desktop/viewer 恢復 true、未開 browser 保持 false。三種故障均通過,fixture 容器清除。
|
||
- bash syntax、diff check、9 個 control screen tests、59 個前端 regression 通過。此次修改 image 腳本並以現行 image 加掛工作樹腳本驗證,尚未重建/部署 production image;原完整 Runner gate 未重跑,因未修改其 root Python/Rust 傳輸。O08/T17 仍保持 PARTIAL:尚需全 X/D-Bus/Cua/browser 的正式自動復原、其他 display slot 並行及公開 GUI 完整流程。P0–P3 原範圍不縮減。
|
||
|
||
|
||
## 兩畫面正式修復與 Cua 截圖驗收(2026-09-11)
|
||
|
||
- 擴充 test-screen-recovery,使用正式 lazyboy-screen 同時 cold ensure slots 6/7(displays :7/:8),各自固定獨立 browser profile、DevTools ports 9228/9229。此驗收 fixture memory 提升到 2 GiB(probe-only fixture 仍 1 GiB),保留 network none/2 CPU/512 pids/256 MiB shm/240 秒總期限;不呼叫外部帳號或部署環境。
|
||
- 對 slot 7 依序終止 WM、panel、desktop、Cua、Xvfb、D-Bus、Chromium,每次發出兩個並行 ensure,要求故障 PID 更換、desktop/browser/viewer 全部 true。小範圍故障要求其餘 PID 不變;X/D-Bus 允許必要相依服務重啟,D-Bus 故障要求新 address。七種故障全部通過。
|
||
- 每次復原都實際執行 cua-driver call start_session 與 get_desktop_state,先刪除舊圖,再確認新 PNG 產生;並非只核對 socket 或進程存在。每次亦核對 slot 6 的全部 PID/D-Bus address 不變且三項真實 probe 仍 true。Chromium 的 process title 正規化後精確比對各 profile path 與 DevTools port;初版測試誤以為 Chromium 保留 NUL-separated argv,已依實際 process-title 格式修正,未把該 assertion 當成產品跨 profile bug。
|
||
- 最終真實兩畫面七種故障+Cua screenshot+peer unchanged 全數通過(/tmp/lazyboy-screen-two-displays-final.log),Python compile/bash syntax/diff check 通過,測試容器清除。本輪主要補實測,不重跑未改動的 Rust/前端。
|
||
- 這補上 O08/T17 的正式腳本服務與兩畫面復原證據;仍非 API owner/screen lease/control epoch 全鏈路故障驗收,不包含公開瀏覽器登入 viewer 操作、AT-SPI 語意操作在 D-Bus 斷線後的完整恢復、全八畫面資源公平性或所有 display backend。各完整項目仍 PARTIAL,P0–P3 範圍維持。
|
||
|
||
|
||
## API screen startup 授權與回覆驗證(2026-09-11)
|
||
|
||
- ensure_bot_screen 開始時在 transaction 鎖定目前 Computer/bot,核對 actor owner/space、bot assignment、provider/generation/running、home/scope/kind/profile mode/execution fence;配置與 per-task profile 更新亦在該 transaction 執行,不再先對任意 captured Computer 建立 ScreenRow。Computer row lock 亦序列化 slot 配置;移除不再使用的非交易 list_screen_slots helper。
|
||
- capabilities 失敗不再默認 multi_screen=true,5 秒期限;screen adapter context 補 captured generation,所有 busy retries 共用 90 秒期限。Provider 回傳 slot/display/view port 必須符合 captured row;啟動後再核對並鎖定 Computer/bot 與 screen identity/mapping/profile/execution fence,變動時拒絕舊結果。
|
||
- SDK 原先缺 slot/display/viewPort 時使用 request slot/:1/6080,並將過大 integer cast 截斷;改 serde required typed fields、alias view_port、精確 layout 比對,拒絕 ok=false/duplicate field/overflow/缺欄位。成功及失敗 body 均限 16 KiB 串流讀取,僅保留 busy starting 可重試分類,不回傳任意 guest error body。新增 serde workspace dependency。
|
||
- 新 SQLx+真實 HTTP regression 通過:foreign actor 不送 RPC、不建 screen;正常配置成功;RPC 中 generation/home/bot assignment/profile/screen fence/computer fence 變動、screen 刪除及錯誤 slot 回覆皆拒絕。SDK decoder regression 通過。完整 API 210 passed/4 ignored、完整 sandbox SDK 13 tests、affected all-targets Clippy(-D warnings)通過;diff check 通過。
|
||
- 本次是中央授權與結果 fence,不等於 guest 端已阻止所有在途過期副作用;restore_computer_screens 的其他 bot 恢復路徑仍需套用同樣核對,control epoch/lease 的全鏈路與自動修復授權仍未全部完成。P0–P3 原範圍维持,未部署。
|
||
|
||
|
||
## 其他 bot 畫面恢復的授權與指派核對(2026-09-11)
|
||
|
||
- 抽出 validate_screen_assignment,ensure_bot_screen 與 restore_one_screen 共用 Computer/bot owner、provider/generation/storage/profile mode/execution fence,以及 ScreenRow identity/slot/display/port/profile/execution fence 核對;另在 RPC 前拒絕不合法 slot/mapping。
|
||
- restore_computer_screens 不再把 get_bot 的 None 當空名稱繼續啟動。每個恢復先驗證目前授權與指派,送出 captured generation 及原 ScreenRow profile,核對 adapter 回覆後再次驗證;其他 bot 的恢復共用 90 秒期限,錯誤明確記錄並繼續適用的其餘項目,timeout 結束本批。
|
||
- 擴充 SQLx+真實 HTTP regression:正常 per-task 恢復沿用 original-task profile;foreign actor/解除 bot 指派在 RPC 前拒絕,呼叫數不變;恢復中八種 generation/home/binding/profile/fence/delete/wrong-slot 情境均拒絕舊結果。focused regression、最終完整 API 210 passed/4 ignored、API all-targets Clippy(-D warnings)及 diff check 通過。
|
||
- 此次補上中央恢復路徑,仍不宣告 guest 已阻止所有在途 stale epoch 副作用;真正 guest 層 lease/epoch admission/barrier 與完整公開 GUI 流程仍待完成。未改 Supervisor/image,未部署;P0–P3 原範圍維持。
|
||
|
||
|
||
## GUI context 傳遞鏈與結構檢查(2026-09-11)
|
||
|
||
- 查出 AdapterContext 雖有 screen_id/screen_lease_id/control_epoch,SDK 原先未傳送,API adapter_context_for 亦未設定 epoch。現以 captured ScreenRow execution_fence 傳送 control_epoch,SDK 保留 screen ID、lease、run、generation;API SQLx HTTP regression 實際核對 screen ID 與 epoch header 等於配置資料。這個 epoch 尚非 X restart/service epoch。
|
||
- Supervisor screens route 在 master token 驗證後核對必要 context:單一 header、合法 bot/screen reference、正 generation、有界非負 epoch、slot/display layout 以及 body/profile 一致;拒絕 missing/duplicate/overflow/identity mismatch,optional lease/run 若存在也須有效格式。兩個 Supervisor tests 包含 SDK→真實 HTTP→同一 validator 的成功與拒絕情境。
|
||
- SDK 的 header builder 改回傳 Result,所有呼叫傳遞錯誤;原先 space/bot/token 解析可能 panic、display/profile 解析失敗被略過,現在無法編碼的身份均以 INVALID_ADAPTER_CONTEXT 拒絕,不洩漏原值,不以移除 header 降級到預設畫面。14 SDK tests 通過,包含 epoch/lease 保留及非法 lease/display/profile。
|
||
- 最終完整 API 210 passed/4 ignored、14 SDK tests、2 Supervisor context tests、affected all-targets Clippy(-D warnings)及 diff check 通過。這是身份傳遞與結構檢查,**不是**目前 epoch/lease 授權或持久化 stale request admission;Supervisor code 亦明註必須由 Runner 對 durable state 核對。guest 層阻擋與 pause acknowledgment/barrier 等仍未完成,P0–P3 原範圍維持;未部署。
|
||
|
||
|
||
## Runner GUI durable fence 基礎(2026-09-11,尚未接入 GUI 執行)
|
||
|
||
- 新 runner_gui_fences.py 提供 root-only SQLite/WAL/FULL 狀態,保留 provider/generation 與各 slot 的 screen/bot/epoch/lease/expiry/paused;root-private directory/file/no-follow/hardlink 檢查。generation 只能前進,相同 generation 不可換 provider;相同 epoch 不可換 owner/screen/lease/paused,舊 epoch 拒絕,重播不縮短既有期限。admit 本身不建立或推進任何授權。
|
||
- generation publication 使用全域 exclusive flock;一般 display admission 共享 generation lock 並独占本 slot lock,其他畫面仍能 admit/publish。所有 lock waits 有界,scope 更新必須等該 scope 執行 guard 退出,generation 更新等待全體;等待後再檢查發布授權是否已到期。執行時不持有 SQLite transaction。
|
||
- 不能把 kernel lock 在 process crash 後自動釋放當成 quiescence:admit 先 durable INSERT active intent,再交給 executor;executor 必須明確 confirm_quiescent 才刪除。例外、程序被 kill、未明確確認,或完成 DELETE 失敗均留下 unresolved,拒絕同 scope 新 admission/epoch 與 generation publication。其他 slot 可繼續。active 狀態最多 8 slots,不依逾時自動清除。
|
||
- 11 個 root fixture tests 通過:missing/forward/stale identity、DB reopen 後 generation/owner 高水位、pause/resume、expiry與重播、真實跨程序鎖與 peer 並行、process kill、exception、正常返回未確認、SQLite generation rollback/intent insert/completion delete fault、等待期間 expiry、UID 1000 真實子程序拒絕、root directory/file/symlink 與 malformed claims。已加入 make test-gui-fences 及既有 Runner gate 的 host checks;Python compile/diff check 通過,fixture temp state 自動清除。未重跑未改動的 Rust/Docker/GUI 整合。
|
||
- **此模組尚未由 Runner bundle/GUI 路由呼叫,不宣告部署中的 stale GUI 已被阻擋。** 下一步需:中央 display epoch 跨 ScreenRow 刪除/重新指派仍單調(目前 execution_fence 重建可能回 0,不能直接當永久 slot epoch);只由已驗證 management publication 更新狀態,普通 GUI 請求不可自升 epoch;executor 持續保有 guard、遵守 expiry deadline 並驗證 quiescence;crash unresolved 須由可信 process/provider retirement proof 恢復,不能直接刪除;API pause/takeover 等待 root barrier acknowledgement 才授予控制。
|
||
- 對應 O27/T41/T56 與 scoped fencing 契約。這是可測的持久化基礎與接線前必須滿足的條件;GUI 全鏈路、agent/Computer 分層整合及 P0–P3 其餘要求維持未完成,未部署。
|
||
|
||
|
||
## 中央 display epoch 跨指派單調性(2026-09-11)
|
||
|
||
- migration 039 新增每 Computer/slot 的 computer_display_epochs 高水位及 ScreenRow.display_epoch BIGINT;刪除 ScreenRow 留下 counter 並推進 tombstone,重新插入/換 owner 不可從 0 重用。配置透過 counter row 的 ON CONFLICT UPDATE 鎖序列化,避免先取 sequence 後等待舊交易造成較舊 epoch 晚提交。Computer 父項刪除仍可 cascade,不把被刪除父項重新插入 counter。
|
||
- trigger 在 profile、identity/mapping、control holder/lease、execution run/fence、lease 縮短/清除及顯式 bump 時配置新 epoch;純 updated_at、no-op、相同 lease 延長不撤銷目前 epoch。外部傳入的 display_epoch 不直接採用;跨 slot 移動推進舊/新 counter,使用固定 counter 順序。
|
||
- API adapter context 改送 display_epoch;ScreenRow 讀取、啟動/restore 的前後核對及 health snapshot 一併接入。SQLx HTTP regression 增加只更新 display_epoch 的第九種在途變更,舊結果拒絕。
|
||
- 兩個新增 SQLx tests 通過:真實舊 schema 資料 backfill(包含 execution_fence=2147483647,升為 2147483648);rollback/delete/recreate/move/expiry renewal/shortening/manual reset 與 parent cascade;透過 pg_stat_activity 確認 replacement INSERT 正在等 slot lock,另一 slot 可更新,刪除提交後 replacement epoch 大於 tombstone。
|
||
- 最終完整 API 212 passed/4 ignored、API all-targets Clippy(-D warnings)及 diff check 通過。僅操作 SQLx disposable DB,未對部署資料套 migration,未部署。
|
||
- 高水位是配置器,不是已送达 Runner 的 barrier receipt:必須先提交中央 epoch/reservation 才能向 Runner 發布;無法把回滾交易中取到的 epoch 對外發布。Runner 管理發布、GUI executor guard/expiry/quiescence、未明效果恢復及中央 pause acknowledgement 仍待接線。P0–P3 完整範圍維持。
|
||
|
||
|
||
## Root GUI 簽章發布接收與 profile 綁定(2026-09-11)
|
||
|
||
- runner_gui_fences 已嵌入正式 root Runner bundle,新增 gui_fence_publish handler。Supervisor 只在此操作透過 root stdin 私有欄位傳入依實際 home label 派生、與 file retirement/controld token 分離的 HMAC key;一般 exec 輸入不能自行簽章。Root 驗證精確 schema、重複 JSON 欄位、audience/home/provider/bot/generation、60 秒發布授權、最長 5 分鐘 scope lease,鎖等待後再驗證授權期限,成功才回傳不含 key/簽章的 receipt。
|
||
- claim 與 durable screen/active intent 加入 profile。相同邏輯 profile 跨 slot 的 active effect 互斥;不同 profile/slot 仍可並行,穩定 generation 的 scope publication 不誤取全域 exclusive lock。舊 schema 缺 profile 的 active intent 保守阻擋,不自動清除;舊 screen 需要較新 epoch 才能重新綁定。profile 目前為嚴格邏輯路徑,尚非 filesystem inode/symlink alias 身份證明。
|
||
- 14 個 host fence tests 通過,涵蓋原真實 process kill/SQLite faults,新增 profile 排他/平行、legacy migration、簽章/時間/identity/schema 拒絕。真實 SDK→Supervisor HTTP→root Runner 驗收用 fixture-only signer,驗證有效 publication、竄改 MAC 明確 INVALID_GUI_PUBLICATION、合法簽章舊 epoch 明確 STALE_GUI_EPOCH,且回覆不含管理 key。測試曾誤把 SDK transport error 當 CommandResult,已依實際契約修正並要求精確拒絕原因。
|
||
- 最終完整 Runner gate 通過(/tmp/lazyboy-gui-publication-gate-verified.log,API 真實恢復整合 25.54 秒),Supervisor all-targets Clippy -D warnings 通過(停用 incremental)、Python compile/diff check 通過。成功 gate 自有容器/network 已清除,未部署。
|
||
- 目前完成的是受簽章保護的接收端;正式管理 issuer/API durable publication intent、GUI executor admission guard/deadline/quiescence、boot/service epoch 與可信 unresolved recovery 尚未接入。generation 與 scope 分兩次 commit,失敗可留下較新 generation,不能把未取得完整 receipt 當成功;中央 pause/takeover 仍必須等待 root barrier acknowledgement。O27 保持 PARTIAL,P0–P3 全部原定範圍維持。
|
||
|
||
|
||
## 正式 GUI 管理簽發與回執核對(2026-09-11)
|
||
|
||
- 新 GuiFenceClaim/GuiPublicationRequest/GuiPublicationReceipt 契約及 SandboxProvider.publish_gui_fence 專用方法,未支援 provider 明確拒絕,不透過一般 execute 模擬簽發。請求核對合法 identity、slot、profile、5 分鐘 lease 上限,以及完整 AdapterContext/home 綁定;呼叫者仍必須先提交並授權中央 epoch。
|
||
- 新 master-authenticated POST /computers/{id}/gui/publish,16 KiB body 限制;逐一核對 bot/screen/generation/epoch/slot/profile/lease/display header,拒絕重複或不一致。Supervisor 再核對實際 container home label,由管理端派生專用 key、產生 60 秒 HMAC publication,經既有私有 root 通道送出;不接受 caller envelope 或 key。
|
||
- Supervisor 與 SDK 均核對 strict typed receipt、完整 claim/paused/有效 expiry,不將其他 scope 或舊 epoch 回覆當成功;SDK 15 秒總期限,回覆串流上限 16 KiB。任何未取得有效 receipt 的呼叫不能視為已交接;特別是 timeout 可能發生在 durable publication 之後,中央必須保留 intent、重試原 epoch,不假定回滾。
|
||
- 159 個 control tests、15 個 SDK tests 與 affected all-targets Clippy -D warnings 通過。新增 SDK 真實 HTTP fixture 驗證有效 receipt,以及 wrong epoch/profile/paused、expired、published=false、未知欄位與不結束的過大 chunked response 拒絕,錯誤不洩漏 response canary。
|
||
- 真實 disposable Computer 的 SDK→正式 Supervisor signer→root receiver 驗證發布、重送相同 receipt、較新 epoch pause,以及 SDK context mismatch、HTTP missing auth/context、錯誤 home 和舊 epoch 拒絕;原 fixture-only signer 的有效/竄改/舊 epoch 接收測試保留。管理 HTTP 段通過(4.14 秒)。
|
||
- 尚未接中央 API durable publication intent/背景 retry/pause acknowledgement,也尚未由 GUI executor 持有 root admission guard,不能宣告部署中的 stale GUI effect 已全面阻擋。boot/service epoch、unresolved recovery 與 P0–P3 其餘完整要求維持未完成,未部署。
|
||
- 最終完整 Runner gate 通過(/tmp/lazyboy-gui-issuer-gate.log,API 真實恢復整合 26.34 秒);本輪 fixture containers/networks 已確認清除,最終 diff check 通過。
|
||
|
||
|
||
## Agent 暫停狀態與 display epoch 同交易更新(2026-09-11)
|
||
|
||
- 檢查中央 GUI publication 前置條件時發現:agent_mutation_barriers 的 paused 改變原先只推進 agent epoch,不更新 ScreenRow.display_epoch。Root GUI publication 將 paused 綁定同一 display epoch,因此先後發布不同 paused 值會形成衝突;只修簽发/重試不能解決。
|
||
- migration 040 新增 agent barrier 的 INSERT/UPDATE/DELETE trigger,只有實際 false↔true 變化才透過既有 039 counter allocator 推進該 bot 的全部 display epochs,與中央 barrier 寫入同一交易。初始 false、重複 true/false 不推進;刪除 paused barrier 及 bot_id 移動亦正確失效舊/新 bot scope。其他 bot 的獨立 display 不受影響。升級時既有 paused bot 的 display epoch 也會推進。
|
||
- 新 SQLx regression 覆蓋正式 set_agent_barrier helper、直接 SQL、重複狀態、交易內新值/交易外未提交值不可見、rollback 保留原 epoch、pause/resume/delete、barrier bot 移動、counter 與 screen 一致,以及真實 populated pre-040 schema migration backfill 僅推進已 paused peer。
|
||
- 最終完整 API 213 passed/4 ignored(/tmp/lazyboy-agent-display-barrier-api.log,27.39 秒),API all-targets Clippy -D warnings、diff check 通過。只操作 SQLx disposable databases,未對部署資料套 migration,未改 root GUI 程式或重新執行 Docker gate。
|
||
- 這是中央 epoch 原子性修復,仍不是 root 已確認停止/交接的證據;API durable publication intent/retry/acknowledgement 與 GUI executor guard 尚待接入。P0–P3 全部原定範圍與 O27 PARTIAL 維持,未部署。
|
||
|
||
|
||
## 中央 GUI 發布意圖與啟動畫面接線(2026-09-11)
|
||
|
||
- migration 041 新增 gui_publications,保存不可由 transport error 刪除的精確 request、pending/acknowledged、receipt 與確認時間。啟動畫面及其他 bot 的畫面恢復均呼叫新 publish_screen:在鎖定目前 owner/bot/Computer/ScreenRow identity 與 display epoch 後保存意圖,commit 後才送專用 provider publication;網路期間不持有 DB transaction。
|
||
- 發布 claim 使用 captured generation/display epoch/profile/execution run,paused 由目前 agent barrier 或 human holder 決定;一般期限 120 秒,執行中 run 另受 execution lease 上限限制。重試重用相同 claim/home/paused 且仍有 30 秒期限的既有精確意圖,不因回覆遺失建立另一個同義請求;過期歷史 pending 不自動刪除,每 Computer/bot 最多 1024 個 unresolved。acknowledged 歷史 retention 與 expired pending 的可信收斂仍待完成。
|
||
- provider 回覆後重新鎖定並核對目前指派,等待 DB lock 後再次檢查 expiry;只有完整 typed receipt 匹配且 intent UPDATE 恰好一列,才同交易保存 acknowledged。途中 pause 的 migration 040 epoch 變更使舊回覆拒絕;未收到有效回執或 DB 寫入失敗仍保留 pending。
|
||
- SQLx+真實 HTTP fixture 驗證:foreign actor 不發布;handler 收到請求時 DB intent 已提交;502 後重用同一 id/request;發布途中中央 pause 不被 DB/network lock 阻塞,舊 acknowledgement 拒絕;receipt UPDATE trigger 故障回滾保留 intent,重試成功。既有 startup/restore 授權與九種在途指派變更測試仍通過。FakeSandbox 新增明確 transport-only publication fixture,不把它當 root locking/crash 證據。
|
||
- 最終完整 API 214 passed/4 ignored(/tmp/lazyboy-gui-central-api-tests.log,28.45 秒)、15 SDK tests、affected all-targets Clippy -D warnings 及 diff check 通過。僅 SQLx disposable DB,未套用部署 migration。
|
||
- 此次是 startup/restore 的前置發布與 request-driven retry;背景 retry、中央 pause/takeover 等待 root acknowledgement、GUI executor guard/deadline/quiescence、boot/service epoch 及完整人類輸入管制尚待接線。P0–P3 全部原定範圍與 O27 PARTIAL 維持,未部署。
|
||
- 真實跨程序完整 Runner gate 亦通過(/tmp/lazyboy-gui-central-gate.log,API 整合 26.62 秒):由正式 boot_for(false) 重建的 Dedicated Computer,中央保存意圖後經正式 signer 到 root 成功發布,proxy 僅丟棄第一次已成功回覆;中央先維持 pending,第二次以同一 intent id/精確 request 取得有效 receipt 並 acknowledged,實際 publication RPC 共兩次。此測試不啟動 GUI effect,也不代替 GUI executor admission 驗收。自有 fixture containers/networks 已確認清除。
|
||
|
||
|
||
## GUI publication 背景重試與原始 actor 綁定(2026-09-11)
|
||
|
||
- API startup 新增 retry_loop,每輪至多取四個 due scopes,每 Computer/bot 一個候選,SKIP LOCKED 避免被別的交易持有的 intent 卡住;retry_after 30 秒 claim/backoff 在 migration 042 持久化,query 5 秒/單筆 delivery 20 秒/provider 15 秒上限,最多四筆並行,輪間 2 秒。這是 delivery 排程,不延長原 GUI lease,不自動建立新 publication。
|
||
- foreground 與 background 共用 deliver,發送前與回覆後核對目前 Computer/bot/ScreenRow assignment;發送前另核對原 claim/context、paused、run lease 上限及精確已提交 intent。背景僅處理原 request 未過期者,過期與未明歷史保留 pending,不把 expiry 當作 quiescence。
|
||
- migration 042 另保存原 owner_user_id/owner_space_id。發送與 ack 都核對此 proof,防止把舊 intent 以後來的新 owner 身分重送。舊 schema 的 intent 缺 actor proof 時保持 NULL、不由目前 owner 推定補值,background 排除;新的 explicit startup 可保存目前合法 actor 的新 intent。
|
||
- SQLx HTTP regression 改由背景批次完成 lost response 與 DB ack fault 後的原 request 重試;驗證舊 epoch 不發 RPC、過期/缺 actor proof/錯誤 owner 不重送且 pending 不刪。新增六 bot 的 bounded batch 測試:另一交易持有 intent lock 時,其他四 scope 先成功,其餘 scope 繼續;解鎖後才完成原 scope。這是批次與鎖行為證據,不代表任意大量歷史 backlog 的公平性已驗收。
|
||
- 完整 API 215 passed/4 ignored(/tmp/lazyboy-gui-retry-api.log,30.87 秒)、API all-targets Clippy -D warnings/diff check 通過。真實完整 Runner gate 通過(/tmp/lazyboy-gui-retry-gate.log,API 整合 26.32 秒):root 成功 publication 回覆被 proxy 丟棄後,由 retry_batch 讀取同一 durable intent、透過正式 signer 重送並取得 ack,未重新呼叫 startup 產生 authority。重用未改動的已建 controld fixture image;測試自有 containers/networks 已確認清除,未部署。
|
||
- 後續仍需改善大量同 scope 舊 pending 的 current-intent 優先序與掃描成本(目前依 due/created 排序,不能把小型六 bot 測試當作大 backlog 無飢餓證明);過期/缺 proof pending 的可信收斂與 acknowledged retention 尚缺。中央 pause/takeover 等待 root acknowledgement、GUI executor guard/deadline/quiescence、boot/service epoch 與完整人類輸入管制仍待接線。P0–P3 原定完整範圍維持,O27 仍 PARTIAL。
|
||
|
||
|
||
## GUI retry 排除過時 backlog 與 Computer 授權快照(2026-09-11)
|
||
|
||
- 修正背景候選只按 due/created 排序的缺口:同 scope 大量尚未到期但已過時 epoch,原先可能逐筆占用批次,令目前有效 intent 等到過期。候選現在先 join 目前 Computer/bot/screen,核對 owner/space、running、home、完整 claim、paused 及 execution lease 上限;過時記錄不取得 retry claim,也不刪除。真正送 RPC 前/後仍保留交易內授權重查,不以候選快照替代 fence。
|
||
- 同時補上原 request 尚未保存的 Computer 授權狀態:migration 043 新增 computer_authority JSONB(scope/kind/browser_profile_mode/execution_fence)。reserve、重用、發送、ack 与背景候選均核對原快照,避免 Computer fence 或 profile mode 已變更後,背景 worker 以新 ComputerRow 重送舊授權。歷史缺快照保持 NULL,不推定 backfill,須新的 explicit request。新增 current claim/expiry partial indexes,將具原始授權的 pending 查詢與歷史資料區分。
|
||
- SQLx 六 bot fixture 加入 900 筆較早、仍未到期的舊 epoch 與 100 筆過期 intent;持有有效 intent 的外部 transaction lock 時,其餘四 scope 於 3 秒測試期限內完成,下一個有效 scope 繼續,解鎖後完成原 scope;全部 1000 筆 obsolete pending 原 retry_after 未變且仍保留。這驗證指定 backlog 下不占用候選,尚非任意量級/全 provider 公平性保證。
|
||
- HTTP fixture 另驗證缺 Computer snapshot、Computer execution_fence 變更、browser_profile_mode 變更均不發 RPC;原 owner/expired/lost reply/ack DB fault/concurrent pause 與 exact request retry tests 保留。
|
||
- 完整 API 215 passed/4 ignored(/tmp/lazyboy-gui-backlog-api.log,30.75 秒)、API all-targets Clippy -D warnings/diff check 通過。完整真實 Runner gate 亦通過(/tmp/lazyboy-gui-backlog-gate.log,API 整合 25.82 秒),包含原 root 成功 publication lost reply 後的 background acknowledgement;中途 UI 誤按中斷未重啟仍在執行的 gate,確認原 handle terminal 後收集結果。自有 fixture containers/networks 已確認清除。未套用部署 migration/未部署。
|
||
- 大規模全域吞吐/公平性量測、pending 可信收斂與歷史 retention 仍需完成;下一步仍是 GUI executor admission guard/deadline/quiescence 與 pause/takeover acknowledgement,全 service epoch/人類輸入/任意 shell 隔離亦未完成。P0–P3 全部範圍維持,O27 仍 PARTIAL。
|
||
|
||
|
||
## GUI transport target 綁定修復(2026-09-11)
|
||
|
||
- 接 GUI executor guard 前查出 routing 缺口:Supervisor act 允許 body display/profile 覆蓋 header target,browser/recording 在 body-only target 情況又可能由 header/default target 決定另一畫面。新增共用 checked_gui_target,body/header 同時存在必須相同,slot/display 必須符合既有八畫面 layout;明確 empty/非法 display/slot/profile control characters 拒絕,不默默降到 primary。無 target 的既有 primary fallback 保留,這不是授權成功證明。
|
||
- Supervisor 及 controld 的 act/browser/recording start/stop/collect 均先呼叫 strict header wrapper,拒絕重複 display/profile/slot header(包含相同重複值),再使用一致 target;controld 不再在這些路徑以 header precedence 隱藏 body 衝突。profile 在此僅檢查 transport 結構與一致性,合法 filesystem scope/實際 inode 身分仍由後續 admission 契約處理。
|
||
- 160 control tests、15 SDK tests、兩個服務的 header regression 與 affected all-targets Clippy -D warnings 通過;Python compile/diff check 通過。
|
||
- 新 disposable tests/gui-targets.test.py 啟動本轮重新編譯的實際 controld,對五個 GUI 路由測 display/profile mismatch、重複 display/profile 及 slot mismatch,共 25 cases 全部得到 route 層的精確 400 Bad Request,結束後回收該 fixture daemon。新增至完整 Runner gate;Supervisor HTTP fixture 同時增加五個路由拒絕 conflict 的驗收。
|
||
- 此次沒有將普通 HTTP 成功當作 quiescence,也尚未接 root GUI executor guard。現行 Computer start 會以 lazyboy UID 啟動 controld,任意同 UID shell/服務身分与可信 completion 邊界仍需補強;executor 在 timeout/斷線時不得自動 confirm_quiescent,pause/takeover 必須等可信 root barrier ack。P0–P3 所有原定範圍維持,未部署。
|
||
- 最終完整 Runner gate 通過(/tmp/lazyboy-gui-target-gate.log,Supervisor HTTP 4.11 秒、API 真實恢復整合 26.92 秒),包含實際 controld 25 種拒絕案例及 Supervisor 五路由 conflict 拒絕。自有 fixture containers/networks 已確認清除。
|
||
|
||
|
||
## Cua 未明呼叫不再自動重開 session 放行(2026-09-11)
|
||
|
||
- 查出 CuaClient 在 mutation timeout 後,下一次 mutation 會 remove suspect marker 並嘗試 start_session;即使 driver 拒絕或其他錯誤,也可能丟失 marker。新 session/HTTP 可回覆都不能證明舊 driver input 已停止,因此移除此自動修復放行。
|
||
- 新 CuaClient 本地 MutationGuard 共用八個 per-display mutex,單次 admission 等待上限 5 秒;進入執行後如果 timeout、outer future 被取消或不明 transport/driver outcome,Drop 在釋放 lock 前保存 unresolved marker。新 mutation/start_session 對同 display 返回 EffectUnknown;其他 display 繼續。只取消尚未取得 guard 的等待者、不曾啟動的缺失 driver,以及正常完成的已知結果不留下 false unresolved。
|
||
- Read path 若遇到 session ended、需要 start_session,也必須取得同樣 mutation guard,不能以 read 名義清掉未明 writer。一般讀取沒有新增 writer lock。delivery-mode escalation 限制為已知 semantic refusal/busy,timeout、DriverUnhealthy、Internal、EffectUnknown 等不再因 recommended delivery 而重送。controld 將 EffectUnknown 回傳 409。
|
||
- 164 個 control tests、2 個 controld tests 與 affected all-targets Clippy -D warnings/diff check 通過(/tmp/lazyboy-cua-unresolved-verified-tests.log)。新增測試包含共享 client 的真實 task abort、等待者取消、peer display、不存在 driver、未知 outcome 不 escalation;另以隔離暫存 fake CLI 啟动真實子程序,等待其讀完 request 後取消,確認 CLI PID 已消失仍不允許 start_session,peer 的兩次正常 call 都成功。此 fixture 只測真實程序/transport cancellation,不是實際 GUI input 完成證明。測試 script 的第一版 Rust 引號錯誤已修正,最終重新編譯/測試與 Clippy 通過。
|
||
- 完整 Runner gate 通過(/tmp/lazyboy-cua-unresolved-gate.log,API 真實恢復整合 26.70 秒),重新建置 controld fixture,原實際 GUI target 25 cases/Supervisor HTTP/native/MCP/retirement 與 publication retry gate 保留。測試自有 containers/networks 已確認清除,未部署。
|
||
- **這是目前 controller instance 內的防護,不是 root durable GUI executor guard。** marker 仍在記憶體,跨 controller instance/process restart 不保留,也未提供可信退休 proof 的清除流程;跨 profile 排他、整個 multi-call GUI operation guard、同 UID shell/daemon authenticity、server-side human input、pause/takeover barrier 仍需完成。不可把 CLI 已退出/一般 HTTP 成功當成遠端 GUI effect quiescence。P0–P3 原範圍維持,O27 PARTIAL。
|
||
|
||
|
||
## GUI 觀察與健康路由的 target 驗證(2026-09-11)
|
||
|
||
- 延續 O26/O27/O46 檢查,修正 controld observe/controller health 仍使用寬鬆 display fallback 的缺口,以及 Supervisor observe 未使用同一 strict target validator 的差異。三個路徑現在拒絕明確空值、非法或遠端 display、重複 target headers 與 slot/display 衝突,使用已驗證的 display/profile 建立 context;未指定 target 時保留既有 primary 行為。Team/Dedicated 共用相同檢查,不改 assignment 或資料。
|
||
- 擴充 disposable controld HTTP fixture:兩個觀察/健康路由各增加 7 種拒絕案例,合計由 25 增為 39;Supervisor 真實 HTTP fixture 增加 observe 空/非法/遠端 display 與重複 header 拒絕。這些案例驗證路由在 backend 前返回 400,不等於實際 GUI effect 或 admission/quiescence 驗收。
|
||
- 兩個服務共 3 個 header/error regression tests、affected all-targets Clippy -D warnings、Python compile 與 diff check 已通過。完整 Runner gate 以重新建置的 controld 通過(/tmp/lazyboy-gui-read-target-gate.log):39 種實際 controld HTTP 案例、Supervisor HTTP 4.07 秒、API 真實恢復整合 26.26 秒;原 native/files/MCP/publication retry 驗證保留。自有 fixture containers/networks 已確認清除。
|
||
- 回滾可還原這三個讀取路由的 target 選取及新增 fixture;未部署。root durable executor、可信完成/退休 proof、pause/takeover acknowledgement 與全部尚缺 P0–P3 項目持續保留,下一步檢查 cursor configuration 的 timeout 是否被忽略而繼續 mutation。
|
||
|
||
|
||
## Cua 游標設定未明即停與使用量暫停交接(2026-09-11)
|
||
|
||
- 修正 configure_cursor_motion 的 500 ms timeout/不可解析回覆/driver timeout 原先被忽略且寫入 cache,隨後繼續 click 的問題。現在返回 Result,未知結果向外傳遞,既有 MutationGuard 保存 unresolved;不送後续 input、不 cache 未明設定。session restart 後的設定同樣傳遞錯誤。已知 semantic refusal/busy 的 optional setting 保留相容 cache 行為。
|
||
- 真實 fake CLI fixture 驗證 timeout、malformed、structured timeout 三種失敗只收到 set_agent_cursor_motion,下一次 click 得 EffectUnknown;unsupported 設定可正常連續兩次 click 且只設定一次。這是本地 transport 證據,不是遠端 GUI quiescence,也不改變 root durable executor 尚未接線的狀態。
|
||
- 最終 165 control tests+2 controld tests 通過(/tmp/lazyboy-cua-motion-tests-final.log);Clippy 最初報 collapsible-if,已改成同語意 let-chain,最終 affected all-targets -D warnings 通過(/tmp/lazyboy-cua-motion-clippy-final.log)。容器 gate 使用該純語法整理前的同語意 runtime snapshot;host 最終測試/Clippy 覆蓋整理後 source。
|
||
- 使用者表示剩約 10% 使用量,要求列已做/未做並收尾等待。已新增 [暫停交接文件](agent-computer-handoff-2026-09-11.md),逐項保留 O01~O48 状態、完整待辦與操作交接;不再開新功能。已啟動的完整 Runner gate 完成後補記,工作樹保留未提交,未部署。
|
||
|
||
- 收尾完整 Runner gate 已通過(/tmp/lazyboy-cua-motion-gate.log,Supervisor HTTP 4.19 秒、API 整合 26.82 秒);本次 fixture containers/networks 已確認清除,所有測試 handle 結束。最終 diff check 通過,依使用者要求暫停等待恢復,不啟動下一個實作。
|