setDraft(previous => ({ ...previous, enabled }))} />
+ {!draft.enabled && 停用后,已绑定助手需解绑或重新启用此后端才能开启新会话。已创建的任务继续使用提交时的配置。
}
{draft.type === "reasoner"
?
: }
diff --git a/implement-stage/task-backends/ASSUMPTIONS.md b/implement-stage/task-backends/ASSUMPTIONS.md
index 5836a01..e4e27d1 100644
--- a/implement-stage/task-backends/ASSUMPTIONS.md
+++ b/implement-stage/task-backends/ASSUMPTIONS.md
@@ -12,7 +12,7 @@
| B-007 | 推理强度 | provider_default 或 low/medium/high;非默认值要求模型资源显式声明 supportsReasoningEffort,按 reasoning_effort 参数发送,不支持时拒绝 | semantic | default |
| B-008 | 技能 | Reasoner 使用后端资源中的命名技能正文;OpenCode 使用远端原生技能/agent,平台仅保存说明,不伪装远程安装或权限隔离 | semantic | default |
| B-009 | 外部选择 | OpenCode保留地址/目录/服务鉴权,新增可选 agent 与 modelProviderId/modelId;推理强度及沙箱由远端管理 | interface | default |
-| B-010 | 执行结果 | Reasoner通过明确完成/提问控制函数返回业务结果;idle、普通文本、接收回执不能直接证明成功 | semantic | default |
+| B-010 | 执行结果 | Reasoner通过complete_task(status,result)明确报告目标成功或失败;部分完成记为failed并说明限制。该状态是执行模型的报告,非平台独立验证;idle、普通文本、接收回执不证明成功 | semantic | sweep |
| B-011 | 生命周期与取消 | 语音断线继续;取消停止当前任务;原生取消不代表外部工具或远端副作用被回滚,需核查远端停止 | semantic | user |
| B-012 | 恢复 | OpenCode只观察已有句柄;内置Reasoner重启标记中断,不自动重放工具;历史任务保留旧恢复语义 | semantic | default |
| B-013 | 时限与并发 | 新任务保存截止时间,排队和执行均计时;全局原生串行并发1,保留远端状态未确认时阻止新提交策略 | semantic | default |
@@ -22,6 +22,8 @@
| B-017 | F0过渡 | Walking skeleton短暂兼容旧tool提交入口以保持累计检查;迁移阶段移除该过渡入口,不保留为新产品接口 | interface | default |
| B-018 | 回退迁移 | 0027为数据语义迁移,downgrade明确拒绝,需备份恢复;不能把新的Reasoner配置静默回退成旧task工具 | semantic | default |
| B-019 | 切换Realtime | 已绑定任务后端时复用现有配置移除确认交互,确认切换后解除绑定,后端资源保留 | semantic | default |
-| B-020 | 配置有效性 | 后端配置拒绝未知字段;资源编辑验证已绑定助手,模型/工具失效时阻止保存和执行,不静默漏掉能力 | semantic | default |
+| B-020 | 配置有效性 | 后端配置拒绝未知字段;启用资源的编辑验证已绑定助手,模型/工具失效时阻止保存和委派;停用资源允许保留结构合法但引用失效的配置 | semantic | sweep |
| B-021 | 类型与凭据 | 后端资源可切换类型,但必须提交完整合法配置;OpenCode转Reasoner清除服务凭据,同类型空值/掩码保留原凭据 | interface | default |
-| B-022 | 指令模板 | 助手绑定 instructions 的 {{变量}} 使用任务接受时冻结的变量单次渲染;资源 instructions 和技能正文按字面使用,不访问密钥变量 | semantic | default |
+| B-022 | 指令模板 | 助手绑定 instructions 的业务变量使用接受时冻结的值单次渲染;system__time/system__time_utc沿用共享渲染器执行时刷新。资源 instructions 和技能正文按字面使用,不访问密钥变量 | semantic | sweep |
+| B-023 | 通话内更新 | 每次Speaker委派前重新读取后端、模型、工具和MCP启用状态及凭据;绑定或任务参数来源改变时要求重新连接。接受后的任务继续用快照,不因配置编辑撤销已有副作用 | semantic | sweep |
+| B-024 | 停用影响 | 允许停用仍被引用的后端并保留绑定;新助手会话因配置校验失败不能启动,需先解绑或启用。已有Speaker通话保留,但新委派会重新校验并拒绝停用资源 | semantic | sweep |
diff --git a/implement-stage/task-backends/SILENT_ASSUMPTION_SWEEP.json b/implement-stage/task-backends/SILENT_ASSUMPTION_SWEEP.json
new file mode 100644
index 0000000..8b35173
--- /dev/null
+++ b/implement-stage/task-backends/SILENT_ASSUMPTION_SWEEP.json
@@ -0,0 +1,42 @@
+{
+ "undeclared": [
+ {
+ "site": "backend/services/brains/prompt_tasks.py:29; backend/services/brains/prompt_tasks.py:55; backend/services/background_tasks/service.py:62",
+ "decision": "语音会话建立时解析的后端配置会用于该连接随后提交的所有任务;提交时复制该运行时对象,不重新读取后端、模型、工具或 MCP Server 的启用状态、配置与凭据。",
+ "why_it_matters": "管理员在通话期间修改配置、停用能力或轮换凭据后,该通话仍可使用旧配置创建新任务。B-004 声明任务提交后的快照冻结,但没有声明提交前配置已经固定于语音连接;B-020 也未说明有效性检查只在解析时进行。",
+ "class": "semantic"
+ },
+ {
+ "site": "backend/services/task_backend_resources.py:135; backend/services/config_resolver.py:328; backend/routes/task_backends.py:82",
+ "decision": "允许停用仍被助手引用的后端并保留绑定;此后解析该助手的完整运行配置会失败,因此新的 Speaker 会话也不能启动。",
+ "why_it_matters": "停用任务后端的影响范围是整个已绑定助手的新会话,而不仅是任务委派。规格和假设记录没有声明这一影响范围。",
+ "class": "semantic"
+ },
+ {
+ "site": "backend/services/background_tasks/reasoner.py:148; backend/services/background_tasks/reasoner.py:174",
+ "decision": "complete_task 只接收结果文本,并将任何被接受的结果统一记为 succeeded;没有让 Reasoner 显式返回业务失败或部分完成状态的控制参数。",
+ "why_it_matters": "模型即使通过结果文本说明目标无法完成,调用 complete_task 后任务卡片仍显示成功。B-010 声明了显式完成控制,但未定义 succeeded 表示结束执行还是业务目标达成。",
+ "class": "semantic"
+ }
+ ],
+ "stale_rows": [
+ {
+ "id": "B-020",
+ "site": "backend/routes/task_backends.py:37; backend/routes/task_backends.py:82",
+ "stated": "资源编辑验证已绑定助手,模型/工具失效时阻止保存。",
+ "actual": "enabled=false 时跳过模型和工具资源有效性检查,也跳过已绑定助手检查;结构合法但引用失效的停用配置可以保存。启用状态下的检查与记录一致。",
+ "why_it_matters": "记录没有声明停用资源的保存例外,因此描述的配置有效性保证强于实际代码。",
+ "class": "semantic"
+ },
+ {
+ "id": "B-022",
+ "site": "backend/services/background_tasks/reasoner.py:107; backend/services/background_tasks/opencode.py:91; backend/services/runtime_variables.py:155",
+ "stated": "助手绑定 instructions 的变量使用任务接受时冻结的变量单次渲染。",
+ "actual": "业务变量使用冻结快照并单次展开;但共享渲染器在执行时刷新 system__time 和 system__time_utc,因此这两个变量使用渲染时刻。最新 OpenCode 可选变量修复正确保留了既有可选变量语义,不改变此时钟行为。",
+ "why_it_matters": "任务排队后,包含系统时间的指令会得到执行时刻,而不是接受任务时刻;记录需要明确这个例外。",
+ "class": "semantic"
+ }
+ ],
+ "semantic_undeclared": 3,
+ "verdict": "gaps"
+}
diff --git a/implement-stage/task-backends/frontend-smoke.json b/implement-stage/task-backends/frontend-smoke.json
new file mode 100644
index 0000000..77bdf80
--- /dev/null
+++ b/implement-stage/task-backends/frontend-smoke.json
@@ -0,0 +1,176 @@
+{
+ "status": "passed_with_limitations",
+ "method": "Headless Google Chrome via temporary Playwright Core; built Next frontend at localhost:3217",
+ "mock_boundary": "Every /api request intercepted with explicit in-memory fixtures; non-local requests aborted. No backend, real model, OpenCode, database or real credentials contacted.",
+ "prior_attempts": [
+ {
+ "outcome": "Reasoner and OpenCode saves passed; narrow resource row edit was obstructed with existing expanded sidebar at390px. Repeated using existing sidebar collapse control; no application code changed."
+ }
+ ],
+ "fixtures": {
+ "models": [
+ "UI Strong Model: supportsReasoningEffort=true",
+ "UI Basic Model: supportsReasoningEffort=false"
+ ],
+ "tools": [
+ "UI HTTP Tool",
+ "UI Client Tool (must be excluded from Reasoner picker)"
+ ]
+ },
+ "steps": [
+ {
+ "name": "Render dedicated resources page and open Reasoner form",
+ "status": "passed",
+ "details": {
+ "desktop_layout": {
+ "viewport": 1440,
+ "width": 751.712158203125,
+ "left": 344.1438903808594,
+ "right": 1095.8560485839844,
+ "scrollWidth": 768,
+ "clientWidth": 768,
+ "horizontalOverflow": false,
+ "outsideViewport": false
+ }
+ }
+ },
+ {
+ "name": "Configure Reasoner model, high effort, inline skill and server-only tool",
+ "status": "passed",
+ "details": {
+ "captured_payload": {
+ "name": "UI Reasoner Smoke",
+ "description": "",
+ "enabled": true,
+ "type": "reasoner",
+ "secrets": {},
+ "config": {
+ "instructions": "",
+ "timeoutSeconds": 600,
+ "maxConcurrency": 1,
+ "modelResourceId": "ui_model_strong",
+ "reasoningEffort": "high",
+ "toolBindings": [
+ {
+ "toolId": "ui_http",
+ "argumentBindings": {
+ "query": {
+ "source": "model"
+ }
+ },
+ "templateBindings": {},
+ "resultAssignments": {}
+ }
+ ],
+ "skills": [
+ {
+ "name": "UI Analysis",
+ "content": "Analyze constraints before proposing a result."
+ }
+ ]
+ }
+ }
+ }
+ },
+ {
+ "name": "Edit preserves Reasoner values; unsupported model disables nondefault effort",
+ "status": "passed"
+ },
+ {
+ "name": "Configure OpenCode connection, agent, paired model and remote skill note",
+ "status": "passed",
+ "details": {
+ "captured_payload": {
+ "name": "UI OpenCode Smoke",
+ "description": "",
+ "enabled": true,
+ "type": "opencode",
+ "secrets": {
+ "password": ""
+ },
+ "config": {
+ "instructions": "",
+ "timeoutSeconds": 600,
+ "maxConcurrency": 1,
+ "url": "http://127.0.0.1:9",
+ "directory": "/test-fixture/repo",
+ "username": "opencode",
+ "agent": "build",
+ "modelProviderId": "ui-provider",
+ "modelId": "ui-model",
+ "skillsNote": "Installed remotely; fixture note only."
+ }
+ }
+ }
+ },
+ {
+ "name": "Responsive resource page and OpenCode modal fit a 390px viewport",
+ "status": "passed",
+ "details": {
+ "page_layout": {
+ "viewport": 390,
+ "width": 390,
+ "left": 0,
+ "right": 390,
+ "scrollWidth": 390,
+ "clientWidth": 390,
+ "horizontalOverflow": false,
+ "outsideViewport": false
+ },
+ "dialog_layout": {
+ "viewport": 390,
+ "width": 340.0999755859375,
+ "left": 24.950002670288086,
+ "right": 365.0499782562256,
+ "scrollWidth": 358,
+ "clientWidth": 358,
+ "horizontalOverflow": false,
+ "outsideViewport": false
+ }
+ }
+ },
+ {
+ "name": "Prompt binds backend independently and sends taskBackend without ToolBinding",
+ "status": "passed",
+ "details": {
+ "captured_task_backend": {
+ "backendId": "ui_backend_1",
+ "instructions": "Return clear evidence and limitations.",
+ "instructionSource": {
+ "source": "model"
+ }
+ },
+ "ordinary_tool_bindings": []
+ }
+ },
+ {
+ "name": "Migrated variable instruction source survives fetch/edit/save unchanged",
+ "status": "incomplete",
+ "error": "Optional legacy-variable fetch/edit/save follow-up timed out waiting for the task-instruction editor after page reload. Not diagnosed; this browser run does not claim to verify that scenario."
+ }
+ ],
+ "console_errors": [],
+ "page_errors": [],
+ "unexpected_requests": [],
+ "captures": [
+ "/private/tmp/ai-video-browser-smoke/reasoner-desktop.png",
+ "/private/tmp/ai-video-browser-smoke/opencode-desktop.png",
+ "/private/tmp/ai-video-browser-smoke/expanded-sidebar-mobile-limitation.png",
+ "/private/tmp/ai-video-browser-smoke/opencode-mobile.png",
+ "/private/tmp/ai-video-browser-smoke/prompt-binding-desktop.png"
+ ],
+ "limitations": [
+ "Browser smoke verifies rendered client behavior and request payloads against mocked APIs, not backend validation or genuine model/coding execution.",
+ "No microphone/WebRTC connection was started.",
+ "Responsive checks cover new resource page/dialog with the existing sidebar manually collapsed. Existing AppShell does not auto-collapse its252px sidebar on resize; the expanded sidebar obstructed row edit at390px.",
+ "Optional migrated variable instructionSource reload was not verified in this browser run; no backend or migration behavior is inferred from it.",
+ "Screenshots are diagnostic captures; mobile capture occurred during modal transition. Geometry checks, not pixel comparison, established the responsive result."
+ ],
+ "completed_at": "2026-10-24T01:25:20.247Z",
+ "acceptance": "Core frontend browser smoke passed against explicitly mocked APIs; optional legacy variable reload remains unverified.",
+ "cleanup": {
+ "temporary_browser_closed": true,
+ "local_next_server_stopped": true,
+ "project_dependencies_changed": false
+ }
+}
diff --git a/implement-stage/task-backends/sweep-metadata.json b/implement-stage/task-backends/sweep-metadata.json
new file mode 100644
index 0000000..e1631d5
--- /dev/null
+++ b/implement-stage/task-backends/sweep-metadata.json
@@ -0,0 +1,7 @@
+{
+ "review_independence": "same-family",
+ "acceptance_status": "provisional",
+ "round_limit": 2,
+ "rounds_completed": 1,
+ "first_round_semantic_undeclared": 3
+}