mirror of
https://github.com/hansjone/oclaw.git
synced 2026-10-18 20:53:16 +08:00
同时收敛启动与运维脚本默认行为(含 wiki worker)、更新 Admin 可观测性与相关测试,降低首轮时延并提高运行稳定性。 Made-with: Cursor
190 lines
7.6 KiB
Markdown
190 lines
7.6 KiB
Markdown
# Config restart capability flip
|
|
|
|
```yaml qa-scenario
|
|
id: config-restart-capability-flip
|
|
title: Config restart capability flip
|
|
surface: config
|
|
coverage:
|
|
primary:
|
|
- config.restart-apply
|
|
secondary:
|
|
- plugins.capabilities
|
|
objective: Verify a restart-triggering config change flips capability inventory and the same session successfully uses the newly restored tool after wake-up.
|
|
successCriteria:
|
|
- Capability is absent before the restart-triggering patch.
|
|
- Restart sentinel wakes the same session back up after config patch.
|
|
- The restored capability appears in tools.effective and works in the follow-up turn.
|
|
docsRefs:
|
|
- docs/gateway/configuration.md
|
|
- docs/gateway/protocol.md
|
|
- docs/tools/image-generation.md
|
|
codeRefs:
|
|
- src/gateway/server-methods/config.ts
|
|
- src/gateway/server-restart-sentinel.ts
|
|
- src/gateway/server-methods/tools-effective.ts
|
|
- extensions/qa-lab/src/suite.ts
|
|
execution:
|
|
kind: flow
|
|
summary: Verify a restart-triggering config change flips capability inventory and the same session successfully uses the newly restored tool after wake-up.
|
|
config:
|
|
setupPrompt: "Capability flip setup: acknowledge this setup so restart wake-up has a route."
|
|
imagePrompt: "Capability flip image check: generate a QA lighthouse image in this turn right now. Do not acknowledge first, do not promise future work, and do not stop before using image_generate. Final reply must include the MEDIA path."
|
|
imagePromptSnippet: "Capability flip image check"
|
|
deniedTool: image_generate
|
|
```
|
|
|
|
```yaml qa-flow
|
|
steps:
|
|
- name: restores image_generate after restart and uses it in the same session
|
|
actions:
|
|
- call: ensureImageGenerationConfigured
|
|
args:
|
|
- ref: env
|
|
- call: readConfigSnapshot
|
|
saveAs: original
|
|
args:
|
|
- ref: env
|
|
- set: originalTools
|
|
value:
|
|
expr: "original.config.tools && typeof original.config.tools === 'object' ? original.config.tools : null"
|
|
- set: originalToolsDeny
|
|
value:
|
|
expr: "originalTools ? (Object.prototype.hasOwnProperty.call(originalTools, 'deny') ? structuredClone(originalTools.deny) : undefined) : undefined"
|
|
- set: denied
|
|
value:
|
|
expr: "Array.isArray(originalToolsDeny) ? originalToolsDeny.map((entry) => String(entry)) : []"
|
|
- set: deniedWithImage
|
|
value:
|
|
expr: "denied.includes(config.deniedTool) ? denied : [...denied, config.deniedTool]"
|
|
- set: sessionKey
|
|
value: agent:qa:capability-flip
|
|
- call: createSession
|
|
args:
|
|
- ref: env
|
|
- Capability flip
|
|
- ref: sessionKey
|
|
- try:
|
|
actions:
|
|
- call: patchConfig
|
|
args:
|
|
- env:
|
|
ref: env
|
|
patch:
|
|
tools:
|
|
deny:
|
|
ref: deniedWithImage
|
|
- call: waitForGatewayHealthy
|
|
args:
|
|
- ref: env
|
|
- call: waitForQaChannelReady
|
|
args:
|
|
- ref: env
|
|
- 60000
|
|
- call: runAgentPrompt
|
|
args:
|
|
- ref: env
|
|
- sessionKey:
|
|
ref: sessionKey
|
|
message:
|
|
expr: config.setupPrompt
|
|
timeoutMs:
|
|
expr: liveTurnTimeoutMs(env, 30000)
|
|
- call: readEffectiveTools
|
|
saveAs: beforeTools
|
|
args:
|
|
- ref: env
|
|
- ref: sessionKey
|
|
- assert:
|
|
expr: "!beforeTools.has(config.deniedTool)"
|
|
message:
|
|
expr: "`${config.deniedTool} still present before capability flip`"
|
|
- set: wakeMarker
|
|
value:
|
|
expr: "`QA-CAPABILITY-${randomUUID().slice(0, 8)}`"
|
|
- call: patchConfig
|
|
args:
|
|
- env:
|
|
ref: env
|
|
patch:
|
|
tools:
|
|
deny:
|
|
expr: "originalToolsDeny === undefined ? null : originalToolsDeny"
|
|
agents:
|
|
defaults:
|
|
imageGenerationModel:
|
|
primary: openai/gpt-image-1
|
|
sessionKey:
|
|
ref: sessionKey
|
|
note:
|
|
ref: wakeMarker
|
|
- call: waitForGatewayHealthy
|
|
args:
|
|
- ref: env
|
|
- 60000
|
|
- call: waitForQaChannelReady
|
|
args:
|
|
- ref: env
|
|
- 60000
|
|
- call: waitForCondition
|
|
saveAs: afterTools
|
|
args:
|
|
- lambda:
|
|
async: true
|
|
expr: "(() => readEffectiveTools(env, sessionKey).then((tools) => (tools.has('image_generate') ? tools : undefined)))()"
|
|
- expr: liveTurnTimeoutMs(env, 45000)
|
|
- 500
|
|
- set: imageStartedAtMs
|
|
value:
|
|
expr: "Date.now()"
|
|
- call: runAgentPrompt
|
|
args:
|
|
- ref: env
|
|
- sessionKey:
|
|
ref: sessionKey
|
|
message:
|
|
expr: config.imagePrompt
|
|
timeoutMs:
|
|
expr: liveTurnTimeoutMs(env, 45000)
|
|
- call: resolveGeneratedImagePath
|
|
saveAs: mediaPath
|
|
args:
|
|
- env:
|
|
ref: env
|
|
promptSnippet:
|
|
expr: config.imagePromptSnippet
|
|
startedAtMs:
|
|
ref: imageStartedAtMs
|
|
timeoutMs:
|
|
expr: liveTurnTimeoutMs(env, 45000)
|
|
# Tool-call assertion (criterion 2 of the parity completion
|
|
# gate in #64227): the restored `image_generate` capability
|
|
# must have actually fired as a real tool call. Without this
|
|
# assertion, a prose reply that just mentions a MEDIA path
|
|
# could satisfy the scenario, so strengthen it by requiring
|
|
# the mock to have recorded `plannedToolName: "image_generate"`
|
|
# against a post-restart request. The `!env.mock || ...`
|
|
# guard means this check only runs in mock mode (where
|
|
# `/debug/requests` is available); live-frontier runs skip
|
|
# it and still pass the rest of the scenario.
|
|
- assert:
|
|
expr: "!env.mock || [...(await fetchJson(`${env.mock.baseUrl}/debug/requests`))].some((request) => String(request.allInputText ?? '').toLowerCase().includes('capability flip image check') && request.plannedToolName === 'image_generate')"
|
|
message:
|
|
expr: "`expected image_generate tool call during capability flip scenario, saw plannedToolNames=${JSON.stringify([...(await fetchJson(`${env.mock.baseUrl}/debug/requests`))].filter((request) => String(request.allInputText ?? '').toLowerCase().includes('capability flip image check')).map((request) => request.plannedToolName ?? null))}`"
|
|
finally:
|
|
- call: patchConfig
|
|
args:
|
|
- env:
|
|
ref: env
|
|
patch:
|
|
tools:
|
|
deny:
|
|
expr: "originalToolsDeny === undefined ? null : originalToolsDeny"
|
|
- call: waitForGatewayHealthy
|
|
args:
|
|
- ref: env
|
|
- call: waitForQaChannelReady
|
|
args:
|
|
- ref: env
|
|
- 60000
|
|
detailsExpr: "`${wakeMarker}\\n${config.deniedTool}=${String(afterTools.has(config.deniedTool))}\\nMEDIA:${mediaPath}`"
|
|
```
|