# 任务卡前端字段变更与路由说明（3.0 → 当前版）

> 对比基线：`任务卡前端展示字段表 3.0.xlsx`
> P0 契约基线：2026-07-11 00:32:56（Asia/Shanghai）
> `booking-desk-event.skill` SHA-256：`64063a6d24c2e70bb0d9d557923fdbecb9e133c1ea5971f3858df03469e48693`
> `归档.zip` SHA-256：`ebf3806cbdf1605d3a023fecff76654ba228c4d5eaf1cf68b609633bb07520cb`

> 文档定位：本文是生产 Prompt、Skill 和 references 的派生开发交接件，不是覆盖上游的第二份规则源。如本文与生产契约不一致，adapter 必须 fail closed，停止使用冲突路由并修复交接件；不得声称本文自动覆盖 Prompt/Skill。

## 1. 本次 P0 收口结论

1. Gateway 在调用 Main Agent 前校验 `source_message.source_message_id`；缺失时只返回基础设施错误，不读正文、附件、OCR、历史或系统上下文，不调用 Skill，不建卡或通知。
2. `S10` 和 `S99` 是两份不同的完整根契约，使用 `route_code` 判别；S10 的 `manual_review=null`，S99 必须带完整 `main_agent_entry_review`。
3. Main 只产生粗分类 `candidate_events[]`；Skill 决定最终 event type、subtype、目标拆分和派生事件。
4. Adapter 按每个 `message_events[i]` 派生三元组，不在邮件根对整封邮件只计算一次。
5. 业务类型和 subtype 已知时，人工复核保留原业务 `task_type + task_subtype`；只有类型或 subtype 本身不可确定时才走 Fallback。
6. 房型不唯一时保留 `room_type_raw`、置 `pms_room_type_code=null`，并要求用户在当前有效 PMS 房型目录中选择。Parent split 不再存在 generic `SUITE` 普通候选例外。
7. Parent split 固定产生 N 个 Child Group New Booking 和 1 个 Parent Group Cancel Booking 候选；Parent 始终需要下游硬校验和 Preflight。
8. 不再支持部分配额维护、部分释放或保留余量继续使用；独立新建 Allotment/Control Block 和明确整块取消仍受支持。
9. “一行一事件”改为行级隔离：每个当前有效行独立处理，不跨行合并；Parent Cancel、linked Trace、TA Recorder 等明确派生事件可以额外产生。
10. 复核在同一张业务卡上解阻；不新建第二张 linked normal task，不改写原 Agent payload。

## 2. 根结果分流

### 2.1 基础设施输入错误

`source_message.source_message_id` 为 `null`、空字符串或纯空白时，Gateway 只返回：

```json
{
  "result_type": "infrastructure_input_error",
  "error_code": "missing_source_message_id",
  "retryable": true,
  "missing_fields": [
    "source_message.source_message_id"
  ]
}
```

该结果不是业务根、S10 或 S99，也不计入下文 42 条 adapter 路由。Adapter 应进入重新取数/重试流程；Main 若意外收到此类输入，使用同一结构作为防御性终态。

### 2.2 S10 和 S99

| `route_code` | 固定 `agent_assessment.status` | `manual_review` | Adapter 三元组 | 去向 |
| --- | --- | --- | --- | --- |
| `S10` | `no_booking_action_detected` | `null` | `source_message_review_notification + Message Notification + S10` | 展示源邮件，0 张业务卡 |
| `S99` | `material_package_unavailable` | 完整 `main_agent_entry_review` | `source_message_review_notification + Message Notification + S99` | 展示源邮件和入口复核信息，0 张业务卡 |

两者的 `result_type` 都是 `source_message_review_notification`，所以必须使用 `route_code` 作为结构判别字段。不得接受 `S10 | S99`、联合 status 或 S99 空复核对象。

### 2.3 业务根

业务根固定包含：

```json
{
  "source_message": {},
  "message_events": [],
  "case_candidates": [],
  "extraction_warnings": [],
  "unhandled_current_intents": []
}
```

Agent 不在邮件根输出业务 `result_type`、`task_type` 或 `task_subtype`，因为同一封邮件可同时存在 normal 和 manual-review sibling events。

## 3. Main → Skill 输入契约

`candidate_events[]` 是粗分类信号，不是最终业务事件。每项固定使用：

```json
{
  "candidate_id": "C1",
  "classification_status": "coarse_supported_signal",
  "possible_event_types": [
    "Voucher Received",
    "Payment Evidence"
  ],
  "evidence_refs": [
    {
      "source": "current_attachments",
      "index": 0
    }
  ],
  "target_hints": [],
  "uncertainty_codes": [
    "document_subtype_unresolved"
  ]
}
```

- `possible_event_types` 只允许 active event；禁止 S10、S99、Need Manual Review、Note 和 Allotment Maintenance。
- Skill 在 `context_used.main_candidate_ids[]` 记录所使用的粗候选 ID。
- Skill 否定全部粗候选时返回内部 `no_supported_event` disposition，由 Main 转成 S10；不伪装成人工复核或契约错误。
- candidate 结构非法时 fail closed 为内部输入契约错误，不进入业务路由。

## 4. 42 条明确 Adapter 路由

### 4.1 19 个业务 subtype 的 normal/manual-review 成对路由

对下表每一行：

- `event.manual_review=null` 时使用“普通三元组”；
- `event.manual_review` 是完整 `business_event_review` 时，使用“复核三元组”；
- 复核路由仅更改 `result_type`，保留同一业务 task type/subtype。

| # | Agent 判别 | 普通三元组 | 复核三元组 |
| ---: | --- | --- | --- |
| 1 | `New Booking` + `booking_object_type=FIT Reservation` | `normal_task + New Booking + new_fit_reservation` | `manual_review + New Booking + new_fit_reservation` |
| 2 | `New Booking` + `booking_object_type=Group Block` | `normal_task + New Booking + new_group_block` | `manual_review + New Booking + new_group_block` |
| 3 | `New Booking` + `booking_object_type=Allotment / Control Block` | `normal_task + New Booking + new_allotment_control_block` | `manual_review + New Booking + new_allotment_control_block` |
| 4 | `Update Booking / Amendment` | `normal_task + Update Booking + update_booking_amendment` | `manual_review + Update Booking + update_booking_amendment` |
| 5 | `Cancel Booking` + `cancel_object_type=fit_reservation` | `normal_task + Cancel Booking + cancel_fit_reservation` | `manual_review + Cancel Booking + cancel_fit_reservation` |
| 6 | `Cancel Booking` + `cancel_object_type=group_block` | `normal_task + Cancel Booking + cancel_group_block` | `manual_review + Cancel Booking + cancel_group_block` |
| 7 | `Cancel Booking` + `relationship_type=linked_parent_release_after_child_split` | `normal_task + Cancel Booking + linked_parent_release_after_child_split` | `manual_review + Cancel Booking + linked_parent_release_after_child_split` |
| 8 | `Cancel Allotment` | `normal_task + Cancel Allotment + cancel_allotment_control_block` | `manual_review + Cancel Allotment + cancel_allotment_control_block` |
| 9 | `Voucher Received` | `normal_task + Voucher Received + lian_tai_credit_voucher` | `manual_review + Voucher Received + lian_tai_credit_voucher` |
| 10 | `Payment Evidence` | `normal_task + Payment Evidence + payment_evidence` | `manual_review + Payment Evidence + payment_evidence` |
| 11 | `Rooming List` | `normal_task + Rooming List + rooming_list` | `manual_review + Rooming List + rooming_list` |
| 12 | `AMEND GROUP CODE` | `normal_task + Amend Group Code + amend_group_code` | `manual_review + Amend Group Code + amend_group_code` |
| 13 | `Trace` + `trace_subtype=extra_bed` | `normal_task + Trace / Reservation Notes + extra_bed` | `manual_review + Trace / Reservation Notes + extra_bed` |
| 14 | `Trace` + `trace_subtype=general_request` | `normal_task + Trace / Reservation Notes + general_request` | `manual_review + Trace / Reservation Notes + general_request` |
| 15 | `TA RECORDER` | `normal_task + TA Recorder + maintain_ta_recorder` | `manual_review + TA Recorder + maintain_ta_recorder` |
| 16 | `Invoice Generation` | `normal_task + Invoice Generation + invoice_generation` | `manual_review + Invoice Generation + invoice_generation` |
| 17 | `Invoice Received` | `normal_task + Invoice Received + invoice_received` | `manual_review + Invoice Received + invoice_received` |
| 18 | `Payment Notice` | `normal_task + Payment Notice + payment_notice` | `manual_review + Payment Notice + payment_notice` |
| 19 | `Manual RateCode` | `normal_task + Manual RateCode + manual_rate_code` | `manual_review + Manual RateCode + manual_rate_code` |

上表是 19 条 normal + 19 条 type-known manual review，共 38 条。

### 4.2 其余 4 条

| # | 上游判别 | Adapter 三元组 | 去向 |
| ---: | --- | --- | --- |
| 39 | 根 `route_code=S10` | `source_message_review_notification + Message Notification + S10` | 源邮件通知，不建卡 |
| 40 | 根 `route_code=S99` | `source_message_review_notification + Message Notification + S99` | 源邮件 + 入口复核信息，不建卡 |
| 41 | `event_type=Need Manual Review` | `manual_review + Fallback + business_event_review` | 类型/subtype 未知的业务复核卡 |
| 42 | 每个 `unhandled_current_intents[i]` | `unhandled_current_intent + Unhandled Current Intent + requires_business_approval_or_unsupported_task_card` | 邮件详情展示块，不建卡 |

`Note` 和 `Allotment Maintenance` 只是 legacy accepted event，各自为 0 条合法三元组。

## 5. 人工复核和同卡解阻

### 5.1 Agent 输出规则

- 业务类型和 subtype 已知：保留原 `event_type` 和 subtype 判别字段，继续输出可安全抽取值，`manual_review` 为完整对象。
- 只有类型或 subtype 本身无法确定：`event_type=Need Manual Review`，adapter 走 Fallback。
- `manual_review.missing_fields[]` 每项是指向当前 event 的 RFC 6901 JSON Pointer，例如 `/extracted_fields/room_items/0/pms_room_type_code`。
- 不新增 `required_user_inputs[]`。Pointer 无法映射到已知可编辑业务字段时，属于 `adapter_contract_error`。
- 房型必须从信息系统当前有效 PMS 房型目录选择，不得猜测或自由填写 PMS code。

### 5.2 信息系统状态

初始：

```json
{
  "review_status": "pending",
  "review_resolution": null
}
```

用户确认后：

```json
{
  "review_status": "resolved",
  "review_resolution": {
    "field_overrides": [
      {
        "field_pointer": "/extracted_fields/room_items/0/pms_room_type_code",
        "value": "SU1"
      }
    ],
    "resolved_by": "<user_id>",
    "resolved_at": "<ISO-8601>"
  }
}
```

实施要求：

- 原 Agent payload 和初始 `result_type=manual_review` 不可变；页面当前模式由 `review_status` 控制。
- 用户覆盖值保存在 `review_resolution.field_overrides[]`，不回写 Agent 原值。
- 所有缺失字段完成、目录校验和依赖字段重验通过后，才进入现有 Preflight。
- 不创建第二张 linked normal task。
- 业务字段可编辑；邮件原文、raw evidence、source ID 和审计字段只读。

## 6. Parent split、配额和行级隔离

### 6.1 Parent split

1 Parent 拆成 N Children 时：

- 每个 Child 输出 `New Booking`，`booking_object_type=Group Block`，`case_keys.group_code=<child_code>`。
- Parent 额外输出一个 `Cancel Booking`，`cancel_object_type=group_block`，`relationship_type=linked_parent_release_after_child_split`。
- `child_group_codes[]` 与 `related_source_event_indices[]` 数量和顺序一一对应；每个 index 必须指向同根中对应 Child Group New Booking。
- 一个 split cluster 只有一个 Parent 候选；Parent 始终 `requires_downstream_hard_validation=true`。
- 禁止走 Update、Cancel Allotment、Allotment Maintenance 或单个复合事件。

某个 Child 房型不唯一时，该 Child 仍是 `New Booking + new_group_block`，但 adapter 派生该卡的 manual-review mode；其他明确 Child 仍走 normal mode。Parent Cancel 候选仍保留并经下游硬校验，不得因一个 Child 歧义丢弃 sibling events。

### 6.2 Allotment 范围

| 当前意图 | 结果 |
| --- | --- |
| 独立新建 Allotment/Control Block | `New Booking + new_allotment_control_block` |
| 明确整块取消 | `Cancel Allotment + cancel_allotment_control_block` |
| 明确部分日期/房量维护、部分释放或保留余量，且是唯一意图 | Main 输出 S10 |
| 上述不支持意图与 active event 混合 | active events 正常输出，该意图进入 `unhandled_current_intents[]` |
| 无法判断是整块拆分/取消还是部分保留 | 因 type/subtype 未知，走 `Need Manual Review` Fallback |

当前 Agent 不得生成 `update_allotment_control_block` 或 `Allotment Maintenance`。

### 6.3 行级隔离

每个当前有效行独立处理，不跨行合并。普通情况下每行、每目标产生一个主事件；Parent Cancel、linked Trace、TA Recorder 等契约明确的 derived event 可以额外产生。测试不再断言“事件数=行数”，而是检查行/目标未跨行合并、主事件不遗漏、必需派生事件完整和关系引用有效。

## 7. 3.0 字段和路由迁移

### 7.1 必须新增或修改

| 作用域 | 字段/行为 | 开发动作 |
| --- | --- | --- |
| Gateway | `infrastructure_input_error` | 在 Main 前校验 source ID；独立重试，禁止建卡/通知 |
| Main/Skill | `candidate_events[]` 粗候选 | 实现 04 输入契约；不把粗候选当最终类型 |
| 业务根 | 五个固定顶层字段 | 按 `message_events[]` 逐项路由；`unhandled_current_intents[]` 只展示 |
| Event | `manual_review: object|null` | 按是否为 null 派生 normal/manual mode；保留业务 subtype |
| Manual review | `missing_fields[]` | 仅接受 RFC 6901 pointer，映射到业务卡编辑控件 |
| Review state | `review_status/review_resolution` | 由信息系统持有，不改写 Agent payload |
| 房型 | `room_type_raw` / `pms_room_type_code` | 分开原文与 PMS code；歧义时 raw 保留、code 为 null |
| 事件关系 | `source_event_index/related_*` | 仅为本次输出内引用，不是 TaskCard/PMS ID |
| Parent split | `child_group_codes[]` + `related_source_event_indices[]` | 强制数量、顺序一一对应，Parent 仅一个 |
| Trace | `trace_items[]` | 同一目标多条补充合并一张 Trace，不按 item 拆卡 |
| Rooming List | 派生 TA Recorder | 每个已确认 target 产生对应关联事件 |
| Update | 多修改项 | subtype 固定 `update_booking_amendment`，修改动作/前后值另存明细 |

### 7.2 Legacy/禁止新生成

| 旧值或旧行为 | 当前处理 |
| --- | --- |
| `manual_review + 各业务 task_type + manual_review` 旧 subtype | 能稳定推导业务 subtype 时归一到本文对应 type-known 复核三元组；不能推导时才走 Fallback |
| `Note` | 仅历史兼容；当前补充信息统一 Trace |
| `Allotment Maintenance` | 仅历史兼容；当前不新生成 |
| `update_allotment_control_block` | 禁止当前生成；不支持部分配额维护 |
| Parent split 当 Update/Allotment Maintenance/Cancel Allotment | 禁止；固定 Child Group New + Parent Group Cancel |
| Parent generic `SUITE` normal 候选例外 | 删除；保留 New Group 卡型但走该卡 manual-review mode |
| “一行一事件”绝对计数 | 删除；改为行级隔离 + 明确派生事件 |

## 8. Adapter 顺序和 fail-closed

```text
0. Gateway 先验 source_message_id；缺失时只返基础设施错误。
1. 根含 route_code 时，严格校验完整 S10 或 S99 结构。
2. 否则严格校验五字段业务根。
3. 遍历 message_events[]：先由 event_type/subtype 得到业务卡型，再由 manual_review 是否为 null 得到 mode。
4. 遍历 unhandled_current_intents[]，只产生展示块。
5. 将候选卡送入现有 Case / Preflight / 去重 / 锁 / 状态校验。
```

下列情况必须 fail closed：

- candidate 输入结构非法：内部输入契约错误，不进业务路由。
- S10/S99 联合值、S10 带复核对象、S99 空复核对象：根契约错误。
- 已知业务路由缺少 subtype 判别字段，或未知/当前禁用 `event_type`：`adapter_contract_error`，该 event 0 卡，siblings 继续。
- `manual_review` 九字段不完整，或 `missing_fields[]` 不是有效/可映射 JSON Pointer：`adapter_contract_error`。
- Parent relationship 字段不完整或一一对应失败：拒绝 Parent event，不猜关系。
- 业务字段未确认、`review_status!=resolved` 或 Preflight 未通过：禁止写 PMS。

契约错误不等于业务人工复核，不得用 Fallback 吞掉接口问题。

## 9. P0 最低验收

| 场景 | 必须结果 |
| --- | --- |
| S10/S99 合法样例 | 各自通过；三种联合/空复核非法结构被拒绝 |
| 图片付款凭证 | Main 粗候选可包含 Voucher/Payment；Skill 最终判型，并记录 candidate ID |
| Skill 否定全部粗候选 | 内部 `no_supported_event` → Main S10，不是 Fallback/契约错误 |
| 1 Parent → 2 Children | 2 Child New Group + 1 Parent Group Cancel；关系数组一一对应 |
| 两个 split clusters | 每个 cluster 恰好一个 Parent，关系不串组 |
| 一个 Child 房型 `SUITE` 不唯一 | 该 Child：`manual_review + New Booking + new_group_block`；sibling Child 仍 normal；Parent 候选仍在 |
| Update 房型歧义 | `manual_review + Update Booking + update_booking_amendment`，保留安全已知字段 |
| Update 只改日期，附带无关房型文字 | 不因无关文字进人工复核 |
| 业务类型/subtype 未知 | `manual_review + Fallback + business_event_review` |
| 部分配额维护单独出现 | S10 |
| 部分配额维护 + 支持事件 | 支持事件照常输出 + `unhandled_current_intents[]` |
| 独立新建/明确整块取消配额 | New Allotment / Cancel Allotment 仍正常 |
| 同卡解阻 | 目录值、覆盖审计、状态转换通过；Preflight 前 0 次 PMS 写入 |
| 缺 source ID，正文为 Cancel/Thanks/see attached | 全部得到同一 infrastructure error；Skill/附件/历史查询调用数都为 0 |

## 10. 生产源与部署必读

权威顺序：

1. `prompts/main_agent_prompt.md`
2. `skills/booking-desk-event/SKILL.md`
3. `skills/booking-desk-event/references/00-output-contract.md`
4. `skills/booking-desk-event/references/04-main-skill-input-contract.md`
5. `skills/booking-desk-event/references/01-current-history-boundary.md`
6. `skills/booking-desk-event/references/03-current-content-completeness.md`
7. `skills/booking-desk-event/references/02-event-routing-map.md`
8. 各业务 reference，特别是 `10`–`18`、`30`、`31`、`50`–`54` 和 `90`

`task_plan.md`、`findings.md`、`progress.md`、审计报告和本文都是过程/派生交付件，不是可以反向覆盖生产契约的规则源。

部署时必须同步发布 Main Prompt 和 `.skill` 包，并使用本文页首 SHA-256 校验 skill 包。本仓库没有 adapter/frontend 代码；实际实现必须以上述契约和 42 条路由为验收基线。
