## 修复记录

- 17:11：记录 bug 修复：财务看板跨租户聚合、快照串租户复用与普通用户越权读取。
  - Git 提交检查：执行 `git fetch --all --prune` 后未发现 `HEAD..origin/main` 新提交；本地比 `origin/main` ahead 17 个提交，最新为 `242d68c3 feat(approval): add task workflow and waiver decisions`，其余为 `28b834ed` 至 `661990b2` 的审批、费用闭环、AI 记忆、迁移安全和认证能力提交，本次没有合并或改写这些历史。
  - 修改：`finance_dashboard.py` 通过 `ExpenseClaimTenantScopeMixin` 按 Expense Case Link 聚合当前租户报销单；缺少 `tenant_id` 的旧预算表仅允许 `default` 租户读取，其他租户返回带原因的明确空预算；新增 `finance_dashboard_scope.py` 统一声明报销与预算数据范围。
  - 修改：`finance_dashboard_snapshot.py` 把 `tenant_id`、数据范围和完整时间参数纳入无歧义缓存键，并在 SQL 查询、Agent Run 路由及工具请求中同时校验租户和范围；`finance_dashboard_scheduler.py` 显式固定 `default` 系统租户，非默认租户不能调用默认定时快照入口。
  - 修改：新增 `finance_dashboard_access_policy.py`，`analytics.py` 显式接收 `CurrentUserContext`，仅允许 `finance`、`executive` 或 admin 只读访问财务看板，普通用户和仅有 `budget_monitor` 角色的用户返回 403。
  - 操作：所有检查均在 `local-x-financial-linux` 容器内执行；新增租户 A/B、default 历史单、旧预算、快照缓存与接口权限测试，没有触碰受保护的财务规则 XLSX 和历史开发文档。
  - 验证：Ruff 对本次 7 个 Python 模块及新增测试检查通过；`test_finance_dashboard_tenant_security.py` 与 `test_finance_dashboard_service.py` 共 8 个测试通过。补充运行财务报告与数字员工回归时 4 个测试通过、1 个既有财务周报用例失败，原因是用例将“当前时间减 2 天”的数据断言进“上一完整周”窗口，和本次租户过滤无关。
  - 影响：财务看板不再读取其他租户的报销单或把 default 旧预算暴露给非默认租户；相同时间参数的租户快照不会互相命中，后台默认快照和前台读取权限也有了可审计的显式边界。

- 18:14：补齐财务看板租户 fail-closed 边界与模块拆分验证。
  - Git 提交检查：18:13 再次执行 `git fetch --all --prune`；`HEAD..origin/main` 无新提交，本地仍比 `origin/main` ahead 17 个提交，范围为 `661990b2 feat(expenses): add transactional expense case events` 至 `242d68c3 feat(approval): add task workflow and waiver decisions`，未合并、改写或覆盖这些提交及工作区内其他智能体改动。
  - 修改：`finance_dashboard_access_policy.py` 对空白租户上下文直接拒绝，避免异常认证上下文回落到 default；将预算摘要、预算卡片和预算瓶颈投影提取到 `finance_dashboard_budget.py`，`finance_dashboard.py` 从 920 行降至 746 行，保持租户过滤和原 API 不变。
  - 操作：只在 `local-x-financial-linux` 容器内执行 Ruff、财务看板服务与接口测试、Agent Run 服务回归和 Ontology 端点回归；未触碰财务规则 XLSX、Savings 迁移或前端文件。
  - 验证：Ruff 检查与格式检查通过；财务看板、Agent Run 服务和 Ontology 端点定向回归共 17 个测试通过。另跑数字员工、系统看板和财务报告回归时 6 个通过、1 个既有周报窗口用例失败；该用例在周四写入“当前时间减 2 天”的单据，却断言它属于“上一完整周”，失败与本次改动无关。
  - 影响：租户身份缺失时财务看板不再隐式读取 default 数据；预算展示职责被独立封装，后续继续扩展财务指标时不会把核心聚合模块推过项目 800 行硬上限。
