# 任务卡前端字段变更与路由说明(开发共识版) > 对比基线:`任务卡前端展示字段表 3.0.xlsx` > 当前规则基线:2026-07-10 17:50(Asia/Shanghai) > 业务共识基线:2026-07-10(房型/Rate Code、Parent split、Note/Trace、Rooming List/TA Recorder、人工复核) > `booking-desk-event.skill` SHA-256:`b5f33e17c642fefc11b41934cdd657ff2df25ea50b544c7a44424c5d3d977192` > 文档定位:本文是信息系统 adapter/任务卡开发的确定性交接契约。如项目 reference 中仍存在旧的含混表述,本文中已标记为“已确认业务决策”的规则优先供开发实现,后续再回写上游 skill。 ## 1. 开发先看结论 1. 当前 Agent **没有**为业务结果直接输出 `result_type`、`task_type`、`task_subtype`。普通业务的真实路由键是 `message_events[].event_type`;只有 S10/S99 原生输出 `result_type=source_message_review_notification`。 2. 三元组应由信息系统 adapter **按每个 `message_events[i]` 派生**,不能在邮件根对象只算一次。一封邮件可以同时生成多张候选卡。 3. `message_events[]` 是候选事件,不是真实 TaskCard;信息系统仍需做 Case 匹配、Preflight、去重、锁和状态检查后再建卡。 4. S10、S99 与 `unhandled_current_intents[]` 都是展示路径,不创建业务 TaskCard。 5. 业务 `Need Manual Review` 与 S99 不是同一种复核:前者是 `business_event_review`,后者是 `main_agent_entry_review`。 6. 房型或 Rate Code 无法唯一映射时,默认转业务人工复核;唯一例外是 Parent split 中某个 child 的房型原文仅为 `SUITE`,该 child 可保留为 New Booking 候选事件,但不得成为可执行任务。 7. Parent split 固定产生 **1 个 Parent Group Cancel Booking 候选 + N 个 Child Group New Booking 候选**,不经过 `Allotment Maintenance`。 8. 每个 Rooming List 目标必须派生一个与该 group/预订明确绑定的 TA Recorder。 9. 修改已有结构化订单字段/备注走 Update;新增补充事实、要求或安排走 Trace;`Note` 不再开放新任务卡。 10. 业务人工复核不按原业务类型细分卡型。进入 `manual_review` 后,卡内业务字段全量可编辑;原邮件、证据、系统 ID 和审计字段仍只读。 ### 1.1 已确认业务决策 | 主题 | 最终规则 | 开发不得做的事 | | --- | --- | --- | | 房型/Rate Code 歧义 | 默认 `Need Manual Review` | 不得猜 PMS 房型或 Rate Code | | Parent split + raw `SUITE` | 只对该 child 保留 `room_type_raw=SUITE`,`pms_room_type_code=null`,`requires_downstream_hard_validation=true` | 不得填 `SU1/SU3/SU6`;不得创建可执行任务或写 PMS | | Parent split | Parent 是 Group Block 取消;children 是 Group Block New Booking | 不得路由到 `Cancel Allotment` 或 `Allotment Maintenance` | | Rooming List | 每个已确认目标必须同时产生 1 个 Rooming List + 1 个 TA Recorder | 不得按邮件/workbook/sheet 只产生 1 个 TA Recorder | | 修改备注 vs 新增补充 | 修改已有结构化值走 Update;新增信息走 Trace | 不得用 `Note` 承接普通补充信息 | | 业务人工复核 | 统一人工复核卡,业务字段可编辑 | 不得依赖 `intended_event_type` 拆分复核卡 | 当前正式契约以以下文件组合为准: - `prompts/main_agent_prompt.md` - `skills/booking-desk-event/SKILL.md` - `skills/booking-desk-event/references/00-output-contract.md` - `skills/booking-desk-event/references/01-current-history-boundary.md` - `skills/booking-desk-event/references/03-current-content-completeness.md` - `skills/booking-desk-event/references/02-event-routing-map.md` - 各业务 reference `task_plan.md`、`findings.md`、`progress.md` 是累积工作日志,不是接口契约。`.skill` 包也不包含 Main Agent prompt,部署时两者必须同步。 ## 2. 信息系统标准三元组 下表是本次确认冻结的 **adapter 输出**。其中 `normal_task`、`manual_review` 延续 3.0 的信息系统语义;`source_message_review_notification` 来自当前上游;`unhandled_current_intent` 是为非任务展示新增的下游枚举。 `result_type` 是本文和 3.0 表使用的标准名。如信息系统实体内部字段名为 `task_result`,必须做同义归一:`task_result := result_type`;不得当成第四个独立路由字段。信息系统的人工复核判别组合固定为 `task_type=Fallback + task_result=manual_review`,`task_subtype=business_event_review`。 | result_type | task_type | task_subtype | 上游判别 | 信息系统具体去向 | 是否建业务卡 | | --- | --- | --- | --- | --- | --- | | `source_message_review_notification` | `Message Notification` | `S10` | 根 `route_code=S10` | 源邮件查看通知组件 | 否 | | `source_message_review_notification` | `Message Notification` | `S99` | 根 `route_code=S99` | 源邮件查看通知组件 + 入口复核信息 | 否 | | `normal_task` | `New Booking` | `new_fit_reservation` | `event_type=New Booking` 且 `extracted_fields.booking_object_type=FIT Reservation` | New Booking 卡(FIT) | 字段校验通过后为候选卡 | | `normal_task` | `New Booking` | `new_group_block` | `event_type=New Booking` 且 `extracted_fields.booking_object_type=Group Block` | New Booking 卡(Group Block) | 字段校验通过后为候选卡 | | `normal_task` | `New Booking` | `new_allotment_control_block` | `event_type=New Booking` 且 `extracted_fields.booking_object_type=Allotment / Control Block` | New Booking 卡(Allotment / Control Block) | 候选卡,Preflight 后 | | `normal_task` | `Update Booking` | `update_booking_amendment` | `event_type=Update Booking / Amendment` | Update Booking 卡;修改项另外展示 | 候选卡,Preflight 后 | | `normal_task` | `Cancel Booking` | `cancel_fit_reservation` | `event_type=Cancel Booking` 且 `extracted_fields.cancel_object_type=fit_reservation` | Cancel Booking 卡(FIT) | 字段校验通过后为候选卡 | | `normal_task` | `Cancel Booking` | `cancel_group_block` | `event_type=Cancel Booking` 且 `extracted_fields.cancel_object_type=group_block` | Cancel Booking 卡(Group Block) | 字段校验通过后为候选卡 | | `normal_task` | `Cancel Booking` | `linked_parent_release_after_child_split` | `event_type=Cancel Booking`;`cancel_object_type=group_block`;`relationship_type` 同名 | Cancel Booking 卡(Parent Group) | 候选卡,必须下游硬校验 | | `normal_task` | `Cancel Allotment` | `cancel_allotment_control_block` | `event_type=Cancel Allotment` | Cancel Allotment 卡 | 候选卡,Preflight 后 | | `normal_task` | `Voucher Received` | `lian_tai_credit_voucher` | `event_type=Voucher Received` | Voucher Received 卡 | 候选卡,Preflight 后 | | `normal_task` | `Payment Evidence` | `payment_evidence` | `event_type=Payment Evidence` | Payment Evidence 卡 | 候选卡,Preflight 后 | | `normal_task` | `Rooming List` | `rooming_list` | `event_type=Rooming List` | Rooming List 卡 | 候选卡,Preflight 后 | | `normal_task` | `Amend Group Code` | `amend_group_code` | `event_type=AMEND GROUP CODE` | Amend Group Code 卡 | 候选卡,Preflight 后 | | `normal_task` | `Trace / Reservation Notes` | `extra_bed` | `event_type=Trace` 且 `extracted_fields.trace_subtype=extra_bed` | Trace 卡(Extra Bed) | 候选卡,Preflight 后 | | `normal_task` | `Trace / Reservation Notes` | `general_request` | `event_type=Trace` 且 `extracted_fields.trace_subtype=general_request` | Trace 卡(General Request) | 候选卡,Preflight 后 | | `normal_task` | `TA Recorder` | `maintain_ta_recorder` | `event_type=TA RECORDER`;与同目标 Rooming List 关联 | TA Recorder 卡(从对应 Rooming List 派生) | 每目标必产生 1 张;Preflight 后 | | `normal_task` | `Invoice Generation` | `invoice_generation` | `event_type=Invoice Generation` | Invoice Generation 卡 | 候选卡;业务字段待冻结 | | `normal_task` | `Invoice Received` | `invoice_received` | `event_type=Invoice Received` | Invoice Received 卡 | 候选卡;业务字段待冻结 | | `normal_task` | `Payment Notice` | `payment_notice` | `event_type=Payment Notice` | Payment Notice 卡 | 候选卡;业务字段待冻结 | | `normal_task` | `Manual RateCode` | `manual_rate_code` | `event_type=Manual RateCode` | Manual RateCode 卡 | 候选卡;业务字段待冻结 | | `manual_review` | `Fallback` | `business_event_review` | `event_type=Need Manual Review` 且 `manual_review.review_record_type=business_event_review` | 统一业务人工复核卡;业务字段全量可编辑 | 是,但不得执行业务写入 | | `unhandled_current_intent` | `Unhandled Current Intent` | `requires_business_approval_or_unsupported_task_card` | 每个 `unhandled_current_intents[i]` | 邮件详情内未覆盖意图展示块 | 否 | 说明: - `task_type` 保留 3.0 已存在的信息系统卡名;上游 `event_type` 的大小写和空格必须按当前契约原值匹配。 - New Booking 的 FIT/Group 对象枚举和普通 Cancel 的 `extracted_fields.cancel_object_type` 按本文第 5 节直接冻结;不能靠前端猜测。 - Update 的多种修改可以同时存在,因此统一使用一个卡 subtype;不得再用单个 action token 决定卡型。 - 人工复核统一使用 `manual_review + Fallback + business_event_review`。前端只需通过 `result_type=manual_review`(或内部别名 `task_result=manual_review`)切换到全量业务字段可编辑模式,不再需要 `manual_review.intended_event_type`。 - Invoice、Payment Notice、Manual RateCode 已进入支持事件目录,但详细 payload 尚未完整冻结。首版只能使用通用事件头、目标、证据和人工复核字段。 ### 2.1 Parent split 的确定性事件集 Parent split 不是 `Allotment Maintenance`,也不是一张复合卡。必须在同一封邮件的 `message_events[]` 中产生: 1. 每个 child group 一个 `New Booking`: - `booking_object_type=Group Block` - adapter 三元组为 `normal_task + New Booking + new_group_block` - `case_keys.group_code=` 2. 一个 parent group `Cancel Booking` 候选: - `cancel_object_type=group_block` - adapter 三元组为 `normal_task + Cancel Booking + linked_parent_release_after_child_split` - `case_keys.group_code=` - `child_group_codes[]` 包含全部 children,`related_source_event_indices[]` 指向全部 child New Booking 事件 - `requires_downstream_hard_validation=true` Parent 取消事件只是候选。下游确认 parent group、全部 child 关系和 PMS 当前状态前,不得创建可执行取消任务,不得写入 PMS。 ### 2.2 Parent split 的 generic `SUITE` 唯一例外 只有当一个 child 的唯一不确定项是房型原文 `SUITE` 时,该 child 才可继续输出 New Booking 候选事件: ```json { "extracted_fields": { "room_type_raw": "SUITE", "pms_room_type_code": null }, "requires_downstream_hard_validation": true } ``` - 不得猜测或填写 `SU1`、`SU3`、`SU6`。 - 下游确认具体 PMS 房型代码前,该 child 只是不可执行候选,不得写 PMS。 - 校验失败时,只将该 child 转业务人工复核;不得阻塞或丢弃同邮件中其他已合格的 child 事件。 - Rate Code 不唯一不在此例外内;仍必须转业务人工复核。 ## 3. Adapter 路由顺序 ```text 1. 如果根 result_type == source_message_review_notification: - route_code 必须是 S10 或 S99;映射通知组件;不遍历业务卡。 2. 否则按业务根处理: - 校验五个根字段存在; - 遍历 message_events[],每个 event 独立计算一组三元组; - 遍历 unhandled_current_intents[],每个 item 生成一个展示块; - case_candidates[] 和 extraction_warnings[] 不参与任务卡路由。 3. 任意稳定判别字段缺失或出现未知 event_type: - 对该 event fail closed,记录接口契约错误并展示源邮件; - 不得按房量、关键词、附件名等在前端重新执行 AI 业务判断; - 不得为该无效 event 自动创建 TaskCard;根结构仍合法时,其他合法 sibling events 继续独立处理。 ``` 关键判别: - New Booking:必须读取 `extracted_fields.booking_object_type`。 - Update:固定 subtype=`update_booking_amendment`;具体修改用 action 数组或 `before_after[]` 展示。 - Cancel:先判断 parent split 的 `relationship_type`;命中时 `cancel_object_type` 必须是 `group_block`,再处理其他普通 cancel object。 - Trace:只允许 `extracted_fields.trace_subtype=extra_bed|general_request`。 - Need Manual Review:必须同时检查 `manual_review.review_record_type=business_event_review`。 - S99 的 `main_agent_entry_review` 只展示在 S99 通知中,不能创建 Fallback 业务卡。 - Unhandled Current Intent:`text_raw` 和 `visible_message` 都必须展示;前者是证据原文,后者是给用户的中文提示。 ### 3.1 “契约错误”和“人工复核”不再混用 | 情况 | 系统处理 | 该项自动业务卡数 | | --- | --- | --- | | 未知/禁用 `event_type`,或已声明某路由但必填判别字段缺失 | 记录 `adapter_contract_error`,保留并展示源邮件;该 event 不转业务复核 | 0 | | 业务类型已识别,但房型/Rate Code/目标/证据存在业务歧义 | 生成统一 `manual_review + Fallback + business_event_review` 卡 | 1 张人工复核卡,0 张可执行卡 | | Parent split 的业务意图已确认,但 parent group 或 parent-child 绑定无法确认 | 该 parent 取消转统一业务人工复核;合法 child events 仍独立处理 | parent 可执行卡 0;人工复核 1 | | event 声明 `linked_parent_release_after_child_split`,但 `parent_group_code/child_group_codes/related_source_event_indices` 等契约字段缺失 | 这是 payload 契约错误,不是业务歧义;记录错误并拒绝该 event | 0 | 因此,开发文档中不再使用“0 卡/复核”这类二选一表述。契约错误和业务不确定必须按上表分开。 ## 4. 3.0 之后已发生的字段变化 ### 4.1 已新增,开发必须消费 | 字段/结构 | 变化 | 开发动作 | | --- | --- | --- | | `source_message` | 所有合规结果新增源邮件身份 | 根级保存并用于原邮件跳转 | | `source_message.source_message_id` | 升级为非空必填,不得猜测 | 缺失视为上游/基础设施错误 | | `message_events[]` | 邮件级多事件数组 | 每个 item 独立路由;不能一封邮件只建一张卡 | | `case_candidates[]` | 新根字段 | 保留;item schema 未冻结,暂不消费 | | `extraction_warnings[]` | 新根字段/语义收窄 | 只显示解析/OCR/抽取告警,不建卡 | | `unhandled_current_intents[]` | 新增第五个业务根字段 | 每项同时展示 `visible_message` 和 `text_raw`,永不建卡 | | `message_events[].event_role` | 新增事件角色 | 审计展示;当前常见值 `travel_agent_request` | | `message_events[].current_or_history` | 新增证据边界 | 正常新事件必须为 `current` | | `message_events[].source_event_index` | 新增批次内关系索引 | 仅用于事件关联,不是 TaskCard ID | | `case_keys.reservation_number` / `block_code` | 通用目标 key 扩展为四键 | 与 group/confirmation 一起固定保留 | | `file_references[]` | 通用文件引用 | 与 `attachments[]` 分开归一,缺失补 `[]` | | `context_used` | 新增历史/系统证据审计对象 | 只做审计和硬校验,不做卡型推断 | | `related_source_event_index` | 新增单来源关系 | linked Trace 等场景使用 | | `related_source_event_indices[]` | 新增多来源关系 | parent release 关联多个 child 时使用 | | `related_event_type` | 新增关联事件类型 | 关系展示 | | `relationship_type` | 新增关系类型 | `linked_trace`、`linked_parent_release_after_child_split` 或 `derived_ta_recorder_from_rooming_list` | | `requires_downstream_hard_validation` | 新增下游硬校验开关 | 为 true 时禁止自动执行 | | `extracted_fields.room_type_raw` | 将房型原文与 normalized/PMS code 分开 | 始终保留原文;不再用单一 `room_type` 同时表示原文和标准值 | | `extracted_fields.pms_room_type_code` | 从默认必填改为“唯一映射后才可填” | 歧义时保持 `null`;Parent split raw `SUITE` 例外中禁止填猜测值 | | `extracted_fields.nights` | 住期新增晚数 | 与 arrival/departure 一起显示/校验 | | `extracted_fields.date_evidence` | 新增酒店/行程/动作/sheet 原始日期证据 | 复核区展示;action date 不是入住日期 | | `extracted_fields.trace_items[]` | Trace 新增结构化明细 | 同一目标仍是一张 Trace,不按 item 拆卡 | | `trace_items[].category/text_raw/service_date/service_period_raw/pax/notify_departments` | Trace 明细字段新增 | 按原顺序展示 | | `extracted_fields.requires_rate_update` | Extra Bed 新增费率更新意图 | 只表示意图,不计算价格 | | parent release/cancel 一组字段 | 新增 parent/child、release reason、候选标记 | 固定映射 Parent Group Cancel Booking 特殊候选卡,使用硬校验 | | TA Recorder 派生关系 | 每个 Rooming List 目标必须派生 | `related_source_event_index=`;`related_event_type=Rooming List`;`relationship_type=derived_ta_recorder_from_rooming_list`;两者 `case_keys` 必须一致 | | QBD/LianTai row evidence | 新增 sheet/row/highlight/status/raw date/room/price 等证据 | 复核区展示;一个有效行一个事件 | | `rate_code_result.manual_price_reason_code` | 手工价原因新增 | 手工价提示 | | `rate_code_result.price_evidence[]` | 价格证据新增 | 审计/复核展示 | | Fix Charge item 扩展字段 | 新增 amount/currency/unit/quantity/total/raw/evidence/follow-up | 保留但不自动调用 TBD 工具 | | `additional_operations[]` | 新增后续操作意图 | 只显示 pending,不直接执行 | | S10/S99 notification 结构 | 新增 route/assessment/notification/user-decision | 统一源邮件查看组件 | ### 4.2 已修改或迁移 | 3.0 字段/枚举 | 当前处理 | 开发动作 | | --- | --- | --- | | 根级/卡级旧字段路径 | 业务字段现在位于 `message_events[i]` | normalizer 加事件数组前缀 | | `result_type/task_type/task_subtype` | 业务 Agent 不再原生输出 | adapter 逐 event 派生 | | 信息系统内部 `task_result` | 如存在,仅是 `result_type` 的别名 | normalizer 统一为 `task_result := result_type` | | `Update Booking` 多个 action 作为 subtype | 一个 Update 可多 action | subtype 固定 `update_booking_amendment`,动作另存数组/明细 | | 修改已有备注/结构化字段 | `Update Booking / Amendment` | 使用 `update_actions[]` 和 `before_after[]` 展示改前/改后 | | 新增补充事实、要求、安排 | `Trace` | 使用 `trace_items[]`,不转 `Note` | | `cancel_allotment_control_block` 属于 Cancel Booking | 当前有独立 `Cancel Allotment` | 迁移到新卡 | | `bank_transfer_slip` 属于 Voucher Received | 当前银行/现金/交易凭证是 `Payment Evidence` | 迁移到新卡 | | Rooming List 四个 subtype | 当前是同一名单事件的证据同义词 | 统一 subtype=`rooming_list`,原文留 evidence | | Rooming List → TA Recorder | 从“可能派生”收口为“每目标必须派生” | 每个 target 产生一对关联事件,保留同一目标键和 Rooming List 关系 | | Parent split 路由 | 旧文档使用 release/cancel 混合描述 | 固定 child Group New Booking + parent Group Cancel Booking;不进入 Cancel Allotment/Allotment Maintenance | | 房型/Rate Code 不唯一 | 旧文档写成“人工复核或 downstream validation” | 默认人工复核;只保留 Parent split child raw `SUITE` 这一个硬校验例外 | | 旧 `extracted_fields.room_type` | 拆分为房型原文、normalized 值和 PMS code | 原文迁移到 `room_type_raw`;PMS code 仅在唯一映射时填写 | | `Trace / Reservation Notes` 上游 task name | 当前 `event_type=Trace` | adapter 映回旧信息系统卡名 | | `linked task` Trace subtype | 当前是 `relationship_type=linked_trace` | subtype 仍必须是 extra_bed/general_request | | `parent_source_event_index` | 当前改用通用 related 字段 | 迁移到 `related_source_event_index/indices` | | `post_confirm_action` | 当前改为 `post_confirmation_intent` | 字段改名;仍是 pending intent | | Rate Code 固定下拉全集 | 当前 reference 仅为 Typical/已知规则 | 不得硬编码文档示例为封闭枚举;使用配置中心或可编辑文本 | | Fix Charge `charge_type=additional_charge` | 当前为 `fixed_charge` | 更新枚举 | | Fix Charge `pricing_mode=unit_price` | 当前为 `unit` | 更新枚举 | | 业务人工复核只展示 reason/known fields | 当前固定九字段结构 | 完整显示 missing/blocking/conflict/action/evidence/known fields;业务字段全量可编辑 | | S10 状态名 `no_booking_action_detected` | 值兼容保留,但语义改为“未匹配支持事件” | 必须展示原邮件并要求用户决定,不能自动关闭 | ### 4.3 废弃、禁止或暂不开放 | 旧值/组合 | 状态 | 替代 | | --- | --- | --- | | `informational_message + Message Notification + thank_you/fyi/...` | 废弃业务卡路由 | 裸礼貌/裸 FYI → S10;具体预订信息 → Trace;输入不足 → S99 | | `Fallback` 的旧 routing subtype | 废弃为路由主键 | S10、S99 或业务 Need Manual Review 三分流 | | `manual_review + 各业务 task_type + manual_review` | 废弃 | 统一 `manual_review + Fallback + business_event_review`;不再新增 intended event type | | `new_booking` | 废弃直接路由 | 必须归一成 FIT/Group/Allotment 三类之一 | | `new_allotment` | 兼容 alias | 归一为 `new_allotment_control_block` | | `update_trace_or_guest_request` | 废弃 | 独立 Trace | | `update_group_block_linkage` | 废弃模糊值 | old→new code 用 Amend Group Code;parent split 用 child New + parent Cancel | | Trace subtype=`linked task` | 废弃 | 使用 relationship 字段 | | Trace subtype=`任务卡展示编辑矩阵` | 3.0 数据污染 | 删除 | | `No Action`、`S000`、`S999` | 非法 | 只允许 S10/S99 | | `Note` | 禁用;仅作为旧契约历史文字 | 普通补充信息统一 Trace;不开放新卡 | | `Allotment Maintenance` | 禁用;无独立 producer/schema/任务卡 | 修改已有 Allotment/Control Block 走 `Update Booking / Amendment` + `update_allotment_control_block`;Parent split 走 child New + parent Cancel | | Fix Charge 独立任务 | 非法 | 只作为 New/Update 内嵌字段和后续意图 | ## 5. 开发可直接收口的技术契约(无需再等产品确认) ### 5.1 路由和标识字段 | 项目 | 必须实现的固定规则 | | --- | --- | | New Booking 对象判别 | 强制 `extracted_fields.booking_object_type`,枚举固定为 `FIT Reservation / Group Block / Allotment / Control Block` | | Cancel Booking 对象判别 | 强制 `extracted_fields.cancel_object_type=fit_reservation\|group_block`;普通 Allotment 取消必须使用 `Cancel Allotment`;Parent split 的 parent 固定为 `group_block` | | Update 修改项 | 冻结 `extracted_fields.update_actions[]`,允许多值;它只用于展示修改项,不用作卡 subtype | | `result_type/task_result` | 对外契约使用 `result_type`;信息系统内部若使用 `task_result`,只做同义别名归一 | | 业务人工复核 | 固定 `result_type/task_result=manual_review`、`task_type=Fallback`、`task_subtype=business_event_review`,不增加 `intended_event_type`;业务字段全量可编辑,证据/审计/系统字段只读 | | 事件关系 ID | `source_event_index/related_*` 仅在本次 Agent 输出内建立关系,绝不是 TaskCard ID 或 PMS ID | ### 5.2 字段归一 | 项目 | 必须实现的固定规则 | | --- | --- | | `attachments[]` / `file_references[]` | 两个字段分开保留;缺失时 normalizer 补 `[]`,不得合并成一个字段 | | `manual_review` | 普通事件缺失时补 `null`;`Need Manual Review` 必须包含完整九字段对象,不完整则契约错误 | | `case_keys` | 固定保留 `group_code/confirmation_number/reservation_number/block_code` 四键,无值用 `null`,不得猜测 | | `unhandled_current_intents[]` | 每项必须保留并展示 `text_raw`、`visible_message`、`requires_user_decision=true`;不建卡 | | `source_message.source_message_id` | 非空必填;缺失时整个根结果按上游/基础设施错误拒绝,不得创建卡或 S10/S99 通知 | | `case_candidates[]` / `extraction_warnings[]` | item schema 冻结前只保留/展示,不参与任务创建 | ### 5.3 执行安全 | 项目 | 必须实现的固定规则 | | --- | --- | | `requires_downstream_hard_validation=true` | 该 event 只能停留在候选/待确认状态;禁止变成可执行 TaskCard,禁止写 PMS | | 房型/Rate Code 歧义 | 默认转业务人工复核;不得由前端、adapter 或配置默认值猜测 | | Parent split raw `SUITE` | 仅保留 raw + `null` PMS code + hard validation;失败后仅转该 child 人工复核 | | Parent split parent cancel | 必须检查 parent group、全部 child relation、去重/锁/当前 PMS 状态;候选事件不等于已取消 | | 未知/禁用 event | 逐 event fail closed,记录 `adapter_contract_error`,不自动转业务人工复核,不影响其他合法 sibling events | | 最终输出 | 直接解析结构化 JSON 对象;不依赖 JSON 文件、包装层、下载链接或 output mode | ### 5.4 尚未冻结的专属编辑区 Invoice Generation、Invoice Received、Payment Notice、Manual RateCode 的专属 `extracted_fields` 仍不完整。Cancel、Rooming List、TA Recorder 也没有完整覆盖旧 3.0 的全部专属字段路径。在新 schema 冻结前: - 允许开发通用候选卡头、目标、证据和人工复核区。 - 不得自行发明专属字段、枚举或必填条件。 - 建议新增 `schema_version`;当前先以本文日期和 skill SHA-256 锁定联调基线。 ## 6. 明确的非法组合与 fail-closed 规则 以下情况必须拒绝自动建卡: - 在邮件根只生成一个三元组,忽略多个 `message_events`。 - S10/S99 创建业务 TaskCard。 - 已匹配支持事件后仍输出或转换成 S99。 - `unhandled_current_intents` 创建 TaskCard。 - 清楚但不支持的业务意图被转换成 Need Manual Review。 - history-only 内容创建事件、Trace 或未覆盖意图。 - 多个 group code 塞进一个 event。 - bank slip 继续进入 Voucher Received。 - 控房/配额取消继续进入普通 Cancel Booking。 - Parent split 被当成普通 Update、`Allotment Maintenance` 或 `Cancel Allotment`。 - Parent split 的 child 不使用 `New Booking + Group Block`,或 parent 不使用 `Cancel Booking + group_block`。 - Parent split 的 parent cancel candidate 被视为已取消成功。 - 普通房型或 Rate Code 不唯一时仍创建 normal task;Parent split raw `SUITE` 例外除外。 - Parent split raw `SUITE` 例外中填写 `SU1/SU3/SU6`,或在硬校验前创建可执行任务/写 PMS。 - Extra Bed 被当成 Update 或房量。 - Trace subtype 不是 `extra_bed/general_request`。 - `trace_items[].category` 被拆成多张任务卡。 - 修改已有结构化备注被路由到 Trace,或新增补充信息被路由到 Update/Note。 - 每个 Rooming List 目标未派生对应 TA Recorder,或两者 target/关系字段不一致。 - `Note` 或 `Allotment Maintenance` 在没有新 schema 的情况下建卡。 - 缺 `source_message_id` 时仍接受任何“合规”结果、创建业务卡或生成 S10/S99 通知;该情况必须按上游/基础设施错误处理。 - 把 `source_event_index/related_*` 当真实 TaskCard ID。 - 根据 Fix Charge 的 TBD 工具名或 payment intent 自动执行外部写入。 ## 7. 最低验收场景 | 场景 | 预期 | | --- | --- | | 只有 Thanks / Noted | 1 个 S10 源邮件通知,0 张业务卡 | | `see attached` 且附件不可取得,无法判断方向 | 1 个 S99 源邮件通知,0 张业务卡 | | Payment Evidence + “余款能否入住时支付” | 1 张 Payment Evidence 候选卡 + 1 个未覆盖意图展示块 | | 上述未覆盖意图展示块 | 同时显示 `visible_message` 和 `text_raw`;不创建 TaskCard | | Update + Meeting + Extra Bed,同一目标 | 1 张 Update 卡 + 1 张合并 Trace 卡;Trace subtype=general_request | | Rooming List 含两个目标 group | 每目标 1 张 Rooming List + 1 张 TA Recorder,共 4 张候选卡;每对 `case_keys` 一致且 TA Recorder 关联回对应 Rooming List | | Parent Group 拆 2 个 Child Group | 2 张 `New Booking/new_group_block` + 1 张 Parent Group `Cancel Booking/linked_parent_release_after_child_split`;parent 必须 hard validation | | Parent split 中 1 个 child 房型仅写 `SUITE` | 该 child 仍输出 New Booking 候选;保留 `room_type_raw=SUITE`,PMS code 为 `null`,hard validation=true;其他 child 不受阻塞 | | 非 Parent split 的 generic `SUITE`,或任意场景 Rate Code 无法唯一映射 | 1 张统一业务人工复核卡,0 张可执行业务卡;不填猜测值 | | 同一 Update 改日期、房型、价格 | 1 张 Update 卡,多个修改项;不能因单值 subtype 丢字段 | | 将订单中已有备注 A 修改为 B | Update Booking,保留 before/after | | 当前邮件新增“客人 20:00 到店” | Trace/general_request,不是 Update 或 Note | | LianTai Credit Voucher | Voucher Received,不是 Payment Evidence | | bank transfer / cash deposit receipt | Payment Evidence,不是 Voucher Received | | 已匹配 Update 但附件不可读 | 业务 Fallback 人工复核卡,不是 S99 | | 任意业务 `Need Manual Review` | adapter 输出 `manual_review + Fallback + business_event_review`(或内部 `task_result=manual_review`);业务字段全量可编辑,证据/审计字段只读 | | 已匹配支持事件但参数不安全,同时还有清楚但不支持的当前意图 | 1 张业务 Fallback 人工复核卡 + 1 个未覆盖意图展示块;不得因复核分支提前返回而丢失展示项 | | 纯付款政策询问且无支持事件 | S10,不是 Trace、Need Manual Review 或未覆盖意图数组 | | 已知但当前禁用的 `Note` / `Allotment Maintenance`,或真正未知/拼写错误的 event type | 该 event 记录 `adapter_contract_error`,0 张自动业务卡,保留源邮件;其他合法 sibling events 仍继续处理 | ## 8. 实施顺序 1. 先实现根结构分流和逐事件循环。 2. 实现本文件的三元组表和 fail-closed 校验。 3. 落地统一业务人工复核卡和全量业务字段可编辑模式。 4. 实现 Parent split 事件集、generic `SUITE` 例外和逐 child 隔离失败。 5. 实现 Rooming List → TA Recorder 强制派生关系。 6. 新增 S10/S99 源邮件通知与 `unhandled_current_intents` 展示组件,同时展示 `visible_message/text_raw`。 7. 迁移 Cancel Allotment、Payment Evidence、Invoice、Manual RateCode 等卡型,更新 Trace、日期证据、Fix Charge 和手工价字段。 8. 尚未冻结的专属 schema 只开发通用候选卡,不自行发明字段。 9. 用第 7 节场景做 adapter 单元测试和端到端回归。 ## 9. 证据位置 - 当前输出与事件结构:`skills/booking-desk-event/references/00-output-contract.md` - 完整覆盖与未覆盖意图:`skills/booking-desk-event/references/03-current-content-completeness.md` - 路由顺序、冲突和多事件拆分:`skills/booking-desk-event/references/02-event-routing-map.md` - 业务复核/S10/S99 边界:`skills/booking-desk-event/references/90-manual-review.md` - Update 多动作:`skills/booking-desk-event/references/11-update-booking.md` - Cancel / Cancel Allotment:`skills/booking-desk-event/references/12-cancel-booking.md` - Voucher / Payment Evidence:`skills/booking-desk-event/references/13-voucher-payment.md` - Trace schema:`skills/booking-desk-event/references/16-trace-notes.md` - Parent split:`skills/booking-desk-event/references/31-allotment-control-block.md` - 房型/Rate/Fix Charge/日期:`skills/booking-desk-event/references/50-room-type-mapping.md` 至 `54-stay-date-parsing.md`