Files
go-sip/docs/plan-config-read-v0.1.md
T

144 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.
# Go SIP Agent:配置读取与有界外呼迭代计划 v0.1
**历史 v0.3 的执行入口。任务发现 v0.3 项目内合同、运行与 F09/F06 本地门禁仅保留历史证据。现行项目内 v0.4 已完成代码及隔离验收,见 [`plan-dispatcher-state-v0.1.md`](plan-dispatcher-state-v0.1.md)、[`thirds/v0.4.md`](thirds/v0.4.md)和[验收证据](evidence/dispatcher-v04-local-acceptance.md);外部 SaaS 签收与真实切换仍未完成。本文下述“当前”均指编写时的 v0.3,不能当作 v0.4 合同。** 现行 `contracts/upstream/v1/` 仍是旧外部基线;本文不授权真实 SaaS、生产部署、拨号或切换。分项设计:[有界消费与控制通道](architecture/Dispatcher有界接纳与控制通道改造计划_v0.1.md);其他本地 SaaS 业务契约仍按[第三方对接 v0.1](thirds/第三方对接事件与请求消费顺序_v0.1.md);历史 v0.3 发现依据为[计划 0926](plan-0926.md)、[v0.3 项目内合同](thirds/v0.3.md)及[独立 Schema](contracts/task-discovery-v0.3-proposal.schema.json),[v0.2](thirds/v0.2.md)也仅保留历史;现行项目内发现见上述 v0.4 合同。机器校验件:[Schema/正反例](contracts/)。分项材料须与本文一起更新,不能继续使用旧 ETag/304、控制 CAS 或即时反馈目标。
## 1. 范围和完成标准
当前 P1 仍为**单 D、单 Agent、单 Asterisk、单 Cell、单租户**,允许同租户多任务验收;第二租户/多 D 只可另做明确标注的合同 fixture,不等于业务运行授权。真实 SaaS、OSS、云、供应商消费和外呼均另获授权;真实外呼仍遵守白名单、Asia/Shanghai `[09:00,20:00)` 及每线路/号码每日次数限制。未经新合同和验收,不替换现行运行规则。
拟定四条只读 GET,共用 `X-DISPATCHER-id` 和 `X-DISPATCHER-SECRET-KEY`,无请求体:
| 路径 | 用途与状态 |
| --- | --- |
| `/internal/v1/dispatcher/sip` | 用户指定路径;本 D 获管理面批准的 SIP 全量。 |
| `/internal/v1/dispatcher/task/:task_id` | 用户指定路径;任务归属、路由/主叫、智能体、时段及两项任务级超时。 |
| `/internal/v1/dispatcher/tasks?after=<cursor>` | **历史 v0.3 运行方式**:首次 `after=0`、重启从已提交事件游标续读,首次与后续均按统一 `tasks[]` 分页。现行项目内 v0.4 改为启动 `mode=snapshot` 全量、运行时 `after=<内存游标>` 在线增量,已通过隔离 Mock;真实 SaaS 未联调。 |
| `/internal/v1/dispatcher/tenant/:tenant_id/quota` | 本地项目定义路径:取得任务后按其 `tenant_id` 读取该租户分配给本 D 的并发额度;真实 SaaS 路径兼容性未验证。 |
**任务队列及精确绑定只由 SaaS 创建、维护和退役;D 只消费,不建队、不绑定、不删除。** SaaS 先确认持久队列 ready 再发布 persistent 消息,mandatory 无 return 且 confirm 成功才记入队。D 离线时消息可积压;无队列时未成功入队的原消息由 SaaS 保留,就绪后按原身份重新发布,不能靠 D 重启倒灌。任务队列按 D+任务 ID 定位;租户身份保留原值用于核验和额度汇总。
**用户明确的消费规则:pause 保留积压,resume 消费原队列积压;stop 持久生效后静默消费并 ACK 所有未接纳积压,不拨号,不向 SaaS 发布这些消息的回执或通话结果。** 静默只限定已停止任务的未接纳命令:停止控制本身仍有处理回执;已接纳/在途通话按 drain/hangup 处理并保留其最终结果。D 的错误日志、停止状态和计数不静默,ACK 丢失后重投仍不拨号、不补发被抑制结果。
完成分三层:**C**:F01/F07 项目内来源、版本、严格 Schema、正反例、SHA-256 对齐并通过 Mock SaaS 端到端验证;不等待外部签收;**L**:单 D 本地隔离闭环和故障矩阵通过;**M**:多 D 独占资源/份额为另行授权的后续。Mock C 不代表真实 SaaS 或供应商验收。
## 2. 权威资料和读法
先读本文并按 §4 选择 F 包,再读[架构](architecture/Go重写方案_v0.3.md)、[现行验收](acceptance/验证与切换验收_v0.3.md)、[通信合同](contracts/通信与事件数据交互_v0.1.md)、[开源复用](dependencies/开源组件选型与复用清单_v0.2.md)及 `contracts/upstream/v1/`。新旧冲突须版本化替换,不向现行严格合同偷偷加字段。[页面分析](references/saas-page-snapshot-analysis.md)只作页面语义来源,不证明现网字段。原 [plan-0918](archive/plan-0918.md)不回写;本计划 §8 是唯一新台账。
## 3. 冻结目标与处理规则
### 3.1 配置、路由、时限
- management 是 SIP 唯一编辑/审批面,SaaS 只分发获批完整版本。SIP 变更先关准入、排空/对账,再核验实际加载;发现变化约 60 秒不等于加载完成约 60 秒。供应商 transport/auth/registration 不明不得给 real 猜默认值。
- 新 `call.execute.payload` 仅 `task_id/callee`;外呼信封仍有 `command_id` 防重复拨号。任务必须返回 `tenant_id/tenant_key`,D 核对消息与任务归属后再按 `tenant_id` 取额度,身份错配/缺失即拒新准入。任务配置不返回 `agent.content_sha256`;版本不可变,同版本异内容拒绝,已有执行持久快照不漂移。
- **最小路由约定(本地项目契约,F01 Schema/Mock 验证):**`allowed_trunk_ids` 按列表顺序作为候选优先级,选首条满足时段、已加载状态、供应商/线路额度且支持任务 `caller_profile_id` 的线路;无满足项不接纳。`caller_profile_id` 是新增任务级明确引用,必须命中选中线路的获批主叫,不默认取第一个主叫。`route_policy_id` 只标识这份任务路由,不依赖另一个未定义的查询接口。选定线路/主叫后持久绑定;拨号失败或未知不自动换线重拨。
- `ring_timeout_ms` 是任务级振铃上限;通话有效上限是任务 `max_call_duration_ms` 与已授权 AI `conversation.max_duration_ms` 的**较小值**,控制器和执行侧均使用同一值。两值不相同必须记录来源/有效值,不改写智能体原快照或悄悄放宽任何上限。没有逐呼 `variables` 的新合同不提供联系人变量来源;需要未提供变量的提示词不得以空字符串代替继续执行,须在 F01 明确静态提示词范围或补齐获批来源。
- SIP/任务配置约 60 秒缓存到期重取完整 `200`,不使用 ETag/304;过期/失败拒绝新准入,不影响既有执行快照。配置内 `status=running` **不是恢复许可**,状态按 §3.3 独立处理。
- F02 项目内 Agent 状态校验:使用受部署配置约束的 `agent_id/cell_id` 查询 Agent status;`applied_configs` 必须恰有一条 `kind="sip"`,且 `state="applied"`、十进制 `revision` 与 `config_sha256` 分别精确匹配获批 SIP artifact revision/SHA-256、`observed_at_unix_ms>0`,才可读取新配置/准入。缺失、重复、身份不符或匹配失败均 fail-closed。此为本地 Agent 协议规则,不是 SaaS/management 外部确认;Mock 状态不能证明真实 Asterisk 已加载。
### 3.2 租户额度
取得有效任务的 `tenant_id` 后,D 请求拟定 `/internal/v1/dispatcher/tenant/:tenant_id/quota`。响应包括 D、tenant_id、原值 tenant_key、额度版本、`max_concurrent_calls`(允许 0)、有效截止时间;该值是 SaaS **分配给此 D 的租户份额**,不是每个任务各得一份。相同租户各任务复用同一额度快照/占用账本,SaaS 必须保证 tenant_id 与 tenant_key 一对一。
额度成功核验后最多缓存约 60 秒且不超过响应有效截止时间;缺失、身份错配、过期或刷新失败,关闭该租户所有任务的新准入,不使用无限额/默认额,不阻塞 stop 静默排空。额度调低时不强挂已接通通话、不抹掉已占用/未知执行,直到占用降至上限以下才再接新呼叫。D 以 SQLite 单事务检查并预留租户+任务+Cell/线路/AI 等额度,不能先分别判断后并发超卖。确认通话终结并释放执行资源后释放通话额度,**不等待录音上传或 MQ 最终结果确认**;未知通话继续占额。多 D 时各份额之和≤租户总额,未经 F05 授权不开放多 D 运行。
### 3.3 暂停、恢复、停止与状态来源(当前已实现合同)
> 下一版目标改为 pause/stop 均挂断在途通话、resume 不挂断,且在线任务状态切换以独立控制队列为准;见 [`下一版计划`](plan-dispatcher-state-v0.1.md)。以下 drain/hangup 和发现状态规则在代码/Schema 切换前仍属**当前合同**,不是新目标已实现。
SaaS 先持久修改权威任务状态,再向独立 D 控制队列发布 `task.control`。请求不含 `command_id/expected_task_revision`,本阶段不设计控制去重;D 按任务串行处理控制与接纳,并持久记录生效状态后才 ACK/回执。
1. **pause:**立即关闭新接纳并保存暂停屏障,停止从该任务队列继续取新执行;已投递但未接纳的有界消息退回原队列,不能 ACK 丢弃或搬进无界 SQLite 待拨队列。已有执行按 drain/hangup 处理。重启仍暂停。
2. **resume:**重新 GET 单任务配置(不使用约 60 秒旧缓存),只有 SaaS 最新状态为 running、归属/授权/租户额度/时段有效、且本地未 stopped,才解除暂停并恢复消费原积压。无需 SaaS 重新发原消息;已过 `not_after` 的旧消息仍不可拨,暂停不能冻结或延长有效期。过期/窗口外非 stopped 命令给明确拒绝回执,不等待次日自动拨号。
3. **stop:**持久化不可逆停止屏障,同任务 ID 不能 resume/重新启用;SaaS 停止新发布。D 不申请通话额度,继续小批量消费未接纳积压,仅计本地处置计数并 ACK,不建外呼结果/outbox,不拨号。已停止任务重投/重启、额度为 0、配置缓存失效时仍可排空;不得借此清掉别的任务、伪造已接纳执行终态或漏掉控制回执/既有通话结果。
4. **优先级:**本地 stopped 永久高于任何 running;本地 paused 只能由有效 resume 解除。`tasks[]` 事件页及缓存更新可使状态更严格;即使较新的发现页把 `paused` 改为 `running`,也只更新 SaaS 状态和游标,不清除已持久的暂停/停止屏障。暂停只由上述 MQ `resume` 配合新鲜任务读取解除,且低于已知 `task_revision` 的状态不得覆盖新状态。冷启动恢复本地屏障并从已提交游标追平 SaaS 事件页后取更严格者;冲突/缺失关闭新准入,不丢已有执行事实。
5. **无编号乱序:**控制重投可能重复返回回执,不保证按消息身份“只处理一次”。pause/stop 先关闭准入;与 SaaS 最新状态不符、不可核验或接收乱序时保持关闭并返回明确失败,不根据到达先后自动恢复。恢复必须重新发送有效 resume 并核对最新权威状态;不引入替代 command_id 或控制 CAS。SaaS 不可把发布成功视为控制已应用,丢失回执下状态不明不得主动扩量。
### 3.4 任务发现、发布与期限(当前 v0.3)
> 重启全量、运行中内存游标增量为 [`v0.4 未实施草案`](thirds/v0.4.md);本节只描述当前运行,不得拿旧持久游标解释新快照。
D 启动/重启从已提交的 v0.3 事件游标续读(无新游标时请求 `/internal/v1/dispatcher/tasks?after=0`),运行中约每 30 秒继续读取。首次与后续均返回统一 `tasks[]` 事件页,每页最多 256 项、每 D 活跃归属最多 256 项;`after` 是规范十进制事件序号,不是最大 task_id。非空页的 `next_cursor` 必须前进,空页的 `next_cursor` 保持请求游标并表示本轮追平;同一任务更新后可再次出现,`status=removed` 为撤销墓碑,不能根据某页未出现任务推断撤销。响应不携带 queue/key,D 按 D+task ID 推导并仅消费 SaaS 预建队列。每页任务、响应和游标同事务提交,首次未追平或任何错误页都拒绝新准入,控制 MQ 保持可用。**不设游标过期/410 自动重建,v0.2 checkpoint 不可解释成 v0.3 事件 ID;错误不可静默重置或回退旧 `changes` 路径。** SaaS 必须保证页内完整有序、旧游标及撤销墓碑长期可读;容量/灾备连续性仍待外部签收。发现项和任务配置须提供 tenant_id/tenant_key 以便核验额度;无变化不撤销消费关系。
**队列 ready 不等于 D 已开始消费。** 首条任务可能等待轮询、配置/额度读取和可用名额;30 秒是轮询间隔,不是端到端发现 SLA。SaaS 设置的 `not_after` 应覆盖允许的排队和准备时间,不能用固定 30 秒示例承诺离线恢复必拨。D 重启仅恢复原消息:未过期且有效的才接纳;过期明确拒绝(已 stopped 则静默 ACK),绝不顺延期限/自动造新 command_id 重拨。若要求更短首呼时限,必须在 F07 另定就绪通知协议,不能假设已存在。
SaaS 清单须保留已 stopped 但未排空任务;撤销握手按第三方契约执行:SaaS 停止发布,D 持久 stop 屏障、退回/排空未接纳积压、确认无未 ACK 且队列为空后回执,SaaS 收到后删除队列并发送 removed。D 不创建/删除队列;task_id 为 `[A-Za-z0-9_-]{1,128}`,每 D 最多 256 个未退役任务队列,routing key/queue name 上限见 MQ topology。
### 3.5 最终结果与无录音
保留呼叫/控制回执及一种最终 `call.result`,不保留对外 query/replay、实时文字/拒联/录音拆分事件。用户已批准仅最终反馈,本轮实现与验收按此更新;真实 SaaS 消费兼容性另行验证。最终结果的身份在本地持久化,MQ 重投仍是同一份事实,不重新拨号或上传。
- 已产生录音:上传 OSS 成功后 `recording.status=uploaded`、携带路径;上传永久失败/到有限期限后 `unavailable`、明确错误与 null 路径。明确上传/授权失败立即以 unavailable 收口,无确定结果最晚于 `call.ended_at + 15m` 收口。
- 通话已尝试但忙线/无应答等**未产生录音**:终结已确认后即可发唯一结果,`recording.status=not_created`、身份/路径等资产值为 null,原因如 `no_answer`;不申请上传、不假称上传失败。录音本应生成却失败用 `unavailable` 和明确阶段原因,不伪装正常无录音。
- 未接纳且被停止屏障吞掉:无命令回执、无最终结果;其他未接纳拒绝只有命令回执,不能伪造通话。已接纳取消/失败/挂断仍按其事实形成最终结果。
- `outcome` 反映通话本身,录音错误写 recording 不改成呼叫失败。通话额度释放与上传/通知独立;日志只保留脱敏事实,不输出完整音频/文本/密钥。
- 最终文字合并后的消息大小与现行 MQ 上限可能冲突,项目内 JSON 正文上限为 8,388,608 bytes,超限保留 outbox 并显式阻塞,不截断完整转写、不拆成用户已移除的实时事件来掩盖。
## 4. 依赖顺序与工作包
**执行顺序:F00 → F01(HTTP/配置/额度)与 F07(MQ/发现/控制/结果)并行冻结 → 项目内 Mock C → F02 → F03 → F04 → F08 → F09 → F06。F05 是另获授权的后续。** F01/F07 可按各自边界并行;严格 Schema、正反例与来源/hash 对齐,并由 Mock SaaS 端到端通过后即为本地 C,不等待外部签收/连通。F03 单独负责新队列消费/状态屏障,F08 只集成结果交付和受控切换,不重复开发同一消费路径。
| 工作包 | 输入、责任与交付 | 门禁 |
| --- | --- | --- |
| F00 草案整理 | 集成负责人同步本文、分项设计、对接文档、配置 Schema/示例;标明用户给定路径与项目新增额度/路由字段。 | 正反例可验证,不冒充 SaaS 现网。 |
| F01 HTTP 与配置/额度合同 | 按第三方文档冻结四条 GET、两 Header、身份映射、任务主叫/候选优先级、两时限较小值、授权/缓存/配置不可变及租户额度份额/版本/有效期。 | 项目内严格 Schema、来源/版本/哈希、正反例;静态提示词或变量来源明确;Mock 验证,不等待外部签收。 |
| F07 MQ、发现及控制/结果合同 | 历史 F07 先冻结 v0.1 分页发现,随后 F03 v0.2 单次响应已退为历史;任务发现现按 v0.3 事件游标合同。其余 SaaS 独占队列生命周期、v3 routing、精简 call.execute、无编号控制、pause/resume/stop、回执/唯一最终结果、录音边界及消息大小/期限维持原合同。 | 项目内严格 Schema、拓扑、正反例、来源/hash;与 F01 一致并由 Mock 端到端验证后 C 通过。移除旧 query/replay/配置 MQ/拆分事件;外部兼容另记未验证。 |
| F02 配置与额度读取(C 后) | 接入四条已冻结 GET(本地先由 Mock 提供);D 读取并校验 SIP 实际加载、任务租户映射、按 tenant_id 的额度,完整 200 缓存;有期限拒绝与持久快照。 | 断服务、降额、同租户多任务、过期/错身份、新旧配置竞态不超额/不漂移;真实 SaaS 未联调不冒称通过。 |
| F03 消费/发现/控制(F02 后) | D 移除旧 `--tenant-key` 自建租户队列路径,按 SaaS 已建任务队列消费;发现游标/暂停恢复/停止静默ACK/独立控制与原子额度;未接纳仍留 MQ。旧分页发现结果仅属历史。 | §5 的队列/控制矩阵通过;D 无建队权限仍可工作,stop静默但可排查。 |
| F03 v0.2 任务发现修订(历史) | 单次完整返回、`changes`/410 的前版本实现;已由 v0.3 项目内运行路径替换。 | 旧 Schema/证据留作追溯,不作当前运行或新版本验收。 |
| F03 v0.3 事件游标发现 | 首次/后续统一 `tasks[]` 分页;按提交游标追平、同任务更新/removed 墓碑、错误页关准入、SQLite 同事务;无 v0.2 兼容或 410 重建。 | [plan-0926](plan-0926.md) 与 v0.3 Schema/正反例/hash、Mock F09 及本地 F06 制品核对;SaaS 页内顺序/长期墓碑和旧状态受控切换仍待外部门禁。 |
| F04 时段及最后拨号屏障(F03 后) | 仅 Dispatcher 在持久接纳与发出呼出指令前复核任务×已绑定线路时段、任务排除日期、白名单、有效期及任务/AI 时限,持久化唯一签发/拒绝决定;Agent 只核验会话及 Dispatcher 签发期限。 | 本地 Mock 验证窗口边界/跨日/更新/暂停竞态不拨号、不等待或自动重试;真实路径固定时段只在外部合同、F09/F06 验收及授权后替换。 |
| F08 结果与受控切换(F04 后) | 组装 uploaded/unavailable/not_created 的最终结果与既有命令/控制回执;通话释放独立于上传;旧新版本互斥,恢复原身份 outbox。 | 本地全流程无重复拨号/PUT,停止积压不产生回传,已有执行结果不丢失。 |
| F09 本地验收(F08 后) | 集成负责人运行 §5 全矩阵;单 D 单租户多任务为 L,扩展 fixture 标为非 P1。 | gofmt、`go vet ./...`、`go test -race ./...`、`go build ./...`、本模块覆盖率≥65%,脱敏证据。 |
| F06 发布/切换(F09 后) | 单写 §8 状态,核对合同/制品/队列实际版本;真实 SaaS/OSS/云/线路另外授权。 | 旧本地证据不代签;回退不能并行旧新写入/额度或引起重拨。 |
| F05 多 D(后续另授权) | SaaS 固定任务归属、分配各 D 有界租户/供应商份额;D 独占 Agent/Asterisk/持久目录。 | 各份额总和≤总额,未知执行不跨 D 迁移,不用 fixture 冒称双 D 业务。 |
## 5. 必测输入与预期结果
| 用例 | 输入/故障 | 必须观察到的结果 |
| --- | --- | --- |
| 配置结构 | 缺身份 Header、task/消息/额度 tenant_id 或 tenant_key 不一致;agent.content_sha256/旧逐呼字段回流 | 拒绝新准入,不能猜默认/覆盖;call.execute.payload 仅 task_id/callee,task含两超时与明确主叫。 |
| 配置/路由 | 第一候选不可用、第二可用;无主叫映射;任务120秒/AI90秒;提示词缺变量 | 首个满足条件的候选并固定;主叫不可解/变量缺失不拨;有效时限90秒,两个控制点一致,拨后失败不自动换线。 |
| 租户共享额度 | 同 tenant 两任务额度各10、租户上限3,同时请求4次 | 合计最多3个占用;不同任务不各得3。未知占用计入。 |
| 降额/失效 | 已占3时从3降为1;额度0、有效期到、HTTP故障 | 现有不强挂、不清未知;新准入关闭,占用低于新上限且授权有效才接新;控制/stop排空不被额度阻塞。 |
| pause/resume | 暂停时队列100条、D已有有界未接纳交付;重启后恢复 | 暂停不丢消息/不新拨;未接纳退回原队列;重启仍暂停;有效resume继续消费原100条且无需SaaS重投,已过期的明确拒绝。 |
| stop 静默 | 未接纳积压100条、在途2通、stop;ACK丢失/重启/额度0 | 100条最终ACK,originate=0、新回执/最终结果outbox=0;本地计数可核查。stop控制有回执,2通按策略终结并各有最终结果;其他任务不受影响。 |
| 状态优先级 | pause/stop后收到旧running快照或低版本tasks;旧resume迟到/控制重复/HTTP断连 | 不自动恢复;resume未获最新running/未过屏障即明确失败;stopped不可逆。控制不按消息身份去重,不保证只回一次;回执不明不当成功。 |
| 新任务/离线期限 | 刚过轮询点新建队列;离线超过not_after;首条期限不足准备时间 | 消息先入已建队列;恢复只消费仍有效者,过期不拨不顺延,不自动新建外呼命令;30秒只是发请求周期。 |
| 发现/退役 | 首次多页、同任务较小 ID 在翻页中更新、空页、重复/超 256 项、游标倒退或 HTTP 410、removed 墓碑、SaaS 建队前误发、stopped 积压 | 不漏更新;每页限 256、活跃任务限 256,拒绝响应内队列字段、不截断结果;无 410 回退或重拉全量,只在同事务提交后推进游标;未入队原消息由 SaaS 保留,stopped 持续静默排空;旧状态或顺序无法证明时阻断切换。 |
| 队列权限/配额 | D禁止create/bind/delete、队列满、SQLite满盘、租户满额 | D只消费,不补建;SaaS保留未成功发布消息;事务失败不ACK已接纳;不会搬全部积压进SQLite,不阻塞控制/stop清理。 |
| 无录音/失败 | 无应答/忙线且无录音;应有录音却失败;OSS超时;停止未接纳命令 | 正常无录音not_created且不等待上传;明确授权/PUT失败立即unavailable,未知最晚15分钟收口;停止未接纳无回传;不能伪造call_id/成功路径。 |
| 释放与结果 | 已确认结束但上传慢、MQ confirm丢失或重启、完整转写超过8 MiB | 通话额度已释放、未知仍占额;原结果身份重投不重拨/PUT;超限 outbox 显式阻塞,不截断/拆分/丢弃。 |
| 时间及真实边界 | 任务/线路交集、空日、排除日、左闭右开、最后许可前控制到达 | 不跨窗或被暂停/停止后新拨,不等待次日自动执行;Mock时钟不等于real放行。 |
## 6. 写入与实施边界
项目设计与本地目标契约在自身 docs 维护。`contracts/upstream/v1` 保持现行外部运行基线,不擅改;原 v0.1 本地合同与 F03 证据保留为历史;`docs/thirds/v0.3.md` 的任务发现事件游标分页已作为**项目内本地运行合同**完成实现与 Mock 校验,独立 v0.3 Schema、正反例和来源哈希见 F09 v0.3 证据;v0.2 Schema/证据仅保留历史。这不代表真实 SaaS 已发布、有序返回或长期墓碑容量已签收。其他业务合同沿用已有本地 v0.1 机器校验件,须记录规范文件与机器文件 SHA-256。该本地契约不冒称 SaaS/management 已发布,外部签收和连通不阻塞本地 C。本文/分项/guide/字段提案必须同步;源码、截图、真实凭据、无关父仓库不改动。TDD 先失败后实施,复用现有库,禁止隐藏错误、HTTP/MQ配置回退、重复外呼。大改另开分支,未获得子 Agent 授权由当前负责人执行。
## 7. I/M/G 前置
**I**:本地实施按本文件与第三方对接文档冻结的项目内契约;F01/F07 的严格 Schema、正反例、来源/hash 对齐以及 Mock SaaS 端到端 C 通过后即可进入 F02,不等待 SaaS/management 外部签收或连通。**M**:旧 W 的通过不能代替新 F02–F09;仅本地隔离验证不代表真线路/OSS通过。**G**:部署版本、网络/ECS只读核验、SHA-256、systemd enabled+active、Asterisk/ARI/PJSIP、媒体监听和非生产诊断门禁全部保留;实际云/生产/拨号另获授权。
## 8. 当前事实总台账
| 项目 | 当前事实 | 下一门禁 |
| --- | --- | --- |
| 历史计划 | [plan-0918](archive/plan-0918.md)保留原文,只作历史。 | 不回写旧W台账。 |
| F00 | 已对齐计划、分项设计、第三方对接、字段提案、架构/验收入口;三条用户给定路径与新增租户额度路径、项目路由字段来源均有标识,均属项目内目标,非现网合同。 | Draft 2020-12 本地校验:4 个有效响应示例通过,1 个额外字段非法样例按预期拒绝;文档链接检查通过。下一门禁为 F01/F07 项目内 Schema/正反例/来源哈希冻结及 Mock C。 |
| F01 | 四条只读 GET 的身份/归属、路由/主叫、缓存/版本、额度/有效期已按第三方对接文档冻结为项目内规则;`resource_not_found`/租户额度等 HTTP 错误映射有正向 fixture。`contracts/upstream/v1` 仍是现行外部基线,真实兼容性未验证。 | `python3 scripts/validate-local-contracts.py` 通过:4 个本地 Schema、17 个配置读取正向例、HTTP 状态映射及负例;manifest SHA-256 校验通过。C 通过见下行;不代表真实 SaaS 兼容性。 |
| F07 | 项目内任务发现/队列所有权与生命周期、MQ v3 控制/回执、单份 `call.result`、大小/期限/退役规则已按第三方文档冻结;本地正反例已校验。`contracts/upstream/v1` 仍是现行外部基线,真实兼容性未验证。 | `python3 scripts/validate-local-contracts.py` 通过:发现 13 个正例、命令 6 个、结果 4 个,各 Schema 负例均拒绝;source 与 18 项产物共 19 个 SHA-256 文件通过。F03 运行时本地交付见下行;外部兼容性仍未验证。 |
| C | 本地 Mock SaaS 通过四条只读 GET、`call.execute`、`command.result` 与 synthetic `call.result` 的 Dispatcher 端到端交互;缺少 result queue 时 outbox 保留并在恢复后交付。 | 证据:[`local-mock-saas-c-v0.1.md`](evidence/local-mock-saas-c-v0.1.md)。契约验证、包测试及 `bash scripts/mq-integration-local.sh` 通过。仅属项目内 Mock;没有真实执行、拨号、录音上传或外部验收。 |
| F02 | 本地配置与额度读取模块完成:四条 GET 严格校验,四份完整响应按版本/hash/有效期持久化,任务/租户身份绑定,task+tenant 配额与 call.execute reservation 原子接纳;SIP Agent applied-config 核验、断连/过期/不匹配 fail-closed、同租户多任务和配置竞态均有 Mock 覆盖。F02 涉及四包覆盖率为 65.1%–75.0%。 | 证据:[`f02-local-config-read-v0.1.md`](evidence/f02-local-config-read-v0.1.md)。包测试、race、`go test ./...`、vet、build 和契约校验通过。F09 按用户确认的本轮 P1 业务模块 ≥65% 口径另验;CLI/历史包低覆盖率单列;F03 应用启动/消费现有本地证据见下行,真实 SaaS/Asterisk 加载仍未验证。 |
| F03(历史分页证据) | 单 D/单租户 v3 运行时曾在本地交付:只被动消费 SaaS 预建队列,持久发现游标/任务归属,暂停保留积压,恢复使用原队列,停止先持久屏障与权威状态、后静默排空并 ACK,排空完成后才写控制回执。RabbitMQ ACL Mock 证明 Dispatcher 无 `configure` 权限仍可消费、排空和发布;旧 tenant-queue consumer、旧 per-call control worker、CLI 旧消费参数及 MQ-only v1 acceptance runner 已移除。 | 证据:[`f03-local-task-runtime-v0.1.md`](evidence/f03-local-task-runtime-v0.1.md)。本地 acceptance、Race、vet、build、契约与无 `configure` 权限的 RabbitMQ 集成测试通过;`internal/dispatcher` 覆盖率 66.2%。仅是本地隔离验证,外部 SaaS/management、云、线路和生产均未验证;F09 新版本地矩阵见下行,历史分页证据不代签。 |\n| F03 v0.2(历史) | 前版单次完整发现、增量 `changes` 和 410 重拉全量曾在本地验证,当前运行代码已移除该路径。 | 证据:[`f03-local-task-discovery-v0.2.md`](evidence/f03-local-task-discovery-v0.2.md),仅作历史,不代表 v0.3 通过。 |
| F07/F03 v0.3 | 项目内 `tasks[]` 事件游标页及来源/hash 冻结;Dispatcher 与 SQLite 按页原子提交,首次追平前和异常页拒绝新执行;同任务更新、撤销墓碑、断页重启从已提交游标续读、控制竞态与无重拨已完成 Mock。旧 `changes`/410 运行路径移除,SaaS 队列仍独占建队。 | 证据:[`f09-local-acceptance-v0.3.md`](evidence/f09-local-acceptance-v0.3.md)。外部 SaaS 有序返回、长期墓碑容量、旧状态受控切换和真实兼容仍未签收。 |
| F04 | 本地隔离 Mock 已将任务×已选线路时段、任务排除日期、白名单/有效期、绑定快照及任务/AI 较小通话时限接入接纳与发起前门禁;执行身份只有一次持久 `issued`/`refused` 决定,队列消费后由 D 经版本化 Unary 向 Mock Agent 下发,Agent 不重算业务策略、不发送 SIP。V3 mixed/real 启动拒绝,原真实固定门禁未放宽。 | 证据:[`f04-local-dial-policy-v0.1.md`](evidence/f04-local-dial-policy-v0.1.md)。Proto/合同、本地 Mock、race、vet、build、RabbitMQ 无 `configure` 权限及 `scripts/acceptance-local.sh` 通过;外部合同/真实拨号未验证。F08 后续本地结果见 [`f08-local-final-result-v0.1.md`](evidence/f08-local-final-result-v0.1.md);F09 本地矩阵与覆盖率见 [`f09-local-acceptance-v0.2.md`](evidence/f09-local-acceptance-v0.2.md)。 |
| F08 | 单份最终 `call.result`、确认终态后独立释放额度、uploaded/unavailable/not_created、本地 Agent→D 录音失败事实与原身份 outbox 已完成隔离 Mock 验证;V3 Mock 不并行写旧分散事件,V3 mixed/real 启动拒绝。证据:[`f08-local-final-result-v0.1.md`](evidence/f08-local-final-result-v0.1.md)。 | `scripts/acceptance-local.sh`(含 race、vet、构建及受限 RabbitMQ)通过;Mock 录音成功/失败由合成事实注入,默认 Mock 只模拟无应答,无实际媒体/OSS PUT,publisher confirm 不等于 SaaS 应用签收。F09 本地验证见 [`f09-local-acceptance-v0.2.md`](evidence/f09-local-acceptance-v0.2.md);F06 真实部署/切换仍未获授权。 |
| F09 | v0.3 首次多页、空页、同任务重现及墓碑、翻页中更新、断页重启、坏游标/410 失败、SQLite 写失败同事务回滚、MQ 控制竞态(含发现 `paused→running` 仅更新状态/游标、须 MQ `resume`+新鲜任务核验才恢复队列)、重复执行不重拨完成单节点/单租户本地 Mock 验证;旧 F09 v0.2 证据仅作历史。证据:[`f09-local-acceptance-v0.3.md`](evidence/f09-local-acceptance-v0.3.md)。 | `scripts/acceptance-local.sh`(格式/合同/Proto/hash、race、vet、构建、受限 RabbitMQ)通过;P1 业务包 66.4%–85.4%(MQ 含 integration)。CLI 30.0% 和历史 callruntime 61.7% 单列技术债,不声称全仓达标;SQLite 写失败用触发器注入,未物理填盘。外部有序返回/墓碑容量/真实验收未完成。 |
| F06 | Go 1.27.1 一次性本地制品/校验和将 v0.1 其他业务+v0.3 任务发现+v3 MQ 拓扑及 Proto 版本/hash 写入清单;已有 release/package/归档拒绝覆盖,制品 mixed/real 在开 DB 前拒绝;旧 CLI 和分散事件不在新运行路径。证据:[`f06-local-release-gates-v0.3.md`](evidence/f06-local-release-gates-v0.3.md)。 | `make release-check-local` 与本地 acceptance 通过;来源 dirty、`production_approval=false`。外部 SaaS 有序返回/长期墓碑容量、v0.2 旧状态受控切换、真实部署/拨号与生产签收均**未完成**;本地阻断不代签真实切换。 |
| F05 | 多D/第二租户不在本轮运行范围。 | 另获阶段与额度/资源授权。 |
| 下一版 Dispatcher 状态/临时数据(仅文档) | [`plan-dispatcher-state-v0.1.md`](plan-dispatcher-state-v0.1.md) 与 [`thirds/v0.4.md`](thirds/v0.4.md) 记录用户本轮提出的重启全量、状态来源收敛、pause/stop 强制挂断与任务结束条件清理;当前代码、Schema、F09/F06 证据**未切换**。日志全部原样的要求与现行禁令冲突,不列为已批准实施。 | 冻结全量分页与控制交接、SaaS 应用收讫/恢复依据、清理期限及新严格 Schema/Mock;TDD 实施后再重跑本地门禁。外部兼容、真实拨号和切换另签收。 |
## 9. 协作与版本记录
仅集成负责人更新本文 §8、公共合同和合并状态;有并行授权时一 lane 一工作区/独占写集合、每批回归,不假定未跟踪文件在 HEAD。v0.1 是项目规划及字段草案,不是 W01/SaaS 正式发布版本。真实环境的资源、拨号和生产权限不因文档改动增加。