|
20265
|
5
|
5
|
5
|
72
|
0
|
0
|
refs/heads/fix/contracts-build-chain-and-review-pa refs/heads/fix/contracts-build-chain-and-review-panel...
|
0
|
{"Commits":[{"Sha1":"67849d6aa {"Commits":[{"Sha1":"67849d6aad796878e6fd40b2e54307780b01442f","Message":"chore: 重跑三级门禁并绑定干净工作区证据\n\n上一提交的报告生成于 dirty 工作区(gitSha 指向父提交 7638e94、\nworktreeDirty=true),只能支撑「本地工作区通过」。此处在 a015400 的\n干净检出上重跑三级门禁,让三份 latest 报告的 provenance 统一绑定\n该提交且 worktreeDirty=false——可支撑「提交 a015400 在本机干净工作区\n通过三级门禁」。\n\npnpm check ✅ 5/5\npnpm check:runtime ✅ 83 用例 / 0 失败(PG:55432/juhai_quotation_runtime_20260731d + Redis:6382)\npnpm check:ui ✅ 8 用例 / 0 失败(PG:55432/juhai_quotation_ui_20260731d + Redis:6382)\n\n仍不得外推为远端已发布或 CI 已通过:本分支未推送,无远端 run 证据(G14)。\n\nCo-Authored-By: Claude Opus 5 \u003cnoreply@anthropic.com\u003e\n","AuthorEmail":"luoguoguo@gmail.com","AuthorName":"luoguoguo","CommitterEmail":"luoguoguo@gmail.com","CommitterName":"luoguoguo","Timestamp":"2026-07-31T06:16:25-07:00"},{"Sha1":"a015400b8aa901246315eb213d20354d6ded3133","Message":"fix: 打通契约构建链并补齐方案复核依据\n\n本提交包含两批改动。工作区里它们已交织到同一行/同一 hunk\n(package.json 的 check 串、next.config.ts 的 output),无法拆开单独提交。\n\n── 批次 A:本轮定位并修复(Claude 作业)────────────────────────────\n\n契约包 src/dist 漂移(C23)——P1「运行时到底读哪一份」的隐藏变体:\ntypecheck 与 contracts 单测读 src,两后端运行时经 package main 读 dist。\n`pnpm --filter` 不走 turbo 的 dependsOn ^build,dist 又被 gitignore,\n于是门禁全绿而运行时按旧契约行为。实测症状:dist 缺 versionNo →\nZod 静默 strip → 技术复核恒返 409 STALE_VERSION。\n负向验证:移走 dist 后 typecheck 立刻 TS2307,证明全新 clone/CI 上必红。\n- package.json: check 前置 build:contracts\n- scripts/check-{runtime,ui}-acceptance.mjs: 各加 contracts 构建步骤\n- .claude/launch.json: 四个配置均前置 contracts 构建\n- packages/contracts/package.json: 补 prepare;clean 补删 tsconfig.tsbuildinfo\n (composite 下留着 buildinfo 会让 clean+build 空 emit)\n\n方案表单默认路径不可用(C24):\n「使用客户主联系人」value 为空串,contactId 契约是 uuid().optional(),\n空串不过校验,导致默认状态下表单永不可提交,错误还被汇总成一句\n指不到联系人的话。register 处归一为 undefined。\n\n客户方案复核依据面板:\n技术复核/经理批准需在同一屏看到客户需求、空间与工区、平面图、逐档配置清单。\n清单只渲染规格字段,刻意不展开 product 以免带出 standardPriceCents\n(角色权限单源里技术角色不继承报价域敏感读取)。缺图纸显式标注但不阻断。\n补填资料入口接上此前零消费者的 useUpdateSolution + PATCH /solutions/:id,\n仅在 DRAFT/NEEDS_INFORMATION/REVISION_REQUIRED 开放——修复「目标客群留空\n的方案走到 APPROVED 后永远发布不了且无处补填」的死路。\n\n契约类型补齐:SolutionProduct 增补 product/surveySpace 嵌套(服务端一直下发,\n此前类型比载荷窄)。\n\nE2E 回归锁:新增专打默认联系人路径的用例,兼测复核依据面板与价格红线。\n负向验证:撤回 contactId 修复 → check:ui 报红;还原 → 8 用例全绿。\n\n附带:next.config.ts 的 output 重复定义(TS2783,本轮之前即存在,\n导致 pnpm check 红)改为三元计算,行为等价;check-vm-deploy 的 standalone\n绊网相应收窄为「字面量或该正确三元」,不放宽成 /output:.*standalone/\n(负向验证:三元写反即红)。\n\n── 批次 B:进入本轮前工作区已有的未提交改动(非本轮作业)──────────\n\nVM 部署强化(Dockerfile/compose/vm-deploy.sh/check-vm-deploy.mjs、restore/\ndrill/rollback)、移动端导航 MobileNav、客户方案域后端与验收测试、\n角色权限与模块对接门禁、方案蓝图与验收记录等。这批未经逐项复核,\n如需单独审阅可 `git reset --soft HEAD~1` 后重新拆分。\n\n── 证据(本地工作区,2026-07-31,工作区 dirty)──────────────────\npnpm check ✅ 5/5\npnpm check:runtime ✅ 83 用例 / 0 失败(地板 79→83)\npnpm check:ui ✅ 8 用例 / 0 失败(地板 7→8)\nownTestCases 地板 90→91。未推送、无远端 CI 运行证据(G14 仍 OPEN)。\n\nCo-Authored-By: Claude Opus 5 \u003cnoreply@anthropic.com\u003e\n","AuthorEmail":"luoguoguo@gmail.com","AuthorName":"luoguoguo","CommitterEmail":"luoguoguo@gmail.com","CommitterName":"luoguoguo","Timestamp":"2026-07-31T06:13:59-07:00"}],"HeadCommit":{"Sha1":"67849d6aad796878e6fd40b2e54307780b01442f","Message":"chore: 重跑三级门禁并绑定干净工作区证据\n\n上一提交的报告生成于 dirty 工作区(gitSha 指向父提交 7638e94、\nworktreeDirty=true),只能支撑「本地工作区通过」。此处在 a015400 的\n干净检出上重跑三级门禁,让三份 latest 报告的 provenance 统一绑定\n该提交且 worktreeDirty=false——可支撑「提交 a015400 在本机干净工作区\n通过三级门禁」。\n\npnpm check ✅ 5/5\npnpm check:runtime ✅ 83 用例 / 0 失败(PG:55432/juhai_quotation_runtime_20260731d + Redis:6382)\npnpm check:ui ✅ 8 用例 / 0 失败(PG:55432/juhai_quotation_ui_20260731d + Redis:6382)\n\n仍不得外推为远端已发布或 CI 已通过:本分支未推送,无远端 run 证据(G14)。\n\nCo-Authored-By: Claude Opus 5 \u003cnoreply@anthropic.com\u003e\n","AuthorEmail":"luoguoguo@gmail.com","AuthorName":"luoguoguo","CommitterEmail":"luoguoguo@gmail.com","CommitterName":"luoguoguo","Timestamp":"2026-07-31T06:16:25-07:00"},"CompareURL":"luoanwu/juhai-quotation-system/compare/7638e946e8817308b94a23c028a18ac465c342c6...67849d6aad796878e6fd40b2e54307780b01442f","Len":2}...
|
1785504181
|
Edit
Delete
|
|
20266
|
5
|
5
|
5
|
72
|
0
|
0
|
refs/heads/fix/contracts-build-chain-and-review-pa refs/heads/fix/contracts-build-chain-and-review-panel...
|
0
|
{"Commits":[{"Sha1":"0d85a3de6 {"Commits":[{"Sha1":"0d85a3de6efbf4ca42ba1345bfbc66f9d1f9ac0d","Message":"docs: 按真实 CI 证据更正 C23 与 G14\n\n上一提交把一个未经正确验证的结论写进了治理真源,此处更正。\n\nC23 原文断言「全新 clone/CI 上 pnpm check 必红(contracts dist 不存在)」。\n该结论来自 `pnpm --filter \u003capp\u003e typecheck` 的负向测试,而它绕过 turbo;\nCI 实际走 `pnpm typecheck` → `turbo run typecheck`,而 turbo.json 的\ntypecheck/lint/test 均带 dependsOn ^build,contracts 会被自动构建。\nGitHub run 30603461723 的 static job 实际通过,证伪原结论。\nC23 仍然成立的部分:绕过 turbo 的调用路径(.claude/launch.json 直接\npnpm dev、check:ui 的 pnpm --filter build)确实会吃到陈旧 dist——本轮\n409 STALE_VERSION 即由此而来;clean 不删 tsconfig.tsbuildinfo 导致\nclean \u0026\u0026 build 空 emit 是独立真 bug。修复本身保留。\n\nG14 原记「尚无远端 CI 运行证据」已过时。实际已有 run 30603461723\n(push main fdf7b59,2026-07-31T04:10Z):static ✅、runtime ❌\n`tests-floor: expected \u003e= 74, got 0`,整步仅 24s,真实 DB 测试没跑起来。\n地板断言拦住了这次「0 tests 假绿」,但根因未定位,需专项排查 CI 上\nturbo run test --force 为何产不出 vitest 报告。基线表 ci-gate 行同步\n改为「已有远端证据但为红盘」,仍 OPEN。\n\n另记:两个远端 main 不同步(origin 7638e94 / github fdf7b59),\nCI 只挂 github 侧,且只在 pull_request 或 push main 触发。\n\npnpm check ✅ 绿(含 check:governance-docs / check:docs-truth)。\n\nCo-Authored-By: Claude Opus 5 \u003cnoreply@anthropic.com\u003e\n","AuthorEmail":"luoguoguo@gmail.com","AuthorName":"luoguoguo","CommitterEmail":"luoguoguo@gmail.com","CommitterName":"luoguoguo","Timestamp":"2026-07-31T06:30:18-07:00"}],"HeadCommit":{"Sha1":"0d85a3de6efbf4ca42ba1345bfbc66f9d1f9ac0d","Message":"docs: 按真实 CI 证据更正 C23 与 G14\n\n上一提交把一个未经正确验证的结论写进了治理真源,此处更正。\n\nC23 原文断言「全新 clone/CI 上 pnpm check 必红(contracts dist 不存在)」。\n该结论来自 `pnpm --filter \u003capp\u003e typecheck` 的负向测试,而它绕过 turbo;\nCI 实际走 `pnpm typecheck` → `turbo run typecheck`,而 turbo.json 的\ntypecheck/lint/test 均带 dependsOn ^build,contracts 会被自动构建。\nGitHub run 30603461723 的 static job 实际通过,证伪原结论。\nC23 仍然成立的部分:绕过 turbo 的调用路径(.claude/launch.json 直接\npnpm dev、check:ui 的 pnpm --filter build)确实会吃到陈旧 dist——本轮\n409 STALE_VERSION 即由此而来;clean 不删 tsconfig.tsbuildinfo 导致\nclean \u0026\u0026 build 空 emit 是独立真 bug。修复本身保留。\n\nG14 原记「尚无远端 CI 运行证据」已过时。实际已有 run 30603461723\n(push main fdf7b59,2026-07-31T04:10Z):static ✅、runtime ❌\n`tests-floor: expected \u003e= 74, got 0`,整步仅 24s,真实 DB 测试没跑起来。\n地板断言拦住了这次「0 tests 假绿」,但根因未定位,需专项排查 CI 上\nturbo run test --force 为何产不出 vitest 报告。基线表 ci-gate 行同步\n改为「已有远端证据但为红盘」,仍 OPEN。\n\n另记:两个远端 main 不同步(origin 7638e94 / github fdf7b59),\nCI 只挂 github 侧,且只在 pull_request 或 push main 触发。\n\npnpm check ✅ 绿(含 check:governance-docs / check:docs-truth)。\n\nCo-Authored-By: Claude Opus 5 \u003cnoreply@anthropic.com\u003e\n","AuthorEmail":"luoguoguo@gmail.com","AuthorName":"luoguoguo","CommitterEmail":"luoguoguo@gmail.com","CommitterName":"luoguoguo","Timestamp":"2026-07-31T06:30:18-07:00"},"CompareURL":"luoanwu/juhai-quotation-system/compare/67849d6aad796878e6fd40b2e54307780b01442f...0d85a3de6efbf4ca42ba1345bfbc66f9d1f9ac0d","Len":1}...
|
1785504640
|
Edit
Delete
|
|
20267
|
5
|
5
|
5
|
72
|
0
|
0
|
refs/heads/fix/contracts-build-chain-and-review-pa refs/heads/fix/contracts-build-chain-and-review-panel...
|
0
|
{"Commits":[{"Sha1":"de7d09aee {"Commits":[{"Sha1":"de7d09aee2a89208f006bc3ca829f289619a64b7","Message":"docs: 记录首份远端 CI 绿盘证据(PR #1 / run 30634897066)\n\nGitHub run 30634897066(PR #1,head 0d85a3d)两 job 全绿:\nstatic ✅;runtime 83 用例(contracts 31 / nestjs 26 / fastify 26)、\nUI 8 用例,与本地逐项一致,耗时 3m21s。这是本仓第一份远端绿盘证据。\n\n对照上一次 run 30603461723(push main fdf7b59):runtime 失败于\ntests-floor「expected \u003e= 74, got 0」,整步仅 24s。\n\n刻意不把这次转绿归因到本轮任何一个提交:PR #1 含 7 个提交,除本轮 3 个外\n还有 4 个存量提交(283f1bf…7638e94),其中任一都可能是真正修好它的那个,\n且 7638e94 从未单独在 CI 上跑过。要归因需另构造对照 run。\n\nG14 仍 OPEN:绿盘只覆盖该 PR 分支,github main 最近一次 run 仍红;\n尚未用受控失败 PR 证明违规真被阻断,分支保护 / required checks 也未定位。\nci-gate 行同步改写,明确「不得宣称 PR 已被机器拦截」。\n\npnpm check ✅ 绿。\n\nCo-Authored-By: Claude Opus 5 \u003cnoreply@anthropic.com\u003e\n","AuthorEmail":"luoguoguo@gmail.com","AuthorName":"luoguoguo","CommitterEmail":"luoguoguo@gmail.com","CommitterName":"luoguoguo","Timestamp":"2026-07-31T06:39:36-07:00"}],"HeadCommit":{"Sha1":"de7d09aee2a89208f006bc3ca829f289619a64b7","Message":"docs: 记录首份远端 CI 绿盘证据(PR #1 / run 30634897066)\n\nGitHub run 30634897066(PR #1,head 0d85a3d)两 job 全绿:\nstatic ✅;runtime 83 用例(contracts 31 / nestjs 26 / fastify 26)、\nUI 8 用例,与本地逐项一致,耗时 3m21s。这是本仓第一份远端绿盘证据。\n\n对照上一次 run 30603461723(push main fdf7b59):runtime 失败于\ntests-floor「expected \u003e= 74, got 0」,整步仅 24s。\n\n刻意不把这次转绿归因到本轮任何一个提交:PR #1 含 7 个提交,除本轮 3 个外\n还有 4 个存量提交(283f1bf…7638e94),其中任一都可能是真正修好它的那个,\n且 7638e94 从未单独在 CI 上跑过。要归因需另构造对照 run。\n\nG14 仍 OPEN:绿盘只覆盖该 PR 分支,github main 最近一次 run 仍红;\n尚未用受控失败 PR 证明违规真被阻断,分支保护 / required checks 也未定位。\nci-gate 行同步改写,明确「不得宣称 PR 已被机器拦截」。\n\npnpm check ✅ 绿。\n\nCo-Authored-By: Claude Opus 5 \u003cnoreply@anthropic.com\u003e\n","AuthorEmail":"luoguoguo@gmail.com","AuthorName":"luoguoguo","CommitterEmail":"luoguoguo@gmail.com","CommitterName":"luoguoguo","Timestamp":"2026-07-31T06:39:36-07:00"},"CompareURL":"luoanwu/juhai-quotation-system/compare/0d85a3de6efbf4ca42ba1345bfbc66f9d1f9ac0d...de7d09aee2a89208f006bc3ca829f289619a64b7","Len":1}...
|
1785505181
|
Edit
Delete
|
|
20272
|
5
|
17
|
5
|
72
|
0
|
0
|
refs/heads/fix/contracts-build-chain-and-review-pa refs/heads/fix/contracts-build-chain-and-review-panel...
|
0
|
|
1785507126
|
Edit
Delete
|
|
20282
|
5
|
5
|
5
|
72
|
0
|
0
|
refs/heads/feat/solution-tier-multi-product
|
0
|
|
1785590611
|
Edit
Delete
|
|
20283
|
5
|
5
|
5
|
72
|
0
|
0
|
refs/heads/feat/solution-tier-multi-product
|
0
|
{"Commits":[{"Sha1":"815f9b634 {"Commits":[{"Sha1":"815f9b634be98eeea1ead07bc5daaf37dec909e0","Message":"feat(solution): 三档方案支持每档多商品,数量默认取项目包房总数\n\n## 多商品\n契约 `solutionOptionInputSchema.products` 一直是 `.min(1).max(500)`,后端逐条落\nSolutionOptionProduct、转报价桥接逐条转成报价明细、客户页也已在显示「配置 N 项」——\n整条链路早就支持多商品,**只有设计器表单把它写死成单元素数组**,能力在用户唯一\n够得着的那一层被锁掉(同 C25/C26 族:能力挂在哪一层决定它存不存在)。\n\n改法复用仓内既有 AccessoryEditor 模式:选择 + 数量 → 添加成可删除的清单行。\n刻意不用 `\u003cselect multiple\u003e`:每个商品要带自己的数量,多选框表达不了;且 Select\n原语的注释明确禁止把它改造成多选(E2E 按 locator(\"select\") 定位、Capacitor 依赖\n原生选择器)。同档重复添加同一商品累加数量,不并排出现两行肉眼一样的配置。\n\n## 数量默认值\n默认 = 本项目包房总数(kind===\"ROOM\" 的空间数量求和),只预填不锁定。\n回落 1 而非 0:契约 quantity 是 int().positive(),预填 0 会让按钮看着能点、\n提交却被 Zod 挡下(C24 同款:默认值不合法 = 表单永远提交不了)。\nUI 显式写明「数量默认按本项目包房总数 N 间预填,可逐项调整」,不做魔法数字。\n\n## 顺带修掉一个跨项目状态串味\nSolutionDesigner 的已选商品与包房数预填都是挂载时求值的本地状态,\n渲染处此前无 key——切换项目不重挂,会把上一个项目的选择带过去。已加 key={project.id}。\n\n## 证据(本地工作区 2026-08-01)\n- pnpm check exit 0;check:runtime 279/0;check:ui 9/0\n- 端到端读回:推荐档配 2 个商品 → 客户页断言「配置 2 项」→ 成功转成正式报价\n- 负向测试两条均实跑判红后恢复:\n ① addProduct 改为 `products: [line]`(回潮到单商品)→ 恰好 solution-product-row\n 期望 2 实得 1 判红;\n ② roomTotal 改为常量 1 → 恰好 待添加数量 期望 \"8\" 实得 \"1\" 判红\n\nCo-Authored-By: Claude Fable 5 \u003cnoreply@anthropic.com\u003e\n","AuthorEmail":"luoguoguo@gmail.com","AuthorName":"luoguoguo","CommitterEmail":"luoguoguo@gmail.com","CommitterName":"luoguoguo","Timestamp":"2026-08-01T06:23:24-07:00"}],"HeadCommit":{"Sha1":"815f9b634be98eeea1ead07bc5daaf37dec909e0","Message":"feat(solution): 三档方案支持每档多商品,数量默认取项目包房总数\n\n## 多商品\n契约 `solutionOptionInputSchema.products` 一直是 `.min(1).max(500)`,后端逐条落\nSolutionOptionProduct、转报价桥接逐条转成报价明细、客户页也已在显示「配置 N 项」——\n整条链路早就支持多商品,**只有设计器表单把它写死成单元素数组**,能力在用户唯一\n够得着的那一层被锁掉(同 C25/C26 族:能力挂在哪一层决定它存不存在)。\n\n改法复用仓内既有 AccessoryEditor 模式:选择 + 数量 → 添加成可删除的清单行。\n刻意不用 `\u003cselect multiple\u003e`:每个商品要带自己的数量,多选框表达不了;且 Select\n原语的注释明确禁止把它改造成多选(E2E 按 locator(\"select\") 定位、Capacitor 依赖\n原生选择器)。同档重复添加同一商品累加数量,不并排出现两行肉眼一样的配置。\n\n## 数量默认值\n默认 = 本项目包房总数(kind===\"ROOM\" 的空间数量求和),只预填不锁定。\n回落 1 而非 0:契约 quantity 是 int().positive(),预填 0 会让按钮看着能点、\n提交却被 Zod 挡下(C24 同款:默认值不合法 = 表单永远提交不了)。\nUI 显式写明「数量默认按本项目包房总数 N 间预填,可逐项调整」,不做魔法数字。\n\n## 顺带修掉一个跨项目状态串味\nSolutionDesigner 的已选商品与包房数预填都是挂载时求值的本地状态,\n渲染处此前无 key——切换项目不重挂,会把上一个项目的选择带过去。已加 key={project.id}。\n\n## 证据(本地工作区 2026-08-01)\n- pnpm check exit 0;check:runtime 279/0;check:ui 9/0\n- 端到端读回:推荐档配 2 个商品 → 客户页断言「配置 2 项」→ 成功转成正式报价\n- 负向测试两条均实跑判红后恢复:\n ① addProduct 改为 `products: [line]`(回潮到单商品)→ 恰好 solution-product-row\n 期望 2 实得 1 判红;\n ② roomTotal 改为常量 1 → 恰好 待添加数量 期望 \"8\" 实得 \"1\" 判红\n\nCo-Authored-By: Claude Fable 5 \u003cnoreply@anthropic.com\u003e\n","AuthorEmail":"luoguoguo@gmail.com","AuthorName":"luoguoguo","CommitterEmail":"luoguoguo@gmail.com","CommitterName":"luoguoguo","Timestamp":"2026-08-01T06:23:24-07:00"},"CompareURL":"luoanwu/juhai-quotation-system/compare/ccbb031fdb47bc913ed2b4ba270c0a49889eb88d...815f9b634be98eeea1ead07bc5daaf37dec909e0","Len":1}...
|
1785590611
|
Edit
Delete
|
|
20284
|
5
|
5
|
5
|
72
|
0
|
0
|
refs/heads/feat/solution-tier-multi-product
|
0
|
{"Commits":[{"Sha1":"b0ed5cba7 {"Commits":[{"Sha1":"b0ed5cba764d5df24360ea70486bad9b6885431d","Message":"feat(solution): 数量来源可选空间分类或手动输入,选品增加商品分类筛选\n\n## 数量来源可选\n此前默认值写死「包房总数」。但走廊音响、前台设备、设备间机柜这类根本不按包房走,\n把包房硬编码成唯一口径等于只服务一种配置习惯。\n\n改为下拉可选:**按任一空间分类的数量合计**(包房/前台/走廊/水吧/办公室/设备间/其他,\n选项上直接标出该类当前数量)或**手动输入**。默认仍落在包房——点歌设备按包房逐间\n配是这门生意的常识配比。\n\n- 按空间分类走时数量输入框只读:避免出现「显示按包房、值却被手改过」这种谁也说不清\n 依据的数字;要自己填就显式切到「手动输入」。\n- 取值一律 Math.max(1, …):契约 quantity 是 int().positive(),项目里没录前台时\n 合计为 0,预填 0 会让「添加」看着能点、提交却被 Zod 挡下(C24 同款教训)。\n- 空间分类总数用枚举 reduce 统一算,不给某一类空间写死分支。\n\n## 选品增加商品分类\n选品下拉前置「全部分类 / 设备 / 服务 / 配件」筛选(沿用商品库同名口径,不另造说法)。\n切换分类时清空已选商品——否则下拉显示的是新分类、value 仍指向旧分类那件商品,\n提交的是用户此刻根本看不见的东西。\n\n添加一件后**保留分类与来源**、只清商品:连着配同一类设备是常态,每加一件就把筛选\n重置回默认等于逼人重选一遍。\n\n来源与分类只是挑选时的辅助,不进已选行——存进去会让配置多出两个读模型不消费、\n契约也不收的字段。\n\n## 证据(本地工作区 2026-08-01)\n- pnpm check exit 0;check:runtime 279/0;check:ui 9/0\n- E2E 新增断言:切「配件」后候选从 4 项收窄到 2 项(证明筛选真的干活);\n 切「手动输入」填 3 后该行数量为 3,而主设备那行仍是按包房总数的 8\n (证明两种来源可在同一档并存)\n\nCo-Authored-By: Claude Fable 5 \u003cnoreply@anthropic.com\u003e\n","AuthorEmail":"luoguoguo@gmail.com","AuthorName":"luoguoguo","CommitterEmail":"luoguoguo@gmail.com","CommitterName":"luoguoguo","Timestamp":"2026-08-01T06:40:01-07:00"},{"Sha1":"18359985bc7144f7c67c3f17d80e7f848770d956","Message":"fix(ci): 让 UI 验收红盘可判责——失败时打印捕获输出并上传证据产物\n\nPR #6 的 Runtime and UI acceptance 在远端判红,但日志里只有一行\n「✗ playwright-test failed (exit 1)」,看不出是哪条断言、为什么失败。\n\n根因:check-ui-acceptance.mjs 的 runSync 把子进程 stdout/stderr 收进 report JSON\n的 stdoutTail/stderrTail 后**从不打印**,而 workflow 又不上传 report——\n证据在当场就被丢掉了。G23 ④ 记的「2026-07-31 红盘 tests-floor got 0 根因至今\n未定位」是同一个病根。\n\n- runSync 失败时把两条尾巴打到 stderr\n- workflow 失败时上传 reports/*.latest.json + e2e-results.json + playwright traces\n\n本机以全新库复跑 check:ui 为 9/9 全绿,无法复现远端红盘;按治理纪律\n「根因未定位不得归因」,先补证据链再判是偶发还是回归。\n\nCo-Authored-By: Claude Fable 5 \u003cnoreply@anthropic.com\u003e\n","AuthorEmail":"luoguoguo@gmail.com","AuthorName":"luoguoguo","CommitterEmail":"luoguoguo@gmail.com","CommitterName":"luoguoguo","Timestamp":"2026-08-01T06:33:17-07:00"}],"HeadCommit":{"Sha1":"b0ed5cba764d5df24360ea70486bad9b6885431d","Message":"feat(solution): 数量来源可选空间分类或手动输入,选品增加商品分类筛选\n\n## 数量来源可选\n此前默认值写死「包房总数」。但走廊音响、前台设备、设备间机柜这类根本不按包房走,\n把包房硬编码成唯一口径等于只服务一种配置习惯。\n\n改为下拉可选:**按任一空间分类的数量合计**(包房/前台/走廊/水吧/办公室/设备间/其他,\n选项上直接标出该类当前数量)或**手动输入**。默认仍落在包房——点歌设备按包房逐间\n配是这门生意的常识配比。\n\n- 按空间分类走时数量输入框只读:避免出现「显示按包房、值却被手改过」这种谁也说不清\n 依据的数字;要自己填就显式切到「手动输入」。\n- 取值一律 Math.max(1, …):契约 quantity 是 int().positive(),项目里没录前台时\n 合计为 0,预填 0 会让「添加」看着能点、提交却被 Zod 挡下(C24 同款教训)。\n- 空间分类总数用枚举 reduce 统一算,不给某一类空间写死分支。\n\n## 选品增加商品分类\n选品下拉前置「全部分类 / 设备 / 服务 / 配件」筛选(沿用商品库同名口径,不另造说法)。\n切换分类时清空已选商品——否则下拉显示的是新分类、value 仍指向旧分类那件商品,\n提交的是用户此刻根本看不见的东西。\n\n添加一件后**保留分类与来源**、只清商品:连着配同一类设备是常态,每加一件就把筛选\n重置回默认等于逼人重选一遍。\n\n来源与分类只是挑选时的辅助,不进已选行——存进去会让配置多出两个读模型不消费、\n契约也不收的字段。\n\n## 证据(本地工作区 2026-08-01)\n- pnpm check exit 0;check:runtime 279/0;check:ui 9/0\n- E2E 新增断言:切「配件」后候选从 4 项收窄到 2 项(证明筛选真的干活);\n 切「手动输入」填 3 后该行数量为 3,而主设备那行仍是按包房总数的 8\n (证明两种来源可在同一档并存)\n\nCo-Authored-By: Claude Fable 5 \u003cnoreply@anthropic.com\u003e\n","AuthorEmail":"luoguoguo@gmail.com","AuthorName":"luoguoguo","CommitterEmail":"luoguoguo@gmail.com","CommitterName":"luoguoguo","Timestamp":"2026-08-01T06:40:01-07:00"},"CompareURL":"luoanwu/juhai-quotation-system/compare/815f9b634be98eeea1ead07bc5daaf37dec909e0...b0ed5cba764d5df24360ea70486bad9b6885431d","Len":2}...
|
1785591608
|
Edit
Delete
|
|
20285
|
5
|
5
|
5
|
72
|
0
|
0
|
refs/heads/feat/solution-tier-multi-product
|
0
|
{"Commits":[{"Sha1":"47ce4c614 {"Commits":[{"Sha1":"47ce4c6146a13c443db38c5816749d0591339e46","Message":"fix(quotations): 视图筛选下沉服务端 + 公开方案页不再泄漏后端英文报错\n\n## 1. 三个筛选视图的分页回归(G24 引入,本次修复)\n审核中心 / 客户报价页 / 报价版本 原先在浏览器里对列表结果 filter。列表全量返回时\n那样是对的,但 G24 加分页后客户端筛选只作用于**当前页**:待审报价不在最新一页\n就会被静默漏掉,而分页条的 total 仍是全量数,出现「共 N 条却一行不显示」的错位。\n与 C27「聚合范围由前端恰好拿到多少行决定」同族——筛选范围必须由拥有数据的一层决定。\n\n- contracts 新增 QUOTATION_VIEW_FILTERS 单源(inbox/review/customer/versions),\n 两后端各自翻译成 Prisma where;total 随之变成「该视图下的全量」;\n- quotationListViewSchema 刻意不 extend paginationQuerySchema——那会让\n quotation.ts 反向 import pagination.ts 的值,与既有 type import 形成循环;\n- 非法 view 是 400 而不是静默退回全部(静默兜底会让筛选看着生效实则没有);\n- 前端只保留一条**权限谓词**(无 customer_link.read 读不到 accessToken):\n 它对同一角色恒为全真或全假,不会像数据谓词那样把行留在分页窗口外。\n\n⚠️ 期间发现 QuoteOption.accessToken 是 @default(uuid()) **非空**,\n原「客户报价页」的 options.some(accessToken) 实际是权限谓词而非数据谓词。\n\n## 2. 公开方案页把后端英文报错渲染给客户\nPublicCustomerSolutionView 直接渲染 query.error.message,客户看到的是\n「Solution project not found」——看不懂、像坏了,还把内部对象名暴露出去。\n改为统一中文文案,且**不区分**链接无效/未发布/返工中:区分开等于告诉持链接的人\n「方案确实存在,只是没轮到你看」,而服务端刻意用 404 而非 403 正是为了不泄露存在性\n(方案蓝图 S1 审计裁决)。前端说破,那道防线就白立了。\n\n## 门禁断言随执行点搬家(不是放宽)\nquotation-review-server-state / quotation-version-snapshot 钉的是被移走的客户端谓词。\n不变量没变、执行点变了,故改为钉:前端必须把 mode 作为 view 传下去、\n两后端必须从 contracts 单源翻译 where,并加 reject 断言禁止客户端重新实现视图谓词。\n\n## 证据(本地工作区 2026-08-01)\n- pnpm check 0 / check:runtime 294 / check:ui 9\n- 两后端各 +3 条断言(view=review 只回待审、total 随筛选收窄、非法 view 400)\n- 负向两条实跑判红后恢复:\n ① 服务端忽略 view → view=review 期望 1 实得 2 判红\n ② 视图谓词写回客户端 → quotation-view-client-side-filter 判红\n\nCo-Authored-By: Claude Fable 5 \u003cnoreply@anthropic.com\u003e\n","AuthorEmail":"luoguoguo@gmail.com","AuthorName":"luoguoguo","CommitterEmail":"luoguoguo@gmail.com","CommitterName":"luoguoguo","Timestamp":"2026-08-01T08:35:11-07:00"},{"Sha1":"b0ba9576541405a0613ae827ad3c80873437ef53","Message":"docs(flow): 重新梳理业务模块与主干流程,补齐审批中心并画 mermaid 流程图\n\nbusiness-flow-overview.md 写于同一天更早的提交,正文里一次都没提 approval-inbox\n(468a6cd 才落地的 9 类聚合)。本次按 contracts 与前端导航逐条核对后重梳理:\n\n- 新增 §6 待办审批中心:明确它是**横切在主干之上的视图,不是主干的一个节点**\n (无写端点、不重定义「什么算待审」、入口不设独立 capability 而挂 event.read),\n 并给出 9 类条目各自落在主干哪一步;第 8/9 类是待办不是审批闸门。\n- 新增 §1.1 业务模块分层,并标注 owner-matrix 里的 inventory/procurement/logistics\n **不是物理模块**:三个域的写链全部实现在 fulfillment.service.ts(~2100 行,\n 两后端各一份),按目录名去搜会扑空。\n- §3 补两条被 happy path 掩盖的主干分支:报价审批是**条件审批**\n (quotationMachine 的 DRAFT 同时接 send,服务端仅在存在 requiresApproval 的\n 方案档时抛 409 拦截);销售订单可先转 PROCUREMENT_REQUIRED,纯服务订单反向跳过发运。\n- §1 主干图 ASCII 换 mermaid(GitHub 可渲染),信息等价。\n\n仍不复制状态机全图——那是 P1 禁止的第二真源,图里只点 contracts 导出符号名。\n未新建流程图文档:本文件已是该事项的登记位置,另开一份即第二真源。\n\n证据:本地工作区 check:docs-truth / check:naming / check:governance-docs /\ncheck:gate-selftest 均绿(静态级,工作区 dirty,不外推远端与运行态)。\n\nCo-Authored-By: Claude Opus 5 \u003cnoreply@anthropic.com\u003e\n","AuthorEmail":"luoguoguo@gmail.com","AuthorName":"luoguoguo","CommitterEmail":"luoguoguo@gmail.com","CommitterName":"luoguoguo","Timestamp":"2026-08-01T08:08:20-07:00"},{"Sha1":"f315e642beda9d7ccc527955b2397f4702d33a9c","Message":"chore(gate): docs-truth 负向测试固化 + 幽灵资产清单扩面\n\n会话里手工跑一次的红盘不留证据,下轮重构会静默失效(G13)。把 docs-truth 的\n负向测试固化为 scripts/lib/docs-truth.test.mjs,由 check:gate-selftest 重跑,\ngateSelftests 地板 2 → 3。\n\n幽灵资产两条断言扩面:\n- no-phantom-config-prisma 清单补入 docs/README.md 与 naming-standard.md\n- 新增 contracts-package-name,递归扫描 docs/ 全域,真值取自\n packages/contracts/package.json 的 name(非黑名单)\n\n扩清单判据:该文件是在**主张**这条幽灵引用,还是在**否定**它?只有前者进清单——\nADR 澄清段与治理经验库引用它正是为宣告作废,扫进来即咬住纠错本身(C1 同类陷阱)。\n\nCLAUDE.md 会话收尾清单同步回灌两个已实锤两次的坑:注入点是否真落在目标断言上\n(replace 只换第一处),以及红是否红在点子上(崩溃/环境不满足同样非 0 退出)。\n\nCo-Authored-By: Claude Opus 5 \u003cnoreply@anthropic.com\u003e\n","AuthorEmail":"luoguoguo@gmail.com","AuthorName":"luoguoguo","CommitterEmail":"luoguoguo@gmail.com","CommitterName":"luoguoguo","Timestamp":"2026-08-01T08:08:01-07:00"},{"Sha1":"468a6cd2ab36d76237ae9c4d849eda17204d722c","Message":"feat(approval-inbox): 跨域待办审批中心(切片一)——9 类聚合 + 中心内直接审批\n\n经 business-module-intake 接待流程落地,蓝图见 docs/domain/approval-inbox-blueprint.md。\n\n## 解决的问题\n全系统有 7 道审批闸门,只有报价那一道有「审核中心」入口;技术复核、经理批准、\n采购、盘点、红冲、退款全部藏在各自工作台深处,待办不可发现。\n\n## 设计要害:聚合层不得成为第二真源\n- **不新建聚合写入口**:中心里的审批动作一律打各域原有端点,写入唯一点不变;\n- **不重新定义「什么算待审」**:判据只引用各域自己的状态枚举;\n- **入参 schema 直接引用各域已有 Zod schema**(APPROVAL_PAYLOAD_SCHEMAS),\n 某域改入参→改自己的 schema→中心类型立刻变→编译期红,而不是运行时静默 400/409;\n- 描述表 `satisfies Record\u003cApprovalTaskKind, …\u003e` 锁全集,漏一类即编译不过;\n- capability 裁剪、自批过滤、排序计数统一交给 contracts 纯函数 buildApprovalInbox,\n 两后端共用——行为对等由结构保证(G17)。\n\n## 覆盖 9 类\n7 道审批 + 2 类客户决定后的待办(客户已接受待申请合同、客户已选档待转报价)。\n后两类**不是审批闸门**:客户决定仍立即生效,只是把「决定之后必须有人做的那一步」\n从各自工作台捞出来,零状态机改动、零迁移、零对客语义变更。\n\n## 实测暴露并绕开的坑\n- 盘点列表 capability(inventory.stocktake) ≠ 审批 capability(.approve),\n SALES_MANAGER 只持后者——中心按**审批** capability 判定,否则审批人看不到自己该审的单;\n- 采购员不持 procurement.approve(三责分离),中心里必须继续不可见;\n- 方案两类需回传 versionNo 做 STALE_VERSION 校验,中心从聚合结果带出。\n\n## 诚实边界(蓝图 §7,切片二再补)\n发起人字段只有 2/7 个域存在,自批过滤只在红冲/退款生效,UI 如实标注可判定范围。\n**本模块不得宣称「已保证不可自批」**——只有这两类成立。\n\n## 证据(本地工作区 2026-08-01)\n- pnpm check 0 / check:runtime 294(地板 279→294)/ check:ui 9\n- contracts 13 条单测;两后端各 1 条聚合 HTTP 验收(capability 裁剪/跨租户/自批计数/待办类)\n- check:module-integration 新增 5 条断言,三条负向实跑判红后恢复:\n ① 聚合层加写端点 ② capability 裁剪改全放行 ③ 去掉实时失效\n 其中 ② 首轮未判红——断言只查标识符、留着未使用 import 就能骗过,\n 已收紧到锚定调用点(断言剧场自查)\n\nCo-Authored-By: Claude Fable 5 \u003cnoreply@anthropic.com\u003e\n","AuthorEmail":"luoguoguo@gmail.com","AuthorName":"luoguoguo","CommitterEmail":"luoguoguo@gmail.com","CommitterName":"luoguoguo","Timestamp":"2026-08-01T07:45:16-07:00"}],"HeadCommit":{"Sha1":"47ce4c6146a13c443db38c5816749d0591339e46","Message":"fix(quotations): 视图筛选下沉服务端 + 公开方案页不再泄漏后端英文报错\n\n## 1. 三个筛选视图的分页回归(G24 引入,本次修复)\n审核中心 / 客户报价页 / 报价版本 原先在浏览器里对列表结果 filter。列表全量返回时\n那样是对的,但 G24 加分页后客户端筛选只作用于**当前页**:待审报价不在最新一页\n就会被静默漏掉,而分页条的 total 仍是全量数,出现「共 N 条却一行不显示」的错位。\n与 C27「聚合范围由前端恰好拿到多少行决定」同族——筛选范围必须由拥有数据的一层决定。\n\n- contracts 新增 QUOTATION_VIEW_FILTERS 单源(inbox/review/customer/versions),\n 两后端各自翻译成 Prisma where;total 随之变成「该视图下的全量」;\n- quotationListViewSchema 刻意不 extend paginationQuerySchema——那会让\n quotation.ts 反向 import pagination.ts 的值,与既有 type import 形成循环;\n- 非法 view 是 400 而不是静默退回全部(静默兜底会让筛选看着生效实则没有);\n- 前端只保留一条**权限谓词**(无 customer_link.read 读不到 accessToken):\n 它对同一角色恒为全真或全假,不会像数据谓词那样把行留在分页窗口外。\n\n⚠️ 期间发现 QuoteOption.accessToken 是 @default(uuid()) **非空**,\n原「客户报价页」的 options.some(accessToken) 实际是权限谓词而非数据谓词。\n\n## 2. 公开方案页把后端英文报错渲染给客户\nPublicCustomerSolutionView 直接渲染 query.error.message,客户看到的是\n「Solution project not found」——看不懂、像坏了,还把内部对象名暴露出去。\n改为统一中文文案,且**不区分**链接无效/未发布/返工中:区分开等于告诉持链接的人\n「方案确实存在,只是没轮到你看」,而服务端刻意用 404 而非 403 正是为了不泄露存在性\n(方案蓝图 S1 审计裁决)。前端说破,那道防线就白立了。\n\n## 门禁断言随执行点搬家(不是放宽)\nquotation-review-server-state / quotation-version-snapshot 钉的是被移走的客户端谓词。\n不变量没变、执行点变了,故改为钉:前端必须把 mode 作为 view 传下去、\n两后端必须从 contracts 单源翻译 where,并加 reject 断言禁止客户端重新实现视图谓词。\n\n## 证据(本地工作区 2026-08-01)\n- pnpm check 0 / check:runtime 294 / check:ui 9\n- 两后端各 +3 条断言(view=review 只回待审、total 随筛选收窄、非法 view 400)\n- 负向两条实跑判红后恢复:\n ① 服务端忽略 view → view=review 期望 1 实得 2 判红\n ② 视图谓词写回客户端 → quotation-view-client-side-filter 判红\n\nCo-Authored-By: Claude Fable 5 \u003cnoreply@anthropic.com\u003e\n","AuthorEmail":"luoguoguo@gmail.com","AuthorName":"luoguoguo","CommitterEmail":"luoguoguo@gmail.com","CommitterName":"luoguoguo","Timestamp":"2026-08-01T08:35:11-07:00"},"CompareURL":"luoanwu/juhai-quotation-system/compare/b0ed5cba764d5df24360ea70486bad9b6885431d...47ce4c6146a13c443db38c5816749d0591339e46","Len":4}...
|
1785598520
|
Edit
Delete
|
|
20286
|
5
|
5
|
5
|
72
|
0
|
0
|
refs/heads/feat/solution-tier-multi-product
|
0
|
{"Commits":[{"Sha1":"fb6c5ad39 {"Commits":[{"Sha1":"fb6c5ad390153c534787dc66d90284ff257d28d1","Message":"docs(governance): 治理入口新增业务主干导航行\n\nCLAUDE.md「使用入口」表加一行,把「搞清业务从头到尾怎么跑 / 新人上手」\n路由到 docs/domain/business-flow-overview.md。\n\n为什么要有这一行:跨域主干导航文档已经落在 docs/domain/ 下,但治理入口表\n不指向它——一份没有入口的资产下轮就没人读、也没人维护,随后静默漂移\n(C25 同族:文档指向的东西运行时到不了,反过来也一样成立)。\n\n该行同时写明它是**派生非真源**、据它下判断前必须回到 contracts / blueprint\n复核,避免入口表把导航文档抬成第二份状态机口径(P1)。\n\nAGENTS.md 为符号链接自动跟随;pnpm check exit 0(含 check:governance-docs)。\n作用域:本地工作区 dirty(并发会话有 approval-inbox 切片二在途),本提交\n只含 CLAUDE.md 一个文件。\n\nCo-Authored-By: Claude Opus 5 \u003cnoreply@anthropic.com\u003e\n","AuthorEmail":"luoguoguo@gmail.com","AuthorName":"luoguoguo","CommitterEmail":"luoguoguo@gmail.com","CommitterName":"luoguoguo","Timestamp":"2026-08-01T19:58:50-07:00"},{"Sha1":"9e6f5674f9279f43dd8bd6b4acbcfbe5d7af29b6","Message":"test(e2e): 待办审批中心转报价的浏览器级用例——堵住两个 bug 的共同逃逸路径\n\n## 为什么补这一条\n\n审批中心 9 类待办里,「客户已选档·待转报价」是**唯一没被 E2E 打过**的一类。\n两个 bug 正是从这个缺口同时逃逸,且叠在一起:\n1. 前端把端点拼成 /api/solutions/:id/quotation,真实路由是 /create-quotation → 404;\n2. 动作必填 optionId,而 ApprovalTask 投影里没有这个字段 → 400。\n路径修对了也只是把 404 换成 400,这个按钮**从落地起就没成功执行过**。\n\n同轮已补两道静态门禁(check:api-routes / check:approval-payload),但它们各自\n只覆盖一半:前者只校验路径不看请求体,后者只校验入参不看路由。本用例是第三层,\n一次性覆盖两者,且打的是真实浏览器链路。\n\n## 用例设计\n\n前置数据用 API 铺(方案要走完设计→双审核→发布→客户选档才会进待办)——本用例的\n验证目标是**中心内的执行动作**,那条设计链路早已被第 4 条用例覆盖。铺数据序列先在\n真实后端上逐步试通,撞到四个真实约束,均已写进用例:\n- 无 body 的动作要传 {},否则 Fastify 回 FST_ERR_CTP_EMPTY_JSON_BODY;\n- design 必须三档齐全(经济/推荐/旗舰);\n- targetSegment 必须**建时就给**:APPROVED 后禁改输入,而对客发布闸门要求它非空;\n- 客户联系人字段是 primaryContact;档案无联系人时选档一律拒绝(审计 S3 fail-closed)。\n\n断言锚到 [data-testid=\"approval-task-row\"][data-approval-kind=\"...\"],不用全页\ngetByText——页面含实时事件流面板会污染宽断言(C16)。成功判据不看 toast,看\n**服务端读回** QUOTATION_CREATED + quotationId:路径 404 或 optionId 缺失都会\n让这里读不到,且条目不会消失。\n\n自带客户与联系人常量(INBOX_*),不复用 CUSTOMER_NAME/CONTACT_NAME,避免这条链路\n把早期用例的客户档案推进到 QUOTATION_CREATED 而影响它们的断言。\n\n## 证据\n\n- 正向:pnpm check:ui 10 用例 0 失败(PG:55432/juhai_quotation_ui_final_20260801 +\n Redis:6382,UI_WEB_PORT=3132/UI_API_PORT=3232)。\n- 负向:移除 NestJS 聚合的 optionId 后重跑,**恰好第 10 条判红、其余 9 条全绿**,\n 失败点正是 approval-blocked-reason 出现(UI 如设计般禁用按钮而非发必然 400 的请求);\n 现场已恢复。\n- ⚠️ 负向那次会把 ui-acceptance.latest.json 覆盖成 status=failed/9 passed——那是负向\n 测试的产物不能当证据,故重跑正向后才提交(本提交内报告为 passed/10)。\n- baseline uiTestsPassed 9→10;CLAUDE.md 证据新鲜度行与 ui-acceptance 基线行同步回灌。\n- pnpm check 全绿 exit=0(18 项,自检 5 组)。本轮未跑 check:runtime(没动写链逻辑)。\n\n工作区 dirty:并发会话在途的报价域改动未包含在本提交内。\n\nCo-Authored-By: Claude Opus 5 \u003cnoreply@anthropic.com\u003e\n","AuthorEmail":"luoguoguo@gmail.com","AuthorName":"luoguoguo","CommitterEmail":"luoguoguo@gmail.com","CommitterName":"luoguoguo","Timestamp":"2026-08-01T09:52:10-07:00"},{"Sha1":"80fbe00d13bcdf8c4bf8ed64dca1c18cccbab449","Message":"feat(gate): 审批中心入参完整性门禁——从 Zod schema 反推必填字段,契约层与实现层各查一遍\n\n## 背景:一个从落地起就没成功执行过的按钮\n\n待办审批第 9 类「客户已选档·待转报价」的动作接口必填 optionId,而 ApprovalTask\n投影里根本没有这个字段,前端 buildPayload 返回 {}。同轮还有一个路径拼错的 bug\n盖在前面,路径修对了也只是把 404 换成 400 optionId Required。\n\n四道防线同时失效:\n- check:api-routes(同轮新建)只校验路径存在,不看请求体;\n- typecheck 抓不到——`optionId?: string` 是**可选**字段,后端不返回照样编译通过;\n- 9 类待办里恰好只有这一类没被 E2E 打过;\n- descriptor 里的 requiresVersionNo 是**手写布尔标记**,作者当初意识到「有些类\n 需要额外字段」却把它硬编码成单个布尔,新增 optionId 时没有对应标记。\n\n## 判据\n\n对每个 kind 从 Zod schema **反推**必填集合(不依赖任何手写标记),逐字段两层校验:\n1. 契约层:不在中心表单可输入白名单(comment/note/internalComment)的必填字段,\n 必须出现在 ApprovalTask 投影里;\n2. 实现层:**两个后端的聚合都真的产出了它**。第二层是关键——可选字段漏返回\n 不会被 typecheck 抓到,本次 bug 正是这么活下来的。\n\n某域给自己的 schema 加一个必填字段,中心立刻红,而不是等用户点按钮才发现。\n\n## 过程中修正了自己的一次误报\n\n初版用「kind 字面量 ±600 字符」文本窗口,一跑报 4 条红。先判责再改代码:方案两类\n共用 solutionReviews() 一个方法,两个 kind 字面量在方法开头推查询条件,而 versionNo\n在 25 行后的统一 map() 里,超窗即被判「没产出」。**字段产出点与 kind 声明点不在\n同一处是正常写法,门禁不该逼代码迁就它**——改按方法体切块。该坑已写成专门的自检\n用例钉死,防止以后被「优化」回窗口法而静默回潮。\n\n## 已知边界(不得外推)\n\n只校验必填不校验可选;不校验字段类型;USER_INPUT_FIELDS 白名单人工维护,\n往里加字段等于宣称中心表单能收集它,加之前必须确认 UI 真渲染了对应控件。\n\n## 证据\n\n- 真实源码两层负向测试:删 ApprovalTask.optionId → 契约层红;删 NestJS 聚合的\n optionId → 实现层红并点名 NestJS;现场均已恢复。\n- 自检 14 项(scripts/lib/approval-payload-contract.test.mjs),含 1 条基线绿 +\n 3 条端到端红,断言 exit **恰好为 1**(崩溃同样非 0,会让「期望红」以错误理由通过)。\n- 接线:pnpm check / governance-report 棘轮 / baseline(新增 0 地板,\n gateSelftests 4→5)/ CLAUDE.md 基线表。\n- 本地工作区 pnpm check 全绿 exit=0(18 项,自检 5 组);工作区 dirty\n (含并发会话在途的报价域改动,未包含在本提交内)。\n\nCo-Authored-By: Claude Opus 5 \u003cnoreply@anthropic.com\u003e\n","AuthorEmail":"luoguoguo@gmail.com","AuthorName":"luoguoguo","CommitterEmail":"luoguoguo@gmail.com","CommitterName":"luoguoguo","Timestamp":"2026-08-01T09:14:36-07:00"}],"HeadCommit":{"Sha1":"fb6c5ad390153c534787dc66d90284ff257d28d1","Message":"docs(governance): 治理入口新增业务主干导航行\n\nCLAUDE.md「使用入口」表加一行,把「搞清业务从头到尾怎么跑 / 新人上手」\n路由到 docs/domain/business-flow-overview.md。\n\n为什么要有这一行:跨域主干导航文档已经落在 docs/domain/ 下,但治理入口表\n不指向它——一份没有入口的资产下轮就没人读、也没人维护,随后静默漂移\n(C25 同族:文档指向的东西运行时到不了,反过来也一样成立)。\n\n该行同时写明它是**派生非真源**、据它下判断前必须回到 contracts / blueprint\n复核,避免入口表把导航文档抬成第二份状态机口径(P1)。\n\nAGENTS.md 为符号链接自动跟随;pnpm check exit 0(含 check:governance-docs)。\n作用域:本地工作区 dirty(并发会话有 approval-inbox 切片二在途),本提交\n只含 CLAUDE.md 一个文件。\n\nCo-Authored-By: Claude Opus 5 \u003cnoreply@anthropic.com\u003e\n","AuthorEmail":"luoguoguo@gmail.com","AuthorName":"luoguoguo","CommitterEmail":"luoguoguo@gmail.com","CommitterName":"luoguoguo","Timestamp":"2026-08-01T19:58:50-07:00"},"CompareURL":"luoanwu/juhai-quotation-system/compare/47ce4c6146a13c443db38c5816749d0591339e46...fb6c5ad390153c534787dc66d90284ff257d28d1","Len":3}...
|
1785639545
|
Edit
Delete
|
|
20287
|
5
|
5
|
5
|
72
|
0
|
0
|
refs/heads/feat/solution-tier-multi-product
|
0
|
{"Commits":[{"Sha1":"bc5d9cc90 {"Commits":[{"Sha1":"bc5d9cc90007b3602ccd809110887ba581f0e4b0","Message":"docs(truth): 清除 docs/README.md 三处幽灵资产 + 两条断言机器化\n\nREADME「配套的工程工具」表长期把 commitlint 与 Husky 列成已有工具\n(\"本地 commit 时自动触发 lint + commitlint\"),实测两者从未存在:\n无 commitlint.config.js、无 .husky/、package.json 也无相关依赖。\n实锤旁证——本轮真实提交 fb6c5ad 未触发任何 hook。\n\n**\"以为有门禁\"比\"知道没有\"更危险**:读者据此跳过手工检查,等于文档\n凭空注销了一道本就不存在的卡口。故两行改为 🟡 规划未落地并写明\n\"提交前请手工跑 pnpm check\",落地模板指回 git-standard。\n\n同表第三处:`scripts/check-naming` 缺扩展名,真身是 check-naming.mjs。\n另把该表补一列「状态」,让已落地/未落地在结构上就分得开。\n\n机器化(C14 纪律:只为已实锤的漂移加断言):\n- commitlint-husky-honesty 从只扫 git-standard 扩到同时扫 README,\n 触发条件同时看 commitlint.config.js 与 .husky 两者是否存在\n- 新增 no-phantom-script-path:README 反引号里的 scripts/xxx 必须真实存在;\n 只扫反引号内且路径后至少一个字符,正文\"scripts/ + CI\"这类泛指不参与判定\n\n负向证据(scripts/lib/docs-truth.test.mjs,由 check:gate-selftest 重跑):\n- 抹掉 README / git-standard 的未落地标注 → 各自判红并命中对应断言 id\n (用 replaceAll,避免只替换第一处而静默拔牙)\n- README 写入不存在的脚本路径 → 判红命中 no-phantom-script-path\n- 泛指目录 \"scripts/ 目录\" → 保持绿,确认新断言够窄不误杀\n\npnpm check exit 0;check:gate-selftest 5 组全绿。作用域:本地工作区\ndirty(并发会话 approval-inbox 切片二在途),本提交只含这 3 个文件。\n\nCo-Authored-By: Claude Opus 5 \u003cnoreply@anthropic.com\u003e\n","AuthorEmail":"luoguoguo@gmail.com","AuthorName":"luoguoguo","CommitterEmail":"luoguoguo@gmail.com","CommitterName":"luoguoguo","Timestamp":"2026-08-01T20:09:57-07:00"}],"HeadCommit":{"Sha1":"bc5d9cc90007b3602ccd809110887ba581f0e4b0","Message":"docs(truth): 清除 docs/README.md 三处幽灵资产 + 两条断言机器化\n\nREADME「配套的工程工具」表长期把 commitlint 与 Husky 列成已有工具\n(\"本地 commit 时自动触发 lint + commitlint\"),实测两者从未存在:\n无 commitlint.config.js、无 .husky/、package.json 也无相关依赖。\n实锤旁证——本轮真实提交 fb6c5ad 未触发任何 hook。\n\n**\"以为有门禁\"比\"知道没有\"更危险**:读者据此跳过手工检查,等于文档\n凭空注销了一道本就不存在的卡口。故两行改为 🟡 规划未落地并写明\n\"提交前请手工跑 pnpm check\",落地模板指回 git-standard。\n\n同表第三处:`scripts/check-naming` 缺扩展名,真身是 check-naming.mjs。\n另把该表补一列「状态」,让已落地/未落地在结构上就分得开。\n\n机器化(C14 纪律:只为已实锤的漂移加断言):\n- commitlint-husky-honesty 从只扫 git-standard 扩到同时扫 README,\n 触发条件同时看 commitlint.config.js 与 .husky 两者是否存在\n- 新增 no-phantom-script-path:README 反引号里的 scripts/xxx 必须真实存在;\n 只扫反引号内且路径后至少一个字符,正文\"scripts/ + CI\"这类泛指不参与判定\n\n负向证据(scripts/lib/docs-truth.test.mjs,由 check:gate-selftest 重跑):\n- 抹掉 README / git-standard 的未落地标注 → 各自判红并命中对应断言 id\n (用 replaceAll,避免只替换第一处而静默拔牙)\n- README 写入不存在的脚本路径 → 判红命中 no-phantom-script-path\n- 泛指目录 \"scripts/ 目录\" → 保持绿,确认新断言够窄不误杀\n\npnpm check exit 0;check:gate-selftest 5 组全绿。作用域:本地工作区\ndirty(并发会话 approval-inbox 切片二在途),本提交只含这 3 个文件。\n\nCo-Authored-By: Claude Opus 5 \u003cnoreply@anthropic.com\u003e\n","AuthorEmail":"luoguoguo@gmail.com","AuthorName":"luoguoguo","CommitterEmail":"luoguoguo@gmail.com","CommitterName":"luoguoguo","Timestamp":"2026-08-01T20:09:57-07:00"},"CompareURL":"luoanwu/juhai-quotation-system/compare/fb6c5ad390153c534787dc66d90284ff257d28d1...bc5d9cc90007b3602ccd809110887ba581f0e4b0","Len":1}...
|
1785640209
|
Edit
Delete
|
|
20288
|
5
|
5
|
5
|
72
|
0
|
0
|
refs/heads/feat/solution-tier-multi-product
|
0
|
{"Commits":[{"Sha1":"b15cb5471 {"Commits":[{"Sha1":"b15cb54710ee5d1849b806adc01fcdddcaff5484","Message":"Merge remote-tracking branch 'github/feat/solution-tier-multi-product' into feat/solution-tier-multi-product\n","AuthorEmail":"luoguoguo@gmail.com","AuthorName":"luoguoguo","CommitterEmail":"luoguoguo@gmail.com","CommitterName":"luoguoguo","Timestamp":"2026-08-01T20:10:47-07:00"},{"Sha1":"7b9d30ff91324ab64ec0c115a0f05c0c503f81b6","Message":"Merge branch 'main' into feat/solution-tier-multi-product","AuthorEmail":"158980461+laoluojuhai@users.noreply.github.com","AuthorName":"laoluojuhai","CommitterEmail":"noreply@github.com","CommitterName":"GitHub","Timestamp":"2026-08-01T20:02:59-07:00"},{"Sha1":"ab8275d49416872ad2c720a1274d8d59068d2495","Message":"Merge pull request #5 from laoluojuhai/feat/g24-list-pagination-and-options\n\nfeat(g24): 列表分页 + 选择器 options 双读路径(关闭 G24)","AuthorEmail":"158980461+laoluojuhai@users.noreply.github.com","AuthorName":"laoluojuhai","CommitterEmail":"noreply@github.com","CommitterName":"GitHub","Timestamp":"2026-08-01T20:01:54-07:00"},{"Sha1":"ad018e731c9be66721d9bae55dde85574d03e6c2","Message":"Merge pull request #4 from laoluojuhai/chore/governance-c25-c26-c27\n\n治理修复:关闭 C25/C26/C27 —— 幽灵 UI 资产、partial schema 丢失的不变量、浏览器端 KPI","AuthorEmail":"158980461+laoluojuhai@users.noreply.github.com","AuthorName":"laoluojuhai","CommitterEmail":"noreply@github.com","CommitterName":"GitHub","Timestamp":"2026-08-01T20:01:33-07:00"},{"Sha1":"c1777844395fa1863bec92d95284c2f74250136b","Message":"Merge pull request #3 from laoluojuhai/chore/close-g14-remote-gate\n\ndocs: 关闭 G14——远端门禁从「能绿」推进到「会拦」","AuthorEmail":"158980461+laoluojuhai@users.noreply.github.com","AuthorName":"laoluojuhai","CommitterEmail":"noreply@github.com","CommitterName":"GitHub","Timestamp":"2026-08-01T20:00:23-07:00"}],"HeadCommit":{"Sha1":"b15cb54710ee5d1849b806adc01fcdddcaff5484","Message":"Merge remote-tracking branch 'github/feat/solution-tier-multi-product' into feat/solution-tier-multi-product\n","AuthorEmail":"luoguoguo@gmail.com","AuthorName":"luoguoguo","CommitterEmail":"luoguoguo@gmail.com","CommitterName":"luoguoguo","Timestamp":"2026-08-01T20:10:47-07:00"},"CompareURL":"luoanwu/juhai-quotation-system/compare/bc5d9cc90007b3602ccd809110887ba581f0e4b0...b15cb54710ee5d1849b806adc01fcdddcaff5484","Len":5}...
|
1785640342
|
Edit
Delete
|
|
20289
|
5
|
5
|
5
|
72
|
0
|
0
|
refs/heads/feat/solution-tier-multi-product
|
0
|
{"Commits":[{"Sha1":"3a22776ed {"Commits":[{"Sha1":"3a22776ed81118bba6b93bfb50d5b36a70b2ee35","Message":"feat(approval-inbox): 切片二(4/5 域)——补发起人字段与不可自批硬校验\n\n蓝图 §7 裁决的方案 B:中心先落地、内控另立一片。本片补齐 5 个缺发起人的域中的 4 个。\n\n## 新增字段(两后端 schema 对等 + 迁移)\n- SolutionProject.reviewRequestedBy —— **把方案推进到当前待审状态的人**。\n 单列足够:同一时刻只有一个待审阶段。技术复核阶段=提交人、经理批准阶段=技术复核人,\n 正好都是各自阶段该被禁止自批的那一个。\n- PurchaseOrder.acceptedBy —— 接单人\n- Stocktake.submittedBy —— 提交盘点的人\n\n三列一律 nullable:迁移前的存量行无从追溯发起人,**判不了就不判**。\n给个假默认值会让不可自批看起来生效、实际保护不了任何一行。\n\n## 不可自批硬校验(两后端对等)\n方案技术复核 / 方案经理批准 / 采购审批 / 盘点审批各加 approver ≠ requester 校验,\n403 + code=SELF_APPROVAL_FORBIDDEN,与红冲/退款既有口径一致。\n角色分离挡不住 ADMIN(持全部 capability),所以必须按 actor 再判一次——两层缺一不可。\n\nactor 串进 5 条写链(两后端各自的 controller/route 补 RequestActor/requestActor)。\n\n## 契约与聚合\nselfRequestedKnown 对这 4 类转 true;聚合把 requestedBy 带出来,\n自批过滤随之在这 4 类上真正生效。UI 文案改为点名「报价审批暂未记录发起人」。\n\n## E2E 随内控调整(这些红盘是闸门在正确工作,不是回归)\n原用例里同一个 ADMIN 既提交又审批——正是不可自批要禁的。浏览器会话取不到身份时\nactor 兜底为 `role:\u003cROLE\u003e`(同角色视为同一人,刻意 fail-closed,不得为测试放宽)。\n故改为:先在浏览器断言「点了没推进」(这是该闸门唯一的浏览器级证据),\n再换一个审批人身份走 API 放行。覆盖方案双审核、采购审批、盘点审批三处。\n\n## 证据(本地工作区 2026-08-01)\n- pnpm check 0 / check:runtime 296(地板 294→296)/ check:ui 10(地板 9→10)\n- 两后端各 +1 条自批 403 断言;contracts 单测覆盖 selfRequestedKnown 全集\n- 负向实跑判红后恢复:拔掉采购不可自批校验 → 期望 403 实得 200 判红\n\n## 仍未闭环\n报价审批(ApprovalRequest 无 requestedBy)**本片未做**——并发会话正在改报价域,\n避让以免卡住对方。中心因此仍**不得**笼统宣称「已保证不可自批」。\n\nCo-Authored-By: Claude Fable 5 \u003cnoreply@anthropic.com\u003e\n","AuthorEmail":"luoguoguo@gmail.com","AuthorName":"luoguoguo","CommitterEmail":"luoguoguo@gmail.com","CommitterName":"luoguoguo","Timestamp":"2026-08-01T20:15:14-07:00"}],"HeadCommit":{"Sha1":"3a22776ed81118bba6b93bfb50d5b36a70b2ee35","Message":"feat(approval-inbox): 切片二(4/5 域)——补发起人字段与不可自批硬校验\n\n蓝图 §7 裁决的方案 B:中心先落地、内控另立一片。本片补齐 5 个缺发起人的域中的 4 个。\n\n## 新增字段(两后端 schema 对等 + 迁移)\n- SolutionProject.reviewRequestedBy —— **把方案推进到当前待审状态的人**。\n 单列足够:同一时刻只有一个待审阶段。技术复核阶段=提交人、经理批准阶段=技术复核人,\n 正好都是各自阶段该被禁止自批的那一个。\n- PurchaseOrder.acceptedBy —— 接单人\n- Stocktake.submittedBy —— 提交盘点的人\n\n三列一律 nullable:迁移前的存量行无从追溯发起人,**判不了就不判**。\n给个假默认值会让不可自批看起来生效、实际保护不了任何一行。\n\n## 不可自批硬校验(两后端对等)\n方案技术复核 / 方案经理批准 / 采购审批 / 盘点审批各加 approver ≠ requester 校验,\n403 + code=SELF_APPROVAL_FORBIDDEN,与红冲/退款既有口径一致。\n角色分离挡不住 ADMIN(持全部 capability),所以必须按 actor 再判一次——两层缺一不可。\n\nactor 串进 5 条写链(两后端各自的 controller/route 补 RequestActor/requestActor)。\n\n## 契约与聚合\nselfRequestedKnown 对这 4 类转 true;聚合把 requestedBy 带出来,\n自批过滤随之在这 4 类上真正生效。UI 文案改为点名「报价审批暂未记录发起人」。\n\n## E2E 随内控调整(这些红盘是闸门在正确工作,不是回归)\n原用例里同一个 ADMIN 既提交又审批——正是不可自批要禁的。浏览器会话取不到身份时\nactor 兜底为 `role:\u003cROLE\u003e`(同角色视为同一人,刻意 fail-closed,不得为测试放宽)。\n故改为:先在浏览器断言「点了没推进」(这是该闸门唯一的浏览器级证据),\n再换一个审批人身份走 API 放行。覆盖方案双审核、采购审批、盘点审批三处。\n\n## 证据(本地工作区 2026-08-01)\n- pnpm check 0 / check:runtime 296(地板 294→296)/ check:ui 10(地板 9→10)\n- 两后端各 +1 条自批 403 断言;contracts 单测覆盖 selfRequestedKnown 全集\n- 负向实跑判红后恢复:拔掉采购不可自批校验 → 期望 403 实得 200 判红\n\n## 仍未闭环\n报价审批(ApprovalRequest 无 requestedBy)**本片未做**——并发会话正在改报价域,\n避让以免卡住对方。中心因此仍**不得**笼统宣称「已保证不可自批」。\n\nCo-Authored-By: Claude Fable 5 \u003cnoreply@anthropic.com\u003e\n","AuthorEmail":"luoguoguo@gmail.com","AuthorName":"luoguoguo","CommitterEmail":"luoguoguo@gmail.com","CommitterName":"luoguoguo","Timestamp":"2026-08-01T20:15:14-07:00"},"CompareURL":"luoanwu/juhai-quotation-system/compare/b15cb54710ee5d1849b806adc01fcdddcaff5484...3a22776ed81118bba6b93bfb50d5b36a70b2ee35","Len":1}...
|
1785640521
|
Edit
Delete
|
|
20290
|
5
|
5
|
5
|
72
|
0
|
0
|
refs/heads/feat/solution-tier-multi-product
|
0
|
{"Commits":[{"Sha1":"fbdc3680f {"Commits":[{"Sha1":"fbdc3680f4ef53e824738ca8cbf8f0e6deefce55","Message":"fix(gate): 文档诚实注收窄到行——文件级标注不该赦免同文件里的另一句谎话\n\ngit-standard 导语长期写「Conventional Commits 规范 + commitlint 自动校验」,\n而同文件 §三 已标注「🟡 规划未落地」。上一轮加的 commitlint-husky-honesty\n是**文件级**判定(文件里出现「规划未落地」即算诚实),因此绿着放行了这句\n自相矛盾的导语——而读者读的正是导语,不会翻到第三节。\n\n粒度选得比缺陷粗一级,门禁就会用\"文件里有免责声明\"赦免\"文件里另一句谎话\"。\n读者是按句子受骗的,断言就得按句子判。\n\n改动:\n- git-standard 导语改为如实描述:校验靠人、无 hook、提交前手工跑 pnpm check\n- 新增 commitlint-husky-no-auto-claim:同一行既点名 commitlint/husky\n 又宣称\"自动\"、且该行无未落地字样 → 判红。收窄到行是为了不误咬 CI 侧\n 合法的\"自动\"表述(C14:宁可窄,不造模糊规则)\n\n负向证据(scripts/lib/docs-truth.test.mjs,由 check:gate-selftest 重跑):\n- 向两份文件各写回**那句历史原文** → 各自 exit 恰为 1 且命中\n [commitlint-husky-no-auto-claim:\u003cfile\u003e],确认红在点子上而非撞上别的断言\n- 同行带未落地标注的「自动」表述 → 仍绿,锁住新断言的窄边界。\n 只有红测试的门禁会朝越咬越宽漂移,直到有人为了让它闭嘴而删掉它\n\npnpm check exit 0;check:gate-selftest 5 组全绿。作用域:本地工作区 dirty\n(并发会话 approval-inbox 切片二在途),本提交只含这 3 个文件。\n\nCo-Authored-By: Claude Opus 5 \u003cnoreply@anthropic.com\u003e\n","AuthorEmail":"luoguoguo@gmail.com","AuthorName":"luoguoguo","CommitterEmail":"luoguoguo@gmail.com","CommitterName":"luoguoguo","Timestamp":"2026-08-01T20:18:46-07:00"}],"HeadCommit":{"Sha1":"fbdc3680f4ef53e824738ca8cbf8f0e6deefce55","Message":"fix(gate): 文档诚实注收窄到行——文件级标注不该赦免同文件里的另一句谎话\n\ngit-standard 导语长期写「Conventional Commits 规范 + commitlint 自动校验」,\n而同文件 §三 已标注「🟡 规划未落地」。上一轮加的 commitlint-husky-honesty\n是**文件级**判定(文件里出现「规划未落地」即算诚实),因此绿着放行了这句\n自相矛盾的导语——而读者读的正是导语,不会翻到第三节。\n\n粒度选得比缺陷粗一级,门禁就会用\"文件里有免责声明\"赦免\"文件里另一句谎话\"。\n读者是按句子受骗的,断言就得按句子判。\n\n改动:\n- git-standard 导语改为如实描述:校验靠人、无 hook、提交前手工跑 pnpm check\n- 新增 commitlint-husky-no-auto-claim:同一行既点名 commitlint/husky\n 又宣称\"自动\"、且该行无未落地字样 → 判红。收窄到行是为了不误咬 CI 侧\n 合法的\"自动\"表述(C14:宁可窄,不造模糊规则)\n\n负向证据(scripts/lib/docs-truth.test.mjs,由 check:gate-selftest 重跑):\n- 向两份文件各写回**那句历史原文** → 各自 exit 恰为 1 且命中\n [commitlint-husky-no-auto-claim:\u003cfile\u003e],确认红在点子上而非撞上别的断言\n- 同行带未落地标注的「自动」表述 → 仍绿,锁住新断言的窄边界。\n 只有红测试的门禁会朝越咬越宽漂移,直到有人为了让它闭嘴而删掉它\n\npnpm check exit 0;check:gate-selftest 5 组全绿。作用域:本地工作区 dirty\n(并发会话 approval-inbox 切片二在途),本提交只含这 3 个文件。\n\nCo-Authored-By: Claude Opus 5 \u003cnoreply@anthropic.com\u003e\n","AuthorEmail":"luoguoguo@gmail.com","AuthorName":"luoguoguo","CommitterEmail":"luoguoguo@gmail.com","CommitterName":"luoguoguo","Timestamp":"2026-08-01T20:18:46-07:00"},"CompareURL":"luoanwu/juhai-quotation-system/compare/3a22776ed81118bba6b93bfb50d5b36a70b2ee35...fbdc3680f4ef53e824738ca8cbf8f0e6deefce55","Len":1}...
|
1785640746
|
Edit
Delete
|
|
20291
|
5
|
5
|
5
|
72
|
0
|
0
|
refs/heads/feat/solution-tier-multi-product
|
0
|
{"Commits":[{"Sha1":"479c74220 {"Commits":[{"Sha1":"479c742208b50c7e788cb4eea1b6b878fcfe615e","Message":"feat(approval-inbox): 切片二收口——报价审批补发起人,不可自批 6/6 道闭环\n\n上一轮避让并发会话对报价域的改动,留下 ApprovalRequest 这最后一道。\n现该域已干净(其工作已合入 b15cb54),补齐收口。\n\n## 改动\n- ApprovalRequest.requestedBy(两后端 schema 对等 + 迁移,nullable)\n- requestApproval 落发起人;decideApproval 校验 approver ≠ requester\n → 403 + code=SELF_APPROVAL_FORBIDDEN,与其余五道口径一致\n- actor 串进两后端的 request-approval / approve / reject-approval\n (Fastify 的 request-approval 因此脱离无 actor 的 noBodyAction)\n- contracts 描述表 QUOTATION_APPROVAL.selfRequestedKnown → true;\n 聚合把 PENDING 子行的 requestedBy 带出来,自批过滤在报价审批上真正生效\n- UI 文案去掉「报价审批暂未记录发起人」的例外说明\n\n## 至此不可自批覆盖 6/6 道真审批\n报价 / 方案技术复核 / 方案经理批准 / 采购 / 盘点 / 红冲 / 退款\n(仅剩的两类 selfRequestedKnown=false 是「客户决定后的待办」,本就无发起人概念)\n\n## 诚实边界(未因本片改变)\n- 列均 nullable:建列前的存量行判不了就不判,**不假装判过**\n- 仍受 G8 制约:Bearer 未验签、actor 取不到时兜底 `role:\u003cROLE\u003e`,\n 所以这是「防误操作」不是「防抵赖」,**不得宣称为审计级职责分离**\n\n## 证据(本地工作区 2026-08-01)\n- pnpm check 0 / check:runtime 296 / check:ui 10\n- 两后端各 +1 条报价自批 403 断言,并各配一条「换个审批人即通过」的反证\n ——只断言 403 无法区分「拦对了」与「谁都批不了」的误杀\n- 负向实跑判红后恢复:拔掉报价不可自批校验 → 期望 403 实得 200\n- 回灌:CLAUDE.md 真源地图新增「跨域待办审批与不可自批」条目 +\n 证据新鲜度/棘轮行更新;蓝图 §4 缺口 1、2 标记闭环\n\nCo-Authored-By: Claude Fable 5 \u003cnoreply@anthropic.com\u003e\n","AuthorEmail":"luoguoguo@gmail.com","AuthorName":"luoguoguo","CommitterEmail":"luoguoguo@gmail.com","CommitterName":"luoguoguo","Timestamp":"2026-08-01T20:28:00-07:00"}],"HeadCommit":{"Sha1":"479c742208b50c7e788cb4eea1b6b878fcfe615e","Message":"feat(approval-inbox): 切片二收口——报价审批补发起人,不可自批 6/6 道闭环\n\n上一轮避让并发会话对报价域的改动,留下 ApprovalRequest 这最后一道。\n现该域已干净(其工作已合入 b15cb54),补齐收口。\n\n## 改动\n- ApprovalRequest.requestedBy(两后端 schema 对等 + 迁移,nullable)\n- requestApproval 落发起人;decideApproval 校验 approver ≠ requester\n → 403 + code=SELF_APPROVAL_FORBIDDEN,与其余五道口径一致\n- actor 串进两后端的 request-approval / approve / reject-approval\n (Fastify 的 request-approval 因此脱离无 actor 的 noBodyAction)\n- contracts 描述表 QUOTATION_APPROVAL.selfRequestedKnown → true;\n 聚合把 PENDING 子行的 requestedBy 带出来,自批过滤在报价审批上真正生效\n- UI 文案去掉「报价审批暂未记录发起人」的例外说明\n\n## 至此不可自批覆盖 6/6 道真审批\n报价 / 方案技术复核 / 方案经理批准 / 采购 / 盘点 / 红冲 / 退款\n(仅剩的两类 selfRequestedKnown=false 是「客户决定后的待办」,本就无发起人概念)\n\n## 诚实边界(未因本片改变)\n- 列均 nullable:建列前的存量行判不了就不判,**不假装判过**\n- 仍受 G8 制约:Bearer 未验签、actor 取不到时兜底 `role:\u003cROLE\u003e`,\n 所以这是「防误操作」不是「防抵赖」,**不得宣称为审计级职责分离**\n\n## 证据(本地工作区 2026-08-01)\n- pnpm check 0 / check:runtime 296 / check:ui 10\n- 两后端各 +1 条报价自批 403 断言,并各配一条「换个审批人即通过」的反证\n ——只断言 403 无法区分「拦对了」与「谁都批不了」的误杀\n- 负向实跑判红后恢复:拔掉报价不可自批校验 → 期望 403 实得 200\n- 回灌:CLAUDE.md 真源地图新增「跨域待办审批与不可自批」条目 +\n 证据新鲜度/棘轮行更新;蓝图 §4 缺口 1、2 标记闭环\n\nCo-Authored-By: Claude Fable 5 \u003cnoreply@anthropic.com\u003e\n","AuthorEmail":"luoguoguo@gmail.com","AuthorName":"luoguoguo","CommitterEmail":"luoguoguo@gmail.com","CommitterName":"luoguoguo","Timestamp":"2026-08-01T20:28:00-07:00"},"CompareURL":"luoanwu/juhai-quotation-system/compare/fbdc3680f4ef53e824738ca8cbf8f0e6deefce55...479c742208b50c7e788cb4eea1b6b878fcfe615e","Len":1}...
|
1785641286
|
Edit
Delete
|
|
20292
|
5
|
5
|
5
|
72
|
0
|
0
|
refs/heads/feat/solution-tier-multi-product
|
0
|
{"Commits":[{"Sha1":"06495232f {"Commits":[{"Sha1":"06495232f1936d9e221f7745216962379994b261","Message":"chore: 合并 main 后刷新治理报告\n","AuthorEmail":"luoguoguo@gmail.com","AuthorName":"luoguoguo","CommitterEmail":"luoguoguo@gmail.com","CommitterName":"luoguoguo","Timestamp":"2026-08-01T20:45:47-07:00"},{"Sha1":"ae1046edf9b030e1a67c30235ddd345590e61fdf","Message":"Merge remote-tracking branch 'github/main' into feat/solution-tier-multi-product\n","AuthorEmail":"luoguoguo@gmail.com","AuthorName":"luoguoguo","CommitterEmail":"luoguoguo@gmail.com","CommitterName":"luoguoguo","Timestamp":"2026-08-01T20:42:40-07:00"},{"Sha1":"8ab2314d69f1885d49bbbf1fc3c5b3c6cccb6711","Message":"Merge pull request #6 from laoluojuhai/feat/solution-tier-multi-product\n\nfeat(solution): 三档方案每档多商品 + 数量默认取包房总数","AuthorEmail":"158980461+laoluojuhai@users.noreply.github.com","AuthorName":"laoluojuhai","CommitterEmail":"noreply@github.com","CommitterName":"GitHub","Timestamp":"2026-08-01T20:09:43-07:00"}],"HeadCommit":{"Sha1":"06495232f1936d9e221f7745216962379994b261","Message":"chore: 合并 main 后刷新治理报告\n","AuthorEmail":"luoguoguo@gmail.com","AuthorName":"luoguoguo","CommitterEmail":"luoguoguo@gmail.com","CommitterName":"luoguoguo","Timestamp":"2026-08-01T20:45:47-07:00"},"CompareURL":"luoanwu/juhai-quotation-system/compare/479c742208b50c7e788cb4eea1b6b878fcfe615e...06495232f1936d9e221f7745216962379994b261","Len":3}...
|
1785642353
|
Edit
Delete
|
|
16220
|
5
|
5
|
5
|
57
|
0
|
0
|
refs/heads/feat/qwen-image-inference
|
1
|
|
1783381399
|
Edit
Delete
|
|
16221
|
5
|
5
|
5
|
57
|
0
|
0
|
refs/heads/feat/qwen-image-inference
|
1
|
{"Commits":[{"Sha1":"ac5f340c0 {"Commits":[{"Sha1":"ac5f340c0422af2a5c3122289142e8517dc434c5","Message":"fix(sidecar): Qwen-Image-Edit 未指定尺寸时保留底图原生分辨率\n\nQwenImageEditPlus 默认把输入塌到 ~1MP 规范面积,密集中文标题在 1MP + 低步数下\n笔画糊/字形崩。新增 _fit_edit_dims:未显式传 width/height 时按底图原生分辨率\n(保纵横比、向下取整 16 倍数、封顶 QWEN_EDIT_MAX_AREA=1.5MP 兜内存/耗时)驱动,\n调用方仍可显式覆盖。\n\n实测 ktv-poster-master(1672x941) 改字:4 步@1.06MP 文字 melted → 20 步@1.49MP\n(1632x912)文字笔画完整清晰、字形正确,无高分辨率伪影。清晰度决定因素是步数\n(生产默认 40),分辨率为次因;两者叠加达到可用海报质量。\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n","AuthorEmail":"luojuhai@luojuhaideMacBook-Pro.local","AuthorName":"luojuhai","CommitterEmail":"luojuhai@luojuhaideMacBook-Pro.local","CommitterName":"luojuhai","Timestamp":"2026-07-07T07:39:05+08:00"},{"Sha1":"973dde30db19928a0937f02a102979d473cce644","Message":"feat(material-factory): 图像模型真实推理对接 + 双后端渲染管线闭环\n\n把嗨设工坊从「权重就绪、推理是桩」推进到「本机真跑转绿」:\n\nsidecar (services/model-sidecar)\n- /v1/generate 真调 Z-Image-Turbo / Qwen-Image-2512(modelKey 路由)\n- /v1/edit 真调 Qwen-Image-Edit-2511(QwenImageEditPlusPipeline)\n- capability() 依赖齐全时开启 qwen-image-master/edit(128 GiB 主机 loadable)\n- 单 pipeline 常驻、kind 切换释放旧模型,防双 20B 同驻 OOM\n- 修 torchvision 隐藏依赖:Qwen2VLProcessor→Qwen2VLVideoProcessor 硬依赖它,\n 缺失则 from_pretrained ImportError(进 pyproject zimage extra)\n- 加载失败包 try/except → 结构化 503(不再冒 500)\n- SIDECAR_QWEN_STEPS:生产默认 40 步,CI/验收可降步提速(真引擎真像素)\n\n双后端 (NestJS + Fastify 同契约)\n- render-pipeline 按 kind 路由:APPEARANCE/SEMANTIC_EDIT→sidecarEdit,\n DESIGN_MASTER/TURBO_VARIANT→sidecarGenerate;job 状态机真流转 + 乐观锁 + outbox 同 tx\n- 修 undici 300s 传输超时:Node 内置 fetch 的 headersTimeout/bodyTimeout 默认 300s\n 会把分钟级同步推理在 ~302s 中止成假 SidecarUnavailableError(AbortSignal 压不住)。\n 改用 undici@6 request() + 请求级 headersTimeout/bodyTimeout 对齐 timeoutMs\n\n验收\n- DATABASE_URL=... INFERENCE_RUN_QWEN=1 pnpm check:inference 双后端各 6/6 绿\n- Qwen-Image-Edit 真改图 SUCCEEDED(金色中文标题精确改写 + OCR gate 联动)\n- pnpm check 全部静态门禁绿;CLAUDE.md(=AGENTS.md) 回灌 G14/真跑证据\n\n仍打开(诚实缺口):render-pipeline 同步端点(G13)、异步 worker 化、\n单 MPS 多进程串行化、Qwen nightly/self-hosted 验收。\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n","AuthorEmail":"luojuhai@luojuhaideMacBook-Pro.local","AuthorName":"luojuhai","CommitterEmail":"luojuhai@luojuhaideMacBook-Pro.local","CommitterName":"luojuhai","Timestamp":"2026-07-07T06:35:04+08:00"}],"HeadCommit":{"Sha1":"ac5f340c0422af2a5c3122289142e8517dc434c5","Message":"fix(sidecar): Qwen-Image-Edit 未指定尺寸时保留底图原生分辨率\n\nQwenImageEditPlus 默认把输入塌到 ~1MP 规范面积,密集中文标题在 1MP + 低步数下\n笔画糊/字形崩。新增 _fit_edit_dims:未显式传 width/height 时按底图原生分辨率\n(保纵横比、向下取整 16 倍数、封顶 QWEN_EDIT_MAX_AREA=1.5MP 兜内存/耗时)驱动,\n调用方仍可显式覆盖。\n\n实测 ktv-poster-master(1672x941) 改字:4 步@1.06MP 文字 melted → 20 步@1.49MP\n(1632x912)文字笔画完整清晰、字形正确,无高分辨率伪影。清晰度决定因素是步数\n(生产默认 40),分辨率为次因;两者叠加达到可用海报质量。\n\nCo-Authored-By: Claude Opus 4.8 \u003cnoreply@anthropic.com\u003e\n","AuthorEmail":"luojuhai@luojuhaideMacBook-Pro.local","AuthorName":"luojuhai","CommitterEmail":"luojuhai@luojuhaideMacBook-Pro.local","CommitterName":"luojuhai","Timestamp":"2026-07-07T07:39:05+08:00"},"CompareURL":"luoanwu/image-generation/compare/fc2ce503c94d763b1d077e7c734720a0c58e23a8...ac5f340c0422af2a5c3122289142e8517dc434c5","Len":2}...
|
1783381399
|
Edit
Delete
|
|
22303
|
5
|
5
|
5
|
57
|
0
|
0
|
refs/heads/feat/qwen-image-3-hosted
|
0
|
|
1786602934
|
Edit
Delete
|
|
22304
|
5
|
5
|
5
|
57
|
0
|
0
|
refs/heads/feat/qwen-image-3-hosted
|
0
|
{"Commits":[{"Sha1":"a3b0b41a7 {"Commits":[{"Sha1":"a3b0b41a799077b4ab46b58ee7290ae27be009f3","Message":"feat: integrate Qwen-Image 3.0 hosted edit via DashScope\n\nQwen-Image 3.0 (released 2026-07-21) ships no public weights (HF-verified;\nlatest open checkpoint remains Qwen-Image-2512), so integration goes through\nthe Aliyun Model Studio hosted API instead of the weights manifest.\n\n- contracts: add qwen-image-3-api capability (kind=hosted-api), HOSTED_EDIT_KINDS,\n buildEditInstruction (prompt stays aligned with OCR gate expectations),\n sidecarCapabilitySchema.kind; fix lingering \"Qwen-Image 2.0\" routing drift\n- sidecar: /v1/edit now really calls DashScope multimodal-generation\n (base64 base image, \u003e2048px downscale, 24h result URL downloaded to disk\n immediately, honest 503 HOSTED_API_KEY_MISSING / 502 HOSTED_API_ERROR;\n zero new Python deps)\n- backends (NestJS+Fastify, isomorphic): render pipeline executes hosted edit\n kinds when credential ready -\u003e Asset(RENDERED, model=real engine id) -\u003e\n server-side OCR gate; other model kinds stay honestly BLOCKED\n- web: EditStudio hosted-edit entry (run-hosted-edit); MODEL_ROUTING_FALLBACK\n now derived from contracts; purge remaining \"Qwen-Image 2.0\" labels\n- acceptance: deterministic no-key BLOCKED assertions + runIf(DASHSCOPE_API_KEY)\n live test; 20B BLOCKED assertion moved to always-deterministic DESIGN_MASTER\n- gates re-run green: pnpm check / check:runtime / check:inference / check:ui\n (refreshed the stale failed ui-acceptance report left by a July port collision)\n- docs: CLAUDE.md C9 closed record, G14 rewrite, baseline refresh\n\nCo-Authored-By: Claude Fable 5 \u003cnoreply@anthropic.com\u003e\n","AuthorEmail":"luoguoguo@gmail.com","AuthorName":"luoguoguo","CommitterEmail":"luoguoguo@gmail.com","CommitterName":"luoguoguo","Timestamp":"2026-08-12T22:57:17-07:00"},{"Sha1":"34dc595ae157cf7904da55c885aee4af2c69c469","Message":"chore: add material factory model weights\n","AuthorEmail":"luoguoguo@gmail.com","AuthorName":"luoguoguo","CommitterEmail":"luoguoguo@gmail.com","CommitterName":"luoguoguo","Timestamp":"2026-07-06T02:21:25-07:00"}],"HeadCommit":{"Sha1":"a3b0b41a799077b4ab46b58ee7290ae27be009f3","Message":"feat: integrate Qwen-Image 3.0 hosted edit via DashScope\n\nQwen-Image 3.0 (released 2026-07-21) ships no public weights (HF-verified;\nlatest open checkpoint remains Qwen-Image-2512), so integration goes through\nthe Aliyun Model Studio hosted API instead of the weights manifest.\n\n- contracts: add qwen-image-3-api capability (kind=hosted-api), HOSTED_EDIT_KINDS,\n buildEditInstruction (prompt stays aligned with OCR gate expectations),\n sidecarCapabilitySchema.kind; fix lingering \"Qwen-Image 2.0\" routing drift\n- sidecar: /v1/edit now really calls DashScope multimodal-generation\n (base64 base image, \u003e2048px downscale, 24h result URL downloaded to disk\n immediately, honest 503 HOSTED_API_KEY_MISSING / 502 HOSTED_API_ERROR;\n zero new Python deps)\n- backends (NestJS+Fastify, isomorphic): render pipeline executes hosted edit\n kinds when credential ready -\u003e Asset(RENDERED, model=real engine id) -\u003e\n server-side OCR gate; other model kinds stay honestly BLOCKED\n- web: EditStudio hosted-edit entry (run-hosted-edit); MODEL_ROUTING_FALLBACK\n now derived from contracts; purge remaining \"Qwen-Image 2.0\" labels\n- acceptance: deterministic no-key BLOCKED assertions + runIf(DASHSCOPE_API_KEY)\n live test; 20B BLOCKED assertion moved to always-deterministic DESIGN_MASTER\n- gates re-run green: pnpm check / check:runtime / check:inference / check:ui\n (refreshed the stale failed ui-acceptance report left by a July port collision)\n- docs: CLAUDE.md C9 closed record, G14 rewrite, baseline refresh\n\nCo-Authored-By: Claude Fable 5 \u003cnoreply@anthropic.com\u003e\n","AuthorEmail":"luoguoguo@gmail.com","AuthorName":"luoguoguo","CommitterEmail":"luoguoguo@gmail.com","CommitterName":"luoguoguo","Timestamp":"2026-08-12T22:57:17-07:00"},"CompareURL":"luoanwu/image-generation/compare/fc2ce503c94d763b1d077e7c734720a0c58e23a8...a3b0b41a799077b4ab46b58ee7290ae27be009f3","Len":2}...
|
1786602934
|
Edit
Delete
|
|
22300
|
5
|
5
|
5
|
72
|
0
|
0
|
refs/heads/feat/product-catalog-search-and-image-u refs/heads/feat/product-catalog-search-and-image-upload...
|
0
|
|
1786602303
|
Edit
Delete
|
|
22301
|
5
|
5
|
5
|
72
|
0
|
0
|
refs/heads/feat/product-catalog-search-and-image-u refs/heads/feat/product-catalog-search-and-image-upload...
|
0
|
{"Commits":[{"Sha1":"35ebc8613 {"Commits":[{"Sha1":"35ebc8613b168219ce82c0b3cf9336d32e958821","Message":"feat(ui): 方案商品多选批量添加 + 交互模式采纳率收口(全量落盘,含并发在途批次)\n\n整棵混合工作区的全量提交:多个战役共树、文件内手笔交错,无法按文件拆分归属,\n经用户裁决全量提交推送;已知红盘如实列出,等对应战役收口后 CI 自然转绿。\n\n本轮两个战役(已验证):\n- 方案设计三档选品改多选批量:新增 TierProductPicker(checkbox 候选 + 分类/关键词\n 收窄 + 待添加纸片跨筛选常驻 + 已添加禁勾),SolutionDesigner 416→252 行并从\n component-size 棘轮毕业;E2E 方案章改写为「勾 2 件一次落 2 行」的多选证明。\n- 交互模式采纳率收口:裸 {x.error.message} 29→1(统一走 ErrorAlert/QueryErrorState,\n receivables 本地 ErrorText 裸实现改包装、13 调用点一次收口,消掉 text-red-600 与\n 「操作失败,请重试」兜底);裸 disabled={isPending} 59→31(单动作按钮换 Button\n loading 保焦点带 aria-busy;行共享 mutation/批准驳回对/兄弟禁用具名保留);\n check:design-tokens 新增 designTokenRawErrorMessage=1 / designTokenBareIsPendingDisable=31\n 两条采纳率棘轮(计数单源 scripts/lib/design-adoption.mjs),design-adoption.test.mjs\n 13 例负向自检进 gate-selftest,活体注入实跑 exit=1 判红后恢复。\n\n同树落盘的并发/前序批次(未逐一复验,以各自战役记录为准):\n- 报价版本中心(进行中,已知红:module-integration 的 quotation-version-detail-visible\n + component-size 3 处【MobileOverview 473\u003e430、QuotationCard 437、QuotationWorkbench\n 665\u003e566】+ gate-selftest 基线用例如实继承)\n- 事件业务编号按域推广、售后/方案后端切片、设计稿真源目录与 08-07 迁移目录等\n\n证据(本地混合工作区,2026-08-13,详见 CLAUDE.md 动态区新条目):\ntypecheck / lint / design-tokens / role-permissions / naming / schema / validation /\ncontract-consumers / governance-docs / workspace-hygiene 绿;真实 Chromium 定向验收\n「商品客户」「客户方案闭环」两章通过(全新库 ui18);「报价闭环」红属版本中心在途,\n「履约闭环」被串行阻断——fulfillment/receivables 的 loading 换写暂只有 typecheck+lint\n级证据,全量 check:ui 复跑时重点看这两章。\n\nCo-Authored-By: Claude Fable 5 \u003cnoreply@anthropic.com\u003e\n","AuthorEmail":"luoguoguo@gmail.com","AuthorName":"luoguoguo","CommitterEmail":"luoguoguo@gmail.com","CommitterName":"luoguoguo","Timestamp":"2026-08-12T23:22:47-07:00"},{"Sha1":"0b2e3703fed8f72749a8dc1824848d0601f1c9ea","Message":"docs(governance): 回灌远端 CI 证据——run 31072021158 @ ac22869 全绿\n\n作用域是**这个提交与这条分支**,不是 main(main 仍落后 21 个提交)。\nstatic ✅ / runtime 497 用例(contracts 395 + 双端各 51)/ UI 12 用例,\nCI 侧真起 MinIO 跑上传写链。\n\n同时记下两次实测判红的插曲:check:runtime 加了 S3 preflight 却没同步 CI 环境;\nMinIO 放进 services: 起不来(Actions 的 service 容器没有传 command 的字段)。\n\nCo-Authored-By: Claude Opus 5 \u003cnoreply@anthropic.com\u003e\n","AuthorEmail":"luoguoguo@gmail.com","AuthorName":"luoguoguo","CommitterEmail":"luoguoguo@gmail.com","CommitterName":"luoguoguo","Timestamp":"2026-08-05T21:52:52-07:00"},{"Sha1":"ac228691be9468a1345b4f3669a7c5aa0ca86bef","Message":"ci: MinIO 改用显式 step 启动——services 传不了启动参数\n\n上一提交把 MinIO 放进 `services:`,CI 实测判红:\n「Failed to initialize container minio/minio:latest」。\n\n根因:Actions 的 service 容器只支持 image/env/ports/options/credentials/volumes,\n**没有传 command/args 的字段**;而 minio 镜像的 entrypoint 需要 `server /data`。\n不带参数时它打印 usage 后正常退出,于是健康检查永远 unhealthy。\n\n改为在 runtime job 里显式 `docker run -d ... server /data` 并轮询\n/minio/health/live。步骤跑在 runner 宿主机上,端口映射到 127.0.0.1:9000,\n与 S3_ENDPOINT 一致。\n\nCo-Authored-By: Claude Opus 5 \u003cnoreply@anthropic.com\u003e\n","AuthorEmail":"luoguoguo@gmail.com","AuthorName":"luoguoguo","CommitterEmail":"luoguoguo@gmail.com","CommitterName":"luoguoguo","Timestamp":"2026-08-05T21:42:48-07:00"},{"Sha1":"6a2a86ba23719de7baa4940fb278b2df8b72b95e","Message":"ci: 补 MinIO 服务与 S3 环境——check:runtime 现在硬要求对象存储\n\n上一提交给 check:runtime / check:ui 的 preflight 加了 S3_ENDPOINT / S3_BUCKET\n强制显式传参与桶名归属守卫(G15 同型:本机与 CI 都可能共用对象存储,\n静默写进别人的桶就是拿别仓存储当验收基座)。\n\nCI 只起了 postgres + redis,因此上一提交推上去会在 preflight 处直接判红——\n这不是回归,是我加了硬要求却没同步跑它的环境。\n\n桶名用 juhai-quotation-ci(须匹配 juhai-quotation* 前缀守卫)。\n健康检查用 /minio/health/live:容器镜像里没有 mc 客户端,`mc ready` 不可用。\n\nCo-Authored-By: Claude Opus 5 \u003cnoreply@anthropic.com\u003e\n","AuthorEmail":"luoguoguo@gmail.com","AuthorName":"luoguoguo","CommitterEmail":"luoguoguo@gmail.com","CommitterName":"luoguoguo","Timestamp":"2026-08-05T21:38:10-07:00"},{"Sha1":"8feb39921ea8a404b09e287154f4c6fd1f7bd3e3","Message":"feat(products): 商品库服务端搜索筛选 + 图片上传至对象存储\n\n## 一、服务端搜索与筛选(此前 115 个商品只能翻页找)\n\n商品列表新增 keyword / kind 两个过滤维度,与既有 active 一并落 DB where。\n谓词收进 contracts `buildProductListFilter` 单源,两后端各自\n`{ tenantId, ...buildProductListFilter(query) }`——行为对等由结构保证,\n任一侧自己拼 where 就会搜出不同结果而无红盘(G17 漂移形态)。\n\n- 关键词语义(SKU + 名称双列 contains、大小写不敏感、不含 description)\n 单独抽 `buildProductKeywordFilter`,换口径只改这一个函数。\n- 空串/纯空白在契约层归一为 undefined(C24 同型:清空搜索框最易发出空串),\n 前端另在 api 层直接不发该参数——双层防御。\n- tenantId 刻意不由该函数产出:租户隔离不能依赖一个可被调用方忘记展开的返回值。\n\n页面同步重构:录入表单默认收起、卡片压缩、展开管理时横跨整行、分页条下移、\n空态区分「筛选无结果」与「库里没商品」、成本 ¥0 改显「成本未录入」\n(导入时成本列整列为空是已知缺口,直显 ¥0.00 会被读成毛利 100%)。\n配件行由固定列宽改 flex-wrap + 最小宽度:视口断点用在 1/3 视口宽的卡片里\n是容器语境错配,断点满足了容器并没有变宽。\n\nProductLibrary 443 → 183 行,拆出 5 个子组件并从 component-size 债务登记毕业。\n\n## 二、图片上传至对象存储(此前只能粘贴外链 URL)\n\nMinIO 进 compose,两后端走 S3 SDK——换阿里云 OSS / 腾讯云 COS 只改 endpoint\n与凭证,写链代码不动。上传链:multipart → 按真实字节魔数判类型 → sharp 转\nWebP + 缩略图 → 写对象存储 → 落库(与 outbox 同 tx)。\n\n- 类型判定收进 contracts,连判定顺序也收:Content-Type 与扩展名都是攻击者\n 可控输入;顺序决定 0 字节文件报「没收到文件」还是「类型不支持」。\n 不收 SVG(可执行文档,直出即存储型 XSS)与 GIF。\n- S3 写入必须先于落库且在事务外:先落库则上传失败留下指向空对象的行(破图),\n 先上传则落库失败只留下无人引用的对象(垃圾,可清理)。代价不对称。\n- 公开回读 /api/files/:tenantId/:imageId/:variant 不鉴权(对客报价页没有内部\n 身份),路径穿越由 schema 校验后重建 key 结构性排除,非法参数回 404 不回 400。\n- DB 存相对路径而非对象存储绝对地址:换存储那天不必迁移存量 URL。\n- backup/restore 成对覆盖 minio 卷:只备份 Postgres 时恢复演练照常报绿,\n 但每行 ProductImage 都指向不存在的对象。卷名问 Docker 要而不是按\n 「项目名_卷名」拼——拼错会备份一个空卷而命令成功。\n\n## 三、修复与门禁\n\n- 修 `after-sales-order-options-hook` 在 HEAD 上的存量误杀(C29 修复插入\n ~700 字符把距离顶到 1749,撑破 1600 窗口,接线本身完好)。\n- 三条断言随组件拆分重新锚定(成本可见性/公开配件/配件确认框/元→分转换)。\n- 新增 6 条上传链断言;其中 2 条初版无牙被负向测试当场抓出并重写:\n 距离型顺序断言不测顺序、`id: imageId` 锚点在 removeImage 里不唯一(G13 同型)。\n 窗口取值均由实测距离确定,不拍脑袋。\n- DIVERGENCE_CEILINGS.products 142→152 属扩容非放水:同轮 PARITY_FLOORS.products\n 74.1→76.9(实测 77.4 高于校准时 74.6),只放松绝对判据是净削弱。\n- turbo globalEnv 补 S3_* 六项(不补则测试进程拿不到凭证,表现为 500)。\n\n## 证据(作用域=本地工作区)\n\n- pnpm check:21 项全绿;负向注入 40 → 44 条\n- check:runtime:497/497(contracts 395 + 双端各 51),真上传打真 MinIO,地板已收紧\n- check:ui:12/12,上传段断言 naturalWidth \u003e 0(src 有值只证明存了个字符串)\n\n尚未核验远端 CI。\n\nCo-Authored-By: Claude Opus 5 \u003cnoreply@anthropic.com\u003e\n","AuthorEmail":"luoguoguo@gmail.com","AuthorName":"luoguoguo","CommitterEmail":"luoguoguo@gmail.com","CommitterName":"luoguoguo","Timestamp":"2026-08-05T21:35:24-07:00"}],"HeadCommit":{"Sha1":"35ebc8613b168219ce82c0b3cf9336d32e958821","Message":"feat(ui): 方案商品多选批量添加 + 交互模式采纳率收口(全量落盘,含并发在途批次)\n\n整棵混合工作区的全量提交:多个战役共树、文件内手笔交错,无法按文件拆分归属,\n经用户裁决全量提交推送;已知红盘如实列出,等对应战役收口后 CI 自然转绿。\n\n本轮两个战役(已验证):\n- 方案设计三档选品改多选批量:新增 TierProductPicker(checkbox 候选 + 分类/关键词\n 收窄 + 待添加纸片跨筛选常驻 + 已添加禁勾),SolutionDesigner 416→252 行并从\n component-size 棘轮毕业;E2E 方案章改写为「勾 2 件一次落 2 行」的多选证明。\n- 交互模式采纳率收口:裸 {x.error.message} 29→1(统一走 ErrorAlert/QueryErrorState,\n receivables 本地 ErrorText 裸实现改包装、13 调用点一次收口,消掉 text-red-600 与\n 「操作失败,请重试」兜底);裸 disabled={isPending} 59→31(单动作按钮换 Button\n loading 保焦点带 aria-busy;行共享 mutation/批准驳回对/兄弟禁用具名保留);\n check:design-tokens 新增 designTokenRawErrorMessage=1 / designTokenBareIsPendingDisable=31\n 两条采纳率棘轮(计数单源 scripts/lib/design-adoption.mjs),design-adoption.test.mjs\n 13 例负向自检进 gate-selftest,活体注入实跑 exit=1 判红后恢复。\n\n同树落盘的并发/前序批次(未逐一复验,以各自战役记录为准):\n- 报价版本中心(进行中,已知红:module-integration 的 quotation-version-detail-visible\n + component-size 3 处【MobileOverview 473\u003e430、QuotationCard 437、QuotationWorkbench\n 665\u003e566】+ gate-selftest 基线用例如实继承)\n- 事件业务编号按域推广、售后/方案后端切片、设计稿真源目录与 08-07 迁移目录等\n\n证据(本地混合工作区,2026-08-13,详见 CLAUDE.md 动态区新条目):\ntypecheck / lint / design-tokens / role-permissions / naming / schema / validation /\ncontract-consumers / governance-docs / workspace-hygiene 绿;真实 Chromium 定向验收\n「商品客户」「客户方案闭环」两章通过(全新库 ui18);「报价闭环」红属版本中心在途,\n「履约闭环」被串行阻断——fulfillment/receivables 的 loading 换写暂只有 typecheck+lint\n级证据,全量 check:ui 复跑时重点看这两章。\n\nCo-Authored-By: Claude Fable 5 \u003cnoreply@anthropic.com\u003e\n","AuthorEmail":"luoguoguo@gmail.com","AuthorName":"luoguoguo","CommitterEmail":"luoguoguo@gmail.com","CommitterName":"luoguoguo","Timestamp":"2026-08-12T23:22:47-07:00"},"CompareURL":"luoanwu/juhai-quotation-system/compare/be1e0734c39315b61a7bf452231a853f44866085...35ebc8613b168219ce82c0b3cf9336d32e958821","Len":5}...
|
1786602303
|
Edit
Delete
|
|
22302
|
5
|
5
|
5
|
72
|
0
|
0
|
refs/heads/feat/product-catalog-search-and-image-u refs/heads/feat/product-catalog-search-and-image-upload...
|
0
|
{"Commits":[{"Sha1":"2c0409a3c {"Commits":[{"Sha1":"2c0409a3cbd22aaba6d36a123d8ba9bea5711059","Message":"chore(dev): dev 端口防跨仓劫持——API 迁 13099、web 改 autoPort\n\n实锤:本机多仓并发会话互抢 3xxx 段端口,3098 被他仓 Next 占、3099 被\n「图像生成」的 fastify 占——后者 /api/health 报文与本仓逐字同构\n(fastify + database/redis up),探活全绿而业务路由全 404,页面表现为\n「Route GET:/api/solutions not found」(G15「端口通≠基座对」的跨仓变体)。\n\n- api-fastify:固定端口 3099 → 13099,迁出拥挤段;\n- web:去掉写死的 -p 3098,改 autoPort(next dev 吃 PORT env),\n API_PROXY_TARGET / NEXT_PUBLIC_WS_URL 同步指向 13099。\n判断「端口上是不是本仓服务」以后要打带 token 的业务路由,不能只看 health。\n\nCo-Authored-By: Claude Fable 5 \u003cnoreply@anthropic.com\u003e\n","AuthorEmail":"luoguoguo@gmail.com","AuthorName":"luoguoguo","CommitterEmail":"luoguoguo@gmail.com","CommitterName":"luoguoguo","Timestamp":"2026-08-12T23:35:20-07:00"}],"HeadCommit":{"Sha1":"2c0409a3cbd22aaba6d36a123d8ba9bea5711059","Message":"chore(dev): dev 端口防跨仓劫持——API 迁 13099、web 改 autoPort\n\n实锤:本机多仓并发会话互抢 3xxx 段端口,3098 被他仓 Next 占、3099 被\n「图像生成」的 fastify 占——后者 /api/health 报文与本仓逐字同构\n(fastify + database/redis up),探活全绿而业务路由全 404,页面表现为\n「Route GET:/api/solutions not found」(G15「端口通≠基座对」的跨仓变体)。\n\n- api-fastify:固定端口 3099 → 13099,迁出拥挤段;\n- web:去掉写死的 -p 3098,改 autoPort(next dev 吃 PORT env),\n API_PROXY_TARGET / NEXT_PUBLIC_WS_URL 同步指向 13099。\n判断「端口上是不是本仓服务」以后要打带 token 的业务路由,不能只看 health。\n\nCo-Authored-By: Claude Fable 5 \u003cnoreply@anthropic.com\u003e\n","AuthorEmail":"luoguoguo@gmail.com","AuthorName":"luoguoguo","CommitterEmail":"luoguoguo@gmail.com","CommitterName":"luoguoguo","Timestamp":"2026-08-12T23:35:20-07:00"},"CompareURL":"luoanwu/juhai-quotation-system/compare/35ebc8613b168219ce82c0b3cf9336d32e958821...2c0409a3cbd22aaba6d36a123d8ba9bea5711059","Len":1}...
|
1786602927
|
Edit
Delete
|
|
22536
|
5
|
5
|
5
|
72
|
0
|
0
|
refs/heads/feat/product-catalog-search-and-image-u refs/heads/feat/product-catalog-search-and-image-upload...
|
0
|
{"Commits":[{"Sha1":"6e260f9ed {"Commits":[{"Sha1":"6e260f9edb613a60434dd3c0b38c78068c346022","Message":"feat(ui): 交互审计全项收口——成功回执、状态机中文报错、子页交互三缺口\n\n同日两轮,基线 2c0409a 干净树,全部改动可干净归属。\n\n轮一(审计项 3/4):\n- 成功回执:保存方案/收款登记/确认到账/发送报价四处 mutation 补 toast\n (按售后成例「结果 + 下一站归谁」;收款文案显式说清「登记≠到账,\n 确认后才解锁发货门禁」的三责分离)。ToastProvider 早已挂载,\n 此前 81 个 mutation 只有售后 1 个消费者。\n- 双后端 24 处状态机报错去英文枚举:`状态 ${STATE} 不允许执行 ${event}`\n → `…当前状态为「中文标签」,不支持该操作`(9 通用 + 3 动作变体 ×2 后端,\n 文案逐字一致);消费 contracts 既有 *_STATE_LABELS——错误路径此前从未用过它们,\n 与 error-messages.ts 的发现同构。内部事件动词不外泄(标注 21);\n code/state/allowed 结构字段原样保留,前端零影响。\n\n轮二(子页交互审计 P1-P3;shell 骨架已把 08-04 诊断 §2.7 实质关闭,本轮修行为缺口):\n- P1 履约 Orders 六 mode 同实例:page/搜索词/状态筛选/选中补 [mode] 重置,\n 镜像售后纪律;「切队列不换单」由 selectedOrderId ?? focusId 回退结构保住。\n- P2 售后流水线胶囊接 AfterSalesSummary.byState 出每段积压数\n (共用 query cache;徽标 aria-hidden,0 不渲染)。\n- P3 新建 WorkspaceMain:main landmark + aria-label=入口名 + tabIndex=-1,\n 子页切换后焦点移入播报并滚回顶部;首帧不抢焦点;带 focusId 的跳转不滚顶\n (报价卡自滚,父效应后跑会砸掉它——标注 16)。\n Home 借道 activeEntry 单查找瘦身回 399/400,下一个动 Home 的必须先拆分。\n\n证据(2026-08-13 本地,详见 CLAUDE.md 动态区两条新记录):\ncheck:runtime 全量 548 用例绿(地板 503,分包 436/56/56,全新库 gov25);\n三端 typecheck / web lint / design-tokens / dual-backend / workspace-hygiene 绿;\n真实 Chromium 全新库(ui19)「商品客户」「客户方案闭环」两章全绿;\n浏览器实测:保存方案 toast 落 aria-live、409 新文案「方案当前状态为「可设计」…」、\n物流搜索词切队列后清空、切维修复检焦点落 main 且 scrollY 600→0、\n流水线徽标计数与真数据一致。module-integration/component-size 仍剩的红\n均属报价版本中心在途战役(quotation-version-detail-visible + 3 处体积),非本轮引入。\n\nCo-Authored-By: Claude Fable 5 \u003cnoreply@anthropic.com\u003e\n","AuthorEmail":"luoguoguo@gmail.com","AuthorName":"luoguoguo","CommitterEmail":"luoguoguo@gmail.com","CommitterName":"luoguoguo","Timestamp":"2026-08-13T08:00:31-07:00"}],"HeadCommit":{"Sha1":"6e260f9edb613a60434dd3c0b38c78068c346022","Message":"feat(ui): 交互审计全项收口——成功回执、状态机中文报错、子页交互三缺口\n\n同日两轮,基线 2c0409a 干净树,全部改动可干净归属。\n\n轮一(审计项 3/4):\n- 成功回执:保存方案/收款登记/确认到账/发送报价四处 mutation 补 toast\n (按售后成例「结果 + 下一站归谁」;收款文案显式说清「登记≠到账,\n 确认后才解锁发货门禁」的三责分离)。ToastProvider 早已挂载,\n 此前 81 个 mutation 只有售后 1 个消费者。\n- 双后端 24 处状态机报错去英文枚举:`状态 ${STATE} 不允许执行 ${event}`\n → `…当前状态为「中文标签」,不支持该操作`(9 通用 + 3 动作变体 ×2 后端,\n 文案逐字一致);消费 contracts 既有 *_STATE_LABELS——错误路径此前从未用过它们,\n 与 error-messages.ts 的发现同构。内部事件动词不外泄(标注 21);\n code/state/allowed 结构字段原样保留,前端零影响。\n\n轮二(子页交互审计 P1-P3;shell 骨架已把 08-04 诊断 §2.7 实质关闭,本轮修行为缺口):\n- P1 履约 Orders 六 mode 同实例:page/搜索词/状态筛选/选中补 [mode] 重置,\n 镜像售后纪律;「切队列不换单」由 selectedOrderId ?? focusId 回退结构保住。\n- P2 售后流水线胶囊接 AfterSalesSummary.byState 出每段积压数\n (共用 query cache;徽标 aria-hidden,0 不渲染)。\n- P3 新建 WorkspaceMain:main landmark + aria-label=入口名 + tabIndex=-1,\n 子页切换后焦点移入播报并滚回顶部;首帧不抢焦点;带 focusId 的跳转不滚顶\n (报价卡自滚,父效应后跑会砸掉它——标注 16)。\n Home 借道 activeEntry 单查找瘦身回 399/400,下一个动 Home 的必须先拆分。\n\n证据(2026-08-13 本地,详见 CLAUDE.md 动态区两条新记录):\ncheck:runtime 全量 548 用例绿(地板 503,分包 436/56/56,全新库 gov25);\n三端 typecheck / web lint / design-tokens / dual-backend / workspace-hygiene 绿;\n真实 Chromium 全新库(ui19)「商品客户」「客户方案闭环」两章全绿;\n浏览器实测:保存方案 toast 落 aria-live、409 新文案「方案当前状态为「可设计」…」、\n物流搜索词切队列后清空、切维修复检焦点落 main 且 scrollY 600→0、\n流水线徽标计数与真数据一致。module-integration/component-size 仍剩的红\n均属报价版本中心在途战役(quotation-version-detail-visible + 3 处体积),非本轮引入。\n\nCo-Authored-By: Claude Fable 5 \u003cnoreply@anthropic.com\u003e\n","AuthorEmail":"luoguoguo@gmail.com","AuthorName":"luoguoguo","CommitterEmail":"luoguoguo@gmail.com","CommitterName":"luoguoguo","Timestamp":"2026-08-13T08:00:31-07:00"},"CompareURL":"luoanwu/juhai-quotation-system/compare/2c0409a3cbd22aaba6d36a123d8ba9bea5711059...6e260f9edb613a60434dd3c0b38c78068c346022","Len":1}...
|
1786633238
|
Edit
Delete
|
|
23623
|
5
|
5
|
5
|
72
|
0
|
0
|
refs/heads/feat/product-catalog-search-and-image-u refs/heads/feat/product-catalog-search-and-image-upload...
|
0
|
{"Commits":[{"Sha1":"3afb568a1 {"Commits":[{"Sha1":"3afb568a1d13a2ceeaf49a2a9bf1d8634f422c23","Message":"fix(gate): 版本中心断言重锚为结构式——距离窗口误杀且漏杀,顺带逼出报价域拆分\n\nPR #11 的 CI 红在 quotation-version-detail-visible。查证为**门禁误杀**:版本中心的\n快照详情一直正常渲染(浏览器实测展开 V1 可见「经济方案 ¥910,000.00 · 1 项明细」),\n只是行内补了 diff 段落与三段设计裁决注释后,两个锚点间距涨到 1309 字符、超过\n{0,500} 距离窗口(after-sales-order-options-hook 同型,本仓第二次踩)。\n\n断言重锚为否定环视 (?:(?!\u003c\\/details\u003e)[\\s\\S])*,语义由「两锚点挨得近」改为\n「快照摘要在版本行元素内部」。差分真值表显示旧写法 4 例错 2 例、两个方向都错:\n不仅误杀,还漏杀「快照挪到行外但离得近」——重锚顺手补上那个洞,并固化为\ncontract-integration.test.mjs 第 24b 条负向用例(实跑 exit=1 且命中预期断言 id)。\n\ncomponent-size 同轮逼出真拆分(门禁明说「先拆分而不是上调登记值」):\n- QuotationWorkbench 661→169:创建卡连同表单状态提为 QuotationCreateCard、\n 单档编辑器提为 QuotationOptionEditor、版本中心提为 QuotationVersionCenter\n- MobileOverview 473→413:报价经营四指标卡提为 OverviewQuoteMetrics\n全部抽为**同文件兄弟组件**——搬到独立文件会让按路径锚定的门禁锚点落空。\nQuotationCreateCard 刻意始终挂载、在 hooks 之后才 return null,保证候选查询的\n发起时机与重构前逐字一致(只搬行不改行为)。登记值同步下调/毕业锁住进步。\n\n⚠️ 抽 OverviewQuoteMetrics 时一度把三元中段当 JSX 搬走并用 \u003c\u003e 包裹,`) : (`\n降格为文本节点原样上屏,且两个分支同时渲染——给有权限的管理员显示「未向当前\n角色开放」。tsc 与 22 项静态门禁全绿,只有浏览器截图抓得到。已修并留档。\n\n证据(作用域=本地工作区):pnpm check 22 项全绿 exit=0、16 组负向自检有牙、\n浏览器实测总览/新建报价/版本中心三处渲染正常。未重跑 check:runtime / check:ui\n(两后端与 contracts 一行未动,那两级证据仍绑定上一轮 commit)。\n\nCo-Authored-By: Claude Opus 5 \u003cnoreply@anthropic.com\u003e\n","AuthorEmail":"luoguoguo@gmail.com","AuthorName":"luoguoguo","CommitterEmail":"luoguoguo@gmail.com","CommitterName":"luoguoguo","Timestamp":"2026-08-17T03:47:39-07:00"}],"HeadCommit":{"Sha1":"3afb568a1d13a2ceeaf49a2a9bf1d8634f422c23","Message":"fix(gate): 版本中心断言重锚为结构式——距离窗口误杀且漏杀,顺带逼出报价域拆分\n\nPR #11 的 CI 红在 quotation-version-detail-visible。查证为**门禁误杀**:版本中心的\n快照详情一直正常渲染(浏览器实测展开 V1 可见「经济方案 ¥910,000.00 · 1 项明细」),\n只是行内补了 diff 段落与三段设计裁决注释后,两个锚点间距涨到 1309 字符、超过\n{0,500} 距离窗口(after-sales-order-options-hook 同型,本仓第二次踩)。\n\n断言重锚为否定环视 (?:(?!\u003c\\/details\u003e)[\\s\\S])*,语义由「两锚点挨得近」改为\n「快照摘要在版本行元素内部」。差分真值表显示旧写法 4 例错 2 例、两个方向都错:\n不仅误杀,还漏杀「快照挪到行外但离得近」——重锚顺手补上那个洞,并固化为\ncontract-integration.test.mjs 第 24b 条负向用例(实跑 exit=1 且命中预期断言 id)。\n\ncomponent-size 同轮逼出真拆分(门禁明说「先拆分而不是上调登记值」):\n- QuotationWorkbench 661→169:创建卡连同表单状态提为 QuotationCreateCard、\n 单档编辑器提为 QuotationOptionEditor、版本中心提为 QuotationVersionCenter\n- MobileOverview 473→413:报价经营四指标卡提为 OverviewQuoteMetrics\n全部抽为**同文件兄弟组件**——搬到独立文件会让按路径锚定的门禁锚点落空。\nQuotationCreateCard 刻意始终挂载、在 hooks 之后才 return null,保证候选查询的\n发起时机与重构前逐字一致(只搬行不改行为)。登记值同步下调/毕业锁住进步。\n\n⚠️ 抽 OverviewQuoteMetrics 时一度把三元中段当 JSX 搬走并用 \u003c\u003e 包裹,`) : (`\n降格为文本节点原样上屏,且两个分支同时渲染——给有权限的管理员显示「未向当前\n角色开放」。tsc 与 22 项静态门禁全绿,只有浏览器截图抓得到。已修并留档。\n\n证据(作用域=本地工作区):pnpm check 22 项全绿 exit=0、16 组负向自检有牙、\n浏览器实测总览/新建报价/版本中心三处渲染正常。未重跑 check:runtime / check:ui\n(两后端与 contracts 一行未动,那两级证据仍绑定上一轮 commit)。\n\nCo-Authored-By: Claude Opus 5 \u003cnoreply@anthropic.com\u003e\n","AuthorEmail":"luoguoguo@gmail.com","AuthorName":"luoguoguo","CommitterEmail":"luoguoguo@gmail.com","CommitterName":"luoguoguo","Timestamp":"2026-08-17T03:47:39-07:00"},"CompareURL":"luoanwu/juhai-quotation-system/compare/6e260f9edb613a60434dd3c0b38c78068c346022...3afb568a1d13a2ceeaf49a2a9bf1d8634f422c23","Len":1}...
|
1786963673
|
Edit
Delete
|
|
23624
|
5
|
5
|
5
|
72
|
0
|
0
|
refs/heads/feat/product-catalog-search-and-image-u refs/heads/feat/product-catalog-search-and-image-upload...
|
0
|
{"Commits":[{"Sha1":"874115874 {"Commits":[{"Sha1":"874115874d1ce6a322367e0553a620a531ebdaf9","Message":"fix(e2e): 修两条潜伏五天的失效断言——\u0026\u0026 串行链把 UI 验收挡在门外\n\n上一条提交让 static governance 转绿后,CI 的 Runtime and UI acceptance 才**首次真正\n跑起来**(自 2026-08-06 起每轮都是 SKIPPED),当场暴露两条 E2E 失效断言。二者均由\n35ebc86(2026-08-12)改文案引入而断言没跟上,与本轮重构无关:\n\n① 侧栏入口叫「报价审核」,子页标题已改为「待我审核」——入口名与标题是两个口径,\n 不能因为同一个入口就复用同一个词。\n② 版本徽标由「版本 1」改为紧凑的「v1」(与版本中心的「V1」看齐)。\n 断言收紧为 /·\\s*v1\\s*·/:裸 `v1` 会被报价号或客户名里任何含 v1 的片段撞上,\n 会让断言以错误理由变绿,那恰恰是它该防的漂移。\n\nmodule-integration 的浏览器证据标记同步跟到**标题**而非入口名——写成入口名会被\nopenWorkspaceEntry(page, \"报价审核\") 白白喂饱,标记就不再证明任何事(负向实测:\n把 E2E 里该断言改掉即 exit=1 并命中 browser-name: \"待我审核\")。\n\n顺带删掉上一条提交遗留的死代码:权限判据已随创建卡下沉进 QuotationCreateCard,\nQuotationWorkbench 里的 userCan 不再有消费者,留着会让下一个人以为还有权限逻辑要接。\n\n三级门禁证据(作用域=本地工作区):\n- 静态 pnpm check:22 项全绿 exit=0,16 组负向自检有牙\n- check:runtime:548/548(地板 503;contracts 436 + NestJS 56 + Fastify 56)\n- check:ui:12/12 全绿\n\n教训已回灌 CLAUDE.md:串行 \u0026\u0026 门禁链报出的违规数是**下界不是总数**,\n本轮 CI 报「共 1 处违规」,实际是 4 处静态 + 2 处 E2E。\n\nCo-Authored-By: Claude Opus 5 \u003cnoreply@anthropic.com\u003e\n","AuthorEmail":"luoguoguo@gmail.com","AuthorName":"luoguoguo","CommitterEmail":"luoguoguo@gmail.com","CommitterName":"luoguoguo","Timestamp":"2026-08-17T04:03:39-07:00"}],"HeadCommit":{"Sha1":"874115874d1ce6a322367e0553a620a531ebdaf9","Message":"fix(e2e): 修两条潜伏五天的失效断言——\u0026\u0026 串行链把 UI 验收挡在门外\n\n上一条提交让 static governance 转绿后,CI 的 Runtime and UI acceptance 才**首次真正\n跑起来**(自 2026-08-06 起每轮都是 SKIPPED),当场暴露两条 E2E 失效断言。二者均由\n35ebc86(2026-08-12)改文案引入而断言没跟上,与本轮重构无关:\n\n① 侧栏入口叫「报价审核」,子页标题已改为「待我审核」——入口名与标题是两个口径,\n 不能因为同一个入口就复用同一个词。\n② 版本徽标由「版本 1」改为紧凑的「v1」(与版本中心的「V1」看齐)。\n 断言收紧为 /·\\s*v1\\s*·/:裸 `v1` 会被报价号或客户名里任何含 v1 的片段撞上,\n 会让断言以错误理由变绿,那恰恰是它该防的漂移。\n\nmodule-integration 的浏览器证据标记同步跟到**标题**而非入口名——写成入口名会被\nopenWorkspaceEntry(page, \"报价审核\") 白白喂饱,标记就不再证明任何事(负向实测:\n把 E2E 里该断言改掉即 exit=1 并命中 browser-name: \"待我审核\")。\n\n顺带删掉上一条提交遗留的死代码:权限判据已随创建卡下沉进 QuotationCreateCard,\nQuotationWorkbench 里的 userCan 不再有消费者,留着会让下一个人以为还有权限逻辑要接。\n\n三级门禁证据(作用域=本地工作区):\n- 静态 pnpm check:22 项全绿 exit=0,16 组负向自检有牙\n- check:runtime:548/548(地板 503;contracts 436 + NestJS 56 + Fastify 56)\n- check:ui:12/12 全绿\n\n教训已回灌 CLAUDE.md:串行 \u0026\u0026 门禁链报出的违规数是**下界不是总数**,\n本轮 CI 报「共 1 处违规」,实际是 4 处静态 + 2 处 E2E。\n\nCo-Authored-By: Claude Opus 5 \u003cnoreply@anthropic.com\u003e\n","AuthorEmail":"luoguoguo@gmail.com","AuthorName":"luoguoguo","CommitterEmail":"luoguoguo@gmail.com","CommitterName":"luoguoguo","Timestamp":"2026-08-17T04:03:39-07:00"},"CompareURL":"luoanwu/juhai-quotation-system/compare/3afb568a1d13a2ceeaf49a2a9bf1d8634f422c23...874115874d1ce6a322367e0553a620a531ebdaf9","Len":1}...
|
1786964630
|
Edit
Delete
|
|
23625
|
5
|
5
|
5
|
72
|
0
|
0
|
refs/heads/feat/product-catalog-search-and-image-u refs/heads/feat/product-catalog-search-and-image-upload...
|
0
|
{"Commits":[{"Sha1":"6a67a7abf {"Commits":[{"Sha1":"6a67a7abf58201b2b407aab3e500b463d3f08bc7","Message":"Merge branch 'feat/product-catalog-search-and-image-upload' of https://github.com/laoluojuhai/juhai-quotation-system into feat/product-catalog-search-and-image-upload\n","AuthorEmail":"luoguoguo@gmail.com","AuthorName":"luoguoguo","CommitterEmail":"luoguoguo@gmail.com","CommitterName":"luoguoguo","Timestamp":"2026-08-17T04:04:06-07:00"},{"Sha1":"c7d8d5944215315512254bb9a8f6cc2bd937c88f","Message":"Merge branch 'main' into feat/product-catalog-search-and-image-upload","AuthorEmail":"158980461+laoluojuhai@users.noreply.github.com","AuthorName":"laoluojuhai","CommitterEmail":"noreply@github.com","CommitterName":"GitHub","Timestamp":"2026-08-17T03:48:25-07:00"},{"Sha1":"e3fc771d3e1769a970dd0638d0ce694fae74a023","Message":"Merge pull request #8 from laoluojuhai/feat/g8-verified-auth\n\nfeat(g8): 已验签身份落地 + 业务闭环五切片 + 工作区卫生门禁","AuthorEmail":"158980461+laoluojuhai@users.noreply.github.com","AuthorName":"laoluojuhai","CommitterEmail":"noreply@github.com","CommitterName":"GitHub","Timestamp":"2026-08-02T19:06:51-07:00"}],"HeadCommit":{"Sha1":"6a67a7abf58201b2b407aab3e500b463d3f08bc7","Message":"Merge branch 'feat/product-catalog-search-and-image-upload' of https://github.com/laoluojuhai/juhai-quotation-system into feat/product-catalog-search-and-image-upload\n","AuthorEmail":"luoguoguo@gmail.com","AuthorName":"luoguoguo","CommitterEmail":"luoguoguo@gmail.com","CommitterName":"luoguoguo","Timestamp":"2026-08-17T04:04:06-07:00"},"CompareURL":"luoanwu/juhai-quotation-system/compare/874115874d1ce6a322367e0553a620a531ebdaf9...6a67a7abf58201b2b407aab3e500b463d3f08bc7","Len":3}...
|
1786964711
|
Edit
Delete
|
|
19163
|
1
|
5
|
1
|
32
|
0
|
0
|
refs/heads/feat/multi-tenant
|
1
|
|
1785215714
|
Edit
Delete
|
|
19164
|
1
|
5
|
1
|
32
|
0
|
0
|
refs/heads/feat/multi-tenant
|
1
|
{"Commits":[{"Sha1":"4d3adab33 {"Commits":[{"Sha1":"4d3adab33937e145ac2c0329518019163bf00172","Message":"feat(tenant): add multi-merchant architecture\n\nBind merchant, platform, and frontend auth to separate scopes.\nScope business data, caches, media, and WebSocket rooms by merchant.\n\nBREAKING CHANGE: users.role is no longer read or written.\nRun sql/2026-07-28_add_multi_tenant.sql before deployment.\n","AuthorEmail":"121158035@qq.com","AuthorName":"zhangjunnan","CommitterEmail":"121158035@qq.com","CommitterName":"zhangjunnan","Timestamp":"2026-07-28T13:13:15+08:00"}],"HeadCommit":{"Sha1":"4d3adab33937e145ac2c0329518019163bf00172","Message":"feat(tenant): add multi-merchant architecture\n\nBind merchant, platform, and frontend auth to separate scopes.\nScope business data, caches, media, and WebSocket rooms by merchant.\n\nBREAKING CHANGE: users.role is no longer read or written.\nRun sql/2026-07-28_add_multi_tenant.sql before deployment.\n","AuthorEmail":"121158035@qq.com","AuthorName":"zhangjunnan","CommitterEmail":"121158035@qq.com","CommitterName":"zhangjunnan","Timestamp":"2026-07-28T13:13:15+08:00"},"CompareURL":"zhangjunnan/zhouhui/compare/17343d79db91c05d317145998ea1962645ee1b13...4d3adab33937e145ac2c0329518019163bf00172","Len":1}...
|
1785215714
|
Edit
Delete
|
|
19181
|
1
|
5
|
1
|
32
|
0
|
0
|
refs/heads/feat/multi-tenant
|
1
|
{"Commits":[{"Sha1":"bb26e7c67 {"Commits":[{"Sha1":"bb26e7c67797f97fd6d510d77049b892a7e93bbf","Message":"fix(tenant): align haihui deployment\n\nRebuild the tenant migration from the current online schema and create meeting_states when absent.\n\nUse haihui database and domain defaults while leaving database data and cloud Nginx/Docker configuration unchanged.\n\nMigration: run sqlnew/2026-07-28_multi_tenant_migration.sql once after backup.\n","AuthorEmail":"121158035@qq.com","AuthorName":"zhangjunnan","CommitterEmail":"121158035@qq.com","CommitterName":"zhangjunnan","Timestamp":"2026-07-28T13:47:49+08:00"}],"HeadCommit":{"Sha1":"bb26e7c67797f97fd6d510d77049b892a7e93bbf","Message":"fix(tenant): align haihui deployment\n\nRebuild the tenant migration from the current online schema and create meeting_states when absent.\n\nUse haihui database and domain defaults while leaving database data and cloud Nginx/Docker configuration unchanged.\n\nMigration: run sqlnew/2026-07-28_multi_tenant_migration.sql once after backup.\n","AuthorEmail":"121158035@qq.com","AuthorName":"zhangjunnan","CommitterEmail":"121158035@qq.com","CommitterName":"zhangjunnan","Timestamp":"2026-07-28T13:47:49+08:00"},"CompareURL":"zhangjunnan/zhouhui/compare/4d3adab33937e145ac2c0329518019163bf00172...bb26e7c67797f97fd6d510d77049b892a7e93bbf","Len":1}...
|
1785217698
|
Edit
Delete
|
|
19210
|
1
|
5
|
1
|
32
|
0
|
0
|
refs/heads/feat/multi-tenant
|
1
|
{"Commits":[{"Sha1":"0ac14d3dd {"Commits":[{"Sha1":"0ac14d3dd7490301d8ab62b52b96074a7136b0ae","Message":"feat(auth): split merchant and platform login\n","AuthorEmail":"121158035@qq.com","AuthorName":"zhangjunnan","CommitterEmail":"121158035@qq.com","CommitterName":"zhangjunnan","Timestamp":"2026-07-28T14:29:33+08:00"}],"HeadCommit":{"Sha1":"0ac14d3dd7490301d8ab62b52b96074a7136b0ae","Message":"feat(auth): split merchant and platform login\n","AuthorEmail":"121158035@qq.com","AuthorName":"zhangjunnan","CommitterEmail":"121158035@qq.com","CommitterName":"zhangjunnan","Timestamp":"2026-07-28T14:29:33+08:00"},"CompareURL":"zhangjunnan/zhouhui/compare/bb26e7c67797f97fd6d510d77049b892a7e93bbf...0ac14d3dd7490301d8ab62b52b96074a7136b0ae","Len":1}...
|
1785220204
|
Edit
Delete
|
|
19251
|
1
|
5
|
1
|
32
|
0
|
0
|
refs/heads/feat/multi-tenant
|
1
|
{"Commits":[{"Sha1":"3fbac823e {"Commits":[{"Sha1":"3fbac823ef2dcfd7660f2c682b1464773277f617","Message":"多租户\n","AuthorEmail":"121158035@qq.com","AuthorName":"zhangjunnan","CommitterEmail":"121158035@qq.com","CommitterName":"zhangjunnan","Timestamp":"2026-07-28T14:55:41+08:00"}],"HeadCommit":{"Sha1":"3fbac823ef2dcfd7660f2c682b1464773277f617","Message":"多租户\n","AuthorEmail":"121158035@qq.com","AuthorName":"zhangjunnan","CommitterEmail":"121158035@qq.com","CommitterName":"zhangjunnan","Timestamp":"2026-07-28T14:55:41+08:00"},"CompareURL":"zhangjunnan/zhouhui/compare/0ac14d3dd7490301d8ab62b52b96074a7136b0ae...3fbac823ef2dcfd7660f2c682b1464773277f617","Len":1}...
|
1785221755
|
Edit
Delete
|
|
19252
|
1
|
5
|
1
|
32
|
0
|
0
|
refs/heads/feat/multi-tenant
|
1
|
{"Commits":[{"Sha1":"af26d3e82 {"Commits":[{"Sha1":"af26d3e828cfe155dc5926f6165d3040599abf54","Message":"多租户\n","AuthorEmail":"121158035@qq.com","AuthorName":"zhangjunnan","CommitterEmail":"121158035@qq.com","CommitterName":"zhangjunnan","Timestamp":"2026-07-28T15:02:22+08:00"}],"HeadCommit":{"Sha1":"af26d3e828cfe155dc5926f6165d3040599abf54","Message":"多租户\n","AuthorEmail":"121158035@qq.com","AuthorName":"zhangjunnan","CommitterEmail":"121158035@qq.com","CommitterName":"zhangjunnan","Timestamp":"2026-07-28T15:02:22+08:00"},"CompareURL":"zhangjunnan/zhouhui/compare/3fbac823ef2dcfd7660f2c682b1464773277f617...af26d3e828cfe155dc5926f6165d3040599abf54","Len":1}...
|
1785222156
|
Edit
Delete
|
|
19269
|
1
|
5
|
1
|
32
|
0
|
0
|
refs/heads/feat/multi-tenant
|
1
|
{"Commits":[{"Sha1":"60df304f7 {"Commits":[{"Sha1":"60df304f776bef84382a0167be2220497cb26924","Message":"fix(settings): hide disabled merchant settings\n","AuthorEmail":"121158035@qq.com","AuthorName":"zhangjunnan","CommitterEmail":"121158035@qq.com","CommitterName":"zhangjunnan","Timestamp":"2026-07-28T15:15:05+08:00"},{"Sha1":"50ce6c2da9a0136ab7a3aa9c1e44bbb565ced0e5","Message":"feat(settings): add merchant default controls\n\nAdd MySQL 5.6 migration for platform defaults and merchant override state.\n","AuthorEmail":"121158035@qq.com","AuthorName":"zhangjunnan","CommitterEmail":"121158035@qq.com","CommitterName":"zhangjunnan","Timestamp":"2026-07-28T15:05:04+08:00"}],"HeadCommit":{"Sha1":"60df304f776bef84382a0167be2220497cb26924","Message":"fix(settings): hide disabled merchant settings\n","AuthorEmail":"121158035@qq.com","AuthorName":"zhangjunnan","CommitterEmail":"121158035@qq.com","CommitterName":"zhangjunnan","Timestamp":"2026-07-28T15:15:05+08:00"},"CompareURL":"zhangjunnan/zhouhui/compare/af26d3e828cfe155dc5926f6165d3040599abf54...60df304f776bef84382a0167be2220497cb26924","Len":2}...
|
1785223066
|
Edit
Delete
|
|
19286
|
1
|
5
|
1
|
32
|
0
|
0
|
refs/heads/feat/multi-tenant
|
1
|
{"Commits":[{"Sha1":"a2faab710 {"Commits":[{"Sha1":"a2faab710667251b8e7a86cccbf8be87f9e7a688","Message":"fix(settings): hide platform AI configuration\n\nDo not expose platform DashScope keys, endpoints, or model values through merchant settings responses.\n","AuthorEmail":"121158035@qq.com","AuthorName":"zhangjunnan","CommitterEmail":"121158035@qq.com","CommitterName":"zhangjunnan","Timestamp":"2026-07-28T15:27:20+08:00"}],"HeadCommit":{"Sha1":"a2faab710667251b8e7a86cccbf8be87f9e7a688","Message":"fix(settings): hide platform AI configuration\n\nDo not expose platform DashScope keys, endpoints, or model values through merchant settings responses.\n","AuthorEmail":"121158035@qq.com","AuthorName":"zhangjunnan","CommitterEmail":"121158035@qq.com","CommitterName":"zhangjunnan","Timestamp":"2026-07-28T15:27:20+08:00"},"CompareURL":"zhangjunnan/zhouhui/compare/60df304f776bef84382a0167be2220497cb26924...a2faab710667251b8e7a86cccbf8be87f9e7a688","Len":1}...
|
1785223876
|
Edit
Delete
|
|
19487
|
1
|
5
|
1
|
32
|
0
|
0
|
refs/heads/feat/multi-tenant
|
1
|
{"Commits":[{"Sha1":"32293f216 {"Commits":[{"Sha1":"32293f2169249dedbe18409d6ba2c5ad51ec3537","Message":"多租户\n","AuthorEmail":"121158035@qq.com","AuthorName":"zhangjunnan","CommitterEmail":"121158035@qq.com","CommitterName":"zhangjunnan","Timestamp":"2026-07-28T16:30:49+08:00"}],"HeadCommit":{"Sha1":"32293f2169249dedbe18409d6ba2c5ad51ec3537","Message":"多租户\n","AuthorEmail":"121158035@qq.com","AuthorName":"zhangjunnan","CommitterEmail":"121158035@qq.com","CommitterName":"zhangjunnan","Timestamp":"2026-07-28T16:30:49+08:00"},"CompareURL":"zhangjunnan/zhouhui/compare/a2faab710667251b8e7a86cccbf8be87f9e7a688...32293f2169249dedbe18409d6ba2c5ad51ec3537","Len":1}...
|
1785227463
|
Edit
Delete
|
|
19528
|
1
|
5
|
1
|
32
|
0
|
0
|
refs/heads/feat/multi-tenant
|
1
|
{"Commits":[{"Sha1":"03a01d246 {"Commits":[{"Sha1":"03a01d246be8087581cf1a79432be6a35d760796","Message":"多租户\n","AuthorEmail":"121158035@qq.com","AuthorName":"zhangjunnan","CommitterEmail":"121158035@qq.com","CommitterName":"zhangjunnan","Timestamp":"2026-07-28T17:29:22+08:00"}],"HeadCommit":{"Sha1":"03a01d246be8087581cf1a79432be6a35d760796","Message":"多租户\n","AuthorEmail":"121158035@qq.com","AuthorName":"zhangjunnan","CommitterEmail":"121158035@qq.com","CommitterName":"zhangjunnan","Timestamp":"2026-07-28T17:29:22+08:00"},"CompareURL":"zhangjunnan/zhouhui/compare/32293f2169249dedbe18409d6ba2c5ad51ec3537...03a01d246be8087581cf1a79432be6a35d760796","Len":1}...
|
1785230977
|
Edit
Delete
|
|
19529
|
1
|
5
|
1
|
32
|
0
|
0
|
refs/heads/feat/multi-tenant
|
1
|
{"Commits":[{"Sha1":"cc213c0db {"Commits":[{"Sha1":"cc213c0dbe79bcb0c34f36ffaa489c644d9833e3","Message":"多租户\n","AuthorEmail":"121158035@qq.com","AuthorName":"zhangjunnan","CommitterEmail":"121158035@qq.com","CommitterName":"zhangjunnan","Timestamp":"2026-07-28T17:45:20+08:00"}],"HeadCommit":{"Sha1":"cc213c0dbe79bcb0c34f36ffaa489c644d9833e3","Message":"多租户\n","AuthorEmail":"121158035@qq.com","AuthorName":"zhangjunnan","CommitterEmail":"121158035@qq.com","CommitterName":"zhangjunnan","Timestamp":"2026-07-28T17:45:20+08:00"},"CompareURL":"zhangjunnan/zhouhui/compare/03a01d246be8087581cf1a79432be6a35d760796...cc213c0dbe79bcb0c34f36ffaa489c644d9833e3","Len":1}...
|
1785231934
|
Edit
Delete
|
|
19530
|
1
|
5
|
1
|
32
|
0
|
0
|
refs/heads/feat/multi-tenant
|
1
|
{"Commits":[{"Sha1":"ccb5de2c5 {"Commits":[{"Sha1":"ccb5de2c58d0e69499eb58b8c90533f913053c55","Message":"多租户\n","AuthorEmail":"121158035@qq.com","AuthorName":"zhangjunnan","CommitterEmail":"121158035@qq.com","CommitterName":"zhangjunnan","Timestamp":"2026-07-28T17:47:03+08:00"}],"HeadCommit":{"Sha1":"ccb5de2c58d0e69499eb58b8c90533f913053c55","Message":"多租户\n","AuthorEmail":"121158035@qq.com","AuthorName":"zhangjunnan","CommitterEmail":"121158035@qq.com","CommitterName":"zhangjunnan","Timestamp":"2026-07-28T17:47:03+08:00"},"CompareURL":"zhangjunnan/zhouhui/compare/cc213c0dbe79bcb0c34f36ffaa489c644d9833e3...ccb5de2c58d0e69499eb58b8c90533f913053c55","Len":1}...
|
1785232038
|
Edit
Delete
|
|
19531
|
1
|
5
|
1
|
32
|
0
|
0
|
refs/heads/feat/multi-tenant
|
1
|
{"Commits":[{"Sha1":"7a04bda7a {"Commits":[{"Sha1":"7a04bda7a7da4294021c8d47716b6e74257e6999","Message":"多租户\n","AuthorEmail":"121158035@qq.com","AuthorName":"zhangjunnan","CommitterEmail":"121158035@qq.com","CommitterName":"zhangjunnan","Timestamp":"2026-07-28T17:56:15+08:00"}],"HeadCommit":{"Sha1":"7a04bda7a7da4294021c8d47716b6e74257e6999","Message":"多租户\n","AuthorEmail":"121158035@qq.com","AuthorName":"zhangjunnan","CommitterEmail":"121158035@qq.com","CommitterName":"zhangjunnan","Timestamp":"2026-07-28T17:56:15+08:00"},"CompareURL":"zhangjunnan/zhouhui/compare/ccb5de2c58d0e69499eb58b8c90533f913053c55...7a04bda7a7da4294021c8d47716b6e74257e6999","Len":1}...
|
1785232591
|
Edit
Delete
|
|
19564
|
1
|
5
|
1
|
32
|
0
|
0
|
refs/heads/feat/multi-tenant
|
1
|
{"Commits":[{"Sha1":"8f90bed0f {"Commits":[{"Sha1":"8f90bed0fa13a1f92b45cdee6d5f5123d7622f5f","Message":"多租户\n","AuthorEmail":"121158035@qq.com","AuthorName":"zhangjunnan","CommitterEmail":"121158035@qq.com","CommitterName":"zhangjunnan","Timestamp":"2026-07-28T18:19:53+08:00"}],"HeadCommit":{"Sha1":"8f90bed0fa13a1f92b45cdee6d5f5123d7622f5f","Message":"多租户\n","AuthorEmail":"121158035@qq.com","AuthorName":"zhangjunnan","CommitterEmail":"121158035@qq.com","CommitterName":"zhangjunnan","Timestamp":"2026-07-28T18:19:53+08:00"},"CompareURL":"zhangjunnan/zhouhui/compare/7a04bda7a7da4294021c8d47716b6e74257e6999...8f90bed0fa13a1f92b45cdee6d5f5123d7622f5f","Len":1}...
|
1785234011
|
Edit
Delete
|
|
19715
|
1
|
5
|
1
|
32
|
0
|
0
|
refs/heads/feat/multi-tenant
|
1
|
{"Commits":[{"Sha1":"69375f170 {"Commits":[{"Sha1":"69375f170df08d3cbbc789072760555c39780d80","Message":"多租户\n","AuthorEmail":"121158035@qq.com","AuthorName":"zhangjunnan","CommitterEmail":"121158035@qq.com","CommitterName":"zhangjunnan","Timestamp":"2026-07-29T15:26:14+08:00"}],"HeadCommit":{"Sha1":"69375f170df08d3cbbc789072760555c39780d80","Message":"多租户\n","AuthorEmail":"121158035@qq.com","AuthorName":"zhangjunnan","CommitterEmail":"121158035@qq.com","CommitterName":"zhangjunnan","Timestamp":"2026-07-29T15:26:14+08:00"},"CompareURL":"zhangjunnan/zhouhui/compare/8f90bed0fa13a1f92b45cdee6d5f5123d7622f5f...69375f170df08d3cbbc789072760555c39780d80","Len":1}...
|
1785310020
|
Edit
Delete
|
|
19716
|
1
|
5
|
1
|
32
|
0
|
0
|
refs/heads/feat/multi-tenant
|
1
|
{"Commits":[{"Sha1":"93d3eb76c {"Commits":[{"Sha1":"93d3eb76ce4e1f60f49601d7eb724898184e9508","Message":"多租户\n","AuthorEmail":"121158035@qq.com","AuthorName":"zhangjunnan","CommitterEmail":"121158035@qq.com","CommitterName":"zhangjunnan","Timestamp":"2026-07-29T15:53:57+08:00"}],"HeadCommit":{"Sha1":"93d3eb76ce4e1f60f49601d7eb724898184e9508","Message":"多租户\n","AuthorEmail":"121158035@qq.com","AuthorName":"zhangjunnan","CommitterEmail":"121158035@qq.com","CommitterName":"zhangjunnan","Timestamp":"2026-07-29T15:53:57+08:00"},"CompareURL":"zhangjunnan/zhouhui/compare/69375f170df08d3cbbc789072760555c39780d80...93d3eb76ce4e1f60f49601d7eb724898184e9508","Len":1}...
|
1785311644
|
Edit
Delete
|
|
19717
|
1
|
5
|
1
|
32
|
0
|
0
|
refs/heads/feat/multi-tenant
|
1
|
{"Commits":[{"Sha1":"7f3a435b8 {"Commits":[{"Sha1":"7f3a435b825db5daf4df80ad944c8395036bc123","Message":"多租户\n","AuthorEmail":"121158035@qq.com","AuthorName":"zhangjunnan","CommitterEmail":"121158035@qq.com","CommitterName":"zhangjunnan","Timestamp":"2026-07-29T16:06:32+08:00"}],"HeadCommit":{"Sha1":"7f3a435b825db5daf4df80ad944c8395036bc123","Message":"多租户\n","AuthorEmail":"121158035@qq.com","AuthorName":"zhangjunnan","CommitterEmail":"121158035@qq.com","CommitterName":"zhangjunnan","Timestamp":"2026-07-29T16:06:32+08:00"},"CompareURL":"zhangjunnan/zhouhui/compare/93d3eb76ce4e1f60f49601d7eb724898184e9508...7f3a435b825db5daf4df80ad944c8395036bc123","Len":1}...
|
1785312403
|
Edit
Delete
|
|
19734
|
1
|
5
|
1
|
32
|
0
|
0
|
refs/heads/feat/multi-tenant
|
1
|
{"Commits":[{"Sha1":"b3b8edbb5 {"Commits":[{"Sha1":"b3b8edbb5213ba014810d3aff789af1dc408bc57","Message":"fix(meeting): enable plugin option scrolling\n\nAdd pending billing, OSS, and content-plugin migration SQL, change records, and project agent skills so branch history is complete before merge.\n","AuthorEmail":"121158035@qq.com","AuthorName":"zhangjunnan","CommitterEmail":"121158035@qq.com","CommitterName":"zhangjunnan","Timestamp":"2026-07-29T16:39:18+08:00"}],"HeadCommit":{"Sha1":"b3b8edbb5213ba014810d3aff789af1dc408bc57","Message":"fix(meeting): enable plugin option scrolling\n\nAdd pending billing, OSS, and content-plugin migration SQL, change records, and project agent skills so branch history is complete before merge.\n","AuthorEmail":"121158035@qq.com","AuthorName":"zhangjunnan","CommitterEmail":"121158035@qq.com","CommitterName":"zhangjunnan","Timestamp":"2026-07-29T16:39:18+08:00"},"CompareURL":"zhangjunnan/zhouhui/compare/7f3a435b825db5daf4df80ad944c8395036bc123...b3b8edbb5213ba014810d3aff789af1dc408bc57","Len":1}...
|
1785314479
|
Edit
Delete
|
|
20407
|
5
|
5
|
5
|
72
|
0
|
0
|
refs/heads/feat/multi-role-assignment
|
0
|
|
1785765661
|
Edit
Delete
|
|
20408
|
5
|
5
|
5
|
72
|
0
|
0
|
refs/heads/feat/multi-role-assignment
|
0
|
{"Commits":[{"Sha1":"744d532b8 {"Commits":[{"Sha1":"744d532b80ba3fd239cfbdd2b3700e3d73c08675","Message":"test(governance): 三级门禁全绿——runtime 396/396、UI 12/12,合同与多角色补齐验收\n\n本轮把欠了整条线的运行态证据补齐:静态 → 真实 DB → 浏览器三级全部实跑通过。\n\n## 合同域门禁(5 条断言 + 自检固化)\n`check:module-integration` 新增:渲染必须调 contracts 纯函数、清单必须走 contracts\n清洗、定稿必须校验金额、激活必须要求合同已定稿、合同列表必须有自己的读模型。\n5 条负向注入逐条实跑判红,并固化进 `scripts/lib/contract-integration.test.mjs`\n(fixture 隔离,不碰真文件;配「未注入 → 绿」基线用例,防止所有\"期望红\"\n以完全错误的理由通过)。gateSelftests 地板 6 → 7。\n\n其中「合同列表必须有自己的读模型」防的是真实发生过的缺陷:\n借用「待激活申请」options(只返 REQUESTED)会让财务一激活,刚签的合同就从界面消失。\n\n## 状态机不变量逼出的设计修正\n`contractMachine` 原本是 `DRAFT ⇄ FINALIZED` 的**无终态环**——合同永远走不完,\n而\"订单已激活则不可修改\"只是运行时守卫。contracts 的状态机不变量测试\n(\"从任一状态都能走到终态\")当场判红。加终态 `EFFECTIVE`,由财务激活时迁入:\n`revise` 从 EFFECTIVE 出发在**状态机层面就不存在**,运行时检查降为第二层。\n\n## 修掉一个我自己制造的严重问题\n早先用批量正则把测试里的 `role:` 改成 `roles:`,**误伤 153 处 `api()` helper 入参**。\n该 helper 收的是 `role`,`roles` 被静默忽略并回落成默认 ADMIN——\n**测试照样绿,但所有「某角色应当 403」的负例都退化成「ADMIN 当然 200」**。\n已全部回退。教训:批量正则改测试比改源码危险得多,源码有编译器兜底,测试没有。\n\n## 修掉一处静默降级(用户反馈两次)\n`draft()` 里 `template ? render(...) : \"\"`——取不到默认模板就给空正文,不报错不提示。\n而\"合同建出来了、正文是空的\"在界面上与\"模板没生效\"完全一样。改为:\n默认模板优先,没设默认但只有一个模板时直接采用;仍取不到时界面明确说明原因。\n另补「套用模板」选择器——此前 `templateId` 存了却从不重渲染,已有合同换不了模板。\n\n## 版式\n按反馈调整为:套用模板 → 合同正文 → 合同清单。\n打印改为只打这一份合同:`@media print` 内 5 条规则(全页隐藏 → 放行合同卡 →\n正文去滚动上限并允许换行 → 按钮与留证快照不打印),沿用配货单打印同一模式,\n含「window.print() 必须在原始 click 内同步调用」那条硬约束。\n\n## 三级门禁证据(本地工作区)\n- `pnpm check` exit 0;门禁自检 8 组\n- `pnpm check:runtime` **396/396**(地板抬至 380),双后端 HTTP 各 45/45\n- `pnpm check:ui` **12/12**,unexpected 0(地板抬至 12)\n- 新增双后端 HTTP 验收:未定稿激活 409 CONTRACT_NOT_FINALIZED、\n 改金额定稿 409 CONTRACT_TOTAL_MISMATCH、合同清单只有对客七列且不含 cost\n- 新增 E2E 真实界面链路:建模板 → 生成合同 → 断言正文按模板渲染 →\n 定稿 → 只读预览 → 激活\n\n## 顺带修复的真账\n多角色改造遗留:两后端测试夹具签单值 role、fromRole/toRole 断言、\nNestJS api() helper 签发、E2E 建账号入参、合同外键 Restrict 导致的清理顺序。\n\nCo-Authored-By: Claude Fable 5 \u003cnoreply@anthropic.com\u003e\n","AuthorEmail":"luoguoguo@gmail.com","AuthorName":"luoguoguo","CommitterEmail":"luoguoguo@gmail.com","CommitterName":"luoguoguo","Timestamp":"2026-08-03T06:17:45-07:00"},{"Sha1":"0e13b856b65685957296087a642c46a9b57c8d0a","Message":"feat(contract): 合同管理界面——模板维护、清单改写、预览与定稿\n\n## 界面三段\n- **模板维护**:正文编辑 + 可用变量清单(真源是 contracts 白名单,不手抄);\n 默认模板里乙方那几行留了「请填写」占位提示,免得有人以为忘了配变量\n- **合同面板**:清单可改名称/规格/单位/备注,**单价与金额只读**——\n 金额与报价锁死,改写法不改钱,只读比\"改了再被服务端拒绝\"少一次挫败\n- **定稿后**:正文转只读预览 + 打印 + 撤回定稿;留证快照可展开查看\n\n## 浏览器验证时抓到一个真实缺陷\n最初用「待激活申请」的 options 驱动合同列表——而它只返 `status=REQUESTED`。\n结果是**财务一激活,刚签的合同就从界面上凭空消失**。\n补了 `GET /api/contracts` 作为合同自己的读模型(两后端对等),\n视图改为「已有合同 + 尚未起草的待激活申请」两段驱动,后者按 contractRequestId 去重。\n\n这个 bug 单测抓不到,也不会有任何红盘——它只在\"数据恰好处于某状态\"时显形,\n而 dev 库里正好一条 REQUESTED 都没有,所以一打开就是空白。\n\n## 前端不做第二份渲染\n预览直接显示服务端渲染好的 `body`,前端**没有任何占位符处理**。\n在前端补一套 replace 会变成第二份渲染实现(G17 漂移形态):\n两边看起来一样,直到某天对空值的处理不同,界面上好好的合同打印出来金额是空的。\n\n## 证据(本地工作区)\n- `pnpm check` exit 0;全仓 typecheck 0 错误\n- 浏览器实测:模板维护渲染、合同面板 1 个、状态「已定稿」、清单 1 行、\n 合计 ¥660000.00、预览/撤回定稿/打印按钮齐全(截图见会话)\n- `GET /api/contracts` 返回真实合同(编号、报价号、客户名、合计、定稿时间)\n\n## 未做\nE2E 用例、两后端 HTTP 验收、`check:module-integration` 合同断言均未补(#31)。\n\nCo-Authored-By: Claude Fable 5 \u003cnoreply@anthropic.com\u003e\n","AuthorEmail":"luoguoguo@gmail.com","AuthorName":"luoguoguo","CommitterEmail":"luoguoguo@gmail.com","CommitterName":"luoguoguo","Timestamp":"2026-08-02T23:19:14-07:00"},{"Sha1":"ef882dee3f2d670038bff1151a04e6cb4a4d3d45","Message":"feat(contract): 合同管理落地——模板、清单清洗、预览、定稿快照与激活前置\n\n动手前先证伪了「优化完善」这个词:**本仓此前没有合同**。\n`ContractRequest` 只是流程凭证(状态 + 报价引用 + 拒绝留痕),没有正文、条款、\n甲乙方、签署;「合同管理」视图做的是财务把报价激活成订单,本质是审批队列。\n所以这是新建业务对象,不是加按钮。\n\n## 用户裁决(五条)\n模板+变量填充 / 定稿即快照 / 仅内部预览打印 / 乙方写进模板正文 / 定稿为激活前置。\n另就清单追加两条:只改写法金额锁死 / PUBLIC 附件归入主行备注。\n\n## 合同清单与报价清单是两份不同明细\n报价行 26 列里 9 列是内部的——成本价、底价、成本合计、低于底价标记、调价原因、\n标准价、三种折扣构成。投影**重建对象字面量**而不是删字段,成本列在结构上进不来\n(单测断言 Object.keys 恰为七列,并扫描序列化结果确认成本数字不出现)。\n最隐蔽的一条是 `QuoteLineAccessory.visibility=INTERNAL`:「别显示成本」人人记得,\n「别显示内部附件」没人会想到,因为它藏在子表里。\n\n**金额锁死**先验了算术前提才敢定:dev 库 `sum(quote_lines.total_cents)` 恒等于\n`quote_options.total_cents`,差额全为 0,否则这条不变量会一上线就误杀所有合同(C1 同型)。\n校验放在**定稿**而非每次保存——草稿期正在合并行,中间态必然对不上,\n每次都校验会让「合并两行」这个动作根本做不完。\n\n## 渲染两层 fail-closed\n未知变量、取不到值的变量都抛错并点名。理由:一份看起来正常、金额那行却空白的合同\n被打印去签,比当场报错危险一个量级。另单独检查未闭合 `{{`——它会被正则整段跳过,\n不专门查就会原样印进合同且无人报错。\n\n## 真实链路验证抓出两个 bug\n① `variablesSnapshot` 恒为空:起草时正文已渲染,到定稿时里面早没有 `{{}}` 可提取。\n 改为快照**全部可解析变量**——留证本就该全记,不该取决于模板作者当初印没印。\n② Fastify 路由多写了 `/api` 前缀(注册作用域已带 prefix),实际成了 `/api/api/...`。\n\n## 又踩了一次同字面量陷阱\nNestJS 激活前置最初插错位置——「仓库不存在」那段在文件里出现多次,\n`replace` 只换第一处,守卫落进了 `createStocktake`(盘点)。\n已改为断言锚点唯一性后再插入。CLAUDE.md 点名过两次的坑,我这是第三次踩。\n\n## 证据(本地工作区,Fastify 真实链路)\n- `pnpm check` exit 0;contracts 单测 302(新增 22 条合同用例)\n- 模板:经理可建 201 / 销售 403 / 未登记变量 409 点名「甲方开户行」\n- 合同:起草生成 1 行清单(七列,含\"含:附件\"备注)、正文变量全替换、大写金额正确\n- 定稿:改金额 → 409 报出差额 -10000.00 元;销售定稿 403;正确金额 → FINALIZED\n- 撤回定稿:订单已激活 → 409 ORDER_ALREADY_ACTIVATED\n- 缺税号时起草 → 409 MISSING_VALUE 点名「甲方税号」(真实数据触发,非构造)\n- 留证快照 12 项(缺签署人两项因该报价未走客户接受,属正确 fail-closed)\n\n## 未做(不隐藏)\n前端界面(模板维护/预览/修改)未做;两后端 HTTP 验收测试与 E2E 未补;\n`check:runtime`/`check:ui` 本轮未跑;激活前置只在代码与单测层验证,\n未构造完整「未定稿→拒绝激活」的真实 HTTP 流程(dev 库无待激活申请,\n且 quotation_id 唯一约束挡住了造数)。\n\nCo-Authored-By: Claude Fable 5 \u003cnoreply@anthropic.com\u003e\n","AuthorEmail":"luoguoguo@gmail.com","AuthorName":"luoguoguo","CommitterEmail":"luoguoguo@gmail.com","CommitterName":"luoguoguo","Timestamp":"2026-08-02T23:07:37-07:00"},{"Sha1":"e0b780d18833713a675d8903f407bd7afd55e650","Message":"feat(auth): 多角色并存——一个账号可持多个角色,能力取并集,互斥组合硬禁止\n\n用户裁决两条:① 多角色并存(而非租户自定义角色);② 互斥组合硬禁止(而非仅警示)。\n\n## 为什么是多角色而不是自定义角色\n备选方案是「租户自建角色 + 勾选任意 capability 子集」。不选它只有一条理由但足够硬:\n那会把「谁能做什么」的真源从 ROLE_PERMISSION_MAP **搬进数据库**,\n`check:role-permissions` 里「数据库不另存权限副本」这条核心断言随即失效,\n排查越权要同时看代码和数据。多角色只在 DB 存**角色分配**,真源不动,门禁继续有牙。\n代价是表达力:拼不出「销售但能看成本」这种现有角色组合不出的能力集。**已知取舍,非遗漏。**\n\n## 互斥矩阵为什么按角色对而不是 capability 对\n按能力对看着更本质,实则注册即红:`FINANCE` 单角色本身就同时持有 `payment.record`\n与 `payment.confirm`,门禁上线当天就判自己违规(C1 同型:扫到自身定义源)。\n故定义在角色层,并由 `it.each(MANAGED_USER_ROLES)`「每个单角色都不触发冲突」常驻断言守住。\n三组:采购三责分离 / 收款与发货放行 / 财务发起与审批;ADMIN 须单独持有。\n**这挡不住 ADMIN**——它持全部 capability,三责分离对它本就不成立,\n所以审批动作仍按 actor 硬校验 `approver !== requester`,两层缺一不可。\n\n## 删掉 requestRole 是本次最关键的决定\n`hasRolePermission` 改为同时接受单角色与角色集,好让 185 处调用点逐字不变。\n但这带来一个隐患:若保留 `requestRole`/`RequestRole` 单值访问器,\n调用点会**照常编译通过、静默只按主角色授权**——并集悄悄失效,零红盘。\n删掉它,编译器逼出 122+29 个错误,一个都跑不掉。\n\n编译器抓不到的恰是最危险的一处:`lockActiveAdmins` 的裸 SQL `\"role\" = 'ADMIN'`。\n它绕过 Prisma 类型检查;若当初保留 role 列做「兼容」,这句会静默匹配陈旧数据——\n**最后管理员保护看似还在,锁的却是错的行**。这是坚持 DROP COLUMN(并按门禁写\n`migration-allow` 留痕)的真正理由。计数侧同理:数组列上等值查询恒不匹配,\n`count` 恒为 0 会让保护永远放行。\n\n## 顺带修掉一个存量 bug\n两后端 `reverseAllocation` 把**角色名当操作人**存进 `reversedBy`(`reversedBy: role` → \"FINANCE\")。\n列是 String? 所以静默收下,反核销记录追不到人。多角色改造让它变成类型错误才暴露,已改传 actor。\n\n## token 与失效语义\nclaims `role` → `roles`,**无兼容分支**:留着它,只带 `role` 的旧令牌会走进「按单角色授权」\n的窄路径而调用方以为拿到并集,这种不一致比直接 401 难查得多。旧令牌一律 401 CLAIMS。\n受管理令牌回查用 `sameRoleSet` **集合语义**——按数组逐位比会让管理员调整勾选顺序\n就把在线用户全部踢下线(同 sameTagSet 那条教训)。\n\n## 迁移\n两端对等:users.role → users.roles、user_events 角色快照转集合,先回填再删旧列。\nDROP COLUMN 与 CREATE INDEX 均按门禁要求加 `-- migration-allow:` 留痕。\n\n## 前端\n账号页角色由单选下拉改多选勾选,实时显示并集能力数与互斥冲突——\n预览调 contracts 同一个 `previewRoleAssignment`,不在前端另写一套 if。\n**前端只提示不裁决**:绕过 UI 直接 curl 照样 400。\n\n## 证据(本地工作区)\n- `pnpm check` exit 0;contracts 273/273(新增 33 条多角色单测);全仓 typecheck 0 错误\n- 门禁负向已实跑判红:注入「退化成只按主角色授权」→ check:role-permissions exit 1;\n 注入「待办中心放行全集」→ check:module-integration exit 1;均恢复后转绿\n- 迁移在 dev 库实跑:单角色→单元素集合、0 个空角色集账号、旧列已删\n- 真实 HTTP 四条对抗:仓库+采购 400 点名冲突/空集 400/ADMIN 混搭 400/销售+技术 201\n- 浏览器:多选控件 10 角色、并集能力数实时更新、互斥冲突当场提示\n\n## 未做(不隐藏)\n`check:runtime` 与 `check:ui` 本轮未实跑;两后端多角色 HTTP 验收与 E2E 用例尚未补齐。\n动态区 runtime/UI 证据行仍是上一轮日期,未据本次改动更新。\n\nCo-Authored-By: Claude Fable 5 \u003cnoreply@anthropic.com\u003e\n","AuthorEmail":"luoguoguo@gmail.com","AuthorName":"luoguoguo","CommitterEmail":"luoguoguo@gmail.com","CommitterName":"luoguoguo","Timestamp":"2026-08-02T22:26:56-07:00"}],"HeadCommit":{"Sha1":"744d532b80ba3fd239cfbdd2b3700e3d73c08675","Message":"test(governance): 三级门禁全绿——runtime 396/396、UI 12/12,合同与多角色补齐验收\n\n本轮把欠了整条线的运行态证据补齐:静态 → 真实 DB → 浏览器三级全部实跑通过。\n\n## 合同域门禁(5 条断言 + 自检固化)\n`check:module-integration` 新增:渲染必须调 contracts 纯函数、清单必须走 contracts\n清洗、定稿必须校验金额、激活必须要求合同已定稿、合同列表必须有自己的读模型。\n5 条负向注入逐条实跑判红,并固化进 `scripts/lib/contract-integration.test.mjs`\n(fixture 隔离,不碰真文件;配「未注入 → 绿」基线用例,防止所有\"期望红\"\n以完全错误的理由通过)。gateSelftests 地板 6 → 7。\n\n其中「合同列表必须有自己的读模型」防的是真实发生过的缺陷:\n借用「待激活申请」options(只返 REQUESTED)会让财务一激活,刚签的合同就从界面消失。\n\n## 状态机不变量逼出的设计修正\n`contractMachine` 原本是 `DRAFT ⇄ FINALIZED` 的**无终态环**——合同永远走不完,\n而\"订单已激活则不可修改\"只是运行时守卫。contracts 的状态机不变量测试\n(\"从任一状态都能走到终态\")当场判红。加终态 `EFFECTIVE`,由财务激活时迁入:\n`revise` 从 EFFECTIVE 出发在**状态机层面就不存在**,运行时检查降为第二层。\n\n## 修掉一个我自己制造的严重问题\n早先用批量正则把测试里的 `role:` 改成 `roles:`,**误伤 153 处 `api()` helper 入参**。\n该 helper 收的是 `role`,`roles` 被静默忽略并回落成默认 ADMIN——\n**测试照样绿,但所有「某角色应当 403」的负例都退化成「ADMIN 当然 200」**。\n已全部回退。教训:批量正则改测试比改源码危险得多,源码有编译器兜底,测试没有。\n\n## 修掉一处静默降级(用户反馈两次)\n`draft()` 里 `template ? render(...) : \"\"`——取不到默认模板就给空正文,不报错不提示。\n而\"合同建出来了、正文是空的\"在界面上与\"模板没生效\"完全一样。改为:\n默认模板优先,没设默认但只有一个模板时直接采用;仍取不到时界面明确说明原因。\n另补「套用模板」选择器——此前 `templateId` 存了却从不重渲染,已有合同换不了模板。\n\n## 版式\n按反馈调整为:套用模板 → 合同正文 → 合同清单。\n打印改为只打这一份合同:`@media print` 内 5 条规则(全页隐藏 → 放行合同卡 →\n正文去滚动上限并允许换行 → 按钮与留证快照不打印),沿用配货单打印同一模式,\n含「window.print() 必须在原始 click 内同步调用」那条硬约束。\n\n## 三级门禁证据(本地工作区)\n- `pnpm check` exit 0;门禁自检 8 组\n- `pnpm check:runtime` **396/396**(地板抬至 380),双后端 HTTP 各 45/45\n- `pnpm check:ui` **12/12**,unexpected 0(地板抬至 12)\n- 新增双后端 HTTP 验收:未定稿激活 409 CONTRACT_NOT_FINALIZED、\n 改金额定稿 409 CONTRACT_TOTAL_MISMATCH、合同清单只有对客七列且不含 cost\n- 新增 E2E 真实界面链路:建模板 → 生成合同 → 断言正文按模板渲染 →\n 定稿 → 只读预览 → 激活\n\n## 顺带修复的真账\n多角色改造遗留:两后端测试夹具签单值 role、fromRole/toRole 断言、\nNestJS api() helper 签发、E2E 建账号入参、合同外键 Restrict 导致的清理顺序。\n\nCo-Authored-By: Claude Fable 5 \u003cnoreply@anthropic.com\u003e\n","AuthorEmail":"luoguoguo@gmail.com","AuthorName":"luoguoguo","CommitterEmail":"luoguoguo@gmail.com","CommitterName":"luoguoguo","Timestamp":"2026-08-03T06:17:45-07:00"},"CompareURL":"luoanwu/juhai-quotation-system/compare/4e06b62635319faead311e0b6799c2e47877f859...744d532b80ba3fd239cfbdd2b3700e3d73c08675","Len":4}...
|
1785765661
|
Edit
Delete
|
|
20409
|
5
|
5
|
5
|
72
|
0
|
0
|
refs/heads/feat/multi-role-assignment
|
0
|
{"Commits":[{"Sha1":"7a0c7fc47 {"Commits":[{"Sha1":"7a0c7fc472beebeaac8232aaff49889c3cb97fa0","Message":"chore(governance): 刷新报告与证据行,清理 tsconfig 临时产物残留\n\n- `apps/web/tsconfig.json`:C28 工作区卫生自愈剥掉 `.next-ui-14845` 残留\n (check:ui 的独立 distDir 会把该路径写进被跟踪的 tsconfig)\n- `reports/*.latest.json`:本轮 `pnpm check` 重跑产物,provenance 绑定当前工作区\n- `CLAUDE.md` 证据行:**含并行会话在同一 checkout 的更新**(客户勘测提交相关,\n 非本会话产出),已与本轮交付培训内容合并,未覆盖任何一方\n\nCo-Authored-By: Claude Fable 5 \u003cnoreply@anthropic.com\u003e\n","AuthorEmail":"luoguoguo@gmail.com","AuthorName":"luoguoguo","CommitterEmail":"luoguoguo@gmail.com","CommitterName":"luoguoguo","Timestamp":"2026-08-03T10:40:43-07:00"},{"Sha1":"167d026f912b2b17c9e4380a5eb18d1d50130751","Message":"feat(fulfillment): 交付培训成为硬卡点——安装完成前使用培训、服务关闭前运维培训\n\n用户裁决两条:培训是**硬卡点**而非记录;由各自角色做、记录客方参加人,\n不引入客户在线签收(本仓无实名认证,凭链接签收只能是具名降级)。\n\n## 卡点做成结构约束,不是运行时 if\n- 安装:`IN_PROGRESS --deliver_training--\u003e TRAINING --complete--\u003e COMPLETED`\n- 客服:`IN_SERVICE --deliver_operation_training--\u003e OPERATION_TRAINING --close--\u003e CLOSED`\n\n⚠️ **安装工单此前根本没有状态机**——三个状态靠 `updateMany` 的 where 条件硬走,\n不受 assertTransition 与状态机不变量守护。本轮补齐 `installationMachine` 并登记进\n不变量表(11 条结构断言自动生效)。否则新卡点又是一条只靠 if、没有结构保障的规则。\n\n客服侧相反:它本来就走 assertTransition,所以在 contracts 插入 `OPERATION_TRAINING`\n态之后,「服务中直接关单」**一行服务端代码都不用改**就自动变成 INVALID_TRANSITION。\n这个对比正是状态机单源的价值。\n\n## 参加人必填是刻意的\n`attendees` 记的是**客方名单**——培训的价值在于「谁被教会了」,不是「谁去讲了」。\n讲师取已验签 actor,不由客户端填,免得填错或冒名。一场没有参加人的培训等于没培训,\n允许留空就等于允许走过场。\n\n## 过程中挖出两处真问题\n1. **动作白名单是双真源**:两端各有一份手抄的 `[\"accept\",\"start\",\"close\"]`,\n 与 contracts 的 `SERVICE_HANDOFF_EVENTS` 平行维护。加了新事件而拷贝不知道,\n 表现是 400「Unsupported action」——排查时很难联想到「白名单没跟上」。\n 已改为两端都读 contracts 事件全集。\n2. **Zod 静默剥字段**:`acceptServiceHandoffSchema` 没登记 `attendees`,\n 请求里填了也会被剥掉,服务端于是说「没填参加人」——**调用方明明填了却被告知没填**,\n 这类不一致最难自查。已在 contracts 补登记并注明。\n\n## 一处「类型绿但数据没出来」\n客服读模型是**白名单重建对象字面量**(C20:不得泄漏成本/毛利/审批意见),\n返回类型是推断的,漏投影新字段**不会有任何编译错误**。安装侧是泛型透传所以自动带上,\n客服侧必须显式列出。已补,并由 `handoff-training-projected-*` 断言守住。\n\n## 界面\n安装工单进度四段(待开始/现场安装/使用培训/客户验收),段位改为按状态映射——\n原来是硬编码 if 链,新增状态会静默停在旧段位。安装中显示培训表单,\n完工表单挪到培训之后;客服服务中显示运维培训表单,关单挪到培训之后。\n\n## 门禁(4 条断言 + 4 条负向注入)\n`installation-training-required-*`、`training-attendees-required-*`、\n`handoff-action-allowlist-single-source-*`、`handoff-training-projected-*`。\n另收紧一条既有断言:完工表单必须挂在 **TRAINING** 段(原来锚的是两个标记的邻近性,\n插入培训表单后被撑开而误报;真正该守的是「完工在培训之后」,锚这个既更强也不怕插内容)。\n自检从 5 条扩到 9 条,均实跑判红。\n\n## 三级门禁证据(本地工作区,PG:55432 + Redis:6382)\n- `pnpm check` exit 0;门禁自检 9 条负向注入\n- `pnpm check:runtime` **413/413**(地板 380→400)\n- `pnpm check:ui` **12/12**,unexpected 0;E2E 断言「安装中不应出现完工按钮」\n ——卡点只在服务端而界面仍能诱导用户走错路,同样是缺陷\n\n## 未做\n客服交接的 E2E 里没有关单步骤,**运维培训卡点缺浏览器级证据**(HTTP 层已覆盖)。\n本次 runtime 增量含并行会话在同一 checkout 写入的约 400 行 HTTP 验收,非本会话产出。\n\nCo-Authored-By: Claude Fable 5 \u003cnoreply@anthropic.com\u003e\n","AuthorEmail":"luoguoguo@gmail.com","AuthorName":"luoguoguo","CommitterEmail":"luoguoguo@gmail.com","CommitterName":"luoguoguo","Timestamp":"2026-08-03T10:36:05-07:00"}],"HeadCommit":{"Sha1":"7a0c7fc472beebeaac8232aaff49889c3cb97fa0","Message":"chore(governance): 刷新报告与证据行,清理 tsconfig 临时产物残留\n\n- `apps/web/tsconfig.json`:C28 工作区卫生自愈剥掉 `.next-ui-14845` 残留\n (check:ui 的独立 distDir 会把该路径写进被跟踪的 tsconfig)\n- `reports/*.latest.json`:本轮 `pnpm check` 重跑产物,provenance 绑定当前工作区\n- `CLAUDE.md` 证据行:**含并行会话在同一 checkout 的更新**(客户勘测提交相关,\n 非本会话产出),已与本轮交付培训内容合并,未覆盖任何一方\n\nCo-Authored-By: Claude Fable 5 \u003cnoreply@anthropic.com\u003e\n","AuthorEmail":"luoguoguo@gmail.com","AuthorName":"luoguoguo","CommitterEmail":"luoguoguo@gmail.com","CommitterName":"luoguoguo","Timestamp":"2026-08-03T10:40:43-07:00"},"CompareURL":"luoanwu/juhai-quotation-system/compare/744d532b80ba3fd239cfbdd2b3700e3d73c08675...7a0c7fc472beebeaac8232aaff49889c3cb97fa0","Len":2}...
|
1785778851
|
Edit
Delete
|
|
20660
|
5
|
5
|
5
|
72
|
0
|
0
|
refs/heads/feat/multi-role-assignment
|
0
|
{"Commits":[{"Sha1":"dddca3924 {"Commits":[{"Sha1":"dddca39245dbd8ff6caea7a87c7e48de227e2daa","Message":"Enforce after-sales lifecycle gates and approval inbox integration\n","AuthorEmail":"luoguoguo@gmail.com","AuthorName":"luoguoguo","CommitterEmail":"luoguoguo@gmail.com","CommitterName":"luoguoguo","Timestamp":"2026-08-04T04:24:41-07:00"},{"Sha1":"c3ea882a2a67fdb7799a39b0d69278e7de50124b","Message":"feat: complete governed business lifecycle\n","AuthorEmail":"luoguoguo@gmail.com","AuthorName":"luoguoguo","CommitterEmail":"luoguoguo@gmail.com","CommitterName":"luoguoguo","Timestamp":"2026-08-03T20:59:05-07:00"}],"HeadCommit":{"Sha1":"dddca39245dbd8ff6caea7a87c7e48de227e2daa","Message":"Enforce after-sales lifecycle gates and approval inbox integration\n","AuthorEmail":"luoguoguo@gmail.com","AuthorName":"luoguoguo","CommitterEmail":"luoguoguo@gmail.com","CommitterName":"luoguoguo","Timestamp":"2026-08-04T04:24:41-07:00"},"CompareURL":"luoanwu/juhai-quotation-system/compare/7a0c7fc472beebeaac8232aaff49889c3cb97fa0...dddca39245dbd8ff6caea7a87c7e48de227e2daa","Len":2}...
|
1785842687
|
Edit
Delete
|
|
20661
|
5
|
5
|
5
|
72
|
0
|
0
|
refs/heads/feat/multi-role-assignment
|
0
|
{"Commits":[{"Sha1":"3ab4474b6 {"Commits":[{"Sha1":"3ab4474b6c62c031d9016986285fed531904c82c","Message":"chore(governance): REDIS_URL 运行时 fail-closed 绊网与报告聚合接线(并发治理会话收尾)\n\nN6:G15 只在 runner preflight 强制显式 REDIS_URL,应用运行时仍会静默默认\nlocalhost:6379——队列投空、pub/sub 静默丢事件且零日志。四个连接位点\n(双后端 BullMQ + pub/sub)全部接入 fail-closed 判定,governance-report 聚合随之扩展。\n\nCo-Authored-By: Claude Fable 5 \u003cnoreply@anthropic.com\u003e\n","AuthorEmail":"luoguoguo@gmail.com","AuthorName":"luoguoguo","CommitterEmail":"luoguoguo@gmail.com","CommitterName":"luoguoguo","Timestamp":"2026-08-04T06:57:12-07:00"},{"Sha1":"a82bc613d079d5b14ccdc66d2b901d99d980403f","Message":"chore(governance): 刷新证据行与棘轮——tests 470、own-test-cases 332、UI 12 含死路恢复链\n\n- CLAUDE.md 真源地图新增「履约死路恢复」条目;G16 条目按事务级咨询锁机制修正\n (含连接池滞留 bug 的完整教训与绊网口径)\n- 动态区证据行:静态/runtime/UI 三级 2026-08-04 实跑全绿,作用域=本地工作区\n- 棘轮收紧:testsPassed 465→470(byPackage 372/49/49)、ownTestCases 327→332、\n gateSelftests 8→14\n- reports/*.latest.json 为终链同 run 产物(provenance 一致)\n\nCo-Authored-By: Claude Fable 5 \u003cnoreply@anthropic.com\u003e\n","AuthorEmail":"luoguoguo@gmail.com","AuthorName":"luoguoguo","CommitterEmail":"luoguoguo@gmail.com","CommitterName":"luoguoguo","Timestamp":"2026-08-04T06:56:25-07:00"},{"Sha1":"68fe619dbf7752a2bdd058e16fa81fa13653e323","Message":"chore(governance): 新增 sites/component-size 门禁、Redis fail-closed 与依赖范围对齐(并发治理会话)\n\n- check:sites-governance / check:component-size 两个新门禁接入 check 链(含负向自检)\n- Redis URL fail-closed 与队列前缀模块收口\n- 双后端依赖范围从 ^X.0.0 对齐到实际锁定版本邻域(N7 断言配套)\n- pnpm-lock.yaml 同步 specifier(仅 5 行,锁定版本零变化)——不同步则干净\n checkout 上 frozen install 直接 ERR_PNPM_OUTDATED_LOCKFILE\n\nCo-Authored-By: Claude Fable 5 \u003cnoreply@anthropic.com\u003e\n","AuthorEmail":"luoguoguo@gmail.com","AuthorName":"luoguoguo","CommitterEmail":"luoguoguo@gmail.com","CommitterName":"luoguoguo","Timestamp":"2026-08-04T06:56:01-07:00"},{"Sha1":"4b637592aa9a1af503cbc7bf02e9e7fafa166f49","Message":"style(web): 移动导航搜索与首页视觉打磨(并发治理会话工作,随本轮一并落库)\n\n移动端「更多」菜单搜索、退款入口定位与首页布局打磨及配套 E2E 断言。\n本切片由并发治理会话实现并已随本轮三级门禁(check / runtime 470 / UI 12)整体验证。\n\nCo-Authored-By: Claude Fable 5 \u003cnoreply@anthropic.com\u003e\n","AuthorEmail":"luoguoguo@gmail.com","AuthorName":"luoguoguo","CommitterEmail":"luoguoguo@gmail.com","CommitterName":"luoguoguo","Timestamp":"2026-08-04T06:56:01-07:00"},{"Sha1":"da3230ece27359e62fd0945dd9d74a79f04fef83","Message":"fix(events): outbox 选主改事务级咨询锁——会话级锁在连接池上滞留导致投递饿死\n\nG16 领取锁闭环(初版为并发治理会话实现,本提交含其 dispatcher 失败判责拆分与\ndual-backend N7 依赖断言,一并落库)。当日实锤修正:\n\n会话级 pg_try_advisory_lock + pg_advisory_unlock 各发一条 $queryRaw——Prisma 是\n连接池,加锁与解锁大概率落在不同池化连接:锁被闲置连接永久滞留,集群投递静默\n饿死。欺骗性极强:低并发 vitest 全绿(池内单连接复用),check:ui 高并发下三连红\n且失败点漂移(前几步实时刷新正常、后面某步等失效超时)。\n\n改为事务级 pg_try_advisory_xact_lock:在 $transaction 事务客户端上取锁,提交/回滚\n自动释放,「解锁落错连接」结构上不可达;锁查询失败=事务抛错=本轮不跑批\n(fail-closed 不变)。事务只为持锁而存在,批内行更新仍走连接池。\n\n绊网同步翻转:xact 锁在位/事务客户端取锁/锁结果闸住跑批/会话级回潮即红/失败阶段\n判责/双端锁键对齐;定位锚用调用形态而非裸词(防注释锁名抢占第一处匹配,G13 同型)。\n12 条负向自检全绿,check:ui 12/12 复绿实证实时链恢复。\n\nCo-Authored-By: Claude Fable 5 \u003cnoreply@anthropic.com\u003e\n","AuthorEmail":"luoguoguo@gmail.com","AuthorName":"luoguoguo","CommitterEmail":"luoguoguo@gmail.com","CommitterName":"luoguoguo","Timestamp":"2026-08-04T06:55:43-07:00"}],"HeadCommit":{"Sha1":"3ab4474b6c62c031d9016986285fed531904c82c","Message":"chore(governance): REDIS_URL 运行时 fail-closed 绊网与报告聚合接线(并发治理会话收尾)\n\nN6:G15 只在 runner preflight 强制显式 REDIS_URL,应用运行时仍会静默默认\nlocalhost:6379——队列投空、pub/sub 静默丢事件且零日志。四个连接位点\n(双后端 BullMQ + pub/sub)全部接入 fail-closed 判定,governance-report 聚合随之扩展。\n\nCo-Authored-By: Claude Fable 5 \u003cnoreply@anthropic.com\u003e\n","AuthorEmail":"luoguoguo@gmail.com","AuthorName":"luoguoguo","CommitterEmail":"luoguoguo@gmail.com","CommitterName":"luoguoguo","Timestamp":"2026-08-04T06:57:12-07:00"},"CompareURL":"luoanwu/juhai-quotation-system/compare/dddca39245dbd8ff6caea7a87c7e48de227e2daa...3ab4474b6c62c031d9016986285fed531904c82c","Len":6}...
|
1785852216
|
Edit
Delete
|
|
20880
|
5
|
5
|
5
|
72
|
0
|
0
|
refs/heads/feat/multi-role-assignment
|
0
|
{"Commits":[{"Sha1":"d6b659343 {"Commits":[{"Sha1":"d6b659343565a590d5c9d75adecc62710643a553","Message":"feat(web): 工作台 UI 改版 + 采购/客服权限补齐 + 订单收款列摘要\n\n前端工作台按侧栏 6 组流水线重组导航,新增 WorkspaceViewSwitcher / AfterSalesPipeline\n与 Drawer/Breadcrumb/Segmented/Toast/ErrorAlert 原语;收款面板从 964 行拆抽屉编排并\n从 component-size 登记表毕业;设计令牌换画布 #e9e9e3 并加深 muted/brand-strong 回 AA。\n\n契约与后端:\n- contracts 新增 formatCents 单源,收口前端 4 份 money 实现与后端错误文案的金额漂移\n- SalesOrderView.shipmentGate 可选字段:订单列表「收款」列摘要,与发运门禁共用\n evaluateShipmentPaymentGate + toGateInstallments 投影,现算不落汇总列\n- ROLE_PERMISSION_MAP 补 PROCUREMENT/CUSTOMER_SERVICE 只读集合(product.read /\n customer.read / fulfillment.order.read),不含写权限与敏感成本列\n\n验收(当前工作区,2026-08-05):\n- pnpm check 全绿(typecheck 强制重跑 Cached:0/7)\n- check:runtime 470/470(contracts 372 / NestJS 49 / Fastify 49),含 shipmentGate 三态\n- check:ui 12/12\n- 对抗探测:鉴权无回退/跨租户404/自批403/状态机409/并发恰好一次 均实测有牙\n\nCo-Authored-By: Claude Opus 5 \u003cnoreply@anthropic.com\u003e\n","AuthorEmail":"luoguoguo@gmail.com","AuthorName":"luoguoguo","CommitterEmail":"luoguoguo@gmail.com","CommitterName":"luoguoguo","Timestamp":"2026-08-05T11:17:13-07:00"},{"Sha1":"ec411c0e6fb0c5cbcebb686f67b01cb355c29665","Message":"docs(analysis): 前端 UI 诊断报告 2026-08-04(并发治理会话产出)\n\n29 入口/84 组件/42 useQuery/81 useMutation 只读走查:流程断裂(仅 1 个跨域跳转)、\n反馈缺位(81 个 mutation 零成功提示)、重复实现(money 格式化 4 份)三条问题线,\n含 1 个多角色待办入口丢失的真 bug 与文末 Top5 修复建议。\n\nCo-Authored-By: Claude Fable 5 \u003cnoreply@anthropic.com\u003e\n","AuthorEmail":"luoguoguo@gmail.com","AuthorName":"luoguoguo","CommitterEmail":"luoguoguo@gmail.com","CommitterName":"luoguoguo","Timestamp":"2026-08-04T09:20:22-07:00"}],"HeadCommit":{"Sha1":"d6b659343565a590d5c9d75adecc62710643a553","Message":"feat(web): 工作台 UI 改版 + 采购/客服权限补齐 + 订单收款列摘要\n\n前端工作台按侧栏 6 组流水线重组导航,新增 WorkspaceViewSwitcher / AfterSalesPipeline\n与 Drawer/Breadcrumb/Segmented/Toast/ErrorAlert 原语;收款面板从 964 行拆抽屉编排并\n从 component-size 登记表毕业;设计令牌换画布 #e9e9e3 并加深 muted/brand-strong 回 AA。\n\n契约与后端:\n- contracts 新增 formatCents 单源,收口前端 4 份 money 实现与后端错误文案的金额漂移\n- SalesOrderView.shipmentGate 可选字段:订单列表「收款」列摘要,与发运门禁共用\n evaluateShipmentPaymentGate + toGateInstallments 投影,现算不落汇总列\n- ROLE_PERMISSION_MAP 补 PROCUREMENT/CUSTOMER_SERVICE 只读集合(product.read /\n customer.read / fulfillment.order.read),不含写权限与敏感成本列\n\n验收(当前工作区,2026-08-05):\n- pnpm check 全绿(typecheck 强制重跑 Cached:0/7)\n- check:runtime 470/470(contracts 372 / NestJS 49 / Fastify 49),含 shipmentGate 三态\n- check:ui 12/12\n- 对抗探测:鉴权无回退/跨租户404/自批403/状态机409/并发恰好一次 均实测有牙\n\nCo-Authored-By: Claude Opus 5 \u003cnoreply@anthropic.com\u003e\n","AuthorEmail":"luoguoguo@gmail.com","AuthorName":"luoguoguo","CommitterEmail":"luoguoguo@gmail.com","CommitterName":"luoguoguo","Timestamp":"2026-08-05T11:17:13-07:00"},"CompareURL":"luoanwu/juhai-quotation-system/compare/3ab4474b6c62c031d9016986285fed531904c82c...d6b659343565a590d5c9d75adecc62710643a553","Len":2}...
|
1785953861
|
Edit
Delete
|