fix: refresh delegation permissions and report Reasoner task outcomes

This commit is contained in:
Xin Wang
2026-10-24 09:27:33 +08:00
parent 7600a297b3
commit 5e0cde85e3
11 changed files with 359 additions and 20 deletions
+5 -3
View File
@@ -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 |
@@ -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"
}
@@ -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": "<fake fixture credential>"
},
"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
}
}
@@ -0,0 +1,7 @@
{
"review_independence": "same-family",
"acceptance_status": "provisional",
"round_limit": 2,
"rounds_completed": 1,
"first_round_semantic_undeclared": 3
}