204 Commits

Author SHA1 Message Date
caoxiaozhu
638b757138 docs(dev-log): add July daily work summaries 2026-07-20 10:31:47 +08:00
caoxiaozhu
b185e58f31 chore(knowledge): ignore tenant runtime storage 2026-07-20 10:31:32 +08:00
caoxiaozhu
2a1a5991cb fix(expenses): refine operation record audit UI 2026-07-20 10:31:27 +08:00
caoxiaozhu
2a9b3e3273 fix(ai): recognize conversational document filters 2026-07-20 10:31:15 +08:00
caoxiaozhu
7a3755ac55 fix(dev-server): limit reload watcher to source 2026-07-20 10:31:00 +08:00
caoxiaozhu
aa6a7d1250 fix(agent-assets): save rule markdown atomically 2026-07-20 10:30:48 +08:00
caoxiaozhu
208e1bed59 fix(audit): expose rule management permission state 2026-07-20 10:30:35 +08:00
caoxiaozhu
140efd1a2a fix(agent-assets): allow admin access to platform rules 2026-07-20 10:30:22 +08:00
caoxiaozhu
74fafc86d3 fix(runtime): provision local document and vector services 2026-07-20 10:30:08 +08:00
caoxiaozhu
15da295963 fix(settings): reconcile secret status and model drafts 2026-07-20 10:29:49 +08:00
caoxiaozhu
044a5669fe fix(employees): preserve age across birthday boundaries 2026-07-20 10:29:42 +08:00
caoxiaozhu
c59990dc35 fix(auth): bind admins to enterprise tenant context 2026-07-20 10:29:30 +08:00
caoxiaozhu
372e35d62a fix(migrations): repair tenant identity lookup indexes 2026-07-20 10:28:31 +08:00
caoxiaozhu
07241b4648 fix(docker): manage local postgres in default compose 2026-07-17 22:50:02 +08:00
caoxiaozhu
787bc3a481 feat(platform): close AI expense value loop
Add tenant-safe value, telemetry, connector, commercial, and production-readiness foundations.
2026-07-17 14:14:08 +08:00
caoxiaozhu
242d68c36f feat(approval): add task workflow and waiver decisions 2026-07-16 16:52:12 +08:00
caoxiaozhu
28b834edd3 fix(approval): replay immutable action responses 2026-07-16 15:49:43 +08:00
caoxiaozhu
4940ebc419 feat(approval): add safe risk disposition workflow 2026-07-16 15:34:58 +08:00
caoxiaozhu
ee88a36baf feat(ai): add tenant-safe hierarchical expense learning 2026-07-16 14:30:41 +08:00
caoxiaozhu
6bdf65bc24 feat(expenses): add authoritative pre-review workflow 2026-07-16 11:42:08 +08:00
caoxiaozhu
ae3f02c35a feat(expense): add persistent zero-entry receipt association 2026-07-16 10:23:23 +08:00
caoxiaozhu
54754b5502 feat(ai): add personal expense application memory 2026-07-14 17:16:23 +08:00
caoxiaozhu
211f85d981 feat(ai): unify verified expense application workflow 2026-07-14 16:03:05 +08:00
caoxiaozhu
5b24630710 feat(ai): issue verified application preview decisions 2026-07-14 14:37:53 +08:00
caoxiaozhu
a662cfe6c3 feat(ai): add expense application feedback ledger 2026-07-14 11:10:55 +08:00
caoxiaozhu
5ed34c2b8f feat(expenses): backfill historical claims into expense cases 2026-07-14 10:09:09 +08:00
caoxiaozhu
11275e4ba6 fix(migrations): enforce schema ownership safety 2026-07-14 09:23:34 +08:00
caoxiaozhu
1347366b95 feat(expenses): secure timeline and draft events 2026-07-14 00:07:07 +08:00
caoxiaozhu
22669a9071 feat(expenses): show unified expense event timeline 2026-07-13 16:58:51 +08:00
caoxiaozhu
a616b30cb2 fix(expenses): unify AI application submission transaction 2026-07-13 15:35:08 +08:00
caoxiaozhu
653eda0596 feat(auth): add opaque bearer sessions 2026-07-13 14:45:36 +08:00
caoxiaozhu
661990b27b feat(expenses): add transactional expense case events 2026-07-13 11:58:48 +08:00
caoxiaozhu
9a84e125d0 merge: 飞轮 2 golden set 回归门禁
# Conflicts:
#	server/src/app/models/__init__.py
2026-07-03 14:41:40 +08:00
caoxiaozhu
3a5fc3c09f merge: 飞轮 1 few-shot 在线检索注入 2026-07-03 14:40:09 +08:00
caoxiaozhu
c7ba7bb453 feat(flywheel): golden case 管理 API 与评测单测
- 新增 GoldenCaseCreate/Read、GoldenEvalRequest/Read schema
- 新增 3 个端点:创建 golden case、按规则列表、手动触发 golden 评测
  (不入门禁,供运营试跑)
- 单测 15 passed:单条 hit/severity 比对、集合 accuracy/precision/recall
  聚合、空集降级、100% 通过/失败拦截、feature flag、异常降级
- 回归 test_agent_asset_service 27 passed(1 个预存失败与本改动无关)
2026-07-03 14:38:43 +08:00
caoxiaozhu
67c3f30eb2 feat(flywheel): golden set 回归门禁拦截风险规则发布
- 新增 RiskRuleGoldenEvaluator:在 GoldenCase 集上跑规则 manifest,复用
  RiskRuleTemplateExecutor + _build_synthetic_claim,输出
  accuracy/precision/recall,按 100% 通过硬阈值判定
- require_pass 门禁入口:未通过抛 PermissionError 并写
  AgentAssetTestRun(test_type=golden) 记录;空集/异常/feature flag 关闭
  一律降级放行,不阻塞发布主链路
- _publish_reviewed_working_version 在 test_passed 校验后接入门禁
  (修订版 _publish_revision 留待下一轮)
2026-07-03 14:38:26 +08:00
caoxiaozhu
73aee622c7 feat(flywheel): 新增 GoldenCase 模型用于规则回归门禁
- 新增 golden_cases 表,承载版本化的风险规则黄金用例
  (case_key/rule_code/values_json/expected_hit/expected_severity/status)
- 注册到 db/base.py 和 models/__init__.py,进入 Base.metadata
2026-07-03 14:38:14 +08:00
caoxiaozhu
52d57c3be7 test(flywheel): 补 few-shot 飞轮单测并沉淀开发文档
- embedding_provider:GLM/Ollama 分支、维度缓存、HTTP 错误降级
- few_shot_ingestion:confirmed/false_positive 入库、ignored 跳过、幂等去重、
  create_feedback hook 触发、feature flag、吞异常
- few_shot_retrieval:去重、token 预算、超长截断;prompt 注入合并 examples + 向后兼容
- 容器内新增测试 20 passed;回归测试 35 passed(RAG/risk_observations/rule_generation)
- 沉淀 document/development/2026-07-03/feature/ai-data-flywheel 概念文档与 TODO,
  飞轮 1 已勾选证据,飞轮 2-6 待后续迭代
2026-07-03 13:56:21 +08:00
caoxiaozhu
3a9d154783 feat(flywheel): few-shot 在线检索注入打通风险规则编译链路
- 新增 FewShotStore:独立 Qdrant collection few_shot_samples,向量 upsert/search/delete,
  全程失败降级不阻塞主链路
- 新增 FewShotIngestionService:RiskObservation confirmed/false_positive → FewShotSample +
  向量,带 sample_key 幂等去重
- 新增 FewShotRetriever:按 case 特征检索相似历史样本,去重 + token 预算 + 单条字符上限裁剪
- risk_observations.create_feedback commit 后挂 hook 自动入库,带 feature flag 和 try/except 兜底
- risk_rule_generation_prompt 新增 few_shot_samples 可选参数,合并进 examples 并标
  source=historical_confirmed;risk_rule_generation 构造 prompt 前调 retriever,失败降级为空
2026-07-03 13:55:52 +08:00
caoxiaozhu
765cfb40f3 feat(flywheel): 抽公共 EmbeddingProvider 并新增 FewShotSample 模型
- 从 knowledge_rag_runtime 抽出 embedding 调用逻辑为独立 EmbeddingProvider,
  复用现有 HTTP 纯函数,RAG 路径零回归
- 新增 FewShotSample 表模型(样本池),注册到 db/base.py 和 models/__init__.py
  供 few-shot 飞轮沉淀已确认风险观测
2026-07-03 13:55:39 +08:00
caoxiaozhu
08f023243e fix(web): 多 task 草稿自动保存后继续推进下一 task
useWorkbenchAiApplicationPreviewFlow 在自动保存草稿分支透传 onApplicationActionCompleted,
确保草稿/提交完成后仍能按 remaining tasks 推进,修复多 task 报销场景后续步骤不启动。
更新 application-context-submit/intent-planner-model 测试,补充 bug 日志与开发日志。
2026-06-29 20:17:36 +08:00
caoxiaozhu
6bdaeed6d4 chore: 忽略 .zcode 本地目录并更新规则表与开发日志
- .gitignore 新增 .zcode/(ZCode 工具本地配置,不入库)
- 更新交通/通信/差旅等财务规则表
- 补充 2026-06-25/26 开发日志(chat UI SaaS 化、主题企业 AI 风格、bug 日志)
2026-06-26 22:42:46 +08:00
caoxiaozhu
d5a8f84703 refactor(web): 应用外壳/差旅详情/报销创建视图适配主题与多 task
- AppShellRouteView/useAppShell 适配主题皮肤与会话入口
- TravelRequestDetailView/travelRequestDetailSetup 差旅详情适配,travel-request-detail-view.css 调整
- TravelReimbursementCreateView/useTravelReimbursementCreateViewLifecycle 创建视图适配
- 更新 app-shell-financial-assistant-entry/travel-request-detail-risk-advice 测试
2026-06-26 22:42:29 +08:00
caoxiaozhu
c4b5fcc067 feat(web): AI 工作台多 task 串行推进与会话适配
- useWorkbenchAiApplicationPreviewFlow/useWorkbenchAiActionRouter/useWorkbenchAiCommandIntents 支持 task1 完成后自动推进 task2,确认按钮直接拉起申请预览,草稿/提交成功后继续推进下一 task
- workbenchAiIntentPlannerModel/workbenchAiMessageModel/workbenchAiCommandIntentModel 适配多 task 意图规划与消息结构
- aiApplicationPreviewActions/aiApplicationPrecheckModel/aiExpenseDraftModel/aiWorkbenchConversationStore 草稿与会话存储适配
- PersonalWorkbenchAiMode 与样式适配,更新 preview-actions/expense-draft/conversation-store/fast-preview/action-router/command-intent/intent-planner 测试
2026-06-26 22:42:23 +08:00
caoxiaozhu
5753899eb3 feat(web): 主题皮肤系统与 LLM 设置面板重构
- useThemeSkin 重构主题皮肤应用逻辑,支持企业 AI 风格主题切换
- settingsModelHelper 新增主题与模型表字段映射,useSettings 适配
- LlmSettingsPanel/SettingsView 面板重构,支持多模型行编辑与主题区块
- settings-view.css 适配主题样式,新增 settings-theme-section 测试,更新 settings-llm-section 测试
2026-06-26 22:42:00 +08:00
caoxiaozhu
9c3fa80d22 feat(server): 设置持久化新增 LLM 模型表与主题字段
- SettingsLlmForm 新增 models 列表(SettingsModelRow:slot/provider/url/apiKey/modelId/type),支持多模型行持久化
- settings 服务读写模型表与主题相关字段,更新 test_settings_persistence 测试
2026-06-26 22:41:40 +08:00
caoxiaozhu
43c3ff860c fix(web): steward plan 确认按钮直接拉起申请预览,不丢失 remaining tasks
根因:steward plan 的'确认创建申请单'按钮 action_type 是
ASSISTANT_SCOPE_ACTION_SWITCH,handleInlineSuggestedAction 没有匹配分支,
落到 startInlineConversation 重新发起对话,steward_remaining_tasks 完全丢失。

修复:当 payload 有 steward_current_task + session_type=application +
task_type=expense_application 时,直接调 startAiApplicationPreviewFromAction
(会透传 steward_remaining_tasks 到申请预览 message),不走 startInlineConversation。

这样保存草稿成功后,targetMessage 上有 stewardRemainingTasks,
buildApplicationPreviewNextTaskAction 能生成'继续处理费用报销'按钮。
2026-06-26 11:40:44 +08:00
caoxiaozhu
3e4b1e1597 fix(web): 保存草稿/提交成功后也推进到下一个 task
之前多 task 串行推进只在 steward action 执行链(executeInlineStewardAction)
生效,但保存草稿走的是 application preview 流(executeInlineApplicationPreviewAction),
成功后只显示'查看单据',不会提示继续下一个报销 task。

- startAiApplicationPreviewFromAction 透传 steward_remaining_tasks 到 preview message
- startAiApplicationPreview 把 stewardRemainingTasks 存到 message 上
- executeInlineApplicationPreviewAction 成功后:检查 targetMessage.stewardRemainingTasks,
  有剩余 task 则追加'继续处理费用报销'按钮(复用 steward_confirm_flow 分支)
- 新增 buildApplicationPreviewNextTaskAction 辅助函数

现在保存草稿/提交申请成功后,用户会看到:[查看单据] [继续处理费用报销]
2026-06-26 11:21:16 +08:00
caoxiaozhu
3a5664c4da feat(web): 多 task 串行推进 - task1 完成后自动展示 task2 确认按钮
场景:'出差并且报销招待费'→LLM 拆出 2 个 task→先做完出差申请→
完成后自动展示'继续处理费用报销'按钮→用户确认后推进到报销流程。

- 新增 buildNextTaskSuggestedAction:从 actionPayload.steward_remaining_tasks
  取第一个剩余 task,根据 task_type 生成推进按钮(steward_confirm_flow)
- executeInlineStewardAction 成功后:除'查看单据'外,追加'继续处理下一个 task'按钮
- 用户点击推进按钮复用 handleInlineSuggestedAction 的 steward_confirm_flow 分支
- 前端 28 passed 无回归
2026-06-26 10:51:43 +08:00
caoxiaozhu
d139a63e64 refactor(server): 意图识别改 LLM 驱动,规则只做闲聊拦截+resume 兜底
规则不再判断'这是哪个业务场景'——那交给 LLM function call。
规则只保留两个不可替代职责:闲聊拦截(省 LLM 成本)、resume 确定性兜底。

- gate_classify 简化:删掉规则匹配门(94 词 CHOICE 匹配)和 ambiguous 提前判断
- 新增 _is_lightweight_off_topic:只拦 greeting+meaningless,不依赖业务关键词
- HANDLER_ONLY 改为 LLM 输出驱动:LLM 返回 query_travel_standard 后转 handler
- 图拓扑简化:gate_classify 只输出 off_topic|resume|model_intent
- 验证:76 passed;复合场景'出差并且报销招待费'→LLM 返回 2 task
2026-06-26 10:19:04 +08:00
caoxiaozhu
8a2ae6eb75 fix(server): gate_classify 复用 _classify_irrelevant_input 修复 off_topic 误杀
回归问题:P1.3 重构时 gate_classify 的 off_topic 门用了 scene_registry
的 35 个 signal_keywords,丢掉了 legacy 的 73 个关键词(城市名/时间词/金额词/
交通词等),导致'下周去上海''昨天打车30块'等正常业务输入被误判 off_topic,
根本进不了 LLM。

修复:gate_classify 的 off_topic 门改用成熟的 _classify_irrelevant_input
(94 词 + registry 信号词 + greeting/meaningless 细分),scene_registry 的
signal_keywords 只用于规则匹配门(CHOICE 路由)。删除残缺的 _matches_any_signal。

验证:76 passed;实测'下周去上海'→llm_function_call、'昨天打车30块'→
llm_function_call、'你好'→off_topic(正确拦截)。
2026-06-25 16:06:52 +08:00
caoxiaozhu
992cf71fa1 refactor(server): Phase 1 图拓扑重构 - LangGraph 成为唯一编排者
P1.3-P1.7:把 endpoint 补丁搬进图节点,门控收敛到 gate_classify 单一决策点。

- StewardGraphState 扩展:recent_history/steward_state/gate_decision/gate_scene_id/conversation_id
- 新增 5 个图节点:load_context(读历史+state)/gate_classify(统一门控)/execute_scene_handler/resume_recent_task/pending_flow wrapper
- 图拓扑从 5 节点重构为 10 节点:load_context → gate_classify → {off_topic/handler_only/resume/ambiguous/model_intent} → attach_action_steps
- gate_classify 四步裁决:resume门 → off_topic门 → 规则匹配门 → LLM门
- resume 门控优先于 off_topic,避免'再提交'被误判闲聊
- schema 放宽 planning_source/next_action Literal → str,支持 scene_handler:*/context_resume/answer_only
- endpoint 按 planner 类型分发 build_plan(LangGraph 接 db,legacy 不接)
- 76 passed + 4 场景端到端验证(出差申请/再提交/查差旅标准/闲聊)
2026-06-25 15:44:20 +08:00
caoxiaozhu
54356ba81a refactor(server): scene 注册表骨架 + 统一门控管道设计文档
Phase 1 P1.1-P1.2:为后端门控收口提供声明式场景注册基础设施。

- 新建 scenes/ 目录:gate_rules(GateRule/SceneRoute 枚举)、scene_descriptor(SceneDescriptor dataclass)、scene_registry(SceneRegistry 单例)
- 3 个场景迁入 descriptor:expense_application / reimbursement / query_travel_standard
- __init__.py 的 bootstrap_scenes 在 import 时注册 + 运行时绑定 handler/builder/executor(解决循环 import)
- 查询场景 priority=50 优先于 MODEL_ONLY 场景,确保规则匹配先于 LLM
- 落地 UNIFIED_GATE_PIPELINE.md 架构文档:目标架构 / 验收标准(接入 O(1))/ 3 阶段迁移路径
- 76 passed,scene 注册表未破坏现有代码;与 intent_registry 暂时并存,P1.3-P1.8 会统一迁移
2026-06-25 15:09:16 +08:00
caoxiaozhu
e9d7c56d5b feat(server): 会话上下文保留(LLM 历史 + 确定性兜底双保险)
解决用户删除草稿后说'再提交'丢失上下文的问题:

- steward.py 新增 _inject_recent_conversation_history:build_plan 前读最近 10 条对话注入 context_json
- steward_intent_agent.py 的 _build_messages 把 recent_history 暴露给模型,system prompt 加确认类话术引导
- 新建 steward_context_resume.py:should_resume_recent_task 检测'再提交'类话术 + state 有可恢复 flow,attach_resumed_task 从 state 恢复 task
- 两个 plan 入口(/plans 和 /plans/stream)都已接入双保险
- 后端 67 passed,端到端验证'上海出差→再提交'成功恢复 task
2026-06-25 15:08:56 +08:00
caoxiaozhu
2ebc2756bf fix(server): 兼容模型 tasks 输出为 JSON 字符串与 flow_id 误填
- StewardModelPlanBuilder 解析 tasks 时兼容模型把数组序列化为字符串的情况,先反序列化;JSON 截断/语法不完整时用正则抢救 task_type/requested_action/ontology_fields 等关键字段
- task_type 未命中意图时尝试 flow_id→task_type 映射还原,避免模型把 flow_id(如 travel_application)误填为 task_type 导致正确意图被丢弃
2026-06-25 12:25:18 +08:00
caoxiaozhu
606a88c805 chore: stewardPlanModel 适配注册表动作结构并更新规则表与日志
- stewardPlanModel 适配新的意图注册表动作步骤结构
- 更新交通/通信/差旅等财务规则表,补 2026-06-25 work-log
2026-06-25 11:50:11 +08:00
caoxiaozhu
eaada4bc57 refactor(server): steward 意图改用声明式注册表编排
- 新增 steward_intent_registry,IntentDescriptor 统一描述意图的识别关键词、动作步骤构建、字段白名单与副作用集合,替代分散的 if/else
- 新增 steward_intent_bootstrap 注册 expense_application 等意图;新增 steward_query_executors 提供差旅标准查询的无副作用执行与城市/席别标签化输出
- action_contracts/action_executor/graph_planner/intent_agent/model_plan_builder/planner_extraction/fallback 适配注册表,识别与执行分发自动从注册表取数
- 新增 intent_registry/query_executors 测试,更新 intent_agent 测试
2026-06-25 11:50:02 +08:00
caoxiaozhu
d321005044 chore: 更新公司通信费规则表与 AI 意图规划/work-log 文档 2026-06-25 10:56:00 +08:00
caoxiaozhu
59353308a2 feat(web): AI 意图规划置信度阈值与动作策略细化
- workbenchAiIntentPlannerModel 新增 WORKBENCH_AI_INTENT_CONFIDENCE_THRESHOLD 与 isLowConfidenceTravelApplicationPlan,shouldRequestWorkbenchAiIntentPlan 增加业务关键词前置过滤
- resolveExecutableTravelApplicationPlan 区分 requestedSubmit 与提交确认(submitRequiresConfirmation),autoSubmit 不再直接置真
- workbenchIntentActionPolicy 改用 policyDecision 路由(need_confirmation/query_candidates),透传 riskLevel/requiresSelection/requiresConfirmation
- workbenchIntentFrameModel 补充 query 动作识别,usePersonalWorkbenchAiMode/useWorkbenchAiActionRouter/useWorkbenchAiApplicationPreviewFlow 接入低置信度与确认流程
- 更新 intent-planner-model/intent-frame-model/application-gate-model/fast-preview 测试
2026-06-25 10:55:49 +08:00
caoxiaozhu
6b0756a55f chore(logs): split agent change log outputs 2026-06-25 09:35:18 +08:00
caoxiaozhu
4d8a606cd6 chore(skills): date-scope development docs 2026-06-25 09:25:28 +08:00
caoxiaozhu
23f7de6cbf chore(skills): add development docs writer 2026-06-25 09:19:37 +08:00
caoxiaozhu
a12c4bea64 chore: 更新公司通信费规则表、work-log 与 superpowers specs 文档 2026-06-24 22:59:10 +08:00
caoxiaozhu
e5b03c6601 feat(web): 文档查询意图补充风险过滤与 X 天前范围
- aiDocumentQueryIntent 新增风险等级过滤(无/高/中/低/有风险)与 N 天前日期范围解析
- aiDocumentQueryModel/aiConversationHtmlRenderer 渲染适配风险过滤标签
- useWorkbenchAiDocumentQueryFlow/aiWorkbenchConversationStore 会话流转适配命令意图
- 更新 ai-document-query-model/ai-conversation-html-renderer/assistant-session-draft-delete 测试
2026-06-24 22:59:05 +08:00
caoxiaozhu
3eb78d343a feat(web): AI 工作台命令意图解析与动作策略
- 新增 workbenchIntentFrameModel,解析删除/审批/查询/咨询政策等意图帧,含风险等级、目标模式(当前上下文/筛选候选)与日期归一化
- 新增 workbenchIntentActionPolicy,按意图帧路由下一步(直通/拦截批量/上下文确认/查询候选)
- 新增 workbenchAiCommandIntentModel 与 useWorkbenchAiCommandIntents,识别草稿删除意图并解析最近草稿载荷生成引导
- usePersonalWorkbenchAiMode 接入命令意图处理,personal-workbench-ai-mode.css 适配
- 新增 command-intent-model/intent-frame-model 测试
2026-06-24 22:58:59 +08:00
caoxiaozhu
a0f6d9f702 chore: 更新 .env.example、财务规则表与 AI 意图规划开发文档 2026-06-24 21:59:15 +08:00
caoxiaozhu
bb681aa1f3 chore(repo): 移除误跟踪的 egg-info 构建产物并新增忽略规则
server/src/x_financial_server.egg-info/ 为 uv/pip 构建产物,此前被误跟踪。
本次 git rm --cached 移除跟踪(本地保留),.gitignore 新增 *.egg-info/ 规则避免后续误入库。
2026-06-24 21:59:09 +08:00
caoxiaozhu
bc560145a4 feat(web): AI 工作台意图规划与规划思考模型
- 新增 workbenchAiIntentPlannerModel,基于 LLM function_call 解析建单/草稿/提交意图,区分 model 与 rule_fallback 来源
- 新增 workbenchAiPlanningThinkingModel 合并规划思考事件流,按 eventId 去重合并
- application gate/preview 模型接入意图规划,usePersonalWorkbenchAiMode/useWorkbenchAiStewardFlow/useWorkbenchAiActionRouter 链路适配,支持上下文提交
- steward 服务与 stewardPlanModel 适配新动作结构,receipt-folder-view 微调样式
- 新增 intent-planner-model/application-context-submit/steward-actions-service 测试,更新 gate-model/action-router/plan-message-copy/fast-preview 测试
2026-06-24 21:58:46 +08:00
caoxiaozhu
5311c99d69 refactor(server): steward 决策链路改用 LangGraph 编排
- 新增 StewardGraphPlannerService,用 LangGraph 状态图编排意图识别→流程判断→模型/规则分支→兜底,替代原 planner 内线性调用
- 新增 StewardGraphRuntimeService 编排运行时决策与槽位决策;StewardActionContracts/Executor 统一动作合约与执行
- steward_intent_agent/application_fact_resolver/runtime_chat 适配图执行器,config 暴露图相关开关
- pyproject/uv.lock 新增 langgraph 依赖
- 新增 graph_planner/graph_runtime/action_executor 测试,更新 intent_agent/planner/fact_resolver/runtime_chat/reimbursement 测试
2026-06-24 21:58:35 +08:00
caoxiaozhu
545b31d32f chore(env): docker-compose 端口与服务配置微调并更新规则表与日志
- docker-compose(.full).yml 与 start.sh 微调端口/服务配置
- AGENTS.md 同步更新协作规范
- 更新交通/通信/差旅等财务规则表,补 2026-06-24 work-log
2026-06-24 12:36:03 +08:00
caoxiaozhu
8417a9f542 feat(web): 设置中心缓存管理与文件预览资产工具
- 新增 documentPreviewAssets 工具,统一从 URL/Blob/File 推断预览类型(image/pdf/file/unsupported)
- SettingsView/SettingsView.js/settingsModelHelper 新增系统缓存管理区块,调用 /settings/cache/clear 并展示清理结果;useSettings/services 适配
- WorkbenchAiFilePreviewDialog/useWorkbenchAiFilePreview 接入预览资产工具,workbenchAiComposerModel 调整文件处理
- ReceiptFolder/LogDetailView/DigitalEmployeeWorkRecords/travelReimbursementAttachmentModel 配套适配
- 新增 settings-cache-management-section 测试,更新 settings-llm/rendering/receipt-folder-view/composer-components/attachment-association 测试
2026-06-24 12:35:59 +08:00
caoxiaozhu
9a5ed0e94a feat(server): 系统缓存清理接口与 OCR 文本层兜底增强
- 新增 system_cache 模块与 POST /settings/cache/clear,管理员可一键清理 OCR 结果/运行时配置/模型失败冷却/知识库索引/地点语义等进程内缓存
- 各服务暴露 clear_*_cache 方法(ocr/runtime_settings/runtime_chat/knowledge/application_location_semantic),SettingsCacheClearRead 汇总清理项
- OCR 转图片失败时尝试用 PDF 文本层兜底构建识别文档(有效字符≥8),并写结果缓存;OcrService 暴露 clear_result_cache
- receipt_folder 车票过滤补充身份证号关键词,附件文档/操作/展示模块同步适配
- 新增 system_cache_endpoints 测试,更新 openapi_schema/ocr/receipt_folder/attachment_association_jobs 测试
2026-06-24 12:35:51 +08:00
caoxiaozhu
50d2dc579a style(web): 微调 AI 工作台样式 2026-06-24 10:43:08 +08:00
caoxiaozhu
f9553a6a1a docs: 清理过期计划文档并补全 work-log 与开发/用户文档
- 删除已落地的 improvement-roadmap、superpowers 计划与 ui-mockups 参考稿,删除早期 work-log(2026-05-06~08)
- 新增 2026-05-23 起的 work-log 与 attachment-association-background-job、reimbursement-draft-action-branching 等开发文档及用户文档
- docker-compose(.full).yml 微调服务配置
2026-06-24 10:42:57 +08:00
caoxiaozhu
ee730aa31c feat(web): AI 工作台文件预览/附件关联任务与草稿分支
- 新增 WorkbenchAiFilePreviewDialog 附件预览对话框及 useWorkbenchAiFilePreview,附件支持点击预览
- 新增 attachmentAssociationJobs/linkedReimbursementDraftJobs 前端服务与对应 composable,接入后台任务轮询与状态展示
- 新增 travelReimbursementDraftBranchModel 草稿分支模型,报销关联门控支持跳过/选择草稿
- PersonalWorkbenchAiMode 及各 composable(expense/document/steward/application-preview/attachment-association)重构适配,WorkbenchAiComposer/FileStrip 样式与交互完善
- DocumentsCenter/ReceiptFolder/TravelReimbursementCreate 等视图及 scripts 重构,风险/差旅规划/审批等工具适配
- 新增/更新前端测试:application-result-card、reimbursement-list-preview-fetch、guided-flow、composer-components 等
2026-06-24 10:42:50 +08:00
caoxiaozhu
0264a4b5b4 refactor(server): user_agent/steward/ocr 等服务重构并适配关联任务
- user_agent 拆分 application/locations/knowledge/response/review 四个子模块,接入申请位置语义与关联草稿分支
- steward planner/runtime/slot/plan_builder 决策链路重构,travel_reimbursement_calculator/orchestrator_expense_query 适配
- ocr/document_preview/document_intelligence/receipt_folder 复用预览与资产缓存,expense_claim_draft_flow/application_handoff 适配
- pyproject.toml 新增依赖,paddleocr bootstrap 脚本与 server_start.sh 调整
- 更新差旅/交通/通信等财务规则表,同步 document_intelligence/ocr/receipt_folder/user_agent 等测试
2026-06-24 10:42:24 +08:00
caoxiaozhu
332f77389d feat(server): 新增附件关联/关联报销草稿后台任务与申请位置语义
- attachment_association_jobs:从票据夹批量关联附件到报销单,识别城市/日期并创建明细项,内存态 job 跟踪
- linked_reimbursement_draft_jobs:基于申请单异步生成关联报销草稿,调用 Orchestrator 编排,区分 succeeded/failed 终态
- application_location_semantics:抽取差旅出发/到达城市、判断具体地址/业务动作等位置语义,供申请单校验复用
- router 注册两个 job 端点,新增对应 job/语义单元测试
2026-06-24 10:42:05 +08:00
caoxiaozhu
d4ff79f326 chore(agent): 新增变更日志/checkpoint skill 与 post-commit hook
- AGENTS.md 新增变更日志 Skill 规范:变更后增量更新 document/work-log,更新前先做 Git 拉取检查
- 新增 .codex/skills 下 agent-change-log、git-checkpoint-commit 两个 skill
- 新增 .githooks/post-commit 与 tools/agent-change-log 自动化脚本,提供提交后最低限度日志追加
2026-06-24 10:41:56 +08:00
caoxiaozhu
93212600eb chore(skills): add git checkpoint commit loop 2026-06-24 09:47:05 +08:00
caoxiaozhu
73966b3a7b refactor: consolidate finance workflow modules 2026-06-23 11:21:18 +08:00
caoxiaozhu
1f40ce3df3 fix(web): 孤儿申请预览消息触发重新生成可编辑表格
useWorkbenchAiApplicationPreviewFlow 识别仅含表格文案但缺 applicationPreview 载荷的孤儿消息,触发重新生成可编辑表格并提示用户,避免提交动作静默失败。
2026-06-23 09:43:29 +08:00
caoxiaozhu
f17098aa58 chore(repo): 移除误跟踪的 expense_claims 运行时票据文件
server/storage/expense_claims/ 下 30 个用户上传票据/预览图/meta 此前在 gitignore 规则生效前被误提交,运行时会持续产生内容变更。
本次 git rm --cached 移除版本跟踪(本地文件保留),配合已有 server/storage/expense_claims/ 忽略规则,避免后续误入库。
2026-06-23 09:43:07 +08:00
caoxiaozhu
8094333e3b style(web): 通知中心列表行布局重构与时间标签格式化
- TopBar 通知行改为 row-main/row-head/row-foot 结构化布局,标题加粗、分类改为 pill、箭头随标题右侧
- formatNotificationTime 改名 formatNotificationTimeLabel,新增 ISO/短日期直通匹配与超长截断兜底
- 更新 sidebar-document-unread-dot 测试
2026-06-23 09:42:56 +08:00
caoxiaozhu
0122f3b250 chore(rules): 更新交通/通信/差旅/出差等财务规则表 2026-06-23 09:42:43 +08:00
caoxiaozhu
dc4cad2baa chore(env): WEB_PORT 统一回退为 5173 并烟雾检查改为可开关
- .env.example/docker-compose(.full).yml WEB_PORT 默认值 5273→5173(Vite 默认),CORS_ORIGINS 同步
- docker-compose 注入 WEB_PORT/SERVER_PORT 环境变量,健康检查端口随之更新
- start.sh 新增 SERVER_SMOKE_CHECK_ENABLED 开关,默认关闭烟雾检查,仅健康探测
- web/web_start.sh 适配端口
- .gitignore 补充忽略 tmp/ 运行时目录
2026-06-23 09:42:40 +08:00
caoxiaozhu
e725b7f19c feat(web): 票据夹资产缓存接入与 AI 工作台附件流程完善
- ReceiptFolderView 删除票据后提示已关联附件副本保留,接入 useToast;fetchReceiptFolderAsset 加 no-store 避免预览缓存
- PersonalWorkbenchAiMode 附件区/对话气泡适配资产缓存,personal-workbench-ai-mode.css 调整布局
- usePersonalWorkbenchAiMode/useWorkbenchAiApplicationPreviewFlow/useWorkbenchAiAttachmentAssociationFlow/useWorkbenchAiStewardFlow 完善附件草稿选择与关联流程
- travelRequestDetailSmartEntryRecognition 智能识别增强,AppShellRouteView/PersonalWorkbenchView/useApplicationPreviewEditor/useTravelReimbursementSubmitComposer 等配套适配
- 新增 expense-attachment-draft-selection、receipt-folder-asset-cache、travel-request-detail-smart-entry-recognition 测试,更新 attachment-association-confirmation、expense-application-fast-preview、workbench-ai-mode-switch 测试
2026-06-23 09:42:13 +08:00
caoxiaozhu
84a8998e59 feat(server): 票据文件夹资产缓存与文档预览统一生成
- 新增 document_preview 模块,DocumentPreviewAssets 统一处理 data URL 解码、pdftoppm PNG 预览生成(poppler-data 编码)、renderer_id 标识
- receipt_folder 服务复用预览生成,缓存票据资产并提供清理;删除票据时保留已关联报销单的附件副本
- document_intelligence 新增票据预览/资产缓存接入与字段提取增强;ocr 抽取复用预览工具,附件分析/文档/操作/展示四个子模块同步适配
- receipt_folder 端点补充资产缓存头,补/扩 document_intelligence、ocr_endpoints、ocr_service、receipt_folder_service、reimbursement_endpoints 测试,新增 attachment_analysis 回归测试
2026-06-23 09:42:00 +08:00
caoxiaozhu
bc743adef3 chore(rules): 更新公司通信费报销规则表 2026-06-22 15:56:13 +08:00
caoxiaozhu
ded8b39ccb feat(web): 申请单预览编辑器增强与报销流程细节适配
- useApplicationPreviewEditor 扩展字段编辑与校验,useTravelReimbursementApplicationPreviewDateEditor 微调日期处理
- travelReimbursementExpenseQueryModel/reimbursements 服务/expenseApplicationPreview 适配工号/邮箱字段与关联动作
- useWorkbenchAiApplicationPreviewFlow/usePersonalWorkbenchAiMode 接入关联门控后的预览流转
- TravelReimbursementCreateView 调整入口,TravelReimbursementMessageItem 适配
- 新增 expense-application-fast-preview 测试,更新 attachment-association-confirmation、review-drawer-switch 测试
2026-06-22 15:56:06 +08:00
caoxiaozhu
ba444a514f feat(web): 报销单新增关联申请单门控与草稿检测流程
- 新增 travelReimbursementAssociationGateModel,查询可关联申请单/草稿报销单并生成跳过/选择/单独新建动作,区分差旅费与业务招待费类型
- travelReimbursementApplicationLinkModel 补充 buildLinkedApplicationReferenceIndex/buildRequiredApplicationActions 等关联构建逻辑
- useTravelReimbursementSuggestedActions 接入 select_required_application/skip 系列动作,'我要报销'入口改为先走关联门控
- useWorkbenchAiActionRouter 新增 SKIP_REQUIRED_APPLICATION_LINK/SKIP_REIMBURSEMENT_DRAFT_CHECK 动作分发
- useWorkbenchAiExpenseFlow 暴露 startAiReimbursementAssociationGate,stewardPlanModel 待处理流程适配
- 新增 workbench-ai-action-router、workbench-ai-reimbursement-association-gate 测试并更新 guided-flow、steward-plan 测试
2026-06-22 15:55:59 +08:00
caoxiaozhu
aa965da69d feat(server): 报销单输出工号/邮箱并扩展申请人邮箱前缀匹配
- ExpenseClaimRead 新增 employee_no/employee_email 字段,ExpenseClaim 模型补对应只读属性
- expense_claim_access_policy 在姓名匹配未果时,按 candidate@% 邮箱前缀匹配 Employee.email,命中唯一记录即返回
- test_backend_pagination/test_expense_claim_service 补充工号/邮箱字段断言与邮箱前缀匹配用例
- 更新公司通信费报销规则表
2026-06-22 15:55:48 +08:00
caoxiaozhu
1b04ee1c4c fix(web): restore travel detail child component styles 2026-06-22 13:34:12 +08:00
caoxiaozhu
103f225f54 chore(rules): 更新差旅/交通/通信等财务规则表 2026-06-22 12:02:11 +08:00
caoxiaozhu
e42dedaba1 docs: 同步 Docker 编排说明并补充重构计划与 UI 参考
- README 与 docker/README 更新双 compose 文件说明:docker-compose.yml 仅主应用、docker-compose.full.yml 完整栈
- 新增 docs/superpowers/plans 800 行重构实施计划,规划前后端代码体量护栏与按职责拆分路径
- 新增 docs/ui-mockups 附件关联企业版 UI 参考稿
2026-06-22 12:02:08 +08:00
caoxiaozhu
607e127f59 chore(docker): 拆分主应用编排与完整本地栈
- 新增 docker-compose.full.yml:编排 main+postgres+qdrant+onlyoffice 完整本地开发栈,依赖与 DATABASE_URL 由 compose 注入
- docker-compose.yml 精简为仅启动主应用,ONLYOFFICE/QDRANT 相关变量改为空值占位,便于复用外部依赖
- docker-compose.postgres.yml 主应用补充 POSTGRES_*/DATABASE_URL 环境变量
2026-06-22 12:02:03 +08:00
caoxiaozhu
6d33ba5742 refactor: enforce 800 line source limits 2026-06-22 11:58:53 +08:00
caoxiaozhu
08a4fa3577 style(web): AI 工作台附件卡片样式与交互适配
- personal-workbench-ai-mode.css 新增 workbench-ai-file-card 系列样式,file-strip 改为 flex-wrap 左对齐排布卡片,移除按钮样式随卡片重构
- workbench-ai-mode-switch 测试补充附件卡片相关断言
2026-06-21 23:24:36 +08:00
caoxiaozhu
d660a961fb feat(web): AI 工作台附件改为卡片化展示并支持单项移除
- PersonalWorkbenchAiMode 附件区由计数条改为按类型图标/名称/类型标签的卡片列表,支持单项移除(removeAiModeFile),复用 buildFileIdentity 作为 key
- resolveAiComposerFileType 按 pdf/图片/表格/文档/压缩包/文件归类,分别对应图标与色调
- .gitignore 补充忽略 server/storage/receipt_folder/ 运行时票据存储目录
2026-06-21 23:24:16 +08:00
caoxiaozhu
669d22e71f feat(web): 差旅领导意见事件结构化与申请审批信息增强
- applicationApproval 新增按日期/时间/审批角色拆分格式化,buildLeaderApprovalEvents 补充 dateLabel/timeLabel/roleLabel 字段
- TravelRequestDetailView 领导意见事件改为日期+时间+审批人结构化展示,travel-request-detail-view.css 重构对应样式
- travelReimbursementAttachmentModel 微调附件标识,同步更新 application-approval-info、travel-request-detail-leader-approval、attachment-association-confirmation 测试
- 更新公司通信费报销规则表
2026-06-21 23:24:09 +08:00
caoxiaozhu
88e91a5900 feat(ocr): PDF 文本层可用时跳过 worker 调用并补装 poppler-data
- OcrService 提取 PDF 文本层后若有效字符达到阈值,直接构建文档并写入结果缓存,不再触发 OCR worker,仅无文本层时才解析 python_bin/worker_path 调用 worker
- _build_text_layer_document 复用 AggregatedOcrDocument 聚合文本层片段,_has_usable_pdf_text_layer 基于 meaningful_char_count 判定
- docker-compose 与 paddleocr bootstrap 脚本补装 poppler-data,保证 PDF 文本层抽取的中文编码正确
- 新增文本层直取与运行时依赖两项 ocr_service 单测
2026-06-21 23:23:59 +08:00
caoxiaozhu
1986b0d945 style(web): 移除顶栏 AI 快捷操作区并优化差旅领导意见事件样式
- TopBar 移除 AI 模式下的公司切换/AI 模式切换快捷操作块及 showAiModeUtilityActions 计算属性,清理 top-bar.css 对应样式
- TravelRequestDetailView 领导意见事件重构为状态/意见/审批人结构化布局,travel-request-detail-view.css 补充对应样式
- 同步更新 topbar-ai-mode-switch、ai-sidebar-rail-mode、travel-request-detail-leader-approval 测试
2026-06-21 22:56:34 +08:00
caoxiaozhu
24b5b71b0f feat(web): 差旅申请详情进度 viewer 与审批/加载态组件增强
- 新增 requestProgressViewer,申请单在直属领导审批视角下将当前步骤展示为'等待批复',travel-request-detail/app-shell/useRequests 接入
- TravelRequestApprovalDialog 增强审批交互,TableLoadingState 补充表格加载占位,ConfirmDialog 扩展确认对话框能力
- useAppShell/useRequests/AppShellRouteView 配套适配申请详情跳转与会话状态
- 同步更新 requestProgressSteps、travel-request-detail-leader-approval、assistant-session-draft-delete、documents-center-status-filter、app-shell-financial-assistant-entry、request-progress-viewer 等测试
2026-06-21 22:49:58 +08:00
caoxiaozhu
8b3495455b feat(web): AI 文档详情引用解析与查询卡片增强
- 新增 aiDocumentDetailReference,统一解析 #ai-open-document-detail / #ai-open-application-detail 引用,兼容 A/R/D 短格式与 AP-/RE-/AD- 旧格式单号,提供 isBusinessDocumentReference 判定
- aiDocumentQueryModel 文档卡片接入详情引用,按申请单/报销单生成对应 href,HTML 渲染器识别单据记录表格并生成卡片链接
- PersonalWorkbenchAiMode 处理文档详情点击跳转,卡片样式重构为结构化布局并更新背景资源
- expenseApplicationPreview 补充事由字段,同步新增/更新 ai-document-detail-reference、document-query-model、html-renderer、workbench-ai-mode 等测试
- 更新公司通信费报销规则表
2026-06-21 22:49:53 +08:00
caoxiaozhu
3b74a330a3 feat(web): AI 文档查询卡片重构与单号判定统一
- documentClassification 抽出 isApplicationDocumentNo,统一兼容 AP-/APP- 旧格式与 A+8 新格式,aiDocumentQueryModel 复用
- aiDocumentQueryModel 文档卡片改为结构化字段布局(单据类型/金额/申请人/编号/操作),新增查询范围摘要区,渲染走 HTML 信任块
- AppShellRouteView/useAppShell/useRequests/detailAlerts/riskVisibility 等差旅详情模型适配单号判定
- 同步更新 ai-document-query-model/workbench-ai-mode-switch 测试,新增 document-classification 测试
2026-06-20 22:04:37 +08:00
caoxiaozhu
8158716e23 test(server): 适配 A/R/D 紧凑单号格式
- approval_routing/service/user_agent 测试中报销单查询统一兼容 RE- 旧格式与 R+8 新格式,申请单单号断言改为短格式
- generate_claim_no 用例重命名为短前缀校验,正则改为 R[A-HJ-NP-Z2-9]{8}
- 同步更新差旅/交通/通信等财务规则表
2026-06-20 22:04:31 +08:00
caoxiaozhu
0cda750ff0 feat(web): AI 工作台会话与文档卡片渲染增强
- aiConversationHtmlRenderer 识别单据记录类表格并渲染为卡片列表,新增删除申请单详情的禁用占位链接
- aiWorkbenchConversationStore 增加草稿删除后会话链接失效处理,避免点击已删除单据跳转
- aiApplicationPreviewActions 调整提交/草稿调用路径,PersonalWorkbenchAiMode 接入新的会话存储与渲染
- ConfirmDialog/TravelRequestDeleteDialog/useAppShell/AppShellRouteView 配套适配,同步更新相关前端测试
2026-06-20 21:44:16 +08:00
caoxiaozhu
81e990ab72 feat(server): 申请单支持草稿保存并统一删除权限口径
- user_agent_application 新增草稿分支:识别'保存草稿/存草稿/先保存'等意图,复用可编辑记录更新或建草稿,提交前单据重叠仍拦截
- 草稿态返回单号与待提交提示,submit 仅在确认提交分支触发,避免草稿进入审批流
- reimbursements 删除接口文案与判定统一为系统管理员可删、申请人删自有草稿/退回单,申请单判定改用 is_application_claim_no
- 更新财务规则表与 reimbursement 端点测试
2026-06-20 21:44:12 +08:00
caoxiaozhu
47c6a4bb73 refactor(server): 单号规则收紧为 A/R/D+8 位紧凑格式
- DOCUMENT_NUMBER_PREFIXES 改为 A/R/D,新增短格式与旧格式正则并存识别,提取正则加边界锚定避免误匹配
- build_document_number 去掉时间戳段,统一生成 A+token 等紧凑单号,is_application_claim_no 兼容旧 AP-/APP- 前缀
- access_policy/status_registry/reimbursements/expense_claims/budget_support 统一复用 is_application_claim_no 判定申请单
- 同步 document_numbering 单元测试覆盖新旧两种格式
2026-06-20 21:44:06 +08:00
caoxiaozhu
96c2e1099a feat(web): 统一平台管理员判定与 AI 工作台申请预览动作接入
- authUser 抽出 resolveAuthUserAdminFlag,统一 isAdmin 解析(含 superadmin、role_codes、中英文角色名),accessControl 复用同一逻辑
- 登录态、应用外壳路由、系统状态接入统一管理员判定,LoginView 与相关 composable 配套调整
- AI 工作台申请提交改为调用新的 /application-preview-action 接口,草稿保存仍走 orchestrator;预审模型补充重叠冲突提示与阻断判断
- 同步更新 accessControl/api-request/ai 预览动作等前端测试
2026-06-20 14:42:04 +08:00
caoxiaozhu
729d833edb feat(server): 新增申请核对预览快速建单接口与平台管理员判定统一
- reimbursements 新增 POST /application-preview-action,AI 工作台表格核对后直接走 UserAgentService 建单/提交,免去通用 Orchestrator 编排
- 平台管理员判定统一抽取 PLATFORM_ADMIN_IDENTITIES 常量,identity 与 role_codes 均支持 admin/superadmin,含 header 开关
- docker-compose 镜像补装 openssh-server
- 同步更新差旅/交通/通信等财务规则表与 reimbursements 端点测试
2026-06-20 14:41:59 +08:00
caoxiaozhu
304bbe1fd4 feat(web): 工作台 AI 模式报销预审与文档查询模型拆分
- 新增 aiApplicationPrecheckModel/aiDocumentQueryModel/aiApplicationPreviewActions/aiConversationHtmlRenderer 四个独立模型与服务,按职责从主组件拆出
- PersonalWorkbenchAiMode 接入拆分后的预审、文档查询与 HTML 渲染逻辑,配合 markdown 工具增强结构化展示
- 文档中心与归档筛选、风险可见性、申请预览等工具同步适配,补充对应单元测试
- 新增 AI 文档卡片背景资源
2026-06-20 10:17:37 +08:00
caoxiaozhu
3d69f8501f feat(risk): 申请单阶段风险可见性细化与规则表更新
- 申请单阶段将 policy/trip/amount 类风险对申请人可见、可自行修正,画像/审批流程类仍走领导可见
- 平台风险标记与语义推断统一采用该策略,更新对应单元测试
- 风险规则中 city_mismatch 等城市匹配规则移除 expense_application 阶段,仅保留 reimbursement
- 同步更新交通/通信/差旅/出差等财务规则表
2026-06-20 10:17:18 +08:00
caoxiaozhu
4d04f4e7af chore(gitignore): 忽略 .env 防止敏感配置入库
将 .env 从版本库移除(本地文件保留),并补充 .env.local 等本地变体忽略规则,
避免数据库密码、密钥等敏感配置进入 git 历史。
2026-06-18 22:14:53 +08:00
caoxiaozhu
3131112952 style(web): 调整 AI 模式用户消息气泡布局与引用图标
- 用户消息气泡的操作区与时间右对齐
- 引用按钮图标由 mdi-reply 改为 mdi-format-quote-open
2026-06-18 22:13:09 +08:00
caoxiaozhu
a2f67af13e docs: 新增 X-Financial 改进路线图 2026-06-18 22:12:38 +08:00
caoxiaozhu
0cde1f8990 feat(web): 工作台 AI 模式与差旅/风险建议交互优化
- 新增 PersonalWorkbenchAiMode 组件、AI 侧边栏与 orb 机器人视觉资源
- 新增 aiApplicationDraftModel / aiExpenseDraftModel / aiWorkbenchConversationStore
  及业务准入 aiSidebarBusinessAccess,支撑 AI 模式下的申请与报销草稿
- 顶栏、侧边栏、工作台样式重构,适配 AI 模式切换与响应式布局
- 同步 steward plan/off_topic、差旅报销引导流、风险建议卡片等测试
2026-06-18 22:12:24 +08:00
caoxiaozhu
a6674a1e76 feat(steward): off_topic 场景细分与引导回复
- 将业务无关输入细分为 greeting / meaningless / off_business 三类场景
- 新增 StewardOffTopicAgent,用 function calling 生成管家语气引导回复
- steward endpoint 与 user_agent_application 串联 off_topic 引导话术
- 补充 planner 与 user agent 的 off_topic 覆盖测试
2026-06-18 22:12:10 +08:00
caoxiaozhu
127d603e7d feat(ontology): 仅放行财务业务相关问题的信号校验
- 新增 _has_supported_business_signal,在加载目录前拦截非财务问题并抛错
- 同步重构 ontology 服务测试覆盖业务信号判定分支
2026-06-18 22:12:00 +08:00
caoxiaozhu
3f17619e0c fix(auth): 登录目录就绪幂等化与并发控制
- employee/settings/user_session_metrics 的 ensure_*_ready 改为按 bind 缓存 + 锁,
  避免每次登录重复建表与并发场景下的竞态
- auth 登录链路先查员工再降级触发目录就绪,并吞掉查询期 SQLAlchemy 异常
- 默认管理员账号由 superadmin 迁移为 admin,兼容历史账号回填
- 补充登录降级与设置持久化相关测试
2026-06-18 22:11:53 +08:00
caoxiaozhu
59ba76c74a feat(startup): 服务端启动 bootstrap 与缓存预热
- 新增 STARTUP_BOOTSTRAP_ENABLED / STARTUP_CACHE_WARMUP_ENABLED 配置开关
- lifespan 拆分 bootstrap 步骤并后台线程预热缓存,失败可降级继续启动
- server_start.sh / web_start.sh 扩展 SERVER_PORT、启动与调度开关的 env 覆盖
- bootstrap_paddleocr_mobile.sh 改用 python3 并补充 poppler-utils 依赖
- 补充启动 bootstrap 与 env 覆盖优先级测试
2026-06-18 22:11:37 +08:00
caoxiaozhu
35372c6661 feat(rules): 更新差旅与通信费用等财务规则表 2026-06-18 22:11:13 +08:00
caoxiaozhu
38653fa365 chore(storage): 清理用户历史报销票据附件
移除测试期残留的 receipt_folder 附件与预览文件,后续通过归档目录维护。
2026-06-18 22:11:10 +08:00
caoxiaozhu
c28e99b714 chore(gitignore): 忽略 .nezha/ 与 .omo/ 本地工具目录 2026-06-18 22:11:06 +08:00
caoxiaozhu
43432534d8 feat(steward): 前端支持 off_topic 与引导话术
- assistantSessionScope.js:新增 ASSISTANT_SCOPE_ACTION_FILL_COMPOSER 常量
- assistantSuggestedActionPrefill.js:识别 fill_composer 与 payload.fill_text
- stewardPlanModel.js:normalizeStewardPlan 透传 suggestedPrompts;
  buildStewardPlanMessageText / buildStewardSuggestedActions
  新增 off_topic 分支,按钮填充输入框不提交
- useStewardPlanFlow.js:isPendingStewardActionMessage 排除 off_topic
- steward-plan-off-topic.test.mjs:覆盖 normalize/文案/按钮/兼容路径
2026-06-18 14:15:30 +08:00
caoxiaozhu
cce19e4c40 feat(steward): 拦截业务无关输入返回 off_topic 计划
- schemas/steward.py:StewardPlanResponse 新增 suggested_prompts 字段
- steward_planner.py:新增 STEWARD_BUSINESS_SIGNAL_KEYWORDS 与
  _is_business_irrelevant_input 守卫,在 build_plan 入口前置;
  新增 _build_off_topic_plan 构造 plan_status=off_topic 的引导计划
- steward_intent_agent.py:system prompt 追加业务无关约束
- test_steward_planner.py:覆盖 123/你好/纯标点走 off_topic,
  并验证正常业务输入不受守卫影响
2026-06-18 14:15:20 +08:00
caoxiaozhu
b8915a29c0 chore(storage): 归档用户报销票据附件 2026-06-17 14:39:41 +08:00
caoxiaozhu
4199feb681 test: 同步报销审批流与预算分析测试
- 新增预算审批合并、风险标记去重与占位条目校验用例
- 补充预算分析对当前审核人与财务的可见性断言
- 调整单据删除权限测试以匹配 admin 限制
2026-06-17 14:39:26 +08:00
caoxiaozhu
0fac8b615f feat(web): 优化差旅详情、风险建议卡片与文档中心交互
- 拆分阶段风险建议卡片样式到独立文件
- 完善差旅申请审批对话框与详情视图交互
- 调整文档中心列表共享样式与状态筛选
- 同步应用外壳、视图初始化与系统状态 composables
2026-06-17 14:39:12 +08:00
caoxiaozhu
a3e5295915 feat(rules): 更新财务差旅与通信费用规则表 2026-06-17 14:38:51 +08:00
caoxiaozhu
1f4681f486 feat(claim): 重构报销审批流并收敛风险标记
- 直属领导兼任部门 P8 预算审批人时合并预算审批,直接流转至财务审批
- 预算超过警戒值时强制要求预算管理者填写审批意见
- 新增风险标记去重工具,消除各审核阶段重复风险卡片
- 新增工作流修复 Mixin,纠正重复预算审批阶段的历史数据
- 收紧单据删除权限至 admin,放宽预算分析可见范围至当前审核人
- 提交校验放宽已上传票据条目的 OCR 字段缺失并忽略尾部占位条目
2026-06-17 14:38:07 +08:00
caoxiaozhu
09a66c72cb chore: 将 web 端口由 5173 调整为 5273 2026-06-17 14:37:50 +08:00
caoxiaozhu
0d525fa64c chore: 忽略 .codex-temp 工具临时目录 2026-06-17 14:34:36 +08:00
caoxiaozhu
470f343b29 fix(expense): narrow travel route risk indicators 2026-06-17 09:36:24 +08:00
caoxiaozhu
9f7b8b46a3 Refine travel reimbursement steward flow
Align planner, runtime rules, and policy assets so travel guidance
matches the updated reimbursement workflow.
2026-06-15 22:55:18 +08:00
caoxiaozhu
792741709a fix(claim): align risk advice with expense rows 2026-06-15 20:53:48 +08:00
caoxiaozhu
5747e85acf fix(risk): restore upload-time rule center review 2026-06-15 20:20:55 +08:00
Codex
8b952c9a26 refactor(travel): split reimbursement create workflow
完整修改内容:

- 拆分 TravelReimbursementCreateView:提取审核面板纯模型、消息操作、建议动作处理、生命周期 watcher/UI 映射、小财管家运行时、续办流程和运行时文本模型,减少主组件继续堆叠业务分支。
- 调整申请预览链路:新增本地申请意图 gate,完善复杂差旅申请的大模型复核判断、交通方式缺失/候选识别、规则中心交通费用预估合并和申请冲突处理。
- 优化小财管家流程:抽出 steward typewriter 分段策略,避免 Markdown 表格逐字闪烁;补齐跨助手 carry、字段补齐续办、文本确认提交和行程规划推荐动作。
- 调整消息与样式:移除申请预览日期 chip 样式,收敛申请卡片/报销草稿消息的展示与复制、朗读、反馈入口逻辑。
- 更新测试:将源码锚点迁移到新模块,覆盖申请预览、提交确认、小财管家续办、引导流和审核抽屉相关断言。

验证:

- node --check web/src/views/scripts/TravelReimbursementCreateView.js 及新增拆分模块
- npm --prefix web run build
- node --test web/tests/expense-application-fast-preview.test.mjs web/tests/expense-application-submit-rich-confirm.test.mjs web/tests/travel-reimbursement-guided-flow.test.mjs

说明:

- 后端/规则/容器配置/Audit 页面等工作区已有改动未纳入本提交。
- 容器内后端定向 pytest 曾运行 timeout 180s /tmp/x-financial-server-venv/bin/pytest -q <相关后端测试>,180 秒超时且超时前已有失败标记,未作为通过项记录。
- TravelReimbursementCreateView 当前仍超过 800 行,后续仍需继续拆分;本提交先把新增职责模块控制在 800 行内,阻止主类/主模块继续膨胀。
2026-06-13 14:53:23 +00:00
336fee9d93 chore: 忽略 .superpowers 工具缓存目录 2026-06-12 09:43:53 +08:00
caoxiaozhu
25724c354f feat: 同步报销流程与工作台改动 2026-06-09 08:32:00 +00:00
caoxiaozhu
e124e4bbcb feat: 报销审批流重构与管家计划全链路贯通
- 重构报销状态注册表、审批流路由与平台风险标记
- 完善管家意图规划器与模型计划构建器全链路
- 新增 OCR Worker 脚本、数据库会话管理与通知状态
- 优化文档中心、日志视图、预算中心与员工管理交互
- 增强工作台摘要、图标资源与全局主题样式
- 补充审批路由、状态注册、OCR 服务与管家规划器测试覆盖
2026-06-06 17:19:07 +08:00
caoxiaozhu
f60cebadb8 feat: 小财管家意图规划与报销提交编排增强
- 完善管家意图识别、模型计划构建与规划器调度
- 重构差旅报销提交编排器与管家计划流程前端交互
- 优化报销消息项样式与文档中心视图
- 新增小财管家与附件上传风险前置复核设计文档
- 补充管家规划器与文档中心测试覆盖
2026-06-04 14:25:14 +08:00
caoxiaozhu
1cbf3fee44 feat: 报销预审会话状态管理与工作台交互增强
- 新增差旅报销会话状态管理与对话模型重构
- 增强风险观测服务与运行时聊天上下文作用域
- 优化工作台图标资源、助理意图识别与摘要工具
- 完善报销创建视图样式与差旅详情页标准调整交互
- 补充风险观测、运行时聊天与报销端点测试覆盖
2026-06-04 11:03:29 +08:00
caoxiaozhu
87da5df91b feat: 风险可见性控制与差旅详情页交互优化
- 新增风险可见性工具函数与风险日趋势图表组件
- 优化差旅请求详情页费用模型与视图交互
- 完善顶部导航栏样式与应用壳路由逻辑
- 补充风险可见性、风险看板与差旅详情测试覆盖
2026-06-03 22:15:45 +08:00
caoxiaozhu
75d5c178e1 feat(workbench): persist topbar notification state 2026-06-03 21:43:35 +08:00
caoxiaozhu
b9826a1985 fix: keep adjusted risks visible to reviewers 2026-06-03 19:14:40 +08:00
caoxiaozhu
0f8bc4071a fix: preserve reviewer risk notice after standard adjustment 2026-06-03 19:10:29 +08:00
caoxiaozhu
cb36d78fa2 fix: 优化顶部导航栏布局与工作台摘要展示并清理旧票据数据 2026-06-03 17:40:52 +08:00
caoxiaozhu
8e2477587f fix: handle risk explanation standard adjustment 2026-06-03 17:31:40 +08:00
caoxiaozhu
67b81a1bd8 fix(workbench): replay profile radar animation 2026-06-03 17:31:12 +08:00
caoxiaozhu
9c24a852e7 fix(workbench): remount expense stats chart on reopen 2026-06-03 17:22:48 +08:00
caoxiaozhu
95956afbc6 fix(notifications): refine bell notification center 2026-06-03 17:16:09 +08:00
caoxiaozhu
c73178b65d fix(documents): move unread notice into bell 2026-06-03 17:05:34 +08:00
caoxiaozhu
8c2f301d85 fix(documents): sort newest rows first 2026-06-03 16:52:49 +08:00
caoxiaozhu
4717ee6086 fix(documents): refine unread badges and mark all read 2026-06-03 16:46:13 +08:00
caoxiaozhu
513ff909f9 fix: remove manual expense detail add action 2026-06-03 16:44:06 +08:00
caoxiaozhu
92198549f6 fix: require explicit transport mode for applications 2026-06-03 16:36:02 +08:00
caoxiaozhu
59d3bf0f00 fix(auth): keep admin out of personal workbench 2026-06-03 16:31:27 +08:00
caoxiaozhu
04f0951b3d fix: restrict application linking for reimbursement drafts 2026-06-03 16:28:09 +08:00
caoxiaozhu
8887cf5a27 fix(workbench): stretch profile tag card 2026-06-03 15:53:30 +08:00
caoxiaozhu
34457f9c3e feat: 本体字段治理与风险规则模板执行器重构
- 新增本体字段注册表与字段治理审计脚本
- 重构风险规则模板执行器、DSL 验证与清单分类器
- 完善票据夹服务与差旅请求详情页交互
- 优化趋势图表与总览页数据展示
- 增强报销平台风险分级与模拟公司筛选
- 补充本体字段、风险规则生成与票据夹服务测试覆盖
2026-06-03 15:46:56 +08:00
caoxiaozhu
e12b140508 fix(workbench): show single expense distribution chart 2026-06-03 15:46:51 +08:00
caoxiaozhu
18d716bc6b feat(workbench): show expense distribution as donut chart 2026-06-03 15:31:09 +08:00
caoxiaozhu
74d488adfa fix(workbench): center progress expense type 2026-06-03 15:21:38 +08:00
caoxiaozhu
31052d0b98 feat(workbench): keep progress detail return context 2026-06-03 15:14:44 +08:00
caoxiaozhu
20cb60e247 feat(workbench): add expense stats detail modal 2026-06-03 14:59:55 +08:00
caoxiaozhu
3130c42d76 feat(workbench): separate stale progress items 2026-06-03 12:38:17 +08:00
caoxiaozhu
6fc5e66ea1 feat(workbench): show progress update time first 2026-06-03 12:28:21 +08:00
caoxiaozhu
27dd2f0a0d feat(dashboard): reorganize budget and risk cards 2026-06-03 10:47:11 +08:00
caoxiaozhu
faa39e6c06 test(dashboard): cover shared loading overlay 2026-06-03 09:45:06 +08:00
caoxiaozhu
d060f89d30 style(dashboard): reuse shared loading overlay 2026-06-03 09:43:36 +08:00
caoxiaozhu
0d6327a990 feat(dashboard): polish risk and digital employee boards 2026-06-03 09:41:32 +08:00
caoxiaozhu
15006a05a7 feat: 数字员工财务报告体系与定时提醒及看板快照调度
- 新增数字员工财务报告生成、邮件投递与渲染调度器
- 引入员工画像扫描调度与定时提醒任务
- 完善财务看板快照、排行口径与部门人员占比计算
- 优化数字员工工作看板仪表盘与技能目录
- 增强前端总览页图表、工作台摘要与顶部导航栏交互
- 新增差旅申请规划推动提醒与报销创建会话状态管理
- 补充财务报告、看板调度、数字员工工作记录测试覆盖
2026-06-03 09:25:23 +08:00
caoxiaozhu
0c74b4ab4a feat: 财务看板口径重构与半年模拟数据及报销状态注册表
- 重构 finance_dashboard 口径计算,新增模拟公司画像数据生成与筛选
- 引入 expense_claim_status_registry 统一报销状态流转
- 完善报销草稿流程、Item Sync 与本体解析器
- 优化总览页趋势图、分页组件与请求进度步骤
- 增强报销申请快速预览、本体工具与详情展示
- 新增半年报销模拟数据种子脚本与状态审计工具
- 补充财务看板、报销状态注册与模拟数据测试覆盖
2026-06-02 16:22:59 +08:00
caoxiaozhu
ca691f3ee0 feat: 优化差旅报销预审流程与个人工作台 UI 体系
- 完善 user_agent_application 申请差旅报销预审槽位与消息组装
- 增强预算助理报告与风险建议卡片交互
- 重构登录页视觉样式与移动端响应式适配
- 优化个人工作台、文档中心、政策中心、员工管理等页面布局
- 拆分 travelRequestDetailPreReviewModel 为 advice/submit 模型
- 补充报销草稿、风险复核、Item Sync 与模板执行器测试覆盖
2026-06-02 14:01:51 +08:00
caoxiaozhu
92444e7eae feat: 扩展风险规则体系、审批动态路由与预算中心列表化改造
- 新增 25+ 条风险规则(预算/报销/申请/通用类),完善风险规则模拟与反馈发布机制
- 引入费用审批动态路由、平台风险分级、预审与风险阶段管理
- 预算中心列表化改造,优化票据夹仪表盘与数字员工工作看板
- 新增 Hermes 风险线索收集器、Agent 链路追踪中心
- 扩展数字员工能力库(18 个领域 Skill)与交通费用自动预估
- 完善报销申请快速预览、权限控制与前端测试覆盖
2026-06-01 17:07:14 +08:00
caoxiaozhu
7989f3a159 feat: 新增风险图谱算法与系统仪表盘及操作反馈体系
后端新增风险图谱算法模块、风险观察与反馈服务、规则 DSL
校验器和可解释性引擎,完善系统仪表盘和财务仪表盘统计,
优化 agent 运行和编排执行链路,清理旧开发文档,前端新增
系统趋势、负载热力图等多种仪表盘图表组件,完善操作反馈
对话框和工作台日期选择器,优化报销创建和审批详情交互,
补充单元测试覆盖。
2026-05-30 15:46:51 +08:00
caoxiaozhu
4c59941ec6 feat: 新增票据夹模块并优化 OCR 与员工画像服务
后端新增票据夹端点、数据模型和服务模块,优化 OCR 端点
Schema 和附件操作逻辑,完善员工行为画像服务和辅助函数,
前端新增票据夹视图和服务层,优化文档中心样式和侧边栏导
航,完善员工画像详情弹窗和权限控制,补充单元测试。
2026-05-29 14:51:18 +08:00
caoxiaozhu
678f64d772 feat: 统一后端分页查询与前端服务层适配
后端新增通用分页模块,为报销单、员工、预算、agent 资产等
端点统一接入分页参数和游标查询,优化 repository 层分页实
现,前端服务层适配分页响应结构,完善预算图表和全局样式,
优化侧边栏和企业选择器组件,引入 Element Plus 插件注册。
2026-05-29 14:11:06 +08:00
caoxiaozhu
e080105f9f feat(ui): finalize shared shells and loading states 2026-05-29 13:17:39 +08:00
caoxiaozhu
64cc76c970 refactor(audit): reuse list shells and split models 2026-05-29 10:13:49 +08:00
caoxiaozhu
99e90798d2 refactor(audit): split list detail flows 2026-05-29 09:44:03 +08:00
caoxiaozhu
064eeb614f refactor(ui): introduce shared list detail shells 2026-05-28 22:49:58 +08:00
caoxiaozhu
b383244a29 chore: backup workspace before list detail shell refactor 2026-05-28 22:33:53 +08:00
caoxiaozhu
e384318046 feat: 引入 ECharts 统一图表并完善员工画像标签分页
后端优化员工行为画像服务和辅助函数,完善系统设置模型和
配置持久化,前端引入 ECharts 替换所有图表组件实现统一
渲染,新增员工画像标签分页器和数字员工工作记录组件,优
化工作台响应式布局和登录页过渡动画,完善预算中心和数字
员工页面样式细节。
2026-05-28 16:24:59 +08:00
caoxiaozhu
8a4a777be7 feat: 新增员工行为画像算法与费用风险标签体系
后端新增员工行为画像算法模块,支持标签规则引擎和评分计算,
完善员工模型、银行信息、序列化和导入逻辑,优化报销审批流
和工作流常量,增强 Hermes 同步和知识同步能力,前端新增费
用画像详情弹窗、雷达图和风险卡片组件,完善登录页和工作台
样式,优化文档中心和归档中心交互,补充单元测试。
2026-05-28 12:09:49 +08:00
caoxiaozhu
04cd6d0f81 feat: 新增数字员工管理页面与工作台首页重构
后端优化 agent 资产种子初始化和常量配置,前端新增数字员工
视图和调度对话框组件,重构个人工作台首页布局和洞察面板,
完善审计页面数字员工详情和运行时模型,优化侧边栏导航和图
标配置,新增工作台摘要和工作台数据模块,补充单元测试。
2026-05-28 09:30:34 +08:00
caoxiaozhu
d4d5d40569 feat: 新增预算费控模型与报销审批流引擎
后端新增预算费控服务和报销单审批流模块,引入申请人费用画像
算法,优化知识库 RAG 运行时和同步逻辑,完善报销单工作流常
量和明细同步,更新差旅报销规则电子表格,前端新增预算分析
组件和数字员工模型,完善审批对话框和洞察面板交互,优化侧
边栏和顶栏样式,补充单元测试。
2026-05-27 17:31:27 +08:00
caoxiaozhu
cbb98f4469 feat: 完善审批退回流程与报销申请关联
后端优化报销单访问策略和常量定义,增强退回原因和审批状态
流转,前端完善退回对话框和审批交互组件,新增报销申请关联
模型,优化文档中心行数据和审批收件箱工具函数,增强引导
流程和会话模型,补充单元测试覆盖。
2026-05-27 14:35:17 +08:00
caoxiaozhu
7d32eae74e feat: 新增预算助手报告组件并优化报销交互细节
新增预算助手报告视图模型和组件,优化报销洞察面板和消息项
样式细节,完善预算中心页面布局和文档中心视图,增强报销创
建会话管理和提交编排器,调整 Vite 构建配置,补充单元测试。
2026-05-27 12:27:17 +08:00
caoxiaozhu
b1a9c8a194 fix: 优化报销创建页面样式与洞察面板交互
修复侧边栏和审计视图样式细节,完善差旅报销洞察面板和消息
组件布局,优化报销创建页面会话管理和流程状态持久化,增强
申请预览工具函数和导航图标,补充单元测试。
2026-05-27 10:32:08 +08:00
caoxiaozhu
2dcc72102d style: 全局 UI 主题皮肤重构与样式模块化
引入 Element Plus 主题定制和主题皮肤 composable,将全局
样式拆分为组件级独立 CSS 文件(侧边栏、顶栏、工作台等),
统一色彩变量和间距规范,重构所有视图和组件样式以适配新
主题系统,优化图表和知识图谱组件视觉表现,提取审计和差
旅报销相关子组件。
2026-05-27 09:17:57 +08:00
caoxiaozhu
df49103f23 feat: 完善预算中心图表与确认对话框交互
后端预算服务增加汇总查询和辅助计算,前端预算中心优化趋
势图组件和数据展示,增强确认对话框通用性和样式,完善预
算编辑对话框布局,补充预算端点单元测试。
2026-05-26 20:07:56 +08:00
caoxiaozhu
e7bef0883d feat: 新增预算后端服务与差旅风险规则库
后端新增预算模型、端点和服务模块,支持预算 CRUD 和余额
查询,清理旧生成规则文件并替换为按严重等级分类的差旅风
险规则库,优化认证权限和报销单访问策略,新增财务规则目
录和演示数据构建脚本,前端预算中心增加对话框交互,完善
审计页面运行时模型和元数据展示,补充单元测试。
2026-05-26 17:29:35 +08:00
caoxiaozhu
e1e515ecae feat: 新增预算中心本体与风险规则评分回填
后端新增预算本体解析模块和风险规则评分回填服务,优化规则
生成本体对齐和提示词构建,增强费用类型关键词和本体验证,
完善报销查询和审计接口,前端预算中心页面增加对话框和本
体工具函数,重构审计页面元数据和视图模型,补充单元测试。
2026-05-26 12:16:20 +08:00
caoxiaozhu
0e861d8fa6 feat: 增强风险规则生成引擎与预算中心页面
后端拆分风险规则生成为解释器、语义分析、本体对齐等子模块,
优化模板执行和流程图生成,完善员工种子数据和导入逻辑,增强
报销单权限策略和草稿持久化,前端新增预算中心视图和趋势图
组件,重构审计页面和风险规则测试对话框交互,完善文档中心
和报销创建页面细节,补充单元测试覆盖。
2026-05-26 09:15:14 +08:00
caoxiaozhu
d0e946cf47 feat: 完善文档中心与报销申请交互及侧边栏重构
后端优化编排器报销查询和本体检测精度,增强报销单草稿保
存和附件回填逻辑,前端重构侧边栏组件支持折叠和图标导
航,完善文档中心状态筛选和详情提示,报销创建和审批详情
页优化会话管理和费用明细交互,新增助手应用服务和预设动
作工具函数,补充单元测试覆盖。
2026-05-25 13:35:39 +08:00
caoxiaozhu
50b1c3f9a9 feat: 增强规则资产管理与审计页面运行时调试
后端新增规则资产版本管理和规则文件 CRUD 接口,优化风险
规则生成模板执行和员工数据模型字段,知识库 RAG 增强本
地回退和文档提取能力,清理旧风险规则文件统一由生成引擎
管理,前端审计页面增加运行时调试面板和规则资产编辑交互,
补充单元测试覆盖。
2026-05-24 21:44:17 +08:00
caoxiaozhu
575f093c74 feat: 新增风险规则生成引擎与知识图谱可视化
后端新增风险规则自动生成和模板执行服务,支持从规则资产
批量生成并持久化风险规则文件;知识库入库日志增强图谱
查询和本地 RAG 回退,前端审计页面增加风险规则模型和流
程图组件,知识入库面板拆分为图谱可视化子组件,报销创
建页面增加引导式流程模型,更新知识库索引数据。
2026-05-23 19:54:42 +08:00
caoxiaozhu
5b388d08c0 feat: 增强知识库索引与设置页面模块化拆分
扩展知识库索引任务和 RAG 检索支持增量入库和文档去重,优
化本体检测和规则匹配精度,前端设置页面拆分为 LLM、邮件
和 Hermes 员工同步子面板并重构样式,新增日志详情组件和
知识入库日志模型,补充单元测试覆盖。
2026-05-22 23:47:28 +08:00
caoxiaozhu
88ff04bef8 feat: 新增归档中心页面并完善知识库与报销查询能力
新增前端归档中心视图及相关工具函数,扩充知识库文档分类和
提取器支持多种格式,增强编排器报销查询的多维度检索,优
化本体规则和用户代理审核消息,前端完善报销创建和审批详
情交互细节,补充单元测试覆盖。
2026-05-22 16:00:19 +08:00
caoxiaozhu
1f15699013 feat(mobile): track mobile app scaffold 2026-05-22 12:41:45 +08:00
caoxiaozhu
222ba0bfdc refactor(server): split oversized backend services 2026-05-22 10:42:31 +08:00
caoxiaozhu
2e57702638 docs: add agent code size standards 2026-05-22 10:42:19 +08:00
caoxiaozhu
5fe3b201d9 feat: 重构报销单服务并完善前端提交与审核交互
重构 expense_claims 服务模块结构并优化差旅票据审核逻辑,
增强用户代理服务的票据类型识别,前端报销创建页面拆分为
附件模型和会话模型模块,重构提交编排器和草稿关联确认流
程,更新知识库索引,补充单元测试。
2026-05-22 08:58:59 +08:00
1974 changed files with 466854 additions and 75798 deletions

View File

@@ -0,0 +1,160 @@
---
name: agent-change-log
description: Use when working in X-Financial after bug fixes that need a split dev log, when maintaining document/development/YYYY-MM-DD/dev-logs/bugs, or when generating the daily 17:00 combined work-logs.med from same-day feature docs and bug logs.
---
# Agent Change Log
## Overview
This skill keeps split development logs for X-Financial. Bug fixes are recorded under the same-day `dev-logs/bugs` folder. Daily summaries combine same-day feature documents and bug records into one `work-logs.med`.
The log should sound like a careful teammate writing for tomorrow's teammate: concrete, warm, and honest.
## When To Use
- After fixing a bug.
- When a post-commit hook detects a bug-like commit.
- At the daily 17:00 summary pass.
- Before updating the log in a branch where other agents may have pushed commits.
- After verification, so the entry can include what was actually checked.
- When a failed attempt changed files, generated artifacts, or revealed a risk worth preserving.
Do not create legacy `document/work-log/YYYY-MM-DD.md` entries for new work.
## Log Location
Use the same date root as development documents:
```text
document/development/YYYY-MM-DD/
├── feature/<feature-point>/CONCEPT.md + TODO.md
├── dev-logs/bugs/<bug-slug>.md
└── work-logs.med
```
If the date folder exists, reuse it. If not, create it.
## Bug Log Structure
For bug fixes, create or update one file per bug:
```text
document/development/YYYY-MM-DD/dev-logs/bugs/<bug-slug>.md
```
The file must contain `## 修复记录` and timestamped bullets similar to the old `当日工作内容`.
Do not add `遗留问题` or `TODO` sections to bug logs.
Use the helper when possible:
```bash
python3 tools/agent-change-log/update_change_log.py \
--kind bug \
--bug-title "<bug 名称>" \
--bug-slug <bug-slug>
```
## Required Git Check
Before writing or updating a bug log, manually check Git for upstream and local-ahead commits from other agents.
Run:
```bash
date '+%Y-%m-%d %H:%M:%S %Z'
git fetch --all --prune
git status -sb
git rev-parse --abbrev-ref --symbolic-full-name @{u}
git log --oneline --decorate --max-count=20 HEAD..@{u}
git log --oneline --decorate --max-count=20 @{u}..HEAD
```
Rules:
- Treat `git fetch --all --prune` as the default safe "pull check"; it updates remote refs without merging into a dirty worktree.
- Treat `HEAD..@{u}` as upstream commits not yet in local history.
- Treat `@{u}..HEAD` as local commits not yet in upstream history; these may also come from another agent working in the same checkout.
- If the worktree is clean and the branch is only behind upstream, `git pull --ff-only` may be used to fast-forward before analysis.
- If the worktree is dirty, diverged, or likely to conflict, do not merge/rebase automatically. Record the upstream commits from `HEAD..@{u}` in the bug fix entry.
- If there is no upstream branch, record that fact in the bug fix entry and continue with local-only logging.
- When `HEAD..@{u}` has commits, summarize those commits in the bug fix entry before describing local edits. Mention commit hash, subject, and inferred impact.
- When `@{u}..HEAD` has commits that were not created in the current task, summarize them too, because another local agent may have committed without pushing yet.
- When no upstream or local-ahead commits exist, still record "Git 提交检查:未发现 upstream 新提交或本地 ahead 新提交" in the work entry.
## Bug Entry Rules
1. Get the current local time first:
```bash
date '+%Y-%m-%d %H:%M:%S %Z'
```
2. Run the required Git check and capture whether upstream or local-ahead has new commits.
3. Ensure `document/development/YYYY-MM-DD/dev-logs/bugs/` exists.
4. Create or update one `<bug-slug>.md` file for the specific bug.
5. Append a new timestamped bullet under `## 修复记录`.
6. Mention Git commits, changed files or modules, the operation, the intent, and the verification result.
7. Do not add leftover issue or TODO sections.
## Daily Summary
At 17:00 every day, generate the combined work log:
```bash
python3 tools/agent-change-log/update_change_log.py --kind summary
```
This reads:
```text
document/development/YYYY-MM-DD/feature/
document/development/YYYY-MM-DD/dev-logs/bugs/
```
Then writes:
```text
document/development/YYYY-MM-DD/work-logs.med
```
The summary should cover:
- Today's feature points from `feature/*/CONCEPT.md` and `TODO.md`.
- Today's bug fixes from `dev-logs/bugs/*.md`.
- A concise combined analysis of what changed that day.
## Entry Rules
- For each bug entry, append a new timestamped bullet under `## 修复记录`.
- Mention Git commits, changed files or modules, the operation, the intent, and the verification result.
- Do not write `遗留问题`.
- Do not write `TODO`.
- If the change is a feature rather than a bug, use the development document skill to keep `feature/<feature-point>/CONCEPT.md` and `TODO.md` current instead of writing a bug log.
## Writing Style
- Write in Simplified Chinese.
- Be specific and a little human: "我把...", "这次先...", "还需要留意..." are good.
- Keep the tone factual. Do not turn the log into a victory lap.
- Prefer concise file names and module names in prose, but include enough context to find the change.
- Work content should be detailed enough that a future agent can continue without asking "你到底改了啥?"
## Bug Entry Template
```markdown
- HH:MM记录 bug 修复:<bug 名称>。
- Git 提交检查:<git fetch 后 HEAD..upstream 与 upstream..HEAD 的结果;没有就写未发现 upstream 或本地 ahead 新提交>。
- 修改:<文件/模块><做了什么>。
- 操作:<运行了什么命令、迁移了什么状态、重启了什么服务等>。
- 验证:<测试/构建/检查结果;如果没跑,说明原因>。
- 影响:<用户可见变化或工程边界变化>。
```
## Final Response Checklist
Before saying work is complete:
- For bug fixes, today's bug log exists under `document/development/YYYY-MM-DD/dev-logs/bugs/`.
- For non-bug feature work, relevant `feature/<feature-point>` documents are current.
- Git check ran for bug logs, and upstream plus local-ahead commits were summarized or explicitly marked as absent.
- No new legacy `document/work-log/YYYY-MM-DD.md` entry was created.
- The final response mentions whether a bug log, feature document, or daily `work-logs.med` was updated.

View File

@@ -0,0 +1,4 @@
interface:
display_name: "Agent Change Log"
short_description: "Record bug logs and daily work summaries"
default_prompt: "Use $agent-change-log after an X-Financial bug fix or at 17:00 to update document/development/<date>/dev-logs/bugs or work-logs.med."

View File

@@ -0,0 +1,85 @@
---
name: git-checkpoint-commit
description: Use when a coding task finishes a verified bug fix, key feature, risky refactor checkpoint, local backup commit, checkpoint commit, 提交备份, 本地提交, or when an active session has accumulated about five meaningful edit rounds and should create a scoped local git commit without pushing.
---
# Git Checkpoint Commit
## Overview
Use this skill to keep a local git backup loop during active development. The goal is to commit verified, task-scoped progress at meaningful checkpoints without mixing unrelated user changes or pushing remotely.
## Trigger Rules
Create a local checkpoint commit when any condition is true:
- A bug fix is implemented and the targeted regression check passes.
- A key feature slice is implemented and the smallest relevant verification passes.
- A risky refactor reaches a behavior-preserving checkpoint.
- The current session has reached about five meaningful edit rounds since the last commit.
- The user asks for `提交`, `本地提交`, `备份`, `checkpoint`, `commit`, or `形成提交循环`.
Do not use this skill when the user explicitly says not to commit, when the change is exploratory and unverified, or when the only available commit would include unrelated dirty files.
## Workflow
1. Inspect the working tree with `git status --short`.
2. Identify files or hunks owned by the current task.
3. Run the smallest relevant verification first.
- In X-Financial, run backend tests inside `x-financial-main` when backend code is involved.
- Use targeted frontend tests/builds for web-only changes.
4. Commit only the task-owned files.
5. Report the commit hash and verification evidence.
6. Reset the session edit counter to zero after a successful checkpoint.
## Safety Rules
- Never commit unrelated user changes just to make the tree clean.
- Never push as part of this skill.
- Never rewrite history, amend old commits, or run destructive git commands.
- If the same file contains unrelated hunks, split the staging carefully with `git add -p` or a narrower manual patch.
- If the index already contains staged changes, inspect them first; do not mix them into a checkpoint unless they belong to the same current task.
- If verification cannot run, say why in the commit body or final report and prefer a `chore(checkpoint)` message rather than a confident `fix` or `feat`.
## Commit Style
Use normal semantic messages for completed, verified work:
- `fix(workbench): keep application preview after draft save failure`
- `feat(reimbursements): add attachment association job polling`
- `refactor(claims): split draft flow serialization`
Use checkpoint messages for interim backup points:
- `chore(checkpoint): backup attachment association flow`
- `chore(checkpoint): backup after five edit rounds`
Keep the subject concise. Add body lines only when the verification state or scope needs clarity.
## Helper Script
Use `scripts/checkpoint_commit.py` when a path-scoped local commit is enough:
```bash
python3 .codex/skills/git-checkpoint-commit/scripts/checkpoint_commit.py \
--message "chore(checkpoint): backup workbench AI flow" \
web/src/composables/workbenchAiMode/useWorkbenchAiExpenseFlow.js \
web/tests/workbench-ai-mode-switch.test.mjs
```
Preview before committing:
```bash
python3 .codex/skills/git-checkpoint-commit/scripts/checkpoint_commit.py \
--dry-run \
web/src/utils/expenseApplicationPreview.js
```
The script refuses to commit the whole tree unless `--allow-all` is passed. Use `--allow-all` only when the current task truly owns every dirty file.
## Common Mistakes
- Mistaking backup for verification. Verify first, then commit.
- Staging `.` in a dirty worktree. Stage explicit paths or hunks.
- Combining documentation cleanup, feature work, and unrelated local edits in one checkpoint.
- Continuing indefinitely after multiple verified slices. Commit once the counter reaches five meaningful edit rounds.

View File

@@ -0,0 +1,4 @@
interface:
display_name: "Git Checkpoint Commit"
short_description: "Create local backup commits at safe milestones"
default_prompt: "Use $git-checkpoint-commit to checkpoint verified task changes into a local git commit."

View File

@@ -0,0 +1,127 @@
#!/usr/bin/env python3
"""Create a path-scoped local git checkpoint commit."""
from __future__ import annotations
import argparse
import subprocess
import sys
from pathlib import Path
def run_git(args: list[str], cwd: Path, *, check: bool = True) -> subprocess.CompletedProcess[str]:
result = subprocess.run(
["git", *args],
cwd=cwd,
text=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
timeout=60,
)
if check and result.returncode != 0:
message = result.stderr.strip() or result.stdout.strip()
raise SystemExit(f"git {' '.join(args)} failed: {message}")
return result
def repo_root() -> Path:
result = subprocess.run(
["git", "rev-parse", "--show-toplevel"],
text=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
timeout=60,
)
if result.returncode != 0:
raise SystemExit("Not inside a git repository.")
return Path(result.stdout.strip())
def load_paths(args: argparse.Namespace) -> list[str]:
paths = list(args.paths)
if args.paths_from_file:
raw_lines = Path(args.paths_from_file).read_text(encoding="utf-8").splitlines()
paths.extend(line.strip() for line in raw_lines if line.strip() and not line.startswith("#"))
return paths
def ensure_clean_index(root: Path) -> None:
staged = run_git(["diff", "--cached", "--name-only"], root).stdout.strip()
if staged:
raise SystemExit(
"Refusing to continue because the index already has staged changes:\n"
f"{staged}\n"
"Commit or unstage them before running this checkpoint helper."
)
def status_for(root: Path, paths: list[str], allow_all: bool) -> str:
command = ["status", "--short"]
if not allow_all:
command.extend(["--", *paths])
return run_git(command, root).stdout.strip()
def infer_message(paths: list[str], allow_all: bool) -> str:
if allow_all or not paths:
return "chore(checkpoint): backup current task changes"
first_parts = [Path(item).parts[0] for item in paths if Path(item).parts]
scope = first_parts[0] if first_parts else "task"
return f"chore(checkpoint): backup {scope} changes"
def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("paths", nargs="*", help="Task-owned paths to stage and commit.")
parser.add_argument("-m", "--message", help="Commit message subject/body.")
parser.add_argument("--paths-from-file", help="Read additional pathspecs from a UTF-8 text file.")
parser.add_argument("--dry-run", action="store_true", help="Show matching changes without staging.")
parser.add_argument("--allow-all", action="store_true", help="Allow committing all dirty files.")
parser.add_argument("--no-verify", action="store_true", help="Pass --no-verify to git commit.")
return parser.parse_args()
def main() -> int:
args = parse_args()
root = repo_root()
paths = load_paths(args)
if not paths and not args.allow_all:
raise SystemExit("Pass explicit paths, or use --allow-all when the current task owns all changes.")
current_status = status_for(root, paths, args.allow_all)
if not current_status:
print("No matching changes to commit.")
return 0
print(current_status)
if args.dry_run:
return 0
ensure_clean_index(root)
# 使用 -A 保留删除/重命名等变更,但只作用于明确传入的 pathspec。
if args.allow_all:
run_git(["add", "-A"], root)
else:
run_git(["add", "-A", "--", *paths], root)
staged_summary = run_git(["diff", "--cached", "--name-status"], root).stdout.strip()
if not staged_summary:
raise SystemExit("No staged changes after git add.")
message = args.message or infer_message(paths, args.allow_all)
commit_command = ["commit"]
if args.no_verify:
commit_command.append("--no-verify")
commit_command.extend(["-m", message])
commit_result = run_git(commit_command, root)
sys.stdout.write(commit_result.stdout)
commit_hash = run_git(["rev-parse", "--short", "HEAD"], root).stdout.strip()
print(f"checkpoint_commit={commit_hash}")
return 0
if __name__ == "__main__":
raise SystemExit(main())

View File

@@ -0,0 +1,125 @@
---
name: write-development-docs
description: Use when working in X-Financial and the user asks to 落文档, 写开发文档, 沉淀方案, 补 concept/todo, or create/update planning documentation under document/development for a feature, refactor, page, algorithm, rule, or business capability.
---
# Write Development Docs
## 目标
把一个功能、重构、算法、页面或业务能力沉淀为项目标准开发文档。
默认落点固定为:
```text
document/development/<YYYY-MM-DD>/feature/<具体功能点目录>/
├── CONCEPT.md
└── TODO.md
```
如果用户说 `concept.md` / `todo.md`,也按仓库现有规范使用大写文件名
`CONCEPT.md``TODO.md`
## 工作流
1. 先读取当前日期,默认使用本地日期 `YYYY-MM-DD` 作为第一层目录。
2. 先阅读 `document/development` 中 2-3 组相邻或同类样例。
3. 再读取本次能力相关的代码、接口、页面、测试或历史文档。
4. 创建或更新 `CONCEPT.md`,先写清业务边界,再写方案和验收。
5. 创建或更新 `TODO.md`,每个任务都要回链到 `CONCEPT.md` 的章节。
6. 已实现或已验证的 TODO 可以勾选 `[x]`,但必须写证据。
7. 交付前检查两份文档互相一致,不额外创建 README、CHANGELOG、SUMMARY。
## 路径规则
- 第一层必须是日期目录,例如 `document/development/2026-06-25/`
- 第二层固定是 `feature/`,表示当天沉淀的功能点集合。
- 第三层是具体功能点目录,每个独立功能点一个目录。
- 每个具体功能点目录内只放 `CONCEPT.md``TODO.md` 两个核心文件。
- 如果一次请求包含多个互不依赖的功能点,拆成多个兄弟目录:
```text
document/development/2026-06-25/feature/
├── receipt-folder-ocr/
│ ├── CONCEPT.md
│ └── TODO.md
└── risk-review-nudge/
├── CONCEPT.md
└── TODO.md
```
- 如果用户明确指定日期,使用用户指定日期;否则使用当前本地日期。
- 如果是更新历史文档,先查找已有目录并原地更新,不自动迁移旧路径。
## 目录命名
- 优先复用用户指定目录名或已有目录名。
- 新增英文目录用小写 kebab-case例如 `receipt-folder`
- 新增中文目录可直接用清晰中文能力名,例如 `费用审批动态路由`
- 同一能力已有目录时更新原目录,不新建近义目录。
## CONCEPT.md 要求
可参考 `assets/CONCEPT.md` 模板。必须包含:
- 标题:`# <功能名> 概念文档`
- `更新时间YYYY-MM-DD`
- `## 功能一句话`
- `## 背景与问题`
- `## 目标与非目标`
- `## 用户与场景`
- `## 功能能力`
- `## 方案设计`
- `## 算法与公式`
- `## 测试方案`
- `## 指标与验收`
- `## 风险与开放问题`
写法要求:
- 先讲业务问题和边界,再讲技术方案。
- 目标与非目标分开写,避免需求无限扩张。
- 方案设计按前端、后端、算法/规则、数据、权限、降级策略分块;
不涉及的块明确写“当前不涉及”。
- 算法与公式必须明确“不涉及”或写出公式、变量说明和适用边界。
- 验收标准必须可验证,不写空泛口号。
## TODO.md 要求
可参考 `assets/TODO.md` 模板。必须包含:
- 标题:`# <功能名> 开发 TODO`
- `更新时间YYYY-MM-DD`
- `## 使用规则`
- 分阶段 checklist
TODO 条目规则:
- 每条用 `- [ ]``- [x]`
- 每条必须包含 `[CONCEPT: <章节名>]`
- 已完成项必须补证据,格式为 `证据:<文件、接口、命令或验证结果>`
- 没有真实证据时不得勾选 `[x]`
建议阶段:
- `## 1. 调研与边界`
- `## 2. 契约与设计`
- `## 3. 后端实现`
- `## 4. 算法/规则实现`
- `## 5. 前端实现`
- `## 6. 测试与验证`
- `## 7. 文档收尾`
## 更新既有文档
- 先读现有 `CONCEPT.md``TODO.md` 全文。
- 新需求先补 `CONCEPT.md`,再补 `TODO.md`
- 实现变化时同步更新“非目标”“风险与开放问题”“本轮实现记录”。
- 不删除历史证据;除非证据明显错误,才替换为新证据。
## 验收检查
- 新建文档路径符合 `document/development/<YYYY-MM-DD>/feature/<具体功能点目录>/`
- `CONCEPT.md``TODO.md` 都存在于同一个具体功能点目录。
- TODO 的 `[CONCEPT: ...]` 都能在 CONCEPT 中找到对应章节或语义段落。
- 已勾选项都有证据。
- 文档没有遗留模板占位符,例如 `<功能名>``<YYYY-MM-DD>``待补充`

View File

@@ -0,0 +1,4 @@
interface:
display_name: "开发文档落地"
short_description: "按日期和功能点生成 CONCEPT/TODO"
default_prompt: "Use $write-development-docs to create CONCEPT.md and TODO.md under document/development/<date>/feature/<feature-point> for an X-Financial feature."

View File

@@ -0,0 +1,132 @@
# <功能名> 概念文档
更新时间:<YYYY-MM-DD>
文档路径document/development/<YYYY-MM-DD>/feature/<具体功能点目录>/CONCEPT.md
## 功能一句话
用一句话说明这个能力解决什么问题、服务谁、交付什么结果。
## 背景与问题
- 当前现状:
- 用户痛点:
- 业务影响:
- 为什么现在需要做:
## 目标与非目标
### 目标
- [G1]
- [G2]
- [G3]
### 非目标
- [NG1] 本轮不做:
- [NG2] 本轮不改变:
- [NG3] 后续再评估:
## 用户与场景
- 目标用户:
- 使用入口:
- 核心场景:
1.
2.
3.
- 异常场景:
-
## 功能能力
- [C1] 输入能力:
- [C2] 处理能力:
- [C3] 输出能力:
- [C4] 状态与权限:
- [C5] 边界与降级:
## 方案设计
### 前端
- 页面/组件:
- 交互状态:
- 展示规则:
- 降级处理:
### 后端
- 接口/服务:
- 权限与校验:
- 持久化:
- 降级处理:
### 算法与规则
- 输入:
- 流程:
- 输出:
- 解释:
### 数据与契约
- 核心字段:
- 状态枚举:
- 兼容策略:
- 版本/审计:
## 算法与公式
当前功能不涉及显式数学公式。
如涉及公式,使用如下格式:
```text
metric = input_a + input_b
```
变量说明:
- metric
- input_a
- input_b
## 测试方案
后端:
-
前端:
-
集成:
-
手工验证:
-
## 指标与验收
- [A1] 功能验收:
- [A2] 性能指标:
- [A3] 质量指标:
- [A4] 安全/权限指标:
- [A5] 可观测性:
## 风险与开放问题
- 风险:
- 已处理依赖:
- 待确认:
- 降级策略:
## 本轮实现记录
-

View File

@@ -0,0 +1,73 @@
# <功能名> 开发 TODO
更新时间:<YYYY-MM-DD>
文档路径document/development/<YYYY-MM-DD>/feature/<具体功能点目录>/TODO.md
## 使用规则
- 每个 TODO 必须对应 `CONCEPT.md` 中的目标、能力、方案或验收点。
- 只有完成并验证后,才能把 `[ ]` 改成 `[x]`
- 勾选时在任务后补充简短证据,例如文件、接口、命令或验证结果。
- 如果需求发生变化,先更新 `CONCEPT.md`,再调整本 TODO。
## 1. 调研与边界
- [ ] [CONCEPT: 背景与问题] 阅读相关页面、接口、服务、测试和历史文档,记录当前实现事实。
证据:
- [ ] [CONCEPT: 目标与非目标] 确认本轮开发范围,写清楚不做项。
证据:
- [ ] [CONCEPT: 风险与开放问题] 标记无法立即确认的依赖、风险和假设。
证据:
## 2. 契约与设计
- [ ] [CONCEPT: 功能能力] 定义输入、输出、状态、权限和边界条件。
证据:
- [ ] [CONCEPT: 方案设计] 明确前端、后端、算法、数据的职责边界。
证据:
- [ ] [CONCEPT: 算法与公式] 补全公式、变量解释或明确当前不涉及公式。
证据:
- [ ] [CONCEPT: 指标与验收] 把验收标准转成可验证的检查点。
证据:
## 3. 后端实现
- [ ] [CONCEPT: 后端] 新增或调整 schema、service、endpoint、权限和持久化逻辑。
证据:
- [ ] [CONCEPT: 数据与契约] 保持响应结构、状态枚举和兼容策略清晰。
证据:
## 4. 算法/规则实现
- [ ] [CONCEPT: 算法与规则] 实现核心处理流程、规则判断或计算逻辑。
证据:
- [ ] [CONCEPT: 结果解释] 输出可读解释、证据链、贡献项或降级原因。
证据:
## 5. 前端实现
- [ ] [CONCEPT: 前端] 新增或调整页面、组件、服务 API 和视图模型。
证据:
- [ ] [CONCEPT: 前端] 实现加载、空态、错误态、权限态和刷新。
证据:
- [ ] [CONCEPT: 前端] 对齐现有企业后台风格,避免营销页或花哨卡片感。
证据:
## 6. 测试与验证
- [ ] [CONCEPT: 测试方案] 补充后端 service/API 定向测试,容器内运行,超时控制在 60s 内。
证据:
- [ ] [CONCEPT: 测试方案] 补充前端视图模型、路由、组件或构建验证。
证据:
- [ ] [CONCEPT: 指标与验收] 记录验证命令、结果和未覆盖风险。
证据:
## 7. 文档收尾
- [ ] [CONCEPT: 指标与验收] 回看所有验收点,确认均有实现或验证证据。
证据:
- [ ] [CONCEPT: 风险与开放问题] 更新剩余风险、后续任务和明确不做项。
证据:
- [ ] [CONCEPT: 功能一句话] 确认最终实现没有偏离原始目标。
证据:

51
.env
View File

@@ -1,51 +0,0 @@
APP_NAME=X-Financial
APP_ENV=local
APP_DEBUG=true
API_V1_PREFIX=/api/v1
SETUP_COMPLETED=true
VITE_SETUP_COMPLETED=true
COMPANY_NAME=YGSOFT
COMPANY_CODE=123
ADMIN_EMAIL='admin@admin.com'
VITE_COMPANY_NAME=YGSOFT
VITE_COMPANY_CODE=123
VITE_ADMIN_EMAIL='admin@admin.com'
# Admin login credentials are stored separately under server/.secrets/
WEB_HOST=10.10.10.122
WEB_PORT=5173
VITE_WEB_HOST=10.10.10.122
VITE_WEB_PORT=5173
SERVER_HOST=0.0.0.0
SERVER_PORT=8000
VITE_SERVER_HOST=0.0.0.0
VITE_SERVER_PORT=8000
SERVER_STARTUP_TIMEOUT=300
SERVER_BLOCKING_STARTUP_TIMEOUT=12
VITE_API_BASE_URL=/api/v1
VITE_AUTH_IDLE_TIMEOUT_MINUTES=30
ONLYOFFICE_ENABLED=true
ONLYOFFICE_PUBLIC_URL=http://10.10.10.122:8082
ONLYOFFICE_BACKEND_URL=http://main:8000
ONLYOFFICE_JWT_SECRET=change-me-onlyoffice
HERMES_AGENT_SHARED_TOKEN=change-me-hermes
POSTGRES_HOST=10.10.10.189
POSTGRES_PORT=5432
POSTGRES_DB=postgres
POSTGRES_USER=root
POSTGRES_PASSWORD=8811614287327Leo
VITE_POSTGRES_HOST=10.10.10.189
VITE_POSTGRES_PORT=5432
VITE_POSTGRES_DB=postgres
VITE_POSTGRES_USER=root
DATABASE_URL='postgresql+psycopg://root:8811614287327Leo@10.10.10.189:5432/postgres'
SQLALCHEMY_ECHO=false
REDIS_URL=
VITE_REDIS_URL=
CORS_ORIGINS='["http://10.10.10.122:5173"]'

View File

@@ -31,6 +31,7 @@ ONLYOFFICE_PUBLIC_URL=http://127.0.0.1:8082
ONLYOFFICE_BACKEND_URL=http://127.0.0.1:8000 ONLYOFFICE_BACKEND_URL=http://127.0.0.1:8000
ONLYOFFICE_JWT_SECRET=change-me-onlyoffice ONLYOFFICE_JWT_SECRET=change-me-onlyoffice
HERMES_AGENT_SHARED_TOKEN=change-me-hermes HERMES_AGENT_SHARED_TOKEN=change-me-hermes
STEWARD_AGENT_RUNTIME=langgraph
POSTGRES_HOST=127.0.0.1 POSTGRES_HOST=127.0.0.1
POSTGRES_PORT=5432 POSTGRES_PORT=5432
@@ -48,4 +49,8 @@ SQLALCHEMY_ECHO=false
REDIS_URL= REDIS_URL=
VITE_REDIS_URL= VITE_REDIS_URL=
OCR_DEVICE=
OCR_TIMEOUT_SECONDS=180
OCR_MAX_CONCURRENT_WORKERS=1
CORS_ORIGINS='["http://127.0.0.1:5173","http://localhost:5173","http://0.0.0.0:5173"]' CORS_ORIGINS='["http://127.0.0.1:5173","http://localhost:5173","http://0.0.0.0:5173"]'

10
.githooks/post-commit Executable file
View File

@@ -0,0 +1,10 @@
#!/bin/sh
# Auto-append a minimal X-Financial agent work-log entry after each commit.
repo_root="$(git rev-parse --show-toplevel 2>/dev/null)" || exit 0
cd "$repo_root" || exit 0
python3 tools/agent-change-log/update_change_log.py \
--kind auto \
--event "post-commit hook" \
>/tmp/x-financial-agent-change-log-hook.log 2>&1 || true

29
.gitignore vendored
View File

@@ -7,7 +7,18 @@ web/.vite/
.omc/ .omc/
.omx/ .omx/
.claude/ .claude/
.codex/ *.egg-info/
.codex/*
!.codex/skills/
.codex/skills/*
!.codex/skills/agent-change-log/
!.codex/skills/agent-change-log/**
!.codex/skills/git-checkpoint-commit/
!.codex/skills/git-checkpoint-commit/**
!.codex/skills/write-development-docs/
!.codex/skills/write-development-docs/**
.codex-temp/
.superpowers/
*.log *.log
.DS_Store .DS_Store
Thumbs.db Thumbs.db
@@ -16,3 +27,19 @@ __pycache__/
server/.venv/ server/.venv/
server/.venv-ocr312 server/.venv-ocr312
server/.secrets/ server/.secrets/
server/logs/
server/storage/expense_claims/
server/storage/finance_reports/
server/storage/receipt_folder/
server/storage/knowledge/platform/
server/storage/knowledge/tenants/
test-results/
.codex-remote-attachments/
tmp-*.png
tmp/
.zcode/
.nezha/
.omo/
.env
.env.local
.env.*.local

1
.tmp/Yuxi Submodule

Submodule .tmp/Yuxi added at fd6803e477

65
AGENTS.md Normal file
View File

@@ -0,0 +1,65 @@
# X-Financial Agent 协作规范
## 语言规范
- 所有分析、解释、计划、提交说明和最终回复默认使用简体中文。
- 技术结论要直击重点,必要时给出可验证的文件、命令或测试结果。
## 变更日志 Skill 规范
- 每次修复 bug 后,必须调用项目级 Skill `agent-change-log`,并在 `document/development/YYYY-MM-DD/dev-logs/bugs/<bug-slug>.md` 记录该 bug 的修复内容。
- 新增功能、重构、配置或项目文档变更不再写入旧的 `document/work-log/YYYY-MM-DD.md`;功能点默认沉淀到 `document/development/YYYY-MM-DD/feature/<具体功能点>/CONCEPT.md``TODO.md`
- 写 bug 日志前必须先执行 Git 拉取检查:默认运行 `git fetch --all --prune``git status -sb``git log HEAD..@{u}``git log @{u}..HEAD`。发现其他智能体已提交到上游或本地 ahead 提交时,要把这些提交摘要写进 bug 修复记录。
- 自动化触发由 `tools/agent-change-log/update_change_log.py``.githooks/post-commit` 提供;新 checkout 需要执行 `tools/agent-change-log/install_post_commit_hook.sh` 安装到本地 `.git/hooks/post-commit` 后,提交后才会按 `--kind auto` 自动识别 bug-like commit 并写入 `dev-logs/bugs`
- bug 日志只保留 `## 修复记录`,记录具体时间、改了什么、操作了什么、验证了什么和影响;不再写 `遗留问题``TODO` 两块。
- 每天 17:00 生成当天综合日志:读取 `document/development/YYYY-MM-DD/feature/``document/development/YYYY-MM-DD/dev-logs/bugs/`,分析功能点与 bug 修复后写入 `document/development/YYYY-MM-DD/work-logs.med`
## 通用代码拆分规范
无论写前端、后端还是算法代码,都必须主动避免“所有方法堆在一个类里 / 一个组件里 / 一个模块里”的写法。遇到类、组件或核心模块持续变大时,优先按职责拆分,而不是继续追加方法和状态。
### 行数与复杂度目标
- 单个类、核心组件、核心算法模块硬上限为 800 行。
- 普通文件建议控制在 300-600 行。
- 复杂业务文件可以接近 800 行,但必须有清晰职责边界。
- 文件或类超过 800 行必须视为重构预警,不应继续直接追加功能。
- 单个类不应长期承载几十个无关方法,更不应演化成上百个方法的万能类。
### 拆分原则
- 对外 API 尽量保持稳定,先把内部实现拆到小模块。
- 按职责拆分编排、状态管理、持久化、权限、文件存储、OCR/票据分析、规则审核、响应构建、序列化、UI 交互、算法策略、数据转换。
- 新增能力时先判断归属模块;没有合适归属时新增小模块,不要默认塞回主类、主组件或主 Service。
- 拆分必须小步进行,每次提取一个明确职责,并配套运行相关测试。
### X-Financial 重点关注对象
- `ExpenseClaimService`:优先拆分申请单、明细项、附件、票据分析、草稿、规则审核、权限、序列化。
- `UserAgentService`:优先拆分知识库问答、报销预审 payload、Markdown 回复、差旅政策、表单槽位、票据分类、建议动作。
- `OrchestratorService`:优先拆分 agent 路由、工具调用、报销查询、响应构建。
- 前端大型 Vue 页面:优先拆分 composable、view model、样式分片、业务工具函数和子组件。
- 算法/规则模块:优先拆分输入解析、规则匹配、评分策略、结果解释和异常处理。
## 容器与运行环境(必读)
本项目代码是 Docker 容器 `local-x-financial-linux`(镜像 `x-financial-dev:latest`)的源码映射。
- **容器映射**:宿主机 `D:\Code\Project\X-Financial` ↔ 容器内 `/app``docker-compose.yml``volumes: - .:/app``working_dir: /app`)。
- **后端 venv**:容器内位于 `/tmp/x-financial-server-venv`(环境变量 `SERVER_VENV_DIR`),不要假设宿主机上有相同的 venv。
- **外部依赖**Qdrant`x-financial-qdrant`、OnlyOffice`x-financial-onlyoffice`)也在同一 compose 网络里。
## 验证规范(硬性约束)
> 本项目代码与运行环境以容器为唯一事实来源。所有后端测试、集成测试、依赖了 Qdrant / OnlyOffice / venv 的验证,都必须在 `local-x-financial-linux` 容器内执行,**不要在宿主机上直接跑 pytest / pip / python**。
- **进入容器跑命令**(最常用):
```bash
docker exec -w /app -e SERVER_VENV_DIR=/tmp/x-financial-server-venv local-x-financial-linux <cmd>
```
- 跑后端测试:`docker exec -w /app -e SERVER_VENV_DIR=/tmp/x-financial-server-venv local-x-financial-linux /tmp/x-financial-server-venv/bin/pytest -q <path>`
- 交互式排查:`docker exec -it -w /app local-x-financial-linux bash`(登录后默认已在 `/app`
- **容器不可用时**(未启动、健康检查失败、镜像丢失):先 `docker compose up -d main` 恢复,再继续验证;不要绕开容器在宿主机另装 venv。
- **单元测试设置合理超时**避免长时间卡死。涉及外部服务Qdrant / OnlyOffice / LLM的测试要么 mock要么确认 compose 网络中依赖服务在线。
- **每次重构后至少运行对应服务的定向测试**;涉及公共协议时补充端到端或接口测试。
- **修改 docker-compose / start.sh / venv 路径相关代码**时,自己也要回容器里跑一次确认改动生效,不要只改文件就声称完成。

View File

@@ -1,45 +1,50 @@
# X-Financial # X-Financial
项目结构已按前后端拆开: 项目结构已按前后端拆开:
- `web/`:前端工程(当前 Vue + Vite 项目) - `web/`:前端工程(当前 Vue + Vite 项目)
- `server/`:后端工程目录 - `server/`:后端工程目录
- `docs/`:方案和阶段文档 - `docs/`:方案和阶段文档
- `UI/`:界面参考稿 - `UI/`:界面参考稿
- `document/`:业务文档 - `document/`:业务文档
根目录统一环境变量: 根目录统一环境变量:
- `.env` - `.env`
- `.env.example` - `.env.example`
这里集中维护: 这里集中维护:
- 前端启动端口 - 前端启动端口
- 后端启动端口 - 后端启动端口
- PostgreSQL 连接参数 - PostgreSQL 连接参数
- `DATABASE_URL` - `DATABASE_URL`
- `REDIS_URL` - `REDIS_URL`
从根目录统一启动: 从根目录统一启动:
```bash ```bash
./start.sh ./start.sh
``` ```
可选模式: 可选模式:
```bash ```bash
./start.sh web ./start.sh web
./start.sh server ./start.sh server
./start.sh all ./start.sh all
``` ```
根目录 `start.sh` 是统一编排入口;前端和后端的子启动脚本分别是 `web/web_start.sh``server/server_start.sh` 根目录 `start.sh` 是统一编排入口;前端和后端的子启动脚本分别是 `web/web_start.sh``server/server_start.sh`
手动进入前端目录 Docker Compose 运行方式见 `docker/README.md`
```bash - `docker-compose.yml`只启动主应用容器适合复用已有数据库、ONLYOFFICE 等外部依赖。
cd web - `docker-compose.full.yml`启动主应用、PostgreSQL、Qdrant、ONLYOFFICE 的完整本地开发栈。
npm run dev
``` 手动进入前端目录:
```bash
cd web
npm run dev
```

144
docker-compose.full.yml Normal file
View File

@@ -0,0 +1,144 @@
services:
main:
image: x-financial-dev:latest
container_name: local-x-financial-linux
restart: unless-stopped
depends_on:
postgres:
condition: service_healthy
onlyoffice:
condition: service_started
qdrant:
condition: service_started
environment:
WEB_HOST: 0.0.0.0
WEB_PORT: "${WEB_PORT:-5173}"
SERVER_HOST: 0.0.0.0
SERVER_PORT: "${SERVER_PORT:-8000}"
SERVER_RELOAD: "${SERVER_RELOAD:-true}"
SERVER_VENV_DIR: /tmp/x-financial-server-venv
X_FINANCIAL_PREFER_ENV_FILE: "false"
POSTGRES_HOST: postgres
POSTGRES_PORT: "5432"
POSTGRES_DB: "${POSTGRES_DB:-x_financial}"
POSTGRES_USER: "${POSTGRES_USER:-x_financial}"
POSTGRES_PASSWORD: "${POSTGRES_PASSWORD:-x_financial}"
DATABASE_URL: "postgresql+psycopg://${POSTGRES_USER:-x_financial}:${POSTGRES_PASSWORD:-x_financial}@postgres:5432/${POSTGRES_DB:-x_financial}"
ONLYOFFICE_ENABLED: "true"
ONLYOFFICE_PUBLIC_URL: "${LOCAL_ONLYOFFICE_PUBLIC_URL:-http://127.0.0.1:${ONLYOFFICE_PORT:-8082}}"
ONLYOFFICE_BACKEND_URL: "${LOCAL_ONLYOFFICE_BACKEND_URL:-http://main:${SERVER_PORT:-8000}}"
ONLYOFFICE_JWT_SECRET: "${ONLYOFFICE_JWT_SECRET:-x-financial-onlyoffice-dev-secret}"
QDRANT_URL: "http://qdrant:6333"
LIGHTRAG_WORKSPACE: "x_financial_knowledge"
ports:
- "${WEB_PORT:-5173}:${WEB_PORT:-5173}"
- "${SERVER_PORT:-8000}:${SERVER_PORT:-8000}"
- "2223:22"
volumes:
- .:/app
working_dir: /app
command:
- /bin/sh
- -lc
- >
apt-get update &&
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends
python3 python3-pip python3-venv fontconfig openssh-server poppler-data mupdf-tools &&
if ! fc-match 'Noto Sans CJK SC' | grep -qi 'Noto'; then if ! timeout "${CJK_FONT_INSTALL_TIMEOUT_SECONDS:-45}" sh -lc 'DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends fonts-noto-cjk fonts-noto-cjk-extra'; then printf '%s\n' '[WARN] CJK font installation timed out or failed; continuing startup without blocking the app.'; fi; fi &&
printf '%s\n'
'<?xml version="1.0"?>'
'<!DOCTYPE fontconfig SYSTEM "fonts.dtd">'
'<fontconfig>'
' <alias><family>SimSun</family><prefer><family>Noto Serif CJK SC</family></prefer></alias>'
' <alias><family>NSimSun</family><prefer><family>Noto Serif CJK SC</family></prefer></alias>'
' <alias><family>KaiTi</family><prefer><family>Noto Serif CJK SC</family></prefer></alias>'
' <alias><family>FangSong</family><prefer><family>Noto Serif CJK SC</family></prefer></alias>'
' <alias><family>SimHei</family><prefer><family>Noto Sans CJK SC</family></prefer></alias>'
' <alias><family>DengXian</family><prefer><family>Noto Sans CJK SC</family></prefer></alias>'
' <alias><family>Microsoft YaHei</family><prefer><family>Noto Sans CJK SC</family></prefer></alias>'
'</fontconfig>'
> /etc/fonts/local.conf &&
fc-cache -f &&
mkdir -p /run/sshd && /usr/sbin/sshd &&
printf '%s\n' 'cd /app >/dev/null 2>&1 || true' > /etc/profile.d/zz-x-financial-app-dir.sh &&
chmod 644 /etc/profile.d/zz-x-financial-app-dir.sh &&
touch /root/.bashrc /root/.profile &&
if ! grep -qxF 'cd /app >/dev/null 2>&1 || true' /root/.bashrc; then printf '\ncd /app >/dev/null 2>&1 || true\n' >> /root/.bashrc; fi &&
if ! grep -qxF 'cd /app >/dev/null 2>&1 || true' /root/.profile; then printf '\ncd /app >/dev/null 2>&1 || true\n' >> /root/.profile; fi &&
sed -i 's/\r$//' /app/start.sh /app/web/web_start.sh /app/server/server_start.sh &&
chmod +x /app/start.sh /app/web/web_start.sh /app/server/server_start.sh &&
cd /app &&
./start.sh all
healthcheck:
test: ["CMD-SHELL", "curl -fsS http://127.0.0.1:${WEB_PORT:-5173}/ >/dev/null || exit 1"]
interval: 15s
timeout: 5s
retries: 10
start_period: 180s
networks:
- financial-internal
postgres:
image: pgvector/pgvector:pg17
container_name: x-financial-postgres
restart: unless-stopped
environment:
POSTGRES_DB: "${POSTGRES_DB:-x_financial}"
POSTGRES_USER: "${POSTGRES_USER:-x_financial}"
POSTGRES_PASSWORD: "${POSTGRES_PASSWORD:-x_financial}"
ports:
- "${POSTGRES_HOST_PORT:-55432}:5432"
volumes:
- postgres-data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U \"$${POSTGRES_USER}\" -d \"$${POSTGRES_DB}\""]
interval: 15s
timeout: 5s
retries: 10
start_period: 30s
networks:
- financial-internal
qdrant:
image: qdrant/qdrant:latest
container_name: x-financial-qdrant
restart: unless-stopped
ports:
- "${QDRANT_HTTP_PORT:-6333}:6333"
- "${QDRANT_GRPC_PORT:-6334}:6334"
volumes:
- qdrant-storage:/qdrant/storage
healthcheck:
test: ["CMD-SHELL", "bash -lc 'exec 3<>/dev/tcp/127.0.0.1/6333' || exit 1"]
interval: 15s
timeout: 5s
retries: 10
start_period: 30s
networks:
- financial-internal
onlyoffice:
image: onlyoffice/documentserver:latest
container_name: x-financial-onlyoffice
restart: unless-stopped
environment:
JWT_ENABLED: "true"
JWT_SECRET: "${ONLYOFFICE_JWT_SECRET:-x-financial-onlyoffice-dev-secret}"
ports:
- "${ONLYOFFICE_PORT:-8082}:80"
healthcheck:
test: ["CMD-SHELL", "curl -fsS http://127.0.0.1/healthcheck >/dev/null || exit 1"]
interval: 15s
timeout: 5s
retries: 10
start_period: 60s
networks:
- financial-internal
networks:
financial-internal:
name: financial-internal
volumes:
postgres-data:
qdrant-storage:

8
docker-compose.gpu.yml Normal file
View File

@@ -0,0 +1,8 @@
services:
main:
gpus: all
shm_size: "8gb"
environment:
NVIDIA_VISIBLE_DEVICES: all
NVIDIA_DRIVER_CAPABILITIES: compute,utility
OCR_DEVICE: "${OCR_DEVICE:-gpu:0}"

View File

@@ -0,0 +1,4 @@
# PostgreSQL 已并入默认 docker-compose.yml。
# 保留此空覆盖文件,兼容历史启动命令:
# docker compose -f docker-compose.yml -f docker-compose.postgres.yml up -d
services: {}

View File

@@ -1,38 +1,50 @@
services: services:
main: main:
image: x-financial-dev:latest image: x-financial-dev:latest
container_name: x-financial-main container_name: local-x-financial-linux
restart: unless-stopped restart: unless-stopped
depends_on: depends_on:
onlyoffice: postgres:
condition: service_started condition: service_healthy
qdrant: onlyoffice:
condition: service_started condition: service_healthy
environment: qdrant:
WEB_HOST: 0.0.0.0 condition: service_healthy
SERVER_HOST: 0.0.0.0 environment:
SERVER_VENV_DIR: /tmp/x-financial-server-venv WEB_HOST: 0.0.0.0
X_FINANCIAL_PREFER_ENV_FILE: "true" WEB_PORT: "${WEB_PORT:-5173}"
ONLYOFFICE_ENABLED: "${ONLYOFFICE_ENABLED:-true}" SERVER_HOST: 0.0.0.0
ONLYOFFICE_PUBLIC_URL: "${ONLYOFFICE_PUBLIC_URL:-http://127.0.0.1:${ONLYOFFICE_PORT:-8082}}" SERVER_PORT: "${SERVER_PORT:-8000}"
ONLYOFFICE_BACKEND_URL: "http://main:${SERVER_PORT:-8000}" SERVER_RELOAD: "${SERVER_RELOAD:-true}"
ONLYOFFICE_JWT_SECRET: "${ONLYOFFICE_JWT_SECRET:-x-financial-onlyoffice-dev-secret}" SERVER_VENV_DIR: /tmp/x-financial-server-venv
QDRANT_URL: "http://qdrant:6333" X_FINANCIAL_PREFER_ENV_FILE: "false"
LIGHTRAG_WORKSPACE: "x_financial_knowledge" POSTGRES_HOST: postgres
ports: POSTGRES_PORT: "5432"
- "${WEB_PORT:-5173}:${WEB_PORT:-5173}" POSTGRES_DB: "${LOCAL_POSTGRES_DB:-x_financial}"
- "${SERVER_PORT:-8000}:${SERVER_PORT:-8000}" POSTGRES_USER: "${LOCAL_POSTGRES_USER:-x_financial}"
- "2223:22" POSTGRES_PASSWORD: "${LOCAL_POSTGRES_PASSWORD:-x_financial}"
volumes: DATABASE_URL: "postgresql+psycopg://${LOCAL_POSTGRES_USER:-x_financial}:${LOCAL_POSTGRES_PASSWORD:-x_financial}@postgres:5432/${LOCAL_POSTGRES_DB:-x_financial}"
- .:/app ONLYOFFICE_ENABLED: "true"
working_dir: /app ONLYOFFICE_PUBLIC_URL: "${LOCAL_ONLYOFFICE_PUBLIC_URL:-http://127.0.0.1:${ONLYOFFICE_PORT:-8082}}"
command: ONLYOFFICE_BACKEND_URL: "${LOCAL_ONLYOFFICE_BACKEND_URL:-http://main:${SERVER_PORT:-8000}}"
- /bin/sh ONLYOFFICE_JWT_SECRET: "${ONLYOFFICE_JWT_SECRET:-x-financial-onlyoffice-dev-secret}"
- -lc QDRANT_URL: "http://qdrant:6333"
- > LIGHTRAG_WORKSPACE: "x_financial_knowledge"
ports:
- "${WEB_PORT:-5173}:${WEB_PORT:-5173}"
- "${SERVER_PORT:-8000}:${SERVER_PORT:-8000}"
- "2223:22"
volumes:
- .:/app
working_dir: /app
command:
- /bin/sh
- -lc
- >
apt-get update && apt-get update &&
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends
python3 python3-pip python3-venv fontconfig fonts-noto-cjk fonts-noto-cjk-extra && python3 python3-pip python3-venv fontconfig openssh-server poppler-data mupdf-tools &&
if ! fc-match 'Noto Sans CJK SC' | grep -qi 'Noto'; then if ! timeout "${CJK_FONT_INSTALL_TIMEOUT_SECONDS:-45}" sh -lc 'DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends fonts-noto-cjk fonts-noto-cjk-extra'; then printf '%s\n' '[WARN] CJK font installation timed out or failed; continuing startup without blocking the app.'; fi; fi &&
printf '%s\n' printf '%s\n'
'<?xml version="1.0"?>' '<?xml version="1.0"?>'
'<!DOCTYPE fontconfig SYSTEM "fonts.dtd">' '<!DOCTYPE fontconfig SYSTEM "fonts.dtd">'
@@ -48,63 +60,85 @@ services:
> /etc/fonts/local.conf && > /etc/fonts/local.conf &&
fc-cache -f && fc-cache -f &&
mkdir -p /run/sshd && /usr/sbin/sshd && mkdir -p /run/sshd && /usr/sbin/sshd &&
printf '%s\n' 'cd /app >/dev/null 2>&1 || true' > /etc/profile.d/zz-x-financial-app-dir.sh && printf '%s\n' 'cd /app >/dev/null 2>&1 || true' > /etc/profile.d/zz-x-financial-app-dir.sh &&
chmod 644 /etc/profile.d/zz-x-financial-app-dir.sh && chmod 644 /etc/profile.d/zz-x-financial-app-dir.sh &&
touch /root/.bashrc /root/.profile && touch /root/.bashrc /root/.profile &&
if ! grep -qxF 'cd /app >/dev/null 2>&1 || true' /root/.bashrc; then printf '\ncd /app >/dev/null 2>&1 || true\n' >> /root/.bashrc; fi && if ! grep -qxF 'cd /app >/dev/null 2>&1 || true' /root/.bashrc; then printf '\ncd /app >/dev/null 2>&1 || true\n' >> /root/.bashrc; fi &&
if ! grep -qxF 'cd /app >/dev/null 2>&1 || true' /root/.profile; then printf '\ncd /app >/dev/null 2>&1 || true\n' >> /root/.profile; fi && if ! grep -qxF 'cd /app >/dev/null 2>&1 || true' /root/.profile; then printf '\ncd /app >/dev/null 2>&1 || true\n' >> /root/.profile; fi &&
sed -i 's/\r$//' /app/start.sh /app/web/web_start.sh /app/server/server_start.sh && sed -i 's/\r$//' /app/start.sh /app/web/web_start.sh /app/server/server_start.sh &&
chmod +x /app/start.sh /app/web/web_start.sh /app/server/server_start.sh && chmod +x /app/start.sh /app/web/web_start.sh /app/server/server_start.sh &&
cd /app && cd /app &&
./start.sh all ./start.sh all
healthcheck: healthcheck:
test: ["CMD-SHELL", "curl -fsS http://127.0.0.1:${WEB_PORT:-5173}/ >/dev/null || exit 1"] test: ["CMD-SHELL", "curl -fsS http://127.0.0.1:${WEB_PORT:-5173}/ >/dev/null || exit 1"]
interval: 15s interval: 15s
timeout: 5s timeout: 5s
retries: 10 retries: 10
start_period: 180s start_period: 180s
networks: networks:
- financial-internal - financial-internal
qdrant: postgres:
image: qdrant/qdrant:latest image: pgvector/pgvector:pg17
container_name: x-financial-qdrant container_name: x-financial-local-postgres
restart: unless-stopped restart: unless-stopped
ports: environment:
- "${QDRANT_HTTP_PORT:-6333}:6333" POSTGRES_DB: "${LOCAL_POSTGRES_DB:-x_financial}"
- "${QDRANT_GRPC_PORT:-6334}:6334" POSTGRES_USER: "${LOCAL_POSTGRES_USER:-x_financial}"
volumes: POSTGRES_PASSWORD: "${LOCAL_POSTGRES_PASSWORD:-x_financial}"
- qdrant-storage:/qdrant/storage ports:
healthcheck: - "127.0.0.1:${LOCAL_POSTGRES_HOST_PORT:-55432}:5432"
test: ["CMD-SHELL", "bash -lc 'exec 3<>/dev/tcp/127.0.0.1/6333' || exit 1"] volumes:
interval: 15s - postgres-data:/var/lib/postgresql/data
timeout: 5s healthcheck:
retries: 10 test: ["CMD-SHELL", "pg_isready -U \"$${POSTGRES_USER}\" -d \"$${POSTGRES_DB}\""]
start_period: 30s interval: 15s
networks: timeout: 5s
- financial-internal retries: 10
start_period: 30s
onlyoffice: networks:
image: onlyoffice/documentserver:latest - financial-internal
container_name: x-financial-onlyoffice
restart: unless-stopped onlyoffice:
environment: image: onlyoffice/documentserver:latest
JWT_ENABLED: "true" container_name: x-financial-onlyoffice
JWT_SECRET: "${ONLYOFFICE_JWT_SECRET:-x-financial-onlyoffice-dev-secret}" restart: unless-stopped
ports: environment:
- "${ONLYOFFICE_PORT:-8082}:80" JWT_ENABLED: "true"
healthcheck: JWT_SECRET: "${ONLYOFFICE_JWT_SECRET:-x-financial-onlyoffice-dev-secret}"
test: ["CMD-SHELL", "curl -fsS http://127.0.0.1/healthcheck >/dev/null || exit 1"] ports:
interval: 15s - "127.0.0.1:${ONLYOFFICE_PORT:-8082}:80"
timeout: 5s healthcheck:
retries: 10 test: ["CMD-SHELL", "curl -fsS http://127.0.0.1/healthcheck >/dev/null || exit 1"]
start_period: 60s interval: 15s
networks: timeout: 5s
- financial-internal retries: 10
start_period: 60s
networks: networks:
financial-internal: - financial-internal
name: financial-internal
qdrant:
volumes: image: qdrant/qdrant:latest
qdrant-storage: container_name: x-financial-qdrant
restart: unless-stopped
ports:
- "127.0.0.1:${QDRANT_HTTP_PORT:-6333}:6333"
- "127.0.0.1:${QDRANT_GRPC_PORT:-6334}:6334"
volumes:
- qdrant-storage:/qdrant/storage
healthcheck:
test: ["CMD-SHELL", "bash -lc 'exec 3<>/dev/tcp/127.0.0.1/6333' || exit 1"]
interval: 15s
timeout: 5s
retries: 10
start_period: 30s
networks:
- financial-internal
networks:
financial-internal:
name: financial-internal
volumes:
postgres-data:
qdrant-storage:

View File

@@ -1,67 +1,127 @@
# Docker Compose # Docker Compose
This project currently uses the Vite `__setup/*` middleware during the initial setup flow. X-Financial 现在按运行依赖分成两层 Docker Compose
Because of that, the Docker deployment keeps the web frontend and FastAPI startup chain in
the same main container and runs the existing root `start.sh`. - `docker-compose.yml`:只启动主应用容器,适合已经有远端 PostgreSQL、ONLYOFFICE 或 Qdrant 的环境。
- `docker-compose.full.yml`:启动完整本地开发栈,适合没有外部依赖、希望本机一次性跑齐所有服务的环境。
## Start
主应用容器仍然同时启动 Web 前端和 FastAPI 后端,并复用根目录 `start.sh`
```bash 项目根目录会挂载到容器内 `/app`
cp .env.example .env
docker compose up -d ## 轻量启动:只跑主应用
```
适合你已经有数据库和 ONLYOFFICE 的情况。
Open:
```bash
```text cp .env.example .env
http://<your-linux-host>:5173 docker compose up -d
``` ```
## Container Layout 默认只会启动:
- `main`: web + FastAPI main container ```text
- `onlyoffice`: ONLYOFFICE Document Server main
- `postgres`: PostgreSQL database container ```
The project root is mounted directly into the main container: 打开:
```text ```text
.:/app http://<your-linux-host>:5273
``` ```
That means the container reads your existing `.env`, source code, `server/.secrets`, logs, 这条路径不会主动拉起本地 PostgreSQL、Qdrant 或 ONLYOFFICE。
and generated dependency directories directly from the mapped project folder. 数据库、ONLYOFFICE 和 Qdrant 地址都从 `.env` 或外部环境变量读取。
This is a `compose`-only setup. There is no custom `Dockerfile`. 常见外部依赖变量:
The tradeoff is that the `main` container installs the Python runtime packages it needs
when it starts. ```text
DATABASE_URL
## Persistence POSTGRES_HOST
POSTGRES_PORT
The PostgreSQL data directory is stored in the named volume `postgres_data`. ONLYOFFICE_ENABLED
ONLYOFFICE_PUBLIC_URL
## Notes ONLYOFFICE_BACKEND_URL
QDRANT_URL
- Most configuration should be maintained in the project root `.env`. ```
- The first `docker compose up -d` does not require an existing `.env`; the compose file
uses built-in defaults for the PostgreSQL container and the main container database URL. ## 完整启动:本地全栈
- Docker Compose only overrides a few values that must differ inside containers:
- `WEB_HOST=0.0.0.0` 适合没有远端数据库和 ONLYOFFICE 的情况。
- `SERVER_HOST=0.0.0.0`
- `POSTGRES_HOST=postgres` ```bash
- `POSTGRES_PORT=5432` docker compose -f docker-compose.full.yml up -d
- `DATABASE_URL=...@postgres:...` ```
- PostgreSQL is also published to the host by default as `127.0.0.1:55432`.
- ONLYOFFICE is published to the host by default as `127.0.0.1:8082`. 会启动:
- First boot with `SETUP_COMPLETED=false` starts the setup UI only.
- After you complete setup in the browser, the Vite setup bridge will start FastAPI in the ```text
same container using the saved runtime configuration. main
- On later restarts, `start.sh` will detect the saved setup state and start both web and postgres
server automatically. qdrant
- If you access the system from another machine, make sure `CORS_ORIGINS` in `.env` includes onlyoffice
the frontend address you actually use. ```
- For Navicat or any host-side client, use `127.0.0.1:55432`.
- If you need to access ONLYOFFICE from another machine, override `ONLYOFFICE_PUBLIC_URL` 本地服务端口:
so the browser can reach the document server address you actually expose.
- For the setup page, using `127.0.0.1` is acceptable in this Docker layout; the internal ```text
test bridge will resolve that back to the Docker PostgreSQL service. Web: 5273
FastAPI: 8000
PostgreSQL: 55432 -> 5432
Qdrant: 6333 / 6334
ONLYOFFICE: 8082
SSH: 2223
```
完整栈会把主容器内的数据库地址指向 `postgres:5432`
并把 Qdrant 地址指向 `http://qdrant:6333`
ONLYOFFICE 默认使用本机可访问地址:
```text
http://127.0.0.1:8082
```
如果浏览器从另一台机器访问,需要覆盖:
```bash
LOCAL_ONLYOFFICE_PUBLIC_URL=http://<host>:8082 \
docker compose -f docker-compose.full.yml up -d
```
如果 ONLYOFFICE 回调后端也需要外部地址,可以同时覆盖:
```bash
LOCAL_ONLYOFFICE_BACKEND_URL=http://<host>:8000 \
docker compose -f docker-compose.full.yml up -d
```
## 可选:只额外启动本地 PostgreSQL
如果只想在轻量主容器旁边补一个本地 PostgreSQL可以使用覆盖文件
```bash
docker compose -f docker-compose.yml -f docker-compose.postgres.yml up -d
```
这会启动:
```text
main
postgres
```
## 停止与清理
停止当前默认轻量栈:
```bash
docker compose down
```
停止完整本地栈:
```bash
docker compose -f docker-compose.full.yml down
```
如需删除本地数据卷,先确认不再需要其中数据,再手动执行带 `-v` 的清理命令。

View File

@@ -0,0 +1,32 @@
# 附件自动关联后台任务实施计划
## 目标
把小财管家 AI 模式里的附件关联从前端会话内存任务改为后端可查询后台任务,保证用户退出或刷新当前会话后,附件关联仍能继续完成并可恢复状态。
## 执行清单
- [x] 定位当前断链根因:前端依赖 `File` 对象和内存 `Map`
- [x] 确认票据夹已有 `receipt_id`、源文件和关联状态能力。
- [x] 落开发方案文档。
- [x] 实现后端任务 schema 和内存任务池。
- [x] 实现后端任务 API。
- [x] 实现后端票据夹源文件归集到报销单明细。
- [x] 增加后端测试。
- [x] 实现前端任务创建、轮询和恢复。
- [x] 增加前端测试断言。
- [x] 执行容器后端定向测试。
- [x] 执行前端定向测试和构建。
## 验证结果
- 后端定向测试:`6 passed`
- 前端定向测试:`12 passed`
- 前端构建:通过,保留既有 chunk size warning。
- 运行时检查:新任务查询路由已加载,未知任务返回“附件关联任务不存在或已失效。”
## 关键决策
- 第一版使用后端内存任务池和 FastAPI `BackgroundTasks`,解决前端会话断链。
- 第一版不新增数据库任务表,服务重启后的任务恢复作为后续增强。
- 前端消息只保存 `job_id`、状态和票据引用,不再保存附件原件。

View File

@@ -0,0 +1,215 @@
# AI 工作台统一意图识别框架设计
## 背景
AI 工作台当前的输入识别分散在多个前端 flow 中:申请预览、报销草稿、单据查询、草稿删除提示各自判断输入。这样的结构能快速修复单点问题,但会让“删除 3 天前的草稿”“审核合规没有风险的申请”这类组合型请求继续变成关键词补丁。
本设计把自然语言输入先统一解析成结构化 `IntentFrame`,再根据目标是否明确、安全等级和业务边界决定下一步动作。
## 目标
- 让工作台所有自然语言输入先进入统一意图框架,不再在各业务 flow 中散落判断。
- 支持动作、对象、筛选条件、上下文指代、安全等级的组合识别。
- 对删除、审核、驳回等高风险动作固定走“筛选候选 + 详情确认”,禁止自然语言直接执行。
- 保留当前会话内的快速指代能力,例如“删除刚才那个草稿”能定位最近创建的草稿。
- 对带筛选条件的请求,例如“删除 3 天前的草稿”“审核无风险申请”,先展示思考过程和候选列表。
## 非目标
- 第一版不引入 LangGraph也不把前端本地识别迁到后端状态机。
- 第一版不做自然语言直接批量删除、批量审核或批量驳回。
- 第一版不改后端审批、删除接口的权限模型。
- 第一版不重写现有申请预览和报销草稿流程,只把入口识别前置统一。
## 总体架构
统一意图识别分三层:
1. `IntentFrame Parser`:把用户输入解析为结构化意图。
2. `Target Resolver`:结合当前会话、最近动作和筛选条件,判断目标是否唯一。
3. `Action Policy`:根据动作风险决定直接查询、展示候选、要求澄清或阻断。
输入链路应调整为:
```text
用户输入
-> IntentFrame Parser
-> Action Policy
-> Target Resolver
-> 业务 flow
- 查询候选
- 打开详情确认
- 进入申请/报销流程
- 要求补充条件
```
## IntentFrame 数据结构
```js
{
action: 'query' | 'delete' | 'approve' | 'reject' | 'create' | 'update' | 'ask_policy',
objectType: 'draft' | 'application' | 'reimbursement' | 'approval_task' | 'receipt' | 'document',
filters: {
timeRange: null,
status: null,
risk: null,
documentType: null,
amount: null,
keyword: null
},
targetMode: 'current_context' | 'filtered_candidates' | 'ambiguous',
safetyLevel: 'read_only' | 'confirm_required' | 'blocked',
confidence: 0,
normalizedQuery: ''
}
```
### 字段含义
- `action` 表示用户想做什么,例如查、删、审核、驳回、创建或咨询规则。
- `objectType` 表示动作对象,例如草稿、申请单、报销单、待审任务或票据。
- `filters` 表示筛选条件,必须可以复用到单据查询 flow。
- `targetMode` 表示目标定位方式:
- `current_context`:明确指向当前会话最近对象。
- `filtered_candidates`:需要查询候选列表。
- `ambiguous`:条件不足,需要澄清。
- `safetyLevel` 表示动作安全级别:
- `read_only`:可直接查询或解释。
- `confirm_required`:只展示候选或详情入口,不直接执行。
- `blocked`:存在批量破坏性风险或越权风险,必须阻断。
- `normalizedQuery` 是给现有查询 flow 使用的可读查询句,例如“我的 3 天前草稿单据”。
## 识别策略
### 动作识别
- 查询:查、看、列出、有哪些、找一下。
- 删除:删除、删掉、移除、作废、撤销。
- 审核:审核、审批、处理待办、去审批。
- 驳回:驳回、退回、拒绝。
- 创建:新建、发起、申请、我要报销。
- 更新:补充、修改、改成、填入。
- 规则咨询:怎么走、能不能、规则、制度、政策、标准。
### 对象识别
- 草稿:草稿、未提交、刚才保存的单据。
- 申请单:申请、申请单、出差申请、费用申请。
- 报销单:报销、报销单、费用报销。
- 待审任务:待办、待我审核、待审批、审核单。
- 票据:发票、票据、附件、图片。
### 筛选条件识别
- 时间今天、昨天、3 天前、近 7 天、上周、本月、具体日期、日期范围。
- 状态:草稿、审批中、已通过、已驳回、待补充。
- 风险:无风险、低风险、中风险、高风险、合规、异常、超标。
- 金额:超过 1000、500 以下、100 到 300。
- 关键词:地点、事由、人员、部门、单号等自由文本。
## 目标解析规则
### 当前上下文直达
当用户使用“刚才那个”“当前”“这个”“上面那个”这类指代,并且当前会话中能找到最近的可操作对象时,`targetMode``current_context`
例子:
- “删除刚才那个草稿”
- “打开这个申请单”
- “继续刚才的报销草稿”
即便目标唯一,删除、审核、驳回仍然只打开详情页或确认入口。
### 筛选候选
当用户输入包含时间、风险、金额、状态、类型等筛选条件时,`targetMode` 必须为 `filtered_candidates`
例子:
- “删除 3 天前的草稿”
- “审核合规没有风险的申请”
- “找一下上海相关的低风险待审申请”
这类请求必须进入单据查询 flow展示候选结果不能直接套最近草稿。
### 条件不足澄清
当动作高风险但目标既不唯一,也没有足够筛选条件时,`targetMode``ambiguous`
例子:
- “把草稿删了”
- “帮我审核一下”
- “退回这个单”
系统应提示用户选择候选或补充条件。
## Action Policy
| 动作 | 安全等级 | 第一版行为 |
| --- | --- | --- |
| 查询 | `read_only` | 直接查询并展示结果 |
| 规则咨询 | `read_only` | 走政策/规则解释,不进入单据查询 |
| 删除 | `confirm_required` | 展示候选或打开详情页确认,不直接删除 |
| 审核通过 | `confirm_required` | 展示待审候选或打开审核详情,不直接通过 |
| 驳回/退回 | `confirm_required` | 展示待审候选或打开审核详情,不直接驳回 |
| 批量删除/批量审核 | `blocked` | 阻断并要求用户选择具体单据 |
## 用户可见思考过程
筛选型命令必须复用现有查询 thinking events并补充动作意图说明
1. 解析自然语言动作和筛选条件。
2. 判断操作风险和目标定位方式。
3. 查询业务单据接口。
4. 按条件组合筛选候选。
5. 展示候选卡片和下一步入口。
示例:
```text
解析识别到“删除”是高风险动作对象是“草稿”时间条件是“3 天前”。
策略:不会直接删除,将先查询我的草稿候选。
结果:命中 2 张草稿,请打开详情页确认删除目标。
```
## 文件边界
- 新增 `workbenchIntentFrameModel.js`:负责解析用户输入到 `IntentFrame`
- 新增 `workbenchIntentActionPolicy.js`:负责动作安全策略和下一步路由判断。
- 调整 `workbenchAiCommandIntentModel.js`:保留会话内最近草稿解析,但不再单独拥有顶层意图判断。
- 调整 `useWorkbenchAiCommandIntents.js`:基于 `IntentFrame` 分发到当前上下文直达或候选查询。
- 调整 `aiDocumentQueryIntent.js``aiDocumentQueryModel.js`:补充风险筛选、相对日期和筛选摘要。
- 补充前端测试,覆盖组合型输入和安全边界。
## 迁移步骤
1. 先为 `IntentFrame` 解析补测试:
- “删除刚才那个草稿”解析为 `delete + draft + current_context + confirm_required`
- “删除 3 天前的草稿”解析为 `delete + draft + filtered_candidates + confirm_required`
- “审核合规没有风险的申请”解析为 `approve + application + risk:none + filtered_candidates + confirm_required`
- “审批规则怎么走”解析为 `ask_policy`,不进入单据查询。
2. 实现 `workbenchIntentFrameModel.js`,不接入 UI。
3. 实现风险和相对日期筛选能力,让查询 flow 能承接筛选型命令。
4. 接入 `useWorkbenchAiCommandIntents.js`
- 当前上下文直达:生成详情确认入口。
- 筛选候选:调用 `handleAiDocumentQueryIntent(normalizedQuery, pendingMessage)`
- 条件不足:提示补充条件或查询可选候选。
5. 移除或降级旧的散落正则,让顶层输入先走统一框架。
6. 跑定向测试、相邻测试、前端构建和 5173 工作台烟测。
## 验收标准
- 输入“删除刚才那个草稿”时,系统定位当前会话最近草稿,并只打开详情确认入口。
- 输入“删除 3 天前的草稿”时,系统展示筛选思考过程和草稿候选,不使用最近草稿快捷路径。
- 输入“审核合规没有风险的申请”时,系统查询待我审核申请,并筛选无风险候选。
- 输入“审批规则怎么走”时,不进入单据查询。
- 删除、审核、驳回均不通过自然语言直接执行最终动作。
- 新增/调整测试全部通过,前端构建通过,`git diff --check` 无空白错误。
## 后续演进
- 当后端 steward planner 能稳定输出同等 `IntentFrame` 时,可以把 Parser 层迁到后端,前端只保留策略兜底。
- 如果需要跨会话目标记忆,可把最近创建/保存草稿写入会话快照,并附带过期时间和用户确认边界。
- 如果未来引入 LangGraph应把它用于多步状态编排而不是替代 `IntentFrame` schema 本身。

View File

@@ -0,0 +1,12 @@
# AI 工作台审核单二轮审核命令丢失候选上下文
日期2026-06-25
文档路径document/development/2026-06-25/dev-logs/bugs/ai-approval-followup-context.md
## 修复记录
- 16:24记录 bug 修复AI 工作台审核单二轮审核命令丢失候选上下文。bug-log:8a2ae6eb
- Git 提交检查fetch 失败fatal: unable to access 'https://www.caoxiaozhu.com:13002/YG-Soft/X-Financial.git/': LibreSSL SSL_connect: SSL_ERROR_SYSCALL in connection to www.caoxiaozhu.com:13002upstream `origin/main`upstream 新提交:未发现;本地 ahead 提交8a2ae6eb (HEAD -> main) fix(server): gate_classify 复用 _classify_irrelevant_input 修复 off_topic 误杀992cf71f refactor(server): Phase 1 图拓扑重构 - LangGraph 成为唯一编排者54356ba8 refactor(server): scene 注册表骨架 + 统一门控管道设计文档e9d7c56d feat(server): 会话上下文保留LLM 历史 + 确定性兜底双保险)。
- 修改:`workbenchAiCommandIntentModel.js` 新增待审单据候选上下文解析与二轮审批命令提示;`useWorkbenchAiCommandIntents.js``query_candidates` 前优先复用上一轮待审候选;`workbench-ai-command-intent-model.test.mjs` 覆盖“我有哪些审核单”后继续说“审核通过”的候选承接。
- 操作:按 TDD 先补失败用例,再实现最小修复;执行 `tools/agent-change-log/update_change_log.py --kind bug` 创建当天 bug 记录,并手动补全真实修复细节。
- 验证:`node --test web/tests/workbench-ai-command-intent-model.test.mjs` 通过;`node --test web/tests/workbench-ai-command-intent-model.test.mjs web/tests/workbench-intent-frame-model.test.mjs web/tests/ai-document-query-model.test.mjs` 通过31 项前端相关测试全部通过。
- 影响:用户在 AI 工作台先查询待审/审核单后,再说“请帮我审核通过”或类似审批命令时,系统会接上刚才候选并要求进入详情确认,不会把二轮命令当成孤立查询或静默失智;仍保留高风险审批动作不直接执行的安全边界。

View File

@@ -0,0 +1,12 @@
# AI模式企业主题光球出现矩形闪动边框
日期2026-06-25
文档路径document/development/2026-06-25/dev-logs/bugs/ai-enterprise-orb-frame.md
## 修复记录
- 15:02记录 bug 修复AI模式企业主题光球出现矩形闪动边框。bug-log:2ebc2756
- Git 提交检查15:01 执行 `git fetch --all --prune` 成功upstream `origin/main`upstream 新提交:未发现;本地 ahead 提交:未发现。
- 修改:`personal-workbench-ai-mode.css`,把企业主题下 `.workbench-ai-orb` 从白底圆角矩形容器改回透明圆形承载,移除边框与阴影;`settings-theme-section.test.mjs` 增加断言锁定 `border: 0``border-radius: 50%``background: transparent``box-shadow: none`
- 操作:复现时确认企业主题覆盖块把光球容器设置为 `border-radius: 18px`、白底、阴影,导致 GIF 光球外层出现明显闪动方框;修复后用浏览器读取已加载 CSSOM确认企业主题规则已变为透明圆形版本。
- 验证:`node web/tests/settings-theme-section.test.mjs``node web/tests/settings-llm-section.test.mjs``node web/tests/settings-rendering-section.test.mjs``git diff --check``npm --prefix web run build` 均通过;本地工作台页面可加载,光球元素存在,页面无 error浏览器 CSSOM 中企业主题光球规则为 `border: 0px``border-radius: 50%``background: transparent``box-shadow: none`
- 影响:企业沉稳主题下 AI 模式欢迎区光球不再显示矩形闪动边框,动感/专业智能主题保持原有光球表现。

View File

@@ -0,0 +1,27 @@
# 申请详情退回/草稿状态修改申请卡壳
日期2026-06-25
文档路径document/development/2026-06-25/dev-logs/bugs/application-detail-edit-returned-draft.md
## 修复记录
- 16:40记录 bug 修复:申请详情退回/草稿状态修改申请卡壳。bug-log:8a2ae6eb
- Git 提交检查fetch 失败fatal: unable to access 'https://www.caoxiaozhu.com:13002/YG-Soft/X-Financial.git/': LibreSSL SSL_connect: SSL_ERROR_SYSCALL in connection to www.caoxiaozhu.com:13002upstream `origin/main`upstream 新提交:未发现;本地 ahead 提交8a2ae6eb (HEAD -> main) fix(server): gate_classify 复用 _classify_irrelevant_input 修复 off_topic 误杀992cf71f refactor(server): Phase 1 图拓扑重构 - LangGraph 成为唯一编排者54356ba8 refactor(server): scene 注册表骨架 + 统一门控管道设计文档e9d7c56d feat(server): 会话上下文保留LLM 历史 + 确定性兜底双保险)。
- 修改:`travelRequestDetailSetup.js``TravelRequestDetailView.vue` 将“修改申请”入口从仅退回态放宽为申请单草稿/退回归一后的可编辑态,仍要求当前用户是申请人;打开助手时补齐原申请 `draftPayload``applicationEditMode` 和可编辑字段白名单。
- 修改:`useAppShell.js``AppShellRouteView.vue``TravelReimbursementCreateView.js``useTravelReimbursementCreateViewLifecycle.js` 贯通 `initialDraftPayload`,让申请详情带出的核对表首条消息保留原 `claim_id`,保存草稿/直接提交时走已有 `application_edit_claim_id` 更新链路,不再新建或卡在无上下文状态。
- 修改:`expenseApplicationPreview.js` 支持 `editableFields`,在修改申请场景只开放事由、时间、地点、出行方式;`aiApplicationPreviewActions.js` 将白名单写入 `application_editable_fields`,便于后续服务端字段级约束。
- 操作manual 触发 `tools/agent-change-log/update_change_log.py --kind bug` 生成日志后补充真实修复记录;未修改后端接口逻辑,因为 `user_agent_application.py` 已允许 `draft/returned/supplement` 申请按 `application_edit_claim_id` 更新。
- 验证:`node --test web/tests/travel-request-detail-risk-advice.test.mjs` 通过;`node --test --test-name-pattern "application edit prefill opens assistant without auto submit" web/tests/app-shell-financial-assistant-entry.test.mjs` 通过;`node --test --test-name-pattern "application edit preview only allows reason time location and transport changes" web/tests/expense-application-fast-preview.test.mjs` 通过;`node web/tests/ai-application-preview-actions.test.mjs` 通过;`git diff --check` 通过;`npm --prefix web run build` 通过。全量跑 `app-shell-financial-assistant-entry.test.mjs``expense-application-fast-preview.test.mjs` 时仍有既有结构断言漂移,失败点与本次修改无关。
- 影响:用户在申请详情里看到草稿和退回申请都能继续修改;修改面被限制在事由、时间、地点、出行方式,其他职级、负责人、费用标准和金额仍由原单或规则测算带入。
- 16:51补充修复取消底部“修改申请”按钮改为申请详情格子内联铅笔编辑。
- Git 提交检查fetch 仍失败fatal: unable to access 'https://www.caoxiaozhu.com:13002/YG-Soft/X-Financial.git/': LibreSSL SSL_connect: SSL_ERROR_SYSCALL in connection to www.caoxiaozhu.com:13002upstream `origin/main`upstream 新提交:未发现;本地 ahead 提交仍为 8a2ae6eb、992cf71f、54356ba8、e9d7c56d。
- 修改:`TravelRequestDetailView.vue` 移除底部“修改申请”按钮,在申请详情事实格子的值旁显示小铅笔;点击后根据字段类型切换为日期框、下拉框或文本输入,并提供保存/取消按钮。
- 修改:`travelRequestDetailSetup.js` 新增 `applicationDetailEditor` 状态、`canEditApplicationDetailItem``openApplicationDetailEditor``saveApplicationDetailEdit` 等内联编辑流程;保存时复用 `runAiApplicationPreviewAction``save_draft` 路径和 `application_edit_claim_id`,保持更新原申请单。
- 修改:`travel-request-detail-view.css` 收窄申请详情标签选择器,避免内联编辑内部 `span` 被误当字段名,并补充铅笔、确认、取消、编辑控件样式。
- 验证:先运行新增目标断言失败,确认旧按钮仍存在;实现后 `node --test --test-name-pattern "draft or returned application detail edits allowed facts inline" web/tests/travel-request-detail-risk-advice.test.mjs` 通过;`node --test web/tests/travel-request-detail-risk-advice.test.mjs` 62 条通过;`node web/tests/ai-application-preview-actions.test.mjs` 通过;`git diff --check` 通过;`npm --prefix web run build` 通过。本地 `http://[::1]:5173/app/documents` 可达但跳转登录页,因无登录态未继续真实详情点击。
- 影响:草稿/退回申请不再需要先打开 AI 助手才能改,用户可以直接在详情表格中改事由、时间、地点、出行方式;其它字段仍不可编辑,由原单或规则测算带入。
- 17:02补充修复详情页因残留 `handleModifyApplication` 返回项导致 setup 阶段崩溃。
- Git 提交检查fetch 仍失败fatal: unable to access 'https://www.caoxiaozhu.com:13002/YG-Soft/X-Financial.git/': LibreSSL SSL_connect: SSL_ERROR_SYSCALL in connection to www.caoxiaozhu.com:13002upstream `origin/main`upstream 新提交:未发现;本地 ahead 提交仍为 8a2ae6eb、992cf71f、54356ba8、e9d7c56d。
- 修改:`travelRequestDetailSetup.js` 删除 return 对象里已经不存在的 `handleModifyApplication`,避免详情页初始化时抛 `ReferenceError`
- 修改:`travel-request-detail-risk-advice.test.mjs` 为内联编辑回归用例增加 `handleModifyApplication` 不得残留的断言;先运行目标用例确认失败,再删除残留返回项后确认转绿。
- 验证:`node --test --test-name-pattern "draft or returned application detail edits allowed facts inline" web/tests/travel-request-detail-risk-advice.test.mjs` 先失败后通过;`node --test web/tests/travel-request-detail-risk-advice.test.mjs` 62 条通过;`node web/tests/ai-application-preview-actions.test.mjs` 通过;`git diff --check` 通过;`npm --prefix web run build` 通过。真实本地 `[::1]:5173` 以管理员打开 `AG2YUJ9FB` 详情页无控制台错误;切换申请人 `caoxiaozhu@xf.com` 后同页可见 `申请详情` 和单号,铅笔按钮 5 个,点击后出现 1 个编辑控件,取消后无保存副作用。
- 影响:申请/报销详情页 setup 不再因为旧按钮处理函数缺失而白屏,内联铅笔编辑入口保留。

View File

@@ -0,0 +1,116 @@
# 写日志技能拆分 概念文档
更新时间2026-06-25
文档路径document/development/2026-06-25/feature/agent-change-log-split/CONCEPT.md
## 功能一句话
把原来单文件三段式工作日志拆成按日期聚合的功能点文档、bug 修复日志和每日 17:00 综合工作日志。
## 背景与问题
- 当前现状:旧 `agent-change-log` 把所有变更追加到 `document/work-log/YYYY-MM-DD.md`,并固定包含 `当日工作内容``遗留问题``TODO`
- 用户痛点功能点规划、bug 修复和当天综合复盘混在一个日志文件里,后续追溯时难以按功能或问题拆开看。
- 业务影响开发资料会越来越长bug 修复证据和功能点设计边界容易互相干扰。
- 为什么现在需要做:`write-development-docs` 已经改为 `document/development/YYYY-MM-DD/feature/<功能点>/`,日志能力也需要跟随同一日期根目录拆分。
## 目标与非目标
### 目标
- [G1] bug 修复记录落到 `document/development/YYYY-MM-DD/dev-logs/bugs/<bug-slug>.md`
- [G2] bug 日志只保留修复记录,不再写 `遗留问题``TODO` 两块。
- [G3] 每天 17:00 汇总当天 `feature/``dev-logs/bugs/`,生成 `work-logs.med`
- [G4] 保留 Git 双向检查,继续识别 upstream 新提交和本地 ahead 提交。
### 非目标
- [NG1] 本轮不迁移历史 `document/work-log/*.md`
- [NG2] 本轮不删除旧历史日志,避免破坏既有追溯。
- [NG3] 本轮不把非 bug 提交强行写入 bug 日志。
## 用户与场景
- 目标用户:使用 Codex/Agent 维护 X-Financial 的开发者和后续接手的智能体。
- 使用入口:`agent-change-log` Skill、`tools/agent-change-log/update_change_log.py`、post-commit hook、每日 17:00 Codex automation。
- 核心场景:
1. 修复 bug 后写入当天 `dev-logs/bugs/<bug-slug>.md`
2. 非 bug 功能点通过 `feature/<功能点>/CONCEPT.md``TODO.md` 沉淀。
3. 每天 17:00 汇总功能点与 bug生成 `work-logs.med`
- 异常场景:
- 没有 feature 或 bug 时,综合日志明确写“未发现”。
- 提交标题不像 bug 时post-commit 自动日志跳过。
## 功能能力
- [C1] 输入能力:支持 `--kind bug``--kind auto``--kind summary` 三种模式。
- [C2] 处理能力:按日期创建 `dev-logs/bugs`,按 bug slug 记录修复内容。
- [C3] 输出能力:输出 bug 修复记录或每日 `work-logs.med`
- [C4] 状态与权限:沿用 Git fetch/status/log 检查,不主动 merge/rebase。
- [C5] 边界与降级:官方 skill 校验脚本不可用时用脚本单测、frontmatter 和 diff check 兜底。
## 方案设计
### 前端
当前不涉及。
### 后端
当前不涉及业务后端;只修改仓库级 Skill、脚本和 hook。
### 算法与规则
- 输入commit subject、用户传入的 bug title/slug、当天 feature 和 bug 文档。
- 流程bug 模式写入 bug 文件auto 模式识别 bug-like commitsummary 模式扫描 `feature/``dev-logs/bugs/` 后生成综合日志。
- 输出:`dev-logs/bugs/*.md``work-logs.med`
- 解释summary 中保留来源目录和综合分析,便于复盘追溯。
### 数据与契约
- 核心字段日期、bug slug、bug title、Git 提交检查、修改、操作、验证、影响。
- 状态枚举:`auto``bug``summary`
- 兼容策略:保留旧 `document/work-log` 历史,不新增旧格式。
- 版本/审计:本轮变更通过本地 checkpoint commit 保留。
## 算法与公式
当前功能不涉及显式数学公式。
## 测试方案
后端:
- 当前不涉及后端服务。
前端:
- 当前不涉及前端构建。
集成:
- 运行 `python3 tools/agent-change-log/test_update_change_log.py`,覆盖 bug 日志、非 bug 自动跳过、每日综合日志聚合。
手工验证:
- 运行 `update_change_log.py --kind bug --dry-run``--kind summary --dry-run` 检查目标路径和输出内容。
## 指标与验收
- [A1] 功能验收bug 日志路径为 `document/development/YYYY-MM-DD/dev-logs/bugs/<bug-slug>.md`
- [A2] 性能指标:脚本单测在 60s 内完成。
- [A3] 质量指标:不再为新日志写入旧三段式 `document/work-log/YYYY-MM-DD.md`
- [A4] 安全/权限指标:脚本不做 merge/rebase不删除历史日志。
- [A5] 可观测性17:00 automation 生成 `work-logs.med` 后报告路径和是否发现 feature/bug。
## 风险与开放问题
- 风险:`work-logs.med` 是按用户原文保留的扩展名,可能与常见 `.md` 扩展名不一致。
- 已处理依赖:已创建 Codex automation 执行每日 17:00 summary。
- 待确认:后续是否需要迁移历史 `document/work-log`
- 降级策略automation 不可用时可手动运行 `python3 tools/agent-change-log/update_change_log.py --kind summary`
## 本轮实现记录
- 2026-06-25重写 `agent-change-log` Skill 和脚本,新增 split log 测试,创建每日 17:00 Codex automation。

View File

@@ -0,0 +1,61 @@
# 写日志技能拆分 开发 TODO
更新时间2026-06-25
文档路径document/development/2026-06-25/feature/agent-change-log-split/TODO.md
## 使用规则
- 每个 TODO 必须对应 `CONCEPT.md` 中的目标、能力、方案或验收点。
- 只有完成并验证后,才能把 `[ ]` 改成 `[x]`
- 勾选时在任务后补充简短证据,例如文件、接口、命令或验证结果。
- 如果需求发生变化,先更新 `CONCEPT.md`,再调整本 TODO。
## 1. 调研与边界
- [x] [CONCEPT: 背景与问题] 确认旧日志 Skill、脚本、hook 和 AGENTS 仍指向 `document/work-log/YYYY-MM-DD.md`
证据:`rg -n "document/work-log|当日工作内容|遗留问题|TODO" AGENTS.md .codex/skills/agent-change-log tools/agent-change-log .githooks/post-commit`
- [x] [CONCEPT: 目标与非目标] 明确本轮不迁移历史 `document/work-log/*.md`
证据:`CONCEPT.md` 非目标已列明历史不迁移。
## 2. 契约与设计
- [x] [CONCEPT: 功能能力] 定义 `auto``bug``summary` 三种脚本模式。
证据:`tools/agent-change-log/update_change_log.py``--kind` 参数。
- [x] [CONCEPT: 数据与契约] 固定新路径 `document/development/YYYY-MM-DD/dev-logs/bugs``work-logs.med`
证据:`agent-change-log` Skill、AGENTS 和脚本常量。
## 3. 后端实现
- [x] [CONCEPT: 后端] 重写日志脚本,支持 bug 记录、auto 跳过非 bug、summary 聚合。
证据:`tools/agent-change-log/update_change_log.py`
- [x] [CONCEPT: 后端] 更新 post-commit hook改为 `--kind auto`
证据:`.githooks/post-commit`
## 4. 算法/规则实现
- [x] [CONCEPT: 算法与规则] 实现 bug-like commit 识别规则。
证据:`looks_like_bug()` 覆盖 `fix``bugfix``修复``失败``异常` 等关键词。
- [x] [CONCEPT: 算法与规则] 实现 feature 和 bug 汇总生成 `work-logs.med`
证据:`build_daily_summary()`
## 5. 前端实现
- [x] [CONCEPT: 前端] 当前不涉及前端页面。
证据:本轮只修改仓库 Skill、脚本、hook、文档和 automation。
## 6. 测试与验证
- [x] [CONCEPT: 测试方案] 补充脚本回归测试。
证据:`tools/agent-change-log/test_update_change_log.py`
- [x] [CONCEPT: 测试方案] 运行脚本单测。
证据:`python3 tools/agent-change-log/test_update_change_log.py`3 tests OK。
- [x] [CONCEPT: 测试方案] dry-run 验证 bug 路径和 summary 路径。
证据:`--kind bug --dry-run` 输出 `document/development/2026-06-25/dev-logs/bugs/draft-preview-disappears.md``--kind summary --dry-run` 输出 `document/development/2026-06-25/work-logs.med`
## 7. 文档收尾
- [x] [CONCEPT: 指标与验收] 更新 `agent-change-log` Skill、AGENTS 和 README。
证据:`.codex/skills/agent-change-log/SKILL.md``AGENTS.md``tools/agent-change-log/README.md`
- [x] [CONCEPT: 指标与验收] 创建每日 17:00 Codex automation。
证据automation id `x-financial-daily-split-work-log`

View File

@@ -0,0 +1,31 @@
# AI 对话 UI 样式重构与 SaaS 化设计方案
为了让 X-Financial AI 助手的对话界面展现更专业的金融与 SaaS 企业化视觉,我们对前端全局的 Markdown 渲染样式进行了重构。
## 设计目标
- **中性色打底**:对话块背景、边框移除饱和偏色,统一采用 Slate/Neutral 冷灰色调(如 `#f8fafc``#cbd5e1`)。
- **降低色彩冗余**:将非必须的高亮蓝色改为中性灰色,各单据卡片仅在状态字样与极淡头部透明底中体现状态点缀,避免彩色杂乱堆积。
- **界面扁平微阴影**:移除带有斜向彩色发光的阴影与复杂的背景图案,采用 1px 实线描边配合微阴影,契合 Stripe, Jira 等现代 SaaS 产品规范。
## 详细参数定义
- **引用块 (`blockquote` / `.ai-html-callout`)**
- 边框:`3px solid #cbd5e1`
- 背景:`#f8fafc`
- 文字:`#334155`
- **信息网格 (`.ai-html-focus-grid`)**
- 边框:`3px solid #cbd5e1`
- 标题颜色:`#475569` (Slate-600)
- **单据卡片 (`.ai-document-card`)**
- 描边:`1px solid #e2e8f0`
- 投影:`0 1px 2px 0 rgba(15, 23, 42, 0.05)`
- 头部默认背景:`rgba(241, 245, 249, 0.5)`
- 状态点缀色:
- `is-success` (Teal-700): `#0f766e`
- `is-warning` (Amber-700): `#b45309`
- `is-danger` (Red-700): `#b91c1c`
- is-pending (Blue-600): `#2563eb`
## 消息排版格式规范
- **限制 Alert 引用块为单条**:在警示/阻塞状态下,只允许包含一条 `>` 引用块用作 Alert 提示。
- **列表扁平化**:发起前核对结果等普通多维状态信息,一律不使用 `>` 引用块,合并为标准的无序列表展示,保持界面视觉扁平、整洁。
- **常规表单数据平铺**:时间、单据编号等字段,均采用扁平加粗文本平铺,不再触发 focus-grid 等具有边框线的额外容器。

View File

@@ -0,0 +1,9 @@
# 任务计划 (SaaS 风格视觉优化)
- [x] 优化全局 `blockquote``.ai-html-callout` 的色彩饱和度,改用 Slate 灰蓝色系打底。
- [x] 优化 `.ai-html-focus-grid` 信息网格与竖线的亮蓝色调。
- [x] 移除单据卡片上的 `ai-document-card-bg.png` 渐变背景图。
- [x] 扁平化单据卡片的描边与中性阴影。
- [x] 优化卡片语义状态角标与操作链接的 SaaS 蓝及点缀。
- [x] 优化原生 Markdown 列表 `li::marker` 与表格外包边框的色彩层级。
- [x] 重构预审与冲突消息的 Markdown 输出格式,消除不必要的 `>` 引用块,攻克“三条大竖杠”的排版痛点。

View File

@@ -0,0 +1,207 @@
# 主题设置与企业沉稳 AI 模式 概念文档
更新时间2026-06-25
文档路径document/development/2026-06-25/feature/theme-settings-enterprise-ai-style/CONCEPT.md
## 功能一句话
将系统设置中的“界面皮肤”升级为“主题设置”,从单纯色板选择扩展为产品体验风格选择,并让 AI 模式在“企业沉稳”主题下呈现更符合企业级 SaaS 的低噪声、结构化、克制风格。
## 背景与问题
当前系统设置里的外观入口仍偏向“界面皮肤”语义,主要表达颜色和视觉皮肤选择。这个命名过窄,无法承载用户希望配置的完整体验风格。
现有 AI 模式默认更接近动感活泼风格,使用较多渐变、明亮色块和活跃视觉标识。它适合演示和助手化体验,但在企业级财务、审批、风控、报销场景中,容易显得色彩过重,不够沉稳。
用户希望在系统设置中明确提供主题类型,至少覆盖:
1. 动感活泼:保留当前这种更有活力、更有 AI 助手感的主题。
2. 企业沉稳:符合企业 SaaS 风格,尤其 AI 模式下的对话图标、样式和整体风格需要克制、稳定,减少颜色渲染。
3. 专业智能:作为第三类默认方案,介于前两者之间,保留少量智能化识别感,但整体更收敛。
## 目标与非目标
目标:
- 将“界面皮肤”入口重命名为“主题设置”,让用户理解这里配置的是整体体验风格。
- 将主题从一组颜色选项收敛成三类可理解的主题类型。
- 在“企业沉稳”主题下,让 AI 模式呈现企业后台应用的专业感。
- 保持现有设置保存链路稳定,优先复用当前 appearance 配置和主题变量机制。
- 为后续进一步扩展租户品牌色、暗色模式、组件密度预留结构边界。
非目标:
- 不重做整个系统设置信息架构。
- 不一次性重写所有业务页面的视觉风格。
- 不改变 AI 意图识别、报销流程、审批逻辑和后端业务规则。
- 不新增复杂的租户级主题发布、审批或版本管理能力。
- 不引入新的前端主题框架或额外依赖。
## 用户与场景
管理员在系统设置中进入“主题设置”,选择适合当前组织的产品体验风格。
演示、培训、轻量工作台场景可以使用“动感活泼”,保留当前更有活力的 AI 交互表达。
企业正式生产环境可以使用“企业沉稳”,让财务、审批、风控和 AI 对话看起来像成熟的企业应用,而不是营销页或玩具化助手。
希望保留智能化识别但又不希望过度活泼的组织,可以使用“专业智能”。
## 功能能力
主题设置页面需要提供三类主题:
- 动感活泼:当前风格延续,允许渐变、轻动效和更明显的 AI 识别色。
- 企业沉稳:低饱和色、白灰底、轻描边、少阴影、少渐变,强调信息层级和业务可信度。
- 专业智能:更克制的智能风格,允许小面积蓝灰、紫灰或品牌色点缀,但避免大面积彩色渲染。
页面文案调整:
- 左侧菜单从“界面皮肤”调整为“主题设置”。
- 页面标题从“界面皮肤与企业主色”调整为“主题风格与界面体验”。
- 保存反馈从“界面皮肤已保存”调整为“主题设置已保存”。
- 说明文案从“皮肤/配色”改为“主题/体验风格”。
AI 模式联动:
- 动感活泼:保留当前 AI 模式视觉语言。
- 企业沉稳:对 AI 对话区、消息气泡、工具调用状态、思考过程、图标、卡片、提示块做克制化覆写。
- 专业智能:保留轻量 AI 识别感,但降低渐变、发光、背景装饰和高饱和强调色。
## 方案设计
配置模型优先沿用当前系统设置外观配置,避免为了命名调整引入后端迁移风险。
首期可以继续复用 `appearanceForm.themeSkin` 作为持久化字段,将值从原先色板语义逐步映射为主题语义:
- `vivid`:动感活泼。
- `enterprise`:企业沉稳。
- `intelligent`:专业智能。
为了减少现有 CSS 变量和本地存储影响,前端可以在兼容期保留 `themeSkin` 字段,同时在 DOM 上补充更清晰的主题标识:
```text
document.documentElement.dataset.themeSkin = value
document.documentElement.dataset.themeMode = value
```
旧值兼容策略:
- 旧的 `sky``blue``emerald` 等明亮主题默认映射到“动感活泼”。
- 旧的 `navy``slate` 等偏稳重主题默认映射到“企业沉稳”。
- 无法识别的值回退到“企业沉稳”,保证生产环境默认更克制。
设置页结构:
- 保留现有表单保存机制。
- 将原色板卡片改为三张主题选项卡。
- 每个主题卡展示名称、适用场景、视觉关键词和小型预览。
- 当前选中主题需要有明确选中态,但避免大面积彩色边框。
企业沉稳 AI 模式样式:
- 通过 `[data-theme-mode="enterprise"]``[data-theme-skin="enterprise"]` 覆写 `personal-workbench-ai-mode.css` 中的 AI 模式变量。
- 背景从多层 radial-gradient 收敛为白灰底或极轻线性渐变。
- 对话气泡使用白底、浅灰描边和稳定文字层级。
- AI 图标使用低饱和单色或品牌主色的小面积点缀。
- 思考过程、工具调用、风险提示等区域使用结构化信息块,减少发光、彩色渐变和装饰性元素。
- 风险、成功、警告等语义色保留,但只在图标、状态条或小面积标签上表达。
专业智能主题样式:
- 保留少量 AI 识别色,例如主色强调、轻量渐变按钮或小面积状态标识。
- 背景和卡片仍以企业应用的可读性为主。
- 避免全屏强背景、过多彩色阴影和过密装饰。
## 算法与公式
本功能不涉及复杂算法。
需要定义稳定的主题值映射函数:
```text
normalizeThemeMode(rawTheme):
if rawTheme in ["vivid", "sky", "blue", "emerald", "purple"]:
return "vivid"
if rawTheme in ["enterprise", "navy", "slate", "gray"]:
return "enterprise"
if rawTheme in ["intelligent"]:
return "intelligent"
return "enterprise"
```
主题应用顺序:
```text
后端保存值 / 本地缓存值
-> normalizeThemeMode
-> 写入 appearanceForm.themeSkin
-> 写入 root dataset
-> 应用 CSS 变量
-> AI 模式按 data-theme-mode 覆写组件样式
```
## 测试方案
前端单元和静态测试:
- 断言系统设置外观入口显示为“主题设置”。
- 断言页面标题显示为“主题风格与界面体验”。
- 断言三类主题均可见:动感活泼、企业沉稳、专业智能。
- 断言企业沉稳主题保存后写入稳定主题值。
- 断言旧主题值能通过 normalize 逻辑回退到可识别主题。
- 断言 AI 模式存在企业沉稳主题 CSS 钩子。
构建验证:
- 运行前端构建,确保主题设置改动不破坏现有页面。
- 运行已有设置相关测试,确保系统设置保存链路不回归。
真实页面验收:
- 打开 `/app/settings?section=appearance`,确认左侧菜单、页面标题、三类主题和保存反馈符合预期。
- 切换到“企业沉稳”后打开 AI 工作台,确认对话区域、图标、消息、思考过程和提示块明显减少彩色渲染。
- 切回“动感活泼”,确认现有风格仍可正常展示。
- 切到“专业智能”,确认介于两者之间,不退化为纯色板换色。
## 指标与验收
功能验收:
- “界面皮肤”在设置入口和页面主标题中完成改名。
- 用户可以选择三类主题,而不是面对一堆颜色皮肤。
- 选择主题后刷新页面仍保持选中态。
- 企业沉稳主题下AI 模式整体视觉明显更接近企业 SaaS。
- 动感活泼主题不丢失现有活力风格。
- 专业智能主题具备独立视觉边界。
设计验收:
- 企业沉稳主题下不出现大面积高饱和渐变背景。
- AI 对话图标和卡片不依赖强发光、强阴影或多彩背景表达层级。
- 风险、审批、单据、工具调用等业务信息优先使用结构化排版。
- 主题卡片文字不溢出,不在移动端产生拥挤或重叠。
工程验收:
- 不新增前端依赖。
- 不引入后端表结构迁移。
- 旧主题值有明确兼容策略。
- 相关测试、构建和真实页面验收通过。
## 风险与开放问题
第三类主题默认命名为“专业智能”。如果后续用户指定更贴合业务的名称,可以只替换展示文案,不影响主题值和实现结构。
仅靠全局 CSS 变量可能无法完全消除 AI 模式的活泼感。企业沉稳主题需要对 AI 模式局部样式做有针对性的覆写。
旧色板值如果直接隐藏,可能让已有用户困惑。首期需要在兼容层处理旧值,并保证保存一次后落到新的三类主题值。
如果后续需要租户品牌色和主题组合,应该把“主题模式”和“品牌主色”拆成两个独立配置,避免再次把体验风格退化成颜色选择。
## 本轮文档记录
本轮已完成主题设置功能的前端实现:系统设置入口改名为“主题设置”,主题选项收敛为“动感活泼 / 企业沉稳 / 专业智能”三类,并通过主题归一化兼容旧色板值。
企业沉稳主题已联动 AI 工作台样式:根节点写入 `data-theme-mode="enterprise"`AI 模式背景、图标容器、输入框、消息、思考过程和建议动作改为低饱和、轻描边、少渲染的企业 SaaS 风格。

View File

@@ -0,0 +1,69 @@
# 主题设置与企业沉稳 AI 模式 开发 TODO
更新时间2026-06-25
文档路径document/development/2026-06-25/feature/theme-settings-enterprise-ai-style/TODO.md
## 使用规则
- 每个任务都需要关联 `CONCEPT.md` 中的章节,格式为 `[CONCEPT: 章节名]`
- 完成实现后再勾选对应任务,不用文档勾选代替代码验证。
- 涉及真实页面效果的任务,需要在 5173 页面完成验收后再标记完成。
## 1. 调研与边界
- [x] [CONCEPT: 背景与问题] 确认当前设置外观入口仍使用“界面皮肤”语义,后续需要改为“主题设置”。
- [x] [CONCEPT: 方案设计] 确认当前主题能力主要依赖 `appearanceForm.themeSkin`、主题选项和根节点 dataset。
- [x] [CONCEPT: 方案设计] 确认企业沉稳 AI 模式主要需要覆写 `personal-workbench-ai-mode.css` 中的背景、对话、图标和提示块样式。
- [x] [CONCEPT: 风险与开放问题] 梳理旧色板值到三类主题值的完整映射清单。
- [x] [CONCEPT: 用户与场景] 确认三类主题在设置页中的展示顺序和说明文案。
## 2. 契约与设计
- [x] [CONCEPT: 功能能力] 将主题枚举收敛为 `vivid``enterprise``intelligent`
- [x] [CONCEPT: 功能能力] 明确三类主题的中文名称、适用场景和视觉关键词。
- [x] [CONCEPT: 方案设计] 设计 `normalizeThemeMode` 兼容函数,保证旧值和未知值都有稳定回退。
- [x] [CONCEPT: 方案设计] 决定是否新增 `themeMode` 前端概念,并保持与 `themeSkin` 字段兼容。
- [x] [CONCEPT: 指标与验收] 定义企业沉稳 AI 模式的视觉验收标准。
## 3. 后端实现
- [x] [CONCEPT: 方案设计] 评估后端 settings schema 是否需要补充主题枚举校验。
- [x] [CONCEPT: 方案设计] 若继续复用 `themeSkin`,确保后端允许新主题值保存。
- [ ] [CONCEPT: 测试方案] 补充或更新设置持久化测试,覆盖三类主题值。
- [x] [CONCEPT: 风险与开放问题] 确认不需要数据库结构迁移,并在实现说明中记录。
## 4. 算法/规则实现
- [x] [CONCEPT: 算法与公式] 实现旧主题值到新主题值的 normalize 逻辑。
- [x] [CONCEPT: 算法与公式] 为未知值设置默认回退策略,优先回退到“企业沉稳”。
- [x] [CONCEPT: 算法与公式] 确保本地缓存、后端返回值和根节点 dataset 使用同一套归一化结果。
## 5. 前端实现
- [x] [CONCEPT: 功能能力] 将设置左侧菜单“界面皮肤”改为“主题设置”。
- [x] [CONCEPT: 功能能力] 将页面标题改为“主题风格与界面体验”。
- [x] [CONCEPT: 功能能力] 将保存反馈和说明文案从“皮肤”语义调整为“主题”语义。
- [x] [CONCEPT: 功能能力] 将原色板式选项调整为三类主题卡片。
- [x] [CONCEPT: 功能能力] 为“动感活泼”保留当前视觉风格。
- [x] [CONCEPT: 方案设计] 为“企业沉稳”新增 AI 模式样式覆写。
- [x] [CONCEPT: 方案设计] 为“专业智能”新增介于活泼和沉稳之间的样式边界。
- [x] [CONCEPT: 指标与验收] 检查主题卡片、按钮和说明文字在移动端不溢出、不重叠。
- [x] [CONCEPT: 方案设计] 保证刷新页面后主题选择和 AI 模式样式仍然一致。
## 6. 测试与验证
- [x] [CONCEPT: 测试方案] 更新设置页相关前端测试,断言“主题设置”和三类主题选项。
- [x] [CONCEPT: 测试方案] 补充 normalize 逻辑测试,覆盖旧值、未知值和三类新值。
- [x] [CONCEPT: 测试方案] 补充 AI 模式企业沉稳 CSS 钩子测试或静态断言。
- [x] [CONCEPT: 测试方案] 运行前端设置相关定向测试。
- [x] [CONCEPT: 测试方案] 运行 `npm --prefix web run build`
- [x] [CONCEPT: 测试方案] 运行 `git diff --check`
- [x] [CONCEPT: 测试方案] 在真实 5173 页面验收 `/app/settings?section=appearance`
- [ ] [CONCEPT: 测试方案] 在真实 5173 页面验收 AI 工作台三类主题切换效果。
## 7. 文档收尾
- [x] [CONCEPT: 本轮文档记录] 实现完成后更新本文勾选状态。
- [x] [CONCEPT: 指标与验收] 在最终交付说明中记录测试、构建和真实页面验收结果。
- [ ] [CONCEPT: 风险与开放问题] 若第三类主题命名发生变化,同步更新概念文档和测试描述。

View File

@@ -0,0 +1,37 @@
# 多 task 串行推进时 task2(业务招待费报销)无法启动
## 修复记录
- 12:14记录 bug 修复:多 task 串行推进时task1出差申请做完后点击"继续处理费用报销"task2业务招待费报销根本无法启动报销草稿交互不起来task2 语义(招待费/2000元/昨天)全部丢失。
- Git 提交检查:`git fetch --all --prune` 因远端 SSL 连接失败未拉到新内容;`HEAD..@{u}` 为空(无 upstream 新提交);`@{u}..HEAD` 本地领先 8 个提交,其中与本 bug 相关的前置提交为 `3a5664c4 feat(web): 多 task 串行推进``3e4b1e15 fix(web): 保存草稿/提交成功后也推进到下一个 task``43c3ff86 fix(web): steward plan 确认按钮直接拉起申请预览,不丢失 remaining tasks`
- 修改:
- `web/src/composables/workbenchAiMode/useWorkbenchAiActionRouter.js``travel_reimbursement` 分支不再硬编码 `requiresApplicationBeforeReimbursement=true`,改为从 `steward_current_task.ontology_fields` 解析费用类型并调 `requiresApplicationBeforeReimbursement(expenseType)` 判断;用 `buildAiExpenseDraftPrefillValues` 把 task 语义(金额/时间/事由/地点)预填进报销草稿;透传 `steward_remaining_tasks``ai_application_start_inline` 分支也透传 `prefill_values``steward_remaining_tasks`,让"查不到申请单→发起申请单"后能回到 task2。`buildNextTaskSuggestedAction` payload 补 `steward_remaining_tasks: remainingTasks.slice(1)`,防 3+ task 断链。
- `web/src/composables/workbenchAiMode/useWorkbenchAiExpenseFlow.js``startAiExpenseDraft` 扩展第 4 参 `options``prefillValues`/`stewardRemainingTasks``resolveAiExpenseApplicationLink` 接收 options查不到申请单时按费用类型动态生成"确认发起业务招待申请"按钮(不再写死"出差申请"),并透传 `prefill_values`/`steward_remaining_tasks`;新增 `attachStewardRemainingTasks`/`resolveStewardRemainingTasks`/`resolveRequiredApplicationLabel`/`buildExpenseDraftNextTaskAction` helper把 remaining tasks 上下文挂在 draft 上贯穿报销→关联申请单→pollLinkedDraftJob 全流程;`advanceAiExpenseDraft``linkAiExpenseApplication``replaceInlineAssistantMessage``resumePendingLinkedReimbursementDraftJobs` 均补 remaining tasks 透传。
- `web/src/composables/workbenchAiMode/useWorkbenchAiApplicationPreviewFlow.js``buildApplicationPreviewNextTaskAction` payload 补 `steward_remaining_tasks: remainingTasks.slice(1)`
- `web/src/utils/aiExpenseDraftModel.js``createAiExpenseDraft` 新增第三参 `prefillValues`,按已填值推进 `stepKey` 到第一个未填字段;新增 `buildAiExpenseDraftPrefillValues` 把 task ontology 映射到草稿字段。
- `web/src/composables/workbenchAiMode/workbenchAiMessageModel.js``createInlineMessage`/`normalizeRuntimeMessage`/`serializeRuntimeMessage` 三处补 `stewardRemainingTasks` 字段读写,避免消息重建/刷新丢失推进上下文。
- `web/src/utils/aiWorkbenchConversationStore.js``normalizeMessage` 持久化时保留 `stewardRemainingTasks`
- 操作:未提交(工作区有大量预先存在的未提交改动,未自动提交)。容器 `local-x-financial-linux` 此前未运行,已 `docker start` 恢复。
- 验证在容器内node v22.22.3)跑 `node --test tests/ai-expense-draft-model.test.mjs tests/ai-workbench-conversation-store.test.mjs tests/workbench-ai-action-router.test.mjs`16 个测试全部通过(含新增的 `buildAiExpenseDraftPrefillValues``createAiExpenseDraft` 预填、`stewardRemainingTasks` 持久化、`travel_reimbursement` 分支预填+透传 4 个用例。其余前端测试失败为基线已存在git stash 对比确认,与本次改动无关,多为正则匹配源码文本的断言因工作区其他未提交改动而失败)。
- 影响:用户一次提问含"出差申请 + 招待费报销"等多 task 时task1 完成后 task2 现在能正常启动招待费类型、2000元金额、时间、事由会预填到报销草稿招待费需要前置招待申请单业务规则保留查不到时按钮文案按类型动态展示并承接语义发起申请单后能回到 task23+ task 不再断链;刷新会话后推进上下文不丢失。
- 12:23继续修复同一 bug模型计划已经返回“出差申请 + 业务招待费报销”两个 task 时AI 工作台入口只消费第一个申请 task第二个 task 没有自动开始。
- Git 提交检查:`git fetch --all --prune` 仍因 `LibreSSL SSL_connect: SSL_ERROR_SYSCALL` 失败;`HEAD..@{u}` 为空,未发现可见 upstream 新提交;`@{u}..HEAD` 本地领先 8 个提交:`43c3ff86 fix(web): steward plan 确认按钮直接拉起申请预览,不丢失 remaining tasks``3e4b1e15 fix(web): 保存草稿/提交成功后也推进到下一个 task``3a5664c4 feat(web): 多 task 串行推进 - task1 完成后自动展示 task2 确认按钮``d139a63e refactor(server): 意图识别改 LLM 驱动,规则只做闲聊拦截+resume 兜底``8a2ae6eb fix(server): gate_classify 复用 _classify_irrelevant_input 修复 off_topic 误杀``992cf71f refactor(server): Phase 1 图拓扑重构 - LangGraph 成为唯一编排者``54356ba8 refactor(server): scene 注册表骨架 + 统一门控管道设计文档``e9d7c56d feat(server): 会话上下文保留LLM 历史 + 确定性兜底双保险)`
- 修改:`workbenchAiIntentPlannerModel.js` 在归一化模型计划时保留 application task 后面的 `stewardRemainingTasks`,并只在存在剩余 task 时放进可执行申请请求;`usePersonalWorkbenchAiMode.js` 把 remaining tasks 传入申请预览,并在预览生成后通过现有 `steward_continue_next_task` 路由自动启动下一个 task低置信确认按钮 payload 也继续携带队列;`useWorkbenchAiApplicationPreviewFlow.js` 在普通申请预览完成后调用 `onPreviewReadyForNextTask``useWorkbenchAiActionRouter.js``ai_application_confirm_intent` 分支同样透传 remaining tasks 并注册自动续跑回调。
- 操作:新增回归测试覆盖“模型返回申请 task + 业务招待报销 task 时,前端可执行请求保留第二个 task”以及“低置信确认按钮不丢队列并提供自动续跑回调”保留既有未提交工作区改动没有回滚或提交其他文件。
- 验证:先看到新增测试红灯(`plan.stewardRemainingTasks``undefined`,确认按钮 options 也没有 remaining tasks修复后 `node --test web/tests/workbench-ai-intent-planner-model.test.mjs web/tests/workbench-ai-action-router.test.mjs` 通过 24/24`git diff --check` 通过;`docker exec -w /app -e SERVER_VENV_DIR=/tmp/x-financial-server-venv x-financial-local-linux timeout 60s /tmp/x-financial-server-venv/bin/pytest -q server/tests/test_steward_planner.py -k 'future_travel_without_apply_word_as_application or uses_llm_for_multi_financial_demands'` 通过 2/2同命令在 `local-x-financial-linux` 收集阶段因该容器缺 `langgraph` 失败,已改用当前映射 5173 的 `x-financial-local-linux` 验证;`npm --prefix web run build` 通过;真实 `http://localhost:5173/api/v1/steward/plans` 采样确认该用户句子返回 `expense_application``reimbursement` 两个 task。
- 影响用户输入“2月20-23日去上海出差3天服务国网服务器部署并且报销昨天的业务招待费2000元”时前端不再在第一个申请预览处截断队列申请预览生成后会自动把第二个业务招待费报销 task 交给现有报销流程继续处理,同时刷新/低置信确认路径也不丢 task 队列。
- 22:30继续修复同一 bug用户点击“保存草稿”后task1 已经完成,但界面仍只展示“继续处理费用报销”按钮,没有自动开始 task2。
- Git 提交检查:`git fetch --all --prune` 仍因 `LibreSSL SSL_connect: SSL_ERROR_SYSCALL` 失败;`HEAD..@{u}` 为空,未发现可见 upstream 新提交;`@{u}..HEAD` 本地仍领先 8 个提交:`43c3ff86``3e4b1e15``3a5664c4``d139a63e``8a2ae6eb``992cf71f``54356ba8``e9d7c56d`
- 修改:`useWorkbenchAiApplicationPreviewFlow.js` 在保存草稿/提交申请成功后,如果当前申请消息还有 `stewardRemainingTasks`,就调用 `onApplicationActionCompleted` 自动续跑下一项;自动续跑时结果消息只保留查看详情动作,不再额外保留“继续处理”按钮,避免重复触发同一个 task。`usePersonalWorkbenchAiMode.js` 将该回调接到 `startModelPlannedNextTask``useWorkbenchAiActionRouter.js` 的低置信确认路径也同步传入保存/提交成功回调。
- 操作:先补红灯断言,要求工作台入口向申请预览流传 `onApplicationActionCompleted`,且申请预览流在保存/提交成功分支调用该回调;然后按同一条 `steward_continue_next_task` 路由复用原有报销启动逻辑。
- 验证:红灯阶段 `node --test web/tests/workbench-ai-intent-planner-model.test.mjs` 失败在缺少 `onApplicationActionCompleted`;修复后 `node --test web/tests/workbench-ai-intent-planner-model.test.mjs` 17/17 通过,`node --test web/tests/workbench-ai-action-router.test.mjs` 7/7 通过;`git diff --check` 通过;`npm --prefix web run build` 通过。
- 影响:复合任务里用户保存第一张出差申请草稿后,系统会立即进入第二个业务招待费报销任务,不再要求用户手动点击“继续处理费用报销”;低置信确认后再保存草稿也保持同样行为。
- 22:45继续修复同一 bug真实页面已经显示“申请草稿已保存”但保存动作结束后没有继续拉起第二个业务招待费报销 task。
- Git 提交检查:`git fetch --all --prune` 仍因 `LibreSSL SSL_connect: SSL_ERROR_SYSCALL` 失败;`HEAD..@{u}` 为空,未发现可见 upstream 新提交;`@{u}..HEAD` 本地领先 13 个提交:`6bdaeed6 chore: 忽略 .zcode 本地目录并更新规则表与开发日志``d5a8f847 refactor(web): 应用外壳/差旅详情/报销创建视图适配主题与多 task``c4b5fcc0 feat(web): AI 工作台多 task 串行推进与会话适配``5753899e feat(web): 主题皮肤系统与 LLM 设置面板重构``9c3fa80d feat(server): 设置持久化新增 LLM 模型表与主题字段`,以及前面记录过的 8 个本地 ahead 提交。
- 修改:`useWorkbenchAiApplicationPreviewFlow.js` 在保存草稿/提交申请成功分支新增 `actionCompletedHandler`,优先使用本次 `executeInlineApplicationPreviewAction` / `startAiApplicationPreview` options 传入的 `onApplicationActionCompleted`,没有时再回落到 composable 初始化回调;自动保存草稿时同步把 `options.onApplicationActionCompleted` 透传给保存动作。`workbench-ai-application-context-submit.test.mjs` 新增“自动保存申请草稿后继续剩余 steward task”的行为测试`workbench-ai-intent-planner-model.test.mjs` 更新结构断言,锁住 options 回调优先和自动保存透传契约。
- 操作:先用新增测试复现红灯:自动保存时确实只触发初始化回调,`options` 里的续跑回调没有被使用;再做最小修复,让保存动作按本次调用上下文续跑下一任务。
- 验证:红灯阶段 `node --test web/tests/workbench-ai-application-context-submit.test.mjs` 失败在 `fromOptions``undefined`;修复后该测试 2/2 通过;`node --test web/tests/workbench-ai-intent-planner-model.test.mjs` 17/17 通过;`node --test web/tests/workbench-ai-action-router.test.mjs` 7/7 通过;真实 `http://127.0.0.1:5173/api/v1/steward/plans` 采样确认该用户句子仍返回 `expense_application` + `reimbursement` 两个 task`npm --prefix web run build` 通过;`git diff --check` 通过。
- 影响:保存草稿结果消息到达后,前端会使用当前任务链路传下来的续跑回调立即处理剩余 task用户截图里的“申请草稿已保存”不再是终点后续业务招待费报销会自动进入现有报销流程。

View File

@@ -0,0 +1,17 @@
# 2026-06-26 综合工作日志
生成时间2026-06-26 17:45:36 CST
来源:`feature/` 功能点文档与 `dev-logs/bugs/` bug 记录
## 今日功能点
- 今日未发现功能点文档。
## 今日 Bugs
- 多 task 串行推进时 task2(业务招待费报销)无法启动影响用户输入“2月20-23日去上海出差3天服务国网服务器部署并且报销昨天的业务招待费2000元”时前端不再在第一个申请预览处截断队列申请预览生成后会自动把第二个业务招待费报销 task 交给现有报销流程继续处理,同时刷新/低置信确认路径也不丢 task 队列。(文件:`multi-task-reimbursement-not-starting.md`
## 综合分析
- 问题侧记录了 1 个 bug 修复。
- 后续复盘优先看本文件,再回到对应功能点或 bug 文件追溯证据。

View File

@@ -0,0 +1,17 @@
# 2026-06-27 综合工作日志
生成时间2026-06-27 17:43:24 CST
来源:`feature/` 功能点文档与 `dev-logs/bugs/` bug 记录
## 今日功能点
- 今日未发现功能点文档。
## 今日 Bugs
- 今日未发现 bug 修复记录。
## 综合分析
- 今日目录下暂无功能点或 bug 记录。
- 后续复盘优先看本文件,再回到对应功能点或 bug 文件追溯证据。

View File

@@ -0,0 +1,17 @@
# 2026-06-28 综合工作日志
生成时间2026-06-28 18:30:35 CST
来源:`feature/` 功能点文档与 `dev-logs/bugs/` bug 记录
## 今日功能点
- 今日未发现功能点文档。
## 今日 Bugs
- 今日未发现 bug 修复记录。
## 综合分析
- 今日目录下暂无功能点或 bug 记录。
- 后续复盘优先看本文件,再回到对应功能点或 bug 文件追溯证据。

View File

@@ -0,0 +1,190 @@
# AI 数据飞轮 概念文档
更新时间2026-07-03
文档路径document/development/2026-07-03/feature/ai-data-flywheel/CONCEPT.md
## 功能一句话
把用户反馈、人工修正、风险样本、评测结果自动沉淀并回流到下一次 LLM 推理与规则生成,让费控系统在不停机的情况下持续提升准确率、降低误报率和人工干预率。
## 背景与问题
- 当前现状:项目已具备"聪明"的骨架——RAG`knowledge_rag_runtime.py` + Qdrant、风险规则自动生成`risk_rule_generation*.py`)、反馈样本沉淀(`skills/domain/false-positive-sample-accumulator` 等 3 个 accumulator、规则回放评测`risk-algorithm-replay-evaluator`)、行为画像(`employee_behavior_profile*`)、用户反馈表(`agent_feedback.py` + `AgentOperationFeedback`)。
- 用户痛点:样本在往 accumulator 池子里堆,但**下一次 LLM 推理时并没有把这些样本检索出来当 few-shot 喂进去**prompt 散落在各 `*_prompt.py`,没有版本号、没有在线 A/B、没有回归门禁OCR 抽取的人工修正值没有回流成评测/训练数据;低分反馈只汇总不归因。
- 业务影响:系统每次推理都从"初始水平"出发,无法把历史踩过的坑转化成下一次的能力;改 prompt / 改规则无法证明是否变好,存在隐性回归风险;运营和算法同事看不到"系统在进步"的证据。
- 为什么现在需要做:飞轮骨架已齐,缺的是把"样本池 → 检索注入 + 评测门禁 → prompt/规则版本"这段断开的箭头接上。补上后整张图就转起来,且改动集中在 prompt 构造层 + 新增 eval 目录,不动业务主链路,风险低。
## 目标与非目标
### 目标
- [G1] few-shot 在线检索注入:推理前从样本池按 case 特征做向量检索,取 top-k 历史样本(含人工结论)拼进 system prompt。
- [G2] 黄金评测集 + 自动回归门禁:版本化 golden setprompt/规则变更后在 golden set 上自动跑分,分数不达标禁止发布。
- [G3] Prompt 版本化 + Canary A/Bprompt 进表带版本号,支持 stable / canary 流量切分,反馈分数对比。
- [G4] 抽取修正回流:附件/明细字段的人工修正值记录为 diff沉淀为抽取评测集与 few-shot 样本。
- [G5] 低分反馈自动归因:低分反馈触发归因 agent拉 trace 诊断错误环节并生成改进任务。
- [G6] AI 智商看板:每周自动跑 golden set输出准确率/召回率/误报率/人工干预率随时间的曲线。
### 非目标
- [NG1] 本轮不做模型微调 / 自训练:只走 prompt 侧的 in-context learning + 规则学习。
- [NG2] 本轮不改变现有业务主链路(申请单、报销、审批)的接口契约。
- [NG3] 本轮不替换 Qdrant / LightRAG 底座,复用现有向量存储与 embedding 配置。
- [NG4] 政策新鲜度检测(外部政策变更 → 自动重生成规则)后续再评估,本轮只在评测门禁侧预留接口。
## 用户与场景
- 目标用户:
1. 报销人 / 申请人:感知到系统越来越准,少打回、少补件。
2. 财务审批人:误报率下降,审批被打断的次数减少。
3. 算法/运营同学:能看到智商曲线、能灰度上线 prompt、能跑回归评测。
- 使用入口:
- 推理时自动注入 few-shot对用户透明
- 后台 Canary 控制台(运营切流量、看分数)。
- AI 智商看板(周报 / 在线查询)。
- 核心场景:
1. 用户提交报销 → 系统预审 → 预审 prompt 自动注入相似历史误报样本 → 给出更准结论。
2. 算法同学改了 risk rule 生成 prompt → 发布前自动跑 golden set → 不达标被门禁拦下。
3. 用户给低分 → 归因 agent 诊断"是检索没召回 / 规则误判 / 回复格式问题"→ 自动建改进任务并回写样本池。
4. 审批人改了 OCR 抽错的金额 → diff 自动沉淀 → 下次同类票据抽取 prompt 多一条 few-shot。
- 异常场景:
- 样本池为空或检索失败 → 退化为无 few-shot 推理,不阻塞主链路。
- 评测门禁服务不可用 → 默认放行 stablecanary 自动暂停。
- Canary 候选 prompt 分数劣化 → 自动回滚到 stable。
## 功能能力
- [C1] 输入能力:消费 accumulator 样本池、`AgentOperationFeedback`、附件修正 diff、trace 数据作为飞轮原料。
- [C2] 处理能力:样本检索(向量 + 元数据过滤)、评测打分(准确率/召回率/误报率/F1、Canary 流量切分、低分归因。
- [C3] 输出能力few-shot 注入后的 messages、评测报告、智商曲线、改进任务、归因结论。
- [C4] 状态与权限:样本带"人工已确认"标签才进可注入集合prompt 版本有 stable/canary/pinned 状态;评测门禁可由运营关闭(审计可见)。
- [C5] 边界与降级检索失败、评测失败、Canary 失败均降级到 stable不阻塞业务推理。
## 方案设计
### 前端
- 页面/组件:
- AI 智商看板(新页面,复用 `finance-report` 看板骨架):准确率/召回率/误报率/人工干预率随时间曲线 + golden set 覆盖度。
- Canary 控制台(并入 `SettingsView` / `PoliciesView`):列出各场景 prompt 版本、流量比例、当前分数、一键回滚。
- 交互状态:加载/空态(样本不足)/错误态(评测失败)/权限态(仅算法运营)。
- 展示规则:曲线按场景(差旅/报销/预算分面Canary 显示置信区间,差异不显著时标注。
- 降级处理:看板数据不可用时提示"数据生成中",不报错。
### 后端
- 接口/服务(新增,按职责拆分,单文件 ≤ 800 行):
- `services/few_shot_retrieval.py`:样本检索器,复用 Qdrant输入 case 特征 → 输出 top-k 样本(带人工结论)。
- `services/prompt_registry.py`prompt 版本注册中心,按场景 + 策略latest/canary/pinned取 prompt。
- `services/eval_harness.py`:在 golden set 上跑评测,输出指标;被发布门禁和智商看板共用。
- `services/feedback_attribution.py`:低分归因 agent复用 `AgentTraceCenter` 数据。
- `services/extraction_correction_recorder.py`:记录 OCR 抽取字段的人工修正 diff。
- 改造点(在现有 prompt 构造文件加 inject 钩子,不改业务接口):
- `risk_rule_generation_prompt.py``user_agent_application.py``expense_claim_pre_review.py``document_intelligence_rules.py``ontology_extraction.py` 的 prompt 构造处。
- 权限与校验Canary 控制台仅算法/运营角色;门禁关闭需审计日志。
- 持久化新表Alembic 迁移):
- `prompt_version`id / scene / content / version / status(stable/canary/pinned) / eval_score / created_by / created_at。
- `golden_set`id / scene / case_payload / expected / source(accumulator/manual) / confirmed / version。
- `extraction_correction`id / attachment_id / field / raw_value / corrected_value / operator / created_at。
- `eval_run`id / prompt_version_id / scene / metrics_json / started_at / finished_at。
- 降级处理:所有飞轮组件故障均降级到无 few-shot + stable prompt主链路不阻塞。
### 算法与规则
- 输入case 特征向量(场景标签 + 文本摘要 + 关键字段、golden set、反馈样本、修正 diff。
- 流程:
1. 推理前:`few_shot_retrieval` 检索 top-k → 拼 system prompt。
2. 推理后:结果 + 反馈写入 accumulator。
3. 发布前:`eval_harness` 在 golden set 上跑分 → 门禁判定。
4. 低分触发:`feedback_attribution` 归因 → 改进任务回写样本池。
- 输出few-shot 样本块、评测指标、归因结论、智商曲线数据点。
- 解释few-shot 注入在 prompt 中保留"参考案例(历史已确认)"段落,可追溯;评测报告附错误 case 列表;归因输出错误环节标签 + 证据 trace 片段。
### 数据与契约
- 核心字段scene、case_signature、few_shot_samples、metrics(acc/recall/fpr/f1)、prompt_version_id、status。
- 状态枚举:
- prompt: `stable` / `canary` / `pinned` / `archived`
- golden case: `draft` / `confirmed` / `deprecated`
- eval: `pass` / `fail` / `blocked`
- 兼容策略prompt_registry 找不到版本时回退到当前硬编码 prompt保证向后兼容
- 版本/审计:每次 prompt / 规则 / golden set 变更记 `eval_run`,可回放历史。
## 算法与公式
### few-shot 检索排序
```text
score(sample, case) = α * sim(emb(sample), emb(case)) + β * match(meta(sample), meta(case))
```
变量说明:
- score样本与当前 case 的综合相似度。
- sim余弦相似度复用 Qdrant 现有 embedding。
- match元数据硬匹配得分场景同 / 域同 / 级别同),取 0 或 1。
- α、β:权重,默认 α=0.8、β=0.2,可在 prompt_registry 中按场景覆盖。
- 适用边界:仅取 `confirmed=true` 的样本top-k 默认 k=3按 token 预算动态裁剪。
### 评测指标
```text
precision = TP / (TP + FP)
recall = TP / (TP + FN)
f1 = 2 * precision * recall / (precision + recall)
```
变量说明:
- TP/FP/FN在 golden set 上推理结论与 expected 比对得出(结论为风险标记/字段值/分类标签三类场景各有比对器)。
- 发布门禁默认阈值recall ≥ 上一版 stable 的 recall 且 f1 不下降超过 2 个百分点,否则 `fail`
## 测试方案
后端:
- `few_shot_retrieval` 单测:样本池空 / 检索失败 / top-k 截断 / 仅取 confirmed 样本。
- `eval_harness` 单测golden set 跑分指标正确性、门禁通过/拦截逻辑、空 golden set 降级。
- `prompt_registry` 单测按策略取版本、回退到硬编码、Canary 流量切分比例。
- `feedback_attribution` 单测mock trace 数据,归因标签正确性。
前端:
- AI 智商看板视图模型:空态、加载态、错误态、曲线渲染。
- Canary 控制台:列表、切流量、回滚交互。
集成:
- 端到端:构造一份 golden set → 改 prompt → 发布被门禁拦截 / 通过 → 智商看板出现新数据点。
- 容器内运行:`docker exec -w /app -e SERVER_VENV_DIR=/tmp/x-financial-server-venv local-x-financial-linux /tmp/x-financial-server-venv/bin/pytest -q server/tests/...`,超时 60s。
手工验证:
- 在 AI 工作台触发一次预审,确认 prompt 中出现 few-shot 块。
- 在 Canary 控制台发布一版劣化 prompt确认被门禁拦下。
## 指标与验收
- [A1] 功能验收:推理时 prompt 中可见 few-shot 块,且样本来自 confirmed 池。
- [A2] 性能指标few-shot 检索 ≤ 200msP95不显著拖慢主链路eval 单场景 ≤ 60s。
- [A3] 质量指标golden set 覆盖至少 5 个核心场景;门禁能正确拦截劣化 prompt。
- [A4] 安全/权限指标Canary 控制台仅算法/运营可操作;门禁关闭记审计日志。
- [A5] 可观测性AI 智商看板按周生成曲线;每次 eval_run 可回放。
## 风险与开放问题
- 风险:
- few-shot 注入增加 prompt 长度,可能触发 token 上限或拖慢推理 → 用 token 预算裁剪 + P95 监控兜底。
- 样本池噪音(错误标注)污染推理 → 只取 confirmed 样本 + 评测门禁把关。
- 评测 golden set 与线上分布漂移 → 季度复审 golden set标注漂移度。
- 已处理依赖:复用 Qdrant / LightRAG / accumulator / AgentTraceCenter / risk_rule_generation 现有能力。
- 待确认:
- Canary 流量切分的具体比例(建议 90/10需与业务确认。
- 智商看板放哪个一级菜单Settings 还是独立"AI 运营"菜单)。
- 政策新鲜度检测是否本轮接入。
- 降级策略:任何飞轮组件故障 → 无 few-shot + stable prompt + 跳过门禁(仅 stable保证业务连续。
## 本轮实现记录
- 2026-07-03完成数据飞轮概念文档与开发 TODO 拆分,作为后续改造的总纲。

View File

@@ -0,0 +1,98 @@
# AI 数据飞轮 开发 TODO
更新时间2026-07-03
文档路径document/development/2026-07-03/feature/ai-data-flywheel/TODO.md
## 使用规则
- 每个 TODO 必须对应 `CONCEPT.md` 中的目标、能力、方案或验收点。
- 只有完成并验证后,才能把 `[ ]` 改成 `[x]`
- 勾选时在任务后补充简短证据,例如文件、接口、命令或验证结果。
- 如果需求发生变化,先更新 `CONCEPT.md`,再调整本 TODO。
- 实施顺序建议:阶段 1 → 2 → 3飞轮 1+2 是地基)→ 4/5/6 并行 → 7。
## 1. 调研与边界
- [x] [CONCEPT: 背景与问题] 盘点现有 accumulator / feedback / RAG / 规则生成能力,确认飞轮骨架已存在、断点在"检索注入 + 评测门禁"。
证据:`server/src/app/skills/domain/{false-positive-sample-accumulator,risk-feedback-sample-accumulator,risk-clue-collector}``services/agent_feedback.py``services/knowledge_rag_runtime.py``services/risk_rule_generation*.py`
- [x] [CONCEPT: 目标与非目标] 确认本轮范围 = 飞轮 1-6few-shot 注入 / golden set 门禁 / prompt 版本化 / 抽取修正回流 / 低分归因 / 智商看板),不做模型微调、不改业务接口契约。
证据CONCEPT.md「目标与非目标」章节。
- [ ] [CONCEPT: 风险与开放问题] 与业务确认 Canary 流量比例、智商看板菜单位置、政策新鲜度检测是否本轮接入。
证据:
## 2. 契约与设计
- [ ] [CONCEPT: 功能能力] 定义 4 张新表的字段、状态枚举prompt stable/canary/pinned/archived、golden draft/confirmed/deprecated、eval pass/fail/blocked、correction
证据:
- [ ] [CONCEPT: 方案设计] 明确 5 个新 service 的职责边界与 inject 钩子点risk_rule_generation_prompt / user_agent_application / expense_claim_pre_review / document_intelligence_rules / ontology_extraction
证据:
- [ ] [CONCEPT: 算法与公式] 确认 few-shot 检索排序公式权重(默认 α=0.8 β=0.2与门禁阈值recall 不降、f1 下降 ≤ 2pp
证据:
- [ ] [CONCEPT: 指标与验收] 把验收点 A1-A5 转成可验证检查项,附命令与期望结果。
证据:
## 3. 后端实现
- [x] [CONCEPT: 后端] 新增 `services/few_shot_retrieval.py`:复用 Qdrant按 case 特征检索 top-k confirmed 样本,带 token 预算裁剪。
证据:`server/src/app/services/few_shot_retrieval.py``server/src/app/services/few_shot_store.py`(独立 Qdrant collection `few_shot_samples``server/src/app/services/embedding_provider.py`(公共 EmbeddingProvider复用 knowledge_rag_runtime 的 HTTP 调用)。
- [ ] [CONCEPT: 后端] 新增 `services/prompt_registry.py`prompt 版本 CRUD + 策略取版latest/canary/pinned+ 回退硬编码。
证据:飞轮 3prompt 版本化 + Canary未启动本轮只做飞轮 1。
- [ ] [CONCEPT: 后端] 新增 `services/eval_harness.py`:在 golden set 上跑评测,输出 precision/recall/f1供门禁与看板共用。
证据:飞轮 2golden set + 门禁)未启动,本轮只做飞轮 1。
- [ ] [CONCEPT: 后端] 新增 `services/feedback_attribution.py`:低分反馈触发,复用 AgentTraceCenter trace 做归因,输出错误环节标签 + 改进任务。
证据:飞轮 5低分归因未启动本轮只做飞轮 1。
- [ ] [CONCEPT: 后端] 新增 `services/extraction_correction_recorder.py`:在附件/明细字段更新处记录 raw vs corrected diff。
证据:飞轮 4抽取修正回流未启动本轮只做飞轮 1。
- [ ] [CONCEPT: 后端] Alembic 迁移prompt_version / golden_set / extraction_correction / eval_run 四张表。
证据:本轮新增的是 FewShotSample 一张表(`server/src/app/models/few_shot_sample.py`),项目靠 `Base.metadata.create_all()` 建表(无 alembic versions/ 目录),已注册到 `db/base.py``models/__init__.py`。其余三表随对应飞轮再建。
- [x] [CONCEPT: 后端] 新增 `services/few_shot_ingestion.py`RiskObservation confirmed/false_positive → FewShotSample + Qdrant 向量,在 `risk_observations.create_feedback` commit 后 hook 触发。
证据:`server/src/app/services/few_shot_ingestion.py``server/src/app/services/risk_observations.py:324-345``_maybe_ingest_few_shot` hook带 feature flag + try/except 兜底)。
- [x] [CONCEPT: 数据与契约] 在现有 prompt 构造文件加 few-shot 注入,不改业务接口。
证据:`server/src/app/services/risk_rule_generation_prompt.py`(新增 `few_shot_samples` 可选 kwarg合并进 examples 字段);`server/src/app/services/risk_rule_generation.py:271-292``_retrieve_few_shot_samples` 在构造 messages 前调用,失败降级为空)。
## 4. 算法/规则实现
- [x] [CONCEPT: 算法与规则] 实现few-shot 检索排序(向量相似度 + 元数据硬匹配),只取 confirmed 样本。
证据:`server/src/app/services/few_shot_store.py`Qdrant 余弦相似度 + payload 过滤 scene/label/status`few_shot_retrieval.py` 去重 + token 预算 + 单条字符上限裁剪。检索仅取 label ∈ {confirmed, false_positive}。
- [ ] [CONCEPT: 算法与规则] 实现评测指标比对器(风险标记 / 字段值 / 分类标签 三类场景)。
证据:飞轮 2未启动。
- [ ] [CONCEPT: 算法与规则] 接入发布门禁:`agent_asset_risk_rule_publish` 前调 eval_harness不达标 block。
证据:飞轮 2未启动。
- [ ] [CONCEPT: 算法与规则] 接入 Canary 流量切分(默认 90 stable / 10 canary+ 劣化自动回滚。
证据:飞轮 3未启动。
- [x] [CONCEPT: 结果解释] few-shot 块在 prompt 中保留 `source: "historical_confirmed"` 标记,可追溯。
证据:`risk_rule_generation_prompt.py` 合并 examples 时每条历史样本带 `source`/`label`/`conclusion` 字段。
## 5. 前端实现
- [ ] [CONCEPT: 前端] AI 智商看板新页面:准确率/召回率/误报率/人工干预率随时间曲线 + golden set 覆盖度,复用 `finance-report` 看板骨架。
证据:
- [ ] [CONCEPT: 前端] Canary 控制台(并入 Settings/Policiesprompt 版本列表、流量比例、分数、一键回滚。
证据:
- [ ] [CONCEPT: 前端] 实现加载/空态(样本不足)/错误态(评测失败)/权限态(仅算法运营)。
证据:
- [ ] [CONCEPT: 前端] 对齐现有企业后台风格(参考 `chat-ui-saas-styling` / `theme-settings-enterprise-ai-style`),避免营销页观感。
证据:
## 6. 测试与验证
- [x] [CONCEPT: 测试方案] 后端单测embedding_providerGLM/Ollama 分支、维度缓存、HTTP 错误降级、few_shot_ingestionconfirmed/false_positive 入库、ignored 跳过、幂等去重、hook 触发、feature flag、吞异常、few_shot_retrieval去重、token 预算、超长截断)+ prompt 注入(合并 examples、向后兼容
证据:`server/tests/test_embedding_provider.py``server/tests/test_few_shot_ingestion.py``server/tests/test_few_shot_retrieval_and_prompt.py`,容器内 `pytest -q` 20 passed。
- [ ] [CONCEPT: 测试方案] 前端:智商看板与 Canary 控制台视图模型 + 构建验证。
证据:飞轮 3/6 前端,未启动。
- [ ] [CONCEPT: 测试方案] 集成golden set → 改 prompt → 门禁拦截/通过 → 看板新增数据点,容器内跑通。
证据:飞轮 2 集成,未启动。
- [x] [CONCEPT: 测试方案] 回归:现有 RAG / risk_observations / risk_rule_generation 测试全过。
证据:容器内 `pytest -q server/tests/test_risk_observations_service.py server/tests/test_knowledge_rag_runtime.py server/tests/test_risk_rule_generation.py server/tests/test_risk_rule_generation_failure.py` → 35 passedEmbeddingProvider 抽离零回归。
- [ ] [CONCEPT: 指标与验收] 记录验证命令与结果,确认 P95 检索 ≤ 200ms、单场景评测 ≤ 60s。
证据:性能指标待飞轮 2 评测上线后连同 golden set 一起量。
## 7. 文档收尾
- [x] [CONCEPT: 指标与验收] 飞轮 1few-shot 注入A1 功能验收已达成:推理时 prompt 中可见带 `source: "historical_confirmed"` 的 few-shot 块,且样本来自 confirmed/false_positive 池。A5 可观测性部分达成(可追溯 source。A2/A3/A4 随飞轮 2/3 补齐。
证据:见阶段 3/4/6 已勾选项。
- [ ] [CONCEPT: 风险与开放问题] 更新 Canary 比例、看板菜单位置、政策新鲜度检测的最终结论与剩余风险。
证据:飞轮 2-6 启动后再定稿。
- [x] [CONCEPT: 功能一句话] 确认飞轮 1 实现没有偏离"让系统越用越聪明"的原始目标。
证据:人工确认风险观测 → 自动入库 + 向量化 → 下次规则编译时检索注入相似历史样本,形成"用得越多 → 样本越丰富 → 推理越准"的闭环。飞轮 2-6 待后续迭代。

View File

@@ -0,0 +1,17 @@
# 2026-07-08 综合工作日志
生成时间2026-07-08 17:01:43 CST
来源:`feature/` 功能点文档与 `dev-logs/bugs/` bug 记录
## 今日功能点
- 今日未发现功能点文档。
## 今日 Bugs
- 今日未发现 bug 修复记录。
## 综合分析
- 今日目录下暂无功能点或 bug 记录。
- 后续复盘优先看本文件,再回到对应功能点或 bug 文件追溯证据。

View File

@@ -0,0 +1,17 @@
# 2026-07-09 综合工作日志
生成时间2026-07-09 17:01:25 CST
来源:`feature/` 功能点文档与 `dev-logs/bugs/` bug 记录
## 今日功能点
- 今日未发现功能点文档。
## 今日 Bugs
- 今日未发现 bug 修复记录。
## 综合分析
- 今日目录下暂无功能点或 bug 记录。
- 后续复盘优先看本文件,再回到对应功能点或 bug 文件追溯证据。

View File

@@ -0,0 +1,17 @@
# 2026-07-10 综合工作日志
生成时间2026-07-10 17:00:31 CST
来源:`feature/` 功能点文档与 `dev-logs/bugs/` bug 记录
## 今日功能点
- 今日未发现功能点文档。
## 今日 Bugs
- 今日未发现 bug 修复记录。
## 综合分析
- 今日目录下暂无功能点或 bug 记录。
- 后续复盘优先看本文件,再回到对应功能点或 bug 文件追溯证据。

View File

@@ -0,0 +1,17 @@
# 2026-07-11 综合工作日志
生成时间2026-07-11 17:09:02 CST
来源:`feature/` 功能点文档与 `dev-logs/bugs/` bug 记录
## 今日功能点
- 今日未发现功能点文档。
## 今日 Bugs
- 今日未发现 bug 修复记录。
## 综合分析
- 今日目录下暂无功能点或 bug 记录。
- 后续复盘优先看本文件,再回到对应功能点或 bug 文件追溯证据。

View File

@@ -0,0 +1,17 @@
# 2026-07-12 综合工作日志
生成时间2026-07-12 17:11:32 CST
来源:`feature/` 功能点文档与 `dev-logs/bugs/` bug 记录
## 今日功能点
- 今日未发现功能点文档。
## 今日 Bugs
- 今日未发现 bug 修复记录。
## 综合分析
- 今日目录下暂无功能点或 bug 记录。
- 后续复盘优先看本文件,再回到对应功能点或 bug 文件追溯证据。

View File

@@ -0,0 +1,9 @@
## 修复记录
- 15:33记录 bug 修复AI 新建费用申请直接提交绕过统一事务。
- Git 提交检查:`git fetch --all --prune` 后未发现 upstream 新提交;本地 ahead 2 个既有提交,分别为 `653eda05 feat(auth): add opaque bearer sessions`(不透明 Bearer 会话与认证收口)和 `661990b2 feat(expenses): add transactional expense case events`Expense Case 与事务业务事件基础)。
- 修改:`user_agent_application.py` 将 AI 新建并直接提交改为先创建草稿,再统一调用 `ExpenseClaimService.submit_claim`,删除入口内手写提交状态和平台风险评估;提交异常或未找到单据时主动回滚,避免外层工具日志提交失败草稿。
- 修改:`budget.py` 的预算就绪检查改为复用当前 Session 连接执行 metadata 检查,避免 Engine 连接隐式提交已经 `flush` 的申请和预算数据;`test_reimbursement_endpoints.py``test_user_agent_service.py` 补齐成功提交、事件失败整体回滚、保存草稿无副作用和必填部门上下文回归。
- 操作:所有后端验证均在 `x-financial-local-linux` 容器内执行,单条命令使用 `timeout 60s`;没有修改数据库结构,没有对持久化开发数据库执行迁移,也没有重启服务。
- 验证AI 直接提交/失败回滚/保存草稿 3 项、申请提交主链路 5 项、预算与 Expense Case 13 项,共 21 项通过;相关 Python 文件 `ruff --select F,I` 通过。整份 `test_reimbursement_endpoints.py` 运行结果为 13 项通过、3 项未通过,失败分别位于既有附件风险等级断言、申请审批路由断言和中文测试请求头编码,不属于本次 AI 提交事务断言。
- 影响AI 一键新建申请现在与编辑重提共用同一提交语义预算预占、提交校验、申请风险标记、Expense Case 事件和审批状态保持一致;业务事件写入失败时,申请、预算额度、预算流水、预算预占和 Case 关联不会残留部分成功数据。

View File

@@ -0,0 +1,8 @@
## 修复记录
- 22:59记录 bug 修复AI 工作台重复保存请求会创建多张申请草稿和多条事件。
- Git 提交检查:已执行 `git fetch --all --prune`,未发现 upstream 新提交;本地 ahead 4 个既有提交,分别为 `22669a90 feat(expenses): show unified expense event timeline`(真实费用事件时间线)、`a616b30c fix(expenses): unify AI application submission transaction`AI 申请提交事务)、`653eda05 feat(auth): add opaque bearer sessions`(不透明 Bearer 会话)和 `661990b2 feat(expenses): add transactional expense case events`Expense Case 事务事件基础)。
- 修改:新增 `expense_application_draft_events.py`以租户、操作人、run ID 和稳定草稿快照生成固定长度幂等键及 UUID5 聚合 ID`user_agent_application.py` 在新建前复用已完成动作,并在并发主键竞争后回滚、重查已提交事件。持久化兜底的当前时间不参与指纹,原始申请时间仍通过申请详情快照区分。
- 操作:先在容器中分别复现“带日期相同请求”和“缺日期半成品相同请求”均生成两个不同 `claim_id`,再补动作级幂等和数据库并发仲裁路径;业务单号继续使用既有随机格式,没有修改数据库结构或执行迁移。
- 验证:相同 HTTP 保存请求连续执行两次返回同一 `claim_id``claim_no`,数据库只有一张申请草稿与一条 `claim_draft_created`;同一 run 的不同快照仍分别留痕事件失败时草稿、Case、Link 和事件整体回滚。容器内受影响后端定向回归 36 项、前端时间线兼容测试 9 项及 Python `ruff --select F,I` 通过。
- 影响:网络重试、用户重复点击或客户端重放不会制造重复申请草稿;并发请求由稳定聚合主键仲裁。真实 PostgreSQL 双会话并发集成测试仍是后续可补的非阻断验证,不影响当前顺序重放与事务契约。

View File

@@ -0,0 +1,8 @@
## 修复记录
- 22:59记录 bug 修复AI 申请预览入口可保留请求体伪造的身份和管理员权限。
- Git 提交检查:已执行 `git fetch --all --prune`,未发现 upstream 新提交;本地 ahead 4 个既有提交,分别为 `22669a90 feat(expenses): show unified expense event timeline`(真实费用事件时间线)、`a616b30c fix(expenses): unify AI application submission transaction`AI 申请提交事务)、`653eda05 feat(auth): add opaque bearer sessions`(不透明 Bearer 会话)和 `661990b2 feat(expenses): add transactional expense case events`Expense Case 事务事件基础)。
- 修改:`reimbursements.py``user_id`、租户、角色、管理员标记、用户名、姓名、部门、职位、职级、员工编号和直属经理全部强制绑定到服务端当前会话,不再对请求体同名字段使用 `setdefault``test_reimbursement_endpoints.py` 增加伪造管理员与他人身份编辑退回申请的对抗用例。
- 操作:先在容器中复现修复前接口返回 200 且允许修改他人申请,再完成服务端身份覆盖;没有修改数据库结构,没有执行迁移或重启服务。
- 验证:修复后恶意请求返回 400目标申请的事由、状态和审批节点保持不变也没有新增费用事件正常保存草稿与直接提交回归通过。本轮受影响后端定向回归共 36 项、前端时间线兼容测试 9 项和 Python `ruff --select F,I` 均在容器内通过。
- 影响AI 工作台快速申请入口不能再通过伪造 `user_id``is_admin`、角色或员工编号绕过申请所有权检查,授权事实与其他受保护接口统一来自服务端会话。

View File

@@ -0,0 +1,8 @@
## 修复记录
- 14:43记录 bug 修复:客户端身份头可伪造且已初始化系统仍存在匿名重配置入口。
- Git 提交检查:已执行 `git fetch --all --prune`;未发现 upstream 新提交,本地 ahead 1 个提交 `661990b2 feat(expenses): add transactional expense case events`,它是本任务上一切片的费用事件事务提交,当前认证改造继续建立在该提交之上。
- 修改:新增 `AuthSession` 不透明 Bearer 会话和摘要存储,后端从服务端会话及员工表解析身份/角色;移除生产 `X-Auth-*` 授权来源,保护 Bootstrap、Settings、模型连通性、缓存、员工、分析、Agent、风险观测、审计及日志接口前端统一 Bearer、`sessionStorage`、集中 `401` 和原子登出Vite Setup 桥在初始化完成后锁定重配置。
- 操作:新增 `20260713_0002_auth_sessions.py` 并只生成 upgrade/downgrade 离线 SQL没有对持久化数据库执行迁移或重启领域回归测试使用仅存在于测试目录的身份依赖覆盖生产代码没有兼容伪造头。测试过程中发现规则初始化用例会重写 5 份 Excel 工件,已停止继续运行该类测试并保持这些文件不进入本次提交。
- 验证:容器 `x-financial-local-linux` 内认证/Bootstrap/费用事件/OpenAPI 21 项通过,受保护端点 13 项通过,全量收集 805 项成功;前端 17 项通过,生产构建成功;认证安全核心文件 Ruff 通过,`git diff --check` 通过,生产源码未检出 `X-Auth-*`Alembic 离线升级包含 `CREATE TABLE auth_sessions`,离线降级包含 `DROP TABLE auth_sessions`
- 影响:用户登录后必须使用服务端签发的短期 Bearer 会话,伪造用户名、角色或管理员头不再生效;业务经理不能冒充平台管理员,已初始化系统的基础设施信息与重配置入口不再向匿名请求开放。租户级查询守卫、会话清理/全部退出、登录限流和 SSO 仍按 P0 后续任务推进。

View File

@@ -0,0 +1,8 @@
## 修复记录
- 11:57记录 bug 修复:关联申请归档/解绑时嵌套审计提前提交业务事务。
- Git 提交检查:已执行 `git fetch --all --prune`、upstream 与 local-ahead 检查,未发现 upstream 新提交或本地 ahead 新提交;工作区原有规则表和历史开发文档改动已保留。
- 修改:`AuditLogService``AuditLogRepository` 增加由调用方控制的提交方式;`ExpenseClaimApplicationHandoffMixin` 的关联申请归档和解绑审计改为 `flush`,由付款/删除外层事务统一提交。
- 操作:新增费用事件事务回滚测试;在付款状态与结构化 Outbox 事件之间制造失败,确认付款、申请归档和嵌套审计能够整体回滚。
- 验证:容器 `x-financial-local-linux` 内新增及核心费用流程共 29 项测试通过静态检查、OpenAPI 校验、启动脚本语法和 Alembic upgrade/downgrade 离线 SQL 验证通过。
- 影响:付款或关联解绑中途失败时,不再留下“申请已归档但付款/事件未提交”的半完成状态,为事务 Outbox 提供可靠边界。

View File

@@ -0,0 +1,628 @@
# AI 费用闭环与价值证明 概念文档
更新时间2026-07-17
文档路径document/development/2026-07-13/feature/ai-expense-closed-loop-and-value-proof/CONCEPT.md
## 功能一句话
把申请、消费、票据、报销、审批、付款、入账、分析和持续学习连接成同一个费用事件,让员工少填表、审批人只处理例外、财务能确认真实节省,并让 AI 在可控边界内越用越准确。
## 背景与问题
- 当前现状项目已经具备费用申请、AI 建单、票据夹、OCR、预算检查、风险预审、动态审批、员工画像、财务看板、知识库、Agent trace、反馈表和 few-shot 样本等较完整的功能骨架。容器运行时 OpenAPI 已覆盖 156 个业务操作,功能广度已经足够。
- 用户痛点:申请、票据、报销、审批和分析仍以多个页面、多个服务和多套状态存在。用户需要在不同入口之间理解系统,而不是由系统主动接住费用事件;移动端主流程仍有 mock审批通过后的真实付款、回执、ERP 凭证和对账没有形成完整闭环。
- AI 痛点当前系统能够记录会话、运行轨迹、风险反馈和员工画像但没有统一记录“AI 建议了什么、用户改了什么、后来是否退回、最终是否付款或产生节省”。AI 能看到历史,却不能稳定把业务结果转化为下一次的个性化、自动化和风险校准。
- 企业痛点:现有看板主要回答花了多少、预算用了多少、发现了多少风险,尚不能可信回答省了多少钱、为什么省、哪些建议被执行、哪些费用仍可优化。
- 工程问题:旧 `ReimbursementRequest` 与当前 `ExpenseClaim` 能力重叠,申请与报销又复用部分字段和 JSON 标记;审批、预算、付款、归档和关系引用部分混入 `risk_flags_json`,不利于事务一致性、事件追溯和持续演进。
- 商业影响:如果不能持续证明处理效率、风险改善和真实节省,产品只能按“报销工具”定价;如果能形成价值闭环,则可以扩展为智能风控、预算经营、价值洞察和企业集成平台。
- 为什么现在需要做现有模块已经足够支撑一条完整费用闭环下一阶段继续横向新增页面会扩大编排和数据割裂。应先收口费用领域、业务事件、AI 决策、学习记忆和节省归因,再逐步开放自动化。
相关既有方案:
- `document/development/2026-07-03/feature/ai-data-flywheel/CONCEPT.md`:作为本功能的 AI 样本回流、评测门禁、Prompt 版本和 Canary 子能力,不重复建设。
- `document/development/AI意图规划器/UNIFIED_GATE_PIPELINE.md`:作为 AI 场景识别和后端唯一编排入口的架构前置,不在前端继续增加影子门控。
## 目标与非目标
### 目标
- [G1] 建立统一 `Expense Case`,把一次费用从需求表达、申请、票据、报销、审批、付款、入账到归档视为同一业务事件。
- [G2] 建立零录入报销体验,自动匹配申请、预算、票据、费用类型、项目、成本中心和历史偏好,让用户主要核对异常项。
- [G3] 建立按动作授权的安全自动化等级,从解释、建议、预填、可逆自动化逐步升级到低风险直通。
- [G4] 建立“AI 决策 → 用户反馈 → 工作流结果 → 分层记忆 → 再决策”的学习闭环。
- [G5] 建立节省机会、执行任务、实际结果和财务确认组成的 Savings Ledger区分风险暴露、预计节省和已实现节省。
- [G6] 建立从描述性统计到异常归因、预算预测、供应商分析、政策模拟和节省任务的费用经营分析能力。
- [G7] 建立企业租户、用量计量、模型成本和客户价值指标,为年度订阅、用量超额和增值模块提供可审计基础。
- [G8] 保持高风险动作、资金支付、制度发布和敏感主数据变更的人为授权、审计和回滚能力。
### 非目标
- [NG1] 本轮不自建商旅、企业卡、银行或支付供应链网络,只定义标准连接器和业务事件契约。
- [NG2] 本轮不一次性替换所有现有报销接口;先建立统一费用事件和旁路事件账本,再分阶段迁移旧模型。
- [NG3] 本轮不做模型微调或无监督自训练优先使用结构化反馈、few-shot、规则学习、Prompt 版本和回归门禁。
- [NG4] 本轮不允许 AI 无人值守执行资金支付、高风险批准/驳回、制度发布和收款账户变更。
- [NG5] 本轮不同时覆盖所有费用场景,首个试点只选择一个高频、可测量、可闭环的场景。
- [NG6] 本轮不做未经授权的跨租户学习和企业间明细 Benchmark后续只允许严格聚合、隐私保护并获得授权的对标能力。
- [NG7] 本轮不把风险关联单据总额、暂缓付款金额或未采纳建议直接计为企业节省。
## 用户与场景
### 目标用户
1. 报销人/申请人:少填字段、少整理票据、少补件、少追问进度。
2. 审批人:只处理必要性、例外和高风险问题,快速获得证据与建议。
3. 财务运营:减少重复审核、退回、对账和月末整理,集中处理异常。
4. CFO/管理层:了解费用增长原因、预算趋势、节省机会和实际 ROI。
5. 风控/审计:查看规则依据、风险证据、算法版本、人工覆盖和审计结果。
6. 系统管理员/IT配置租户、组织、权限、连接器、数据保留、模型成本和自动化上限。
### 使用入口
- AI 工作台自然语言入口。
- 统一费用事件详情页。
- 移动端拍票、报销和审批入口。
- 票据夹和自动票据收件箱。
- 审批例外工作台。
- CFO 费用价值看板。
- AI 记忆与自动化策略设置。
### 核心场景
1. 员工说“下周去上海出差三天”,系统自动生成合规申请、预算影响和可选节省方案,用户核对后提交。
2. 消费期间,邮箱发票、拍照票据、企业卡或商旅订单进入统一票据收件箱,系统按时间、金额、地点和商户自动归集到费用事件。
3. 行程结束后,系统自动生成报销草稿,只要求用户处理缺票、归属或异常金额。
4. 提交前,系统按事实、规则、证据和风险等级给出绿色通过、黄色修正、红色复核结果。
5. 审批人看到预算影响、历史相似单、政策依据、风险证据和建议意见,低风险单据进入快速通道。
6. 财务完成付款、回执、ERP 凭证和对账,所有结果回写费用事件并形成审计链。
7. 月末系统识别预算超支、供应商价格漂移、重复采购、异常路线和流程瓶颈,生成有负责人和目标金额的节省任务。
8. 用户修正字段、审批人覆盖建议、财务确认误报或节省结果后,系统更新用户、部门和企业记忆,下次减少重复操作。
### 异常场景
- OCR、模型、向量检索或外部连接器不可用时回退到人工录入或稳定规则不阻塞草稿保存和主链路。
- 票据、申请或费用事件匹配置信度不足时,只展示候选,不自动绑定。
- 企业规则与个人偏好冲突时,企业规则优先,必须解释冲突原因。
- 高风险、超金额阈值、敏感账户变更或证据不足时,强制人工确认。
- 自动化动作失败时必须幂等、可重试、可撤销,并保留错误状态和操作证据。
- 预计节省没有财务确认或实际结果时,只能标记为机会,不得计入客户 ROI。
## 功能能力
- [C1] 费用事件能力:统一表达申请、消费、票据、报销、审批、付款、入账和归档关系。
- [C2] 零录入能力:自动抽取并匹配申请、预算、票据、费用类型、项目、成本中心、参与人和历史偏好。
- [C3] 预审与修复能力:输出事实、规则、证据、判断和建议动作,并提供一键补件、修正和解释入口。
- [C4] 审批例外能力:风险分级、证据摘要、预算影响、推荐路由、意见草稿、批量处理、委托、转交、加签和超时升级。
- [C5] 支付入账能力以连接器方式接收付款批次、支付回执、ERP 凭证、银行流水和对账结果。
- [C6] 学习记忆能力:记录 AI 建议、用户修改、审批覆盖、退回、付款和审计结果,形成用户、部门、企业三级记忆。
- [C7] 自动化策略能力:按动作、金额、场景、风险、置信度、证据、可逆性和抽检率配置自动化等级。
- [C8] 节省价值能力:把节省机会、建议动作、负责人、目标时间、预计节省、实际节省和财务确认形成闭环。
- [C9] 费用经营能力:费用结构、预算预测、异常归因、供应商分析、政策模拟、流程成本和客户 ROI。
- [C10] 商业计量能力:企业租户、套餐、配额、用量、模型/OCR 成本、增值模块、客户贡献毛利和价值证明。
- [C11] 状态与权限:服务端认证、租户隔离、角色权限、动作授权、审计日志和敏感数据保留策略。
- [C12] 边界与降级:外部依赖失败时主链路可用;高风险动作始终保留人工控制;所有学习与自动化可关闭、遗忘或回滚。
## 方案设计
### 前端
#### 统一费用事件
- 新增或重构费用事件详情容器,不再让用户理解申请单、票据夹、报销草稿、审批单和付款状态之间的内部关系。
- 页面按“计划 → 消费 → 报销 → 审批 → 付款/入账 → 复盘”展示时间线和当前待办。
- 每个 AI 填充字段显示来源、置信度和修改入口;低置信度字段集中进入“需要确认”区。
- 退回、补件和断点续办直接回到对应问题,不要求用户重新开始对话。
#### 零录入报销首个切片
- 用户在小财管家上传已完成 OCR 的票据并发送后,附件关联任务必须以当前租户、当前员工和 `Expense Case` 为边界,优先寻找已审批申请自动生成的可编辑报销草稿。
- 匹配信号首期使用费用事件关系、申请状态、票据日期、行程城市、费用场景和草稿状态;返回结构化分数、置信度和命中原因,不把模型自由文本作为授权依据。
- 只有唯一高置信候选允许自动归集。低置信、多个接近候选、申请缺少系统生成草稿、票据已属于其他单据或证据不足时,任务以 `requires_confirmation=true` 成功返回候选和异常,不修改任何报销单或票据关系。
- 自动归集成功后直接返回费用事件、申请、报销草稿、归集数量、跳过数量、风险项、缺失项和可解释原因;前端只展示完成结果或“需要确认”异常,不要求用户再次上传同一票据。
- 自动归集是 L3 可逆动作,只允许写入草稿、票据关系和业务事件;不自动提交、审批、付款或重建历史遗留申请缺失的草稿。
- 相同租户、用户、票据和目标草稿重试必须幂等;已归集到同一草稿的票据计入跳过数,不重复创建费用明细、附件或业务事件。
#### 移动端
- 把拍照、相册选票、报销列表、审批列表和 AI 助手接入真实后端。
- 支持离线拍票、失败重试和后台上传状态。
- 未实现能力必须隐藏或明确标为不可用,不保留无响应的主按钮。
#### 审批例外工作台
- 以风险、金额、预算影响、等待时长和证据完整度排序。
- 支持批量处理低风险事项,行内保留真实按钮和键盘可访问入口。
- 高风险审批展示模型版本、规则版本、政策依据、证据和人工覆盖原因。
- 正式审批任务切片由 `/api/v1/approval-tasks` 返回租户安全的数据库分页投影优先级由风险、SLA、金额和证据完整度共同计算任务保存节点进入时间、处理人、乐观版本、批量资格和权威动作集合。旧 `/approval-workbench/items` 仍可承担 advisory-only 摘要,但不再作为“待我审核”的业务动作事实源。
- 批准、退回和付款均使用动作协议:客户端在确认时冻结 `request_id`、预期状态和预期审批节点;服务端以租户 + 操作人 + 请求 ID 唯一账本、请求指纹、PostgreSQL advisory lock、Claim 行锁和同事务业务事件确保相同请求可安全重放,不同内容或陈旧快照以 409 拒绝。
- 高风险门禁同时读取持久化风险观察和尚未落表的原始风险标记;未处置的严重/高危可行动风险在路由、预算和状态修改前阻断,原始重大风险持久化失败时 fail-closed。仅明确标记为 `route_review` 的路由型风险进入对应审批节点而不冒充已解决。
- 门禁只消费与当前单据业务阶段一致的显式风险:申请阶段不会被明确标记为 `reimbursement` 的风险阻断,反之亦然;缺少阶段的未知重大风险仍保守处理。`route_review` 是显式路由语义,审批路由可以继续但风险仍保留在证据和后续复核中。
- 风险处置采用判定与生命周期双状态:支持确认风险、误报、补件、开始整改、申请豁免、财务/管理层批准或拒绝豁免,以及完成处置;每个动作绑定请求指纹、预期版本、操作人和只追加事件。申请人不得自批/自拒,仅在职 finance/executive 且非申请人可决定,过期申请 fail-closed。完整证据只对管理员或当前审批人开放风险池对财务/管理/预算角色开放。
- 并发锁顺序固定为 Claim → RiskObservation → RiskDisposition审批动作、人工处置和 Hermes 扫描共用 Claim 锁。扫描在锁外计算、锁内校验状态与更新时间,过期快照丢弃并等待下一轮,避免旧图结果覆盖刚完成的人工处置。
- 所有单据动作首次完整 API 响应写入动作账本;风险处置首次完整响应在 append-only 事件 INSERT 前原子写入。相同请求重放只返回首次版本,风险旧事件无快照时也只从目标版本及以前的审计链重建,不读取当前 Claim、当前处置投影或后续事件。
- 审批任务支持低风险批量通过、委托/撤销委托、永久转交、顺序加签、并行会签和手动/调度 SLA 升级SLA 以当前节点进入时间为基准。前端“待我审核”完全消费服务端分页、筛选、`available_actions` 与版本,筛选和页码可从详情返回恢复。尚未完成的主要边界是统一必要性/政策证据摘要、真实企微/钉钉/邮件触达及外部结果回写。
#### CFO 价值看板
- 首页优先展示财务确认现金节省、已核验工时价值、安全智能直通率和重大风险护栏。
- 支持按部门、项目、费用类型、供应商、城市、时间和费用事件下钻。
- 每项节省可打开来源、基线、建议、执行、确认和去重证据。
#### AI 记忆与自动化设置
- 用户可以查看“系统记住了什么、为什么记住、在哪里使用”,并支持修改、忘记和关闭个性化。
- 企业管理员配置部门/企业记忆边界、有效期、敏感等级和自动化上限。
- 首个个人记忆切片只在申请核对表展示“常用出行方式”来源、证据数量和可编辑状态,并提供“忘记此偏好”;不在本轮新建大型设置页,也不把风险画像当作个人偏好。
### 后端
#### 费用领域与编排
- 新增 `ExpenseCaseService` 作为跨阶段编排入口,避免继续扩大万能 `ExpenseClaimService`
- `ExpenseCaseService` 只负责阶段协调,申请、票据、报销、审批、支付、入账、记忆和节省由独立协作者负责。
- 复用统一 AI 场景注册与 LangGraph 编排,前端只按后端返回的 plan/action 渲染,不再增加业务门控。
- 零录入票据归集由独立匹配器计算候选和证据,由独立关联编排器执行可逆写入;后台 Job 只负责身份绑定、状态保存和结果投影,不继续堆评分、附件和事件逻辑。
- 票据存储命名空间必须至少包含租户与用户;后台任务查看和执行同时校验租户及用户,平台管理员也不能跨租户读取任务结果。
#### 业务事件与 AI 决策
- 所有关键动作写入 append-only `business_events`,使用 `correlation_id` 串联同一费用事件、Agent run、审批和外部连接器事件。
- 业务状态变更与对应 Outbox 事件必须在同一数据库事务中持久化;消费者按事件 ID 幂等处理,避免审计、学习和 ROI 数据因异步失败永久丢失。
- 只有画像刷新、分析聚合和消息通知等可重建派生任务允许异步失败并重试,不得把申请、提交、退回、审批、支付和入账事件降级为可丢弃旁路日志。
- 每个 AI 输出写入 `ai_decisions`记录建议值、置信度、证据、模型、Prompt、规则、政策版本、风险等级和自动化模式。
- 用户接受、修改、拒绝或忽略写入 `ai_decision_feedback`;审批、退回、付款和审计写入 `workflow_outcomes`
#### 记忆与学习
- `memory_entries` 支持用户、部门、企业作用域,以及 candidate/active/suppressed 状态。
- `memory_evidence_links` 保留记忆与业务事件、决策、反馈、结果的来源关系。
- 企业制度优先于部门基线,部门基线优先于个人偏好;冲突时返回解释。
- 复用既有 AI 数据飞轮的 few-shot、golden case、Prompt 版本、Canary 和回归门禁。
- 首个切片使用关系库精确键检索,只允许低敏枚举 `travel_application.transport_mode`;不复用当前缺少完整租户键的 FewShot/Qdrant 或员工风险画像存储个人记忆。
- 记忆证据只从认证动作事务中的服务端核验纠正产生,关联 Decision、Feedback、Outcome 和 Expense Case草稿、客户端观察、重复请求和同一 Case 多次修改不能重复计票。
- 个人记忆只填充当前申请的空白软字段;当前明确输入、服务端 HR/组织主数据、企业制度和规则计算结果都优先于个人记忆。每次应用或抑制都返回可解释原因,不把优先级交给 Prompt 或 LLM 自行判断。
- 分层记忆首个组织级切片继续只允许 `travel_application.transport_mode` 的“飞机/火车/轮船”。企业记忆由租户管理员显式确认,部门记忆必须绑定稳定的 `organization_unit_id` 并只把部门名称作为展示标签;隐式证据暂只生成个人 Candidate不允许少量个人行为自动升级为部门或企业制度。
- 分层解析顺序固定为“当前显式输入/规则禁用 > 企业记忆 > 部门记忆 > 个人记忆”。不同层值冲突时只应用最高优先级可用项,并返回候选层级、冲突值、未采用原因、有效期和衰减后置信度;不得静默覆盖当前输入,也不得把优先级交给模型自由判断。
- 管理员显式确认的组织记忆可直接激活,但必须记录创建人、确认来源、适用作用域和有效期;隐式个人记忆仍使用 3 个不同 Case、2 次审批通过、证据跨 7 天的最小样本门槛。组织记忆过期、撤销或服务异常时降级到下一层,不阻塞申请预览。
- 已确认历史案例接入报销预审前,`few_shot_samples` 与 Qdrant payload 必须补齐租户、业务场景、制度/规则标识、规则版本和 active 状态;每次向量命中还必须回关系库按租户和样本状态二次校验,样本改判时删除或稳定覆盖旧向量,禁止跨租户或陈旧结论进入提示。
- 历史案例只能输出结构化 `historical_case_evidence`,明确标记 `advisory_only`、人工确认结论、相似度、制度和版本是否匹配;它不得修改确定性预审的 `decision``passed``blocking_count`不得改变预算复核和审批路由。旧版本只可作为降权参考Qdrant 或 Embedding 不可用时返回空证据并继续规则链。
#### 节省与价值
- `savings_opportunities` 记录基线、风险暴露、建议动作、预计节省、负责人和截止时间。
- `savings_realizations` 记录执行结果、实际节省、财务确认人、证据、归因状态和去重键。
- 风险关联金额、暂缓付款和未采纳建议不得自动转为实际节省。
#### 连接器
- 定义统一 `ExpenseConnector` 协议覆盖票据邮箱、税务验真、企业卡、商旅、支付、银行流水、ERP、HR、SSO、企微/钉钉。
- 外部事件必须具备幂等键、来源系统、原始事件 ID、发生时间、处理状态、错误码和重试次数。
#### 商业计量
- 以租户、套餐、模块和时间窗口记录模型、OCR、文档、分析任务、连接器与人工实施用量。
- 成本事件与客户价值事件分开记账,支持计算客户贡献毛利、实施成本摊销和私有部署成本。
- 套餐与配额只控制商业权益,不改变安全规则、租户隔离和高风险人工复核边界。
### 算法与规则
#### 自动化决策
- 自动化等级按动作计算,不按 Agent 整体授权。
- 动作风险、模型置信度、证据完整度、历史命中率、金额阈值、可逆性、企业上限和抽检率共同决定是否执行。
- L0 只读解释L1 建议L2 预填L3 可逆自动化L4 低风险直通L5 资金支付和高风险决策始终保留人工。
#### 记忆激活
- 明确偏好后续可由用户认证确认后立即激活;首个切片只实现隐式候选,不把普通字段编辑冒充明确授权。
- 隐式出行方式偏好需要同租户、同员工、同场景和同归一化值的 3 个不同 Expense Case 服务端核验纠正,其中至少 2 个出现 `application_approved`,且首尾证据至少跨 7 天;同一 Case、重试、草稿续签只计一票。
- `accepted``client_observed``draft_saved` 和单纯 `application_submitted` 只能作为观察信息,不能单独激活;退回、反向纠正、结果 reversed 或制度冲突会抑制现有记忆。
- Candidate 默认 90 天有效Active 默认 180 天;状态支持 `candidate / active / suppressed / expired / revoked`。相反值先抑制旧记忆并建立新候选,不原地覆盖;用户“忘记”后立即 revoked清除可恢复值且不可自动复活。
- 错误标注、违规习惯、一次性例外、自由文本和敏感字段不能直接变为默认记忆。
#### 风险与预审
- 统一输出事实、规则、证据、风险、建议和可执行修复动作。
- 报销提交必须先完成服务端预审握手。预审结果至少包含稳定 `review_id`、输入指纹、规则集指纹、流水线版本、`ready / needs_fix / ready_with_review` 决策、结构化发现和修复动作;客户端不得仅凭旧的 `passed` 标记提交。
- 输入字段、明细、票据、风险事实或规则集发生变化时,旧预审自动失效并在提交事务内重算;同一输入和规则集重复预审复用相同 `review_id`,避免生成重复事件。
- 只有 high/critical 且 `fixable_by_submitter` 的未解决风险阻断提交;预算治理、领导判断和财务复核风险随单进入对应审批角色,不把所有高风险简单放行或全部拦截。
- `needs_fix` 必须在预算占用和 `claim_submitted` 之前阻断,保持草稿状态并返回 HTTP 409 结构化整改信息;可提交结果的预审事件与提交事件共享 correlation并通过 causation 形成可追溯链路。
- 使用已确认正/负样本校准误报与漏检;规则或 Prompt 发布前运行 golden case。
- 新策略先进入 shadow再 Canary最后按动作开放自动化。
#### 费用分析与节省
- 描述性分析回答“发生了什么”;诊断分析回答“为什么”;建议分析回答“做什么”;价值闭环回答“是否执行并产生了多少结果”。
- 供应商、费用类型、城市、项目和部门基线必须保存数据窗口、样本量、算法版本和政策版本。
- 节省归因必须区分现金节省、可释放工时价值和不可货币化效率改善。
### 数据与契约
#### 核心数据对象
- `expense_cases`:统一费用事件和当前阶段。
- `expense_case_links`:连接申请、报销、票据、审批、付款、凭证和外部对象。
- `business_events`append-only 业务事件账本。
- `ai_application_preview_decisions`:申请落单前由服务端重新规范化并短期签发的预览建议;不创建空 Expense Case不保存建议明文动作成功后一次消费。
- `ai_decisions`:结构化 AI 建议与版本证据。
- `ai_decision_feedback`:接受、修改、拒绝和忽略。
- `workflow_outcomes`:退回、补件、审批、付款、审计和最终结果。
- `memory_entries`:按租户、作用域主体、场景、字段和值指纹保存候选/激活/抑制/过期/撤销状态,只允许白名单低敏值可恢复。
- `memory_evidence_links`:把记忆与服务端 Decision、Feedback、Outcome、Expense Case 和业务结果关联;唯一键保证同一 Case 对同一候选最多计一票。
- `automation_policies` / `automation_grants`:动作级自动化策略和授权。
- `profile_baseline_snapshots`:持久化员工、部门、供应商、费用和流程基线。
- `savings_opportunities` / `savings_realizations`:节省机会和实现结果。
- `usage_meter_events`租户、模块、模型、OCR、文档和分析用量。
#### 零录入附件关联任务契约
- 保留现有 `status``message``claim_id``claim_no``uploaded_count``skipped_count` 字段,追加字段保持前端向后兼容。
- 任务追加 `resolution``requires_confirmation``expense_case_id``application_claim_id``application_claim_no``confidence``confidence_score``match_reasons``exceptions``missing_fields``risk_items``candidates`
- `status=succeeded` 只表示匹配流程完成;`resolution=auto_associated` 表示已经完成可逆归集,`resolution=requires_confirmation` 表示没有业务写入,需要用户选择候选或处理异常。
- 候选最少返回目标类型、费用事件 ID、申请 ID/编号、草稿 ID/编号、分数、置信度和命中原因;不得返回其他租户或当前员工数据范围之外的候选。
- `receipt_received``attachment_associated` 使用票据 ID 与目标草稿组成稳定幂等键,并与票据 Link、附件写入在同一数据库事务中完成若文件存储写入失败数据库事务回滚且任务进入失败态。
#### 最小事件词典
- `expense_case_created`
- `historical_claim_imported`:迁移前旧单的当前快照;只证明已纳入统一费用事件,不重建或伪造迁移前审批历史。
- `application_generated` / `application_submitted` / `application_approved`
- `application_pre_review_completed` / `claim_pre_review_completed`
- `receipt_received` / `receipt_verified` / `ocr_corrected`
- `field_suggested` / `field_accepted` / `field_edited` / `field_rejected`
- `attachment_associated` / `draft_saved` / `claim_submitted`
- `claim_returned` / `supplement_completed`
- `risk_flagged` / `risk_confirmed` / `risk_false_positive`
- `route_suggested` / `route_overridden`
- `claim_approved` / `payment_requested` / `payment_completed`
- `accounting_entry_created` / `reconciliation_completed`
- `saving_opportunity_created` / `saving_action_completed` / `saving_confirmed`
#### 状态枚举
- Expense Case`planning` / `approved_to_spend` / `spending` / `claiming` / `reviewing` / `paying` / `accounting` / `closed` / `cancelled`
- AI Decision`suggested` / `accepted` / `edited` / `rejected` / `ignored` / `executed` / `rolled_back`
- Memory`candidate` / `active` / `suppressed` / `expired` / `revoked`
- Automation`explain` / `recommend` / `prefill` / `reversible_auto` / `low_risk_straight_through` / `human_only`
- Savings`identified` / `accepted` / `in_progress` / `realized` / `verified` / `rejected` / `expired`
#### 兼容策略
- 迁移初期可用 shadow 事件校验现有 `ExpenseClaim` 的映射;某个状态一旦正式纳入事件模型,其业务写入与 Outbox 事件必须进入同一事务。
- 迁移前已有 `ExpenseClaim` 通过显式维护命令写入一条 `historical_claim_imported` 快照事件:事件发生时间使用真实回填时间,原创建、发生、提交和更新时间只进入内部 payload`history_reconstructed=false`,不得按当前状态反推并伪造历史提交、审批或付款动作。
- 历史回填默认 dry-run必须显式提供租户、创建时间边界和数据库目标apply 还要求精确目标确认、迁移 head、advisory lock 和分批事务。稳定幂等键、源快照指纹、已有 Link 跳过及孤立 Event 冲突拒绝共同保证可审计重跑。
- 历史快照事件使用 `delivery_status=suppressed`,供时间线和分析读取,但不进入实时 Outbox 投递;用户态 API 仍只返回事件白名单,不暴露回填批次、源指纹、幂等键和投递状态。
-`ReimbursementRequest` 进入只读兼容和迁移状态,停止新增第二套业务编排。
- 现有 `risk_flags_json` 保持读取兼容,新审批、付款、归档和关系事件写入结构化表。
- API 新字段优先追加,不在同一阶段破坏现有前端契约。
- 从未成功签发 `decision_id` 的旧申请预览,或签发接口失败且服务端没有当前会话有效决策时,保存/提交继续走现有 `client_observed` 路径;当前会话一旦存在有效服务端决策,省略 `decision_id` 必须拒绝,不能主动降级绕过核验。兼容路径不得在用户已经编辑后把最终值反向登记成“原始建议”。
- 新预览使用 30 分钟有效的服务端 `decision_id`。保存草稿或提交成功后一次消费;保存草稿会尽力签发基于已保存事实的新 `decision_id`,供后续提交继续核验,但续签属于业务提交后的派生能力,续签失败不能把已成功保存返回为失败。相同动作请求 ID、动作和最终指纹允许安全重放其他重放拒绝。
- AI 工作台、小财管家和通用 Orchestrator 的结构化申请预览统一复用服务端签发/消费工作流。小财管家签发失败时保持可编辑和可重试,但保存、提交必须 fail-closed不再降级到旧 Orchestrator 副作用;只有没有结构化预览的历史消息保留旧兼容入口。
- Orchestrator 将已签发 decision 保存在服务端会话状态,后续保存/提交只消费该状态,不采信浏览器回传的 preview 或 decision。保存草稿返回的新 decision 继续写回会话,服务端消息中的结构化预览可在刷新或换端后恢复。
- 迁移桥接期集中维护 migration-owned 表集合;所有 legacy bootstrap 只能创建集合之外的表。标准启动在 `alembic upgrade head` 前只读核对 revision 与自有表集合发现漂移时拒绝启动不自动猜测、stamp 或修复。
- 所有表结构通过 Alembic 迁移,不继续在请求路径执行 DDL。
#### 版本与审计
- 事件记录 actor、tenant、source、run、model、Prompt、rule、policy 和 schema 版本。
- 记忆、自动化策略、规则、Prompt 和基线均支持 supersede、有效期和回滚。
- 高风险人工覆盖必须记录原因、证据和审批主体。
### 权限与安全
- 登录后签发服务端可验证会话或 JWT服务端从会话和数据库解析用户、租户、角色和数据范围。
- 移除客户端 `X-Auth-*` 作为授权事实来源管理面、Bootstrap、设置和模型连通性接口必须受平台管理员保护。
- P0 采用不透明 Bearer 会话:明文 token 仅在登录成功时返回,数据库只保存 SHA-256 摘要;每次请求从服务端会话和员工数据重新解析当前身份与角色,过期、撤销或不存在的 token 统一拒绝。
- Web 端只在 `sessionStorage` 保存 token 和过期时间,普通请求、流式请求及页面关闭收尾统一携带 Bearer任一 `401` 触发本地会话清理,登出时会话指标与 token 撤销在同一事务完成。
- 已初始化系统的 Bootstrap 状态只返回脱敏信息Bootstrap 写入、系统设置、模型连通性、缓存、审计与系统日志等敏感管理面由平台管理员权限保护Vite 本地 Setup 桥在初始化完成后锁定重新配置入口。
- P0 数据契约即引入最小 `tenant_id`、数据库约束、行级过滤、向量库命名空间和对象存储前缀隔离;删除传播、数据导出和私有部署加固可在商业化阶段继续完善。
- Expense Case 用户态查询只返回流程摘要Case 基础状态、关系类型、事件类型、操作人、发生时间及白名单业务载荷;不返回关联资源 ID、幂等键、correlation、causation、聚合标识或 Outbox 投递状态。有权查看入口单据的本人、当前审批人、财务和管理员可查看整 Case 的安全摘要,无权限与跨租户查询继续以 404 隐藏资源存在性。
- 申请预览签发与消费同时绑定 `tenant_id`、actor、Bearer 登录会话和会话 ID动作入口按这些服务端事实锁行校验不能只凭随机 UUID 授权。浏览器提供的模型来源、`finalValue`、用户和角色声明均不作为可信事实。
- 个人记忆主体优先使用服务端 `employee_id`,缺失时才使用规范化用户名;所有唯一键、读取、证据外键和忘记动作都必须以 `tenant_id` 开头。同邮箱或同员工号在不同租户形成完全独立的记忆。
- 记忆值采用 deny-by-default 白名单;首个切片仅保存规范枚举“飞机/火车/轮船”。事由、地点、日期、金额、客户/项目自由文本、附件、银行卡、收款人与支付账户均不得进入记忆明文、Prompt、向量 payload 或日志。
- 外部 `/orchestrator/run` 的用户消息、定时任务和系统事件都必须携带有效登录会话,不能由请求体中的 `source` 自行声明可信内部来源;其中 `schedule` / `system_event` 只允许平台管理员触发。服务端使用登录态覆盖用户、租户、角色、管理员、员工、审批人和调度操作人别名,并移除客户端 preview/decision 状态。最近会话查询与删除只使用当前登录用户和租户,查询参数中的 `user_id` 仅保留协议兼容,不参与授权。
- 预览与学习账本中的字段值使用独立、可版本化的费用申请密钥计算 HMAC-SHA256 指纹,密钥目录和文件权限分别为 `0700``0600`;核验旧决策必须使用其签发版本,缺失版本直接拒绝,不静默生成替代密钥。字段指纹同时编码“字段是否存在”,避免新增或删除字段被误判为采纳;训练资格仍保持关闭,直至审批、付款或人工核验闭环完成。
- 自动化权限按动作、金额、场景、风险和有效期授予,不使用全局“允许 Agent 自动执行”开关。
- 收款账户变更、资金支付、制度发布、高风险驳回和敏感主数据变更执行双人或更高等级复核。
### 降级策略
- LLM 不可用:回退到规则和人工填写。
- OCR 不可用:文件保留并进入待识别队列,用户可手工补录。
- Qdrant/few-shot 不可用:使用 stable Prompt 和基础规则,不阻塞主流程。
- 历史案例证据只返回固定的脱敏标签与摘要,不返回样本 ID、费用单号、人工评论或历史结论原文它只能辅助复核不参与确定性规则、阻断数量、预算复核和审批路由计算。
- 外部支付/ERP/税务连接器不可用:事件进入 retryable 状态,保留人工处理入口和幂等键。
- 记忆冲突或可信度不足:只展示建议,不自动应用。
- 结构化预览签发超时或不可用:允许继续编辑和重新签发,但保存、提交保持 fail-closed只有不存在结构化预览的历史兼容入口保留原有降级路径且只能记录 `client_observed` 行为证据。
- 记忆服务不可用、过期或校验失败:回退为“无个人记忆 + 当前规则”,不阻塞预览,也不读取跨租户、旧缓存或无租户向量样本。
- Savings 证据不足:保留机会状态,不进入已实现节省。
## 算法与公式
### 自动化资格分数
```text
automation_score
= w1 * model_confidence
+ w2 * evidence_completeness
+ w3 * historical_precision
+ w4 * reversibility
- w5 * action_risk
- w6 * amount_risk
```
变量说明:
- `model_confidence`:模型或规则对当前建议的置信度。
- `evidence_completeness`:发票、申请、预算、合同、订单和政策证据完整度。
- `historical_precision`:相同租户、场景、动作和版本的历史正确率。
- `reversibility`:动作是否可以无损撤销或回滚。
- `action_risk`:动作类型风险,支付和制度发布最高。
- `amount_risk`:金额相对企业阈值和历史基线的风险。
- `w1...w6`:按企业和动作配置的权重。
- 适用边界:分数只决定候选自动化等级,仍需满足企业硬性白名单、金额上限、抽检率和人审要求。
### 记忆激活置信度
```text
memory_confidence
= consistent_evidence_weight
+ outcome_success_weight
+ explicit_confirmation_weight
- conflict_weight
- age_decay
```
变量说明:
- `consistent_evidence_weight`:多次一致修改或选择产生的证据。
- `outcome_success_weight`:建议最终一次通过、付款或审计确认的权重。
- `explicit_confirmation_weight`:用户或管理员明确确认的权重。
- `conflict_weight`:与企业制度、部门规则或其他记忆冲突的惩罚。
- `age_decay`:记忆随时间衰减。
- 适用边界:敏感信息、一次性例外和违规行为不得依赖分数自动激活。
### 安全智能直通率
```text
safe_straight_through_rate
= qualified_completed_cases_without_manual_correction_or_return
/ eligible_completed_cases
```
分子还必须满足必要审批完成,且事后抽样审计未发现重大问题。
### 客户可验证 ROI
```text
verified_value
= verified_cash_savings
+ verified_releasable_labor_value
customer_roi
= (verified_value - customer_total_cost) / customer_total_cost
```
变量说明:
- `verified_cash_savings`:客户财务确认的重复支付阻止、超标准调整、采购或预算优化等实际现金节省。
- `verified_releasable_labor_value`:基于上线前后人工分钟、单据量和角色完全成本计算,并经客户认可的工时价值。
- `customer_total_cost`:订阅、用量、实施、集成和客户内部运营成本。
- 适用边界:现金节省和工时价值分开披露;风险暴露、未采纳建议和暂缓付款不得计入。
### 客户贡献毛利
```text
customer_contribution_margin
= subscription_revenue
+ usage_revenue
+ module_revenue
+ verified_savings_share
- llm_ocr_storage_cost
- third_party_cost
- support_cost
- amortized_implementation_cost
```
用于判断高收入但高度定制或私有部署客户是否实际盈利。
## 测试方案
### 后端
- Expense Case 状态机、关系绑定、幂等和非法状态跃迁单元测试。
- 历史 ExpenseClaim 回填的 dry-run 零写、租户/时间边界、快照真实性、批次回滚、冲突拒绝、重复执行和数据库目标防误连测试。
- Business Event、AI Decision、Feedback、Outcome、Memory、Automation Policy、Savings Ledger service 单元测试。
- 服务端会话、租户隔离、角色和动作权限的正向/越权测试。
- 连接器事件幂等、重试、回执、失败恢复和重复支付防护测试。
- Alembic baseline、升级、回滚边界和旧数据迁移测试。
- 现有报销、预算、风险、知识库和 Agent 回归测试。
### 前端
- 费用事件时间线、异常确认、断点续办和操作反馈视图模型测试。
- 移动拍票、上传失败恢复、真实列表和审批 mutation 测试。
- 审批例外工作台键盘操作、焦点管理和权限态测试。
- CFO 价值看板对预计/实际/确认节省的展示和下钻测试。
- AI 记忆查看、修改、忘记和关闭个性化测试。
- 生产构建、lint、typecheck 和浏览器关键流程测试。
### 算法与规则
- 自动化分数、硬阈值、金额上限、动作白名单和抽检策略测试。
- 记忆候选、激活、冲突、过期、撤销和污染样本测试。
- 风险规则、Prompt、few-shot 和 golden case 回归门禁测试。
- Savings 去重、基线、归因和确认状态测试。
### 集成
- 一句话申请 → 票据归集 → 自动报销 → 预审 → 审批 → 付款事件 → 入账 → 归档端到端。
- 持久开发库只读流式克隆 → Alembic 升级 → 历史回填 dry-run/apply/重复 apply → 服务端登录 → 旧单时间线查询 → 持久库不变验证。
- AI 建议 → 用户修改 → 退回/通过 → 记忆候选 → 下次建议变化闭环。
- 常用出行方式 1/2 次纠正保持候选,第 3 个不同 Case 且至少 2 次审批通过、跨 7 天后激活;当前输入覆盖、跨租户拒绝、忘记后不再预填。
- 风险命中 → 人工确认/误报 → few-shot → 新版本回放 → Canary/回滚闭环。
- 节省机会 → 负责人执行 → 实际结果 → 财务确认 → ROI 看板闭环。
- 多租户同名员工、同号单据、向量检索和对象存储隔离测试。
### 容器验证
所有后端、集成、数据库迁移和外部依赖验证必须在项目容器内执行,单条测试命令最大超时 60s
```bash
docker exec -w /app -e SERVER_VENV_DIR=/tmp/x-financial-server-venv \
x-financial-local-linux \
/tmp/x-financial-server-venv/bin/pytest -q server/tests/test_expense_case_service.py
```
如果本地 Compose 使用不同容器名,先通过 `docker ps` 确认当前主应用容器,不得改为宿主机直接运行后端测试。
### 手工验证
- 在真实容器页面完成申请、拍票、报销、退回补件、审批、付款和价值看板流程。
- 检查每个 AI 建议是否能展示来源、置信度、修改结果和后续业务结果。
- 检查用户能否查看、修改和忘记 AI 记忆。
- 检查高风险动作无法绕过人工,低风险可逆动作能够撤销和回放。
- 检查每项实际节省能够追溯到基线、建议、执行、确认人和证据。
## 指标与验收
以下目标在缺少真实客户基线前均为首轮试点的方向性目标,正式数值必须在试点基线采集后冻结。
- [A1] 全流程验收任一费用事件可以从申请追溯到票据、报销、审批、付款、凭证、归档、AI 决策和节省结果。
- [A2] 体验指标:首个试点场景报销创建时间 P50 目标不超过 60 秒。
- [A3] 自动化指标:可结构化字段的自动填充率方向性目标不低于 85%,且字段修改率持续下降。
- [A4] 质量指标:首次提交完整率方向性目标不低于 90%,退回率和每单人工触点低于试点基线。
- [A5] 智能指标AI 字段采纳率、重复纠正率、建议接受率和记忆复用成功率可按租户、场景、版本统计。
- [A6] 风险护栏:重大风险漏检率、误报干扰率、人工覆盖率和抽样审计结果可计算;自动化提升不得以护栏恶化为代价。
- [A7] 价值指标:每项节省区分机会、预计、执行、实现和财务确认;客户月度 ROI 可回放。
- [A8] 商业指标:早期可计算付费试点转年度合同率、持续产生可验证价值的客户率和按客户贡献毛利;成熟后计算 NRR。
- [A9] 性能指标:同步页面接口 P95、AI 预填 P95、OCR 队列等待、后台任务恢复和连接器重试达到试点 SLA具体阈值在基线后确定。
- [A10] 安全指标:客户端无法伪造管理员、跨租户访问返回拒绝、敏感动作具备二次授权和审计。
- [A11] 可观测性每个费用事件、AI 决策、工具调用、连接器事件、自动化执行和失败重试使用统一 correlation ID。
- [A12] 工程验收相关后端定向测试、前端测试、lint、typecheck、构建和端到端验证在容器事实环境中通过。
## 风险与开放问题
### 风险
- 范围过大费用闭环、AI 学习、价值分析和商业化不能同时全量实现,需要按 P0/P1/P2 阶段交付。
- 领域模型迁移:旧 `ReimbursementRequest``ExpenseClaim` 和 JSON 状态并存,必须旁路记录、小步迁移和双读校验。
- 历史证据边界:旧单快照只表达回填时可确认的当前状态,迁移前逐节点办理过程仍以原单据和既有审计为准;后续分析不得把快照事件误当成历史审批事实。
- 认证和租户:当前客户端身份头不适合自动化和 SaaS多租户、记忆和高风险动作开发前必须修复。
- 会话运维:不透明会话已经替代客户端身份头,但仍需补充定时清理、活跃会话查看/全部退出、密钥轮换策略、登录限流和企业 SSO当前 `tenant_id` 仍是最小契约,不代表跨租户查询守卫已经完成。
- Agent 会话租户边界Orchestrator 与 Steward 动作运行时都把可信 `tenant_id` 写入会话状态;创建、恢复、幂等检查点重放、单条删除和批量删除同时校验租户与用户名,同名用户不能跨租户复用 decision 或动作结果。历史无租户状态的会话在认证入口 fail-closed不会被恢复或删除。`agent_conversations` 仍缺少独立 `tenant_id` 列和数据库复合约束,后续迁移需要把当前应用层守卫下沉为结构化租户键。
- 费用事件读取边界:用户态精简 DTO 和首批 HTTP 权限测试已完成;剩余风险是同一 URL 若已有外部客户端依赖旧内部字段会产生契约变更,且未来新增敏感 payload 字段必须继续显式进入白名单,不能恢复任意字典透传。
- 反馈投毒:一次点击或违规习惯不能直接成为记忆,需要候选态、最小样本、制度约束和结果权重。
- 记忆值恢复:现有学习账本只保存 HMAC 指纹,无法恢复具体偏好值;必须由独立记忆表在认证事务中只提取白名单枚举,不能为了复用把自由文本重新塞回账本。
- 记忆与制度冲突:首个切片的三种交通方式当前均只有估价和舱等规则,没有“禁止某种交通方式”的制度维度;后续一旦增加按职级、路线或金额禁用交通方式的规则,必须先提供服务端允许性判定、抑制原因和回归用例,再允许个人记忆参与预填。
- 自动化失控:高准确率不代表高风险动作可以无人值守,必须按动作授权并支持 shadow、Canary、抽检和回滚。
- 虚假节省:风险暴露金额、暂缓付款和工时估算容易被夸大,必须由客户财务确认并执行去重。
- 外部依赖税务、支付、银行、商旅、ERP 和消息平台连接器存在可用性、资质和交付周期风险。
- 移动端成熟度:拍票基础已有,但主业务仍有 mock必须先完成真实登录、列表、草稿和审批闭环。
- 成本失控:高频 LLM、OCR、向量检索和私有部署可能降低毛利需要按租户和模块计量成本。
- 数据稀疏:本地开发库缺少报销、反馈、风险和 golden 样本,正式目标必须来自真实试点而不是演示数据。
### 已处理依赖
- 已有预算、票据夹、OCR、报销草稿、风险规则、审批路由、知识库、员工画像、Agent trace、few-shot 和财务看板可复用。
- 已有 AI 数据飞轮概念与阶段 1 few-shot 实现,不重复建设样本检索底座。
- 已有统一门控管道设计,可作为 AI 场景收口依据。
### 待确认
- 首个目标客户规模和部署形态:中小 SaaS、中大型企业 SaaS 或集团私有部署。
- 首个 90 天付费试点场景:差旅报销、业务招待、日常费用或预算风控。
- 是否进入企业支付、商旅预订或公司卡领域;如果不进入,应明确聚焦 AI 费控与经营分析。
- 客户是否接受工时价值进入 ROI以及对应角色完全成本口径。
- L3/L4 自动化允许的动作、金额阈值、抽检率和授权主体。
- 税务验真、银行、ERP、企微/钉钉等连接器的首批合作范围。
- SaaS 定价的员工档位、包含用量、超额计价和增值模块边界。
### 降级策略
- 首期只选择一个费用场景做完整闭环,其他场景保留现有流程。
- 正式切换前允许 shadow 事件用于一致性校验;正式切换后的关键业务状态与 Outbox 事件必须原子提交,失败时整体回滚并向用户返回可重试状态。
- 画像刷新、分析聚合、消息通知等可重建派生任务失败时进入重试/死信队列,不阻塞已成功提交的关键业务状态。
- 自动化默认停留在 L1/L2只有试点评估和风险护栏通过后才逐项开放 L3/L4。
- 外部连接器未完成前保留人工导入、确认和对账入口,但状态语义和审计事件按正式契约记录。
## 本轮实现记录
- 2026-07-16审批任务正式化新增 `approval_tasks` 与 append-only `approval_task_events`,报销提交、批准、退回和下一节点推进统一生成/完成任务。支持数据库分页筛选、可解释优先级、当前节点 SLA、委托/撤销、转交、顺序加签、并行会签、手动/调度升级、独立事务批量部分成功、乐观版本和不可变幂等响应;历史回填默认 dry-run。
- 2026-07-16风险豁免决定豁免申请新增范围、条件与到期时间只有在职 finance/executive 且非申请人可批准或拒绝;过期 fail-closed。风险变更在同一事务刷新开放审批任务的风险、证据、批量资格和优先级不重置 SLA 窗口;前端展示完整追加审计链并在到期时刷新权威投影。
- 2026-07-16审批工作台接入单据中心“审核单”切换到 `ApprovalTaskWorkspace`,移除旧 Claim 预览列表和静态“批量通过 23 条”提示。工作台提供真实批准、退回、委托、转交、加签、会签、升级和批量动作;未筛选 pending 摘要与队列筛选结果分离,详情返回恢复任务筛选/页码,相同负载安全重试、编辑负载自动换新幂等键。
- 2026-07-16阶段验证容器内相关后端 `122 passed, 4 skipped`、前端审批/单据中心/风险专项 `60 passed`、Ruff 与 Vite 构建通过;一次性 PostgreSQL 17 空库迁移循环 `1 passed`、审批任务和风险并发 `3 passed`。完整前端套件未新增真实回归,仍有 31 项既有陈旧断言/质量债待后续硬化阶段清理。
- 2026-07-16审批动作协议批准、退回和付款统一接入租户化幂等账本、请求指纹、乐观状态/节点前置条件、advisory lock 与 Claim 行锁;完全相同的网络重试不重复扣减预算、写审批、生成事件或付款,不同内容和陈旧状态稳定返回 409。前端确认流在用户确认时冻结请求和前置条件并保持服务端状态与展示标签分离。
- 2026-07-16风险门禁与处置高危/严重可行动风险在任何审批副作用前阻断;类型化风险处置覆盖确认、误报、补件、整改、豁免申请和完成处置,具备乐观版本、权限守卫和只追加审计。门禁合并持久观察与未匹配原始风险,重大观察落库失败 fail-closed审批、处置和 Hermes 扫描通过 Claim 公共锁协调,扫描过期快照不会覆盖新事实。
- 2026-07-16审批工作台审批中心接入可解释优先级投影展示风险、预算、金额、等待时长、证据完整度和 advisory-only AI 建议;风险证据卡读取真实持久字段、显示生命周期和明确动作,但不提供绕过风险门禁的直接审批入口。
- 2026-07-16审批与风险验证容器内审批/风险/费用服务组合 174 项、迁移/所有权 54 项、前端 73 项及 Vite 生产构建通过;一次性 tmpfs PostgreSQL 17 完成 13 项迁移和 1 项真实并发验证,临时数据库自动清理且持久开发库未修改。变更 Python 文件 Ruff F/I 与 `git diff --check` 通过。
- 2026-07-16严格响应重放修复审批和风险动作只保证副作用幂等、却在重放时返回当前投影的问题。审批账本保存完整 `ExpenseClaimRead`;风险 append-only 事件通过 `20260716_0012` 保存完整处置响应。单据或风险后续推进后,旧请求只返回首次快照和当时事件,不暴露后续状态;历史无快照事件按目标版本安全重建。门禁同步增加申请/报销阶段匹配和显式 `route_review` 路由语义。容器内审批、费用 Case、路由、风险和严格重放组合 182 项通过,一次性 PostgreSQL 17 迁移与并发共 17 项通过。
- 2026-07-16分层组织记忆`travel_application.transport_mode` 已形成当前输入/规则 > 企业 > 部门 > 个人的解析链。租户管理员可维护 30-365 天有效的企业/部门低敏记忆;同级冲突 fail-closed低级覆盖、过期和撤销均返回可解释但脱敏的状态。创建、更新、撤销使用作用域锁、稳定幂等键和请求指纹响应丢失可安全重放请求内容变化返回冲突。
- 2026-07-16租户化历史案例风险观测、人工反馈、few-shot 关系数据和 Qdrant 向量统一绑定租户、场景、制度和规则版本Hermes 按租户分别构建图与历史,风险规则生成不再回落到默认租户。报销预审与 AI 助手接入只读 `historical_case_evidence`,旧版本显式标 stale检索故障降级为空证据。
- 2026-07-16历史证据隐私边界公开协议仅返回“历史已确认/历史误报,仅供复核”的固定摘要,不暴露样本 ID、费用单号、人工评论和历史结论原文历史命中不改变 review ID、规则 findings、passed、blocking count、预算复核或审批路由。
- 2026-07-16分层学习验证容器内分层与组织记忆 44 项、历史案例/风险/预审/规则生成 90 项、报销接口与费用服务 137 项、Golden 与历史注入 28 项、迁移/模型/租户组合 70 项通过;前端 18 项和 Vite 生产构建通过。一次性 tmpfs PostgreSQL 17 完整迁移循环 9 项通过,临时容器自动清理且开发数据库未修改;变更 Python 文件 Ruff F/I、compileall 与 `git diff --check` 通过。
- 2026-07-16P0 提交前预审握手):新增稳定预审决策契约、输入/规则/动态 findings 指纹和结构化整改动作;申请与报销提交前均由服务端重新计算完整风险上下文,可整改重大风险在预算占用前返回 409握手期间风险变化返回 `PRE_REVIEW_CHANGED`,预算治理和人工判断风险继续进入领导与 P8。
- 2026-07-16P0 费用事件续接):新增 `application_pre_review_completed` / `claim_pre_review_completed` 时间线语义,预审与提交共享 correlation/causation票据归集完成后刷新预审申请批准生成空报销草稿时 Expense Case 保持 `approved_to_spend`,首张票据归集后再进入 `claiming`
- 2026-07-16租户与路由修复AI 草稿、申请预览、Steward 和差旅测算透传真实租户Claim 核心访问和关联草稿后台任务按 CaseLink 与服务端租户隔离;仅未整改的人工判断/预算治理高风险申请进入同部门 P8已解决风险不再重复升级。
- 2026-07-16结构化风险回填409 findings 保留原始 source、business stage、risk domain 与 actionability前端即使详情刷新失败也能生成可见整改卡不再被 `ai_pre_review` 汇总过滤规则丢弃。
- 2026-07-16工程拆分提取申请关联、职级标准调整、预审端点、风险清单指纹与租户查询范围职责所有本轮触及的核心服务、端点和前端提交模块均回到 800 行硬上限以内。
- 2026-07-16验证容器内费用服务与审批路由 126 项、费用事件/接口/租户/票据关联 63 项、报销接口全量 20 项和前端预审/时间线 24 项通过Vite 生产构建、Python compileall、关键未定义符号检查与 `git diff --check` 通过。
- 2026-07-13完成产品能力、端到端费用旅程、AI 学习飞轮、KPI 和商业模式分析,形成总功能概念文档。
- 2026-07-13规划阶段只沉淀功能规划没有修改业务代码、数据库结构或现有接口。
- 2026-07-13P0 基础切片):新增 `ExpenseCase``ExpenseCaseLink``BusinessEvent` 模型与 `ExpenseCaseService`,提供 `/api/v1/expense-cases/by-claim/{claim_id}` 时间线查询接口。
- 2026-07-13P0 基础切片草稿、提交、退回、审批、申请转报销、付款和申请归档开始旁写结构化事件事件具备租户字段、correlation、causation、幂等键和待投递状态并与业务状态在同一事务提交。
- 2026-07-13事务修复关联申请归档/解绑的内部审计改为 `flush`,由外层业务事务统一提交,避免审计日志提前提交付款或归档状态。
- 2026-07-13迁移桥接新增第一条 migration-owned schema revision服务启动先执行 Alembic`create_all` 明确排除三张迁移表。完整历史 schema baseline、正式数据库 upgrade/rollback 和停止旧 DDL 仍未完成。
- 2026-07-13验证容器内新增测试与既有差旅主链路回归共 24 项通过Alembic PostgreSQL upgrade/downgrade 离线 SQL、启动脚本语法、OpenAPI 路由和新增文件静态检查通过。未对持久化开发数据库执行迁移。
- 2026-07-13P0 认证安全切片):新增 `AuthSession` 不透明 Bearer 会话及 `20260713_0002_auth_sessions.py`,登录 token 只返回一次、数据库只保存摘要;`/auth/me`、会话结束和登出均从服务端会话解析身份,登出与使用指标收尾原子提交。
- 2026-07-13管理面收口移除生产代码中的 `X-Auth-*` 授权来源,保护 Settings、模型连通性、缓存、员工、分析、Agent 运行/轨迹、风险观测、审计及系统日志;已初始化 Bootstrap 返回脱敏状态并拒绝匿名重配置Vite Setup 桥同步锁定。
- 2026-07-13前端会话Web 请求、流式响应和页面关闭收尾统一使用 Bearertoken 与过期时间只保存在 `sessionStorage`,集中处理 `401`、空闲过期和服务端过期;业务经理不再被前端视为平台管理员。
- 2026-07-13认证验证容器内认证/Bootstrap/费用事件/OpenAPI 定向测试 21 项通过,受保护业务端点回归 13 项通过,全量测试收集 805 项成功前端会话、请求、Setup 锁和权限测试 17 项通过,生产构建通过。迁移仅生成并检查 upgrade/downgrade 离线 SQL未写入持久化开发数据库。
- 2026-07-13P0 提交一致性补缝AI 新建费用申请并直接提交时,先持久化为草稿,再统一委托 `ExpenseClaimService.submit_claim`;提交校验、预算预占、申请提交风险标记、`application_submitted` 事件和审批状态不再由 AI 入口分别维护。
- 2026-07-13事务边界修复预算表运行时就绪检查改为复用当前 Session 连接,避免通过 Engine 执行 metadata 检查时隐式提交已 `flush` 的申请;即使预算预占后 Expense Case 事件写入失败,申请、预算额度、预算流水、预占和 Case 数据也会整体回滚。
- 2026-07-13提交一致性验证容器内 AI 申请直接提交、失败回滚、保存草稿副作用、申请提交主链路及预算/Expense Case 回归共 21 项通过,`ruff --select F,I` 通过;未修改数据库结构,未对持久化开发数据库执行迁移。
- 2026-07-13P0 前端时间线):申请/报销详情在原有横向进度下接入真实 `/api/v1/expense-cases/by-claim/{claim_id}`,新增稳定排序、中文事件语义、状态迁移、退回原因、操作人和发生时间展示;未知事件使用通用文案,不暴露 correlation、幂等键或 Outbox 投递状态。
- 2026-07-13前端兼容与验证单据切换时清空旧时间线并通过请求序列防止乱序覆盖未纳入 Expense Case 的 404 显示非阻断兼容态,其他接口错误支持重试且不影响原有进度与单据操作。容器内 99 条定向前端测试和 Vite 生产构建通过。
- 2026-07-13P0 用户态事件契约Expense Case 响应收口为 Case 基础状态、关系类型和事件安全摘要,事件载荷采用递归白名单;关联单据 ID、聚合信息、幂等与关联链、Outbox 投递字段及未知嵌套内部字段不再通过用户接口返回。
- 2026-07-13P0 草稿事件补缝AI 工作台与小财管家新建/更新费用申请草稿分别写入 `claim_draft_created` / `claim_draft_updated`租户从服务端身份透传事件、Case、Link 与草稿同事务提交失败整体回滚。动作幂等组合租户、操作人、run ID 与稳定草稿快照:完全相同的 HTTP 保存重放复用同一草稿和事件,并发竞争由稳定聚合 ID 与数据库主键仲裁,同一 run 内的真实内容变化仍保留独立事件。
- 2026-07-13AI 申请身份边界):申请预览快速入口不再接受请求体中的 `user_id`、管理员标记、角色、员工编号或其他身份字段作为授权事实,全部强制绑定服务端会话;伪造管理员身份编辑他人退回申请会返回 400且原申请与费用事件不发生变化。
- 2026-07-13安全与事务验证容器内受影响后端定向回归 36 项、Expense Case 前端兼容测试 9 项和 Python `ruff --select F,I` 通过;覆盖本人、当前审批人、财务、管理员、无权限、跨租户、整 Case 安全摘要、草稿新建/更新、失败回滚、HTTP 创建重放、相同快照去重、不同快照留痕、Steward 重放及伪造身份越权。未修改数据库结构,未执行持久化开发数据库迁移。
- 2026-07-13联调边界当前持久化开发数据库尚无 `auth_sessions``expense_cases``expense_case_links``business_events` 表,浏览器登录无法获得有效认证凭证,因此本轮未声称完成真实页面端到端联调,也未擅自执行数据库迁移。计划、消费/票据、入账、对账和复盘事件仍待后续补齐。
- 2026-07-14迁移所有权加固新增统一 `schema_ownership.py`,七个运行时初始化入口只创建 legacy 表;标准启动在 Alembic upgrade 前执行只读漂移预检revision 与 migration-owned 表集合不一致时 fail-fast且不会自动 stamp 或修改数据库。
- 2026-07-14真实迁移验证在主应用容器连接的一次性 tmpfs PostgreSQL 17 中完成空库升级、重复升级、关键约束/索引、真实外键级联、降级到 base、legacy 哨兵保留、无版本自有表漂移拒绝和再次升级,`test_alembic_migrations.py` 4 项通过,最终 revision 为 `20260713_0002`;临时容器已自动清理,持久化开发数据库复查仍未迁移。
- 2026-07-14剩余边界当前两条 revision 只覆盖 Expense Case、Business Event 和 Auth Session完整 legacy schema baseline 及停止其余运行时 DDL 仍未完成;本轮受影响服务回归 46 项通过,既有员工目录历史部门归一化用例仍单独失败,未混入本次迁移安全范围。
- 2026-07-14历史旧单接入新增独立 `ExpenseCaseLegacyBackfillService``backfill_legacy_expense_claim_cases.py`。命令只读取显式 `DATABASE_URL`,默认 dry-runapply 强制目标核验、精确确认、迁移 head、advisory lock 和批次事务。每张旧单只创建一条 `historical_claim_imported` 系统快照,保留源时间和指纹、明确不重建历史,并以 `suppressed` 阻止实时投递。
- 2026-07-14克隆库联调将持久开发库以只读 `pg_dump` 流式恢复到一次性 tmpfs PostgreSQL 17原 40 张表、4 张费用单、105 名员工、248 条预算和 62 个 Agent 资产完整保留。升级到 `20260713_0002` 后首次 dry-run 识别 4 张旧单apply 创建 4 组 Case/Link/Event重复 apply 创建 0 条;隔离后端完成登录、`/auth/me`、旧单时间线 200 和登出,内部回填字段未出现在 API。临时数据库和隔离进程已清理持久库复查仍为原数据签名且没有 migration-owned 表。
- 2026-07-14安全复核与质量验证交叉审查后补齐超长租户幂等键稳定哈希、Link/Case 租户一致性、孤立 Case 冲突、URL 路由参数覆盖防护和部分批次失败进度摘要;前端将快照语义明确为“纳入时状态/节点”。容器内 65 项后端定向测试、11 项前端测试、Ruff 和 Vite 生产构建通过。全库代码体积门禁仍被本次未修改的 `RiskRuleGenerationService` 807 行存量问题阻断,未混入当前功能提交。
- 2026-07-14AI 行为闭环首个切片):新增 `AIDecision``AIDecisionFeedback``WorkflowOutcome` 三类独立事实,把申请预填建议、用户确认或显式字段纠正、草稿保存或申请提交结果通过稳定 `decision_id` 关联。只有已认证的申请预览快速入口显式传入服务端 `CurrentUserContext` 时才写账本;通用 User Agent、模板预览和单据详情编辑路径均不写入避免伪造身份和非 AI 操作污染样本。
- 2026-07-14字段纠正与隐私边界申请表编辑器在当前会话中维护首次建议值与最终显式编辑值多次修改保留最初建议改回原值会移除差异日期调整同时记录 `time` 与派生 `days`。服务端最终值始终从解析后的申请 facts 重建,不信任客户端 `finalValue`;持久化层只保存字段名和带密钥版本的 HMAC-SHA256 值指纹不复制事由、地点、人员、金额等原始值原始对话、Prompt、模型全文、附件和支付敏感信息也不进入学习账本。
- 2026-07-14可信度边界浏览器编辑轨迹仍标记为 `verification_status=client_observed``trust_level=behavioral_analytics_only``training_eligible=false`,只能用于受限产品统计;服务端签发预览消费后提升为 `server_verified`,仅证明“服务端签发快照与最终业务事实的差异已核验”,不证明浏览器声称的模型来源,也仍不直接训练模型、自动放行风险或激活企业记忆。
- 2026-07-14事务与一致性学习三表与申请、预算及 Business Event 使用同一 Session在最终 commit 前 `flush`,失败整体回滚;提交场景通过提交服务的事务内回调关联真实 `application_submitted` 事件。Case、Business Event 和 Decision 使用包含 `tenant_id + expense_case_id` 的复合外键,既拒绝跨租户,也拒绝同租户跨 Case 串联。前端估算使用消息级请求版本和输入指纹,只合并估算派生字段,旧响应不会覆盖后续编辑。
- 2026-07-140003 迁移与验证):新增 `20260714_0003_ai_learning_loop.py`三张学习账本表纳入集中迁移所有权AgentRun 与旧 ExpenseClaim 保持带索引软引用以兼容空库迁移顺序。一次性 tmpfs PostgreSQL 17 的升级、重复升级、跨租户/同租户跨 Case 外键拒绝、降级和再次升级 4 项通过;容器内学习账本、入口边界和迁移所有权定向 29 项,日期联动及异步估算关键场景 5 项通过。持久开发库只读复查仍为 `40|4|105|248|62`7 张 migration-owned 表数量为 0。
- 2026-07-14服务端预览决策新增认证的 `POST /api/v1/reimbursements/application-previews`。服务端从原始申请文本和当前登录身份重新解析字段、重跑规则测算,再签发 30 分钟有效的 `decision_id`浏览器返回的模型来源和建议值不被直接认证。预览绑定租户、actor、登录会话和 conversation字段只保存独立版本密钥生成的 HMAC 指纹;字段存在性也纳入指纹,新增、删除和改值都能由服务端识别。
- 2026-07-14消费与续签快速保存/提交显式携带动作类型、`decision_id` 和稳定请求 ID。服务端锁行校验后在 Claim、Expense Case、Business Event、AIDecision、Feedback、Outcome 的同一事务中一次消费;当前会话已有有效决策时禁止省略 ID 降级。保存草稿提交成功后再尽力签发基于服务端事实的新 `decision_id`,续签失败只返回无新 ID不反转已成功业务动作相同请求安全重放不会重复建单或重复写学习账本。
- 2026-07-140004 迁移与验证):新增 `20260714_0004_ai_application_preview_decisions.py`,预览决策表和正式 Decision 复合租户外键纳入集中迁移所有权。一次性隔离 PostgreSQL 17 完整 upgrade、重复 upgrade、约束/索引、downgrade 和再次 upgrade 4 项通过;容器内新增决策安全用例 9 项、旧快速保存/提交 4 项、申请学习账本 9 项、迁移与所有权 26 项通过且条件型 PostgreSQL 用例 1 项跳过3 组前端定向测试及 Vite 生产构建通过。持久开发库只读确认 8 张 migration-owned 表数量仍为 0。
- 2026-07-14多入口统一决策闭环抽取 `ExpenseApplicationPreviewWorkflow`,认证签发、锁行消费、动作幂等、学习落账和草稿续签不再由 HTTP 端点重复编排。Steward、小财管家结构化预览和通用 Orchestrator 均复用该工作流;用户纠正字段后仍消费原始 decision使服务端能够把建议值与最终业务事实记为 `server_verified` 接受或纠正证据。
- 2026-07-14Orchestrator 身份与会话边界):外部用户消息、定时任务和系统事件统一强制认证,`schedule` / `system_event` 进一步要求平台管理员;登录态覆盖请求中的身份、租户和调度操作人别名,堵住匿名或普通用户伪造来源触发 Hermes 管理任务的路径。会话创建、恢复和删除同时绑定可信租户与用户名,历史无租户状态会话 fail-closed。申请 decision 只从服务端会话恢复,客户端 preview/decision 在进入编排前被移除;保存草稿后的 next decision 回写会话并支持刷新/换端恢复结构化预览。
- 2026-07-14小财管家 fail-closed完整预览展示前先使用稳定 request ID 签发 canonical decision签发失败时允许继续编辑和以同一 ID 重试,但禁止保存/提交且不回退旧副作用链路。保存/提交失败重试复用稳定 action request ID保存成功同步草稿信息与 next decision。
- 2026-07-14decision 生命周期加固同一租户、用户、登录会话、conversation 和 HMAC 快照即使使用不同签发 request ID也复用当前 active decision避免多个并行 decision 命中草稿幂等捷径后遗留未消费状态。动作发现 decision 过期、已消费或不可用时,前端清空旧 ID、生成新的签发 request ID 并开放重新签发。
- 2026-07-14跨租户检查点加固Steward 动作会话创建显式写入服务端租户;租户 A/B 即使使用相同用户名、conversation 和 trace也会获得独立会话与 decision不再跨租户返回幂等结果或敏感预览内容。
- 2026-07-14统一闭环验证容器内服务端预览、Steward 动作/图运行、Orchestrator 外部来源授权及决策消费组合回归 50 项通过Python Ruff F/I 通过;前端结构化动作、会话恢复、工作台路由、富确认和动作脚本共 18 项通过Vite 生产构建通过。并行读取用户正在变动的规则工作簿曾触发 `openpyxl` ZIP 句柄竞争,相关套件改为串行后全部通过,未修改规则工作簿。
- 2026-07-14个人出行方式记忆新增租户化 `memory_entries``memory_evidence_links`,首个切片只存储 `travel_application.transport_mode` 的“飞机/火车/轮船”。同一租户、员工、场景和值需要 3 个不同 Expense Case 的服务端核验纠正、至少 2 个当前审批通过且证据跨 7 天才激活Candidate/Active 默认有效期分别为 90/180 天。
- 2026-07-14证据信任边界记忆证据严格绑定当前 tenant、actor、employee、Case owner 和 Claim owner并要求 Decision、Feedback、Outcome 指向同一申请、同一 Case 和同一个真实 `application_submitted` 业务事件;事件聚合必须是对应 `expense_claim`。同 Case 重放、invalidated/reversed 事实、最新退回、反向纠正和非白名单纠正不会继续支持 active 记忆。
- 2026-07-14应用、抑制与遗忘个人记忆只补空白出行方式任何当前显式值都优先。命中后服务端重新计算交通与总额估算再签发 canonical decision记忆异常降级为无记忆不阻塞预览。反向或非白名单纠正抑制 active用户忘记后状态改为 revoked、清空可恢复值和指纹旧请求不可复活。
- 2026-07-14前端记忆解释申请核对表新增独立 `TravelReimbursementMemoryPanel`,展示已应用的常用出行方式、证据数量、学习回执和“忘记此偏好”入口;本地会话快照支持跨刷新恢复,忘记偏好不会篡改当前申请字段。
- 2026-07-14个人记忆验证容器内个人记忆专项 16 项、记忆/预览决策/迁移/所有权组合回归 56 项通过且 1 项条件跳过、前端申请与记忆组合 83 项通过;一次性 PostgreSQL 迁移循环、Ruff、Vite 生产构建和 `git diff --check` 均通过。当前规则中心尚不存在交通方式禁用维度,制度冲突守卫作为后续规则扩展的前置门禁保留,不以恒真占位判断冒充已实现。
- 2026-07-16零录入票据首个闭环附件后台任务从“大而全”的内存编排中拆出只读 `ExpenseReceiptMatcher` 与可逆 `ExpenseReceiptAssociationService`。系统按当前租户、员工、Expense Case、已审批申请、票据日期、城市、路线和场景选择草稿只有唯一高置信草稿自动归集低置信、多候选、无草稿及仅有申请时均以需要确认的安全终态返回并保持零业务写入。
- 2026-07-16事务、幂等与租户边界票据 Link、`receipt_received`、附件写入和 `attachment_associated` 由同一关联编排器提交,数据库快照与文件目录备份共同补偿中途失败,事件或文件元数据异常时恢复 Claim/Case/Link/Event/票据及旧附件目录相同票据任务按租户、owner 和票据集合持久去重。票据目录加入无碰撞租户摘要,任务、候选 Claim 和票据同时绑定租户与本人,平台管理员也不能跨租户读取任务。
- 2026-07-16持久任务与并发新增 migration-owned `attachment_association_jobs``20260716_0006`。任务状态、结构化结果、owner 上下文、attempt、租约与 generation 写入数据库GET 可恢复 queued 或租约过期任务,`attempt_count + running` 作为栅栏阻止旧 worker 或迟到回调覆盖新终态。同票据和同 Claim 分别使用进程锁与 PostgreSQL advisory lock 串行化Claim 锁内清理旧事务并重新匹配,避免不同票据并发选择同一空明细。待确认或失败任务保留原代历史,再次发起创建新 generation 并重新评估;已自动关联成功的代际继续幂等复用。评分改为纯只读查询,每份票据必须独立达到最小证据,避免无关票据被同批强证据带入。
- 2026-07-16小财管家交互与验证任务结果新增 Case、申请、置信度、原因、异常、缺失项、风险项、候选和草稿载荷前端可跨会话恢复自动完成直接查看草稿仅申请候选查看申请待确认不伪装成功幂等重放显示“已关联”成功结果仍展示风险和复核要求。容器内后端归集专项 20 项、归集与相邻服务/迁移所有权组合回归 71 项、前端关联链路组合回归 29 项、Ruff F/I/UP 和 Vite 生产构建通过;一次性 tmpfs PostgreSQL 17 的 0006 完整迁移循环 4 项通过并已清理,持久开发库未修改。既有大型报销服务与接口套件仍存在旧审批、删除和风控断言失败,未把这些基线问题误报为已解决。
- 2026-07-16保留边界预算、项目、成本中心和个人记忆偏好尚未接入票据候选评分任务已持久化并支持租约恢复但尚未建设独立消息队列、运维重试面板和死信治理完整 G2 与平台级异步任务治理仍未完成。
- 2026-07-17费用价值闭环新增 Savings Ledger、CFO 价值分析和财务确认/冲回链路;现金、工时、风险暴露和预计机会严格分账。住宿标准调整从服务端锁定原金额创建机会,付款/生产连接器回执产生 actual realization独立财务确认后才进入现金 KPI退款以负向追加事实冲回。
- 2026-07-17财务连接器建立配置生命周期、HMAC、防重放、事件幂等、对账、ERP 入账、退款/冲回、operational event 和 mock/test/staging 隔离。端到端以本地自签的 production-mode 事件验证申请、票据、报销、预审、审批、回执、ERP、归档、Savings 与商业价值契约;它不代表真实银行/ERP 回执,模拟回执也不会改变核心财务事实。
- 2026-07-17真实发布遥测规则发布从真实 observation、可信 disposition/reviewer label、独立盲审负样本和保守 recall 进入 Monitor/Guardcollecting、积压、聚合失败或低 precision 均不晋级并保持 stable。0023 提供 audit sample、双人票、append-only 和 PostgreSQL 并发保护。
- 2026-07-17商业闭环新增套餐、订阅、账期、权益、配额、用量、成本、ROI、毛利和定价走廊中央 Orchestrator、Runtime Chat、OCR、金融连接器和附件源文件写入按权威数量预占、结算或释放客户价值、平台收入和内部成本不混账。
- 2026-07-17全链租户安全新增 0025-0028统一 Tenant、Employee、Claim、Agent Asset、Knowledge、Ontology、Hermes、Finance Report、Qdrant、文件和缓存作用域ONLYOFFICE 改为数据库一次性会话与 SSRF/重放保护。审批员工解析按认证企业或结构化 Claim 企业首层过滤,跨租户身份和相同名称不再命中。
- 2026-07-17工程收口验证176 个后端测试文件在主应用容器内分片或专项通过,费用主服务 121 项通过fresh PostgreSQL 迁移/并发总探针 `87 passed / 0 skipped / 0 failed`head 为 0028Web 全量 `815 passed / 0 failed` 与 Vite build 通过Mobile lint/typecheck、197 个新增 Python 文件 Ruff、目标 compileall、受门禁核心类/组件 800 行检查和 `git diff --check` 通过。
- 2026-07-17完成边界工程代码和容器验收已收口生产 ONLYOFFICE DNS/TLS、备份副本迁移演练、逐租户 SMTP、真实 provider/会计规则、移动/浏览器实机联调、30/90 天企业试点、客户财务签字和合同定价仍必须使用目标环境与真实业务证据完成,不能由 mock 或本地测试替代。总验收见 `document/development/2026-07-17/feature/engineering-closure-and-production-readiness/`

View File

@@ -0,0 +1,316 @@
# AI 费用闭环与价值证明 开发 TODO
更新时间2026-07-17
文档路径document/development/2026-07-13/feature/ai-expense-closed-loop-and-value-proof/TODO.md
## 使用规则
- 每个 TODO 必须对应 `CONCEPT.md` 中的目标、能力、方案或验收点。
- 只有完成实现并验证后,才能把 `[ ]` 改成 `[x]`
- 已完成项必须补充文件、接口、命令、容器测试或真实页面证据。
- 如果需求发生变化,先更新 `CONCEPT.md`,再调整本 TODO。
- 实施顺序遵循P0 费用闭环与数据基础 → P1 安全自动化与学习 → P2 费用经营与价值证明 → P3 商业化复制。
- 所有后端、集成、迁移和依赖外部服务的验证只允许在项目容器内运行,单条测试命令最大超时 60s。
## 1. 调研与边界
- [x] [CONCEPT: 背景与问题] 盘点申请、票据、报销、预算、风险、审批、付款、分析、Agent trace、反馈和 AI 数据飞轮现状。
证据:`server/src/app/api/v1/endpoints``server/src/app/services``web/src``mobile/app/src`、容器运行时 OpenAPI 共 156 个业务操作。
- [x] [CONCEPT: 背景与问题] 确认当前主要断点是消费/取票、真实支付入账、结果回流、节省归因和商业计量,而不是页面数量不足。
证据:`CONCEPT.md`“背景与问题”;`expense_claim_approval_flow.py` 付款为状态更新;本地容器关键反馈/样本表聚合结果为 0。
- [x] [CONCEPT: 目标与非目标] 确认本功能采用一个总功能点分阶段实施,不一次性替换所有现有接口,不自建支付和商旅供应链网络。
证据:`CONCEPT.md`“目标与非目标”。
- [x] [CONCEPT: 风险与开放问题] 记录认证、租户、旧模型迁移、反馈污染、虚假节省、自动化失控、连接器和数据稀疏风险。
证据:`CONCEPT.md`“风险与开放问题”。
## 2. 开发前置门禁:试点与基线
- [ ] [CONCEPT: 待确认] 确认首个目标客户规模、部署形态、购买决策人和最小租户边界。
- [ ] [CONCEPT: 待确认] 确认首个 90 天试点费用场景,只允许一个场景进入 P0 开发。
- [ ] [CONCEPT: 待确认] 确认是否进入企业支付、商旅预订或公司卡领域,并冻结首期连接器范围。
- [ ] [CONCEPT: 待确认] 确认客户 ROI 中现金节省与工时价值的口径、基线窗口和签字人。
- [ ] [CONCEPT: 指标与验收] 在 P0 开发前采集人工分钟、退回率、完成周期、人工触点、风险反馈、费用金额和付款结果基线。
- [ ] [CONCEPT: 指标与验收] 为 P0/P1/P2 分别冻结一条可纵向跑通的闭环验收,不以“模块代码已完成”代替业务结果。
## 3. 契约与设计
- [x] [CONCEPT: 费用领域与编排] 定义 `Expense Case`、申请、票据、报销、审批、付款、凭证和归档的领域边界与迁移关系。
证据:`expense_cases.py`、Expense Case/Link/Event 模型、连接器付款/ERP/归档与 Savings 冲回 E2E。
- [x] [CONCEPT: 数据与契约] 定义 `expense_cases``expense_case_links` 和最小状态机,明确非法状态跃迁。
证据:`20260713_0001_expense_case_business_events.py``ExpenseCaseService` 与状态/事件回归。
- [x] [CONCEPT: 数据与契约] 定义 `business_events` 事件信封、事件词典、correlation ID、幂等键和版本策略。
证据:`BusinessEvent` schema/service、业务事件词典及申请/报销/审批/支付/ERP/Savings 回归。
- [x] [CONCEPT: 业务事件与 AI 决策] 定义 `ai_decisions``ai_decision_feedback``workflow_outcomes` 契约。
证据:`ai_learning.py``expense_application_learning.py``20260714_0003_ai_learning_loop.py`;三类事实分别表达 AI 建议、用户采纳/显式字段纠正和业务结果,技术执行成功、用户反馈与工作流结果不复用同一状态。
- [x] [CONCEPT: 业务事件与 AI 决策] 定义落单前服务端预览决策的签发、授权、过期、一次消费、重放和续签契约。
证据:`ai_application_preview.py``expense_application_preview_decisions.py``expense_application_snapshot.py`;预览不创建空 Case绑定租户、actor、登录会话与 conversation30 分钟过期,保存/提交成功后一次消费;保存草稿在业务提交后尽力返回基于服务端事实的新 `decision_id`,续签失败不反转已成功动作。
- [x] [CONCEPT: 记忆与学习] 定义并实现 `memory_entries``memory_evidence_links`、优先级、有效期、敏感白名单、撤销和遗忘契约;首个切片仅覆盖 `travel_application.transport_mode`
证据:`ai_memory.py``expense_application_memory.py``expense_application_memory_evidence.py``20260714_0005_ai_memory.py`;记忆使用租户、主体、场景、字段和值指纹精确隔离,值只允许“飞机/火车/轮船”,服务异常降级为不应用记忆。
- [x] [CONCEPT: 自动化决策] 定义动作风险、金额阈值、置信度、证据完整度、可逆性、抽检率和企业授权策略。
证据:`automation_eligibility.py``test_automation_eligibility.py`;资金、制度和高风险动作始终人控。
- [x] [CONCEPT: 节省与价值] 定义 Savings Ledger 的机会、执行、实现、确认、去重和归因状态。
证据Savings 模型、0015 迁移、状态服务与并发/E2E 测试。
- [ ] [CONCEPT: 连接器] 定义票据邮箱、税务、企业卡、商旅、支付、银行、ERP、消息和 SSO 连接器协议。
- [x] [CONCEPT: 权限与安全] 定义服务端会话、租户数据范围、角色和动作级授权契约。
证据:不透明 Bearer Session、`CurrentUserContext`、租户访问策略和 endpoint role dependencies0025-0028 多租户安全迁移。
- [ ] [CONCEPT: 指标与验收] 完成首个试点指标字典、基线采集方案、分子分母、数据源和负责人。
- [x] [CONCEPT: 方案设计] 完成分阶段架构评审,确认新增 service 不继续堆入 `ExpenseClaimService``UserAgentService` 或大型前端 composable。
证据访问策略、员工解析、附件、预审、Savings、商业、连接器、发布遥测及前端 composable 均按职责拆分;受门禁核心类/组件 800 行检查通过。
## 4. P0 后端实现:费用闭环与数据基础
- [x] [CONCEPT: 权限与安全] 实现服务端可验证会话/JWT移除客户端身份头作为授权事实来源。
证据:`auth_sessions.py``auth_session.py``deps.py``auth.py``authSessionStorage.js`;登录签发不透明 Bearer token数据库仅保存 SHA-256 摘要,生产代码不再信任 `X-Auth-*`,过期/撤销/伪造会话回归测试通过。
- [x] [CONCEPT: 权限与安全] 为管理面、Bootstrap、Settings、模型连通性、缓存、审计和系统日志补齐平台管理员保护。
证据:`bootstrap.py``settings.py``audit_logs.py``agent_traces.py``system_logs.py``vite.config.js`;已初始化 Bootstrap 脱敏且拒绝匿名重配,平台管理员/业务经理权限边界和 Vite Setup 锁测试通过。
- [x] [CONCEPT: 权限与安全] 继续盘点并收口风险规则发布、制度发布及其他尚未纳入本轮的敏感动作,按动作定义平台管理员或双人复核权限。
证据Agent Asset/Rule Editor/Reviewer、发布盲审双人票、风险豁免独立决定、平台资产只读和 ONLYOFFICE 写入权限均有服务端守卫与安全测试。
- [x] [CONCEPT: 权限与安全] 为本轮新增表及 Claim、Employee、Agent Asset、Knowledge、Ontology、Hermes 和 Report 等纳入范围的共享核心数据补齐 `tenant_id`、约束、查询守卫和默认租户迁移。
证据0025-0028 迁移、复合租户外键、首层 SQL 过滤和 tenant security 测试fresh PostgreSQL 迁移总探针通过。
- [ ] [CONCEPT: 权限与安全] 将 `agent_conversations` 等仍依赖 JSON tenant 的 legacy 状态迁移到结构化租户列和数据库约束。
证据要求:后继迁移、历史回填、复合约束和跨租户回归;当前只能由服务层校验 `state_json.tenant_id`,不得表述为数据库边界已完成。
- [x] [CONCEPT: 权限与安全] 为 Expense Case 查询提供面向用户的精简事件 DTO移除幂等键、correlation、causation 和 Outbox 投递字段,并明确审批意见、退回原因和操作人可见范围。
证据:`expense_case.py``test_expense_case_endpoints.py`;用户态响应只保留安全流程摘要,关联资源 ID 和未知嵌套 payload 被递归过滤,本人、当前审批人、财务、管理员、无权限及跨租户边界测试通过。
- [x] [CONCEPT: 权限与安全] 为 Qdrant collection/namespace、对象存储前缀和缓存键补齐租户隔离回归测试。
证据Knowledge tenant scope、RAG workspace、Qdrant namespace、票据/附件路径、运行缓存和财务快照 tenant fingerprint 回归。
- [x] [CONCEPT: 数据与契约] 建立 Alembic baseline 和正式迁移链,停止请求路径运行 DDL。
证据migration ownership/preflight 与 0001-0028 正式链fresh PostgreSQL 完整 upgrade/downgrade/re-upgrade 通过,请求路径不再创建 migration-owned 表。
- [x] [CONCEPT: 兼容策略] 集中 migration-owned 表所有权并在标准启动迁移前执行只读漂移预检,禁止 legacy bootstrap 越权建表。
证据:`schema_ownership.py``migration_preflight.py``server_start.sh``test_migration_preflight.py``test_schema_ownership.py`;无版本自有表、缺表、多表、未知/多 revision 均 fail-fast不自动 stamp 或修改数据库。
- [x] [CONCEPT: 费用领域与编排] 新增 `ExpenseCaseService` 和费用事件查询接口,保持编排与具体职责分离。
证据:`server/src/app/services/expense_cases.py``server/src/app/api/v1/endpoints/expense_cases.py``GET /api/v1/expense-cases/by-claim/{claim_id}`;容器 OpenAPI 校验通过。
- [x] [CONCEPT: 数据与契约] 新增 `expense_cases``expense_case_links``business_events` 表及迁移。
证据:`20260713_0001_expense_case_business_events.py`;一次性 PostgreSQL 17 已验证表、唯一约束、复合索引、外键级联、降级和再次升级。
- [x] [CONCEPT: 业务事件与 AI 决策] 新增 `ai_decisions``ai_decision_feedback``workflow_outcomes` 表及迁移。
证据:`20260714_0003_ai_learning_loop.py``schema_ownership.py``migration_preflight.py`三表具备租户幂等约束Case/Event/Decision 使用包含费用 Case 的复合租户外键。一次性 PostgreSQL 17 完整升级、重复升级、跨租户及同租户跨 Case 外键拒绝、降级和再次升级 4 项通过,持久开发库未迁移。
- [x] [CONCEPT: 业务事件与 AI 决策] 新增服务端预览决策表、认证签发接口和正式 Decision 关联迁移。
证据:`20260714_0004_ai_application_preview_decisions.py``expense_application_previews.py``reimbursements.py`;动作消费与 Claim、Case、Business Event 和学习三表同事务,跨登录会话重放被拒绝;独立版本密钥生成的 HMAC 指纹不保存低熵字段明文,并编码字段存在性。只有从未成功签发的旧调用可无 ID 降级,当前会话已有有效决策时省略 ID 会返回 409。
- [ ] [CONCEPT: 数据与契约] 为申请、票据、草稿、提交、退回、审批、付款和归档接入统一 correlation ID。
当前进度:报销显式预审与提交已共享 correlation/causationAI 草稿和票据归集已透传租户与稳定幂等键;申请、审批、付款和外部连接器仍需继续统一。
- [ ] [CONCEPT: 业务事件与 AI 决策] 建立事务 Outbox申请、提交、退回、审批、支付和入账状态与事件同事务提交消费端按事件 ID 幂等处理。
- [x] [CONCEPT: 业务事件与 AI 决策] 完成首批草稿、提交、退回、审批、申请转报销、付款和申请归档事件旁写,具备 correlation、causation、幂等键及事务回滚。
证据:`expense_claim_draft_flow.py``expense_claims.py``expense_claim_approval_flow.py``test_expense_case_service.py`;容器测试覆盖同 Case 关联、重复事件去重及 Outbox 失败整体回滚。
- [x] [CONCEPT: 风险与预审] 建立申请与报销提交前服务端预审握手,使用稳定 review ID、输入/规则/动态结果指纹、三态决策和结构化整改动作阻止旧预审复用。
证据:`expense_claim_pre_review_decision.py``expense_claim_pre_review.py``expense_claims.py``reimbursement_pre_review.py`;提交前始终重算完整风险上下文,`needs_fix` 在预算占用前保持草稿并返回结构化 409握手期间风险变化返回 `PRE_REVIEW_CHANGED`,相同输入、规则和 findings 重试只保留一条预审事件。申请与报销使用同一契约:提交人可整改风险必须先处理,人工判断或预算治理风险才进入领导与 P8。
- [x] [CONCEPT: 统一费用事件] 补齐票据归集 → 预审 → 提交切片的阶段与事件衔接,并修复非默认租户在 AI 草稿、预览和 Steward 链路回落到 default 的问题。
证据:`expense_receipt_association.py``expense_claim_approval_flow.py``expense_claim_draft_flow.py``expense_claim_review_preview.py``steward_action_executor.py`;申请批准生成空报销草稿后 Case 保持 `approved_to_spend`,票据关联后进入 `claiming`,跨租户 Case/Event 不能串线。
- [x] [CONCEPT: 权限与安全] 将 Claim 核心查询和关联报销后台任务纳入服务端租户范围,拒绝同用户名跨租户读取、修改、审批或查询任务。
证据:`expense_claim_tenant_scope.py``expense_claim_access_policy.py``linked_reimbursement_draft_jobs.py`;默认租户仅为无 CaseLink 的历史单保留兼容,后台任务强制覆盖客户端 tenant关联申请按 CaseLink 与租户共同校验。
- [x] [CONCEPT: 业务事件与 AI 决策] 收口 AI 新建申请直接提交入口,统一复用申请提交事务,并消除预算 metadata 检查对外层事务的隐式提交。
证据:`user_agent_application.py``budget.py``test_reimbursement_endpoints.py``test_user_agent_service.py`;容器测试覆盖有效预算预占、`application_submitted` 事件、提交风险标记、事件失败整体回滚及保存草稿无提交副作用。
- [x] [CONCEPT: 业务事件与 AI 决策] 为 AI 工作台和小财管家申请草稿补齐新建/更新事件、租户透传、事务回滚和动作幂等。
证据:`expense_application_draft_events.py``user_agent_application.py``reimbursements.py``steward_action_executor.py``test_user_agent_application_draft_events.py``test_reimbursement_endpoints.py``test_steward_action_executor.py`;完全相同 HTTP 保存重放复用同一草稿和事件,同一 run 内不同快照分别留痕,事件失败后草稿与 Case 数据整体回滚。
- [x] [CONCEPT: 权限与安全] 将 AI 申请预览快速入口的用户、租户、角色与管理员身份强制绑定到服务端会话,拒绝请求体伪造身份编辑他人申请。
证据:`reimbursements.py``test_reimbursement_endpoints.py`;对抗用例修复前返回 200修复后返回 400且目标申请和费用事件保持不变。
- [x] [CONCEPT: 权限与安全] 收口通用 Orchestrator 用户消息和会话管理的认证边界,拒绝客户端身份与 decision 注入。
证据:`orchestrator.py``agent_conversations.py``orchestrator_expense_application_workflow.py``steward_graph_action_runtime.py``test_orchestrator_auth_endpoints.py``test_steward_action_executor.py``test_orchestrator_review_flow.py`;用户消息、定时任务和系统事件无认证均返回 401普通用户触发 `schedule` / `system_event` 返回 403登录态覆盖请求身份和调度操作人别名Orchestrator 会话创建/恢复/删除及 Steward 幂等检查点同时校验租户与用户名,申请动作只消费服务端会话 decision。
- [x] [CONCEPT: 兼容策略] 建立迁移桥接:服务启动先执行 Alembic旧 metadata bootstrap 排除 migration-owned 表。
证据:`server_start.sh``schema_ownership.py``migration_preflight.py``20260713_0001_expense_case_business_events.py``20260713_0002_auth_sessions.py`;容器 Shell/静态检查及一次性 PostgreSQL 完整 upgrade/downgrade/re-upgrade 通过。
- [x] [CONCEPT: 兼容策略] 为迁移前已有 `ExpenseClaim` 提供显式、幂等且不伪造办理历史的费用事件快照回填。
证据:`expense_case_legacy_backfill.py``maintenance_database_target.py``backfill_legacy_expense_claim_cases.py`;默认 dry-runapply 强制租户/截止时间、精确目标、迁移 head、advisory lock 和批次事务,事件使用真实回填时间、`history_reconstructed=false``delivery_status=suppressed`。一次性克隆库首次创建 4 组 Case/Link/Event重复 apply 创建 0 条。
- [ ] [CONCEPT: 兼容策略] 正式切换前以 shadow 事件校验现有 `ExpenseClaim` 映射;切换后禁止关键事件可丢弃写入。
- [ ] [CONCEPT: 兼容策略] 制定旧 `ReimbursementRequest` 只读兼容、迁移和停止新增编排的计划。
- [ ] [CONCEPT: 兼容策略] 把新增审批、付款、归档和关系事件移出 `risk_flags_json`,保留旧数据读取兼容。
- [ ] [CONCEPT: 数据与契约] 补齐撤回、取消、驳回、作废、补件、支付失败、对账异常和归档状态。
- [x] [CONCEPT: 连接器] 实现财务连接器统一事件信封、认证、幂等、重试、错误状态和回执事件。
证据:`financial_connector_auth.py``financial_connector_ingestion.py``payment_reconciliation.py`、配置生命周期及 operational events服务/API/并发测试通过。
- [ ] [CONCEPT: 连接器] 实现支付批次、回执、重复付款防护、ERP 凭证和对账的内部契约,首期允许 mock connector 但不得再只写单一“已付款”状态。
- [x] [CONCEPT: 降级策略] 将附件关联和关联报销草稿后台任务迁为可持久化、可恢复、可幂等的任务状态。
证据:新增 migration-owned `attachment_association_jobs``20260716_0006`任务使用租户、owner、票据集合和 generation 去重,运行态带租约与 `attempt_count + running` 栅栏GET 可恢复 queued/租约过期任务。同票据和同 Claim 均由进程锁与 PostgreSQL advisory lock 串行化Claim 锁内重新匹配;待确认或失败历史保留原代并以新 generation 重新评估,自动关联成功代继续幂等复用。进程状态清空、租约过期、旧 worker 回写、不同票据并发同 Claim 和代际重评估回归通过。
- [x] [CONCEPT: 零录入附件关联任务契约] 扩展附件关联任务的向后兼容结果契约,区分自动完成与需要确认,并返回 Case、申请、置信度、原因、异常、缺失项、风险项和候选。
证据:`attachment_association_job.py``attachmentAssociationJobModel.js` 保留旧字段并追加结构化结果;`status=succeeded + resolution=requires_confirmation` 明确表示匹配完成但零业务写入。
- [x] [CONCEPT: 零录入报销首个切片] 新增独立票据匹配器按当前租户、员工、Expense Case、已审批申请、日期、城市和场景计算候选唯一高置信才允许自动归集。
证据:`expense_receipt_matcher.py` 只读评分;覆盖唯一高置信、相近候选、无草稿、仅有已审批申请和已关联重放,申请缺少系统草稿时只返回 `approved_application` 候选。
- [x] [CONCEPT: 零录入报销首个切片] 新增独立归集编排器,把票据 Link、`receipt_received`、附件写入和 `attachment_associated` 纳入同一事务,并保证同票据重试不重复建明细或事件。
证据:`expense_receipt_association.py` 统一编排数据库写入,文件系统使用元数据快照和附件目录补偿;稳定幂等键保证相同票据重试只增加跳过数。事件写入失败回归证明 Claim、Case、Link、Event、票据元数据和附件最终状态均回退。
- [x] [CONCEPT: 权限与安全] 将票据夹存储命名空间和附件关联后台任务授权同时绑定租户与用户,补充同用户名跨租户隔离回归。
证据:`receipt_folder.py` 使用租户与用户联合命名空间,任务状态绑定 `owner_tenant_id + owner_username`;普通用户及平台管理员跨租户查询均返回 404同用户名跨租户不能读取票据。
## 5. P0 前端实现:一键报销与真实移动端
- [x] [CONCEPT: 统一费用事件] 在申请/报销详情接入已有真实 Expense Case 事件时间线,展示申请提交、报销提交、退回、审批、自动生成报销草稿、付款和归档事件。
证据:`TravelRequestExpenseCaseTimeline.vue``useExpenseCaseTimeline.js``expenseCases.js``expenseCaseTimeline.js`保留原有横向进度404 与接口异常均安全降级,容器内 99 条定向前端回归和 Vite 生产构建通过。
- [ ] [CONCEPT: 统一费用事件] 补齐计划、消费/票据、入账、对账和复盘事件,并在真实迁移数据库上跑通完整时间线。
- [ ] [CONCEPT: 统一费用事件] 自动匹配申请、预算、票据、费用类型、项目、成本中心和常用字段。
首个切片先交付申请、票据、费用事件、日期、城市和费用场景匹配;预算、项目、成本中心和记忆偏好随后接入同一候选契约。
- [ ] [CONCEPT: 统一费用事件] 增加“需要确认”区,只展示低置信、缺失或冲突字段。
首个切片在小财管家附件关联结果中复用统一异常模型;后续再收口全屏助手和票据夹入口。
- [x] [CONCEPT: 零录入报销首个切片] 在小财管家实现“上传并发送 → 自动归集已审批申请生成的草稿 → 查看草稿”,不再次上传票据;低置信时只展示候选和异常卡片。
证据:附件任务 composable 将待确认视为安全终态,不发送伪成功通知;自动完成提供“查看草稿”,申请候选提供“查看候选申请”,不伪造报销草稿。
- [x] [CONCEPT: 零录入附件关联任务契约] 会话刷新后恢复新任务结果、候选和异常状态,旧任务结果仍可正常展示。
证据:`workbenchAiMessageModel.js` 持久化新协议,规范化层兼容旧字段;容器内前端票据关联、协议、卡片、幂等重放、风险复核和会话恢复组合回归 29 项通过。
- [x] [CONCEPT: 风险与预审] 报销详情提交前先执行真实预审,`needs_fix` 直接展示结构化风险并停止提交,其他结果携带 review ID 与输入指纹完成握手。
证据:`reimbursements.js``api.js``useTravelRequestDetailRiskSubmit.js`;错误响应保留 status/code/detail/review风险可即时回填并触发详情刷新前端预审、409、请求头、时间线组合测试 24 项和 Vite 生产构建通过。
- [ ] [CONCEPT: 风险与预审] 风险卡提供一键补件、修正、解释和重新预审入口。
- [ ] [CONCEPT: 统一费用事件] 退回后直接定位问题字段,支持断点续办,不要求重新发起对话。
- [ ] [CONCEPT: 移动端] 接通真实登录恢复、路由守卫、报销列表、详情、草稿、上传和审批 API。
- [ ] [CONCEPT: 移动端] 完成拍照/相册 → OCR → 票据夹 → 费用事件 → 报销草稿真实闭环。
- [ ] [CONCEPT: 移动端] 删除或禁用没有真实行为的报销、审批、发送和语音主按钮。
- [ ] [CONCEPT: 前端] 修复表格整行点击键盘不可达、弹窗焦点管理、焦点环和移动触控目标。
- [ ] [CONCEPT: 前端] 移除页面级全局 `transform: scale()`,使用真实响应式布局承载费用闭环页面。
## 6. P1 算法与规则实现:安全自动化与持续学习
- [x] [CONCEPT: 自动化决策] 实现 L0-L5 动作级自动化等级和资格计算器。
证据:`AutomationEligibilityCalculator` 将 L0-L4 资格与 L5 人控分开计算,不直接执行动作;单元测试覆盖保守降级。
- [x] [CONCEPT: 自动化决策] 为每个可自动动作实现硬白名单、金额上限、证据要求、抽检率和企业上限。
证据:系统硬白名单与企业白名单取交集;资金、制度和高风险动作永远人控,金额、置信度、证据、历史精度、样本和抽检任一不足即降级。
- [x] [CONCEPT: 风险与预审] 统一风险输出为事实、规则、证据、判断、建议动作和降级原因。
证据:结构化 pre-review findings、平台风险投影、整改动作、historical evidence 和 fail-closed 原因已用于申请/报销提交与审批风险卡。
- [x] [CONCEPT: 记忆激活] 为首个个人出行方式切片实现 candidate/active/suppressed/expired/revoked 记忆状态机。
证据Candidate/Active 默认有效期分别为 90/180 天;反向或非白名单纠正抑制已激活记忆,过期后新证据创建新 generation忘记后清值并阻止旧请求复活。
- [x] [CONCEPT: 记忆激活] 实现用户、部门、企业记忆优先级、冲突解释、时间衰减和最小样本要求。
证据:首个切片限定 `travel_application.transport_mode`,按当前输入/规则 > 企业 > 部门 > 个人解析;组织记忆由管理员显式激活并受 30-365 天有效期约束,个人记忆继续执行 3 Case、2 次审批通过、跨 7 天门槛和时间衰减。同优先级不同值 fail-closed低优先级覆盖关系返回脱敏解释过期、撤销和并发换代均不会继续应用。
- [x] [CONCEPT: 记忆与学习] 新增租户化个人记忆与证据链接迁移,唯一键保证同一 Expense Case 对同一候选最多计一票。
证据记忆、证据、Decision、Feedback、Outcome、Expense Case 和 Claim 采用租户复合外键;证据同时校验 actor、员工和 Case/单据 owner跨租户或跨主体关系不能计票。
- [x] [CONCEPT: 记忆激活] 为常用出行方式实现隐式激活门槛3 个不同 Case 的一致服务端纠正、至少 2 次审批通过、证据跨 7 天草稿、client_observed、accepted 和重试不激活。
证据:只有当前有效的 `server_verified` 编辑、`application_submitted` 结果和最新审批状态参与计算;提交事件还必须绑定对应 `expense_claim` 与 Claim IDinvalidated/reversed 事实、同 Case 重放和旧请求均被排除。
- [x] [CONCEPT: 记忆与学习] 在服务端申请预览签发前只为空白出行方式应用 active 个人记忆,并返回 memory id、证据数与来源当前输入、主数据和企业规则优先。
证据:`expense_application_preview_workflow.py` 应用记忆后重新执行规则测算和签名;仅缺出行方式时前端仍请求服务端,命中记忆后可直接生成完整核对表。
- [x] [CONCEPT: AI 记忆与自动化设置] 提供当前用户记忆查询与“忘记此偏好”接口;撤销后清除可恢复值并在后续预览中不再应用。
证据:`GET /api/v1/expense-application-memories/me``DELETE /api/v1/expense-application-memories/{id}` 均绑定当前租户和主体,越权以 404 隐藏资源存在性。
- [ ] [CONCEPT: 记忆与学习] 当规则中心新增按职级、路线或金额禁用交通方式的制度维度时,在个人记忆应用前接入显式允许性判定、冲突抑制原因和回归测试。
- [x] [CONCEPT: 记忆与学习] 为 AI 申请预填记录用户原样采纳、显式字段修改和草稿/提交结果证据。
证据:`expenseApplicationDecisionFeedback.js``useApplicationPreviewEditor.js``expense_application_learning.py``expense_application_preview_decisions.py`;改回原建议会清除字段差异,日期联动同步记录天数,最终值由服务端 facts 重建。旧预览保持 `client_observed`;服务端签发预览由版本化 HMAC 快照与最终 facts 逐字段比对,字段新增、删除或改值都标记为 `server_verified` 编辑。两者均保持 `training_eligible=false`,尚不直接训练模型或激活记忆。
- [x] [CONCEPT: 记忆与学习] 将小财管家、Steward 与通用 Orchestrator 的申请预览切换到认证签发与消费链路,并把结构化预览失败策略收口为 fail-closed。
证据:`expense_application_preview_workflow.py``orchestrator_expense_application_workflow.py``steward_action_executor.py``useTravelReimbursementApplicationPreviewActions.js`;签发/动作请求 ID 可稳定重试,草稿续签回写服务端会话,字段接受/纠正按 `server_verified` 落账,未签发结构化预览不能保存或提交。
- [ ] [CONCEPT: 记忆与学习] 从字段接受/修改/拒绝、退回、审批覆盖、付款和审计结果生成记忆证据。
- [x] [CONCEPT: 记忆与学习] 将已确认 few-shot 扩展到报销预审和审批辅助,并按租户、场景、制度版本过滤。
证据:`few_shot_ingestion.py``few_shot_retrieval.py``expense_claim_historical_evidence.py``expense_claim_pre_review.py`;样本关系库和 Qdrant 同时绑定租户、场景、制度与规则版本,检索命中后再由关系库校验,旧版本标记 stale依赖异常返回空证据。公开 `historical_case_evidence` 与 AI 助手只显示“历史已确认/历史误报,仅供复核”的固定脱敏摘要,不暴露 sample ID、单号、人工评论或结论原文也不改变确定性结论、阻断数量、预算复核和审批路由。
- [x] [CONCEPT: 风险与预审] 完成 golden case、Prompt/规则版本、Canary、回归门禁和自动回滚的工程闭环。
证据Golden evaluator、版本化资产、真实 observation/label、盲审负样本、Release Monitor/Guard、Canary 与 stable 回滚组合测试通过;生产效果仍由试点章节单独验收。
- [x] [CONCEPT: 自动化决策] 实现默认 shadow、按动作开放 L3、L4 单独受控的代码策略。
证据Automation Policy 的 `release_stage`、企业上限、硬白名单和 L4 资格检查;资金/制度/高风险动作不进入自动执行。
- [ ] [CONCEPT: 自动化决策] 在真实企业按周运行 shadow 并依据签字阈值逐项开放 L3/L4。
证据要求:生产样本、抽检、回滚演练和企业授权;本地代码测试不得替代。
- [x] [CONCEPT: 降级策略] 对模型、OCR、Qdrant、连接器和记忆服务实现稳定降级和可观测状态。
证据Runtime Chat attempt、OCR worker、Knowledge RAG、连接器 operational events/health、记忆 fail-closed 与发布告警均有明确失败状态和回归。
## 7. P1 前端实现:审批例外与 AI 记忆
- [x] [CONCEPT: 审批例外工作台] 按风险、金额、预算影响、等待时长和证据完整度排序审批事项。
证据:`GET /api/v1/approval-workbench/items``ApprovalWorkbenchService` 统一生成可解释优先级、风险/SLA/预算/金额/证据分项和排序原因;审批中心使用该投影,不再用前端临时状态覆盖服务端乐观并发状态。
- [x] [CONCEPT: 审批例外工作台] 展示预算压力、风险证据、材料完整度、历史标签和简短 AI 复核建议。
证据:工作台返回证据完整度、缺失证据、预算影响、风险摘要、历史确认标签及 `advisory_only` AI 建议;详情页风险卡读取持久化证据、政策依据、贡献度和决策轨迹,并明确禁止从风险卡直接审批。
- [ ] [CONCEPT: 审批例外工作台] 补齐统一必要性摘要、完整政策/相似单证据和可编辑审批意见草稿。
- [x] [CONCEPT: 审批例外工作台] 为批准、退回和付款建立幂等动作协议与乐观前置条件。
证据:动作请求冻结 `request_id``expected_status``expected_approval_stage`;服务端以租户 + 操作人 + 请求 ID 唯一账本、请求指纹、advisory lock、Claim 行锁和同事务事件保证同请求安全重放,不同内容或过期快照返回 409。首次完整 API 响应以不可变快照落账,单据后续推进后重放仍只返回首次结果,不读取或泄露后续状态。
- [x] [CONCEPT: 审批例外工作台] 为风险处置动作建立不可变原响应快照和历史安全重放。
证据:`20260716_0012` 为 append-only 风险事件增加 `response_json`;新事件在 INSERT 前原子写入完整响应重放只返回目标事件版本及以前的事件。0012 前无快照事件从 `after_json``version <= target` 的审计链安全重建,不读取当前处置投影。
- [x] [CONCEPT: 审批例外工作台] 建立高风险门禁与类型化风险处置生命周期。
证据:严重/高危可行动风险在任何审批副作用前阻断;确认风险、误报、补件、整改、豁免申请和完成处置具有乐观版本、请求指纹、租户权限、只追加事件和 Claim 共用锁。未持久化的原始高风险仍保守阻断,重大风险持久化失败 fail-closed。
- [x] [CONCEPT: 审批例外工作台] 补齐批量审批、委托、转交、加签、会签和超时升级交互。
证据:`approval_tasks`/`approval_task_events``/api/v1/approval-tasks``ApprovalTaskWorkspace.vue``20260716_0013`;支持数据库分页筛选、节点进入时间 SLA、委托/撤销、永久转交、顺序加签、并行会签、手动/调度升级和逐项独立事务批量结果。服务端动作集合与版本为唯一权限事实,筛选/页码可从详情返回恢复。
- [x] [CONCEPT: 审批例外工作台] 完成风险豁免申请、批准、拒绝、到期和审计展示闭环。
证据:`20260716_0014``risk_waiver_decision_policy.py`、风险读投影与 `RiskWaiverActionDialog.vue`/`RiskWaiverRecord.vue`;申请人不能决定自己的申请,仅在职 finance/executive 可决定,过期 fail-closed全部事件保留版本、人员、请求 ID、原因和关键前后快照。
- [ ] [CONCEPT: 审批例外工作台] 接入真实企微/钉钉/邮件触达和处理结果回写。
- [ ] [CONCEPT: AI 记忆与自动化设置] 新增“我的 AI 记忆”,支持来源解释、修改、忘记和关闭个性化。
- [x] [CONCEPT: AI 记忆与自动化设置] 在申请核对表展示常用出行方式的记忆来源、证据数量和“忘记此偏好”,并在保存/提交后区分候选记录与已应用回执。
证据:`TravelReimbursementMemoryPanel.vue` 与独立样式分片承载记忆解释、学习回执和可访问的忘记入口;会话快照可跨刷新恢复已应用记忆。
- [x] [CONCEPT: AI 记忆与自动化设置] 新增企业/部门出行方式记忆管理首个低敏切片。
证据:租户管理员可创建、查看、更新和撤销企业/部门记忆,配置 30-365 天有效期;创建、更新、撤销使用请求指纹和稳定幂等键,作用域锁与数据库唯一约束阻止并发静默换代。接口只接受“飞机/火车/轮船”,自由文本、金额、客户、项目、附件和支付信息保持禁用。
- [ ] [CONCEPT: AI 记忆与自动化设置] 扩展可配置保留策略、敏感等级目录和动作级自动化上限管理;在新增字段前先完成制度允许性与数据分级评审。
- [ ] [CONCEPT: 前端] 展示自动化动作、执行依据、撤销入口、抽检状态和版本信息。
## 8. P2 实现:费用经营与价值证明
- [x] [CONCEPT: 节省与价值] 新增 Savings baseline、opportunity、realization、evidence、event 和去重/确认表结构。
证据:`models/savings.py``20260716_0015_savings_value_ledger.py` 与模型/迁移测试。
- [x] [CONCEPT: 节省与价值] 实现预计机会、接受、执行中、实际结果、财务确认、拒绝、过期和冲回的严格状态转换。
证据Savings actions/realization 服务、版本/指纹、canonical benefit 去重与 PostgreSQL 并发测试;风险暴露保持独立护栏,不混入节省金额。
- [x] [CONCEPT: 费用分析与节省] 持久化员工、部门、费用类型、城市、项目和流程基线,记录窗口和样本量。
证据:`savings_baseline_generation.py` 与 baseline/insight 测试;维度、窗口、样本、算法版本、查询指纹和质量等级均冻结。
- [ ] [CONCEPT: 费用分析与节省] 接入租户化供应商、合同价、数量和单位价格事实后生成供应商基线。
证据要求:真实供应商主数据和合同/采购事实;当前明确返回 `supplier_dimension_unavailable`,不从模拟应付数据伪造。
- [x] [CONCEPT: 费用分析与节省] 实现预算预测、异常归因、重复小额浪费和只读政策模拟准备项。
证据Savings insight budget/analysis/attribution缺正式反事实时 `estimated_savings=None` 且不创建机会。
- [ ] [CONCEPT: 费用分析与节省] 接入真实供应商价格漂移分析。
证据要求:租户化合同价、数量、单位价和供应商证据;当前保持 coverage gap。
- [x] [CONCEPT: CFO 价值看板] 展示现金节省、工时价值、直通率、风险护栏、节省来源和责任人。
证据CFO API/analytics/dashboard现金、工时、风险和机会分卡缺数据使用 collecting/unavailable。
- [x] [CONCEPT: CFO 价值看板] 实现部门、项目、费用类型、供应商、城市、时间和单据下钻。
证据CFO filters、`cfoValueSourceLinks.js`、URL 状态恢复与前端回归;供应商无事实时显示缺口而非零。
- [x] [CONCEPT: CFO 价值看板] 每项节省支持查看基线、建议、执行、实际结果、确认人和证据。
证据:`CfoValueOpportunityDrawer.vue` 与 Savings detail projection无证据时不开放实际结果登记。
- [ ] [CONCEPT: 指标与验收] 生成客户月度 ROI 报告,现金节省与工时价值分开披露。
- [ ] [CONCEPT: 风险与开放问题] 建立节省归因复核、重复收益去重和客户财务签字流程。
## 9. P3 实现:商业化与规模复制
- [x] [CONCEPT: 商业计量] 在 P0 最小租户隔离基础上新增套餐、订阅、账期、配额、用量和增值模块授权模型。
证据商业模型、0016/0019/0021/0024 迁移、管理/查询 API 与商业工作台。
- [x] [CONCEPT: 商业计量] 按客户和权威 meter 记录模型、OCR、附件存储、连接器、分析任务与模块用量/成本。
证据Orchestrator、Runtime Chat、OCR、附件、连接器 permit/预占/结算;已识别运行入口资源组合 63 项通过。
- [x] [CONCEPT: 商业计量] 建立客户 ROI、平台贡献毛利、成本明细和私有部署成本输入模型。
证据:`commercial_analytics.py`、商业价值/成本前端;客户价值、平台收入和内部成本分账,多币种不合并。
- [ ] [CONCEPT: 目标与非目标] 定义年度基础订阅、用量超额、智能风控、预算经营、价值洞察、企业集成和私有部署包。
- [x] [CONCEPT: 目标与非目标] 定价引擎仅允许对财务确认的已实现节省计算可选价值分享上限。
证据:`commercial_pricing.py` 使用 confirmed value、成本下限和成功费封顶实际合同仍需客户签署。
- [ ] [CONCEPT: 连接器] 建立 ERP、HR、SSO、支付、电子档案和消息平台标准实施模板。
- [ ] [CONCEPT: 权限与安全] 完成删除传播、数据导出、审计增强和私有部署安全验收,不把基础租户隔离留到本阶段。
## 10. 测试与验证
- [x] [CONCEPT: 测试方案] 完成 Expense Case/Link、业务事件幂等、租户边界、同事务回滚、申请转报销同 Case 和付款归档事件首批测试。
证据:容器内 `pytest -q server/tests/test_expense_case_service.py` 7 项通过;联合差旅主链路定向回归共 24 项通过。
- [x] [CONCEPT: 测试方案] 为 AI 新建申请直接提交补充统一事务回归,覆盖提交成功、事件失败回滚和仅保存草稿三条边界。
证据:容器内直接提交定向测试 3 项、申请提交回归 5 项、预算与 Expense Case 回归 13 项通过;相关 Python 文件 `ruff --select F,I` 通过。
- [x] [CONCEPT: 测试方案] 为 Expense Case 状态机、事件账本、AI 决策、结果、记忆、自动化和节省服务补充单元测试。
证据Case/Event、AI learning/memory、`test_automation_eligibility.py`、Savings model/service/API/E2E 与全量后端分片均通过。
- [x] [CONCEPT: 测试方案] 为服务端会话、管理员保护和 Bootstrap 重配置补充首批安全回归测试。
证据:`test_auth_session_endpoints.py``test_auth_service.py``test_bootstrap_security.py`;容器定向测试覆盖 token 摘要、伪造身份头、过期/撤销、登出原子收尾、业务经理越权和初始化后匿名重配置拒绝。
- [x] [CONCEPT: 测试方案] 为租户隔离、跨租户访问、规则/制度发布和双人复核等敏感动作补充安全回归测试。
证据Tenant Identity、Agent Asset、Knowledge、Ontology/Employee、Hermes/Report、Steward、Finance Dashboard、Approval、Release Review 与 ONLYOFFICE 安全测试通过。
- [x] [CONCEPT: 测试方案] 为 Expense Case GET 接口补充 owner、审批人、财务、管理员、无权限用户和整 Case 关联事件可见范围的 HTTP 权限测试。
证据:`test_expense_case_endpoints.py` 容器内 8 项通过,覆盖跨租户、无 Case、申请与报销关联摘要以及内部字段递归过滤。
- [x] [CONCEPT: 测试方案] 为 Alembic baseline、升级、旧数据迁移和回滚边界补充 PostgreSQL 集成测试。
证据fresh PostgreSQL 最终总探针 `87 passed / 0 skipped / 0 failed`,覆盖 62 项迁移、完整降级/再升级、旧结构迁移、复合租户约束、append-only 和有事实回滚保护head 为 0028。
- [x] [CONCEPT: 测试方案] 为当前 migration-owned schema 切片补充一次性 PostgreSQL 集成测试和危险 URL 防误连门禁。
证据:`test_alembic_migrations.py` 默认无显式 URL 时跳过,主机和库名必须带 disposable 标记;当前 0012 Head 在 tmpfs PostgreSQL 17 中通过完整迁移验证,覆盖空库升级、重复升级、审批动作账本、风险处置复合租户约束、只追加事件触发器、不可变响应快照及有数据降级拒绝、组织 active 脏数据升级前拒绝、版本化 few-shot 无损降级拒绝、外键级联、base 降级、legacy 哨兵保留、漂移拒绝和再次升级;临时容器自动清理,持久化开发库未被修改。完整 legacy baseline 仍保留在上一条未完成项中。
- [x] [CONCEPT: 测试方案] 验证审批动作幂等、任务编排、风险门禁、豁免决定权限和共同锁顺序。
证据:容器内本阶段后端回归 `122 passed, 4 skipped`、前端审批/单据中心/风险专项 `60 passed`、Ruff 与生产构建通过;一次性 PostgreSQL 17 空库迁移循环 `1 passed`、审批任务和风险并发 `3 passed`,证明同 request ID 并发只生成一个事件、陈旧版本只有一个胜者、风险重新打开与审批竞争时按 Claim 公共锁读取最新事实。
- [x] [CONCEPT: 测试方案] 为连接器幂等、重试、回执、失败恢复、重复付款和对账补充测试。
证据:连接器 service/endpoint/config/operational/concurrency 测试覆盖签名、防重放、冲突、错配、失败、ERP、退款和非生产隔离。
- [x] [CONCEPT: 测试方案] 跑通申请 → 票据 → 报销 → 预审 → 审批 → 付款 → 入账 → 归档端到端。
证据:`test_expense_financial_value_chain_e2e.py` 使用测试密钥自签 production-mode 事件,覆盖 HMAC、ERP posted、独立财务确认、Savings、商业价值和退款冲回契约不声明真实 provider 或真实现金。
- [x] [CONCEPT: 测试方案] 跑通首个个人出行方式切片的 AI 建议 → 用户修改 → 工作流结果 → 记忆激活 → 下次建议变化闭环。
证据:容器内记忆、预览决策、迁移与所有权组合回归 56 项通过、1 项条件跳过;一次性 PostgreSQL 迁移循环 1 项通过前端申请快速预览、个人记忆、Steward 与会话恢复组合 83 项通过Vite 生产构建通过Python Ruff F/I 与 `git diff --check` 通过。
- [x] [CONCEPT: 测试方案] 跑通首个行为采集切片AI 申请预填 → 用户接受/显式修改 → 草稿或提交结果同事务落账。
证据:`test_user_agent_application_draft_events.py``test_reimbursement_endpoints.py``expense-application-decision-feedback.test.mjs``expense-application-fast-preview.test.mjs`;覆盖可信入口、模板/详情排除、隐私指纹、同事务事件关联、日期联动及异步乱序响应。容器内学习账本与迁移所有权定向 29 项、一次性 PostgreSQL 迁移 4 项和前端关键场景 5 项通过。
- [x] [CONCEPT: 测试方案] 跑通风险反馈 → few-shot → golden case → Canary → 回滚工程闭环。
证据风险处置学习、Golden evaluator、发布 runtime/telemetry/review/recall/monitor 与 PostgreSQL 并发测试;低 precision 和失败路径恢复 stable。
- [x] [CONCEPT: 测试方案] 跑通节省机会 → 执行 → 实现 → 财务确认 → ROI 看板闭环。
证据:`test_savings_value_e2e.py``test_expense_financial_value_chain_e2e.py` 和 CFO analytics/frontend 测试。
- [x] [CONCEPT: 测试方案] 为已有 Expense Case 事件时间线补充视图模型、404 降级、详情页接入及相关响应式回归,并完成前端生产构建。
证据:容器内 `node --test` 定向执行 99 项通过;`npm --prefix web run build` 通过。一次性克隆迁移库上的隔离后端已完成真实登录、身份读取和旧单时间线 200 联调;持久开发库仍未迁移,因此日常本地页面仍保持兼容提示。
- [x] [CONCEPT: 测试方案] 为 AI 申请草稿事件补充事务失败回滚、同快照幂等、同 run 多版本留痕和 Steward 重放回归。
证据:本轮受影响后端定向回归 36 项、Expense Case 前端兼容测试 9 项和 Python `ruff --select F,I` 在容器内通过。
- [x] [CONCEPT: 测试方案] 在现有开发数据的只读一次性克隆上验证迁移、历史回填和真实认证时间线链路。
证据:源库与克隆初始签名均为 40 张表、4 张费用单、105 名员工、248 条预算、62 个 Agent 资产;克隆升级后 dry-run 为 eligible=4apply created=4重复 apply created=0四条事件均为 system/suppressed登录、`/auth/me`、旧单时间线和登出均返回 200持久库复查不变且仍无 migration-owned 表。
- [ ] [CONCEPT: 测试方案] 补充其余前端组件、键盘操作、移动真实接口和完整浏览器关键流程验证。
- [x] [CONCEPT: 测试方案] 验证服务端预览决策签发、字段新增/改值差异判定、跨会话拒绝、无 ID 降级防绕过、安全重放、独立密钥权限、续签失败容错、旧路径兼容、会话恢复和真实 PostgreSQL 迁移。
证据:容器内新增决策安全用例 9 项、旧快速保存/提交 4 项、申请学习账本 9 项、迁移与所有权 26 项通过且条件型 PostgreSQL 用例 1 项跳过;一次性 PostgreSQL 17 迁移 4 项、前端定向 3 组及 Vite 生产构建已通过。临时 PostgreSQL 已清理,持久开发库 8 张 migration-owned 表数量仍为 0。
- [x] [CONCEPT: 测试方案] 验证小财管家、Steward、通用 Orchestrator 的统一预览闭环、认证绑定、fail-closed、稳定重试和跨刷新恢复。
证据:容器内后端组合回归 50 项通过覆盖服务端预览、Steward 动作/图运行、跨租户 checkpoint、Orchestrator 匿名/普通用户/管理员来源授权及决策消费Python Ruff F/I 通过;前端结构化动作、会话恢复、工作台路由、富确认和 `ai-application-preview-actions` 共 18 项通过Vite 生产构建通过。共享规则工作簿相关套件按容器内串行执行,避免并行读取正在变动的 XLSX 产生非业务性 ZIP 竞争。
- [x] [CONCEPT: 测试方案] 所有后端、集成和迁移测试在当前主应用容器内执行,单条命令最大超时 60s。
证据176 个后端测试文件按有界分片或专项执行PostgreSQL 探针由应用容器运行,未在宿主机安装替代 venv。
- [x] [CONCEPT: 指标与验收] 记录测试、lint、typecheck、构建、端到端和未覆盖风险证据。
证据后端分片与费用主服务通过PostgreSQL `87/0/0`Web `815/0` 与 Vite buildMobile lint/typecheck新增 Python Ruff、compileall、受门禁核心类/组件 800 行检查和 `git diff --check`。未覆盖的生产/试点项保留在第 11-12 节。
## 11. 分阶段试点与价值验证
- [ ] [CONCEPT: 指标与验收] 确认开发前置门禁已完成,试点场景、客户群体、基线和纵向验收未发生未经评审的漂移。
- [ ] [CONCEPT: 自动化决策] 前四周只运行建议和 shadow不开放高风险自动化。
- [ ] [CONCEPT: 指标与验收] 验证报销创建时间、自动填充率、首次提交完整率、人工触点和风险护栏。
- [ ] [CONCEPT: 节省与价值] 由客户财务确认现金节省、工时价值、归因口径和去重结果。
- [ ] [CONCEPT: 商业计量] 计算试点客户的订阅、模型、OCR、实施、支持和集成贡献毛利。
- [ ] [CONCEPT: 指标与验收] 输出 90 天 ROI 报告和年度合同/扩展模块建议。
## 12. 文档收尾
- [ ] [CONCEPT: 指标与验收] 将试点实际基线替换方向性目标,冻结正式验收阈值。
- [ ] [CONCEPT: 风险与开放问题] 更新目标客户、试点场景、支付边界、连接器范围和自动化授权结论。
- [x] [CONCEPT: 本轮实现记录] 每个工程阶段完成后补充实现文件、迁移、接口和容器测试证据。
证据:本 CONCEPT/TODO、2026-07-16/17 分功能文档与 `engineering-closure-and-production-readiness` 总验收文档。
- [x] [CONCEPT: 功能一句话] 确认最终工程实现持续服务于“不用填表、少被退回、真正省钱”的核心结果。
证据零录入票据、服务端预审、审批例外、可信学习、Savings/CFO 与商业计量形成同一费用闭环;真实成效仍由第 11 节企业试点验证。

View File

@@ -0,0 +1,19 @@
# 2026-07-13 综合工作日志
生成时间2026-07-13 17:01:12 CST
来源:`feature/` 功能点文档与 `dev-logs/bugs/` bug 记录
## 今日功能点
- AI 费用闭环与价值证明 概念文档:把申请、消费、票据、报销、审批、付款、入账、分析和持续学习连接成同一个费用事件,让员工少填表、审批人只处理例外、财务能确认真实节省,并让 AI 在可控边界内越用越准确。TODO 完成 15 项,未完成 98 项;目录:`ai-expense-closed-loop-and-value-proof`
## 今日 Bugs
- bugs影响AI 一键新建申请现在与编辑重提共用同一提交语义预算预占、提交校验、申请风险标记、Expense Case 事件和审批状态保持一致;业务事件写入失败时,申请、预算额度、预算流水、预算预占和 Case 关联不会残留部分成功数据。(文件:`ai-application-submit-bypasses-transaction.md`
- bugs影响用户登录后必须使用服务端签发的短期 Bearer 会话,伪造用户名、角色或管理员头不再生效;业务经理不能冒充平台管理员,已初始化系统的基础设施信息与重配置入口不再向匿名请求开放。租户级查询守卫、会话清理/全部退出、登录限流和 SSO 仍按 P0 后续任务推进。(文件:`client-auth-forgery-and-bootstrap-reconfiguration.md`
- bugs影响付款或关联解绑中途失败时不再留下“申请已归档但付款/事件未提交”的半完成状态,为事务 Outbox 提供可靠边界。(文件:`nested-audit-premature-commit.md`
## 综合分析
- 功能侧沉淀了 1 个功能点,问题侧记录了 3 个 bug 修复。
- 后续复盘优先看本文件,再回到对应功能点或 bug 文件追溯证据。

View File

@@ -0,0 +1,12 @@
# 迁移自有表被运行时 bootstrap 越权创建
日期2026-07-14
文档路径document/development/2026-07-14/dev-logs/bugs/migration-owned-table-bootstrap-drift.md
## 修复记录
- 09:21记录 bug 修复:迁移自有表被运行时 bootstrap 越权创建。bug-log:1347366b
- Git 提交检查fetch 成功upstream `origin/main`upstream 新提交:未发现;本地 ahead 提交1347366b (HEAD -> main) feat(expenses): secure timeline and draft events22669a90 feat(expenses): show unified expense event timelinea616b30c fix(expenses): unify AI application submission transaction653eda05 feat(auth): add opaque bearer sessions661990b2 feat(expenses): add transactional expense case events。
- 修改:新增 `schema_ownership.py` 统一四张 migration-owned 表的所有权边界,把 Agent Foundation、员工、预算、设置、系统看板、数字员工看板和演示数据初始化中的全量 `create_all` 改为只创建 legacy 表;新增 `migration_preflight.py`,在启动迁移前只读核对 Alembic revision 与自有表集合,遇到无版本表、缺表、多表、未知版本或多版本时直接拒绝,不自动 stamp 或修复。
- 操作:调整 `server_start.sh` 的启动顺序和 `alembic.ini` 的脚本绝对定位方式在无端口、无持久卷、tmpfs 数据目录的一次性 PostgreSQL 17 容器中执行空库升级、重复升级、降级到 base、漂移拦截和再次升级结束后自动删除临时容器没有执行持久化开发数据库迁移。
- 验证:迁移与预检定向测试 `19 passed, 1 skipped`;一次性 PostgreSQL 真实迁移测试 `4 passed`,最终 revision 为 `20260713_0002`关键唯一约束、复合索引和外键级联均通过legacy 哨兵跨降级/再升级保留;受影响服务回归 `46 passed, 1 failed`,唯一失败为既有员工目录历史部门归一化用例,单独运行同样失败,与本次建表 helper 无关;新增文件 Ruff 全规则、旧服务 F/I/B/UP、Shell 语法、Alembic heads 和 `git diff --check` 均通过。
- 影响:运行时 bootstrap 不再可能越权重建 Alembic 管理的表,启动会在可能破坏数据前暴露版本/表漂移;完整 legacy schema baseline 仍未建立,后续迁移旧表时必须继续小步验证。

View File

@@ -0,0 +1,13 @@
# Orchestrator 未认证与费用申请决策绕过
日期2026-07-14
文档路径document/development/2026-07-14/dev-logs/bugs/orchestrator-auth-preview-decision-bypass.md
## 修复记录
- 15:43记录 bug 修复Orchestrator 未认证与费用申请决策绕过。
- Git 提交检查:`git fetch --all --prune` 成功,`origin/main` 没有新提交;本地 ahead 9 条,依次为 `5b246307` 服务端申请预览决策、`a662cfe6` 申请反馈账本、`5ed34c2b` 历史费用 Case 回填、`11275e4b` 迁移所有权安全、`1347366b` 安全时间线与草稿事件、`22669a90` 统一费用时间线、`a616b30c` AI 申请提交事务、`653eda05` Bearer 会话、`661990b2` 事务型费用事件;工作树为 dirty未执行合并或变基。
- 修改:`orchestrator.py` 的所有外部来源改为强制认证,`schedule` / `system_event` 进一步要求平台管理员;`OrchestratorService` 用登录态覆盖客户端身份与调度操作人别名并丢弃 preview/decision 注入。`agent_conversations.py` 与 Steward 动作运行时使用可信租户状态隔离会话创建、恢复、幂等检查点和删除;统一申请预览工作流要求 Steward、工作台和小财管家先签发再保存/提交,并复用同会话同快照的 active decision。
- 操作:把 Orchestrator 申请 decision 保存到服务端会话,草稿保存后的 next decision 继续回写;工作台 Steward 申请动作改为先打开结构化签发预览。过期/已消费 decision 会清空旧 ID 并进入可重新签发状态;租户 A/B 使用相同用户名、conversation 和 trace 时创建独立会话与 decision。所有验证均在 `x-financial-local-linux` 容器 `/app` 内执行,未修改七个用户规则工作簿。
- 验证:后端组合回归 50 项通过覆盖服务端预览、Steward 动作/图运行、跨租户 checkpoint、Orchestrator 匿名/普通用户/管理员来源授权与 decision 消费Python Ruff F/I 通过;前端结构化动作、会话恢复、工作台动作路由、富确认和动作脚本 18 项通过Vite 生产构建通过。共享工作簿并行读取曾触发 `openpyxl` ZIP 竞争,串行复跑相关套件后全部通过。
- 影响:未登录用户不能再通过伪造 `source` 调用 Orchestrator普通用户也不能伪装调度来源触发 Hermes 管理任务;同名用户不能跨租户恢复、删除或重放 Steward 会话检查点。申请保存/提交只消费当前登录会话签发的 decision重复签发、过期和失败均有确定的安全处理。

View File

@@ -0,0 +1,9 @@
## 修复记录
- 18:14记录 bug 修复Agent Run 详情绕过财务看板权限暴露跨租户快照与工具响应。
- 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`,本次未合并或改写这些提交。
- 修改:新增 `agent_run_access_policy.py`,识别 `finance_dashboard_snapshot` 运行记录并同时核对 route/ontology 中的 `tenant_id``data_scope` 及当前用户财务角色;两份范围标签缺失、不一致、跨租户或与当前数据范围不符时一律 fail-closed。
- 修改:`agent_runs.py` 在列表返回前过滤无权查看的财务快照,在详情返回 `snapshot_payload`、工具请求和工具响应前执行同一访问策略;跨租户(包括其他租户 admin按 404 处理,同租户普通用户按 403 处理,只有同租户 `finance``executive` 或 admin 能读取完整快照。
- 操作:在 `test_finance_dashboard_tenant_security.py` 构造 tenant-a、tenant-b、无租户旧快照和损坏 data scope 快照,覆盖列表、详情、普通用户、财务用户和跨租户管理员路径;所有命令均在 `local-x-financial-linux` 容器执行。
- 验证Ruff 检查与格式检查通过财务看板、Agent Run 服务和 Ontology 端点定向回归共 17 个测试通过,证明合法同租户详情仍可读取,跨租户载荷、无范围旧记录和损坏范围记录均不可见。
- 影响:已登录用户不能再通过猜测或复用 `run_id` 绕过财务看板领域权限读取其他租户的报销金额快照和工具调用明细,列表入口也不会泄露这些快照的摘要记录。

View File

@@ -0,0 +1,8 @@
## 修复记录
- 22:33记录 bug 修复Agent Run 轻量列表遗漏语义解析结果。
- Git 提交检查:已执行 `git fetch --all --prune``HEAD..@{u}` 为空,未发现 upstream 新提交;本地 `main` ahead 17。审批链相关提交为 `242d68c3``28b834ed``4940ebc4`AI 报销学习与申请链相关提交为 `ee88a36b``6bdf65bc``ae3f02c3``54754b55``211f85d9``5b246307``a662cfe6`,费用事件与事务链相关提交为 `5ed34c2b``1347366b``22669a90``a616b30c``661990b2`,另有迁移安全 `11275e4b` 与会话鉴权 `653eda05`;这些均为当前任务开始前已有的本地提交,本次没有改写或合并。
- 修改:在 `agent_run.py` 的轻量仓储查询中按列表已经筛选出的 run_id 批量读取每个 Run 的首条语义解析;在 `agent_runs.py` 中恢复 `AgentRunRead.semantic_parse` 的既有列表契约,同时保留工具调用和大 JSON 字段的轻量预览策略。
- 操作:先在容器复现 seeded trace 用例失败,再核对 foundation seed、详情序列化和前端消费字段没有修改 seed fixture 来掩盖列表序列化断层。
- 验证:容器内 `test_agent_runs_service.py``test_agent_run_tenant_security.py``test_agent_trace_service.py` 与 OnlyOffice 定向测试共 11 项通过;`test_agent_asset_service.py` 全部 28 项通过;相关实现文件 Ruff 与 `git diff --check` 通过。
- 影响Agent Run 列表重新携带真实语义解析摘要seeded trace、运行轨迹界面和依赖 `semantic_parse` 的流程可继续使用;补充查询仅以租户过滤后的 run_id 为输入,不扩大数据作用域。

View File

@@ -0,0 +1,11 @@
## 修复记录
- 22:10记录 bug 修复Agent Run 普通日志跨租户读取与统计泄露。
- Git 提交检查22:10 执行 `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`,本次未合并或改写这些既有提交。
- 修改:`agent_run.py` repository 将 `route_json.tenant_id``ontology_json.tenant_id` 的双重一致性条件下推到 SQL在排序和 `limit` 前完成租户过滤;任一标记缺失、空作用域或两处标记冲突的记录均 fail-closed详情也在数据库查询阶段按租户收窄。
- 修改:`agent_runs.py` service 新增显式的租户级列表、统计和详情入口,保留带注释的受信任内部跨作用域入口;`agent_run_access_policy.py` 增加返回前二次租户校验,并把财务快照的角色与 `data_scope` 门禁同步下推,防止不可见快照挤占列表和统计窗口。
- 修改Agent Run API 的列表、统计和详情统一使用当前认证租户;普通 run 的跨租户详情、无作用域旧记录和冲突标记记录均返回 404跨租户管理员也没有旁路工具 `request_json`/`response_json`、语义 `raw_query` 与错误统计不会跨租户暴露。
- 修改:`create_run` 支持显式 `tenant_id` 并同时写入 route/ontology后续整体更新或 route 合并会保留已验证的双重标记发现调用方已有冲突标记时拒绝写入Orchestrator、Ontology、知识同步和财务快照的认证/租户感知创建路径已传入可信 tenant知识同步的活动任务复用也改为租户内查询。
- 操作:新增 `test_agent_run_tenant_security.py`,构造 tenant-a、tenant-b、无作用域、冲突作用域和同租户财务快照覆盖 limit 前过滤、列表、统计、详情、跨租户 admin 及敏感载荷反向断言;全部命令均在 `local-x-financial-linux` 容器内以 60 秒超时执行。
- 验证Agent Run、财务快照、Ontology 端点、Orchestrator 认证和知识服务定向回归 31 个测试通过;核心变更文件 Ruff、`git diff --check`、PostgreSQL 方言 SQL 编译与代码行数检查通过,最大核心文件 `agent_runs.py` 为 760 行,低于 800 行硬上限。额外 Ontology 全文件回归共 82 个通过、4 个既有业务信号识别用例失败,失败堆栈位于未由本次改动触碰的 `_has_supported_business_signal` 判定。
- 影响Agent Run 日志现在以认证租户为强边界,其他租户、历史无归属记录及损坏作用域记录不再进入列表、统计或详情响应,同时保留内部后台任务按明确可信入口读取的兼容性。

View File

@@ -0,0 +1,8 @@
## 修复记录
- 19:19修复 AI 报销申请预检的阻断提示与既有交互文案契约不一致问题。
- Git 提交检查:已执行 `git fetch --all --prune``HEAD..origin/main` 无新提交;本地相对 upstream ahead 17 个既有提交,依次为 `242d68c3``28b834ed``4940ebc4``ee88a36b``6bdf65bc``ae3f02c3``54754b55``211f85d9``5b246307``a662cfe6``5ed34c2b``11275e4b``1347366b``22669a90``a616b30c``653eda05``661990b2`;本次未合并、覆盖或改写共享工作树中的其他变更。
- 修改:`aiApplicationPrecheckModel.js` 将受限状态和证据快照提示统一为“请先检查”“请先核对”,恢复动作前置关系并与页面测试契约一致。
- 操作:检查在 `local-x-financial-linux` 容器内执行,未修改财务规则 XLSX。
- 验证AI 申请预检模型定向前端回归 `4 passed`
- 影响:用户在提交前能清楚理解必须先完成的检查动作,避免因提示语义弱化而误以为可直接继续。

View File

@@ -0,0 +1,9 @@
## 修复记录
- 19:01修复一个 AI 决策关联多条工作流结果后,原申请动作幂等重放可能命中多行的问题。
- Git 提交检查:已执行 `git fetch --all --prune``HEAD..origin/main` 无新提交;本地相对 upstream ahead 17 个既有提交,从 `661990b2 feat(expenses): add transactional expense case events``242d68c3 feat(approval): add task workflow and waiver decisions`,其中包含认证、费用 Case、AI 反馈/记忆、迁移安全、预审、风险处置和审批任务能力;本次未拉取、合并或改写这些历史。
- 修改:`ExpenseApplicationLearningService._find_existing()` 不再按 `decision_id` 使用可返回多行的无序 scalar 查询,而是根据原始动作的租户、幂等键和稳定 UUID 精确取回首次 Feedback/Outcome并二次校验租户与 Decision 关联。
- 修改:工作流结果桥接只关联事件发生前最新的已提交 AI Decision原动作重放只回填同 correlation 的预审结论,不会把后续重新提交的退回或审批结果污染到旧决策。
- 操作:在 `local-x-financial-linux` 容器中使用 `/tmp/x-financial-server-venv` 运行 Ruff 和 AI 预览/工作流学习回归;未在宿主机执行 Python 或 pytest。
- 验证:`test_expense_application_preview_decisions.py``test_expense_workflow_learning.py` 组合回归 `17 passed`;新用例验证第二次提交后的退回仅关联最新 Decision多 Outcome/Feedback 不影响原请求重放。
- 影响:申请动作重试不再因后续付款、退回或审计结果增多而出现多行异常,也不会将新一轮流程结果错标到历史 AI 建议上。

View File

@@ -0,0 +1,15 @@
## 修复记录
- 2026-07-16 15:10:42 CST修复审批、退回和付款动作缺少稳定请求标识、预期状态与预期审批节点校验的问题。新增租户 + 操作人 + 请求 ID 唯一账本、请求指纹、PostgreSQL advisory lock 与 Claim 行锁;完全相同的重试返回原结果,请求内容变化或状态已过期返回 409不重复扣减预算、写审批记录、生成业务事件或付款结果。
- 2026-07-16 15:10:42 CST修复高风险观察仅展示但不阻止审批的问题。审批流程在任何路由、预算和业务状态变更之前检查同租户高危/严重风险;只有已判定误报或完成处置的观察允许继续,阻断响应返回稳定错误码和观察状态,整笔动作账本随事务回滚。
- 2026-07-16 15:10:42 CST前端确认弹窗冻结请求 ID、预期单据状态和审批节点网络结果不确定时复用同一请求避免用户重试造成重复业务副作用。
- 2026-07-16 15:10:42 CST验证在 `local-x-financial-linux` 容器完成:费用服务 116 项、审批动作/风险门禁/风险处置组合 26 项、报销接口回归 22 项;一次性 tmpfs PostgreSQL 17 迁移循环 13 项通过。变更 Python 文件 Ruff F/I 与格式检查通过。
- 2026-07-16 15:10:42 CSTGit 拉取检查已执行;上游没有本地尚未包含的新提交,当前分支相对上游 ahead 14。已有本地提交包括 `ee88a36b` 分层费用学习、`6bdf65bc` 权威预审、`ae3f02c3` 零录入票据闭环等,本次修复在这些能力之上继续演进。
- 2026-07-16 15:33:03 CST交叉审查后修复三项并发绕过审批动作不再调用会隐式提交的读取修复逻辑列表/分页 GET 也不再修改审批节点;审批中心保留服务端原始状态并单独生成展示标签;门禁将持久化风险观察与未匹配原始高风险合并,重大观察持久化失败时 fail-closed。
- 2026-07-16 15:33:03 CST审批、风险处置和 Hermes 扫描统一以 Claim 行锁为公共协调点,锁顺序固定为 Claim → Observation → DispositionHermes 锁外计算、锁内刷新并核对状态和更新时间,旧快照被丢弃。一次性 PostgreSQL 并发用例证明风险重新打开与审批竞争时审批等待后读取最新风险并阻断Claim 节点和动作账本均保持无副作用。
- 2026-07-16 15:33:03 CST最终容器回归为审批/风险/费用服务 174 项、迁移/所有权 54 项、前端审批/风险 73 项通过Vite 生产构建通过;一次性 tmpfs PostgreSQL 17 上 13 项迁移和 1 项真实并发测试通过,临时数据库自动清理。再次执行 Git 拉取检查,上游仍无新增提交,本地 ahead 14。
- 2026-07-16 15:44:39 CST提交后交叉审查发现“副作用幂等”不等于“响应幂等”动作账本虽然已有 `response_json`,重放却重新读取当前 Claim后续节点变化会返回新状态。现改为首次动作在同一事务保存完整 `ExpenseClaimRead`,首次和重放均从该快照构造响应;快照缺失、单据/状态/节点身份不一致时 fail-closed 返回冲突,不再退回当前投影。
- 2026-07-16 15:44:39 CST新增“首次批准 → 单据继续推进并写入后续内部状态 → 旧 request_id 重放”服务和 HTTP 回归,确认响应逐字段等于首次结果、后续风险字段不泄露、数据库当前状态不被回退;审批动作与报销接口组合 29 项通过。
- 2026-07-16 15:44:39 CST再次完成 Git 拉取检查,上游无新增提交,本地 ahead 15新增 ahead 为 `4940ebc4` 审批风险安全底座;用户维护的财务规则工作簿和历史日志继续保持未暂存。
- 2026-07-16 15:48:38 CST扩大回归范围后发现两个兼容边界首次动作若直接返回 Pydantic 快照会破坏既有服务层 ORM 可变语义,因此首次执行继续返回已提交并刷新的 ORM只有重放从不可变快照构造HTTP 首次与重放仍逐字段一致。另为风险门禁增加申请/报销阶段匹配,显式错阶段风险不阻断当前流程,未知阶段重大风险仍 fail-closed。
- 2026-07-16 15:48:38 CST路由型重大风险统一使用显式 `route_review`,它允许当前审批进入预算/财务复核,但不会被标记为已解决;普通无 actionability 的原始重大风险继续阻断。容器内费用服务、审批路由、租户/Case、动作协议、接口和风险处置组合 182 项通过。

View File

@@ -0,0 +1,8 @@
## 修复记录
- 2026-07-16 16:50:44 CST修复 SQLAlchemy Session 关闭 autoflush 后的两处审批任务状态错误。根任务完成后先显式 flush 再生成下一节点,避免仍被查询为开放任务并误标 supersededSLA 扫描每次升级后 flush避免同一事务重复扫描生成相同版本事件。
- 2026-07-16 16:50:44 CST修复审批队列前后端 SLA 与权限动作契约不一致。前端统一使用 `due_soon`,服务端正式支持 `escalated` 并拒绝未知筛选;批准/退回仍要求 `can_act`,委托撤销、管理员转交和 SLA 升级则严格按服务端 `available_actions` 展示,不再被前端错误隐藏。
- 2026-07-16 16:50:44 CST修复任务筛选总数污染全局待办数、批量审批后摘要不刷新、队列错误与动作错误相互覆盖以及详情返回丢失任务筛选/页码的问题。工作台独立查询未筛选 pending 摘要,批量成功聚合回流单据更新,审核路由保存专属风险/SLA/关键词/页码状态,旧响应由请求代次丢弃。
- 2026-07-16 16:50:44 CST修复审批动作网络失败后允许编辑负载却继续复用旧幂等键的问题。单项、委托/转交、加签/会签和批量审批均对请求负载生成稳定指纹;完全相同的重试复用 request ID理由、候选人、参与人或批量意见变化时自动生成新 ID避免同键不同内容冲突被误解为操作失败。
- 2026-07-16 16:50:44 CST验证全部在 `local-x-financial-linux` 容器完成。审批/风险/迁移相关后端 `122 passed, 4 skipped`,变更 Python 文件 Ruff 通过;审批、单据中心和风险前端专项 `60 passed`Vite 生产构建通过;一次性 PostgreSQL 17 空库迁移循环 `1 passed`,审批任务与风险锁并发 `3 passed`,探针数据库均已销毁。
- 2026-07-16 16:50:44 CST执行 `git fetch --all --prune`、工作区状态和上下游提交差异检查;上游无新增提交,本地相对 `origin/main` ahead 16最近两个检查点为 `28b834ed` 不可变审批响应重放和 `4940ebc4` 安全风险处置。本次继续排除 7 个用户维护的财务规则工作簿及历史未跟踪开发文档。

View File

@@ -0,0 +1,9 @@
## 修复记录
- 19:09修复 CFO 价值看板允许发起无证据手工实际结果、与后端证据门禁冲突的问题。
- Git 提交检查:已执行 `git fetch --all --prune``HEAD..origin/main` 无新提交;本地相对 upstream ahead 17 个既有提交,依次为 `242d68c3``28b834ed``4940ebc4``ee88a36b``6bdf65bc``ae3f02c3``54754b55``211f85d9``5b246307``a662cfe6``5ed34c2b``11275e4b``1347366b``22669a90``a616b30c``653eda05``661990b2`;本次未合并、覆盖或改写共享工作树中的其他变更。
- 修改:`CfoValueOpportunityDrawer.vue` 过滤 `record_realization` 空证据入口并明确提示实际结果必须来自平台付款事件或可追溯的支付、银行、ERP 凭证;`CfoValueActionDialog.vue` 删除不可履行证据要求的手工金额表单;`useCfoValueDashboard.js` 增加防御性拒绝,避免旁路重新提交空证据。
- 修改:`cfo-value-dashboard.test.mjs` 将序列化样例改为带外部凭证的请求,并增加前端不暴露空证据登记入口的回归断言。
- 操作:全部检查均在 `local-x-financial-linux` 容器内执行,未修改财务规则 XLSX 或历史开发文档。
- 验证CFO/应用壳/财务看板组合前端回归 `22 passed`Vite 生产构建成功(`2227 modules transformed``built in 5.08s``git diff --check -- web` 通过。
- 影响:用户不会再遇到“页面允许登记、后端必然拒绝”的假动作;在真实连接器或凭证上传入口接入前,现金节省仍只能依靠可信付款事件落账并由独立财务确认。

View File

@@ -0,0 +1,8 @@
## 修复记录
- 21:40记录 bug 修复:定价毛利率边界舍入越过后端上限。
- Git 提交检查:已执行 `git fetch --all --prune``HEAD..origin/main` 无新增提交;本地 `main` ahead 17 个既有提交,分别为 `242d68c3` 审批任务流、`28b834ed` 不可变动作回放、`4940ebc4` 风险处置、`ee88a36b` 分层费用学习、`6bdf65bc` 权威预审、`ae3f02c3` 零录入票据关联、`54754b55` 个人申请记忆、`211f85d9` 统一申请流、`5b246307` 申请预览决策、`a662cfe6` 申请反馈台账、`5ed34c2b` 历史申请回填、`11275e4b` 迁移归属校验、`1347366b` 费用时间线与草稿事件、`22669a90` 统一费用事件时间线、`a616b30c` AI 申请事务、`653eda05` bearer 会话和 `661990b2` 费用案例事件;均非本次修复产生。
- 修改:`commercialWorkspaceModel.js` 先把百分比量化为后端允许的六位小数,再校验目标贡献毛利率严格小于 `0.95``CommercialPricingScenarioPanel.vue` 同步把可输入上限收紧到 `94.9999%`,并在模型测试中覆盖舍入临界值。
- 操作:在容器 `local-x-financial-linux` 内执行边界探针、商业模型与组件定向测试、全量前端测试、code-size 门禁和 Vite production build。
- 验证:临界探针确认 `94.9999%` 序列化为 `0.949999``94.999999%` 在请求前被拒绝;商业定向测试 35 项通过,修复后的模型与组件回归 28 项通过,全量前端 802 项通过code-size 通过Vite 完成 2246 个模块转换。
- 影响:管理员无法再提交一个表面小于 95%、但六位小数量化后等于后端禁值 `0.95` 的场景,避免无意义的 422 往返,同时不改变合法目标毛利率的精度。

View File

@@ -0,0 +1,8 @@
## 修复记录
- 21:09记录 bug 修复:未配置硬配额被前端误显示为剩余 0。
- Git 提交检查:已执行 `git fetch --all --prune``HEAD..origin/main` 无新增提交;本地 `main` ahead 17 个既有提交,分别为 `242d68c3` 审批任务流、`28b834ed` 不可变动作回放、`4940ebc4` 风险处置、`ee88a36b` 分层费用学习、`6bdf65bc` 权威预审、`ae3f02c3` 零录入票据关联、`54754b55` 个人申请记忆、`211f85d9` 统一申请流、`5b246307` 申请预览决策、`a662cfe6` 申请反馈台账、`5ed34c2b` 历史申请回填、`11275e4b` 迁移归属校验、`1347366b` 费用时间线与草稿事件、`22669a90` 统一费用事件时间线、`a616b30c` AI 申请事务、`653eda05` bearer 会话和 `661990b2` 费用案例事件;均非本次修复产生。
- 修改:`commercialWorkspaceModel.js` 的数值规范化显式把 `null``undefined` 和空字符串保留为“未知”,不再依赖 JavaScript 的 `Number(null) === 0` 隐式转换。
- 操作:在容器 `local-x-financial-linux` 内运行商业服务、模型和 Vue 组件定向 Node 测试。
- 验证:商业服务、模型和组件定向测试共 28 项通过;全量 `web/tests/*.test.mjs` 共 795 项通过;`code-size-limits` 通过Vite production build 完成 2233 个模块转换。权益测试同时覆盖不限量、未配置硬上限和失败关闭状态。
- 影响:真实硬配额为 0 时仍显示 0未配置硬配额时显示“未配置硬上限”避免管理员把未知配置误判为已耗尽配额。

View File

@@ -0,0 +1,11 @@
## 修复记录
- 19:37修复商业分析 ROI 口径、商业合同可见角色和已消费权益历史可变三类问题。
- Git 提交检查:已执行 `git fetch --all --prune``HEAD..origin/main` 无新提交;本地相对 upstream ahead 17 个既有提交,依次为 `242d68c3``28b834ed``4940ebc4``ee88a36b``6bdf65bc``ae3f02c3``54754b55``211f85d9``5b246307``a662cfe6``5ed34c2b``11275e4b``1347366b``22669a90``a616b30c``653eda05``661990b2`;本次未合并、覆盖或改写共享工作树中的其他变更。
- 修改:`commercial_analytics.py` 将客户现金 ROI 统一为“(财务确认现金节省-客户合同收费代理值)/ 客户合同收费代理值”,并把 ratio numerator 改为净收益;禁止截止时间早于窗口开始及无时区分析时间。
- 修改:`commercial_access_policy.py` 移除直属经理对套餐、订阅和配额的默认读取权限仅保留财务、executive 和平台管理员。
- 修改:`commercial_admin.py` 在权益已有用量后禁止回改配额、计价配置和有效期,只允许暂停/恢复;完全相同的 PUT 不再无意义增加版本。
- 修改:`commercial.py` 强制套餐、订阅、权益、用量和成本事实时间显式带时区,避免跨时区账期歧义。
- 操作:全部检查均在 `local-x-financial-linux` 容器内执行,未修改财务规则 XLSX 或历史开发文档。
- 验证:商业模型、服务与 HTTP 定向回归 `10 passed`;相关 Ruff 检查通过。
- 影响:商业 ROI 与既定合同口径一致,普通直属经理不能查看敏感商业合同,历史用量不会因事后回改权益配置而改变含义。

View File

@@ -0,0 +1,23 @@
## 修复记录
- 21:56记录 bug 修复:非成功 Agent 工具调用可能被商业账本误计量。
- Git 提交检查:已执行 `git fetch --all --prune``HEAD..origin/main` 无新增提交;本地 `main` ahead 17 个既有提交,分别为 `242d68c3` 审批任务流、`28b834ed` 不可变动作回放、`4940ebc4` 风险处置、`ee88a36b` 分层费用学习、`6bdf65bc` 权威预审、`ae3f02c3` 零录入票据关联、`54754b55` 个人申请记忆、`211f85d9` 统一申请流、`5b246307` 申请预览决策、`a662cfe6` 申请反馈台账、`5ed34c2b` 历史申请回填、`11275e4b` 迁移归属校验、`1347366b` 费用时间线与草稿事件、`22669a90` 统一费用事件时间线、`a616b30c` AI 申请事务、`653eda05` bearer 会话和 `661990b2` 费用案例事件;均非本次修复产生。
- 修改:`commercial_runtime_policy.py` 把工具调用状态明确分为可计量、采集中和不可计量;`commercial_runtime_metering.py` 只允许 `succeeded/success/ok/completed` 的真实终态调用进入用量与成本账本,`running/pending/queued` 保留待终态补偿语义,`blocked/failed/skipped/cancelled` 不再产生商业事实。同步用 `commercial_runtime_bridge.py` 将未配置租户兼容放行、显式配置后的执行前配额门禁、成功调用后的幂等追加和故障补偿接入真实 `AgentToolCall` 生命周期。
- 操作:在 `AgentRunService` 的创建与终态更新后触发桥接计量;在中央工具执行器调用真实 executor 前执行权益预检;将工具执行职责拆到 `orchestrator_tool_execution.py`,让核心编排文件回落到 762 行;所有命令均在 `local-x-financial-linux` 容器内运行并设置 60 秒超时。
- 验证:商业运行计量 17 项通过,商业模型/服务/接口/运行计量合计 28 项通过AgentRun 与 Orchestrator 鉴权相关回归合计 34 项通过;范围内 Ruff、`compileall`、diff 检查和类级 800 行检查通过。全库 code-size 门禁仍被本次范围外的 `RiskRuleGenerationService` 817 行阻断,未在本修复中改动该类。
- 影响:未成功完成的工具调用不会再消耗客户配额或形成内部成本;未配置商业计量的既有租户继续执行;已配置租户在执行前受配额约束。计量系统故障时真实工具调用记录保持不变,返回 `requires_reconciliation` 并可按同一工具调用 ID 幂等重试,避免把计量失败伪装成已入账。
- 22:31修复执行前只读配额在并发下可超卖、直接调用绕过预占及补偿误判问题。
- Git 提交检查:再次执行 `git fetch --all --prune``git status -sb``git log HEAD..@{u}``git log @{u}..HEAD`;上游无新增提交,本地仍 ahead 17 个既有提交:`242d68c3``28b834ed``4940ebc4``ee88a36b``6bdf65bc``ae3f02c3``54754b55``211f85d9``5b246307``a662cfe6``5ed34c2b``11275e4b``1347366b``22669a90``a616b30c``653eda05``661990b2`,均非本次修复产生。
- 修改:新增 `commercial_runtime_reservations` 运营占位及 `0019` 迁移;在订阅、权益行锁内按“已用量 + 有效预占 + 本次预占”原子校验硬配额。中央 Orchestrator 先生成稳定 tool call ID 并预占,再执行真实工具;成功且真实量不超过预占才追加用量/成本并提交,失败或阻断释放,变量基准没有执行器 hard max 时失败关闭。
- 修改:新增持久 `reconciliation_required` 和过期补偿器。缺少执行前预占的旧直接调用不再静默补写正常用量,而是冻结对应容量;补偿器仅在真实工具成功、失败或运行已终止且无调用时结算/释放,运行中或来源不确定继续保留。修复历史过期订阅/权益错误启用门禁、无租户旧运行错误进入补偿、相同预占重试被自身占位判定为额度耗尽三个边界。
- 操作:拆出运行时成本解析、周期键、标量校验、预占和补偿模块,相关核心类均低于 800 行更新模型注册、迁移所有权、前置检查、商业配额投影、AgentRun/中央工具调用点和迁移/并发/补偿测试。所有 Python、Alembic 和 PostgreSQL 验证均在 `local-x-financial-linux` 容器内以 60 秒超时执行。
- 验证:运行时定向 25 项通过商业、Agent、权限、迁移组合 140 项通过;一次性 PostgreSQL 17 商业并发 4 项通过,两个线程竞争一份硬配额时只有一个预占成功;全新 PostgreSQL 0019→0020 完整 Alembic 升降级循环 1 项通过;范围内 Ruff、`compileall``git diff --check` 和相关类 800 行检查通过。Orchestrator review 套件保持既有 5 项本体/申请流失败、11 项通过;全库 code-size 仍仅被范围外 `RiskRuleGenerationService` 817 行阻断。
- 影响:商业硬配额从“执行前提示”升级为数据库原子 permit真实工具并发不能再穿透上限成功事实可幂等结算计量或成本故障保留可补偿状态且不伪造成功未配置 runtime meter 的路径继续兼容,历史失效配置不会误拦截用户。
- 22:39修复“用量已提交、成本写入失败”缺少持久补偿身份的问题。
- Git 提交检查:再次执行 `git fetch --all --prune``git status -sb``git log HEAD..@{u}``git log @{u}..HEAD`;上游仍无新增提交,本地仍 ahead 17 个既有提交:`242d68c3``28b834ed``4940ebc4``ee88a36b``6bdf65bc``ae3f02c3``54754b55``211f85d9``5b246307``a662cfe6``5ed34c2b``11275e4b``1347366b``22669a90``a616b30c``653eda05``661990b2`,均非本次修复产生。
- 修改:为运行时预占增加 `committed_reconciliation_required` 状态。真实用量已经追加但内部成本失败时,保留真实数量、结算时间和失败原因并进入补偿队列;按同一 tool call 重试只幂等补写成本,成功后恢复 committed。该状态不再计入有效预占避免真实用量和冻结量重复消耗配额。
- 修改:直连调用发生后若当前唯一匹配合同已暂停,允许补偿记录绑定原订阅/权益快照并进入 `reconciliation_required`;正常执行前 reserve 仍严格要求 active/trialing 订阅和 active 权益,不放宽真实执行许可。
- 验证:容器内运行时定向 27 项、商业/Agent/权限/迁移组合 183 项通过,另 1 项因未显式配置外部迁移库跳过;全新 PostgreSQL 完整迁移循环 1 项、商业并发 4 项通过。成本故障用例验证 `used=1``reserved=0`,重试后用量仍为 1 且只新增 1 条成本;范围内 Ruff 通过。
- 影响:成本账本短暂故障不再只依赖日志发现,也不会让客户额度被重复扣减;暂停发生与工具终态竞态时,已发生业务仍有持久、租户隔离的补偿证据。

View File

@@ -0,0 +1,15 @@
## 修复记录
- 2026-07-16 11:03 CST执行 `git fetch --all --prune``git status -sb``git log HEAD..@{u}``git log @{u}..HEAD`。上游没有新增提交;本地相对 `origin/main` ahead 12依次包含 `ae3f02c3` 零录入票据关联、`54754b55` 个人申请记忆、`211f85d9` 认证申请工作流、`5b246307` 预览决策签发、`a662cfe6` 申请反馈账本、`5ed34c2b` 历史费用 Case 回填、`11275e4b` 迁移所有权安全、`1347366b` 安全时间线与草稿事件、`22669a90` 统一费用时间线、`a616b30c` AI 申请提交事务、`653eda05` Bearer 会话和 `661990b2` 事务费用事件。
- 2026-07-16 11:03 CST修复报销提交只检查旧 `ai_pre_review` 标记、前端不执行真实预审、规则或单据变化后仍可复用过期结果的问题。新增稳定 review ID、输入/规则指纹、流水线版本、`ready / needs_fix / ready_with_review` 决策、结构化发现与整改动作;高危且可由提交人修复的风险在预算占用前保持草稿并返回结构化 409预算治理和人工复核风险继续进入审批。
- 2026-07-16 11:03 CST修复预审和提交事件关联断裂、票据归集后预审未刷新、申请批准生成空报销草稿过早把 Expense Case 推入 `claiming` 的问题。预审与提交事件共享 correlation/causation同输入和规则重试只保留一条预审事件Case 在申请批准后保持 `approved_to_spend`,票据关联后再进入 `claiming`
- 2026-07-16 11:03 CST修复 AI 报销草稿、申请预览、Steward 和差旅测算链路未透传非默认租户、事件回落到 `default` 的问题;新增租户隔离与 Case 阶段回归,阻止跨租户 Case/Event 串线。
- 2026-07-16 11:03 CST修复高风险费用申请未进入同部门 P8 预算审批、缺失 P8 未报错、P8 直属领导合并审批缺少审计标记及预算预占提前转移的问题;高风险恢复动态预算路由,中等普通预算预警与低风险申请仍保持快速流转。
- 2026-07-16 11:03 CST修复职责拆分后发票去重助手未接回 `ExpenseClaimService`、英文 reimbursement/travel application 意图被财务门禁拒绝,以及 OCR 成功返回空文档时普通图片被降为“待识别中风险”的问题;复用统一票据键提取器,补齐英文财务信号,并把无有效 OCR 信号附件进入高风险校验。
- 2026-07-16 11:03 CST容器内验证费用服务与审批路由 126 项、费用事件/接口/租户/票据关联 63 项、报销接口全量 20 项和前端预审/时间线 24 项通过Vite 生产构建、Python compileall、关键未定义符号检查及 `git diff --check` 通过。影响范围为报销预审与提交、费用事件、申请预算路由、租户隔离、票据去重、英文财务意图和空 OCR 附件风险分级,不修改用户维护的财务规则工作簿。
- 2026-07-16 11:39 CST再次执行 `git fetch --all --prune``git status -sb``git log HEAD..@{u}``git log @{u}..HEAD`。上游仍无新增提交;本地相对 `origin/main` ahead 12提交摘要与 11:03 检查一致,当前未提交改动继续排除 7 个用户维护的财务规则工作簿和历史未跟踪开发文档。
- 2026-07-16 11:39 CST根据独立审查修复旧 `ready` 预审未覆盖动态风险上下文的问题。提交前申请与报销均重算完整风险review ID 新增动态 findings 指纹;风险在握手期间变化时返回 `PRE_REVIEW_CHANGED`,新增重复发票/历史风险变化和嵌套无序集合回归,实际提交事件始终关联本次有效预审事件。
- 2026-07-16 11:39 CST修复结构化 409 findings 被前端 `ai_pre_review` 汇总过滤规则丢弃、首票归集后 Case 未进入 `claiming`、申请可整改风险可绕过前端直接 API 提交,以及已解决高风险仍被路由至 P8 的问题。findings 现在保留来源、业务阶段、风险域和 actionability申请与报销统一在预算前阻断提交人可整改风险仅人工判断和预算治理风险进入领导/P8。
- 2026-07-16 11:39 CST完成 Claim 核心访问、关联报销后台任务、草稿 ID/单号/最近候选查询和历史风险统计的租户隔离。同用户名、同员工 ID 在不同租户下不能读取、修改、关联或污染预审/P8默认租户仅对无 CaseLink 历史单保留兼容。新增租户范围 helper 和跨租户对抗测试。
- 2026-07-16 11:39 CST修复规则工作簿损坏或保存中间态导致报销主链抛出 `BadZipFile` / `InvalidFileException` 的问题;单个不可读工作簿现在安全跳过,默认规则和其他已加载规则继续可用。修复只增加运行时容错与临时文件单测,未写入、回滚或替换任何用户 XLSX。
- 2026-07-16 11:39 CST按工程硬约束拆出申请关联、预审端点、职级标准调整、规则清单指纹和租户范围职责本轮触及的核心服务、端点和前端提交模块均不超过 800 行。容器最终验证核心费用/审批/预审/规则容错 133 项、接口/Case/租户/任务/票据 74 项、前端 25 项通过Vite 生产构建、全量 Python compileall、变更文件未定义符号检查和 `git diff --check` 通过。

View File

@@ -0,0 +1,17 @@
## 修复记录
- 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 行硬上限。

View File

@@ -0,0 +1,11 @@
## 修复记录
- 22:29记录 bug 修复非生产财务回执会进入核心付款状态机连接器配置缺少版本化生命周期审计normalized payload 冗余保存完整单号。
- Git 提交检查:执行 `git fetch --all --prune` 后未发现 `HEAD..origin/main` 新提交;当前 `main` 相对 `origin/main` ahead 17范围为 `661990b2..242d68c3`包含认证、费用事件、AI 学习、审批任务/风险处置和迁移所有权等既有基础提交,本轮未合并或改写这些提交。
- 修改:`financial_connector_ingestion.py` 与新增 `financial_connector_simulation.py` 将 test/mock/staging 六类事件收口为 `simulation_only` 只读事实,禁止修改 Claim、对账、ERP、Business Event、归档和 Savings生产 origin 查询只接受同来源生产事实,不能引用模拟结算触发冲回。
- 修改:`financial_connector_config_lifecycle.py``financial_connector_config_audit.py`、配置 schema/API 和 `FinancialConnectorConfigEvent` 新增带 expected version、认证 actor、request ID、reason 的 activate/disable/rotate 状态机;新配置只能 disabled 创建,激活/轮换前解析服务端 `secret_ref` 并校验 HMAC 密钥强度,审计前后快照不保存密钥引用或明文。
- 修改:`20260716_0020_financial_connector_config_lifecycle.py` 基于 0019 增加配置 version、复合租户约束和 PostgreSQL append-only 审计 trigger受控移除历史 connector event normalized payload 中的完整 `claim_reference`,保留金额/币种、必要尾号和内容指纹,并把可能已有旧非生产副作用的响应标记为 `legacy_nonproduction_effect_unknown`,不伪装为新策略下的无副作用模拟事实。
- 修改:保留并验证 HMAC v2 对 tenant/provider/key version/timestamp/method/path 的绑定拒绝共享密钥跨来源重放ERP 回执按协议只要求 origin、Claim、金额和币种不再错误强制重复支付参考号空白密钥即使长度足够也按强度不足失败关闭。
- 操作:在独立一次性 PostgreSQL 17 容器完成空库升级到 0020、schema/约束/trigger 探针、并发激活单版本胜者、0019→0020 历史脱敏探针和完整升降级循环验证完成后删除临时容器。同步更新模型注册、迁移所有权、preflight、HEAD revision、迁移断言及连接器 CONCEPT/TODO。
- 验证:容器内连接器/配置/费用价值链/迁移组合回归 `140 passed, 1 skipped`PostgreSQL 连接器并发 `3 passed`;全新 PostgreSQL 完整迁移循环 `1 passed`,商业迁移 agent 在另一空库复跑同样 `1 passed`Ruff、全树 `git diff --check` 均通过,连接器核心文件最大 509 行,低于 800 行硬上限。
- 影响:模拟、测试和预发布环境现在可以安全演练完整事件协议而不会改账;只有 `production_verified` 且精确匹配的回执能够推进付款、ERP 与 Savings。管理员可以安全激活、停用和轮换密钥所有配置动作可追溯且不泄露密钥或完整单号。

View File

@@ -0,0 +1,9 @@
## 修复记录
- 19:01修复风险规则 Golden 评测的 FP/FN 统计颠倒、修订版本未进门禁以及异常/空用例默认放行问题。
- Git 提交检查:已执行 `git fetch --all --prune``HEAD..origin/main` 无新提交;本地相对 upstream ahead 17 个既有提交,依次为 `242d68c3``28b834ed``4940ebc4``ee88a36b``6bdf65bc``ae3f02c3``54754b55``211f85d9``5b246307``a662cfe6``5ed34c2b``11275e4b``1347366b``22669a90``a616b30c``653eda05``661990b2`;本次未合并、覆盖或改写共享工作树中的其他变更。
- 修改:`risk_rule_golden_evaluator.py` 将 false positive 改为“期望不命中但实际命中”false negative 改为“期望命中但实际未命中”Precision/Recall 分母恢复正确。
- 修改:初次发布和 revision 发布均强制执行 Golden 门禁;缺规则文档、缺 rule code、缺 active Golden case 或评测异常均 fail-closed。仅当 `GOLDEN_SET_GATE_ENABLED=false` 被显式配置时允许跳过,且仍写入 status=skipped 的 `AgentAssetTestRun`
- 操作:在 `local-x-financial-linux` 容器中运行 Golden、发布、修订和安全自动化定向回归并执行 Ruff未修改财务规则 XLSX。
- 验证Golden、release guard 和自动化资格组合回归 `29 passed`;并行定向发布/修订回归纳入总计 `62 passed`,评测异常、空用例和缺配置均留下 failed 记录并拦截发布。
- 影响误报不再被错计为漏报Precision/Recall 可用于可信的 Canary 与回滚判断;新规则和修订规则不能因评测器失败或没有黄金用例而静默上线。

View File

@@ -0,0 +1,7 @@
## 修复记录
- 2026-07-16 13:42:57 CST在分层记忆回归测试中发现AI 新建非 `default` 租户费用申请后会立即调用 `submit_claim()`,但此时尚未建立租户化 Expense Case Link提交服务重新查询单据时按 Case Link 执行租户过滤,因此刚创建的申请返回不可见并报“未找到可提交的申请单”。
- 2026-07-16 13:42:57 CST`UserAgentApplicationSlotMixin._create_expense_application_record()` 完成 Claim `flush` 后、进入草稿或提交分支前,使用当前认证租户同步建立 Expense Case 与 Claim Link使新建记录、租户归属和后续提交保持在同一数据库事务失败时仍由既有外层回滚不留下半成品 Case 或 Link。
- 2026-07-16 13:42:57 CST按项目规范执行 `git fetch --all --prune`、上下游状态与提交差异检查;当前分支相对 `origin/main` ahead 13未发现新的上游提交本地 ahead 包含最近的费用闭环、个人记忆、零录入票据和权威预审阶段提交。
- 2026-07-16 13:42:57 CST在 Docker 容器 `local-x-financial-linux` 内运行原失败用例、分层记忆服务与组织记忆接口测试,结果 `9 passed`;验证非默认租户申请可完成预览提交并生成学习回执,组织记忆生命周期和租户隔离未受影响。
- 影响:修复 AI 助手、Steward 和 Orchestrator 共用申请创建链路在非默认租户下无法直接提交的问题;默认租户与既有草稿事件继续复用同一 Case不改变确定性预审、预算和审批规则。

View File

@@ -0,0 +1,8 @@
## 修复记录
- 22:33记录 bug 修复OnlyOffice 回调测试仍 patch 已拆分前的网络符号。
- Git 提交检查:已执行 `git fetch --all --prune``HEAD..@{u}` 为空,未发现 upstream 新提交;本地 `main` ahead 17。审批链相关提交为 `242d68c3``28b834ed``4940ebc4`AI 报销学习与申请链相关提交为 `ee88a36b``6bdf65bc``ae3f02c3``54754b55``211f85d9``5b246307``a662cfe6`,费用事件与事务链相关提交为 `5ed34c2b``1347366b``22669a90``a616b30c``661990b2`,另有迁移安全 `11275e4b` 与会话鉴权 `653eda05`;这些均为当前任务开始前已有的本地提交,本次没有改写或合并。
- 修改:把 `test_onlyoffice_callback_summary.py` 的网络 patch 目标切换到实际查找符号的 `agent_asset_onlyoffice` 模块,并删除对旧版本元数据方法及“回调自行生成 change_note”的过期假设测试现在验证下载内容、回调用户和 `onlyoffice` 来源被完整委托给统一上传流程。
- 操作:没有在 `agent_assets` 中恢复底层 `urlopen` 兼容导出,因为该符号不是公共 API且兼容别名也无法拦截拆分模块中的真实调用差异摘要仍由 `upload_rule_spreadsheet` 统一生成和审计,已有服务测试覆盖其工作表/单元格统计。
- 验证:容器内回调定向测试通过;包含该测试的 Agent Run/租户/轨迹组合共 11 项通过,`test_agent_asset_service.py` 全部 28 项通过;测试文件 Ruff 与 `git diff --check` 通过。
- 影响OnlyOffice 回调测试重新拦截真实网络边界,不会发出外部请求,也不会因内部模块拆分误报;生产回调与摘要生成职责保持不变。

View File

@@ -0,0 +1,8 @@
## 修复记录
- 2026-07-16 14:29:13 CST组织记忆创建在“检查 active → 新建 generation”之间存在竞态后到请求可能静默压制并发产生的新版本创建、更新的幂等键也没有绑定请求内容撤销操作则无法在响应丢失后安全重试。
- 2026-07-16 14:29:13 CST为企业/部门记忆增加租户与作用域级事务锁、幂等请求锁、请求 payload 指纹和数据库唯一约束;创建、更新、撤销现在都满足“同键同内容重放、同键不同内容 409”新 generation 激活前严格核对预期 active 集合。锁协调职责拆到 `organization_memory_locks.py`,主服务从接近 800 行降至 761 行。
- 2026-07-16 14:29:13 CST加固 `20260716_0009` 迁移active 唯一索引只约束企业/部门作用域DDL 前先检测重复 active 并 fail-fast避免个人学习记忆换代受到影响非 PostgreSQL 在任何结构变更前明确拒绝,避免 SQLite 留下半迁移列。
- 2026-07-16 14:29:13 CST执行 `git fetch --all --prune`、工作区状态和上下游提交差异检查;`origin/main` 没有新增提交,当前分支 ahead 13最近本地检查点为权威提交前预审、持久化零录入票据和个人申请记忆本次未改写这些提交。
- 2026-07-16 14:29:13 CST全部验证在 `local-x-financial-linux` 内执行并设置 60 秒超时。分层与组织记忆回归 44 项、迁移/模型/租户组合 70 项通过;前端记忆与申请链路 18 项通过Vite 生产构建通过,变更 Python 文件 Ruff F/I 与 compileall 通过。一次性 tmpfs PostgreSQL 17 完整迁移循环 9 项通过,临时容器自动清理,开发数据库未修改。
- 影响:管理员并发维护组织记忆时不再发生静默覆盖,网络重试不会产生重复版本或把不同请求伪装成成功;个人记忆状态机保持原有最小样本与换代语义,迁移遇到脏数据时在写 DDL 前停止并给出明确错误。

View File

@@ -0,0 +1,12 @@
# 报销审批非参与者错误映射与陈旧测试契约
日期2026-07-16
文档路径document/development/2026-07-16/dev-logs/bugs/reimbursement-approval-task-access-error-mapping.md
## 修复记录
- 21:38记录 bug 修复报销审批非参与者错误映射与陈旧测试契约。bug-log:242d68c3
- Git 提交检查:已手工执行 `git fetch --all --prune`upstream `origin/main` 无本地尚未包含的新提交;当前分支 ahead 17 且工作区已有其他智能体和用户的未提交改动因此未自动合并或变基。ahead 包括审批任务与风险链 `242d68c3``28b834ed``4940ebc4`AI/费用学习与预审链 `ee88a36b``6bdf65bc``ae3f02c3``54754b55``211f85d9``5b246307``a662cfe6`Expense Case/时间线与事务链 `5ed34c2b``1347366b``22669a90``a616b30c``661990b2`,以及迁移安全 `11275e4b`、认证会话 `653eda05`
- 修改:`reimbursement_approval_actions.py` 将审批任务访问策略抛出的 `LookupError` 显式映射为 404避免非任务参与者通过报销审批/退回接口触发 500`test_expense_claim_service.py``test_reimbursement_endpoints.py` 同步到正式审批任务的资源隐藏与任务冲突契约,并增加申请人审批、退回均无状态和动作账本副作用的 HTTP 回归。
- 操作:完整读取 `agent-change-log` Skill沿 `ExpenseClaimActionProtocolMixin → ApprovalTaskLifecycleService → ApprovalTaskAccessPolicy` 诊断调用链;保留“非参与者不可读取任务”的权限语义,没有放宽审批人、管理员、申请人或租户边界;随后运行日志 helper 创建本记录并补齐实际证据。
- 验证:在 `local-x-financial-linux` 容器中,原失败用例与新增 HTTP 用例 2 项通过;审批任务、审批路由和报销接口组合 65 项通过;费用服务审批、退回和付款相关筛选回归 30 项通过;相关 Python 文件 Ruff F/I 检查通过。
- 影响:申请人或其他非任务参与者调用审批动作时稳定返回 404不再出现服务端 500也不会创建动作账本、审批事件或修改 Claim可见但不可操作的任务仍按既有策略返回 403状态/版本冲突继续返回 409。

View File

@@ -0,0 +1,27 @@
# AI 发布门禁全局资产越权与快照损坏静默放行
## 修复记录
- 19:48记录 AI 分阶段发布的租户越权与运行时完整性失效修复。
- Git 提交检查:执行 `git fetch --all --prune` 后未发现 `HEAD..origin/main` 上游新提交;本地 `main` 比上游 ahead 17最新为 `242d68c3 feat(approval): add task workflow and waiver decisions`其余为审批安全、AI 费用学习、报销预审、迁移安全与会话认证等既有检查点,本次未合并或改写这些提交。
- 修改:`agent_asset_release_guard.py` 为所有发布查询、启动、评测、晋级和回滚入口增加平台全局资产管理边界;未绑定租户的共享资产只允许平台管理员管理,租户级 manager 统一返回不可见。`agent_asset_releases.py` 与既有风险规则发布入口只从认证上下文传递平台管理员权限,不能由请求体或自报 actor 绕过。
- 修改:`expense_claim_risk_rule_loader.py` 在候选快照与稳定快照均无法通过 SHA-256 完整性校验时生成强制阻断信号,`expense_claim_platform_risk.py` 将该信号转换为 critical/block 风险,而不是把损坏规则当成“未命中”静默跳过。
- 操作:仅通过 `apply_patch` 修改源码和测试;保护现有财务规则 XLSX、历史未跟踪开发目录及其他智能体改动未执行提交、推送或数据库破坏操作。
- 验证:在 `local-x-financial-linux` 容器内运行 Ruff 定向检查通过;`test_agent_asset_release_guard.py``test_agent_asset_release_runtime.py` 共 12 项测试全部通过,新增覆盖租户 manager 无权管理全局发布资产、平台 admin 可管理,以及双快照损坏后报销自动流转被阻断。
- 影响:单租户管理员不再能影响所有企业共享的风险规则;受控发布元数据损坏时系统优先停流并提示平台恢复稳定版本,避免风险规则失效后继续自动审批。
- 19:53继续修复发布质量指标可由管理人员手工伪造的问题。
- Git 提交检查:再次执行 `git fetch --all --prune``HEAD..origin/main` 仍无上游新提交;本地仍 ahead 17提交范围与 19:48 检查一致,未自动合并、变基或覆盖共享工作区改动。
- 修改:新增 `agent_asset_release_monitor_auth.py`,评测请求必须使用至少 32 字节独立密钥对时间戳、租户、资产、release ID、当前阶段和规范化请求体摘要做 HMAC-SHA256 签名;仅允许 5 分钟时钟窗口并使用常量时间比较。`agent_asset_releases.py` 在写入评测记录前验证签名,密钥未配置返回 503缺失、过期或错误签名返回 401。
- 操作:保留服务层直接写入能力供同进程可信监控使用,但关闭普通 HTTP manager 仅凭自报数字写入“通过”证据的路径;签名绑定 release ID 和阶段,旧阶段请求不能在晋级后重复使用。
- 验证:容器内 Ruff 定向检查通过;发布 guard/runtime 共 12 项测试通过HTTP 用例新增未签名评测返回 401同时签名监控数据仍可触发 shadow→Canary→active 和指标越界自动回滚。
- 影响:人工管理权限与机器监控证据分离,发布门禁不再把未经认证的手工数字当成可信质量结果。
- 21:51补齐“签名合法但指标仍可伪造”的第二层真实性修复并接通真实证据自动回滚。
- Git 提交检查:执行 `git fetch --all --prune``git status -sb``git log HEAD..@{u}``git log @{u}..HEAD``origin/main` 无新提交,本地仍 ahead 17最新提交仍为 `242d68c3 feat(approval): add task workflow and waiver decisions`,其余 ahead 提交为既有审批、AI 费用学习、报销预审、迁移安全和会话认证检查点。本次未合并、变基、提交或推送,也未触碰受保护财务规则 XLSX。
- 修改Release Monitor HTTP 契约改为禁止额外字段的空触发请求HMAC 继续绑定 tenant/asset/release/stage`total``precision` 等质量数字只能由服务端查询 append-only observation/label 聚合;即使签名正确,携带伪造汇总字段也返回 422。
- 修改:真实 shadow/Canary/active manifest 执行追加脱敏 observation类型化 `RiskDispositionEvent` 追加可信标签并即时触发 Guard相同 release 聚合快照复用同一测试运行,低 precision 或结构化运行失败自动回滚 stable。即时监控失败由租户周期调度补偿`collecting` 不写虚假 passed。
- 修改:新增 `0018` 两张发布遥测表、复合租户/release 外键、幂等唯一约束和 PostgreSQL append-only 触发器,并登记主 metadata、迁移所有权与启动前置检查将风险处置发布同步拆到独立服务使核心 `risk_dispositions.py` 保持 800 行以内。
- 操作:全部源码与文档通过 `apply_patch` 修改;用独立 `pgvector/pgvector:pg17` 一次性数据库验证完整迁移链及数据库约束,未连接或变更生产数据库。
- 验证:容器内发布 Guard/Runtime/Telemetry/Monitor/风险处置组合 44 项通过调度器、Monitor 与风险处置定向 24 项通过;迁移/schema owner 静态回归 112 项、启动前置检查 77 项、一次性 PostgreSQL 完整迁移循环 1 项通过PostgreSQL savings/commercial/financial connector/approval 并发探针共 14 项通过。Ruff 定向检查与 `git diff --check` 通过(文档回填后仍需最终全量复验)。
- 影响:发布质量门禁不再信任“会签名的调用方”提交的汇总数字,而是由数据库真实执行与可信人工结论生成;遥测暂时失败只会阻止晋级,不会撤销已成功人工处置或关闭现有 stable 保护。

View File

@@ -0,0 +1,13 @@
## 修复记录
- 2026-07-16 15:10:42 CST修复风险观察列表和完整证据链读取范围过宽的问题。风险池仅允许管理员、财务、管理层和预算监控角色访问单据完整风险证据只允许管理员或当前审批人查看申请人和无权主体统一以 404 隐藏资源存在性,所有查询继续绑定可信租户。
- 2026-07-16 15:10:42 CST修复风险反馈只有自由文本状态、无法可靠表达处置过程的问题。新增类型化判定与生命周期投影区分确认风险、误报、补件、整改、豁免申请和完成处置使用乐观版本、请求指纹、行锁和仅追加事件表保证并发与审计一致性已完成状态不可重新判定。
- 2026-07-16 15:10:42 CST修复前端读取错误 JSON 字段名导致证据、政策依据、贡献度和决策轨迹可能丢失的问题;风险证据卡改用服务端持久字段并展示处置状态、版本和明确动作,高风险卡不提供直接审批入口。
- 2026-07-16 15:10:42 CST验证在 `local-x-financial-linux` 容器完成:风险观察、风险处置、审批工作台与风险门禁组合 26 项,前端审批/风险/动作协议定向 72 项Vite 生产构建通过;一次性 PostgreSQL 验证了复合租户外键、唯一约束、仅追加触发器和非空降级保护。
- 2026-07-16 15:10:42 CSTGit 拉取检查已执行;上游没有本地尚未包含的新提交,当前分支相对上游 ahead 14。已有 ahead 提交均保留在修复上下文中,未修改用户正在维护的财务规则工作簿和历史未跟踪日志。
- 2026-07-16 15:33:03 CST交叉审查后收紧处置动作语义误报、补件和豁免申请必须提供服务端校验的说明完成处置必须提供处理结果版本冲突返回明确消息并触发前端刷新已完成生命周期不再展示重复动作。
- 2026-07-16 15:33:03 CST处置服务在 Claim 公共锁内重新确认当前审批人Observation 和 Disposition 使用租户范围行锁及固定顺序;风险观察写入同样获取 Claim 锁。完整证据、风险池和处置权限分别执行最小授权,跨租户和无权资源继续以 404 隐藏。
- 2026-07-16 15:33:03 CST最终验证全部在 `local-x-financial-linux` 容器完成并设置 60 秒超时;相关后端 174 项、迁移/所有权 54 项、前端 73 项与生产构建通过,一次性 PostgreSQL 17 的结构和并发验证 14 项通过。规则工作簿和历史未跟踪日志未被改动或纳入提交。
- 2026-07-16 15:44:39 CST交叉审查发现风险处置重放在权限复核前读取当前 Disposition 及全部事件,旧 request_id 可能看到首次动作之后的处置状态。新增 `20260716_0012`:完整响应在 append-only Event 的 INSERT 前原子写入 `response_json`,重放只返回该不可变快照并将 `replayed=true`;不得通过 UPDATE 补写审计事件。
- 2026-07-16 15:44:39 CST兼容 0012 前历史事件时,不回读当前 Disposition只使用目标 Event 的 `after_json``version <= target` 的只追加事件重建;快照身份、租户、观察、处置、请求和版本任一不一致均 fail-closed。新增后续补件/整改后重放首个裁决的服务与 HTTP 回归,确认只返回版本 1 和当时事件。
- 2026-07-16 15:44:39 CST容器内风险处置、迁移和前置检查 69 项通过、1 项条件跳过,审批快照与报销接口 29 项通过;一次性 tmpfs PostgreSQL 17 的 0012 Head 迁移与审批风险真实并发共 17 项通过,临时数据库自动清理且持久开发库未修改。

View File

@@ -0,0 +1,8 @@
## 修复记录
- 2026-07-16 16:50:44 CST修复首次风险尚无 disposition 时前端丢弃 Observation 顶层 `available_actions/read_only_reason`、导致“确认风险”等首次处置入口永久隐藏的问题。前端只消费服务端权威投影,缺少 disposition 时构造 version 0 展示模型,不进行角色猜测或本地越权推导。
- 2026-07-16 16:50:44 CST修复页面持续打开跨过豁免到期点后仍显示“豁免有效”的问题。证据卡按 `waiver_expires_at` 注册到期刷新,切换单据和卸载时清理定时器;服务端过期门禁继续 fail-closed前端不再长期展示过期授权。
- 2026-07-16 16:50:44 CST修复风险豁免界面只展示当前快照、拒绝后重新申请会让上一轮审计信息消失的问题。服务保留 actor、request ID、before/after JSON记录组件展示申请、批准、拒绝的追加事件、版本、人员、时间、原因、申请关键字段和请求 ID。
- 2026-07-16 16:50:44 CST风险豁免申请、批准、拒绝和到期后的任务投影刷新与风险处置同事务执行保持 Claim → Observation → Disposition → Task 固定锁顺序;刷新风险、证据、批量资格和优先级时保留 SLA 时间窗与显式升级原因。
- 2026-07-16 16:50:44 CST容器内风险处置、门禁与审批投影定向 `30 passed`,扩展回归 `39 passed, 3 skipped`,前端风险专项 `12 passed`,生产构建通过;一次性 PostgreSQL 并发验证包含风险重新打开与审批竞争,未发现绕过或脏写。
- 2026-07-16 16:50:44 CSTGit 拉取检查确认上游无新增提交、本地 ahead 16本记录基于当前未提交修复未纳入用户维护的财务规则工作簿和历史开发日志。

View File

@@ -0,0 +1,9 @@
## 修复记录
- 19:19修复商业计量迁移上线后标准调整 Savings 回填脚本因精确锁定旧版本而错误拒绝执行的问题。
- Git 提交检查:已执行 `git fetch --all --prune``HEAD..origin/main` 无新提交;本地相对 upstream ahead 17 个既有提交,依次为 `242d68c3``28b834ed``4940ebc4``ee88a36b``6bdf65bc``ae3f02c3``54754b55``211f85d9``5b246307``a662cfe6``5ed34c2b``11275e4b``1347366b``22669a90``a616b30c``653eda05``661990b2`;本次未合并、覆盖或改写共享工作树中的其他变更。
- 修改:`backfill_standard_adjustment_savings.py` 不再要求当前 revision 必须精确等于 `20260716_0015`,改为遍历 Alembic `down_revision` 祖先链,只有当前迁移确实包含 Savings 数据契约时才允许回填。
- 修改:`test_standard_adjustment_savings_backfill.py` 覆盖所需版本本身、后继商业迁移 `0016`、更旧版本和未迁移数据库四类边界。
- 操作:全部检查均在 `local-x-financial-linux` 容器内执行,未修改财务规则 XLSX 或历史开发文档。
- 验证Ruff 通过;标准调整 Savings 回填定向回归 `12 passed`
- 影响:数据库升级到 `0016` 及未来合法后继版本后仍可安全执行历史节省回填;更旧、未迁移或不包含目标契约的版本仍会 fail-closed。

View File

@@ -0,0 +1,17 @@
## 修复记录
- 19:24修复历史费用偏离分析可能读取分析截止时间之后才冻结的基线、形成时间穿越的问题。
- Git 提交检查:已执行 `git fetch --all --prune``HEAD..origin/main` 无新提交;本地相对 upstream ahead 17 个既有提交,依次为 `242d68c3``28b834ed``4940ebc4``ee88a36b``6bdf65bc``ae3f02c3``54754b55``211f85d9``5b246307``a662cfe6``5ed34c2b``11275e4b``1347366b``22669a90``a616b30c``653eda05``661990b2`;本次未合并、覆盖或改写共享工作树中的其他变更。
- 修改:`savings_insight_analysis.py` 在历史基线查询中增加 `frozen_at <= as_of`,确保候选分析只能使用截止时间当时已经存在的冻结快照。
- 修改:`test_savings_baseline_insights.py` 新增未来冻结基线回归;确认这类快照不会产生历史价格偏离候选,并返回基线不可用的数据质量提示。
- 操作:全部检查均在 `local-x-financial-linux` 容器内执行,未修改财务规则 XLSX 或历史开发文档。
- 验证Savings 基线/洞察、端点与 CFO 组合回归 `10 passed`;相关 Ruff 检查通过。
- 影响:`as_of` 历史回放不再引用未来才生成的知识CFO 候选洞察和审计结果保持时间一致性。
- 22:50继续修复预算预测读取分析窗口或 `as_of` 之后预算配置与核销流水的问题。
- Git 提交检查:已执行 `git fetch --all --prune``HEAD..origin/main` 无新提交;本地仍相对 upstream ahead 17 个既有提交(`242d68c3``661990b2`,内容为审批任务/风险处置、AI 学习与预审、Expense Case、迁移安全和认证等共享能力未发现新的上游提交也未合并或改写共享工作树。
- 修改:`savings_insight_budget.py``min(as_of, window_end)` 作为预算分析截止点,只读取当时已经创建且最后更新的预算配置,以及预算期间开始至截止点的核销/回滚事实;部门范围优先使用稳定部门 ID缺 ID 才使用部门名称或成本中心。
- 修改:`test_savings_baseline_insights.py` 增加窗口后核销、截止时间后配置、稳定重放与零机会副作用回归。
- 操作:所有 pytest 和 Ruff 均在 `local-x-financial-linux` 容器内以 60 秒超时执行;未接触财务规则 XLSX、商业/连接器/发布迁移或迁移 HEAD。
- 验证Savings/CFO 定向组合 `34 passed, 6 skipped`6 项为未配置 PostgreSQL 专用测试 URL 的预期跳过),相关 Ruff 检查通过。
- 影响:历史预算预测不再使用报告窗口之后才出现的配置或交易,异常归因、政策模拟候选与 CFO 审计回放保持同一时间边界。

View File

@@ -0,0 +1,9 @@
## 修复记录
- 18:11记录 bug 修复:接受住宿职级标准调整时不再信任客户端金额、天数或金额快照。
- Git 提交检查:已执行 `git fetch --all --prune``HEAD..origin/main` 未发现 upstream 新提交;本地相对 upstream ahead 17 个既有提交,依次为 `242d68c3 feat(approval): add task workflow and waiver decisions``28b834ed fix(approval): replay immutable action responses``4940ebc4 feat(approval): add safe risk disposition workflow``ee88a36b feat(ai): add tenant-safe hierarchical expense learning``6bdf65bc feat(expenses): add authoritative pre-review workflow``ae3f02c3 feat(expense): add persistent zero-entry receipt association``54754b55 feat(ai): add personal expense application memory``211f85d9 feat(ai): unify verified expense application workflow``5b246307 feat(ai): issue verified application preview decisions``a662cfe6 feat(ai): add expense application feedback ledger``5ed34c2b feat(expenses): backfill historical claims into expense cases``11275e4b fix(migrations): enforce schema ownership safety``1347366b feat(expenses): secure timeline and draft events``22669a90 feat(expenses): show unified expense event timeline``a616b30c fix(expenses): unify AI application submission transaction``653eda05 feat(auth): add opaque bearer sessions``661990b2 feat(expenses): add transactional expense case events`;这些提交均早于本次未提交修复,本次没有拉取、合并或覆盖共享工作树。
- 修改:`expense_claim_standard_adjustment.py` 只从已锁定的 `ExpenseClaimItem.item_amount` 读取原始金额,只接受服务端差旅规则计算出的最终可报金额;客户端携带的 `application_days``original_amount``reimbursable_amount` 仅作为旧界面兼容展示字段,不参与计算。服务端快照新增规则名、规则版本(无发布版本时使用内容指纹)、地点、匹配城市、职级、职级档、天数、每日住宿标准、住宿标准总额及计算指纹。
- 修改:为标准调整增加 PostgreSQL advisory lock + Claim/Item 行锁和非 PostgreSQL 进程内串行锁;请求支持 `request_id` 幂等键与 `expected_updated_at` 乐观前置条件。相同请求直接重放且不改 `created_at`/计算快照,同请求号改选其他明细会被拒绝;单次调整只替换被选明细的快照,不再误删其他明细已接受的标准调整。
- 操作:在 `local-x-financial-linux` 容器及 `/tmp/x-financial-server-venv` 中运行定向、接口全量和服务全量测试;运行 scoped Ruff 与 `git diff --check`,未在宿主机运行 Python/pytest也未修改规则表或其他用户文件。
- 验证:标准调整定向回归(含非 PostgreSQL 同租户同单据锁竞争)`9 passed``test_reimbursement_endpoints.py` 全量 `22 passed`Scoped Ruff 与 `git diff --check` 通过。`test_expense_claim_service.py` 全量为 `112 passed, 8 failed`8 个失败均位于既有审批任务配置/旧错误文案断言(直属领导任务、费用申请提交、本人审批、重复退回),不经过标准调整实现;本次新增及关联标准调整用例全部通过。
- 影响:伪造低原金额、任意可报金额或超长住宿天数不能降低或抬高实际报销额;规则缺失时整次操作失败关闭且不改金额。审批人看到的原额、可报额和差额均可追溯到数据库明细与服务端规则证据,重复点击和并发请求不会重写金额证据。

View File

@@ -0,0 +1,9 @@
## 修复记录
- 2026-07-16 14:29:13 CST历史风险观测、few-shot 样本、Qdrant 向量和规则生成链路原先缺少完整租户、场景及制度版本边界Hermes 全局扫描还可能把不同租户的 Claim 放进同一风险图,历史检索结果若直接公开也可能泄露样本 ID、费用单号或人工评语。
- 2026-07-16 14:29:13 CST风险观测与 few-shot 关系数据改为租户复合唯一,风险接口、反馈、查询和 Hermes 图构建按认证租户隔离Qdrant 写入与检索显式携带 tenant、scene、policy_ref、rule_version使用稳定向量 ID并清理旧向量命中后再由关系库校验租户、状态和版本。风险规则生成与再生成从认证用户透传租户缺失租户时禁用历史注入而不回落到 default。
- 2026-07-16 14:29:13 CST报销预审新增只读 `historical_case_evidence`,检索或向量服务异常时降级为空证据。公开协议和 AI 助手只返回固定的“历史已确认/历史误报,仅供复核”标签与脱敏摘要,不返回 sample ID、claim_no、人工评论或历史结论原文历史证据不参与 review ID、确定性 findings、passed、blocking count、预算复核或审批路由计算。
- 2026-07-16 14:29:13 CST加固 `20260716_0008` 迁移:`RiskObservation.claim_id` 明确改为旧 Claim 表的 view-only 软引用,使空库与旧表采用路径的 Head 结构一致;降级若发现非默认租户或非空 policy/rule 版本数据则在 DDL 前 fail-fast禁止静默丢失隔离和版本信息非 PostgreSQL 在变更前明确拒绝。
- 2026-07-16 14:29:13 CST执行 `git fetch --all --prune`、工作区状态和上下游提交差异检查;`origin/main` 没有新增提交,当前分支 ahead 13最近本地检查点为权威提交前预审、持久化零录入票据和个人申请记忆本次未改写这些提交。
- 2026-07-16 14:29:13 CST全部验证在 `local-x-financial-linux` 内执行并设置 60 秒超时。历史案例、风险观测、风险图、预审和规则生成组合 90 项,报销接口与费用服务 137 项,补充 Golden/规则历史注入 28 项通过;迁移/模型/租户组合 70 项通过Ruff F/I、compileall 和 `git diff --check` 通过。一次性 tmpfs PostgreSQL 17 完整迁移循环 9 项通过,临时容器自动清理,开发数据库未修改。
- 影响:同名用户、相同样本键或相似向量不能跨租户读取、覆盖或进入 Prompt历史案例开始帮助预审与复核但只以脱敏、可降级、非决策证据存在不会扩大 AI 自动放行或审批权限。

View File

@@ -0,0 +1,9 @@
## 修复记录
- 19:37修复 Workbench AI 超大运行时导致职责耦合,以及前端回归测试仍绑定旧单体文件和宿主机缺失 Pillow 的问题。
- Git 提交检查:已执行 `git fetch --all --prune``HEAD..origin/main` 无新提交;本地相对 upstream ahead 17 个既有提交,依次为 `242d68c3``28b834ed``4940ebc4``ee88a36b``6bdf65bc``ae3f02c3``54754b55``211f85d9``5b246307``a662cfe6``5ed34c2b``11275e4b``1347366b``22669a90``a616b30c``653eda05``661990b2`;本次未合并、覆盖或改写共享工作树中的其他变更。
- 修改:将会话滚动、流式响应、持久化/重置提取到 `useWorkbenchAiConversationRuntime.js`,把模型意图规划、低置信确认和多任务衔接提取到 `useWorkbenchAiIntentExecution.js``usePersonalWorkbenchAiMode.js` 降至 768 行。
- 修改Workbench、会话删除、报销关联与快速申请预览测试改为联合审计入口与职责模块视觉验证复用容器已有 ImageMagick保留像素和动画断言不再依赖未安装的 Pillow。
- 操作:全部 node 测试和构建均在 `local-x-financial-linux` 容器内执行,未修改后端或财务规则 XLSX。
- 验证Workbench AI/会话组合 `85 passed`,快速申请预览 `67 passed`Vite 生产构建成功2229 modules`git diff --check` 通过。
- 影响:会话清理、详情智能录入、关联门禁和申请预览的回归测试不再因内部职责迁移误报,核心运行时恢复到项目 800 行硬上限内。

View File

@@ -0,0 +1,12 @@
# 零录入票据归集跨租户隔离与失败回滚
日期2026-07-16
文档路径document/development/2026-07-16/dev-logs/bugs/zero-entry-receipt-tenant-and-rollback.md
## 修复记录
- 09:35记录 bug 修复零录入票据归集跨租户隔离与失败回滚。bug-log:54754b55
- Git 提交检查2026-07-16 09:35 CST 执行 `git fetch --all --prune` 成功;`origin/main` 没有远端新提交;本地 ahead 11 条,依次是 `54754b55` 个人费用申请记忆、`211f85d9` 统一核验申请工作流、`5b246307` 申请预览决策、`a662cfe6` 申请反馈账本、`5ed34c2b` 历史费用单回填、`11275e4b` 迁移所有权安全、`1347366b` 费用时间线与草稿事件安全、`22669a90` 统一费用事件时间线、`a616b30c` AI 申请提交事务、`653eda05` Bearer 会话、`661990b2` 事务化费用事件;这些提交均为本轮开始前已有的本地检查点,本次未合并或改写历史。
- 修改:`receipt_folder.py` 把票据命名空间从仅用户名收口为租户与用户联合边界,并用稳定摘要消除有损清洗碰撞;`attachment_association_jobs.py` 同时校验任务租户和 owner平台管理员也不能跨租户读取。`expense_receipt_matcher.py` 改用纯只读本人 Claim 查询并要求每份票据独立满足证据门槛。`expense_receipt_association.py` 将 Claim、Case、Link、业务事件、票据元数据和新旧附件目录纳入统一失败补偿避免事件或文件中途异常留下半关联状态。
- 操作:新增持久化 `attachment_association_jobs` 模型、仓储和 `20260716_0006` 迁移任务按租户、owner、票据集合和 generation 去重,运行态使用租约、`attempt_count + running` 栅栏、进程锁和 PostgreSQL advisory lock。同票据与同 Claim 分别串行化Claim 锁内清理旧事务并重新匹配;待确认或失败任务保留原代审计历史,以新 generation 重新评估,自动关联成功代继续幂等复用。低置信、相近候选、混入无关票据、无草稿和仅有申请统一返回需要确认的零写入结果;同步扩展前端任务协议、会话恢复、候选卡片、幂等重放和成功风险复核提示,并更新功能 CONCEPT/TODO。
- 验证:所有后端验证均在项目主容器内执行并设置 60 秒超时。后端归集专项 20 项、归集与相邻服务及迁移所有权组合回归 71 项通过,覆盖纯只读确认、逐票据证据、事件失败、文件元数据失败、既有申请 Case 保留、同票据并发、不同票据并发同 Claim、租约过期恢复、旧 worker/迟到回调栅栏、待确认重评估、失败代际历史保留、普通用户及管理员跨租户隔离;前端关联链路组合回归 29 项通过;相关 Python 文件 `ruff --select F,I,UP` 通过Vite 生产构建通过。一次性 tmpfs PostgreSQL 17 中完整迁移循环 4 项通过,临时数据库已清理,持久开发库未修改。
- 影响:同用户名在不同租户的票据、任务和候选 Claim 不再互相可见;自动归集中途失败不会留下已改 Claim、孤立 Case/Link/Event、错误票据状态或被覆盖的旧附件目录低证据和混合票据批次只提示确认不扩大自动化权限任务重启或多 worker 后仍可查询和恢复。非默认租户原先落在旧用户名目录中的历史票据改为 fail-closed不做未经确认的跨目录迁移。

View File

@@ -0,0 +1,333 @@
# AI 分阶段发布真实遥测 概念文档
更新时间2026-07-17
## 功能一句话
把真实 shadow/Canary 规则执行、正负样本盲审真值和服务端保守聚合串成可审计证据链,只有精确率、召回率下界与运行质量同时满足门禁时才允许晋级,越界时自动回滚到稳定版本。
## 背景与问题
风险规则已经具备 Golden Case、`shadow → canary → active → rolled_back` 状态机、稳定流量路由和自动回滚能力,但线上评测仍有一个关键证据缺口:`ReleaseEvaluationInput` 由外部调用方直接提交 `total``failure_count``precision``baseline_precision` 汇总数字Release Guard 无法证明这些数字来自哪一次真实规则执行、哪一个租户、哪一个 release也无法证明精度分子和分母来自可信人工结论。
现有运行与学习链路的事实边界如下:
- `ExpenseClaimRiskRuleLoader` 能按租户和稳定路由键选择 stable、shadow 或 Canary 候选版本,并在快照损坏时保守阻断。
- `evaluate_platform_risk_rules()` 会返回 shadow 候选的 `asset_id / rule_code / rule_version / release_stage / hit / severity`Canary 命中会进入带版本和阶段的风险 flag但当前返回值不会持久化成发布样本。
- `RiskDispositionEvent` 是类型化、租户化、只追加的人工处置事实,`confirm``false_positive` 可作为正例命中的可信真值来源。
- `RiskObservationFeedback` 的自由评论、`AIDecisionFeedback``WorkflowOutcome` 可以支持业务学习,但它们没有同时绑定 `asset_id + release_id + stage + version`,不能直接作为某次发布的精度标签。
- Release Monitor 的 HMAC 能认证请求来自持有共享密钥的调用方,并限制传输重放;它不能证明请求体中的汇总数字由真实数据库 observation 和人工 label 计算得出。
因此,线上门禁必须从“相信外部汇总数字”改为“服务端从只追加事实计算指标”。本能力是 `ai-data-flywheel` 在线质量闭环和 `ai-expense-closed-loop-and-value-proof` 分阶段发布目标的证据层,不替代离线 Golden Case。
## 目标与非目标
### 目标
- [G1] 为每次真实候选规则执行记录租户、资产、release、阶段、版本、规则、命中、基线命中和结构化运行状态。
- [G2] 将专用发布复核或数据库中可信 `RiskDispositionEvent` 转换成只追加的正例判断;将独立盲审转换成与模型预测语义分离的 `risk_present / risk_absent` 真值。
- [G3] 使用稳定幂等键处理同一执行或人工动作的安全重放,不同内容复用同一来源时拒绝冲突。
- [G4] 从真实 observation 和最新可信 label 聚合运行总量、运行失败、候选 precision 和基线 precision。
- [G5] 未标注候选命中保持 `collecting`,不得把“没有人工结论”当成成功或正确。
- [G6] 聚合结果可转换成现有 `ReleaseEvaluationInput`,供 shadow/Canary 门禁、自动晋级和自动回滚使用。
- [G7] 全链路租户隔离、数据脱敏、append-only并拒绝跨租户和陈旧 release/stage/version 标签。
- [G8] 对候选未命中人群实施分层盲审:基线命中分歧样本全量复核,其余负样本按不可预测稳定分数随机抽检;证据不足时 recall/FN 仍显式不可用。
- [G9] 使用抽样漏检率估计总体 FN并以 Wilson 上界反推保守召回率下界;发布门禁只使用下界,不把点估计冒充确定事实。
### 非目标
- [NG1] 不用线上遥测替代离线 Golden Case前者验证真实分布后者验证覆盖明确预期的回归集合。
- [NG2] 不把候选未命中直接判为 false negative也不从“后续未退回”反推规则正确。
- [NG3] 不保存报销事由、票据内容、人工评论、单号、操作者账号原值或原始风险 payload。
- [NG4] 不接受客户端、浏览器或普通管理员提交的 precision/recall 作为发布真值。
- [NG5] 不因为 HMAC 校验通过就信任汇总数字HMAC 只解决传输来源和重放,不解决数据生成真实性。
- [NG6] 不让通过质量门禁的规则绕过审批、风险处置或资金动作的人类控制。
- [NG7] 不把遥测或自动回滚做成绕开共享风险循环、迁移所有权、审批控制或稳定版本保护的旁路。
## 用户与场景
### 用户
1. 风险运营/规则管理员:查看某个 release 的真实样本数、待标注数、误报率和基线比较,决定是否继续采集或人工回滚。
2. 财务审计/审批人:在既有风险处置或专用发布复核队列中给出类型化确认/误报结论,不接触发布汇总公式。
3. Release Monitor只从数据库聚合当前 release满足证据条件后把服务端计算结果交给 Release Guard。
4. 平台审计员按租户、release、版本和来源指纹回放 observation、label、评测与状态转换不读取业务正文。
### 核心场景
1. shadow 阶段同时执行 stable 和候选规则;记录候选 hit/miss并记录同一单据上 stable 是否命中。
2. 候选或 stable 命中进入人工复核;`confirm` 表示风险事实成立,`false_positive` 表示该次正向命中是误报。
3. 每条成功 observation 同步决定盲审层:候选命中全量入队、候选未命中但基线命中全量入队、双方均未命中按发布时冻结比例随机入队。
4. 复核队列混排正负样本,只提供业务单据、规则和业务阶段,不返回 candidate/baseline hit复核人只回答“存在真实风险/确认无该风险”。
5. 所有候选未命中样本必须由两个不同复核人给出一致结论;同一人的重复动作不增加法定票数,冲突时继续 collecting。
6. 候选命中仍有任何未标注项时,聚合状态保持 `collecting`Release Monitor 不提交通过评测。
7. 候选正例全部标注后,服务端计算 candidate precision基线正例也全部标注时才计算 baseline precision。
8. 负样本达到最小独立复核量后,服务端分别披露实际观察 FN、总体估计 FN、FN 置信上界、recall 点估计和 recall 置信下界。
9. Canary 路由中的候选 hit、miss 和结构化运行失败继续追加错误率、precision 或 recall 下界越界时 Release Guard 自动回滚稳定快照。
10. release 已晋级、回滚或被新 release 替代后,旧 observation/sample/label 仍保留,但不能再接收新标签或作为当前晋级输入。
## 功能能力
- [C1] 运行样本生产:消费现有 `shadow_evaluations`、Canary/active flag并提供 manifest 执行循环级 hook 记录 Canary 未命中和结构化失败。
- [C2] 可信标签:支持认证发布复核动作和数据库中真实 `RiskDispositionEvent`;不接受评论文本作为标签。
- [C3] 保守聚合:分别计算 observation、completed、runtime failure、candidate hit/labeled/pending、baseline hit/labeled/pending。
- [C4] 门禁转换:仅 `ready` 聚合可生成 `ReleaseEvaluationInput``collecting` 调用转换时明确拒绝。
- [C5] 证据隔离:运行来源、标签来源、操作者均只保存租户内、带密钥版本的
HMAC-SHA-256 指纹;原始 claim、事件和账号值不进入遥测表。
- [C6] 幂等与冲突同一租户、release、阶段、版本、规则和来源形成稳定键相同重放复用首次记录不同载荷冲突。
- [C7] 历史不可变observation 与 label 均只追加;标签纠正追加新 label聚合取最新可信标签不原地覆盖旧事实。
- [C8] 盲审抽样:候选正例和候选/基线分歧样本全量入队,其余负样本按发布策略的 `negative_sample_percent` 与 HMAC 来源伪名生成稳定随机分数。
- [C9] 真值盲化:样本表不保存明文单据 ID业务来源加密保存API 不返回 candidate/baseline hit前端也拒绝接收预测字段。
- [C10] 保守召回:证据未满足时 `false_negative_count / estimated_false_negative_count / recall / recall_lower_bound` 保持 `null`;满足后分别披露,不用点估计替代下界。
## 方案设计
### 证据链与自动判定
```text
[真实 stable / candidate 执行]
[append-only Observation]
tenant + asset + release + stage + version + hit + runtime status
[append-only Audit Sample]
正例全量 + 分歧全量 + 其余负例稳定随机抽样
[盲化可信 Label]
typed disposition / release review / blind release review
[服务端 Aggregate]
runtime + precision + baseline + sampled FN + recall lower bound
┌───────┴────────┐
│ collecting │ ready
▼ ▼
[继续采集/告警] [ReleaseEvaluationInput]
[Release Guard 判定]
shadow → canary → active
或自动 rolled_back
```
该链路中的每一层只消费上一层可验证的结构化事实。离线 Golden Case 仍是进入 shadow 前的回归门禁;线上 telemetry 是进入 Canary、active 及运行中回滚的分布证据,两者不能互相替代。
### 前端
- 发布控制台分别展示运行样本、候选待标注、候选/基线 precision、运行失败、负样本池/抽样/积压、实际 FN、估计 FN、FN 上界、recall 点估计和置信下界。
- 盲审队列只接收服务端安全字段;`candidate_hit / baseline_hit` 即使误入响应也不会进入页面状态。
- 复核按钮使用中性业务语义“存在真实风险 / 确认无该风险”,不使用“模型命中/误报”暗示预测;来源单据在新标签页打开并隔离 opener。
- `null` 指标统一显示“证据不足/暂不可用”,不得渲染成 0`collecting``ready``failed/rolled_back` 使用不同状态。
- `collecting``ready``failed/rolled_back` 使用不同状态,不把空样本或缺标签显示为 100%。
### 后端
- `AgentAssetReleaseTelemetryService.record_expense_risk_result()` 可从当前风险评测返回值生产 shadow 样本和 Canary/active 命中样本。
- `record_manifest_evaluation()` 设计为风险 manifest 执行循环 hook可记录 Candidate 未命中及 `evaluator_error / artifact_integrity_error / unsupported_evaluator / timeout` 等结构化失败。
- `record_review_label()` 只接收类型化 label、认证 actor ID 和 request IDactor 与 request 进入表前被指纹化。
- `AgentAssetReleaseSamplingService` 在 observation 同一事务内完成分层选择与来源加密;加密失败会连同 observation 一起回滚,避免留下无法复核的孤立事实。
- `AgentAssetReleaseReviewService` 只查询当前租户、当前 release 的样本,混排后输出去预测队列;发布发起人不可自审,负样本强制两个不同 actor。
- `agent_asset_release_aggregation` 将精度和召回证据拆开聚合;`agent_asset_release_recall` 使用随机层漏检率与 Wilson 上界生成总体 FN 估计和 recall 下界。
- `record_risk_disposition_label()` 会重新查询数据库中的同租户 `RiskDispositionEvent``RiskObservation`,校验 action、claim/rule 来源指纹和版本,不信任调用方提供的人工结论副本。
- `aggregate()` 只读取同租户、同资产、同 release、同阶段、同版本事实并验证它仍是资产当前 release。
- `to_release_evaluation_input()` 只允许 `ready` 聚合转换;未标注、无候选正例或空样本会抛出 collecting 错误。
- `AgentAssetReleaseMonitor` 与周期调度器只在服务端查询事实并调用上述方法HTTP 入口只接受签名触发,不再接收外部汇总数字。
- Agent 资产基础 CRUD/表格/版本接口与风险规则生成、测试、启停和发布子路由分离;
资产版本只读投影由独立序列化 mixin 承担Release Guard 只编排状态与持久化,
阈值归一化和质量门禁计算下沉为无数据库副作用的纯策略模块。
### 算法/规则
- shadow 同时保留候选与 stable 的命中信息,用同一人工真值分别估计 candidate precision 和 baseline precision。
- Canary 使用稳定路由键分流;必须在 manifest 执行循环记录候选 miss否则只从最终 flag 采集会产生“只有命中样本”的选择偏差。
- candidate 正向命中使用 `confirmed / false_positive` 计算 precision盲审统一使用 `risk_present / risk_absent` 描述业务真值,再在聚合层规范化,不向复核人暴露预测结论。
- candidate miss 只有进入服务端抽样表并完成独立双人盲审后才可形成 FN/TN 证据;未抽中的个体不能直接被标签,也不能由“后续无退回”反推正确。
- 运行失败与业务误报分开:`failure_count` 表示 evaluator/快照/超时等结构化运行失败,`false_positive_count` 只进入 precision。
- 阶段最小样本数、最大错误率、最低 precision 和最大 precision drop 仍由 `ReleaseGuardPolicy` 统一判定。
- `ReleaseGuardPolicy.reviewer_quorum``1..2` 的受控整数随 release 策略固化,
telemetry 只统计不同 actor 指纹的最新票;票数不足或不同复核人结论冲突时保持
`collecting`,不会把部分意见交给 Release Guard。
- 新发布默认开启 recall 门禁,并冻结 `negative_sample_percent / negative_min_reviewed / min_recall / recall_confidence_level`;旧 release 未携带开关时保持兼容,不追溯伪造历史抽样事实。
- recall 门禁只比较 `recall_lower_bound``min_recall`;点估计再高,只要保守下界不足也不能晋级。明显低 precision 可直接失败,不必等待召回样本凑齐。
### 数据
#### `agent_asset_release_observations`
- 身份:`tenant_id / asset_id / release_id / stage / version / rule_code`
- 运行事实:`candidate_hit / baseline_hit / runtime_status / failure_code / business_stage`
- 脱敏来源:`source_kind / source_fingerprint`;不保存 claim ID、单号或业务正文。
- 一致性:租户幂等键唯一,保存 payload fingerprint同一来源不同内容冲突。
#### `agent_asset_release_labels`
- 身份复制tenant、observation、asset、release、stage、version并通过复合外键绑定原 observation。
- 标签:正例判断使用 `confirmed / false_positive`,盲审真值使用 `risk_present / risk_absent`
- 来源:`typed_risk_disposition / release_review / blind_release_review`;数据库组合约束禁止标签语义与来源交叉使用。
- 历史:只追加;纠正写新行,不修改或删除旧标签。
#### `agent_asset_release_audit_samples`
- 身份复制tenant、observation、asset、release、stage、version通过复合外键绑定原 observation。
- 分层:`candidate_positive_census / candidate_disagreement_census / candidate_negative_random`
- 抽样事实:保存入样概率和稳定选择分数;同租户 observation 最多一条样本,重放必须匹配 payload fingerprint。
- 来源保护:原始单据引用使用 SecretBox 加密,表中不保存明文;只有通过租户与复核角色检查的队列读取才解密。
三类模型同时具备 ORM 层 UPDATE/DELETE 拒绝。`0018` 建立 observation/label后继 `0023` 建立 audit sample、扩展标签约束并复用 PostgreSQL append-only 触发器;存在盲审事实或新标签语义时拒绝有损降级。
### 权限
- 所有写入、读取和聚合以 `tenant_id` 为第一条件;租户绑定资产不允许其他租户观察或标签。
- 平台共享资产可为不同租户分别保存 observation/label但各租户样本和 precision 不混算。
- 标签前重新校验资产当前 `release_id + stage + candidate_version`;旧 release、已晋级阶段或已回滚阶段拒绝新增标签。
- 类型化处置标签必须来自数据库中真实存在且同租户的 `RiskDispositionEvent`action 只允许 `confirm / false_positive`
- 专用复核队列只允许 `manager``admin`;租户和 actor 全部来自认证上下文,
跨租户资产返回 404非复核角色返回 403发布发起人自审返回 400。
- 标签写入必须携带 `X-Request-Id`;新客户端只发送 `risk_present / risk_absent`,旧客户端的 `confirmed / false_positive` 仅在复核 API 边界映射为盲审真值。标签、
actor 指纹和 request 来源只追加保存,客户端不能覆盖 tenant、release 或 actor。
- `reviewer_quorum=2` 时必须由两个不同复核人给出相同结论;同一人的重复提交不增加票数,
冲突结论进入待仲裁状态并继续阻止晋级。
### HMAC 与数据真实性边界
- HMAC 可以证明传输请求由持有密钥的一方生成、请求在允许时间窗口内且签名未被修改。
- HMAC 不能证明调用方提交的 `total=100``precision=0.99` 真的来自 100 条数据库 observation也不能证明人工标签存在。
- 因此 HMAC 只保留为自动 Monitor 的传输认证和防重放手段;指标必须由接收端使用当前数据库 observation/label 重新计算。
- 最终 Monitor 请求应只携带受控 release 触发信息或聚合作业游标,而非可被签名后照单采用的质量汇总数字。
- 即使 HMAC 认证失败,也不得影响 stable 规则继续保护业务;应停止晋级、记录安全告警并保持 `collecting`
### 降级策略
- 遥测表或写入暂不可用:不阻断已生效 stable 风险规则和报销主流程,但当前 release 不能晋级,状态保持 collecting 并告警。
- 人工标签迟到:保留 observation待标签追加后重新聚合不使用默认正确值填补。
- 运营端同时展示待标注数量和最早积压时长;超过 24 小时生成结构化逾期告警。
- 处置事件与 observation 无法安全关联:拒绝标签,不按相似文本、姓名或评论做模糊匹配。
- baseline 标签不完整:`baseline_precision = null`;候选指标可继续采集,但不能声称已完成可靠基线比较。
- 负样本未抽中、未完成双人复核或未达到最小复核量recall、估计 FN 和置信下界保持不可用,继续 collecting 并告警;不会用零填充。
- 聚合或 Guard 判定越界:按现有冻结快照恢复 stable回滚不删除候选 observation 和 label。
- 周期聚合异常形成 `release_aggregation_failed` 告警并隔离到单资产;运行失败率、
precision 下降、baseline 不可用和自动回滚分别使用独立告警码,稳定版本继续服务。
## 算法与公式
### 候选精确率
```text
candidate_precision = candidate_confirmed / (
candidate_confirmed + candidate_false_positive
)
```
- 分母只包含候选 `candidate_hit=true` 且已有可信最新标签的 observation。
- 任一候选正向命中仍未标注时,聚合保持 `collecting`,不得将部分 precision 交给 Release Guard 作为通过证据。
### 基线精确率
```text
baseline_precision = baseline_confirmed / (
baseline_confirmed + baseline_false_positive
)
```
- 只使用同一 shadow 样本上 `baseline_hit=true` 的可信标签。
- 任一基线正向命中待标注时baseline precision 显式不可用,不用部分样本制造有利比较。
### 运行错误率
```text
runtime_error_rate = runtime_failure_count / observed_count
```
- `observed_count` 是该 release/stage/version 的真实运行 observation 数。
- `runtime_failure_count` 只统计结构化执行失败,不把业务误报混成技术错误。
- 误报通过 precision 体现;运行错误通过 `ReleaseGuardPolicy.max_error_rate` 体现。
### Recall 与 false negative
```text
recall = TP / (TP + FN)
```
线上负样本分为两层,不能把抽检样本数直接当总体 FN
```text
disagreement_FN = 全量复核(candidate_hit=false, baseline_hit=true)中的真实风险数
random_FN_rate = 随机盲审层真实风险数 / 已完成双人复核的随机样本数
estimated_FN = disagreement_FN + random_FN_rate * random_negative_population
recall_point = TP / (TP + estimated_FN)
random_FN_rate_upper = WilsonUpper(random_FN, reviewed, confidence)
FN_upper = disagreement_FN + random_FN_rate_upper * random_negative_population
recall_lower_bound = TP / (TP + FN_upper)
```
- `false_negative_count` 仅表示已完成法定复核样本中实际观察到的 FN不等于总体 FN。
- `estimated_false_negative_count` 是总体点估计,`false_negative_upper_bound` 是保守上界,二者必须分字段展示。
- 随机层存在但尚无已复核样本、仍有抽中样本待审或未达到最小复核量时,上述估计统一保持 `null`
- 没有随机负例人群时可使用全量复核的精确 recall否则发布门禁只消费 `recall_lower_bound`
- 离线 Golden Case recall 只证明测试集表现,不能冒充线上 recall线上抽样也不能替代 Golden Case 的边界覆盖。
## 测试方案
- 模型:租户/release 复合身份、sample/label 到 observation 复合外键、标签来源组合约束和 append-only。
- 样本生产shadow hit/miss、stable baseline hit、Canary hit、manifest 循环 Canary miss 和结构化运行失败。
- 标签:专用复核、真实 `RiskDispositionEvent`、盲审语义、负样本双人法定票、来源/actor 脱敏。
- 幂等:相同 observation/label 稳定重放;同一来源不同载荷返回冲突。
- 租户与时效:跨租户隐藏、陈旧 release/stage/version 拒绝、错误规则/单据来源拒绝。
- 聚合无样本、无候选正例、无标签、部分标签、完整标签、baseline 部分标签、运行失败和 precision drop。
- 运营告警:待标注数量/最早时长、24 小时积压、运行失败率、聚合失败、baseline
不可用、precision 下降和自动回滚使用去敏结构化告警。
- 证据边界:未抽中负样本拒绝标签;预测字段不进入队列/API抽样不足时 recall/FN 为 null充分时点估计与下界分离。
- 组合回归Telemetry 生成的 `ReleaseEvaluationInput` 可被现有 Release Guard 消费,且不改变 shadow/Canary/回滚状态机。
- PostgreSQL0018/0023 upgrade/downgrade、复合外键、标签组合约束、数据库 append-only、并发同幂等键单赢家、同 actor 去重和标签/阶段竞争。
- 所有验证在 `local-x-financial-linux` 容器内执行,单条命令最大 60 秒。
## 指标与验收
- [A1] 每条线上发布样本可追溯到 tenant、asset、release、stage、version、rule 和来源指纹,且不含业务正文。
- [A2] 相同运行/标签重放只保留一条事实;不同载荷复用同一来源 100% 拒绝。
- [A3] 任一候选正向命中未标注时状态为 collecting不能生成 Release Guard 通过输入。
- [A4] precision 和 baseline precision 只由真实 observation 与可信类型化标签计算,外部汇总值不作为权威事实。
- [A5] recall/FN 证据不足时明确 unavailable充分时同时披露实际 FN、估计 FN、FN 上界、recall 点估计和置信下界,门禁只使用下界。
- [A6] 跨租户、陈旧 release/stage/version、错误处置来源和纯负样本伪标签均被拒绝。
- [A7] 质量越界时自动回滚 stable遥测故障或 HMAC 故障时停止晋级但不关闭既有稳定保护。
- [A8] PostgreSQL 迁移、append-only、并发、后端组合回归、Ruff 和 `git diff --check` 全部在容器内通过。
## 风险与开放问题
- 模型注册、真实 manifest hook、类型化处置标签、盲审抽样、服务端聚合、即时 Guard、
租户周期调度、发布复核队列和运营控制台已经接通;`0023` 后继迁移与完整 PostgreSQL
循环仍须完成最终验证后才能关闭本能力。
- 租户调度器只自动处理租户绑定资产。平台共享资产可以按租户保存隔离样本,但在建设跨租户、加权且可审计的聚合口径前,不能由单租户样本自动回滚全局版本。
- 线上标签可能集中在高风险或有争议样本precision 仍可能受人工复核选择偏差影响;控制台必须同时披露 hit、labeled 和 pending 数量。
- 规则稀有时可能长期没有候选正例;不能为了晋级降低为“零命中等于 100% precision”需要延长 shadow 或补充经审核 Golden Case。
- 标签纠正采用追加新事实和“每个 actor 最新票”聚合;数据库索引、复合外键和只追加
约束已在 PostgreSQL 验证,同 observation/label 的并发单赢家、阶段晋级竞争和调度器
advisory leader lease 均有 PostgreSQL 并发验证。
- HMAC 密钥泄露会让攻击者通过传输认证,但仍不应允许其伪造数据库 observation/label服务端重算是不可省略的第二道边界。
- 线上 recall 已有分层抽样、预测盲化、双人复核、冲突保持 collecting、最小样本量和置信下界仍需用试点数据校准抽样比例、人工一致率与业务风险容忍度不能把默认阈值当行业通用真理。
## 本轮实现记录
- 2026-07-16完成现有 Loader、平台风险评测、shadow_evaluations、风险处置和 AI workflow feedback/outcome 的只读审计,确认 `RiskDispositionEvent` 是当前最可信线上人工标签源,通用学习结果缺少 release 身份不能直接用于门禁。
- 2026-07-16新增独立 observation/label 模型与服务完成脱敏、append-only、稳定幂等、租户/陈旧 release 拒绝、shadow/Canary 样本生产、可信标签和保守聚合。
- 2026-07-16独立切片阶段新增遥测测试 7 项,与当时 Release Guard/Runtime 组合共 19 项通过;该阶段留下的共享注册、迁移和运行 hook 已在后续记录中完成。
- 2026-07-16完成主模型注册、迁移所有权与 `0018`;一次性 PostgreSQL 17 完整迁移循环、复合外键和数据库 append-only 探针通过。
- 2026-07-16真实 shadow/Canary/active manifest 执行已写 observation类型化风险处置自动追加标签并即时触发 Guard相同聚合快照幂等复用测试运行低 precision 或运行失败自动恢复 stable。
- 2026-07-16Release Monitor HTTP 改为空触发 + HMAC禁止调用方提交 precision/total新增租户级周期调度作为即时监控失败的补偿链。发布组合回归 44 项、调度/风险定向回归 24 项通过。
- 2026-07-16完成后端大文件职责拆分`agent_assets.py` endpoint 降至 714 行、
`AgentAssetService` 降至 675 行、`AgentAssetReleaseGuardService` 降至 636 行;
风险规则子路由、资产序列化和发布纯策略分别独立,旧路由路径、公开类型导入和状态机行为保持兼容。
- 2026-07-16发布纯策略新增 `reviewer_quorum`(默认 1、范围 1..2)并随 release state 保存,
专用去敏复核队列按独立 actor 计票,禁止发布人自审,双人同意前或结论冲突时保持 collecting。
- 2026-07-16发布控制台展示真实样本、命中、待审、最早积压时长、运行失败率、
candidate/baseline precision 和 recall 不可用;新增积压、超时、运行故障、聚合失败、
precision 下降、baseline 不可用和自动回滚结构化告警。
- 2026-07-16新增 append-only 盲审样本、正例/分歧全量与其余负例稳定随机抽样来源引用加密保存observation 与 sample 同事务写入,保护失败整体回滚。
- 2026-07-16发布复核队列改为预测盲化混排负样本强制两个不同复核人新增实际/估计 FN、Wilson FN 上界、recall 点估计与保守下界Release Guard 只消费下界。
- 2026-07-16前端拒绝 prediction hit 字段,使用中性业务真值动作,并显示负样本池、抽样进度、积压及置信方法;证据缺失保持不可用,不渲染为零。
- 2026-07-16新增 `0023` 后继迁移和标签来源组合约束;最终 PostgreSQL 全链升级/降级、并发和全量回归结果待验证后回填。

View File

@@ -0,0 +1,120 @@
# AI 分阶段发布真实遥测 开发 TODO
更新时间2026-07-17
## 使用规则
- 每项必须回链 `CONCEPT.md` 对应章节;没有代码、迁移、接口或容器证据不得勾选。
- observation、label、聚合和 Release Guard 输入必须按证据层分开,不能用 HMAC 请求或客户端汇总值替代数据库事实。
- `collecting` 不得包装成通过recall/false-negative 没有达到独立盲审证据阈值时必须保持 unavailable。
- 所有后端、迁移和并发验证只在 `local-x-financial-linux` 容器内执行,单条命令最长 60 秒。
## 1. 调研与边界
- [x] [CONCEPT: 背景与问题] 审计 Loader、平台风险评测、shadow_evaluations、风险处置和 AI workflow feedback/outcome 链路,确认线上发布评测缺少持久真实样本。
证据:`expense_claim_risk_rule_loader.py``expense_claim_platform_risk.py``risk_dispositions.py``expense_workflow_learning.py``ai_learning.py` 只读审计。
- [x] [CONCEPT: 背景与问题] 确认 `RiskDispositionEvent confirm/false_positive` 是当前可绑定风险命中的可信人工结论,通用 feedback/outcome 缺少完整 release 身份。
证据:`risk_disposition.py``risk_dispositions.py``agent_asset_release_telemetry.py` 的可信事件查询和关联校验。
- [x] [CONCEPT: HMAC 与数据真实性边界] 冻结 HMAC 只负责传输认证、防篡改和防重放,不证明汇总指标的数据真实性。
证据:`CONCEPT.md`“HMAC 与数据真实性边界”;现有 `agent_asset_release_monitor_auth.py``ReleaseEvaluationInput` 契约对照审计。
- [x] [CONCEPT: 目标与非目标] 明确未标注 collecting、敏感数据不落遥测表、线上 recall/FN 证据不足不可用。
证据:`CONCEPT.md`“目标与非目标”“Recall 与 false negative”。
## 2. 契约与设计
- [x] [CONCEPT: 数据] 定义 observation 与 label 的 tenant/asset/release/stage/version 复合身份、稳定幂等键和只追加边界。
证据:`server/src/app/models/agent_asset_release_telemetry.py`
- [x] [CONCEPT: 证据链与自动判定] 定义 observation → trusted label → aggregate → ReleaseEvaluationInput → Release Guard 的证据链。
证据:`CONCEPT.md`“证据链与自动判定”、`ReleaseTelemetryAggregate.to_release_evaluation_input()`
- [x] [CONCEPT: 算法/规则] 分开定义运行失败、业务误报、candidate precision、baseline precision 和缺失负样本真值。
证据:`agent_asset_release_telemetry.py``aggregate()``test_agent_asset_release_telemetry.py` 的 collecting、baseline 和 FN 不可用断言。
- [x] [CONCEPT: 权限] 冻结专用发布复核接口的角色矩阵、双人复核阈值和跨租户 HTTP 错误契约。
证据:`require_rule_reviewer_user` 只允许 manager/admin跨租户 404、非角色 403、
发布人自审 400`reviewer_quorum` 限制 1..2 且按不同 actor 指纹计票。
## 3. 独立模型与服务
- [x] [CONCEPT: 数据] 新增 append-only observation/label ORM 模型、复合租户/release 外键和更新/删除拒绝。
证据:`server/src/app/models/agent_asset_release_telemetry.py`
- [x] [CONCEPT: 后端] 实现真实 shadow 结果、Canary/active 命中和 manifest 执行循环样本生产器。
证据:`AgentAssetReleaseTelemetryService.record_expense_risk_result()``record_manifest_evaluation()`
- [x] [CONCEPT: 后端] 实现专用发布复核标签和可信 `RiskDispositionEvent` 标签转换,不接收自由评论。
证据:`record_review_label()``record_risk_disposition_label()`
- [x] [CONCEPT: 后端] 实现租户隔离、陈旧 release/stage/version 拒绝、来源关联校验和稳定幂等冲突。
证据:`_require_current_release()``_observation_replay()``_label_replay()` 及对应测试。
- [x] [CONCEPT: 算法与公式] 实现保守聚合;未标注候选命中保持 collecting基线标签不完整时 baseline precision 不可用。
证据:`ReleaseTelemetryAggregate``aggregate()``to_release_evaluation_input()`
- [x] [CONCEPT: 数据] 实现 claim、事件、actor 来源指纹化,不保存业务正文、评论和账号原值。
证据:模型无自由文本业务字段;`_fingerprint()`;脱敏测试断言。
## 4. 共享注册、迁移与运行接入
- [x] [CONCEPT: 数据] 在 `db/base.py``models/__init__.py` 注册 `AgentAssetReleaseObservation``AgentAssetReleaseLabel`,保证主应用 metadata 与迁移所有权检查可见。
证据:`db/base.py``models/__init__.py``schema_ownership.py``migration_preflight.py` 已登记两张迁移自有表;前置检查 77 项通过。
- [x] [CONCEPT: 数据] 新增后继 `20260716_0018` Alembic 迁移,创建两张表、复合租户/release 约束、检查约束、索引和数据库级 append-only UPDATE/DELETE 触发器。
证据:`20260716_0018_agent_asset_release_telemetry.py`;一次性 PostgreSQL 17 完整升级/降级循环 1 项通过,静态迁移/schema owner 回归 112 项通过。
- [x] [CONCEPT: 算法/规则] 在真实候选 manifest 执行循环接入 `record_manifest_evaluation()`,完整记录 shadow/Canary hit、miss 和结构化执行失败。
证据:`expense_claim_platform_risk.py``expense_claim_release_telemetry.py``test_agent_asset_release_runtime.py` 覆盖 shadow、Canary、active 和损坏快照,`test_agent_asset_release_telemetry.py` 覆盖结构化运行失败。
- [x] [CONCEPT: 后端] 在类型化风险处置事务接入 release label按同租户、同 claim/rule、当前 release 精确关联 observation关联失败保守拒绝。
证据:`agent_asset_release_disposition_labels.py``risk_disposition_release_sync.py`;风险确认/误报会追加标签,误报越界自动回滚 stable。
- [x] [CONCEPT: 后端] 将现有 Release Monitor 从“提交外部汇总数字”改为“触发服务端聚合”,只在 aggregate ready 时构造 `ReleaseEvaluationInput`
证据:`AgentAssetReleaseMonitor.evaluate_current()` 只接受 release 身份HTTP body 为禁止额外字段的空触发契约,真实聚合未 ready 时不调用 Guard。
- [x] [CONCEPT: HMAC 与数据真实性边界] 保留 HMAC 作为 Monitor 传输认证,但禁止签名请求覆盖数据库聚合结果,并补充签名通过但汇总伪造的反向测试。
证据:`agent_asset_releases.py``AgentAssetReleaseMonitorTriggerWrite`;带有效签名的伪造 `precision/total` 请求仍返回 422。
- [x] [CONCEPT: 降级策略] 遥测持久化或聚合失败时保持 stable 规则、停止晋级并记录结构化告警,不让观测故障阻断正常报销。
证据:`ExpenseClaimReleaseTelemetryRecorder``risk_disposition_release_sync.py` 将遥测/标签/监控故障隔离并记录日志;未获得 ready 真实聚合不会写 passed也不会改变 stable 路由。
- [x] [CONCEPT: 后端] 按职责拆分 Agent 资产接口、版本只读投影和 Release Guard 纯策略计算,保持公开 API 与状态机行为稳定。
证据:`agent_assets.py` endpoint 714 行、`agent_asset_risk_rules.py` 534 行、`agent_assets.py` service 675 行、`agent_asset_serialization.py` 217 行、`agent_asset_release_guard.py` 636 行、`agent_asset_release_policy.py` 201 行,相关核心文件均低于 800 行;纯策略模块保存范围为 1..2 的 `reviewer_quorum`,第 5 节已完成复核执行与运营闭环。
## 5. 自动聚合、告警与运营闭环
- [x] [CONCEPT: 证据链与自动判定] 实现按 tenant/asset/release/stage/version 的周期聚合作业与幂等快照。
证据:`agent_asset_release_scheduler.py` 按租户有界扫描;`AgentAssetReleaseMonitor._existing_evaluation()` 复用相同 release 聚合快照,不重复生成测试运行。
- [x] [CONCEPT: 证据链与自动判定] aggregate ready 后自动调用 Release Guardcollecting 只更新采集状态,不写虚假 passed test run。
证据:人工标签提交后即时触发 monitor后台 scheduler 提供失败补偿;`test_agent_asset_release_monitor.py` 覆盖 collecting 不调用 Guard、低 precision/运行失败自动回滚与相同快照幂等。
- [x] [CONCEPT: 降级策略] 增加待标注数量/时长、运行失败率、precision 下降、baseline 不可用、聚合失败和自动回滚告警。
证据:`agent_asset_release_alerts.py``AgentAssetReleaseMonitor._metrics()`24 小时
待审逾期和单资产聚合失败均返回结构化告警,定向 Monitor/Telemetry/Runtime 29 项通过。
- [x] [CONCEPT: 前端] 在发布控制台展示 observed/hit/labeled/pending、候选/基线 precision、运行失败和召回证据。
证据:`AuditReleaseMonitorPanel.vue` 展示负样本池、抽样进度、积压、实际/估计 FN、FN 上界、recall 点估计/下界/置信方法;空值不渲染为零。
- [x] [CONCEPT: 权限] 实现认证发布复核队列、操作审计和需要时的双人复核,不允许规则发布人独自伪造所有标签。
证据:`agent_asset_release_review.py``agent_asset_release_label_votes.py`、专用 GET/POST
API`X-Request-Id`、append-only label、actor HMAC 指纹、发布人隔离和独立双人同意均有测试。
- [x] [CONCEPT: Recall 与 false negative] 实现独立分层负样本抽样、预测盲化、双人标注、冲突保持 collecting 和保守召回估计。
证据:`agent_asset_release_sampling.py``agent_asset_release_review.py`
`agent_asset_release_aggregation.py``agent_asset_release_recall.py`;前端只发送
`risk_present / risk_absent`,负样本要求两个不同 actor门禁只读取 recall 下界。
- [x] [CONCEPT: 数据] 完成 `0023` audit sample 迁移注册、数据库标签组合约束、append-only 触发器和无损降级验证。
证据:`20260716_0023_agent_asset_release_blind_audit.py``release_telemetry_migration_assertions.py`fresh PostgreSQL 完整迁移链、约束、触发器及降级/再升级均通过。
## 6. 测试与验证
- [x] [CONCEPT: 测试方案] 独立模型/服务测试覆盖 shadow、Canary、baseline、真实 typed disposition、脱敏、幂等、租户、陈旧 release、append-only、collecting 和 FN 不可用。
证据:容器内 `test_agent_asset_release_telemetry.py` 7 项通过。
- [x] [CONCEPT: 测试方案] 与现有 Release Guard 和 Runtime 组合回归通过。
证据:容器内 `test_agent_asset_release_guard.py``test_agent_asset_release_runtime.py``test_agent_asset_release_telemetry.py` 共 19 项通过Ruff 通过,`git diff --check` 通过。
- [x] [CONCEPT: 测试方案] 新增 0018 upgrade/downgrade、schema owner、复合外键和数据库 append-only PostgreSQL 验证。
证据:一次性 `pgvector/pgvector:pg17` 数据库中完整迁移循环 1 项通过;迁移运行探针验证复合租户外键、幂等唯一约束和两张表的数据库级 UPDATE/DELETE 拒绝。
- [x] [CONCEPT: 测试方案] 新增 PostgreSQL 并发同 observation、同 label、标签与阶段晋级竞争和幂等冲突测试。
证据:一次性 PostgreSQL 17 中 `test_agent_asset_release_telemetry_concurrency_postgres.py` 4 项通过;相同重放只保留一条,不同载荷单赢家,阶段转换持锁后旧标签保守拒绝。
- [x] [CONCEPT: 测试方案] 验证 audit sample 并发单赢家、同 actor 重复不增加负样本法定票、第二独立 actor 完成双人复核,以及预测字段不进入前端队列状态。
证据:`test_agent_asset_release_telemetry_concurrency_postgres.py` 覆盖单赢家和独立双人票;`test_agent_asset_release_telemetry.py``agent-release-monitor-panel.test.mjs` 验证队列不暴露 candidate/baseline 命中预测。
- [x] [CONCEPT: 测试方案] 跑通真实风险循环 → observation → disposition label → aggregate → Release Guard 回滚端到端。
证据:`test_agent_asset_release_runtime.py``test_risk_dispositions.py` 覆盖真实执行样本、类型化处置标签、服务端聚合、低 precision 自动恢复 stable发布组合回归 44 项通过。
- [x] [CONCEPT: 测试方案] 验证职责拆分后的资产服务、风险规则子路由、Release Guard/Runtime、Monitor/Scheduler/Telemetry 和 API schema 兼容性。
证据:容器内资产服务 27 项、Guard/Runtime 13 项、Monitor/Scheduler/Telemetry 22 项、风险规则生成/解释 30 项、修订/反馈 14 项通过;迁移后的既有 publish HTTP 防绕过用例通过Ruff 与 `git diff --check` 通过。
- [x] [CONCEPT: 指标与验收] 验证 HMAC 失败、遥测库失败、标签积压和聚合失败均停止晋级但不破坏 stable 业务保护。
证据:`test_agent_asset_release_runtime.py` 覆盖签名失败;`test_agent_asset_release_monitor.py` 覆盖 collecting、积压和聚合失败`ExpenseClaimReleaseTelemetryRecorder` 隔离遥测持久化异常stable 报销路径继续服务。
- [x] [CONCEPT: 指标与验收] 完成相关后端全量回归、Ruff、迁移完整循环和 `git diff --check`,逐项回填 A1-A8 证据。
证据:遥测组合 45 项通过fresh PostgreSQL 总探针 `87 passed / 0 skipped / 0 failed`,其中发布遥测并发 7 项Web 全量 815 项及 Vite build 通过;新增 Python 文件 Ruff 与 `git diff --check` 通过。全仓既有 Ruff 基线债不伪装为本轮新增错误。
## 7. 文档收尾
- [x] [CONCEPT: 本轮实现记录] 新建独立 CONCEPT/TODO记录证据边界、已实现切片和共享集成缺口。
证据:`document/development/2026-07-16/feature/ai-release-real-telemetry/CONCEPT.md``TODO.md`
- [x] [CONCEPT: 风险与开放问题] 共享集成完成后回填 0018、运行 hook、自动作业、PostgreSQL 和端到端证据。
证据:本 TODO 第 4-6 节与 `CONCEPT.md`“本轮实现记录”已回填发布面板已展示积压、失败、precision 和 recall生产运营效果由下一条真实流量验收单独保留。
- [x] [CONCEPT: 指标与验收] 与上位 AI 闭环 TODO 对齐代码与容器验证状态。
证据:`document/development/2026-07-13/feature/ai-expense-closed-loop-and-value-proof/TODO.md` 已回填 Golden、Canary、盲审和回滚的工程证据。
- [ ] [CONCEPT: 指标与验收] 使用生产真实流量、独立复核样本和试点阈值验证 Golden/Canary/自动回滚运营效果。
证据要求:目标企业生产 observation/label、盲审样本、阈值签字和真实回滚演练本地测试不得替代。

View File

@@ -0,0 +1,237 @@
# 商业计量、客户 ROI 与可持续定价 概念文档
更新时间2026-07-17
## 功能一句话
把套餐、订阅、权益、真实用量、内部成本、客户确认价值和平台毛利拆成可审计事实,并只在成本与价值证据同时成立时给出可持续定价走廊。
## 背景与问题
- 平台要成为可长期经营的产品,既要证明客户省了钱,也要知道每个租户消耗了多少 OCR、AI、存储、连接器和支持成本。
- “风险金额”“预计节省”“流程耗时”不能直接作为客户 ROI平台收入、平台内部成本和客户价值也不能混在一个指标里。
- 只有套餐创建而没有暂停、取消、历史查询和配额硬门禁,商业后台无法真正运营。
- 仅在执行前读取 `SUM(usage)` 再放行无法抵抗并发;两个工具可同时看到剩余额度并一起执行,事后计量再准确也已形成不可逆超卖。
- 固定拍一个价格无法适配客户规模、真实成本和价值覆盖。需要先计算平台可持续下限,再计算客户价值可接受上限;没有交集时不能强行报价。
- 试点期通常缺少 30/90 天真实成本和财务确认收益,因此系统必须显示“采集中”,而不是用 mock 或估计值伪造单位经济性。
本能力是 `2026-07-13/feature/ai-expense-closed-loop-and-value-proof` 中商业模式与价值证明部分的实施拆分。
## 目标与非目标
### 目标
- [G1] 建立租户隔离、版本化的套餐、订阅和权益配置。
- [G2] 建立追加式用量与内部成本事实,支持幂等、冲回、配额和并发门禁。
- [G3] 把商业权益门禁与安全门禁做收紧式合并,付费不能绕过高风险人工审核。
- [G4] 严格分开客户收费、内部成本、平台贡献毛利、财务确认现金节省、客户 ROI 和工时价值。
- [G5] 支持套餐/订阅/权益生命周期、历史查询、用量成本查询和数据质量状态。
- [G6] 根据真实成本和财务确认节省给出基础费下限、价值上限和可封顶成功费,不自动改合同。
- [G7] 形成“试点采集 → 基础订阅 → 基础费 + 封顶成功费”的可验证商业演进路径。
- [G8] 用不可变账期承载收费、用量、成本与配额历史,并对商业配置和自动续期保留脱敏追加式审计。
### 非目标
- [NG1] 不在代码中硬编码某个客户的最终价格、税率、折扣或合同条款。
- [NG2] 不把风险暴露、预计机会、未确认结果、未锁定汇率或未经客户认可的工时估值计入价值定价。
- [NG3] 不让套餐或配额放宽审批、风险、租户和人工确认门禁。
- [NG4] 不自建开票、税务、收款或第三方订阅扣费网络;只保存受控外部订阅引用。
- [NG5] 不跨币种直接求和,也不在缺少同币种成本/价值时生成综合 ROI。
- [NG6] 不把定价场景结果自动写成生效套餐,最终合同仍需平台商业负责人审批。
## 用户与场景
### 用户
1. 平台商业管理员:维护套餐版本、订阅、权益、状态和定价场景。
2. 平台运营/财务:查看用量、成本、毛利和异常数据质量。
3. 客户 CFO/财务负责人:查看自己租户的当前套餐、用量、配额和客户价值口径。
4. 产品运行时:在执行 OCR、AI 或连接器能力前检查配额,并写入真实用量。
### 核心场景
1. 试点客户先配置 `pilot` 套餐和明确的合同周期,开始采集真实用量、成本与已确认价值。
2. 运行时检查某项权益;只有商业配额允许且安全决策为 allow 时才最终允许。
3. 同一用量事件重试返回首次结果;不同内容复用幂等键返回冲突。
4. 客户暂停、逾期、取消或过期时,商业权益即时失败关闭,历史用量和成本不被删除。
5. 商业负责人选择 90 天窗口,输入目标贡献毛利率和客户最大价值分享比例,系统按币种输出定价走廊。
6. 成本下限高于价值上限时,系统建议先优化单位经济性或扩大可信价值,不生成强行报价。
## 功能能力
- [C1] 套餐版本subscription、usage、hybrid、pilot、custom支持生效区间和旧版本退役。
- [C2] 订阅快照:合同周期、计费周期、席位、基础费、外部订阅引用和状态历史。
- [C3] 权益与配额feature、metered、unlimited包含量、硬上限、重置周期和超额策略。
- [C4] 用量事实usage、credit、adjustment、reversal保存主体、来源、correlation 和首次请求指纹。
- [C5] 成本事实AI、OCR、存储、连接器、支持、实施、基础设施、支付等分类及汇率快照。
- [C6] 商业分析:收费、成本、贡献毛利、确认节省、客户 ROI 和工时价值分账展示。
- [C7] 定价走廊:最低可持续收费、最高价值对齐收费、最大成功费和证据状态。
- [C8] 生命周期与查询:暂停、恢复、逾期、取消、过期,以及套餐/订阅/权益/用量/成本历史。
- [C9] 账期与审计:月/季/年自动续期、合同边界失败关闭、账期历史和商业管理追加审计。
## 方案设计
### 前端
- 商业工作台分为“当前账户”“套餐与订阅”“权益与配额”“用量与成本”“价值与定价”五块。
- 客户 ROI 与平台贡献毛利必须使用不同卡片、不同说明,不允许用一个“综合收益”混合展示。
- 多币种按币种分行;缺成本、缺确认价值、仅有试点数据和证据冲突使用不同状态。
- 暂停、取消和定价场景均需要确认;终态操作明确提示不能原地恢复。
- 普通客户财务只能查看本租户账户;平台级配置、成本、毛利和定价仅平台管理员可见。
### 后端
- `CommercialAdminService` 管理套餐、订阅、权益和状态转换。
- `CommercialBillingPeriodService` 签发和定位不可变账期;用量、成本和运行时预占必须绑定真实账期编号。
- `CommercialSubscriptionRolloverService` 在订阅行锁内按月/季/年边界幂等续期;合同制或跨越 `ends_at` 时失败关闭。
- `CommercialRolloverScheduler` 以 PostgreSQL advisory lock 选举单一执行者,再按订阅行锁串行签发到期账期。
- `CommercialAdminAuditService` 只记录字段白名单快照,排除合同正文、配置、外部订阅编号、元数据和凭证类字段。
- `CommercialQueryService` 负责租户范围内历史与追加事实查询。
- `CommercialEntitlementService` 计算配额和商业/安全合并门禁。
- `CommercialMeteringService` 写追加式用量和成本、执行幂等与冲回。
- `CommercialRuntimeReservationService` 在订阅/权益锁内完成执行前额度预占,管理 reserved、committed、released、expired、reconciliation_required 和 committed_reconciliation_required 状态。
- `CommercialRuntimeBridge` 把可信 AgentRun、中央工具执行、真实 AgentToolCall 和商业事实接成预占—执行—结算链;未配置商业计量时保持兼容。
- `CommercialRuntimeReconciler` 只根据可验证的工具/运行终态补偿过期预占,不按超时猜测业务是否发生。
- `CommercialAnalyticsService` 按窗口和币种分账聚合,不伪造缺失数据。
- `CommercialPricingService` 只读分析真实成本和确认节省,输出价格区间,不写套餐。
- HTTP 管理入口仅平台管理员可用;租户账户读取仅 finance、executive 或平台管理员可用。
### 算法/规则
- 用量硬配额在数据库锁内计算,最终用量超过限制时整个写入失败。
- 真实工具执行前按 `已用量 + 有效预占 + 本次预占 <= 硬上限` 原子判断;成功时真实量不得超过预占,失败/阻断只释放预占,不写用量或成本。
- call 基准可直接预占一次token、duration 等变量基准必须由执行器声明并强制最大量。缺少可信 hard max 时拒绝执行,不用正文长度或估算值代替。
- 已有相同 tool call 的预占请求按指纹稳定重放;真实 AgentToolCall 的用量/成本继续使用追加式幂等键。用量已写入但成本失败时进入 `committed_reconciliation_required`,重试只补缺失成本,不重复占用额度或追加用量。
- 权益已有用量后,配额、定价和有效期不可回改,只允许暂停/恢复;结构变化必须新建订阅版本。
- 客户 ROI 只使用财务确认 canonical 现金节省与客户收费。
- 贡献毛利只使用平台收费与内部成本,不混入客户节省。
- 定价只在相同币种内计算,并保留成本/价值证据状态。
### 数据
- `tenant_commercial_plans`:租户、套餐编码、版本、价格模型、基础费、币种、生效期和合同条款摘要。
- `tenant_subscriptions`:租户、套餐、周期、基础费快照、状态、席位、外部引用和版本。
- `commercial_entitlements`:租户、订阅、权益键、计量键、配额、状态和有效期。
- `usage_meter_events`:追加式用量、冲回引用、幂等键、请求指纹和关联链。
- `commercial_cost_events`:追加式内部成本、原币/报告币、汇率、分摊键和冲回引用。
- `commercial_runtime_reservations`:工具执行前的可变运营占位,保存 tenant/subscription/entitlement/run/tool call、基准、预占量、真实量、周期、配置快照、状态和补偿原因它参与配额但不是客户用量事实。
- `commercial_billing_periods`:不可变账期签发事实,保存订阅/套餐引用、窗口、顺序、币种、基础费、席位、计价模式和来源快照PostgreSQL 禁止更新和删除。
- `commercial_admin_events`:套餐、订阅、权益、账期和续期动作的脱敏追加审计,保存 tenant、actor、`X-Request-Id`、原因、动作、资源版本和白名单 before/after。
- `usage_meter_events.period_key` 表示真实账期键,`quota_period_key` 独立表示权益重置周期;成本与运行时预占同样通过 `billing_period_id` 绑定账期,避免订阅当前周期滚动后污染历史。
- 事实表在 PostgreSQL 使用触发器禁止 UPDATE/DELETE租户复合外键防止跨租户引用。
运行时占位状态如下:
```text
reserved -> committed # 成功且 actual <= reserved
reserved -> released # 工具失败或执行前阻断
reserved -> expired # 运行已终止且没有真实工具调用
reserved -> reconciliation_required # 已发生调用但缺少执行前预占等人工补偿场景
committed -> committed_reconciliation_required -> committed
# 用量已提交、成本等后续事实失败,幂等补齐后恢复
```
过期但运行仍在进行、运行记录缺失或工具终态不确定时继续保留额度,不允许补偿器仅因 TTL 到期释放后造成超卖。
`committed_reconciliation_required` 已有真实用量事实,因此不再计入有效预占;配额只消费一次,同时保留明确的待补偿队列。
### 权限
- 平台管理员可配置所有租户商业账户,但商业配置不能授予财务确认或风险审批能力。
- finance/executive 只读本租户当前账户,不读取平台内部成本或其他租户数据。
- manager、employee 默认无商业账户与商业分析权限。
- 所有管理接口从认证上下文判断平台管理员,目标租户来自受控路径参数。
### 降级策略
- 无订阅:返回 unavailable 和明确说明,不自动赠送无限权益。
- 订阅非 active/trialing配额保留展示但最终消费失败关闭。
- 缺成本:贡献毛利和可持续价格下限不可用。
- 缺财务确认价值:客户 ROI、价值上限和成功费不可用建议继续试点采集。
- 多币种缺少共同币种:分别展示,禁止跨币种净额。
- 并发或幂等冲突:返回 409不覆盖首次事实。
- 生产中央工具路径未配置 runtime meter兼容执行且不生成商业事实配置只在当前订阅周期和权益有效期内参与门禁历史过期配置不会误触发 enforcement。
- 已发生的旧直接工具路径缺少执行前预占:不补写为正常用量,持久化 `reconciliation_required` 并冻结对应容量,等待受控补偿;即使当前合同已暂停,也保留其唯一可验证的租户、订阅和权益归属。
- 用量已追加但内部成本写入失败:持久化 `committed_reconciliation_required`,配额以真实用量为准且预占归零;按相同 tool call 重试只补成本,完成后回到 committed。
- 自动续期只处理 `trialing/active + auto_renew`;月、季、年按自然月边界滚动。合同制、缺少可推导边界或下一完整账期越过 `ends_at` 时失败关闭,不创建部分账期或猜测续约。
- 调度器即使发生重复扫描或多进程竞争,也先获取 leader lease再锁定订阅账期窗口和幂等键的唯一约束保证同一周期最多签发一次。数据库触发器还会按租户与订阅获取事务级 advisory lock并拒绝任何半开区间重叠账期防止绕过服务层直接写入破坏时间线。
## 算法与公式
### 客户 ROI
```text
customer_roi = (verified_cash_savings - customer_charges) / customer_charges
```
- `verified_cash_savings` 只包含独立财务确认、canonical、已计冲回的现金节省。
- `customer_charges` 来自订阅基础费快照和有可信同币种单价的用量计费。
- 分母必须大于 0否则状态为 unavailable。
### 平台贡献毛利
```text
contribution_margin = customer_charges - internal_costs
contribution_margin_rate = contribution_margin / customer_charges
```
- 内部成本来自追加式成本账本,不使用估算页面数字。
### 可持续定价走廊
```text
minimum_sustainable_charge = internal_costs / (1 - target_margin_rate)
maximum_value_aligned_charge = verified_cash_savings * max_value_share
maximum_success_fee = max(0, maximum_value_aligned_charge - minimum_sustainable_charge)
```
-`minimum_sustainable_charge <= maximum_value_aligned_charge` 时,建议 hybrid基础费不低于成本下限成功费封顶为剩余价值空间。
- 只有成本证据时建议 subscription成本与价值都不足时建议 pilot_collecting。
- 成本下限高于价值上限时建议 optimize_unit_economics不自动提高客户报价。
## 测试方案
- 模型/迁移:租户复合外键、状态检查、金额符号、冲回引用和 append-only。
- 服务:套餐版本、订阅终态、权益不可回改、配额硬门禁、幂等、冲回和多币种。
- 权限平台管理员、租户财务、manager、employee 与跨租户访问。
- 分析:收费/成本/节省/毛利/ROI 分账,缺证据状态和 `as_of` 回放。
- 定价:可行区间、成本高于价值、仅成本、完全无证据和多币种。
- 前端:状态、权限、操作确认、空态、错误态、币种分组与生产构建。
- PostgreSQL并发配额、同幂等键、成本冲回单赢家和迁移完整性。
- PostgreSQL 账期0020→0021→0020 升降级、账期/审计 UPDATE/DELETE 拒绝、重叠账期 INSERT 拒绝、历史事实账期绑定和双线程续期单赢家。
- 运行时预占:无配置兼容、成功结算、失败释放、变量 hard max、真实量超预占、幂等重放、历史配置隔离、直接路径补偿和过期补偿。
- 所有命令在 `local-x-financial-linux` 容器内执行,单次最长 60 秒。
## 指标与验收
- [A1] 每个商业消费可追溯到租户、订阅、权益、用量事件、来源和 correlation。
- [A2] 并发不能突破硬配额;重复事件稳定重放,冲突内容被拒绝。
- [A3] 付费状态不能绕过安全或人工审核门禁。
- [A4] 客户 ROI、平台毛利、客户节省和工时价值在 API/UI 中不混算。
- [A5] 暂停、恢复、取消、过期和历史查询可操作,终态不可原地复活。
- [A6] 定价场景只使用真实同币种成本与确认节省;证据不足时不输出虚假价格。
- [A7] 相关后端、PostgreSQL、前端、构建、Ruff 与迁移验证在容器内通过。
## 风险与开放问题
- 真实计费仍需把 OCR、LLM、存储、连接器和支持运行事件自动接入用量/成本账本;手工管理员写入只能用于校验,不是最终生产采集。
- 中央 Orchestrator 工具已经执行前预占;绕过中央执行器的其他生产入口仍须逐一迁移到 permit 契约,当前只会形成可见补偿积压,不会伪装成正常计量。
- 合同制自动续期仍不推断新合同窗口;管理员或外部订阅连接器必须先提供显式续约事实,再创建后继合同/订阅版本。
- 首个客户的实际套餐金额、席位、包含量、毛利目标、价值分享比例和折扣需要商业负责人确认。
- 发票、税率、回款、坏账、渠道分成与收入确认尚未接入,当前 `customer_charges` 是合同/用量计费基准,不等同已收现金。
- 价值分享合同必须定义基线、排除项、冲回、确认人、封顶和争议期。
- 工时价值默认不进入现金 ROI只有客户确认活跃工时基线、角色成本和可释放比例后才单独披露。
## 本轮实现记录
- 2026-07-16完成五张商业事实表与 0016 迁移、套餐/订阅/权益、用量/成本、配额门禁、幂等和冲回服务。
- 2026-07-16完成客户收费、内部成本、贡献毛利、财务确认现金节省、客户 ROI 和工时价值分账分析;修正 ROI 为净收益口径并收紧角色、时区和权益历史不可变边界。
- 2026-07-16补齐订阅暂停/逾期/取消/过期、恢复和套餐/订阅/权益/用量/成本历史查询。
- 2026-07-16完成基于真实成本下限与确认价值上限的定价场景禁止风险暴露、预计节省或跨币种混入报价。
- 2026-07-16完成商业工作台五块能力、订阅生命周期确认、用量/成本与价值分账、按币种定价场景两步确认;全量前端 802 项、code-size 和生产构建通过。
- 2026-07-16一次性 PostgreSQL 17 并发配额、幂等和成本冲回 3 项通过;真实开票、回款与客户合同参数继续保留为外部试点边界,不以 mock 标记完成。
- 2026-07-16新增 `0019` 运行时预占迁移和中央 Agent 工具 permit在订阅/权益锁内按已用量加有效预占原子控额,成功按真实 AgentToolCall 结算,失败/阻断释放,变量用量超过预占拒绝写入。
- 2026-07-16新增持久补偿状态和过期补偿器旧直接调用缺少预占时冻结容量并进入 reconciliation_required运行终态不确定时不按 TTL 误释放;用量已提交但成本失败进入 committed_reconciliation_required相同预占与用量/成本均可幂等重试。
- 2026-07-16新增 `0021` 不可变账期和商业管理审计;订阅创建即签发首期,月/季/年到期由 leader 调度器和订阅行锁幂等滚动,合同制与越界周期失败关闭。
- 2026-07-16用量、成本、运行时预占和配额查询改为绑定 `billing_period_id`,并以独立 `quota_period_key` 保留权益重置语义;客户收费基础费改从账期快照聚合,不再读取可变订阅当前周期。
- 2026-07-16容器内商业/迁移前置定向 131 项、前端商业 35 项和 Vite 构建通过;一次性 PostgreSQL 17 商业并发 5 项通过。0021 的升级、模型/约束/触发器(含账期重叠阻断)/运行不变量及 0021→0020 降级通过;全链 44/45 唯一失败来自后继 0023 尚未接管的盲审临时表,不属于 0021。
- 2026-07-16容器内商业/Agent/迁移组合 183 项通过、1 项因未显式配置外部迁移库跳过;运行时定向 27 项、PostgreSQL 商业并发 4 项和 0019→0020 完整迁移循环通过;全局 code-size 仅剩共享 `RiskRuleGenerationService` 817 行既有门禁失败,本轮所有相关核心类低于 800 行。

View File

@@ -0,0 +1,87 @@
# 商业计量、客户 ROI 与可持续定价 开发 TODO
更新时间2026-07-17
## 使用规则
- 每项必须回链 `CONCEPT.md` 对应章节。
- 只有代码、接口或容器验证提供证据后才能勾选。
- 客户价值、平台收入、内部成本和工时估值必须分账mock 与手工事件不得标记为生产事实。
## 1. 调研与边界
- [x] [CONCEPT: 背景与问题] 明确商业权益、用量、成本、客户 ROI、平台毛利和定价不是同一事实。
证据:`CONCEPT.md`“背景与问题”“目标与非目标”。
- [x] [CONCEPT: 目标与非目标] 确认不硬编码客户价格、不混算风险暴露、不跨币种求和、不让付费绕过安全门禁。
证据:`CONCEPT.md`“目标与非目标”。
## 2. 契约与设计
- [x] [CONCEPT: 数据] 定义套餐、订阅、权益、用量和内部成本五类事实及状态。
证据:`commercial.py` 模型与 schema、`20260716_0016_commercial_metering.py`
- [x] [CONCEPT: 算法与公式] 定义客户 ROI、贡献毛利和可持续定价走廊公式。
证据:`CONCEPT.md`“算法与公式”、`commercial_analytics.py``commercial_pricing.py`
- [x] [CONCEPT: 权限] 定义平台配置、租户只读和商业/安全门禁分离。
证据:`commercial_access_policy.py``commercial_entitlements.py`
## 3. 后端实现
- [x] [CONCEPT: 数据] 新增五张商业表、复合租户约束、幂等、冲回和 append-only 迁移。
证据:`models/commercial.py``20260716_0016_commercial_metering.py`、迁移/模型测试。
- [x] [CONCEPT: 数据] 新增运行时预占运营表、状态约束、复合租户外键、全局 tool call 幂等和迁移所有权。
证据:`models/commercial_runtime.py``20260716_0019_commercial_runtime_reservations.py``commercial_migration_assertions.py`0019→0020 一次性 PostgreSQL 完整升降级循环通过。
- [x] [CONCEPT: 后端] 实现套餐版本、订阅、权益、配额、用量、成本与商业分析服务。
证据:`commercial_admin.py``commercial_entitlements.py``commercial_metering.py``commercial_analytics.py`
- [x] [CONCEPT: 生命周期与查询] 实现暂停、逾期、取消、过期、恢复与五类历史查询。
证据:`CommercialAdminService.transition_subscription()``commercial_queries.py``/commercial/admin/tenants/{tenant_id}/...` 分资源接口。
- [x] [CONCEPT: 定价走廊] 实现成本下限、确认价值上限、成功费封顶和商业模式建议。
证据:`commercial_pricing.py``POST /commercial/admin/tenants/{tenant_id}/pricing-scenarios`
- [x] [CONCEPT: 后端] 把中央 Orchestrator 工具接入执行前预占、真实 AgentToolCall 结算和失败释放。
证据:`orchestrator_tool_execution.py``agent_runs.py``commercial_runtime_bridge.py`;无配置兼容,配置后先 reserve 再执行,成功只追加真实用量/成本,失败和阻断不计量。
- [x] [CONCEPT: 降级策略] 持久化缺预占和计量故障补偿状态,并安全处理过期预占。
证据:`commercial_runtime_reservations.py``commercial_runtime_reconciler.py`;直接调用形成 reconciliation_required运行中/未知终态继续持有额度,终态无调用才过期释放;用量成功但成本失败形成 committed_reconciliation_required重试只补成本且不重复冻结额度。
- [x] [CONCEPT: 风险与开放问题] 把已识别的权威运行入口迁移到 permit 契约。
证据:中央 Orchestrator、`ocr_commercial.py``runtime_chat_commercial.py``financial_connector_commercial.py``expense_claim_attachment_commercial.py` 已接通预占/结算/释放;资源组合 63 项通过。
- [ ] [CONCEPT: 风险与开放问题] 对后续新增的知识库/ONLYOFFICE 存储、实施和支持等资源入口持续执行 meter 盘点,不允许绕过 permit。
证据要求:新增真实资源入口时提供权威数量口径、事务边界、成本来源和回归测试;当前不把未发生的未来入口伪装成已计量。
- [x] [CONCEPT: 风险与开放问题] 通过不可变 billing period 和幂等 rollover 实现 `auto_renew` 周期滚动。
证据:`20260716_0021_commercial_billing_periods.py``commercial_billing_periods.py``commercial_subscription_rollover.py``commercial_rollover_scheduler.py`;月/季/年自动滚动,合同制和 `ends_at` 越界失败关闭PostgreSQL 双线程仅生成一个账期,数据库触发器拒绝重叠账期。
- [x] [CONCEPT: 数据] 让用量、成本、运行时预占和配额历史绑定不可变账期,并分离配额重置键。
证据:`UsageMeterEvent``CommercialCostEvent``CommercialRuntimeReservation``billing_period_id`,以及 usage/reservation 的 `quota_period_key`;收费分析从账期快照读取基础费和币种。
- [x] [CONCEPT: 权限] 为套餐、订阅、权益和续期建立脱敏追加式审计与租户安全历史 API。
证据:`commercial_admin_events``commercial_admin_audit.py``commercial_billing.py`;管理写接口强制 `X-Request-Id` 和原因,普通 finance/executive 只能读本租户账期,审计仅平台管理员可读。
- [ ] [CONCEPT: 非目标] 对接真实开票/收款/订阅提供商并区分合同计费、已开票与已收现金。
证据:等待目标客户和 provider 选择,不使用 mock 冒充完成。
## 4. 前端实现
- [x] [CONCEPT: 前端] 完成商业工作台的账户、套餐/订阅、权益/配额、用量/成本、价值/定价五块。
证据:`CommercialWorkspace.vue` 组合账户、生命周期、权益、用量成本、价值分析与定价场景面板。
- [x] [CONCEPT: 前端] 接入订阅暂停/取消/恢复、历史查询和操作确认。
证据:`useCommercialWorkspace.js``CommercialSubscriptionLifecyclePanel.vue`;终态和定价均有确认步骤,操作后按租户重新加载。
- [x] [CONCEPT: 前端] 严格分开展示客户 ROI 与平台毛利,并支持多币种和证据缺口状态。
证据:`CommercialValueAnalysisPanel.vue``CommercialPricingScenarioPanel.vue``commercialWorkspaceModel.js`按币种分组null/unavailable 显示“不可用”,不跨币种合计。
- [x] [CONCEPT: 前端] 接入现有应用入口、权限态、移动端和生产构建。
证据:`OverviewView.vue`/顶部导航已接入“商业化管理”;商业定向 35 项、全量 web 802 项、code-size 与 Vite 2246 modules 构建通过。
## 5. 测试与验证
- [x] [CONCEPT: 测试方案] 后端模型、服务、HTTP、权限、生命周期、查询和定价回归通过。
证据:容器内 Ruff 通过;`test_commercial_models.py``test_commercial_services.py``test_commercial_endpoints.py` 当前 12 项通过。
- [x] [CONCEPT: 测试方案] 一次性 PostgreSQL 并发用量、原子预占、硬配额和成本冲回验证通过并记录当前命令结果。
证据:一次性 PostgreSQL 17 中 `test_commercial_concurrency_postgres.py` 4 项通过;两个并发工具竞争 1 份额度时仅一个 reservation 成功。
- [x] [CONCEPT: 测试方案] 运行时预占、结算、释放、幂等、变量上限、历史配置和补偿回归通过。
证据:`test_commercial_runtime_metering.py``test_commercial_runtime_reservations.py` 27 项通过;商业/Agent/权限/迁移相关组合 183 项通过、1 项因未显式配置外部迁移库跳过Ruff、compileall 和相关类 800 行检查通过。
- [x] [CONCEPT: 测试方案] 前端行为测试、全量 web 测试、code-size 门禁和 Vite 构建通过。
证据:商业定向 35 项、全量 web 802 项通过code-size 通过Vite production build 转换 2246 个模块。
- [x] [CONCEPT: 测试方案] 不可变账期、脱敏审计、自动续期和调度器验证通过。
证据:容器内商业/迁移前置定向 131 项、前端商业 35 项及 Vite build 通过PostgreSQL 17 商业并发 5 项通过含双线程续期单赢家0021 升级、重叠账期阻断、运行不变量和 0021→0020 降级通过。
- [x] [CONCEPT: 指标与验收] 逐项核对 A1-A7并回填最终文件、接口和容器证据。
证据:商业模型/服务/API/前端、硬配额、账期、生命周期、ROI/毛利分账和定价走廊均有回归;资源边界组合 63 项、PostgreSQL 商业并发 5 项、Web 全量 815 项及 Vite build 通过。
## 6. 商业与试点收尾
- [ ] [CONCEPT: 风险与开放问题] 用真实试点 30/90 天数据冻结目标毛利率、最大价值分享、包含量、超额策略和封顶。
- [ ] [CONCEPT: 风险与开放问题] 确认发票、税率、回款、坏账、渠道和收入确认边界。
- [x] [CONCEPT: 本轮实现记录] 同步更新上位闭环文档与工程验收手册,不删除证据不足项。
证据:上位 AI 闭环 TODO 与 `engineering-closure-and-production-readiness` CONCEPT/TODO 已区分工程完成、生产上线和真实试点。

View File

@@ -0,0 +1,226 @@
# 财务连接器与支付对账闭环 概念文档
更新时间2026-07-17
## 功能一句话
把经过租户、来源、密钥版本和请求路径共同认证的 production-mode 财务事件契约接入付款、ERP、对账与 Savings 闭环,同时让所有非生产回执严格停留在只读模拟事实层;真实外部现金仍以目标 provider 联调为准。
## 背景与问题
当前报销单可以由财务人员在平台内确认“已付款”,并能联动申请归档和 Savings 实现记录,但这只是内部业务状态,不是银行、支付平台或 ERP 的外部现金事实。若继续把单一状态当成真实回执,会留下重复付款、金额/币种错配、回执伪造、凭证缺失、对账异常未处置和虚假现金节省等风险。
本功能把外部财务系统接入收敛成统一、可审计的连接器契约。首期允许使用 mock adapter 验证协议和流程,但 mock 必须完整模拟签名、幂等、失败、重试、乱序、退款、凭证和对账差异,不以直接写“已付款”代替连接器事实。
## 目标与非目标
### 目标
- 建立租户隔离、来源可验证、追加式的支付/银行/ERP 事件账本。
- 支持支付批次、结算成功、支付失败、退款/冲回、ERP 入账凭证和对账结果。
- 只有单据、金额、币种、外部引用和签名均通过校验时,才推进报销付款状态。
- 把外部事件与 Expense Case、Business Event、Savings Evidence、归档事件用同一 correlation 链关联。
- 对重复、冲突、乱序和不完整事件 fail-closed并提供可人工处理的对账异常。
- 以版本化 activate/disable/rotate 状态机管理连接器密钥,所有配置动作形成不含密钥材料的追加式审计。
### 非目标
- 不自建银行清算、企业支付网络、税务开票网络或 ERP 总账。
- 不保存银行卡号、银行流水原文、完整付款人账号或连接器密钥明文。
- 不允许连接器绕过报销审批、风险门禁、租户权限或财务确认。
- 不把 mock 回执标记为生产级外部现金证据;运行环境和证据等级必须显式区分。
## 用户与场景
- 财务付款员:提交或查看付款批次,处理失败与待匹配回执。
- 财务复核员:复核金额/币种/收款主体和对账异常,确认或拒绝处置。
- 财务负责人/CFO查看已匹配、待对账、失败、退款和未入账金额。
- 平台管理员:配置连接器公钥/密钥版本、来源白名单和健康状态,但不能代替财务确认业务结果。
- 外部连接器:按租户和来源签名推送支付、银行或 ERP 事件,安全重试并读取幂等结果。
## 功能能力
- 连接器注册与密钥版本:来源、环境、允许事件、时钟偏差、启停和轮换状态。
- 统一事件信封tenant、provider、event ID、event type、occurred at、payload hash、correlation、signature version。
- 支付批次与回执:批次创建、提交、受理、成功、失败和部分成功。
- ERP 入账:凭证号、会计期间、入账时间、受控摘要和原始内容哈希。
- 对账:按单据、金额、币种和外部引用自动匹配;差异进入人工处置。
- 冲回:退款、撤销和补付使用新事件,不更新或删除原事件。
- 可观测性:最近成功时间、失败率、重试次数、积压、签名失败和对账差异。
## 方案设计
### 模块职责
- `financial_connector_auth`:验证来源、签名、时间戳、密钥版本和重放窗口。
- `financial_connector_ingestion`:规范化事件、计算指纹、幂等写入、生产/模拟分流和冲突检测。
- `financial_connector_simulation`:只读校验 test/mock/staging 回执,不创建或修改 Claim、对账、ERP、Business Event 或 Savings。
- `financial_connector_mock_adapter`:平台管理员显式触发的非生产场景适配器;用已激活配置的真实签名链确定性生成成功、失败、乱序、重复、冲突、退款和 ERP 回执,但不提供任意 payload 注入能力。
- `financial_connector_observability`:按租户和配置聚合最近成功、失败率、重试、积压、签名失败、冲突和对账异常;只读取最小化事实与追加式运行事件。
- `financial_connector_payment_evidence`:从 Claim 的付款事实中提取统一证据 DTO明确区分 production-mode 外部回执分类、非生产模拟回执和人工付款内部状态;分类本身不证明真实 provider 已接通。
- `financial_connector_config_lifecycle`:执行带 expected version 的激活、停用和原子密钥轮换。
- `payment_reconciliation`:匹配 Claim、金额、币种和状态生成 matched / exception 结果。
- `financial_connector_actions`:在可信匹配后调用现有付款动作;支付失败、退款和 ERP 入账分别旁写事件。
- `financial_connector_projection`:为财务工作台提供脱敏列表、详情、差异和健康度。
- 供应商 adapter 只负责供应商字段映射,不直接修改 Claim、Savings 或预算。
### 数据与契约
首期新增以下 migration-owned 表,均带 `tenant_id`
- `financial_connector_configs`provider、environment、allowed event types、secret/key version、status、last success/error只保存密钥引用或不可逆验证材料。
- `financial_connector_config_events`created、activated、disabled、rotation started/replacement created 的追加式配置审计;保存 actor、request、reason、expected version 和脱敏前后状态,不保存 `secret_ref`
- `financial_connector_events`:方向、事件类型、外部事件 ID、请求指纹、原始内容哈希、发生/接收时间、验证等级、处理状态、关联单据/Case 和错误码UPDATE/DELETE 禁止。
- `payment_reconciliation_cases`Claim、期望/实际金额与币种、匹配状态、差异、处置版本、负责人和最后事件;作为可变投影,历史动作另存事件。
- `payment_reconciliation_events`:创建、自动匹配、人工确认、拒绝、重开、退款和关闭的追加式审计事件,保存请求指纹和首次响应。
- `financial_connector_operational_events``0022`):保存 `replay / auth_failure / payload_conflict` 三类运行事实的 tenant/config/provider/environment、受控原因码、两类 HMAC 指纹、幂等键和发生时间;不保存原始 payload、签名、外部事件 ID、Claim 引用、correlation 或密钥材料。复合租户外键防止跨租户归属PostgreSQL 触发器禁止 UPDATE/DELETE存在运行事实时拒绝有损降级。
关键约束:
- `(tenant_id, provider, external_event_id)` 唯一。
- 配置从 `disabled/version=1` 创建;激活和停用必须命中 expected version轮换原子地产生新 active key version 并把旧版本置为 rotating。
- 同幂等键不同 payload hash 返回冲突,不能覆盖首次事件。
- 同一个运行事实 candidate 的补偿写入按 `(tenant_id, idempotency_key)` 幂等;不同 HTTP 尝试即使请求内容相同,也因可信发生时间不同而分别计数,避免把真实重放次数永久压成一次。
- Claim、Case、配置和对账记录使用复合租户外键。
- 退款/冲回必须引用同租户原结算事件。
- 生产事件必须通过已激活密钥验证test/mock/staging 即使签名和业务字段全部匹配,也只能生成 `projection_scope=simulation_only` 的连接器事实,绝不进入核心财务状态机。
### 接口
- `POST /api/v1/integrations/financial-events`:连接器签名事件入口;返回稳定接收/重放结果。
- `POST /api/v1/financial-connectors/admin/tenants/{tenant}/configs/{id}/activate|disable|rotate`带版本、actor、request ID 和 reason 的配置状态机。
- `POST /api/v1/financial-connectors/admin/tenants/{tenant}/configs/{id}/simulate`平台管理员运行确定性非生产场景production 配置和未激活配置 fail-closed。
- `GET /api/v1/financial-connectors/admin/tenants/{tenant}/config-events`:读取不含密钥材料的配置审计时间线。
- `GET /api/v1/financial-connectors/admin/tenants/{tenant}/observability`:平台管理员读取目标租户脱敏运行指标;从 config/event/reconciliation/operational event 聚合指定窗口真实值。
- `GET /api/v1/financial-connectors/observability`:财务角色读取当前租户脱敏运行指标;返回 `window_started_at / as_of / generated_at / source_revision`,以及 replay、认证失败、签名失败、payload conflict 的真实计数与最近发生时间。`0022` 起四类指标均标记 `available`,没有事实时真实返回零而不是“待采集”占位。
- `GET /api/v1/financial-connectors/payment-evidence/{claim}`:有单据读取权限的当前租户用户读取付款证据等级,不返回原始连接器内容。
- `GET /api/v1/financial-reconciliation/cases`:财务角色分页查看匹配与异常。
- `GET /api/v1/financial-reconciliation/cases/{id}`:查看脱敏证据和追加式时间线。
- `POST /api/v1/financial-reconciliation/cases/{id}/confirm`:独立财务确认差异或人工匹配。
- `POST /api/v1/financial-reconciliation/cases/{id}/reject`:拒绝错误回执并记录原因。
- 连接器配置管理接口仅平台管理员可用,业务确认接口仅财务角色可用,两类权限不互相继承。
### 匹配算法
1. 验证 tenant/provider/key version/timestamp/signature 和事件类型HMAC canonical request 同时绑定固定 HTTP method/path禁止共享密钥跨 provider 或 key version 重放。
2. 以 canonical JSON 生成 payload hash按外部事件 ID 与幂等键检查首次请求。
3. 通过受控引用解析 Claim不允许仅凭模糊姓名或备注自动匹配。
4. 校验 Claim 已完成审批且处于待付款;比较金额、币种、外部业务引用和事件方向。
5. 只有 `production_verified` 且完全一致时才自动 matched非生产来源只生成模拟投影任何生产差异进入 exception 且不改变 Claim。
6. matched 事件在同事务调用付款动作、记录 Business Event并把外部事件哈希作为 Savings 证据。
7. ERP posted 只表示入账不重复触发付款refund/reversal 追加负向业务与 Savings 冲回候选。
### 权限与安全
- 连接器入口不使用普通用户会话,使用租户绑定的签名认证;普通 Bearer token 不能伪装连接器。
- HMAC/签名比较使用常量时间函数,限制时间窗口并记录 nonce/外部事件 ID 防重放。
- 激活和轮换前由服务端解析 `secret_ref` 并校验至少 128-bit HMAC 密钥;数据库和审计 DTO 均不返回引用或明文。
- 日志、响应和 DTO 最多暴露外部引用后八位或不可逆摘要,不返回原始 payload、签名或密钥。
- replay 随成功重放事务提交;认证失败与 payload 冲突先回滚失败业务事务,再独立提交脱敏运行事实,审计写入异常不得覆盖原始 401/409 响应。
- 只有服务端按 tenant/provider/key version 解析出 active 配置,并成功解析该配置的服务端密钥后,才构造可归属的 operational context。缺认证头、请求 tenant 不一致、未知/未激活配置或密钥不可用均不接受客户端自报租户,只写不带租户归属的结构化警告;签名、时间窗和事件白名单失败才可安全归入已解析配置。
- 运行表只保存 `hmac-sha256:` 请求/外部事件指纹和 `sha256:` 幂等键。HMAC 输入可以包含外部 ID 和业务引用但这些原值不会进入表、DTO 或错误日志。
- 不可变连接器事实的 normalized payload 不再保存完整 `claim_reference``0020` 受控迁移会删除历史冗余值,保留内容哈希、受控 Claim ID 与必要尾号。
- 跨租户资源统一按 404 隐藏;配置管理员不能确认对账,付款申请人不能确认自己的异常。
- 连接器故障、未知密钥、签名异常、金额/币种不一致和数据库异常均 fail-closed。
### 状态转换
- 连接器事件:`received → verified → processed`,失败进入 `rejected`;事实本身追加只读。
- 连接器配置:`disabled → active → rotating → disabled`;正常启用走 `disabled → active`,轮换时旧 active 原子进入 rotating、新 key version 以 active 创建。
- 对账记录:`pending → matched | exception → confirmed | rejected`;退款可从 confirmed 进入 `reopened`,重新处置后关闭。
- Claim 仅在可信 `payment_settled + matched` 后从 `pending_payment` 进入 `paid`
- ERP 凭证从 `pending_posting` 进入 `posted | posting_failed`,不反向伪造支付成功。
### 降级策略
- 连接器离线:保留待付款,不自动标记已付;显示积压和最后成功时间。
- 回执乱序:先保存事实,等待前置事件或进入 pending不猜测状态。
- 回执冲突:保留首次事实并返回 409生成对账异常。
- ERP 未接入:付款事实可进入已付,但“已入账/凭证号”保持待采集。
- test/mock/staging返回 `simulation_only`,只保留追加式 connector fact/response projection不创建对账 Case、不修改 Claim/ERP/归档/Savings也不进入生产现金证明。
- 显式 mock adapter 只能选择预定义场景和当前租户 Claim事件 ID、correlation 和受控外部引用由 tenant/config/scenario/request ID 确定性派生。重复执行同一请求只产生稳定重放,不能借模拟接口注入生产配置或任意字段。
- 升级前若已有非生产事件且响应曾关联对账 Case`0020` 将其标记为 `legacy_nonproduction_effect_unknown` 供审计,不伪装成新策略下的无副作用模拟事实,也不在迁移中猜测性冲回历史财务状态。
### 兼容策略
- 保留现有人工“确认已付款”作为低等级内部证据;付款证据 DTO 使用 `internal_manual_payment`,生产连接器使用 `external_cash`,非生产连接器使用 `simulated_connector`/`staging_connector`。UI 必须同时展示来源标签和可信等级,不能只显示“已付款”。
- 新连接器路径复用现有幂等付款动作、Case 时间线和 Savings 实现服务,不复制第二套状态机。
- 现有 `risk_flags_json` 付款摘要继续只读兼容,新连接器事实进入正式事件表。
## 测试方案
- 单元签名、时间窗口、canonical hash、幂等重放、冲突、乱序和字段白名单。
- 权限:跨租户、普通用户伪造、管理员越权、申请人自证和密钥停用。
- PostgreSQL复合外键、唯一键、append-only、并发同事件、不同 payload 冲突和安全降级。
- 集成:申请 → 票据 → 报销 → 预审 → 审批 → 外部付款 → 对账 → ERP 入账 → 归档 → Savings 待确认。
- 反向:支付失败不推进、金额/币种错配不推进、重复回执只写一次、退款追加冲回。
- adapter逐场景验证确定性结果、重复请求、跨租户 Claim 隐藏、production 拒绝,以及 Claim/对账/ERP/Business Event/Savings 零副作用。
- 可观测性:验证租户隔离、财务/管理员权限、窗口边界、签名失败与重放计数,并断言 DTO/日志无原始 payload、签名和密钥。
- 运行事件:验证同 candidate 并发/补偿重试单赢家、不同请求尝试分别计数、冲突回滚后独立持久化、复合租户外键、HMAC 格式检查和数据库 append-only。
- 所有验证只在 `local-x-financial-linux` 容器内执行,每条命令最长 60 秒。
## 算法与公式
本能力不做概率预测,核心是确定性门禁:
```text
canonical_effect_allowed = (
verification_level == production_verified
AND signature_valid
AND tenant_provider_key_path_bound
AND claim_amount_currency_reference_match
)
```
任一条件为假都不得产生核心财务副作用;非生产环境无论其他条件是否为真,`canonical_effect_allowed` 固定为 false。
运行指标使用确定性窗口聚合,不从应用日志估算:
```text
operational_count(type, window) = COUNT(
tenant_id = current_tenant
AND event_type = type
AND window_started_at <= occurred_at <= as_of
)
operational_idempotency_key = SHA256(
source_revision, tenant, config, type, reason,
HMAC(request), HMAC(external_event), occurred_at
)
```
`source_revision=20260716_0022` 表示当前运行指标的数据源与聚合契约版本;它不是 provider 协议版本。发生时间属于本次接收尝试,因此同一 candidate 重试仍稳定,而新尝试会形成新的真实计数。
## 指标与验收
- 100% 外部结算事件具有租户、来源、签名版本、payload hash、外部 ID 和接收时间。
- 重复相同事件稳定重放,冲突 payload 100% 拒绝。
- 任何金额/币种/Claim/审批状态不一致均不会推进已付款。
- 外部支付、ERP 凭证、对账处置、归档和 Savings 证据可由 correlation 链回放。
- 连接器离线或异常时不出现虚假“已付款”“已入账”或现金节省。
- observability 响应 100% 标明窗口起止和 source revision三类运行事实按租户真实计数并提供各类最近发生时间。
## 风险与开放问题
- 首期真实 provider、签名算法、事件字段和 SLA 需要目标客户确认。
- 部分 ERP 只有批次级凭证,需要明确批次到单据的拆分和舍入规则。
- 多币种付款需要锁定汇率来源和会计期间;本功能不自行猜测汇率。
- 退款、补付、员工自担调整是否进入现金节省,仍需客户财务签字口径。
- 对账大额阈值及双人复核需按企业策略配置。
## 本轮实现记录
- 2026-07-16完成现有内部付款、申请归档、Business Event、Savings 实现链路盘点,并冻结统一连接器、安全认证、对账状态和完整 E2E 方案;实现与容器证据保留在同目录 TODO 中继续执行。
- 2026-07-16完成 `0017` 四表迁移、HMAC/密钥版本/时间窗认证、租户隔离、追加式事件、幂等冲突和对账投影;一次性 PostgreSQL 17 迁移循环与 2 项并发探针通过。
- 2026-07-16支付结算只在金额、币种、Claim、审批状态与来源全部匹配时复用正式付款动作ERP、失败、退款/冲回、Savings 失效和财务处置均进入同一 correlation 审计链,连接器服务/HTTP 7 项通过。
- 2026-07-16真实 provider、批次拆分、汇率、会计期间和大额双人复核仍由目标客户确认当前 mock/test 证据始终标记 simulated不冒充生产现金事实。
- 2026-07-16补齐 `0020` 配置版本与追加式审计迁移;新配置只能停用创建,激活前校验服务端密钥,轮换原子切换 key versionHTTP 时间线不暴露 `secret_ref`
- 2026-07-16把 test/mock/staging 六类事件收口到 `simulation_only` 只读投影生产事件继续完成付款、ERP、归档、Savings 和冲回,生产冲回也不能引用模拟原事件。
- 2026-07-16normalized payload 删除完整 `claim_reference`,历史冗余值由 `0020` 受控脱敏HMAC v2 继续以内容哈希和 tenant/provider/key version/path 证明请求边界。
- 2026-07-16容器内连接器/配置/费用价值链组合 16 项、PostgreSQL 并发 3 项、迁移静态 124 项及全新 PostgreSQL 17 完整升降级循环通过;独立 0019→0020 探针确认版本回填、历史脱敏和 legacy 不确定性标记符合契约。
- 2026-07-16新增显式非生产 adapter以 tenant/config/claim/scenario/request ID 派生稳定签名回执,覆盖成功、失败、乱序、重复、冲突、退款和 ERP 场景HTTP 与服务测试确认 simulation-only 且 Claim、对账、Business Event、ERP 与 Savings 零副作用。
- 2026-07-16新增租户级脱敏可观测性 API 与财务看板面板,现有 config/event/reconciliation 表提供最后成功、失败率、积压和对账异常真实值retry/auth_failure 在 `0022` 运行事件落地前明确显示 unavailable不伪造零值。
- 2026-07-16新增付款证据等级 DTO 和 UI 口径,通过 production-mode 签名契约的回执分类为 `external_cash`,人工确认标为低等级 `internal_manual_payment`,模拟/预发布回执明确不进入核心账;当前本地自签测试不作为真实现金或 provider 接通证据。
- 2026-07-17完成 `0022` 追加式运行事实迁移与服务接入,耐久记录 replay、可信归属后的 auth failure 和 payload conflict请求与外部事件只保存 HMAC 指纹,失败事务回滚后独立补偿提交。
- 2026-07-17可观测性改为返回 `20260716_0022` source revision、明确窗口、真实计数和最近时间同一 candidate 补偿重试幂等,不同 HTTP 尝试分别计数。迁移总头继续串到既有 `0023``0022` 不越界创建后继 AI 表。
- 2026-07-17全新一次性 PostgreSQL 17 完整迁移循环 51 项、连接器并发 4 项、后继盲审并发 7 项通过验证租户复合外键、HMAC 格式、运行事实 append-only 和并发单赢家。

View File

@@ -0,0 +1,76 @@
# 财务连接器与支付对账闭环 TODO
更新时间2026-07-17
## 使用规则
- 每项必须回链 `CONCEPT.md`;只有代码、迁移、接口或容器验证提供证据后才能勾选。
- 外部回执、内部付款状态、ERP 入账和财务确认必须分开mock 不得伪装成生产现金事实。
## 1. 契约与安全
- [x] [CONCEPT: 背景与问题] 盘点内部付款、申请归档、Business Event、Savings 实现和证据边界。
证据:`expense_claim_approval_flow.py``expense_claim_application_handoff.py``expense_cases.py``savings_realization.py` 只读审计。
- [x] [CONCEPT: 目标与非目标] 冻结签名事件、幂等、对账、ERP 凭证、冲回和 mock 环境边界。
证据:`CONCEPT.md`“目标与非目标”“数据与契约”“匹配算法”“降级策略”。
- [x] [CONCEPT: 权限与安全] 实现连接器签名认证、密钥版本、时间窗口、来源白名单和防重放。
证据:`financial_connector_auth.py``financial_connector_ingestion.py`HMAC 使用常量时间比较tenant/provider/key version/timestamp/method/path 均进入签名边界,共享密钥跨 provider/key version 重放被拒绝,相同事件幂等重放、冲突 payload 返回 409。
- [x] [CONCEPT: 权限与安全] 实现平台配置权限与财务处置权限分离、跨租户 404 和申请人自证拒绝。
证据:`financial_connectors.py``financial_connector_projection.py`HTTP/服务回归覆盖普通用户、平台管理员、财务角色、跨租户隐藏与申请人自证拒绝。
## 2. 数据与迁移
- [x] [CONCEPT: 数据与契约] 新增配置、外部事件、对账投影和对账事件模型。
证据:`models/financial_connector.py``schemas/financial_connector.py`
- [x] [CONCEPT: 数据与契约] 新增后继 Alembic 迁移、迁移所有权、复合租户外键、唯一/检查约束和 append-only 触发器。
证据:`20260716_0017_financial_connector_reconciliation.py``schema_ownership.py``migration_preflight.py`;一次性 PostgreSQL 17 完整迁移循环通过。
- [x] [CONCEPT: 数据与契约] 实现外部事件与退款/冲回引用、首次响应和 payload 指纹冲突。
证据:`financial_connector_ingestion.py``payment_reconciliation.py`;同外部事件不同 payload 拒绝,退款/冲回必须绑定同租户已处理结算原事件。
- [x] [CONCEPT: 数据与契约] 新增配置 version、追加式配置审计和历史 normalized payload 脱敏迁移。
证据:`20260716_0020_financial_connector_config_lifecycle.py``FinancialConnectorConfigEvent`;配置审计使用 PostgreSQL append-only trigger历史 `claim_reference` 从不可变事件的 normalized payload 中受控移除,内容哈希继续保留;全新 PostgreSQL 17 完整升降级循环 1 项通过,另一个独立库验证 0019→0020 历史数据脱敏与 legacy 标识。
## 3. 服务与接口
- [x] [CONCEPT: 模块职责] 拆分认证、ingestion、reconciliation、action 和 projection 服务,核心文件不超过 800 行。
证据:`financial_connector_auth.py``financial_connector_ingestion.py``payment_reconciliation.py``financial_connector_actions.py``financial_connector_projection.py` 职责独立,最大核心文件低于 800 行。
- [x] [CONCEPT: 接口] 实现统一事件入口、连接器配置、对账列表/详情、确认和拒绝接口。
证据:`api/v1/endpoints/financial_connectors.py`
- [x] [CONCEPT: 接口] 实现带 expected version、actor、request ID、reason 的 activate/disable/rotate 状态机与脱敏审计查询。
证据:`financial_connector_config_lifecycle.py``financial_connector_config_audit.py``FinancialConnectorConfigLifecycleAction``FinancialConnectorConfigRotateAction`;激活/轮换前解析服务端密钥并校验强度,轮换原子切换新旧 key version。
- [x] [CONCEPT: 匹配算法] 完全匹配时复用现有幂等付款动作;任何金额、币种、单据或审批状态差异不产生付款副作用。
证据:`FinancialConnectorActionService` 复用 `ExpenseClaimService.mark_claim_paid_from_connector()`;反向测试验证 mismatch/failure/conflict 无付款副作用。
- [x] [CONCEPT: 证据与审计] 把外部事件哈希写入 Expense Case/Business Event/Savings 证据 correlation 链。
证据连接器结算动作写入脱敏内容哈希、verification/evidence classification 与 correlation服务 E2E 可回放付款、Case、Business Event 和 Savings evidence。
- [x] [CONCEPT: 状态转换] 实现支付失败、ERP posted/posting_failed、退款/冲回和对账重开。
证据:`PaymentReconciliationService` 对六类生产事件分流ERP 不重复付款,生产退款/冲回恢复 Claim 并追加 Savings 冲回事实。
- [x] [CONCEPT: 降级策略] test/mock/staging 六类事件只写 simulation-only connector fact/response projection不修改核心财务状态。
证据:`financial_connector_simulation.py``financial_connector_ingestion.py``test_financial_connector_services.py` 参数化覆盖三类非生产环境和六类事件Claim、申请归档、对账、Business Event、ERP 与 Savings 均保持不变。
## 4. Mock 与可观测性
- [x] [CONCEPT: 降级策略] 实现明确标识 test/mock 的 adapter覆盖成功、失败、乱序、重复、冲突、退款和 ERP 回执。
证据:`financial_connector_mock_adapter.py``FinancialConnectorSimulationCreate/Read` 与平台管理员 simulate API仅 active test/mock/staging 可运行tenant/config/claim/scenario/request ID 确定性派生事件production、disabled 和跨租户请求 fail-closed7 场景服务/HTTP 回归通过。
- [x] [CONCEPT: 可观测性] 从现有事实输出最后成功时间、失败率、积压和对账异常,并在安全 DTO/UI 声明未采集指标。
证据:`financial_connector_observability.py`、当前租户/管理员 observability API、`FinancialConnectorHealthPanel.vue`;所有查询先绑定 tenant仅聚合 config/event/reconciliation 最小化事实,不返回原始 payload、签名和密钥。
- [x] [CONCEPT: 可观测性] 用后继 `0022` 追加式运行事件补齐 replay、auth_failure 和 payload conflict 耐久计数。
证据:`20260716_0022_financial_connector_operational_events.py``financial_connector_operational_events.py``financial_connector_auth.py``financial_connector_ingestion.py``financial_connector_observability.py`;只有可信配置与服务端密钥解析后才归属认证失败,表内只保存 HMAC 指纹;同 candidate 重试幂等、不同接收尝试分别计数API 返回窗口、source revision、真实数量和最近时间。
- [x] [CONCEPT: 兼容策略] 保留人工付款为低等级内部证据,并在 DTO/UI 区分外部回执和内部确认。
证据:`financial_connector_payment_evidence.py`、payment-evidence API、`FinancialPaymentEvidenceRead` 与面板证据口径;人工付款=`internal_manual_payment`,通过 production-mode 契约验证的外部回执分类=`external_cash`,非生产回执=`simulated_connector|staging_connector`。真实 provider 仍待联调。
## 5. 测试与验收
- [x] [CONCEPT: 测试方案] 签名、防重放、幂等、冲突、字段白名单和错误恢复单元测试通过。
证据:`test_financial_connector_services.py``test_financial_connector_endpoints.py``test_financial_connector_config_lifecycle.py` 与费用价值链 E2E 共 16 项通过;乱序原事件缺失保守进入 exception不推进付款。
- [x] [CONCEPT: 测试方案] PostgreSQL 迁移、复合租户约束、append-only、并发和安全降级验证通过。
证据fresh PostgreSQL 17 最终迁移总探针 62 项通过;`financial_connector_migration_assertions.py` 验证配置/事件/运行事实 trigger、租户约束、HMAC 格式和 version check`test_financial_connector_concurrency_postgres.py` 4 项通过,覆盖同事件、冲突补偿事实、配置单版本胜者和 operational candidate 单赢家;最终 head 为 `20260717_0028`
- [x] [CONCEPT: 测试方案] 申请 → 票据 → 报销 → 预审 → 审批 → 外部付款 → 对账 → ERP 入账 → 归档端到端通过。
证据:`test_expense_financial_value_chain_e2e.py` 使用测试密钥自签 production-mode HMAC 事件覆盖申请审批、报销审批、ERP 入账、独立财务确认、Savings、商业价值与退款冲回契约与连接器定向组合共 16 项通过,不代表真实 provider 回执或现金。
- [x] [CONCEPT: 测试方案] 支付失败、金额/币种错配、重复回执和退款反向链路通过。
证据:`test_financial_connector_services.py` 覆盖 production-mode 失败/错配无副作用、稳定重放、ERP、reversal/refund 追加 Savings 冲回,以及非生产回执完全不创建 Savings。
- [x] [CONCEPT: 容器验证] 相关 pytest、Ruff、前端测试和构建均在 `local-x-financial-linux` 内通过。
证据:历史连接器/配置/费用价值链/迁移组合 `140 passed, 1 skipped`adapter/观测/证据 DTO 与既有连接器回归 `21 passed, 3 skipped`。2026-07-17 的 `0022` 收尾在容器内新增/定向回归 `115 passed`,全新 PostgreSQL 17 完整迁移循环 `51 passed`,连接器并发 `4 passed`,后继 `0023` 并发 `7 passed`;相关 Ruff、文件行数和全树 `git diff --check` 通过。历史连接器前端 3 项与 Vite 生产构建已通过;共享前端曾有 5 项旧路径测试失败,已单独记录且不属于本切片。
## 6. 客户配置待确认
- [ ] [CONCEPT: 风险与开放问题] 确认首个 provider、签名算法、字段映射、事件 SLA 和重试窗口。
- [ ] [CONCEPT: 风险与开放问题] 确认批次到单据映射、多币种汇率、会计期间、大额双人复核和退款口径。

View File

@@ -0,0 +1,373 @@
# 节省事实账本与 CFO 经营价值看板 概念文档
更新时间2026-07-17
## 功能一句话
把费用优化从“发现风险和预计能省”推进为“执行、实际结果、独立财务确认、可回放冲回”的事实账本,并让 CFO 只看到有来源、有基线、有证据、可去重的企业价值。
## 背景与问题
- 现有财务看板能够回答支出、单量、待付款、预算使用和风险分布,但不能可信回答企业已经节省了多少钱。
- 风险观察金额、暂缓付款、未使用预算和未执行建议都不是现金节省;如果直接汇总,会形成虚假 ROI。
- 当前最接近真实节省的链路是“住宿超标准 → 用户接受职级标准重算 → 审批 → 付款”。它已经真实降低报销金额,但尚未形成独立机会、付款后结果、财务签字、去重和冲回记录。
- `ExpenseClaim`、预算和应付旧表没有结构化租户键Claim 只能通过 `ExpenseCaseLink` 判断租户;旧 `FinanceDashboardService` 仍有全表读取和跨租户快照复用风险,不能作为客户 ROI 的事实源。
- 现有 `accept_standard_adjustment` 优先使用客户端传入的原金额,客户端理论上可以放大差额;任何节省计算必须改为只使用服务端锁定的明细金额和政策计算快照。
- 当前“已付款”是财务人员在系统内确认的业务状态,尚没有银行流水、支付回执或 ERP 凭证。因此付款只能把机会推进到“实际结果待确认”,不能直接进入财务确认 KPI。
- 报销提交时间到同租户首个 `payment_completed` 业务事件可以形成可审计的端到端流程周期,但它包含等待与系统处理,不是人工活跃工时;在没有人工计时基线、角色成本和客户认可释放比例前,工时价值必须显示“待采集”,不能伪造为 0 或现金节省。
本方案是 `2026-07-13/feature/ai-expense-closed-loop-and-value-proof` 中 P2“费用经营与价值证明”的实施拆分。
## 目标与非目标
### 目标
- [G1] 建立租户安全的 Savings Ledger完整区分风险暴露、预计机会、执行中、实际结果、财务确认和冲回。
- [G2] 建立不可变基线和证据链,所有金额都能追溯到费用事件、单据明细、政策版本、执行动作、付款事件和确认人。
- [G3] 建立经济收益去重键和归因约束,避免同一单据、付款义务或政策差额被多个风险重复计入。
- [G4] 建立严格状态机、乐观版本、幂等响应和数据库并发约束,防止重复确认、陈旧操作和跨租户访问。
- [G5] 建立 CFO 价值看板,分开展示财务确认现金节省、可释放工时价值、安全智能直通率、经营漏斗和风险护栏。
- [G6] 支持部门、项目、费用类型、供应商、城市、时间、负责人、来源和单据下钻,并显示数据覆盖、口径和新鲜度。
- [G7] 持久化费用基线快照,记录窗口、样本量、算法版本、政策版本、来源指纹和数据质量。
- [G8] 修复旧财务聚合与快照的租户边界,禁止真实接口失败时回退成看似真实的演示数字。
### 非目标
- [NG1] 不把风险关联金额、暂缓付款金额、未采纳建议、未使用预算或预计金额计入已确认节省。
- [NG2] 首个切片不宣称已具备外部银行或 ERP 付款凭证;后续通过连接器补齐。
- [NG3] 首个切片不使用缺少租户、合同价、采购数量和付款凭证的 `AccountsPayableRecord` 计算供应商节省。
- [NG4] 不把申请金额与最终报销差额默认归因给 AI缺少具体 AI 决策、采纳动作和结果链时AI 归因金额为 0。
- [NG5] 不把流程经过时长换算为人工工时,不直接暴露个人薪酬或个人成本。
- [NG6] 不跨币种直接求和;没有锁定汇率的金额只按原币展示并进入数据质量提醒。
- [NG7] 不删除、覆盖已确认收益;补付、退款、申诉或归因修正使用追加负向冲回事件。
- [NG8] 不在本阶段重写整个 Overview也不把不可信的预算中心模拟数据接入价值看板。
## 用户与场景
### 目标用户
1. CFO/管理层:查看企业已经确认的现金价值、价值兑现速度和风险护栏。
2. 财务运营:复核机会、实际结果、重复归因、凭证和冲回事项。
3. 费用治理负责人:领取机会、执行动作、补充结果和跟进逾期。
4. 预算负责人:只在授权部门或成本中心范围内查看机会与驱动。
5. 审计/风控:回放基线、政策、执行、付款、确认、冲回和操作事件。
6. 普通员工:仅在自己的费用事件中看到与本人相关的调整说明,不访问企业 CFO 汇总。
### 核心场景
1. 员工接受住宿职级标准重算。服务端锁定明细原金额、城市、天数、职级、政策版本和可报销上限,同事务生成唯一节省机会。
2. 机会进入执行后仍只展示预计金额;审批未通过、单据取消或超过期限时保留失败/到期事实,不从兑现率分母中消失。
3. 单据完成付款业务事件后,系统根据冻结差额记录实际结果,但不进入财务确认 KPI。
4. 与机会负责人和结果填报人不同的财务人员检查证据、去重、币种和成本后确认;确认后才计入 CFO 现金节省。
5. 后续发生例外补付或申诉时,追加负向冲回并保留原确认,历史月报按报告 `as_of` 可回放。
6. CFO 从价值总览下钻到部门、项目、费用类型、城市、负责人和具体单据,查看基线、建议、执行、实际、确认人和证据。
7. 费用治理负责人查看异常集中维度和只读政策模拟准备项;历史中位数只能作为异常信号,缺少正式政策反事实时不显示预计节省,也不自动创建机会。
### 异常场景
- 服务端政策无法计算、明细金额缺失或差额不为正:原报销流程可继续,但不创建可货币化节省机会。
- Claim 没有合法 `ExpenseCaseLink` 或租户不一致fail-closed不自动归入默认租户。
- 相同请求重复发送:返回首次不可变响应;相同请求 ID 内容不同409 拒绝。
- 陈旧版本、重复付款事件或重复经济收益:通过版本锁、事件唯一键和收益去重键拒绝。
- 缺少付款/凭证、汇率、独立确认或证据不完整:停留在实际待确认,不进入主 KPI。
- 看板接口失败、无权限、无数据、基线不足或快照过期:分别展示明确状态,绝不使用模拟数字伪装真实指标。
## 功能能力
- [C1] 机会发现:从服务端核验的政策调整、后续分析洞察或风险复核创建机会。
- [C2] 状态管理:支持 identified、accepted、in_progress、realized、verified、rejected、expired 和 reversed 事实。
- [C3] 实现记录:保存实际毛收益、新增执行成本、净收益、发生时间、币种和结果证据。
- [C4] 财务确认:独立确认人复核去重、证据、汇率、成本和归因后签字。
- [C5] 证据与审计只追加事件、内容指纹、before/after、首次响应和 correlation 全链路回放。
- [C6] 基线快照:按员工、部门、费用类型、供应商、城市、项目和流程持久化窗口、样本量和版本。
- [C7] 价值分析:经营漏斗、兑现率、周期、逾期、来源、责任人和数据质量。
- [C8] CFO 看板真实指标、全局筛选、URL 恢复、下钻、移动端和口径抽屉。
- [C9] 安全边界:租户、角色、数据范围、自证禁止、管理员业务权限分离和字段白名单。
- [C10] 冲回能力:补付、退款、申诉或归因修正只能追加负向记录,不改历史。
## 方案设计
### 前端
- 在现有“分析看板”增加 `value` / “经营价值看板”,复用统一时间筛选,不新增一级导航。
- `OverviewView.vue` 只负责挂载独立 `CfoValueDashboard.vue`;价值加载、筛选和展示模型拆到 `useCfoValueDashboard.js``cfoValueDashboardModel.js``analyticsValue.js`,避免继续扩大接近 800 行的 `useOverviewView.js`
- 默认视图从上到下为:主 KPI 与护栏、价值漏斗、现金节省趋势、来源/组织驱动、机会执行表、数据质量和口径说明。
- 全局筛选只保留时间、部门、费用类型和价值类型;项目、供应商、城市、负责人、状态和置信度进入高级筛选。
- 看板状态同步到 URL query当前机会使用 `value_opportunity` 保存,刷新、浏览器前进/后退和分享链接能够恢复同一抽屉。非法 ID、403/404、跨租户不可见或不再符合当前筛选/时间窗口的机会会 fail-closed 清理,避免残留上一租户详情。
- 机会详情展示基线、建议、执行、实际结果、财务确认、去重与证据时间线;证据只使用服务端可见性 DTO。来源动作由独立 helper 根据 Claim、Expense Case、AI Decision、维度和 Evidence Resource 构造,不在抽屉组件内拼接路由规则。
- 单据来源进入 `app-document-detail`;风险来源优先进入关联单据,并只携带风险 focus、观察/决策 ID 与现有锚点。详情返回动作恢复 `dashboard=value`、时间窗口和 `value_*` 查询。
- 预算来源进入 `app-budget` 的“预算配置视图”,按授权范围应用部门和费用类型焦点;页面明确说明配置、阈值及当前演示金额不是该机会的真实预算事实。未配置的费用科目显示“未找到配置”,不得解释为预算为零。
- 部门、项目、费用类型、供应商、城市、负责人和来源维度可返回 CFO 看板相应筛选;切换维度时移除旧机会 ID避免筛选与抽屉详情不一致。
- 实际结果登记必须具备真实付款事件或可追溯外部凭证;外部凭证上传/连接器尚未接入时,前端隐藏无证据手工登记并解释下一步,不发送必然失败或可能污染价值账本的空证据请求。
- 真实为 0、无数据、基线不足、无权限、接口失败、部分数据和快照过期使用不同状态。
- 禁止复用 `data/metrics.js``BudgetCenterView` 静态种子或遗留 `demoTotals` 作为 CFO 真实回退。
### 后端
- `SavingsDiscoveryService` 只负责从可信业务事实发现/创建机会,不提交事务。
- `SavingsActionService` 负责机会状态动作、版本、权限、幂等和事件。
- `SavingsRealizationService` 负责付款后实际结果、财务确认、拒绝和冲回。
- `SavingsQueryService` 负责租户安全分页、详情和可见动作投影。
- `SavingsFactScopeReader` 只读取当前租户与授权部门中的已归档报销事实,并以报告窗口和 `as_of` 排除未来单据、修改和完成事件。
- `SavingsBaselineGenerationService` 分开冻结金额中位数与流程历时中位数;流程只使用提交时间和首个付款完成业务事件,指标固定为 elapsed minutes。
- `SavingsAnomalyAttributionAnalyzer` 只生成描述性异常集中归因和政策模拟准备项,不声称因果,不写 `SavingsOpportunity`
- `CfoValueAnalyticsService` 只从 Savings Ledger、风险事实和明确资格快照聚合不从 UI mock 或风险金额推导节省。
- 标准重算只使用数据库行锁中的 `ExpenseClaimItem.item_amount` 作为原金额;客户端原金额和可报销金额只可作为展示输入,不能成为节省事实。
- 标准重算在同一事务内写 Claim 调整、机会、证据、Savings 事件和 `saving_opportunity_created` 业务事件API 边界统一提交。
- 付款动作在 Claim → Opportunity 的固定锁顺序中创建 actual realization 和 `saving_action_completed`,与 `payment_completed` 同事务。
- 财务确认写 `saving_confirmed`,拒绝和冲回写对应只追加事件;相同请求安全重放。
-`/analytics/finance-dashboard` 必须接收可信 `CurrentUserContext`Claim 通过 `ExpenseCaseLink` 限定租户;快照键至少包含租户与数据权限范围,后台任务必须显式指定租户。
### 算法与规则
#### 第一条可信机会
```text
server_original_amount = locked ExpenseClaimItem.item_amount
policy_target_amount = server policy calculator result
estimated_net_saving = max(0, server_original_amount - policy_target_amount)
```
- 仅当政策计算成功、输入快照完整、币种一致、差额大于 0 时创建可货币化机会。
- 机会唯一键首期为 `tenant + claim + item + policy_version + policy_input_fingerprint`
- 接受重算表示建议已采纳,机会进入 `in_progress`;付款完成后进入 `realized`,独立财务确认后进入 `verified`
- 员工自行承担差额同时是员工体验护栏,必须跟踪申诉和例外补付率,防止通过不合理转嫁美化节省。
#### 流程基线、异常归因与政策模拟
```text
workflow_elapsed_minutes
= first_tenant_payment_completed_event.occurred_at - claim.submitted_at
```
- 流程窗口按首个付款完成事件归属;提交时间缺失、完成早于提交、跨租户事件、`as_of` 之后完成或截止后被修改的单据全部排除。
- 流程快照使用 `median_submission_to_payment_elapsed_minutes``minutes` 单位、独立算法版本和来源指纹;证据元数据固定声明 `elapsed_cycle_not_active_labor`
- 异常归因按部门、费用类型、城市和项目聚合质量合格的历史偏离候选,只表示异常集中度,不表示该维度导致支出。
- 政策模拟候选只输出版本化政策、生效期、适用范围、限额和例外规则等必需输入;历史中位数不是政策反事实,缺少正式反事实时 `estimated_savings=None`
- 预算预测复用现有预算分配和核销事实,以 `min(as_of, window_end)` 为截止点;旧预算表没有租户字段时仅允许 default 租户,部门权限优先按稳定部门 ID 收紧。
- 供应商缺少核验 ID、数量和单位价格时继续返回 unavailable不读取 `AccountsPayableRecord` 演示或应付种子。
#### 收益去重
- `benefit_key` 表达同一个经济结果,不表达同一个风险观察。
- 多条风险可指向一个机会;同一发票、付款义务、报销明细或价格变化只能有一个 canonical 确认收益。
- 同一收益多个动作的归因比例之和不得超过 1。
- 确认后大额、超预计、手工基线、缺外部凭证和归因异常进入二次复核或数据质量队列。
#### 状态转换
```text
identified -> accepted -> in_progress -> realized -> verified -> reversed
| | | |
+-------- rejected -------+------------+
+-------- expired --------+
```
- `identified`:冻结基线、方法、价值类型、币种、预计净值、负责人、截止时间、去重键和来源证据。
- `accepted`:负责人明确采纳。
- `in_progress`:保存执行动作、执行人、开始时间和动作证据;预计值不得静默上调。
- `realized`:保存实际结果、净值、发生时间、归因和付款/结果证据,但不计主 KPI。
- `verified`:完成去重、币种、成本、证据和独立财务确认。
- `reversed`:追加负向冲回,原确认不可删除。
- `rejected/expired`:保留失败事实,防止只保留成功机会美化兑现率。
### 数据与契约
#### `profile_baseline_snapshots`
- 租户、基线类型、稳定维度 ID、指标、单位和原币。
- 基线值、窗口开始/结束、样本量、方法、查询指纹和数据质量。
- 算法版本、政策版本、冻结时间/人和有效期。
- 历史群组基线强制窗口与样本量;政策反事实基线强制政策版本、生效区间和目标明细。
- 金额基线按员工、部门、费用类型、城市和项目分组;流程基线按稳定流程键分组,使用独立 metric/unit不能与币种金额比较或求和。
#### `savings_opportunities`
- 租户、费用事件、Claim 软引用、来源类型/ID、类别和价值类型。
- 风险暴露只作护栏;基线、目标、预计毛收益、预计成本、预计净收益和区间分开保存。
- 原币、报告币、负责人、截止时间、状态、版本、`benefit_key` 和去重组。
- 部门、项目、费用类型、供应商、城市和流程维度使用明确快照字段或受控 JSON。
- 唯一约束至少覆盖 `(tenant_id, opportunity_key)`
#### `savings_realizations`
- 租户、机会、费用事件、Claim、BusinessEvent 和实际发生时间。
- 实际毛收益、新增执行成本、实际净收益、原币、报告金额和汇率快照。
- 归因方法/比例、`benefit_key`、去重状态和 canonical realization。
- 财务确认/拒绝/冲回人、时间、说明和证据。
- 只追加金额事实;确认投影可更新,但每次变更必须有不可变事件。
#### `savings_evidence_links` 与 `savings_events`
- 证据保存实体、证据角色、资源类型/ID、来源系统、外部事件 ID、内容哈希、发生/采集时间和验证状态。
- 事件保存动作、请求 ID、操作人、版本、指纹、before/after、首次响应、correlation 和时间。
- PostgreSQL 触发器禁止修改或删除 `savings_events`
### 权限
- `finance``executive` 可读取租户 CFO 汇总;预算负责人仅看被授权部门/成本中心。
- 普通员工、普通经理不得读取 CFO 汇总;只能看到本人费用事件中的最小调整说明。
- 机会接受、拒绝和指派需要 finance/executive 或明确负责人权限。
- 财务确认必须是 finance/executive且不能是机会负责人或实际结果填报人。
- 只有 `admin` 而没有财务角色时允许运维只读,不允许业务确认。
- 所有 API、聚合、快照、导出和后台任务强制 `tenant_id` 与数据范围;不允许默认全表扫描。
- Claim 通过 `ExpenseCaseLink` 校验租户;缺 Link 的非默认历史数据不自动猜测归属。
### 降级策略
- 政策或基线服务失败:不创建货币化机会,原报销主流程保留人工处理。
- 外部付款/ERP 连接器未接入:付款业务事件只能推进到 realized必须人工财务确认。
- 汇率缺失:保留原币明细,不进入跨币种总计。
- 工时基线缺失:显示“待采集”,不显示 0不计扩展 ROI。
- 流程历时可用但活跃工时缺失:只展示 elapsed cycle 驱动指标CFO 工时价值仍保持 collecting。
- CFO 聚合失败:显示错误和重试,不加载演示值;旧财务支出看板独立可用。
- 快照过期:展示过期提示并触发受控刷新,不能跨租户复用旧快照。
## 算法与公式
### 主 KPI 1财务确认净现金节省
```text
verified_net_cash_savings
= sum(actual_gross_saving - incremental_execution_cost + reversal_amount)
where value_kind = cash
and confirmation_status = finance_confirmed
and dedupe_status = canonical
and confirmed_at <= report_as_of
```
-`realized_at` 归属业务期间,按 `confirmed_at` 和报告 `as_of` 保证历史可回放。
- 风险暴露、预计金额、执行中金额和未确认实际金额不得进入。
- 多币种只有存在锁定汇率时才折算;否则按原币分组。
### 主 KPI 2财务确认可释放工时价值
```text
verified_releasable_labor_value
= max(0, baseline_active_minutes_per_unit - actual_active_minutes_per_unit)
* eligible_units
* approved_role_cost_per_minute
* approved_releasable_ratio
```
- 现金与工时分账、分卡、分报告,默认不相加。
- 缺少上线前后活跃分钟、角色完全成本、生效期或客户认可释放比例时不可计算。
### 主 KPI 3安全智能直通率
```text
safe_straight_through_rate
= qualified_completed_cases_without_manual_correction_or_return
and no_major_post_audit_issue
/ eligible_completed_cases_frozen_at_creation
```
- 必须保存 eligibility 快照、策略版本、必要审批完成和事后抽检结果。
### 驱动指标
- 现金兑现率:同一成熟机会队列的财务确认净现金 / 冻结预计净现金。
- 机会到财务确认 P50 天数、逾期负责人占比。
- 提交至首个付款完成的端到端 P50 elapsed minutes它与每单人工活跃分钟分开后者在采集前保持不可用。
- 人工触点、首次提交完整率和 AI 字段采纳率。
### 风险护栏
- 开放且已确认的高危/重大风险暴露,按单据或经济义务去重;它不是节省。
- 重大风险漏检率、事后审计重大问题率、误报率和人工覆盖率。
- 财务确认后冲回率、实际超过预计异常率、去重待复核金额和证据不完整金额。
- 标准重算员工申诉/例外补付率。
## 测试方案
### 后端
- 状态机合法/非法转换、确认人独立性、管理员只读和角色权限。
- 机会/实现/确认/冲回幂等、请求内容冲突、陈旧版本和租户隔离。
- 服务端明细金额锁定、客户端放大原金额无效、政策快照完整性。
- 付款事件重复、经济收益去重、跨币种、成本扣除、冲回和归因上限。
- 看板按租户、时间、部门、项目、费用类型、城市、来源和负责人聚合对账。
- 六维基线验证员工、部门、费用类型、城市、项目和流程的窗口、样本量、算法版本、来源指纹、租户/部门范围与稳定重放。
- 洞察验证预算截止点、描述性归因、政策模拟准备项、供应商 unavailable、所有无反事实候选 `estimated_savings=None` 且不会创建机会。
- 旧财务看板 Claim、预算和缓存键租户隔离回归。
- Alembic 空库升级、重复升级、约束、append-only 触发器、无损降级和 PostgreSQL 并发。
### 前端
- API snake/camel 归一化、部分数据和过期快照。
- verified、realized、estimated 和 risk exposure 严格分区,不得混算。
- loading/error/empty/partial/stale/permission-denied/baseline-missing 状态。
- 时间、部门、费用类型、价值类型筛选与 URL 恢复。
- 机会详情证据链、可用动作、版本冲突、幂等重放和确认反馈。
- 单据、风险、预算和维度下钻参数。
- 机会 `value_opportunity` 的恢复、关闭清理、非法格式、403/404、跨筛选和时间窗口清理。
- 风险来源最小定位参数、单据返回 CFO、预算配置焦点和“非真实预算金额”口径。
- 响应式、键盘操作、44px 触控目标和生产构建。
### 集成
- 住宿超标准 → 服务端重算 → 机会 → 审批 → 付款 → actual realization → 独立财务确认 → CFO 看板。
- 相同重算/付款/确认并发只产生一个经济收益和一条对应版本事件。
- 确认后补付/申诉 → 负向冲回 → 历史报告 `as_of` 可回放。
- 多租户同单号、同员工名、相同 request ID 和快照缓存隔离。
### 容器验证
所有 pytest、Alembic、PostgreSQL 并发、前端测试和构建必须在 `local-x-financial-linux` 容器内完成,单条命令超时不超过 60 秒。
## 指标与验收
- [A1] 任一 verified cash saving 可追溯到费用事件、明细原金额、政策快照、执行动作、付款事件、确认人、去重键和证据。
- [A2] 风险暴露、预计、执行中、实际待确认、财务确认和冲回在 API、数据库和 UI 中均不混算。
- [A3] 客户端伪造原金额、跨租户访问、陈旧版本、自证确认和重复经济收益被服务端拒绝。
- [A4] CFO 三个主 KPI 有口径、时间窗口、来源、新鲜度、数据覆盖和护栏;缺数据时明确“待采集”。
- [A4.1] 流程 elapsed cycle 有独立 metric/unit/算法版本/来源指纹,且不会进入工时价值或现金节省。
- [A5] 价值看板支持部门、项目、费用类型、供应商、城市、时间、负责人、来源和单据下钻。
- [A6] 真实接口失败不出现演示数字;零值、无数据、无权限、错误和过期可区分。
- [A7] 迁移在一次性 PostgreSQL 空库完成升级、重复升级、约束验证和安全降级边界测试。
- [A8] 相关后端、前端、Ruff、构建、端到端和并发测试全部在容器内通过。
## 风险与开放问题
### 风险
- 员工承担差额不一定等同企业创造价值;需要跟踪申诉、补付和政策公平性,避免激励扭曲。
- 当前付款是人工状态,不是外部现金事实;确认页必须清晰披露证据等级。
- 旧 Claim/预算缺租户列,读取必须经过 Case Link 或正式迁移,不能依赖默认租户猜测。
- 数据稀疏且包含模拟种子,试点目标值必须在真实基线采集后冻结。
- 当前预算中心仍是配置演示视图CFO 来源入口只用于定位部门/费用类型配置,页面和价值计算均不得把其中金额当成预算事实或节省事实。
- 多币种、分摊收益和跨期冲回会显著增加财务口径复杂度,必须保留原始事实和版本。
- 工时价值若没有活跃时间采集与客户认可成本率,容易被夸大,因此默认不计现金 ROI。
- 历史异常集中不是因果,历史中位数也不是政策反事实;模拟候选必须在正式政策版本和客户确认适用口径补齐后才可货币化。
### 已处理决策
- 首条闭环选择“住宿职级标准重算”,不选择缺少付款事实的重复支付阻止。
- 价值看板进入现有分析看板,独立拆组件和 composable。
- 只设 3 个主 KPI其余作为驱动和护栏现金与工时分开披露。
- Savings Ledger 直接带结构化租户键,不复用旧财务快照作为价值事实源。
- 流程基线采用提交至首个付款完成的端到端 elapsed cycle人工活跃工时继续作为独立待采集事实。
- 异常归因仅做描述性集中度,政策模拟候选保持只读,不自动写入节省机会。
### 待后续真实客户确认
- 独立财务确认是否要求双人复核及大额阈值。
- 报告币、汇率来源和月末汇率锁定规则。
- 工时价值是否进入扩展 ROI、角色成本口径和可释放比例。
- 标准重算差额在客户会计政策中属于现金节省、成本避免还是员工自担调整。
- 试点 30/90 天目标、CFO 月报签字人和节省分成合同边界。
## 本轮实现记录
- 2026-07-16完成现有财务、预算、风险、付款、标准重算、前端入口与 KPI 口径盘点;确认旧财务聚合租户边界和客户端原金额信任问题。
- 2026-07-16冻结 Savings Ledger、CFO KPI、状态机、权限、去重、证据和首条纵向闭环方案尚未完成的代码与验证全部保留在同目录 TODO 中继续执行。
- 2026-07-16完成 Savings Ledger 五表与 0015 迁移、租户/权限/幂等/并发/证据/冲回契约,并把住宿标准重算和付款动作接入同事务价值链。
- 2026-07-16完成独立财务确认、证据复核留痕、canonical 收益去重、负向冲回、`as_of` 回放和历史标准调整安全回填。
- 2026-07-16完成 CFO 价值分析 API只将已确认 canonical 现金结果计入主 KPI多币种分组工时、安全直通率和审计事实缺口显式披露不使用演示数据回退。
- 2026-07-16完成 CFO 经营价值前端入口、URL 筛选恢复、主 KPI、漏斗、趋势、驱动维度、护栏、数据质量、机会证据链与响应式状态详情明确展示财务确认人、时间和说明。
- 2026-07-16收紧无证据手工登记边界。真实付款事件仍可自动形成待确认实际结果在支付/银行/ERP 凭证连接器接入前,页面不再提交空证据,并向用户解释必须先完成付款或等待外部回执。
- 2026-07-16完成租户隔离的员工、部门、费用类型、城市和项目五维历史中位数冻结基线以及预算预测偏差、重复小额模式和历史中位数偏离候选非政策反事实信号只披露暴露不生成节省金额并补齐 `as_of` 基线时间一致性门禁。
- 2026-07-16补齐第六维流程周期基线以同租户首个付款完成业务事件冻结提交到完成的 elapsed minutes窗口、样本量、算法版本、来源指纹、质量状态和证据完整保存明确禁止推算人工活跃工时。
- 2026-07-16新增部门/费用类型/城市/项目异常集中归因和版本化政策模拟准备项;复用预算预测并收紧配置/交易截止点,所有缺少反事实的候选保持 `estimated_savings=None`、零机会副作用,供应商分析继续明确 unavailable。
- 2026-07-16完成机会抽屉 `value_opportunity` 深链、刷新/前进/后退恢复和非法/越权/跨筛选安全清理;来源动作可跳关联单据、风险证据位置、预算配置视图及 CFO 维度筛选,单据返回时恢复经营价值查询状态。
- 2026-07-16预算来源只应用授权部门与费用类型配置焦点未覆盖科目显示“未找到配置”所有入口和页面均明确演示预算金额不是当前机会的真实预算事实。

View File

@@ -0,0 +1,141 @@
# 节省事实账本与 CFO 经营价值看板 开发 TODO
更新时间2026-07-17
## 使用规则
- 每条 TODO 必须回链 `CONCEPT.md` 的章节或语义段落。
- 只有代码、接口或容器验证提供真实证据后才能勾选 `[x]`
- 现金节省、工时价值、风险暴露和预计机会始终分开,不以演示数据代替缺失事实。
- 实施顺序为:安全前置 → 账本 → 首条闭环 → 基线/分析 → CFO 前端 → PostgreSQL/E2E → 文档收口。
## 1. 调研与边界
- [x] [CONCEPT: 背景与问题] 盘点财务看板、预算、Claim、Expense Case、Business Event、风险、审批、付款和标准重算事实源。
证据:`finance_dashboard.py``finance_dashboard_snapshot.py``financial_record.py``expense_cases.py``expense_claim_standard_adjustment.py``expense_claim_approval_flow.py` 的只读审计。
- [x] [CONCEPT: 目标与非目标] 确认风险金额、未用预算、预计机会和未确认结果不得进入已确认节省。
证据CONCEPT「目标与非目标」「算法与公式」。
- [x] [CONCEPT: 用户与场景] 选择住宿职级标准重算作为首条纵向闭环,不选择证据不足的重复支付或供应商议价。
证据:现有标准重算和付款业务事件可形成最短可信证据链;`AccountsPayableRecord` 已有租户字段,但仍缺合同价、数量、单位价格和外部付款事实。
- [x] [CONCEPT: 风险与开放问题] 识别旧财务聚合全表读取、快照键缺租户、接口缺角色限制和标准重算信任客户端原金额问题。
证据:`FinanceDashboardMetricMixin._fetch_claims()``_fetch_budget_allocations()``FinanceDashboardSnapshotService._cache_key()``accept_standard_adjustment()`
## 2. 契约与设计
- [x] [CONCEPT: 数据与契约] 定义 baseline、opportunity、realization、evidence 和 append-only event 的职责与关键字段。
证据CONCEPT「数据与契约」。
- [x] [CONCEPT: 算法与规则] 定义 identified → accepted → in_progress → realized → verified → reversed 状态和 rejected/expired 终态。
证据CONCEPT「状态转换」。
- [x] [CONCEPT: 算法与规则] 定义 benefit key、canonical realization、归因比例和追加冲回规则。
证据CONCEPT「收益去重」。
- [x] [CONCEPT: 权限] 定义租户、finance/executive、预算范围、管理员只读、自证禁止和数据范围。
证据CONCEPT「权限」。
- [x] [CONCEPT: 算法与公式] 定义 3 个主 KPI、驱动指标、风险护栏和缺数据边界。
证据CONCEPT「算法与公式」。
## 3. 安全前置
- [x] [CONCEPT: 后端] 让旧财务看板显式接收可信租户与数据范围Claim/预算查询不得全表混算。
证据:`finance_dashboard_access_policy.py``finance_dashboard_scope.py``finance_dashboard_budget.py``finance_dashboard.py`Claim 直接按结构化 `ExpenseClaim.tenant_id` 首层过滤,预算历史兼容仅限 default 租户。
- [x] [CONCEPT: 后端] 给财务快照缓存键和后台任务加入租户/数据范围,禁止跨租户复用。
证据:`finance_dashboard_snapshot.py``finance_dashboard_scheduler.py`;快照键包含 tenant 与 scope fingerprint调度入口必须显式携带租户。
- [x] [CONCEPT: 权限] 给财务与 CFO 分析接口增加后端角色和范围校验,管理员身份不自动获得业务确认权。
证据:`finance_dashboard_access_policy.py``savings_access_policy.py``agent_run_access_policy.py``GET /api/v1/analytics/cfo-value`;容器租户/缓存/角色回归 17 项通过。
- [x] [CONCEPT: 第一条可信机会] 标准重算只使用锁定数据库明细原金额,客户端原金额不能影响结果和节省。
证据:`expense_claim_standard_adjustment.py`;原金额只读锁定 `ExpenseClaimItem.item_amount`,政策结果只由服务端重算,陈旧版本使用内容指纹并降低基线质量等级。
- [x] [CONCEPT: 后端] 将标准重算、审计和账本写入收口到同一事务边界。
证据:`expense_claim_standard_adjustment.py``commit=False` 写审计,同一 API 事务写 Claim、Baseline、Opportunity、Evidence、SavingsEvent 和 BusinessEvent事务失败整体回滚。
## 4. Savings Ledger 后端
- [x] [CONCEPT: 数据与契约] 新增 `profile_baseline_snapshots``savings_opportunities``savings_realizations``savings_evidence_links``savings_events` 模型。
证据:`savings.py`(模型)及 `test_savings_models.py`;五类事实分离保存基线、机会、结果、证据和不可变操作。
- [x] [CONCEPT: 数据与契约] 新增 Alembic 0015、迁移所有权、复合租户外键、唯一键、检查约束、索引和 append-only 触发器。
证据:`20260716_0015_savings_value_ledger.py``migration_preflight.py``schema_ownership.py`;一次性 PostgreSQL 17 空库完整迁移循环通过。
- [x] [CONCEPT: 后端] 实现 discovery、action、realization、query、access policy 和 response builder 独立服务。
证据:`savings_discovery.py``savings_actions.py``savings_realization.py``savings_query.py``savings_access_policy.py``savings_read_projection.py``savings_protocol.py`
- [x] [CONCEPT: 后端] 实现分页、筛选、排序、详情、可用动作、证据和事件 DTO。
证据:`GET /api/v1/savings/opportunities``GET /api/v1/savings/opportunities/{id}``savings.py`schema/API`test_savings_endpoints.py`
- [x] [CONCEPT: 算法与规则] 实现版本锁、请求指纹、不可变响应重放、收益去重和归因比例约束。
证据:`SavingsRequestProtocol`、数据库唯一/检查约束与 `test_savings_concurrency_postgres.py`;同 request 并发仅一次写入,同 benefit 双确认仅一个 canonical winner。
- [x] [CONCEPT: 状态转换] 实现 accept/start/record/verify/reject/expire/reverse 合法与非法转换。
证据:`savings_actions.py``savings_realization.py``test_savings_ledger_services.py`;负向 reversal 只追加,不改写原已确认金额。
- [x] [CONCEPT: 证据与审计] 把 Savings 关键动作写入同 Case 的 `BusinessEvent` 并保留 correlation/causation。
证据:`ExpenseCaseService` 资源链接与 `savings_*` 服务;端到端测试核验 opportunity/payment/action/confirm/reverse 事件同 Case 可回放。
- [x] [CONCEPT: 后端] 新增历史标准重算机会 dry-run/apply 回填脚本;缺 Case/租户/政策证据只输出数据质量报告。
证据:`savings_standard_adjustment_backfill.py``backfill_standard_adjustment_savings.py`;显式租户/目标库、指纹重算、批次锁、稳定键重放,容器直接测试 8 项通过。
## 5. 首条真实纵向闭环
- [x] [CONCEPT: 第一条可信机会] 接受住宿标准重算时冻结服务端原金额、政策输入/版本、目标金额、差额、维度和证据。
证据:`SavingsDiscoveryService`冻结 `ProfileBaselineSnapshot` 与内容指纹,政式发布版本为 complete内容指纹版本显式标记 partial。
- [x] [CONCEPT: 第一条可信机会] 同事务以稳定 opportunity key 创建或重放机会,并进入 in_progress。
证据:稳定键由 tenant + claim + item + policy version + calculation fingerprint 生成;重试返回原机会。
- [x] [CONCEPT: 后端] 付款业务事件后同事务创建 actual realization重复付款事件不重复计入。
证据:`expense_claim_approval_flow.py`调用 `realize_paid_claim()`PostgreSQL 同付款事件并发结果为 `[0, 1]`,仅一条 realization 和一条完成事件。
- [x] [CONCEPT: 财务确认] 实现独立财务 verify/reject未确认实际结果不得进入主 KPI。
证据:所有人工实际结果必须至少一条可追溯证据;独立确认人会固化证据复核人/时间pending 结果在 CFO 口径中为 0。
- [x] [CONCEPT: 冲回能力] 实现补付/申诉/归因修正的负向 reversal原确认不可删除。
证据:原 actual 保留 `finance_confirmed`,新增负向 canonical reversal`as_of` 可回放冲回前结果。
- [x] [CONCEPT: 权限] 验证申请人、机会负责人、结果填报人、纯管理员和跨租户用户不能自证或越权。
证据:`SavingsAccessPolicy`与 PostgreSQL 并发安全测试owner/recorder/admin-only 自证拒绝,独立 finance 可确认,跨租户无副作用。
## 6. 费用基线与经营分析
- [x] [CONCEPT: 基线快照] 按员工、部门、费用类型、城市、项目和流程从租户安全真实数据生成基线窗口、样本量和版本。
证据:`savings_fact_scope.py``savings_baseline_generation.py``savings_insights.py`;金额五维使用已归档明细中位数,流程维度只使用同租户报销提交时间与首个 `payment_completed` 业务事件,冻结 elapsed minutes、窗口、样本量、算法版本、查询指纹、质量、证据和审计事件`source_workflow_cycle_count` 明确披露覆盖,活跃工时保持 unavailable。
- [x] [CONCEPT: 基线快照] 供应商数据缺少租户/合同事实时显示 coverage gap不从应付模拟种子生成可信基线。
证据:基线和洞察 API 均返回 `supplier_dimension_unavailable` / `supplier_price_drift_unavailable`,要求核验供应商、数量和单位价格;不会读取 `AccountsPayableRecord` 模拟事实或创建货币化机会。
- [x] [CONCEPT: 算法与规则] 实现预算预测、描述性异常归因、重复小额浪费、历史偏离和只读政策模拟准备项;证据不足时不自动货币化。
证据:`savings_insight_budget.py` 复用预算配置/核销事实并以 `min(as_of, window_end)` 截止;`savings_insight_analysis.py``savings_insight_attribution.py` 输出历史偏离、部门/费用类型/城市/项目异常集中和版本化政策模拟必需输入。所有缺少正式反事实的候选 `estimated_savings=None``created_opportunity_ids=[]`,稳定重放不写 `SavingsOpportunity`
- [x] [CONCEPT: 后端] 实现 `GET /analytics/cfo-value`,统一时间、维度、币种、状态和 `as_of` 口径。
证据:`cfo_value.py`API/schema`cfo_value_analytics.py`;租户、角色、预算范围、时间和维度过滤均在服务端执行。
- [x] [CONCEPT: 算法与公式] 实现确认现金、工时待采集、安全直通率资格、漏斗、兑现周期、逾期、来源和护栏聚合。
证据:确认现金只求和 finance_confirmed + canonical + cash工时和安全直通率显式返回 collecting漏斗、趋势、驱动、逾期、冲回和数据质量不与主 KPI 混算。
- [x] [CONCEPT: 降级策略] 多币种、工时、外部付款和审计结果缺失时返回明确数据质量状态,不伪造 0。
证据:多币种按原币分组;无人工活跃时间/审计事实时返回 collecting/unavailable 和 coverage gap不使用 mock 回退。
## 7. CFO 前端
- [x] [CONCEPT: 前端] 在分析看板新增 `value` 入口,并把 dashboard 状态同步 URL。
证据:`useTopBarOverviewRange.js``AppShellRouteView.vue``OverviewView.vue``dashboard=value` 与价值筛选/页码写入 query刷新和返回可恢复。
- [x] [CONCEPT: 前端] 新增独立 CFO 组件、composable、API service 和展示模型,不继续扩大 `useOverviewView.js`
证据:`CfoValueDashboard.vue``CfoValueTrendChart.vue``CfoValueOpportunityDrawer.vue``CfoValueActionDialog.vue``useCfoValueDashboard.js``analyticsValue.js``cfoValueDashboardModel.js`;业务组件和状态职责已拆分,核心文件均低于 800 行。
- [x] [CONCEPT: CFO 看板] 实现主 KPI、护栏、价值漏斗、趋势、来源/组织驱动、机会表和数据质量。
证据:`CfoValueDashboard.vue``cfo-value-dashboard.css`;现金、工时和直通率分卡,趋势按币种切换,预计/实际/确认/冲回不混算,缺数据显式展示。
- [x] [CONCEPT: 前端] 实现时间、部门、费用类型、价值类型筛选和项目/供应商/城市/负责人高级筛选。
证据:顶部时间窗口与价值 query 联动,`createEmptyValueFilters``readValueFiltersFromQuery``writeValueFiltersToQuery` 和 API query 白名单覆盖全部筛选字段。
- [x] [CONCEPT: 前端] 实现基线、建议、执行、实际、确认、去重和证据详情。
证据:`CfoValueOpportunityDrawer.vue` 展示冻结基线、机会状态、实际净值、记录人、canonical 去重、财务确认人/时间/说明、证据索引和不可变事件;无可追溯凭证时不开放手工实际结果登记。
- [x] [CONCEPT: 前端] 实现单据、风险、预算、维度下钻与返回状态恢复。
证据:`cfoValueSourceLinks.js` 统一构造来源路由Claim 进入 `app-document-detail`,风险携带最小 focus/观察/决策参数和现有锚点,预算进入 `app-budget` 配置视图并应用部门/费用类型焦点,维度返回 `app-overview?dashboard=value` 相应筛选。`useCfoValueDashboard.js``value_opportunity` 恢复抽屉,并在非法 ID、403/404、跨租户不可见或不符合当前筛选/时间窗口时安全清除;`useAppShell.js` 从单据详情恢复 CFO 查询。限制:预算中心仍是演示配置视图,页面明确金额不是当前机会的真实预算事实;未覆盖费用科目显示未配置而不是零预算。
- [x] [CONCEPT: 降级策略] 区分零、无数据、基线不足、无权限、失败、部分数据和快照过期;删除 CFO 演示回退。
证据:`classifyCfoDashboardState``buildValueKpis` 与页面状态区;接口失败不读取 `data/metrics.js` 或 demo/fallback 数字。
- [x] [CONCEPT: 前端] 完成移动端、键盘、焦点、44px 触控和无障碍状态提示。
证据CFO 样式移动断点、44px 按钮、语义化 `label`/`role=alert`/`aria-live`、抽屉关闭标签及趋势表格降级;生产构建通过。
## 8. 测试与验证
- [x] [CONCEPT: 测试方案] 后端状态、金额、权限、租户、幂等、证据、去重、冲回和看板聚合单测通过。
证据:容器组合回归 84 项通过;本轮 Savings/CFO 基线、洞察、端点、账本、回填与 E2E 组合 `34 passed, 6 skipped`6 项为未配置 PostgreSQL 专用 URL 的预期跳过。
- [x] [CONCEPT: 测试方案] 标准重算客户端金额伪造、政策失败、Case 缺失和事务回滚测试通过。
证据:`test_expense_claim_service.py -k standard_adjustment` 8 项加付款集成 1 项通过HTTP 标准重算 1 项通过。
- [x] [CONCEPT: 测试方案] 前端数据归一化、状态、筛选、URL、证据动作和响应式测试通过。
证据:容器内 `cfo-value-dashboard.test.mjs` 14 项通过,新增机会 URL 恢复/清理、筛选上下文、风险/单据/预算/维度链接和预算非事实口径断言;与 App Shell 返回链、路由加载和筛选样式组合回归 37 项通过;带凭证序列化和无证据入口 fail-closed 均有断言。
- [x] [CONCEPT: 测试方案] 一次性 PostgreSQL 空库迁移、重复升级、约束、append-only、无损降级和并发测试通过。
证据:一次性 PostgreSQL 17 迁移循环通过;`test_savings_concurrency_postgres.py` 6 项通过覆盖重放、canonical 竞态、跨租户、独立确认、付款单事实和 append-only DB 触发器。
- [x] [CONCEPT: 集成] 住宿标准重算 → 机会 → 审批 → 付款 → 实际 → 财务确认 → CFO 看板 E2E 通过。
证据:`test_savings_value_e2e.py` 从服务端政策差额、付款动作、待确认排除、独立财务确认到 CFO 金额对账单项通过。
- [x] [CONCEPT: 集成] 确认后负向冲回和报告 `as_of` 回放 E2E 通过。
证据:`test_savings_value_e2e.py``test_cfo_value_analytics.py`同时验证当前净值归零和冲回前历史金额回放。
- [x] [CONCEPT: 容器验证] 相关 pytest、Ruff、前端测试和生产构建均在 `local-x-financial-linux` 内通过。
证据Savings/CFO 相关切片与端到端均通过fresh PostgreSQL 总探针 `87 passed / 0 skipped / 0 failed`,其中 Savings 并发 6 项Web 全量 `815 passed / 0 failed` 与 Vite build 通过;新增 Python 文件 Ruff 和 `git diff --check` 通过。
## 9. 文档收尾
- [x] [CONCEPT: 指标与验收] 逐项核对 A1-A8并把文件、接口、迁移、测试和运行结果写回证据。
证据A1 纵向闭环由 `test_savings_value_e2e.py`A2-A4/A4.1 由 Savings schema、`cfo_value_analytics.py`、基线/分析测试A5-A6 由 CFO 组件、来源下钻和前端状态测试A7 由 0015 迁移与 PostgreSQL 并发A8 由本节最终容器汇总证明。
- [ ] [CONCEPT: 风险与开放问题] 记录付款证据等级、汇率、双人复核、工时口径和试点目标的最终边界。
证据:
- [x] [CONCEPT: 本轮实现记录] 同步更新上位 `ai-expense-closed-loop-and-value-proof` 文档,不删除历史证据。
证据:上位 TODO 已回填 Savings Ledger、状态机、CFO 看板、下钻、证据详情和 E2E历史证据原样保留。

View File

@@ -0,0 +1,13 @@
## 修复记录
- 13:08修复 AgentAsset 全局读写、跨租户子记录注入、风险样本串租户、审核身份伪造和 ONLYOFFICE 匿名/可重放回调问题。
- Git 提交检查:已执行 `git fetch --all --prune``git status -sb``git log HEAD..@{u}``git log @{u}..HEAD``origin/main` 无新提交,本地 `main` ahead 17工作区包含多智能体并行未提交变更且未被清理或覆盖。
- 本地 ahead 摘要:`242d68c3` 审批任务与豁免、`28b834ed` 审批动作幂等回放、`4940ebc4` 风险处置流程、`ee88a36b` 租户安全分层学习、`6bdf65bc` 权威预审、`ae3f02c3` 零录入票据关联、`54754b55` 个人报销记忆、`211f85d9` 已验证申请工作流、`5b246307` 申请预览决策、`a662cfe6` AI 反馈账本、`5ed34c2b` 历史申请回填、`11275e4b` 迁移 ownership 安全、`1347366b` 时间线与草稿事件安全、`22669a90` 统一费用时间线、`a616b30c` AI 申请事务、`653eda05` 不透明会话、`661990b2` 费用案例事务事件。
- 修改:为 AgentAsset、Version、Review、TestRun 和 RuleFeedback 增加结构化 `tenant_id + scope`所有资产、发布、监控、召回、遥测、调度、foundation 和风险运行时查询接入企业/平台作用域;同编码按企业优先、平台回退解析,跨企业资源统一不可见,平台资产仅平台管理员可写。
- 修改:真实风险场景强制显式目标企业并先按 `ExpenseClaim.tenant_id` 过滤;测试证据归目标企业;版本、审核和规则表变更主体改为登录会话中的稳定 employee/username 标识,客户端 actor/reviewer 不能覆盖审计事实。
- 修改:新增 DB-backed ONLYOFFICE content/callback 会话和 `active → processing → consumed|failed` 原子状态机token 绑定租户、资源、资产、document key/version/fingerprint、权限、actor、audience、时间和 JTI平台只读、跨资产、旧版本、错 key、过期和重放回调均拒绝。
- 修改:回调复用安全下载器,限制配置 origin校验 DNS 全部地址并固定已验证公网 IP拒绝重定向、超限、错误 MIME、危险 ZIP 和异常 OOXML新增 `20260717_0026` 迁移并对无法归属的旧数据和有企业事实的 downgrade fail-closed。
- 操作:拆出 AgentAsset access/serialization/ONLYOFFICE security 与风险规则字段推断模块;将核心文件控制在 800 行以内;补齐功能 CONCEPT/TODO 和迁移、权限、安全回归测试。
- 验证:容器内发布/监控/召回/运行时/调度/遥测/租户安全/ONLYOFFICE 汇总 58 项通过AgentAsset service/foundation 28 项通过;风险生成/修订/golden 49 项通过;相关文件 Ruff、py_compile、ORM mapper84 张表)和 `git diff --check` 均通过。
- 验证:一次性 PostgreSQL 探针完成旧平台数据升级、同编码多企业、scope/check、跨租户复合外键和有事实 downgrade 保护;新库 `base → head(0028)``head → 0025 → head` 均成功AgentAsset 与 Knowledge ONLYOFFICE 会话表正常创建。
- 影响:企业只能读取本企业和平台只读资产,无法观察或修改其他企业的资产、版本、审核和测试证据;规则测试不会抽取其他企业费用;审计身份不可由请求伪造;文档回写失败时保持原文件不变,也不会向任意或内部地址发起下载。

View File

@@ -0,0 +1,10 @@
## 修复记录
- 13:40修复 0026 Agent 资产租户安全迁移在真实历史建表路径下无法完整回退的问题。
- Git 提交检查:已执行 `git fetch --all --prune``git status -sb``git rev-parse --abbrev-ref --symbolic-full-name @{u}``git log HEAD..@{u}``git log @{u}..HEAD``origin/main` 无新提交,本地 `main` ahead 17工作区包含多智能体并行变更未合并、覆盖或提交。
- 本地 ahead 摘要17 个提交覆盖审批任务与幂等回放(`242d68c3``28b834ed``4940ebc4`)、租户安全费用学习/预审/票据关联/申请记忆与反馈(`ee88a36b``6bdf65bc``ae3f02c3``54754b55``211f85d9``5b246307``a662cfe6`)、历史回填与迁移安全(`5ed34c2b``11275e4b`)、费用时间线与事务(`1347366b``22669a90``a616b30c``661990b2`)及不透明认证会话(`653eda05`)。
- 根因:空库先经过 0026 时 Agent 资产旧表尚不存在迁移会按设计跳过这些表之后旧表由当前模型补建PostgreSQL 为列级租户外键生成 `*_tenant_id_fkey`,而 0026 回退硬编码删除 `fk_*_tenant`,首个 `agent_asset_rule_feedback` 约束不存在即中断。
- 修改:`20260717_0026_agent_asset_tenant_security.py` 新增带 PostgreSQL 标识符引用的约束安全删除器0026 回退对它负责的复合外键、租户外键、范围检查和唯一约束统一使用 `DROP CONSTRAINT IF EXISTS`;升级路径与最终升级约束保持不变,模型自动命名的列级外键仍随租户列删除安全清理。
- 操作:在 `financial-internal` 网络启动独立 `postgres:16-alpine` 一次性数据库,使用应用容器和项目 venv 执行真实 Alembic 循环;验证结束后停止探针,并确认 `--rm` 已删除容器。
- 验证:相关 Ruff 检查通过PostgreSQL-only 迁移防误用测试 42 项通过;一次性 PostgreSQL 上的完整迁移循环 1 项通过7.00 秒),覆盖空库升级至 0028、回退至 0008、再次升级、完整回退至 base、最终再次升级至 0028同时验证约束存在与缺失两种删除路径。
- 影响:真实历史路径与当前模型补建路径都能安全回退 0026缺少迁移命名约束时不再失败已有约束仍被正常移除且不会改变 head 升级结构。

View File

@@ -0,0 +1,8 @@
## 修复记录
- 13:25记录 bug 修复:未配置商业计量器时独立 SQLite Session 回滚调用方事务。
- Git 提交检查:执行 `git fetch --all --prune` 后,`HEAD..origin/main` 无新提交;本地 `main` ahead 17 个既有提交,依次为 `242d68c3` 审批任务与豁免、`28b834ed` 审批幂等响应、`4940ebc4` 风险处置、`ee88a36b` 分层费用学习、`6bdf65bc` 权威预审、`ae3f02c3` 票据零入口归集、`54754b55` 个人申请记忆、`211f85d9` 申请流程统一、`5b246307` 预览决策、`a662cfe6` 反馈账本、`5ed34c2b` 历史费用 Case 回填、`11275e4b` migration ownership、`1347366b` 时间线与草稿事件、`22669a90` 费用时间线、`a616b30c` AI 申请提交事务、`653eda05` bearer session、`661990b2` 费用 Case 事务事件;这些提交均早于本轮且未改写。
- 修改:`commercial_direct_operation.py` 增加调用方 `lookup_session` 的只读未配置短路OCR、RuntimeChat、金融连接器和附件 observer 均显式传入当前 Session。这样没有计量器时不再创建第二个 Session也不会在 SQLite `StaticPool` 共用连接上意外 rollback 已 flush 的费用明细。
- 操作:先用附件归集回归复现 `expense_claim_items expected to update 1 row; 0 were matched`,再将未配置判断前移到调用方 Session保留真正配置计量器时的独立事务预占与结算。
- 验证:容器内附件归集与票据夹 `31 passed`;商业资源边界 `9 passed`;报销端点与连接器组合 `38 passed`;相关 Ruff 通过。
- 影响:未启用商业计量的开发、测试和兼容租户不再因为商业探测破坏调用方事务;生产 PostgreSQL 的独立商业事务语义保持不变。

View File

@@ -0,0 +1,8 @@
## 修复记录
- 13:25记录 bug 修复:业务回滚释放预占后相同请求无法安全重试。
- Git 提交检查:执行 `git fetch --all --prune` 后,`HEAD..origin/main` 无新提交;本地 `main` ahead 17 个既有提交,依次为 `242d68c3` 审批任务与豁免、`28b834ed` 审批幂等响应、`4940ebc4` 风险处置、`ee88a36b` 分层费用学习、`6bdf65bc` 权威预审、`ae3f02c3` 票据零入口归集、`54754b55` 个人申请记忆、`211f85d9` 申请流程统一、`5b246307` 预览决策、`a662cfe6` 反馈账本、`5ed34c2b` 历史费用 Case 回填、`11275e4b` migration ownership、`1347366b` 时间线与草稿事件、`22669a90` 费用时间线、`a616b30c` AI 申请提交事务、`653eda05` bearer session、`661990b2` 费用 Case 事务事件;这些提交均早于本轮且未改写。
- 修改:`commercial_runtime_reservations.py` 在相同指纹、相同订阅/权益/账期下允许 `released → reserved`,重开时重新锁定合同、校验 meter 快照并执行硬配额判断;跨账期重试继续失败关闭。
- 操作:补充 Direct operation 的 not-sent 后重试测试,并把金融连接器“业务 rollback 后同一外部事件重试并提交”加入资源边界回归。
- 验证:容器内 Direct + 商业资源组合 `22 passed`,商业资源边界 `9 passed`;回滚阶段无 usage重试提交后仅一个 usage 且预占终态为 committed。
- 影响:数据库瞬时失败或显式回滚不再把同一幂等业务请求永久卡在 released重试仍受当前硬配额和账期约束不会绕过额度。

View File

@@ -0,0 +1,15 @@
# 默认 Compose 未统一管理本地 PostgreSQL
日期2026-07-17
文档路径document/development/2026-07-17/dev-logs/bugs/default-compose-local-postgres-lifecycle.md
## 修复记录
- 22:47记录 bug 修复:默认 Compose 未统一管理本地 PostgreSQL。bug-log:787bc3a4
- Git 提交检查fetch 成功upstream `origin/main`upstream 新提交:未发现;本地 ahead 提交787bc3a4 (HEAD -> main) feat(platform): close AI expense value loop242d68c3 feat(approval): add task workflow and waiver decisions28b834ed fix(approval): replay immutable action responses4940ebc4 feat(approval): add safe risk disposition workflowee88a36b feat(ai): add tenant-safe hierarchical expense learning6bdf65bc feat(expenses): add authoritative pre-review workflowae3f02c3 feat(expense): add persistent zero-entry receipt association54754b55 feat(ai): add personal expense application memory... 另有 10 条。
- 修改:`docker-compose.yml` 将 PostgreSQL、健康依赖、本机端口和持久卷并入默认启动链路本地数据库插值改用独立的 `LOCAL_POSTGRES_*` 命名空间,避免根 `.env` 的外部数据库账号污染 Compose`docker-compose.postgres.yml` 保留为空兼容覆盖文件。
- 修改:`start.sh``server_start.sh` 在读取 `.env` 前保存并在读取后恢复完整 PostgreSQL 运行参数,保证 Compose 注入的 host、port、database、user、password 和 URL 始终优先;`test_env_file_precedence.py` 增加根脚本、后端脚本和默认 Compose 回归。
- 操作:先将旧库备份到 `/tmp/x-financial-local-pre-compose-20260717.dump`,再把旧卷只读复制到 Compose 管理的 `x-financial_postgres-data`;旧卷未删除。统一本地开发角色凭据后执行 `docker compose up -d`,迁移前置检查从 `unversioned/base` 通过并升级到 `20260717_0028`
- 过程披露:第一次 Compose 重建暴露环境优先级问题时,主容器仍沿用了根 `.env` 指向的外部数据库,并按既有启动流程将其从 `20260716_0006` 升级到 `20260717_0028`;迁移日志无失败,未对外部库执行回滚。修复后已确认当前主容器只连接本地 PostgreSQL 容器。
- 验证:容器内定向测试 `107 passed`Ruff、格式、两个 shell 脚本语法、默认/兼容 Compose 配置和 `git diff --check` 均通过;重复执行 `docker compose up -d` 只保持两个服务运行,没有创建额外容器。
- 验证:`main``postgres` 均为 healthy前端代理健康接口返回数据库 `ok=true`;应用实际连接 PostgreSQL 容器地址;本地库为 85 张 public 表、105 条员工数据、Alembic head `20260717_0028`PostgreSQL 日志无 FATAL/ERROR/PANIC。
- 影响:以后在仓库根目录执行一次默认 `docker compose up -d` 即可同时启动应用和本地数据库,并复用 Compose 管理的数据卷;外部数据库 `.env` 配置不再悄悄覆盖本地 Compose 连接。

View File

@@ -0,0 +1,13 @@
# 员工导入与成员资格部分提交
日期2026-07-17
文档路径document/development/2026-07-17/dev-logs/bugs/employee-import-membership-transaction.md
## 修复记录
- 14:07记录 bug 修复员工导入与成员资格部分提交。bug-log:242d68c3
- Git 提交检查:已执行 `git fetch --all --prune`、状态、upstream 和双向日志检查;`origin/main` 无新提交,本地 `main` ahead 17。ahead 摘要17 个提交覆盖审批/风险处置、租户安全费用学习、权威预审、票据关联、个人记忆、历史回填、迁移安全、费用事件事务和不透明认证会话;本轮未改写这些提交。
- 根因:`EmployeeImportCoordinator._apply_import_rows()` 在写员工和上级关系后先 commit`EmployeeService.import_employees()` 才补 `TenantMembership` 并第二次 commit成员资格失败时接口报错但员工已永久落库。
- 修改:协调器只 flush 员工、角色、组织、上级和变更日志,不再拥有 commit外层服务仅在成功结果后补齐租户成员资格并对两部分执行一次统一 commit任一异常统一 rollback。
- 操作:新增成员资格同步注入失败测试,导入新员工后故意抛错并验证员工记录不存在;校验失败结果不触发成员资格或无意义提交。
- 验证:容器内员工服务、导入、认证和行为画像 35 项通过;差旅计算器 5 项通过;目标 Ruff、format check、compileall、代码体积门禁和 `git diff --check` 通过。
- 影响:员工批量导入现在满足“全部员工数据与认证成员资格一起成功或一起失败”,不会出现接口失败但部分账号已经创建/修改的状态。

View File

@@ -0,0 +1,11 @@
## 修复记录
- 13:19修复用户会话结算测试身份错配、缓存命中后旧部门不再归一化以及 Excel 导入清空上级时误清空员工租户的问题。
- Git 提交检查:已执行 `git fetch --all --prune``git status -sb``git log HEAD..@{u}``git log @{u}..HEAD``origin/main` 无新提交,本地 `main` ahead 17工作区仍包含多智能体并行变更未做清理、覆盖或提交。
- 本地 ahead 摘要17 个提交覆盖审批任务与幂等回放(`242d68c3``28b834ed``4940ebc4`)、租户安全费用学习/预审/票据关联/申请记忆与反馈(`ee88a36b``a662cfe6`)、历史回填与迁移安全(`5ed34c2b``11275e4b`)、费用时间线/事务(`1347366b``22669a90``a616b30c``661990b2`)及不透明认证会话(`653eda05`)。
- 修改:会话结算正向用例改用会话真实所有者认证,保留服务端 username ownership 校验;新增其他用户不能关闭该会话的反向测试,避免用放宽授权掩盖 `durationMs=0`
- 修改:目录建表/种子初始化继续按 bind 与租户缓存,但缓存命中时仍以租户过滤查询旧部门编码并持久化映射到规范部门;不再因初始化缓存永久跳过外部同步产生的旧编码。
- 修改Employee 与 OrganizationUnit 的复合租户关系只把 `organization_unit_id``manager_id` 标记为 SQLAlchemy 可同步外键,`tenant_id` 只参与关联过滤;清空部门或上级不会再把员工租户写成 `NULL`,数据库复合外键仍阻止跨租户关联。
- 测试:补充会话所有权反例、导入后 `tenant_id/manager_id` 持久化断言,以及旧部门归一化后租户与数据库组织归属断言。
- 验证:容器内三个原失败点与新增反例 4 项通过员工服务、Excel 导入、行为画像/会话和认证会话相关回归 31 项通过ORM mapper 确认关系同步列仅为 `organization_unit_id/manager_id`;相关文件 Ruff 与 `git diff --check` 均通过。
- 影响:会话时长能在正确登录主体下正常结算,其他用户仍不能关闭该会话;旧组织编码会持续收敛到标准部门;员工导入或资料更新清空上级/部门时不会破坏不可为空的租户归属。

View File

@@ -0,0 +1,13 @@
# 报销审批身份解析跨租户串读
日期2026-07-17
文档路径document/development/2026-07-17/dev-logs/bugs/expense-claim-approver-tenant-isolation.md
## 修复记录
- 13:48记录 bug 修复报销审批身份解析跨租户串读。bug-log:242d68c3
- Git 提交检查:已执行 `git fetch --all --prune``git status -sb`、upstream 解析及双向日志检查;`origin/main` 无新提交,本地 `main` ahead 17。ahead 摘要:`242d68c3/28b834ed/4940ebc4` 为审批任务、不可变回放和风险处置,`ee88a36b/6bdf65bc/ae3f02c3/54754b55/211f85d9/5b246307/a662cfe6` 为租户安全费用学习、预审、票据关联、个人记忆和申请反馈,`5ed34c2b/11275e4b` 为历史回填与迁移安全,`1347366b/22669a90/a616b30c/661990b2` 为费用时间线及事务,`653eda05` 为不透明会话;本轮未改写这些提交。
- 修改:`expense_claim_access_policy.py` 的当前员工、申请人、直属领导、部门预算负责人和财务负责人解析全部先绑定认证用户或报销单的结构化 `tenant_id`;员工、组织和下属子查询增加租户首层谓词,避免相同姓名、邮箱前缀、部门或角色在另一企业命中。
- 修改:把身份候选、申请人回填和唯一姓名判断拆入 `expense_claim_employee_resolver.py`,保持公开策略 API 不变,并将访问策略主文件降到 701 行;无可信租户、跨租户关联或结构化归属冲突统一失败关闭。
- 操作:同步补齐审批任务、风险并发、层级记忆和报销测试夹具的显式企业归属;没有放宽生产授权,也没有用默认企业兼容掩盖跨租户错误。
- 验证:容器内 `test_expense_claim_service.py` 121 项通过,访问策略文件大小与租户作用域定向 10 项通过审批任务、PostgreSQL 并发与全量后端分片均通过fresh PostgreSQL 专项最终 `87 passed / 0 skipped / 0 failed`;新 Python 文件 Ruff 和 `git diff --check` 通过。
- 影响:审批队列、审批人快照、退回/通过权限和历史回填不会再因另一企业存在同名员工或同名部门而串租户;多租户环境下报销审批保持可解释且失败关闭。

View File

@@ -0,0 +1,8 @@
## 修复记录
- 13:33记录 bug 修复:财务驾驶舱租户安全测试仍按旧 Case Link 契约构造报销单。
- Git 提交检查:`git fetch --all --prune` 后未发现 upstream 新提交;本地相对 `origin/main` ahead 17 个既有提交,分别为 `242d68c3` 审批任务工作流、`28b834ed` 不可变响应重放、`4940ebc4` 风险处置工作流、`ee88a36b` 租户安全分层费用学习、`6bdf65bc` 权威预审、`ae3f02c3` 零录入票据关联、`54754b55` 个人申请记忆、`211f85d9` 申请工作流统一、`5b246307` 申请预览决策、`a662cfe6` 反馈账本、`5ed34c2b` 历史 Expense Case 回填、`11275e4b` 迁移所有权安全、`1347366b` 时间线与草稿事件安全、`22669a90` 统一事件时间线、`a616b30c` AI 申请事务统一、`653eda05` 不透明 bearer 会话、`661990b2` 事务化 Expense Case 事件;本次未改写这些提交。
- 修改:`test_finance_dashboard_tenant_security.py` 的 Claim 构造器显式接收并写入 `tenant_id`,租户夹具不再只依赖历史 `ExpenseCaseLink`;新增“结构化 Claim 租户与旧 Link 不一致时以 Claim 为准”的隔离回归。
- 操作:先在容器内单跑复现 3 个失败,确认不是测试顺序或全局 monkeypatch 污染,再执行最小夹具修复并串行复跑财务、连接器与 Hermes 相邻测试组。
- 验证:单文件 `8 passed`;排序相邻组 `67 passed, 4 skipped`;目标文件 Ruff 检查与 `git diff --check` 均通过。
- 影响:财务驾驶舱测试与新的结构化租户模型保持一致,同时固定了旧关联索引不能移动报销单租户归属的安全边界;生产 fail-close 与首条 SQL 租户过滤没有放宽。

View File

@@ -0,0 +1,13 @@
# 财务连接器运行事件计数与固定时钟偏差
日期2026-07-17
文档路径document/development/2026-07-17/dev-logs/bugs/financial-connector-operational-counting-clock.md
## 修复记录
- 11:39记录 bug 修复:财务连接器相同载荷的不同运行尝试被永久合并,固定时钟场景的接收时间偏离观测窗口。
- Git 提交检查:已执行 `git fetch --all --prune`upstream `origin/main` 无新提交;本地 ahead 17 条,包含 `242d68c3 feat(approval): add task workflow and waiver decisions``28b834ed fix(approval): replay immutable action responses``4940ebc4 feat(approval): add safe risk disposition workflow``ee88a36b feat(ai): add tenant-safe hierarchical expense learning` 等共享工作区既有提交,本次未改写这些提交。
- 修改:`financial_connector_operational_events.py` 把规范化 UTC 发生时间纳入运行事实幂等命名空间,使同一 candidate 的补偿重试保持单条、不同 HTTP 尝试分别计数;`financial_connector_ingestion.py` 让注入的可信 `now_epoch` 同时驱动接收时间和配置健康时间;`financial_connector_observability.py` 统一把数据库时间规范为 UTC避免 SQLite/驱动返回 naive datetime 时窗口结果不稳定。
- 操作补充运行事实服务、HTTP 冲突、可信认证归属、敏感原值不落库和 PostgreSQL 并发探针;所有 Python、pytest、Ruff 与迁移操作均在 `local-x-financial-linux` 容器中执行PostgreSQL 验证使用新建的一次性 `disposable-probe` 容器,没有连接项目配置中的外部数据库。
- 验证:容器内连接器与迁移前置定向 `115 passed`,全新 PostgreSQL 17 完整迁移循环 `51 passed`,连接器并发 `4 passed`,后继 0023 并发 `7 passed`;相关 Ruff 和 `git diff --check` 通过。
- 影响:可观测性不会再把多次真实重放压成一次,也不会因测试/模拟可信时钟与实际系统日期不同而漏掉事件;同一补偿 candidate 仍由数据库唯一约束保证幂等。

Some files were not shown because too many files have changed in this diff Show More