比较提交

...
9 次代码提交
作者 SHA1 备注 提交日期
wangsli和Claude Opus 5.5 36b949ecf6 借款出借人增加员工与公司组合数据库唯一约束(09-17-company-loan-principal-repayment)
ci-minimal / check-minimal (push) Canceled after 0s
ci-e2e / e2e (push) Canceled after 0s
- 新迁移 20261011010000_loan_lender_employee_unique:(legal_entity_id, employee_id) 唯一,归档计入、无员工出借人不冲突;生产只读排查重复 0 组
- 触到约束时返回原有 loan.employee_lender_conflict,不报 500 或误报请求重复;员工行锁与代码检查保留
- 测试改为断言数据库拒绝模糊旧数据,补空员工共存与归档占用;变异 12 项含去索引与数据库兜底两种运行
- PRD 补本迁移回退说明;独立只读复核无阻断,发布窗口需重跑重复计数(AC59)

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 13:21:08 +08:00
wangsli和Claude Opus 5.5 5469db585c 移交报销真实 OCR 与科目映射余项并归档费用政策票据门禁任务(08-29-reimbursement-expense-policy-invoice-guard)
- 报销发票真实 OCR 验收(RC-OCR-1–4)与财务科目映射(RC-ACCOUNT)移交 09-08-reimbursement-payment-closeout,保留三层既有证据与责任分工
- 归档父任务及部署子任务 08-30-reimbursement-expense-policy-invoice-guard-deploy;实现 6e999d5/e5685f1 包含于生产 4a5cd8f2,原清单不补勾
- 归档后修正指向接收任务的相对链接

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 11:05:07 +08:00
wangsli和Claude Opus 5.5 89f46790e8 归档本地并发问题修复任务并补收口记录(09-18-local-concurrency-product-fixes)
- 已完成 P1–P4、P6–P10,实现 1f8d5b65/合并 46a70aa5 包含于生产 4a5cd8f2
- P5 旧 WebKit 派生 registry 保护余项转由 09-08-reimbursement-payment-closeout 跟进
- 仅 P9 经补审,全量独立复核未做,不补记为通过;原清单保留

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 10:49:22 +08:00
wangsli和Claude Opus 5.5 314274b5a9 归档登录前置条件任务:已完成范围收口,后续阶段取消(09-07-e2e-auth-preconditions)
- 已完成:立项与认证状态合同、阶段 0 取证(用户独立复核批准,AC1)、在线对照
- 阶段 1 迁移、回归、矩阵验证与独立复核未获批未执行,按取消记录,AC2–AC6 不补勾
- 环境启动合同与登录后线上冒烟由发布门禁任务跟进

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 10:47:32 +08:00
wangsli和Claude Opus 5.5 377bda322e 归档报销状态空隙与财务处理视角任务(09-04-reimbursement-state-gap-finance-view)
- 依用户线上反馈收口:待组批可找到、他人未提交草稿不可见、权限内退回/撤回草稿仍可见(设备未注明,不记为双端验收)
- 实现 f9aee4c9、229867f5 均包含于生产 4a5cd8f2;原未勾清单与历史记录保留
- 银行导入、付款交接、通知送达等继续由原任务跟进

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 10:41:38 +08:00
wangsli和Claude Opus 5.5 b85ff6a9bf 归档 11 个已上线 Trellis 任务并补收口记录(09-17-company-loan-principal-repayment 任务整理)
- 报销一页式审批工作台父任务及 B-2a/B-2b/只读一致性子任务(AC11 依用户 2026-09-27 确认)
- 报销详情材料预览修复及其部署子任务(由候选 59dc5f6 发布覆盖,原步骤未执行,记录保留)
- 资产事前批准纠偏、Trellis 流程对齐、ERP 遗留精简、预算池并发、采购提交超时
- 各任务 prd 顶部新增收口记录,实现均包含于生产 4a5cd8f2;原未勾清单保留不补勾
- B 组与有真实待办的任务保持进行中

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 10:19:21 +08:00
wangsli和Claude Opus 5.5 3c5cc544f5 追回证据刷新期间禁用选择并修复员工资料登录误报(09-17-company-loan-principal-repayment)
- 上传银行证据后、刷新完成前,证据下拉框与确认按钮禁用并提示,避免空证据提交
- 员工来源版本去掉 updated_at(登录会刷新),保留全部真实资料字段的拦截
- 补登录误报回归、字段变更拦截、两项变异及刷新挂起的浏览器检查
- 任务记录 E 节与 AC52–55,独立只读复核无阻断项

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 09:22:34 +08:00
wangsli 91fe31f0db 修复授权分页并补齐追回到账诊断验证(09-17-company-loan-principal-repayment) 2026-10-10 21:56:08 +08:00
wangsli和Claude Opus 5.5 db919dc6f6 记录出借人线上确认发布结果与办理顺序(09-17-company-loan-principal-repayment)
- 补记 4a5cd8f2 门禁、部署与生产 83/83 迁移证据;本次未做快照按用户决定记录
- 更新 PRD、implement 与《试运行与自动化验证手册》的办理顺序;真实业务验收及两处历史失败仍开放

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-10 18:22:49 +08:00
共修改 115 个文件,包含 1063 行新增和 98 行删除
@@ -1155,6 +1155,7 @@ await canReadReimbursementBusinessFact(prisma, reimbursement, employee.id);
- Same-company employee association includes archived/history rows in duplicate checks, including the old identity-confirmation entry. Reuse a unique matching lender; reject ambiguous links and require explicit refresh for different data. Existing-lender actions lock lender before sorted employees. New creation never locks an existing lender after employees: finish and reacquire in the correct order if a link appeared. Use employee `FOR NO KEY UPDATE` to serialize association/source edits while allowing audit/actor FK `KEY SHARE`; `FOR UPDATE` can deadlock against another employee's refresh/audit. Persist and serialize each actor's request-key binding for both creation and reuse, with audit in the same transaction.
- Explicit employee refresh rejects reserved repayments, checks source/identity/account versions and advances both lender versions with renewed finance identity confirmation. This invalidates old company-representative grants without changing principal or historical payment snapshots. Employee-linked lenders cannot use the manual account PATCH; employee-master edits alone never rewrite the loan copy. Verify the actual audit-FK interleaving, duplicate/legacy races, refresh versus repayment creation, audit rollback and reverse mutations using `RUN_ISOLATED_LOAN_EMPLOYEE_ONLY=1` and optional `RUN_ISOLATED_LOAN_EMPLOYEE_MUTATIONS=1` on the isolated loan runner; retain the original full-loan regression.
- Related-party loans use the explicit `loanApprovalPolicy` branch, not a global reviewer exception. Finance proposes `LoanRelatedAuthorization` for one company/lender, its verified employee identity and specific confirmed loans; that designated active manager/CEO confirms it. Grants, scope and lifecycle events are append-only. Resolve the latest confirmed version without falling back to an older grant when the new scope is narrower or stopped; a superseded version cannot receive stop events.
- The authorization selector calls `GET /loan-lenders?eligible=related_authority` under the existing finance/`loan.manage` gate. Filter verified, employee-linked, non-self lenders before both pagination and count; this selector filter does not replace final representative-role/scope checks. Omitted `eligible` preserves the ordinary list, unsupported values reject, and opening the dialog resets to page 1. Browser pagination waits for a loaded, changed option list and checks the intended IDs, including cache hits; a network response alone is insufficient. After evidence upload, select the exact returned evidence ID when its option is attached, never the last option or placeholder. Verify genuine page 3 with desktop/mobile scope registration and designated confirmation, an unchanged-page counterexample, and distinct synthetic payer/payee accounts for direction refusals. Set `RUN_ISOLATED_LOAN_BROWSER_DIAGNOSTIC` to `authorization-pagination` or `refund` with the existing isolated DB/browser switches; diagnostic checks do not confer release eligibility.
- Freeze `LoanRepayment.approval_mode`, `authorization_id` and `approval_context` at creation. Only `related_party` waives the designated payee-conflict reason. Keep maker/submitter/exporter/uploader/manual-entry/proposer conflicts as a separate union of all history, including voided and superseded materials; do not remove the payee employee from that union. Reject designated-reviewer participation at maker entry points as well as decision/final-consumer checks. Old independent loans and shared reimbursement/travel rules do not switch modes.
- New payment creation/approval/export checks current payment authority; new reconciliation decisions and final reservation release check current reconciliation authority. `decisionFields()` stores purpose, actual actor, target, scope, authorization version/event and conflict identities. `assertDecision()` validates immutable historical provenance at receipt reconstruction, settlement, terminal/recovery consumption and bank-event booking; it does not require the old reviewer to remain active or the old grant to remain enabled. Stopping payment and stopping reconciliation are separate events. Previously approved bank facts remain usable if their evidence and all money/transaction checks still hold.
- Related payment review may return an application with `decision=reject` and a reason. This records an immutable decision without releasing reservations; it cannot later be approved, exported or paid. Finance uses safe never-sent cancellation and creates a corrected application. The UI, responsibility and notification consumers must distinguish this from an approved payment or an independent review.
@@ -1,3 +1,25 @@
## 2026-10-11 报销真实 OCR 余项移交(当前文档接续点)
接收来源任务 `08-29-reimbursement-expense-policy-invoice-guard` 与部署子任务 `08-30-reimbursement-expense-policy-invoice-guard-deploy` 的报销发票 OCR 及财务科目映射余项;合同入口为 [PRD 的移交节](./prd.md#2026-10-11-接收余项报销真实-ocr-验收与财务科目映射)。本节不更新下方其他工作线的历史结果,独立复核与真实银行/通知/恢复等义务继续按原合同跟进。
### 本轮文档移交
- [x] 对照原父任务最新记录,区分指定样票真实腾讯单独调用、mock 提供商隔离审批整链、真实腾讯线上合成草稿三层证据;原 2026-09-10 草稿 PASS 不降级,也不扩大到公网审批或全票种。
- [x] 双向登记 RC-OCR-1–4、责任和退出条件;接回 RC-ACCOUNT,保留财务负责人、原已过目标日期和无科目表留空规则。
- [x] 本机确认 `live-draft-result-v6.json`、`live-draft-final-readback.json` 及原测试脚本仍在既有发布目录;本轮未重跑它们。本地 Git 祖先检查确认 `6e999d5`、`e5685f1` 已包含于用户报告的生产版本 `4a5cd8f2`,不据此声称本轮公网复核或业务验收。
- [x] 基线 `89f46790` 的本地文档移交检查通过:三个任务 `task.py validate`、流程基线、Trellis 治理、敏感内容扫描与 `git diff --check` 均 exit 0;文档登记检查 errors=0、warnings=1(既有 22 个文件未登记)。完整差异仅三个任务的 prd/implement/notes,历史正文与复选框保留,状态/提交锚点/任务关系未改;未跑业务回归、云端或公网检查,不是独立复核。
- 补充核对通过:九文件范围、新移交链接/锚点、JSON 末尾换行及历史 notes 后缀均正确。正文保留检查初版用“原全文必须是连续子串”误把标题后插入判为改写;修正为逐行仅允许新增后通过,没有为此改动历史正文。
- 未提交、未归档,两个来源任务仍为 `in_progress`;不以移交自动授权完成旧部署清单或原生产操作。
### 后续验收接续(未执行)
1. 执行端先界定候选、环境和样票,复用原脚本前核对其当前合同;提供商检查与业务上传使用同一获准样票摘要,不能复用真实票号绕过查重。
2. 明确云调用与业务写入范围、测试身份、审批隔离及清理前提后,再执行 PRD 的 RC-OCR-1–4;前提不足保留缺口,不反复尝试生产登录或交给真人员工试错。
3. 逐项回填字段一致性、实际队列及状态、页面/数据库消费者、异常回归和清理结果;区分草稿、审批、付款及既有 mock 证据。只有本项全部退出条件满足后才可收口 RC-OCR。
4. RC-ACCOUNT 等财务正式映射及回填日期;没有依据保持 null,生产回填另批。旧独立复核余额仍由本任务已有复核工作线跟进,不把主会话文档检查记为独立通过。
本轮仅任务文档,不改产品/规约/数据库,不跑业务回归或完整发布门禁。没有新增可复用工程规则,沿用现有 `docs-and-evidence.md` 与报销 OCR 规约。
## 2026-09-19 当前进度:本地完整验收通过
g20-full-r6从static完成同一冻结候选:后端4437通过/1原条件跳过、372本地浏览器全过,零失败/跳过/重试;33页与33PNG、采购/报销付款整链、新旧编号及财务同页进度全部通过。回执PARTIAL仅dirty_candidate,localChecksPassed=true,无部署资格;环境/输入/归属清理PASS。
@@ -1,3 +1,33 @@
## 2026-10-11 接收余项:报销真实 OCR 验收与财务科目映射
本节接收 `08-29-reimbursement-expense-policy-invoice-guard` 及其部署子任务 `08-30-reimbursement-expense-policy-invoice-guard-deploy` 的未完成责任。移交只改变跟进归属,不把原未勾选项判为通过;本任务继续 `in_progress`。原付款、真实银行、通知及独立复核余项保留。
### RC-OCR 报销真实 OCR 验收
范围是**报销发票**的“上传→真实腾讯 OCR→实际队列→我的发票选票→生成/关联明细→保存刷新→提交审批”及字段核对,不是借入回单或付款回单的 BankSlipOCR。借款/银行回单证据不能代替本项。
已完成证据按原环境分别保留:
- 2026-09-09:指定样票 `SAMPLE-PDF-INVOICE-FIRST` 单独调用真实腾讯,归一化必填字段检查通过;没有逐字段人工核对识别精度,也没有通过该调用完成业务上传/队列/浏览器链路。来源为原父任务 implement 同日“用户指定样票的真实腾讯 OCR 验证”节。
- 2026-09-09:隔离数据库、真实认证和 pg-boss、电脑/手机到提交及三段审批通过,但 provider 为 mock;不能与样票单独调用结果相加成真实腾讯整链通过。
- 2026-09-10:在 `e5685f1` 线上用全合成样票完成真实腾讯、队列、选票、分类编辑、草稿保存/刷新及电脑转手机操作;`PASS_DRAFT_ONLY`,未提交、未审批、未付款。来源为原父任务 implement 顶部及 `artifacts/qa-flow/20260909-release-e5685f1/live-draft-result-v6.json`、`live-draft-final-readback.json`。这已满足原“真实腾讯识别合成样票”的草稿阶段要求,不重复列作从未验证。
仍待验收:
- [ ] RC-OCR-1:执行端记录本次候选、获准环境、样票别名及摘要,将真实提供商、队列任务、票据状态、页面操作和持久化回读关联到同一次运行。复用历史证据时保留版本与范围,不能当作新候选复测。
- [ ] RC-OCR-2:对约定样票的号码、日期、金额/税额/合计和购销方等适用字段与原件核对,只记录是否一致、缺失或需人工修正;不记录真实字段值。执行端核对测试样票与技术结果,财务负责真实业务原件核对及费用用途/分类确认;不把商户名称推断为费用用途,不扩成全票种精度承诺。
- [ ] RC-OCR-3:同一候选、同一获准环境完成真实腾讯到选票、可编辑明细、保存刷新及提交/审批的串联,证明添加前零新增明细、用户添加后关联正确、必要字段/票面金额门禁有效。生产审批隔离条件不足时停在草稿并保留审批缺口,不改真人审批路由,不以历史 mock 审批替代本项。
- [ ] RC-OCR-4:接回原范围的重复/占用、识别失败或缺字段、人工补正、手改不被刷新覆盖与重试不重复建明细等回归证据;逐项注明 mock、本地或真实提供商层次。记录本轮清理与回读,归档票据不等于物理删除附件/OCR/审计。
责任:执行端负责技术前置、自动化、结果核验和证据回填,不由真实员工承担软件回归;财务提供/批准可用业务材料并核对业务字段;用户决定云端发送、生产账号/数据/配置写入及提交审批的操作授权。原样票单次调用和过去合成验收授权不自动扩展到新真实票据、生产写入、真实消息或真实付款。付款回单与借入回单仍由各自既有合同验收。
### RC-ACCOUNT 财务科目映射回填
- [ ] 财务负责人提供正式科目表后,核对并回填 `account_code_default` 的映射结果,执行端记录依据与结果;生产回填须另获明确授权。未提供时继续保持 null,不编造编码、不阻塞票据门禁,也不将其记为已完成。
- 原目标回填日期 `2026-09-05` 已过,当前未找到正式科目表及回填证据;责任角色仍为财务负责人,下次日期待其确认,本轮不虚构或自动延期。
本轮只落移交记录、核对引用并运行文档治理检查,不执行以上验收。两个来源任务的归档、此次提交,以及云调用/生产操作均保留各自授权边界。
## 2026-09-18 G19 最新验收状态
分层验收已获明确批准并本地实施验证:完整发现65/365,351本地、12同候选公网义务、2冻结旧链。74项覆盖/runner、7项诊断检查和2条本地冻结保护通过;原完整full-r2仍失败,公网NOT_RUN_LOCAL、独立PENDING,R1未启用。具体合同、证据、失败保留及后续顺序见implement顶部;未提交部署或执行真实操作。
@@ -21,7 +21,7 @@
"children": [],
"parent": null,
"relatedFiles": [],
"notes": "2026-09-19 g20-full-r6:4437后端通过/1原跳过,372本地浏览器全过;PARTIAL仅dirty_candidate,独立PENDING/无部署,旧registry保护例外保留且无新增漂移。当前余额见implement顶部。\n2026-09-18 G19分层验收已批准并本地实施验证,74+7合成检查和2条冻结保护通过;公网12义务/完整门禁/WebKit/R1/独立及真实事实保持开放。\n2026-09-18 G18本地接续完成:33路由项、R108、OCR;并发4/4、认证/OCR18/18、unit131。原full-r2不回写;12live/2冻结验收G19本地实施验证完成,公网义务待兑现、WebKit/R1/独立/外部事实仍开放。新包local-coverage-g18-20260918,未提交推送部署。\n2026-09-18协调更新:09-06完成G16/G17本地批次,普通全量仍失败;14原失败观测及2串行缺失已定向重验,WebKit/冻结差旅/完整覆盖与可靠性、R1/独立及外部事实仍开放。借款T1–T4、报销反馈与ERP退役维持此前本地完成,非公网事实。最新入口见implement顶部。\n2026-09-17本轮W8/W6:22任务231条件逐项处置,154历史支持/56部分/11外部/4替代/4冻结/2条件未触发;82引用定位,接回AP/seed/预检b87cc55发布事实。B34损坏导入500与合成日志内容已复现并规划、未修;低年份分支分类完成,AP反向保持原语义,73项通过。K0/G8两主题完整性与运行时保持、独立PENDING;无新产品、远端或提交发布。\n连续推进至D10/D11,另9文件61通过;后续剩HR多格式策略、589错误遗留及各外部门禁,未宣称全部结束。\n2026-09-17后期连续执行:日期D5-D9、9原因及520本地验证完成;事件六缺口方案、22历史task231条件索引、借款T1a技术草案落盘;独立/恢复/自然业务/产品准入仍开放,无生产或提交发布。\n2026-09-17后期总表:官方current已切回本task,仅整理规划;K0本地30项/166键、B1/B2的450项与前端构建、D1–D4的192项/治理均有各自回执,未提交/发布,独立复核PENDING。按8条既有收口/治理+1条借款新功能共9条工作线编排;错误债务577指纹/598命中/58扫描分组,2026-10-05到期不变;日期新D5–D8只规划。K1/K2、历史缺件/完整计时、备份事件、自然付款/通知、22项历史任务验收映射均保留。借款planning,4阶段/8项AC待实现验收。原受限恢复PARTIAL与入口已消费不变,未连接生产/ARM、读真实秘密/备份或重跑恢复,未提交推送部署归档。最新范围、依赖、量级和人工节点见implement顶部,不按测试数量宣称全量完成。",
"notes": "2026-10-11 接收08-29及08-30部署子任务的报销发票真实OCR余项(RC-OCR-1–4)与财务科目映射(RC-ACCOUNT),详见prd/implement顶部。保留真实腾讯单次样票、mock隔离审批及真实腾讯线上合成草稿三层证据;余项未执行,独立/真实业务等原义务开放。本轮仅文档移交,未提交、未归档、未调用云端或生产。\n2026-09-19 g20-full-r6:4437后端通过/1原跳过,372本地浏览器全过;PARTIAL仅dirty_candidate,独立PENDING/无部署,旧registry保护例外保留且无新增漂移。当前余额见implement顶部。\n2026-09-18 G19分层验收已批准并本地实施验证,74+7合成检查和2条冻结保护通过;公网12义务/完整门禁/WebKit/R1/独立及真实事实保持开放。\n2026-09-18 G18本地接续完成:33路由项、R108、OCR;并发4/4、认证/OCR18/18、unit131。原full-r2不回写;12live/2冻结验收G19本地实施验证完成,公网义务待兑现、WebKit/R1/独立/外部事实仍开放。新包local-coverage-g18-20260918,未提交推送部署。\n2026-09-18协调更新:09-06完成G16/G17本地批次,普通全量仍失败;14原失败观测及2串行缺失已定向重验,WebKit/冻结差旅/完整覆盖与可靠性、R1/独立及外部事实仍开放。借款T1–T4、报销反馈与ERP退役维持此前本地完成,非公网事实。最新入口见implement顶部。\n2026-09-17本轮W8/W6:22任务231条件逐项处置,154历史支持/56部分/11外部/4替代/4冻结/2条件未触发;82引用定位,接回AP/seed/预检b87cc55发布事实。B34损坏导入500与合成日志内容已复现并规划、未修;低年份分支分类完成,AP反向保持原语义,73项通过。K0/G8两主题完整性与运行时保持、独立PENDING;无新产品、远端或提交发布。\n连续推进至D10/D11,另9文件61通过;后续剩HR多格式策略、589错误遗留及各外部门禁,未宣称全部结束。\n2026-09-17后期连续执行:日期D5-D9、9原因及520本地验证完成;事件六缺口方案、22历史task231条件索引、借款T1a技术草案落盘;独立/恢复/自然业务/产品准入仍开放,无生产或提交发布。\n2026-09-17后期总表:官方current已切回本task,仅整理规划;K0本地30项/166键、B1/B2的450项与前端构建、D1–D4的192项/治理均有各自回执,未提交/发布,独立复核PENDING。按8条既有收口/治理+1条借款新功能共9条工作线编排;错误债务577指纹/598命中/58扫描分组,2026-10-05到期不变;日期新D5–D8只规划。K1/K2、历史缺件/完整计时、备份事件、自然付款/通知、22项历史任务验收映射均保留。借款planning,4阶段/8项AC待实现验收。原受限恢复PARTIAL与入口已消费不变,未连接生产/ARM、读真实秘密/备份或重跑恢复,未提交推送部署归档。最新范围、依赖、量级和人工节点见implement顶部,不按测试数量宣称全量完成。",
"meta": {
"g18Local": "verified_scoped_not_full_gate",
"independentReview": "PENDING",
@@ -1,3 +1,43 @@
## 2026-10-10 D:浏览器遗留失败排查设计(已批准实施、产品复核及本地补验完成,未提交)
沿当前借款任务 AC46–AC51,先补最小可观察实验,再定位并修复;不预判两项都是测试脚本问题。没有迁移、生产连接或新的业务规则。
最终产品行为:授权出借人列表使用可选 `eligible=related_authority`,在服务端分页和计数前排除未核实、未关联员工及财务本人;未传参数的原列表保持原行为,无效取值拒绝。授权弹窗每次打开重置出借人页码为 1。追回上传仍先关闭弹窗、再刷新;等待刷新后才关闭的试验已撤回,刷新期间禁用选项列为 P3,未实现。下面保留获批的实验合同,实际完成与证据以 implement 顶部为准。
提交前复核发现第三页诊断原先只验证选项,没有直接在第三页完成授权;追回旧策略诊断原先只记录选项,没有实际提交;资金回滚原例仅测晚到成功。按原 AC46/AC49/AC50 补实际第三页经办/本人确认、原生验证零 POST、精确 POST 的拒绝/pending/200 后刷新门闩,以及独立和关联两模式的追回金额、交易重复、审计失败回滚。补验不修改产品代码。
### D0:采证与定向运行
- 复用 `test-company-loan-db.cjs` 的隔离数据库、真实 Fastify/鉴权、合成 PDF 和真实浏览器。增加仅测试使用的可选 `RUN_ISOLATED_LOAN_BROWSER_DIAGNOSTIC=authorization-pagination|refund` 定向入口,严格校验值并要求已有 DB/browser 开关;输出明确标为定向范围,不能冒称完整回归。现有员工专项入口保留。
- 白名单事件时间线:设备、阶段、相对时间、角色别名、接口路径与必要页码/类别、请求开始/结束/取消、HTTP 状态、requestId、安全 reasonCode、按钮 pending/disabled、已显示页的合成 ID 和证据 ID。禁止 token/cookie、原始请求响应 payload、账号、真实原件及完整本地路径。表单采证仅记录有效性和字段是否存在,不保存账号值。
- 监听在动作之前安装,退出/失败时统一收拢 Promise、移除监听并清理测试自有资源。截图和限定环节的浏览器 trace 只用合成页面;保留失败证据,控制产物大小。原 r6/r7、full-loan-r1 证据不覆盖不删除。
- 同步区分浏览器原生验证无 POST、网络 pending/失败、服务端拒绝、提交成功但界面刷新失败;200 不能单独证明流程完成,DOM 关闭也不能单独证明写入成功。
### D1:授权翻页实验
受影响入口:`company-loan-employee-fixture.cjs` / `company-loan-employee-browser.cjs`,必要时 `LoanRelatedAuthorizations.tsx` 及 `/loan-lenders` 查询;保留每页 30 条、姓名/ID 排序与现有授权过滤。
1. 隔离 fixture 固定建立至少 65 名活跃、身份核实、员工关联且非财务本人的出借人,用稳定的合成姓名排序;通过实际列表 API 校验第二/第三页目标位置。位置不满足即 fixture 失败,不能跳过翻页或依赖其他测试多建的资料。
2. 电脑/手机执行第一页→第二页→第三页→第二页→第三页,明确断言各页真实选项和目标 ID;选择目标借款、登记授权、本人确认,验证授权关联与审计、零还款创建。旧 `open()` 没有重置 lenderPage 的行为仅作为待观察条件,不预先增加“每次回第一页”的产品要求。
3. 使用可控路由门闩而非任意 sleep:首次响应正常/快速、第二页响应延迟、无关同路径的其他页响应先到、5 秒有效期内返回缓存页、关闭重开。缓存场景以选项正确到达为结果,不强制等待不存在的新 GET;需要等待响应时精确识别目标 page/pageSize 与 actor,并继续检查渲染。
4. 控制实验让页码不变化或返回错误页,证明测试确实抓到停在旧页。还原旧等待方式时从保留版本/差异取证,若不能逐字还原则注明限制;人为注入的等待失败不是原事故根因证据。仅在同条件能复现历史失败并通过单一修复消除后,才归因。
### D2:追回到账实验
受影响入口:`company-loan-reconciliation-browser.cjs`、`company-loan-related-browser.cjs`、相应 HTTP fixture;必要时 `LoanBankReconciliation.tsx`、`paymentBatches.js` / `loanBankResults.js`。复用既有退款证据类别 `incoming_return`、相反账户方向、booked 的晚到成功父事件、金额/归属/声明/审计和银行交易唯一保护。
1. 为每台设备和审批模式各建独立场景,不用一次场景的成功代替另一模式。保留原始独立模式失败链,当前关联模式由不同身份的财务/公司代表分别办理。
2. 先产生已登记晚到成功和 40 元待追回,再分别上传合成退款证据、追回 15 和 25 元。上传响应绑定本批次 POST;保存返回的 evidenceId,等待该 ID 出现在实际 options,断言选中后才提交;用旧“最后一个 option”策略与延迟刷新作对照,不假定这一竞争就是旧事故原因。
3. 对每次登记精确观察 `/payment-batches/:id/loan-bank-results` POST;检查 returned event 的 parent/evidence/金额,再由另一人复核和财务入账。200 后分别等待对应列表刷新与状态显示,读取数据库确认待追回金额、交易占用、冻结原因与原借款/原终态/批次数;不把等待弹窗消失作为 HTTP 请求的替代品。
4. 小矩阵覆盖:提交时无有效证据、上传完成但列表读取延迟、登记 POST 延迟、200 后刷新延迟、有效可见的拒绝信息,以及实际事务内审计失败回滚。既有 HTTP 反例优先复用;补本次遗漏而非复制整套资金守卫。
5. 重试按既有请求键/银行交易身份合同检验:相同键同内容幂等,新的声明可按既有取代规则保留历史,但同一交易不能重复入账;部分追回不解除其他原因冻结,不能改成直接消除本金或生成退款付款批次。
### 修复、验证与兼容边界
采证先定位层次;纯等待/fixture 问题只改测试。已证实的界面或接口缺陷才做对应最小改动,不扩展通用组件、不统一放大超时、不加盲目重试。触及权限、状态消费、资金保护时补只读独立 review;若需要改既定合同或新增迁移,返回方案确认。
最终分别重验强制触发的两项场景(每设备连续 3 轮),再一次完整借款回归及受影响层检查;未改业务代码时无需为测试变化重复前端构建。提交/发布另行授权,测试修复不自动部署。原事故无法复现时可交付采证与稳定分支覆盖,但结论仍为原根因未定,不凭多轮绿灯结案。
## 2026-10-10 N5-C 出借人在线确认设计
1. 新增专用声明、借款快照及单次决定三张表,记录固定的公司/出借人/关联员工、财务发起人、请求键、日期、金额、原件 ID/摘要和版本。声明与范围在一个事务内完整成立,数据库约束保障范围一致及历史不可改删;失效只允许从有效到无效,保留原决定。
@@ -1,4 +1,155 @@
## 2026-10-10 N5-C 发布前入口调整(本地验证及只读复核完成,未提交、未发布)
## 2026-10-11 F:员工与公司组合唯一约束——实施(用户已批准;未提交)
- 迁移 `20261011010000_loan_lender_employee_unique`(迁移总数 83→84):`CREATE UNIQUE INDEX "loan_lenders_legal_entity_id_employee_id_key" ON loan_lenders(legal_entity_id, employee_id)`;Prisma `@@unique([legal_entity_id, employee_id])`。普通唯一约束,归档行计入、空 `employee_id` 互不冲突,与现行代码规则一致;不改数据、不回填。
- 冲突提示:新增 `isEmployeeLenderUniqueViolation`(同时识别 `meta.target` 与驱动适配器的约束字段/名称)。从员工创建路径先判它再判请求键重复;旧“确认出借人身份”入口在事务外映射。触到约束时返回原有 409 `loan.employee_lender_conflict`,不报 500,也不误报为 `loan.duplicate_registration`。原员工行锁与代码检查保持不变。
- 测试:原“直接写入模糊旧数据”的夹具改为断言数据库拒绝(P2002,且被新判定识别);新增两条无员工出借人可共存、归档关联继续占用组合(应用与数据库两层)。变异 11→12:`legacy_duplicate` 在临时删除索引的隔离库中运行,证明代码检查仍有效,运行后解除重复关联再重建索引;新增 `legacy_duplicate_db_backstop` 在索引存在时运行同一变异,整套夹具通过,证明数据库单独兜底也返回同一冲突提示。
- 发布要求:部署前在同一发布窗口重跑下节只读计数确认 0 组,部署按 1 个待执行迁移校验。
- 验证:员工专项 + 变异 12/12(`employee-only-mutations-r1.log`);完整回归 `full-regression-r1/r2.log` 各退出 0、84 迁移、12 阶段、去重 247 项(新增断言并入原检查项名,项数不变),自有资源清理 PASS;`loanLenderAcknowledgement` 单测 28/28;Prisma validate、前端构建通过。首次后台回归因相对日志路径在并行步骤切换目录后重定向失败、未实际运行,非测试失败。只读复核回执 `review-20261011-codex.json`:无阻断项,20 个内存探针通过;P2 迁移回退说明已补入 PRD F 节。证据在 `artifacts/qa-flow/loan-lender-employee-unique-20261011/`。
## 2026-10-11 F:员工与公司组合唯一约束——存量排查(只读)
用户授权本轮生产只读查询。经生产主机按部署项目标签定位唯一数据库容器,在 `BEGIN TRANSACTION READ ONLY … ROLLBACK` 内只返回计数,不输出 ID、姓名或账号,未写库:出借人共 1 条,关联员工 1 条,归档 0 条;同一公司与员工的重复组合 **0** 组。
代码现状:从员工创建、关联已有出借人、从员工资料刷新三条写入路径均先锁员工行,再用 `assertUniqueEmployeeLender` 拒绝同公司同员工的另一条出借人(`loan.employee_lender_conflict`);查询有意包含已归档出借人,即现行规则为“同一公司与员工最多一条,归档也计入”。代码中没有归档出借人的路径;员工停用/归档后原关联保留并继续占用。手工创建的无员工出借人 `employee_id` 为空,不要求唯一。
约束建议(待批准后另行实施):普通唯一约束 `(legal_entity_id, employee_id)`。PostgreSQL 唯一约束中空值互不冲突,无员工出借人不受影响;归档行计入,与现行代码一致,不采用只约束未归档行的部分索引(会放宽规则)。实施需新迁移,部署前须在同一发布窗口重跑本只读计数,确认仍为 0 组。
## 2026-10-10 E:追回证据刷新提示与员工资料误报(已实施、本地验证完成;未提交)
用户批准两项方案后实施,基线 `91fe31f0`;未提交、推送、部署或连接生产,无新迁移。资料拦截规则先由实施会话自查;2026-10-11 另一会话独立只读复核结论 `REVIEWED_NO_BLOCKER`,无阻断项,补跑 12 组服务层内存探针与电脑/手机 4 组组件浏览器检查,两轮 247 项回归与 11 项变异只核对日志未重跑;回执 `review-20261011.json` 中 7 个文件摘要与提交前工作区一致。复核限制:组件补验的 API、身份为合成替身;上线前打开的旧 v1 预览在上线后保存会被要求重新核对(有意拒绝旧版本);未跑完整发布门禁。
- E1 追回证据刷新(原 D2 的 P3):上传成功后对话框仍立即关闭;后台刷新完成前,对账弹窗内所有证据下拉框禁用、占位项显示“正在刷新证据列表…”、下方 `role=status` 提示“刚上传的证据稍后可选”,确认按钮同时禁用(禁用的必填下拉框不参与原生校验,单禁下拉会让空证据离开浏览器)。刷新结束(成功或失败)自动恢复。未采用 D 阶段撤回的“刷新完再关闭”。
- E1 测试:抽出 `uploadWithHeldRefresh`,用门闩挂起批次详情刷新,断言选项仅占位、下拉与确认均禁用、提示可见、零 POST;放行后下拉恢复、含刚上传证据 ID、确认可用。常规回归在第一笔追回(15.00)执行,诊断模式 `refund` 的首段由“复现旧占位项被原生校验拦截”改为同一新行为断言(旧行为已不可能出现,原证据保留在 D 阶段日志)。
- E2 员工资料误报:根因证实——成功登录会清零失败计数,Prisma `@updatedAt` 刷新员工 `updated_at`,而来源版本包含该时间。版本标签升为 `loan-employee-source-v2`,去掉 `updated_at`(不再查询),保留姓名、所属公司、状态、归档、开户行、银行机构(含启用/归档)与账号密文。`employee_no` 由数据库触发器禁止修改,确认页显示但无需入版本;尝试加入时测试被触发器拒绝,已撤回并在注释写明。
- E2 测试:员工预览后真实错误登录再正确登录,断言 `updated_at` 已变而来源版本不变、保存成功;姓名、开户行、所属公司、银行机构四项在保持 `updated_at` 的情况下逐一修改均 409 `loan.employee_source_changed` 且不建出借人。变异由 9 项增至 11 项:版本漏姓名、恢复修复前指纹(多处变异,查询与哈希同时加回 `updated_at`),后者以 `login_reported_as_source_change` 精确识别。
- 只读复核要点:保存写入的字段(姓名、银行机构、开户行、账号)全部在版本内;状态/归档另在读取时拒绝;确认页的公司名称来自公司表,不入员工版本,与修复前一致;已登记出借人与历史付款快照不受影响。
- 结果:员工专项 + 变异 11/11 通过;`refund-diagnostic.log` 电脑/手机五步诊断全部通过;常规完整回归 `full-regression-r1/r2.log` 各退出 0、83 个迁移、12 个阶段、去重 247 项(244 + 登录误报 1 + 电脑/手机刷新提示 2),自有资源清理 PASS;前端 typecheck/build、Prisma validate 通过。过程日志(缺环境变量、粗粒度变异判定、`employee_no` 触发器拒绝、改码前中止的回归)均保留。证据在 `artifacts/qa-flow/loan-employee-source-fingerprint-20261010/`。
- 剩余:员工与公司组合的数据库唯一约束仍待先查存量重复再单独规划;上线随下次发版,发版前跑完整门禁。
## 2026-10-10 D:本地提交收尾(用户已明确授权)
用户已明确授权本次本地提交,范围为两项遗留失败的测试修复与诊断补验、授权列表的分页前筛选及弹窗重开重置,以及本任务记录和既有 backend spec,共 14 个文件。沿用下节已完成的业务验证,提交前补做最终文档、治理、敏感内容和暂存差异检查;具体提交结果以 Git 回执为准。
本次授权不包含推送、部署、正式发布门禁或归档。本轮没有新迁移,产品改动可并入下次发版;父任务保持 `in_progress`,N5/N6 真实办理、暂停的登记默认值和 P3 上传后刷新体验继续保留。下节“未提交”等表述记录提交前验证时点。
## 2026-10-10 D:提交前复核与补验完成(Codex 会话,提交前验证时点记录)
在 Claude 实施结果上继续收尾。官方 current 与执行工作区一致,基线 `db919dc6`;未提交、推送、部署或连接生产。产品三文件只读交叉复核:原鉴权保留、可选筛选先于分页且列表/计数共用条件、原无筛选调用兼容、打开时重置页码;未发现产品阻断项。本会话仅补测试、task 文档和既有 backend spec 的稳定规则,新增测试属于主会话自查,不冒称另一个 reviewer 独立重跑。
复核核实原三轮日志各为 12 阶段、去重 224 项,但发现三个验收证据缺口:第三页诊断没有实际登记/本人确认;旧追回选择诊断只记录占位项,没有提交证明零 POST;已有审计失败针对晚到成功,不能替代追回回滚。按原 AC46/AC49/AC50 补齐,不放宽验收,也不改产品范围。原记录在下节保留实施时点。
- D1:在明确 65 名可选出借人之外补本人/未核实排除样本;精确比较各页,真实第三页登记、本人确认、范围/审计及零还款断言。`review-pagination-r2/r3/r4.log` 连续三轮电脑/手机全部通过,包含实际缓存命中和不变页面反例。`review-pagination-r1.log` 因新增诊断误点公司代表页面不存在的财务标签而失败,已删该多余动作,日志和 finance trace 保留;该次代表 context 没有 trace,不宣称它已采到代表页面失败。
- D2:改用可释放的刷新/请求门闩,实际填旧占位项并点击、验证只有证据选择无效且零 POST;分别核对实际 409、pending、200 后刷新及绑定的 event/parent/evidence。电脑/手机各五类诊断全部通过,诊断 context 保留合成 trace。资金补验在独立和关联模式共用同一断言,各 10 组通过,覆盖方向/类别/作废/剩余额度、请求键、银行交易、复核拒绝、真实 `LOAN_RETURN_BOOKED` 审计失败和其他冻结原因保留。AC50 的本轮证据缺口已关闭;不是依赖晚到成功的回滚例。
- `review-refund-full-r1.log` 在取消后尝试作废未消费材料,被已有 `loan.payment_not_ready` 正确拒绝,未走到资金断言;改为取消前经实际接口作废,未据此改产品的作废规则。
- `review-refund-full-r2.log` 的独立/关联各 10 组金额保护已通过,电脑旧策略的原生验证明确零 POST。但错误方向探针得到 200,检查发现旧追回 UI 合成素材的公司账号与出借人账号相同,交换后没有改变输入。仅将该 UI 素材的双方账号分开,不降低 409 断言;失败日志及合成 trace 保留。未把 r2 报为整轮通过。
- 最终 `review-refund-full-r3.log`:退出 0,83 个迁移、12 个阶段全部通过,去重 244 个检查项(比原 224 项多独立/关联各 10 组);电脑/手机独立及关联的追回链、员工授权、借入识别及在线声明等完整回归通过,自有资源清理 PASS。两项原根因待查已按本轮复现及反例关闭,P3 上传后刷新体验保留。原三轮成功链与本轮加强矩阵分开报告,不声称新增的五类矩阵每端各跑了三遍。
- 已通过:前端 typecheck/build、Prisma validate、错误理由/UI 合同、诊断开关三个拒绝反例、8 个 JS/CJS 语法检查、task/process/docs/governance/sensitive。docs 保留原有 22 份未登记文件的 1 条告警;缺 JSONL 在 inline 模式按官方 task validate 允许跳过。backend spec 仅同步列表筛选、渲染等待和证据 ID 选择合同,不复制运行日志。
- 证据仍在 `artifacts/qa-flow/loan-browser-diagnostics-20261010/`,本次新增 `review-*` 回执;`review-source.json` 中 10 个受影响产品/测试源码摘要与最终运行后文件一致;`review-result.json` 为 PASS。摘要仅覆盖列出的源码,不代表整个工作区、候选门禁或生产发布,`normalReleaseEligible=false`。
本轮 AC46–AC51 本地范围已完成。验证完成时尚无提交授权;随后用户明确批准本地提交,见顶部收尾记录。产品有改动,正式发版前对最终提交跑完整候选门禁,再另获推送/部署批准;本轮没有迁移,可并入下次发版。父任务仍 `in_progress`,N5/N6 真实办理及暂停的登记默认值继续开放。
实际补验命令(运行时另提供已准备的 `PAYMENT_ORDER_PYTHON_BIN` 锁定 BIFF8 环境,不记录真实路径):
```sh
RUN_ISOLATED_LOAN_DB_TEST=1 RUN_ISOLATED_LOAN_BROWSER=1 RUN_ISOLATED_LOAN_BROWSER_DIAGNOSTIC=authorization-pagination node scripts/qa/test-company-loan-db.cjs
RUN_ISOLATED_LOAN_DB_TEST=1 RUN_ISOLATED_LOAN_BROWSER=1 RUN_ISOLATED_LOAN_BROWSER_DIAGNOSTIC=refund node scripts/qa/test-company-loan-db.cjs
```
相关检查使用 `npm run typecheck`、`npm run build`、`npm run prisma:validate`、受影响脚本 `node --check`,以及官方 task validate、`check-error-reason-contract.mjs`、`check-ui-design-rules.mjs`、`check-process-contract.mjs --baseline --json`、`audit-docs.mjs --check-registry`、`check-trellis-governance.mjs`、`check-sensitive-content.mjs` 和 `git diff --check`;各回执以 `review-*` 保留。
## 2026-10-10 D:两项遗留失败排查结果(Claude 会话实施时点记录,提交前复核见上节)
用户要求由 Claude 会话按本节规划实施,附加条件为:失败时开启 trace 采证、以半天为限、不影响 N5 办理。证据目录 `artifacts/qa-flow/loan-browser-diagnostics-20261010/`,全部使用合成数据和隔离环境。
### D1 员工授权翻页超时:根因已证实,已修复
- 诊断入口:`RUN_ISOLATED_LOAN_BROWSER_DIAGNOSTIC=authorization-pagination`,脚本 `scripts/qa/company-loan-pagination-diagnostic.cjs`。造 65 名可授权出借人,目标位于第 3 页;在电脑和手机上,以 CPU 降速 20 倍模拟负载。
- 根因:原测试循环只等网络响应,不等页面渲染,就检查目标选项。渲染滞后时(例如过期数据重新获取导致第 1 页被请求两次,请求序列为 1、1、2、3),循环会再点一次“下一页”,此时按钮已禁用,于是挂到 30 秒超时,且来不及截图。这与 r6、r7 的失败形态一致。
- 复现情况:降速下原循环在 `pagination-r4/r5-baseline`、`pagination-fixed-r1/r2` 中超时,按电脑/手机分别计,在修复后 3 轮的 6 次中失败 3 次;新循环 6/6 通过,包括往回翻页命中缓存的情况。反例“页面数据不变”时,新的等待 6/6 按要求失败。
- 诊断中顺带发现的两个产品问题,均已修复:
- 对话框在前端过滤出借人,可能出现空白页。现由服务端 `GET /loan-lenders?eligible=related_authority` 只返回已核实、已关联员工、且非本人的出借人;其他取值返回 400。
- 对话框重新打开后停在上次的页码。现在 `open()` 会重置到第 1 页,6/6 验证通过。
- 测试修复:`company-loan-employee-browser.cjs` 改为等选项列表加载出另一页后再判断;员工场景浏览器 3/3 通过(`employee-fixed-r1..r3`)。
### D2 登记追回到账:根因已证实(测试选证据方式),产品改动已撤回
- 诊断入口:`RUN_ISOLATED_LOAN_BROWSER_DIAGNOSTIC=refund`,代码在 `company-loan-reconciliation-browser.cjs` 第一次追回处。上传后把批次详情的刷新延迟 4 秒,再用原来的“取最后一个选项”方式。
- 根因:上传成功后,对话框先关闭,再在后台刷新数据。原测试立即打开登记对话框时,证据下拉只有占位项,选中占位项后必填校验拦下提交:不发 POST、提示框为空、对话框不关。这与 full-loan-r1 的空提示一致。修复前诊断(`refund-before-fix.log`)在电脑和手机上都选中了占位项。
- 测试修复:此前已改为按上传返回的证据 ID 精确选择,保持不变。
- 产品修复试验后撤回:曾试验“刷新完成后再关闭对话框”。`refund-after-fix-r1` 证明这样能选中正确证据,但 r2、r3 在其他对账步骤上,对话框关闭超过了现有测试的 5 秒期限,说明负载下财务端也会明显变慢,因此撤回,保持原行为。“刚上传的证据要等刷新完才能选”记为 P3 体验问题,后续可以改为刷新期间禁用下拉,不靠延迟关闭来解决。
- 修复前和修复后的追回链(40 → 25 → 0)都在完整回归中通过。
### 验证
- Claude 转交的撤回后版本,完整借款回归共 3 轮通过,每轮去重 224 项、12 个阶段:`refund-before-fix.log`、`final-regression-r2.log`、`final-regression-r3.log`。Codex 独立核对了统计及清理 PASS;“before-fix 的代码与撤回后相同”沿用实施会话说明,旧日志本身没有源码摘要,不据此冒称独立证明了整棵源码一致。本次最终源码补验见顶部。
- 前端类型检查与构建通过。
- 原失败日志全部保留。
### 结论与边界
- D1:根因已证实并修复,修复包括测试等待和两个产品小问题。
- D2:根因已证实,是测试选择方式,已修复;产品体验问题记为 P3,未修。
- 产品改动只有两处:服务端出借人列表的可选过滤,以及授权对话框的页码重置与改用过滤。不涉及权限模型、状态机或迁移。
- 未提交、未推送、未部署。
## 2026-10-10 D:两项遗留失败排查规划(规划时点,结果见上节)
用户本轮要求规划员工授权翻页超时与登记追回到账。沿当前 task 增补 PRD AC46–AC51 和 design 的最小实验,分 D1/D2 独立报告,优先 D1。起点 HEAD=`db919dc6`、工作区干净;此阶段未改测试/业务代码、未运行浏览器/数据库实验、未复现或修复任何根因,不将父任务进行中状态解释为新范围的实施批准。
- [x] D0-P:核对旧 r6/r7 response 超时、full-loan-r1 空提示,检查现有脚本/组件/路由/缓存和旧版本,明确证据缺口。事实及 file:line 集中记录在 PRD;相关历史对话已在本会话和 task,不额外检索会话原文。
- [x] D0-P:形成范围、实验、受影响文件和可观察验收;没有新增产品行为待决问题,方案交用户审阅。复用当前借款 task,不新建目录或更改官方 current。
- 本轮规划检查:完成新增 PRD 段落的收敛检查,无临时问答或未决产品选择;task 上下文、流程基线、Trellis 治理、敏感内容及 `git diff --check` 均通过。文档登记 0 错误,保留既有 22 份未登记文档的 1 条告警;没有业务测试运行回执,尚未提交。
- [x] D0-E:已读受影响层规范并按合成/隔离环境运行,磁盘补验前约 7.9 GiB;定向开关约束和白名单采证已补,具体分支回执见顶部。
- [x] D1-E:固定 65 名可选出借人及第三页目标;电脑/手机实际分页、往返缓存、重开和第三页授权/本人确认已连续三轮通过,旧/新等待及不变页反例保留。原失败的请求时间线未记录,不声称能逐帧还原原 r6/r7;本轮复现支持等待网络未等待渲染的归因。
- [x] D2-E:独立与关联电脑/手机成功链通过,精确 ID 和原生验证/无 POST/pending/409/200 后刷新矩阵已补;20 组资金反例含追回审计失败真实回滚,详见顶部最终 r3。
- [x] D3-E:产品修复限已证实的筛选/重开问题;旧三轮成功链保留,第三页加强场景再连续三轮,新增追回诊断矩阵每端一轮,最终完整回归去重 244 项通过。产品由 Codex 主会话只读交叉复核,新增测试主会话自查;没有虚报独立 reviewer 重跑或候选门禁。
- [x] D4-E:当前 task 与既有 backend spec 已更新;两项遗留根因待查关闭、P3 体验问题保留,N5/N6 和暂停默认值不混入结案。提交/推送/生产操作仍须另获明确批准。
拟用证据目录:`artifacts/qa-flow/loan-browser-diagnostics-20261010/`,分 authorization-pagination/refund 子范围,历史证据保留。当前只读磁盘余量约 8.9 GiB;先跑定向、只留必要 trace/截图,完整回归前再核对余量,不自动删旧产物。
执行命令计划(本轮未运行;已有 BIFF8 Python 环境按受控本地配置提供):
```sh
# 现有员工专项:新增强制分页场景后使用。
RUN_ISOLATED_LOAN_DB_TEST=1 RUN_ISOLATED_LOAN_EMPLOYEE_ONLY=1 RUN_ISOLATED_LOAN_BROWSER=1 node scripts/qa/test-company-loan-db.cjs
# 以下 diagnostic 开关为计划新增,目前尚不可用。
RUN_ISOLATED_LOAN_DB_TEST=1 RUN_ISOLATED_LOAN_BROWSER=1 RUN_ISOLATED_LOAN_BROWSER_DIAGNOSTIC=authorization-pagination node scripts/qa/test-company-loan-db.cjs
RUN_ISOLATED_LOAN_DB_TEST=1 RUN_ISOLATED_LOAN_BROWSER=1 RUN_ISOLATED_LOAN_BROWSER_DIAGNOSTIC=refund node scripts/qa/test-company-loan-db.cjs
# 最终一次完整借款回归:移除 ONLY/DIAGNOSTIC 开关。
RUN_ISOLATED_LOAN_DB_TEST=1 RUN_ISOLATED_LOAN_BROWSER=1 node scripts/qa/test-company-loan-db.cjs
```
业务代码变化才按实际影响运行前端 typecheck/build、Prisma validate、后端语法/相关测试及错误理由/UI 检查;文档与流程检查按既有 task/process/docs/governance/sensitive/diff 合同执行。上述任务运行不包含真实账号、真实银行资料、云 OCR、生产连接或真实付款,不影响已发布 `4a5cd8f2` 的线上办理。
## 2026-10-10 N5-C 已发布至生产(4a5cd8f2,83/83 迁移)
出借人在线确认及站内通知直达已随 `4a5cd8f2b3e04da24551ddf1ee99b4167aa76710` 发布。本次按发布会话转交结果补记,只核对本地 Git、已有候选验证和部署日志,没有重新连接生产或办理真实借款。父任务继续 `in_progress`;下方各轮“未提交、未发布、待门禁”等表述保留其历史时点,以本节为最新发布状态。
### 本地核实的发布证据
- 补记前工作区干净,HEAD 与本地 `origin/main` 均为上述提交;提交说明引用本任务。
- `artifacts/qa-flow/release-4a5cd8f2-20261010/verify-candidate.log`:最终候选验证 `status=PASS`、`localChecksPassed=true`、`deploymentEligible=true`、`issues=[]`、退出 0;服务端全量 4670 项通过、1 项跳过,浏览器 372 项通过。`artifacts/release-gate/release-4a5cd8f2-20261010/release-result.json` 同样为 PASS、可部署。本节转录既有最终提交回执,没有重跑门禁,不以入口调整前的 216 项代替该候选验证。
- 同目录 `deploy.log`:部署前聚合体检 `PASS=13 FAIL=0`,来源提交一致,`branch=main`、`published=true`、`remoteVerified=true`;待应用迁移仅为 `20261010010000_loan_lender_acknowledgements`。应用构建切换、external deps smoke、runtime smoke 及公网前端资源扫描通过,扫描 `buildMarker=PASS`、`findings=0`。最终“应用发布与备份合同刷新完整通过”、deploy 模式 PASS,退出 0。
- `artifacts/production-preflight-20261010-172051/{pre,post}/`:部署前旧应用 Prisma 为 82 个迁移、结构已最新,候选差集为上述 1 个;部署后 Prisma 为 `83 migrations found`、`Database schema is up to date`,复核 `pending=none`。前后 `rollbackWindow=OPEN`,仅表示当次检查时点,不代表已实际演练回退;旧应用不理解在线替代材料的兼容边界仍按设计保留。
- 部署日志明确跳过登录后的 live smoke,原因为未提供完整凭据。OCR 等 external deps smoke 只验证配置契约,不代表真实识别或真实借还款链路已验收。
### 发布会话转交结果与证据边界
- 用户回报已推送部署、SSH 未中断;公网 `/ready` 为 `ok:true`、版本 `4a5cd8f2b3e0`,`/health` 为 200;首页、`/login`、`/mobile`、电脑/手机公司借款页、`/notifications` 均为 200,前端主脚本加载正常。新出借人确认接口未登录返回 401。上述公网路由明细沿用发布会话回报,本次未重新请求公网;可达和未登录拦截不等于登录后的本人确认流程通过。
- 快照:按用户转交记录,此次未做腾讯云手工快照;本会话此前的建议为迁移前做快照,未在本会话执行或核实云侧快照。该记录不将跳过快照的决定归为本会话授权。
### 真实办理交接与未决项
1. 财务在“公司借款 → 出借人确认”对两笔借款发起声明,系统仅给对应出借人生成站内通知。
2. 马经理用本人账号点击通知,逐项核对原件、金额、日期后确认;有误则退回财务。出借人确认与公司代表身份分别办理。
3. 财务取得并上传代账“其他应付款—马经理”的明细账/余额核对材料,与代账核对后填写引用并确认期初余额。未还本金少于原本金时,系统外已还本金依据仍必需。用户表示当前待取得材料为代账明细账;本次未独立读取真实附件或余额,不据此断言其他材料均已齐全。
4. 财务登记公司代表授权及范围,由马经理以公司代表身份确认。完成 N5 后,再按已确定的真实金额、分配和日期办理 N6 首笔还款。
登录后的上述流程尚未在生产实际验收,N5/N6 及相关真实银行验收继续开放。追回到账与员工授权翻页两处历史失败根因仍未定,保留原失败证据和另行确定性排查安排;本次未创建或实施新的排查任务。借入登记默认值继续只记录、暂不修改。
本次仅补发布记录及手册状态,未修改业务代码、重跑业务测试或再次发布;文档补记尚未提交。任务上下文、流程基线、Trellis 治理、敏感内容及差异检查通过;文档登记 0 错误,保留既有 22 份未登记文档的 1 条告警。回执见同一发布证据目录的 `*-record.log`。本次没有新增可复用工程合同,不扩写 spec。
## 2026-10-10 N5-C 发布前入口调整(历史本地验收记录,后续已发布见上节)
用户复核提出普通员工常驻入口无必要,并给出“只对已关联出借人的员工显示”或“发起后通知直达”两种方案、委托本会话取舍。本次选择第二种,沿已批准的本人确认功能收敛入口,复用现有 OA 站内通知。范围包含通知创建和声明定位,不能只删导航;不新增通用待办、外部消息或迁移。
@@ -89,7 +240,7 @@ cd ../client && npm run build
| 2:技术排查,可与 N5 并行 | 开发确定性复现授权对话框翻页超时 | 复用本任务,先形成局部排查方案。隔离环境构造超过 30 个符合列表条件的出借人,明确目标位于第二页或以后;电脑/手机均断言实际翻页、请求和选项渲染完成,再选择目标并完成授权。留存失败响应、时序及脱敏截图,区分等待竞争、服务端响应和界面问题。仅修改等待方式或未触发该分支的通过不能结案;若实际办理遇到,提升优先级。 |
| 3:业务主线 N6 | 财务申请、网银提交及代账交接;马经理按公司代表身份确认 | N5 完成后,按真实付款安排明确本次金额、逐借款分配及日期;申请 → 确认 → 导出 → 实际网银提交并登记提交详情 → 正式付款回单核验/必要人工确认 → 核销 → 代账交接。以本次已付本金和可定位的银行交易为验收依据,不从余额推定全额还款,不为测试制造付款。扫描付款回单按付款路径的人工依据与确认规则办理。 |
| 4:技术优先项 | 开发评估将回单重检的文件读取/PDF 解析移到全局锁外 | 沿 N3 已记录的 P3 优先顺序设计;锁内保留原件摘要、版本、权限及全部状态重验。用重检与认领/人工核验/执行并发、附件作废/换版等反例证明不使用过期结果,再独立只读复核。工作线程限时 5 秒不是整个持锁时长的上界;不能只凭耗时变短判定修复完成。 |
| 5:技术近期项 | 开发验证员工来源版本的误报,再评估数据库唯一约束 | 来源指纹包含员工 `updated_at`,登录会更新认证字段;先验证“财务预览后员工登录再保存”的冲突,不泛称每次登录必然提示资料变化。修正时须同时保留姓名、公司、状态和银行资料真实变更的拦截。唯一性当前由代码检查与员工行锁保护、缺少员工和公司组合的数据库唯一约束;新增约束前明确归档/空员工语义并检查存量,不直接加迁移或清理真实记录。 |
| 5:技术近期项 | 开发验证员工来源版本的误报,再评估数据库唯一约束 | (E2 已修复误报并经 2026-10-11 独立复核;唯一约束仍待办)来源指纹包含员工 `updated_at`,登录会更新认证字段;先验证“财务预览后员工登录再保存”的冲突,不泛称每次登录必然提示资料变化。修正时须同时保留姓名、公司、状态和银行资料真实变更的拦截。唯一性当前由代码检查与员工行锁保护、缺少员工和公司组合的数据库唯一约束;新增约束前明确归档/空员工语义并检查存量,不直接加迁移或清理真实记录。 |
| 6:线上验收补证 | 运维准备账号与安全凭据来源,财务/马经理补借款角色验收 | 现有通用 live smoke 需要管理员、员工两类账号;凭据通过受控本地环境或既有秘密配置提供,不写聊天和任务记录。先确认检查范围与授权,再执行登录后只读页面/权限检查。通用 smoke 不覆盖本次借款登记及公司代表确认,后者随 N5/N6 留证。真实云 OCR 单列授权和验收;带 OCR 的准备脚本会上传材料,不能当纯只读 smoke 执行。 |
| 7:较低优先级改进 | 开发与财务明确旧记录重检、记账日期、字段提示规则 | 旧记录缺少原云端限制字段,重检不能重建未保存证据,先明确策略再实施;记账日期下限先区分正常还款与历史补记,不直接统一硬卡审批日;第五轮 N1 可统一 OCR 与本地无效编号的提示,但保持整段校验、多页关联和冲突拦截不变。 |
| 8:文档收尾 | 开发提交发布记录及本次清单 | 内容与检查已具备,可在收到明确本地提交授权后单独提交;提交说明引用当前 task。此次梳理不自动提交,也不再次部署。 |
@@ -1,5 +1,69 @@
# 公司借款台账与本金归还
## 2026-10-11 F:员工与公司组合数据库唯一约束(已实施、本地验证与只读复核完成;未提交)
生产只读排查重复组合为 0 组后,用户批准把“同一公司与员工最多一个出借人(归档计入)”落到数据库。结果见 implement 顶部 F 节。
- [x] AC56:数据库拒绝同一公司与员工的第二个出借人,含归档行;空员工的手工出借人可多条共存。
- [x] AC57:应用触到约束时返回原有“出借人冲突”提示,不报 500、不误报请求重复;代码检查与员工行锁保持。
- [x] AC58:借款完整回归通过,并经独立只读复核(涉及迁移)。两轮完整回归各 84 迁移、12 阶段、去重 247 项通过;另一会话只读复核无阻断项、20 个内存探针通过,唯一 P2(缺本迁移回退说明)已在下方补齐。
- [ ] AC59:发布窗口重跑存量重复计数为 0,并按 1 个待执行迁移部署。
### 迁移回退说明(`20261011010000_loan_lender_employee_unique`)
- **建索引失败**:迁移只有一条 `CREATE UNIQUE INDEX`,单语句原子执行;若发布窗口内出现重复组合导致失败,索引不会部分建立,Prisma 将该迁移记为失败并阻止后续迁移,应用不切换到新版本。处理方式是停止本次发布,回到只读计数重新排查;不在发布中手工删改出借人数据,不改写已应用的迁移历史。失败迁移记录的处理(如标记回滚后重试)须另行批准后按运维手册执行。
- **应用回退**:回退到上一版应用时**保留**该索引,不删除。上一版代码在员工行锁内已拒绝同一组合,正常路径不会触到约束;只有极端情况越过旧检查时,旧版创建路径会把约束冲突报为“请求重复”(409),旧身份确认入口会报 500,两者都不会写入重复数据。
- **撤销约束**:如确需取消该约束,另起新迁移 `DROP INDEX` 并单独批准,不回退或删除本迁移文件。
## 2026-10-10 E:追回证据刷新提示与员工资料误报(已实施、本地验证及独立只读复核完成;未提交)
用户批准两项小问题按方案修复,复用本任务,不含数据库唯一约束(需先查存量重复、单独规划)。结果见 implement 顶部 E 节。
- [x] AC52:上传银行证据后对话框立即关闭;刷新完成前证据下拉框与确认按钮均不可用并有可见提示,不发出提交;刷新后可选中刚上传的证据。电脑/手机浏览器验证。
- [x] AC53:财务预览员工资料后员工登录(含失败再成功),保存不再误报“员工资料已变化”。
- [x] AC54:姓名、所属公司、开户行、银行机构、账号的真实变更,以及状态/归档,仍拦截保存;反向变异能被测试发现。
- [x] AC55:资料拦截规则改动经独立只读复核(2026-10-11 `REVIEWED_NO_BLOCKER`,回执 `review-20261011.json`)。
## 2026-10-10 D:授权翻页与追回到账排查(已本地提交 `91fe31f0`,未发布)
用户要求规划“员工授权翻页超时”和“登记追回到账”两项遗留问题。复用本任务作为执行合同,分成 D1/D2 两个独立验收阶段,不把当前父任务 `in_progress` 当作本轮实施批准。规划基线为 `db919dc6`,开始时工作区干净;线上业务基线仍为 `4a5cd8f2`。本阶段目标是查清失败发生在哪一层,并形成能稳定触发相关分支的回归,避免漏走分支的通过掩盖问题。
### 规划时点的历史证据及缺口(后续结果见 implement 顶部)
| 阶段 | 已确认事实及来源 | 尚未确认 |
| --- | --- | --- |
| D1:公司代表授权的出借人翻页 | 原 `loan-incoming-prefill-20261009/regression-r6.log`、`regression-r7.log` 为 `page.waitForResponse` 30 秒超时,旧任务记录标注手机端;没有对应失败截图。当前 `scripts/qa/company-loan-employee-browser.cjs:160` 仅在目标不在当前选项时翻页,`:163` 等待谓词不限定页码。服务端 `server/src/routes/companyLoans.js:245` 按姓名、ID 排序;`client/src/api/companyLoans.ts:52` 每页 30 条,`client/src/query/queryClient.ts:6` 查询缓存有效期 5 秒。 | 点击是否真正发生、等待是否遗漏/误捕响应、目标实际位于哪页、是否走缓存,以及是否为产品分页问题。 |
| D2:登记追回到账 | `loan-lender-ack-20261010/full-loan-r1.log` 只有 `synthetic UI 登记追回到账:` 空提示;没有可据以归因的请求结果。旧脚本按最后一个 option 选证据;当前 `scripts/qa/company-loan-reconciliation-browser.cjs:45`–`:59` 已改为捕获上传 evidenceId 并按 ID 选择,`:32` 增加表单有效性采证,但这不能证明旧失败根因。提交在 `client/src/components/common/LoanBankReconciliation.tsx:59`,退款入账在 `server/src/services/loanBankResults.js:172`。 | 当时选中哪份证据、是否发出 POST、服务端是否拒绝/挂起、是否已经成功但刷新未完成;原日志未记录设备和第几次追回。 |
### 范围与顺序
1. D0:核对历史日志、脚本版本及最小采证方案;只使用合成资料和隔离 PostgreSQL/浏览器环境。
2. D1 优先:固定至少 65 名满足授权选择条件的出借人,按接口实际排序校验目标在第二/第三页,并强制浏览器走到第三页、返回上一页后再进入。区分首次加载、快速响应、可控延迟、5 秒内命中缓存、弹窗关闭重开及无关同路径响应。
3. D2 随后:保留旧失败所属的独立复核模式,并补当前关联方模式对照。固定晚到成功产生待追回 40 元、两次追回 15/25 元的现有合成路径;分别观察上传、证据选项刷新、登记、复核、入账和余额/冻结刷新,不把“弹窗关闭”当作唯一成功依据。
4. 先用采证区分测试等待、测试资料、界面状态和服务端规则,再做对应最小修复。如果仅是测试问题,只修改测试;若证实产品问题,修复限定在该分页/登记路径。材料、权限、审批、币种、重复入账和金额边界保持现有合同。
不包含:生产读写/真实付款、云 OCR、数据库迁移、通用对话框重构、其他 P3、暂停的登记默认值、提交/推送/部署、清理历史证据。若结论要求改变权限、状态机合同、核算规则或迁移,先回到方案确认。相关现有权限/资金守卫的缺陷修复仍须只读独立复核。
### 验收标准
- [x] AC46(翻页分支真实触发):电脑、手机均通过实际服务证明目标不在第一页,明确收到/命中第二、第三页数据并显示对应选项;往返及关闭重开不混入其他页或他人的选项,财务登记后由正确公司代表确认,未创建还款。
- [x] AC47(翻页归因):采证能区分没有点击、没有请求/合法缓存命中、请求失败/等待错误、响应成功但界面错误。可还原的旧等待方式与当前/修复方式做同条件对照;故意让页码不变或返回错误页时,测试须失败。缓存命中不能被要求“必须再发一次请求”。
- [x] AC48(追回成功链):独立复核及关联方模式的电脑/手机均实际完成上传指定证据、登记、不同身份复核、财务入账;待追回从 40 到 25 到 0,仅全额完成后关闭对应追回冻结,不关闭其他原因;原终态、本金、付款批次数及历史事实按既有规则保持。
- [x] AC49(追回失败可定位):无有效选择/原生表单阻止提交、可控请求延迟、服务端拒绝、200 后刷新延迟分别有可区分证据;有 HTTP 结果则绑定准确批次、方法、请求 ID 与安全错误理由,无提交则明确记录无 POST,不再仅留空 alert。
- [x] AC50(金额与重复保护):错误方向、作废/错误类别证据、超出剩余待追回金额受阻;相同请求键重试不重复创建,同一银行交易不能重复入账,已入账重放不重复计算。拒绝或事务失败不提前减待追回、关闭冻结、改本金或生成付款批次。独立及关联两模式共 20 组补验通过,回执和边界见 implement 顶部。
- [x] AC51(验证与结案):两项定向场景各完成电脑/手机连续 3 轮(每轮新隔离环境,含明确缓存分支),随后最终版本一次完整借款回归;必要的类型/构建/后端与合同检查、按实际影响的只读复核通过。结论逐项注明“根因已证实并修复”“已补强但原事故未定”或“待进一步排查”,只有能解释历史失败并经反例对照验证的项才关闭原根因待查。
技术未知在 design 的实验清单中处理,没有需要先决定的新增产品行为。方案已在 Claude 会话获准实施,授权来源见 implement 顶部;本会话按同一验收范围补齐复核发现的测试证据。提交、推送、部署仍须明确授权。
## 2026-10-10 N5-C 已发布(4a5cd8f2,真实办理待验收)
出借人在线确认及站内通知直达已随 `4a5cd8f2b3e04da24551ddf1ee99b4167aa76710` 推送部署。补记时本地 HEAD 与 `origin/main` 一致、工作区干净;已有最终候选验证 PASS,部署前 13/13,唯一待应用迁移为 `20261010010000_loan_lender_acknowledgements`,部署后 83/83、无待应用迁移、合同刷新通过。公网健康、页面与资源正常及未登录接口 401 沿用发布会话转交结果;本轮只读核对本地日志,没有重新访问生产。
用户回报此次未做手工快照;本会话此前建议做快照,决策来源在 implement 顶部区分记录。部署日志跳过登录后的 live smoke,真实发起声明、本人确认、余额确认、公司代表授权及首笔还款尚未验收。财务/出借人按现有手册办理,代账余额核对材料及必要的系统外已还本金依据继续必需。
整体任务保持 `in_progress`,N5/N6 和暂停的登记默认值待办继续开放。两项浏览器遗留失败的最新诊断与本地修复见 D 节;本节及以下“根因待查、未提交、未发布、未重跑最终门禁”等表述保留各轮历史时点,以 implement 顶部最新证据为准。文档补记不修改线上版本或工程合同。
## 2026-10-10 N5-C 出借人在线确认借款(用户已批准实施)
用户批准将“借款确认书”增加为 OA 内可追溯的出借人声明,范围仅限本功能。此前“先记录、不修改”对本功能的限制由本次批准取代;登记默认值调整仍只记录、不实施。线上记录是本系统接受的借款确认依据,不宣称仅凭账号操作留痕即当然取得与纸质签字相同的法律效力。
@@ -1,5 +1,9 @@
# 报销详情修复部署与公网验证
## 2026-10-11 收口归档
本子任务停留在规划阶段,原冻结、审批与部署步骤未在本任务下执行。修复 `46bfa7e0`/`174cb7ac` 及非参与人只读投影 `e8d6766` 已包含于候选 `59dc5f6`,后续“费用政策与票据门禁部署”任务记载其为当时生产运行版本,即由该发布覆盖;现均包含于当前生产 `4a5cd8f2`。原规划记录保留。原未勾选清单为当时规划记录,保留原样、不补勾。经用户 2026-10-11 明确授权归档。
## Goal
在 Task A 与非参与人只读投影修复都完成后,冻结新的已发布候选,执行受控生产部署、显式迁移状态校验、角色化公网检查和固定回滚验证。
@@ -3,7 +3,7 @@
"name": "reimbursement-detail-material-preview-deploy",
"title": "报销详情修复部署与公网验证",
"description": "在报销详情两轮权限修复完成后,以新的已发布候选执行受控部署、迁移状态校验、固定回滚和公网角色验证。",
"status": "planning",
"status": "completed",
"dev_type": null,
"scope": "production deployment preflight; migration status; public role and material-preview verification; fixed rollback",
"package": null,
@@ -11,7 +11,7 @@
"creator": "shunli",
"assignee": "shunli",
"createdAt": "2026-08-28",
"completedAt": null,
"completedAt": "2026-10-11",
"branch": null,
"base_branch": "main",
"worktree_path": null,
@@ -23,4 +23,4 @@
"relatedFiles": [],
"notes": "旧候选 8c28e15 因后续文档提交导致 HEAD 漂移而失效。以本次 PRD 修订提交完成后的最终干净 HEAD 冻结新候选,并在外部审批记录中写死 SHA;冻结后禁止任何仓库变化。push 与生产部署是两个独立授权门,当前均未批准。",
"meta": {}
}
}
@@ -1,5 +1,9 @@
# 报销详情权限与材料预览缺陷修复
## 2026-10-11 收口归档
撤回展示与 EXIF 尺寸修复 `46bfa7e0`(回填 `174cb7ac`)随候选 `59dc5f6` 上线,现包含于当前生产 `4a5cd8f2`;部署子任务由该发布覆盖。`BX-20260828-0017` 首图未显示在只读复现中未重现,按原记录保留。原未勾选清单为当时规划记录,保留原样、不补勾。经用户 2026-10-11 明确授权归档。
## Goal
修复报销只读查看者误看到“撤回报销”操作,以及带 EXIF 方向信息的图片进入材料合并预览后页面尺寸错误的问题;同时对 `BX-20260828-0017` 的首张图片未正常显示现象做受控、脱敏、只读诊断,让财务只看到职责内动作,并让图片材料在合并 PDF 中保持正确方向和比例。
@@ -3,7 +3,7 @@
"name": "reimbursement-detail-material-preview-fixes",
"title": "报销详情权限与材料预览缺陷修复",
"description": "修复财务只读查看报销时误显示撤回操作、EXIF 图片材料合并尺寸错误,并对 BX-20260828-0017 首图异常做脱敏只读诊断。",
"status": "in_progress",
"status": "completed",
"dev_type": null,
"scope": "client reimbursement action projection; server material PDF image normalization; production read-only preview diagnostics",
"package": null,
@@ -11,7 +11,7 @@
"creator": "shunli",
"assignee": "shunli",
"createdAt": "2026-08-28",
"completedAt": null,
"completedAt": "2026-10-11",
"branch": null,
"base_branch": "main",
"worktree_path": null,
@@ -26,4 +26,4 @@
"relatedFiles": [],
"notes": "任务范围内聚焦验证通过。完整 r102 为 5/6,唯一失败是既有用例仍查找已不存在的‘编辑’按钮;本任务新增的管理员撤回不可见断言通过,不把该历史失败静默视为绿色基线。",
"meta": {}
}
}
@@ -1,5 +1,11 @@
# 实施清单:报销费用口径与票据明细门禁
## 2026-10-11 余项移交结果(当前接续点)
已将报销真实 OCR 与财务科目映射移到 [09-08 接收合同](../../../09-08-reimbursement-payment-closeout/prd.md#2026-10-11-接收余项报销真实-ocr-验收与财务科目映射),执行接续见接收任务 implement 顶部。保留指定样票 `SAMPLE-PDF-INVOICE-FIRST` 与 `artifacts/qa-flow/20260909-release-e5685f1/` 原证据;2026-09-10 的 `PASS_DRAFT_ONLY` 仍有效于原合成草稿阶段,不扩为提交/审批或逐字段精度通过。科目映射未回填,财务负责人责任和原目标日期继续记录在 RC-ACCOUNT。
本轮只移交文档,未调用腾讯、未写生产、未重跑业务或补勾历史清单;原独立复核余额仍在接收任务开放。父任务及部署子任务归档待明确授权,当前未归档、未提交。
## 当前接续点:已部署,公网草稿闭环验证完成
### 2026-09-10 公网草稿最终 PASS(本节替代前次样票阻塞状态)
@@ -1,5 +1,13 @@
# 报销费用口径与票据明细门禁
## 2026-10-11 余项移交(待归档授权)
本地 Git 检查确认实现 `6e999d5`、后续发票优先版本 `e5685f1` 均已包含于用户报告的生产 `4a5cd8f2`。本轮未连接生产;下方“未部署”等是原阶段记录,原未勾选项不补勾,不把包含关系当作全部验收通过。
报销发票真实 OCR 的跟进入口移到 [报销付款收尾 PRD](../../../09-08-reimbursement-payment-closeout/prd.md#2026-10-11-接收余项报销真实-ocr-验收与财务科目映射) 的 RC-OCR-1–4,财务科目映射移到同节 RC-ACCOUNT。责任、原样票别名、既有证据、退出条件和云/生产授权边界均保留;借款回单 OCR 不能代替报销发票验收。原独立复核未完成记录由接收任务既有复核余项继续跟进,不因移交判为通过。
原 implement 顶部已记录 2026-09-10 真实腾讯线上合成样票到草稿的 PASS,不能继续描述为“合成样票从未真实识别”;尚未具备同次真实腾讯到提交/审批及约定字段精度核对证据。移交后本任务可进入归档审批,当前仍 `in_progress`,未归档、未提交。
## Goal
### 当前交付口径(2026-09-09,最终自检收口)
@@ -3,7 +3,7 @@
"name": "reimbursement-expense-policy-invoice-guard",
"title": "报销费用口径与票据明细门禁",
"description": "统一企业报销费用类型与材料政策,并以默认关闭的双闸门逐步启用票据—明细强关联和申报金额门禁。",
"status": "in_progress",
"status": "completed",
"dev_type": null,
"scope": "expense type policy/schema/master data; reimbursement item invoice relation; staged submit guard",
"package": "server,client,docs",
@@ -11,7 +11,7 @@
"creator": "shunli",
"assignee": "shunli",
"createdAt": "2026-08-29",
"completedAt": null,
"completedAt": "2026-10-11",
"branch": null,
"base_branch": "main",
"worktree_path": null,
@@ -38,6 +38,6 @@
"docs/报销发票附件规则.md",
"docs/需求-报销发票OCR规约.md"
],
"notes": "本地实施和只读专项复核已完成;B2 env/DB 双闸仍默认关闭,未部署、未应用生产 migration、未写生产配置。2,000 元/条明细的选类提示阈值已批准。会计科目映射由财务负责人在 B1 生产启用前提供,目标回填日期 2026-09-05;未取得正式科目表时保持 null,不由开发人员猜测。生产只读审计确认 0014/0016/0017/0018 均需退回修正;顺序改为先部署 B1、隔离验证并启用 B2,再由真实财务统一退回。B2 不校验日期粒度,0014 必须按 5 个发票日期拆分并由财务人工复核。部署合同见子任务 08-30-reimbursement-expense-policy-invoice-guard-deploy。B3 财务一页式及行内费用标准提示恢复仍为后续任务。",
"notes": "2026-10-11 报销真实OCR余项与财务科目映射已移交09-08-reimbursement-payment-closeout的RC-OCR-1–4/RC-ACCOUNT;责任、证据及原未勾选项保留,独立复核由接收任务既有余项跟进。6e999d5/e5685f1本地祖先检查确认包含于用户报告的生产4a5cd8f2,本轮未查生产。已具备原合成草稿阶段PASS,不扩成审批或精度通过;待归档授权,未提交。以下为历史阶段说明:\n本地实施和只读专项复核已完成;B2 env/DB 双闸仍默认关闭,未部署、未应用生产 migration、未写生产配置。2,000 元/条明细的选类提示阈值已批准。会计科目映射由财务负责人在 B1 生产启用前提供,目标回填日期 2026-09-05;未取得正式科目表时保持 null,不由开发人员猜测。生产只读审计确认 0014/0016/0017/0018 均需退回修正;顺序改为先部署 B1、隔离验证并启用 B2,再由真实财务统一退回。B2 不校验日期粒度,0014 必须按 5 个发票日期拆分并由财务人工复核。部署合同见子任务 08-30-reimbursement-expense-policy-invoice-guard-deploy。B3 财务一页式及行内费用标准提示恢复仍为后续任务。",
"meta": {}
}
}
@@ -1,5 +1,9 @@
# 取消报销资产事前批准强制
## 2026-10-11 收口归档
取消资产事前批准强制 `36c86f1d`(回填 `0f3ecfab`)已上线,包含于当前生产 `4a5cd8f2`。原“上线后再退回 0017”的前提已满足:据用户 2026-09-27 确认,0014/0016/0017/0018 批次已重新提交并经财务审批通过。父任务“费用政策与票据门禁”仍保持开放,不随本子任务归档。原未勾选清单为当时规划记录,保留原样、不补勾。经用户 2026-10-11 明确授权归档。
## Goal
纠正 B1 把不存在的公司制度做成系统强制的问题:公司当前不要求办公家具、通用设备或办公电子设备采购事前审批,报销系统不得要求员工填写“事前批准依据”才能保存或提交。保留已上线的兼容字段与历史数据,不做 migration;把 assets 类型的员工说明改为资产登记导向,要求在明细说明中写清物品、使用人或责任人、存放位置。
@@ -3,7 +3,7 @@
"name": "reimbursement-assets-prior-approval-correction",
"title": "取消报销资产事前批准强制",
"description": "纠正不存在的 assets 事前审批制度:取消报销明细强制批准依据,隐藏无用途 UI,保留兼容字段与历史数据,并把资产类型说明改为登记导向。",
"status": "in_progress",
"status": "completed",
"dev_type": null,
"scope": "assets reimbursement validation; PC/mobile item editing; expense-type seed descriptions; reimbursement product/spec contracts",
"package": null,
@@ -11,7 +11,7 @@
"creator": "shunli",
"assignee": "shunli",
"createdAt": "2026-08-30",
"completedAt": null,
"completedAt": "2026-10-11",
"branch": null,
"base_branch": "main",
"worktree_path": null,
@@ -23,4 +23,4 @@
"relatedFiles": [],
"notes": "生产 0f9f403 已上线 B1/B2;0016 非 assets 可并行作为真人 canary,0017 为家具采购,必须等待本纠偏上线后再退回。无 migration、无 flag 或生产账号写入。",
"meta": {}
}
}
@@ -1,5 +1,11 @@
# 实施清单:报销费用政策与票据门禁生产部署
## 2026-10-11 文档接续:余项已移交,未执行旧部署清单
报销真实 OCR 与财务科目映射已登记至 [09-08 接收合同](../../../09-08-reimbursement-payment-closeout/prd.md#2026-10-11-接收余项报销真实-ocr-验收与财务科目映射),由其 implement 顶部继续安排。父任务 2026-09-10 已有真实腾讯线上合成草稿证据,提交/审批、约定字段核对与科目回填仍开放;原独立复核和真实业务义务不因移交关闭。
本任务原部署步骤未逐项留有执行证据,保留全部未勾选清单,不用后续部署替它补勾。后续发版须按当时最终候选重新核迁移、运行版本、备份/回滚及授权,不执行下方过期参数。本轮仅本地文档移交,归档、提交待明确授权。
## 1. 规划与冻结
- [ ] PRD、design、implement 经用户审查并明确批准进入实施。
@@ -1,5 +1,13 @@
# 报销费用政策与票据门禁生产部署
## 2026-10-11 余项移交与旧部署合同边界(待归档授权)
父任务实现 `6e999d5` 与后续发票优先版本 `e5685f1` 已由后续发布带入用户报告的生产 `4a5cd8f2`,本轮以本地 Git 祖先关系核对,未连接生产。原 B1/B2 分阶段冻结、测试身份准备、灰度/全局启用及单据退回清单保留当时合同;本任务没有逐项执行记录,不能因为后续版本包含而补勾或声称这些步骤都在本任务完成。资产纠偏已由原子任务归档记录单独收口,不回退到旧“资产事前批准硬门禁”重做。
当前报销发票真实 OCR 及财务科目映射统一移到 [09-08 接收合同](../../../09-08-reimbursement-payment-closeout/prd.md#2026-10-11-接收余项报销真实-ocr-验收与财务科目映射) 的 RC-OCR-1–4 与 RC-ACCOUNT,含原证据、财务/执行端责任及验收边界;未完成责任不取消,不放入借款回单 OCR。原未满足的真实业务、发布/独立证据义务仍按接收任务现有工作线跟进,不由移交判为通过。
本轮未执行任何生产部署或配置/业务写入;下方固定回滚版本与迁移预期属于旧发布时点,不是下次发版的执行参数。移交后本子任务可进入归档审批,仍 `in_progress`,归档及提交待明确授权。
## Goal
把已完成并复核的报销费用政策与票据门禁安全发布到生产:先以 B2 有效关闭的状态完成 B1 migration、主数据和 owner 基线验收,再在隔离部门内验证 B2,最后全局启用并让财务统一退回 4 张真实审批中单据。全过程不得拿真人单据做灰度试验,不得因应用回滚误删 migration 或已建立的票据—明细关系。
@@ -3,7 +3,7 @@
"name": "reimbursement-expense-policy-invoice-guard-deploy",
"title": "报销费用政策与票据门禁生产部署",
"description": "受控部署 B1 migration 与费用主数据,在隔离部门验证 B2 后全局启用,并统一处置四张审批中报销。",
"status": "in_progress",
"status": "completed",
"dev_type": null,
"scope": "production deployment, migration, feature flag rollout, smoke identities, reimbursement remediation",
"package": "deploy,server,client,trellis",
@@ -11,7 +11,7 @@
"creator": "shunli",
"assignee": "shunli",
"createdAt": "2026-08-30",
"completedAt": null,
"completedAt": "2026-10-11",
"branch": null,
"base_branch": "main",
"worktree_path": null,
@@ -28,6 +28,6 @@
"server/src/services/featureFlags/index.js",
"server/src/routes/reimbursements.js"
],
"notes": "规划阶段;不执行未单独授权的 push、生产账号/部门/合成数据写入、migration、部署、env 配置、feature flag 更新或真实单据退回。固定应用回滚基线为 59dc5f6。B2 不校验日期粒度,0014 必须依赖退回说明和财务人工复核。employee/manager/finance smoke 终态常驻‘系统测试专用(勿分配员工)’部门;每次灰度前成员集合必须恰好为三个 smoke。finance smoke 含付款能力,仅在获批验收窗口临时 active,窗口外 disabled。普通 employee 无直属 manager 时 workflow 会以 no_manager_found 阻断。B2 DB 记录必须显式存在为 force_off,灰度必须记录 actor 级精确 reason。",
"notes": "2026-10-11 报销真实OCR余项及财务科目映射移交09-08-reimbursement-payment-closeout的RC-OCR-1–4/RC-ACCOUNT;原部署清单无逐项执行证据,未补勾。后续发布已包含父任务实现,不虚报按本任务旧冻结/灰度步骤执行。旧回滚/迁移参数不用于下次发版。待归档授权,未提交、未查生产。以下为历史阶段说明:\n规划阶段;不执行未单独授权的 push、生产账号/部门/合成数据写入、migration、部署、env 配置、feature flag 更新或真实单据退回。固定应用回滚基线为 59dc5f6。B2 不校验日期粒度,0014 必须依赖退回说明和财务人工复核。employee/manager/finance smoke 终态常驻‘系统测试专用(勿分配员工)’部门;每次灰度前成员集合必须恰好为三个 smoke。finance smoke 含付款能力,仅在获批验收窗口临时 active,窗口外 disabled。普通 employee 无直属 manager 时 workflow 会以 no_manager_found 阻断。B2 DB 记录必须显式存在为 force_off,灰度必须记录 actor 级精确 reason。",
"meta": {}
}
@@ -1,5 +1,9 @@
# B-2a 报销审批内容工作台
## 2026-10-11 收口归档
B-2a 实现 `c840574d`(元数据 `6bca91a3`)已上线,并由后续 B-2b 统一工作台承接;已包含于当前生产 `4a5cd8f2`。真人验收随父任务 AC11 收口。原未勾选清单为当时规划记录,保留原样、不补勾。经用户 2026-10-11 明确授权归档。
## Goal
先把主管和财务每天真实使用的报销审批页改成可独立完成核验与决策的一页式工作台,并产出后续填写态必须采用的共享“明细 + 票据”组件合同。
@@ -3,7 +3,7 @@
"name": "reimbursement-approval-content-workbench",
"title": "B-2a 报销审批内容工作台",
"description": "在 PC/移动审批详情中直接展示报销明细、所属票据、附件与快照/实时核验,并建立共享内容组件。",
"status": "in_progress",
"status": "completed",
"dev_type": null,
"scope": "client,server,spec",
"package": null,
@@ -11,7 +11,7 @@
"creator": "shunli",
"assignee": "shunli",
"createdAt": "2026-09-01",
"completedAt": null,
"completedAt": "2026-10-11",
"branch": null,
"base_branch": "main",
"worktree_path": null,
@@ -23,4 +23,4 @@
"relatedFiles": [],
"notes": "父任务第一阶段;独立交付、独立部署。已确认 PC 右侧单一动作面板、主列靠后展示完整历史;报销审批必须联网且证据不得进入离线缓存。",
"meta": {}
}
}
@@ -1,5 +1,9 @@
# B-2b 统一报销工作台迁移
## 2026-10-11 收口归档
B-2b 实现 `abdd2503`、布局优化 `4b974fd8`(元数据 `dda625b1`)已上线;已包含于当前生产 `4a5cd8f2`。真人验收随父任务 AC11 收口。原未勾选清单为当时规划记录,保留原样、不补勾。经用户 2026-10-11 明确授权归档。
## Goal
把报销从“填写详情页、审批详情页、旧只读详情页分别组织同一业务对象”收敛为唯一的交互式报销工作台。工作台以报销内容为主体,通过 `edit`、`readonly`、`approval` 三个模式适配器注入数据、状态和能力;审批只是叠加在报销对象上的流程与决策能力,不再复制一套报销页面壳。
@@ -3,7 +3,7 @@
"name": "reimbursement-form-shared-component-migration",
"title": "B-2b 统一报销工作台迁移",
"description": "把 PC/移动报销编辑、只读、审批收敛为一个工作台壳和三个能力适配器,并删除通用审批页的报销旧分支。",
"status": "in_progress",
"status": "completed",
"dev_type": null,
"scope": "client,workflow,spec",
"package": null,
@@ -11,7 +11,7 @@
"creator": "shunli",
"assignee": "shunli",
"createdAt": "2026-09-01",
"completedAt": null,
"completedAt": "2026-10-11",
"branch": null,
"base_branch": "main",
"worktree_path": null,
@@ -23,4 +23,4 @@
"relatedFiles": [],
"notes": "父任务第二阶段;方案 A 的统一报销工作台已以候选 9fc3b76 部署,生产 build marker 为 9fc3b764382d。上线真人目视反馈触发 KISS 收尾:PC 明细表格、移动明细卡片、票据/附件独立分组、单一文件查看入口和退回动作内渐进引用已完成,实现提交为 4b974fd;typecheck/build、UI strict/self-test、R102/R111/workflow/视觉聚焦回归及治理通过,等待最终复核,尚未 push、部署或触碰真人单据。",
"meta": {}
}
}
@@ -1,5 +1,9 @@
# 报销审批一页式工作台
## 2026-10-11 收口归档
三个子任务实现均已上线(见各子任务收口记录)。AC11 真人重提与财务复审以用户 2026-09-27 口头确认计为完成(记录于 `9cbefdf2`);Claude 未读取生产数据逐项核对。原未勾选清单为当时规划记录,保留原样、不补勾。经用户 2026-10-11 明确授权归档。
## Goal
让主管和财务在同一审批页面内完成“看报销内容—核明细与票据—识别风险—填写意见—作出审批决定”,不再把“查看报销单”外链作为取得核心报销内容的必经入口;同时建立可复用的“报销明细 + 所属票据”业务内容组件,并承诺在后续填写态迁移后消除历史布局分叉。
@@ -3,7 +3,7 @@
"name": "reimbursement-one-page-approval-workbench",
"title": "报销审批一页式工作台",
"description": "拆分交付报销审批一页式内容工作台与填写态共享组件迁移,统一明细、票据和核验信息结构。",
"status": "planning",
"status": "completed",
"dev_type": null,
"scope": "trellis,client,server,spec",
"package": null,
@@ -11,7 +11,7 @@
"creator": "shunli",
"assignee": "shunli",
"createdAt": "2026-09-01",
"completedAt": null,
"completedAt": "2026-10-11",
"branch": null,
"base_branch": "main",
"worktree_path": null,
@@ -27,4 +27,4 @@
"relatedFiles": [],
"notes": "采用 B 路线:B-2a 先审批、B-2b 后迁移填写态;父任务仅在旧重复布局收口后完成。审批页回归既有右侧单一动作面板规则;移动报销审批要求联网且证据不进入离线缓存。规划阶段不编码、不部署、不处理真人单据。",
"meta": {}
}
}
@@ -1,5 +1,9 @@
# 报销只读内容一致性与审批上下文收敛
## 2026-10-11 收口归档
实现 `e48514d5`(回填 `d63ccdf7`)已上线;已包含于当前生产 `4a5cd8f2`。真人验收随父任务 AC11 收口。原未勾选清单为当时规划记录,保留原样、不补勾。经用户 2026-10-11 明确授权归档。
## Goal
让同一张报销单在 PC/移动、编辑/只读/审批和不同授权用户眼中保持同一份报销正文语义,同时把审批动作、审批历史和员工处理进度明确留在各自上下文中。补齐费用明细后的权威合计,避免审批人依赖页头回看或自行心算;在不复制内容、不引入第二业务金额来源的前提下保持 KISS。
@@ -3,7 +3,7 @@
"name": "reimbursement-readonly-content-consistency",
"title": "报销只读内容一致性与审批上下文收敛",
"description": "统一报销工作台三模式的报销正文口径,补齐权威明细合计,并收敛 PC 审批历史与右侧决策上下文。",
"status": "in_progress",
"status": "completed",
"dev_type": null,
"scope": "trellis,client,docs",
"package": null,
@@ -11,7 +11,7 @@
"creator": "shunli",
"assignee": "shunli",
"createdAt": "2026-09-02",
"completedAt": null,
"completedAt": "2026-10-11",
"branch": null,
"base_branch": "main",
"worktree_path": null,
@@ -23,4 +23,4 @@
"relatedFiles": [],
"notes": "本地实施与专项验证完成,等待 diff 复核;权威合计、两类完整性 fail-closed、PC 历史右置和进度/历史职责合同已落地。未 commit、push、部署;AC11 仍待部署后真人单据验证。",
"meta": {}
}
}
@@ -1,3 +1,23 @@
## 2026-10-11 归档执行完成(用户已批准,未提交)
用户核对三项页面反馈、实现提交的生产包含关系及独立余项归属后,明确回复“可以归档”。本次仅收口本任务,保留原验收清单,不修改业务代码或关闭其他任务,不提交、推送或部署。
已使用官方 `task.py archive`,显式 `--no-commit` 禁止自动提交,命令退出 0。该任务直接在 `main` 实施,登记分支与 `base_branch` 同为 `main`,采用工具支持的 `--skip-branch-validation` 归档已有发布的非 PR 任务;没有重复执行一次已知会拒绝的默认命令。6 个文件完整移入 `.trellis/tasks/archive/2026-10/09-04-reimbursement-state-gap-finance-view/`,原目录已不存在;移前移后除 `task.json` 状态更新外摘要一致,随后只补本节和 PRD 的实际归档结果。状态 `completed`、完成日期 `2026-10-11`,活动任务目录从 14 个减为 13 个,当前会话指针已由官方命令清除。
官方 JSON 写入会再次移除末尾换行,已在归档后的 `task.json` 补回并核实;保留历史 notes 原文。所有 Git 改动仅属本任务记录,尚未暂存或提交,未推送或部署。
下节是授权前的收口核对时点记录;其中“未归档”“待批准”等表述由本节接续。
## 2026-10-11 线上反馈收口记录(授权前核对时点)
本轮接续 B 组本任务的收口核对。开始时工作区干净、HEAD 为 `b85ff6a9`;已通过官方 `task.py start` 切换当前任务并回读,自动登记执行分支 `main`。只更新本任务记录,不改业务代码、数据库或生产配置,不执行归档、提交、推送或部署。
- 用户确认待组批可找到、他人从未提交草稿不可见、原权限范围内的退回/撤回草稿仍可见;按原回复记录,不补充设备或未明确确认的重复候选结论。
- 本地 Git 的祖先关系证明 `f9aee4c9`、`229867f5` 已包含在已确认生产基线 `4a5cd8f2`。基线之后的五个产品文件均属公司借款,不改变本任务的报销实现;这只是本地 Git 核对,不是重新连接生产或实时版本检查。
- `0014` 已有后续组批、复核、导出制单记录,不再把旧 AC1/AC11 的“在待组批搜索到 0014”当作当前要求。采用符合条件的现有报销来理解本次用户反馈,不新建生产合成单据、不重复组批。
- 本轮只补用户反馈和任务记录,文档/流程检查独立执行;既有业务测试、部署及独立复核证据沿各阶段原记录,不虚报重跑。原未勾清单和历史时点的 PENDING/未部署表述保留,不能据此将整份原清单勾成新一轮通过。
- 收口建议:本任务可以进入归档审批,关联真实业务交接和环境验收继续由原任务跟进,不因本次反馈关闭。归档与提交尚须用户明确批准;批准归档后使用官方命令并禁用自动提交。任务在获批前保持 `in_progress`,未移动目录。
## 2026-09-17 本批最终本地验证
付款待办已显示当前可复核/办理姓名、站内提醒对象及企微个人发送事实。`paymentResponsibility`9/9,最终借款桌面/手机正式登录浏览器同样验证姓名;多通知接收人的单anchor成功不冒称全部送达。真实批次未查询,不能给出其真实待办姓名或宣称个人消息送达。财务原地进度15/15与排序回归保留;借款及ERP改动后的付款128/128、最终前端typecheck/build通过。F4接续已执行;独立复核PENDING、未部署。保护回执与本批候选见借款implement当前入口。
@@ -1,5 +1,13 @@
# 报销状态空隙与财务处理视角
## 2026-10-11 收口归档(用户已批准,未提交)
用户针对财务线上使用情况回复“1可以找到 2看不到 3能看见”:符合条件的报销可在待组批找到;全范围看不到他人从未提交的私人草稿;原权限范围内的退回或撤回草稿仍可见。设备未注明,不将本次反馈写成电脑、手机双端验收。第一项仅记录“可以找到”,不追加“已有批次不重复”的现场确认,后者仍以既有软件测试为据。
本地 Git 已核实本任务登记的实现提交 `f9aee4c9`、本人报销发布 `229867f5` 均包含在已确认的生产基线 `4a5cd8f2` 中;本轮未连接生产。原 AC1/AC11 要求 `0014` 位于待组批是历史时点,既有记录表明它后来已组批、复核并导出制单,现在不要求它再次成为候选,也不回放真实业务来满足旧清单。
三项主要页面行为已有本次用户反馈支持。用户核对记录后明确回复“可以归档”,授权收口本任务;官方归档命令已成功,任务状态为 `completed`、完成日期为 `2026-10-11`,目录位于 `archive/2026-10/`。原勾选清单和历史证据保留,不补勾为全量重新验收;真实银行导入、付款交接、通知送达及恢复验收不由本次页面反馈替代,继续沿各自原任务跟进。本次授权仅归档,不含提交、推送或部署,具体收尾见 implement 顶部。
## 2026-09-17 真人反馈:审批后进度刷新与付款明细顺序
用户要求修复财务复核通过后当前页进度仍旧、同批多报销明细未按单号排序。沿本task既有详情一致性范围本地实施,先于借款与精简;不读取真人单据、不重放真实审批。
@@ -3,7 +3,7 @@
"name": "reimbursement-state-gap-finance-view",
"title": "报销状态空隙与财务处理视角",
"description": "修复报销状态空隙、草稿读取、退回留痕删除及付款详情一致性;追加已批准的财务本人报销CEO一次审批,保留付款独立复核与生产写入边界。",
"status": "in_progress",
"status": "completed",
"dev_type": null,
"scope": "server,client,tests,spec",
"package": "hyb-oa",
@@ -11,8 +11,8 @@
"creator": "shunli",
"assignee": "shunli",
"createdAt": "2026-09-04",
"completedAt": null,
"branch": null,
"completedAt": "2026-10-11",
"branch": "main",
"base_branch": "main",
"worktree_path": null,
"commit": "f9aee4c93272204d18408217358d95334cedcd5e",
@@ -2,6 +2,7 @@
## 当前状态
- 2026-10-11:已完成范围收口,后续阶段取消(未获批、未执行,不计通过);详见 prd 顶部收口记录。登录后线上冒烟与环境合同由发布门禁任务跟进。以下为历史状态。
- in_progress / inline。创建轮使用 task.py create --no-start;用户随后明确批准阶段 0,已 task start 切换到本任务,不授予阶段 1 批量修改权限。
- 门禁任务获准 21 文件逐字节未动;HEAD=66f25b1,本地 origin/main=492920c。新任务独立、非子任务,未转移合同阶段 B 的业务验收责任。
- 创建轮只读已有 single trace;随后在阶段 0 授权内实际执行一次原合同用例的观测实验,结果 timedOut,未改真实源码。新现场条件与旧 trace 严格分开,见 design.md §1/§3.1。
@@ -1,5 +1,17 @@
# E2E 认证前置统一
## 2026-10-11 收口记录(已完成范围收口,后续阶段取消;待归档批准)
**已完成的范围**(任务记录提交 `096576d8`,证据在 `artifacts/qa-flow/e2e-auth-preconditions/`):
- 立项与认证状态合同、范围边界:用户 2026-09-07 批准。
- 阶段 0:一次本地定向启动条件取证(单条原合同用例、1 worker、零重试、trace=on,结果 timedOut,未改源码)及静态盘点;用户已独立复核批准,对应 AC1。证据 `phase0-20260907-01/02/03`。
- 在线对照:一次相同原用例,确认原生 `navigator.onLine=true`,认证前置断言通过、下游“更多”超时,不记为整条通过;`online-20260907-01/closeout-receipt.json` valid=true。用户认可该结果并撤回默认迁移。
**取消的后续阶段**:阶段 1 的 31+2 文件迁移、状态合同回归、固定矩阵验证、剩余失败归类及独立复核,均从未获批、未执行,按“取消”记录,不补记为验收通过。AC2–AC6 保持未勾选;207 原浏览器清单撤销决策资格的结论不变,原件保留,不倒写为逐条已证明假失败。
**转由其他任务跟进,不随本任务关闭**:环境启动合同沿 `09-06-release-gate-integrity` 的 R2/R6 与设计 §10;登录后的线上冒烟由发布门禁任务的发布后公网必跑项跟进(凭据准备另见公司借款任务后续第 6 项)。场景 8、经理权限缺口与 R1 启用不在本任务范围,亦未因收口改变。
## Goal
先确定合成浏览器验收的运行前提与认证起点,再决定修正环境合同还是认证夹具;不预设需要批量迁移 spec。覆盖登录、会话恢复和注销的真实边界,任务独立于 release-gate-integrity,不以修复全部浏览器红项或启用发布门禁作为完成条件。
@@ -3,7 +3,7 @@
"name": "e2e-auth-preconditions",
"title": "E2E 认证前置统一",
"description": "阶段 0 及在线对照已获用户复核认可;31+2 迁移暂缓。207 原浏览器清单撤销决策资格且原件保留;环境合同规划归 release-gate-integrity R2/R6 与设计 §10,本任务保留取证,不实施或重跑。",
"status": "in_progress",
"status": "completed",
"dispatch_mode": "inline",
"dev_type": "frontend",
"scope": null,
@@ -12,7 +12,7 @@
"creator": "shunli",
"assignee": "shunli",
"createdAt": "2026-09-07",
"completedAt": null,
"completedAt": "2026-10-11",
"branch": "main",
"base_branch": "main",
"worktree_path": null,
@@ -1,5 +1,9 @@
# 修复 Trellis 任务切换与执行闭环
## 2026-10-11 收口归档
治理修复 `c4025083` 已提交并推送;纯治理与文档,不需要部署。原记录中“下一轮观察真实注入、独立复核未做”保留为历史事实,不补记为已复核。原未勾选清单为当时规划记录,保留原样、不补勾。经用户 2026-10-11 明确授权归档。
## Goal
依据已安装官方指南修复任务激活与实际执行脱节,保持 inline 和授权边界,不改运行时或业务代码
@@ -3,7 +3,7 @@
"name": "trellis-workflow-alignment",
"title": "修复 Trellis 任务切换与执行闭环",
"description": "依据已安装官方指南修复任务激活与实际执行脱节,保持 inline 和授权边界,不改运行时或业务代码",
"status": "in_progress",
"status": "completed",
"dev_type": null,
"scope": "trellis-workflow",
"package": null,
@@ -11,7 +11,7 @@
"creator": "shunli",
"assignee": "shunli",
"createdAt": "2026-09-09",
"completedAt": null,
"completedAt": "2026-10-11",
"branch": "main",
"base_branch": "main",
"worktree_path": null,
@@ -1,5 +1,9 @@
# ERP 专属遗留实现精简
## 2026-10-11 收口归档
ERP 专属实现移除随 `1f8d5b65` 上线(状态同步 `2769408b`),包含于当前生产 `4a5cd8f2`。原未勾选清单为当时规划记录,保留原样、不补勾。经用户 2026-10-11 明确授权归档。
## Goal
落实用户“用不到的功能、代码、测试要精简”的要求,沿当前产品不接 ERP 的决定移除专属实现,减少隐藏入口和无效回归。
@@ -3,7 +3,7 @@
"name": "erp-exclusive-retirement",
"title": "ERP 专属遗留实现精简",
"description": "按现行不接 ERP 的产品决定移除专属客户端、同步入口、后台作业与失效测试,保留通知、企微、通用 outbox 和历史数据;仅本地验证。",
"status": "in_progress",
"status": "completed",
"dev_type": null,
"scope": "erp-exclusive-code-retirement",
"package": null,
@@ -11,7 +11,7 @@
"creator": "shunli",
"assignee": "shunli",
"createdAt": "2026-09-17",
"completedAt": null,
"completedAt": "2026-10-11",
"branch": null,
"base_branch": "main",
"worktree_path": null,
@@ -23,4 +23,4 @@
"relatedFiles": [],
"notes": "2026-09-17 本地退役实现完成:6个ERP service、1个job、14个专属测试删除,共享通知/企微/outbox与历史数据保留;新enqueue/dispatch及历史retry拒绝,旧pending不重放。受影响回归、typecheck/build、模块图与strict原因码通过,17条ERP旧债退出后legacy=0。独立复核PENDING,未部署。 2026-09-26 更新:本地实现已通过候选分支合并落地到 origin/main(合并提交 46a70aa5),不再是仅存在于未提交分支的工作;但真实样本验证/独立复核/生产切换等验收项仍开放,任务状态维持 in_progress,未视为完成。",
"meta": {}
}
}
@@ -1,5 +1,18 @@
# PWA 更新事件与本地并发产品缺陷最小修复
## 2026-10-11 收口记录(待归档批准)
**已完成的范围**:P1–P4、P6–P10 已勾选——PWA 更新后页面收敛,采购创建/保存草稿、资产创建、单个账号创建与报销提交的事务过期最小修复。本地完整候选 g20-full-r6:后端 4437 通过/1 原条件跳过、372 本地浏览器全过(证据入口见 09-06 implement 顶部)。实现随 `1f8d5b65` 进入候选分支,合并提交 `46a70aa5` 已包含于当前生产 `4a5cd8f2`;其后 10 月历次发布均经完整门禁通过。2026-09-27 用户关于事务上限的决定由已归档的 `09-27-purchase-submit-tx-timeout` 实施。
**未完成、如实保留**:
- P5 保持未勾:旧测试材料中一份 WebKit 派生 registry 已披露的字节变化使“旧材料保护”合取项未恢复,原 baseline 不改。这是测试材料保护问题,不是产品代码缺陷;作为余项由 `09-08-reimbursement-payment-closeout` 继续跟进。
- 独立复核:仅 P9(报销提交)经补审 PASS,且 reviewer 未逐行审全部路由分支;本任务全量独立复核未做,不补记为通过。
**已分流**:预算池 used_amount 无锁读改写缺陷已单列 `09-19-budget-pool-concurrency` 修复并归档。
原未勾清单与历史“未发布/PENDING”表述为当时记录,保留原样。
## 来源与批准范围
用户已批准按余项连续推进,未规划内容先落方案。09-06 G18两次同样的四文件4-worker并发验证复现采购500及PWA45秒不收敛;环境与输入/清理PASS。此task拥有两个在用产品缺陷的最小本地修复,09-06仍拥有完整覆盖合同。不能改变R2/R1或自动取得真实操作与发布授权。
@@ -3,7 +3,7 @@
"name": "local-concurrency-product-fixes",
"title": "PWA 更新事件与采购创建事务最小修复",
"description": "PWA原生控制器更新与采购最小写事务已本地实施;131单测、原四文件并发4/4、认证/OCR18/18通过。独立及完整门禁仍待,不提交发布。",
"status": "in_progress",
"status": "completed",
"dev_type": null,
"scope": "client PWA lifecycle and server purchase draft transaction",
"package": null,
@@ -11,7 +11,7 @@
"creator": "shunli",
"assignee": "shunli",
"createdAt": "2026-09-18",
"completedAt": null,
"completedAt": "2026-10-11",
"branch": null,
"base_branch": "main",
"worktree_path": null,
@@ -1,5 +1,9 @@
# 预算池占用与释放并发一致性修复
## 2026-10-11 收口归档
预算池并发修复及独立复核完成后随 `1f8d5b65` 上线(状态同步 `2769408b`),包含于当前生产 `4a5cd8f2`;B6“随正式候选完整门禁”由其后历次完整门禁与发布满足。原未勾选清单为当时规划记录,保留原样、不补勾。经用户 2026-10-11 明确授权归档。
## Goal
修复业务独立补审发现的预算池丢失更新与陈旧超额判定;统一保证报销、采购及冻结差旅共享服务的并发一致性,以本地合成数据验证。
@@ -3,7 +3,7 @@
"name": "budget-pool-concurrency",
"title": "预算池占用与释放并发一致性修复",
"description": "预算并发本地修复及独立审查完成;两项必修经Claude定向928项验证通过并关闭。发布完整门禁延期,任务保留in_progress,未提交部署。",
"status": "in_progress",
"status": "completed",
"dev_type": "backend",
"scope": "budget-control",
"package": null,
@@ -11,7 +11,7 @@
"creator": "shunli",
"assignee": "shunli",
"createdAt": "2026-09-19",
"completedAt": null,
"completedAt": "2026-10-11",
"branch": null,
"base_branch": "main",
"worktree_path": null,
@@ -77,4 +77,4 @@
"reviewFixResultSha256": "cb7996f6dcacc3ffd44a398b705ed840bf51ed6a0e312336933a25f93813a8f9",
"requiredReviewFindingsOpen": 0
}
}
}
@@ -1,5 +1,9 @@
# 采购提交事务负载下超时返回500
## 2026-10-11 收口归档
任务元数据曾停留在 planning,实际实施 `b6f0c836`(采购提交与报销新建/提交事务显式 10 秒上限,记录 `9cbefdf2`)已上线,包含于当前生产 `4a5cd8f2`;唯一未勾项“随下次发布上线”已满足。原未勾选清单为当时规划记录,保留原样、不补勾。经用户 2026-10-11 明确授权归档。
## Goal
消除采购提交(及同结构的报销新建/提交)在负载下超过 Prisma 交互事务默认 5000ms 上限而返回 500(P2028)的问题。
@@ -3,7 +3,7 @@
"name": "purchase-submit-tx-timeout",
"title": "采购提交事务负载下超时返回500",
"description": "负载复现中采购提交交互事务超过Prisma默认5000ms(P2028)返回500;生产b1d25d3c起同写法。查清耗时构成(锁等待/事务内查询),给出最小修复。",
"status": "planning",
"status": "completed",
"dev_type": null,
"scope": "transaction-timeout",
"package": null,
@@ -11,7 +11,7 @@
"creator": "shunli",
"assignee": "shunli",
"createdAt": "2026-09-27",
"completedAt": null,
"completedAt": "2026-10-11",
"branch": null,
"base_branch": "main",
"worktree_path": null,
@@ -30,4 +30,4 @@
"meta": {
"implementationApproval": "APPROVED_BY_USER_20260927"
}
}
}
+2 -2
查看文件
@@ -48,8 +48,8 @@ export type LoanReceiptAttributionState = {
export function fetchCompanyLoans(page: number, lenderId?: string, signal?: AbortSignal) {
return apiRequestData<{ items: CompanyLoan[]; total: number }>(`/api/company-loans?page=${page}&pageSize=30${lenderId ? `&lenderId=${encodeURIComponent(lenderId)}` : ""}`, { signal });
}
export function fetchLoanLenders(page: number, signal?: AbortSignal) {
return apiRequestData<{ items: LoanLender[]; total: number }>(`/api/loan-lenders?page=${page}&pageSize=30`, { signal });
export function fetchLoanLenders(page: number, signal?: AbortSignal, eligible?: "related_authority") {
return apiRequestData<{ items: LoanLender[]; total: number }>(`/api/loan-lenders?page=${page}&pageSize=30${eligible ? `&eligible=${eligible}` : ""}`, { signal });
}
export function fetchLoanRepayments(page: number, signal?: AbortSignal) {
return apiRequestData<{ items: LoanRepayment[]; total: number }>(`/api/loan-repayments?page=${page}&pageSize=30`, { signal });
@@ -24,6 +24,9 @@ export function LoanBankReconciliation({ repayment: r, attachments }: { repaymen
const [details, setDetails] = useState<Awaited<ReturnType<typeof readLoanBankResultDetails>> | null>(null);
const [action, setAction] = useState<string | null>(null); const [target, setTarget] = useState("");
const [pendingIds, setPendingIds] = useState<string[]>([]);
// The upload dialog closes before the batch detail refetch lands; until then a
// just-uploaded document is missing from the evidence lists.
const [refreshingEvidence, setRefreshingEvidence] = useState(false);
const [values, setValues] = useState<Record<string, string>>({}); const [file, setFile] = useState<File | null>(null);
const value = (key: string) => values[key] ?? "";
const set = (key: string, next: string) => setValues((old) => ({ ...old, [key]: next }));
@@ -59,12 +62,16 @@ export function LoanBankReconciliation({ repayment: r, attachments }: { repaymen
if (action === "event" || action === "refund") await saveLoanAction(`${base}/loan-bank-results`, { kind: action === "refund" ? "return" : "late_success", ...(action === "refund" ? { parentId: target } : {}), evidenceId: value("evidence"), requestKey: crypto.randomUUID(), attributionNote: note, exclusionNote: value("exclusion"), fields: [{ Name: "付款账号", Value: value("payer") }, { Name: "收款账号", Value: value("payee") }, { Name: "金额", Value: value("amount") }, { Name: "币种", Value: "CNY" }, { Name: "交易状态", Value: "交易成功" }, { Name: "流水号", Value: value("reference") }] });
if (action === "reviewEvent") await saveLoanAction(`${base}/loan-bank-results/${target}/review`, { ...reviewBody, ...(decision === "reject" && value("evidence") ? { correctionEvidenceId: value("evidence"), correctionKind: value("correction") || "misassociated" } : {}) });
if (action === "apply") await saveLoanAction(`${base}/loan-bank-results/${target}/apply`, {});
}, onSuccess: async () => { if (action === "details") return; setAction(null); await Promise.all(["payment-batches", "company-loans", "loan-repayments"].map((key) => cache.invalidateQueries({ queryKey: [key] }))); } });
}, onSuccess: async () => {
if (action === "details") return; const uploaded = action === "upload"; setAction(null); if (uploaded) setRefreshingEvidence(true);
try { await Promise.all(["payment-batches", "company-loans", "loan-repayments"].map((key) => cache.invalidateQueries({ queryKey: [key] }))); }
finally { if (uploaded) setRefreshingEvidence(false); }
} });
function open(name: string, id = "") { setAction(name); setTarget(id); setValues({}); setPendingIds([]); setDetails(null); setFile(null); mutation.reset(); }
const button = (name: string, id = "") => <Button key={`${name}:${id}`} type="button" variant="outline" onClick={() => open(name, id)}>{actionLabel(name)}</Button>;
const input = (key: string, label: string, type = "text") => <label className="grid gap-2 text-sm" key={key}>{label}<Input required type={type} value={value(key)} onChange={(e) => set(key, e.target.value)} /></label>;
const select = (key: string, label: string, choices: Array<[string, string]>, required = true) => <label className="grid gap-2 text-sm" key={key}>{label}<select aria-label={label} required={required} className="min-h-11 min-w-0 w-full rounded border px-3" value={value(key)} onChange={(e) => set(key, e.target.value)}><option value="">请选择{required ? "" : "(可不选)"}</option>{choices.map(([id, title]) => <option key={id} value={id}>{title}</option>)}</select></label>;
const evidenceSelect = (key: string, label: string, kind: string, required = true) => select(key, label, evidences.filter((e) => !e.voided && e.evidenceKind === kind).map((e) => [e.id, e.originalName]), required);
const select = (key: string, label: string, choices: Array<[string, string]>, required = true, refreshing = false) => <label className="grid gap-2 text-sm" key={key}>{label}<select aria-label={label} required={required} disabled={refreshing} aria-busy={refreshing || undefined} className="min-h-11 min-w-0 w-full rounded border px-3 disabled:opacity-60" value={value(key)} onChange={(e) => set(key, e.target.value)}><option value="">{refreshing ? "正在刷新证据列表…" : `请选择${required ? "" : "(可不选)"}`}</option>{choices.map(([id, title]) => <option key={id} value={id}>{title}</option>)}</select>{refreshing ? <span role="status" className="text-sm text-amber-800">正在刷新证据列表,刚上传的证据稍后可选</span> : null}</label>;
const evidenceSelect = (key: string, label: string, kind: string, required = true) => select(key, label, evidences.filter((e) => !e.voided && e.evidenceKind === kind).map((e) => [e.id, e.originalName]), required, refreshingEvidence);
const exportLabel = (id: string | null) => id ? `第 ${exports.find((e) => e.id === id)?.sequence ?? "未知"} 次导出` : "系统外手工提交";
const submissionLabel = (id: string) => submissions.find((s) => s.id === id)?.bankInstructionRef ?? "提交记录已不可用";
const evidenceLink = (id: string) => <Button type="button" variant="outline" onClick={() => open("download", id)}>证据 {evidences.findIndex((e) => e.id === id) + 1}:{evidences.find((e) => e.id === id)?.originalName ?? "不可用"}</Button>;
@@ -105,7 +112,7 @@ export function LoanBankReconciliation({ repayment: r, attachments }: { repaymen
{action === "reviewEvent" && value("decision") === "reject" ? <>{evidenceSelect("evidence", "误关联或重复登记的更正证据(晚到成功必填)", "bank_terminal", false)}{select("correction", "关闭原因", [["misassociated", "误关联"], ["duplicate", "重复登记"]], false)}</> : null}
{action === "event" || action === "refund" ? <>{evidenceSelect("evidence", "对应的成功银行回单", action === "refund" ? "incoming_return" : "late_success")}{input("payer", "回单付款账号")}{input("payee", "回单收款账号")}{input("amount", "回单人民币金额")}{input("reference", "回单流水号")}{input("exclusion", "为什么不属于其他候选批次或交易")}</> : null}
{!['upload', 'submitRecovery', 'confirm', 'apply'].includes(action ?? '') ? <label className="grid gap-2 text-sm">依据与说明<textarea required minLength={10} maxLength={500} className="min-h-24 rounded border px-3 py-2" value={value("note")} onChange={(e) => set("note", e.target.value)} /></label> : null}
<Button type="submit" disabled={mutation.isPending}>{mutation.isPending ? "处理中…" : "确认"}</Button>
<Button type="submit" disabled={mutation.isPending || refreshingEvidence}>{mutation.isPending ? "处理中…" : "确认"}</Button>
</form>
</DialogContent></Dialog>
</section>;

某些文件未显示,因为此 diff 中更改的文件太多 显示更多