docs(ops): close audit batch 4 report and park medium leftovers

Critical/high fixes are already on dev. Record the frontend test report, restore the audit note, and move remaining medium/low items into long-term so they are not treated as release blockers.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
weichen
2026-09-19 07:55:43 +00:00
co-authored by Cursor
parent d73bc79800
commit d9cd7ebb82
6 changed files with 294 additions and 3 deletions
+130
View File
@@ -0,0 +1,130 @@
---
title: "系统深度体检审计(2026-09-19,dev@e8ed33b)"
status: done
created: 2026-09-19
updated: 2026-09-19
severity: high
---
# 系统深度体检审计(2026-09-19)
> 只读审计,未修改业务代码。分支 `docs/system-deep-audit-2026-09-19`,基点 dev@e8ed33b。
> 方法:go vet / go test -short 静态基线 + 五路并行深潜(后端 server/chat、docgov/events/工作流/迁移、前端 web、配置 flag/死代码、安全鉴权)。
## 一、总体结论
- **静态基线健康**:`go vet ./...` 零告警;`go test -short ./...` 55 包全 PASS;全仓 TODO/FIXME 仅 2 处非实质。
- **四条已声明红线全部核实一致**:`MULTI_INTENT_PLAN_TIER=legacy`、`SEMANTIC_SUMMARY_REPLACE` 未接线、`FAV_DEGRADED_READ=false`、SWBOOT-10 fail-closed stub——代码与三环境 compose 均对齐,无漂移。
- **IAM 主链路复核成立**:路由统一挂鉴权中间件、SQL 全参数化、token 哈希存储、错误出参有内部信息过滤。但存在 dev/test 兜底与端点级鉴权缺口(见 S1/S2)。
- **本次新发现**:2 项 critical(后端数据竞态 + docgov 吞错)、10 项 high、若干 medium/low。其中 3 项被两路独立审查交叉命中(见 ★ 标记),可信度最高。
- FEATURE-MATRIX 已声明的 partial/deferred(rdev、SWBOOT、FAV、D7、首载性能)不在本次重复申报范围;本次发现均为**新增**缺口。
## 二、Critical(发布前必须评估)
### C1 ★ docgov 持久化全链路 fire-and-forget 吞错;fencing token 未读回
`internal/docgov/lease.go:460-467, 485-490`;`internal/docgov/engine.go:318, 392, 405, 470, 477, 650, 743, 750, 1081-1134`
租约获取/续约/回收、正文哈希、认领状态、提案决定及**全部审计日志**均为 `_, _ = e.pool.Exec(...)` 静默吞错:
- 审计 INSERT 失败 → 合规事件永久丢失,无重试无告警;
- 租约 UPDATE 失败 → 内存与 DB 分叉,重启后可并行编辑同一文档;
- `lease.go:485` 用 `context.Background()` 脱离请求生命周期;
- `loadDocumentFromDBLocked` 的 SELECT 列表**不含 `lease_fencing_token` 列**(00034 迁移已建列)→ 进程重启后 fencing 语义完全失效(合法持锁者被误判 stale,或 stale 写者被放行——两个方向都有缺口)。
**建议**:持久化失败必须返回 error 或 slog.Error+指标;审计写入失败进重试队列;lease SELECT 补 fencing 列;persistLeaseLocked 失败回滚内存状态。
### C2 ★ Session 全字段数据竞态 + 跨项目污染
`internal/chat/session.go:275-277`;`internal/server/server.go:432-435`;`internal/server/handlers.go:196-198`
- `Messages`/`UserCtx`/`LastAccessedAt` 的读写锁粒度碎片化:SSE 路径持 `execMu`,而 `hydrateVisibleHistory`、`handleGetProject`、`cleanStaleSessionsLocked`、`server.go:435` 均不持锁。并发 SSE+REST 同会话时 `go test -race` 可复现 data race。
- `handleChat` 无锁写 `sess.ProjectID = req.ProjectID`,同一 sessionID 被不同项目请求交错时跨项目串扰。
**建议**:Session 加 `sync.RWMutex` 统一保护;ProjectID 改为请求级参数不写回共享 sess。
### C3 SSE WriteTimeout=120s 硬杀长任务 + 心跳 goroutine 生命周期不一致
`internal/server/server.go:352-355`;`internal/server/sse.go:252-271`
全局 `WriteTimeout: 120s`;SSE 靠 45s 续期+15s 心跳兜底,心跳被中间代理吞掉时长 LLM 回合(dialogueTimeout=3min)被强杀。非流式 `handleRunTask` 同步执行数十分钟任务,120s 必然截断响应(业务已执行、客户端收不到结果 → 幂等重试造成重复执行观感)。心跳 goroutine 模式与 `handleTaskRunEvents`(主循环 select,无此问题)不一致。
**建议**:长任务端点异步化(提交+轮询/SSE);心跳并入主 select 或 WaitGroup 收敛。
### C4 工作流计划保存跨语句竞态 + completed_by 错填
`internal/projectinstance/service.go:185-190`;`internal/projectinstance/stage_store.go:132-138`
- `SaveWorkflowPlan`:upsert 与 bump revision 两条独立事务,并发下同版本号先互相覆盖 plan 再一方 bump 失败 → **版本号与 plan 内容错位落库**。`Activate` 的 token 前置校验与激活事务间存在 TOCTOU。
- `completed_by = (SELECT activated_by ...)`:最终阶段完成的归属人被错填为激活人,审计归属错误。
**建议**:upsert+bump 同事务 + `FOR UPDATE` 锁 project 行;completed_by 显式传当前操作者。
## 三、High(建议进入 short-term 整改)
| # | 发现 | 位置 | 要点 |
|---|------|------|------|
| H1 ★ | dev/test 未认证静默兜底为 user id=1 | `internal/server/auth_middleware.go:48-51` | `Env=dev|test` + `AllowInMemory` + 无 ContextDB 时,无 token 请求静默识别为 id=1(管理员)。main 已被 `validateProductionSecrets` 兜住。建议:兜底路径加审计日志+启动告警;中期删除 legacy 兜底 |
| H2 ★ | docgov 批注/审计端点无资源级鉴权且信任请求体身份 | `internal/server/handlers.go:858-940, 534-552` | 任意登录用户读任意文档批注/审计;`AddComment` 直接用 `req.UserID/UserName/UserRole` 可伪造他人身份;`Claim` 角色由客户端自选。两路审查交叉命中。建议:actor 一律取 principal;读类端点加项目成员校验 |
| H3 ★ | 会话查询类端点水平越权 | `chat_turn_api.go:12-45`;`handlers.go:947-981, 429-451` | `handleGetChatTurn`/`handleListChatSessions`/`handleGetUserContext` 的 session_id/user_id 来自 query 不校验归属;userID<=0 时默认 1(种子 admin)。建议:改用 principal 或比对不一致 403 |
| H4 | 登录/注册无速率限制 | `server.go:295-296` | 全仓无限流;bcrypt 高成本反而便于 CPU 耗尽 DoS。建议:IP+用户名滑动窗口限流 |
| H5 | SSE 任务流缺幂等前置检查 | `internal/server/sse.go:398-424` | REST 路径有 `FindByIdempotencyKey`,SSE 路径没有;EventSource 断线重连产生重复任务执行 |
| H6 | 事件 dispatcher stale-fencing 无限重放 | `internal/events/worker.go:206-275` | ErrStaleFencing 直接 cancel+return 不计 Failed;handler 半成功被回滚后事件无限重放,永不可达 dead,无告警 |
| H7 | 迁移 00001–00009 缺 down;00012 seed 迁移破坏性 DELETE | `deploy/postgres/goose/` | 前 9 个迁移无 down;00012 无条件 DELETE 30 个 session 的真实消息再插种子;多个迁移事务内 CREATE INDEX(非 CONCURRENTLY)大表会锁表 |
| H8 | compose 弱口令兜底 + main 注册默认开放 | `docker-compose.{dev,test}.yml:17,86,101`;`main.yml:101` | dev/test 的 DB/ADMIN 口令有弱默认值;main 已用 `:?required` 强制但 `AUTH_REGISTRATION_ENABLED` 默认 true。建议 test 对齐 main 必填,main 注册默认 false |
| H9 | 前端 KaTeX 占位符 XSS | `web/src/engine/renderMarkdown.ts:44-64, 106-108` | 数学公式 catch 分支把未转义原文放入占位符数组,还原发生在 escapeHtml 之后 → XSS。建议:catch 分支 escapeHtml(math) + 占位符随机化 |
| H10 | `context.Background()` 脱离请求生命周期 | `internal/chat/session.go:131-134`;`server.go:423,458,566` | LLM 摘要/DB 调用用 Background,客户端断开无法取消,慢库时 handler 挂死。建议:透传请求 ctx + WithTimeout |
## 四、Medium(选录,详见各专项)
| # | 发现 | 位置 |
|---|------|------|
| M1 | 全局缺安全响应头(CSP/nosniff/X-Frame-Options/Referrer-Policy) | `internal/server/server.go:624-636` |
| M2 | 租约 TTL 无上限(可锁文档 1000 天)+ AcquireLease 无编辑权校验 | `handlers.go:1196-1203`;`docgov/lease.go:62-70` |
| M3 | coordinator 关键词命中强制改写意图 + 模板硬编码"张工您好" | `internal/coordinator/engine.go:409-416, 485` |
| M4 | `fmt.Sscanf` 解析 `u_sess_xxx` 必然失败 → UserID=0 审计落空 | `coordinator/engine.go:930-934, 1067-1071` |
| M5 | `Runtime.Start` 被 main 与 Server.Start 双重调用且错误被吞 | `server.go:343-345`;`cmd/server/main.go:47` |
| M6 | sessions map 无硬上限,惰性清理可被随机 session_id 放大 | `server.go:451-453` |
| M7 | 修订三阶段写(账本→DocGov→freeform)无事务,靠客户端重试补偿 | `document_revision_handlers.go:274-318` |
| M8 | 测试 flag 名笔误 `FAV_PUBLISHER_ENABLE`(少 D)门闩失效 | `internal/artifactvault/publisher_gate_test.go:16` |
| M9 | dev compose 声明永不消费的 `SEMANTIC_SUMMARY_REPLACE`(诱导性配置);dev 与 test/main 默认模型不一致(M2.7-highspeed vs M3) | `docker-compose.dev.yml:119,44` |
| M10 | OpsCutover 硬编码 `"dev"`,test/main 无合规 cutover 路径 | `internal/events/cutover.go:12-19` |
| M11 | 前端乐观已读无失败回滚;MessageStream prevMsgCount 跨会话不重置;SSE 无 online 感知重连 | `web/src/hooks/useUnreadMarks.ts:176-233`;`MessageStream.tsx:148-154` |
| M12 | ExcelViewer 无行数上限内存爆炸;React 19 下 30+ 处裸 setTimeout 无清理 | `ExcelViewer.tsx:42-55` |
| M13 | `deploy/postgres/migrations/` 疑似死目录(14 个旧 SQL 无引用,与 goose 重复) | `deploy/postgres/migrations/` |
| M14 | `Orchestrator.ResolveDocTask` 静默返回 0 值三权掩盖数据不一致 | `internal/workflow/runtime/orchestrator.go:69-83` |
| M15 | PG 事件投递允许乱序而 MemoryBus 有 gap 语义——测试替身与生产行为分叉 | `internal/events/dispatcher.go:56-63` vs `bus.go:99-103` |
## 五、Low(选录)
- `handleHealth` 每请求同步两次 DB 往返无缓存无超时(`handlers.go:97-110`)
- `writeStoredEvents` json.Marshal 错误被吞(`task_run_handlers.go:437`)
- `ReplayPlan.Digest()` 不含 DryRun 字段(纵深防御,`internal/events/replay.go:24-38`)
- 前端 localStorage 缓存无版本字段;`lucide-react@^1.16.0` 版本异常需确认来源;App.tsx 4600 行/30 个 useEffect 巨石组件
- `handleRunTaskStream` 死参数 `includeDetails/requestID`(`sse.go:417-418`)
## 六、复核确认无问题的面(证据性记录)
- SQL 注入:全部 pgx 参数化;仅 4 处 fmt.Sprintf 涉 SQL 均为常量
- CORS:白名单制、无 `*`+凭证;SSE 鉴权失败返回 JSON 而非污染流
- 日志:密码 bcrypt、token SHA-256 摘要存储;`httpx.LooksInternal` 过滤内部错误串
- os/exec:bridge 路径来自服务端配置,用户输入只进 stdin payload;无 text/template 引用
- 事件系统:claim 幂等(SKIP LOCKED + fencing 复合判断)、ack+inbox+checkpoint 单事务、envelope 完整性校验、replay 审批人≠操作人——设计正确
- 前端:Mermaid strict 模式、链接 noopener、ErrorBoundary 覆盖根组件、EventSource 卸载清理正确
## 七、优先级建议
1. **进 main 前必须处理**:C1–C4、H1–H5、H8、H9(H8 的 main 注册默认开放一项)
2. **short-term 整改批**:H6、H7、H10、M1、M2、M8(一行修复恢复门闩)
3. **backlog**:其余 medium/low
## 关联整改
- Remediation: 待立项(建议按第七节优先级拆 short-term 任务)
## 证据
- `go vet ./...` 零输出;`go test -short ./...` 55 包 ok(2026-09-19,dev@e8ed33b)
- 五路只读深潜:后端 server/chat、docgov/events/工作流/迁移、前端 web、配置/flag/死代码、安全鉴权
- 任务条目:[ops/short-term/2026-09-19-system-deep-audit.md](../ops/short-term/2026-09-19-system-deep-audit.md)
## 开放问题
- dev 与 test/main 默认模型不一致(M2.7-highspeed vs M3)是否刻意?未见 `.doc/ops` 对齐记录
- `deploy/postgres/migrations/` 是否还有外部工具依赖?无则建议归档
- H1 legacy 兜底是否有仍在使用的内部工具依赖?删除前需盘点
+7 -2
View File
@@ -5,7 +5,7 @@
## 焦点
1. **系统体检整改**:Wave 0–7 **done**(Wave 7 编排确认见 [决策记录](./short-term/2026-09-18-wave7-roadmap-orchestration.md))
1. **2026-09-19 审计整改批 1–4 done**(合入 `844f757` / `08f4e0b` / `a2a49a6` / `d73bc79`)。余项见 [long-term](./long-term/2026-09-19-audit-remainder.md)。未晋升 test/main,未 rebuild 容器。
2. **Control Center 已移出本仓库**:项目进度由独立统一管理系统承担;本仓库恢复为智能体系统。见 [任务](./short-term/2026-09-19-remove-control-center.md)
3. **rdev 模板深度 / document-author-only**:已合入 `origin/dev`;未晋升 test/main
4. **红线**:禁止整支 merge FAV;不默认升 `MULTI_INTENT_PLAN_TIER=full`;`SEMANTIC_SUMMARY_REPLACE` 不随 W2 开启;FAV live 观察窗证据驱动;`FAV_DEGRADED_READ=false`;项目 143 只读;SWBOOT-10 保持 fail-closed stub;D7 待合规签字
@@ -14,6 +14,11 @@
| owner | task | branch | worktree | mutex | status |
|-------|------|--------|----------|-------|--------|
| Cursor | audit-b1-data-consistency | fix/audit-b1-data-consistency | - | docgov+workflow+chat | done |
| Cursor | audit-b2-authz-hardening | fix/audit-b2-authz-hardening | - | authz | done |
| Cursor | audit-b3-sse-events | fix/audit-b3-sse-events | - | chat+events | done |
| Cursor | audit-b4-frontend | fix/audit-b4-frontend | - | web | done |
| Cursor | audit-remainder-backlog | docs/audit-remediation-backlog | - | audit | done |
| Cursor-Auto | 2026-09-19-remove-control-center | fix/remove-control-center | - | controlcenter | done |
| Cursor-Auto | 2026-09-17-control-center-server | feature/control-center-server | - | controlcenter | cancelled |
| Kimi | 2026-09-18-user-manual | feature/user-manual | .worktrees/feature-user-manual | docs | done |
@@ -144,7 +149,7 @@
| holder | phase | since | scope | queue |
|--------|-------|-------|-------|-------|
| - | idle | 2026-09-19 | audit-b1~b3 已合入 dev(b3 merge a2a49a6,vet/test 全绿);批4 待开 | - |
| - | idle | 2026-09-19 | 审计批 1–4 已合入 dev(844f757 / 08f4e0b / a2a49a6 / d73bc79);medium 见 long-term | - |
**phase**: `idle` | `merging` | `smoke` | `e2e`
@@ -0,0 +1,52 @@
---
title: "2026-09-19 审计剩余项(medium/low,不挡已合入批 1–4)"
status: planned
created: 2026-09-19
updated: 2026-09-19
---
# 2026-09-19 审计剩余项
## 动机
[系统深度体检](../../audit/2026-09-19-system-deep-audit.md) 的 C1–C4 与 H1–H10 已按四批合入 `dev`。下列 medium/low 仍真实存在,但不进入本轮发布回退。
## 成功标准
- 每条有独立 short-term、负例测试或明确关闭理由后再标 done
- 不把本清单自动写成 gap-matrix blocker
## 依赖
- 批 1–4 已在 `dev`:`844f757`、`08f4e0b`、`a2a49a6`、`d73bc79`
## 暂不启动原因
本轮范围只到 critical + high。下列项要么是加固、要么需要产品/合规决策,要么改动面大于收益。
## 清单
| ID | 项 | 位置 | 说明 |
|----|----|------|------|
| M1 | 安全响应头 | `internal/server/server.go` CORS 中间件 | 补 `nosniff` / `X-Frame-Options` / `Referrer-Policy` / 收敛 CSP |
| M2 | 租约 TTL 无上限 | `handlers.go` AcquireLease | clamp 到 `DefaultHumanLeaseTTL`,Acquire 前校验编辑权 |
| M3 | 关键词改写意图 + 硬编码「张工」 | `internal/coordinator/engine.go` | 移到意图层并加置信度;称呼取用户名 |
| M4 | `fmt.Sscanf` 解析 `u_sess_*` 得到 UserID=0 | 同上 | 统一数字 ID |
| M5 | `Runtime.Start` 双调用且吞错 | `cmd/server/main.go` 与 `server.go` | `sync.Once` 或删 Server.Start 内调用 |
| M6 | sessions 无硬上限 | `server.go` cleanStaleSessionsLocked | LRU + 服务端发 session id |
| M7 | 修订三阶段写无事务 | `document_revision_handlers.go` | outbox 或单事务投影 |
| M9 | `SEMANTIC_SUMMARY_REPLACE` 只在 dev compose 声明;dev/test 默认模型不一致 | compose | 删未接线 flag 并钉死测试;确认 M3 是否刻意 |
| M10 | cutover 只允许 `dev` | `internal/events/cutover.go` | 写明 test/main 切换路径 |
| M13 | `deploy/postgres/migrations/` 死目录 | 与 goose 重复 | 确认无外部依赖后归档 |
| M14 | ResolveDocTask 静默 0 值三权 | `orchestrator.go` | 找不到 duties 应报错 |
| M15 | PG 投递允许乱序,MemoryBus 有 gap | `dispatcher.go` vs `bus.go` | 对齐测试替身与生产顺序语义 |
| L1 | health 每次打两次 DB | `handlers.go` | 短缓存 + 超时 |
| L2 | SSE marshal 错误被吞 | `task_run_handlers.go` | 失败写 error 帧 |
| L3 | ReplayPlan digest 不含 DryRun | `replay.go` | 纳入 digest |
| H1-del | 删除 legacy user-id=1 兜底 | `auth_middleware.go` | 已加日志;删除前盘点调用方 |
| FE | App.tsx 巨石、localStorage 无版本、lucide 版本确认 | `web/` | 拆 hook;缓存加 version;确认包来源 |
## 相关链接
- 审计:[../../audit/2026-09-19-system-deep-audit.md](../../audit/2026-09-19-system-deep-audit.md)
- 批 1–3 报告:`.doc/test-reports/2026-09-19-audit-b*.md`
@@ -0,0 +1,50 @@
---
title: "系统深度体检审计(独立审查潜在缺陷与未完善点)"
status: done
created: 2026-09-19
updated: 2026-09-19
owner: Cursor
branch: docs/audit-remediation-backlog
worktree: "-"
mutex: audit
---
# 系统深度体检审计(2026-09-19)
## 目标
在不改业务代码的前提下,对当前 dev(e8ed33b)做一次独立深度审查,输出 `.doc/audit/` 审计笔记,列出潜在 bug 与未完善项并给出严重度分级。
## 范围
- Go 静态验证:`go vet ./...` + `go test -short ./...`
- 后端:server 路由/chat SSE/docgov/events 错误处理与并发风险
- 前端 web:错误处理、SSE 重连、竞态
- 配置:flag 默认值一致性、死代码、环境差异(dev/test schema 漂移)
- 安全:IAM 旁路、密钥泄露面
- DB:goose 迁移一致性与回滚风险
## 非目标
- 不修改业务代码(docs-only 分支)
- 不验证已明确 deferred 的事项本身(FAV live 放量、D7 签字、SWBOOT-10)——仅核对声明与代码是否一致
## 步骤
1. [x] 创建 docs 分支、登记 CURRENT
2. [x] go vet + go test -short
3. [x] 并行子代理深潜后端/前端/配置/DB/安全
4. [x] 汇总撰写 audit 笔记并提交
## 验收标准
- [x] 每个发现均有代码路径 + 行号证据
- [x] 严重度分级(critical/high/medium/low)
- [x] 与 FEATURE-MATRIX 已声明的 partial/deferred 项对齐,不重复申报
## 证据 / 备注
- go vet 零告警;go test -short 55 包全 PASS(dev@e8ed33b)
- 审计笔记:[.doc/audit/2026-09-19-system-deep-audit.md](../../audit/2026-09-19-system-deep-audit.md)
- 五路并行只读深潜全部完成;四条红线核实一致;新发现 4 critical(C1 docgov 吞错 / C2 Session 竞态 / C3 SSE 超时 / C4 工作流竞态)+ 10 high
- 整改:critical/high 已分四批合入 `dev`(`844f757` / `08f4e0b` / `a2a49a6` / `d73bc79`)。medium/low 见 [long-term](../long-term/2026-09-19-audit-remainder.md)。
+3 -1
View File
@@ -1,6 +1,6 @@
# 模块与功能完成度(SSOT)
**更新**: 2026-09-17(W3 PI 叙事 done;W4 多意图扩面 done)
**更新**: 2026-09-19(审计整改批 1–4 已合入 dev;medium/low 见 [long-term](../ops/long-term/2026-09-19-audit-remainder.md))
**production_ready**: yes(权威来源:[gap-matrix](../remediation/gap-matrix.md);本表不推导发布门禁)
**发布缺口**: [../remediation/gap-matrix.md](../remediation/gap-matrix.md)
**协作焦点**: [../ops/CURRENT.md](../ops/CURRENT.md)
@@ -8,6 +8,8 @@
**整改计划**: [../remediation/2026-09-16-feature-matrix-production-readiness-plan.md](../remediation/2026-09-16-feature-matrix-production-readiness-plan.md)
> 本表描述**能力成熟度**;上线 blocker 见 gap-matrix。二者正交:能力行不必全部 `done` 才能发布;`partial`/`deferred` 不得自动回退 `production_ready`。
>
> 2026-09-19 深度审计的 critical/high 已分四批合入 `dev`(数据一致性、鉴权、SSE/事件、前端 XSS)。不改 `production_ready`。未做项在 [long-term](../ops/long-term/2026-09-19-audit-remainder.md)。
---
@@ -0,0 +1,52 @@
# 审计整改批 4(前端)测试报告
## 一、 测试背景 (Background)
审计 H9:KaTeX 失败回退把未转义公式放进占位符,还原发生在 `escapeHtml` 之后。另有未读乐观更新失败不回滚、会话切换后滚动基线不重置、Excel 全量进内存。
## 二、 测试目标 (Test Objectives)
- `npm test` 全绿(含伪造占位符用例)
- `npm run build` 通过
## 三、 测试方案与环境 (Test Plan & Environment)
- 分支 `fix/audit-b4-frontend` → dev(merge `d73bc79`)
- 命令:`cd web && npm test && npm run build`
- 未在浏览器复现 KaTeX catch 与万行 Excel(依赖构造输入);XSS 主路径由单测钉住
## 四、 预期结果 (Expected Results)
- 伪造 `XMATHTOKENX0XENDX` 不会被还原成 HTML
- catch 分支公式经 `escapeHtml`
- 未读 POST 失败回滚本地集合
- `sessionId` 变化时 `prevMsgCountRef` 归零
- Excel 预览最多 1000 行并提示
## 五、 实际测试结果与判定 (Actual Results & Evaluation)
### 用例 1:前端测试
- 输入:`npm test`
- 输出:`tests 406 pass`,vitest `3 files / 32 tests passed`,fail 0
- 判定:PASS
### 用例 2:生产构建
- 输入:`npm run build`
- 输出:`tsc && vite build` 成功,`precompress: wrote 49 .br and 49 .gz`
- 判定:PASS
## 六、 自动化测试套件汇总 (Test Suite Execution)
- node:test 406 + vitest 32,全部 PASS
- `tsc` 无错误
## 七、 测试结论与后续优化建议 (Conclusion & Next Steps)
- 批 4 已合入 dev(`d73bc79`)。
- 浏览器端到端未覆盖 Excel 截断与未读回滚;需要时用真实文件和断网点测。
- 容器仍是合入前二进制,前端 dist 由 Go 服务托管时需 rebuild 才进 `:8080`。
## 证据
- merge:`d73bc79`
- commit:`6c28fca`