docs: 更新自动化记忆设计文档与协议路由

- 重构 automation-memory-design.md 为 v2 版本,新增 Execution Profile 抽象层
- 删除 auth-global-rewrite-design.md 和 auth-global-rewrite-plan.md
- 更新 agent/api-endpoints.md 协议文档
- 更新 ASR 与 worker token latency 优化 TODO 文档
This commit is contained in:
qzl
2026-03-18 17:03:33 +08:00
parent 8539f05a66
commit 257cb0f5d5
6 changed files with 303 additions and 439 deletions
+236 -147
View File
@@ -1,227 +1,316 @@
# 自动化记忆任务设计方案(v1
# 自动化记忆任务设计方案(v2
## 1. 背景与目标
本方案用于落地后端自动化记忆任务
本方案用于重构自动化记忆能力,目标是
- 提供每日/每周自动执行能力
- 自动提取用户近期对话中的可沉淀记忆并写入 `memories`
- 支持“忘记”操作
- 复用现有 Agent 运行链路,避免并行维护两套运行时
- 保证可审计、可追溯,同时对用户界面隐藏自动输入提示词
- 保持单一运行时框架,不并行维护第二套执行系统
- 在运行时中新增一层抽象,使 `router``executor` 的启用策略可配置
- 将记忆处理职责从通用 worker 中剥离为独立 `memory agent` 角色
- 保留审计能力与用户可见性隔离(自动输入可审计、默认对用户隐藏)
- 维持与现有 `/api/v1/agent`、SSE、history 协议的兼容演进路径
## 2. 范围与非目标
该版本是架构重构设计文档,重点解决职责边界与扩展性问题。
### 2.1 范围
## 2. 设计原则
- `automation_jobs` 增加 `config`JSONB,强约束);
- 调度器执行 `automation_jobs`,按用户本地时区计算执行时间
- 自动任务复用现有 `router -> agent` 运行策略;
- 自动输入落库但对用户不可见;
- 工具集按任务配置分发,仅允许白名单工具;
- 正式启用 `memory_prompt`,将 `memories` 注入系统提示词。
1. **单框架,多角色**
- 仍复用同一套 runtime、队列、持久化、SSE 通道
- 不再让 worker 承担所有领域职责。
### 2.2 非目标
2. **配置驱动执行拓扑**
- 运行路径由配置决定:是否经过 router、最终由哪个 executor 执行;
- 执行拓扑可被审计并可回放。
- 不开放复杂 DSL 编排;
- 不开放细粒度策略配置(如 `execution_profile/history_window_days/memory_policy`
- 不引入第二套独立 Agent 运行框架
3. **记忆职责显式化**
- 记忆提取、写入、遗忘由 `memory agent` 执行
- worker 继续承担通用对话/工具编排场景
## 3. 关键决策
4. **默认安全最小权限**
- 工具集必须是 `declared_tools ∩ allowlist`
- memory executor 默认仅开放记忆域工具。
1. **调度与执行分离**
- `run_at/next_run_at/timezone` 负责“何时执行”;
- `config` 负责“如何执行”(提示词与工具集)。
5. **协议先行**
- 任何对运行协议和数据结构的变更,先更新 `docs/protocols/` 再落地代码。
2. **配置最小化**
- `automation_jobs.config` 仅保留:
- `prompt: string`
- `tools: string[]`
## 3. 范围与非目标
3. **自动输入可审计但用户不可见**
- 自动任务提示词作为“用户消息”写入 `messages`
- 在 metadata 加可见性标记并在用户历史接口中默认过滤;
- 审计路径可查询完整消息链路。
### 3.1 范围
4. **时区策略**
- 调度语义采用“用户本地时区 10:00”
- 存储执行时间统一为 UTC 的 `next_run_at`
- 调度器仅按 UTC 扫描,降低运维复杂度。
- `automation_jobs` 引入执行拓扑配置,支持是否启用 router 与 executor 选择;
- 运行时新增抽象层:`Execution Profile`
- 支持 executor 类型:`worker``memory`
- 自动任务默认使用 memory executor
- 消息可见性和审计策略保持增强。
## 4. 数据模型设计
### 3.2 非目标
## 4.1 `automation_jobs` 调整
- 不引入复杂 DSL 编排语言;
- 不开放无限制策略字段;
- 不新增独立部署形态的第二运行时;
- 不在本阶段改造前端交互形态(仅保证协议兼容)。
- 保留字段:`id`, `owner_id`, `title`, `schedule_type`, `run_at`, `next_run_at`, `timezone`, `last_run_at`, `status` 等;
- 新增字段:`config JSONB NOT NULL`
- 迁移策略:
- 将历史 `prompt` 迁移到 `config.prompt`
- 切换业务代码后删除旧 `prompt` 字段(可分两步灰度)。
## 4. 核心决策
### 4.2 `config` 强约束 schema
1. **新增抽象层:Execution Profile(关键)**
- 在 scheduler/orchestrator 与具体 agent 之间新增执行配置层;
- 该层决定是否调用 router、调用哪个 executor、可用工具策略。
2. **executor 职责分离**
- `worker executor`:通用任务、复杂工具编排、常规聊天;
- `memory executor`:记忆提取、记忆写入、记忆遗忘、记忆总结。
3. **router 可配置启用**
- 自动记忆任务默认 `enable_router=false`,减少不必要决策成本;
- 普通 chat 默认 `enable_router=true`
4. **拓扑合法性约束**
- 必须保证至少有一个 executor;
- 禁止出现 `enable_router=false``executor` 为空;
- executor 仅允许枚举值:`worker | memory`
5. **保持事件/审计统一出口**
- 无论 executor 类型,SSE 与消息持久化走同一通道;
- 仅扩展阶段字段枚举,不拆协议体系。
## 5. 目标架构
## 5.1 运行链路(重构后)
```text
scheduler/api trigger
-> execution profile resolver
-> (optional) router step
-> executor step (worker | memory)
-> persistence + redis stream
-> history/sse consumers
```
与旧链路相比,变化点是新增 `execution profile resolver``memory executor`
## 5.2 Execution Profile 抽象
建议抽象(运行时内部模型):
```json
{
"prompt": "提取最近对话中的稳定记忆并写入",
"tools": ["memory_write", "memory_forget"]
"name": "automation_memory_default",
"enable_router": false,
"executor": "memory",
"tool_policy": {
"mode": "intersection",
"allowlist": ["memory_write", "memory_forget"]
},
"history_policy": {
"window": "today_yesterday"
}
}
```
约束要求
说明
- `prompt`:必填、非空、长度受限(建议 <= 2000)
- `tools`:必填数组,元素为注册工具名
- `extra="forbid"`,拒绝未知字段
- 执行前二次校验:`tools` 必须与后端 allowlist 取交集,禁止越权
- `enable_router`:是否执行 router 决策步骤
- `executor`:最终执行角色
- `tool_policy`:工具授权策略,默认交集模式
- `history_policy`:上下文窗口策略(保持最小化,不扩展过多字段)
### 4.3 `messages.metadata` 扩展
## 5.3 默认 Profile 建议
新增建议字段:
- `chat_default`
- `enable_router=true`
- `executor=worker`
- `automation_memory_default`
- `enable_router=false`
- `executor=memory`
## 6. 数据模型设计
## 6.1 `automation_jobs` 调整
保留现有调度相关字段:
- `id`, `owner_id`, `title`, `schedule_type`, `run_at`, `next_run_at`, `timezone`, `last_run_at`, `status`
新增/调整字段:
- `config JSONB NOT NULL`:任务输入与工具声明;
- `execution_profile JSONB NOT NULL`:运行拓扑配置(MVP 可先内联,后续可抽到 profile registry)。
建议结构:
```json
{
"config": {
"prompt": "提取最近对话中的稳定记忆并写入",
"tools": ["memory_write", "memory_forget"]
},
"execution_profile": {
"enable_router": false,
"executor": "memory"
}
}
```
## 6.2 Schema 约束
- `config.prompt`:必填非空,建议长度 `<= 2000`
- `config.tools`:必填数组,元素为已注册工具名;
- `execution_profile.enable_router`:必填布尔;
- `execution_profile.executor`:必填枚举 `worker|memory`
- `extra=forbid`,拒绝未知字段;
- 执行前二次校验:`declared_tools ∩ runtime_allowlist`
## 6.3 `messages.metadata` 扩展
建议标准化字段:
- `hidden_from_user: bool`
- `origin: "chat" | "automation"`
- `executor: "worker" | "memory"`
- `execution_profile_name: string`
用途:
- `hidden_from_user=true` 的消息在用户历史默认不可见
- 审计查询可读取全量。
- 用户视图过滤自动输入
- 审计可按 executor/profile 追踪行为;
- 支持后续灰度比较(同任务不同 profile)。
## 5. 运行时架构
## 7. 运行时行为约束
## 5.1 复用现有 Agent 链路
## 7.1 自动任务
自动任务不新建运行时,沿用现有 `router -> orchestrator -> worker` 流程:
- 会话类型:`session_type=automation`
- 自动输入消息:`role=user` + `hidden_from_user=true`
- 默认 profile`automation_memory_default`
- executor`memory`
- 输入:来自 `automation_jobs.config.prompt` 的合成“用户输入”;
- 上下文:默认加载“今天 + 昨天”历史;
- 工具:仅启用 `config.tools` 且通过 allowlist 校验。
## 7.2 普通对话
## 5.2 会话与消息策略
- 会话类型:`session_type=chat`
- 默认 profile`chat_default`
- executor`worker`
- 自动任务写入独立 `session_type=automation` 会话,并关联 `job_id`
- 普通对话维持 `session_type=chat`
- 自动输入消息:`role=user``hidden_from_user=true`
- Assistant 输出消息:默认可见。
## 7.3 工具授权
## 6. 调度器设计
- 输入工具集合来自 `config.tools`
- 实际可调用集合来自 `config.tools ∩ profile_allowlist ∩ system_allowlist`
- 对被拒绝工具记录审计日志与拒绝原因。
## 6.1 扫描与触发
## 8. 调度器设计
- 周期扫描条件:
## 8.1 扫描与触发
- 扫描条件:
- `status='active'`
- `next_run_at <= now_utc`
- 命中后入队执行命令(复用现有任务队列)
- 命中后投递执行命令,命令中携带 `execution_profile` 快照
## 6.2 幂等与并发控制
## 8.2 幂等与并发
- 使用任务槽位去重键:`job_id + scheduled_slot`
- 使用行级抢占或原子更新防止并发重复执行;
- 失败重试并记录错误上下文。
- 幂等键:`job_id + scheduled_slot`
- 通过原子更新或行级锁抢占,防止重复执行;
- 失败重试并保留错误上下文。
## 6.3 下次执行时间计算
## 8.3 时区策略
- 每次执行完成后按 `schedule_type + timezone` 计算下一次触发时间
- 保存到 `next_run_at`UTC
- `last_run_at` 记录实际执行时间
- 调度语义按用户本地时区(例如每日 10:00)
- 存储执行点统一使用 UTC `next_run_at`
- 调度器仅按 UTC 扫描,避免多时区计算分散
## 7. 默认任务创建
## 9. 协议影响与兼容策略
用户创建后自动创建一条默认 daily 记忆任务
本阶段是设计重构,协议建议如下
- `schedule_type = 'daily'`
- 本地时区目标时间:10:00
- `timezone`:优先用户设置时区,缺省 `Asia/Shanghai`
- `config.prompt`:内置记忆提取提示词模板
- `config.tools = ["memory_write", "memory_forget"]`
1. `docs/protocols/agent/sse-events.md`
- `STEP_STARTED/STEP_FINISHED.stepName``router|worker` 扩展为 `router|worker|memory`
-`enable_router=false` 时,不产出 router step 事件。
## 8. 记忆工具设计
2. `docs/protocols/agent/run-agent-input.md`
- 增补运行上下文说明:内部执行可由 profile 决定 executor
- 对外 API 入参保持兼容,不强制前端传 executor。
## 8.1 `memory_write`
3. `docs/protocols/agent/api-endpoints.md`
- 增补说明:`/runs` 的运行阶段由后端 profile 决定,历史与 SSE 保持统一消费方式。
- 用途:写入/更新用户记忆;
- 输入:受 schema 限制(标题、内容、来源等);
- 安全:owner 作用域强制绑定,不信任模型输入的 owner 信息。
兼容要求:
## 8.2 `memory_forget`
- 老客户端仍可消费现有事件;
- 仅新增枚举值,不删除既有字段;
- 如客户端未识别 `memory`,按未知阶段容错处理。
- 用途:失效或删除用户记忆;
- 输入:`memory_id` 或受限条件;
- 安全:仅允许当前 owner 范围内操作。
## 10. 默认任务创建
## 8.3 工具授权
用户创建后自动创建 daily 记忆任务:
- 任务声明工具集来自 `config.tools`
- 运行时执行 `declared_tools ∩ allowlist`
- 非白名单工具直接拒绝并记录审计日志。
## 9. `memory_prompt` 启用策略
- 从数据库读取当前用户可用 `memories`
- 组装为系统提示词 memory section
- 设置条数与长度上限,避免 token 膨胀;
- 自动任务与普通对话统一使用该注入机制。
## 10. 前端展示策略
- 历史消息接口默认过滤 `hidden_from_user=true`
- 用户仅看到 assistant 输出(以及可选系统摘要,不包含原始自动 prompt);
- 前端无需大量工具分支 if/else,可统一按 metadata 标记处理。
- `schedule_type='daily'`
- 本地时区目标时间 `10:00`
- `timezone` 优先用户设置,缺省 `Asia/Shanghai`
- `config.prompt` 使用内置记忆提取模板
- `config.tools=["memory_write","memory_forget"]`
- `execution_profile={"enable_router":false,"executor":"memory"}`
## 11. 安全与审计
- 关键要求
- 禁止越权工具调用;
- 禁止跨用户 memory 读写
- 日志不输出敏感数据和完整私密上下文。
- 审计能力:
- 自动输入、工具调用、输出全链路可追踪;
- 用户界面只显示允许可见内容。
安全约束
- 禁止越权工具调用
- memory 读写必须强制 owner 作用域;
- 日志不输出敏感内容与完整私密上下文。
审计能力:
- 自动输入、工具调用、输出全链路可追踪;
- 可按 `origin/executor/execution_profile_name` 回放。
## 12. 迁移与发布步骤
1. `config` 字段与 schema 代码
2. 数据迁移:旧 `prompt -> config.prompt`
3. 运行时切换到 `config.prompt/config.tools`
4. 灰度验证后移除旧 `prompt`
5. 上线调度器与默认任务创建
6. 启用 `memory_prompt` 并完成联调。
1. `docs/protocols`(先协议)
2. 新增 `execution_profile` schema 与运行时模型
3. 数据迁移:历史任务写入默认 profile
4. 运行时接入 profile resolver
5. 引入 `memory executor` 并接管自动记忆任务
6. 灰度:先新建任务使用 memory executor,再迁移存量任务;
7. 稳定后收敛旧逻辑分支。
## 13. 测试与验收
### 13.1 单元测试
## 13.1 单元测试
- `config` schema 校验(非法字段、非法工具、空 prompt);
- `next_run_at` 计算(跨时区、边界时间)
- 隐藏消息过滤逻辑
- `memory_write/memory_forget` owner 边界
- profile schema 校验(非法枚举、空 executor、未知字段);
- 工具授权交集逻辑
- `next_run_at` 跨时区计算
- 隐藏消息过滤与审计字段落库
### 13.2 集成测试
## 13.2 集成测试
- 用户创建触发默认 daily 任务创建
- 调度触发 -> Agent 执行 -> memory 写入 -> `next_run_at` 更新
- 自动输入在审计可见、在用户历史不可见。
- 自动任务触发后进入 memory executor
- `enable_router=false` 时无 router step 事件
- SSE `stepName=memory` 可被稳定消费;
- 自动输入审计可见、用户历史不可见。
### 13.3 验收标准
## 13.3 验收标准
- 每日 10:00(用户本地时区)稳定执行;
- 自动化链路有完整审计
- 工具权边界有效;
- 前端历史展示符合“隐藏自动输入、展示输出”的预期。
- memory 任务不经过 router 亦可稳定完成
- 工具权边界有效;
- 审计链路完整;
- 对现有 chat 流程零回归。
## 14. 实施顺序建议
1. 先更新协议文档(`docs/protocols`明确契约
2. 数据库与 schema 改造(`automation_jobs.config`
3. 工具集与授权边界实现
4. 调度器与默认任务创建
5. `memory_prompt` 注入与端到端验证
6. 前端过滤逻辑收口
1. 协议文档更新`docs/protocols/agent/*`);
2. `automation_jobs` 数据结构改造与迁移
3. profile resolver 与 executor 抽象接入
4. memory executor 接入与工具边界验证
5. 调度器联调与默认任务创建
6. 端到端压测与灰度发布
---
方案是最小可行版本(MVP)设计,重点保障
版(v2)将“抽象 Agent 运行”落到可执行的配置模型,核心是
- 复用现有架构
- 低配置复杂度
- 可审计与可维护并存
- 为后续扩展保留演进空间
- 新增一层 `Execution Profile`
- `router``executor` 拓扑配置化
- 使用独立 `memory executor` 承载记忆职责
- 在不引入第二运行时的前提下完成大规模重构演进
@@ -1,102 +0,0 @@
# Auth 全局模块重写设计(跨端并存、同端互斥)
## 1. 目标
- 彻底消除 Auth 分裂状态:`token` 状态与 `AuthBloc` 状态必须单一真相源。
- 会话策略升级为:
- 同账号允许跨端并存:`mobile + web + desktop`
- 同账号同端互斥:同端新登录会挤下线旧设备
- 保证任何 401 链路在刷新失败后都能统一收敛为“未登录 + 清理本地 + 路由回到登录页”。
- 消除设备差异导致的不一致行为(部分机型“假登录”或“卡死页”)。
## 2. 边界与约束
- 仅重写 `apps/**` 的 Auth 客户端架构与规则,不改后端协议语义。
- 保持现有登录/注册 UI 路由入口,避免用户操作路径变化。
- 认证属于高风险域,重写必须覆盖:
- 启动恢复
- 运行时 token 过期
- 并发 401
- 手动登出与自动过期登出的差异行为
## 3. 核心架构
### 3.1 单一真相源(Single Source of Truth
- `AuthBloc` 成为唯一认证状态源。
- `ApiInterceptor` 只负责协议级拦截与刷新,不直接做路由跳转。
- 401 刷新失败时,`ApiInterceptor -> ApiClient callback -> AuthBloc(AuthSessionInvalidated)`
- 路由守卫只看 `AuthBloc` 状态,不再做隐式 token 判定。
### 3.2 会话状态机
- `AuthInitial`
- `AuthLoading`(启动恢复/会话检查)
- `AuthAuthenticated(user)`
- `AuthUnauthenticated(reason)`
`reason` 取值:
- `signedOut`
- `expired`
- `startupRecoveryFailed`
### 3.3 登出语义分离
- 手动登出:`deleteSession()`
- 尝试调用后端注销
- 最终清本地
- 自动过期:`clearSessionLocalOnly()`
- 仅清本地
- 不调用后端注销接口
### 3.4 并发与抖动控制
- `ApiInterceptor` 继续使用 refresh singleflight。
- 新增 auth failure singleflight:多并发 401 刷新失败,只触发一次全局会话失效事件。
### 3.5 设备差异治理
- 启动时 token 读取异常必须兜底:进入 `AuthUnauthenticated(startupRecoveryFailed)`,避免卡死在 Boot。
- `FlutterSecureStorage` 显式平台配置:
- Android 使用 `encryptedSharedPreferences`
- iOS 指定 keychain accessibility(保证行为稳定)
## 4. 数据流
### 4.1 冷启动
1. `main` 触发 `AuthStarted`
2. `AuthBloc` 读取 refresh token
3. 有 refresh token -> 刷新会话 -> 成功进入 `AuthAuthenticated`
4. 无 token 或异常 -> `AuthUnauthenticated(startupRecoveryFailed)`
### 4.2 运行时 API 请求
1. 请求携带 access token
2. 401 -> 触发 refresh
3. refresh 成功 -> 自动重试原请求
4. refresh 失败 -> 触发一次全局 auth failure
5. `AuthBloc` 收到 `AuthSessionInvalidated(expired)` -> 清本地 -> `AuthUnauthenticated(expired)`
6. Router 根据状态回登录页
## 5. 测试策略
- `AuthBloc`
- 启动读取 refresh token 异常兜底
- session invalidated 事件导致统一未登录
- `ApiInterceptor`
- 并发 401 refresh 失败仅触发一次 auth failure
- `AuthRepository`
- 手动登出 vs 自动过期清理行为差异
## 6. 迁移计划
- 先引入新事件/新回调/新状态原因,不改 UI 交互。
- 再改路由守卫识别未登录原因。
- 最后补齐 `apps/AGENTS.md` Auth 强约束,防止后续回归为“各处乱写”。
## 7. 风险与回滚
- 风险:回调链路接错导致频繁误登出。
- 规避:并发 singleflight + 精确触发条件(仅 401 refresh 失败)。
- 回滚:保留旧事件兼容层,出现异常可快速退回旧路由判定。
@@ -1,158 +0,0 @@
# Auth Global Rewrite Implementation Plan
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
**Goal:** 将 Flutter 客户端 Auth 重构为全局单一状态源,解决 401 后会话不一致、页面卡死和设备行为分裂问题。
**Architecture:**`AuthBloc` 为唯一认证真相源,`ApiInterceptor` 仅负责协议层刷新与失败信号上抛。401 刷新失败后通过统一回调触发 `AuthSessionInvalidated`,由 `AuthBloc` 执行本地会话失效与状态切换,Router 仅根据 Auth 状态跳转。
**Tech Stack:** Flutter, flutter_bloc, dio, flutter_secure_storage, flutter_test, mocktail, bloc_test
---
### Task 1: 定义 Auth 失效语义与事件模型
**Files:**
- Modify: `apps/lib/features/auth/presentation/bloc/auth_event.dart`
- Modify: `apps/lib/features/auth/presentation/bloc/auth_state.dart`
- Test: `apps/test/features/auth/presentation/bloc/auth_bloc_test.dart`
**Step 1: Write the failing test**
新增失败测试:收到 session invalidated 事件后,状态应进入 `AuthUnauthenticated(expired)`
**Step 2: Run test to verify it fails**
Run: `flutter test test/features/auth/presentation/bloc/auth_bloc_test.dart`
Expected: FAIL(事件/状态原因不存在)
**Step 3: Write minimal implementation**
新增失效来源枚举、失效事件、未登录原因字段。
**Step 4: Run test to verify it passes**
Run: `flutter test test/features/auth/presentation/bloc/auth_bloc_test.dart`
Expected: PASS
### Task 2: 重写 AuthBloc 启动恢复与失效收敛逻辑
**Files:**
- Modify: `apps/lib/features/auth/presentation/bloc/auth_bloc.dart`
- Modify: `apps/lib/features/auth/data/auth_repository.dart`
- Modify: `apps/lib/features/auth/data/auth_repository_impl.dart`
- Test: `apps/test/features/auth/presentation/bloc/auth_bloc_test.dart`
- Test: `apps/test/features/auth/data/auth_repository_test.dart`
**Step 1: Write failing tests**
- 启动读取 refresh token 抛异常 -> `AuthUnauthenticated(startupRecoveryFailed)`
- 自动过期登出只清本地不调后端
**Step 2: Run tests to verify failure**
Run: `flutter test test/features/auth/presentation/bloc/auth_bloc_test.dart test/features/auth/data/auth_repository_test.dart`
Expected: FAIL
**Step 3: Implement minimal code**
- `AuthBloc._onStarted` 增加异常兜底
- `AuthRepository` 新增 `clearSessionLocalOnly()`
- `AuthBloc` 处理 `AuthSessionInvalidated`
**Step 4: Run tests to verify pass**
Run: `flutter test test/features/auth/presentation/bloc/auth_bloc_test.dart test/features/auth/data/auth_repository_test.dart`
Expected: PASS
### Task 3: 改造 ApiInterceptor / ApiClient 全局失效回调链
**Files:**
- Modify: `apps/lib/core/api/api_interceptor.dart`
- Modify: `apps/lib/core/api/api_client.dart`
- Modify: `apps/lib/core/di/injection.dart`
- Test: `apps/test/core/api/api_interceptor_test.dart`
**Step 1: Write failing test**
并发 401 + refresh 失败时,`onAuthFailure` 仅触发一次。
**Step 2: Run test to verify it fails**
Run: `flutter test test/core/api/api_interceptor_test.dart`
Expected: FAIL
**Step 3: Implement minimal code**
- interceptor 新增 auth failure singleflight
- api client 新增 `setAuthFailureCallback`
- DI 中将回调绑定到 `AuthBloc(AuthSessionInvalidated)`
**Step 4: Run test to verify pass**
Run: `flutter test test/core/api/api_interceptor_test.dart`
Expected: PASS
### Task 4: 平台安全存储配置与稳定性增强
**Files:**
- Modify: `apps/lib/core/di/injection.dart`
**Step 1: Add platform options**
`FlutterSecureStorage` 显式设置 Android/iOS 选项,减少机型差异。
**Step 2: Run targeted tests/analyze**
Run: `flutter analyze lib/core/di/injection.dart`
Expected: PASS
### Task 5: 路由与使用点适配
**Files:**
- Modify: `apps/lib/core/router/app_router.dart`
- Modify: `apps/lib/features/settings/ui/screens/account_screen.dart`
- Modify: `apps/lib/features/settings/ui/screens/change_password_screen.dart`
**Step 1: Update route/auth checks**
兼容 `AuthUnauthenticated(reason)` 新结构,保持原有登录流 UX。
**Step 2: Run focused tests**
Run: `flutter test test/features/auth`
Expected: PASS
### Task 6: 增加 Auth 全局强约束
**Files:**
- Modify: `apps/AGENTS.md`
**Step 1: Add mandatory auth rules**
新增“Auth 全局模块(MUST)”章节:
- 401 只允许走统一失效回调链
- 禁止 feature 私自清 token/私自跳登录
- Auth 状态只能由全局模块写入
**Step 2: Verify docs consistency**
Run: `git diff -- apps/AGENTS.md`
Expected: 仅新增约束,不改现有视觉/UI强规则
### Task 7: 全量验证
**Files:**
- Modify if needed after fixes
**Step 1: Run test suites**
Run: `flutter test test/core/api/api_interceptor_test.dart test/features/auth`
**Step 2: Run analyze on touched auth scope**
Run: `flutter analyze lib/core/api lib/features/auth lib/core/router/app_router.dart lib/core/di/injection.dart`
**Step 3: Report residual risks**
输出剩余风险、可观测性建议、生产灰度建议。