refactor(qa): make taxonomy coverage ids unique (#110280)

This commit is contained in:
Dallin Romney
2026-07-22 16:28:44 +09:00
committed by GitHub
parent 4ab686220f
commit 132d91e427
384 changed files with 3098 additions and 2353 deletions
+6 -5
View File
@@ -63,10 +63,11 @@ the selected profile instead of defining separate lanes. The resulting
counts and missing coverage IDs; the individual evidence entries remain the
source of truth for the tests, coverage roles, and results. Taxonomy feature
coverage IDs are exact proof targets, not aliases: primary scenario coverage
fulfills matching IDs, secondary coverage stays advisory. Coverage IDs use
dotted `namespace.behavior` form with lowercase alphanumeric/dash segments;
profile, surface, and category IDs may still use the existing dashed or dotted
taxonomy IDs.
fulfills matching IDs, while secondary coverage stays advisory. Every coverage
ID is exactly `taxonomy-surface.feature`, using the short surface ID from
`taxonomy.yaml`. A scenario's separate `surface` field is an execution/reporting
label (for example, `channel` or `runtime-tool`); it does not define taxonomy
ownership.
Slim evidence omits per-entry `execution` and sets `evidenceMode: "slim"`;
`smoke-ci` defaults to slim, and `--evidence-mode full` restores full entries:
@@ -74,7 +75,7 @@ Slim evidence omits per-entry `execution` and sets `evidenceMode: "slim"`;
```bash
pnpm openclaw qa run \
--qa-profile smoke-ci \
--category channel-framework.conversation-routing-and-delivery \
--category channels.conversation-routing-and-delivery \
--provider-mode mock-openai \
--output-dir .artifacts/qa-e2e/smoke-ci-profile-dispatch
```
@@ -55,6 +55,16 @@ describe("createQaSmokeCiPart", () => {
scenarioIds.map((scenarioId) => scenarioById.get(scenarioId)?.sourcePath),
);
const scorecardReport = readQaScorecardTaxonomyReport([...scenarioById.values()]);
const smokeScenarioRefs = new Set(
scorecardReport.categories
.filter((category) => category.profiles.includes("smoke-ci"))
.flatMap((category) => category.scenarioRefs),
);
expect(
[...selectedScenarioPaths].every(
(scenarioPath) => scenarioPath !== undefined && smokeScenarioRefs.has(scenarioPath),
),
).toBe(true);
const uncoveredCategoryIds = scorecardReport.categories
.filter((category) => category.profiles.includes("smoke-ci"))
.filter((category) => !category.scenarioRefs.some((ref) => selectedScenarioPaths.has(ref)))
+20 -20
View File
@@ -383,15 +383,15 @@ describe("qa cli runtime", () => {
{
test: {
kind: "qa-scenario",
id: "dm-chat-baseline",
title: "DM baseline conversation",
id: "channel-top-level-reply-shape",
title: "Channel top-level reply shape",
source: {
path: "qa/scenarios/channels/dm-chat-baseline.yaml",
path: "qa/scenarios/channels/channel-top-level-reply-shape.yaml",
},
},
coverage: [
{
id: "channels.dm",
id: "channels.group-final-reply",
role: "primary",
},
],
@@ -438,9 +438,9 @@ describe("qa cli runtime", () => {
repoRoot: "/tmp/openclaw-repo",
outputDir: ".artifacts/qa-e2e/smoke-ci",
profile: "smoke-ci",
surface: "channel-framework",
category: "channel-framework.conversation-routing-and-delivery",
scenarioIds: ["dm-chat-baseline"],
surface: "channels",
category: "channels.outbound-delivery-and-reply-pipeline",
scenarioIds: ["channel-top-level-reply-shape"],
transportId: "qa-channel",
fastMode: true,
concurrency: 2,
@@ -461,7 +461,7 @@ describe("qa cli runtime", () => {
channel: "telegram",
channelDriver: "crabline",
});
expect(suiteArgs.scenarioIds).toEqual(["dm-chat-baseline"]);
expect(suiteArgs.scenarioIds).toEqual(["channel-top-level-reply-shape"]);
expect(process.env.OPENCLAW_QA_PROFILE).toBe("release");
const evidence = JSON.parse(await fs.readFile(suiteEvidencePath, "utf8")) as {
evidenceMode?: unknown;
@@ -489,13 +489,13 @@ describe("qa cli runtime", () => {
expect(evidence.scorecard).not.toHaveProperty("profile");
expect(evidence.scorecard?.coverageIds?.fulfilled).toBe(1);
expect(evidence.scorecard?.categoryReports?.[0]).toMatchObject({
id: "channel-framework.conversation-routing-and-delivery",
id: "channels.outbound-delivery-and-reply-pipeline",
coverageIds: {
fulfilled: 1,
},
});
expect(evidence.entries?.[0]).not.toHaveProperty("execution");
expect(JSON.stringify(evidence.scorecard)).not.toContain("dm-chat-baseline");
expect(JSON.stringify(evidence.scorecard)).not.toContain("channel-top-level-reply-shape");
expectWriteContains(stdoutWrite, "QA run profile: smoke-ci; categories: 1; scenarios:");
expectWriteContains(stdoutWrite, `QA profile scorecard: ${suiteEvidencePath}`);
} finally {
@@ -511,8 +511,8 @@ describe("qa cli runtime", () => {
await runQaProfileCommand({
repoRoot: "/tmp/openclaw-repo",
profile: "release",
surface: "agent-runtime-and-provider-execution",
category: "agent-runtime-and-provider-execution.agent-turn-execution",
surface: "agent-runtime",
category: "agent-runtime.agent-turn-execution",
providerMode: "mock-openai",
});
@@ -525,7 +525,7 @@ describe("qa cli runtime", () => {
await runQaProfileCommand({
repoRoot: "/tmp/openclaw-repo",
profile: "release",
surface: "channel-framework",
surface: "channels",
providerMode: "mock-openai",
scenarioIds: ["channel-chat-baseline", "telegram-help-command"],
});
@@ -542,8 +542,8 @@ describe("qa cli runtime", () => {
await runQaProfileCommand({
repoRoot: "/tmp/openclaw-repo",
profile: "all",
surface: "agent-runtime-and-provider-execution",
category: "agent-runtime-and-provider-execution.agent-turn-execution",
surface: "agent-runtime",
category: "agent-runtime.agent-turn-execution",
providerMode: "mock-openai",
});
@@ -568,12 +568,12 @@ describe("qa cli runtime", () => {
await runQaProfileCommand({
repoRoot: "/tmp/openclaw-repo",
profile: "smoke-ci",
scenarioIds: ["dm-chat-baseline", "instruction-followthrough-repo-contract"],
scenarioIds: ["channel-top-level-reply-shape", "control-ui-qa-channel-image-roundtrip"],
});
const suiteArgs = mockFirstObjectArg(runQaSuite);
expect(suiteArgs.channelDriver).toBe("crabline");
expect(suiteArgs.scenarioIds).toEqual(["dm-chat-baseline"]);
expect(suiteArgs.scenarioIds).toEqual(["channel-top-level-reply-shape"]);
expect(suiteArgs.scenarioIds).not.toEqual(
expect.arrayContaining([
"instruction-followthrough-repo-contract",
@@ -636,11 +636,11 @@ describe("qa cli runtime", () => {
runQaProfileCommand({
repoRoot: "/tmp/openclaw-repo",
profile: "smoke-ci",
category: "channel-framework.conversation-routing-and-delivery",
category: "channels.outbound-delivery-and-reply-pipeline",
scenarioIds: ["not-a-real-scenario"],
}),
).rejects.toThrow(
"qa run did not find taxonomy scenarios for --qa-profile smoke-ci --category channel-framework.conversation-routing-and-delivery --scenario not-a-real-scenario.",
"qa run did not find taxonomy scenarios for --qa-profile smoke-ci --category channels.outbound-delivery-and-reply-pipeline --scenario not-a-real-scenario.",
);
expect(runQaSuite).not.toHaveBeenCalled();
});
@@ -2087,7 +2087,7 @@ describe("qa cli runtime", () => {
await runQaCoverageReportCommand({ repoRoot: process.cwd() });
expectWriteContains(stdoutWrite, "# QA Coverage Inventory");
expectWriteContains(stdoutWrite, "memory.recall");
expectWriteContains(stdoutWrite, "session-memory.embedding-search-recall");
});
});
+6 -6
View File
@@ -215,9 +215,9 @@ describe("qa cli registration", () => {
"--qa-profile",
"smoke-ci",
"--surface",
"channel-framework",
"channels",
"--category",
"channel-framework.conversation-routing-and-delivery",
"channels.conversation-routing-and-delivery",
"--scenario",
"dm-chat-baseline",
"--evidence-mode",
@@ -240,8 +240,8 @@ describe("qa cli registration", () => {
repoRoot: "/tmp/openclaw-repo",
outputDir: ".artifacts/qa-e2e/smoke-ci",
profile: "smoke-ci",
surface: "channel-framework",
category: "channel-framework.conversation-routing-and-delivery",
surface: "channels",
category: "channels.conversation-routing-and-delivery",
scenarioIds: ["dm-chat-baseline"],
evidenceMode: "slim",
transportId: "qa-channel",
@@ -257,8 +257,8 @@ describe("qa cli registration", () => {
it.each([
["--output-dir", [".artifacts/qa-e2e/smoke-ci"]],
["--surface", ["agent-runtime-and-provider-execution"]],
["--category", ["channel-framework.conversation-routing-and-delivery"]],
["--surface", ["agents"]],
["--category", ["channels.conversation-routing-and-delivery"]],
["--scenario", ["dm-chat-baseline"]],
["--evidence-mode", ["slim"]],
["--exclude-test-execution-evidence", []],
+8
View File
@@ -0,0 +1,8 @@
import { z } from "zod";
export const qaCoverageIdSchema = z
.string()
.trim()
.regex(/^[a-z0-9][a-z0-9-]*\.[a-z0-9][a-z0-9-]*$/, {
message: "coverage ids must use exactly <surface-id>.<feature-id>",
});
+69 -63
View File
@@ -11,12 +11,12 @@ import {
import { readQaScenarioPack, type QaSeedScenarioWithSource } from "./scenario-catalog.js";
import { readQaScorecardTaxonomyReport, type QaMaturityTaxonomy } from "./scorecard-taxonomy.js";
const TEST_EXECUTABLE_CATEGORY_ID = "agent-runtime-and-provider-execution.agent-turn-execution";
const TEST_EXECUTABLE_COVERAGE_ID = "channels.dm";
const TEST_BROWSER_CATEGORY_ID = "browser-control-ui-and-webchat.browser-ui";
const TEST_BROWSER_COVERAGE_ID = "ui.control";
const TEST_WEBCHAT_COVERAGE_ID = "ui.webchat";
const DOTTED_COVERAGE_ID_PATTERN = /^[a-z0-9][a-z0-9-]*(?:\.[a-z0-9][a-z0-9-]*)+$/;
const TEST_EXECUTABLE_CATEGORY_ID = "agent-runtime.agent-turn-execution";
const TEST_EXECUTABLE_COVERAGE_ID = "agent-runtime.dm";
const TEST_BROWSER_CATEGORY_ID = "control-ui.browser-ui";
const TEST_BROWSER_COVERAGE_ID = "control-ui.gateway-hosted-ui-control";
const TEST_WEBCHAT_COVERAGE_ID = "agent-runtime.webchat";
const TWO_PART_COVERAGE_ID_PATTERN = /^[a-z0-9][a-z0-9-]*\.[a-z0-9][a-z0-9-]*$/;
function buildQaScorecardTaxonomyReport(params: {
taxonomy: QaMaturityTaxonomy;
@@ -35,7 +35,6 @@ function buildQaScorecardTaxonomyReport(params: {
function testMaturityTaxonomy(params?: {
categoryId?: string;
coverageIds?: readonly string[];
featureCoverageIds?: readonly (readonly string[])[];
includeAllCategories?: boolean;
includeArchivedSurface?: boolean;
}): QaMaturityTaxonomy {
@@ -76,14 +75,9 @@ function testMaturityTaxonomy(params?: {
category_note: "test-category.md",
docs: [],
search_anchors: [],
features: (
params?.featureCoverageIds ??
(params?.coverageIds ?? [TEST_EXECUTABLE_COVERAGE_ID]).map((coverageId) => [
coverageId,
])
).map((coverageIds) => ({
name: coverageIds.join(" + "),
coverageIds: [...coverageIds],
features: (params?.coverageIds ?? [TEST_EXECUTABLE_COVERAGE_ID]).map((coverageId) => ({
name: coverageId,
coverageIds: [coverageId],
})),
},
],
@@ -103,7 +97,12 @@ function testMaturityTaxonomy(params?: {
category_note: "legacy-category.md",
docs: [],
search_anchors: [],
features: [{ name: "legacy.feature", coverageIds: ["legacy.feature"] }],
features: [
{
name: "Legacy feature",
coverageIds: ["archived-surface.legacy-feature"],
},
],
},
],
},
@@ -178,9 +177,9 @@ describe("qa coverage report", () => {
channelDriver: "live",
});
for (const [categoryId, scenarioRef] of [
["docker-podman-hosting.container-setup", "qa/scenarios/runtime/compose-setup.yaml"],
["containers.container-setup", "qa/scenarios/runtime/compose-setup.yaml"],
[
"docker-podman-hosting.image-release-and-validation",
"containers.image-release-and-validation",
"qa/scenarios/runtime/docker-package-install.yaml",
],
] as const) {
@@ -197,9 +196,7 @@ describe("qa coverage report", () => {
inventory.scorecardTaxonomy.profiles.find((profile) => profile.id === "all"),
).toMatchObject({
channelDriver: "live",
categoryIds: expect.arrayContaining([
"browser-automation-and-exec-sandbox-tools.tool-invocation-and-execution",
]),
categoryIds: expect.arrayContaining(["tools.tool-invocation-and-execution"]),
});
expect(inventory.scorecardTaxonomy.categoryCount).toBeGreaterThan(200);
expect(inventory.scorecardTaxonomy.requiredCategoryCount).toBeGreaterThan(0);
@@ -215,7 +212,7 @@ describe("qa coverage report", () => {
expect(
inventory.scorecardTaxonomy.categories
.flatMap((category) => category.coverageIds)
.every((coverageId) => DOTTED_COVERAGE_ID_PATTERN.test(coverageId)),
.every((coverageId) => TWO_PART_COVERAGE_ID_PATTERN.test(coverageId)),
).toBe(true);
expect(inventory.scorecardTaxonomy.validationIssues.length).toBeGreaterThan(0);
expect(
@@ -247,16 +244,18 @@ describe("qa coverage report", () => {
const observabilityPack = inventory.scenarioPacks.find((pack) => pack.id === "observability");
expect(personalPack?.missingScenarioIds).toStrictEqual([]);
expect(personalPack?.scenarioIds).toContain("personal-share-safe-diagnostics-artifact");
expect(personalPack?.coverageIds).toContain("personal.redaction");
expect(personalPack?.coverageIds).toContain("security.redaction-personal-redaction");
expect(observabilityPack?.missingScenarioIds).toStrictEqual([]);
expect(observabilityPack?.scenarioIds).toEqual(["otel-trace-smoke", "docker-prometheus-smoke"]);
expect(observabilityPack?.coverageIds).toContain("telemetry.prometheus");
expect(observabilityPack?.coverageIds).toContain("observability.prometheus");
expect(
expectDefined(inventory.byTheme.memory, "memory QA theme").map((coverage) => coverage.id),
).toContain("memory.recall");
).toContain("session-memory.memory-recall");
expect(
expectDefined(inventory.bySurface.memory, "memory QA surface").map((coverage) => coverage.id),
).toContain("memory.recall");
expectDefined(inventory.bySurface["session-memory"], "memory QA surface").map(
(coverage) => coverage.id,
),
).toContain("session-memory.memory-recall");
});
it("rejects duplicate ownership across YAML and non-YAML catalogs", () => {
@@ -288,7 +287,7 @@ describe("qa coverage report", () => {
expect(report).toContain("# QA Coverage Inventory");
expect(report).toContain("- Missing coverage metadata: 0");
expect(report).toContain("- Overlapping coverage IDs:");
expect(report).toContain("memory.recall");
expect(report).toContain("session-memory.embedding-search-recall");
expect(report).toContain("primary: memory-recall (qa/scenarios/memory/memory-recall.yaml)");
expect(report).toContain("secondary: active-memory-preprompt-recall");
expect(report).toContain("## Scenario Packs");
@@ -307,9 +306,11 @@ describe("qa coverage report", () => {
expect(report).toContain("- Inventory refs:");
expect(report).toContain("- Scenario coverage IDs:");
expect(report).toContain(
"- browser-automation-and-exec-sandbox-tools.tool-invocation-and-execution (browser-automation-and-exec-sandbox-tools / Tool Invocation and Execution; partial): profiles: all, release, smoke-ci; coverage IDs:",
"- tools.tool-invocation-and-execution (tools / Tool Invocation and Execution; partial): profiles: all, release; coverage IDs:",
);
expect(report).toContain(
"primary:playwright:ui/src/e2e/chat-flow.e2e.test.ts (control-ui.gateway-hosted-ui-control)",
);
expect(report).toContain("primary:playwright:ui/src/e2e/chat-flow.e2e.test.ts (ui.control)");
expect(report).not.toContain("### Unknown Scenario Coverage IDs");
});
@@ -445,7 +446,7 @@ describe("qa coverage report", () => {
repoRoot: process.cwd(),
scenarios: [
scenarioWithCoverage({
primary: ["agent-runtime-and-provider-execution.agent-turn-execution.missing-coverage"],
primary: ["agents.missing-coverage"],
}),
],
});
@@ -497,7 +498,7 @@ describe("qa coverage report", () => {
it("counts partial coverage IDs proportionately for taxonomy inventory", () => {
const report = buildQaScorecardTaxonomyReport({
taxonomy: testMaturityTaxonomy({
featureCoverageIds: [[TEST_EXECUTABLE_COVERAGE_ID, TEST_WEBCHAT_COVERAGE_ID]],
coverageIds: [TEST_EXECUTABLE_COVERAGE_ID, TEST_WEBCHAT_COVERAGE_ID],
}),
repoRoot: process.cwd(),
scenarios: [
@@ -525,7 +526,7 @@ describe("qa coverage report", () => {
);
});
it("counts each required taxonomy coverage ID once across categories", () => {
it("rejects one coverage ID assigned to different exact features", () => {
const taxonomy: QaMaturityTaxonomy = {
...testMaturityTaxonomy(),
profiles: [
@@ -534,15 +535,12 @@ describe("qa coverage report", () => {
description: "Test release profile.",
includeAllCategories: false,
channelDriver: "qa-channel",
categoryIds: [
"agent-runtime-and-provider-execution.agent-turn-execution",
"agent-runtime-and-provider-execution.tool-execution-controls",
],
categoryIds: ["agent-runtime.agent-turn-execution"],
},
],
surfaces: [
{
id: "agent-runtime-and-provider-execution",
id: "agent-runtime",
name: "Agent Runtime",
family: "test",
level: "experimental",
@@ -555,18 +553,9 @@ describe("qa coverage report", () => {
search_anchors: [],
features: [
{
name: "shared plus unique",
coverageIds: [TEST_EXECUTABLE_COVERAGE_ID, TEST_WEBCHAT_COVERAGE_ID],
name: "shared",
coverageIds: [TEST_EXECUTABLE_COVERAGE_ID],
},
],
},
{
id: "tool-execution-controls",
name: "Tool Execution Controls",
category_note: "tool-execution-controls.md",
docs: [],
search_anchors: [],
features: [
{
name: "shared",
coverageIds: [TEST_EXECUTABLE_COVERAGE_ID],
@@ -577,21 +566,38 @@ describe("qa coverage report", () => {
},
],
};
const report = buildQaScorecardTaxonomyReport({
taxonomy,
repoRoot: process.cwd(),
scenarios: [
scenarioWithCoverage({
primary: [TEST_EXECUTABLE_COVERAGE_ID],
secondary: [TEST_WEBCHAT_COVERAGE_ID],
sourcePath: "qa/scenarios/channels/dm-chat-baseline.yaml",
}),
],
});
expect(() =>
buildQaScorecardTaxonomyReport({
taxonomy,
repoRoot: process.cwd(),
scenarios: [],
}),
).toThrow(
`coverage ID ${TEST_EXECUTABLE_COVERAGE_ID} already belongs to agent-runtime.agent-turn-execution feature shared; coverage IDs must identify exactly one taxonomy feature`,
);
});
expect(report.requiredCoverageIdCount).toBe(2);
expect(report.inventoriedCoverageIdCount).toBe(1);
expect(report.coverageIdInventoryPercent).toBe(50);
it("requires one two-part ID owned by each exact taxonomy feature", () => {
const taxonomy = testMaturityTaxonomy();
const feature = expectDefined(
taxonomy.surfaces[0]?.categories[0]?.features[0],
"test taxonomy feature",
);
feature.coverageIds = [TEST_EXECUTABLE_COVERAGE_ID, TEST_WEBCHAT_COVERAGE_ID];
expect(() =>
buildQaScorecardTaxonomyReport({ taxonomy, repoRoot: process.cwd(), scenarios: [] }),
).toThrow("taxonomy features must define exactly one coverage ID");
feature.coverageIds = ["agents.delivery.group"];
expect(() =>
buildQaScorecardTaxonomyReport({ taxonomy, repoRoot: process.cwd(), scenarios: [] }),
).toThrow("coverage ids must use exactly <surface-id>.<feature-id>");
feature.coverageIds = [TEST_BROWSER_COVERAGE_ID];
expect(() =>
buildQaScorecardTaxonomyReport({ taxonomy, repoRoot: process.cwd(), scenarios: [] }),
).toThrow(`coverage ID ${TEST_BROWSER_COVERAGE_ID} must belong to surface agent-runtime`);
});
it("inventories script producer declarations", () => {
+3 -2
View File
@@ -1,5 +1,6 @@
// Qa Lab plugin module implements QA evidence summary behavior.
import { z } from "zod";
import { qaCoverageIdSchema } from "./coverage-id.js";
import { resolveQaEvidenceEnvironment } from "./evidence-environment.js";
import { splitQaModelRef } from "./model-selection.js";
import { getQaProvider, type QaProviderMode } from "./providers/index.js";
@@ -19,7 +20,6 @@ const qaEvidenceStatusSchema = z.enum(["pass", "fail", "blocked", "skipped"]);
const nonEmptyStringSchema = z.string().trim().min(1);
const nullableStringSchema = nonEmptyStringSchema.nullable();
const qaEvidenceProfileIdSchema = nonEmptyStringSchema;
const qaEvidenceIdSchema = z.strictObject({ id: nonEmptyStringSchema });
const qaEvidenceProviderSchema = z.strictObject({
id: nonEmptyStringSchema,
@@ -82,7 +82,8 @@ const qaEvidenceRefSchema = z.strictObject({
path: nonEmptyStringSchema,
});
const qaEvidenceCoverageSchema = qaEvidenceIdSchema.extend({
const qaEvidenceCoverageSchema = z.strictObject({
id: qaCoverageIdSchema,
role: nonEmptyStringSchema,
});
+41 -28
View File
@@ -59,7 +59,13 @@ function flowContainsCall(value: unknown, callName: string): boolean {
}
describe("qa scenario catalog", () => {
const dottedCoverageIdPattern = /^[a-z0-9][a-z0-9-]*(?:\.[a-z0-9][a-z0-9-]*)+$/;
const twoPartCoverageIdPattern = /^[a-z0-9][a-z0-9-]*\.[a-z0-9][a-z0-9-]*$/;
const agentRuntime = "agent-runtime";
const browserUi = "control-ui";
const cli = "cli";
const codex = "openai";
const memory = "session-memory";
const otel = "observability";
it("keeps repo-backed scenarios YAML-only", () => {
expect(listScenarioMarkdownPaths()).toStrictEqual([]);
@@ -113,14 +119,15 @@ describe("qa scenario catalog", () => {
pack.scenarios.every(
(scenario) =>
(scenario.coverage?.primary ?? []).every((coverageId) =>
dottedCoverageIdPattern.test(coverageId),
twoPartCoverageIdPattern.test(coverageId),
) &&
(scenario.coverage?.secondary ?? []).every((coverageId) =>
dottedCoverageIdPattern.test(coverageId),
twoPartCoverageIdPattern.test(coverageId),
),
),
).toBe(true);
expect(readQaScenarioById("memory-recall").coverage?.primary).toContain("memory.recall");
const recall = readQaScenarioById("memory-recall");
expect(recall.coverage?.primary).toContain(`${memory}.memory-recall`);
});
it("exposes bootstrap data from the YAML pack", () => {
@@ -241,7 +248,7 @@ describe("qa scenario catalog", () => {
"sends a chat turn through the GUI and renders the final Gateway event",
);
expect(scenario.execution.flow).toBeUndefined();
expect(scenario.coverage?.primary).toContain("ui.control");
expect(scenario.coverage?.primary).toContain(`${browserUi}.gateway-hosted-ui-control`);
expect(otelSmoke.execution.kind).toBe("script");
if (otelSmoke.execution.kind !== "script") {
throw new Error(`expected script scenario, got ${otelSmoke.execution.kind}`);
@@ -252,24 +259,24 @@ describe("qa scenario catalog", () => {
"--logs-exporter",
"both",
]);
expect(otelSmoke.coverage?.secondary).not.toContain("harness.qa-lab");
expect(otelSmoke.coverage?.secondary).not.toContain(`${otel}.otlp-http-traces-qa-lab`);
});
it("loads helper-backed HTTP API scenarios as supporting taxonomy coverage", () => {
expect(readQaScenarioById("openai-compatible-chat-tools").coverage?.secondary).toStrictEqual([
"gateway.openai-compatible-apis",
"runtime.hosted-tool-use",
`${agentRuntime}.hosted-tool-use`,
]);
expect(readQaScenarioById("openai-web-search-minimal").coverage?.secondary).toEqual(
expect.arrayContaining([
"runtime.reasoning-and-cache-controls",
`${agentRuntime}.reasoning-and-cache-controls`,
"web-search.openai-native-web-search",
"plugins.web-search-and-fetch",
]),
);
expect(readQaScenarioById("openwebui-openai-compatible").coverage?.secondary).toEqual(
expect.arrayContaining(["gateway.openai-compatible-apis", "runtime.hosted-provider-turns"]),
);
const webuiCoverage = readQaScenarioById("openwebui-openai-compatible").coverage?.secondary;
expect(webuiCoverage).toContain("gateway.openai-compatible-apis");
expect(webuiCoverage).toContain(`${agentRuntime}.hosted-provider-turns`);
});
it("routes Docker runtime scenarios through the shared lane adapter", () => {
@@ -509,10 +516,13 @@ describe("qa scenario catalog", () => {
searchQuery?: string;
}
| undefined;
const claims = scenario.coverage;
expect(scenario.sourcePath).toBe("qa/scenarios/runtime/qa-bus-tool-trace-visibility.yaml");
expect(scenario.coverage?.primary).toContain("harness.tool-trace-visibility");
expect(scenario.coverage?.secondary ?? []).toStrictEqual(["runtime.qa-bus", "tools.trace"]);
expect(claims?.primary).toContain(`${otel}.telemetry-tool-trace-visibility`);
expect(claims?.secondary ?? []).toStrictEqual([
`${otel}.telemetry-qa-bus`,
`${otel}.telemetry-trace`,
]);
expect(config?.expectedToolName).toBe("exec");
expect(config?.expectedRedaction).toBe("[redacted]");
expect(config?.searchQuery).toBe("exec");
@@ -524,9 +534,8 @@ describe("qa scenario catalog", () => {
it("loads the opt-in update.run package self-upgrade script proof", () => {
const scenario = readQaScenarioById("update-run-package-self-upgrade");
expect(scenario.sourcePath).toBe("qa/scenarios/runtime/update-run-package-self-upgrade.yaml");
expect(scenario.coverage?.primary).toContain("runtime.update-run");
expect(scenario.coverage?.secondary).toContain("runtime.package-update");
expect(scenario.coverage?.primary).toEqual([`${cli}.update-status-and-rpc`]);
expect(scenario.coverage?.secondary).toEqual([`${cli}.managed-gateway-restart`]);
expect(scenario.execution.kind).toBe("script");
if (scenario.execution.kind !== "script") {
throw new Error(`expected script execution, got ${scenario.execution.kind}`);
@@ -582,7 +591,7 @@ describe("qa scenario catalog", () => {
it("loads Codex plugin lifecycle scenarios into the standard runtime tier", () => {
const coldInstall = readQaScenarioById("codex-plugin-cold-install");
expect(coldInstall.runtimeParityTier).toBe("standard");
expect(coldInstall.coverage?.primary).toContain("runtime.codex-plugin.lifecycle");
expect(coldInstall.coverage?.primary).toEqual(["plugins.lifecycle-hot-install"]);
expect(coldInstall.coverage?.secondary).toBeUndefined();
expect(coldInstall.execution.kind).toBe("script");
@@ -610,8 +619,8 @@ describe("qa scenario catalog", () => {
kind: "vitest",
path: "test/e2e/qa-lab/runtime/codex-auth-doctor-migration-product-proof.e2e.test.ts",
});
expect(scenario.coverage?.primary).toContain("runtime.doctor-repair");
expect(scenario.coverage?.secondary).toContain("runtime.codex-plugin.auth");
expect(scenario.coverage?.primary).toEqual([`${codex}.codex-oauth-profiles-doctor-repair`]);
expect(scenario.coverage?.secondary).toEqual([`${otel}.doctor-codex-plugin-auth`]);
});
it("routes the Codex mixed-profile row through the product-backed Vitest", () => {
@@ -623,8 +632,11 @@ describe("qa scenario catalog", () => {
kind: "vitest",
path: "test/e2e/qa-lab/runtime/codex-auth-product-proof.e2e.test.ts",
});
expect(scenario.coverage?.primary).toContain("runtime.codex-plugin.auth");
expect(scenario.coverage?.secondary).toContain("runtime.doctor-repair");
expect(scenario.coverage?.primary).toEqual([`${codex}.codex-oauth-profiles-codex-plugin-auth`]);
expect(scenario.coverage?.secondary).toEqual([
`${agentRuntime}.auth-profile-selection-provider-selection`,
`${codex}.codex-oauth-profiles-doctor-repair`,
]);
});
it("keeps the character eval scenario natural and task-shaped", () => {
@@ -1040,11 +1052,11 @@ describe("qa scenario catalog", () => {
| undefined;
const flow = JSON.stringify(scenario.execution.flow);
expect(scenario.sourcePath).toBe("qa/scenarios/memory/dreaming-shadow-trial-report.yaml");
expect(scenario.coverage?.primary).toContain("memory.dreaming");
expect(config?.prompt).toContain("Dreaming shadow trial report check");
expect(config?.reportName).toBe("dreaming-shadow-trial-report.md");
expect(config?.seededMemory).toBe("# Memory\n\n");
expect(scenario.coverage?.primary).toEqual([`${memory}.memory-files-dreaming`]);
expect(scenario.coverage?.secondary).toEqual([
`${memory}.memory-files-promotion`,
`${memory}.memory-files-artifact-safety`,
]);
expect(config?.expectedReportAll).toContain("verdict: helpful");
expect(config?.expectedReportAll).toContain("exact verification commands and remaining risk");
expect(config?.expectedReportAll).toContain("omits the exact command and remaining risk");
@@ -1058,7 +1070,8 @@ describe("qa scenario catalog", () => {
it("enables Telegram previews for channel streaming evidence", () => {
const scenario = readQaScenarioById("channel-message-flows");
expect(scenario.coverage?.primary).toContain("channels.streaming");
expect(scenario.coverage?.primary).toEqual([`${agentRuntime}.streaming-replies`]);
expect(scenario.coverage?.secondary).toEqual([`${agentRuntime}.streaming-replies-delivery`]);
expect(scenario.gatewayConfigPatch).toMatchObject({
channels: { telegram: { streaming: { mode: "partial" } } },
});
+1 -7
View File
@@ -3,6 +3,7 @@ import path from "node:path";
import YAML from "yaml";
import { z } from "zod";
import { isRepoRootRelativeRef } from "./cli-paths.js";
import { qaCoverageIdSchema } from "./coverage-id.js";
import { resolveQaRepoPath, type QaRepoPathKind } from "./repo-path.js";
import { qaScenarioModuleFlow } from "./scenario-module-flow.js";
@@ -121,13 +122,6 @@ const qaScenarioExecutionSchema = z.union([
qaTestFileScenarioExecutionSchema,
]);
const qaCoverageIdSchema = z
.string()
.trim()
.regex(/^[a-z0-9][a-z0-9-]*(?:\.[a-z0-9][a-z0-9-]*)+$/, {
message: "coverage ids must use lowercase dotted tokens",
});
const qaCoverageIdListSchema = z.array(qaCoverageIdSchema).min(1);
const qaScenarioCoverageSchema = z
@@ -82,14 +82,17 @@ async function buildQaProfileScorecardEvidence(params: {
}
describe("profile scorecard evidence", () => {
it("scores partial multi-id feature coverage by covered coverage IDs", async () => {
it("scores atomic feature coverage by its one exact coverage ID", async () => {
const category: QaScorecardCategoryCoverageReport = {
id: "surface.category",
taxonomySurfaceId: "surface",
taxonomyCategoryName: "Category",
inventoryStatus: "partial",
profiles: ["release"],
features: [{ name: "Multi-id feature", coverageIds: ["coverage.one", "coverage.two"] }],
features: [
{ name: "Covered feature", coverageIds: ["coverage.one"] },
{ name: "Missing feature", coverageIds: ["coverage.two"] },
],
coverageIds: ["coverage.one", "coverage.two"],
inventoriedCoverageIds: ["coverage.one"],
inventoryRefs: [],
@@ -117,11 +120,11 @@ describe("profile scorecard evidence", () => {
expect(scorecard.categoryReports[0]?.status).toBe("partial");
expect(scorecard.categoryReports[0]?.features).toMatchObject({
total: 1,
fulfilled: 0,
partial: 1,
missing: 0,
fulfillmentPercent: 0,
total: 2,
fulfilled: 1,
partial: 0,
missing: 1,
fulfillmentPercent: 50,
});
expect(scorecard.categoryReports[0]?.coverageIds).toMatchObject({
total: 2,
@@ -137,11 +140,11 @@ describe("profile scorecard evidence", () => {
fulfillmentPercent: 50,
});
expect(scorecard.features).toMatchObject({
total: 1,
fulfilled: 0,
partial: 1,
missing: 0,
fulfillmentPercent: 0,
total: 2,
fulfilled: 1,
partial: 0,
missing: 1,
fulfillmentPercent: 50,
});
});
+49 -8
View File
@@ -3,6 +3,7 @@ import fs from "node:fs";
import path from "node:path";
import YAML from "yaml";
import { z } from "zod";
import { qaCoverageIdSchema } from "./coverage-id.js";
import { resolveQaRepoPath, type QaRepoPathKind } from "./repo-path.js";
import type { QaSeedScenarioWithSource } from "./scenario-catalog.js";
@@ -25,13 +26,6 @@ const qaScorecardIdSchema = z
message: "scorecard ids must use lowercase dotted or dashed tokens",
});
const qaCoverageIdSchema = z
.string()
.trim()
.regex(/^[a-z0-9][a-z0-9-]*(?:\.[a-z0-9][a-z0-9-]*)+$/, {
message: "coverage ids must use lowercase dotted tokens",
});
function isRepoRootRelativeRef(value: string) {
return !path.isAbsolute(value) && value.split(/[\\/]+/u).every((part) => part !== "..");
}
@@ -156,7 +150,9 @@ const qaMaturityScoresSchema = z.strictObject({
const qaMaturityFeatureSchema = z.object({
name: z.string().trim().min(1),
coverageIds: z.array(qaCoverageIdSchema).default([]),
coverageIds: z
.array(qaCoverageIdSchema)
.length(1, { message: "taxonomy features must define exactly one coverage ID" }),
description: z.string().trim().min(1).optional(),
});
@@ -255,6 +251,7 @@ const qaMaturityTaxonomySchema = z
}
const categoryIds = new Set<string>();
const coverageIdOwners = new Map<string, { key: string; label: string }>();
const surfaceIds = new Set<string>();
for (const [surfaceIndex, surface] of taxonomy.surfaces.entries()) {
if (surfaceIds.has(surface.id)) {
@@ -277,6 +274,50 @@ const qaMaturityTaxonomySchema = z
}
localCategoryIds.add(category.id);
categoryIds.add(`${surface.id}.${category.id}`);
for (const [featureIndex, feature] of category.features.entries()) {
const featureOwner = {
key: `${surfaceIndex}.${categoryIndex}.${featureIndex}`,
label: `${surface.id}.${category.id} feature ${feature.name}`,
};
for (const [coverageIdIndex, coverageId] of feature.coverageIds.entries()) {
if (!coverageId.startsWith(`${surface.id}.`)) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
path: [
"surfaces",
surfaceIndex,
"categories",
categoryIndex,
"features",
featureIndex,
"coverageIds",
coverageIdIndex,
],
message: `coverage ID ${coverageId} must belong to surface ${surface.id}`,
});
}
const existingOwner = coverageIdOwners.get(coverageId);
if (existingOwner && existingOwner.key !== featureOwner.key) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
path: [
"surfaces",
surfaceIndex,
"categories",
categoryIndex,
"features",
featureIndex,
"coverageIds",
coverageIdIndex,
],
message: `coverage ID ${coverageId} already belongs to ${existingOwner.label}; coverage IDs must identify exactly one taxonomy feature`,
});
continue;
}
coverageIdOwners.set(coverageId, featureOwner);
}
}
}
}
@@ -60,10 +60,7 @@ function makeTestFileScenario(
id: `scenario-${executionKind}`,
title: `${executionKind} scenario`,
surface: executionKind === "playwright" ? "control-ui" : "qa-lab",
category:
executionKind === "playwright"
? "browser-control-ui-and-webchat.browser-ui"
: "qa-lab.coverage",
category: executionKind === "playwright" ? "control-ui.browser-ui" : "qa-lab.coverage",
coverage: {
primary: [executionKind === "playwright" ? "ui.control" : "qa.coverage"],
secondary: [executionKind === "playwright" ? "ui.streaming" : "qa.reporting"],
@@ -448,9 +445,13 @@ describe("qa test file scenario runner", () => {
},
coverage: [
{
id: "ui.control",
id: "qa.coverage",
role: "primary",
},
{
id: "qa.reporting",
role: "secondary",
},
],
execution: {
runner: "evidence-producer-script",
@@ -742,6 +743,10 @@ describe("qa test file scenario runner", () => {
kind: "script-producer-check",
id: "script-producer.web-ui.smoke",
},
coverage: [
{ id: "qa.coverage", role: "primary" },
{ id: "qa.reporting", role: "secondary" },
],
result: {
status: "fail",
failure: {
@@ -192,6 +192,20 @@ function buildScenarioEvidenceTarget(scenario: QaTestFileScenario) {
};
}
function coverageForScenario(scenario: QaTestFileScenario) {
return [
...(scenario.coverage?.primary ?? []).map((id) => ({ id, role: "primary" as const })),
...(scenario.coverage?.secondary ?? []).map((id) => ({ id, role: "secondary" as const })),
];
}
function withScenarioCoverage(
entry: QaEvidenceSummaryJson["entries"][number],
scenario: QaTestFileScenario,
) {
return { ...entry, coverage: coverageForScenario(scenario) };
}
async function runScenarioCommandSteps(params: {
commandTimeoutMs: number;
env: NodeJS.ProcessEnv;
@@ -341,8 +355,12 @@ function buildTestFileEvidence(params: {
evidenceMode?: QaScorecardEvidenceMode;
env?: NodeJS.ProcessEnv;
}) {
const producerEntries = params.results.flatMap(
(result) => result.producerEvidence?.entries ?? [],
const producerEntries = params.results.flatMap((result) =>
// Producer artifacts own execution facts; the scenario catalog remains the
// sole owner of which semantic features those facts cover.
(result.producerEvidence?.entries ?? []).map((entry) =>
withScenarioCoverage(entry, result.scenario),
),
);
if (producerEntries.length > 0) {
const definition = testFileRunnerDefinitions[params.kind];
@@ -6,8 +6,7 @@ import {
renderQaToolCoverageMarkdownReport,
} from "./tool-coverage-report.js";
const TEST_TOOL_COVERAGE_ID =
"agent-runtime-and-provider-execution.tool-calls-and-response-handling.tool-call-handling";
const TEST_TOOL_COVERAGE_ID = "agents.tool-call-handling";
function makeScenario(
id: string,
+41 -41
View File
@@ -19,7 +19,7 @@ rollups:
score: 72
label: Beta
surfaces:
- id: gateway-runtime
- id: gateway
name: Gateway runtime
family: core
level:
@@ -187,7 +187,7 @@ surfaces:
supported: true
reason: human_override
human_override: true
- id: cli-install-update-onboard-doctor
- id: cli
name: CLI
family: core
level:
@@ -289,7 +289,7 @@ surfaces:
supported: true
reason: human_override
human_override: true
- id: plugin-sdk-and-bundled-plugin-architecture
- id: plugins
name: Plugins
family: core
level:
@@ -413,7 +413,7 @@ surfaces:
supported: false
reason: none
human_override: false
- id: agent-runtime-and-provider-execution
- id: agent-runtime
name: Agent Runtime
family: core
level:
@@ -537,7 +537,7 @@ surfaces:
supported: true
reason: human_override
human_override: true
- id: session-memory-and-context-engine
- id: session-memory
name: Session, memory, and context engine
family: core
level:
@@ -661,7 +661,7 @@ surfaces:
supported: true
reason: human_override
human_override: true
- id: channel-framework
- id: channels
name: Channel framework
family: core
level:
@@ -774,7 +774,7 @@ surfaces:
supported: true
reason: human_override
human_override: true
- id: security-auth-pairing-and-secrets
- id: security
name: Security, auth, pairing, and secrets
family: core
level:
@@ -865,7 +865,7 @@ surfaces:
supported: true
reason: human_override
human_override: true
- id: telemetry-diagnostics-and-observability
- id: observability
name: Observability
family: core
level:
@@ -945,7 +945,7 @@ surfaces:
supported: true
reason: human_override
human_override: true
- id: automation-cron-hooks-tasks-polling
- id: automation
name: "Automation: cron, hooks, tasks, polling"
family: core
level:
@@ -1036,7 +1036,7 @@ surfaces:
supported: false
reason: none
human_override: false
- id: media-understanding-and-media-generation
- id: media
name: Media understanding and media generation
family: core
level:
@@ -1127,7 +1127,7 @@ surfaces:
supported: false
reason: none
human_override: false
- id: voice-and-realtime-talk
- id: voice
name: Voice and realtime talk
family: core
level:
@@ -1218,7 +1218,7 @@ surfaces:
supported: false
reason: none
human_override: false
- id: browser-control-ui-and-webchat
- id: control-ui
name: Gateway Web App
family: core
level:
@@ -1309,7 +1309,7 @@ surfaces:
supported: false
reason: none
human_override: false
- id: tui-and-terminal-ux
- id: tui
name: TUI
family: core
level:
@@ -1389,7 +1389,7 @@ surfaces:
supported: false
reason: none
human_override: false
- id: clawhub-and-external-plugin-distribution
- id: clawhub
name: ClawHub
family: core
level:
@@ -1458,7 +1458,7 @@ surfaces:
supported: false
reason: none
human_override: false
- id: openclaw-app-sdk
- id: app-sdk
name: OpenClaw App SDK
family: core
level:
@@ -1549,7 +1549,7 @@ surfaces:
supported: false
reason: none
human_override: false
- id: macos-gateway-host
- id: macos-host
name: macOS Gateway host
family: platform-app
level:
@@ -1651,7 +1651,7 @@ surfaces:
supported: false
reason: none
human_override: false
- id: macos-companion-app
- id: macos-app
name: macOS companion app
family: platform-app
level:
@@ -1764,7 +1764,7 @@ surfaces:
supported: false
reason: none
human_override: false
- id: linux-gateway-host
- id: linux-host
name: Linux Gateway host
family: platform-app
level:
@@ -1844,7 +1844,7 @@ surfaces:
supported: false
reason: none
human_override: false
- id: linux-companion-app
- id: linux-app
name: Linux companion app
family: platform-app
level:
@@ -1924,7 +1924,7 @@ surfaces:
supported: false
reason: none
human_override: false
- id: windows-via-wsl2
- id: wsl
name: Windows via WSL2
family: platform-app
level:
@@ -2015,7 +2015,7 @@ surfaces:
supported: false
reason: none
human_override: false
- id: native-windows-cli-and-gateway
- id: windows
name: Native Windows
family: platform-app
level:
@@ -2084,7 +2084,7 @@ surfaces:
supported: false
reason: none
human_override: false
- id: native-windows-companion-app
- id: windows-app
name: Native Windows companion app
family: platform-app
level:
@@ -2164,7 +2164,7 @@ surfaces:
supported: false
reason: none
human_override: false
- id: android-app
- id: android
name: Android app
family: platform-app
level:
@@ -2266,7 +2266,7 @@ surfaces:
supported: false
reason: none
human_override: false
- id: ios-app
- id: ios
name: iOS app
family: platform-app
level:
@@ -2379,7 +2379,7 @@ surfaces:
supported: false
reason: none
human_override: false
- id: watchos-companion-surfaces
- id: watchos
name: watchOS companion surfaces
family: platform-app
level:
@@ -2459,7 +2459,7 @@ surfaces:
supported: false
reason: none
human_override: false
- id: raspberry-pi-small-linux-devices
- id: small-linux
name: Raspberry Pi and small Linux devices
family: platform-app
level:
@@ -2528,7 +2528,7 @@ surfaces:
supported: false
reason: none
human_override: false
- id: docker-podman-hosting
- id: containers
name: Docker and Podman hosting
family: platform-app
level:
@@ -2597,7 +2597,7 @@ surfaces:
supported: false
reason: none
human_override: false
- id: kubernetes-hosting
- id: kubernetes
name: Kubernetes hosting
family: platform-app
level:
@@ -2666,7 +2666,7 @@ surfaces:
supported: false
reason: none
human_override: false
- id: nix-install-path
- id: nix
name: Nix install path
family: platform-app
level:
@@ -3488,7 +3488,7 @@ surfaces:
supported: false
reason: none
human_override: false
- id: mattermost-line-irc-nextcloud-talk-nostr-twitch-tlon-synology-chat
- id: community-channels
name: Mattermost, LINE, IRC, Nextcloud Talk, Nostr, Twitch, Tlon, Synology Chat
family: channel
level:
@@ -3557,7 +3557,7 @@ surfaces:
supported: false
reason: none
human_override: false
- id: feishu-qq-bot-wechat-yuanbao-zalo-zalo-personal-regional-channels
- id: regional-channels
name: Feishu, QQ Bot, WeChat, Yuanbao, Zalo, Zalo Personal, regional channels
family: channel
level:
@@ -3626,7 +3626,7 @@ surfaces:
supported: false
reason: none
human_override: false
- id: voice-call-channel
- id: voice-call
name: Voice Call channel
family: channel
level:
@@ -3706,7 +3706,7 @@ surfaces:
supported: false
reason: none
human_override: false
- id: openai-codex-provider-path
- id: openai
name: OpenAI and Codex provider path
family: provider-tool
level:
@@ -3786,7 +3786,7 @@ surfaces:
supported: false
reason: none
human_override: false
- id: anthropic-provider-path
- id: anthropic
name: Anthropic provider path
family: provider-tool
level:
@@ -3866,7 +3866,7 @@ surfaces:
supported: false
reason: none
human_override: false
- id: google-provider-path
- id: google-ai
name: Google provider path
family: provider-tool
level:
@@ -3946,7 +3946,7 @@ surfaces:
supported: false
reason: none
human_override: false
- id: openrouter-provider-path
- id: openrouter
name: OpenRouter provider path
family: provider-tool
level:
@@ -4015,7 +4015,7 @@ surfaces:
supported: false
reason: none
human_override: false
- id: local-model-providers-ollama-vllm-sglang-lm-studio
- id: local-models
name: "Local model providers: Ollama, vLLM, SGLang, LM Studio"
family: provider-tool
level:
@@ -4095,7 +4095,7 @@ surfaces:
supported: false
reason: none
human_override: false
- id: long-tail-hosted-providers
- id: hosted-providers
name: Long-tail hosted providers
family: provider-tool
level:
@@ -4153,7 +4153,7 @@ surfaces:
supported: false
reason: none
human_override: false
- id: web-search-tools
- id: web-search
name: Web search tools
family: provider-tool
level:
@@ -4222,7 +4222,7 @@ surfaces:
supported: false
reason: none
human_override: false
- id: browser-automation-and-exec-sandbox-tools
- id: tools
name: Browser automation, exec, and sandbox tools
family: provider-tool
level:
@@ -4280,7 +4280,7 @@ surfaces:
supported: true
reason: human_override
human_override: true
- id: image-video-music-generation-tools
- id: media-generation
name: Image, video, and music generation tools
family: provider-tool
level:
@@ -5,9 +5,9 @@ scenario:
surface: repo-contract
coverage:
primary:
- agents.instructions
- session-memory.instruction-profile-instructions
secondary:
- runtime.first-action
- session-memory.instruction-profile-first-action
objective: Verify the agent reads repo instruction files first, follows the required tool order, and completes the first feasible action instead of stopping at a plan.
successCriteria:
- Agent reads the seeded instruction files before writing the requested artifact.
@@ -5,10 +5,11 @@ scenario:
surface: subagents
runtimeParityTier: live-only
coverage:
primary:
- agent-runtime.delegated-tool-access
secondary:
- agents.subagents
- runtime.delivery
- channels.qa-channel
- agent-runtime.subagent-turns-subagents
- agent-runtime.subagent-turns-delivery
gatewayConfigPatch:
tools:
toolsBySender:
@@ -5,9 +5,11 @@ scenario:
surface: subagents
runtimeParityTier: live-only
coverage:
primary:
- agent-runtime.delegated-tool-access
secondary:
- agents.subagents
- tools.fs.read
- agent-runtime.subagent-turns-subagents
- agent-runtime.tool-fs-read
gatewayConfigPatch:
tools:
toolsBySender:
@@ -5,10 +5,10 @@ scenario:
surface: subagents
coverage:
primary:
- agents.subagents
- agent-runtime.subagent-turns-subagents
secondary:
- runtime.delivery
- channels.qa-channel
- agent-runtime.subagent-turns-delivery
- channels.qa-channel-final-reply
objective: Verify a yielded parent still receives a successful subagent result through direct fallback delivery when the dormant announce turn produces no visible reply.
successCriteria:
- Parent launches a native subagent.
@@ -5,9 +5,9 @@ scenario:
surface: subagents
coverage:
primary:
- agents.subagents
- agent-runtime.subagent-turns-subagents
secondary:
- agents.synthesis
- agent-runtime.subagent-turns-synthesis
objective: Verify the agent can delegate multiple bounded subagent tasks and fold both results back into one parent reply.
successCriteria:
- Parent flow launches at least two bounded subagent tasks.
@@ -5,7 +5,7 @@ scenario:
surface: subagents
coverage:
primary:
- agents.subagents
- agent-runtime.subagent-turns-subagents
objective: Verify the agent can choose forked subagent context when the child needs the current transcript.
successCriteria:
- Agent launches a native subagent with context=fork.
+1 -1
View File
@@ -5,7 +5,7 @@ scenario:
surface: subagents
coverage:
primary:
- agents.subagents
- agent-runtime.subagent-turns-subagents
objective: Verify the agent can delegate a bounded task to a subagent and fold the result back into the main thread.
successCriteria:
- Agent launches a bounded subagent task.
@@ -5,9 +5,10 @@ scenario:
surface: subagents
coverage:
primary:
- agents.subagents
- gateway.session-apis-subagents
- agent-runtime.subagent-turns-subagents
secondary:
- gateway.sessions-list
- gateway.session-apis-sessions-list
objective: Verify restarted gateways hide stale persisted subagent child links without hiding live or fresh children.
successCriteria:
- Old ended subagent run records are not exposed as current children.
@@ -5,10 +5,10 @@ scenario:
surface: channel
coverage:
primary:
- runtime.delivery
- agent-runtime.subagent-turns-delivery
secondary:
- channels.qa-channel
- tools.message
- agent-runtime.subagent-turns-qa-channel
- channels.message-final-reply
objective: Verify a sessions_send A2A turn whose nested target run replies through message(action=send) delivers once to the requester channel and does not re-announce the delivery-mirror transcript row.
gatewayConfigPatch:
messages:
+3 -3
View File
@@ -5,10 +5,10 @@ scenario:
surface: channels
coverage:
primary:
- channels.group-messages
- channels.mention-required-group-visible-replies
secondary:
- channels.qa-channel
- runtime.delivery
- channels.qa-channel-final-reply
- channels.automatic-final-reply
objective: Verify the selected channel transport can deliver one mention-triggered group reply through the canonical QA host.
successCriteria:
- One mentioned group message produces one visible reply containing the exact marker.
@@ -5,9 +5,9 @@ scenario:
surface: channel
coverage:
primary:
- channels.group-messages
- channels.mention-gating
secondary:
- channels.qa-channel
- channels.mention-required-qa-channel
objective: Verify the QA agent can respond correctly in a shared channel and respect mention-driven group semantics.
successCriteria:
- Agent replies in the shared channel transcript.
@@ -5,10 +5,10 @@ scenario:
surface: channels
coverage:
primary:
- channels.dm
- channels.group-messages
- channels.inbound-routing-dm
- channels.runtime-conversation-routing
secondary:
- runtime.delivery
- channels.automatic-final-reply
objective: Verify direct and group replies stay on their originating conversation routes.
successCriteria:
- A direct message receives its marker only on the direct conversation.
@@ -5,9 +5,9 @@ scenario:
surface: channels
coverage:
primary:
- channels.group-messages
- channels.mention-gating
secondary:
- runtime.delivery
- channels.automatic-final-reply
objective: Verify required-mention group traffic stays quiet until the bot is explicitly mentioned.
successCriteria:
- Unmentioned group traffic produces no outbound reply.
@@ -5,9 +5,9 @@ scenario:
surface: channels
coverage:
primary:
- channels.streaming
- agent-runtime.streaming-replies
secondary:
- runtime.delivery
- agent-runtime.streaming-replies-delivery
objective: Verify streaming channel replies produce visible previews that resolve to one final answer.
gatewayConfigPatch:
channels:
@@ -5,9 +5,9 @@ scenario:
surface: channels
coverage:
primary:
- runtime.turn-ordering
- agent-runtime.session-turn-ordering
secondary:
- channels.group-messages
- channels.room-allowlist
objective: Verify blocked secondary-actor traffic does not poison a later allowlisted driver turn.
successCriteria:
- The observer turn remains unanswered.
@@ -5,9 +5,9 @@ scenario:
surface: channels
coverage:
primary:
- channels.group-messages
- channels.room-session-group-messages
secondary:
- runtime.turn-ordering
- agent-runtime.session-turn-ordering
objective: Verify a reply in a secondary conversation stays isolated from the primary conversation.
successCriteria:
- The primary conversation receives only its marker.
@@ -5,9 +5,9 @@ scenario:
surface: channels
coverage:
primary:
- channels.group-messages
- channels.room-allowlist
secondary:
- runtime.delivery
- channels.automatic-final-reply
objective: Verify a blocked secondary actor stays quiet while the configured driver override still triggers a reply.
successCriteria:
- A mentioned message from the observer actor produces no reply.
@@ -5,9 +5,9 @@ scenario:
surface: channels
coverage:
primary:
- channels.group-visible-replies
- channels.group-final-reply
secondary:
- runtime.delivery
- channels.automatic-final-reply
objective: Verify a top-level group prompt produces a top-level reply without thread or quote relation metadata.
successCriteria:
- The reply contains the exact marker.
+2 -1
View File
@@ -3,7 +3,8 @@ scenario:
id: discord-canary
surface: channels
coverage:
primary: [channels.group-messages]
primary:
- discord.configured-and-runtime-routing
execution:
profiles: { "discord:adapter": 0, "discord:default": 0 }
kind: flow
@@ -3,7 +3,8 @@ scenario:
id: discord-mention-gating
surface: channels
coverage:
primary: [channels.group-messages]
primary:
- discord.mention-gating
execution:
profiles: { "discord:adapter": 1, "discord:default": 1 }
kind: flow
@@ -3,7 +3,8 @@ scenario:
id: discord-status-reactions-tool-only
surface: channels
coverage:
primary: [channels.message-actions]
primary:
- channels.reply-message-actions
execution:
kind: flow
channel: discord
@@ -3,7 +3,8 @@ scenario:
id: discord-thread-reply-filepath-attachment
surface: channels
coverage:
primary: [channels.media-rich-content]
primary:
- discord.media-and-rich-content
execution:
kind: flow
channel: discord
+2 -2
View File
@@ -5,9 +5,9 @@ scenario:
surface: dm
coverage:
primary:
- channels.dm
- channels.inbound-routing-dm
secondary:
- channels.qa-channel
- channels.inbound-routing-qa-channel
objective: Verify the QA agent can chat coherently in a DM, explain the QA setup, and stay in character.
successCriteria:
- Agent replies in DM without channel routing mistakes.
@@ -5,8 +5,8 @@ scenario:
surface: dm
coverage:
primary:
- channels.dm
- memory.session-key-construction
- matrix.sender-bound-dm-routing
- channels.session-key-construction
objective: Verify Matrix dm.sessionScope=per-room isolates two DM rooms and suppresses the shared-session notice.
gatewayConfigPatch:
channels:
+2 -2
View File
@@ -5,8 +5,8 @@ scenario:
surface: dm
coverage:
primary:
- channels.dm
- memory.session-key-construction
- matrix.sender-bound-dm-routing
- channels.session-key-construction
objective: Verify the default Matrix per-user DM session emits a cross-room notice when the same sender moves to another DM room.
successCriteria:
- Both DM rooms receive top-level marker replies.
@@ -5,10 +5,10 @@ scenario:
surface: channel
coverage:
primary:
- channels.group-visible-replies
- channels.group-final-reply
secondary:
- channels.qa-channel
- tools.message
- channels.qa-channel-final-reply
- channels.message-final-reply
objective: Reproduce the group-visible-reply bug class where message_tool mode selected tool-only delivery even though group tool policy removed the message tool.
gatewayConfigPatch:
messages:
@@ -5,10 +5,10 @@ scenario:
surface: channel
coverage:
primary:
- channels.group-visible-replies
- channels.outbound-group-reply
secondary:
- channels.qa-channel
- tools.message
- channels.qa-channel-outbound
- channels.outbound-message
objective: Verify a group-sourced QA channel turn replies visibly through message(action=send) in the same room.
gatewayConfigPatch:
messages:
@@ -4,7 +4,7 @@ scenario:
surface: channels
coverage:
primary:
- channels.access-policy
- channels.room-allowlist-access-policy
execution:
profiles: { "matrix:all": 36, "matrix:fast": 8, "matrix:transport": 35 }
kind: flow
@@ -4,7 +4,7 @@ scenario:
surface: channels
coverage:
primary:
- channels.dm
- matrix.dm-policy
execution:
profiles: { "matrix:all": 40, "matrix:transport": 39 }
kind: flow
@@ -4,7 +4,7 @@ scenario:
surface: channels
coverage:
primary:
- channels.access-policy
- channels.room-allowlist-access-policy
execution:
profiles: { "matrix:all": 38, "matrix:fast": 9, "matrix:transport": 37 }
kind: flow
@@ -4,7 +4,7 @@ scenario:
surface: channels
coverage:
primary:
- channels.access-policy
- channels.room-allowlist-access-policy
execution:
profiles: { "matrix:all": 39, "matrix:transport": 38 }
kind: flow
@@ -4,7 +4,7 @@ scenario:
surface: channels
coverage:
primary:
- channels.access-policy
- channels.room-allowlist-access-policy
execution:
profiles: { "matrix:all": 41, "matrix:transport": 40 }
kind: flow
@@ -4,7 +4,7 @@ scenario:
surface: channels
coverage:
primary:
- channels.access-policy
- channels.room-allowlist-access-policy
execution:
profiles: { "matrix:all": 42, "matrix:transport": 41 }
kind: flow
@@ -4,7 +4,7 @@ scenario:
surface: channels
coverage:
primary:
- channels.access-policy
- channels.room-allowlist-access-policy
execution:
profiles: { "matrix:all": 43, "matrix:transport": 42 }
kind: flow
@@ -4,7 +4,7 @@ scenario:
surface: channels
coverage:
primary:
- channels.access-policy
- channels.room-allowlist-access-policy
execution:
profiles: { "matrix:all": 37, "matrix:transport": 36 }
kind: flow
@@ -4,7 +4,7 @@ scenario:
surface: channels
coverage:
primary:
- channels.native-approval-prompts
- channels.channel-native-approval-prompts
execution:
profiles: { "matrix:all": 30, "matrix:transport": 29 }
kind: flow
@@ -4,7 +4,7 @@ scenario:
surface: channels
coverage:
primary:
- channels.native-approval-prompts
- channels.channel-native-approval-prompts
execution:
profiles: { "matrix:all": 28, "matrix:transport": 27 }
kind: flow
@@ -4,7 +4,7 @@ scenario:
surface: channels
coverage:
primary:
- channels.native-approval-prompts
- channels.channel-native-approval-prompts
execution:
profiles: { "matrix:all": 26, "matrix:fast": 6, "matrix:transport": 25 }
kind: flow
@@ -4,7 +4,7 @@ scenario:
surface: channels
coverage:
primary:
- channels.native-approval-prompts
- channels.channel-native-approval-prompts
execution:
profiles: { "matrix:all": 25, "matrix:fast": 5, "matrix:transport": 24 }
kind: flow
@@ -4,7 +4,7 @@ scenario:
surface: channels
coverage:
primary:
- channels.native-approval-prompts
- channels.channel-native-approval-prompts
execution:
profiles: { "matrix:all": 27, "matrix:transport": 26 }
kind: flow
@@ -4,7 +4,7 @@ scenario:
surface: channels
coverage:
primary:
- channels.native-approval-prompts
- channels.channel-native-approval-prompts
execution:
profiles: { "matrix:all": 29, "matrix:transport": 28 }
kind: flow
@@ -4,7 +4,7 @@ scenario:
surface: channels
coverage:
primary:
- channels.mention-gating
- matrix.mention-gates
secondary:
- channels.inbound-media-normalization
execution:
@@ -4,9 +4,7 @@ scenario:
surface: channels
coverage:
primary:
- channels.threads
secondary:
- channels.dm
- matrix.matrix-thread-reply-routing
execution:
profiles: { "matrix:all": 16, "matrix:transport": 15 }
kind: flow
@@ -4,7 +4,7 @@ scenario:
surface: channels
coverage:
primary:
- channels.access-and-identity
- security.redaction-artifact-safety
execution:
profiles: { "matrix:all": 64, "matrix:e2ee-smoke": 6 }
kind: flow
@@ -4,7 +4,7 @@ scenario:
surface: channels
coverage:
primary:
- channels.access-and-identity
- matrix.encryption-setup
execution:
profiles: { "matrix:all": 58, "matrix:e2ee-smoke": 0, "matrix:fast": 11 }
kind: flow
@@ -4,7 +4,7 @@ scenario:
surface: channels
coverage:
primary:
- channels.access-and-identity
- matrix.encryption-setup
execution:
profiles: { "matrix:all": 60, "matrix:e2ee-smoke": 2 }
kind: flow
@@ -4,7 +4,7 @@ scenario:
surface: channels
coverage:
primary:
- channels.access-and-identity
- matrix.encryption-setup
execution:
profiles: { "matrix:all": 84, "matrix:e2ee-cli": 0 }
kind: flow
@@ -4,7 +4,7 @@ scenario:
surface: channels
coverage:
primary:
- channels.access-and-identity
- matrix.encryption-setup
execution:
profiles: { "matrix:all": 87, "matrix:e2ee-cli": 3 }
kind: flow
@@ -4,7 +4,7 @@ scenario:
surface: channels
coverage:
primary:
- channels.access-and-identity
- matrix.encryption-setup
execution:
profiles: { "matrix:all": 86, "matrix:e2ee-cli": 2 }
kind: flow
@@ -4,7 +4,7 @@ scenario:
surface: channels
coverage:
primary:
- channels.access-and-identity
- matrix.encryption-setup
execution:
profiles: { "matrix:all": 90, "matrix:e2ee-cli": 6 }
kind: flow
@@ -4,7 +4,7 @@ scenario:
surface: channels
coverage:
primary:
- channels.access-and-identity
- matrix.encryption-setup
execution:
profiles: { "matrix:all": 85, "matrix:e2ee-cli": 1 }
kind: flow
@@ -4,7 +4,7 @@ scenario:
surface: channels
coverage:
primary:
- channels.access-and-identity
- matrix.encryption-setup
execution:
profiles: { "matrix:all": 89, "matrix:e2ee-cli": 5 }
kind: flow
@@ -4,7 +4,7 @@ scenario:
surface: channels
coverage:
primary:
- channels.access-and-identity
- matrix.encryption-setup
execution:
profiles: { "matrix:all": 88, "matrix:e2ee-cli": 4 }
kind: flow
@@ -4,7 +4,7 @@ scenario:
surface: channels
coverage:
primary:
- channels.access-and-identity
- matrix.encryption-setup
execution:
profiles: { "matrix:all": 92, "matrix:e2ee-cli": 8 }
kind: flow
@@ -4,7 +4,7 @@ scenario:
surface: channels
coverage:
primary:
- channels.access-and-identity
- matrix.encryption-setup
execution:
profiles: { "matrix:all": 91, "matrix:e2ee-cli": 7 }
kind: flow
@@ -4,7 +4,7 @@ scenario:
surface: channels
coverage:
primary:
- channels.access-and-identity
- matrix.legacy-state
execution:
profiles: { "matrix:all": 73, "matrix:e2ee-deep": 7 }
kind: flow
@@ -4,7 +4,7 @@ scenario:
surface: channels
coverage:
primary:
- channels.access-and-identity
- matrix.encryption-setup
execution:
profiles: { "matrix:all": 78, "matrix:e2ee-deep": 12 }
kind: flow
@@ -4,7 +4,7 @@ scenario:
surface: channels
coverage:
primary:
- channels.dm
- matrix.encryption-setup
execution:
profiles: { "matrix:all": 81, "matrix:e2ee-deep": 15 }
kind: flow
@@ -4,7 +4,7 @@ scenario:
surface: channels
coverage:
primary:
- channels.access-and-identity
- matrix.encryption-setup
execution:
profiles: { "matrix:all": 77, "matrix:e2ee-deep": 11 }
kind: flow
@@ -4,7 +4,7 @@ scenario:
surface: channels
coverage:
primary:
- channels.access-and-identity
- matrix.encryption-setup
execution:
profiles: { "matrix:all": 65, "matrix:e2ee-smoke": 7 }
kind: flow
@@ -4,7 +4,7 @@ scenario:
surface: channels
coverage:
primary:
- channels.media-rich-content
- matrix.encrypted-media-upload-download
execution:
profiles: { "matrix:all": 57, "matrix:media": 6 }
kind: flow
@@ -4,7 +4,7 @@ scenario:
surface: channels
coverage:
primary:
- channels.access-and-identity
- matrix.encryption-setup
execution:
profiles: { "matrix:all": 79, "matrix:e2ee-deep": 13 }
kind: flow
@@ -4,7 +4,7 @@ scenario:
surface: channels
coverage:
primary:
- channels.access-and-identity
- matrix.encryption-setup
execution:
profiles: { "matrix:all": 61, "matrix:e2ee-smoke": 3 }
kind: flow
@@ -4,7 +4,7 @@ scenario:
surface: channels
coverage:
primary:
- channels.access-and-identity
- matrix.encryption-setup
execution:
profiles: { "matrix:all": 62, "matrix:e2ee-smoke": 4 }
kind: flow
@@ -4,7 +4,7 @@ scenario:
surface: channels
coverage:
primary:
- channels.reconnect
- matrix.legacy-state
execution:
profiles: { "matrix:all": 63, "matrix:e2ee-smoke": 5 }
kind: flow
@@ -4,7 +4,7 @@ scenario:
surface: channels
coverage:
primary:
- channels.access-and-identity
- matrix.encryption-setup
execution:
profiles: { "matrix:all": 72, "matrix:e2ee-deep": 6 }
kind: flow
@@ -4,7 +4,7 @@ scenario:
surface: channels
coverage:
primary:
- channels.access-and-identity
- matrix.encryption-setup
execution:
profiles: { "matrix:all": 71, "matrix:e2ee-deep": 5 }
kind: flow
@@ -4,7 +4,7 @@ scenario:
surface: channels
coverage:
primary:
- channels.access-and-identity
- matrix.encryption-setup
execution:
profiles: { "matrix:all": 74, "matrix:e2ee-deep": 8 }
kind: flow
@@ -4,7 +4,7 @@ scenario:
surface: channels
coverage:
primary:
- channels.access-and-identity
- matrix.encryption-setup
execution:
profiles: { "matrix:all": 75, "matrix:e2ee-deep": 9 }
kind: flow
@@ -4,7 +4,7 @@ scenario:
surface: channels
coverage:
primary:
- channels.access-and-identity
- matrix.encryption-setup
execution:
profiles: { "matrix:all": 80, "matrix:e2ee-deep": 14 }
kind: flow
@@ -4,7 +4,7 @@ scenario:
surface: channels
coverage:
primary:
- channels.access-and-identity
- matrix.encryption-setup
execution:
profiles: { "matrix:all": 70, "matrix:e2ee-deep": 4 }
kind: flow
@@ -4,7 +4,7 @@ scenario:
surface: channels
coverage:
primary:
- channels.access-and-identity
- matrix.legacy-state
execution:
profiles: { "matrix:all": 66, "matrix:e2ee-deep": 0 }
kind: flow
@@ -4,7 +4,7 @@ scenario:
surface: channels
coverage:
primary:
- channels.access-and-identity
- matrix.legacy-state
execution:
profiles: { "matrix:all": 67, "matrix:e2ee-deep": 1 }
kind: flow
@@ -4,7 +4,7 @@ scenario:
surface: channels
coverage:
primary:
- channels.access-and-identity
- matrix.legacy-state
execution:
profiles: { "matrix:all": 69, "matrix:e2ee-deep": 3 }
kind: flow
@@ -4,7 +4,7 @@ scenario:
surface: channels
coverage:
primary:
- channels.access-and-identity
- matrix.legacy-state
execution:
profiles: { "matrix:all": 68, "matrix:e2ee-deep": 2 }
kind: flow
@@ -4,7 +4,7 @@ scenario:
surface: channels
coverage:
primary:
- channels.reconnect
- matrix.legacy-state
execution:
profiles: { "matrix:all": 76, "matrix:e2ee-deep": 10 }
kind: flow
@@ -4,7 +4,7 @@ scenario:
surface: channels
coverage:
primary:
- channels.threads
- matrix.matrix-thread-reply-routing
execution:
profiles: { "matrix:all": 59, "matrix:e2ee-smoke": 1 }
kind: flow
@@ -4,7 +4,7 @@ scenario:
surface: channels
coverage:
primary:
- channels.access-and-identity
- matrix.encrypted-media-upload-download
execution:
profiles: { "matrix:all": 82, "matrix:e2ee-deep": 16 }
kind: flow
@@ -4,7 +4,7 @@ scenario:
surface: channels
coverage:
primary:
- channels.access-and-identity
- matrix.encryption-setup
execution:
profiles: { "matrix:all": 83, "matrix:e2ee-deep": 17 }
kind: flow
@@ -4,7 +4,7 @@ scenario:
surface: channels
coverage:
primary:
- channels.reconnect
- matrix.conversation-routing-and-delivery
execution:
profiles: { "matrix:all": 34, "matrix:transport": 33 }
kind: flow
@@ -4,7 +4,7 @@ scenario:
surface: channels
coverage:
primary:
- channels.message-actions
- matrix.message-send-read-edit-delete
execution:
profiles: { "matrix:all": 49, "matrix:transport": 48 }
kind: flow
@@ -4,7 +4,7 @@ scenario:
surface: channels
coverage:
primary:
- channels.message-actions
- matrix.message-send-read-edit-delete
execution:
profiles: { "matrix:all": 50, "matrix:transport": 49 }
kind: flow
@@ -4,7 +4,7 @@ scenario:
surface: channels
coverage:
primary:
- channels.reconnect
- matrix.conversation-routing-and-delivery
execution:
profiles: { "matrix:all": 31, "matrix:transport": 30 }
kind: flow
@@ -4,7 +4,7 @@ scenario:
surface: channels
coverage:
primary:
- channels.media-rich-content
- matrix.media-and-rich-content
execution:
profiles: { "matrix:all": 53, "matrix:media": 2 }
kind: flow
@@ -4,7 +4,7 @@ scenario:
surface: channels
coverage:
primary:
- channels.access-policy
- channels.room-allowlist-access-policy
execution:
profiles: { "matrix:all": 45, "matrix:transport": 44 }
kind: flow

Some files were not shown because too many files have changed in this diff Show More