Files
LWLT-AIBOT/archive/project-history/2026-08-17/findings-pre-compression-platform-attachment.md
T

251 lines
33 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 当前关键发现
## 2026-08-17 — AgentBus listener 启动/重载状态竞态
- `AgentBusManager.reload()` 会先停止旧 listener,再创建启用渠道的新 listener;旧实现的 `AgentBusListener.stop()` 无条件写入 `status='disabled'`,而新 listener 的“连接中/已连接”状态通过未等待的异步回调写入,可能产生乱序,导致页面显示“已停用”而内存 listener 实际已连接。
- 启动阶段也存在同类乱序:同一渠道的 `connecting` 写入可能晚于 `connected` 写回数据库,导致页面刷新后一直显示“连接中”。现在内部重载使用 `listener.stop(false)`,并按渠道串行化运行状态写入;只有管理员明确设置 `enabled=false` 或控制面真正关闭时才持久化 `disabled`。
- 已重启运行中的控制面;`/health/ready` 当前显示王徐明、阿虹两个渠道均 `enabled=true`、`connected=true`、`session_ready=true`。
- 带登录态的 `/api/channels` 和刷新后的 `/channels` 页面均显示两个渠道为“已连接”。控制面测试 53/53、构建和 `git diff --check` 通过;仓库卫生/legacy 仍被既有 `.DS_Store` 与 `dist/老挝联泰AI指令表-0.5.118.docx` 哈希不一致阻断,未改动这些用户文件。
## 2026-08-17 — 插件超时自愈需求
- 用户确认需要两级超时:插件实际领取后的单任务 10 分钟无最终结果自动失败;历史任务 30 分钟无处理进展自动退出活动队列。
- 10 分钟不能从 AgentBus“已受理”或服务端排队时间起算,否则历史队列拥堵会把尚未执行的任务误判为执行失败。
- 现有租约看门狗会把失联/过期执行转为 `reconciliation_pending`,但 `claimForBrowser()` 又把该状态当作活动执行,导致历史任务永久占用组织级槽位。
- 安全实现必须区分 `no_erp_write=true` 与可能已跨过 ERP 写入边界的任务:前者可失败释放;后者保留回查和不自动重试,但不应继续阻塞新的插件领取。
- 当前 `claimForBrowser()` 把浏览器租约固定为 10 分钟,但 `recordExecutionResult()` 对非终态进度又从当前时间重新延长 10 分钟,因此连续发送 `running` 进度可以绕过单任务硬截止。
- 当前插件 `execution-guard.js` 只有持久化执行状态,没有结果超时;`runAutoTask()` 也没有总截止时间。浏览器页面保活只维持执行,不负责超时终止。
- 平台会轮询插件本地结果并调用服务端 `/result` 持久化;因此需要控制面看门狗和插件本地看门狗双重兜底,且两者都必须使用实际领取时间而不是排队时间。
- 安全的超时表示为:顶层任务可显示失败并释放活动槽;若写入边界不确定,执行结果保留 `reconciliation_required` 和只读回查依据,禁止自动重试。
- 运行态核查发现历史维护查询曾因 `SELECT DISTINCT` 与 `ORDER BY a.finished_at` 组合触发 PostgreSQL `42P10`,导致维护阶段被跳过;已移除不必要的 `DISTINCT`,并将现场 15 条超时 reconciliation 任务标记失败、释放队列,当前超过 30 分钟的残留为 0。
## 2026-08-17 — AgentBus 渠道名称二次维护
- 渠道 PATCH 接口原本已支持 `display_name`,本轮补上独立 `/channels` 页面中的“维护名称”操作,因此普通渠道和默认兼容渠道都可改名。
- 默认兼容渠道以 `external_user_ref='legacy-env'` 作为稳定标记;更新时保留该标记,启动时按标记查找而不是按显示名称查找,避免改名后再次插入“默认 AgentBus 渠道”。
- 由于名称维护发生在仍加载旧启动逻辑的运行进程中,旧逻辑已经产生的重复兼容记录不能靠 `NOT EXISTS` 自动消失;当前源码启动时会按创建时间保留最早记录,将其余记录停用并标记为重复,渠道列表和 listener 只使用保留记录。该处理不物理删除历史记录。
- 名称仍受同组织唯一约束和 120 字符限制,key 不回显,改名不会重建渠道连接凭据。
- 已通过 TypeScript、前端语法、build、check:repo 9/9、control-plane 53/53、legacy 202/202 和 git diff --check;运行态清理已随重启验证。
## 2026-08-17 — AgentBus 渠道独立根路径与 404 定位
- 当前监听 `127.0.0.1:8786` 的进程 PID 22950 于本轮源码更新前启动,实际请求 `GET /api/channels` 返回 404;这是旧进程未重载新控制面路由,不是当前源码缺少路由。
- 当前源码和 `.build/control-plane/src/server.js` 均包含 `GET /api/channels`;新增 `GET /channels` 返回操作台 HTML,由前端按路径显示独立渠道目录。
- 渠道面板已从 AI 工作台移出;`/channels` 只显示渠道管理,数据仍通过 `/api/channels` 按当前管理员组织鉴权。
- 前端静态资源缓存版本已更新,避免浏览器继续使用旧 `app.js`/`styles.css`。
- 用户已授权重启控制面;重启后 `/api/channels` 返回正常 401 鉴权响应、`/channels` 返回 200,`/health/ready` 显示 migration 012 和默认渠道 `connected/session_ready`。
## 2026-08-17 — 多 AgentBus 用户渠道与插件队列实施结果
- 已新增 migration 012:`user_channels` 保存组织级外部渠道、加密 AgentBus key、启用/连接状态和可选 bot address;`tasks.channel_id` 固化任务来源;`agentbus_deliveries` 保存按渠道与入站 frame 去重的受理/最终回执 outbox。
- `AgentBusChannelService` 负责管理员 CRUD、key 轮换、加密/解密和安全列表;`AgentBusManager` 为每个启用渠道建立独立 listener,通用 WebSocket URL 走环境配置,渠道 key 不回显。
- durable listener 不依赖内存超时:入站任务事务内写入受理回执,终态任务生成最终回执;发送采用租约、稳定 outbound id、失败退避和断线释放,重连后可补发。
- `claimForBrowser()` 在组织事务锁下按 `created_at,id` 选择最早 awaiting-handoff 任务;已有活动 ERP attempt 或不确定结果时阻止第二个 claim,并返回 `waiting/queue_position/active_task_id`。
- 操作台渠道面板只显示安全元数据;自动派发遇到服务端排队会停止本轮派发,不会绕过组织级单槽。
- 配置兼容:`AGENTBUS_ENABLED=true` 可只配置全局 WebSocket URL、把 key 放在渠道表;旧的完整环境级 token/bot 配置仍会自动建立默认兼容渠道。
- 当前已通过 TypeScript 检查、控制面测试 51/51、仓库卫生 9/9、legacy 201/201、聚合测试和构建。
## 2026-08-17 — 多 AgentBus 用户渠道与插件队列需求梳理
- 当前 `control-plane/src/agentbus.ts` 只有一个进程级 `AgentBusListener`,通过 `AGENTBUS_WS_TOKEN`、`AGENTBUS_BOT_ADDRESS` 等环境变量建立单个 WebSocket session;没有渠道表、每用户 key 或按来源绑定用户的运行时配置。
- AgentBus 入站帧可带 `from`/`conversation_id`,当前仅用于回复路由和会话 ID;`processInboundTask()` 固定使用组织级 `organizationId`,`TaskContext.userId` 为空,任务只能记录 `source='agentbus'`,不能归属到具体用户/渠道。
- 当前 `users` 表只允许 `role='admin'`;组织级自动化开关存在,但没有“用户渠道”这一独立管理对象,也没有 key 加密/轮换/禁用/最后连接状态的持久化边界。
- 解析队列当前允许最多两个 parse worker,这不等同于 ERP 插件执行队列;多个 AgentBus 任务可以并行解析是可接受的,但 ERP 写入必须另设单独的串行执行槽。
- 插件下发链路的 `autoDispatchReadyTasks()` 只在当前操作台页面内用 `autoHandoffInProgress` 防止重复扫描;它会逐个调用 `handoffTaskToExtension()`,而服务端 `claimForBrowser()` 目前只锁单个任务并创建单个 `erp` attempt,没有组织/浏览器级活动执行锁。
- Chrome 扩展的 `runningTasks` 以 `task_id` 去重,不是全局单任务锁;因此多个任务可先后通过服务端 claim,再被同一扩展并发启动。当前用户所说“插件同一时间只能受理一个任务”需要在服务端做持久化、可恢复的执行槽,不能只依赖前端布尔变量。
- 需要先确认串行槽是同一组织/同一 ERP 浏览器会话全局共享,还是每个用户/渠道独立;推荐前者,因为多个 AgentBus 用户最终复用同一个已登录 ERP 插件会话,并发会破坏 ERP 页面状态与写前回查边界。
- 用户已确认:串行槽按同一组织/同一 ERP 浏览器会话全局共享;其他用户任务进入服务端持久化队列并按创建时间依次领取。
- 当前 key 的唯一使用点是 `agentbus.ts` 创建 WebSocket 时的 `Authorization: Bearer <AGENTBUS_WS_TOKEN>`;因此如果“每个用户单独 key”指的是当前 AgentBus WebSocket key,技术上需要每个启用渠道拥有独立连接/listener,不能只在单连接收到帧后按 `from` 选择 key。
- 当前 AgentBus 最终回执路由只保存在 `AgentBusListener.pendingFinalReplies` 内存 Map;`waitForTaskOutcome()` 到 `AGENTBUS_TASK_TIMEOUT_MS`(当前默认 300 秒)后会把任务回为失败,进程重启也不会恢复未发送回执。引入跨用户串行插件队列后,排队时间可能超过该窗口;推荐持久化入站渠道/回复路由和最终回执 outbox,按 channel listener 重连后补发,并把投递幂等键设为 `channel_id + inbound_frame_id`。
- 这是一项跨所有已登记业务的基础设施能力,不改变 Agent/Skill operation 的业务字段;业务适配登记表已覆盖当前 action,代表性链路已核对 `team_order_create` 的业务页、`lwlt-newbooking` Skill、`agent_operation.schema.json`、`orders_add.mapping.json` 及控制面/插件实现。
### 已确认设计边界
- 用户已确认:每个外部用户渠道对应独立 AgentBus listener/连接和独立 key;渠道用户是独立外部身份,不直接创建后台登录账号;后台管理员统一管理。
- 用户已确认:AgentBus 入站路由和最终回执状态需要持久化,排队超过当前 300 秒或服务重启不能造成假超时/丢回执。
- 实施默认保留组织级自动化开关;WebSocket URL、client type、重连/超时为全局环境配置,渠道独立保存加密 key,并可覆盖 bot address。
- 实施默认使用禁用/归档而非物理删除渠道;禁用后不接收新入站,但保留任务和待发送回执,重新启用后继续投递。
- 插件执行采用组织级 FIFO 单槽;活动 execution 或 `reconciliation_pending` 等不确定结果会阻止后续 ERP claim,解析 worker 仍保持当前并行能力。
## 2026-08-17 — AgentBus onboarding 更新后重新对接
- onboarding 文档提供的公开连接事实为 WebSocket `wss://mesh.nianxx.cn/ws`、bot 地址 `bot:2076193998368161793:listener`、client type `bot`,并要求使用 `ready=1` 与 `Authorization: Bearer` 完成握手后等待 `session.ready`。
- 当前源码已按上述握手方式连接,并在 `session.ready` 后记录 session;不把 token 打入日志。
- 当前运行进程的 `/health/ready` 显示 AgentBus `enabled=true`、`connected=true`、`session_ready=true`,地址与 onboarding 一致;公开参数没有发现需要改动的地方。
- onboarding 文档中的 token 属于敏感配置,不能在 Planning 文件、源码、命令输出或最终回复中记录;本轮只做受控运行时对接验证。
- 已按当前受保护配置刷新 listener;新进程收到 `session.ready`,当前 session epoch 为 `2`,随后健康检查保持 connected/session_ready。
- 本轮未修改 `.env`、源码、发布物或业务数据,也未通过 AgentBus 发送业务消息。
- 经用户明确授权,已调用 onboarding 的 Invoke API 发送一条不含业务字段的连通性探测;HTTP `202` 显示 delivery accepted,当前 listener 日志出现 `frame_received`、`inbound_task_accepted`、`outbound_progress_sent` 和 `outbound_result_sent`。
- 探测最终因外部解析服务未返回可识别标准 JSON 而失败,但失败回执已通过 AgentBus 发出;因此 AgentBus 入口、listener 收件和回件均正常。
- 探测之后,当前 listener 日志没有发现新的 `frame_from=channel:wechat` 入站帧;用户刚才的微信消息更可能卡在 Channel Adapter/渠道投递层。
- 用户随后再次发送消息,连续观察 20 秒仍未出现新的微信入站帧;listener 健康状态持续正常,说明这次不是控制面进程或 WebSocket session 卡住。
- 随后用户确认修复并要求文件测试;已发送 59 字节临时文本附件,AgentBus `202 accepted`,listener `frame_received`/`inbound_task_accepted` 的 `payload_keys` 明确包含 `attachments`。
- 当前控制面只读取 `payload.text`,没有入站附件字段到任务/结果的映射;该测试的最终 `task.result` 未带附件。因此“附件到 AgentBus”已通,“附件经控制面返回渠道”尚未通。
- 用户上一条微信消息已由当前 listener 处理,最终 `task.result` 收到 AgentBus `accepted` 和 `delivered` 回执;文本渠道已恢复。
- 再次通过 Invoke API 发送纯文本“仅验证消息收发、不执行 ERP”的探测,HTTP `202`,listener 收到并发出受理和最终失败回执;最终回执收到 AgentBus `accepted`。
## 2026-08-16 — 生产 PostgreSQL / OSS 对接
- 远程 PostgreSQL 为 15.14;账号 `wxm` 无权新建数据库,但能在 `lwlt_agent` 数据库创建 Schema。该数据库的 `public` Schema 已存在旧测试表,因此生产使用新 `liansyn_prod` Schema,迁移后任务/组织/附件均为 0 条。
- 远程 `liansyn_prod` 已应用 `001_initial` 至 `011_task_artifacts`;旧 `public` Schema 的测试数据未被修改。
- OSS Bucket 位于广州,Bucket ACL 已是 `public-read`,匿名 PUT 返回 403;V4 签名上传、匿名 GET、后端删除均已通过临时对象验证,探针对象已删除。
- 当前附件 provider 已从固定 `DatabaseArtifactStore` 改为按 `ARTIFACT_STORAGE_BACKEND` 选择 database/OSS;生产模板使用 OSS,PostgreSQL 仅保存元数据和 `storage_key`。
- OSS 对象 key 为 `liansyn-platform/attachments/<execution_uuid>/<artifact_index>`,不包含客户名、团号或原始文件名;文件名通过 OSS `Content-Disposition` 保存并由任务回执展示。
- 生产默认 `DATA_RETENTION_ENABLED=false`,当前不会自动删除任务、审计或 OSS 附件;正式保留期限确认后再启用。
- 当前凭据只用于本轮探测和对接,未写入仓库;正式上线前必须轮换已在聊天中暴露的 OSS/数据库凭据,并更新部署机受保护环境变量。
## 2026-08-16 — AgentBus 用户回复契约
- 用户已确认:失败时有明确 ERP 业务反馈就返回 ERP 反馈;没有 ERP 反馈才返回简短错误摘要。
- 用户已确认:成功时按业务 action 返回客户定义的业务回执;解析完成、进入全自动化 ERP 执行不作为用户侧消息。
- 用户侧消息收敛为两类:受理通知、最终返回通知。技术状态继续保留在任务生命周期和审计中,但不进入 AgentBus 用户文本。
- 酒店安排客户回执已提供中英文格式样例,要求至少包含团号、入住日期、退房日期、晚数、房型/房数;模板中的日期、团号和业务字段必须由执行结果填充,不能写死样例值。
- 已核对现有五类业务 action、客户模板入口、AgentBus payload 与后台附件模型,并已统一接入控制面回执生成与状态过滤。
### 已核对的业务范围
- 新建类:`team_order_create`、`team_order_batch_create`、`shared_plan_create`、`shared_child_order_create`,核心成功结果是一个或多个团号/子单号。
- 修改类:`order_update_independent`、`order_update_shared_plan`、`order_update_shared_child`,成功回执应说明目标对象和已更新的业务字段,不应回传执行脚本细节。
- 安排类:`arrangement_guide`、`arrangement_vehicle`、`arrangement_hotel`、`arrangement_transport`、`arrangement_other`;酒店需要优先采用客户给定模板,其他安排先提供简洁业务确认。
- 生命周期类:`passenger_list_import`、`order_cancel`、`order_restore`;成功回执应说明名单导入/状态变更结果,ERP 业务拒绝继续返回 ERP 原文。
- 文件类:`confirmation_export`;成功回执需要说明文件已准备并提供可交付的文件信息/附件,不能只返回“读取成功”的技术状态。
### 当前实现差距
- `control-plane/src/task-service.ts` 的公开重要消息目前只有 `awaiting_user_input`、`error`、`success` 三类,成功文本只会追加团号/订单号;没有按 action 生成安排、修改、名单和文件回执的统一入口。
- `control-plane/src/agentbus.ts` 当前仍有 `task.progress` 进度帧,且 `taskResultText` 会把 `awaiting_confirmation` 作为用户文本;本轮 AgentBus 应只发送受理确认和最终 `task.result`,不发送解析完成/进入 ERP 的进度通知。
- AgentBus 当前 `task.result` 只携带 `text` 与裁剪后的 `important_message`,没有文件附件列表;确认单附件已经存入平台任务结果,回复契约需要决定以文件名/下载信息还是平台侧附件呈现为准。
- 当前 `taskResultStatus` 的失败集合遗漏 `blocked` 等业务阻断状态;ERP 明确业务拒绝必须继续按失败回执输出,而不能被误判为成功。
- 成功回执目前由 `successReceiptFromResult` 从 `erp_receipt`、`verification`、批量结果或生命周期结果中提取,主要保留 `group_number(s)`、`order_number(s)` 和验证标识;它还没有把安排/修改/名单/文件的业务字段整理成客户可读回执。
- AgentBus 现有 `reply_policy.progress` 才发送进度帧;本轮应把它改成一次性的受理通知,后续解析、确认和 ERP 执行阶段不再发送进度帧。
- 执行结果已有可用业务证据:安排类会回传 `operation.data.arrangement`、`resolved_refs`、`requery.subpage.arrangement_target` 和 `changes`;修改类会回传 `changes` 与 `requery.checks`;名单导入会回传目标行变更及人数/投影回查;取消/恢复会回传 `to_status` 和状态回查。
- 酒店安排的实际字段来自最终执行态 `data.arrangement`:`resource.name`、`room_type`、`start_date`、`end_date`、`room_count`;酒店安排成功模板可据此计算晚数,并从 `customer.name`/业务引用补充客户名。
- 确认单附件在控制面落库后会获得 `fileName`、`contentType`、`byteSize`、`sha256`、`artifact_id` 和鉴权 `download_url`;原始 `content_base64` 会被剥离,不得进入 AgentBus。
### 默认回执设计(已实现)
- 新建类默认返回:`team_order_create` / `shared_child_order_create` 返回团号或子单号;批量/散拼母团返回完整团号列表,不能只取第一条。
- 安排类默认返回:团号 + 安排类型 + 已落实资源 + 日期/数量;`arrangement_hotel` 使用客户给定的英文/中文双段模板。
- 修改类默认返回:团号/子单号 + 已更新字段和值;不返回字段映射、页面控件名、回查哈希等内部细节。
- 生命周期类默认返回:团号/子单号 + 已完成的名单导入或目标状态;母团取消附带“子单状态同步”,恢复不声称恢复子单。
- 文件类默认返回:团号/子单号 + 文件已准备 + 文件名/数量;通过 AgentBus 传递受控附件元数据时只允许文件名、类型、下载地址等交付字段,禁止 base64、哈希和执行阶段。
- 解析态酒店安排只提供酒店搜索词和日期/房数,房型由 ERP 选择框联动补全;生命周期执行态已经有最终 `executionOperation`,但当前控制面最终回执没有保存它,若要完整生成酒店模板需在插件回执中保留最小业务结果快照(不含隐藏 ID)。
- 当前 AgentBus 结果帧没有附件字段;平台后台已能生成鉴权下载 URL,但外部 AgentBus 用户是否能直接访问该 URL 仍未接入渠道级文件转发,因此本轮先把附件作为受控最终回执字段建模,禁止把原始文件内容塞进消息。
- 各 Skill 的解析输出契约明确禁止输出执行过程和完成声明;本轮只改执行完成后的 AgentBus 用户回执,不改 Agent/Skill 的解析 JSON 语义。
### 本轮边界与实现假设
- 酒店新增的中英文两段日期统一来自同一组已验证的入住/退房日期;用户样例中两段日期不一致时不照抄,而以实际执行结果为准。
- 酒店客户名不是当前所有 `arrangement_hotel` operation 的必填字段;只有执行上下文提供客户名时才追加到中文团号行,不在契约中硬编码“老挝联泰”。
- 当非生命周期插件只在 `report.business_error` 或 `server_response.business_blocked` 中提供 ERP 原文、没有顶层 `failure_source` 时,控制面会将其提升为 ERP 来源并返回原文。
### 本轮实现进展
- 新增 `control-plane/src/reply-contract.ts`,统一生成 action 级成功回执、酒店中英文模板、更新字段、名单/状态、文件名和受控附件元数据。
- `task-service` 现在把业务回执写入成功 receipt,并在公开重要消息中提供用户文本和安全附件元数据;文件原始内容、base64、哈希和执行证据不会进入 AgentBus。
- `agentbus.ts` 现在固定发送一次 `task.progress(status=accepted)` 受理通知和一次最终 `task.result`;解析/确认/ERP 执行中间状态不再发送。`blocked`、`saved_unverified` 和 `execution_uncertain` 都走失败结果。
- 控制面测试已通过 46/46,覆盖酒店新增模板、酒店变更/清除、批量完整团号、修改字段、ERP 嵌套业务反馈和文件附件回执。
- `test:legacy` 已通过 201/201;当前新增回复契约文档、控制面模块和测试未破坏现有业务/Schema/插件契约。
- 最终 `check:repo` 9/9、`check`、`build`、聚合 `test` 和 `git diff --check` 均通过。
本文件只保留仍会影响当前设计、执行和发布的事实。治理前完整内容见 [2026-08-16 快照](archive/project-history/2026-08-16/findings.pre-governance.md),更早完整历史见 [2026-08-12 快照](archive/project-history/2026-08-12/findings.full.md)。
## 项目文件治理
- 项目唯一治理入口为 [AGENTS.md](AGENTS.md);根目录只允许项目入口、Planning with Files 三文件、构建/部署配置和被 Git 忽略的本地 `.env`。
- `agent设计规范/` 是 Prompt、五个 Skill、业务模板和稳定夹具的唯一可编辑源;桌面和 `dist/` 不维护文本源副本。
- TypeScript 编译输出位于 Git 忽略的 `.build/`;`dist/` 只保存当前版本化插件、五个 `.skill`、运营 DOCX 与 `release-manifest.json`。
- `archive/` 只读追溯,`reports/` 只接收临时输出,`quarantine/` 只隔离不可信输入;三者不能定义当前业务规则。
- `task_plan.md`、`findings.md`、`progress.md` 是正式项目记忆。超出 16/32/32 KiB 建议边界时先完整冻结,再语义压缩;不删除三文件。
## 当前版本与契约
- 插件源码和当前版本化制品为 `0.5.130`;平台最低版本与生命周期 mapping 已一致。
- 五个业务 Skill 和运营 DOCX 使用 `0.5.118` 交付基线。
- Agent Prompt 为 `ltjt-agent-prompt-v1.4-updating-wide-parse`;生命周期契约为 `ltjt-lifecycle-v2.8-updating-wide-parse-2026-08`。
- 当前制品文件名和哈希只由 `dist/release-manifest.json` 定义;运行中的 Chrome 版本必须实时握手确认,不能从静态文档推断。
## 当前业务事实
- 无完整编号的独立团业务以预订客户 + 出发日期为主定位;客户支持中文分词匹配,客户原生筛选无结果时按日期回退后在候选行校验客户。
- 产品名称和领队始终是选填补充校验,不进入独立团原生列表硬筛;完整团号/子单号仍可直接使用。
- 散拼子单先定位母团,再在具体子单候选中校验客户、产品和领队;新增子单的客户不属于母团筛选条件。
- 散拼母团尚无子团时没有预订客户,计划收客数修改只强制出发日期;母团领队关键词对应 ERP 接团/接送地点/标志。
- 酒店、大交通、其他/备案使用统一选择框组合文本搜索;零候选停止,多候选按 ERP 下拉顺序默认第一条并由原生选择联动带入只读字段。
- 独立团修改保留用户明确的全部目标。插件只自动执行已开放字段,其他目标整体转人工复核,不能静默丢弃或改写。
## 已验证边界与待验证项
- 已有真实证据:四条创建路线、独立团/具体散拼子单名单、五类安排 create/clear、三类窄修改、取消/恢复、团队文件源读取与测试数据精确清理。
- 独立团 `twin_room_count`、散拼母团 `planned_capacity`、散拼子单 `lodging_note` 及唯一未确认酒店行的离店日/房间数已有真实持久化证据。
- 0.5.128 保留 `rooms.SGL`/`rooms.TWN` 与 `pax.adult`/`pax.child_bed`/`pax.leader` 原生映射和 fresh 回查门禁;并保留酒店安排变更的 ISO 日期规范化、空备注保留校验和恢复状态无操作识别。
- 未逐字段验证的订单宽字段、非酒店安排修改、酒店其他字段/状态、文件格式转换/外发、主数据、财务、采购和通知继续失败关闭;团队文件原始字节的后台附件保存、OSS 存储与鉴权/公共读取链路已接入。
- 写入状态不确定时只读回查同一 operation/execution,禁止自动重试;HTTP 200 或页面提示不能单独证明成功。
## 当前业务拒绝回传事实
- 取消任务历史执行中,ERP 原生响应明确为:`此团还有【应收团款】账,不能取消!`;目标团为 `LW-260903A-A`,当前 ERP 状态仍为 `预订`。
- 这是 ERP 财务前置条件拒绝,不是自然语言解析失败,也不是插件无法定位目标;本轮不代替授权人员清理应收账。
- 插件现在把该类响应标记为 `lifecycle_business_blocked`,平台错误码为 `erp_business_rule_blocked`,用户重要消息使用 ERP 原文;明确拒绝不会进入 `execution_uncertain` 或自动 reconciliation。
- 跨订单列表和计划列表的生命周期候选会在合并后再次使用已提供的产品/领队可见文本筛选;可见且唯一时才继续,缺少可见证据或仍多候选时继续阻断。
- 恢复/取消在精确编辑页读取当前状态;当前状态等于目标状态时返回业务阻断 `lifecycle_transition_noop`,不进入 ERP 写入,也不把技术契约错误直接展示给用户。
- 恢复订单的 ERP 列表入口需要同步切换顶部状态标签:独立团/母团恢复查【已取消】,散拼子单恢复查母团【收客中】。旧实现只清空或写入隐藏 `S_zhuangtai`,未点击 `.Menu2[setval]`,会受 ERP 保留的旧筛选状态影响;0.5.129 已补原生菜单点击和请求校验。
## 2026-08-16 历史酒店任务证据
## 2026-08-16 团队文件后台附件交付
- 原始日志显示 ERP 文件请求返回 HTTP 200、Word 类型、28,892 字节和 SHA-256,但插件结果固定 `downloaded=false` 且没有文件内容,平台因此只能看到来源元数据。
- 当前导出链路把文件内容以 base64 送入控制面;控制面按配置上限校验字节数与 SHA-256,去除 base64 后再保存任务结果,避免大文件内容进入 JSONB/前端详情。
- `task_artifacts` 使用组织、任务和执行尝试外键,正文以现有字段加密密钥加密保存;下载接口同时校验登录会话、组织和公开任务号,不暴露对象存储凭据。
- 任务成功回执新增 `backend_stored=true`、`artifact_id`、`storage_backend` 和 `download_url`;附件保存失败返回明确 blocked,不生成成功回执。`storage_backend=database` 保留为开发/回退实现,生产配置使用 `OssArtifactStore` 并通过 `TaskArtifactStore` 接口保持业务层不变。
- 插件版本已升至 `0.5.130`,因附件临时经由 `chrome.storage.local` 传输而启用 `unlimitedStorage`;本轮已完成生产 Schema/OSS 临时链路验证,尚未做真实生产部署或 ERP 复测。
## 2026-08-16 后台附件详情与酒店验证
- 导出详情以后台附件为主,只显示文件名和鉴权下载按钮;操作审核信息、生命周期日志和下载接口保持不变。
- `LW-260903A-A` 酒店变更已通过原生 fresh GET/POST 双回查,离店日 `2026-09-05`、房间数 `9`,其他保留字段未变。
## 本轮治理发现
- 活动源、生成物、交付物和历史边界以 `AGENTS.md` 与 `dist/release-manifest.json` 为准;完整治理过程已进入 `archive/project-history/`。
- TypeScript 编译输出只进入 `.build/`;`dist/` 只保存版本化制品,Prompt/Skill/业务模板分别从活动源同步生成。
- 仓库卫生会阻断根 `.DS_Store`、活动规划文件超限、发布物哈希漂移和 Word 实时锁文件;这些阻断不能通过回退用户改动规避。
- 桌面历史已归档并移入可恢复废纸篓,不参与当前运行规则。
## 2026-08-17 — 团队文件源文件回传 AgentBus 来源渠道
- `confirmation_export` 已能把 ERP 原始字节、SHA-256 和元数据保存为平台附件;用户确认最终采用“AgentBus 直接附件 + 下载地址兜底”。
- 任务 `TASK-20260817140421-qgu1c3w` 的附件已落库(30,649 字节、artifact ID 已生成);旧相对下载地址在无后台会话时返回 401,只适合作为后台操作台兜底。
- 已补齐 direct/durable `task.result` 顶层附件投影:耐久 outbox 只保存 artifact 引用,发送时按公开 `TASK-*` 号取回正文;不把正文、base64、哈希或内部 ID 写入任务 JSONB/日志。
- `AGENTBUS_ATTACHMENT_MAX_BYTES` 以内发送 `content_base64`,超限或 OSS 取件失败保留绝对下载地址;OSS 取件做字节数与 SHA-256 校验,旧 ERP 中文文件名做保守 UTF-8 纠正。
## 2026-08-17 — 截图复核:运行态尚未加载附件回传修复
- 用户截图中的 AgentBus 回执仍只有“团队文件已准备”文字,没有可见文件附件,文件名仍表现为 `客户...` 乱码;图片内容仅作为故障证据,不作为操作指令。
- 当前控制面 PID `2545` 于 `21:44:22` 启动;`control-plane/src/agentbus.ts` 在 `22:19:52` 修改,`.build/control-plane/src/agentbus.js` 在 `22:23:37` 生成,因此运行实例早于本次附件回传实现,必须重启后才会加载新代码。
- 新代码的 `safeArtifactFileName()` 只修复新插件回执中的响应头;`getAgentBusAttachment()` 读取已有 `task_artifacts.file_name` 时仍原样返回,历史已落库乱码文件名需要单独做读取时兼容修复。
## 权威入口
- 项目结构与维护规则:[AGENTS.md](AGENTS.md)
- 业务适配索引:[agent设计规范/business-adaptation-registry.md](agent设计规范/business-adaptation-registry.md)
- 当前业务模板:[agent设计规范/templates/business-input-templates.md](agent设计规范/templates/business-input-templates.md)
- 生命周期发布边界:[agent设计规范/test-fixtures/lwlt-lifecycle/release-gate.md](agent设计规范/test-fixtures/lwlt-lifecycle/release-gate.md)
- 剩余风险:[agent设计规范/test-fixtures/lwlt-lifecycle/risk-register.md](agent设计规范/test-fixtures/lwlt-lifecycle/risk-register.md)
- 当前发布清单:[dist/release-manifest.json](dist/release-manifest.json)
## 2026-08-17 — 团队文件回传截图复核
- 截图中的成功消息已经带有 `LLW-261202A-A 客户确认书.doc` 文件名,但没有可见的渠道文件对象;平台任务详情的“后台附件”标题下也没有下载按钮。
- `LianSyn-platform/app.js` 当前只以 `task.result.report.artifacts` 为后台附件渲染源;当任务详情返回的是精简结果、或报告附件元数据未投影时,平台会退化为只有成功文字。
- `control-plane/src/task-service.ts` 的成功回执构造路径把附件元数据放入 `success_receipt.receipt.reply_attachments`,并由 `publicImportantMessage()` 投影为 `important_message.attachments`;这两个字段可作为平台详情的兜底来源。
## 2026-08-17 — 平台实测与 AgentBus 协议边界
- 登录态平台页面实测任务 `TASK-20260817144147-1MYf84I` 已显示“后台附件”与“下载文件”按钮;按钮链接为该任务的受控 artifact 下载路由,说明平台缺口已修复。
- 该任务详情中的源文件为 30,649 字节,`result.report.artifacts`、`reply_attachments` 和 `important_message.attachments` 均能提供文件名/下载元数据。
- 本地 AgentBus direct/durable 测试确认小文件会进入顶层 `payload.attachments`,包含 `content_base64`;公开 NianMesh listener 文档只规定文本 `task.result`,没有可据以改写的官方附件字段。
- 因此当前剩余问题不能继续靠控制面猜字段解决:若渠道仍只显示文件名,需要上游渠道适配器确认其支持的附件字段/渲染能力,或提供一次不含业务敏感内容的协议回执;本轮未再次向外部渠道发送探测。