20 KiB
P08 项目内验收对照
本表保留 P08 当时的本地测试结果、来源路径与哈希;后续整理未改写验收事实。原计划现位于
docs/archive/sources/plan-saas-dispatcher-v05-v0.1.md,完成阶段计划位于docs/archive/plan-saas-dispatcher-completed.md;当前唯一规范见../thirds/saas-dispatcher.md。
**当前补充(不改写 P08 历史结果):**现行运行期间不再定时轮询任务列表,删除了 DiscoveryFollower 及其两个测试;新建任务的 start 和暂停后修改的 resume 均重读任务配置,但不重新拉取/核验 SIP;SIP 仅在启动及通知待处理时独立全量读取,Agent 加载核验和本地任务绑定不依赖重读任务列表;全局 AI 服务商列表在 Dispatcher 每次启动时读取一次,start/resume 仅使用该次快照,服务商变更须重启。TestStartReadsNewTaskWithoutAgentControl、TestResumeRefreshesModifiedTaskRevision、TestStartDoesNotReopenPausedOrStoppedTasks 与隔离 RabbitMQ 的 TestRuntimeIsolatedControlBacklogExecuteAndSharedResult 已通过;本次 PATH=/tmp/sip-go-agent-tools/bin:$PATH make check(含 Proto 重新生成、隔离 RabbitMQ 测试)、go test -race ./...、go vet ./... 和构建通过,业务覆盖率 72.0%;此前含隔离 RabbitMQ 的 Dispatcher 测试覆盖率 75.0%(此轮未重算单包覆盖率)。
本记录按 docs/plan-saas-dispatcher-v05-v0.1.md §3.1、§6 核对 P01–P08、K01–K16 和 A01–A12。范围仅限单节点、单 Dispatcher、单 Agent、单 Cell、单租户的隔离 Mock。 本机证书、RabbitMQ 容器和 OSS/AI 模拟服务不代表 SaaS 应用收讫、供应商验收或真实拨号。原计划是当时的来源事实,不是第二份运行合同;当时的状态入口 docs/plan-saas-dispatcher.md 已归档。
可复现的本地门禁与来源
| 命令或来源 | 本地结果和边界 |
|---|---|
PATH=/tmp/sip-go-agent-tools/bin:$PATH make check |
通过:格式、当前合同正反例、Proto 重新生成及来源/hash、go vet ./...、go test -race ./...、构建、本地验收;三项必需隔离 RabbitMQ 测试均实际 PASS,不是零项筛选。 |
bash scripts/check-current-mq-mock.sh |
TestBrokerSharedResultQueueAndNoConfigure、TestRuntimeIsolatedControlBacklogExecuteAndSharedResult、TestDispatcherCommandStartsWithIsolatedMQHTTPAndAgent 三项实际 PASS;使用临时隔离 RabbitMQ,不连接真实 SaaS。 |
make coverage |
Go 1.27.1;手写业务代码语句覆盖率 72.0%,排除生成目录 gen/,未排除本轮修改的手写代码;超过 65% 门槛。 |
PATH=/tmp/sip-go-agent-tools/bin:$PATH make release-check-local |
通过本地制品/契约门禁;production_approval=false,未启动生产服务。 |
git diff --check |
通过;仅作差异格式检查,不代表真实通信通过。 |
docs/plan-saas-dispatcher-v05-v0.1.md |
固定来源 SHA-256 666f39e56ea9f4b55661efcac82edd6f9729848e2d60e5f24cdf5aa3ac97ee87;contracts/local/manifest.json 固定其来源、34 个合同 JSON 与对应哈希,清单自身 SHA-256 668aea5a742d2bf9bed4685c06f8dd95099d5d29a6bc2f32c2211bbc21197b40。 |
proto/manifest.json |
Proto 服务和生成物一致;错误说明更新后 proto/ERRORS.md 为 5108 字节、SHA-256 024f53a02c01151f7223ad49ad2d32c73d7701497dfd032bed0de820d72c7dc4,未更改 RPC 方法或 SaaS Schema。 |
上述门禁记录的是本地运行结果;新增的验收文件和门禁记录提交前再次执行,最终结果以本文件末尾复核为准。目标分支为
feat/saas-dispatcher-contract。已有 SQLite、Agent 恢复文件、spool 和 outbox 均未自动迁移或清理;两份使用者修改的docs/thirds/文档不属于本批改动。
P01–P08
| 阶段 | 本项目可核验证据 | 状态 |
|---|---|---|
| P01 契约 | contracts/local/ 当前 Schema、MQ 拓扑、正反例与 manifest.json;contracts/current_contract_test.go、contracts/current_topology_test.go。 |
本地通过;真实 SaaS 字段未签收。 |
| P02 单入口/Proto | cmd/sip-go-agent/main.go 唯一 Mock 入口;proto/agent/agent.proto 八个方法;scripts/check-proto.sh 重新生成和核验。 |
本地通过。 |
| P03 只读配置 | internal/configread/snapshots_test.go、discovery_test.go、internal/ai/binding_test.go:任务、发现、SIP、额度、供应商及失效边界。 |
本地通过;真实 HTTP 未联调。 |
| P04 MQ/SQLite | internal/mq/broker_integration_test.go、internal/dispatcher/runtime_integration_test.go、internal/store/result_test.go:队列归属、实际路由、持久状态与唯一结果。 |
本地通过;实际 SaaS 应用收讫未验证。 |
| P05 AI/媒体 | internal/ai/binding_test.go、keyword_hangup_test.go、internal/rpc/approved_full_ai_integration_test.go:ASR-only/full-AI 与失败关闭。 |
本地通过;真实 AI/SIP/RTP 未验。 |
| P06 录音/结果 | internal/agent/recording_delivery_test.go、recording_retry_test.go、internal/rpc/recording_transport_test.go,见下方五条路径。 |
本地通过;真实 OSS 未验。 |
| P07 唯一当前入口 | docs/evidence/saas-dispatcher-p07-audit.md 的旧→新映射、历史例外与复扫;旧路径已删或按原字节归档。 |
本地通过;不处置现存数据。 |
| P08 对照验收 | 本文件 A01–A12、K01–K16 与故障/重启对照;53 个命名测试引用和 21 个源文件引用均存在;本地构建、覆盖率和诊断门禁的隔离负例。 | 本地证据通过;真实现场门禁仍待授权核验。 |
A01–A12 逐项对照
| 检查 | 本地依据及结论 | 不能据此宣称的事项 |
|---|---|---|
| A01 契约一致性 | contracts/local/config-read.schema.json、task-discovery.schema.json、mq.schema.json、mq-topology.json 及 34 个示例/Schema 文件受清单约束;TestCurrentContractExamples 对合法/非法实例实际执行,TestCurrentContractTopology 固定通信形态。五类 HTTP、命令/回执、共享最终结果和 provider/agent 字段仅按本项目已确认版本核验。 |
真实 SaaS 的响应字段或审批来源已兼容。 |
| A02 无代次名称 | docs/evidence/saas-dispatcher-p07-audit.md 逐项记录旧→新、已删除/归档和合法固定 v1、业务 revision、历史事实;本轮新增 internal/session/ 不引入自有运行代次。本轮复扫 cmd internal gen proto contracts/local scripts deploys 的活动路径,意外 _v2/_v3、v0.x 路径 0 项;扫描 cmd internal scripts deploys 的废弃入口符号 0 项,保留 P07 已列的固定协议与历史例外。 |
把历史文档里的代次事实误当成现行运行入口。 |
| A03 固定通信名称 | TestCurrentContractTopology、TestBrokerSharedResultQueueAndNoConfigure 和根命令隔离集成测试核对实际 exchange/queue/routing key、共享结果接收及 /internal/v1/dispatcher/...;没有旧拓扑回退。 |
不同真实 SaaS 部署的路由已联调。 |
| A04 引用及生成一致性 | make check 执行当前合同及 Proto 清单/来源 SHA-256、重新生成、构建;P07 审计核对当前/历史引用和归档。固定历史来源哈希与本地清单各自独立。 |
从源代码仓库测试推断真实 management 已发布相同配置。 |
| A05 配置读取与失效 | TestCurrentContractExamples 覆盖字符串 tenant_id、旧结构字段和新增未知字段反例;TestReadTaskDoesNotReuseConfigAfterHTTPFailure、TestReadTaskRejectsMismatchedOwnerAndMalformedSIP、TestBindCurrentFullAIUsesApprovedSDKFields、TestBindCurrentRejectsUnauthorizedProviderBeforeCall 等核对 0/false/null、授权绑定、无缓存回退和虚构 credential 的 SDK 参数。 |
真实凭据或供应商 SDK 已端到端联调。 |
| A06 队列所有权与路由 | 上述三项隔离 MQ 测试实际 PASS;无 configure 权限、被动检查、D1/D2 路由和共享 SaaS 队列实际收件。TestBrokerSharedResultQueueAndNoConfigure 覆盖缺/错绑定、mandatory return、关闭连接拒发后显式重开并收取原待发消息;TestExecuteTimeoutAndMQFailureDoNotRedial 注入发布确认丢失,保持原状态、不重拨。 |
不能把 confirm 当 SaaS 处理回执,不能把显式重开写成自动热重连;没有注入真实 RabbitMQ 确认帧丢失。D1/D2 仅夹具。 |
| A07 控制与发现竞态 | TestReadAllTasksContinuesAfterShortPageUntilEmptyPage、TestDiscoveryFollowerFailedPageKeepsCursorAndClosesAdmission、TestControlPauseDefaultHangupAckAfterAgentDispatch、TestControlStopCannotResumeOrDispatchPending、TestSIPNotificationPersistsBarrierAndWaitsForLoadedFullSnapshot:游标、持久屏障、旧 running 及停用不可恢复。 |
Agent/Asterisk 实际加载的 revision 已在真实环境核验。 |
| A08 执行与 AI 行为 | TestExecuteRejectsInvalidCalleeWithoutStoppingTask、TestExecuteWaitsForRulesThenDispatchesOriginalIdentity、TestExecuteTimeoutAndMQFailureDoNotRedial、TestSelectTrunkWhitelistScheduleAndCaller、TestBindCurrentASROnlyDoesNotBindOtherProviders、TestKeywordHangupOnlyFinalUserLiteralAndOnlyOnce;重复与未知执行不重拨,只有用户最终 ASR 命中关键词。 |
Mock 时段/门禁不授权任何真实号码拨号;真实 SIP/AI 仍未验证。 |
| A09 结果与录音 | 下方五路径和额外的恢复/窗口测试;TestFinalResultNeedsConfirmedEndAndRemainsExactlyOne、TestFinalResultPayloadNoAnswerHasNoFabricatedTranscriptOrAsset。48 小时到期保留待人工、不发假结果。 |
模拟 HTTPS 对象接收不等于供应商 OSS 成功。 |
| A10 数据保护与恢复 | TestRecordingRecoveryDiscoversAndResumesAfterProcessRestart、TestRecordingRecoveryUnknownPUTNeverRetriesAfterRestart、TestFinalResultOutboxFailureRollsBackAndCanResume、TestFinalResultRestartResumesSameOutboxBody;会话代际竞态测试见下表。旧 SQLite 与旧 Agent 状态只读失败关闭,未自行清理。 |
未保存恢复双文件时进程崩溃不保证零丢失;真实主机重启未验。 |
| A11 测试与构建 | TDD:新增会话/录音测试先红后绿;上述 make check、72.0% 覆盖率及本地发布检查通过。TestNonprodCallEvidenceFailsBeforeDialWithoutRequiredGates、TestNonprodPreflightRejectsIncompleteCapturedEvidence 以临时假工具验证缺资源、缺诊断/抓包事实即失败关闭。 |
假工具不能替代实际 ECS、Asterisk/systemd、ARI/PJSIP、拨号前 SIP/RTP 抓包及收尾哈希。未经另行授权不执行这些现场动作。 |
| A12 交付边界 | 验收时的状态入口为 docs/plan-saas-dispatcher.md,业务规范为 docs/thirds/saas-dispatcher.md;前者现已归档,本表仍分开本地证据和外部缺口。production_approval=false;已有业务数据未自动处置。 |
不宣称第二 Dispatcher、第二 Cell/租户、多机、容量、真实线路或生产通过。 |
A09 五条录音路径及错误边界
| 路径 | 隔离测试与可观察结果 |
|---|---|
| 录音生成并上传成功 | TestRecordingDeliveryDirectPUTThenOneResultWithoutBusinessFiles:一次模拟 HTTPS PUT,仅录音字节;不先写再删业务录音/结果文件;确认通话结束和上传后才给唯一结果。 |
| OSS 明确失败 | TestRecordingDeliveryFailedPUTPersistsOriginalAndNeverRetriesImplicitly、TestRecordingDeliveryRestartRetriesOriginalAndReportsConfirmedPUTOnly:原录音/结果双文件、原对象与消息身份保留,重启后显式恢复,不自动重传不确定 PUT。 |
| 无录音/无应答 | TestRecordingDeliveryNoRecordingReportsOnlyAfterConfirmedEnd、TestFinalResultPayloadNoAnswerHasNoFabricatedTranscriptOrAsset:确认结束后直接回报,空录音,不编造转写/OSS 路径。 |
| 预期录音生成失败 | TestRecordingDeliveryCaptureFailureDescribesFailureWithoutInventingUpload、TestApprovedRecordedMockCallGenerationFailureReportsEmptyRecording:真实通话结果、空录音并说明失败;不当作 OSS 失败重试。 |
| 失败且无法保存双文件 | TestRecordingDeliveryRecoveryDiskFailureNeverReportsUploaded:明确报错,无完整副本不发最终结果,不伪称已开始可靠重试。 |
补充:TestRecordingRecoveryBackoffAndResultRetryDoNotRepeatSuccessfulPUT 核对 1/2/4/8/16/32/60 分钟及上限;TestRecordingRecoveryExpiresAfterExactly48HoursWithoutResult 以双文件保存后时刻为起点,到期留原文件待人工;TestRecordingDeliveryConfirmedPUTReportRetryDoesNotRepeatPUT 确认 MQ 失败不触发第二次成功 PUT。未知 PUT 由 TestRecordingDeliveryUnknownPUTKeepsManualBarrierAcrossRestart 和 TestRecordingRecoveryPersistsInFlightBeforeUnknownPUT 禁止盲重试。
K01–K16 对照
| 决定 | 对应实现/测试及本地结论 |
|---|---|
| K01 | contracts/local/mq-topology.json、TestCurrentContractTopology、TestBrokerSharedResultQueueAndNoConfigure:任务/控制队列 SaaS 预建,D 无 configure,共享结果队列实际接收。 |
| K02 | TestCurrentContractExamples:tenant_id 数字,字符串及旧 schema_version 反例拒绝;内部事件/幂等身份保留。 |
| K03 | TestReadAllTasksContinuesAfterShortPageUntilEmptyPage、TestDiscoveryFollowerPersistsPageBeforeAdvancingToTerminalEmpty:非空短页继续、含游标空页结束,逐页持久化后前进;出错关准入。 |
| K04 | TestControlStopCannotResumeOrDispatchPending、TestStoppedTaskSilentlyAcknowledgesOldExecuteWithoutRecreatingInbox:同 task_id 的 stop 不可恢复;pause/resume 另有新鲜核验,不做任务删除或跨 D 迁移。 |
| K05 | TestControlPauseDefaultHangupAckAfterAgentDispatch、TestControlAgentFailureKeepsBarrierWithoutSuccessAck:缺省挂断,成功回执仅表示已交 Agent;未调度不回成功。 |
| K06 | TestExecuteWaitsForRulesThenDispatchesOriginalIdentity、TestFinalResultNeedsConfirmedEndAndRemainsExactlyOne:dispatched 与已接通分离;外部 task_id/callee 关联,内部仍保存每次事实身份。 |
| K07 | TestSIPNotificationPersistsBarrierAndWaitsForLoadedFullSnapshot、TestReadSIPWithoutTasks:全量 SIP revision 与通知对应,变更期间关准入;实际 Asterisk 加载仍待现场证明。 |
| K08 | TestBindCurrentFullAIUsesApprovedSDKFields、TestBindCurrentRejectsUnauthorizedProviderBeforeCall:虚构 credential 作为获批 provider 参数传入,不发明 ref,不记录真实密钥。 |
| K09 | TestKeywordHangupOnlyFinalUserLiteralAndOnlyOnce、TestKeywordHangupConcurrentDuplicateFinalTriggersOnce:仅用户侧最终 ASR 文本的字面关键词生效,重复通知一次。 |
| K10 | A09 五路径及 TestRecordingRecoveryBackoffAndResultRetryDoNotRepeatSuccessfulPUT、TestRecordingRecoveryExpiresAfterExactly48HoursWithoutResult:明确失败才持久化双文件,48 小时原起点/间隔/待人工边界。 |
| K11 | TestFinalResultPayloadRejectsMissingOrInventedCallFacts、TestFinalResultPayloadNoAnswerHasNoFabricatedTranscriptOrAsset:无真实 SIP 码保持 null 和文字原因,不伪造本地数字码。 |
| K12 | TestExecuteWaitsForRulesThenDispatchesOriginalIdentity、TestSelectTrunkWhitelistScheduleAndCaller:规则不满足等待并重新校验,不自动取消人工 pause/stop,不转为伪失败。 |
| K13 | TestExecuteRejectsInvalidCalleeWithoutStoppingTask:单号不合规只拒绝该执行,不出 dispatched/最终结果,不暂停整个任务。 |
| K14 | TestFinalResultPayloadNoAnswerHasNoFabricatedTranscriptOrAsset、TestFinalResultNeedsConfirmedEndAndRemainsExactlyOne:已确认 answered/no_answer/failed 与未知占用分离,不按 OSS 失败改写通话事实。 |
| K15 | TestRecordingDeliveryCaptureFailureDescribesFailureWithoutInventingUpload:录音生成失败仍给真实通话结果、recording={}、文字说明。 |
| K16 | TestRecordingDeliveryRecoveryDiskFailureNeverReportsUploaded:双文件保存失败不发结果、不编造可恢复状态。 |
故障、重启与会话竞态
| 注入/重启边界 | 观测证据 | 剩余限制 |
|---|---|---|
| HTTP 页/配置网络失败或过期 | TestReadTaskDoesNotReuseConfigAfterHTTPFailure、TestReadTasksFailsOnUnavailableRefreshWithoutStaleFallback、TestDiscoveryFollowerFailedPageKeepsCursorAndClosesAdmission。 |
本地假 HTTP 服务。 |
| MQ 无绑定、不可路由、关闭后重开、确认结果不明 | TestBrokerSharedResultQueueAndNoConfigure 实际隔离收件;TestExecuteTimeoutAndMQFailureDoNotRedial 的发布确认丢失注入;TestFinalResultRestartResumesSameOutboxBody 保留原 outbox 消息身份。 |
未注入真实 SaaS/RabbitMQ 生产网络的确认帧丢失;confirm 不是应用收讫。 |
| Agent 执行/会话代际切换 | TestInboundAgentFactRequiresCurrentUnexpiredActivatedSession、TestRecordingServerClassifiesOnlyVerifiedSessionCutover、TestRecordingFactsCrossRealMutualTLSUnaryTransportWithoutExternalOSS、TestRecordingClientDoesNotRetryUnverifiedOrUnknownOutcomes、TestRecordingClientStopsWhenSecondOutcomeIsUnknown、TestRecordingClientSessionCutoverReReportIsBounded。只在同 Agent/Cell/boot/epoch、相邻有效代际且明确拒绝时,在六秒或调用方更早期限内重报原三类事实;未知结果立即停止。 |
窗口耗尽明确失败,不称已交付;换 boot/身份或真正过期不能凭此自动恢复。未做真实多主机重启。 |
| OSS 明确失败、未知 PUT、写盘错误、48 小时耗尽 | A09 测试分别验证原对象/进度、重启、未知不重传、失败不伪造结果和待人工。 | 成功上传不写业务恢复文件;在未完成失败落盘前崩溃,不保证恢复。 |
| 非生产诊断/现场证据缺失 | TestNonprodCallEvidenceFailsBeforeDialWithoutRequiredGates、TestNonprodPreflightRejectsIncompleteCapturedEvidence 假工具负例在缺时段、systemd、ARI/PJSIP、安装包哈希或抓包哈希时阻止验收。 |
未运行真实主机 capture-first 诊断、SIP/RTP/录音/ASR 现场采集。 |
**待真实环境完成,不构成本地失败的伪证:**真实 SaaS/management 配置签收与审批版本同源;真实 RabbitMQ ACL/TLS、队列所有权、应用收讫和结果消费;Agent/Asterisk 实际加载、ECS/systemd/ARI/PJSIP 状态、媒体/抓包;真实 OSS/AI/SIP 供应商;多节点/容量和生产切换。没有云资源操作、真实号码拨打或现存数据清理的授权与执行。任何缺少现场抓包、状态快照或权限的非生产验证均须失败关闭,不能把隔离 Mock、制品 hash 或三项本地 MQ PASS 写作真实验收。
最终复核
PATH=/tmp/sip-go-agent-tools/bin:$PATH make check:PASS,包含重新生成/核验 Proto、当前合同正反例和来源 hash、格式、go vet ./...、go test -race ./...、构建及三项实际 PASS 的隔离 MQ 用例;新增显式重连及全部会话/录音测试已计入。make coverage:PASS;Go 1.27.1,排除生成文件后的手写代码语句覆盖率 72.0%。PATH=/tmp/sip-go-agent-tools/bin:$PATH make release-check-local:PASS;本地制品 SHA-25600b283d422a27766f1d4763ee193d705857274a5ec9f3c4dba699594536ff30a;清单报告production_approval=false、source_dirty=true。这不是生产包签收,也未执行部署或拨号。- 活动路径/退役符号复扫均为 0;53 个测试符号、21 个源文件引用及当前/索引/证据文档 54 条本地链接均存在。来源计划、当前合同与 Proto 错误说明的哈希见首表;两份使用者修改的第三方文档保持原状且未被纳入本轮变更。