lzf_0626
|
01e4e6a687
|
修掉"Worker 会自己退出"的真缺陷(场外游标续租失败误取消主任务)
## 现象(2026-09-14 实测,非人为停止)
Worker 进程自己退出,退出码 1,日志末尾是
`ConnectionResetError: [WinError 10054] 远程主机强迫关闭了一个现有的连接`
(发生在 `offsite_worker.close()` 的 IMAP `logout()`),
而真正的起点是更上面那句 `asyncio.exceptions.CancelledError`。
## 根因(两处,都是真缺陷)
1. **取消错了对象**:`OffsiteMailWorker._process_batch` 把
`asyncio.current_task()`(= `__main__.serve()` 的**主循环任务**)交给游标心跳,
心跳在续租失败(`rowcount != 1`)或续租抛异常时执行 `task.cancel()` ——
于是"放弃这一批"变成了"**杀掉整个 Worker**"。`CancelledError` 从 `run_once()`
一路冒到 `serve()`,主循环直接结束。
2. **收尾异常盖掉退出原因**:`serve()` 的 `finally` 里 `await offsite_worker.close()`
在网络已断时抛 `ConnectionResetError`,把 `CancelledError` 顶掉,
表现为"关闭流程崩了",看不出真实原因。
## 改法
- `_process_batch` 把"这一批"跑在**独立任务** `batch` 里,心跳只取消 `batch`;
调用方捕获 `CancelledError` 后区分两种情况:
**本批被放弃**(`batch.cancelled()` 且当前任务自己没有在取消)→ 记 warning、返回 `False`、
Worker 继续下一轮;**外层在取消当前任务**(Ctrl+C / 进程关闭)→ 原样上抛,绝不吞掉。
- `serve()` 的 `finally` 里关闭场外 Worker 包 try/except:收尾失败只记日志,
**不改变退出码与退出原因**。
- 新增 `_current_task_is_cancelling()` 用 `Task.cancelling()` 做这个区分(3.11+)。
## 守卫
`tests/unit/worker/test_offsite_mail_worker.py::test_cursor_lease_loss_abandons_batch_without_cancelling_the_worker`
—— 续租失败(rowcount=0)时断言:批次确实跑起来过、返回 `False`、
**调用方任务没有被取消**。修复前这条用例会挂在"调用方被取消"上。
## 验证
- `pytest tests/unit/worker` → 112 passed
- `pytest tests/unit tests/contract` → 见下方(0 failed)
- `mypy app/worker/offsite_mail_worker.py app/worker/__main__.py` → 0 错(除组员文件里既有的 1 个)
- 重启 Worker 后跑记忆演示链路:候选 → verified → active → **2 秒**收敛,进程稳定
## 文档
`docs/演示用/记忆系统演示文档-2026-09-14.md`:
场景五补"如果发现 Worker 自己退了是怎么回事",问答补"Worker 会不会自己中途退出"。
|
2026-09-15 08:01:00 +08:00 |
|
lzf_0626
|
9490e5043b
|
记忆系统演示文档 + 修掉两处会让演示断链的真问题
## 新增:docs/演示用/记忆系统演示文档-2026-09-14.md
按"操作 → 看到什么 → 这体现什么"写,五个场景(探针看存储 / 记住一件事 / 谁能读谁读不到 /
画像版本与投影 / 停 Worker),每个场景配可复现命令与**实测输出**,另附建议顺序与时长、
六条问答话术、演示前自检清单、受控信号词表摘录。
配套两个新工具(都已实跑):
- `tools/memory_demo_chain.py`:一键走完"客户说 → 候选 → 客户确认 → 管理员批准 →
记忆与画像自动收敛",打印演示前后对比(实测 2 秒收敛)。
- `tools/memory_recall_demo.py`:同一客户换六个身份召回,当场看出"客户只读自己 /
员工要权限码+归属 / 运营读不到 / 管理员有权限码但没归属也读不到"。
## 修掉两处会让演示当场断链的问题(都是真机复现的)
1. **候选批准后画像字段不收敛**
`CustomerProfileCandidateService._promote` 只写画像快照、**不投**
`profile.rebuild_requested`,而 `user_facts` 与画像字段是 `ProfileAssemblyService`
的重建链路写的。后果:批准后记忆变 active、快照版本 +1,但**画像字段停在旧值**
(客户说"三年以内",画像里还是"十年以上"),要等一次无关的重建才收敛 ——
而"客户说完 → 批准 → 画像变了"正是演示主线。
现补一条重建事件(走事件而不是就地重建:本方法所在事务还没提交,
另开 session 看不到刚写入的记忆)。
2. **画像快照的唯一键从来没起作用,而且埋雷**
`uk_profile_snapshot_current` 建在列 `current_customer_id` 上(不是 `is_current`),
但 `ProfileAssemblyService._write_snapshot` 旧行只置 `is_current=False`(不清该列)、
新行**不写**该列(实测 13 行该列全 NULL)。
后果:只要客户**先被重建过一次**,旧 current 行仍占着 `current_customer_id=9001`,
下一次"批准画像候选"就会撞 `Duplicate entry '9001' for key
uk_profile_snapshot_current` → **整次批准 500**(本次实测踩到)。
现按唯一键的真实语义写:清旧行的该列、新行显式写客户号。
(`ProfileGenerationService` / 候选路径本来就是这么写的,只有这一处没对齐。)
## 守卫
新增 `tests/integration/test_profile_snapshot_current_invariant_mysql.py`:
按真实顺序"先重建再批准候选",断言 ① 只有一条 current 且它占着唯一键、
② 历史版本已归还该列、③ 批准不再 500、④ 批准投出了重建事件。
修复前这条用例会在第 ② 步失败。
## 验证
- `pytest tests/unit tests/contract` → 1490 passed, 2 skipped, 0 failed
- 新增集成用例通过;真机实测演示主线:候选 → verified → active → **2 秒内**
`user_facts` 与 `fin_customer_profile.investment_horizon` 都变成新值
- `mypy tools/memory_demo_chain.py tools/memory_recall_demo.py` → 0 错;ruff 全绿
## 文档
`docs/44-演示流程.md`:配套文档清单与"记忆链路"备选场景都指向新演示文档。
|
2026-09-15 00:55:44 +08:00 |
|
lzf_0626
|
076d786bc6
|
补齐客服转人工工单流:从"只能看"到"能推进"(基线状态机,不自行发明)
## 问题
`svc_handover_ticket` 的 DDL 与状态机在 `docs/02` §7.2 早就定好了
(pending → assigned → processing → resolved → closed,未解决可 cancelled),
但平台**只有 handover:read(只读队列)**:没有任何入口能改状态、assigned_to /
accepted_at / resolved_at / closed_at / resolution 五列**全库 0 非空**,
于是 40 张工单永远停在 pending —— 用户看到的就是"工单全都长一样"。
## 改了什么
后端:
- 新增 `app/service/customer_service_handover_action_service.py`:五个动作
(分配/接单/解决/关闭/取消),`SELECT ... FOR UPDATE` 锁单后判状态;
接单允许从 pending 自助接管(同时记受理人);取消不写 closed_at(该列属 closed 状态);
每次流转写一条 interaction_audit(handover.assigned/accepted/resolved/closed/cancelled);
非法流转 409、坐席不存在 422、工单不存在 404;回包不含 customer_id/session_id。
- 只读服务保持只读(读侧与写侧是两条边界,单测守着"读侧不许长出写方法"),
但列表支持 `?status=` 六态筛选、详情补上受理人与流转时间(坐席侧路由信息,非客户数据)。
- `app/api/controllers/admin.py`:五个 action 端点 A049–A053
(assignments / acceptances / resolutions / closures / cancellations),
走 `ApiTransactionService.execute_in` —— 幂等记录与业务写入同事务、重复键回放。
- 权限:新增 `handover:write`(9069,只授 admin),已并进种子
`tools/seed_test_rbac.py`;配套幂等脚本 `tools/grant_handover_write_permission.py`。
前端(管理员工作台 · 转人工工单页):
- 按状态给按钮(待处理→分配/直接接单、已分配→接单、处理中→解决、已解决→关闭、
未解决都可取消),加了状态筛选与"刷新";摘要弹窗补上受理人与四个时间点、处置结论。
- api-client 注册五个端点;workspace.js 的 api-client 引用与页面自身的 ?v= 一并升版,
避免浏览器拿旧缓存(旧缓存里没有这些端点)。
冒烟与测试:
- `tools/e2e_smoke_test.py`:B 段建的测试工单由 F 段走完 分配→接单→解决→关闭 收尾
—— 既不再把测试件堆在 pending 队列里(此前每次冒烟攒一张),又让每次冒烟都覆盖一遍状态机。
总数 40 → 44 项,实测 44/44 全绿。
- 新增单测 24 条(状态机合法/非法路径、越权、坐席不存在、审计、视图不泄漏客户标识)
与一条真机集成用例(HTTP 十步 + 数据库侧审计证据 + 自动清理)。
- 读侧那条"详情不得返回 assigned_to"的旧断言按新口径更新,并写清为什么。
## 验证
- `pytest tests/unit tests/contract` → 1489 passed, 2 skipped, 0 failed
- 新增集成用例通过;`tests/integration` 全量跑时
`test_memory_extraction` / `test_run_cancellation_mysql` 两条偶发红 —— 单独跑都通过,
是 AGENTS.md 已登记的"常驻 Worker 抢队列"(跑验收前须先停 Worker)
- `tools/portal_api_check.py` → 41 项通过 39、失败 0
- `tools/e2e_smoke_test.py` → 44/44 全通过
- `python tools/check_rbac_seed_consistency.py` → 通过(种子 63 条权限)
- 真机 HTTP 实测:分配→接单→解决→关闭四步 200 且时间戳齐全;取消路径 200 且 closed_at 为空;
同键重发回放不二次推进;对已关闭工单再分配 409;风控账号处置 403
## 文档
`docs/44-演示流程.md`(场景 4/8 + 命令 + 44 项)、`docs/演示用/后端接口文档`(新增 §11.4b 与
A049–A053)、`docs/演示用/全功能流程-大白话版.md`(工单页签改"读写"+ 已知偏差)、
`AGENTS.md`(9066-9069 号段演进 + 冒烟 44 项)
|
2026-09-15 00:41:59 +08:00 |
|
lzf_0626
|
449d078a02
|
新增《记忆架构与底座 Worker 工作流程 · 大白话版》
两部分,各讲一件事,接口处互相对齐:
第一部分 · 记忆架构
- 四个存储的分工:MySQL 是唯一账本(memory_unit / memory_evidence / memory_conflict /
user_facts / fin_customer_profile / profile_snapshots),Neo4j 存关系、Milvus 存向量
(集合 user_long_term_memory_v1 是代码常量)、Redis 只做加速(召回热缓存 300 秒 /
客服短期会话 30 分钟滑动)
- 一条记忆的一生七步;为什么画像重建要绕事件;investor_type 只来自风险测评
- 客服走"候选画像"两把钥匙(客户确认 verified → 管理员批准 active);不抽记忆的三种情形
- 召回三路(MySQL 结构化 + Milvus 语义 0.7 权重 + Redis 热缓存),没有"图召回"这一路;
当前只有风控 Agent 消费召回结果
- 可读范围一个文件说了算(客户只自己;员工要 memory:read:customer 且归属;上限 10)
- 失效/删除链路与"清不掉就如实留痕"
- 实测表行数 + 一条要如实说明的观察:9001 的向量在 Milvus 里,但 memory_sync_outbox
当前没有它的行,不能据此断言投影链路正常
- 八条已知遗留逐条核实(episode 幂等哈希不覆盖记忆内容、投顾两处不发 memory_sources、
风控 system 上下文召回恒空、current_customer_id 从不写入致唯一键形同虚设、user_facts
无唯一键、两个未接线组件、文档过期)
第二部分 · 底座 Worker
- 三段式(受理 202 → 排队 → Worker 执行)与"不跑就没有任何报错"
- 一轮 run_once 的四件事与顺序、单轮上限、每 30 轮才做的片段聚合、失败隔离
- 五条工作线逐个讲:Agent 运行(状态机/领取加锁/租约 60s+心跳 20s/重试 3 次/取消收口/
执行前重新解析身份)、领域事件(12 类、幂等两层、重试 5 次进死信、退避公式)、
记忆与画像投影、会话片段与知识向量、场外收件(四道闸门 + 游标租约 + blocked)
- 显式降级清单(不伪造成功)
- 怎么判断它活着/卡住/积压:看表 + 看日志关键词 + 当前死信实况(570 条 dead 的构成)
- 三个常被搞错的边界(风控定时扫描是独立进程且默认关;AgentRunWorker 生产未用;
GraphProjectionWorker / ProjectionReconciliationService 未接线)+ 一页速查
|
2026-09-15 00:16:35 +08:00 |
|
lzf_0626
|
776b504ee6
|
新增《全平台功能流程 · 大白话版》:从登入讲到六门户全部流程
- 读者是第一次接触平台的人:全文不用"链路/编排/收敛"这类词,讲"点一下之后发生了什么"
- 结构:三个角色(浏览器/API/Worker)→ 访客 → 登入与角色落地 → 客户 8 页 → 风控 →
投顾 → 运营三条线 → 管理员 8 个页签 → 底座 9 件事 → 一条主线时间线 → 常见疑问 → 速查
- 每节都标了文件/接口依据,并写清"已知空数据"与"已知小偏差"(通知类型下拉未接线、
NL2SQL 错误信息列恒为 --、推广页无 form 导致 required 不生效、场外默认 dry-run 等)
- 顺带纠正三处易误传的说法:幂等键只防"同一次请求重发"(连点仍会下两笔)、
客户侧前端权限门是装饰性的(后端 403 才是拦截)、FM-03 熔断目前只在前端拦
- 风控演示数据那 6 行 RISKDEMO 场外申购/赎回:代码注释与今日盈亏说明改口径为
"风控异常交易演示的触发材料,刻意保留",不再当脏数据;今日盈亏按 transaction_type 语义排除它们
|
2026-09-15 00:10:23 +08:00 |
|
lzf_0626
|
9dd2802d67
|
客户看板「今日盈亏」不再是硬编码 0:按行情/净值基准真实计算(含当日买卖)
- 口径:今日盈亏 = 今日市值 − 昨日持仓市值 − 今日买入金额 + 今日卖出金额(不含费用,与「持有盈亏」同口径)
- 昨日持仓数量由当日成交反推,不需要新表新字段
- 基准优先场内行情最近两个交易日收盘价(与同页 latest_price/market_value 同源、客户可核对),
行情只有一天时回退基金净值(15911/159991-159995 的行情本身就来自净值序列)
- 只认 transaction_type ∈ {买入,卖出}:演示库里混进的风控场外申购/赎回(RISKDEMO-*)
会把「昨日持仓数量」抬到 76 万份、今日盈亏从 -21.22 变成 -1612.72
- 接口字段与前端一行未改:HoldingItem.today_profit_loss / PortfolioSummary 两个字段数值变真
- 新增 tools/check_today_profit_loss.py:纯 SQL 独立复算并与接口逐只比对(实测 9001 = -132.70 / -0.2528%)
- 新增 6 条单测;pytest tests/unit tests/contract → 1466 passed, 0 failed
|
2026-09-15 00:03:23 +08:00 |
|
lzf_0626
|
9eebf9627f
|
记忆召回:按 sys_customer_assignment 归属定范围,不再把员工号当客户号
## 修的是什么
`governance.recall()` 把 `int(context.user_id)` 当客户号用。后果有两个,
方向相反但都致命:
1. **员工身份(风控/投顾/运营/管理员/system)恒空** —— 员工不是客户,
那是个不存在的客户号;日志只说 "empty",看不出是"设计如此"还是"记忆坏了"。
2. **越权陷阱** —— 员工号与客户号同号段(演示数据里客户 9001-9020、
员工 9002/9020 并存)。`int(user_id)` 一旦与真实客户号重合,就会把
**陌生客户的长期记忆读进来并注入提示词**,且不报错、看起来正常。
同一个问题在代码里还有另外两处**各自判断**、口径互不一致:
`BaseAgent.recall_memory()` 要求"每条记忆 customer_id == context.user_id"
(否则抛"越过客户范围"),`review_output()` 的引用校验只认同一条件。
## 怎么修的
新增 `app/core/memory_scope.py` 作为**唯一判定口径**,三处共用:
- 客户身份(customer / authenticated_user):**只读自己**,分配表里有别行也不读别人;
- 员工身份:**只读 `sys_customer_assignment` 分配给自己**的客户
(`context.customer_ids`,由 `IdentityRepository.load_context()` 读入);
归属未维护 ⇒ **失败关闭**,并在日志里点名"归属未维护",与"库里确实没有记忆"区分开;
- 访客:无(上游已拦)。
细节约定:
- 归属客户按客户号**升序**召回、单次上限 `MAX_RECALL_CUSTOMERS=10`
—— 升序是为了确定性(同一身份每次取同一批,不随数据库返回顺序漂移),
上限是为了别把成百上千条他人记忆塞进一个提示词;
- 跨客户合并后按置信度降序、`(客户号, uuid)` 兜底排序,最多 10 条;
- 员工同时持有多个归属客户的记忆时,`memory_context_text()` **逐行标注客户号**
并把提示词改成"多个客户的长期事实" —— 否则模型会把 A 客户的事实当成 B 客户的。
单一客户时保持原格式(客户身份的提示词与改动前逐字相同);
- 引用校验与范围守卫都改用同一口径:员工引用**归属客户**的记忆不再被判成伪造引用;
引用**非归属客户**的记忆即便被塞进 memories 也照样拦下。
## 验证(真实身份链路 + 生产召回装配)
`IdentityRepository.load_context` → `PlatformGovernance.recall`(含 Milvus 语义通道):
- 身份展开:roles=('advisor',)、customer_ids=('9001',)(sys_customer_assignment
里唯一那行 9020→9001)、可读范围 (9001,);
- **修复前** `recall(int(user_id)=9020)` → **0 条**;
- **修复后** `recall(按归属)` → **2 条**(客户9001:进取型 / 约三年);
- 边界:客户身份 9001 可读范围 (9001,);无归属员工 9002 = ()(失败关闭,
且**没有**把 9002 当客户号);未分配时的 9020 = ()。
测试:`pytest tests/unit tests/contract` → **1445 passed, 2 skipped, 1 failed**
(1432 + 新增 13;唯一失败是组员正在改的投顾页面,与记忆链路无关)。
新增用例:`tests/unit/core/test_memory_scope.py`(8 条,含"员工号不得被当成客户号"
的反例断言)、`tests/unit/service/test_agent_governance.py`(+5 条:归属召回/
无归属失败关闭且不碰数据库/客户只读自己/引用校验/越界守卫)。
## 遗留(已在 AGENTS.md 与文档里写明,未自行实施)
风控扫描这条线**仍读不到记忆**:它是唯一消费召回内容的地方
(`risk_agent.py:224`),而扫描上下文是 user_id="0"/roles=("system",) 且无归属行。
根因是**顺序问题**:召回发生在 handle() 之前,上下文里没有"本次目标客户"这个概念。
出路有两条:① 给风控专员补 sys_customer_assignment 行(运维动作,立即可用);
② 在 RequestContext 加显式的 target_customer_id 并校验它落在归属集合内
(推荐,但属跨线协议改动,等确认)。
文档:docs/演示用/记忆召回恒空-根因与修复-2026-09-14.md 新增 §五(含 §5.4 遗留说明)、
AGENTS.md 新增"记忆可读范围只有一个判定口径"易错点,并按 2026-09-14 复测更新测试基线。
|
2026-09-14 21:33:16 +08:00 |
|
lzf_0626
|
0c642133d4
|
修复长期记忆向量链路:投影入队 + 集合名三侧同源 + 召回按客户过滤
语义召回"恒空"的根因分四层,本提交修掉投递层与读取层(另两层——重试计数
门禁、episode 不投 rebuild 事件——已在前两个提交修复)。
R2 投递层:dispatch_profile_rebuild 只做图投影,没有任何 Milvus 投递,
长期记忆的向量从未被写入过(实测客户 9001 在 memory_sync_outbox 里 0 行)。
新增 _enqueue_memory_vector_projection(),在画像重建后投
MemorySyncOutbox(target_store="milvus")。走事件而不是同步写,是为了拿
Outbox 的重试/退避/死信,且不把 embedding 的网络等待拖进事务。
⚠️ payload 必须带 version(正整数):适配器 _coerce_profile_version 缺它
直接抛 ValueError(实测踩到,事件立刻 failed)。
R4 读取层:写的集合与读的集合不是同一个 ——
写(MilvusProfileProjection)用 "user_long_term_memory_v1",
读(bootstrap.get_vector_memory_adapter)与删(projection_cleanup_service)
却用 settings.milvus_collection = "jr_memory",而该集合从未被创建。
⇒ 召回:MilvusClient 构造不校验集合存在,适配器"构造成功"但每次 search
抛异常被 VectorMemoryAdapter 吞成 degraded → 召回恒
degraded_reasons=('milvus_unavailable',)、向量命中恒 0 条;
⇒ 清理:jr_memory 不在集合列表 → 走 vector_collection_absent 分支 →
报清理成功但一个向量都没删,陈旧向量永久留存。
修法:PROFILE_COLLECTION 成为唯一常量,读/删两侧直接引用它;
并删除 Settings.milvus_collection 配置项、清掉 .env.example 的
MILVUS_COLLECTION —— 写侧从来没读过它,一个只在契约一侧生效的配置项
比没有配置项更危险(Settings 的 extra="ignore" 会让其他环境残留的该
变量被安全忽略)。
顺带:
- 语义检索把客户过滤下推到 Milvus(filter="customer_id == N")。此前不带
过滤,别家客户的命中会白占 limit 名额,稀释本客户的召回条数。
- upsert 在 sources 为空时先返回,不再无条件 load_collection ——
"本来就没有可写内容"不该被记成投递失败(10001/10002 那两条事件即如此
重试 5 次进死信)。
回归守卫:tests/unit/infrastructure/test_memory_vector_collection_consistency.py
断言读侧与删侧用的都是 PROFILE_COLLECTION,且被删掉的配置项不得回归。
这个缺陷能活下来,正是因为两侧单测全绿而接缝无人守。
验证(走生产装配、进程内调用,未重启你正在跑的 API 窗口):
读侧集合打印 user_long_term_memory_v1(修前为 jr_memory);
召回 degraded=False / reasons=() / sources 含 milvus,且排序随 query 语义
变化(投资期限→horizon 0.288 > risk_level 0.201;风险偏好→risk_level 0.287);
query="进取型" 双通道合并且 vector_score=0.9987;query=None 走 mysql 全量。
pytest tests/unit tests/contract → 1432 passed, 2 skipped, 1 failed
(唯一失败是同事正在改的投顾页面,与记忆链路无关)。
文档:docs/演示用/记忆召回恒空-根因与修复-2026-09-14.md(新增,四层根因+证据)、
docs/37-记忆投影链路实现说明.md(补集合名三侧契约)。
|
2026-09-14 21:26:03 +08:00 |
|
lzf_0626
|
36c7a9d8d2
|
文档:审查报告入库 + 全量校对补注
## 新入库(`docs/演示用/`)
- `代码库全面审查报告-2026-09-14.md`
- `代码修改方案-2026-09-14.md`
- `记忆系统排查报告-2026-09-14.md`
- `记忆系统修复文档-2026-09-14.md`
- `文档一致性审计报告-2026-09-14.md`
- `多Worker接入方案-2026-09-14.md`
## 全量校对(32 个既有文档 + `AGENTS.md`)
跨 39 个文件、**1125 insertions / 148 deletions**。
⚠️ **这批改动同样不是本次会话写的**。我抽样核对过性质:是**实质内容补充**而不是
格式/换行转换。例如 `docs/44-演示流程.md` 新增两条"2026-09-14 补注":
- `启动金融Agent平台.bat` 只在**桌面**上,仓库里只有 `启动平台.bat` 这一份
(两份由同一个 `tools/make_launcher_bat.py` 产出,改完 `start.ps1` 重跑它一起更新);
- `advisor_t`(9020) 与 `offsite_t`(9006) **不在 `tools/seed_test_rbac.py` 的演示用户里**
(那里只有 `cust_t`/`risk_t`/`admin_t`/`review_t` 四个),由 `grant_*.py` 系列创建,
**重跑种子不会重建它们** —— 换机器时这两个账号登录失败,要先查 `sys_user` 有没有这两行,
而不是查密码。
这两条都是对的地方,与我这一路踩到的现象一致(我确实用到了 `advisor_t`/`offsite_t`)。
**我没有逐字审阅全部 39 个文件**,只抽样确认了改动性质与规模。若其中有需要复核的段落,
请指明文件,我逐处核对。
|
2026-09-14 20:36:00 +08:00 |
|
lzf_0626
|
484cccc145
|
记录三个演示文档的移动,并把 .workbuddy/ 加进 .gitignore
## 文档移动(docs/ -> docs/演示用/)
`代码结构与关键逻辑梳理-2026-09-14.md`、`后端接口文档-2026-09-14.md`、
`软件需求文档-2026-09-14.md` 移入 `docs/演示用/`。
git 识别为 **rename**(状态 `R`),三份文档的历史完整保留,不是"删掉再新建"。
## .workbuddy/ 加入 .gitignore
它与文件里已有的 `.agents/`、`skills-lock.json` 属同一类 —— AI 编码助手的工具产物,
不是项目内容。放在同一节下,避免以后 `git add -A` 时误提交。
## 未包含(有意留下)
`docs/23-记忆分层与画像设计.md` 的工作区改动**没有**一并提交:那不是我改的,
也不在这次确认的范围内,留在工作区由作者决定何时提交。
(暂存时被 `git add docs` 一并带上,已 `git restore --staged` 退回。)
|
2026-09-14 12:09:23 +08:00 |
|