Skip to content

Commit 9f2c0a8

Browse files
committed
fix(qa): keep searchable tool coverage report-only
1 parent da1925c commit 9f2c0a8

5 files changed

Lines changed: 75 additions & 2 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ Docs: https://docs.openclaw.ai
1616
### Fixes
1717

1818
- Providers/Ollama: treat Docker/OrbStack host aliases as local Ollama endpoints so `ollama-local` marker auth works when OpenClaw runs inside a VM/container and Ollama runs on the host. Fixes #84875.
19+
- QA-Lab: keep explicitly searchable/deferred OpenClaw dynamic tool rows report-only by default so tool-coverage gates do not treat mock discovery gaps as hard product failures. (#80319) Thanks @100yenadmin.
1920
- Agents: cap heartbeat model bleed context hints by the stored session window when runtime model metadata is unavailable, so overflow recovery advice does not suggest a larger window than the active session actually has.
2021
- Control UI/Web Push: use `https://openclaw.ai` as the generated default VAPID subject instead of the old localhost mailbox so iOS PWA push setup uses an Apple-acceptable subject when `OPENCLAW_VAPID_SUBJECT` is unset. Fixes #83134. (#83317) Thanks @IWhatsskill.
2122
- Agents/Pi: keep embedded session transcript writes from tripping false takeover detection after packaged npm onboarding agent turns.

extensions/qa-lab/src/runtime-tool-metadata.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,10 @@ export function readRuntimeToolCoverageMetadata(params: {
150150
const capabilityLayer = capabilityLayerInput
151151
? (capabilityLayerInput as QaRuntimeCapabilityLayer)
152152
: DEFAULT_CAPABILITY_LAYER_BY_BUCKET[bucket];
153-
const required = readBoolean(toolCoverage?.required) ?? bucket !== "optional-profile-or-plugin";
153+
const explicitSearchableDynamic = capabilityLayerInput === "openclaw-dynamic-searchable";
154+
const required =
155+
readBoolean(toolCoverage?.required) ??
156+
(bucket !== "optional-profile-or-plugin" && !explicitSearchableDynamic);
154157
return {
155158
bucket,
156159
expectedLayer,

extensions/qa-lab/src/tool-coverage-report.test.ts

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,68 @@ describe("qa tool coverage report", () => {
223223
);
224224
});
225225

226+
it("keeps searchable OpenClaw dynamic tool rows report-only by default", () => {
227+
const report = buildQaToolCoverageReport({
228+
scenarios: [
229+
makeScenario("tool-searchable-web-search", "web-search", {
230+
toolName: "web_search",
231+
toolCoverage: {
232+
bucket: "openclaw-dynamic-integration",
233+
expectedLayer: "openclaw-dynamic",
234+
capabilityLayer: "openclaw-dynamic-searchable",
235+
},
236+
}),
237+
],
238+
summary: {
239+
scenarios: [
240+
{
241+
name: "tool web_search searchable",
242+
status: "fail",
243+
runtimeParity: {
244+
scenarioId: "tool-searchable-web-search",
245+
drift: "tool-call-shape",
246+
driftDetails: "searchable discovery was report-only",
247+
cells: {
248+
pi: {
249+
runtime: "pi",
250+
transcriptBytes: "",
251+
toolCalls: [{ tool: "web_search", argsHash: "a", resultHash: "r" }],
252+
finalText: "",
253+
usage: { inputTokens: 0, outputTokens: 0, totalTokens: 0 },
254+
wallClockMs: 1,
255+
bootStateLines: [],
256+
},
257+
codex: {
258+
runtime: "codex",
259+
transcriptBytes: "",
260+
toolCalls: [],
261+
finalText: "",
262+
usage: { inputTokens: 0, outputTokens: 0, totalTokens: 0 },
263+
wallClockMs: 1,
264+
bootStateLines: [],
265+
},
266+
},
267+
},
268+
},
269+
],
270+
},
271+
generatedAt: "2026-05-10T00:00:00.000Z",
272+
});
273+
274+
expect(report.pass).toBe(true);
275+
expect(report.failures).toEqual([]);
276+
expect(report.reportOnlyTools).toBe(1);
277+
expect(report.passingTools).toBe(0);
278+
expect(report.searchableDynamicTools).toBe(1);
279+
expect(report.rows[0]).toEqual(
280+
expect.objectContaining({
281+
capabilityLayer: "openclaw-dynamic-searchable",
282+
required: false,
283+
drift: "tool-call-shape",
284+
}),
285+
);
286+
});
287+
226288
it("passes required OpenClaw dynamic tool coverage when both runtimes exercise the tool", () => {
227289
const report = buildQaToolCoverageReport({
228290
scenarios: [

extensions/qa-lab/src/tool-coverage-report.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ export type QaToolCoverageReport = {
6161
trackedTools: number;
6262
nativeWorkspaceTools: number;
6363
dynamicIntegrationTools: number;
64+
searchableDynamicTools: number;
6465
optionalTools: number;
6566
passingTools: number;
6667
failingTools: number;
@@ -286,10 +287,14 @@ export function buildQaToolCoverageReport(params: {
286287
nativeWorkspaceTools: rows.filter((row) => row.bucket === "codex-native-workspace").length,
287288
dynamicIntegrationTools: rows.filter((row) => row.bucket === "openclaw-dynamic-integration")
288289
.length,
290+
searchableDynamicTools: rows.filter(
291+
(row) => row.capabilityLayer === "openclaw-dynamic-searchable",
292+
).length,
289293
optionalTools: rows.filter((row) => row.bucket === "optional-profile-or-plugin").length,
290294
passingTools: evaluated
291295
? rows.filter(
292296
(row) =>
297+
row.required &&
293298
!row.tracking &&
294299
row.pi === "pass" &&
295300
row.codex === "pass" &&
@@ -315,6 +320,7 @@ export function renderQaToolCoverageMarkdownReport(report: QaToolCoverageReport)
315320
`- Tracked issue rows: ${report.trackedTools}`,
316321
`- Codex-native workspace tools: ${report.nativeWorkspaceTools}`,
317322
`- OpenClaw dynamic integration tools: ${report.dynamicIntegrationTools}`,
323+
`- Searchable/deferred dynamic tools: ${report.searchableDynamicTools}`,
318324
`- Optional/profile/plugin-dependent tools: ${report.optionalTools}`,
319325
`- Passing tools: ${report.passingTools}`,
320326
`- Failing tools: ${report.failingTools}`,

qa/scenarios/index.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@ Runtime parity tiers:
3131
default runtime-tool fixtures. OpenClaw dynamic integration tools in this
3232
tier are hard-gated by `openclaw qa coverage --tools --summary`; Codex-native
3333
workspace rows remain separately tracked until native/live behavior is the
34-
asserted surface. Selected with
34+
asserted surface. Rows that explicitly target searchable/deferred OpenClaw
35+
dynamic loading stay report-only unless a fixture promotes them to required. Selected with
3536
`openclaw qa suite --runtime-pair pi,codex --runtime-parity-tier standard`
3637
- `optional`: profile-, plugin-, or external-service-dependent runtime-tool
3738
fixtures that stay out of the default release gate

0 commit comments

Comments
 (0)