# 当前实现 本文描述 0.12.3 的当前实现,包含实际步骤与手工用例、工作区文件解析、释放准入、账本报告优化及文字规划收敛。测试增强附着在发起命令的当前 Agent 上;原生请求、工具执行、审批、消息显示、工作区会话保存均由 DSH 负责。历史独立 Agent 方案已被替代,见[改造决策](../design/原生对话增强改造.md)。 从开始运行一条用例到结束的参与者职责、模型循环、MCP/HTTP 调用、计划审核、可信采集和停止时序,见[单条用例执行与参与者交互](单条用例执行与参与者交互.md)。 当前宿主 SDK 为 DSH 0.2.1-alpha.3。原生计划审核关闭时,`exit_plan_mode` 可以正常返回 `approved: false` 并结束轮次;插件保留草案、等待用户反馈,不催促模型重开审核。只有当前草案明确返回 `approved: true` 才冻结执行。升级核对与验证范围见[宿主升级适配验收](../project/DSH宿主升级适配验收.md)。 | 模块 | 职责 | | --- | --- | | index.ts | 注册命令、限制输入路径、注册认证报告路由 | | case-input.ts | UTF-8文件读取、根列表/文本行识别、CSV解析、单次替换与输入快照 | | language.ts | 读取宿主语言偏好,约束原生对话的用户可见说明语言 | | text-plan.ts | 自然语言短句计划、文字检查点和纯文字审核呈现 | | native-test.ts | 当前 Agent 的测试作用域、指导、工具、原生事件观察、停止与清理 | | recovery.ts / client/recovery-panel.tsx | 在测试插件设置页显示隔离原因与超时提醒;用户确认后经原生浏览器关闭和预览收尾释放,使用记录摘要校验确认是否过期并保存处置审计 | | runbridge.ts / runbridge-preview.ts | 当前 Agent 的远程 MCP 工具绑定、会话恢复和持续画面缓存;详见[远程架构](RunBridge远程执行与画面.md) | | config.ts | 测试阶段的时间和修订预算 | | contracts.ts / revisions.ts | 冻结计划、身份校验、追加检查点、实例与结果聚合 | | adapters.ts / assertions.ts | 可信工具响应采集、固定比较器、操作数证据绑定 | | actual-operations.ts / manual-case.ts / manual-source.ts | 脱敏运行记录 JSON、手工专用原始资料及简洁 Markdown 执行模板 | | recorder.ts / ledger-rebuild.ts | 插件自身的脱敏事件、两份已提交检查点、证据及新旧账本流式只读重建 | | recording.ts / mp4.ts / report-media.ts | 录像片段状态、CLI 摘要解析、MP4 检查与归档、按实例生成媒体入口 | | report.ts / report-data.ts / report-access.ts | 静态 HTML、单份下载与调用数据索引、认证访问和流式附件下载 | | test-ui-access.ts / client/report-controls.tsx | 界面报告重建、原生恢复和证据提交,复用宿主认证与已有业务服务 | | progress-model.ts / progress-access.ts | 按会话隔离的脱敏展示投影、只读进度及图片路由 | | preview.ts / preview-page.ts | CDP 与首帧门禁、Browscreen 旁路生命周期和同源只读 iframe | | browscreen-command.ts / preview-settings-access.ts | 本机命令解析、有限版本查询、设置页主动检测与认证路由接入 | | preview-setup.ts / preview-preferences.ts | 下一次运行前读取偏好,仅拦截选中 MCP 的运行配置,退出时恢复原始参数 | | client/preview-settings.tsx | 原生 settings.section 表单,复用 configForms 保存和冲突校验 | | cdp-publisher.ts | MCP 公开页面初始化扩展,交付当前运行、实例和页面的 CDP | | client/ | 官方 UI 插槽中的步骤图、实际耗时、详情页和条件画面浮窗 | ## 正常执行 1. 命令调用当前 `Agent.followup()` 提交真实用户输入。命令只确认接收,输入框随即回到原生运行状态;本轮执行仍占用同一个对话。不会建立后台任务或额外 Agent。 2. 在 `agent.ctx` 注册 `systemPrompt.section()` 与测试工具。模型仅根据用户输入分析、规划、追问;工具守卫禁止规划阶段浏览器、API和命令执行;用户补充信息仍进入同一对话。自然语言提交包含name、简短rationale、steps[].description及可选checks[],插件生成身份和文字规则来源;冻结的是业务短句,而不是预先编译的动作。 3. 模型按正常循环观察页面,业务交互经 `test_execute_operation` 携带简短说明,再转交原 Playwright 工具。`test_capture` 通过同一 Agent 的原生工具管线调用可信采集器,保留父调用、取消信号和原有审批策略。程序计算断言,不接受模型填写实际值。 4. `session/event` 只读观察调用身份;`tools/result` 记录最终工具结果。所有插件账本写入项目输出目录,插件不追加自定义 DSH 事件、不改写宿主日志或浏览器本地状态。 5. `test_finish` 返回权威状态、逐步结果、断言、路径和报告链接,模型在原生最终回复中总结。轮次结束后移除测试提示、工具和监听器,后续普通聊天沿用原会话。 客户端通过 `conversation.input.dock` 显示当前步骤焦点与按需展开的完整业务列表,每秒读取 `/test-progress/` 脱敏快照,本地平滑更新包含等待的墙钟耗时;准备与清理不计入业务步骤数。详情使用 `sidebar.right.pane.tab`,画面使用宿主原生 `sidebarRight.float`。读取进度、查看详情及切换画面不派发测试动作;报告重建显式读取账本并写入独立文件。工具轨迹仍按宿主默认方式显示。 文件命令 `/test-run`、`/test-plan --file` 与 `/test-data` 从发起 Agent 的公开 `session.header.cwd` 读取当前对话工作区,相对路径及绝对路径都按这个目录校验。仅无 cwd 的历史会话回退到插件配置 workspace;不会修改全局配置或读取其他已添加工作区的同名文件。realpath 与相对边界检查继续拒绝上级路径和符号链接逃逸。设置页高级处置证据保留配置 workspace 边界;outputRoot 仍由插件配置决定。 客户端监听宿主公开 `command/executed` 本地回执,只接受本插件的四个命令(`test`、`test-plan`、`test-run`、`test-data`)。命令失败时,将错误提示保存在发起会话的客户端作用域;未发起测试命令的会话不显示提示,其他命令不触发提示。提示可关闭,测试命令成功或设置释放成功后清除;刷新浏览器后不从全局隔离记录重建常驻提示。普通对话不查询 `/test-recovery`,只有进入“设置 → 测试插件”才挂载隔离状态面板并查询。设置页通过公开 `uiSession.adapter.current` 取得所选对话(设置覆盖对话时仍可用)。`POST /test-ui/recover` 经宿主认证后查找该会话已有 Agent,用 `agents.withInitiator` 沿用原生工具审批与取消;没有已加载对话时提示先打开。`POST /test-ui/release` 接收工作区内的处置证据路径和当前隔离摘要,保留证据合同、互斥与过期确认拒绝。`POST /test-ui/report` 根据会话或指定运行 ID 重建报告,不调用模型或业务工具,不覆盖原始文件;中断记录仍能重建,活动运行拒绝重建。卸载或请求断开时取消在途恢复,迟到成功不会解除隔离。 可选预览配置下,仅实际持有浏览器上下文的运行发布 `owner.json`;MCP 的 CommonJS 页面初始化扩展利用公开 CDP 命令交付页面级 `.cdp` 与 `cdp.json`。预览管理器核对运行/实例/target、CDP 可达及唯一页面,然后从宿主 PATH 或完整可执行文件路径查询 `browscreen version`,确认稳定版本 `>=0.3.0,<0.4.0` 后直接 spawn 采集命令。无需源码项目或运行时 uv;有效新 PNG 到达后才返回 ready 和同源 iframe 地址。纯 API、无 CDP、不可达或无首帧均不会打开画面浮窗;本轮不增加 App 执行适配器。 关闭浮窗不停止业务;同一运行自动打开一次。客户端从公开 `sidebarRight.openTabs` 按会话和预览类型查找窗口,复用已有窗口并清理重复项;显示/隐藏按钮通过原生布局 API 切换,隐藏后轮询不重新弹出。结束时撤销画面并释放自己启动的 Browscreen;画面异常不改变测试断言。多标签不自动选择首个目标,当前明确禁用预览;鼠标轨迹未接入。 `Config.browserPreview` 为整组 volatile 偏好,包含启用、命令、端口与 MCP;没有偏好时可使用新 CLI `preview` 运行配置,显式关闭优先。Settings 经 profile patch 持久化,配置更新只改变引用,不重载测试插件。在新测试的共享环境门禁之后、创建 NativeTest 之前读取偏好并准备 MCP;启动准备也受互斥保护。0.9.1 通过 config-editor 枚举现有 MCP,仅针对选中 Entry 注册 Cordis `internal/config` 拦截,使用该 MCP Fiber 的 `restart()` 应用运行配置;不调用持久编辑,不改变 Entry 的原始配置或 profile 文件。生成 JSON 保留原始浏览器配置、页面初始化扩展、其他 CLI 参数和环境变量;配置摘要作为文件名,初始化扩展按内容摘要复制到运行目录。内容变化才触发必要重连。切换 MCP、关闭后的下一次准备、停用和卸载移除拦截并恢复最新原始配置;整体停服时跳过重启正在退出的 MCP。 设置页 GET 元信息不执行命令;认证 POST 检测路由只查询版本,不保存配置或启动服务。版本查询 5 秒内结束,进程输出有界。启动至首帧共用 60 秒预算;失败原因只交付给当前拥有浏览器资源的测试,进度区提示设置路径,终止性失败会关闭已有浮窗。同一归属/端点的失败不会随轮询重新启动,切换归属会取消旧探测并回收自有进程。端口冲突不读取未知服务;纯预览回收仍先 SIGTERM,1.5 秒后使用 SIGKILL;录像使用当前步骤/清理的剩余期限等待收尾,超时封存失败再 SIGKILL。画面异常不改变业务断言。 未显式指定调试端口时加入 `--remote-debugging-port=0`,由当前浏览器分配。CDP 初始化扩展通过公开 Browser.getBrowserCommandLine 获取参数;值为 0 时,从参数指定的浏览器临时目录读取 DevToolsActivePort 的实际端口,再按 targetId 交付页面连接。不扫描系统浏览器或读取 Playwright 私有字段。 ## 浏览器录像与报告媒体 `browserPreview.recordingEnabled` 独立于 `enabled`,默认 false;任一开启才接入兼容 MCP。显式 `preview` 使用 `previewEnabled` 与 `recordingEnabled`。有当前实例浏览器资源和有效唯一页面 CDP 才构造 `RecordingSession`;API 不产生录像字段或片段。录像模式启动服务端采集定时器,不依赖 `/test-progress` 请求,隐藏浮窗或关闭客户端页面后继续。预览与录像共用一个 Browscreen 进程。每次进程启动生成固定的运行目录内 `evidence/browser-.mp4` 路径。 解析 0.3.0 CLI stderr 的 UTF-8 分片与完整/部分/无帧/失败摘要,只接受预先生成的输出路径。正常媒体结算等待进程 `close`,检查文件、MP4 视频轨道和时长,流式计算 SHA-256,登记 `run.recordings`、视频证据与 `recording_started`/`recording_finished` 事件。首帧时间来自经过验证的采集响应。实例清理后等待归档再进入下一实例;正常报告等待结算。紧急结束先封存片段 FAILED,迟到摘要不能覆盖已冻结结果。媒体失败不改业务断言,取消视频保留取消说明。 设置检测只检查命令版本。CLI 报缺少 PyAV/libx264 时给出 `browscreen[video]==0.3.0` 安装提示;识别到录制准备失败且预览开启时仅回退一次无录像参数的启动。只录像模式不发布浮窗 ready。API 报告无视频或截图对话框,混合批次按 `case_run_id` 生成媒体区域,切换到没有录像的实例后回退步骤页并暂停播放器。 视频独立保存,原始与重建 HTML 使用相对路径。HTTP 路由支持 MP4 流式读取、GET/HEAD、单段 Range 206 与越界 416;下载链接用附件响应。HTML 保留既有 sandbox,CSP 仅增加 `media-src self`。宿主严格 Cookie 在 opaque sandbox 的视频请求中不可依赖,因此只在已认证 HTML 响应中签发文件专用的 HMAC 只读地址,限一个 MP4 的 GET/HEAD,24 小时过期,服务重启失效;不能用于其他附件或写请求。离线 HTML 不写入票据,复制完整运行目录即可播放。 ## 停止与清理 自动初始化、默认关闭及纯 browser_close 采集步骤仅允许状态查询、可信关闭采集和结算工具;原生关闭子调用必须匹配程序登记的父 token。点击、输入、导航、快照、任意脚本、shell/API 绕行及操作封装均拒绝派发。正常通过或失败进入 cleanup 与停止后的 cleanup 复用准入规则;用户声明的业务清理仍按当前冻结定义执行。test_current 明确提示关闭流程,完成的业务和检查保持冻结。 纯浏览器释放明确失败时立即持久隔离并停止后续实例业务;可信关闭成功时才按原规则进入下一实例。操作拒绝保留调用错误,不能生成成功的实际业务步骤。纯 API 不增加初始化或关闭。 原生停止按钮禁止后续业务动作。等待当前 Agent 的在途操作结算后,用同一 Agent 的 `followup()` 发起明确标记的清理轮次;只准入冻结的当前清理步骤及必要采集/结算工具。不会创建清理会话,也不会恢复业务步骤。用户停止记录 CANCELLED;规划或步骤超时记录 ERROR 并注明超时原因。 未能在取消宽限期内结算、清理失败或重启发现未结算运行时持久隔离共享资源,拒绝下一批测试。迟到结果仅记录审计,不改写封存结果。解除必须提供实际停止与环境重置证据。 ## 边界 0.12.3 的账本格式为 2,SuiteRun 与计划格式继续为 1。每次保存先原子写入 checkpoints/state-序号.json,再 fsync 提交包含路径、覆盖序号、字节数与 SHA-256 的 state_checkpoint 事件,随后原子更新 results.json;所有必需写入成功后才继续派发。正常保留最近两份已提交检查点,较早状态副本回收,业务事件和证据保留。回收失败只提示并保留文件,必需写入失败仍终止派发。 rebuild 为异步、可取消的逐行校验,兼容旧内嵌 state_saved;验证序号、版本、应保留快照的完整性及运行归属,再补已保存调用和操作事实。尾损坏或快照后未封存状态标中断,中间损坏、序号断裂、应保留快照损坏拒绝可信重建。不能用旧缓存掩盖新提交异常,不访问模型或目标。最大读取单元仍包括一个旧快照行或新完整状态文件。 HTML 内嵌单份调用与下载数据,详情按需创建文本,图片预览与下载复用同一图片。日志以独立 events.jsonl 流式下载,不再生成内嵌 gzip;已有 events.jsonl.gz 的受保护访问仍可用。在线响应对准确日志/录像路径签发 GET/HEAD 只读票据,离线原件不含票据。日志和一般附件用流发送,断开即关闭流。完整目录保留才能离线下载账本及播放录像;单 HTML 仍包含结论、步骤、断言和内嵌内容。 - 模型、MCP、审批、轮次配置复用当前 Agent;只支持 native 工具模式。推荐宿主 `maxParallelToolCalls: 1`,测试增强另行拒绝并发步骤提交。 - 同一插件同时只允许一个活动测试,浏览器实例必须专用于测试。每个数据实例顺序执行,初始化/清理在同一会话完成,浏览器上下文依实例释放。 - API 采集只支持 GET JSON;网页地址用 DOM 的 url 模式。自然语言计划只固定业务短句和文字检查点;能力、目标在当前步骤执行时通过 test_define_step 确定,输出类型由采集方式登记,选择器/接口字段通过 test_capture 绑定,比较器通过 test_bind_check 绑定,可根据实际页面修正并记录原因。已采集观察不可覆盖,只能补采缺少输出。JSON 默认沿用固定 capture,也可显式声明 capture_mode: runtime。 - 不再提供旧 `testRunner` 自定义调度和 hook 接口。其他插件使用宿主公开事件与工具扩展点,不依赖独立执行器。 - 模型最终文字仍属于模型输出;可核验结果是 `test_finish`、报告及账本。失败、阻塞或缺少观察不能口头改判通过。 - 报告链接默认对应本机 DSH HTTP 端口;远程代理部署需按实际浏览器服务地址访问相同 `/test-reports/` 路径。离线 HTML 始终可用。 运行时采集方式保存在步骤的 `capture_attempts` 和 `capture_configured` 事件中,原始 plan.json 不被改写。规划阶段只允许计划提交、状态查询、待办与用户追问;规划停止不派发浏览器清理。 ## 原生计划审核 0.6.0 增加 `/test-plan --file` 与 `/test-data`,复用下述原生审核。文件/参数化入口的文字计划包含全部展开实例;实例边界、参数和数量由 `case-input.ts` 决定,模型仅对给定 instance_id 提交文字步骤。每个展开实例生成独立内部 case,避免不同数据行共享可变执行定义;步骤和规则ID带实例前缀。原始模板、CSV、参数和展开任务保存在 `plan.planning.input`,运行目录同时保存 `input.json`。审核展示全量参数和步骤,旧草案不能批准新计划,执行不重新读取源文件。 TXT/Markdown 的直接 `/test-run` 先通报全部用例后执行;普通 JSON 路径保留原有完整执行定义。报告的来源用例筛选使用模板标识,数据筛选使用 CSV 行标识,同时展示原文、参数和任务;内部实例ID仍用于账本与证据绑定。 `/test-plan` 使用官方 `commands.find/execute` 解析当前 Agent 的 `/plan` 命令,在原会话设置 plan 模式。Web 预设把 plan 服务放在隔离组中,不能从全局或 Agent 根 Context 直接读取该服务。`test_submit_plan` 校验并保存可替换的 `plan-draft.json`,此时不展开实例、不启动动作。插件生成只包含业务短句和文字检查点的审核 Markdown,工具守卫要求 `exit_plan_mode` 审核参数与当前草案完全一致,确保用户批准的是当前文字草案。观察原生工具成功结果 `{ approved: true }` 后才冻结同一草案;拒绝、要求修改、停止或单纯 `/plan off` 均不会放行执行。审核工具及用户交互由宿主提供,插件不伪造宿主审批事件。 草案提交后停掉规划计时器,人工审核时间不限;执行超时从批准后的具体动作开始。每次草案及批准调用ID进入插件账本。未批准取消恢复进入前的模式;批准退出由宿主处理。宿主未提供原生 `/plan` 命令时仅 `/test-plan` 返回明确错误,原有入口不受影响。 纯 API 计划复用相同状态机和报告:`test_capture` 经原生管线调用 `test_api_get`,以 `http-json-v1` 保存原始响应并提取观察;同一个对象观察可绑定状态码、响应体等多条嵌套字段断言,无浏览器初始化、关闭或截图阶段。 ## 文字步骤的运行时定义 文字步骤以 kind=intent 和 checks 保存,JSON文件入口仍仅接受完整action/assertion契约。plan.json保持原文不变;每个检查点在展开实例时生成固定必需ID,缺少绑定不能视为通过。进入当前步骤后才允许 test_define_step,采集方式决定本步输出类型,模型无需重复声明Schema;不能修改未来步骤或已有观察,但可以补采新输出。首次实际定义浏览器步骤才初始化浏览器并登记同会话清理,纯接口计划不触发浏览器。 test_bind_check以check_index选择当前文字规则,插件固定rule_ref及必需身份;模型负责在当前上下文中把文字预期转换为比较器和引用,不得从观察反推预期。结构、类型、引用由既有契约校验,结果仍由确定性比较器计算。自然语言含义的一致性依赖模型理解,记录原文、文字检查点、实际定义和证据供审计,不能把结构校验称为自然语言语义的程序证明。 运行定义进入实例effective_steps及step_defined/check_bound事件,原始文字计划不会被替换。动态追加检查仍保留尚未执行的文字检查必需集合,避免追加后丢失原有检查义务。 明确的简单数字条件(如“不为0”“状态码为200”)在绑定断言时检查数值和比较器一致性,不能用数字字符串或不同预期替代。此检查不在规划阶段生成操作参数,也不是对任意自然语言的完整语义验证。 ## 规划说明与语言 `plan.planning` 保存插件提供的原始任务和面向用户的一两句拆分说明,不作为新增断言的授权来源。审核卡片和 `/test` 的提交结果都包含原始任务、拆分思路、步骤清单;普通入口通过本会话公开 `assistant/message` 事件观察这三部分及计划内容,缺少通报时工具守卫要求模型先在正常对话补齐,再执行,不增加用户确认。工具结果、其他会话消息不能代替通报;不写入宿主聊天数据。用户追加说明仍记录到规则来源,原始任务不会被模型摘要替换。 每次组装测试提示时通过宿主公开 `settings.describe({redactSecrets:true})` 读取 locale.preference;不可用或未设置时沿用用户消息语言。DSH浏览器自动推断的locale未暴露给Host模型请求,本实现不声称能读取所有浏览器临时语言。语言指导覆盖进度、简短分析、工具reason、计划和最终回复;原始资料保持原文。 客户端的 `progress-view.ts` 只计算展示焦点、跨实例编号与状态;`progress-dock.tsx` 负责折叠、长文字及限定高度的列表,不参与执行。面板复用宿主输入框宽度变量和公开图标组件,图标库通过浏览器模块共享,不打包整套 UI 库。 ## 实际操作说明与手工文件 `StepResult.actual_operations` 保存程序生成的身份、本步顺序、固定 Binding、简短说明、父/子调用 ID、粒度、派发及结算状态。调用参数和结果沿用 CallRecord。登记、关联、派发、结束事件及快照持久保存后才执行;必需写入失败禁止继续派发。只信任 NativeTest.call 生成并关联父 token 的子调用,不把任意 parent 当作可信入口。API 的 test_capture 逐个实际 HTTP 子调用保存采集原因,无重复父操作;检查由 AssertionResult 投影。 入口只存在于当前测试作用域。成功子调用的文字、图片经 output.render 原样呈现,上下文与 concludesTurn 按公开 SDK 传递。around-dispatch 将规范子失败传回外层,保持 isError/error/content,不包装成成功。审批、目标校验、参数校验、取消仍由原生子管线负责。 最终业务编号按实例步骤顺序产生;准备和用户收尾单列,自动 __reset/__close 不计号。NOT_DISPATCHED 入附录;重试不合并;在途封存为 UNKNOWN,迟到结果仅审计。复合工具和直接页面分析脚本无法证明内部原子动作,完整性标 PARTIAL;旧运行标 NOT_RECORDED,不补写。说明不作为实际输入或新增预期的依据。 `actual-steps.json` 保存脱敏的运行记录,继续包含状态、输入、来源及证据引用。`manual-cases.md` 是另一种交付:只含名称、用户原文、前置、实际步骤和收尾,步骤只有序号、操作及预期;实际数据合并到操作中,没有预期时留空。JSON 缺独立原始描述时原文留空,CSV 使用未替换的用户模板。失败尝试、未知或未派发操作不写成下一次执行动作,原业务检查即使 FAIL 仍保留冻结预期。 NativeTest 在操作派发前将解释和真实输入存入独立 manual-source.json;保存期同步原始用例、前置/收尾及冻结检查预期。只保存手工需要的字段,去掉短期 ref 与脚本,内容未变时不重复写,权限 600;不放入脱敏账本或运行结果。该原始文件不开放报告访问。输出批次及 ordinal 编号的 manual-cases-1.md 等独立文件,HTML 仅保留下载链接;准确手工 Markdown 路径复用 GET/HEAD 票据与流式附件访问,正文不内嵌 HTML。离线下载要保留这些文件。 文件导出失败保留可重建快照并提示,不改断言。重建从账本及原始手工资料生成 -rebuilt 文件,保留原件,不访问模型或目标;旧记录没有原始值时不能解除脱敏。实际步骤 JSON 的独立格式标识仍不可作为执行计划。 ## 用户格式与开发回归 面向用户的命令帮助和指南只介绍 TXT、Markdown 用例及 CSV 参数化。JSON 执行支持保留为开发调试入口,字段与生成方式见[开发者说明](../reference/开发者JSON用例与调试.md)。case-input.ts 仍按最外层列表项读取 Markdown,其中的表格和前置/收尾文字原样留在用例内;没有新增另一套解析格式。 scripts/gitlab/markdown.mjs 将公开 Markdown 模板绑定到私有账号配置和固定准备记录,输出两条 UI、三条接口场景及单例文件,使用 writePrivateText 保持私有路径和 600 权限。生成器同时保留完整开发集合,默认只打印 Markdown 命令。业务预期来自既有 seeds,资源 ID 仅用于本次定位。 文字运行的完整 HTTP 采集固定登记 object 类型,子字段不按实际值推断类型;不支持的数组括号路径在检查绑定前拒绝并提示数字路径段。普通规划轮次未提交计划时补一次 plugin:test 提醒,保留原预算;再次结束未提交则封存 ERROR。必要信息追问和原生审核保持等待,普通用户补充信息后重新计时;不允许无计划的 0/0 占用会话。