From 6eeaee07d4fdeef3d29dd7cf46ac33929699eb8b Mon Sep 17 00:00:00 2001 From: andy Date: Thu, 17 Sep 2026 11:03:19 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8C=87=E4=BB=A4=E9=97=AE=E9=A2=98=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...0260916-independent-dbl-update-01a0858b.md | 43 +++++ LianSyn-platform/app.js | 2 +- agent设计规范/business-adaptation-registry.md | 2 +- agent设计规范/skills/lwlt-updating/SKILL.md | 1 + .../lwlt-updating/references/input-contract.md | 2 + .../references/operation-contract.md | 2 + .../templates/business-input-templates.md | 4 +- .../test-fixtures/lwlt-lifecycle/release-gate.md | 5 +- .../independent-dbl-update-01a0858b/README.md | 17 ++ .../independent-dbl-update-01a0858b/README.md | 3 + .../ltjt-order-assistant-0.5.186.zip | Bin .../lwlt-arrangement-0.5.128.skill | Bin .../lwlt-confirmation-0.5.128.skill | Bin .../lwlt-lifecycle-0.5.128.skill | Bin .../lwlt-newbooking-0.5.128.skill | Bin .../lwlt-updating-0.5.128.skill | Bin .../release-manifest.json | 69 ++++++++ .../老挝联泰AI指令表-0.5.135.docx | Bin .../ltjt-order-assistant/README.md | 8 +- .../ltjt-order-assistant/inpage.js | 2 +- .../lifecycle-adapters.js | 5 +- .../ltjt-order-assistant/manifest.json | 2 +- .../ltjt-order-assistant/operation-plans.js | 2 +- .../ltjt-order-assistant/team-batch-inpage.js | 2 +- control-plane/src/business-routes.ts | 4 +- control-plane/src/program-parser.ts | 8 +- control-plane/test/program-parser.test.ts | 27 ++- dist/lwlt-arrangement-0.5.129.skill | Bin 0 -> 5965 bytes dist/lwlt-confirmation-0.5.129.skill | Bin 0 -> 4070 bytes dist/lwlt-lifecycle-0.5.129.skill | Bin 0 -> 8269 bytes dist/lwlt-newbooking-0.5.129.skill | Bin 0 -> 5961 bytes dist/lwlt-updating-0.5.129.skill | Bin 0 -> 6462 bytes dist/release-manifest.json | 44 ++--- dist/老挝联泰AI指令表-0.5.136.docx | Bin 0 -> 34570 bytes mappings/lifecycle.mapping.json | 13 +- samples/business-parser-gold-corpus.json | 9 +- schemas/standard_system_operation.schema.json | 2 +- tools/independent-room-update.test.mjs | 156 ++++++++++++++++++ tools/lifecycle-contract.test.mjs | 18 +- tools/repository-hygiene.test.mjs | 2 +- 40 files changed, 395 insertions(+), 59 deletions(-) create mode 100644 .project-docs/30-worklog/tasks/20260916-independent-dbl-update-01a0858b.md create mode 100644 archive/evidence/2026-09-17/independent-dbl-update-01a0858b/README.md create mode 100644 archive/releases/2026-09-17/independent-dbl-update-01a0858b/README.md rename {dist => archive/releases/2026-09-17/independent-dbl-update-01a0858b}/ltjt-order-assistant-0.5.186.zip (100%) rename {dist => archive/releases/2026-09-17/independent-dbl-update-01a0858b}/lwlt-arrangement-0.5.128.skill (100%) rename {dist => archive/releases/2026-09-17/independent-dbl-update-01a0858b}/lwlt-confirmation-0.5.128.skill (100%) rename {dist => archive/releases/2026-09-17/independent-dbl-update-01a0858b}/lwlt-lifecycle-0.5.128.skill (100%) rename {dist => archive/releases/2026-09-17/independent-dbl-update-01a0858b}/lwlt-newbooking-0.5.128.skill (100%) rename {dist => archive/releases/2026-09-17/independent-dbl-update-01a0858b}/lwlt-updating-0.5.128.skill (100%) create mode 100644 archive/releases/2026-09-17/independent-dbl-update-01a0858b/release-manifest.json rename {dist => archive/releases/2026-09-17/independent-dbl-update-01a0858b}/老挝联泰AI指令表-0.5.135.docx (100%) create mode 100644 dist/lwlt-arrangement-0.5.129.skill create mode 100644 dist/lwlt-confirmation-0.5.129.skill create mode 100644 dist/lwlt-lifecycle-0.5.129.skill create mode 100644 dist/lwlt-newbooking-0.5.129.skill create mode 100644 dist/lwlt-updating-0.5.129.skill create mode 100644 dist/老挝联泰AI指令表-0.5.136.docx create mode 100644 tools/independent-room-update.test.mjs diff --git a/.project-docs/30-worklog/tasks/20260916-independent-dbl-update-01a0858b.md b/.project-docs/30-worklog/tasks/20260916-independent-dbl-update-01a0858b.md new file mode 100644 index 0000000..a96a7eb --- /dev/null +++ b/.project-docs/30-worklog/tasks/20260916-independent-dbl-update-01a0858b.md @@ -0,0 +1,43 @@ +# 独立团房间修改语法与大床房执行 + +## 范围与授权 + +- 用户授权修复“房间数更改为 12 标+2 大”的解析及独立团大床房修改执行。 +- 补充确定性表达别名,DBL 使用当前表单 Schema 登记的 `frenshu3`,复用原生房间合计、保存边界与 fresh GET 核验。 +- 保留未指定房型和原有字段;不扩大其他未实现修改目标。 +- 本次不执行真实 ERP 写入、任务变更、部署、服务重启、插件重载、提交或推送。 + +## Worktree 检查 + +- Branch:`main`;Worktree:`/Users/andy/IdeaProjects/LWLT-AIBOT`。 +- Base:`a3505f3921c5af37dd01d3bffba70bfa6db198f6`;仅一个 worktree。 +- 既有未跟踪 `.idea/` 保留;本次目标无重叠,Overlap:Clear。 +- Feature 模式,只维护本任务项目记忆;未启用子 Agent。 + +## 实施计划 + +1. 增加“房间数”“更改为”“大”别名及冲突/非法输入回归。 +2. 扩展 DBL 字段映射,验证混合房型完整执行、合计、未指定值保留、保存后不一致失败和保存前阻断。 +3. 同步业务契约、插件最低版本、发布物及操作 DOCX,并完成全套本地检查。 + +## 结果与验证 + +- 已完成“房间数”“更改为”“大”的确定性解析兼容;原输入解析为 `rooms.TWN=12`、`rooms.DBL=2`,同义房型、重复目标、非法数量和未知残留均有回归。 +- 独立团 DBL 使用 `frenshu3`,与 SGL/TWN 共用写前非负整数校验、原生合计及保存后的 fresh GET 核验。只修改明确指定房型,其他房型、人数和备注保留;显式“大床房数改为0”可以清零。 +- 新增 6 个合成执行测试,覆盖混合房型、清零/同值、字段缺失、错误合计、保存后不一致、未实现目标整体阻断和只读回查。 +- 当前版本:程序解析器 `v1.0.13`,插件/平台最低版本 `0.5.187`,五个 Skill `0.5.129`,输入契约及运营 DOCX `0.5.136`。无需数据库迁移。 +- `node --run check:repo`、`node --run check`、`node --run build` 通过;控制面 272/272,legacy 423/423。检查使用当前源码与交付物的干净临时快照,保留当前工作区既有 `.idea/`,未清理或认领其内容。 +- 曾发现新增解析 Skill 说明混入执行机制,已将其收敛为业务解析语义,保留原检查并重新通过全部 legacy 测试。 +- 五个 Skill 官方校验通过;插件 ZIP 和 Skill 包逐文件与当前源全等,清单哈希通过;旧发布物按原清单哈希原样归档。 +- DOCX 已渲染并逐页检查全部 16 页,新增房型说明在第 11 页,无乱码、遮挡或截断。 +- 完成主线程只读最终核对;未启用多 Agent 工作流。真实 ERP 保存未执行,部署、重启和插件重载未执行,以上为本地验证结论。 +- 证据:[本地验证摘要](../../../archive/evidence/2026-09-17/independent-dbl-update-01a0858b/README.md)。 + +## 交付与后续 + +- 源码与版本化交付物已就绪,未提交或推送。部署方需同步更新服务端并重启、将扩展更新为 `0.5.187`、刷新平台及 ERP 页面后使用原指令。 +- 新版真实 ERP 的 DBL 保存及读取验收仍待另行授权;不得将合成回归表述为生产实写验证。 + +## Promotion Candidates + +- 下次 Integration 可将“独立团房型修改兼容房间数/更改为/大,并实现 SGL/TWN/DBL 明确字段修改”纳入当前状态和业务规则;证据为本任务测试、字段 Schema 和发布包核对。DBL 真实 ERP 验收状态继续标为待验证,不扩大其他字段执行能力。 diff --git a/LianSyn-platform/app.js b/LianSyn-platform/app.js index 37dcacd..9eb46e6 100644 --- a/LianSyn-platform/app.js +++ b/LianSyn-platform/app.js @@ -103,7 +103,7 @@ const persistedExtensionResultVersions = new Map(); let taskCreateInProgress = false; const AUTO_HANDOFF_RETRY_MS = 30_000; -const REQUIRED_EXTENSION_VERSION = '0.5.186'; +const REQUIRED_EXTENSION_VERSION = '0.5.187'; const MANUAL_HANDOFF_LABEL = '确认并提交到 ERP 插件'; const RETRY_HANDOFF_LABEL = '继续提交到 ERP 插件'; const RECONCILE_LABEL = '只读回查 ERP 现有结果'; diff --git a/agent设计规范/business-adaptation-registry.md b/agent设计规范/business-adaptation-registry.md index 67e5176..5e7912b 100644 --- a/agent设计规范/business-adaptation-registry.md +++ b/agent设计规范/business-adaptation-registry.md @@ -22,7 +22,7 @@ | `shared_child_order_create` | 散拼团单个新增子单/计划 | [`lwlt-newbooking`](skills/lwlt-newbooking/SKILL.md) / `shared_child_order_create` | [业务入口](businesses/shared_child_order_create.md) | [ERP 流程](businesses/shared_child_order_create.md) | [Skill actions](skills/lwlt-newbooking/references/actions-and-fields.md) | 已重复真实创建并完成后续全生命周期;仍受统一发布门槛约束 | | `passenger_list_import` | 独立团/散拼子单名单导入/补充 | [`lwlt-lifecycle`](skills/lwlt-lifecycle/SKILL.md) / `passenger_list_import`(Program-only) | [统一输入模板 §6—7](templates/business-input-templates.md);文字定位 + 单个 `.xls/.xlsx` 附件;程序在前 100 行自动定位唯一 ERP 字段表头,按精确语义从任意表头行和列顺序中选择 11 个必需字段及可选电话列(缺少时输出空值,存在时仍须唯一并校验),并读取其下连续名单数据;身份证、年龄、源证件类型和其他普通额外列忽略且不输出 | 识别 route 后等待附件 → 程序规范化为加密 13 列 TSV,并从唯一严格`领队`备注行提取姓名/电话 → 读取 ERP 游客位 → 未确认的占用序号要求覆盖确认;确认后所有指定序号强制覆写(同值也写)→ `DaoRuDones` → 原生游客位不足时返回“名单 N 人超过 ERP 最多 M 人”且不保存 → 恢复未指定行 → 投影领队联系人 → 完整写前投影 → 父页原生保存;ERP 明确返回成功即完成,不再逐行写后回查 | 用户模板 `.xls` 已只读通过 25 行规范化及单一领队行结构核验;解析阶段不以初始 16/31 行作统一静态上限,5000 为技术上限;执行时以精确 ERP 页面实际物化游客位为准。2026-08-28 一笔散拼子单 25 行及领队联系人已持久化全等;另一次独立团 25 人只物化 16 位并在保存前安全阻断 | 名单与领队联系人真实写入已有一笔证据;`0.5.155` 起确认覆盖同值也写,`0.5.156` 起容量不足使用人数/上限业务提示并仅在技术详情保留原始 blocker;其他对象/边界继续按发布门槛分级 | | `order_update_shared_plan` / `order_update_shared_child` | 散拼母团容量/散拼子单住宿说明 | [`lwlt-updating`](skills/lwlt-updating/SKILL.md) | [统一输入模板:修改](templates/business-input-templates.md) | `DoInfoSP` / `DoInfo_order`;写前读取当前值 | 母团容量、子单住宿说明通过;用户只填目标值;无年份定位日期取任务日后的下一次发生日 | Skill 全量解析;仅已验证字段自动执行,其他字段保留并转人工复核 | -| `order_update_independent` | 独立团信息修改 | [`lwlt-updating`](skills/lwlt-updating/SKILL.md) / `order_update_independent` | [统一输入模板 §15](templates/business-input-templates.md) | `DoInfoJH`;写前读取当前值;SGL/TWN 分别映射 `frenshu0`/`frenshu1`,成人/占床儿童/不占床儿童/领队分别映射 `darenshu`/`xiaorenshu`/`ertrenshu`/`quanrenshu` | 标间数 `8→9→8` 已有明确响应和 fresh GET;无年份定位/目标日期取下一次发生日;SGL/TWN 与四类人数待新版本授权 ERP 实写复测 | Skill 全量解析;当前插件执行标间数、SGL/TWN 与四类人数,其他目标保留并转人工复核 | +| `order_update_independent` | 独立团信息修改 | [`lwlt-updating`](skills/lwlt-updating/SKILL.md) / `order_update_independent` | [统一输入模板 §15](templates/business-input-templates.md) | `DoInfoJH`;写前读取当前值;SGL/TWN/DBL 分别映射 `frenshu0`/`frenshu1`/`frenshu3`,成人/占床儿童/不占床儿童/领队分别映射 `darenshu`/`xiaorenshu`/`ertrenshu`/`quanrenshu` | 标间数 `8→9→8` 已有明确响应和 fresh GET;无年份定位/目标日期取下一次发生日;SGL/TWN/DBL 与四类人数待新版本授权 ERP 实写复测 | Skill 全量解析;当前插件执行标间数、SGL/TWN/DBL 与四类人数,其他目标保留并转人工复核 | | `arrangement_guide` / `arrangement_vehicle` / `arrangement_hotel` / `arrangement_transport` / `arrangement_other` | 安排导游/用车/酒店/大交通/其他备案 | [`lwlt-arrangement`](skills/lwlt-arrangement/SKILL.md) | [统一输入模板 §8—12](templates/business-input-templates.md) | 五类 action-specific `DoInfo_*`,全类 `create|clear`;酒店另有窄 `update`,程序模式支持五类创建指令加“变更”;用车新增/变更支持选填用车类型,与结算单位搜索共同筛选唯一候选;大交通新增/变更支持选填车票说明,与结算单位共同筛选唯一候选;原日期仅定位已有安排,支持 MM-DD 由指定团队中唯一原记录确定年份,YYYY-MM-DD 精确匹配,歧义列候选并停止;导游按团队唯一已有导游定位 | 两类团队共 10 create + 10 clear;酒店资源/房型由唯一 ERP 模糊匹配结果带入;大交通省略车票说明时及其他/备案的结算单位组合候选按下拉顺序首条原生联动,其他/备案数量可选,省略时保留 ERP 默认值;散拼母团无子单时 ERP 禁止安排酒店并隐藏提交入口,插件在三项页面证据同时成立时提示先新增子单 | create/clear 与酒店窄 update 已有真实证据;程序模式五类资源替换变更已完成本地回归,待真实 ERP 验收;状态和直接修改联动字段继续阻断 | | `arrangement_hotel` | 安排变更 | [`lwlt-arrangement`](skills/lwlt-arrangement/SKILL.md) / `arrangement_hotel` | [统一输入模板 §16](templates/business-input-templates.md) | fresh 查询 → 精确未安排或未确认酒店行 → `DoInfo_jiudian` → 双回查 | `2026-09-12/8 → 2026-09-13/9`,备注与资源保持;无年份目标离店日取下一次发生日 | 窄能力已真实验证;其他目标转人工复核 | | `order_cancel` / `order_restore` | 取消与恢复 | [`lwlt-lifecycle`](skills/lwlt-lifecycle/SKILL.md) | [统一输入模板 §17—18](templates/business-input-templates.md) | 列表取消或编辑页状态;列表恢复禁用 | 三类对象及母子传播共 7 次状态验证通过;无年份定位日期取任务日后的下一次发生日 | 已接入分段执行链;统一发布待批准 | diff --git a/agent设计规范/skills/lwlt-updating/SKILL.md b/agent设计规范/skills/lwlt-updating/SKILL.md index b6a0a45..8fd11a0 100644 --- a/agent设计规范/skills/lwlt-updating/SKILL.md +++ b/agent设计规范/skills/lwlt-updating/SKILL.md @@ -21,6 +21,7 @@ description: 解析 LTJT 的散拼母团、散拼子单和独立团修改输入 - “增加/减少若干”无法得到目标总数时,追问目标总数。 - 识别并保留业务可见目标,如 `pax.*`、`rooms.*`、`prices.*`、`departure_date`、`customer`、`product`、`leader`、`route`、`trip_days`、`status`、`remark`、`booking_note` 等;不因某字段的后续处理能力不同而丢弃或拒绝。 - 人数组合只映射原文明确定义的类别:兼容旧三段 `人数改为8+1(占)+1`,并将 `人数改为8+1(占)+1(不占)+1` 映射为 `pax.adult=8`、`pax.child_bed=1`、`pax.child_no_bed=1`、`pax.leader=1`。未出现的婴儿等类别不自动清零;需要清零或只改某一类时使用“婴儿改为0”“成人改为…”等明确字段表达。 +- 房型组合兼容“房间数更改为12标+2大”,拆为 `rooms.TWN=12` 和 `rooms.DBL=2`;“大/大床/大床房”均指 DBL。“更改为”与“改为”均为 set;未指定房型保持原值,不凭总间数推断各房型。 - 订房说明追加内容不得超过 50 个字符;现有内容长度不由本 Skill 判断。 - 用户可见单号是兼容选填,填写时原样保留;独立团和散拼子单修改在无单号时要求预订客户+出发日期,散拼母团计划收客数修改在无单号时只要求出发日期,预订客户、产品名称和领队均可选填;不生成内部引用。 - 修改类的业务定位日期和目标出发日期若只写 `MM-DD`,按任务当前日期取下一次发生日:已过补下一年、未过补当前年;用户明确写出的年份保持不变,不得仅因月日缺少年份追问。 diff --git a/agent设计规范/skills/lwlt-updating/references/input-contract.md b/agent设计规范/skills/lwlt-updating/references/input-contract.md index 5ae3282..0946511 100644 --- a/agent设计规范/skills/lwlt-updating/references/input-contract.md +++ b/agent设计规范/skills/lwlt-updating/references/input-contract.md @@ -94,6 +94,8 @@ | 标间数 | `twin_room_count`(等价于 `rooms.TWN` 时按原文保留业务语义) | 正整数 | | 出发日期、产品、客户、领队、行程、天数、状态、备注 | `departure_date`、`product`、`customer`、`leader`、`route`、`trip_days`、`status`、`remark` | 按字段类型 | +房型组合可使用“房间数/用房数量/房型数量/用房/房间”搭配“改为/更改为/修改为/调整为”。例如“房间数更改为 12 标+2 大”仅设置 TWN=12、DBL=2;“大/大床/大床房”均为 DBL,不自动清零其他房型。不含房型的单一房间总数无法确定分配,应补充房型;清空大床房可明确写“大床房数改为0”。 + “人数改为8+1(占)+1(不占)+1”解析为 `pax.adult=8`、`pax.child_bed=1`、`pax.child_no_bed=1`、`pax.leader=1`。旧三段“人数改为8+1(占)+1”仍表示成人、占床儿童、领队。组合中未出现的婴儿等类别不自动写入或清零;需要修改时必须另写“婴儿改为0”等明确目标。一个输入可以生成多个 `updates.actions`;后续处理会依据字段能力决定是否需要复核,但不能在 Skill 层丢弃、改写或误报解析失败。 三类修改均兼容完整单号。填写时写入 `data.existing_refs.identifier`;未填写时只保留由首行确定的 `existing_refs.kind`。独立团和散拼子单保留四个业务检索字段;散拼母团计划收客数修改在无单号时只要求 `departure_dates`,`customer`、`product`、`leader` 按用户提供情况保留。`领队`姓名写入 `data.leader`,不要与人数类别混淆;母团计划列表下游将其映射到 `S_youkexinxi`。 diff --git a/agent设计规范/skills/lwlt-updating/references/operation-contract.md b/agent设计规范/skills/lwlt-updating/references/operation-contract.md index 9a147a2..61132a7 100644 --- a/agent设计规范/skills/lwlt-updating/references/operation-contract.md +++ b/agent设计规范/skills/lwlt-updating/references/operation-contract.md @@ -12,6 +12,8 @@ 解析边界与后续处理边界分离:`twin_room_count`、`planned_capacity`、`lodging_note` 是已通过 ERP 实测的目标;其他已识别字段必须保留在 operation 中,由后续流程决定是否需要复核,不得静默丢弃或改写。 +“房间数更改为12标+2大”输出两个 `set`:`rooms.TWN=12`、`rooms.DBL=2`。数量必须是非负整数;未指定房型不生成清零动作,其他已识别的修改意图仍完整保留。 + ## 对象引用 只使用用户可见单号: diff --git a/agent设计规范/templates/business-input-templates.md b/agent设计规范/templates/business-input-templates.md index 7c26dd3..d629c35 100644 --- a/agent设计规范/templates/business-input-templates.md +++ b/agent设计规范/templates/business-input-templates.md @@ -1,6 +1,6 @@ # 运营业务 AI 输入模板总表 -交付版本:0.5.135 +交付版本:0.5.136 适用范围:当前 23 条业务路由,按 19 项运营场景分组;执行能力以生命周期发布门槛为准。 本文件用于向系统提交 ERP 业务需求。复制对应模板填写;示例只供参考,不要直接用于生产业务。 @@ -423,6 +423,8 @@ 只填写需要修改的目标。人数中未出现的类别保持原值;需要清零时单独写明,例如`婴儿改为0`。 +房间修改可写`目标修改:房间数更改为12标+2大`,表示标间改为12间、大床房改为2间;也可写`用房数量改为12TWN+2DBL`。支持单间、标间、大床房同时修改,未填写的其他房型保持原值,房间总数根据各房型重新合计。需要清空大床房时写`大床房数改为0`。这是独立团订单的用房数量修改;具体酒店安排使用“安排酒店变更”。 + ### 16. 安排变更 新任务使用下面五种独立指令,按程序解析执行,并需开通对应业务权限。复制相应的完整模板填写,不要混入旧格式的“目标变更”或“安排类型”。 diff --git a/agent设计规范/test-fixtures/lwlt-lifecycle/release-gate.md b/agent设计规范/test-fixtures/lwlt-lifecycle/release-gate.md index 8744eb2..492fff0 100644 --- a/agent设计规范/test-fixtures/lwlt-lifecycle/release-gate.md +++ b/agent设计规范/test-fixtures/lwlt-lifecycle/release-gate.md @@ -2,11 +2,12 @@ ## 当前基线 -- Chrome 插件:`0.5.186` +- Chrome 插件:`0.5.187` +- `0.5.187` 与程序解析器 `v1.0.13` 支持独立团“房间数更改为 12 标+2 大”,DBL 映射 `frenshu3` 并参与原生合计及保存后 fresh GET 核验;语法、保存前阻断和保存后不一致的本地合成回归通过,真实 ERP 实写待部署方验收。服务端及插件需同步更新,无迁移。见[任务记录](../../../.project-docs/30-worklog/tasks/20260916-independent-dbl-update-01a0858b.md)。 - `0.5.186` 修复车票说明中车次与时刻的数字边界,明确供应商与说明联合唯一匹配及多候选提示;新增/变更本地合成回归通过,真实 ERP 验收待部署方完成。见[任务记录](../../../.project-docs/30-worklog/tasks/20260916-transport-description-match-01a0858b.md)。 - Agent Prompt:`ltjt-agent-prompt-v1.8-independent-headcount-categories` - 生命周期契约:`ltjt-lifecycle-v2.9-roster-leader-contact-2026-08` -- 五个 Skill:`0.5.128`;运营 DOCX:`0.5.135` +- 五个 Skill:`0.5.129`;运营 DOCX:`0.5.136` - 发布状态:核心窄能力已有真实证据;一笔名单附件 Program-only 任务已把 25 行及唯一领队联系人正确保存到 ERP,后续只读逐字段核验全等。两种写后逐行读取方式都曾在真实成功写入后产生假阴性;名单在全部写前门禁通过后,以父表单 ERP 明确成功响应作为终结证据,不再执行写后逐行回查或自动名单对账。`0.5.155` 规定确认覆盖的指定序号同值也写;`0.5.156` 把原生游客位不足统一映射为包含名单人数和 ERP 实际上限的业务提示,原技术 blocker 仅留技术详情。`0.5.157` 新增散拼母团“整团游客信息”的 `shared_plan + visitor-list + tid-only` 窄分支,静态契约与回归已通过,仍待授权后的新版运行态只读复测。`0.5.158` 把正式控制面 `https://lwlt.nianxx.cn` 同步加入 Popup、后台、Manifest 权限和 content script 精确白名单;其他 HTTPS origin 仍保持阻断。`0.5.159` 修复 MV3 后台恢复被中断后持久化 `running` 永久阻断插件派发的问题:仅在内存恢复 Promise 已不存在时收敛为 `interrupted`,真实活动恢复仍保持阻断,下一次成功只读保活把状态归零为 `idle`。`0.5.160` 为独立团单个下单写前漂移增加仅字段名诊断,并把数字后缀动态应收字段纳入保护。`0.5.161` 等待客户、跟单人和销售人原生 lookup 数据就绪后才开始单团预检;生产反馈证明仅检查 lookup 数据仍不足。`0.5.162` 进一步要求页面 jQuery Ajax 在预检前归零,并在 `GetProduct` 后等待原生 `ajaxStop` 再应用最终业务字段。`0.5.163` 按生产操作方明确要求恢复 `0.5.156` 的实际校验范围:核心订单字段继续写前阻断,带编号的动态 `ys_*` 应收行不再纳入保护哈希,允许 ERP 原生回调在预检后调整这些字段。`0.5.164` 新增平台账号、AgentBus 渠道、唯一云电脑 worker 与 ERP 登录身份四方一致门禁;旧未绑定渠道和历史未归属 AgentBus 任务默认不执行。`0.5.165` 让散拼新增计划和独立团批量下单在本地候选未命中时先使用表单原生非空产品检索,实测两类表单均能由完整关键词唯一返回目标产品;本轮未勾选或保存,自动选择与写入仍由原门禁保护。新增独立团房型/人数映射仍待实写;未验证宽能力继续失败关闭。 - `0.5.166` 在散拼母团列表入口加入立即检查、100ms 条件轮询和 15 秒总超时;每次探测都重新取得当前主 iframe document,按钮一旦可用便立即继续。该等待只发生在点击前,不固定拖慢快速页面,也不触发业务写入重试。 diff --git a/archive/evidence/2026-09-17/independent-dbl-update-01a0858b/README.md b/archive/evidence/2026-09-17/independent-dbl-update-01a0858b/README.md new file mode 100644 index 0000000..5c7b20b --- /dev/null +++ b/archive/evidence/2026-09-17/independent-dbl-update-01a0858b/README.md @@ -0,0 +1,17 @@ +# 独立团房型修改本地验证 + +此目录记录 2026-09-17 的历史验证结论,不是活动业务规则。任务:`20260916-independent-dbl-update-01a0858b`,基于 `a3505f3921c5af37dd01d3bffba70bfa6db198f6` 的当前变更。只使用合成客户与合成 ERP 表单,不含真实旅客信息、凭据或 ERP 写入。 + +## 已验证 + +- “房间数更改为 12 标+2 大”解析为 TWN=12、DBL=2;既有输入格式兼容。裸总间数、重复房型/目标、非法数量及未识别文字阻断。 +- 合成表单通过真实字段修改、原生校验、表单序列化、模拟 POST 和 fresh GET 核验链;查找/页面就绪及对象所有权证明在测试中隔离。 +- DBL 对应 `frenshu3`;标间/大床房同时更新,其他房型、人数、备注保留;显式 DBL=0 清零;合计正确。缺字段、非法原房数及错误原生合计保存前阻断;保存后大床房或合计不一致不能报告成功;未实现目标整体阻断;只读回查不发送 POST。 +- 仓库检查、静态检查和编译通过;控制面 272/272,legacy 423/423,其中新增执行回归 6 项。全量验证使用与当前变更一致的干净临时快照,既有 `.idea/` 未变动。 +- 五个 Skill 官方校验通过;插件 `0.5.187` 和五个 Skill `0.5.129` 包与当前源逐文件全等;清单及旧发布物归档哈希核对通过。 +- 运营 DOCX `0.5.136` 已完整渲染检查 16 页;第 11 页新增房型说明,未发现布局或中文字体问题。 + +## 边界 + +- 未部署、重启服务、重载扩展或操作真实 ERP;新增 DBL 保存仍待部署后的授权实测。 +- 结论针对本次源与发布清单;解析规则、页面字段、原生合计或保存核验发生变更时,应重新验证。 diff --git a/archive/releases/2026-09-17/independent-dbl-update-01a0858b/README.md b/archive/releases/2026-09-17/independent-dbl-update-01a0858b/README.md new file mode 100644 index 0000000..4239f3f --- /dev/null +++ b/archive/releases/2026-09-17/independent-dbl-update-01a0858b/README.md @@ -0,0 +1,3 @@ +# 独立团大床房修改发布前的交付物 + +此目录为只读历史,不能作为当前业务规则。按原 SHA-256 保留插件 0.5.186、五个 Skill 0.5.128、操作说明 0.5.135 与更新前发布清单。当前交付物仅以根目录 dist/release-manifest.json 为准。 diff --git a/dist/ltjt-order-assistant-0.5.186.zip b/archive/releases/2026-09-17/independent-dbl-update-01a0858b/ltjt-order-assistant-0.5.186.zip similarity index 100% rename from dist/ltjt-order-assistant-0.5.186.zip rename to archive/releases/2026-09-17/independent-dbl-update-01a0858b/ltjt-order-assistant-0.5.186.zip diff --git a/dist/lwlt-arrangement-0.5.128.skill b/archive/releases/2026-09-17/independent-dbl-update-01a0858b/lwlt-arrangement-0.5.128.skill similarity index 100% rename from dist/lwlt-arrangement-0.5.128.skill rename to archive/releases/2026-09-17/independent-dbl-update-01a0858b/lwlt-arrangement-0.5.128.skill diff --git a/dist/lwlt-confirmation-0.5.128.skill b/archive/releases/2026-09-17/independent-dbl-update-01a0858b/lwlt-confirmation-0.5.128.skill similarity index 100% rename from dist/lwlt-confirmation-0.5.128.skill rename to archive/releases/2026-09-17/independent-dbl-update-01a0858b/lwlt-confirmation-0.5.128.skill diff --git a/dist/lwlt-lifecycle-0.5.128.skill b/archive/releases/2026-09-17/independent-dbl-update-01a0858b/lwlt-lifecycle-0.5.128.skill similarity index 100% rename from dist/lwlt-lifecycle-0.5.128.skill rename to archive/releases/2026-09-17/independent-dbl-update-01a0858b/lwlt-lifecycle-0.5.128.skill diff --git a/dist/lwlt-newbooking-0.5.128.skill b/archive/releases/2026-09-17/independent-dbl-update-01a0858b/lwlt-newbooking-0.5.128.skill similarity index 100% rename from dist/lwlt-newbooking-0.5.128.skill rename to archive/releases/2026-09-17/independent-dbl-update-01a0858b/lwlt-newbooking-0.5.128.skill diff --git a/dist/lwlt-updating-0.5.128.skill b/archive/releases/2026-09-17/independent-dbl-update-01a0858b/lwlt-updating-0.5.128.skill similarity index 100% rename from dist/lwlt-updating-0.5.128.skill rename to archive/releases/2026-09-17/independent-dbl-update-01a0858b/lwlt-updating-0.5.128.skill diff --git a/archive/releases/2026-09-17/independent-dbl-update-01a0858b/release-manifest.json b/archive/releases/2026-09-17/independent-dbl-update-01a0858b/release-manifest.json new file mode 100644 index 0000000..2beca46 --- /dev/null +++ b/archive/releases/2026-09-17/independent-dbl-update-01a0858b/release-manifest.json @@ -0,0 +1,69 @@ +{ + "manifest_version": 1, + "generated_on": "2026-09-16", + "baselines": { + "chrome_extension": "0.5.186", + "skills": "0.5.128", + "business_instruction_docx": "0.5.135", + "agent_prompt": "ltjt-agent-prompt-v1.8-independent-headcount-categories" + }, + "artifacts": [ + { + "kind": "chrome_extension", + "version": "0.5.186", + "path": "dist/ltjt-order-assistant-0.5.186.zip", + "source": "chrome-extension/ltjt-order-assistant", + "sha256": "c79d108ce4cbed85a856b336e1643fd990d81f21d16a0bbd66be27510d1c2568" + }, + { + "kind": "skill", + "name": "lwlt-arrangement", + "version": "0.5.128", + "path": "dist/lwlt-arrangement-0.5.128.skill", + "source": "agent设计规范/skills/lwlt-arrangement", + "sha256": "ae3fc66965d2a7968aeaa2ecf371d75e5365ef4cc4dfd8ef080f9343e0d0f09c" + }, + { + "kind": "skill", + "name": "lwlt-confirmation", + "version": "0.5.128", + "path": "dist/lwlt-confirmation-0.5.128.skill", + "source": "agent设计规范/skills/lwlt-confirmation", + "sha256": "c2f3689d8a0b4d6b250bb2cfce572fe3a9fca3f5519ea5fae5ef0d382b44dae4" + }, + { + "kind": "skill", + "name": "lwlt-lifecycle", + "version": "0.5.128", + "path": "dist/lwlt-lifecycle-0.5.128.skill", + "source": "agent设计规范/skills/lwlt-lifecycle", + "sha256": "fcf23bb0e12e7026f03dc3670fe35db77c740a9902a7a415e2efa464d4eceb1e" + }, + { + "kind": "skill", + "name": "lwlt-newbooking", + "version": "0.5.128", + "path": "dist/lwlt-newbooking-0.5.128.skill", + "source": "agent设计规范/skills/lwlt-newbooking", + "sha256": "b64593d88145966cd4e702d2532ffe3ea85052e8d56fbe75d503a19ababa1715" + }, + { + "kind": "skill", + "name": "lwlt-updating", + "version": "0.5.128", + "path": "dist/lwlt-updating-0.5.128.skill", + "source": "agent设计规范/skills/lwlt-updating", + "sha256": "54f10dd6bd6d2ddf47c700f1bca4b1fcd3346dc6143bf27118a23587b1b11736" + }, + { + "kind": "business_instruction_docx", + "version": "0.5.135", + "path": "dist/老挝联泰AI指令表-0.5.135.docx", + "source": "agent设计规范/templates/business-input-templates.md", + "builder": "tools/build_business_instruction_docx.py", + "sha256": "55dcd03b7ddb43cf3333497faeddc105aff3d57729e06cff200a7e190cc95ea2", + "source_sha256": "a28ecf96a09c9c9dfdeed590ccff44999ecd019abd3f30102c98b6cbb66c5f62", + "builder_sha256": "c162884210da22e3b78368749b66f1f177df5e9fc6155f4fd9954bd516d187f1" + } + ] +} diff --git a/dist/老挝联泰AI指令表-0.5.135.docx b/archive/releases/2026-09-17/independent-dbl-update-01a0858b/老挝联泰AI指令表-0.5.135.docx similarity index 100% rename from dist/老挝联泰AI指令表-0.5.135.docx rename to archive/releases/2026-09-17/independent-dbl-update-01a0858b/老挝联泰AI指令表-0.5.135.docx diff --git a/chrome-extension/ltjt-order-assistant/README.md b/chrome-extension/ltjt-order-assistant/README.md index 32bfeff..2877336 100644 --- a/chrome-extension/ltjt-order-assistant/README.md +++ b/chrome-extension/ltjt-order-assistant/README.md @@ -11,9 +11,13 @@ Chrome Manifest V3 扩展,在用户已登录的 LTJT ERP 页面内执行经过 ## 当前版本 -当前源码版本为 `0.5.186`。版本化 ZIP、文件哈希和 Skill/DOCX 基线见 [`../../dist/release-manifest.json`](../../dist/release-manifest.json)。旧版本实现流水已冻结在 [`../../archive/project-history/2026-08-16/chrome-extension-README.pre-governance.md`](../../archive/project-history/2026-08-16/chrome-extension-README.pre-governance.md)。 +当前源码版本为 `0.5.187`。版本化 ZIP、文件哈希和 Skill/DOCX 基线见 [`../../dist/release-manifest.json`](../../dist/release-manifest.json)。旧版本实现流水已冻结在 [`../../archive/project-history/2026-08-16/chrome-extension-README.pre-governance.md`](../../archive/project-history/2026-08-16/chrome-extension-README.pre-governance.md)。 -0.5.186 当前重点: +0.5.187 当前重点: + +- 独立团修改新增 DBL 大床房到 frenshu3 的字段映射,与 SGL/TWN 一样按明确目标值填写,重新计算房间合计,并通过保存后 fresh GET 核对大床房和合计。程序解析器 v1.0.13 兼容“房间数更改为 12 标+2 大”;未指定房型保持原值,其他未实现字段继续整体阻断。需同步更新服务端、插件和平台最低版本,无数据库迁移;本地合成验证通过,真实 ERP 实写待验收。 + +继续保留 0.5.186 的车票匹配修复: - 大交通选填车票说明在忽略空格/括号时保留原数字边界,修复 `C91 12:52` 无法用 `C91` 匹配的问题,继续阻止 `C91` 命中 `C911`。供应商与说明仍须匹配同一唯一候选;仅路线也可在唯一时使用,歧义时停止并显示最多 5 条供应商/说明,不暴露内部 ID 或价格。新增/变更以及原生联动前复核使用同一规则。零候选与候选未加载分别提示;未填车票说明的旧行为保持。回归为本地合成验证,真实 ERP 验收待部署方执行;需更新插件及平台最低版本,本修复无数据库迁移。 diff --git a/chrome-extension/ltjt-order-assistant/inpage.js b/chrome-extension/ltjt-order-assistant/inpage.js index c503938..09e8162 100644 --- a/chrome-extension/ltjt-order-assistant/inpage.js +++ b/chrome-extension/ltjt-order-assistant/inpage.js @@ -6619,7 +6619,7 @@ } window.LTJTOrderAssistant = { - version: '0.5.186', + version: '0.5.187', resolveNativeListSearchValues, lookupKeywordMatchesText, inspectLifecycleSearchCriteria: lifecycleSearchCriteria, diff --git a/chrome-extension/ltjt-order-assistant/lifecycle-adapters.js b/chrome-extension/ltjt-order-assistant/lifecycle-adapters.js index b2f293f..323b1eb 100644 --- a/chrome-extension/ltjt-order-assistant/lifecycle-adapters.js +++ b/chrome-extension/ltjt-order-assistant/lifecycle-adapters.js @@ -41,6 +41,7 @@ twin_room_count: 'frenshu1', 'rooms.SGL': 'frenshu0', 'rooms.TWN': 'frenshu1', + 'rooms.DBL': 'frenshu3', 'pax.adult': 'darenshu', 'pax.child_bed': 'xiaorenshu', 'pax.child_no_bed': 'ertrenshu', @@ -446,7 +447,7 @@ && (!Number.isInteger(item.value) || item.value <= 0)) blockers.push('independent_twin_room_count_invalid'); if (action === 'order_update_independent' && /^pax\.(?:adult|child_bed|child_no_bed|leader)$/.test(target) && (!Number.isInteger(item.value) || item.value < 0)) blockers.push(`independent_passenger_count_invalid:${target}`); - if (action === 'order_update_independent' && /^rooms\.(?:SGL|TWN)$/.test(target) + if (action === 'order_update_independent' && /^rooms\.(?:SGL|TWN|DBL)$/.test(target) && (!Number.isInteger(item.value) || item.value < 0)) blockers.push(`independent_room_count_invalid:${target}`); if (action === 'order_update_shared_plan' && (!Number.isInteger(item.value) || item.value <= 0)) blockers.push('planned_capacity_invalid'); if (action === 'order_update_shared_child' && (typeof item.value !== 'string' || !item.value.trim())) blockers.push('lifecycle_update_value_missing'); @@ -2864,7 +2865,7 @@ if (changes.length > beforeCount) { changes[changes.length - 1].target = item.target; if (operation.action === 'order_update_independent' - && (item.target === 'twin_room_count' || /^rooms\.(?:SGL|TWN)$/.test(item.target))) roomCountChanged = true; + && (item.target === 'twin_room_count' || /^rooms\.(?:SGL|TWN|DBL)$/.test(item.target))) roomCountChanged = true; } } if (roomCountChanged) { diff --git a/chrome-extension/ltjt-order-assistant/manifest.json b/chrome-extension/ltjt-order-assistant/manifest.json index 29b9a80..6ed06a1 100644 --- a/chrome-extension/ltjt-order-assistant/manifest.json +++ b/chrome-extension/ltjt-order-assistant/manifest.json @@ -1,7 +1,7 @@ { "manifest_version": 3, "name": "联泰下单助手", - "version": "0.5.186", + "version": "0.5.187", "description": "在已登录 LTJT ERP 页面内规划并受控执行联泰 ERP 业务操作。", "permissions": [ "activeTab", diff --git a/chrome-extension/ltjt-order-assistant/operation-plans.js b/chrome-extension/ltjt-order-assistant/operation-plans.js index 748fba2..690a5a1 100644 --- a/chrome-extension/ltjt-order-assistant/operation-plans.js +++ b/chrome-extension/ltjt-order-assistant/operation-plans.js @@ -211,7 +211,7 @@ ]; const EXISTING_REF_KINDS = new Set(['independent_order', 'shared_plan', 'shared_child_order']); const UPDATE_TARGETS = Object.freeze({ - order_update_independent: new Set(['twin_room_count', 'rooms.SGL', 'rooms.TWN', 'pax.adult', 'pax.child_bed', 'pax.child_no_bed', 'pax.leader']), + order_update_independent: new Set(['twin_room_count', 'rooms.SGL', 'rooms.TWN', 'rooms.DBL', 'pax.adult', 'pax.child_bed', 'pax.child_no_bed', 'pax.leader']), order_update_shared_plan: new Set(['planned_capacity']), order_update_shared_child: new Set(['lodging_note']) }); diff --git a/chrome-extension/ltjt-order-assistant/team-batch-inpage.js b/chrome-extension/ltjt-order-assistant/team-batch-inpage.js index 95809b7..73a11b6 100644 --- a/chrome-extension/ltjt-order-assistant/team-batch-inpage.js +++ b/chrome-extension/ltjt-order-assistant/team-batch-inpage.js @@ -944,7 +944,7 @@ window.LTJTOrderAssistant = { ...(window.LTJTOrderAssistant || {}), - version: '0.5.186', + version: '0.5.187', openTeamBatchForm, pingTeamBatchFrame, preflightTeamBatchNative, diff --git a/control-plane/src/business-routes.ts b/control-plane/src/business-routes.ts index 9e15aef..8fb71d0 100644 --- a/control-plane/src/business-routes.ts +++ b/control-plane/src/business-routes.ts @@ -79,8 +79,8 @@ export function effectiveParserModeForRoute(route: BusinessRouteDefinition, conf return route.parserPolicy === 'program_only' ? 'program' : 'ai'; } -const PROGRAM_VERSION = 'ltjt-program-parser-v1.0.12'; -export const BUSINESS_INPUT_CONTRACT_VERSION = 'business-input-templates-0.5.135'; +const PROGRAM_VERSION = 'ltjt-program-parser-v1.0.13'; +export const BUSINESS_INPUT_CONTRACT_VERSION = 'business-input-templates-0.5.136'; const lookupFields = ({ customerRequired = true } = {}): ProgramFieldDefinition[] => [ { key: 'order_no', label: '单号', aliases: ['团号', '订单号'], kind: 'text' }, diff --git a/control-plane/src/program-parser.ts b/control-plane/src/program-parser.ts index a9f75d3..78a656b 100644 --- a/control-plane/src/program-parser.ts +++ b/control-plane/src/program-parser.ts @@ -387,14 +387,14 @@ const ROOM_CODES = new Map([ ['SGL', 'SGL'], ['单', 'SGL'], ['单间', 'SGL'], ['单人间', 'SGL'], ['TWN', 'TWN'], ['标', 'TWN'], ['标间', 'TWN'], ['双标间', 'TWN'], ['TRP', 'TRP'], ['三', 'TRP'], ['三人间', 'TRP'], - ['DBL', 'DBL'], ['双', 'DBL'], ['双人间', 'DBL'], ['大床', 'DBL'], ['大床房', 'DBL'] + ['DBL', 'DBL'], ['双', 'DBL'], ['双人间', 'DBL'], ['大床', 'DBL'], ['大床房', 'DBL'], ['大', 'DBL'] ]); function parseRoomCounts(rawValue: string): FieldParseResult { const value = compactText(rawValue).replace(/\s+/gu, ''); const result: JsonRecord = {}; const consumed: string[] = []; - const pattern = /(\d+)\s*(SGL|TWN|TRP|DBL|单人间|单间|单|双标间|标间|标|三人间|三|双人间|大床房|大床|双)/giu; + const pattern = /(\d+)\s*(SGL|TWN|TRP|DBL|单人间|单间|单|双标间|标间|标|三人间|三|双人间|大床房|大床|大|双)/giu; for (const match of value.matchAll(pattern)) { const code = ROOM_CODES.get(match[2].toUpperCase()) || ROOM_CODES.get(match[2]); if (!code) continue; @@ -475,7 +475,7 @@ function splitControlledClauses(rawValue: string): string[] { } function controlledAssignment(clause: string): { target: string; value: string } | null { - const match = clause.match(/^(.+?)(?:调整为|变更为|修改为|改为|设为|=|:|:)\s*(.+)$/u); + const match = clause.match(/^(.+?)(?:调整为|变更为|修改为|更改为|改为|设为|=|:|:)\s*(.+)$/u); const target = compactText(match?.[1] || '').replace(/\s+/gu, ''); const value = compactText(match?.[2] || ''); return target && value ? { target, value } : null; @@ -504,7 +504,7 @@ function parseIndependentUpdates(rawValue: string, context: DateContext, nextOcc } continue; } - if (/^(?:用房数量|房型数量|用房|房间)$/u.test(target)) { + if (/^(?:用房数量|房型数量|房间数|用房|房间)$/u.test(target)) { const rooms = parseRoomCounts(value); if (!rooms.ok) return { ok: false, code: 'program_invalid_value', message: rooms.message, field: 'update_targets' }; for (const [key, count] of Object.entries(rooms.value as JsonRecord)) { diff --git a/control-plane/test/program-parser.test.ts b/control-plane/test/program-parser.test.ts index 34b510b..eb44c0c 100644 --- a/control-plane/test/program-parser.test.ts +++ b/control-plane/test/program-parser.test.ts @@ -189,7 +189,7 @@ test('versioned sanitized gold corpus replays deterministically', async () => { const corpus = JSON.parse(await readFile( new URL('../../samples/business-parser-gold-corpus.json', import.meta.url), 'utf8' )); - assert.equal(corpus.version, 'ltjt-program-parser-v1.0.12'); + assert.equal(corpus.version, 'ltjt-program-parser-v1.0.13'); assert.ok(corpus.cases.length >= 28); for (const candidate of corpus.cases) { const parse = () => parseProgramInput({ @@ -313,6 +313,31 @@ test('passenger TSV rejects duplicate leaders or a leader without both name and } }); +test('independent room updates accept DBL shorthand without dropping other intentions', async () => { + for (const target of [ + '房间数更改为 12 标+2 大', '用房数量改为12标+2大床', + '房型数量修改为12TWN+2DBL', '房间调整为12标间+2大床房' + ]) { + const result = await parseProgramInput({ rawText: `独立团信息修改\n预订客户:测试客户\n出发日期:2026-09-16\n目标修改:${target}`, receivedAt }); + assert.equal(result.status, 'agent_parse_passed', JSON.stringify(result)); + assert.deepEqual((result.operation as any).data.updates.actions, [ + { target: 'rooms.TWN', operation: 'set', value: 12 }, + { target: 'rooms.DBL', operation: 'set', value: 2 } + ]); + } + const combined = await parseProgramInput({ rawText: '独立团信息修改\n预订客户:测试客户\n出发日期:2026-09-16\n目标修改:房间数更改为12标+2大,成人更改为28', receivedAt }); + assert.equal(combined.status, 'agent_parse_passed'); + assert.equal((combined.operation as any).data.updates.actions[2].value, 28); + for (const target of ['房间数更改为12', '房间数更改为12标+2大楼', '房间数更改为12标+2大+1DBL', '大床房数改为-1', '房间数更改为12标+2大;DBL改为3']) { + const result = await parseProgramInput({ rawText: `独立团信息修改\n预订客户:测试客户\n出发日期:2026-09-16\n目标修改:${target}`, receivedAt }); + assert.notEqual(result.status, 'agent_parse_passed', target); + assert.equal(result.operation, null); + } + const clear = await parseProgramInput({ rawText: '独立团信息修改\n预订客户:测试客户\n出发日期:2026-09-16\n目标修改:大床房数更改为0', receivedAt }); + assert.equal(clear.status, 'agent_parse_passed'); + assert.deepEqual((clear.operation as any).data.updates.actions, [{ target: 'rooms.DBL', operation: 'set', value: 0 }]); +}); + test('independent headcount distributions keep occupied and non-occupied children distinct', async () => { const parseHeadcount = (value: string) => parseProgramInput({ rawText: routeCases[14].input.replace( diff --git a/dist/lwlt-arrangement-0.5.129.skill b/dist/lwlt-arrangement-0.5.129.skill new file mode 100644 index 0000000000000000000000000000000000000000..dea23f8ad7512b1d04e3b3133238f4ce93f5d391 GIT binary patch literal 5965 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 JAnAW@{{;_973=^2 literal 0 HcmV?d00001 diff --git a/dist/lwlt-confirmation-0.5.129.skill b/dist/lwlt-confirmation-0.5.129.skill new file mode 100644 index 0000000000000000000000000000000000000000..63898bff889c2ebbd13431fc70d924bee0f163bc GIT binary patch literal 4070 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 a(|?pYin1`D{y{+hy(oV}Zd&4hUjGX{Mz2r+ literal 0 HcmV?d00001 diff --git a/dist/lwlt-newbooking-0.5.129.skill b/dist/lwlt-newbooking-0.5.129.skill new file mode 100644 index 0000000000000000000000000000000000000000..3b9aa9e170837671a4eeee706ebb65b167bb926a GIT binary patch literal 5961 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 literal 0 HcmV?d00001 diff --git a/dist/lwlt-updating-0.5.129.skill b/dist/lwlt-updating-0.5.129.skill new file mode 100644 index 0000000000000000000000000000000000000000..5abfbf65d3440438b4f12c234f841070791df197 GIT binary patch literal 6462 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 50" @@ -429,14 +429,15 @@ "field_mapped_pending_live_validation": { "rooms.SGL": "frenshu0", "rooms.TWN": "frenshu1", + "rooms.DBL": "frenshu3", "pax.adult": "darenshu", "pax.child_bed": "xiaorenshu", "pax.child_no_bed": "ertrenshu", "pax.leader": "quanrenshu" }, - "parsed_business_fields": "Skill 可解析人数、房型、日期、客户、产品、领队、行程、状态、备注和价格等用户明确目标;未逐字段实测的目标不得自动写入", - "executable_business_updates": ["set one or more non-negative integer rooms.SGL/rooms.TWN targets, one positive integer twin_room_count, or one or more non-negative passenger counts per request; passenger-count mappings are pending the next fresh authorized ERP validation"], - "manual_review_policy": "保留完整 updates.actions;当前插件对 twin_room_count、rooms.SGL、rooms.TWN、pax.adult、pax.child_bed、pax.child_no_bed、pax.leader 做字段映射和写后回查,其余目标在插件预检阶段转人工复核且 no_erp_write=true", + "parsed_business_fields": "Skill 可解析人数、房型、日期、客户、产品、领队、行程、状态、备注和价格等用户明确目标;自动执行仅限当前明确列出的字段映射,未实现目标整体转人工复核;新增映射的真实 ERP 验收状态单独记录", + "executable_business_updates": ["set one or more non-negative integer rooms.SGL/rooms.TWN/rooms.DBL targets, one positive integer twin_room_count, or one or more non-negative passenger counts per request; passenger-count mappings are pending the next fresh authorized ERP validation"], + "manual_review_policy": "保留完整 updates.actions;当前插件对 twin_room_count、rooms.SGL、rooms.TWN、rooms.DBL、pax.adult、pax.child_bed、pax.child_no_bed、pax.leader 做字段映射和写后回查,其余目标在插件预检阶段转人工复核且 no_erp_write=true", "allowed_non_business_operation": "receivable_fixture.clear_generated_zero only, using exact current-run row ids", "known_result": "booking_note failed to persist both before and after arrangement history; HTTP 200 alone is never success", "release_state": "wide_parse_field_mapped_room_and_passenger_counts_pending_live_validation_other_fields_manual_review" diff --git a/samples/business-parser-gold-corpus.json b/samples/business-parser-gold-corpus.json index 2622a9e..d7bbddd 100644 --- a/samples/business-parser-gold-corpus.json +++ b/samples/business-parser-gold-corpus.json @@ -1,5 +1,5 @@ { - "version": "ltjt-program-parser-v1.0.12", + "version": "ltjt-program-parser-v1.0.13", "received_at": "2026-08-25T09:00:00+08:00", "description": "脱敏的 19 路由确定性解析 gold corpus;只包含合成业务值。", "cases": [ @@ -234,6 +234,13 @@ "expected_status": "agent_parse_blocked", "expected_error_code": "program_unsupported_syntax", "input": "独立团信息修改\n单号:SYNTH-GROUP-001\n目标修改:内部计调编号改为123" + }, + { + "id": "independent-room-count-dbl-shorthand", + "route_id": "order_update_independent", + "expected_status": "agent_parse_passed", + "expected_action": "order_update_independent", + "input": "独立团信息修改\n预订客户:合成客户\n出发日期:2026-09-16\n产品名称:合成产品\n目标修改:房间数更改为 12 标+2 大" } ] } diff --git a/schemas/standard_system_operation.schema.json b/schemas/standard_system_operation.schema.json index c1193cf..7025afc 100644 --- a/schemas/standard_system_operation.schema.json +++ b/schemas/standard_system_operation.schema.json @@ -2161,7 +2161,7 @@ "An HTTP 200 response without an explicit success signal and a matching fresh requery is never a completed lifecycle write.", "For shared-plan order_cancel, the adapter must discover every concrete child from a fresh SP_OrderList response, fetch every child detail afresh, and require every child status to equal 已取消 before completion.", "Shared-plan order_restore restores only the parent; every child status must be re-read and no child may be reported restored without its own order_restore operation.", - "Update parsing preserves all recognized business-visible targets and allows multiple actions; the browser adapter automatically submits only its live-validated target map (currently independent twin_room_count, rooms.SGL, rooms.TWN and the mapped passenger counts, shared-plan planned_capacity and shared-child lodging_note), while other recognized targets require manual review. Exact clear_generated_zero receivable cleanup remains a separate internal operation on the same route.", + "Update parsing preserves all recognized business-visible targets and allows multiple actions; the browser adapter automatically submits only its implemented target map (currently independent twin_room_count, rooms.SGL, rooms.TWN, rooms.DBL and the mapped passenger counts, shared-plan planned_capacity and shared-child lodging_note; newly mapped room and passenger counts still require authorized live ERP validation), while other recognized targets require manual review. Exact clear_generated_zero receivable cleanup remains a separate internal operation on the same route.", "Arrangement create uses status 未安排 for hotels and 未确认 for other arrangement types. Arrangement update targets one exact existing hotel row, status 未安排 or 未确认. Legacy updates change end_date/room_count at slot 0; Program date-selected updates may replace resource and its ERP-linked room_type plus room_count at the resolved slot. Program-only guide/vehicle/transport/other replacements now target a unique existing record; dates only locate it. Native selection supplies linked fields, optional fields remain unchanged when omitted, payment/audit/confirmed/realized guards and historical test-context restrictions still apply.", "A multi-date shared_plan_create carrying customer/passenger split facts remains restricted to the explicit shared_batch_split_order_probe test context; E2E3/E2E4 established the ERP behavior, but ordinary release still requires business approval and every date must retain native response plus object-level requery.", "confirmation_export reads and validates the ERP source; visitor-list retains the original ERP XLS as a platform archive artifact and generates a real XLSX delivery artifact, with AgentBus/WeChat limited to the XLSX. If XLSX conversion fails, external attachment delivery is blocked instead of returning the raw XLS. Other types are converted to PDF before attachment delivery and fall back to their source file if conversion fails. It must not accept a requested output format or claim scheduling or external delivery." diff --git a/tools/independent-room-update.test.mjs b/tools/independent-room-update.test.mjs new file mode 100644 index 0000000..a207609 --- /dev/null +++ b/tools/independent-room-update.test.mjs @@ -0,0 +1,156 @@ +import assert from 'node:assert/strict'; +import test from 'node:test'; +import { readFileSync } from 'node:fs'; +import { loadPageActionRealm } from './page-action-test-realm.mjs'; + +const source = readFileSync(new URL('../chrome-extension/ltjt-order-assistant/lifecycle-adapters.js', import.meta.url), 'utf8'); +// Isolate room editing from separately covered lookup/readiness logic. The +// real field application, validation, serialization, POST, fresh GET and +// result classification run against a synthetic ERP form and transport. +const adapter = source.replace('assistant.preflightLifecycleOperation = preflightLifecycleOperation;', ` + preflightLifecycleOperation = async (operation) => { + const blockers = operationBlockers(operation); + return { status: blockers.length ? 'lifecycle_preflight_blocked' : 'lifecycle_preflight_ready', + blockers, no_erp_write: true, write_attempted: false, preflight: {} }; + }; + ownershipListProof = async () => ({ matched: true }); + assistant.preflightLifecycleOperation = preflightLifecycleOperation;`); + +function fixture({ initial = {}, missing = [], persisted = {}, wrongTotal = false, fallbackTotal = false } = {}) { + const requests = []; + let saved = null; + let sumCalls = 0; + const fields = new Map(); + const window = { location: new URL('https://erp.invalid/System/Business/orders_add.asp?ddid=123&tdid=456'), setTimeout, clearTimeout }; + const document = { body: { innerText: 'LW-SYNTHETIC' }, querySelector: () => form, querySelectorAll: () => [] }; + const form = { id: 'ListForm', elements: [], ownerDocument: document }; + document.defaultView = window; + for (const [name, value] of Object.entries({ + tdid: 456, ddid: 123, frenshu0: 1, frenshu1: 8, frenshu2: 1, frenshu3: 1, + frenshu4: 0, frenshu5: 1, frenshu6: 12, darenshu: 20, xiadanbeizhu: '保留备注', ...initial + })) { + if (missing.includes(name)) continue; + const control = { name, id: name, value: String(value), type: 'text', tagName: 'INPUT', + getAttribute(key) { return key === 'value' ? this.value : null; }, dispatchEvent() {} }; + fields.set(name, control); form.elements.push(control); + } + const freshDocument = () => { + const values = { ...saved, ...persisted }; + const freshForm = { elements: Object.entries(values).map(([name, value]) => ({ name, id: name, + tagName: 'INPUT', type: 'text', value: String(value), getAttribute: (key) => key === 'value' ? String(value) : null })) }; + return { body: { innerText: 'LW-SYNTHETIC' }, querySelector(selector) { + if (selector.includes('#ListForm') || selector.includes('#InfoForm1')) return freshForm; + return freshForm.elements.find((control) => selector.includes(`[name="${control.name}"]`)) || null; + }, querySelectorAll: () => [] }; + }; + window.Vidy = { CheckMastInput: () => true }; + if (!fallbackTotal) window.sum_fang = () => { + sumCalls++; + fields.get('frenshu6').value = wrongTotal ? '999' : String([0, 1, 2, 3, 4, 5].reduce((sum, i) => sum + Number(fields.get(`frenshu${i}`).value || 0), 0)); + }; + const globals = { + window, document, location: window.location, Event, URL, URLSearchParams, TextEncoder, + crypto: globalThis.crypto, AbortController, Blob, setTimeout, clearTimeout, + DOMParser: class { parseFromString() { return freshDocument(); } }, + async fetch(url, options) { + requests.push({ url, ...options }); + if (options.method === 'POST') saved = Object.fromEntries(new URLSearchParams(options.body)); + return { ok: true, status: 200, text: async () => options.method === 'POST' ? '修改成功' : 'LW-SYNTHETIC' }; + } + }; + loadPageActionRealm(window, globals, { 'lifecycle-adapters.js': adapter }); + const operation = { action: 'order_update_independent', order_nature: 'formal', submit_mode: 'dry_run', data: { + existing_refs: { kind: 'independent_order', identifier: 'LW-SYNTHETIC', tid: '456', ddid: '123', resolved: true, resolution_source: 'erp_unique_match' }, + updates: { actions: [ + { target: 'rooms.TWN', operation: 'set', value: 12 }, + { target: 'rooms.DBL', operation: 'set', value: 2 } + ] } + } }; + return { api: window.LTJTOrderAssistant, plans: window.LTJTOperationPlans, operation, fields, + requests, sumCalls: () => sumCalls, saved: () => saved }; +} + +test('mixed TWN/DBL updates serialize both fields, preserve other rooms and verify a fresh GET', async () => { + for (const fallbackTotal of [false, true]) { + const f = fixture({ fallbackTotal }); + const plan = f.plans.validateOperation(f.operation); + assert.equal(plan.ok, true, JSON.stringify(plan)); + assert.doesNotMatch(plan.warnings.join(), /rooms.DBL/); + const result = await f.api.liveSubmitLifecycleOperation(f.operation); + assert.equal(result.status, 'lifecycle_completed', JSON.stringify(result)); + assert.equal(f.saved().Act, 'DoInfoJH'); + assert.equal(f.saved().frenshu1, '12'); + assert.equal(f.saved().frenshu3, '2'); + assert.equal(f.saved().frenshu6, '17'); + assert.equal(f.saved().frenshu0, '1'); + assert.equal(f.saved().frenshu2, '1'); + assert.equal(f.saved().frenshu4, '0'); + assert.equal(f.saved().frenshu5, '1'); + assert.equal(f.saved().xiadanbeizhu, '保留备注'); + assert.equal(f.saved().darenshu, '20'); + assert.equal(f.requests.filter((r) => r.method === 'POST').length, 1); + assert.equal(f.requests.at(-1).cache, 'no-store'); + assert.equal(f.sumCalls(), fallbackTotal ? 0 : 1); + assert.ok(result.requery.checks.some((check) => check.field === 'frenshu3' && check.matched)); + } +}); + +test('DBL zero is an explicit reset and identical room targets never resubmit', async () => { + const f = fixture(); + f.operation.data.updates.actions = [{ target: 'rooms.DBL', operation: 'set', value: 0 }]; + assert.equal((await f.api.liveSubmitLifecycleOperation(f.operation)).status, 'lifecycle_completed'); + assert.equal(f.saved().frenshu3, '0'); + assert.equal(f.saved().frenshu1, '8'); + assert.equal(f.saved().frenshu6, '11'); + const same = fixture({ initial: { frenshu1: 12, frenshu3: 2, frenshu6: 17 } }); + assert.match((await same.api.liveSubmitLifecycleOperation(same.operation)).blockers.join(), /lifecycle_update_no_effect/); + assert.equal(same.requests.length, 0); +}); + +test('a successful POST cannot hide a missing or stale DBL value or total in fresh ERP data', async () => { + for (const persisted of [{ frenshu3: '1' }, { frenshu3: '' }, { frenshu6: '16' }]) { + const f = fixture({ persisted }); + const result = await f.api.liveSubmitLifecycleOperation(f.operation); + assert.equal(result.status, 'lifecycle_execution_uncertain'); + assert.equal(result.write_attempted, true); + assert.equal(result.requery.matched, false); + assert.match(result.blockers.join(), /write_after_requery_mismatch/); + assert.equal(f.requests.filter((r) => r.method === 'POST').length, 1); + } +}); + +test('missing controls, invalid existing rooms and incorrect native totals stop before saving', async () => { + for (const options of [{ missing: ['frenshu3'] }, { missing: ['frenshu6'] }, { initial: { frenshu2: '-1' } }, { wrongTotal: true }]) { + const f = fixture(options); + const result = await f.api.liveSubmitLifecycleOperation(f.operation); + assert.equal(result.status, 'lifecycle_apply_blocked', JSON.stringify(result)); + assert.equal(result.no_erp_write, true); + assert.equal(f.requests.length, 0); + } +}); + +test('invalid DBL targets and unimplemented fields block the whole mixed operation', async () => { + for (const item of [ + { target: 'rooms.DBL', operation: 'set', value: -1 }, + { target: 'rooms.DBL', operation: 'set', value: 1.5 }, + { target: 'rooms.DBL', operation: 'delta', value: 1 }, + { target: 'rooms.TRP', operation: 'set', value: 2 } + ]) { + const f = fixture(); + f.operation.data.updates.actions[1] = item; + const result = await f.api.liveSubmitLifecycleOperation(f.operation); + assert.equal(result.status, 'lifecycle_preflight_blocked', JSON.stringify(result)); + assert.equal(f.fields.get('frenshu1').value, '8'); + assert.equal(f.requests.length, 0); + } +}); + +test('read-only reconciliation verifies the requested DBL field and never posts', async () => { + const f = fixture({ persisted: { frenshu1: '12', frenshu3: '2' } }); + const result = await f.api.verifyLifecycleOperation(f.operation); + assert.equal(result.status, 'lifecycle_requery_matched', JSON.stringify(result)); + assert.ok(result.requery.checks.some((check) => check.field === 'frenshu3' && check.matched)); + assert.equal(f.requests.length, 1); + assert.equal(f.requests[0].method, undefined); + assert.equal(f.requests[0].cache, 'no-store'); +}); diff --git a/tools/lifecycle-contract.test.mjs b/tools/lifecycle-contract.test.mjs index fc5e3c9..765f096 100644 --- a/tools/lifecycle-contract.test.mjs +++ b/tools/lifecycle-contract.test.mjs @@ -1374,7 +1374,7 @@ test('passenger explicit server success is terminal without a post-save row requ assert.doesNotMatch(passengerBranch, /passengerRequery|verifyLifecycleOperation/); assert.doesNotMatch(background, /attemptAutomaticPassengerReconciliation/); assert.match(background, /名单已取得 ERP 明确成功响应,按业务规则确认录入成功/); - assert.match(platformApp, /REQUIRED_EXTENSION_VERSION = '0\.5\.186'/); + assert.match(platformApp, /REQUIRED_EXTENSION_VERSION = '0\.5\.187'/); }); test('uncertain lifecycle writes can only converge through a read-only plugin requery', async () => { @@ -1584,7 +1584,7 @@ test('lifecycle execution keeps the MV3 worker alive until the durable task sett assert.match(background, /arrangement_resource_candidate_data_missing/); }); -test('extension 0.5.186 keeps reload dormant unless its durable execution state is fully idle', async () => { +test('extension 0.5.187 keeps reload dormant unless its durable execution state is fully idle', async () => { const [background, bridge] = await Promise.all([ readFile(new URL('../chrome-extension/ltjt-order-assistant/background.js', import.meta.url), 'utf8'), readFile(new URL('../chrome-extension/ltjt-order-assistant/business-bridge.js', import.meta.url), 'utf8') @@ -1805,6 +1805,7 @@ test('update actions expose validated writes and preserve unvalidated fields for { target: 'twin_room_count', operation: 'set', value: 9 }, { target: 'rooms.SGL', operation: 'set', value: 3 }, { target: 'rooms.TWN', operation: 'set', value: 2 }, + { target: 'rooms.DBL', operation: 'set', value: 2 }, { target: 'pax.adult', operation: 'set', value: 8 }, { target: 'pax.child_bed', operation: 'set', value: 1 }, { target: 'pax.child_no_bed', operation: 'set', value: 1 }, @@ -1827,6 +1828,7 @@ test('update actions expose validated writes and preserve unvalidated fields for const adapter = readFileSync(new URL('../chrome-extension/ltjt-order-assistant/lifecycle-adapters.js', import.meta.url), 'utf8'); assert.match(adapter, /'rooms\.SGL': 'frenshu0'/); assert.match(adapter, /'rooms\.TWN': 'frenshu1'/); + assert.match(adapter, /'rooms\.DBL': 'frenshu3'/); assert.match(adapter, /ownerWindow\?\.sum_fang/); assert.match(adapter, /frenshu6/); assert.match(adapter, /independent_room_total_mismatch/); @@ -1835,7 +1837,7 @@ test('update actions expose validated writes and preserve unvalidated fields for assert.match(adapter, /'pax\.child_no_bed': 'ertrenshu'/); assert.match(adapter, /'pax\.leader': 'quanrenshu'/); assert.match(adapter, /independent_room_count_invalid/); - assert.doesNotMatch(plans.validateOperation(independent).warnings.join('\n'), /rooms\.SGL|rooms\.TWN/); + assert.doesNotMatch(plans.validateOperation(independent).warnings.join('\n'), /rooms\.SGL|rooms\.TWN|rooms\.DBL/); assert.doesNotMatch(plans.validateOperation(independent).warnings.join('\n'), /pax\.adult|pax\.child_bed|pax\.child_no_bed|pax\.leader/); const negativeRoomCount = structuredClone(independent); @@ -2035,7 +2037,7 @@ test('delete guard distinguishes independent, shared child, and shared parent ro for (const operation of operations) assert.equal(plans.validateOperation(operation).ok, true, plans.validateOperation(operation).blockers.join('; ')); const mapping = JSON.parse(await readFile(new URL('../mappings/lifecycle.mapping.json', import.meta.url), 'utf8')); - assert.equal(mapping.current_extension_version, '0.5.186'); + assert.equal(mapping.current_extension_version, '0.5.187'); assert.equal( mapping.updates.order_update_independent.field_mapped_pending_live_validation['pax.child_no_bed'], 'ertrenshu' @@ -2419,10 +2421,10 @@ test('schema and browser adapters contain the v2 safety fields and no confirm ov assert.match(inpage, /`ys_danweiid\$\{index\}`, resolvedCustomerId/); assert.doesNotMatch(inpage, /product_customer_source_region|sourceRegionCheck|source_reference/); assert.doesNotMatch(teamBatchInpage, /product_customer_source_region|sourceRegionCheck/); - assert.equal(extensionManifest.version, '0.5.186'); - assert.match(inpage, /version: '0\.5\.186'/); - assert.match(teamBatchInpage, /version: '0\.5\.186'/); - assert.match(platformApp, /REQUIRED_EXTENSION_VERSION = '0\.5\.186'/); + assert.equal(extensionManifest.version, '0.5.187'); + assert.match(inpage, /version: '0\.5\.187'/); + assert.match(teamBatchInpage, /version: '0\.5\.187'/); + assert.match(platformApp, /REQUIRED_EXTENSION_VERSION = '0\.5\.187'/); assert.match(inpage, /function strictIsoDate\(value\)/); assert.match(inpage, /const startDate = strictIsoDate\(current\('riqi'\)\)/); assert.match(inpage, /const endDate = strictIsoDate\(current\('riqis'\)\)/); diff --git a/tools/repository-hygiene.test.mjs b/tools/repository-hygiene.test.mjs index 6e488eb..d4fa84e 100644 --- a/tools/repository-hygiene.test.mjs +++ b/tools/repository-hygiene.test.mjs @@ -304,7 +304,7 @@ test('single-source and generated-output boundaries remain explicit', async () = const diagnoseServer = await readFile(path.join(ROOT, 'infra/diagnose-server.sh'), 'utf8'); const gitignore = await readFile(path.join(ROOT, '.gitignore'), 'utf8'); - assert.match(template, /^交付版本:0\.5\.135\s*$/mu); + assert.match(template, /^交付版本:0\.5\.136\s*$/mu); assert.match(template, /^适用范围:当前 23 条业务路由,按 19 项运营场景分组;执行能力以生命周期发布门槛为准。\s*$/mu); assert.match(template, /^### 5\. 散拼团多个新增子单\s*$/mu); assert.doesNotMatch(template, /^事实基线:/mu);