mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-12 21:53:00 -06:00
refactor(qa): make taxonomy coverage ids unique (#110280)
This commit is contained in:
@@ -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)))
|
||||
|
||||
@@ -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");
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -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", []],
|
||||
|
||||
@@ -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>",
|
||||
});
|
||||
@@ -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", () => {
|
||||
|
||||
@@ -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,
|
||||
});
|
||||
|
||||
|
||||
@@ -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" } } },
|
||||
});
|
||||
|
||||
@@ -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,
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -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
@@ -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.
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
+1
-1
@@ -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
Reference in New Issue
Block a user