归档说明(2026-10-01):本文保留归档时的完整正文和历史验收要求,不再作为现行状态或执行入口。后续工作统一见架构调整收尾工作计划。正文中的缺口、完成状态与下一步仅代表当时记录;运行证据保留原位置。
架构评估(2026-09-30)
现有模块边界继续成立,当前应优先修正升级兼容、普通 Run 收敛和版本指纹传播,再完成容量与恢复验收。 平台已经有真实截图、构建、多副本执行和故障恢复记录,可以支持继续开展受控的内部部署试用;现有证据还不足以支持任意混版、零中断升级、完整多租户隔离或固定容量承诺。
本轮基线为 9ebc414af6c7a2983072c21dd86b59daabcdc791,分支 dev,审查开始时工作区干净。本报告接替 09-29 基线评估,继续沿用 AR/W/M 编号与现行工作计划,避免另起一套任务清单。
1. 本轮证据范围
本轮读取当前代码、部署模板、CI、Git 历史和已入库的 09-30 实机记录,运行针对性本地检查,并在独立内存数据库复现一个旧 ORM 与新表结构的兼容错误。没有连接测试机、重启业务服务、修改业务数据库、构建或发布镜像,也没有重新执行历史实机演练。
| 证据 | 本次如何使用 |
|---|---|
| 当前代码与本地检查 | 支持实现状态、调用链与本轮检查结果 |
| 已入库的实机 JSON、PNG | 支持文件实际记录的场景、样本与产物 |
| 实机摘要中的后续复测描述 | 保留进展;缺对应原始复测文件时,不升级为可重复验收 |
a06120e + 修复 的测试镜像 | 属于特定候选及补丁组合,不能自动代表当前 HEAD 的最终发布镜像 |
| 新的缺列复现 | 使用实际 N-1 ORM 与当前模型列集合的内存表,证明该读取不兼容;不是生产故障或真实 PG 迁移复测 |
本轮不新增 P0。下文 P1 表示对应部署或交付承诺前必须处理的问题,P2 表示持续维护成本。
2. 已完成工作如何改变判断
| 能力 | 相比 09-29 的进展 | 当前结论 |
|---|---|---|
| Backend / Runtime / Renderer 边界 | 独立 Renderer 的真实控制 API、PNG 产出和项目 ZIP 已有记录;跨容器导航、资源基址、allowedHosts、pending epoch 已修 | 保留现有进程与职责划分 |
| API 与 previewSchema 契约 | Editor 核心实体实际引用生成类型;CI 隔离重生成;Backend 生产解析消费 JSON Schema | AR-02 的核心实现缺口已补;完整 UI/权限/跨仓行为仍待 M08 |
| 队列观测 | v2 区分领域 Job、外部投影、Batch 续跑与 RenderAttempt;真实渲染状态参与统计 | AR-03 的漏报实现已修;延迟分解与容量验收仍开放 |
| 构建凭证隔离 | 实机摘要记录 root 领取器、UID 10001 子进程、0400 凭证 EACCES,以及降权后真实构建成功 | 全局凭证读取边界有局部实测;完整 M02 尚未完成 |
| 多副本基础执行 | 两项目构建并行成功有 JSON;摘要记录截图、摘流及一次重启后 129.2 秒终态 | 支持基础运行可行性;完整竞争、围栏和 AI 恢复门仍开放 |
| 版本拒绝 | 指纹失配的业务错误已保留 409;迁移与中断恢复有摘要 | 入口拒绝成立;N-1 业务兼容与浏览器子请求保护须重开 |
| Lite 容量 | 已有 2C/3.8 GiB、4 GiB swap 下的有限混合负载和并发阶梯记录 | 不再说“未采集”;仍属于探索性样本 |
| Editor 拆分 | AssetsView 筛选、批量与详情已分离,主体约 1009 行 | W07 的这一轮拆分计入完成,剩余按变更风险推进 |
| 备份恢复 | PG 备份/恢复脚本、清单和操作顺序已具备 | 不再说“没有入口”;M07 业务恢复演练仍缺 |
本轮 test:contracts:generated 重导出 249 条路径、331 个 Schema,提交生成物一致。不能把生成检查扩大成所有业务调用都已端到端验收。
3. 需要优先处理的架构问题
AR-05a 升级删除列破坏已测试 N-1 的页面写入路径
P1,代码确认并在内存库复现。 20260930_0100 删除 ai_page_mutation_batches.lease_generation,但 M05 选择的 N-1 4c7eee8 仍在 AiPageMutationBatch ORM 中映射该列。旧版 page_mutation_enqueue.py 使用 select(AiPageMutationBatch) 查找 pending Batch,读取会选择包括该列在内的全部 ORM 字段。
本轮从 Git 读取实际旧模型,使用独立 DeclarativeBase 编译查询,在按当前模型列集合创建的空内存表执行,得到:
no such column: ai_page_mutation_batches.lease_generation即使没有历史 Batch 行,查询也会因缺列失败。M05 中两版登录均 200 的记录可以保留,但不能推出“旧应用在新 schema 上业务可运行”。这里既有新增列,也有删除列;“多余列可忽略”没有覆盖这次变化。
建议: 当前组合先按停机升级约束操作,禁止 N-1 页面写入流量在删列后继续运行。若要支持滚动和仅回退应用镜像,应跨发布保留旧列:先发布停止使用旧列的代码,确认旧实例退出和回滚窗口结束,再执行删除迁移。是否通过补偿迁移恢复列,应在实施时结合该 revision 的发布状态决定。
验收必须覆盖 N-1 在 N schema 上的页面任务入队、Batch 读取/写入、取消及外部续跑,不能只检查登录。依据:删列迁移、当前入队路径、M05 记录。旧代码可用 git show 4c7eee8:backend/app/models/ai_page_mutation.py 与相同 ref 下的入队文件核对。
AR-05b 普通 Run 的跨容器遗留收敛没有生产闭环
P1,静态确认。 启动恢复按 hostname/PID 过滤,保护其他实例,这一方向正确。但容器重建改变 hostname 后,旧 Run 被跳过;recover_stale_active_run() 在生产源码中只有定义和说明文字,没有实际调用,调用只出现在集成测试。SSE 已明确忽略 idle timeout,保持纯观察;get_active_run() 也只是读取,ensure_no_active_run() 会拒绝有非终态 Run 的新请求。
因此,“旧 hostname Run 会由空闲超时自动终态化”目前没有实现依据。异常停止留下的 Run 可能持续占据会话,最终依赖用户取消。正常退出管理器能取消本进程任务,不覆盖进程被强杀的情况。
建议: 增加独立于 SSE 的 owner 存活登记与过期收敛路径,以持久化心跳、实例身份和 CAS 决定谁可以写终态;继续维持普通 Run 不自动续跑。不要直接按“多久没有模型事件”全局失败化 Run,长工具执行和慢模型可能仍然存活。若暂不实现后台收敛,应明确只提供手动解除,撤回自动有界恢复描述。
验收场景是强杀 A、重建为新 hostname/PID、保持 B 的长任务活跃,并确认旧 Run 在约定时间内终态且会话可重新发起。依据:启动恢复、空闲方法与活跃检查、SSE 观察链路、会话门面。
AR-05c 版本指纹没有覆盖正常浏览器预览的完整链路
P1,静态确认。 当前检查只在 __preview HTML 入口执行;无 x-expected-runtime-version-fingerprint 请求头时放行。全仓搜索未找到 Backend、Gateway 或 Editor 自动设置该期望值的生产代码,后续虚拟模块分支也没有调用该检查。Backend HTML 代理响应仅保留 Content-Type,不传出 Runtime 的指纹响应头。
还有两个配置问题:镜像没有固定注入发布构建标识,未配置时各版本可能都成为 1.0.0+dev;分角色模板把 RUNTIME_BUILD_ID 写成 runtime-preview-1/2,把发布身份和副本身份混在一起,同版副本反而不同指纹。
M05-F 已证明“人工携带失配头的入口请求返回 409”,没有证明正常 iframe 导航与模块请求可以阻止混版。页面源码公开路径 .vN 的兼容约束继续有效,但它不等价于 Runtime 编译器与壳层版本相同。
建议: 发布时注入同一源码/镜像构建标识,副本 ID 单独保存;由服务端创建预览时绑定版本,并把版本约束传播到 HTML、模块、CSS 和其他相关子请求,或采用明确的版本路由与整池切换。在此之前,同池只放同一发布版本,升级按排空/停机处理。
验收应真实打开浏览器,让 HTML 与至少一个模块落到不同版本,确认得到明确拒绝或稳定路由。依据:指纹检查、入口与模块分支、Backend 代理、模板、Runtime 镜像。
AR-04 与 AR-08 验收完成标记超过了入库证据范围
P1 针对交付验收,P2 针对文档维护。 本轮认可实机进展,但按原计划逐项复核后,M01/M04/M05 应保留部分完成状态。
| 门 | 已有证据 | 本轮仍开放的范围 |
|---|---|---|
| M01 | 可解码 PNG、ZIP 校验和与入口;F5–F9 修复描述 | 最终候选固定 digest 的官方探针、构建入口实际加载、Worker 缺失/空凭证负例、取消后浏览器与槽位回收 |
| M02 | 摘要中的 secret EACCES 与降权构建 | 跨任务文件/身份/网络、token 越权、取消/超时进程回收的完整矩阵 |
| M03 | 混合与阶梯负载 JSON、资源/队列采样 | 正式目标、独立预览与编辑样本、压力下公平性、M03-F1 原因、双库对照 |
| M04 | 两项目并行构建 JSON;摘要中的恢复与摘流复测 | B 活跃 Run 保护、hostname/PID 场景、过期 attempt 迟到上传、双协调器竞争与额度、页面/图片/组件/Batch 恰好一次消费 |
| M05 | 特定 N/N-1 组合的摘要、迁移中断与 409 描述 | AR-05a 旧 ORM 缺列、AR-05c 浏览器子请求、完整业务升级/回滚复测 |
| M06 | 发布 workflow 定义 | Registry/模板 digest 与拉取权限、arm64 实际执行、外部 Gateway 契约及远端 required checks |
| M07 | 脚本与恢复清单 | DB、资源、密钥、版本一致的完整业务恢复和实际 RPO/RTO |
| M08 | 本地代码与部分既有测试记录 | UI、权限与匹配 Agent Kit 版本的联合回归 |
尤其是 M04 初次场景 JSON 仍记录重启超时、CLI 缺 uv、取消路径 404;S1 JSON 的四次截图返回同一个 job ID,构建为 partial。后续摘要描述的修复不应被否定,但缺少对应复测 JSON。相同 job 被重复查询成功不能证明四个独立 attempt,更不能证明竞争认领没有重复。
建议: 以“场景—输入—唯一 job/attempt/batch—执行者—终态—产物”逐项记录。补入最终复测文件、脚本、Compose 差异与镜像 digest。原摘要的“门关闭”作为历史记录保留,本轮按完整完成条件重新开放剩余范围。
4. 容量结果与下一步执行策略
混合负载 记录 5 用户、并发 3、82 次操作、成功率 100%、合并 P95 97.96 秒、无 OOM、SQLite 写冲突与 busy timeout 均为 0。这证明该有限负载完成,不能推出 5–10 人持续并发的体验保证;机器使用 swap,且没有图片生成或独立交互预览的完整样本。
阶梯原始报告 更明确地揭示排队延迟:
| 负载工人数 | 截图样本 | 截图 P50 | 截图 P95 | 页更样本 | 构建样本 |
|---|---|---|---|---|---|
| 4 | 16 | 18.57 秒 | 20.23 秒 | 8 | 4 |
| 8 | 24 | 40.71 秒 | 43.82 秒 | 2 | 1 |
| 12 | 33 | 63.96 秒 | 68.23 秒 | 0 | 0 |
| 16 | 35 | 86.95 秒 | 98.06 秒 | 0 | 0 |
推断: 单槽预算与有限 CPU 是首先值得对照验证的因素。当前没有支持把 SQLite 写锁作为首要瓶颈的证据,也没有支持“加 Redis 或换数据库即可提速”的证据。
原摘要把高档位页更 0 次解释为编辑饥饿,还把截图数除以计划时长推导成固定服务时间。这些推论超过了原始数据:没有入队、开始、完成时间与独立请求到达记录,无法区分测试生成器被慢请求占住、阶段末尾在途任务与真实调度饥饿。12/16 档实际只完成截图,也不再代表同样的混合业务比例。单槽是 Lite 已设定的约束,不能据此标为 P0 故障;“预览并发 3”与“截图执行槽 1”也属于不同容量单位。
下一轮建议先补 queue_wait_ms、execution_ms、端到端时间、拒绝/超时、到达/完成数量和停止负载后的排空时间。独立发送预览、页更、截图和构建请求,分别统计;避免把成功结束的样本当全部到达请求。2C 上保持默认单槽,4C 上对比角色拆分与第二个单槽 Renderer,再决定是否提高全局预算。Renderer 继续保持单槽契约,多槽不是本轮推荐实现。
M03-F1 仍为待定位问题。当前内存后端会检查 String 的 key+value、Hash 的单 field+value 和总量,不能仅凭“总量 6.9 MB 小于 128 MiB”排除单项超限。但 drill 的 small/medium/large 循环实际反复创建同一整项目预览,既不是三种独立大小,也不是重复刷新同一个 artifact。先取得拒绝操作、实际 UTF-8 大小、命中的预算及清扫状态,再决定修实现还是修测试;不要先提高上限。依据:内存预算、运行态错误映射、drill。
5. 应保留的边界与剩余维护成本
Backend 保持模块化控制面;Runtime 保持编译/预览角色;Renderer 保持每 attempt 浏览器执行;render-contracts 保持纯协议。共享 CAS、租约和写围栏继续承载可靠性,领域 Job、模型 step 的 Batch 与资源占用 Attempt 保持各自职责。当前进展没有给出引入微服务拆库或通用工作流平台的必要性。
W01 的 UID 与 0400 已保护全局 Worker 凭证,但所有执行子进程仍共用 UID/GID 10001,任务目录以同组可写权限创建。这不是任务之间的文件隔离证明;权限说明中的“只能访问自己的任务”需要更细的实测与实现边界。本轮没有证明用户 SFC 可执行任意 Node 代码,也没有进行逃逸实验。M02 应据既有威胁模型完成负例,不自动扩大成多租户隔离承诺。
大模块仍有维护成本,优先随高风险修改拆分,而不是再做全仓重排。当前物理行数含空行/注释、排除生成物:runtime-build-runner.ts 1684、runtime-build-worker.ts 1593、runtime-saas-preview.ts 1533、asset_service.py 1511、project_template_package_service.py 1861、platform_runtime.py 1236。下一次优先按凭证/身份、进程生命周期、工作区物化、产物上传拆 Runtime;包服务按解析、边界校验、导入事务与导出分责。分层白名单继续缩小,不以机械行数达标作为验收。
6. 决策与承诺边界
| 事项 | 本轮建议 |
|---|---|
| 当前交付形态 | 按已验证方向保留 platform-lite + 独立 Renderer;NAS 单镜像仍是历史产品需求与待实现候选,不能用双容器证据背书 |
| 双库 | SQLite Lite 继续是一等公民;当前压力未触发写锁问题,沿用现有方言预算 |
| 普通 AI Run | 保持中断不自动续跑;补终态收敛与会话解除,不能把遗留 running 当“允许丢” |
| 多副本 | 基础执行已有局部实测;完整 M04、M02/M07 等相应门通过前,不升级完整高可用/隔离承诺 |
| 升级与回滚 | 本次 N/N-1 页面业务兼容不成立;先按停机与有序 schema 恢复,滚动须满足跨发布保留列和完整业务复测 |
| 预览版本 | 同池固定同一发布版本,排空切换;人工头 409 不等于自动混版保护 |
| Lite 容量 | 把人员数、交互预览、截图槽、构建槽分开表述;保持探索性基线,不倒填 SLA |
| 系统恢复 | 有脚本,未有完整演练;先完成 M07 的资源、密钥、凭据解密、预览/截图/构建验证 |
实施顺序建议:W05 修正迁移兼容与 Run 收敛 → W05 补版本传播及 M04/M05 定向复测 → W01/M02 与 W06/M07 → W03/M03 容量对照 → W04/M06、W02/W07/M08 交付回归。 M07、M06 的准备可独立推进;这些前后关系不要求重做已通过的所有测试。
7. 本轮验证与交付
| 检查 | 本轮结果 |
|---|---|
pnpm run test:contracts:generated | 通过,249 paths / 331 schemas 与生成物一致 |
| Backend DB 边界、分层、Run owner、队列指标、内存语义五组单测 | 52 通过;使用 uv run --no-sync --project backend pytest |
| Runtime 指纹、build worker、runner infrastructure 三组测试 | 49 通过、3 跳过;Windows 不替代 POSIX 权限验收 |
| 实际 N-1 ORM 对当前列集合的内存库查询 | 复现缺列;不访问业务 DB |
pnpm run test:repository | 13 通过,覆盖文档链接、围栏、环境与部署约束 |
| 本轮未执行 | 真实 PG/Redis、E2E、容器重启、镜像构建/发布、远端门禁、M02/M06/M07/M08 和完整 M04/M05 复测 |
本轮交付评估、现行计划状态与导航,以及版本矩阵中被证据推翻的表述修正;业务实现没有随评估改动。后续验收继续在同一计划维护,历史实机结果保留原文并明确适用范围。