From 2eaf0450b86be46d3d15ea2ccdad4384d28c43d9 Mon Sep 17 00:00:00 2001 From: brother7 <7brother7@gmail.com> Date: Wed, 26 Aug 2026 16:41:53 +0800 Subject: [PATCH] fix: integrate updater downgrade guard --- .project-docs/00-brief/success-criteria.md | 9 +- .../adr-006-pi-runtime-hard-cutover.md | 50 ++++++++++ .project-docs/10-decisions/decision-index.md | 1 + .project-docs/20-architecture/data-flow.md | 28 +++--- .project-docs/20-architecture/module-map.md | 29 +++--- .../20-architecture/system-overview.md | 13 ++- .project-docs/30-worklog/current-state.md | 35 +++++-- ...recover-pi-updater-integration-8f3a6c21.md | 95 +++++++++++++++++++ .project-docs/40-domain/business-rules.md | 15 ++- .project-docs/50-evidence/evidence-index.md | 1 + .project-docs/80-commitments/commitments.md | 11 ++- electron/main/updater.ts | 3 + tests/unit/app-updater.test.ts | 33 ++++++- 13 files changed, 276 insertions(+), 47 deletions(-) create mode 100644 .project-docs/10-decisions/adr-006-pi-runtime-hard-cutover.md create mode 100644 .project-docs/30-worklog/tasks/20260826-recover-pi-updater-integration-8f3a6c21.md diff --git a/.project-docs/00-brief/success-criteria.md b/.project-docs/00-brief/success-criteria.md index 4df151d..7c86216 100644 --- a/.project-docs/00-brief/success-criteria.md +++ b/.project-docs/00-brief/success-criteria.md @@ -18,6 +18,11 @@ - Robot Guided Hotspot Binding 必须保持固件零改动、Main-owned default-on capability、精确环境值 `0` 回滚、固定系统浏览器 Portal 和现有六位 Binding facade。Windows/macOS 页面内只能扫描开放 `Xiaozhi-*` 短效候选并连接用户明确选择的项;Makelore 不得接触家庭 Wi-Fi 凭据,也不得把热点发现或 Binding 成功等同于可信身份/在线。 - 用户模块入口策略必须由 Electron Main 将 Works `/api/auth/me` 安全投影为四个布尔值;缺失对象/字段默认开启,`design` 映射 `painting`,Renderer 不获得 Token 或原始 profile。 - 被关闭模块必须在卡片和根/深层/别名路由两层均无法进入,且在 `MainLayout`/模块初始化前阻断;Code provider 等待 policy hydration,全局 `/settings` 仍可访问,终止性 `401` 清理 Main/Renderer 会话。该客户端入口策略不能代替 API 授权。 +- Makelore Code 必须只使用精确 pin 的 Pi `0.84.2` production runtime;产品源码、安装包和资源根不得保留 OpenCode runtime/package/plugin/fallback/双路径。上游 Pi Provider adapter 的精确静态文件例外必须单列,不能误报为产品旧 runtime。 +- 新 project/Agent/Conversation 必须本地原子创建,Composer 在 worker 准备期间仍可编辑。每条 active/warm Conversation 独立绑定 persistent worker/session;Snapshot/Patch、model/thinking、queue、interaction、subagent 和 recovery 不得跨 Conversation 泄漏。 +- prompt/compact 的 `202` acceptance 与 uncertain 状态必须保持 request/run/process/background ownership,禁止自动重放;迟到 terminal 事件必须 exactly-once 收敛并重新解锁 Composer。手动 compact 在 Pi `0.84.2` 缺少 `agent_settled` 时必须由 correlated RPC 结果正确结束。 +- Provider secret 只能进入目标 worker env。未解析 Conversation 必须 validate → persist resolved model → prepare;确定性 Works user-context 缺失必须失效缓存凭据、停止 Pi 自动重试并投影固定 Provider-auth 错误,而不是表现为 runtime crash、永久 submitting 或不可恢复的 Conversation。 +- top-level worker 并发 4、child 并发 4、warm idle LRU 4、parent/child 总进程预算 8 与同项目 write lease 必须在正常、abort、crash、timeout、rebuild 和 app quit 路径全部归零;运行中的 Conversation 不得因页面隐藏而被停止。 ## Quality Checks @@ -27,7 +32,9 @@ - Learning 上线前必须部署 Works Square 项目管理、对象存储、README HTTPS URL 校验和 list/detail/media/archive API,并使用真实账号和正式安装包验证发布/下架、分页、README 图片、下载取消、缺失或错误 `Content-Length`、损坏/摘要不匹配 ZIP,以及 Windows 与签名 macOS 保存行为。 - Robot 默认引导路径必须通过聚焦 Renderer/Main/native 测试并保留 `NIANCODE_AI_HARDWARE_GUIDED_HOTSPOT_BINDING=0` 回滚。真实 Host API/native seam Electron E2E、Windows Robot 真机、签名 macOS x64/arm64、指定固件镜像与六位码发行契约未完成前,不得宣称完整双平台硬件兼容或端到端配网已经验收。 - 四模块入口策略发布前必须部署 Works migration/API、重新打包客户端,并以真实账号对 Code、Canvas、Learning、Robot 分别执行关闭 smoke;客户端自动化不得代替服务端 API 授权验收。 +- Windows 最终安装包必须通过 Pi production closure、最终 `app.asar` Main、真实 extension/ephemeral child、4 parent + 4 child live-PID、prompt/compact uncertainty、late-settle 和零残留进程验证。loopback/provider-shaped smoke 只能证明协议序列化与本地隔离,`realTurnVerified=false` 时不得宣称真实 Provider Pass。 +- macOS x64/arm64 与 native non-WSL Linux desktop/compositor 的最终安装产物仍是 cross-platform release gate;Windows 或 WSL2/WSLg 通过不能替代这些平台证据。 ## Last Reviewed -2026-08-20 +2026-08-26 diff --git a/.project-docs/10-decisions/adr-006-pi-runtime-hard-cutover.md b/.project-docs/10-decisions/adr-006-pi-runtime-hard-cutover.md new file mode 100644 index 0000000..c323d71 --- /dev/null +++ b/.project-docs/10-decisions/adr-006-pi-runtime-hard-cutover.md @@ -0,0 +1,50 @@ +# ADR-006: Makelore Code Uses Pi As Its Sole Runtime + +## Status + +Accepted and implemented on 2026-08-26. + +## Context + +The former OpenCode integration coupled Renderer state, a shared runtime lifecycle, provider configuration, and Session-specific wire behavior. Installed users observed multi-minute first-chat waits, worker exits, stuck submitting/compacting states, model-change failures, and Conversation ownership errors. The product decision was an explicit hard cutover to Pi without an OpenCode fallback or compatibility execution layer. + +## Decision + +- Pin `@earendil-works/pi-coding-agent` `0.84.2` as the sole Makelore Code production runtime. Do not maintain OpenCode fallback, dual execution, RPC/SDK alternatives, or Renderer access to runtime HTTP/RPC/event types. +- Use product-owned schema-v2 project, Agent, Conversation, model, Snapshot/Patch, command, interaction, attachment, file and subagent contracts. Pi wire and Provider details remain private to Electron Main. +- Electron Main owns the single `/api/coding/*` composition, project storage, Provider credentials, managed Prompt/Skill/extension resources, persistent per-Conversation worker/session registry, recovery, event projection, background lifecycle, process budget, subagent scheduler and same-project write lease. +- Keep one persistent worker/session per active or warm Conversation. Limit top-level running workers to 4, warm idle workers to 4, child concurrency to 4, and all parent/child processes to a shared FIFO budget of 8. A coding child shares its parent's project write lease and cannot recursively dispatch subagents. +- Public streaming is Snapshot-first plus bounded `patch-batch` SSE. Generation/sequence recovery is target-only; accepted or uncertain mutations are never replayed automatically. +- A prompt or compact confirmation timeout preserves target run permit, process ownership and Main background lease until authoritative success, failure, exit or abort converges exactly once. Page hiding cannot stop an active or uncertain run. Pi `0.84.2` manual compact may terminalize through its correlated RPC result because it does not emit `agent_settled`. +- A new unresolved Conversation validates and persists its resolved model before first worker prepare. Same-account model changes may use target `set_model`; cross-account changes rebuild only the target worker after an active run settles. +- Provider secrets enter only the selected worker environment. The deterministic Works missing user-context response expires the cached gateway credential, fails without replay and projects as a fixed Provider-auth error; it is not a Pi crash. +- Back up v1 metadata and old OpenCode sessions during migration but do not continue them. Remove only exact generated legacy Agent files; preserve unknown or modified files in the migration backup and leave unrelated `.opencode` content alone. + +## Consequences + +- Makelore Code has one runtime architecture and one product contract instead of a long-lived compatibility seam. +- A single Conversation can recover or rebuild without invalidating siblings, while process and write budgets provide bounded real concurrency. +- Installed-package regressions must be verified against the final `app.asar` and `resources/pi-runtime`, not only workspace tests. +- Provider-shaped loopback proves serialization and local isolation only. The user explicitly waived real Provider account testing and accepted authentication, endpoint/proxy/rate-limit, protocol variation, real concurrency and credential-isolation risk; `realTurnVerified=false` must remain visible and is not Pass. +- Windows x64 final-package evidence is available. macOS x64/arm64 and native non-WSL Linux desktop/compositor remain release evidence gaps, so the product is not yet cross-platform release-ready. + +## Supersedes + +- The product OpenCode runtime, plugin, package, routes, Renderer store/UI, resource bundle and compatibility execution behavior integrated before the Pi cutover. + +## Evidence + +- Pi cutover planning/spec delivery: `a8c0806` +- Runtime qualification through feature UI and hard cutover: `2bc423e` through `977445b` +- Release proof and resilience chain: `a795e0c` through `9f05e2d` +- Provider-context correction implementation: `a098266` +- Integrated delivery: `48a9189` +- Runtime release runbook: `docs/pi-runtime-release-runbook.md` + +## Related + +- `.project-docs/20-architecture/system-overview.md` +- `.project-docs/20-architecture/module-map.md` +- `.project-docs/20-architecture/data-flow.md` +- `.project-docs/40-domain/business-rules.md` +- `.project-docs/00-brief/success-criteria.md` diff --git a/.project-docs/10-decisions/decision-index.md b/.project-docs/10-decisions/decision-index.md index c768609..abd010f 100644 --- a/.project-docs/10-decisions/decision-index.md +++ b/.project-docs/10-decisions/decision-index.md @@ -9,6 +9,7 @@ | ADR-003 | Robot 配网页内扫描并连接 Windows/macOS 热点 | Accepted / implemented with physical release gates pending | 2026-08-16 | Robot Renderer、Host API、Electron Main、Windows WLAN、macOS CoreWLAN/CoreLocation | `adr-003-robot-in-app-hotspot-connection.md` | | ADR-004 | Works Square 统一拥有桌面认证生命周期边界 | Accepted / implemented | 2026-08-19 | Renderer、Host API、Electron Main、Works Square auth facade | `adr-004-square-auth-lifecycle-boundary.md` | | ADR-005 | Learning 采用运营精选项目目录、直接渲染经校验的 HTTPS README 图片、下载不做大小校验并退役课程运行时 | Accepted / implemented, amended 2026-08-20 | 2026-08-20 | Learning Renderer、Host API、Electron Main、Works Square operations/API | `adr-005-learning-project-catalog.md` | +| ADR-006 | Makelore Code 以 Pi `0.84.2` 为唯一 runtime,Main 独占 worker/session/provider/lease,产品只暴露 Snapshot/Patch 合同 | Accepted / implemented | 2026-08-26 | Code Renderer、Host API、Electron Main、Pi runtime、Provider/resource、packaging | `adr-006-pi-runtime-hard-cutover.md` | ## Superseded Decisions diff --git a/.project-docs/20-architecture/data-flow.md b/.project-docs/20-architecture/data-flow.md index b97e1c6..13ee9dc 100644 --- a/.project-docs/20-architecture/data-flow.md +++ b/.project-docs/20-architecture/data-flow.md @@ -12,11 +12,12 @@ | 提交绑定 | 云端成功上传响应 | Main → submission binding v2 | 只持久化成功的 app/version/review/hash;落盘失败返回固定告警但不反转提交 | | 运营发布 | Works Square 审核与交付 | 公共 `play_url` | 客户端只消费服务端发布结果;真实合同校验 → OSS/CDN 生产链仍待整链验收 | | 可信发布门禁 | source+built+artifact contract | 服务端逐字节重算/合同校验 → 不可变 Release 固化 → 人工审核 | 服务端不运行项目 Vite;仍是不可绕过权威,未来 runtime 强门禁需可信 verifier | -| AI 编程 Session 执行提交 | ChatPanel 当前 Agent / Session | Renderer per-Session run state → deferred provider sync → Host API → Main manager FIFO → per-project Agent FIFO → runtime/config + Agent readiness → OpenCode message / command / summarize | 新建 session 已知为空时不等待空历史;普通 prompt、项目命令和压缩不重发模型 override;不同 Session 没有 reply-duration 全局锁。provider/runtime 或 Agent 未应用时以 typed `409`、`promptSent:false` 在执行前终止,且不自动重启、reload 或 dispose 共享 runtime | -| AI 编程 Session 模型切换 | 页面模型选择器或 `/models` / `/model` | Renderer Session model action → Host API → Main OpenCode client → `POST /api/session/{sessionID}/model` | Main client 将内部 `modelID` 映射为 OpenCode 1.18.9 wire `ModelRef.id`;只改变现有 Session 的后续 turn 模型并从 Session metadata 恢复,不写伙伴默认模型、provider 配置或 runtime freshness,也不要求 runtime restart | -| AI 编程启动确认 | Host POST、OpenCode HTTP polling 与 Session events | Renderer `{sessionId, runToken}` watchdog → `sessionRunStates[sessionId]` | Host 接受与 user transcript 不算 ACK;busy/retry、assistant、question、permission 或 typed terminal event 才确认/结束启动。10 秒无确认只终止该 Session 为 `SESSION_START_UNCONFIRMED`,清理其队列且不自动重放 | -| AI 编程上下文压缩 | OpenCode compaction Part / `session.compacted` / `/compact` | Renderer Store → per-session transcript state → Chat mixed timeline | 手动请求先创建 immutable UI event;native identity 负责归并与回放去重;完成只更新压缩项,真实 idle 才结束 run 和释放队列 | -| AI 编程模型代理错误 | OpenCode provider 请求 | Main Host AI proxy → Works 模型上游 | 配额耗尽保持独立终止态;只有明确的分组上游饱和才投影为 OpenCode 终止状态,通用限速仍保留 `429` | +| AI 编程 Conversation 创建与准备 | 当前 project / Agent / 本地 Conversation | Renderer 本地创建 → `/api/coding/*` → Main schema-v2 store → target Pi worker/session lazy prepare | Composer 与 worker 准备解耦;首次 Conversation 可立即编辑。未解析模型必须先验证并持久化,再打开首个 worker;旧 OpenCode 会话只备份,不继续运行 | +| AI 编程 prompt 与流式投影 | 当前 Conversation draft / attachment refs | Renderer mutation → Host `202` acceptance/dedupe → Main target worker → Pi events → Snapshot + `patch-batch` SSE → Renderer reducer | 每个 Conversation 独立 generation/seq;gap 只恢复目标 Conversation。accepted/uncertain mutation 不自动重放,权威 Snapshot 到达前保留相同 optimistic node identity | +| AI 编程模型与思考强度切换 | 当前 Conversation | Renderer → `/api/coding/conversations/{id}` → Main validate/persist → target runtime | 同账号已解析模型复用 `set_model`;跨账号模型等 active run settled 后只重建目标 worker,旧凭据不会进入新 generation;切换失败不销毁可继续使用的旧 worker | +| AI 编程上下文压缩 | Renderer compact action / Pi compaction events | correlated compact RPC → target projector → Snapshot/Patch timeline | Pi `0.84.2` 的手动 compact 不发 `agent_settled`;RPC success 或权威 compaction failure 终结 compact 并 exactly-once 释放对应 ownership,不提前结算普通 prompt | +| AI 编程 Provider 错误 | Pi worker Provider 请求 | selected worker env → Main AI proxy → Works 模型上游 → projector safe error | 确定性的 Works user-context 缺失使缓存 gateway credential 过期、返回非重试认证失败且不重放请求;Renderer 只看到固定 Provider-auth 提示,不能把它解释为 Pi 崩溃 | +| AI 编程子 Agent | parent `subagent.v1` tool call | explicit extension → authenticated Main bridge → scheduler → ephemeral Pi child | child 并发最多 4、单次最多 8、禁止递归;父子共享总进程预算 8,coding child 与 parent 共用项目 write lease,父 abort/crash/generation 失效必须清理 child 与 permit | | 客户端更新检查 | 设置页 | Renderer update store → IPC → Main AppUpdater → 目标 feed | Main 记录并重抛原始错误;Renderer 只显示去重、脱敏的单条提示,稳定源缺包不伪装为最新版 | | 设计会话创建/切换 | AI 绘画页面或侧栏 | Renderer API → Main → Workspace Conversation API | 新会话属于现有 Workspace;读取独立消息、Brief、Quote 和 `turnRevision` | | 设计消息与确认 | 当前 Conversation | Main ↔ 持久 Agent Gateway Session WebSocket → Conversation 快照 | `command.submit`、Run 与设计事件共用连接;传输失败才以同一幂等 ID 回退 REST;结构化业务错误不重试且未知文本由 Main 脱敏;请求和流式结果同时绑定 Workspace + Conversation | @@ -45,12 +46,13 @@ - 图生图参考图与视频首帧都先归一为当前 Workspace 的 Asset;从作品选择时复用生成结果 Asset,本地选择时先走既有上传接口,再把唯一 Asset ID 随 Turn 提交。选择或上传成功后关闭选择器。 - 本地开发适配器将旧单会话 schema v2 原子迁移为带默认 Conversation 的 schema v3;打包应用不使用该本地适配器作为云端失败回退。 - 注销和退出会关闭本地事件流并清除本机 Conversation Session-id 缓存;服务端持久 Session 保留,下一次访问从 Conversation API 重新读取。 -- AI 编程 Store 的 `sessionMessagesBySessionId` own-key 是加载状态契约:键缺失表示历史未知,存在且值为 `[]` 表示已知为空。只有后者可使用不读取历史的快速选择;普通历史会话选择继续刷新消息。 -- AI 编程 Store 的运行态、错误、startup deadline 和内部 pending queue 按 Session 持有;top-level error 只表示真实全局错误。任何不确定的远程失败只清理对应 Session 的旧队列,手动重试不得复活失败前已排队的 prompt。 -- OpenCode Session 持有现有会话的 active provider/model;伙伴 `model` 只在创建新 Session 时提供默认值。页面与 slash command 通过同一 native Session endpoint 切换,后续执行和压缩不得用伙伴默认值覆盖该选择。 -- Main 以 manager identity + runtime generation provenance 持有 runtime-config latch,并按 Agent id 持有 desired/applied 内容状态与本 generation 已知 id。owned `fresh` generation 中首次出现的唯一 id 可在 live `/agent` 暴露后应用,且不会阻断已有未变 Agent;同 id 编辑、删除后重建、live id 缺失以及 attached/unknown generation 均不能越过门禁。托管本地 AI proxy 的 fresh runtime config 直接使用当前进程 Host API token;把该已生效 token 写回 provider storage 不构成 runtime freshness 变化,attached/unknown generation 或 timeout/partial persistence 仍保持 sticky pending。 -- AI 编程压缩状态由 transcript 中的 `compactionsById` / `compactionOrder` 单一持有。运行中 hydration 保留 manual pending identity 并用 native Part 合并;completed 不得回退为 running。`session.compacted` 不是 run idle,不能据此释放 queued prompt;失败、中止或 runtime generation 变化只清理对应未完成事件。 -- Main Host AI proxy 可为固定 OpenCode 重试契约做窄化的内部状态投影:配额耗尽投影为 `402`,明确上游分组饱和的 `429` 投影为终止 `400`,其他 `429` 原样保留;升级 OpenCode 时必须重新验证该契约。 +- project、Agent、Conversation schema v2 与每条 Conversation 的 opaque Pi session binding 由 Main 原子持久化。旧 v1 先做时间戳备份再迁移;旧 OpenCode Session 不进入新 runtime,也不删除无关 `.opencode` 内容。 +- Renderer Store 只拥有产品中立 Snapshot、Patch、optimistic request 与 project-scoped metadata。每条 Conversation 的 generation/seq 独立;stale generation 丢弃,gap/reconnect 只为目标 Conversation 取 Snapshot 并重放严格连续的缓冲 tail,不重放 mutation。 +- Main 为每条 active/warm Conversation 持有一个 persistent Pi worker/session、Provider/resource revision 和 selected-account secret env。运行中 stale revision 保留本 run snapshot,settled 后重建;其他 Conversation 不受影响。 +- top-level worker 并发上限 4、warm idle LRU 上限 4、child 并发上限 4,parent/child 共用 FIFO process budget 8。同项目 coding mutation 共用 write lease;read-only child 可旁路写锁。 +- prompt/compact 的 RPC confirmation timeout 只进入 uncertain,不能释放 run permit、process ownership 或 Main background lease。迟到 success/failure/exit/abort 必须单调且 exactly-once 收敛;隐藏页面超过 grace 也不得停止 active/uncertain run。 +- AI 编程压缩状态属于目标 Conversation Snapshot。Pi `0.84.2` 手动 compact 的 correlated RPC success 可代替缺失的 `agent_settled` 终结 compact;普通 prompt 仍只由权威 terminal/settled 结束。 +- Main Host AI proxy 持有凭据刷新和错误分类。确定性 Works user-context 缺失必须失效缓存 credential 并 fail fast;真实认证、endpoint/proxy/rate-limit、Provider variation、真实并发与跨 worker 凭据隔离仍是用户明确接受但未验证的风险。 - Main AppUpdater 持有 feed、原始异常、下载和安装状态;设置页只消费状态投影。一次 electron-updater `error` 事件覆盖的并发检查不会在 Renderer 重复发错,但独立的后续检查仍有自己的报告生命周期。 - Robot V1 引导状态只在 Renderer 当前进程内保存,不持久化 Wi-Fi 凭据、activation code 或 Binding operation ID。相同进程内的模糊 Binding 重试复用 operation ID;无效码或重启后必须取得新码并使用新 operation ID。 - Robot Hotspot Module 只在 Main 内保存最近一次扫描的短效、不透明候选快照。新的扫描、clear、60 秒过期或进程重启使旧 candidate ID 失效;Renderer 关闭/重开向导以 generation 防止旧扫描/连接结果回写。 @@ -60,7 +62,7 @@ - Works Square 项目创建、版本上传、构建状态与 Release 状态 API。 - Works Square `/api/auth/me` 模块权限 API;Electron Main 持有 Bearer 并只向 Renderer 投影 `programming`/`design`/`learning`/`robot` 对应的四个布尔值。 - 本机 Host API 的发布路由;发布路由要求 Renderer capability。 -- 本机 Host API 的 OpenCode session、history 与 prompt 路由,以及 Main-owned AI 模型代理。 +- 本机 `/api/coding/*` project/Agent/Conversation、Snapshot/patch-batch SSE、prompt/compact/fork/recover、attachment/file/interaction/diagnostics 路由,以及 Main-owned AI 模型代理。 - Main-owned electron-updater IPC 与 Works Square 平台/架构稳定 feed;正式安装产物发布不由 Renderer 控制。 - 服务端安全投影后的公共 `play_url`;只接受同源 HTTPS、精确 App 路径和可信版本状态。 - Works Square Workspace/Conversation API、每个 Conversation 的持久 Agent Gateway Session、单次 WebSocket ticket、双向命令/事件帧与幂等 REST 传输回退。 @@ -70,4 +72,4 @@ ## Last Updated -2026-08-21 +2026-08-26 diff --git a/.project-docs/20-architecture/module-map.md b/.project-docs/20-architecture/module-map.md index b3981d1..703c5a1 100644 --- a/.project-docs/20-architecture/module-map.md +++ b/.project-docs/20-architecture/module-map.md @@ -15,16 +15,15 @@ | `electron/services/project-packager.ts` | 受控项目扫描、静态 ZIP 生成和敏感/历史控制文件排除 | 只允许可发布 `ProjectType`,不提供 Compose 或手工 ZIP 路径 | | `electron/services/works-submission-binding.ts` | submission binding v2 持久化与旧 schema 迁移 | 旧中间态终止为 `legacy_retired`;文件名暂作安装兼容 | | `electron/api/works-play-url.ts` | 公共播放 URL 的共享安全校验 | 公共 `play_url` 必须同源 HTTPS 且精确匹配 App 路径 | -| `src/pages/Chat/OpencodeChatPanel.tsx` | AI 编程 Agent 选择、首次 session 创建、per-Session 模型选择、prompt 提交与消息/压缩混合时间线 | 页面模型选择器与 `/models` / `/model` 共用 OpenCode Session 切换动作;伙伴模型只作为新 Session 默认值,选中 Session 只投影自己的 run error | -| `src/lib/opencode-session-state.ts` | OpenCode 会话消息与上下文压缩时间线的规范化、hydration 和事件归并 | 压缩 UI identity 不变;native Part/event identity 用于回放去重,completed 状态不可降级 | -| `src/stores/opencode.ts` | OpenCode runtime、session、消息缓存、per-Session 模型/提交/启动确认与压缩生命周期 | Session 列表 hydration 恢复当前模型;每个 Session 独立持有 model/run/error/queue,10 秒 run-token watchdog 只终止对应 Session 且不自动重放;`session.compacted` 只完成压缩项,真实 idle 才释放 run 队列 | -| `electron/opencode/client.ts` | Makelore 内部 OpenCode DTO 与固定 runtime HTTP wire contract | 内部 `{ providerID, modelID }` 在 Session 模型切换边界映射为 OpenCode 1.18.9 `ModelRef` 的 `{ providerID, id }`;runtime 升级时复核 schema | -| `electron/opencode/provider-config.ts` | Main-owned OpenCode provider/env 配置构建 | 托管本地 AI proxy 在 owned fresh runtime 启动时使用当前进程 Host API token;持久化旧 token 不得覆盖本次 runtime 已注入值 | -| `electron/api/routes/opencode.ts` | Main-owned OpenCode Session 模型切换、execution acceptance、provider/runtime freshness 与项目 Agent 门禁 | Session 模型切换调用 OpenCode native endpoint,不写 provider/伙伴配置;fresh runtime 已生效 proxy token 的持久化不标记 stale,attached/unknown 与不确定写入仍 fail closed;message/command/summarize 共用 bounded manager FIFO,普通执行路径不自动 restart/reload/dispose | -| `electron/opencode/project-agent-runtime.ts` | 项目 Agent 逐 id desired/applied 内容状态、runtime generation provenance、live registry acceptance 与配置 mutation 串行化 | owned fresh generation 中首次出现的唯一 id 可在 live `/agent` 暴露后应用;同 id 编辑、删除后重建及 attached/unknown generation 仍 fail closed,用户修改的 retired Agent 文件不得被删除 | -| `electron/opencode/runtime-config-readiness.ts` | manager + generation scoped provider/runtime stale latch | timeout/partial persistence 保持 sticky uncertain 状态;只有成功显式 apply 后的合格 fresh generation 可解除,迟到 lease 不能改写 readiness | -| `electron/api/routes/ai-proxy.ts` | Main-owned 模型代理、凭据边界与上游响应投影 | 仅对明确上游分组饱和做终止态兼容投影,通用限速保持 `429` | -| `shared/opencode-error-details.ts` | OpenCode 上游饱和错误的窄化共享分类 | 不以通用 `rate_limit_exceeded` 单独判定饱和 | +| `src/pages/Chat/CodingChatPanel.tsx` / `CodingConversationTimeline.tsx` / `CodingComposer.tsx` | Agent/Conversation 选择、Snapshot-first 时间线、prompt/steer/follow-up、交互、压缩、分支与附件 UI | Composer 不等待 worker 准备;所有异步结果按 project/Agent/Conversation 所有权隔离,Renderer 不导入 Pi wire 类型 | +| `src/stores/coding-conversations.ts` / `coding-workspace.ts` | per-Conversation Snapshot/Patch、乐观提交、gap recovery 与项目级元数据 | generation/seq 严格递增;只恢复目标 Conversation,SSE 重连不重放 mutation | +| `shared/coding-conversation-*.ts` / `coding-project-contracts.ts` | Main/Renderer 共用的产品中立 Conversation、project tool、subagent 与 reducer 合同 | 这是唯一公共实现;Pi 事件、RPC 与 Provider 形状保持 Main-private | +| `electron/api/coding-composition.ts` / `electron/api/routes/coding-*.ts` | 唯一 Main-owned Coding composition 与 `/api/coding/*` Host API | 管理 project/Agent/Conversation、SSE、附件、文件、命令、交互和安全诊断;mutation 返回 202 acceptance 且按 request id 去重 | +| `electron/coding-projects/` | schema-v2 项目/Agent/Conversation 数据、原子持久化、旧 v1 备份迁移与项目文件能力 | 旧 OpenCode 会话只备份不续写;modified/unknown legacy Agent 文件先备份,valid v2 不读取旧 runtime 数据 | +| `electron/coding-runtime/conversation-service.ts` | 产品级 Conversation orchestration、模型/思考强度、fork/archive/recover 与运行时门禁 | unresolved Conversation 先验证并持久化模型再首次 prepare;已解析 Conversation 的模型切换复用 target runtime seam | +| `electron/coding-runtime/pi/{worker-pool,worker-process,rpc-client,session-registry,event-projector}.ts` | 一 Conversation 一 persistent Pi worker/session、严格 LF JSONL RPC、generation recovery 与 Snapshot/Patch 投影 | top-level cap 4、共享总进程预算 8、warm idle LRU 4;worker 替换只使目标 generation 失效 | +| `electron/coding-runtime/pi/{provider-config,provider-refresh,resource-loader}.ts` / `electron/api/routes/ai-proxy.ts` | Main-owned Provider catalog、secret/env 投影、资源 revision、一次凭据刷新与上游错误分类 | secret 不进入 argv/catalog/Renderer;确定性的 Works user-context 缺失使缓存凭据过期并 fail fast,不自动重放 mutation | +| `electron/coding-runtime/pi/{extension-host,subagent,write-lease}.ts` / `electron/main/background-lifecycle.ts` | 唯一显式 Makelore extension、交互、子 Agent、同项目写租约与后台运行租约 | child 并发 4、单次最多 8、禁止递归;active/uncertain run 不因页面隐藏或确认超时被回收,所有 stop/replacement 必须有原因 | | `electron/main/updater.ts` | 目标 feed 解析、electron-updater 生命周期与原始诊断 | 缺少稳定 manifest 保持错误;事件/Promise 重复失败按检查实例去重 | | `src/components/settings/UpdateSettings.tsx` | 更新状态、重试与用户可读错误展示 | 只显示一条简洁中文提示;技术诊断统一回退到本地化通用文案 | | `shared/image-workspace.ts` | AI 绘画 Workspace、Conversation、Task、Asset 与事件共享契约 | Conversation 状态与 Workspace 任务归属必须分离 | @@ -51,7 +50,7 @@ ## Dependency Direction - Renderer UI → Renderer API contract → Main Host routes → Main services → Works Square;Renderer 不反向读取 Main 凭据、文件系统或归档。 -- AI 编程 Renderer per-Session model/run state → Host API → Main Session switch 或 manager-scoped execution acceptance → per-project Agent acceptance → OpenCode runtime;OpenCode provider 请求再经 Main AI proxy 访问模型上游。Renderer 不直接持有上游凭据或本地 runtime URL,Main 临界区只覆盖配置/请求 acceptance,不覆盖模型回复时长。 +- AI 编程 Renderer product Snapshot/commands → typed `/api/coding/*` Host API → Main Coding composition → target `CodingConversationRuntime` → persistent Pi worker;Pi Provider 请求再经 Main AI proxy 访问模型上游。Renderer 不持有 Pi wire、凭据或本地 runtime URL。 - Project configuration 决定产品分流;Main release builder 生成 source/built/contract,服务端独立重算和校验决定发布安全,本地 `ProjectType` 不是授权结论。 - Built artifact preflight 检查最终上传的同字节快照,但客户端可被绕过且不产生可信 receipt;服务端仍是合同、摘要和不可变 Release 安全权威。 - Robot Renderer → typed AI hardware API → Main Host route → Robot Hotspot Module → Windows/macOS Adapter。云端 Binding 仍由 Main 代理;热点选择/连接移入页面,但家庭 Wi-Fi 凭据输入仍只留在固件 Portal,系统 Wi-Fi 保留为兜底。 @@ -65,10 +64,10 @@ - `electron/agent-browser/module.ts` 的预检必须继续拒绝外域访问、隔离临时 partition 并清理所有 view/listener;不能因本地通过而跳过服务端逐字节校验。生产 opaque-origin 行为不由该 loopback 检查覆盖。 - `works-cloud-deploy.json` 是兼容文件名;不得因名称重新引入自动部署协调语义。 - `runtime_url` 是一个客户端版本的迁移回退;删除前必须确认服务端和存量数据稳定提供 `play_url`。 -- `ai-proxy.ts` 的上游饱和状态投影依赖当前固定 OpenCode 的重试语义和窄化错误文案;升级 runtime 或调整上游错误格式时必须复核,不能把所有 `429` 统一终止。 -- OpenCode 压缩时间线依赖 native compaction Part、Session run token 与 runtime generation 的关联;hydration 必须保持 completed 单调,不能用 `session.compacted` 提前结束 run 或释放 queued prompt。 -- OpenCode execution acceptance 同时涉及 provider persistence、runtime lifecycle、Agent config mutation 和 runtime HTTP。锁顺序必须保持 manager → project,所有等待与 HTTP 都使用同一 hard deadline/AbortSignal;timeout 后 lease 必须撤销,queued cancellation 不能让后续 mutation 绕过前驱。 -- OpenCode Agent hot reload 没有 authoritative whole-instance quiescence oracle。owned fresh generation 可接受 live registry 新发现的唯一 id,但不得以 live 同 id 代替内容 freshness;同 id 编辑或删除后重建若要即时生效,必须先获得 upstream directory-scoped invalidation 或权威 quiescence seam,不能从 `/session/status` 推断 dispose/reload 安全。 +- Pi RPC confirmation timeout is an uncertainty boundary, not permission to release run/process/background ownership. Late success/failure/exit/abort must converge monotonically and exactly once; no accepted or uncertain mutation may be replayed automatically. +- Pi manual compaction in locked `0.84.2` does not emit `agent_settled`; correlated compact RPC success or an authoritative compaction failure must terminalize the compact operation without prematurely settling an ordinary prompt. +- Parent and child workers share one FIFO process budget of 8. Child reservation may reclaim ready/idle or queued parent capacity without losing FIFO order, session binding, or generation ownership; stop/reclaim failure must release leases in `finally`. +- Provider/resource revisions apply before the next prompt for idle workers and after `agent_settled` for active runs. Same-account model changes may use `set_model`; cross-account changes rebuild only the target Conversation so old credentials never enter the new worker. - `electron/main/updater.ts` 的稳定源错误归一化必须保持窄化:只识别 Works Square 对应 manifest 的 404,不得吞掉其他 feed/网络/签名错误;Renderer 的脱敏边界不能取代 Main 原始日志。 - 多 Conversation 事件处理必须区分对话快照与 Workspace 任务更新;不得用任务时间戳推进 Conversation 流水位,也不得让旧会话的迟到流覆盖当前会话。 - Quote 编辑、重报价、确认和项目删除都跨 Renderer/Main/Works Square。异步结果必须核对当前 Workspace + Conversation;删除当前项目时必须先使旧选择和事件流失效,再加载剩余 Workspace。 diff --git a/.project-docs/20-architecture/system-overview.md b/.project-docs/20-architecture/system-overview.md index 00b1585..8ed14df 100644 --- a/.project-docs/20-architecture/system-overview.md +++ b/.project-docs/20-architecture/system-overview.md @@ -18,6 +18,11 @@ Makelore 是 Electron 桌面客户端。Renderer 负责项目操作与状态展 | Play URL Projection | 校验服务端公共播放地址 | 只接受同源 HTTPS 和精确 `/apps/{app_id}/`;优先 `play_url`,`runtime_url` 仅一版本回退 | | Built Artifact Preflight | 在上传前对最终 built snapshot 做桌面/移动 UX fail-fast | Main 使用临时 loopback origin 与 Electron WebContents/CDP;不使用 Playwright、不生成可信证明 | | App Updater | Main 选择平台/架构 feed、驱动 electron-updater 并记录原始诊断 | Renderer 只接收状态和脱敏后的单条用户提示;稳定源缺包保持错误 | +| Makelore Code Workspace | project/Agent/Conversation schema v2、产品中立 Snapshot/Patch、Composer 与时间线 | Conversation 本地创建且不等待 worker;旧 OpenCode 会话备份后不再继续,Renderer 不导入 Pi RPC/event 类型 | +| Coding Host API & Composition | 唯一 `/api/coding/*` composition、202 acceptance/dedupe、SSE、附件/文件/交互/诊断 | Electron Main 拥有 project/Conversation 服务、选中目标、认证和错误脱敏;SSE 公开面只有 Snapshot 与 `patch-batch` | +| Pi Conversation Runtime | 每条 active/warm Conversation 一个 persistent Pi `0.84.2` worker/session | 严格 LF JSONL RPC、generation recovery、Snapshot hydration;top-level 并发 4、warm idle LRU 4、总进程预算 8 | +| Pi Provider & Managed Resources | Provider catalog、secret/env 投影、model/resource revision、Prompt/Skill/extension materialization | 凭据只进入选中 worker env;不扫描项目或用户的 `.pi/.agents/.codex`,不把 secret 放进 argv、catalog 或 Renderer | +| Pi Extension, Subagents & Lifecycle | 唯一显式 Makelore extension、UI interaction、ephemeral child、write lease 与 background run lease | child 并发 4、单次最多 8、禁止递归;active/uncertain run 不因页面隐藏或 confirmation timeout 被停止,replacement/stop 必须可解释并清理所有 ownership | | AI Design Workspace | 保存项目身份、Conversation 列表、生成任务和资产 | 任务和资产在切换 Conversation 后继续可见 | | AI Design Conversation | 保存消息、Brief、Quote、`turnRevision` 与服务端 Agent Session 绑定 | 同一 Workspace 内互相隔离;Session 由服务端持久化 | | AI Design Image Source Picker | 当前 Workspace 已完成图片或本地上传 | 图片 Brief 绑定图生图参考图;视频 Brief 绑定首帧;提交一个真实 Asset ID | @@ -47,6 +52,10 @@ Makelore 是 Electron 桌面客户端。Renderer 负责项目操作与状态展 - Robot model, language, and voice choices are dynamically projected from the Xiaozhi USER catalog through Works Square and Electron Main; the catalog is bounded, account-scoped, and `private, no-store` at each public hop. - One local account maps to one server-side Xiaozhi account binding. Agents and devices are resources beneath that account binding, not separate Xiaozhi users. - Robot/Canvas/module-selection routes must not initialize AI Programming projects or providers. +- Makelore Code 的唯一 production runtime 是精确 pin 的 Pi `0.84.2`;不得恢复 OpenCode fallback、双 runtime、兼容执行路径或 Renderer 直连本地 runtime。上游 `@earendil-works/pi-ai` 内名称含 `opencode` 的静态 Provider adapter 不属于产品旧 runtime。 +- Renderer 只消费共享的产品中立 project/Conversation/Snapshot/Patch DTO,并经 `src/lib/host-api.ts` 或 `src/lib/api-client.ts` 访问 Main。Pi RPC、event、Provider credential 和本地 runtime 路径保持 Main-private。 +- accepted/uncertain Coding mutation 不得自动重放;RPC confirmation timeout 后仍保留 target run permit、process ownership 和 background lease,直到迟到 success/failure/exit/abort 权威收敛。其他 Conversation 必须继续可用。 +- selected Provider credential 只投影到目标 worker env;跨账号模型变化必须重建目标 worker。确定性 Works user-context 缺失是 Provider-auth failure:失效缓存 credential、fail fast、固定脱敏提示,不得归类为 Pi worker crash。 - Guided Hotspot Binding is implemented behind a Main-owned capability that is true by default; exact `NIANCODE_AI_HARDWARE_GUIDED_HOTSPOT_BINDING=0` disables it, while capability-read failure falls back to direct six-digit Binding. The guided state is process-local, opener failures expose only the same fixed address for manual copy, and Binding conflicts refresh the safe account overview. - Robot hotspot scanning and connection are local Main operations that return before Works credentials/upstream access. Renderer may submit only an opaque candidate ID from the latest bounded scan; Main alone filters open printable `Xiaozhi-*` SSIDs, performs platform association, and verifies the exact current SSID. - Hotspot discovery and connection do not authenticate a Robot. BSSID, interface/profile details, native diagnostics, location data, and Wi-Fi credentials never cross the Main boundary; permission or platform failure keeps the system-settings/manual path available. @@ -78,8 +87,8 @@ Makelore 是 Electron 桌面客户端。Renderer 负责项目操作与状态展 ## Related Decisions -- 当前长期边界记录于 README、ADR-001 至 ADR-005、集成任务 `20260807-integrate-login-client-a4f8`、源任务 `20260810-static-release-only-a91c`、`20260812-client-built-release-makelore-7e5b`、`20260812-design-image-to-image-client-c91e` 及本次 Integration Gate;后续如改变唯一入口、凭据所有权、构建执行边界、Conversation 状态归属、Learning 项目分发边界、Robot 配网/绑定安全边界或重新引入客户端部署协调器,应新增 ADR。 +- 当前长期边界记录于 README、ADR-001 至 ADR-006、集成任务 `20260807-integrate-login-client-a4f8`、源任务 `20260810-static-release-only-a91c`、`20260812-client-built-release-makelore-7e5b`、`20260812-design-image-to-image-client-c91e` 及本次 Integration Gate;后续如改变唯一入口、凭据所有权、构建执行边界、Conversation 状态归属、Pi 单 runtime/worker/lease 边界、Learning 项目分发边界、Robot 配网/绑定安全边界或重新引入客户端部署协调器,应新增 ADR。 ## Last Updated -2026-08-20 +2026-08-26 diff --git a/.project-docs/30-worklog/current-state.md b/.project-docs/30-worklog/current-state.md index b70f734..2d39193 100644 --- a/.project-docs/30-worklog/current-state.md +++ b/.project-docs/30-worklog/current-state.md @@ -4,6 +4,28 @@ This file is the integrated default-branch snapshot. Feature tasks record progre ## Integrated Through +- Updater downgrade-prevention source commit `2e61800` from feature task + `20260826-fix-version-update-check-7c91a4` is integrated by task + `20260826-recover-pi-updater-integration-8f3a6c21`. Every automatic-update + channel assignment now immediately restores + `autoUpdater.allowDowngrade=false`, so installed `2.0.0` does not treat an + online `1.1.9` manifest as an available update; later explicit channel + changes preserve the same invariant. +- Local `main` is integrated through delivery `48a9189` by task + `20260826-integrate-pi-provider-fix-6e4c2a91`. The strict 101-commit + fast-forward replaces OpenCode with pinned Pi `0.84.2` as Makelore Code's + sole production runtime, adds schema-v2 project/Agent/Conversation storage, + persistent per-Conversation workers, product Snapshot/Patch contracts, + `/api/coding/*`, Provider/resource isolation, extension/subagents, process + and write budgets, and background-run uncertainty ownership. Implementation + `a098266` additionally validates/persists an unresolved Conversation model + before first prepare and converts the exact Works missing user-context + response into a non-replayed Provider-auth failure after expiring the cached + gateway credential. The final Windows installer and final packaged Pi proof + passed; real Provider turns remain explicitly waived with + `realTurnVerified=false`, while macOS x64/arm64 and native non-WSL Linux + remain unverified. Older OpenCode entries below are retained only as + historical integration evidence and are superseded for current behavior. - OpenCode model-switch runtime correction source commit `cce7722` from feature task `20260821-model-switch-runtime-fix-a83d6c91` is integrated on local `main` by task `20260821-integrate-model-switch-fix-8f2d6c41`. The Main-owned @@ -131,7 +153,7 @@ Makelore 在会话恢复、登录和刷新后由 Electron Main 请求 Works `/ap AI 学习现在是已启用的运营精选项目目录,并继续受登录和 `module_access.learning` 控制。Renderer 通过 Main-owned Host API 获取分页项目卡片和 README 详情;Markdown 支持 GFM、禁用原始 HTML。服务端发布时只校验图片 URL 为无凭据、默认端口、无 fragment 且当前 DNS 结果全部为公网地址的 HTTPS URL,保留地址而不下载、识别格式、转码或镜像;客户端仅为 README 图片节点启用直连,因此 SVG 和 Electron 支持的其他格式可直接显示,单图失败不阻断详情。封面和历史发布媒体继续走受控路径。详情页的下载按钮打开系统保存对话框;Main 将 ZIP 流式写入临时文件,只允许最多五跳同 Works origin 重定向,不校验 `Content-Length`、`archiveBytes`、实际流字节数或客户端大小上限,校验 SHA-256 和 ZIP 签名后原子保存,Renderer 只接收 `saved` 或 `cancelled`。课程生成、进度、本地课程库、OpenMAIC player、Agent、ASR、课堂 runtime、Learning IPC 和 player artifact 打包已删除且没有兼容读取路径;历史课程数据保留但不再读取。服务端和客户端源码契约已完成,不代表生产部署或真实账号安装包联调已经完成。 -AI 编程首次发送在新建 OpenCode session 已知为空时不再等待冗余历史读取,prompt 可直接进入 Host API;普通历史会话仍刷新消息。伙伴模型只作为新 Session 默认值,现有 Session 的 active model 由 OpenCode 持有;页面选择器和 `/models` / `/model` 走同一个 native Session 切换动作,Main 在 OpenCode client 边界把内部 `modelID` 映射为 1.18.9 `ModelRef.id`,后续 prompt、项目命令和上下文压缩不再用伙伴默认值覆盖选择。每个 Session 独立持有 model、run、loading、error 与内部队列;一个 Session 的回复或终止不会锁住、改写或重放另一个 Session。Host 接受或用户消息落库不算启动确认,只有明确 busy/retry、assistant、question、permission 或 typed terminal event 才确认/终结启动;独立的 10 秒 watchdog 到期时仅以 `SESSION_START_UNCONFIRMED` 终止该 Session。Main 按 Agent id 维护 desired/applied 内容状态与 runtime generation provenance;owned fresh generation 中首次出现的唯一 id 可在 live registry 暴露后启用且不阻断已有 Agent,同 id 编辑、删除后重建及 attached/unknown generation 仍保持 pending。fresh runtime 的本地 AI proxy provider 在构建时使用当前 Main Host API token;后续将该已生效 token 持久化不构成 provider/runtime 变更,attached/unknown generation 或不确定写入仍保持 pending。provider/runtime 或 Agent 未应用时在执行前返回 typed `409` 且 `promptSent:false`。普通 message、command、summarize 和后台 provider sync 不自动重启、reload 或 dispose 共享 runtime;只有显式用户 apply/lifecycle 操作可以推进运行时刷新。Main AI proxy 只把明确的上游分组饱和投影为当前 OpenCode 的终止状态,配额耗尽保持独立终止态,通用限速继续保留 `429`。上下文压缩以每个 Session 的持久时间线事件呈现:自动与手动压缩使用不同文案,运行态原位弱化显示,完成后静态保留并可从历史恢复;`session.compacted` 只完成对应事件,只有真实 idle 才结束 run 和释放排队消息。 +AI 编程已经硬切到精确 pin 的 Pi `0.84.2`,不存在 OpenCode fallback 或双 runtime。project、Agent 与 Conversation 采用本地 schema v2;旧 OpenCode 会话只做备份,不继续执行。每条 active/warm Conversation 拥有独立 persistent worker/session、generation/seq、Snapshot/Patch、model/thinking、队列、interaction 与错误状态,Composer 在 lazy prepare 期间仍可编辑。Renderer 只通过 `/api/coding/*` 和 Snapshot-first/`patch-batch` SSE 消费产品中立合同;gap/reconnect 只恢复目标 Conversation,accepted/uncertain mutation 不自动重放。未解析 Conversation 第一次选模先 validate 并持久化 resolved metadata,再 prepare;同账号模型切换使用 target `set_model`,跨账号只重建目标 worker。top-level/child 并发分别为 4,共享总进程预算 8,warm idle LRU 4;coding child 与 parent 共用项目 write lease。prompt/compact confirmation timeout 后仍保留 run/process/background ownership,迟到 success/failure/exit/abort 单调且 exactly-once 收敛,页面隐藏不会停止 active/uncertain run。Pi `0.84.2` 手动 compact 不发 `agent_settled`,由 correlated compact RPC 结果终结。Provider credential 只进入选中 worker env;确定性的 Works user-context 缺失会失效 gateway credential、fail fast、不重放并投影固定 Provider-auth 错误,不归类为 Pi worker crash。真实 Provider 验证仍为用户明确接受的未验证风险,macOS x64/arm64 与 native non-WSL Linux 也未通过平台发布门禁。 Updater 仍由 Electron Main 选择目标 feed、记录原始诊断并保持失败语义。正式稳定源缺少对应平台 manifest 时,设置页只显示一条简洁中文提示并允许重试,不把缺包误报为已是最新版,也不向普通界面暴露堆栈、URL、路径或错误码;签名产物发布和真实升级安装仍属于外部 Release Gate。 @@ -139,6 +161,7 @@ Robot 绑定设备默认先显示“引导配网 / 已有激活码”路径选 ## Recently Completed +- 2026-08-26: Fast-forwarded the complete Pi hard-cutover and installed-package resilience chain into local `main`, including per-Conversation workers/Snapshot projection, extension/subagents, bounded process/write/background ownership and the deterministic Works user-context Provider-auth correction. The final Windows installer, packaged Pi runtime, extension/child, 4+4 pressure, uncertainty/late-settle and zero-lingering-process proofs passed. Real Provider turns are explicitly waived rather than passed; macOS and native non-WSL Linux remain release evidence gaps. - 2026-08-20: Replaced AI Learning's course generation/player stack with the curated project catalog defined by ADR-005. The authenticated/module-gated client now renders project cards and safe README detail, loads validated credential-free HTTPS Markdown images directly, and saves verified ZIP archives through the native dialog. Old course/runtime/player packaging was removed; server/client regressions and full suites passed, while production deployment and real-account package smoke remain pending. - 2026-08-19: Integrated native password/SMS login, the temporary HTTP/2-disabled diagnostic bootstrap, and the AI Design freeze fix. Workspace JSON calls and shared token refresh now settle within 30 seconds, transport abort is paired with deterministic rejection, and implicit Electron-to-Node fallback no longer replays mutation requests. Installed-client Quote retry/confirm smoke and the final HTTP/2 policy decision remain pending. - 2026-08-17: Integrated application-side multi-Session isolation for AI Programming. Session A may remain busy while Session B is independently accepted or terminally rejected; errors, startup deadlines and uncertain-failure cleanup stay Session-scoped. Main now fail-closes stale Agent/provider runtime state before execution, applies bounded manager/project FIFO acceptance with revocable timeouts, and never refreshes the shared runtime automatically from ordinary execution paths. Full unit, typecheck, lint, build, focused Electron E2E and independent Sol review passed; a real paid-provider/bundled-runtime concurrency smoke remains pending. @@ -203,13 +226,13 @@ Robot 绑定设备默认先显示“引导配网 / 已有激活码”路径选 - AI 绘画 Main-owned Workspace JSON 请求和共享 Works token refresh 必须在 30 秒内结束并释放共同等待者;只允许 `GET`/`HEAD`/`OPTIONS` 在 Electron transport 失败后透明改走 Node fetch,PATCH/POST 等 mutation 必须由具有显式幂等身份的上层协议决定是否重试。临时 `disable-http2` 只用于安装包诊断,不能替代该有限生命周期与非重放边界。 - 服务端持久 Conversation Session 不由客户端在注销或退出时删除;Main 只关闭本地流、清除本机缓存并撤销本地凭据。 - 客户端对最终构建字节的 loopback 检查没有可信 receipt,且不复刻生产 opaque-origin;服务端必须独立重算 source/built/contract、校验不可变 Release,人工审核仍不可绕过。如未来需要不可绕过的 runtime gate,必须引入可信 verifier 并绑定精确构建产物。 -- AI proxy 的明确饱和 `429 → 400` 是与当前 OpenCode 重试契约匹配的内部投影;升级 OpenCode 或上游饱和错误文案变化时必须复核。不得把通用 `429` 或仅含 `rate_limit_exceeded` 的响应误分类为终止饱和。 -- AI 编程压缩事件必须按 Session、run token、runtime generation 与 native Part/event identity 关联;`session.compacted` 不等于 `session.idle`,历史 hydration 不得把 completed 降回 running,失败或中止必须清理本 run 的未完成事件。 -- AI 编程多 Session 隔离只证明应用提交、状态、错误、超时和队列互不污染,不证明 bundled OpenCode 或真实 provider 会并行执行两个模型 turn。任何产品或性能结论都必须保留上游可能串行、限流或拒绝的边界。 -- 项目 Agent 与 provider/runtime freshness 必须在 Main 的 bounded acceptance 临界区内验证。新增唯一 id 的 live discovery 只在 owned fresh generation 可接受;同 id 不能证明同内容已经加载,删除后重建及 attached/unknown runtime generation 必须 fail closed。普通 message、command、summarize、Session 模型切换和后台同步不得自动重启、reload 或 dispose 共享 runtime。升级 OpenCode 时必须复核 native Session model wire schema;不得把已注入 fresh generation 的 Main 临时 proxy token 误判为上游 provider credential rotation。 +- Pi RPC confirmation timeout 是不确定性边界,不是释放 run permit、process ownership 或 Main background lease 的依据;accepted/uncertain mutation 不得自动重放,迟到 terminal 必须 exactly-once 收敛。隐藏/显示、abort、recover、replacement 与 app quit 都必须保留可解释 reason 并最终清零 ownership。 +- Pi `0.84.2` 手动 compact 不发 `agent_settled`。只有 correlated compact RPC success 或权威 compaction failure 可以结束 compact;普通 prompt 仍需自己的 terminal/settled 语义,不能相互释放 lease。 +- 本地 provider-shaped loopback 和 4+4 process proof 证明客户端序列化、调度与隔离 seam,不证明真实 Provider 会并发、不会限流或正确隔离账号凭据。真实认证、endpoint/proxy/rate-limit、协议和图片差异仍为 `Explicitly Waived / Accepted Risk`,`realTurnVerified=false`。 +- Provider/resource freshness 属于目标 Conversation worker generation。idle stale 在下一 prompt 前重建,running stale 在 settled 后重建;同账号 refresh single-flight 且最多一次 reopen。确定性 Works user-context 缺失必须失效缓存凭据并 fail fast,不能触发无限 Pi 重试、自动 replay 或把 Provider 故障写成 runtime crash。 - AI 绘画单图选择器仍精确匹配 quick reply `从作品列表选择图片`,用途再从当前 Brief medium 推断;本地化或协议文案变化可能破坏入口。扩展更多素材输入前应升级为结构化 action/purpose。 - Updater 源码错误提示不能代替发布正式签名产物;稳定 feed 缺 manifest 必须保持失败,Renderer 不得展示原始堆栈、URL、路径或错误码。升级链只有在旧版本完成发现、下载、重启和安装 smoke 后才可视为生产就绪。 ## Last Updated -2026-08-21 +2026-08-26 diff --git a/.project-docs/30-worklog/tasks/20260826-recover-pi-updater-integration-8f3a6c21.md b/.project-docs/30-worklog/tasks/20260826-recover-pi-updater-integration-8f3a6c21.md new file mode 100644 index 0000000..0f84661 --- /dev/null +++ b/.project-docs/30-worklog/tasks/20260826-recover-pi-updater-integration-8f3a6c21.md @@ -0,0 +1,95 @@ +# Task: Recover Pi integration and merge updater fix + +## Identity + +- Task ID: 20260826-recover-pi-updater-integration-8f3a6c21 +- Mode: Integration +- Branch: main +- Worktree: D:\Datas\OthersProjects\makelore +- Base commit: 48a9189939f3f9a581a76e9d6f5ba2f49879a67c +- Owner: codex +- Status: Verification complete; documentation gate pending + +## Scope + +- Recover the abandoned Pi integration documentation already present in the + `main` worktree and validate it against the completed Pi source-task records. +- Integrate the updater downgrade-prevention change from source commit + `2e61800fed793b63b3b7804a03a4c7d8617e23d7` into `main`. +- Verify the focused updater behavior, type safety, lint, and production Vite + build before handing back the integrated branch. +- Preserve unrelated untracked task records byte-for-byte and outside this + task's commits. + +## Intent And Constraints + +- User explicitly authorized force-releasing and taking over the abandoned Pi + integration task. +- Treat completed source task records as read-only evidence; promote only the + canonical Pi documentation that is supported by those records. +- Keep unverified Pi release gaps explicit: real Provider execution, macOS + x64/arm64 packaged artifacts, and native non-WSL Linux remain unverified. +- Apply only the two updater source/test changes from the verified feature + commit so the feature task record remains source-owned. +- Do not modify, delete, or commit + `20260819-package-learning-off-115-9c4d.md` or the superseded Pi integration + task record without separate authority. + +## Plan + +1. Validate the pending Pi canonical documentation against source-task facts + and current product boundaries. +2. Apply the verified updater downgrade guard and its focused regression tests. +3. Run focused tests, typecheck, scoped lint, and `build:vite`; inspect the + final diff and commit the integrated result on `main`. +4. Run the Task Documentation Gate, release ownership, and report any remaining + foreign-record blocker without weakening or bypassing the gate. + +## Outcome + +- Recovered and semantically reviewed the pending Pi canonical documentation + against the completed hard-cutover, provider, release-proof, legacy-removal, + and background-run source tasks. +- Integrated the exact updater source/test change from + `2e61800fed793b63b3b7804a03a4c7d8617e23d7` into `main` without copying or + editing its source-owned task record. +- Automatic update channel selection and later channel changes now explicitly + restore `autoUpdater.allowDowngrade = false`, preventing installed `2.0.0` + from treating online `1.1.9` as an available update. +- Preserved both unrelated/unregistered task records byte-for-byte and outside + the staged integration set. + +## Verification + +- Exact source parity: + `git diff --exit-code 2e61800 -- electron/main/updater.ts tests/unit/app-updater.test.ts` + passed before task-record finalization. +- `pnpm exec vitest run tests/unit/app-updater.test.ts`: passed, 1 file / 15 tests. +- `pnpm run typecheck`: passed. +- `pnpm exec eslint electron/main/updater.ts tests/unit/app-updater.test.ts`: passed. +- `pnpm test`: passed, 181 regular files / 1543 tests passed / 2 skipped, + followed by the pressure file with 1/1 passed. +- `pnpm run build:vite`: passed for Renderer, Electron Main, Preload, and the + release utility worker; existing dynamic-import and large-chunk warnings + remain warnings only. +- `git diff --check`: passed. +- `check_doc_drift.py --task-id 20260826-recover-pi-updater-integration-8f3a6c21`: + blocked only by the two preserved foreign task records listed below. + +## Follow-ups + +- Human authorization is required to move the unregistered foreign records + `20260819-package-learning-off-115-9c4d.md` and + `20260826-integrate-pi-provider-fix-6e4c2a91.md` out of the `main` worktree + into a recovery directory. Until then the Task Documentation Gate cannot + pass and integration ownership cannot be cleanly released. +- Real Provider execution, packaged macOS x64/arm64 artifacts, and native + non-WSL Linux artifacts remain explicitly unverified Pi release gates. + +## Promotion Candidates + +- Applied the already-supported Pi hard-cutover facts to the canonical success + criteria, decision index/ADR, architecture, current state, business rules, + evidence index, and commitments. +- No additional updater promotion candidate: the updater behavior is captured + by focused tests and does not change an architectural boundary. diff --git a/.project-docs/40-domain/business-rules.md b/.project-docs/40-domain/business-rules.md index 1f5c472..b52d9ac 100644 --- a/.project-docs/40-domain/business-rules.md +++ b/.project-docs/40-domain/business-rules.md @@ -24,8 +24,14 @@ - 运营端可按用户关闭 Code、Canvas、Learning 或 Robot 客户端入口,默认全开。Makelore 通过 Main-owned `/api/auth/me` 只消费四布尔安全投影;缺失 `module_access` 或字段按开启处理,服务端 `design` 对应现有客户端 `painting`。 - 关闭的模块卡片必须置灰且无法点击;其根路由、深层路由和别名路由必须在 `MainLayout` 或模块初始化前阻断。Code provider 只能在 auth policy hydration 完成且 Code 已开启时初始化;`/settings` 是全局设置,不得随 Code 关闭而失去访问。 - 模块置灰/路由阻断不是 API 授权边界。每个 Works/模块服务端 API 仍必须独立执行身份与权限检查;`/api/auth/me` 返回终止性 `401` 时必须清理 Main 和 Renderer 会话,不得以默认全开继续。 -- AI 编程伙伴 `model` 只决定新 OpenCode Session 的默认模型;现有 Session 的 active model 由 OpenCode 持有。页面模型选择器与 `/models` / `/model` 必须调用同一个 native Session 切换动作,普通 prompt、项目命令和上下文压缩不得重发伙伴默认模型覆盖用户选择,也不得把该操作写成 provider 配置变更或 runtime restart。 -- owned fresh runtime generation 中,新创建且本 generation 从未见过的唯一 Agent id 可在 live registry 暴露后立即使用,并且不得阻断已有未修改伙伴。同 id 内容编辑、删除后同 id 重建、live id 缺失以及 attached/unknown generation 必须保持 pending,直到权威 reload 或 generation rollover。 +- Makelore Code 的唯一 production runtime 是精确 pin 的 Pi `0.84.2`。不得恢复 OpenCode fallback、RPC/SDK 双轨、兼容执行路径或 Renderer runtime 直连;产品公共合同必须保持 project/Agent/Conversation/Snapshot/Patch 中立,Pi wire 只属于 Main。 +- project、Agent、Conversation 使用 schema v2。Agent id、名称、原始 prompt、Skills 与 archive 状态必须稳定保存;旧 v1 在写入前做时间戳备份,旧 OpenCode 会话迁为本地空 Conversation 且不继续运行。只处理 exact generated legacy Agent 文件,modified/unknown 文件先进入备份,其他 `.opencode` 内容不得被迁移顺带删除。 +- 每条 active/warm Conversation 绑定一个 persistent Pi worker/session。首次本地 Conversation 创建和 Composer 编辑不得等待 worker;未解析 Conversation 第一次选模必须先验证模型、持久化 resolved metadata,再 prepare。已解析且同账号的模型变化复用 target `set_model`,跨账号变化等 active run settled 后只重建目标 worker。 +- prompt、steer、follow-up、compact 等 mutation 必须先获得目标 Conversation 的 `202` acceptance/dedupe 结果。confirmation timeout 只表示 uncertain,不得自动重发,也不得释放 run permit、process ownership 或 Main background lease;迟到 success/failure/exit/abort 必须单调、exactly-once 收敛,并且 sibling Conversation 继续独立工作。 +- Renderer 只消费 Snapshot-first 与 `patch-batch` SSE。每条 Conversation 的 generation/seq 独立;stale generation 丢弃,gap/reconnect 只恢复目标 Snapshot 并应用严格连续的缓冲 tail,不重放 mutation,也不改变乐观消息的 UI identity。 +- top-level worker 并发上限 4、warm idle LRU 上限 4、child 并发上限 4,parent/child 共用 FIFO process budget 8。单次 subagent dispatch 最多 8 个 child 且禁止递归;coding child 与 parent 共用同项目 write lease,父 abort/crash/generation 失效必须清理所有 child、permit 与 process lease。 +- Provider Account、credential、custom header 和 proxy token 只可投影到选中 worker env,不能进入 argv、catalog、Renderer 或未选中 worker。确定性的 Works user-context 缺失必须使缓存 gateway credential 过期、fail fast 且不重放 mutation,并向产品投影固定的 `CODING_PROVIDER_AUTH_REQUIRED`;不能把它解释为 Pi worker 崩溃。 +- Provider/resource revision 在 idle worker 下一 prompt 前应用;running worker 使用当前 run snapshot,settled 后重建。同账号 refresh single-flight 且最多一次 auth refresh/reopen。真实 Provider 认证、endpoint/proxy/rate-limit、协议差异、真实并发和跨 worker 凭据隔离由用户明确豁免并接受风险,`realTurnVerified=false`,不得写成 Pass。 - 客户端更新检查由 Electron Main 持有。缺少当前平台正式稳定 manifest 必须保持错误并提示稍后重试或从官网下载,不得宣称“已是最新版”;设置页只显示一条中文用户提示,原始堆栈、URL、路径和错误码只保留在 Main 日志。 - 一个 AI 绘画 Workspace 可以包含多条独立 Conversation;新建 Conversation 不得创建新 Workspace。 - Conversation 持有消息、Brief、Quote 和 `turnRevision`;生成任务和资产属于 Workspace,切换 Conversation 后必须继续可见。 @@ -47,7 +53,7 @@ - README 使用安全 Markdown/GFM 并禁用原始 HTML。运营发布时必须解析 Markdown,只保留无凭据、默认端口、无 fragment 且当前 DNS 全部指向公网地址的 HTTPS 图片 URL;不得下载、识别格式、转码或镜像 README 图片。客户端仅允许 Markdown 图片节点直连这些 URL,支持范围和可用性由 Electron 与远程 origin 决定;封面和历史发布媒体继续走受控媒体路径。 - Main 打开原生保存对话框,将项目 ZIP 下载流写入临时文件,只允许无 userinfo、最多五跳且保持 Works origin 的重定向,重定向请求不携带 Bearer;客户端下载不校验 `Content-Length`、`archiveBytes`、实际流字节数或大小上限,只在 SHA-256 与 ZIP 签名匹配后原子重命名。Works Square 可保留独立的运营上传/存储限制。Renderer 不得获得 Works Token、对象存储地址、任意下载 URL、临时路径或最终本地路径。 - 新客户端不读取、迁移或展示历史下载课程,但也不自动删除用户现有课程数据。任何清理功能必须另行设计为用户明确控制的可恢复维护动作。 -- `game-engine` 不再是内置 OpenCode Skill。`planning-with-files` 在复杂任务中把 `task_plan.md`、`findings.md`、`progress.md` 写到当前项目根目录;不得写入 Skill 安装目录或用户目录。 +- `game-engine` 不再是内置 Coding Skill。`planning-with-files` 在复杂任务中把 `task_plan.md`、`findings.md`、`progress.md` 写到当前项目根目录;不得写入 Skill 安装目录或用户目录。 - Robot V1 在现有 Binding 体验内扫描符合条件的开放 `Xiaozhi-*` 配网热点,并只连接用户明确选择的短效候选;该便利信号不得宣称为可信设备发现、自动下发家庭 Wi-Fi、自动认领或自动确认在线。 - Guided Hotspot Binding capability 由 Electron Main 持有且默认开启。精确 `NIANCODE_AI_HARDWARE_GUIDED_HOTSPOT_BINDING=0` 关闭引导;关闭或 capability 读取失败时保留现有六位码 Binding,Renderer 可以读取但不能覆盖它。 - Robot hotspot scan/connect 必须由 Electron Main 持有并在读取 Works 凭据前本地完成。Renderer 只能提交最近扫描生成的短效不透明 candidate ID,不能提交任意 SSID、BSSID、接口、profile 或命令。 @@ -73,7 +79,8 @@ - Prompt Museum 客户端契约和页面完成不等于 Works Square 内容后台、审核数据或 CDN 已部署;上线前需以真实登录账号验证列表、详情、署名链接、分页、图片和 Prompt 回填。 - Learning 客户端集成不等于 Works Square 运营后台、对象存储或项目 API 已部署;上线前需先部署服务端合同,再以真实账号验证发布/下架、分页、远程 HTTPS README 图片(含 SVG 与失败隔离)、保存取消、缺失或错误 `Content-Length` 的 ZIP、摘要/签名错误以及 Windows/签名 macOS 安装包。 - 四模块入口策略上线前需确认 Works `module_access` migration/API 已部署,安装包包含对应客户端,并用真实账号逐一关闭四个模块验证卡片、根/深层/别名路由和独立 API 授权。 +- Makelore Code 当前只有 Windows x64 最终安装包和 WSL2/WSLg Linux 证据。macOS x64/arm64 与 native non-WSL Linux desktop/compositor 尚未验收;在补齐前不得宣称 cross-platform release-ready。真实 Provider 风险虽经用户明确豁免,但仍不得从 loopback/provider-shaped smoke 推断为真实 Provider Pass。 ## Last Reviewed -2026-08-20 +2026-08-26 diff --git a/.project-docs/50-evidence/evidence-index.md b/.project-docs/50-evidence/evidence-index.md index 6f94a41..6826a98 100644 --- a/.project-docs/50-evidence/evidence-index.md +++ b/.project-docs/50-evidence/evidence-index.md @@ -4,6 +4,7 @@ Use this index for searchable, traceable evidence records. | Date | Topic | Status | Source | Detail | |---|---|---|---|---| +| 2026-08-26 | Makelore Code Pi hard cutover、后台运行 ownership 与 Works user-context Provider 修复 | 本地 `main` 已集成;Windows 最终安装包与 packaged proof Pass;真实 Provider/macOS/native Linux 非 Pass | 集成交付 `48a9189`、实现 `a098266`、任务 `20260826-fix-pi-model-provider-8d4c2a71` / `20260826-integrate-pi-provider-fix-6e4c2a91`、`docs/pi-runtime-release-runbook.md` | OpenCode production runtime/package/plugin/fallback 已删除,Pi `0.84.2` 是唯一 runtime。未解析 Conversation 现在 validate→persist resolved model→prepare;exact Works `works square AI gateway did not return one-api user context` 会失效缓存 credential、以非重试 Provider-auth failure 结束且不自动 replay,`agent_end willRetry:false` 不再被 `agent_settled` 覆盖成 completed。聚焦 47/47、related 54/54、projector 12/12、181 files / 1541 full unit + pressure、typecheck、lint、Vite build、Windows Electron 4/4、publish/runtime/artifact/Pi/subagent/final packaged proof 通过。NSIS 208,162,564 bytes,SHA-256 `A02F79FCD3273FB0B013450492DC788B6E5ED7BBC1007468FB83BE949B8CB30D`,未签名;final app.asar SHA-256 `A8394D0F7324B27D2C95469ABD4E2A39C739EABCC6D8B41E1D65BA4B41FB9D39`。真实 Provider 是 Explicitly Waived / Accepted Risk 且 `realTurnVerified=false`;macOS x64/arm64 与 native non-WSL Linux 未验收。 | | 2026-08-21 | AI 编程模型切换真实 OpenCode wire 与 fresh runtime proxy token 修复 | 客户端实现及自动化验证通过;新构建真实本地 smoke 待执行 | 源任务 `20260821-model-switch-runtime-fix-a83d6c91`、源提交 `cce7722`、集成任务 `20260821-integrate-model-switch-fix-8f2d6c41` | 本地 1.1.8 / OpenCode 1.18.9 日志暴露旧请求缺少 `model.id`,且每进程 Host API token 被误判为 provider credential rotation。Main client 现将内部 `modelID` 映射为 wire `id`;owned fresh runtime 构建时注入当前 proxy token,持久化该已生效值不再要求 restart,attached/unknown 与 timeout/partial persistence 仍 fail closed。4 files / 171 focused、177 files / 2074 full unit、typecheck、lint(0 errors / 6 existing warnings)、production build 与 Electron E2E 1/1 通过。 | | 2026-08-20 | AI 编程 Session 模型切换与新增伙伴 hot-add | 客户端实现及自动化验证通过;真实 provider 多 Session smoke 仍沿用既有待验收项 | 源任务 `20260820-session-model-agent-hotfix-6e4c9a2f`、源提交 `c0163bc`、集成任务 `20260820-integrate-session-model-hotfix-7b3e91c4` | 页面选择器与 `/models` / `/model` 通过 OpenCode native Session endpoint 切换模型,普通执行不再覆盖选择或要求 runtime restart;Agent readiness 改为逐 id,新增唯一 id 在 owned fresh generation 被 live registry 发现后可用,同 id 编辑与删后重建仍 fail closed。405 focused、176 files / 2068 full unit、typecheck、lint(0 errors / 6 existing warnings)、Vite build 与 Electron E2E 1/1 通过。 | | 2026-08-20 | Learning 项目下载移除大小校验 | 客户端实现与自动化验证通过;真实账号安装包 smoke 待验收 | 源任务 `20260820-remove-download-size-check-4f8a2c1d`、源提交 `8509084`、集成任务 `20260820-integrate-download-size-6e3a91c2` | 聚焦 Learning 2 files / 17 tests、176 files / 2061 full unit、typecheck、lint(0 errors / 6 existing warnings)及 Renderer/Main/Preload/utility production build 通过。覆盖缺失或错误 `Content-Length`、`archiveBytes` 超过旧 512 MiB 上限时继续下载;SHA-256、ZIP 签名、同源重定向、临时文件清理和原子落盘仍保留。未执行真实账号、安装包或磁盘空间压力 smoke。 | diff --git a/.project-docs/80-commitments/commitments.md b/.project-docs/80-commitments/commitments.md index 2f76454..f78b90b 100644 --- a/.project-docs/80-commitments/commitments.md +++ b/.project-docs/80-commitments/commitments.md @@ -4,20 +4,21 @@ Track future-facing memory: promised follow-ups, unfinished loops, timed checks, | Date | Commitment | Trigger / Due | Owner | Status | Next Action | |---|---|---|---|---|---| -| 2026-08-21 | 验收修复后的真实本地 OpenCode Session 模型切换 | 构建并启动包含源 `cce7722` 的 Makelore 后 | 客户端/OpenCode 集成 | Pending | 重启桌面应用以加载新 Main 代码,在无需手动重启 runtime 的前提下分别从页面选择器和 `/model` 切换模型并发送后续 prompt;核对运行日志不再出现 `Missing key at ["model"]["id"]` 或因当前进程 Host API token 持久化产生的 false runtime-refresh pending。attached/unknown runtime 的真实 token mismatch 仍应 fail closed。 | +| 2026-08-21 | 验收修复后的真实本地 OpenCode Session 模型切换 | OpenCode runtime 尚存时 | 客户端/OpenCode 集成 | Superseded / Closed | Pi hard cutover 已删除该 runtime 与 native Session model wire;不得再执行或恢复此验收。当前 Conversation 模型规则由 ADR-006、schema v2 与 target Pi worker contract 约束。 | | 2026-08-19 | 验收 AI Design 有限请求生命周期并决定正式 HTTP/2 策略 | 发布包含源 `87e4140` 的正式安装包前 | 客户端/Works 服务端/发布集成 | Pending | 使用真实账号连续执行 Quote PATCH、重试报价与继续调整,制造或等待慢响应,确认 30 秒内稳定返回、其他请求不被共同挂住、服务端无隐式重复 mutation;随后恢复 HTTP/2 重新打包做同场景对照。只有证据确认后才能移除临时 `disable-http2` 或把协议策略固化为正式决定。 | -| 2026-08-17 | 验收 AI 编程真实双 Session runtime/provider 行为与同 id Agent 内容刷新边界 | 升级 bundled OpenCode、改变 provider 并发策略,或对外宣称双会话真实并发/即时应用同 id Agent 编辑前 | 客户端/OpenCode/provider 集成 | Pending | 使用明确配置的测试 provider 运行 opt-in bundled OpenCode 双 Session smoke,区分应用隔离与上游并发/串行/限流。owned fresh generation 已允许 live registry 新发现的唯一 id hot-add;若要求运行期即时应用同 id 编辑或删除后重建,仍须先提供 directory-scoped authoritative invalidation 或 whole-instance quiescence oracle,不从 `/session/status` 推断 dispose/reload 安全。 | +| 2026-08-17 | 验收 AI 编程真实 Provider 双 Conversation 行为 | 未来撤销用户豁免,或对外宣称真实 Provider 并发/凭据隔离已经验证前 | 客户端/Pi/provider 集成 | Explicitly Waived / Accepted Risk | 当前 `realTurnVerified=false`。若重新开启验收,必须覆盖真实认证、endpoint/proxy/rate-limit、四协议响应与图片差异、真实并发或串行、abort/event/session/model/credential 跨 worker 隔离;provider-shaped loopback 与 4+4 本地 PID 证据不能替代该验证。 | | 2026-08-17 | 验收用户级四模块入口策略的真实发布链 | 发布包含源 tip `3b799af` 的 Makelore 安装包前 | Works 服务端/客户端/发布集成 | Pending | 部署 `module_access` migration 和 `/api/auth/me` API,重新打包并安装 Makelore;使用真实账号依次关闭 Code、Canvas、Learning、Robot,重启后验证卡片置灰/不可点、根/深层/别名路由阻断、`/settings` 仍可访问和 terminal `401` 退出;另行以直接 API 请求确认服务端授权,不以 UI 置灰代替 | | 2026-08-20 | 部署并验收 AI Learning 项目目录完整链路 | 发布包含源 `38db158`、`9956739`、`8509084` 的 Makelore 安装包前 | Works 运营后台/服务端/客户端/发布集成 | Pending | 实现并部署项目管理、对象存储、发布代际、README HTTPS URL 校验、list/detail/media/archive API;以真实账号验证发布/下架、排序分页、README/GFM/远程图片(含 SVG、失效 origin 和请求隐私说明)、原生保存取消、同源重定向、缺失或错误 `Content-Length` 时仍可下载、SHA-256/ZIP 签名失败和 Windows/签名 macOS 安装包。客户端下载不再验证 `archiveBytes`、实际字节数或大小上限;不得以客户端测试宣称运营后台或生产数据已上线 | | 2026-08-17 | 验收首次项目封面并补齐已有资料条件写 | 发布包含源 `145a6ce` 的 Makelore 前,或启用 draft/published metadata 编辑前 | Works 服务端/客户端发布集成 | Partial / Pending | 首次封面已由服务端源 `407c883` 与客户端源 `145a6ce` 通过单请求绑定和失败补偿落地;仍需先部署服务端、重新打包客户端,并以真实对象存储/账号验证 404→create 409、cover 失败和版本阻断。已有资料编辑继续等待 metadata revision/ETag 与 draft-only 条件写;在此之前保持 existing version-only。 | | 2026-08-16 | 验收 Prompt Museum 与 Canvas 删除/重报价的真实服务端链路 | 发布包含 `26b52d7` Canvas 行为的安装包前 | 客户端/服务端集成 | Pending | 使用真实 Works 账号核对 Museum 列表/详情/分页/筛选/署名/CDN/Prompt 回填,并确认固定相对媒体路径可经 Main Bearer 代理、401 刷新、10 MiB/可信 raster MIME 边界后在 Renderer 展示;核对最终 Prompt/options 重报价和确认设计点;删除 Workspace 后确认软删除可见性、未提交任务取消/预留积分释放、已运行任务结算。保留 Main 错误脱敏和严格 DTO/HTTPS 投影,不以客户端回归替代服务端验收 | | 2026-08-16 | 验收 default-on Robot Guided Hotspot Binding 的 Windows/macOS 真实设备链路 | 下一份包含页面内热点连接行为的安装包发布前 | 客户端/硬件/服务端集成 | Pending | 核对精确出货固件与固定 Portal、六位码发行/消费语义;执行 Windows Robot 真机扫描/连接、签名 macOS x64/arm64 CoreLocation/CoreWLAN/worker/ASAR/Koffi smoke,以及真实 Host/native Electron 端到端配网+Binding;保留 `NIANCODE_AI_HARDWARE_GUIDED_HOTSPOT_BINDING=0` 回滚并记录支持矩阵 | | 2026-08-10 | 完成客户端提交到 App `play_url` 播放的真实生产整链验收 | source+built+contract 服务端协议、OSS immutable Release、CDN/Edge 与 App 消费链成组集成后 | 客户端/服务端集成 | Pending | 使用真实账号执行小游戏和小程序创建、客户端本地构建与同字节预检、双归档提交、服务端逐字节校验/不可变 Release 固化、运营批准、CDN 发布、App 播放与监控核对;如需不可绕过 runtime gate,另行设计可信 verifier | -| 2026-08-12 | Windows 发布流水线保留固定 npm 运行时产物门禁 | 每次生成正式 Windows 安装器时 | 客户端发布 | Pending | 运行 `pnpm verify:publish-runtime` 和 `pnpm verify:artifact:win`;当前 223,547,912-byte / SHA-256 `08A0BB7BC66EE4AD8B120E8955B149951CD86AF53CC966C7973AD4D77A5815C1` 安装器仅为本地证据,尚未发布 | +| 2026-08-12 | Windows 发布流水线保留固定 npm/Pi 运行时产物门禁 | 每次生成正式 Windows 安装器时 | 客户端发布 | Pending | 运行 `pnpm verify:publish-runtime`、`pnpm verify:artifact:win`、`pnpm verify:artifact:pi` 与 final packaged proof。当前 208,162,564-byte / SHA-256 `A02F79FCD3273FB0B013450492DC788B6E5ED7BBC1007468FB83BE949B8CB30D` 安装器为本地未签名证据,尚未 push、发布或安装。 | | 2026-08-10 | 删除客户端 `runtime_url` 兼容回退 | 一个客户端兼容版本结束,且服务端与存量数据稳定提供 `play_url` | 客户端 | Pending | 删除类型字段、读取分支和对应回归测试 | | 2026-08-11 | 部署并验收 AI 绘画多 Conversation 服务端契约 | 发布包含多会话客户端之前 | 客户端/服务端集成 | Pending | 确认迁移 `0033`、Conversation list/create/read/turn/confirm API、持久 Agent Session 与事件字段已上线 | -| 2026-08-12 | 复核 AI 编程上游饱和终止态投影 | 升级 bundled OpenCode 或上游饱和错误格式时 | 客户端/OpenCode 集成 | Pending | 重新验证 retry contract;继续区分配额耗尽、明确分组饱和与通用 `429`,必要时调整或移除 `429 → 400` 内部投影 | -| 2026-08-15 | 复核 AI 编程上下文压缩原生事件契约 | 升级 bundled OpenCode 或模型上下文配置时 | 客户端/OpenCode 集成 | Pending | 运行真实长上下文 smoke,复核 native compaction Part 的 `auto`、Part identity、完成时序和 `session.compacted`/idle 顺序;继续保持 completed 单调和 queued prompt 只由真实 idle 释放 | +| 2026-08-12 | 复核 AI 编程 Provider 错误与重试投影 | 升级 Pi、Works gateway 或上游 Provider 错误格式时 | 客户端/Pi/provider 集成 | Pending | 重新验证确定性 Works user-context 缺失仍会 expire credential、非重试 fail fast、固定脱敏为 `CODING_PROVIDER_AUTH_REQUIRED`,并且不会被后续 `agent_settled` 改写为 completed;不得把其他 401/429/5xx 扩大归类。 | +| 2026-08-15 | 复核 AI 编程 Pi 上下文压缩事件契约 | 升级锁定的 Pi `0.84.2` 或模型上下文配置时 | 客户端/Pi 集成 | Pending | 运行长上下文与 manual compact smoke,确认新版本是否仍缺 `agent_settled`;保持 correlated compact RPC terminal、Snapshot completed 单调、普通 prompt 独立 settled、late result exactly-once 和 lease 清零。 | +| 2026-08-26 | 补齐 Makelore Code 跨平台最终产物验收 | 对外宣称 cross-platform release-ready 或发布对应平台安装包前 | 客户端发布 | Pending | 在 macOS x64、macOS arm64 与 native non-WSL Linux desktop/compositor 上执行最终 artifact、Pi closure、extension/subagent、4+4、SSE/UI/performance、abort/uncertainty/cleanup 验收。Windows Pass 与 WSL2/WSLg 证据不得替代这些平台结果;失败时回到对应实现修复,不恢复 OpenCode fallback。 | | 2026-08-13 | 将 AI 绘画素材选择 quick reply 升级为结构化 action/purpose | 新增第三种素材输入用途、调整本地化文案或重构 Design Message 协议时 | 客户端/服务端集成 | Pending | 用 `select_image_asset` + `image_reference | video_first_frame` 等稳定字段替代精确中文字符串匹配,并覆盖延迟 Brief 快照兼容 | | 2026-08-13 | 完成图生图客户端与服务端生产整链 smoke | 相匹配客户端与服务端部署后 | 客户端/服务端集成 | Pending | 用当前 Workspace 已完成作品与本地上传各执行一次图生图,核对 Asset 所有权、Quote、任务创建、结果展示与下载;不以本地单测替代付费 Provider 验收 | | 2026-08-13 | 发布并验收 Makelore 稳定更新链 | 权威版本对齐且 Windows/macOS 正式产物已签名/公证后 | 客户端发布 | Pending | 将 updater-role 产物 inactive-first 发布到 Works Square,核对 manifest/签名/架构后激活,并从旧稳定版执行发现、下载、重启、安装与版本确认 smoke;源码错误文案不替代该验收 | diff --git a/electron/main/updater.ts b/electron/main/updater.ts index 78a2f87..f68d2d3 100644 --- a/electron/main/updater.ts +++ b/electron/main/updater.ts @@ -115,7 +115,9 @@ export class AppUpdater extends EventEmitter { // Set channel so electron-updater requests the correct yml filename. // e.g. channel "alpha" → requests alpha-mac.yml, channel "latest" → requests latest-mac.yml + // The channel setter also enables downgrades, which automatic updates must never allow. autoUpdater.channel = feed.channel; + autoUpdater.allowDowngrade = false; if (feed.url) { autoUpdater.setFeedURL({ @@ -334,6 +336,7 @@ export class AppUpdater extends EventEmitter { */ setChannel(channel: 'stable' | 'beta' | 'dev'): void { autoUpdater.channel = channel; + autoUpdater.allowDowngrade = false; } /** diff --git a/tests/unit/app-updater.test.ts b/tests/unit/app-updater.test.ts index 36ba17d..f5960e8 100644 --- a/tests/unit/app-updater.test.ts +++ b/tests/unit/app-updater.test.ts @@ -11,11 +11,19 @@ const loggerMocks = vi.hoisted(() => ({ const updaterMocks = vi.hoisted(() => { const listeners = new Map void>>(); + let channel = 'latest'; const autoUpdater = { autoDownload: false, autoInstallOnAppQuit: true, + allowDowngrade: false, logger: null as unknown, - channel: 'latest', + get channel() { + return channel; + }, + set channel(value: string) { + channel = value; + autoUpdater.allowDowngrade = true; + }, on: vi.fn((event: string, listener: (...args: unknown[]) => void) => { const current = listeners.get(event) ?? []; current.push(listener); @@ -95,6 +103,7 @@ describe('AppUpdater feed delegation', () => { vi.clearAllMocks(); electronMocks.getVersion.mockReturnValue('0.9.1'); updaterMocks.autoUpdater.channel = 'latest'; + updaterMocks.autoUpdater.allowDowngrade = false; updaterMocks.autoUpdater.checkForUpdates.mockResolvedValue(null); }); @@ -131,6 +140,28 @@ describe('AppUpdater feed delegation', () => { } }); + it('ignores an older stable feed version even when electron-updater reports it as available', async () => { + electronMocks.getVersion.mockReturnValue('2.0.0'); + updaterMocks.autoUpdater.checkForUpdates.mockImplementation(async () => ({ + isUpdateAvailable: updaterMocks.autoUpdater.allowDowngrade, + updateInfo: { version: '1.1.9' }, + })); + const updater = new AppUpdater(); + + await updater.checkForUpdates(); + + expect(updaterMocks.autoUpdater.allowDowngrade).toBe(false); + expect(updater.getStatus()).toMatchObject({ status: 'not-available' }); + }); + + it('keeps downgrade disabled when changing update channels', () => { + const updater = new AppUpdater(); + + updater.setChannel('beta'); + + expect(updaterMocks.autoUpdater.allowDowngrade).toBe(false); + }); + it('reports a concise actionable error when the stable feed has no latest manifest', async () => { const rawError = new Error( 'Cannot find channel "latest.yml" update info: HttpError: 404\n' +