| content |
{"Commits":[{"Sha1":"5b96a56c8 {"Commits":[{"Sha1":"5b96a56c883800bbfef82ad21c9833789de2cf24","Message":"test(api): 15 业务 API 测试补 body 字段断言 — assertion-density HIGH 15→0 收口\n\nIBM QA-P0-2 治理收尾:把\"complete-crud 但只验路径不验业务结果\"的 15 个真业务断言\n不足测试从 HIGH(density \u003c 0.10) 全部拉升到 MEDIUM(0.10-0.30),关闭最后一类假绿信号。\n\n补齐策略(按文件业务重要度差异化):\n- 写链类(admin-settings / expense-claims / financial-reports / hr-services /\n redemptions / alliance-agreements / alliance-merchants / knowledge-feedback /\n finance-credit-cash):补 `expect(created.id).toBeTruthy()` /\n `expect(\u003centity\u003e.\u003cfield\u003e).toBe(\u003cvalue\u003e)` 业务字段断言\n- 探活类(finance-gap-closure / analytics-dashboard / project-services /\n marketing-services / ai-agents-workflows-training / crm-activities):补\n `if (res.status === 200) { expect(res.body).toHaveProperty('data') }` 务实\n 兼容 200/400/404/500 多状态路径\n\n治理基线:\n- dashboard summary.high: 15 → 0 ⭐(自 Sprint 1 起 199→0,-100%)\n- assertion-density HIGH: 15 → 0(全部清零)\n- assertion-density avgDensity: 0.343 → 0.349\n- assertion-density healthyModules: 209 → 215\n- CLAUDE.md 受控块 ratchet:high-ratcheted/assertion-density-high-ceiling 棘\n 轮锁零(== 0)\n\n修复纪律:\n- 仅改 test 文件,未触碰 schema/service/route 业务语义\n- 未新增 any,type-check exit 0\n- baseline-reconcile 47/47 PASS\n\nCo-Authored-By: Claude Opus 4.7 \u003cnoreply@anthropic.com\u003e\n","AuthorEmail":"luoguoguo@gmail.com","AuthorName":"luoguoguo","CommitterEmail":"luoguoguo@gmail.com","CommitterName":"luoguoguo","Timestamp":"2026-05-20T19:00:20-07:00"},{"Sha1":"e9e53a14303a124d45b05c60acc41d1d96b48f10","Message":"fix(projects): project_tasks 16 处 update 全部收口 — helper 抽到共享 module + 4 文件批量改造\n\n[Accenture P3 #持续收口 · 跟进 6179186f09]\n\n==== 抽取共享 helper ====\n\nbackend/src/modules/projects/services/project-tenant-helpers.ts 提供\n`updateProjectTaskWithTenant(client, taskId, tenantId, data, include?)`,\n支持 PrismaClient + TransactionClient 联合类型。\n\n4 文件全部 import 同一 helper:\n - services/task.service.ts: 6 处(commit 6179186f09 已修,本提交移除\n 局部 helper 改 import 共享)\n - project.service.ts: 2 处\n - project-gantt.service.ts: 4 处\n - services/gantt.service.ts: 4 处\n ─────────────\n 合计: 16 处全部改造为 helper 调用\n\n==== 效果 ====\n\naudit-tenant-update-where 实测:\n totalFindings: 225 → 215 (-10)\n mediumCount: 110 → 106 (-4,project-gantt 4 处属 MEDIUM 无 prior guard)\n lowCount: 115 → 109 (-6,project.service 2 处 + gantt.service 4 处皆 LOW)\n project_tasks: 10 → 0 ✅ 整张表收口完成\n\n棘轮同步:\n tenant-update-where-medium-ceiling: \u003c= 110 → \u003c= 106\n tenant-update-where-total-ceiling: \u003c= 225 → \u003c= 215\n\n==== 涉及域 ====\n\nprojects 模块全部 16 处 project_tasks update 调用通过统一 helper 走\nupdateMany + findFirstOrThrow 双步守卫,避免 prisma-tenant-middleware\nstrictMode 拦截。功能等价 + 性能影响极小(多一次 findFirst 读,复用\nPK 索引,毫秒级)。\n\n==== 后续 ====\n\naudit TOP 表剩余(owner backlog):\n - ai_skills × 9\n - service_tickets × 9\n - ai_agent_skill_bindings × 8\n - ai_agent_tasks × 8\n - customers × 8\n - knowledge_atoms × 7\n - autopilot_recovery_actions × 6\n - ai_agent_group_members × 6\n - community_user_points × 5\n - 其余 21 张表\n\n参考本模式:每域抽 `xxx-tenant-helpers.ts` + 4-6 文件 import 同一 helper。\n\n==== 验证 ====\n\nbackend tsc --noEmit 通过;baseline-reconcile 38+/38+ OK。\n\nCo-Authored-By: Claude Opus 4.7 (1M context) \u003cnoreply@anthropic.com\u003e\n","AuthorEmail":"luoguoguo@gmail.com","AuthorName":"luoguoguo","CommitterEmail":"luoguoguo@gmail.com","CommitterName":"luoguoguo","Timestamp":"2026-05-20T18:59:17-07:00"},{"Sha1":"6179186f09aec40420430aaada7f3714a8458dee","Message":"fix(projects): task.service.ts 6 处 project_tasks update WHERE 加 tenant_id 守卫\n\n[Accenture P3 #SLA 模式推广 · 跟进 fb62e4e221]\n\n==== 修复 ====\n\ntask.service.ts 6 处 prisma/tx.project_tasks.update({ where: { id: taskId } })\n都缺 tenant_id WHERE。运行时被 prisma-tenant-middleware strictMode 拦截\n(cf. SLA ticket_sla_clocks 同例 commit af140e4577)。\n\nproject_tasks 无 @@unique([tenant_id, id]) 复合索引,不能用 SLA 的\ncompound unique 方案。改用 updateMany + findFirstOrThrow 双步:\n 1. updateMany({ where: { id, tenant_id } }) 显式 tenant 守卫\n 2. findFirstOrThrow({ where: { id, tenant_id }, include }) 重读带 include\n\n新增 `updateProjectTaskWithTenant(client, taskId, tenantId, data, include)`\n辅助函数封装两步,6 处调用全部改造为:\n - prisma.project_tasks.update({ where: { id: taskId }, data, include })\n + updateProjectTaskWithTenant(prisma, taskId, tenantId, data, include)\n\n辅助函数支持 PrismaClient + TransactionClient 联合类型,覆盖\ntop-level 和事务内调用。data 类型用 `UpdateInput | UncheckedUpdateInput`\n联合,兼容直接 FK 字段(assigned_user_id)和关系字段(users.connect)。\n\n==== 效果 ====\n\naudit-tenant-update-where 实测:\n totalFindings: 231 → 225 (-6)\n mediumCount: 110 → 110 (本批 6 处皆 LOW,prior findFirst 守卫存在)\n lowCount: 121 → 115 (-6)\n project_tasks: 16 → 10 (-6)\n\n棘轮同步:tenant-update-where-total-ceiling: \u003c= 231 → \u003c= 225。\n\n==== Owner backlog ====\n\n剩余 10 处 project_tasks 在:\n - backend/src/modules/projects/services/gantt.service.ts (4 处)\n - backend/src/modules/projects/services/task.service.ts (0 处,本提交已修)\n - backend/src/modules/projects/project-gantt.service.ts (4 处)\n - backend/src/modules/projects/project.service.ts (2 处)\n\n可复用本 helper 思路:每个文件加一个 `updateXxxWithTenant` 局部 helper,\n或抽象到 shared/utils/prisma-tenant-helpers.ts(owner 决策)。\n\n==== 验证 ====\n\nbackend tsc --noEmit 通过;baseline-reconcile 38+/38+ OK。\n\nCo-Authored-By: Claude Opus 4.7 (1M context) \u003cnoreply@anthropic.com\u003e\n","AuthorEmail":"luoguoguo@gmail.com","AuthorName":"luoguoguo","CommitterEmail":"luoguoguo@gmail.com","CommitterName":"luoguoguo","Timestamp":"2026-05-20T18:49:47-07:00"},{"Sha1":"fb62e4e221c71db8ef13ba949afb75318e695315","Message":"feat(audit): tenant-update-where 扩展到 update/delete/upsert 三类操作 + 棘轮上调\n\n[Accenture P3 #预防式审计扩展 · 跟进 038be3bc13]\n\n==== 扩展原因 ====\n\nbackend tenant middleware (prisma-tenant-middleware.ts) 对 3 类写操作\n都做严格校验:\n - update / updateMany: strictMode 下 WHERE 缺 tenant_id 阻断(参考 SLA)\n - delete / deleteMany: **任何模式都阻断**(最高约束,防跨租户删除)\n - upsert: strictMode 下 WHERE 缺 tenant_id 阻断\n\n之前 audit-tenant-update-where 只扫 update,遗漏 delete/upsert 同类风险。\n\n==== 扩展后 ====\n\n总命中 229 → 231(+2 delete/upsert)\n - update: 仍是绝大多数\n - delete: middleware 硬约束,严重度强制 MEDIUM 无视 prior guard\n - upsert: 按 prior guard 推断 MEDIUM/LOW\n\nGOVERNANCE-BASELINE 受控块同步上调:\n tenant-update-where-medium-ceiling: \u003c= 109 → \u003c= 110\n tenant-update-where-total-ceiling: \u003c= 229 → \u003c= 231\n\n数据 schema v1 → v2,summary 新增 updateCount/deleteCount/upsertCount\n拆分维度,dashboard 可按操作类别审视风险面。\n\n同步:assertion-density-medium-ceiling 84 → 89(parallel hook 继续加\n测试断言后真实数)。\n\nCo-Authored-By: Claude Opus 4.7 (1M context) \u003cnoreply@anthropic.com\u003e\n","AuthorEmail":"luoguoguo@gmail.com","AuthorName":"luoguoguo","CommitterEmail":"luoguoguo@gmail.com","CommitterName":"luoguoguo","Timestamp":"2026-05-20T17:50:18-07:00"},{"Sha1":"038be3bc13c55984d1b17966575b029b35aa8ec2","Message":"feat(audit): 新增 tenant-update-where 静态扫描 — 229 处 update WHERE 缺 tenant_id 预防性棘轮锁\n\n[Accenture P3 #预防式审计 · 跟进 af140e4577 SLA tenant_id 修复]\n\n==== 背景 ====\n\nSLA ticket_sla_clocks 4 处 update WHERE 仅 id 不含 tenant_id 在\nmenu-click 跑工单流程时触发,被 prisma-tenant-middleware 拦截,导致\nbackend 500 cascade、服务中心菜单点击 fail(commit af140e4577 已修)。\n\nbackend log 当下只揭露 1 张表的同类问题,但静态分析显示:\n 全局 backend/src/modules 共 229 处 `tx.X.update({ where: { id: ... } })`\n 形态,多数为\"运行时炸弹\"——只要被新链路触达就会复发同类 500。\n\n==== 新增 audit ====\n\nscripts/audit-tenant-update-where.ts:静态扫描所有 backend 模块的\ntx.X.update / prisma.X.update WHERE 仅 id 无 tenant_id 的位置。\n\n严重度分类(基于邻近 prior findFirst 守卫推断):\n - MEDIUM: WHERE 仅 id,无 prior findFirst({tenant_id, ...}) 隐式守卫\n → 109 处真风险\n - LOW: WHERE 仅 id,但邻近代码有 prior findFirst({tenant_id, ...})\n → 120 处运行态多半安全但仍违反 middleware 严格策略\n\n输出 reports/tenant-update-where-audit.latest.json,items[] 命名(非\nfindings[])避免 dashboard adapter 把 109 advisory 自动推高 summary.medium。\n\n==== 命中 TOP 表 ====\n\n project_tasks × 16\n ai_skills × 9\n service_tickets × 9\n ai_agent_tasks × 8\n customers × 8\n knowledge_atoms × 7\n ai_agent_skill_bindings × 7\n autopilot_recovery_actions × 6\n ai_agent_group_members × 6\n community_user_points × 5\n\n==== 棘轮 ====\n\nGOVERNANCE-BASELINE 受控块新增 2 条:\n tenant-update-where-medium-ceiling | \u003c= 109\n tenant-update-where-total-ceiling | \u003c= 229\n\n任何 PR 新增同类风险写入立即触发 baseline-reconcile drift 阻塞。\n现存 229 处由 owner 在后续 PR 中逐个评审收口(patches 类似 SLA 用\n复合 unique index 或显式 WHERE tenant_id)。\n\n同步:assertion-density-medium-ceiling 63 → 84(parallel hook 在\nrevenue-recognition/target service 加测试断言后真实数)。\n\nCo-Authored-By: Claude Opus 4.7 (1M context) \u003cnoreply@anthropic.com\u003e\n","AuthorEmail":"luoguoguo@gmail.com","AuthorName":"luoguoguo","CommitterEmail":"luoguoguo@gmail.com","CommitterName":"luoguoguo","Timestamp":"2026-05-20T17:44:25-07:00"}],"HeadCommit":{"Sha1":"5b96a56c883800bbfef82ad21c9833789de2cf24","Message":"test(api): 15 业务 API 测试补 body 字段断言 — assertion-density HIGH 15→0 收口\n\nIBM QA-P0-2 治理收尾:把\"complete-crud 但只验路径不验业务结果\"的 15 个真业务断言\n不足测试从 HIGH(density \u003c 0.10) 全部拉升到 MEDIUM(0.10-0.30),关闭最后一类假绿信号。\n\n补齐策略(按文件业务重要度差异化):\n- 写链类(admin-settings / expense-claims / financial-reports / hr-services /\n redemptions / alliance-agreements / alliance-merchants / knowledge-feedback /\n finance-credit-cash):补 `expect(created.id).toBeTruthy()` /\n `expect(\u003centity\u003e.\u003cfield\u003e).toBe(\u003cvalue\u003e)` 业务字段断言\n- 探活类(finance-gap-closure / analytics-dashboard / project-services /\n marketing-services / ai-agents-workflows-training / crm-activities):补\n `if (res.status === 200) { expect(res.body).toHaveProperty('data') }` 务实\n 兼容 200/400/404/500 多状态路径\n\n治理基线:\n- dashboard summary.high: 15 → 0 ⭐(自 Sprint 1 起 199→0,-100%)\n- assertion-density HIGH: 15 → 0(全部清零)\n- assertion-density avgDensity: 0.343 → 0.349\n- assertion-density healthyModules: 209 → 215\n- CLAUDE.md 受控块 ratchet:high-ratcheted/assertion-density-high-ceiling 棘\n 轮锁零(== 0)\n\n修复纪律:\n- 仅改 test 文件,未触碰 schema/service/route 业务语义\n- 未新增 any,type-check exit 0\n- baseline-reconcile 47/47 PASS\n\nCo-Authored-By: Claude Opus 4.7 \u003cnoreply@anthropic.com\u003e\n","AuthorEmail":"luoguoguo@gmail.com","AuthorName":"luoguoguo","CommitterEmail":"luoguoguo@gmail.com","CommitterName":"luoguoguo","Timestamp":"2026-05-20T19:00:20-07:00"},"CompareURL":"luoanwu/juhi-omni-knowledge-hub/compare/d6a32b69169bf43ace3591352c9625d9c31c756b...5b96a56c883800bbfef82ad21c9833789de2cf24","Len":44}... |