11 KiB
11 KiB
title, task_id, status, created, updated, owner, task_type, task_tier, policy_revision, branch, worktree, base_dev_sha, write_scope, allowed_environments, head_sha, mutex, review_state, integration_state, deployment, cleanup, cleanup_owner, cleanup_trigger, remote_sync
| title | task_id | status | created | updated | owner | task_type | task_tier | policy_revision | branch | worktree | base_dev_sha | write_scope | allowed_environments | head_sha | mutex | review_state | integration_state | deployment | cleanup | cleanup_owner | cleanup_trigger | remote_sync | |||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 上传文档跨端不可访问:源文件元数据与正文服务端持久化修复 | upload-doc-access-2026-09-27 | done | 2026-09-27 | 2026-09-27 | pi | implementation | small | agent-governance-2026-09-21-r1;dev a0e40919a7 |
fix/upload-doc-access-2026-09-27 | .worktrees/upload-doc-access-2026-09-27 | a0e40919a7 |
|
307e9e6569 |
freedoc+web-upload | not_required | merged | passed | pending | 独立集成/维护执行者 | owner 退出且 dev 集成验收完成后复查 | not_requested |
上传文档跨端不可访问:源文件元数据与正文服务端持久化修复
用户目标与授权
- 用户目标:登录测试用户账户后发现上传的私密文档无法访问,查看时显示注册占位模板(
upload-mui2xpg2/ 上传人 E2E编制-mui2xpg2 / 文件大小 0 KB / 文档概述与背景…),此前人工测试亦大量出现;要求定位并修复。 - 授权来源:用户在本会话中对「开任务 worktree 实施修复(方案 1+2+3 一起做)」回复「授权」。
- 交付终点:代码与迁移在任务 worktree 完成并通过验证;dev 集成与环境验证按门禁另行执行(本任务记录集成状态)。
- 禁止项:默认不 push;不操作 test/main/共享 KB;分析阶段不含业务实施(已进入实施)。
根因(已证实)
- 上传注册正文是占位模板:
web/src/lib/uploadFreeformRegister.ts的buildUploadRegistrationMarkdown生成的模板是服务端唯一持久化的"文档内容"。 - 文件二进制不落服务端(文本类):
useSendMessage仅对非 TEXT_EXTS 扩展名调用uploadProjectAsset登记 CAS;txt/md/csv/json 等只存浏览器 IndexedDB + 内存 fileRaw。非文本文件虽入 CAS,但 MinerU 解析结果只进知识库,不回写文档正文。 - 元数据不落服务端:
freeform_documents表无文件名/大小/mime/asset_id 列;useServerDocumentSync恢复的 deliverable 无sourceType/fileMeta,DocumentPane的isUploadedFile启发式五条件全部不命中 → 走普通 AST 编辑器 →fetchFreeformDoc渲染占位模板。触发条件:换浏览器/换设备/清存储/其他成员查看共享文档。 - "0 KB":
InputArea用(file.size/1024).toFixed(0),<512B 文件四舍五入为 0,且被烘焙进持久化占位 markdown(E2E txt 仅约 41 字节)。 - E2E 盲区:
FS-UPLOAD-01只断言注册出doc-free-*pane,不校验跨会话内容可见性。
方案
- 迁移
00076:freeform_documents增加source_filename/source_mime_type/source_size_bytes/source_asset_id(软关联 CASfile_references,不加 FK)。 - freedoc:Create/Get/List 读写上述列;负值参数拒绝(ErrInvalidParam);Summary 带出元数据。
- 前端上传编排(
useSendMessage):注册前先 CAS 登记全部附件(去掉 TEXT_EXTS 排除)并预读文本正文;注册时携带元数据与 asset_id;文本正文嵌入注册 markdown(## 1. 文档正文(上传原文))。 - 跨端恢复(
useServerDocumentSync→ 新lib/freeformDocSync.ts):按服务端 summary 恢复sourceType: 'uploaded_file'与fileMeta(含 assetId/sizeBytes),DocumentPane 路由回 FilePreviewPanel,经 CAS raw 端点恢复原件预览。 - 大小显示:新
lib/fileSize.tsformatFileSize(B/KB/MB,任何正数不显示 0)。 - E2E:新增
FS-UPLOAD-02(全新浏览器上下文登录同账户,断言真实文件正文可见且占位句消失)。
范围与非目标
- 允许写入:internal/freedoc、internal/server(无需改动,请求透传)、deploy/postgres/goose、web/src、web/e2e、.doc。
- 非目标:MinerU 解析结果回写文档正文(二进制文件正文仍为占位+原件预览,属后续增强);历史已损坏数据(既无元数据也无 CAS 原件的旧上传文档)不可恢复,仅保持现状。
相关问题与经验
- 开工检索:ISSUES.md 检索「上传/upload」,命中 ISSUE-20260924-non-author-400-duty(权限问题,机制无关,未采纳);lessons INDEX 无上传链路相关命中。
- 待验证候选(本次发现、非本任务因果链):
TestCreateDocument_PG_ClientKeyIdempotentAndLedgerSurvivesNewService在全新隔离库(goose 76 + 全部 init SQL)上于基线 dev HEAD 同样失败(rev2 跨服务水合 HeadRevisionNo=0),疑似 docrevision 水合依赖未满足或用例自身问题;需独立排查后按闭环规范登记。
步骤与验收
- 迁移 00076 + freedoc 元数据读写 — 隔离库迁移到 76 通过;单测/PG 测试通过。
- 前端注册编排与跨端恢复 — tsc、npm test(525+34 PASS)、npm run build 通过。
- E2E 用例 FS-UPLOAD-02 编写并入选例清单 —
playwright --list含该用例;真实执行见下。 - dev 集成 — 候选
86efba2c(父1=a0e40919、父2=307e9e65);validate 收据b6fab208(vet/arch-check/全量 short/freedoc PG/web test+tsc 全绿);integrate 收据94991da2,dev 前进至 86efba2c。 - dev 环境部署验证 —
build-single-binary.sh(embed dist @86efba2c)+launch.sh --no-docker重启(env:dev 租约 op510f105a);/health 与 /ready schema=76 ok;新进程 pid 2636482。 - 真实浏览器验收 — dev 环境 5 用例 PASS:FS-IAM-01/02、FS-PROJ-01、FS-UPLOAD-01、FS-UPLOAD-02(全新浏览器上下文可见真实正文);DB 核验 source_filename/mime/size=50/asset_id=1243、正文含上传原文。
验证与证据
| 范围 / 被测 SHA | 命令与环境 | 预期 | 实际 / 判定 | 证据 / 脱敏说明 |
|---|---|---|---|---|
| Go 迁移链(worktree HEAD) | 隔离 PG(docker pgvector:pg16,127.0.0.1:5599,一次性容器)go run ./.tmp/migrate <dsn> + system/docgov/context_init.sql |
迁移至 76 无错 | PASS(schema_version: 76) | 容器用后即删;DSN 仅本机随机端口 |
| freedoc 单元+PG | TEST_DATABASE_URL=<隔离库> go test ./internal/freedoc/ -count=1 -v |
全 PASS | 除下方预存在失败外全 PASS | TestCreateDocument_PG_ClientKeyIdempotentAndLedgerSurvivesNewService 失败:基线 dev HEAD 在同一隔离库同样失败,判定预存在、与本任务无因果(见"相关问题") |
| Go 全量门禁 | go vet ./...;./scripts/arch-check.sh --mode block;go test -short ./...(TEST_DATABASE_URL/CONTEXT_DB_* 指向隔离库) |
全绿 | vet 无输出;arch-check "production gate passed";short 69 包 ok,仅上述预存在失败 | — |
| 前端 | npm test;npm run build(web/) |
全 PASS | 525(node:test)+34(vitest)PASS;build 成功 | — |
| E2E | npx playwright test --list |
含 FS-UPLOAD-02 | PASS(枚举) | 真实浏览器执行需 dev 环境运行新代码后补跑 |
环境说明:worktree 无 .env,config.Load 默认 DSN 指向 dev 库(127.0.0.1:5433);首次误跑 TestFreeformDoc_DatabaseIntegration 曾写入 dev 库测试数据(chat_messages 297–301、user 101 若干软删 doc,session 前缀 sess-db-freedoc-*,与该测试在主目录运行的既有行为一致)。后续全部测试改用隔离库环境变量。
集成与环境证据
- 候选验证收据:
agentctl validaterunb6fab208-b9df-47e0-ba41-c7bf37b22d83@86efba2c(VET_OK / ARCH_OK / GO_SHORT_ALL_OK / FREEDOC_PG_OK / WEB_TEST_OK / WEB_TSC_OK;freedoc PG 套件排除TestCreateDocument_PG_ClientKeyIdempotentAndLedgerSurvivesNewService,理由:基线 dev HEAD 在同配方隔离库同样失败,预存在、与本任务无因果,已登记候选)。 - 集成收据:
agentctl integraterun94991da2-f817-4f72-864b-099a50824837,expected-deva0e40919,dev →86efba2c。 - 部署:
bin/agent-server重建于 08:06(86efba2c),launch.sh --no-docker重启;/health、/readyschema_version=76。 - 浏览器验收:
RUN_UI_ACCEPTANCE=1 playwright -g "FS-IAM|FS-PROJ-01|FS-UPLOAD"5/5 PASS(6.4s);证据.doc/test-reports/assets/2026-09-27-upload-doc-access-e2e/(ui-results.json、run-meta、截图)。运行期间 env:dev 由本任务部署操作510f105a持有(launch nohup 分离致租约保持,无并发持有者)。 - DB 事实核验:
doc-free-1790496487674865360-69→ source_filename=upload-mujjdjp5.txt / text/plain / 50 B / asset_id=1243;版本正文含「E2E upload body」原文。
过程事件(如实记录)
- 误连 dev 库两次:worktree 无
.env时config.Load默认 DSN 指向 127.0.0.1:5433 dev 库。07:37 freedoc 测试误写 chat_messages 297–301(sess-db-freedoc-*)与 user 101 若干软删文档;07:53 首次 validate(未设 CONTEXT_DB_*)经 server 测试触发迁移,dev 库被应用到 schema 76(内容与随后合入 dev 的 00076 完全一致)并留下合成残留:project id=1「受控工程研发项目」、doc_proj1_intake/doc_proj1_cleanroom_spec(drafting)、user 101 文档一条。历史业务数据核对无损(21 项目/64 用户/320 消息,9/26 E2E 数据俱在)。残留未删除(删除属破坏性操作,留待授权清理);后续全部测试显式指向隔离库。 - E2E 证据目录误写:spec 默认 ASSETS 指向 9/26 历史目录,本次运行覆写其 run-meta/截图/ui-results;已从 git 还原历史目录(0 差异),本次证据迁至
assets/2026-09-27-upload-doc-access-e2e/。后续运行应显式设UI_ACCEPTANCE_ASSETS_DIR。 - 预存在测试环境敏感(候选登记,未立项):
TestCreateDocument_PG_ClientKeyIdempotentAndLedgerSurvivesNewService、TestPG_ActivationAndStageAdvance、TestWorkflowGovernance_PG_ActivateApproveAdvanceChain等在自建隔离库上基线同样失败("approved requires frozen review_snapshot_id"),疑依赖特定运行顺序/外部服务;隔离库配方:扩展 → goose 链 → 三个 init SQL(顺序反了 00001 会因 day_key 失败)。
遗留与限制
- 存量损坏数据不可恢复:修复前上传的文档(含用户报告的
upload-mui2xpg2)无服务端元数据与原件,仍显示占位模板;重新上传即可获得正确行为。 - 二进制(docx/pdf 等)文档正文仍为占位+原件预览;解析正文回写为后续增强。
readAttachmentsAsMaterials行为变化:文本附件现在优先以 asset_id 引用(此前 inline Base64);CAS 失败时回退旧行为。
收口与交接
- 剩余工作:无(授权范围内验收满足)。可选后续:二进制文件解析正文回写文档(非目标)、预存在测试敏感用例独立排查、dev 库合成残留授权清理。
- 本地提交
307e9e65(+本文档收口提交);dev 集成86efba2c(含第二轮文档收口集成);部署制品bin/agent-server@86efba2c 运行中;远端未同步(未要求 push)。 - cleanup:两个 worktree(任务 + 集成候选)保留,cleanup=pending,由独立维护者按门禁复查后处置。
相关链接
- 问题分析见本文件"根因"节(用户会话内交付)。