# Implementation Plans

Generated by the improve skill on 2026-07-05, planned against commit `ac47f3b`.
Source: full read-only audit of `apps/web` and `apps/server` across correctness,
security, performance, tech-debt/architecture, and test coverage.

2026-08-01 在 commit `60568afe` 上补充 Plan 073（XL）：Provider 一等身份完整轨 —
identity + kill kind→auth + dual-endpoint first-class（DeepSeek/Moonshot 等）+
显式 Bind + Import suggest 确认写入。**明确不做** Kimi OAuth。聚焦 `/improve plan`。

Supplemented on 2026-07-14 against commit `e3d008c` from the five-direction
web/server architecture review. Plans 042-046 were independently audited and
then vetted against current source; they do not reopen completed Plans 024,
035, 040, or 041.

Supplemented again from the exhaustive architecture review on the same commit:
Plans 048-051 cover the four remaining evidence-backed correctness/ownership
directions selected for execution. Plan 047 is the independently prepared
Download Center track and remains unchanged.

Supplemented on 2026-07-14 against commit `c450147`: Plan 052 replaces Codex's
chat-session-scoped app-server processes with one provider-target-owned host that
multiplexes isolated threads. It was requested as a focused plan and does not
reopen the wider audit.

2026-07-15 在同一 commit 上补充 Plan 054：修复 Web 内页 multiplex WebSocket 的
half-open 收包中断、transport seq/run cursor 混淆与不可恢复 end handling。它是已完成
Plan 014 的后续 breaking protocol refactor，不涉及 Desktop、滚动 UI 或 DB schema。

2026-07-15 在 commit `3dca102` 上补充 Plans 056-057：Plan 056 先建立声明式
Managed Resource Catalog 与统一 Resources/Transfers 页面，让 owner 在下载前声明资源，
并以精确 resource identity 关联 Download Center 任务；Plan 057 再把内置 OpenCode
adapter/SDK 对应的 `opencode` CLI 接成可选 managed runtime。两者是聚焦的
`/improve plan`，不重启全仓审计，也不把 Download Center 扩成 generic installer。

2026-07-16 在 commit `fa17597` 上补充 Plan 058：为 Desktop Server 数据根增加
可选位置、可后续迁移和 crash-safe 的复制/校验/切换/回滚流程。只迁移 server-owned
data root，保留 Electron userData 作为固定 bootstrap/cache 根；旧目录只有在新根
健康启动后才归档为 `.bak-*`，不在同一事务内删除。

2026-07-17 在 commit `2867b64` 上补充 Plan 059：可编程 JavaScript Session Await。
源自 GitHub issue #35 的讨论收敛：v1 用受管 Node 子进程（V8 old-space 设置 +
进程组超时终止）执行 Agent 编写的 inline JavaScript cell，明确不引入 QuickJS/WASM isolate、
不做第二套权限系统；新增 `javascript-eval` 模块（standalone evaluate 路由）与
session-await 的 `javascript` source，终态失败经 durable queue 恢复 Session
（`resumeOnFailure` opt-in，仅 javascript 启用）。是聚焦的 `/improve plan`，
不重启全仓审计。

2026-07-18 在分支 `feat/enhance-work` 的 commit `038ca7d7` 上补充 Plan 060：把
agent 交付工具 `work_submit` 改名为 git 语义的 `manage_pull_request`（Work 概念只保留在
用户-facing 的 CLI/UI/路由），并新增「首个 PR 存在之前」的 branch rename 能力
（`git branch -m` + `worktrees.branch`，硬校验无 bound PR 且远端无此 branch）。
明确不做 agent-tools 的 env/session-header 上下文解析（maintainer 已否决，`workId`
保留为显式参数）。执行前必须先切到 `feat/enhance-work` 或其下游分支。
（编号 059 已被 `advisor/059-javascript-session-await` 分支上的
`059-programmable-javascript-session-await` 占用，故本计划取 060。）

2026-07-20 在 commit `b2d90065` 上补充 Plan 061：针对 Session
`8f9964fe-9df6-4d09-aa28-88f0aeee0235` 的 880-way Claude synthetic turn storm，
把 all-origin admission、durable terminal、boot recovery 与 bounded history hydration
收敛为一个 Chat Runtime 生命周期方案。Plan 061 取代只覆盖 completion 的 Plan 044；
不删除历史 event，而以 upgrade-safe recovery、DB 单 streaming-run invariant 和 cursor
pagination 同时封死写入 fan-out 与打开 Session 的无界 CPU 成本。

2026-07-21 补充 Plan 061：Cradle Recall（Cradle 版 Obelisk agent design）。先落
Agent Cognition Stack（L1 Evidence → L5 Orchestration）与 CodeAct 契约
（`recall_query` / `recall_attune`），再分阶段实现 runtime。明确不做 harness 自动注入、
不做 helper 级 MCP tool sprawl、不引入 async CQRS 索引。Phase A 仅 design docs + skill；
implementation 建议等 Plan 050/051 收敛 session/issue projection 后再做 Phase B。

2026-07-21 在 commit `be474dca` 上补充 Plan 062：以 Claude Agent SDK 原生会话为权威，
把 Cradle 从「第二套 turn scheduler」收敛为 session 事件投影层。保留长期 Query +
AsyncIterable；允许 post-result 开新的 UI Run（展示容器）；删除 absorb/adopt/
queued_command 文本匹配 / 150ms settle，以及用 UI Run 完成态阻塞 `inputStream.push`
的做法。与 Plan 061 互补：061 管 UI Run 准入与风暴，062 管 Claude 输入通道与
native 队列所有权。

2026-07-23 在 commit `598007aa` 上补充 Plan 063：解决 Desktop Tearoff 共享 Chromium
HTTP/1.1 六连接池导致的 SSE/fetch 饥饿。初版曾规划 multiplexed 子进程 IPC framing（后已废弃）。

2026-07-31 在 commit `00ba970e` 上重新核验 Plan 063：Electron 42 开发态已验证
custom-protocol streaming/cancellation、FormData、image、dynamic import、binary、renderer
stream upload 与 default-session isolation，以及 default session 的 HttpOnly auth cookie。
packaged artifact、64 MiB memory bound 与真实 plugin module/CSP 仍是 M0 硬门槛。计划纳入
bootstrap lifecycle、raw upload/plugin fetch，以及 Plan 071 的 snapshot-first recovery；
不得恢复 active-run replay。

2026-08-02 重写 Plan 063 架构（锁定）：废弃 multiplexed child-process Request/Response
framing、`desktop-server-contracts` pull-credit 协议、Server `desktop-transport/` IPC host、
以及 PTY-over-IPC。改为 `cradle-server://local`（defaultSession `protocol.handle`）→ Main
剥离 renderer 凭证并注入 owned bearer → undici Agent 代理到现有 Elysia HTTP listener
（v1 loopback；v2 可选 UDS/named pipe）。PTY 与 `/sync` 保持 native WebSocket，经 Main 在
defaultSession 建立的 HttpOnly browser-session cookie 鉴权（WS 不占用 HTTP/1.1 六连接池）。
连接判别改为 `owned-proxy` / `attached-http`（不再使用 `owned-ipc`）。不变量是「renderer
对 owned Server 零 Chromium HTTP/1.1 pool 连接」，而非发明私有协议的 zero-socket。
Effort XL→M–L；M0 packaged custom-scheme gate 仍为 STOP；若 M0 失败则另议 Plan B
（local HTTP/2 TLS），不得回退到 IPC framing。只迁 transport，不改 Plan 061 Chat lifecycle
或 Plan 071 snapshot-first recovery。

2026-07-26 在 commit `cc3facef` 上补充 Plans 065-069：Claude Agent SDK 集成正确性系列，
源自对该 provider 的完整审查（advisor 会话转录）。065 修权限启动竞态、dispose 未接线、
getPresentation 子进程风暴与 snapshot 无界数组；066 让长生命周期 Query 成为历史与配置的
权威（修 claudeAi 每轮全量历史重放 O(N²)、claudeAi 模型切换丢失、每轮重建被丢弃的
queryOptions、长会话 maxTurns=100 静默死亡、apiKey 模式 settingSources 不加载 CLAUDE.md）；
067 用 PreToolUse hook 让 ExitPlanMode 硬拒绝与 AskUserQuestion 传输在所有权限模式下成立；
068 把 3041 行的 provider.ts 按 codex owner 目录形态拆分、重新论证 workflow
declaration-extractor 的 worker 执行式启发、删除 provider-base.ts 中无消费者的
permissionMode 投影。不重开 Plan 062 既定设计。执行顺序 065 → 066 → 067 → 068。
同日追加 Plan 069（架构方向，经讨论确认）：把 claude-agent state snapshot 降级为
resume-state + 可丢弃 checkpoint,UI activity feed 改为从 SDK transcript JSONL 与 Cradle
事件历史重建的 read model（仓内范本：workflow/artifact-stream.ts 的 journal+reducer
形态）。分两阶段：Phase A 逐字段证明可重放性、不可重放的补 write-once 事件；Phase B
切换冷读路径、blob 删除 feed 数组（kit/state-snapshot 注册 v1→v2 迁移）。065 的数组 cap
保留为 checkpoint 上限。同日追加 Plan 070（测试方向，经讨论确认）：兑现
advisor-plans/001 留下的"provider 接入 simulator"后续——vitest globalSetup 全局单实例
@cradle/model-api-simulator,claude-agent 集成测试走真实 CLI 子进程 + 真实 HTTP 线路,
把 mock 掉 SDK 后自指的行为断言（历史重放、权限门禁、cancel 进程生命周期）迁移到
request ledger 断言;Options 构造类断言保留 unit test。Step 1 为可行性门(CLI 离线
带不起来就 STOP,维持 unit 现状)。

2026-07-27 在 commit `28ff968c` 上补充 Plan 071：删除 top-level active Chat run 的
append-only `UIMessageChunk` replay retention。Plan 054 的 run-owned monotonic cursor、
terminal ordering 与 Sync liveness 保留；所有 active-run 初次连接/重连统一从当前
`UIMessage + UIMessageStreamState` 生成 snapshot，再衔接更大 cursor 的 live chunks。
原 replay observability 改为不持有 payload 的 scalar publication counters。明确不做
payload 截断、provider 限流、DB raw-chunk log、短 tail cache 或兼容 shim。

2026-07-29 在 commit `facc38f5` 上补充 Plan 072：把 chat message 的字节搬进按内容寻址的
blob store，并且是无损的。起因是单 session `message_json` 观测到 ~117 MB（base64 附件内联 +
tool 输出内联），以及一个静默 bug：超过 128 000 字符的 tool payload 在持久化时被销毁成
`cradle.truncated-json-payload.v1` 标记，而 `apps/web` 完全不认识该标记，导致大 tool 结果渲染成
空块。方案把 Step 0（渲染修复，纯前端零依赖）排在最前，随后是 blob store + 引用表 + GC、
统一的 externalization seam（附件字节与 tool payload 同一次遍历）、单一 `toDurableMessagePayload`
序列化收口、provider 反向投影、前端 `cradle-blob://` 解析，最后 Step 9 回填现有胖行——只有回填
才让已有 session 真正变小，前面各步只是止住新增长。经讨论明确砍掉三件事：不改道 `assets`
写入（避免 `ALTER TABLE assets` 与 `deleteAsset` 行为变更，两个字节存储的重复作为已记录的
延后债）、不外置 composer draft 附件（`surface_id` 不是 session id，且软删会让 GC 永久 pin
住 blob，收益也不随历史累积）、不给 `chat_message_payloads` 加 `schema_version`。本计划取代
最初的 072/073/074 三份拆分草稿与其未写的 075 回填。

2026-08-02 补充 Plan 073：Cradle Platform Constitution — Jarvis as Agent Kind。
**宪法/方向**文档（非实现计划）。核心定论：Jarvis 是窄义 Platform Kind（管家身份），不是聊天升级、不是 HiJarvis 品牌、不是第二调度器；默认 propose-before-act；诚实委托语义 + 工作账本 + Session 执行载体双中心；IRON LAW 升格为对所有 native 与 Kind 概念本身的宪法。竞争姿态是拒克隆 / niche 天花板，不是征服。人类决策：accept / amend / reject。不重开 Plan 061/062 的既定生命周期边界。

Each executor: read the plan fully before starting, run its drift check, honor its
STOP conditions, and update your row below when done. Plans are self-contained —
they do not assume you saw the audit or any other plan.

## Execution order & status

Ordered by leverage (security/correctness first, structural refactors last).

| Plan | Title                                                    | Priority | Effort | Depends on | Status                                                                                 |
| ---- | -------------------------------------------------------- | -------- | ------ | ---------- | -------------------------------------------------------------------------------------- |
| 001  | Fix embedded browser (WebContentsView) flicker/occlusion | P2       | L      | —          | DONE                                                                                   |
| 002  | Add auth to HTTP + WebSocket surface                     | P1       | L      | —          | DONE                                                                                   |
| 003  | Require auth on relay-tunneled traffic                   | P1       | M      | 002        | DONE                                                                                   |
| 004  | Constrain filesystem browse + shell cwd to roots         | P1       | S      | 002        | DONE                                                                                   |
| 005  | Close SSRF gaps (link-preview + provider-catalog)        | P1       | M      | —          | DONE                                                                                   |
| 006  | Stop returning relay pairing codes on read routes        | P1       | S      | 002        | DONE                                                                                   |
| 010  | Fix issue-agent run-tracking races + delegation tx       | P1       | M      | —          | DONE                                                                                   |
| 013  | Include apps/web tests in root test run                  | P1       | S      | —          | DONE                                                                                   |
| 014  | Crash-safe SSE/WS handlers + cursor-correct reconnect    | P1       | M      | —          | DONE                                                                                   |
| 007  | Secrets key versioning + rotation path                   | P2       | M      | —          | DONE                                                                                   |
| 008  | Harden server plugin loading (provenance + confirm)      | P2       | L      | —          | DONE                                                                                   |
| 009  | Gate worktree setup-hook execution behind confirmation   | P2       | S      | —          | DONE                                                                                   |
| 011  | Coalesce check-then-act races on registries              | P2       | M      | —          | DONE for surviving site (managed local relayd single-flight); host connector already reserves before start; stale conversation scope superseded by 2026-08-01 relay hardening |
| 012  | Terminal cleanup + error handling on run/boot paths      | P2       | M      | —          | DONE                                                                                   |
| 015  | Add route-level error boundaries                         | P2       | M      | —          | DONE                                                                                   |
| 016  | Version + validate persisted Zustand stores              | P2       | M      | —          | DONE                                                                                   |
| 017  | Reduce chat streaming render cost                        | P2       | M      | —          | DONE                                                                                   |
| 018  | Code-split heavy panels and routes                       | P2       | M      | —          | BLOCKED (drift check: settings/chat feature files changed since ac47f3b)               |
| 019  | Rewrite apps/server/AGENTS.md to match Elysia            | P2       | S      | —          | DONE                                                                                   |
| 022  | Characterization tests for untested web paths            | P2       | M      | 013        | BLOCKED (test seams: driver hook and file-tree live sync require large mocks/refactor) |
| 020  | Split the largest god files                              | P3       | L      | 022        | REJECTED (superseded by ownership-first Plan 041)                                      |
| 021  | Restore server dependency direction (infra≠modules)      | P3       | M      | 019        | REJECTED (superseded by enforced domain boundaries in Plan 041)                        |
| 023  | Consolidate web data-fetching onto generated hooks       | P3       | L      | 022        | REJECTED (superseded by feature gateways in Plan 040)                                  |
| 024  | Rebuild the chat core on native Event Sourcing           | P2       | XL     | —          | DONE                                                                                   |
| 025  | Authoritative usage fields per runtime provider          | P1       | M      | —          | REJECTED（Codex run-final cumulative 会重复历史；Plan 055 改为逐调用事实）             |
| 026  | Publish plugin SDK + open marketplace to any repo        | P2       | M      | —          | DONE                                                                                   |
| 027  | Persisted, live-reloadable external plugin sources        | P1       | L      | 026        | DONE                                                                                   |
| 028  | Mirror dynamic plugin sources into the desktop layer      | P2       | M      | 027        | DONE                                                                                   |
| 029  | Redesign Plugins Settings for C-end                      | P1       | M      | 027        | TODO (see plan file; duplicate number with review-guide plan)                          |
| 029b | Review Guide UX overhaul (`029-review-guide-ux-overhaul`) | P2       | M      | —          | TODO (filename collision with plugin 029 — execute by path)                            |
| 030  | Plugin install foundation: preview endpoint + shared wizard + trust consent | P1 | L | 027, 029 | TODO                                                                                   |
| 031  | Plugin Center: marketplace + installed + import surfaces (consumes 030) | P1 | XL | 027, 030 | TODO                                                                                   |
| 032  | Transparent remote-host Upstream Gateway; delete RemoteCradleClient | P1 | L | —     | DONE                                                                                   |
| 033  | Remote session projection + link; block local runtime    | P1       | L      | 032        | DONE                                                                                   |
| 034  | Web remote-execution UX for projected sessions           | P1       | M      | 032, 033   | DONE                                                                                   |
| 035  | Unify model Inventory / Enrichment / Visibility / Selection | P1    | XL     | —          | IN PROGRESS (M0–M6 core done; polish/orphan UI copy optional)                          |
| 036  | User-controlled local Work: isolated task to Draft PR    | P1       | XL     | —          | DONE                                                                                   |
| 038  | Close control-plane trust boundary                      | P0       | L      | —          | DONE                                                                                   |
| 039  | Replace secret rotation with runtime keyring             | P0       | M      | 038        | DONE                                                                                   |
| 040  | Establish web state authority                            | P1       | L      | 038        | DONE                                                                                  |
| 041  | Enforce domain and lifecycle ownership                   | P1       | XL     | 038, 040   | DONE                                                                                  |
| 042  | Collapse Automation contract and query path              | P0       | L      | 040        | DONE (scoped gates pass; repository-wide CI remains red on inherited baseline failures) |
| 043  | Deepen Composer Draft lifecycle ownership                | P1       | M      | 040        | TODO                                                                                  |
| 044  | Establish one Chat turn completion owner                 | P1       | L      | 024, 041   | REJECTED (superseded by end-to-end lifecycle Plan 061)                                |
| 045  | Close the Provider Catalog target query seam             | P2       | M      | 035, 048   | TODO (execute after Plan 035 status is reconciled and Plan 048 lands)                  |
| 046  | Deepen Terminal lifetime ownership                       | P2       | M      | 041        | TODO                                                                                  |
| 047  | Build a unified, thin Download Center                    | P1       | XL     | 028, 041   | TODO                                                                                  |
| 048  | Publish a safe Provider Endpoint catalog projection      | P1       | M      | 035        | TODO (execute only after Plan 035 status is reconciled)                               |
| 049  | Complete lossless Navigation surface round-trip          | P1       | L      | 040        | TODO                                                                                  |
| 050  | Own Session projection and cache coherence               | P0       | L      | 040        | TODO                                                                                  |
| 051  | Own Issue–execution association end to end               | P0       | L      | 050        | TODO                                                                                  |
| 052  | Make Codex app-server provider-owned and thread-multiplexed | P0     | XL     | 041        | TODO                                                                                  |
| 054  | Make WebSocket run streams resumable and liveness-aware   | P0       | L      | 024, 040   | DONE                                                                                  |
| 055  | Record authoritative Cradle Codex model-call usage in `usage_logs` | P0 | L | operator cleanup of 053 | DONE |
| 056  | Declare managed resources and add a unified Resources page | P1      | L      | 047        | DONE                                                                                  |
| 057  | Manage OpenCode CLI as an optional built-in runtime       | P1       | L      | 047, 056   | DONE                                                                                  |
| 058  | Add a configurable, crash-safe desktop server data directory | P1       | L      | —         | DONE (`cb781d4`; reviewed in disposable worktree)                                     |
| 059  | Programmable JavaScript Session Awaits via worker-thread cells | P1       | L      | —         | DONE (`cc7c1e9`; reviewed in `tmp/worktree-plan-059`)                                 |
| 060  | Rename agent tool to `manage_pull_request` + pre-PR branch rename | P2  | M      | branch `feat/enhance-work` | DONE (`dfe2fb88`..`a7aa8e93` on `feat/enhance-work`; advisor-reviewed)   |
| 061  | Unify Chat turn lifecycle authority and eliminate synthetic run storms | P0 | XL | 024, 041, 054 | IN PROGRESS |
| 062  | Cradle Recall — agent cognition stack + CodeAct retrieval contract | P1 | XL     | 024, 041   | TODO (Phase A: design docs; Phase B+: `recall_query` runtime)            |
| 062  | Claude native session projection (SDK owns queue; Cradle projects UI Runs) | P0 | XL | 061 (compose) | DONE |
| 063  | Eliminate Desktop Tearoff HTTP/1.1 pool starvation via custom-scheme + undici proxy | P0 | M–L | 038, 040, 054, 071 | TODO (rewritten 2026-08-02: custom-scheme→undici loopback/UDS; native cookie WS; IPC framing deleted; M0 packaged gate remains) |
| 064  | Connect GitHub through the Cradle App and attribute PR actions to the user | P1 | L | current PR Console work reconciled | IN PROGRESS (implementation complete; real GitHub App acceptance pending) |
| 065  | Make the Claude Agent SDK integration honest (permission modes, dispose, settle-on-cancel teardown, presentation, snapshot bounds) | P0 | L | — | DONE |
| 066  | Make the long-lived Claude Query the authority for history and live config | P0 | M | 065 | DONE |
| 067  | Enforce Cradle's hard tool-call denies via PreToolUse hook in every permission mode | P1 | M | 065 | DONE |
| 068  | Split claude-agent provider.ts into an owner directory + declaration-extractor re-justification | P2 | L | 065, 066, 067 | DONE |
| 069  | Demote the claude-agent state snapshot to a checkpoint; rebuild the UI activity feed from authoritative history | P2 | XL | 065, 066 (coordinate with 050, 061) | BLOCKED (SDK transcripts are pruned and omit provider activity facts; re-plan over Cradle `session_events`) |
| 070  | Test the Claude Agent provider against the real wire via a shared model-api-simulator harness | P1 | L | 065, 066 | DONE |
| 071  | Eliminate active-run replay retention and recover from snapshots | P0 | M | — (supersedes Plan 054 replay retention only) | DONE |
| 072  | Move chat message bytes into a content-addressed blob store, losslessly | P0 | XL | — | TODO (Step 0 is a standalone web-only bug fix; Step 9 backfill is what shrinks existing sessions) |
| 073  | Cradle Platform Constitution — Jarvis as Agent Kind | P0 | — | 061, 062 (conceptual) | FINAL — awaiting human accept / amend / reject (direction only; not an implementation plan) |
| 073  | Provider first-class identity + dual-endpoint platform (no Kimi OAuth) | P1 | XL | — | TODO |

Status values: TODO | IN PROGRESS | DONE | BLOCKED (one-line reason) | REJECTED (one-line rationale).

## Follow-up notes

- 2026-07-15，Plan 055 完成：Codex `thread/tokenUsage/updated` 在 root filtering 前投影为
  deterministic per-call usage events，TurnExecutor 附加 Cradle session/run/message/provider identity，
  Usage owner 通过现有 `usage_logs` 幂等入账；Codex 不再写 run-final summary。启动与 turn-terminal
  reconciliation 只从 Codex `backend_session_bindings` 和 Cradle dedicated Codex runtime home 出发，
  使用 native root/descendant metadata、`session_meta`、`turn_context`、`model_reroute` 与 `token_count`
  恢复相同 event id；歧义、越界或缺失 identity 记录 incident 并跳过。Usage turn counts 改为 distinct
  logical turn，session usage 增加 generated `byModel` projection。Server/Web typecheck、114 个 scoped tests、
  Server module boundary、Web API boundary、scoped lint 与 diff integrity 通过。完整 Server suite 为
  1082 passed / 1 skipped / 32 failed；失败来自 sandbox local-listen、Git GPG，以及并行的
  preferences/remote/Chronicle/OpenCode baseline。完整 lint 仅剩 `documentations/next-env.d.ts` 两个范围外
  error 与既有 warnings；Plan 055 文件 lint clean。

- 2026-07-15，Plan 055 取代 Plan 025 与已移除的 Plan 053 方向：只统计 Cradle-owned
  Codex，将现有 `usage_logs` 深化为逐模型调用事实表，并要求每条 Codex event 都有确定的
  Cradle session、Provider thread/turn 与 model。明确排除全局 `~/.codex`、Claude
  transcripts、single-writer 机制与平行 usage event table。

- 2026-07-15，Plan 054：`run-chunks` 已迁移到 Chat Runtime-owned append-only
  `{ runId, cursor }` log，删除 sender-local transport seq 与 mutable buffer index resume；
  Web sync client 增加 pong deadline、socket generation guard、single initial subscribe 和
  retryable end recovery。Focused Server WebSocket/runtime tests、完整 Web suite、Web/Server
  typecheck、模块/API boundary、scoped lint 与 diff hygiene 均通过。

- 2026-07-05, Plan 001: `DesktopBrowserManager` now updates already-attached `WebContentsView` bounds without re-adding the child view, hides attached views via `setVisible(false)`, and keeps destruction removal on the runtime cleanup path. Renderer bounds sync now uses one rAF frame and no longer pauses for ordinary layout drag/animation; `nativeBoundsPaused` is retained only for browser-panel closing. Native surface layering is centralized through a reference-counted suppression store, browser address suggestions use occlusion props, and global `Dialog` / `AlertDialog` / `Sheet` primitives suppress the native browser surface while open. Legacy browser backend/script/annotation-overlay files were already absent; `setBorderRadius` was confirmed present in Electron 42.4.1 types but skipped because there is no current native viewport radius requirement. Verification passed `pnpm typecheck`, `pnpm test` (245 files / 1345 tests), full `pnpm --filter @cradle/web test` (71 files / 302 tests), focused browser web tests, root Vitest for `apps/desktop/src/main/browser-manager.test.ts`, local ESLint for `browser-panel.test.tsx`, and `git diff --check`. `pnpm lint` was run and still fails on existing repository-wide lint debt outside Plan 001 paths; `react-doctor --diff` exits 1 on existing `file-tree.tsx` / `await-panel.tsx` findings but remains at score 70 with no Plan 001 file findings. The originally listed desktop filtered Vitest command is not usable in this repo state because it resolves root project config paths from `apps/desktop`.
- 2026-07-05, Plan 013: root `pnpm test` now includes `node`, `apps/server`, and `apps/web` Vitest projects. The newly exposed `src/components/editor/markdown-editor.test.tsx` i18n provider gap was fixed, and root test now passes 238 files / 1310 tests.
- 2026-07-05, Plan 014: server tail replay already catches missed session/global events from persisted cursors and sends `SnapshotRequired` on large gaps, so cursor-refresh URL rebuilding was downgraded to snapshot/list/runtime catch-up on tail error. Malformed workspace file, Chronicle download-progress, and PTY frames are now dropped without escaping handler boundaries.
- 2026-07-05, Plan 007: `agent_credentials.key_version` was added with a non-destructive migration. Secrets now use `vN:iv:payload:tag` envelopes while still reading legacy `iv:payload:tag`; rotation re-encrypts all credential rows inside one transaction and intentionally has no CLI exposure because it accepts master secrets.
- 2026-07-05, Plan 008: external local plugin activation is now gated by a checksum-bound operator trust grant in `trust_grants` (subject_type=plugin_package); `PATCH /plugins/:routeSegment/enabled` records the grant for the currently discovered package revision. External local plugins remain blocked while relay host enrollments expose the server. Marketplace install receipts can optionally provide `packageChecksum`; mismatches are refused, while existing receipts without checksum keep provenance/permission behavior and are noted as an integrity gap rather than treated as verified.
- 2026-07-05, Plan 009: `.cradle/worktrees.json` setup hooks now require a workspace-scoped trust grant in `trust_grants` (subject_type=worktree_setup_hook) or an explicit `confirmedSetupHooks` flag on worktree creation. Untrusted or relay-exposed runs return warnings that list skipped commands and do not execute them.
- 2026-07-05, Plan 012: run finalization now releases active runs and drains the queue from a guarded `finally`; boot relay/conversation tasks now attach non-fatal `.catch` loggers. Provider runtime disposal failures record `PROVIDER_RUNTIME_DISPOSAL_FAILED`, and OpenCode event subscription failures log, emit observability, and fail fast when recovery cannot start. Verification passed `pnpm --filter @cradle/server typecheck`, focused `opencode provider-runtime turn-executor` tests, and a retried full `pnpm --filter @cradle/server test` run (135 files / 807 tests); the first full run hit unrelated `diff-review` 5s timeouts that passed when run focused.
- 2026-07-05, Plan 015: added a shared `RouteErrorFallback` with route retry and home navigation, wired it as the TanStack router `defaultErrorComponent`, and added scoped error components for chat split workspaces, workspace/browser-panel host, plugin panel host, diff review, workspace diffs, and kanban. Common i18n resources now include route-error strings for all supported locales. Verification passed `pnpm --filter @cradle/web typecheck`, focused route-error/i18n tests, and full `pnpm --filter @cradle/web test` (67 files / 285 tests).
- 2026-07-05, Plan 016: `surface-store` and `browser-panel` now define explicit persist versions, validate old unversioned payloads during merge, and validate versioned mismatches during migrate. Corrupt persisted surfaces fall back to home, corrupt browser history/collapsed maps reset to empty maps, and fixture tests cover old/corrupt payloads. Verification passed `pnpm --filter @cradle/web typecheck`, focused store tests, and full `pnpm --filter @cradle/web test` (68 files / 291 tests).
- 2026-07-05, Plan 017: hot chat render-plan classifier reads now use a descriptor cache invalidated by tool identity, state, input/output identity, arguments text, and error text. The streaming-message-id selector now reuses a shared idle Set and preserves the active Set reference while membership is unchanged. Verification passed `pnpm --filter @cradle/web typecheck`, focused classifier/store tests, and full `pnpm --filter @cradle/web test` (69 files / 294 tests).
- 2026-07-05, Plan 019: `apps/server/AGENTS.md` now describes the actual Elysia composition root, runtime bootstrap, `infra.ts`, HTTP plugins, `AppError`/error mapping, TypeBox/OpenAPI/`x-cradle-cli` route metadata, lifecycle cleanup, and real server verification scripts. Verification checked that old framework references were removed and documented paths exist.
- 2026-07-05, Plan 022: partial characterization coverage landed for Chronicle download-progress parsing/EventSource behavior, sync-socket pending subscribe/malformed-frame/reconnect cursor behavior, and PTY pending queue/reconnect cursor behavior. The sync-socket test exposed and fixed a delayed initial-subscribe race that could resend a stale cursor after reconnect. The remaining driver hook and file-tree live-sync targets are blocked because meaningful tests require large mocks or extracting seams first. Verification passed `pnpm --filter @cradle/web typecheck`, focused new tests, and full `pnpm --filter @cradle/web test` (71 files / 301 tests).
- 2026-07-06, Plan 024 M0: added `apps/server/src/modules/chat-runtime/es/parity.ts` and `apps/server/scripts/chat-es-parity.ts` to replay a session event log through the real projectors inside a rollback-only transaction and diff `messages`, `backend_runs`, and `chat_session_queue_items`. Focused characterization covers clean replay, queue claim/terminal projection, steer, rollback, persisted-run abort, interrupted-run recovery, expected streaming/import bypasses, and unexplained backend-run drift. Verification passed `pnpm --filter @cradle/server exec vitest run src/modules/chat-runtime/es/parity.test.ts`, `pnpm --filter @cradle/server typecheck`, and an earlier full `pnpm --filter @cradle/server test -- src/modules/chat-runtime/es/parity.test.ts` invocation that ran the full server suite (136 files / 812 tests).
- 2026-07-06, Plan 024 M1-M2: event payload contracts are now owned in `es/events.ts` without Drizzle row payload imports, stored payload JSON is written with `v: 2`, v1 payloads upcast through `parseStoredChatSessionEvent`, and the old `assistantMessageProjection` hint is only read by the v1 upcaster. `es/aggregate.ts` now exposes `evolve`, `es/decide.ts` owns active-run / terminal-run / queue-claim / rollback invariants, and command commits append with `expectedVersion` through the session actor. Verification passed `pnpm --filter @cradle/server typecheck`, `pnpm --filter @cradle/server exec vitest run src/modules/chat-runtime/es`, and `git diff --check`. A full `pnpm --filter @cradle/server test` run was attempted but did not return a captured final summary after several minutes; no lingering Vitest process remained, so it is not counted as a pass.
- 2026-07-06, Plan 024 M3-M5 partial: non-streaming direct projection writes now flow through facts: `PlanImplementationResponded`, `MessageImported`, and `QueueItemProviderTargetCleared`. `persistMessageSnapshot` was removed. Added `es/rebuild.ts` plus `scripts/chat-es-rebuild.ts` for per-session projection rebuild, changed terminal drift recovery to replay stored terminal facts through the projector, and added recovery bootstrap facts for historical projection-only queue/run rows before terminal repair. Documented versioned facts, `decide/evolve`, `expectedVersion`, rebuild tooling, recovery bootstrap, and current `session_events` retention. Local replay profile against the configured DB top sessions: 14,073 events read in 44.875ms and reduced in 3.618ms; 1,897 events read in 28.155ms and reduced in 0.504ms, so no aggregate-state cache was added. Verification passed `pnpm --filter @cradle/server exec vitest run src/modules/chat-runtime/es`, `pnpm --filter @cradle/server exec vitest run tests/chat-runtime-recovery.test.ts --maxWorkers=1 --reporter=dot`, `pnpm --filter @cradle/server exec vitest run tests/chat-runtime.test.ts --maxWorkers=1 --reporter=dot`, `pnpm --filter @cradle/server typecheck`, `pnpm --filter @cradle/web typecheck`, `git diff --check`, the direct-write grep excluding projectors/parity/rebuild (only `active-run-stream.ts` remains), and full `pnpm --filter @cradle/server exec vitest run --maxWorkers=1 --reporter=dot` (139 files / 827 tests).
- 2026-07-06, Plan 024 M3 Option A: active streaming partial snapshots now append `AssistantMessageSnapshotted` facts through the session actor and projector instead of updating `messages` directly; duplicate compacted snapshots are skipped. Event tail/contracts/web sync include the new slim message-refresh event, and parity now treats logless streaming message mutations as unexplained drift. Focused verification passed `pnpm --filter @cradle/server exec vitest run src/modules/chat-runtime/es --maxWorkers=1 --reporter=dot`, `pnpm --filter @cradle/server exec vitest run tests/chat-runtime.test.ts tests/chat-runtime-recovery.test.ts --maxWorkers=1 --reporter=dot`, and the direct-write grep excluding projectors/parity/rebuild with no output.
- 2026-07-06, Plan 024 complete: final verification passed `pnpm --filter @cradle/server typecheck`, `pnpm --filter @cradle/web typecheck`, full `pnpm --filter @cradle/server exec vitest run --maxWorkers=1 --reporter=dot` (139 files / 832 tests), root `pnpm test` (249 files / 1369 tests), `git diff --check`, and the direct-write grep excluding projectors/parity/rebuild with no output.
- 2026-07-06, Plans 026-028: `@cradle/plugin-sdk` is now publishable from `dist` with public npm publish config; marketplace install links accept any valid GitHub `owner/repo`, repo-root or normalized relative plugin paths, and scoped/unscoped npm package names while preserving checksum and install-confirmation gates. Added persisted `plugin_sources` storage, source installer support for local path / GitHub tarball / `npm pack`, live server discovery/removal APIs and generated CLI commands, Settings source add/remove UI, and desktop source mirroring through server HTTP plus preload IPC. New persisted-source plugins are registered as `externalLocal` and disabled until operator enable records the existing checksum trust grant. Verification passed `pnpm --filter @cradle/plugin-sdk build`, `pnpm --filter @cradle/plugin-sdk typecheck`, `pnpm --filter @cradle/server typecheck`, `pnpm --filter @cradle/web typecheck`, `pnpm --filter @cradle/desktop typecheck`, `pnpm --filter @cradle/cli typecheck`, `pnpm --filter @cradle/cli cradle --help`, focused Vitest for desktop plugin install/loader, focused server loader Vitest, and full web Vitest (`71 files / 303 tests`). `pnpm --filter @cradle/server test -- src/plugins src/modules/plugins` was attempted but the repo's broad Vitest matching ran unrelated relay/WebSocket/preferences suites and failed on sandboxed `127.0.0.1` listeners plus pre-existing 500 assertions outside the plugin-source path; it is not counted as a pass. npm publish itself was not executed; manual publish from `packages/plugin-sdk` remains the release step.

- 2026-07-09, Plans 030/031 redesign: split the plugin-install work into a reusable **foundation** (030) and a **surface** (031) to eliminate throwaway work. The original 030 specified a full `plugins-add-dialog.tsx` rewrite that 031 then deleted and re-implemented as a tab. Redesigned 030 now ships surface-agnostic primitives - `InstallWizard`, `PluginSourcePasteInput`, `TrustConsentDialog` - plus the stateless `POST /plugins/sources/preview` endpoint and the `loader.ts` `refresh->resolve` cache-reuse switch, and guts the existing dialog to host them (independently demoable). 031 consumes those primitives verbatim for its Marketplace-install and Import tabs and deletes only the thin dialog shell. Also corrected two factual errors: `evaluatePluginSourceTrust` takes `PluginTrustEvaluationInput` (`{ source: { kind, packageDir, provenance? }, pluginName, relayHostExposed? }`), not the shape the original 030 assumed; and `sourceCacheKey`/`cacheDirForSource` are module-private, so the preview's throwaway `PluginSource` needs no special id (cache reuse works off `{kind,location,ref,subPath}` alone). The marketplace module is described as a simple `guardedFetch` + zod + TTL singleton, not a `provider-catalog` mirror (provider-catalog is a multi-provider dispatcher).

- 2026-07-09, Plan 032: Transparent `ALL /remote-hosts/:hostId/upstream/*` gateway landed on
  branch `advisor/032-remote-host-upstream-gateway` (`57f6b58`, follow-up `c49de41`).
  `RemoteCradleClient` and hand-written cradle-server workspace/file routes deleted; settings/
  sidebar use upstream fetches; CLI workspace-list command removed via regen. Reviewer
  re-ran `pnpm --filter @cradle/server typecheck` and focused remote-hosts/upstream tests
  (2 files / 6 tests). Full server suite had 5 pre-existing failures outside remote-hosts
  scope (relay e2e / external-provider-sources / preferences / profiles). Note: executor
  committed in the main working tree on the advisor branch rather than a disposable
  worktree clone — scope and criteria still verified clean. Merge is the operator's call.

- 2026-07-09, Plan 033: Remote session projection landed on `main` (`42c6d6b`, `5f8a690`).
  Added `remote_session_links`, create/delete projection via upstream, local runtime
  hard-reject `chat_session_executes_on_remote_host` (409), and minimum chat proxy for
  `POST /chat/sessions/:id/response`, `GET .../stream`, `GET .../messages`. Reviewer
  re-ran typecheck + focused tests (2 files / 11 tests). Remaining chat routes
  (cancel/queue/events/runtime-settings/…) deferred to plan 034 wiring notes.
  Operator asked to stay on `main` (no advisor branch).
- 2026-07-09, Plan 034: Web remote-execution UX on `advisor/034-remote-session-web`.
  Session `execution` helpers, remote catalog via Upstream Gateway, connect gate +
  Connect CTA, header/sidebar remote badges, and error mapping for remote codes.
  Also completed the deferred linked-session catch-all proxy from 033.
- 2026-07-11, Plans 040/041 completed: chat snapshots/cache/sync now use authoritative server revisions and explicit empty state; touched session, settings, and workspace access goes through feature-owned gateways and the generated authenticated client. The web boundary ratchet holds at 186 legacy non-gateway generated imports and 23 approved raw-fetch transport/external/binary calls; the full web suite passes at 88 files / 399 tests. Preference writes have a deferred two-writer regression test. Server runtime resources have one ordered lifecycle owner, remote connections cannot resurrect after cancellation/deletion, remote session writes are atomic, migration dry-run/rollback behavior is proven, and owner-aligned local-tunnel/security capabilities reduce the largest runtime-domain SCC from 22 to 21. Plan-scoped server tests, the module graph checker, focused lint, and diff hygiene pass. Concurrent repository changes leave the full server baseline red: legacy HTTP tests receive `401` after authentication changes, while Claude Agent provider `steerTurn`/UUID typing changes block server typecheck; these are recorded as external to Plans 040/041 rather than keeping completed ownership work open.

## Recommended sequencing

### 2026-07-11 architecture track

Execute `038 → 039`, then `040`, then `041`. Plan 038 closes the transport and
credential boundary used by every later gateway. Plan 039 replaces the unsafe
runtime rotation path. Plan 040 establishes client-side authority before Plan
041 removes server cycles and centralizes runtime lifecycle. Do not begin bundle
splitting until this track is stable.

1. **Security first** (002 → 003, 004, 006; 005 in parallel): the relay feature makes
   the "local-only, no-auth" assumption false. 002 is the keystone; 003/004/006 build on it.
2. **Correctness that requires a restart to recover** (010, 012, 011, 014): run-state
   corruption, terminal-cleanup leaks, registry races, and sync reconnect gaps.
3. **Test baseline** (013 then 022): make root `pnpm test` meaningful for the frontend,
   then add a net under the paths that later refactors touch.
4. **Perf + hardening + hygiene** (007, 008, 009, 015, 016, 017, 018, 019).
5. **Structural refactors last** (020, 021, 023): high value, but must follow the test
   net and the security/correctness fixes.
6. **Remote Control product** (032 → 033 → 034): after transport/auth (002/003) are DONE,
   this track makes remote workspaces actually runnable. Do not start 033/034 before 032.

## Dependency notes

- 039 requires 038 so secret administration is not exposed through the old optional-auth boundary.
- 040 requires 038 because all feature gateways must consume the shared authenticated transport.
- 064 is independent of the Work/PR delivery track but must not begin until the
  current uncommitted PR Console and generated OpenAPI changes are committed or
  deliberately reconciled. It adds GitHub App Device Flow to the local Desktop
  server and replaces only the shared GitHub identity boundary; it does not add
  hosted OAuth, webhooks, or bot automation.
- 041 requires 038 and 040 so server domain boundaries and web projection boundaries converge on one API contract.
- 066 requires 065: both rewrite the `streamTurn` prologue; 066's excerpts are pre-065 and must be re-located by symbol name after 065 lands.
- 067 requires 065: it assumes the Query starts in the user's real permission mode and adds mode-independent denies on top.
- 068 requires 065-067: splitting `provider.ts` first would force the three behavior plans through a moving target.
- 069 is blocked: the SDK transcript defaults to 30-day cleanup and omits permission, auth, rate-limit, and task lifecycle facts, so it cannot be the cold-read authority. Re-plan durable Claude activity as normalized Cradle `session_events`; keep transcripts as optional provider artifacts.
- 070 requires 065 and 066 (its integration specs assert the fixed behavior; landing them earlier would pin the bugs as green). Its Step 1 is a feasibility gate — if the bundled CLI cannot run against the simulator offline, the plan stops at "keep unit tests" and reports.
- 071 preserves completed Plan 054's run-scoped cursor, terminal ordering, and WebSocket liveness but supersedes its append-only active-run replay retention after snapshot recovery landed. It is independent of Plan 061 semantics; coordinate overlapping lifecycle files and stop on drift rather than changing admission/completion ownership.
- 042 follows 040 and removes Automation's remaining raw-fetch/auth exception; its authentication bypass can make the entire feature return 401, so execute this P0 first.
- 043 follows 040 and centralizes Draft state/transport transitions; it is independent of 042 and may run in parallel in an isolated worktree.
- 044 builds on 024's durable facts and 041's process lifecycle owner; it must not change event schemas or provider protocols.
- 045 is post-035/048 consolidation, not a second model-layer redesign. Reconcile/finish 035, then land 048's canonical endpoint projection before touching the overlapping Provider Catalog route/model/docs files.
- 046 consumes 041's ownership rules but remains web TUI scoped; it preserves the server PTY lease and the two terminal adapters.
- 047 consumes 041's lifecycle ownership and 028's Server-owned dynamic Plugin source boundary. Its internal milestone order is mandatory: security/characterization → shared HTTP kernel → Server host → Server owners → Desktop host/owners → Web projection → legacy cleanup. It does not reuse Background Job or wait for Plans 030/031, but must reconcile their already-landed Plugin preview/cache foundation before migrating Plugin downloads.
- 048 keeps endpoint templates Server-owned and publishes only a safe projection; it precedes 045 to avoid overlapping Provider Catalog edits and never exposes runtime auth policy.
- 049 is independent after 040 and replaces duplicated route/type/persistence definitions with one lossless Navigation codec. It intentionally resets the reconstructible v1 surface snapshot rather than adding a compatibility shim.
- 050 follows 040 and repairs the concrete snapshot-gap stale-cache bug before consolidating all Session projection semantics. It must land before association cache work.
- 051 follows 050 so Issue association mutations reuse the Session projection gateway. Server-side workspace invariants and participant-owned writes are mandatory before Web cache reconciliation.
- 052 follows completed Plan 041's lifecycle ownership rules and is independent of Plans 042-051. Its internal order is mandatory: characterize isolation -> retain idle hosts -> establish the provider host/router -> move session env to thread config -> bind/resume threads -> migrate all callers -> ratchet docs/tests. Do not land a rollback-only resume patch first.
- 054 follows completed Plans 024 and 040: chat-runtime owns the run cursor/log, while persisted Session projections remain the recovery authority when exact in-memory replay is impossible. It does not depend on unfinished Plans 044 or 050, but those plans must preserve its terminal-publication and snapshot-recovery contracts.
- 056 consumes Plan 047's already-landed thin Download Center but adds a separate declaration/dispatch layer: Managed Resources owns catalog projection and exact-key command routing; each adapter keeps discovery, versions, installation truth, storage, activation, rollback, and uninstall; Download Center keeps bytes/cancel/resume/history. Chronicle model manifests are the first adapter. Reconcile 047's stale TODO row and execute from a clean worktree because current Server composition, Chronicle, navigation, locale, and generated-client paths overlap operator work.
- 057 follows 056 and declares `{ opencode, runtime, cli }` through the shared catalog. OpenCode owns release identity, archive extraction, executable verification, installation truth, process leases, and uninstall; its archive transfer uses the same exact resource identity. It must not add an OpenCode-only Settings installer or parallel HTTP command surface.
- 063 follows completed Plans 038, 040, 054, and 071: proxied HTTP must still cross the established auth boundary, generated clients remain transport infrastructure beneath feature-owned projections, and cursor-aware snapshot-first recovery remains owned by Chat Runtime. It coordinates with in-progress Plan 061 only at transport injection points and must stop rather than alter Chat admission/completion/queue semantics or restore active-run replay superseded by 071. Its internal order is mandatory: packaged custom-protocol proof -> parity fixtures -> undici `DesktopServerTransport.fetch` + `owned-proxy`/`attached-http` -> default-session `cradle-server://local` handler -> Desktop/Web/SSE migration -> credential removal + HttpOnly cookie bootstrap (native WS for PTY/`/sync`) -> Chromium-pool ratchet and many-Tearoff packaged smoke. Do not invent process IPC Request/Response framing. Plan 028's HTTP choice is superseded only as transport; plugin ownership remains unchanged.
- 040 supersedes blocked Plan 023: generated clients remain transport infrastructure, while feature-owned gateways own query/error/invalidation semantics.
- 041 supersedes blocked Plans 020 and 021: dependency enforcement and lifecycle ownership precede god-file extraction.

- 003 requires 002 (relay tokens layer on the base auth boundary).
- 004 and 006 assume 002's auth boundary as the primary protection (they are defense-in-depth / info-leak fixes on top).
- 022 requires 013 so the new web tests actually run in the main suite.
- 020 (god-file splits) requires 022's characterization tests for the web targets — don't refactor untested behavior blind.
- 021 pairs with 019 so the AGENTS.md dependency rules and the code agree.
- 023 (data-fetching) should follow 022 (chronicle test net) and ideally 020's chronicle split.
- 027 depends on 026 (naming/repo openness) but is independently useful even if 026 hasn't landed.
- 028 depends on 027 (needs the persisted source registry + its HTTP routes to exist before desktop can mirror it).
- 031 consumes 030 (the shared `InstallWizard` / `PluginSourcePasteInput` / `TrustConsentDialog` / `POST /plugins/sources/preview` must ship first); 030 is independently shippable. Execute 030 before 031 - the redesigned split removed the throwaway dialog the original 031 would have deleted.
- 032 is independent of the plugin track; it is the Remote Control product-layer keystone.
- 033 requires 032 (session projection forwards through the upstream gateway; RemoteCradleClient must be gone).
- 034 requires 032 and 033 (web consumes `execution` metadata and connection gates; server must proxy linked chat).
- 036 is intentionally independent of 032-034: it composes the existing local Session/worktree/PR path and must not redesign remote execution.

## Remote Control product track (032-034)

Planned 2026-07-09 against commit `0ff0271` via `/improve plan` (design already decided;
not an audit finding). Goal: Relay/SSH/direct already yield `localBaseUrl`; make the remote
Cradle Server fully reusable without a second agent protocol.

1. **032** — Transparent `ALL /remote-hosts/:hostId/upstream/*` gateway; delete
   `RemoteCradleClient` and hand-written cradle-server workspace/file routes.
2. **033** — `remote_session_links` + local session projection; create/delete/chat for
   remote workspaces go upstream; local chat-runtime hard-rejects linked sessions (409).
3. **034** — Web: connect gate, remote catalog for remote execution, badges; keep calling
   local session ids (server proxies).

Explicitly deferred: session handoff / code migration / take-back. Explicitly rejected:
expanding typed `RemoteCradleClient`; teaching local chat-runtime remote cwd; browser
global `baseUrl` pointed at tunnel; reviving agentd session links.

## Plugin external-loading track (026-028)

Ordered by the user's explicit decisions: publish the SDK to npm, defer sandboxing, allow
installing from any repo. 026 removes the naming/repository allowlists and makes
`@cradle/plugin-sdk` npm-installable. 027 adds a persisted `pluginSources` registry with
git/npm/local-path installers and a Settings UI, so server+web plugin layers can be added
and removed live, with no restart. 028 closes the remaining gap for `cradle.desktop`
capabilities by having the desktop process pull resolved source state from the
already-running server over HTTP instead of duplicating the installer. None of these three
add process isolation for `activate()` — that remains full in-process trust, gated only by
the existing Plan 008 checksum-bound operator grant. A follow-up sandboxing plan should be
written separately if that decision changes.

## Effect.TS / framework-migration question — considered and rejected

A full migration to Effect.TS (or another effect-system framework) was evaluated and
**rejected**. Rationale: the codebase's actual pain points — missing security boundaries,
9000-line god files, and scattered check-then-act races — are not what an effect system
fixes. Migration is viral (any `Effect<A,E,R>` return type propagates through the call
chain) and would require rewriting ~2100 source files deeply integrated with Elysia,
Drizzle, and three AI-provider SDKs — multi-person-month work with a functionality freeze
and a new team-wide mental model, introducing new bugs while fixing none of the above.
The equivalent capabilities already exist in lighter form (`app.onStop` for resource
cleanup, `AppError` + error-mapping for errors, native Promises for concurrency); what's
missing is discipline on existing patterns, delivered by plans 010–012. If typed errors
are desired, adopt `neverthrow` locally in the provider layer rather than Effect globally.
A scoped Effect.TS experiment inside a _new, isolated_ subsystem (e.g. a future relay
orchestration layer) would be reasonable; a whole-codebase migration is negative-value.

## Findings considered and rejected (so they aren't re-audited)

- **Fix inline attachment bytes at each producer** — rejected in favor of Plan 072's single persistence seam. There are six known producers of `data:` URLs (web composer, browser appshot, three provider event mappers, Codex local-image reader); patching each is six diffs and still misses the seventh.
- **Truncate oversized tool payloads at persist time** — rejected by Plan 072. It is the current behavior and it is silent data loss: the bytes exist nowhere afterwards, and `apps/web` never learned to read the truncation marker, so large tool results render as empty blocks. Truncation stays only on transient surfaces (streaming checkpoints, observability snapshots).
- **Give `chat_message_payloads` a `schema_version` column** — rejected by Plan 072. Message parts are self-describing, so a legacy row is simply a row with no reference parts and hydrates through identical code; a row version would force a dual read path for no benefit. Mirrors how `session_events` versions payloads inside the JSON (`v: 4`) rather than in a column.
- **Externalize composer draft attachments** — rejected by Plan 072. Drafts do not accumulate (one row per surface, reset on send or discard), `composer_drafts.surface_id` is not a session id, and drafts are only soft-deleted, so ref-table ownership would need `owner_kind`/`owner_id` plus a soft-delete-aware GC liveness check for a bounded sink.
- **Bound or truncate active-run replay by bytes/count** — rejected in favor of Plan 071. A cap only changes when complete recovery fails and still retains repeated large payload copies below the cap; snapshot bootstrap preserves complete current state without historical retention.
- **Move raw active-run chunk replay into SQLite** — rejected. Raw AI SDK deltas and repeated tool-output snapshots are transient transport, not domain facts; persisting them duplicates the current message projection and moves the same incorrect retention model to disk.
- **Throttle repeated provider tool-output updates** — rejected. Live consumers must continue receiving provider updates; Plan 071 changes reconnect storage semantics rather than suppressing upstream events.
- **Adopt SDK `sessionStore` (alpha) for claudeAi persistence** — deferred from Plan 066; principled long-term fix for claudeAi session replay, but the API is alpha. Re-evaluate when stable.
- **Live-reconcile `mcpServers`/`skills`/`tools` mid-session via `setMcpServers`/`reloadSkills`/`applyFlagSettings`** — deferred from Plan 066; needs a product policy on which settings may change mid-session. Until then they are creation-time only.
- **Migrate claude-agent `activeQueries` onto `provider-runtime/host-manager.ts` (refcount/TTL/idle reap)** — deferred from Plan 065; 065 adds deterministic disposal (shutdown + archive), idle reaping is a follow-up that should reuse the host manager.
- **Keep AskUserQuestion silently auto-answered in bypassPermissions mode** — rejected in favor of Plan 067's deny-with-reason; flagged there as a product call if owners disagree.
- **Replace the provider state snapshot blob with derived-on-read projections (split resume state from UI activity feed)** — now planned as **Plan 069** (two phases: re-derivability inventory + write-once emission for non-derivable fields, then read-path switch with the blob demoted to a bounded checkpoint). Plan 065's array caps land first and become the checkpoint bounds.

- **Redo Provider Catalog inventory/enrichment/visibility/selection architecture** — rejected as duplicate of Plan 035. Plan 045 only closes the still-open target query seam and cold-cache Conversation Bridge path.
- **Recreate `apps/web/src/features/tui/tui-runtime-registry.ts` from the review snapshot** — rejected because the file no longer exists and a shallow registry would duplicate current state. Plan 046 specifies a narrow lifetime controller instead.
- **Persist Composer file attachments in the existing draft JSON** — deferred from Plan 043; attachment size, security, and serialization need a separate contract before storage.

- **Expose the full internal Provider Endpoint template over HTTP** — rejected. `anthropicWireAuth` and future credential/runtime policies remain Server-only; Plan 048 publishes an explicit field allowlist.
- **Kimi / Moonshot OAuth in the Provider identity track** — rejected from Plan 073 by product decision (2026-08-01); Moonshot ships dual endpoints + API Key only. Revisit only with an explicit new plan.
- **Keep a bundled Web Provider Endpoint fallback registry** — rejected because it recreates the drift Plan 048 removes. Catalog failure degrades detection only; manual import remains available.
- **Treat Diff `line`/`side` omission as implicitly transient** — rejected. Plan 049 persists existing deep-link context unless product owners explicitly choose a different policy at its STOP condition.
- **Repair Session event gaps by invalidating lists only or adding polling** — rejected. Open detail/runtime/queue projections must converge through Plan 050's targeted semantic recovery; timeout/polling is not the authority.
- **Allow cross-workspace Issue–execution links or patch caches caller-by-caller** — rejected. Plan 051 enforces a single workspace invariant and uses owner APIs for old/new association reconciliation.
- **Fix Codex rollback by catching `thread not found` or calling `thread/resume` only in rollback** — rejected. It leaves session-scoped process ownership, notification/request routing, environment isolation, diagnostics hosts, and every other maintenance operation incorrect; Plan 052 migrates the ownership model instead.

- **Workspace symlinks escaping the lexical workspace root** — rejected as a vulnerability on 2026-07-11. Multi-folder workspaces intentionally use top-level symlinks to explicitly configured external project directories, and Workspace Explorer intentionally follows resolvable symlinks. Future hardening must preserve this capability and instead validate the authorized resolved-root set, expose resolved write targets, and require the existing non-Cradle-owned write consent.

- **Expand RemoteCradleClient method-per-API for full remote chat** — rejected 2026-07-09; use transparent upstream gateway (plan 032) instead. Typed client is debt.
- **UI/browser P2P direct to tunnel `localBaseUrl` as primary architecture** — rejected as default; keys/pairing/lifecycle stay on local server. Optional desktop optimization only, not required for 032–034.
- **Teach chat-runtime `hostId` / remote cwd** — rejected; remote sessions must not enter local TurnExecutor. Projection + upstream (033).
- **Revive agentd / `remote_host_agentd_session_links`** — rejected; table dropped in migration 0017; Cradle-Server-to-Cradle-Server HTTP is the path.
- **Handoff in the same track as first remote chat** — deferred (not rejected forever); 032–034 ship without handoff.
- **Git subprocess command injection** — `git-command.ts` uses `spawn` with arg arrays; injection-resistant. Not a finding.
- **Workspace file path traversal** — `workspace/files.ts` already uses `resolveWorkspaceFilePath` + `isWithinRoot`. Correct.
- **External provider list leaking secrets** — returns `credentialRef`, not raw values. Correct.
- **`test-reset` route exposure** — gated on `NODE_ENV === 'test'`. Acceptable.
- **Graceful shutdown not calling `abortAllRuns`** — mitigated by `recoverPersistedRunProjections` on next boot; low leverage. Noted in plan 012 maintenance, not planned separately.
- **Empty catches on chronicle download-progress / opportunistic PTY/Codex captures** — non-critical paths; lower leverage than the run-lifecycle catches in plan 012.
