Document v0.3 Mock acceptance and enforce local release gates
This commit is contained in:
@@ -77,7 +77,7 @@
|
||||
|
||||
## 当前范围
|
||||
|
||||
- 后续Agent必须先读 **`docs/plan-config-read-v0.1.md`**:按 F 工作包/文档索引读取需求与契约,确认 C/L/M 和 I/M/G 前置、授权及写入边界后实施;并行时按该计划§9认领,子Agent只写独占模块/证据,§8本轮总台账、公共文件及合并状态由集成负责人单写。已恢复原内容的 `docs/archive/plan-0918.md` 仅保留旧 W00–W16 历史,不是现行计划入口。计划不替代权威Schema或验收,不重复审批已确认方向,不自动授权真实云/付费/拨号。
|
||||
- 后续Agent必须先读 **`docs/plan-config-read-v0.1.md`**:按 F 工作包/文档索引读取需求与契约,确认 C/L/M 和 I/M/G 前置、授权及写入边界后实施;并行时按该计划§9认领,子Agent只写独占模块/证据,§8本轮总台账、公共文件及合并状态由集成负责人单写。已恢复原内容的 `docs/archive/plan-0918.md` 仅保留旧 W00–W16 历史,不是现行计划入口。任务发现现行项目内合同另读 `docs/plan-0926.md` 和 `docs/thirds/v0.3.md`;计划中 v0.2 状态仅作历史。计划不替代权威Schema或验收,不重复审批已确认方向,不自动授权真实云/付费/拨号。
|
||||
- 当前MQ-only/OSS调整目标已获用户明确批准由当前Agent独立执行,**不启动子Agent**,不得再以子Agent模型/fast环境阻塞本目标;此前按任务类型自动分工规则在本目标不适用。若未来另获启动授权,仍遵守下条模型要求。
|
||||
- 用户指定:若启动开发及配套审查子Agent,固定 **gpt-5.6-luna、max思考、fast模式**。启动前查询精确provider/model和runner支持并显式配置(当前工具用模型`:max`后缀及`fast: true`,不继承默认);不可用/不支持/无法核验则报告阻塞,不静默换模型、降思考档、关fast或换CLI。此为后续执行约束,本轮仅修复计划,未启动开发子Agent。
|
||||
- 并行开发须先有获授权的可追溯Git/契约基线、一lane一工作区/测试资源、无交叠写集合及每批合并后回归;当前子项目尚未跟踪的文件不能假定存在于HEAD/worktree。不得自动提交、暂存或清理父项目无关改动;详情见 `docs/plan-config-read-v0.1.md` §9。
|
||||
@@ -155,7 +155,7 @@
|
||||
- **现行合同(尚未切换)**:Dispatcher按MQ任务agent_version_id,经RabbitMQ专用Topic向SaaS取得不可变AI配置/授权,响应回原Dispatcher;校验租户/源Schema/不可变摘要/能力并持久绑定后向Agent交付执行快照。过去已废弃的独立AI版本GET不复活;新获批准的方向是**任务配置只读接口内含智能体**,须另冻合同而非旧GET兼容层。Agent不直连SaaS,不从CLI/env/源码常量或SDK默认覆盖AI业务值,不新增任务MQ模式字段或调参后台。
|
||||
- 已有model/prompt/voice/speed/ASR输入与识别/temperature/max_tokens/timeout及对话控制必须实际传入SDK或控制器;热词/VAD/top_p/音量/阶段时限等所需扩展先在上游补GAP-09,再生成校验。严格additionalProperties不放宽,不借metadata/raw_request透传。
|
||||
- **现行 AI 合同**:SaaS新版本供新任务引用,无需改代码/重启D/A;在途/原排队任务固定快照,同版本异内容拒绝。缓存按租户+版本隔离,断SaaS无有效授权缓存拒新准入;显式0/false与未提供保真,并发通话不得共享可变SDK参数。
|
||||
- **本轮用户授权的项目内配置目标(现行外部 v1 不变)**:除任务发现已按 `docs/thirds/v0.2.md` §2.5 切换外,以 `docs/thirds/第三方对接事件与请求消费顺序_v0.1.md` 为其余本地契约;F01/F07 Schema、正反例、来源/hash 与 Mock C 通过后可继续本地实现,不等待外部签收/连通。实际 SaaS 字段兼容性仍未验证。拟定 SaaS 只读路径 `/internal/v1/dispatcher/sip`、`/internal/v1/dispatcher/task/:task_id`,以及下一轮归属任务发现 `/internal/v1/dispatcher/tasks`;新增第四条拟定 `/internal/v1/dispatcher/tenant/:tenant_id/quota`(项目路径草案)在取得任务后按 tenant_id 读取分给该D的租户总份额;前两条返回获 management 批准的 SIP 全量和含智能体获授权不可变版本的任务配置;D 用 `X-DISPATCHER-id`/`X-DISPATCHER-SECRET-KEY` 按需获取,不将凭据写入源码、样例或日志。SIP 在启动/重启先取全量、核验 Agent/Asterisk 已加载再读执行队列;任务按租户+任务缓存约60秒,活跃且有待接纳执行时到期主动刷新;已接纳/运行中执行固定原快照,未接纳允许最多约一分钟配置生效延迟。不采用 ETag/304,缓存到期重新读取完整 200 响应;过期或 HTTP 不可用时拒绝新执行,**不使用过期缓存或MQ配置回退**;停/暂停控制不随配置缓存延迟;**现行** opt-out 仍以 MQ 即时事件通知,下一版仅在最终 `call.result` 中通知 SaaS,业务签收前不可冒称两者等价。任务终结后仅清配置缓存,不删执行恢复/幂等/outbox。SIP 变更须关执行准入、排空/对账、确认加载后恢复;不能保证一分钟内完成发布。旧 `call.execute` 固定引用/排队 AI 固定规则须经新合同定义重绑定与修改-准入竞态;未发布新版前不可变现行语义。
|
||||
- **本轮用户授权的项目内配置目标(现行外部 v1 不变)**:除任务发现已按 `docs/thirds/v0.3.md` 项目内版本切换外,以 `docs/thirds/第三方对接事件与请求消费顺序_v0.1.md` 为其余本地契约;F01/F07 Schema、正反例、来源/hash 与 Mock C 通过后可继续本地实现,不等待外部签收/连通。实际 SaaS 字段兼容性仍未验证。拟定 SaaS 只读路径 `/internal/v1/dispatcher/sip`、`/internal/v1/dispatcher/task/:task_id`,以及下一轮归属任务发现 `/internal/v1/dispatcher/tasks`;新增第四条拟定 `/internal/v1/dispatcher/tenant/:tenant_id/quota`(项目路径草案)在取得任务后按 tenant_id 读取分给该D的租户总份额;前两条返回获 management 批准的 SIP 全量和含智能体获授权不可变版本的任务配置;D 用 `X-DISPATCHER-id`/`X-DISPATCHER-SECRET-KEY` 按需获取,不将凭据写入源码、样例或日志。SIP 在启动/重启先取全量、核验 Agent/Asterisk 已加载再读执行队列;任务按租户+任务缓存约60秒,活跃且有待接纳执行时到期主动刷新;已接纳/运行中执行固定原快照,未接纳允许最多约一分钟配置生效延迟。不采用 ETag/304,缓存到期重新读取完整 200 响应;过期或 HTTP 不可用时拒绝新执行,**不使用过期缓存或MQ配置回退**;停/暂停控制不随配置缓存延迟;**现行** opt-out 仍以 MQ 即时事件通知,下一版仅在最终 `call.result` 中通知 SaaS,业务签收前不可冒称两者等价。任务终结后仅清配置缓存,不删执行恢复/幂等/outbox。SIP 变更须关执行准入、排空/对账、确认加载后恢复;不能保证一分钟内完成发布。旧 `call.execute` 固定引用/排队 AI 固定规则须经新合同定义重绑定与修改-准入竞态;未发布新版前不可变现行语义。
|
||||
- 凭据/供应商端点来自受控引用且有授权/出口校验,不能因可调参数绕过安全硬限额或启用不安全重试。OpenAI默认自动重试显式关闭;日志只留脱敏版本/摘要/有效参数,不打印prompt/变量/密钥。
|
||||
- 静态发布只约束SIP/节点制品,不将AI配置硬编码;GAP-08/09及SDK参数PoC为P1门禁,验证入口见验收§5.1(现有E/L项子场景,不新增虚假通过数)。
|
||||
|
||||
@@ -167,7 +167,7 @@
|
||||
- **本轮项目内目标(尚未替换真实外部运行)**:任务(含智能体)、SIP、任务发现和按 tenant_id 的租户额度走四条只读 HTTP;呼叫、控制及其必要回执/单份最终结果走 MQ,取消对外业务查询/补传和分散通话事件。不提供配置HTTP→MQ回退,也不恢复其它业务HTTP通道。SaaS须分发 management 已批准的唯一 SIP 版本,management 仍为唯一编辑/审批面。该本地语义按第三方契约及版本化 Schema/示例/hash 冻结,Mock C 是本地门禁,不等待外部签收;真实切换仍需另行授权。
|
||||
- 新HTTP配置字段阶段草案见 `docs/contracts/config-read-fields-v0.1-proposal.md` 及同目录 `config-read-v0.1.schema.json`/mock示例;截图只证实UI含义,英文响应键为项目自定义,绝非SaaS现网接口已确认字段。用户新增任务排除日期、线路时段等未见截图项按项目需求设计;SIP传输/鉴权/注册及额度未知不能猜默认值。Schema/Mock 校验可满足项目内 C,但不代表 SaaS/management 已发布或真实兼容;真实响应、审批来源、摘要和 Agent/Asterisk 实际加载仍须单独验证。当前唯一权威运行契约不因草案变化。
|
||||
- **每个Dispatcher必须有独立、全局唯一且不重复的ID及独立接收Topic/队列**;指定D的任务/现行MQ配置结果/上传结果不能由其它D抢收,也不能广播后仅靠正文过滤;新目标只读HTTP配置由该D UUID+SECRETKEY获取且须核对任务归属。身份与tenant/Agent/Cell ID、dispatcher_epoch分开;现行合同保留租户独立队列及原值tenant_key,完整新路由长度预算须重验。具体ID生成/持久化、Topic/绑定、消息字段/关联/错误/期限须随W01新版本冻结,不凭本文给旧严格Schema添加字段。
|
||||
- **本轮项目内任务队列所有权硬边界(规则见第三方契约 §1.2 和 `mq-topology-v0.1-proposal.json`;F03 本地实现状态见本条末尾):任务队列及 RabbitMQ 绑定只能由 SaaS 创建、维护、退役;Dispatcher 只消费,不能自行声明/建队、绑定或删除任务队列;F03 本地 v0.2 运行代码已实现此边界,外部兼容性未验证。** SaaS 需先确保持久任务/控制队列及绑定就绪,再向按归属 D+任务 ID 定位的任务队列发布 persistent 命令;D 离线时已存在的任务队列可积压,缺队列而未入队的原消息由 SaaS 保留并在就绪后按同一身份重发,不能称作 D 重启自动补收。新增按 D 身份授权的 `tasks` 全量清单用于 D 重启恢复,运行中拟每 30 秒 `GET /internal/v1/dispatcher/tasks?after=<cursor>` 拉取该 D 新增/更新/撤销,`cursor` 为 SaaS 变更水位而非最大任务 ID,失效则重新取一致全量快照;轮询间隔不等于端到端发现时限,也不能替代 MQ 即时控制;pause保留积压、resume重新核验后继续消费原积压,过期不延长;stop持久生效后未接纳旧消息**静默消费/ACK,不拨号、不回逐条结果**,不能直接删队列;控制本身和已有在途通话仍有处理/最终结果,本地错误与计数不静默。同一 D 下任务响应须带 tenant_id/原值 tenant_key,按 tenant_id 读取的租户额度在各任务间共用,0停新准入、缺失/过期失败拒新;降额不强挂或清未知占用,跨 D 仍须权威份额;任务数与队列资源有上限。F03 本地实现已移除 `--tenant-key` 和自建租户队列路径,按任务发现只被动消费 SaaS 预建任务/控制队列;本地 RabbitMQ 无 `configure` 权限测试通过。当前任务发现每 D 单次完整响应最多 256 条,不分页、不在正文返回队列地址;按 D+task ID 推导队列,空变更 `next_cursor` 等于请求 `after`,410 过期重拉全量,`removed` 撤销归属。发现失败持久关新准入并停任务消费,不停 MQ 控制;完整响应、归属和游标须原子持久化。旧 v0.1 分页证据仅保留历史,v0.2 本地证据见 `docs/evidence/f03-local-task-discovery-v0.2.md`;均不证明外部 SaaS/management 兼容、多 D 业务运行或生产可用。
|
||||
- **本轮项目内任务队列所有权硬边界(见第三方契约 §1.2 与 `mq-topology-v0.1-proposal.json`):任务队列和 RabbitMQ 绑定只能由 SaaS 创建、维护、退役;Dispatcher 只消费,不能自行建队、绑定或删除;F03 v0.3 已本地验证,外部兼容性未验证。** SaaS 须先确认持久任务/控制队列与绑定就绪,再向归属 D+任务 ID 的队列发布 persistent 命令;D 离线时已存在队列可积压,缺队列而未入队的原消息由 SaaS 保留并按原身份重发。现行项目内任务发现从 `GET /internal/v1/dispatcher/tasks?after=0` 开始,以持久十进制事件游标逐页读取同一 `tasks[]`(每页最多 256、每 D 活跃任务最多 256),后续约每 30 秒从已提交游标续读;同任务更新可再次出现,`status=removed` 为墓碑,空页保持游标并表示追平。**无 410/游标过期重建、`changes` 兼容或配置回退;不得用旧 v0.2 checkpoint 解释事件 ID。** SaaS 必须保证分页完整有序、序号及撤销墓碑长期可读;累计墓碑不受活跃任务上限约束,容量和灾备连续性尚待外部签收。每页任务/归属/响应/游标同一 SQLite 事务提交;首次未追平、请求/响应/持久化错误均持久关新准入、停任务消费,但 MQ 即时控制和结果恢复照常处理,不自动拨号或重置游标。轮询周期不是端到端发现 SLA,也不能取代 MQ 控制。pause 保留积压,resume 重新核验后消费原积压且不延长期限;stop 持久生效后未接纳旧消息**静默消费/ACK,不拨号、不回逐条结果**,不删队列;控制本身及在途通话仍有处理/最终结果,本地错误/计数不静默。任务带 tenant_id/原值 tenant_key,同 D 各任务共用按 tenant_id 取得的租户额度;额度 0/缺失/过期拒新,降额不强挂或清未知占用,跨 D 仍需权威份额。已移除 `--tenant-key` 及自建租户队列,按 D+task ID 推导 SaaS 预建队列;本地 RabbitMQ 无 `configure` 权限测试通过。v0.1/v0.2 发现证据仅作历史,v0.3 本地证据见 `docs/evidence/f09-local-acceptance-v0.3.md`;不证明真实 SaaS/management、多 D 业务或生产可用。
|
||||
- **OSS相关配置存于Dispatcher配置文件,Agent向Dispatcher领取临时上传TOKEN后直传OSS,不保存长期凭据;SaaS不再下发OSS配置/TOKEN。** D复用官方SDK提供受限TOKEN/目标信息,配置缺失/无效明确失败;不在样例、源码、日志或证据中保存实际密钥/完整TOKEN。过期只允许A显式向D重新申请,不自动续期或向SaaS申请TOKEN;精确配置格式/TOKEN形态/UploadGrant映射另行核验,不猜字段。
|
||||
- **本轮项目内目标:上传仅负责 Agent 直传及 D 可靠通知 MQ,SaaS 后续处理不属本项目职责;外部 v2 的现行上传事实不因本地目标自动改变。** D保留签发能力、不转发文件;R13持久保存事实与recording.uploaded outbox,消息为persistent、进入指定durable队列/绑定、mandatory无return且publisher confirm成功后才记交付完成。只写本地outbox不算入队;不申请SaaS会话、不等待verified/OSS ID、不新增VERIFYING、不伪造SaaS结果。
|
||||
- 新版recording.uploaded取代本项目recording.ready,字段为call_id/recording_id/upload_id/bucket/object_key/format/channels/sample_rate_hz/duration_ms/size_bytes/checksum_sha256;不含TOKEN/密钥/签名URL。MQ失败/确认丢失/重启只恢复原消息身份的交付,不重新PUT或新建资产。现行AI/控制等必要请求响应不受此收缩影响;新目标AI改用任务只读HTTP获取,控制仍走MQ。
|
||||
|
||||
@@ -26,7 +26,7 @@ Go SIP 调度与执行项目:同一 module/二进制通过 Cobra 提供 `dispa
|
||||
|
||||
## 当前本地实现
|
||||
|
||||
旧 MQ-only 基线的身份隔离、配置、回执、控制、AI授权及上传通知有 loopback RabbitMQ 本地证据,见 `docs/evidence/20260922-mq-only-local-final.md`;该证据不代签新目标。本轮项目内结果分别见 [F03 v0.2](docs/evidence/f03-local-task-discovery-v0.2.md)、[F04](docs/evidence/f04-local-dial-policy-v0.1.md)、[F08](docs/evidence/f08-local-final-result-v0.1.md)、[F09](docs/evidence/f09-local-acceptance-v0.2.md) 和 [F06 本地发布阻断](docs/evidence/f06-local-release-gates-v0.2.md)。部署 preflight 在缺失 Asterisk/tcpdump 时明确失败并保留日志;本地测试不等同真实 SaaS、供应商或生产验收。
|
||||
旧 MQ-only 基线的身份隔离、配置、回执、控制、AI授权及上传通知有 loopback RabbitMQ 本地证据,见 `docs/evidence/20260922-mq-only-local-final.md`;该证据不代签新目标。本轮项目内结果分别见 [F03 v0.3/F09](docs/evidence/f09-local-acceptance-v0.3.md)、[F04](docs/evidence/f04-local-dial-policy-v0.1.md)、[F08](docs/evidence/f08-local-final-result-v0.1.md) 和 [F06 本地发布阻断](docs/evidence/f06-local-release-gates-v0.3.md);[F03 v0.2](docs/evidence/f03-local-task-discovery-v0.2.md) 只留作历史。部署 preflight 在缺失 Asterisk/tcpdump 时明确失败并保留日志;本地测试不等同真实 SaaS、供应商或生产验收。
|
||||
|
||||
- `contracts/upstream/` 嵌入项目内自包含 W01 基线,记录父源 commit、dirty 继承和 SHA-256;运行时代码读取该包,不读取父目录。
|
||||
- `internal/store/` 提供 SQLite inbox、任务、租户/跨 Cell 配额、控制 CAS、replay 和 outbox 事务。
|
||||
@@ -35,7 +35,7 @@ Go SIP 调度与执行项目:同一 module/二进制通过 Cobra 提供 `dispa
|
||||
- 旧 `internal/control/` HTTP业务实现、启动入口、CLI参数及环境配置已删除,不保留兼容通道。
|
||||
- `internal/rpc/` 提供 `agent.v1` Unary handlers、mTLS TLS1.3 配置、会话世代/fencing、CAS、permit/fact/upload metadata 边界;Agent CLI 可选启动受证书保护的 gRPC listener,并校验静态 Cell 制品与 AI 授权边界。`internal/calllog/` 提供按手机号 HMAC 关联、掩码和 allow-list JSONL 外呼业务日志,不写原始号码、凭据、音频、转写或 prompt。
|
||||
- `internal/health/` 使用 gopsutil 采样主机/进程资源;媒体端口和 AI 配额未接入时明确报告 unknown。`internal/mq/` 默认 bounded prefetch=1、per-tenant DLQ 和 publisher confirm;`make mq-integration-local` 只启动 disposable RabbitMQ。
|
||||
- `make acceptance-local` 校验当前项目内合同、格式、race/vet/build 和受限 RabbitMQ 本地矩阵;不访问真实服务。`make release-check-local` 构建并校验一次性本地制品与归档包、v0.1 业务/v0.2 发现/v3 队列草案和 Proto 的版本/hash,验证已有制品、打包目录、归档及校验文件均不会被覆盖,且 CLI 旧开关不存在。`make release` 仅允许在 `dist/` 下指定**尚不存在**的目标目录,记录 dirty-source 与版本/hash,始终标记 `production_approval=false`;两者均不部署或拨号。真实 OSS 授权测试仍须另用受控环境显式开启,不能从 Mock 或上述制品检查推定通过。
|
||||
- `make acceptance-local` 校验当前项目内合同、格式、race/vet/build 和受限 RabbitMQ 本地矩阵;不访问真实服务。`make release-check-local` 构建并校验一次性本地制品与归档包、v0.1 业务/v0.3 发现/v3 队列草案和 Proto 的版本/hash,验证已有制品、打包目录、归档及校验文件均不会被覆盖,且 CLI 旧开关不存在、mixed/real 制品在打开资源前拒绝。`make release` 仅允许在 `dist/` 下指定**尚不存在**的目标目录,记录 dirty-source 与版本/hash,始终标记 `production_approval=false`;两者均不部署或拨号。真实 OSS 授权测试仍须另用受控环境显式开启,不能从 Mock 或上述制品检查推定通过。
|
||||
- 直接依赖版本/本地许可证清单见 [`docs/evidence/20260918-dependencies.md`](docs/evidence/20260918-dependencies.md);门禁结论见 [`docs/evidence/20260918-acceptance-matrix.md`](docs/evidence/20260918-acceptance-matrix.md);SIP 注册/认证/From/PAI/前缀/选路对比见 [`docs/evidence/20260919-sip-routing-implementation-comparison.md`](docs/evidence/20260919-sip-routing-implementation-comparison.md),手机号业务日志边界见 [`docs/evidence/20260919-phone-call-business-log.md`](docs/evidence/20260919-phone-call-business-log.md)。
|
||||
|
||||
以上 20260918–20260922 的历史证据只覆盖旧基线;本轮新合同、Mock C 和 F03–F09/F06 的本地证据单独列在前文。两组证据都不等于真实供应商、生产 SaaS/MQ receipt、实际切换或第二阶段拓扑通过。范围和延期项见 [`docs/evidence/20260920-scope-amendment.md`](docs/evidence/20260920-scope-amendment.md)。
|
||||
@@ -59,10 +59,11 @@ sip-go-agent agent upload-retry --spool /path/to/agent-spool \
|
||||
| 文档 | 内容 |
|
||||
| --- | --- |
|
||||
| [本轮配置读取与有界外呼计划](docs/plan-config-read-v0.1.md) | 首读入口:F00–F09、C/L/M 与 I/M/G 门禁、单写/验证状态;四条只读 GET 的外部合同仍待签收 |
|
||||
| [任务发现事件游标计划](docs/plan-0926.md) | v0.3 项目内合同、运行与本地 Mock 范围;外部顺序、墓碑容量和切换另行签收 |
|
||||
| [旧 plan-0918 归档原文](docs/archive/plan-0918.md) | W00–W16 历史状态及旧 MQ-only 阶段证据;非本轮执行入口 |
|
||||
| [Go重写方案 v0.3](docs/architecture/Go重写方案_v0.3.md) | 本次单节点/单 Cell/单租户目标、至少3 SIP fixture、双AI模式、P0/P1/第二阶段分期 |
|
||||
| [通信与事件数据交互 v0.1](docs/contracts/通信与事件数据交互_v0.1.md) | 首发Unary、静态SIP与双模式、§6.1–6.3 SaaS配置来源/参数矩阵/安全边界 |
|
||||
| [SaaS↔Dispatcher 第三方对接顺序](docs/thirds/第三方对接事件与请求消费顺序_v0.1.md)、[任务发现 v0.2](docs/thirds/v0.2.md) | 仅任务发现按 v0.2 §2.5 本地修订,其余项目内 SaaS 协议仍用 v0.1;外部合同/取消查询补传仍待签收 |
|
||||
| [SaaS↔Dispatcher 第三方对接顺序](docs/thirds/第三方对接事件与请求消费顺序_v0.1.md)、[任务发现 v0.3](docs/thirds/v0.3.md) | 仅任务发现按 v0.3 项目内修订;[v0.2](docs/thirds/v0.2.md) 是历史,其余项目内 SaaS 协议仍用 v0.1;外部合同/取消查询补传仍待签收 |
|
||||
| [现行 MQ 机器契约](contracts/upstream/v1/mq.schema.json)、[Agent Proto](proto/agent/v1/agent.proto) | 现行消息和Unary字段权威;HTTP配置仅有项目提案 |
|
||||
| [OpenAPI与MQ字段索引 v0.1](docs/references/OpenAPI与MQ字段索引_v0.1.md) | 旧源只读提取42个HTTP操作/115个组件及哈希;不代表新MQ-only入口,不手改生成物 |
|
||||
| [deploys:物理机 systemd 发布包](deploys/README.md) | 锁定 Debian 13/Go 1.27.1/发布版本,构建并上传不依赖 Docker 的 Agent/Dispatcher 安装包 |
|
||||
|
||||
+4
-4
@@ -4,11 +4,11 @@
|
||||
|
||||
## 首读顺序
|
||||
|
||||
1. [`plan-config-read-v0.1.md`](plan-config-read-v0.1.md):本轮两只读配置接口、有界消费及外呼时段的 F00–F06 执行入口、C/L/M 和 I/M/G 门禁。原 [`plan-0918.md`](archive/plan-0918.md) 已按 HEAD 原文归档,仅作旧 W00–W16 历史。
|
||||
1. [`plan-config-read-v0.1.md`](plan-config-read-v0.1.md):四条只读接口、任务发现、有界消费及外呼时段的 F00–F09/F06 执行入口、C/L/M 和 I/M/G 门禁;任务发现 v0.3 另读 [`plan-0926.md`](plan-0926.md)。原 [`plan-0918.md`](archive/plan-0918.md) 按 HEAD 原文归档,仅作旧 W00–W16 历史。
|
||||
2. [`architecture/Go重写方案_v0.3.md`](architecture/Go重写方案_v0.3.md):单节点、单 Cell、单租户的总体方案与分期。
|
||||
3. [`contracts/`](contracts/):SaaS、Dispatcher、Agent、RabbitMQ 和 OSS 的项目内契约。
|
||||
4. [`acceptance/验证与切换验收_v0.3.md`](acceptance/验证与切换验收_v0.3.md):验收门禁、证据要求和第二阶段边界。
|
||||
5. [`evidence/20260922-mq-only-local-final.md`](evidence/20260922-mq-only-local-final.md):当前 MQ-only 本地/隔离范围的最新汇总证据。
|
||||
5. [`evidence/f09-local-acceptance-v0.3.md`](evidence/f09-local-acceptance-v0.3.md) 和 [`evidence/f06-local-release-gates-v0.3.md`](evidence/f06-local-release-gates-v0.3.md):任务发现当前项目内 Mock 与发布阻断证据;[`20260922-mq-only-local-final.md`](evidence/20260922-mq-only-local-final.md) 仅作旧 MQ-only 本地基线。
|
||||
|
||||
## 按职责查找
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
|
||||
### 契约与决策
|
||||
|
||||
- [`thirds/第三方对接事件与请求消费顺序_v0.1.md`](thirds/第三方对接事件与请求消费顺序_v0.1.md):只描述 SaaS↔Dispatcher 的配置读取、现行 MQ 呼叫/控制命令及回执、拟定单份最终通话结果;取消对外查询/补传和分散事件尚待签收,替代已删除的六份分散说明。
|
||||
- [`thirds/第三方对接事件与请求消费顺序_v0.1.md`](thirds/第三方对接事件与请求消费顺序_v0.1.md):SaaS↔Dispatcher 的项目内配置与 MQ 业务契约;仅其中旧 §2.5 任务发现已由 [`thirds/v0.3.md`](thirds/v0.3.md) 项目内合同替换,[`v0.2`](thirds/v0.2.md) 为历史。取消对外查询/补传及分散事件仍待外部签收。
|
||||
- [`../contracts/upstream/v1/mq.schema.json`](../contracts/upstream/v1/mq.schema.json)、[`event-payloads.schema.json`](../contracts/upstream/v1/event-payloads.schema.json)、[`mq-topology.json`](../contracts/upstream/v1/mq-topology.json):现行 MQ 信封、事件正文、路由的机器契约。
|
||||
- [`contracts/config-read-fields-v0.1-proposal.md`](contracts/config-read-fields-v0.1-proposal.md):两只读接口字段来源、响应结构与项目自拟 Schema/Mock 示例;**不是现网 SaaS JSON 或正式合同**。
|
||||
- [`decisions/`](decisions/):已记录的关键技术决策。
|
||||
@@ -44,4 +44,4 @@
|
||||
|
||||
- 不在 `docs/` 复制上游 Schema;上游契约包、生成物和字段索引保持单一来源。
|
||||
- 不把证据摘要改写成新的通过结论;范围、环境和未执行项必须保持可追溯。
|
||||
- 新增文档先归入方案、契约、决策、证据或归档中的一个明确类别;本轮根目录的 `plan-config-read-v0.1.md` 是单一执行入口,不新增其它无类别根文件。
|
||||
- 新增文档先归入方案、契约、决策、证据或归档中的一个明确类别;根目录 `plan-config-read-v0.1.md` 是总执行入口,用户指定的 `plan-0926.md` 仅说明 v0.3 任务发现分项,不另设第二个总台账;不新增无类别根文件。
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
# F06 P1 本地发布阻断:任务发现 v0.3
|
||||
|
||||
范围:单 Dispatcher/Agent/Cell/租户的项目内隔离 Mock;仅构建和检查临时制品,**未安装、部署、连接真实 SaaS/OSS/SIP、拨号或切换生产**。以前的 [`v0.2 本地制品记录`](f06-local-release-gates-v0.2.md) 仅作历史,不代替本次核验。
|
||||
|
||||
## 本地制品与合同
|
||||
|
||||
- `make release-check-local`:通过;从 Go 1.27.1 的独立项目构建一次性二进制及归档包,检查已存在 release/package 目录、压缩包或校验文件时均拒绝覆盖;校验二进制、归档、checksum/manifest。临时 release SHA-256:`243387b2441d0f88a156e955b821532c6f4246d98669ccbb9a3d91ddb4b2c0ea`。临时文件随测试删除,不冒充已发布制品。
|
||||
- `scripts/build-release.sh` 的 `contract_attestation` 和本地制品检查一致:其他项目内业务协议 `local-contract-manifest.v0.1`,**任务发现 `local-contract-manifest.v0.3`**,MQ 拓扑 `project-saas-dispatcher.v0.1`(v3 队列草案),以及当前 Proto manifest。v0.3 的版本化来源为 [`docs/thirds/v0.3.md`](../thirds/v0.3.md),来源 SHA-256 `2fa8f5a10b1509044b04b4416a44496c5e9a2d9c741e5437862e2edcb92649ce`;[`local-contract-manifest-v0.3.json`](../contracts/local-contract-manifest-v0.3.json) SHA-256 `436de2fe5314deb6a2b773f0217b72c61140db4195f68dcb2f9adb5290b93286`。旧 v0.2 Schema/清单保留追溯但不进入当前运行制品的发现 attestation。
|
||||
- `scripts/check-contracts.sh` 验证 v0.3 严格 Schema、8 个正例、2 个反例与 13 个来源/产物摘要;旧上游 `contracts/upstream/v1/` 没有更改。`./scripts/acceptance-local.sh`(含格式、`go test -race ./...`、`go vet ./...`、构建及受限 RabbitMQ)通过,具体矩阵与覆盖率见 [`f09-local-acceptance-v0.3.md`](f09-local-acceptance-v0.3.md)。
|
||||
- 制品已直接启动测试 `dispatcher` 的 `mixed` 和 `real` 两种模式:均返回隔离 Mock 限制错误,在创建 SQLite 文件前拒绝;旧 `--tenant-key`、`--consume` 开关不在制品中。隔离 RabbitMQ 测试账号无 `configure` 权限,Dispatcher 不建队或绑定,真实模式没有因 v0.3 合同放行。
|
||||
- 本地构建来源有未提交变更,制品仅标记 `local-development`、`production_approval=false`;Go/Proto/合同摘要可核对不等于发布、运行环境或真实配置已验收。
|
||||
|
||||
## 保持阻断的真实事项
|
||||
|
||||
1. SaaS/management 尚未签收 v0.3 精确响应、每页完整有序及无游标过期保证;响应仅有页级 `next_cursor`、无逐项 `event_id`,Dispatcher **不能独立证明页内未漏项**。撤销墓碑长期保留的容量、灾备后序号连续性和长期旧游标可读性没有外部验收,活动任务 256 上限不能代替累计墓碑预算。
|
||||
2. 旧 v0.2 checkpoint 无法直接解释为 v0.3 事件序号。当前代码会拒绝静默迁移;任何真实旧队列/在途执行的排空、状态回迁及资源所有权移交都须另有获批且可核验的方案,不删旧任务、执行事实、幂等与 outbox,也不并行运行两套消费者。
|
||||
3. 真实 SaaS/MQ/OSS、供应商与 Asterisk/ECS/systemd 部署、媒体/线路拨号、容量、生产准入/回滚/切换均未执行或未授权。本地 Mock 的成功与版本/hash 匹配不构成这些事项的通过记录;生产安全屏障仍须单独满足。
|
||||
@@ -0,0 +1,27 @@
|
||||
# F09 P1 本地 Mock 验收:任务发现 v0.3
|
||||
|
||||
仅验收单节点、单 Dispatcher、单 Agent、单 Cell、单租户的隔离 Mock。任务发现采用项目内拟定 `docs/thirds/v0.3.md`,其余 SaaS 项目内业务协议仍为 v0.1;`contracts/upstream/v1/` 未修改。下述结果不是 SaaS/management 或生产验收。
|
||||
|
||||
## 适用矩阵
|
||||
|
||||
| 场景 | 本地事实与测试 |
|
||||
| --- | --- |
|
||||
| 同一合同及分页 | 首次与后续均读取 `tasks[]`,`after` 是规范十进制事件游标;首次多页、空页保持游标、同任务后续更新、`removed` 墓碑均通过。响应不含逐项 `event_id`;请求中的 `page_token`、旧 `changes`/410 路径不运行,也无 HTTP→MQ 回退。见 `internal/configread/discovery_v03_test.go`、`internal/dispatcher/task_runtime_v03_test.go`。 |
|
||||
| 并发翻页、页中断与重启 | Mock 在翻页时加入同任务更新事件,D 追至最终空页并应用较新状态;第二页 HTTP 失败时仅持久保存首个成功页,关新准入;关闭并重新打开 SQLite 后从已提交的游标续读,不从零重建,也不跳过页。见 `internal/dispatcher/task_runtime_v03_recovery_test.go`、`task_runtime_v03_test.go`。 |
|
||||
| 故障与事务 | 非规范/倒退游标、异常响应及 HTTP 410 均报错,不伪造空页;发现服务中断时停止旧任务消费者但保留 MQ 控制。SQLite 触发器模拟分配写失败后,任务、游标和响应证据全部回滚;未物理填满磁盘。v0.2 checkpoint 无法直接当事件 ID 使用,阻断静默切换。见 `internal/configread/discovery_v03_test.go`、`internal/store/local_v03_discovery_test.go`、`local_v03_failure_test.go`、`internal/dispatcher/task_runtime_v3_test.go`。 |
|
||||
| 控制与重复投递 | 发现页中的 `running` 不解除 MQ pause/stop 或已撤销墓碑;pause 保留积压、stop 对未接纳命令静默 ACK,在途终结仍出最终结果。重复执行恢复原回执,持久一次性拨号决定不产生第二次呼叫。见 `internal/store/local_v03_failure_test.go`、`execute_business_duplicate_test.go`、`local_origination_test.go`、`internal/dispatcher/task_control_v3_test.go`、`task_queue_v3_stop_active_test.go`。 |
|
||||
| SaaS 建队与 MQ/最终结果 | 受限 Dispatcher RabbitMQ 用户的 `configure` 权限为空,只消费 SaaS Mock 预建任务/控制队列;队列缺失或满、确认丢失与重投不冒充已接纳。HTTP→SQLite→MQ→Mock 最终结果与 outbox 恢复集成通过。见 `internal/mq/amqp_v3_integration_test.go`、`amqp_v3_queue_full_integration_test.go`、`internal/dispatcher/local_v01_integration_test.go`。 |
|
||||
| 既有门禁 | 号码白名单、时段、配额、超时、录音结果与最后拨号判断继续使用 F04/F08 本地测试;V3 mixed/real 入口在资源连接前拒绝,Mock 不发真实呼叫。 |
|
||||
|
||||
## 执行记录与覆盖率
|
||||
|
||||
- `./scripts/acceptance-local.sh`:通过。依次核对 gofmt、Proto/本地 Schema 正反例和 SHA-256 清单、`go mod verify`、`go test -race ./... -count=1`、`go vet ./...`、构建及受限 RabbitMQ 集成测试。首次执行发现一个旧集成夹具仍使用 v0.2 全量响应;改为 v0.3 `after=0` → `after=1` 并在载入任务配置前追平后复测通过。
|
||||
- `scripts/check-contracts.sh`:v0.3 正例 8、反例 2、清单文件 13 均通过;旧 v0.2 清单仅作为历史,不参与运行。`docs/contracts/local-contract-manifest-v0.3.json` SHA-256 为 `436de2fe5314deb6a2b773f0217b72c61140db4195f68dcb2f9adb5290b93286`;`docs/thirds/v0.3.md` SHA-256 为 `2fa8f5a10b1509044b04b4416a44496c5e9a2d9c741e5437862e2edcb92649ce`。
|
||||
- `go test -cover -count=1`:P1 业务包 `agent` 71.1%、`callwindow` 84.4%、`configread` 75.2%、`contract` 74.0%、`dispatcher` 70.5%、`rpc` 69.9%、`store` 67.4%、`tenant` 85.4%;RabbitMQ integration 下 `mq` 66.4%、`dispatcher` 70.6%。本轮 P1 业务包均 ≥65%。`cmd/sip-go-agent` 30.0%、历史 `internal/callruntime` 61.7% 单列技术债,不冒称全仓库达标;`mq` 未带 integration tag 的结果不用于验收。
|
||||
- 工具链为 Go 1.27.1;RabbitMQ 本地镜像 `rabbitmq:4.1-management-alpine`,受限账号无 `configure` 权限。`git diff --check` 通过;测试时工作树未提交,不把当前产物宣称为可复现生产制品。
|
||||
|
||||
## 未通过的外部门禁
|
||||
|
||||
逐项无 `event_id` 是已确认的项目内响应形态:D 只能核验响应级游标进退,**不能独立证明 SaaS 页内顺序和没有漏项**。外部 SaaS 必须签收完整有序返回和无过期游标;长期保留撤销墓碑所需容量及灾备连续性仍未签收。v0.2 已运行状态不得按 v0.3 游标自动解释,也不删旧任务、执行/幂等/outbox;真实切换须另获受控回迁方案和授权。
|
||||
|
||||
真实 SaaS/management、OSS/SIP/云部署、拨号、第二节点/Cell/租户、容量与生产切换均未验证或未授权;本地通过不解除 F06 真实模式阻断。
|
||||
@@ -0,0 +1,49 @@
|
||||
# 任务发现统一事件游标分页计划(设计稿 v0.1)
|
||||
|
||||
> 状态:本文件保留用户确认的目标与门禁;**项目内 v0.3 合同、运行代码和本地 Mock/F06 检查已完成,外部 SaaS/management 未签收,也未部署或拨号**。当前本地任务发现依据为 [`docs/thirds/v0.3.md`](thirds/v0.3.md) 及其独立 Schema;[v0.2](thirds/v0.2.md) 仅保留历史。实施顺序及总体状态仍以 [`docs/plan-config-read-v0.1.md`](plan-config-read-v0.1.md) 为入口。
|
||||
|
||||
## 1. 目标和不变边界
|
||||
|
||||
当前 v0.2 首次返回全量 `tasks`,之后返回独立的 `changes`,还需处理过期游标与 410。目标是**只保留一种任务列表形状**:首次按页读取,后续沿同一事件游标读取更新;不再提供 `changes`、`operation` 或两套响应模式。`after` 是变更事件序号,**不是任务 ID,也不是按任务 ID 排序的翻页位置**。任务 `a01` 更新后可以在下一页或下一次查询中再次出现。
|
||||
|
||||
| 已确认方向 | 约束 |
|
||||
| --- | --- |
|
||||
| 统一 `tasks[]` | 新任务按 `task_id` 识别;已有任务只因 `status` 变化触发任务生命周期动作。其他已有字段仍用于身份、归属和一致性校验,不因其变化擅自重置运行中执行。 |
|
||||
| 事件游标分页 | 首次查询和每约 30 秒的后续查询都允许多页;每页有界,Dispatcher 以返回的游标继续,直到本轮无更多任务。SaaS 不必一次返回该 D 的全部任务。 |
|
||||
| 撤销 | 在同一 `tasks[]` 中以 `status=removed` 返回墓碑项;没有任务出现在某一页**不等于**撤销。撤销不删执行恢复、幂等或 outbox。 |
|
||||
| 永不过期 | 不提供 `cursor_expired`/410 自动重建路径;SaaS 必须保证旧游标仍可继续看到尚未消费的任务变化及撤销。游标无效、记录丢失或无法证明连续时拒绝新执行并报错,不静默重置。 |
|
||||
| 控制与队列 | MQ 的 pause/resume/stop 即时控制不等 30 秒轮询;SaaS 独占创建、绑定和退役任务队列,Dispatcher 只消费预建队列。 |
|
||||
|
||||
本轮仅实现并验证单 Dispatcher、单 Agent、单 Cell、单租户的本地 Mock 路径;单 D 当前任务上限 256 不因分页放宽。其他 SaaS 项目内协议维持 v0.1;不修改 `contracts/upstream/v1/`,不授权真实供应商、云资源、拨号或切换。
|
||||
|
||||
## 2. 项目内 v0.3 读取语义(外部未签收)
|
||||
|
||||
- 路径沿用 `/internal/v1/dispatcher/tasks`;使用现有受控 D 身份。首次从起始事件序号读取(项目草案记为 `after=0`),以后发送**上次已持久化**的 `next_cursor`。`next_cursor` 代表本页最后一个已返回任务的事件位置,不能跳到尚未返回的全局最新事件。
|
||||
- 每次 HTTP 200 都使用 `tasks[]` 和 `next_cursor`,不再区分 `snapshot`、`changes`。`tasks[]` 可以为空;空页表示本轮追平,游标不跳跃。非空页的游标必须前进。项目内 v0.3 已冻结为规范十进制游标、单页最多 256 项、仅响应级 `next_cursor`,不增加 `has_more` 或逐项 `event_id`;必须读到空页,不能凭“本页少于上限”推断结束。
|
||||
- SaaS 对该 D 的任务按**各自最新变更事件序号**升序分页;该序号必须持久、单调且不可复用。同一任务在两次查询之间多次更新,只需返回足以表达**最新状态**的记录,不要求保存每次中间状态。若分页期间再次更新,新的序号必须使该任务可再次被读到;绝不能因游标前进跳过尚未返回的任务。
|
||||
- 普通任务沿用已确认的身份、原值 `tenant_key`、归属和状态字段。`removed` 墓碑必须至少能唯一定位原 D、租户和任务;其余必填规则由项目内 v0.3 Schema 定义;`task_revision` 是任务状态修订,不能代替事件序号,也不从旧 `changes[].operation=removed` 猜字段。已存在任务的租户/任务归属与本地绑定冲突时拒绝处理,不静默迁移。
|
||||
- Dispatcher 对新 `task_id` 建立归属并核验 SaaS 预建队列;对已有任务只按最新 `status` 更新生命周期。`paused`/`stopped` 的本地持久屏障不能被较旧的 `running` 覆盖;`removed` 阻止新接纳,已有在途执行、最终结果和 outbox 按原身份收口,不由轮询触发重复拨号或擅自强挂。`status` 不变时可推进游标,但不重复执行控制动作。任务配置仍通过单独的只读任务接口核验,不让发现列表替代执行快照。
|
||||
|
||||
`tasks[]`、`next_cursor` 的项目内精确字段与正反例已按 [`v0.3 Schema`](contracts/task-discovery-v0.3-proposal.schema.json) 冻结并在 Mock 验证;**不是 SaaS 现网或对外正式合同**。逐项不带 `event_id`,Dispatcher 不能独立证明 SaaS 页内排序/没有漏项;完整有序返回、长期墓碑及授权绑定仍需 SaaS/业务另行签收。
|
||||
|
||||
## 3. 持久化、启动和失败边界
|
||||
|
||||
1. 新加入或重启的 D 先从本地已提交游标继续;没有新协议游标时从起始序号逐页读取。完成首次追平、核验队列和配置前,不消费执行队列或开放新拨号;MQ 控制仍可处理。每页校验 D 身份、任务数上限、身份绑定、状态和游标连续性。
|
||||
2. **任务记录与本页游标必须在同一 SQLite 事务提交**。进程在提交前崩溃则重读原页;提交后重读只会按同一任务身份幂等应用。启动是否已经追平须单独持久标记,不能因为读到第一页就认为拥有完整任务清单。
|
||||
3. 请求失败、响应不完整、页大小超限、非空页游标不前进、重复/倒退位置、身份冲突或 SQLite 提交失败时,不推进游标,不用部分列表推断缺席任务已撤销,拒绝新执行并保留现场错误日志。成功追平并重新完成当前准入核验后才可恢复;不等下一窗口自动拨号、不换线、不重拨。
|
||||
4. 当前 v0.2 游标**不能**直接解释为新事件游标。切换前需有独立版本与受控排空/恢复核验;无法证明旧任务、积压及在途执行安全收口时停止切换,不清除旧执行事实或重建执行身份。不得同时运行 v0.2 与新任务发现消费者,也不提供新接口失败后回退旧增量路径。
|
||||
|
||||
## 4. “永不过期”的实际成本和阻断项
|
||||
|
||||
SaaS 可以维护每个任务的最新事件序号及状态,而不必为 `changes` 另建完整的逐次变更响应。但 D 可能长期离线且仍持有任意旧游标,因此**已撤销任务的墓碑和足以恢复顺序的序号不能提前丢弃**。删除墓碑、复用序号或无声截断历史都与“永不过期”冲突。活跃任务上限 256 **不限制历次撤销的累计数量**;墓碑存储量和首次分页追赶耗时会持续增长。
|
||||
|
||||
在 SaaS/业务签收容量预算、长期保留责任、备份恢复后事件序号不回退、旧 D 游标的可读性和极端积压上限之前,**不能把该方向称为可上线或有界容量方案**。若这些条件无法满足,必须另行确认恢复/清理合同,而不是在实现中偷偷设置过期、忽略墓碑或返回伪造的空页。
|
||||
|
||||
## 5. 本地结果与后续外部门禁
|
||||
|
||||
1. **F07 合同**:发布与当前 v0.2 并列区分的新项目内任务发现版本;冻结起始游标、排序/翻页结束、单页和总量边界、墓碑身份、状态及错误响应;提供正反例、哈希和严格 Schema。旧 v0.2 合同仅保留历史,不原地改写成已发布合同。SaaS/业务签收与项目内 Mock 通过分别标注。
|
||||
2. **F03 实现**:在独立分支以先失败的测试驱动替换配置读取、逐页应用、SQLite 游标和启动追平门禁;移除旧 `changes`/410 运行路径,不保留双模式或故障回退。更新发布制品的合同版本与摘要核对,保持其他 v0.1 业务接口不变。
|
||||
3. **F09 本地验收**:覆盖首轮多页、空页、同任务再次出现且状态变化、墓碑、分页并发更新、跨重启重读、控制与轮询竞态、SQLite 写失败、异常游标及错误页;证明不误删、不重拨、不绕过 SaaS 队列所有权。按本轮 P1 业务模块的覆盖率口径验证,Mock 结果不冒称真实 SaaS 或生产验收。
|
||||
4. **F06 切换阻断**:只有新合同、容量责任、版本化制品、旧状态排空及实际 SaaS 联调各自签收后,才讨论真实部署;本文不授权真实外呼、OSS、ECS、供应商消费或生产切换。
|
||||
|
||||
项目内完成记录:F07 v0.3 Schema/正反例/哈希与 Mock C,F03 v0.3 按页持久化及失败拒新,F09 单节点/单租户本地矩阵见 [`f09-local-acceptance-v0.3.md`](evidence/f09-local-acceptance-v0.3.md);F06 本地发布阻断见 [`f06-local-release-gates-v0.3.md`](evidence/f06-local-release-gates-v0.3.md)。不把这四项本地完成记录外推为 SaaS 顺序、墓碑容量或真实部署签收。
|
||||
@@ -1,6 +1,6 @@
|
||||
# Go SIP Agent:配置读取与有界外呼迭代计划 v0.1
|
||||
|
||||
**当前迭代唯一执行入口。状态:用户已授权 P1 本地迭代;项目内契约冻结与 Mock C、实现及本地验收进行中。** 现行 `contracts/upstream/v1/` 和代码仍是旧外部基线;本文不授权真实 SaaS、生产部署、拨号或切换。分项设计:[有界消费与控制通道](architecture/Dispatcher有界接纳与控制通道改造计划_v0.1.md);原本地 SaaS 结构契约:[第三方对接 v0.1](thirds/第三方对接事件与请求消费顺序_v0.1.md);任务发现单次响应的[修订草案 v0.2](thirds/v0.2.md)及[独立 Schema](contracts/task-discovery-v0.2-proposal.schema.json)不回写 v0.1 历史证据,待新合同及 F03 实现验收后切换。机器校验件:[Schema/正反例](contracts/)。分项材料须与本文一起更新,不能继续使用旧 ETag/304、控制 CAS 或即时反馈目标。
|
||||
**当前迭代唯一执行入口。状态:用户已授权 P1 本地迭代;任务发现 v0.3 项目内合同、运行与 F09/F06 本地门禁已完成,外部签收与真实切换未完成。** 现行 `contracts/upstream/v1/` 仍是旧外部基线;本文不授权真实 SaaS、生产部署、拨号或切换。分项设计:[有界消费与控制通道](architecture/Dispatcher有界接纳与控制通道改造计划_v0.1.md);其他本地 SaaS 业务契约仍按[第三方对接 v0.1](thirds/第三方对接事件与请求消费顺序_v0.1.md);任务发现现用[计划 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)仅保留历史。机器校验件:[Schema/正反例](contracts/)。分项材料须与本文一起更新,不能继续使用旧 ETag/304、控制 CAS 或即时反馈目标。
|
||||
|
||||
## 1. 范围和完成标准
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
| --- | --- |
|
||||
| `/internal/v1/dispatcher/sip` | 用户指定路径;本 D 获管理面批准的 SIP 全量。 |
|
||||
| `/internal/v1/dispatcher/task/:task_id` | 用户指定路径;任务归属、路由/主叫、智能体、时段及两项任务级超时。 |
|
||||
| `/internal/v1/dispatcher/tasks`(可 `?after=<cursor>`) | 用户指定路径;启动一致全量快照,运行中每 30 秒按变更游标发现任务。 |
|
||||
| `/internal/v1/dispatcher/tasks?after=<cursor>` | 用户指定路径;首次 `after=0`、重启从已提交事件游标续读,首次与后续均按统一 `tasks[]` 分页,约每 30 秒发现新变化。 |
|
||||
| `/internal/v1/dispatcher/tenant/:tenant_id/quota` | 本地项目定义路径:取得任务后按其 `tenant_id` 读取该租户分配给本 D 的并发额度;真实 SaaS 路径兼容性未验证。 |
|
||||
|
||||
**任务队列及精确绑定只由 SaaS 创建、维护和退役;D 只消费,不建队、不绑定、不删除。** SaaS 先确认持久队列 ready 再发布 persistent 消息,mandatory 无 return 且 confirm 成功才记入队。D 离线时消息可积压;无队列时未成功入队的原消息由 SaaS 保留,就绪后按原身份重新发布,不能靠 D 重启倒灌。任务队列按 D+任务 ID 定位;租户身份保留原值用于核验和额度汇总。
|
||||
@@ -49,12 +49,12 @@ SaaS 先持久修改权威任务状态,再向独立 D 控制队列发布 `task
|
||||
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` 增量/全量及缓存更新可使状态更严格,不能清除已持久的暂停/停止屏障,且低于已知 `task_revision` 的状态不得覆盖新状态。冷启动恢复本地屏障与 SaaS 全量后取更严格者;冲突/缺失关闭新准入,不丢已有执行事实。
|
||||
4. **优先级:**本地 stopped 永久高于任何 running;本地 paused 只能由有效 resume 解除。`tasks[]` 事件页及缓存更新可使状态更严格,不能清除已持久的暂停/停止屏障,且低于已知 `task_revision` 的状态不得覆盖新状态。冷启动恢复本地屏障并从已提交游标追平 SaaS 事件页后取更严格者;冲突/缺失关闭新准入,不丢已有执行事实。
|
||||
5. **无编号乱序:**控制重投可能重复返回回执,不保证按消息身份“只处理一次”。pause/stop 先关闭准入;与 SaaS 最新状态不符、不可核验或接收乱序时保持关闭并返回明确失败,不根据到达先后自动恢复。恢复必须重新发送有效 resume 并核对最新权威状态;不引入替代 command_id 或控制 CAS。SaaS 不可把发布成功视为控制已应用,丢失回执下状态不明不得主动扩量。
|
||||
|
||||
### 3.4 任务发现、发布与期限
|
||||
|
||||
D 启动/重启用 `/internal/v1/dispatcher/tasks` 拉一致全量快照和水位,运行中每 30 秒 `?after=<cursor>` 拉本 D 新增/更新/撤销。cursor 是不透明变更水位,不是最大 task_id。新版单次返回完整快照或完整变更集,最多 256 条且不分页;响应不带每个任务的 queue/key,D 按已约定的 D+task ID 命名规则推导并只消费 SaaS 预建队列。全量 `cursor`、增量 `next_cursor` 仅在完整校验和原子持久化后推进;空变更的 `next_cursor` 与请求 `after` 相同。`removed` 表示完成退役握手后的撤销归属,不等同于 stop;HTTP 410 `cursor_expired`(含增量超限)关闭新准入并重拉全量,禁止截断或跳过变更。发现清单/任务响应必须提供 tenant_id/tenant_key 以便取额度。无变化不撤销消费关系。
|
||||
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 另定就绪通知协议,不能假设已存在。
|
||||
|
||||
@@ -78,10 +78,11 @@ SaaS 清单须保留已 stopped 但未排空任务;撤销握手按第三方契
|
||||
| --- | --- | --- |
|
||||
| F00 草案整理 | 集成负责人同步本文、分项设计、对接文档、配置 Schema/示例;标明用户给定路径与项目新增额度/路由字段。 | 正反例可验证,不冒充 SaaS 现网。 |
|
||||
| F01 HTTP 与配置/额度合同 | 按第三方文档冻结四条 GET、两 Header、身份映射、任务主叫/候选优先级、两时限较小值、授权/缓存/配置不可变及租户额度份额/版本/有效期。 | 项目内严格 Schema、来源/版本/哈希、正反例;静态提示词或变量来源明确;Mock 验证,不等待外部签收。 |
|
||||
| F07 MQ、发现及控制/结果合同 | 历史 F07 先冻结 v0.1 分页发现;仅任务发现随后由 F03 v0.2 单次响应修订取代。其余 SaaS 独占队列生命周期、v3 routing、精简 call.execute、无编号控制、pause/resume/stop、回执/唯一最终结果、录音边界及消息大小/期限维持原合同。 | 项目内严格 Schema、拓扑、正反例、来源/hash;与 F01 一致并由 Mock 端到端验证后 C 通过。移除旧 query/replay/配置 MQ/拆分事件;外部兼容另记未验证。 |
|
||||
| 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 任务发现修订(F04 前) | 每 D 单次完整响应≤256,正文无 queue/key;失效游标 410 恢复全量,空变更原游标,退役 `removed`;完整响应、任务归属、游标原子持久化,发现失败关准入/停任务消费但不中断控制。 | v0.2 Schema/示例/hash 与 Mock/本地 RabbitMQ 再验;只运行新协议,不把旧 F03 证据当作新版通过。 |
|
||||
| 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%,脱敏证据。 |
|
||||
@@ -100,7 +101,7 @@ SaaS 清单须保留已 stopped 但未排空任务;撤销握手按第三方契
|
||||
| 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条、游标失效、removed 退役、SaaS创建前误发、stopped仍有积压 | 不漏旧任务变更;不接收分页或响应内队列字段,不截断超限结果,失效后重拉全量且只在原子持久化后推进游标;未入队原消息由SaaS保留;stopped保留清单直到静默排空,无未ACK/未知执行再退役。 |
|
||||
| 发现/退役 | 首次多页、同任务较小 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 显式阻塞,不截断/拆分/丢弃。 |
|
||||
@@ -108,7 +109,7 @@ SaaS 清单须保留已 stopped 但未排空任务;撤销握手按第三方契
|
||||
|
||||
## 6. 写入与实施边界
|
||||
|
||||
项目设计与本地目标契约在自身 docs 维护。`contracts/upstream/v1` 保持现行外部运行基线,不擅改;原 v0.1 本地合同与 F03 证据保留为历史;`docs/thirds/v0.2.md` §2.5 中任务发现单次响应已作为**本地运行修订合同**完成实现和 Mock 校验,独立 v0.2 Schema、正反例和来源哈希见新版 F03 证据;这不代表真实 SaaS 已发布或签收。其他业务合同沿用已有本地 v0.1 机器校验件,须记录规范文件与机器文件 SHA-256。该本地契约不冒称 SaaS/management 已发布,外部签收和连通不阻塞本地 C。本文/分项/guide/字段提案必须同步;源码、截图、真实凭据、无关父仓库不改动。TDD 先失败后实施,复用现有库,禁止隐藏错误、HTTP/MQ配置回退、重复外呼。大改另开分支,未获得子 Agent 授权由当前负责人执行。
|
||||
项目设计与本地目标契约在自身 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 前置
|
||||
|
||||
@@ -124,11 +125,12 @@ SaaS 清单须保留已 stopped 但未排空任务;撤销握手按第三方契
|
||||
| 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 修订 | 本地运行已采用单次完整发现:每 D 最多 256 个归属任务、响应不含队列地址;队列名按 D+task ID 推导且只能被动消费 SaaS 预建队列。空变更保持 `next_cursor`,过期游标 HTTP 410 重拉全量,`removed` 退役,超额拒绝且不推进游标;发现故障关闭新准入/停任务消费但保留 MQ 控制。旧 F03 v0.1 证据只作历史。 | 证据:[`f03-local-task-discovery-v0.2.md`](evidence/f03-local-task-discovery-v0.2.md)。新版 Schema/正反例/hash 与本地 Mock、race、vet、构建和无 `configure` 的 RabbitMQ 测试通过;F09 本地全矩阵/覆盖率见下行,真实 SaaS/management/拨号/生产未验证。 |
|
||||
| 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.2 的 ≤256 单次完整返回、空变更/removed/410 恢复及计划 §5 单节点/单租户适用矩阵均已重新进行本地 Mock 验证;stop 前两条已发起执行的 Agent 归属与一次性拨号决定现由同一事务持久化,stop 可对两条发 hangup 且旧执行仍返回结果。证据:[`f09-local-acceptance-v0.2.md`](evidence/f09-local-acceptance-v0.2.md)。 | `scripts/acceptance-local.sh`(格式/合同/Proto/hash、race、vet、构建及受限 RabbitMQ)通过;本轮 P1 业务模块均 ≥65%,MQ 仅带 integration tag 时为 66.4%。CLI 29.6% 和历史 callruntime 61.7% 未达线,按用户确认口径单列技术债,**不声称全仓达标**。队列满真实 RabbitMQ Mock 拒绝;SQLite 满盘仅作写入错误注入,未物理填盘。外部/真实验收未完成。 |
|
||||
| F06 | 本地发布准备与切换阻断已核对:Go 1.27.1 一次性制品/校验和、v0.1 其他协议+v0.2 任务发现+项目 v3 MQ 队列及 Proto manifest 的版本/hash 写入制品清单;已有 release/package 输出及归档拒绝覆盖,mixed/real V3 执行在触碰 DB/MQ 前 fail-closed,旧消费 CLI 和旧分散事件不能并行写入新路径。证据:[`f06-local-release-gates-v0.2.md`](evidence/f06-local-release-gates-v0.2.md)。 | `make release-check-local` 与本地 acceptance 通过;来源 dirty,制品仅 local-development、`production_approval=false`。未获真实部署/拨号/切换授权,旧 Python 资源移交、ECS/systemd/Asterisk 实际加载和生产签收均**未执行**;本地阻断不代替真实切换。 |
|
||||
| F09 | v0.3 首次多页、空页、同任务重现及墓碑、翻页中更新、断页重启、坏游标/410 失败、SQLite 写失败同事务回滚、MQ 控制竞态、重复执行不重拨完成单节点/单租户本地 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/第二租户不在本轮运行范围。 | 另获阶段与额度/资源授权。 |
|
||||
|
||||
## 9. 协作与版本记录
|
||||
|
||||
@@ -30,4 +30,4 @@ go build -trimpath -buildvcs=false -o "$tmp/sip-go-agent" ./cmd/sip-go-agent
|
||||
|
||||
./scripts/mq-integration-local.sh
|
||||
|
||||
echo "P1 local F09 checks passed (business-module coverage only; see docs/evidence/f09-local-acceptance-v0.2.md); external SaaS/management, cloud, supplier, SIP, F06 deployment, and production acceptance remain unverified"
|
||||
echo "P1 local F09 v0.3 checks passed (business-module coverage only; see docs/evidence/f09-local-acceptance-v0.3.md); external SaaS/management, tombstone capacity, cloud, supplier, SIP, deployment, and production acceptance remain unverified"
|
||||
|
||||
@@ -56,10 +56,10 @@ def project_sha256(path):
|
||||
return hashlib.sha256((project / path).read_bytes()).hexdigest()
|
||||
|
||||
v01 = json.loads((project / "docs/contracts/local-contract-manifest-v0.1.json").read_text())
|
||||
v02 = json.loads((project / "docs/contracts/local-contract-manifest-v0.2.json").read_text())
|
||||
v03 = json.loads((project / "docs/contracts/local-contract-manifest-v0.3.json").read_text())
|
||||
topology = json.loads((project / "docs/contracts/mq-topology-v0.1-proposal.json").read_text())
|
||||
if (v01["manifest_version"], v02["manifest_version"], topology["contract_version"]) != (
|
||||
"local-contract-manifest.v0.1", "local-contract-manifest.v0.2", "project-saas-dispatcher.v0.1"
|
||||
if (v01["manifest_version"], v03["manifest_version"], topology["contract_version"]) != (
|
||||
"local-contract-manifest.v0.1", "local-contract-manifest.v0.3", "project-saas-dispatcher.v0.1"
|
||||
):
|
||||
raise SystemExit("unexpected project-local contract or queue topology version")
|
||||
|
||||
@@ -81,8 +81,8 @@ manifest = {
|
||||
"manifest_sha256": project_sha256("docs/contracts/local-contract-manifest-v0.1.json"),
|
||||
},
|
||||
"task_discovery": {
|
||||
"version": v02["manifest_version"],
|
||||
"manifest_sha256": project_sha256("docs/contracts/local-contract-manifest-v0.2.json"),
|
||||
"version": v03["manifest_version"],
|
||||
"manifest_sha256": project_sha256("docs/contracts/local-contract-manifest-v0.3.json"),
|
||||
},
|
||||
"mq_topology": {
|
||||
"version": topology["contract_version"],
|
||||
|
||||
@@ -61,13 +61,13 @@ assert manifest["binary"]["sha256"] == hashlib.sha256((release / "sip-go-agent")
|
||||
assert manifest["go_version"].startswith("go version go1.27.1 ")
|
||||
|
||||
v01 = json.loads((root / "docs/contracts/local-contract-manifest-v0.1.json").read_text())
|
||||
v02 = json.loads((root / "docs/contracts/local-contract-manifest-v0.2.json").read_text())
|
||||
v03 = json.loads((root / "docs/contracts/local-contract-manifest-v0.3.json").read_text())
|
||||
topology = json.loads((root / "docs/contracts/mq-topology-v0.1-proposal.json").read_text())
|
||||
assert v01["manifest_version"] == "local-contract-manifest.v0.1"
|
||||
assert v02["manifest_version"] == "local-contract-manifest.v0.2"
|
||||
assert v02["source"]["path"] == "docs/thirds/v0.2.md"
|
||||
assert "docs/contracts/task-discovery-v0.2-proposal.schema.json" in {item["path"] for item in v02["artifacts"]}
|
||||
assert all("task-discovery-v0.1" not in item["path"] for item in v02["artifacts"])
|
||||
assert v03["manifest_version"] == "local-contract-manifest.v0.3"
|
||||
assert v03["source"]["path"] == "docs/thirds/v0.3.md"
|
||||
assert "docs/contracts/task-discovery-v0.3-proposal.schema.json" in {item["path"] for item in v03["artifacts"]}
|
||||
assert all("task-discovery-v0.2" not in item["path"] for item in v03["artifacts"])
|
||||
assert topology["contract_version"] == "project-saas-dispatcher.v0.1"
|
||||
assert all(topology["queues"][kind]["owner"] == "saas" for kind in ("task", "control", "result"))
|
||||
assert all(topology["queues"][kind]["queue_name"].endswith(".v3") for kind in ("task", "control", "result"))
|
||||
@@ -77,8 +77,8 @@ expected_attestation = {
|
||||
"manifest_sha256": hashlib.sha256((root / "docs/contracts/local-contract-manifest-v0.1.json").read_bytes()).hexdigest(),
|
||||
},
|
||||
"task_discovery": {
|
||||
"version": v02["manifest_version"],
|
||||
"manifest_sha256": hashlib.sha256((root / "docs/contracts/local-contract-manifest-v0.2.json").read_bytes()).hexdigest(),
|
||||
"version": v03["manifest_version"],
|
||||
"manifest_sha256": hashlib.sha256((root / "docs/contracts/local-contract-manifest-v0.3.json").read_bytes()).hexdigest(),
|
||||
},
|
||||
"mq_topology": {
|
||||
"version": topology["contract_version"],
|
||||
@@ -88,7 +88,7 @@ expected_attestation = {
|
||||
}
|
||||
assert manifest["contract_attestation"] == expected_attestation
|
||||
print("local artifact SHA-256:", manifest["binary"]["sha256"])
|
||||
print("task discovery source SHA-256:", v02["source"]["sha256"])
|
||||
print("task discovery source SHA-256:", v03["source"]["sha256"])
|
||||
print("source dirty:", manifest["source_dirty"])
|
||||
print("production approved:", manifest["security"]["production_approval"])
|
||||
PY
|
||||
@@ -97,6 +97,20 @@ if "$WORK/release/sip-go-agent" dispatcher --help | grep -Eq -- '--tenant-key|--
|
||||
echo 'release exposes deprecated Dispatcher queue/tenant switches' >&2
|
||||
exit 1
|
||||
fi
|
||||
for mode in mixed real; do
|
||||
blocked_db="$WORK/blocked-$mode.sqlite"
|
||||
if SIP_GO_AGENT_MODE="$mode" DISPATCHER_DB="$blocked_db" RABBITMQ_URL='amqp://127.0.0.1:1/' \
|
||||
DISPATCHER_GRPC_LISTEN='' DISPATCHER_AGENT_ENDPOINTS_FILE='' \
|
||||
GO_SIP_OSS_ACCESS_KEY_ID='local-test-placeholder' GO_SIP_OSS_ACCESS_KEY_SECRET='local-test-placeholder' \
|
||||
"$WORK/release/sip-go-agent" dispatcher --config "$ROOT/deploys/config/dispatcher.json.example" > "$WORK/blocked-$mode.log" 2>&1; then
|
||||
echo "release accepted unapproved $mode execution" >&2
|
||||
exit 1
|
||||
fi
|
||||
if ! grep -q 'isolated Mock only' "$WORK/blocked-$mode.log" || test -e "$blocked_db"; then
|
||||
echo "release reached resources before rejecting $mode execution" >&2
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
rm -- "$PACKAGE_RELEASE/sentinel"
|
||||
rmdir -- "$PACKAGE_RELEASE"
|
||||
"$ROOT/deploys/build-package.sh" "$PACKAGE_VERSION" > "$WORK/package.log" 2>&1 || {
|
||||
@@ -117,6 +131,6 @@ manifest = json.loads((package / "manifest.json").read_text())
|
||||
assert manifest["version"] == version
|
||||
assert manifest["scope"] == "local-development"
|
||||
assert manifest["security"]["production_approval"] is False
|
||||
assert manifest["contract_attestation"]["task_discovery"]["version"] == "local-contract-manifest.v0.2"
|
||||
assert manifest["contract_attestation"]["task_discovery"]["version"] == "local-contract-manifest.v0.3"
|
||||
PY
|
||||
echo 'F06 local release/package artifact and contract/queue gates passed; no deployment or dial attempted'
|
||||
|
||||
Reference in New Issue
Block a user