Vendor OpenClaw source as Adolf fork baseline
Some checks failed
ClawSweeper Dispatch / dispatch (push) Has been cancelled
CodeQL / Security High (actions) (push) Has been cancelled
CodeQL / Security High (channel-runtime-boundary) (push) Has been cancelled
CodeQL / Security High (core-auth-secrets) (push) Has been cancelled
CodeQL / Security High (mcp-process-tool-boundary) (push) Has been cancelled
CodeQL / Security High (network-ssrf-boundary) (push) Has been cancelled
CodeQL / Security High (plugin-trust-boundary) (push) Has been cancelled
CodeQL / Security High (process-exec-boundary) (push) Has been cancelled
Docs Sync Publish Repo / sync-publish-repo (push) Has been cancelled
Docs / docs (push) Has been cancelled
OpenClaw Stable Main Closeout / Resolve stable release closeout inputs (push) Has been cancelled
OpenClaw Stable Main Closeout / Verify stable main closeout (push) Has been cancelled
Workflow Sanity / no-tabs (push) Has been cancelled
Workflow Sanity / actionlint (push) Has been cancelled
Workflow Sanity / generated-doc-baselines (push) Has been cancelled
CI / runner-admission (push) Has been cancelled
CI / preflight (push) Has been cancelled
CI / security-fast (push) Has been cancelled
CI / pnpm-store-warmup (push) Has been cancelled
CI / build-artifacts (push) Has been cancelled
CI / native-i18n (push) Has been cancelled
CI / ${{ matrix.check_name }} (push) Has been cancelled
CI / ${{ matrix.checkName }} (push) Has been cancelled
CI / checks-node-compat-node22 (push) Has been cancelled
CI / check-bundled-channel-config-metadata (push) Has been cancelled
CI / check-dependencies (push) Has been cancelled
CI / check-guards (push) Has been cancelled
CI / check-lint (push) Has been cancelled
CI / check-prod-types (push) Has been cancelled
CI / check-shrinkwrap (push) Has been cancelled
CI / check-test-types (push) Has been cancelled
CI / check-additional-boundaries-a (push) Has been cancelled
CI / check-additional-boundaries-bcd (push) Has been cancelled
CI / check-additional-extension-bundled (push) Has been cancelled
CI / check-additional-extension-channels (push) Has been cancelled
CI / check-additional-extension-package-boundary (push) Has been cancelled
CI / check-additional-runtime-topology-architecture (push) Has been cancelled
CI / check-session-accessor-boundary (push) Has been cancelled
CI / check-session-transcript-reader-boundary (push) Has been cancelled
CI / check-docs (push) Has been cancelled
CI / skills-python (push) Has been cancelled
CI / macos-swift (push) Has been cancelled
CI / ios-build (push) Has been cancelled
CI / ci-timings-summary (push) Has been cancelled
Native App Locale Refresh / Refresh native fa (push) Has been cancelled
Native App Locale Refresh / Refresh native fr (push) Has been cancelled
Native App Locale Refresh / Refresh native hi (push) Has been cancelled
Native App Locale Refresh / Refresh native id (push) Has been cancelled
Native App Locale Refresh / Refresh native it (push) Has been cancelled
Native App Locale Refresh / Refresh native ja-JP (push) Has been cancelled
Control UI Locale Refresh / plan (push) Has been cancelled
Control UI Locale Refresh / Refresh ${{ matrix.locale }} (push) Has been cancelled
Control UI Locale Refresh / Commit control UI locale refresh (push) Has been cancelled
Live Media Runner Image / Build live media runner image (push) Has been cancelled
Native App Locale Refresh / Refresh native ar (push) Has been cancelled
Native App Locale Refresh / Refresh native de (push) Has been cancelled
Native App Locale Refresh / Refresh native es (push) Has been cancelled
Native App Locale Refresh / Refresh native ko (push) Has been cancelled
Native App Locale Refresh / Refresh native nl (push) Has been cancelled
Native App Locale Refresh / Refresh native pl (push) Has been cancelled
Native App Locale Refresh / Refresh native pt-BR (push) Has been cancelled
Native App Locale Refresh / Refresh native ru (push) Has been cancelled
Native App Locale Refresh / Refresh native sv (push) Has been cancelled
Native App Locale Refresh / Refresh native th (push) Has been cancelled
Native App Locale Refresh / Refresh native tr (push) Has been cancelled
Native App Locale Refresh / Refresh native uk (push) Has been cancelled
Native App Locale Refresh / Refresh native vi (push) Has been cancelled
Native App Locale Refresh / Refresh native zh-CN (push) Has been cancelled
Native App Locale Refresh / Refresh native zh-TW (push) Has been cancelled
Native App Locale Refresh / Commit native locale refresh (push) Has been cancelled
Plugin Init Scaffold Validation / Validate provider scaffold (push) Has been cancelled
Plugin NPM Release / preview_plugins_npm (push) Has been cancelled
Plugin NPM Release / Validate release publish approval (push) Has been cancelled
Plugin NPM Release / preview_plugin_pack (push) Has been cancelled
Plugin NPM Release / publish_plugins_npm (push) Has been cancelled
Sandbox Common Smoke / sandbox-common-smoke (push) Has been cancelled
Website Installer Sync / static (push) Has been cancelled
Website Installer Sync / linux-docker (push) Has been cancelled
Website Installer Sync / macos-installer (push) Has been cancelled
Website Installer Sync / windows-installer (push) Has been cancelled
Website Installer Sync / sync-website (push) Has been cancelled
Some checks failed
ClawSweeper Dispatch / dispatch (push) Has been cancelled
CodeQL / Security High (actions) (push) Has been cancelled
CodeQL / Security High (channel-runtime-boundary) (push) Has been cancelled
CodeQL / Security High (core-auth-secrets) (push) Has been cancelled
CodeQL / Security High (mcp-process-tool-boundary) (push) Has been cancelled
CodeQL / Security High (network-ssrf-boundary) (push) Has been cancelled
CodeQL / Security High (plugin-trust-boundary) (push) Has been cancelled
CodeQL / Security High (process-exec-boundary) (push) Has been cancelled
Docs Sync Publish Repo / sync-publish-repo (push) Has been cancelled
Docs / docs (push) Has been cancelled
OpenClaw Stable Main Closeout / Resolve stable release closeout inputs (push) Has been cancelled
OpenClaw Stable Main Closeout / Verify stable main closeout (push) Has been cancelled
Workflow Sanity / no-tabs (push) Has been cancelled
Workflow Sanity / actionlint (push) Has been cancelled
Workflow Sanity / generated-doc-baselines (push) Has been cancelled
CI / runner-admission (push) Has been cancelled
CI / preflight (push) Has been cancelled
CI / security-fast (push) Has been cancelled
CI / pnpm-store-warmup (push) Has been cancelled
CI / build-artifacts (push) Has been cancelled
CI / native-i18n (push) Has been cancelled
CI / ${{ matrix.check_name }} (push) Has been cancelled
CI / ${{ matrix.checkName }} (push) Has been cancelled
CI / checks-node-compat-node22 (push) Has been cancelled
CI / check-bundled-channel-config-metadata (push) Has been cancelled
CI / check-dependencies (push) Has been cancelled
CI / check-guards (push) Has been cancelled
CI / check-lint (push) Has been cancelled
CI / check-prod-types (push) Has been cancelled
CI / check-shrinkwrap (push) Has been cancelled
CI / check-test-types (push) Has been cancelled
CI / check-additional-boundaries-a (push) Has been cancelled
CI / check-additional-boundaries-bcd (push) Has been cancelled
CI / check-additional-extension-bundled (push) Has been cancelled
CI / check-additional-extension-channels (push) Has been cancelled
CI / check-additional-extension-package-boundary (push) Has been cancelled
CI / check-additional-runtime-topology-architecture (push) Has been cancelled
CI / check-session-accessor-boundary (push) Has been cancelled
CI / check-session-transcript-reader-boundary (push) Has been cancelled
CI / check-docs (push) Has been cancelled
CI / skills-python (push) Has been cancelled
CI / macos-swift (push) Has been cancelled
CI / ios-build (push) Has been cancelled
CI / ci-timings-summary (push) Has been cancelled
Native App Locale Refresh / Refresh native fa (push) Has been cancelled
Native App Locale Refresh / Refresh native fr (push) Has been cancelled
Native App Locale Refresh / Refresh native hi (push) Has been cancelled
Native App Locale Refresh / Refresh native id (push) Has been cancelled
Native App Locale Refresh / Refresh native it (push) Has been cancelled
Native App Locale Refresh / Refresh native ja-JP (push) Has been cancelled
Control UI Locale Refresh / plan (push) Has been cancelled
Control UI Locale Refresh / Refresh ${{ matrix.locale }} (push) Has been cancelled
Control UI Locale Refresh / Commit control UI locale refresh (push) Has been cancelled
Live Media Runner Image / Build live media runner image (push) Has been cancelled
Native App Locale Refresh / Refresh native ar (push) Has been cancelled
Native App Locale Refresh / Refresh native de (push) Has been cancelled
Native App Locale Refresh / Refresh native es (push) Has been cancelled
Native App Locale Refresh / Refresh native ko (push) Has been cancelled
Native App Locale Refresh / Refresh native nl (push) Has been cancelled
Native App Locale Refresh / Refresh native pl (push) Has been cancelled
Native App Locale Refresh / Refresh native pt-BR (push) Has been cancelled
Native App Locale Refresh / Refresh native ru (push) Has been cancelled
Native App Locale Refresh / Refresh native sv (push) Has been cancelled
Native App Locale Refresh / Refresh native th (push) Has been cancelled
Native App Locale Refresh / Refresh native tr (push) Has been cancelled
Native App Locale Refresh / Refresh native uk (push) Has been cancelled
Native App Locale Refresh / Refresh native vi (push) Has been cancelled
Native App Locale Refresh / Refresh native zh-CN (push) Has been cancelled
Native App Locale Refresh / Refresh native zh-TW (push) Has been cancelled
Native App Locale Refresh / Commit native locale refresh (push) Has been cancelled
Plugin Init Scaffold Validation / Validate provider scaffold (push) Has been cancelled
Plugin NPM Release / preview_plugins_npm (push) Has been cancelled
Plugin NPM Release / Validate release publish approval (push) Has been cancelled
Plugin NPM Release / preview_plugin_pack (push) Has been cancelled
Plugin NPM Release / publish_plugins_npm (push) Has been cancelled
Sandbox Common Smoke / sandbox-common-smoke (push) Has been cancelled
Website Installer Sync / static (push) Has been cancelled
Website Installer Sync / linux-docker (push) Has been cancelled
Website Installer Sync / macos-installer (push) Has been cancelled
Website Installer Sync / windows-installer (push) Has been cancelled
Website Installer Sync / sync-website (push) Has been cancelled
Adolf is a fork/vendored clone of github.com/openclaw/openclaw (v2026.6.11), free to diverge. Tree copied sans upstream .git; upstream remote added for future syncs. Node pinned to 24 (.nvmrc); engines already require >=22.19. Preserves docs/ARCHITECTURE.md. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LeqyaxJF2nbRXJtae2kNB2
This commit is contained in:
553
extensions/qa-lab/src/coverage-report.ts
Normal file
553
extensions/qa-lab/src/coverage-report.ts
Normal file
@@ -0,0 +1,553 @@
|
||||
// Qa Lab plugin module implements coverage report behavior.
|
||||
import { normalizeStringEntriesLower } from "openclaw/plugin-sdk/string-coerce-runtime";
|
||||
import {
|
||||
buildLiveTransportCoverageLaneSummaries,
|
||||
type LiveTransportCoverageLaneSummary,
|
||||
} from "./live-transports/shared/live-transport-scenarios.js";
|
||||
import { QA_SCENARIO_PACKS, type QaSeedScenarioWithSource } from "./scenario-catalog.js";
|
||||
import {
|
||||
readQaScorecardTaxonomyReport,
|
||||
type QaScorecardTaxonomyReport,
|
||||
} from "./scorecard-taxonomy.js";
|
||||
|
||||
type QaCoverageScenarioSummary = {
|
||||
id: string;
|
||||
title: string;
|
||||
sourcePath: string;
|
||||
theme: string;
|
||||
surfaces: string[];
|
||||
risk: string;
|
||||
};
|
||||
|
||||
type QaScenarioSearchMatch = QaCoverageScenarioSummary & {
|
||||
coverageIds: string[];
|
||||
docsRefs: string[];
|
||||
codeRefs: string[];
|
||||
executionKind: QaSeedScenarioWithSource["execution"]["kind"];
|
||||
executionPath?: string;
|
||||
runtimeParityTier?: string;
|
||||
requiredProviderMode?: string;
|
||||
requiredChannelDriver?: string;
|
||||
requiredProvider?: string;
|
||||
requiredModel?: string;
|
||||
};
|
||||
|
||||
type QaCoverageIntent = "primary" | "secondary";
|
||||
|
||||
type QaCoverageScenarioReference = QaCoverageScenarioSummary & {
|
||||
intent: QaCoverageIntent;
|
||||
};
|
||||
|
||||
type QaCoverageIdSummary = {
|
||||
id: string;
|
||||
scenarios: QaCoverageScenarioReference[];
|
||||
};
|
||||
|
||||
type QaCoverageScenarioPackSummary = {
|
||||
id: string;
|
||||
title: string;
|
||||
scenarioIds: string[];
|
||||
coverageIds: string[];
|
||||
missingScenarioIds: string[];
|
||||
};
|
||||
|
||||
type QaCoverageInventory = {
|
||||
scenarioCount: number;
|
||||
coverageIdCount: number;
|
||||
primaryCoverageIdCount: number;
|
||||
secondaryCoverageIdCount: number;
|
||||
coverageIds: QaCoverageIdSummary[];
|
||||
overlappingCoverage: QaCoverageIdSummary[];
|
||||
missingCoverage: QaCoverageScenarioSummary[];
|
||||
byTheme: Record<string, QaCoverageIdSummary[]>;
|
||||
bySurface: Record<string, QaCoverageIdSummary[]>;
|
||||
scenarioPacks: QaCoverageScenarioPackSummary[];
|
||||
liveTransportLanes: LiveTransportCoverageLaneSummary[];
|
||||
scorecardTaxonomy: QaScorecardTaxonomyReport;
|
||||
};
|
||||
|
||||
function assertUniqueQaScenarioIds(
|
||||
scenarios: readonly QaSeedScenarioWithSource[],
|
||||
nonYamlScenarios: readonly { id: string; sourcePath: string }[],
|
||||
): void {
|
||||
const sourcePathsById = new Map<string, string[]>();
|
||||
for (const { id, sourcePath } of [...scenarios, ...nonYamlScenarios]) {
|
||||
const sourcePaths = sourcePathsById.get(id) ?? [];
|
||||
sourcePaths.push(sourcePath);
|
||||
sourcePathsById.set(id, sourcePaths);
|
||||
}
|
||||
const duplicates = [...sourcePathsById.entries()]
|
||||
.filter(([, sourcePaths]) => sourcePaths.length > 1)
|
||||
.toSorted(([left], [right]) => left.localeCompare(right));
|
||||
if (duplicates.length > 0) {
|
||||
const details = duplicates
|
||||
.map(([id, sourcePaths]) => `${id} (${sourcePaths.join(", ")})`)
|
||||
.join("; ");
|
||||
throw new Error(`duplicate qa scenario id(s): ${details}`);
|
||||
}
|
||||
}
|
||||
|
||||
function scenarioTheme(sourcePath: string) {
|
||||
const parts = sourcePath.split("/");
|
||||
return parts[2] ?? "unknown";
|
||||
}
|
||||
|
||||
function scenarioSurfaces(scenario: QaSeedScenarioWithSource) {
|
||||
return scenario.surfaces && scenario.surfaces.length > 0 ? scenario.surfaces : [scenario.surface];
|
||||
}
|
||||
|
||||
function scenarioRisk(scenario: QaSeedScenarioWithSource) {
|
||||
return scenario.risk ?? scenario.riskLevel ?? "unassigned";
|
||||
}
|
||||
|
||||
function summarizeScenario(scenario: QaSeedScenarioWithSource): QaCoverageScenarioSummary {
|
||||
return {
|
||||
id: scenario.id,
|
||||
title: scenario.title,
|
||||
sourcePath: scenario.sourcePath,
|
||||
theme: scenarioTheme(scenario.sourcePath),
|
||||
surfaces: scenarioSurfaces(scenario),
|
||||
risk: scenarioRisk(scenario),
|
||||
};
|
||||
}
|
||||
|
||||
function normalizeSearchText(value: string) {
|
||||
return value.toLowerCase();
|
||||
}
|
||||
|
||||
function tokenizeScenarioSearchQuery(query: string) {
|
||||
return normalizeStringEntriesLower(query.split(/\s+/u));
|
||||
}
|
||||
|
||||
function scenarioSearchText(scenario: QaSeedScenarioWithSource) {
|
||||
const config = scenario.execution.config ?? {};
|
||||
return normalizeSearchText(
|
||||
[
|
||||
scenario.id,
|
||||
scenario.title,
|
||||
scenario.sourcePath,
|
||||
scenario.surface,
|
||||
...(scenario.surfaces ?? []),
|
||||
scenario.category ?? "",
|
||||
scenario.runtimeParityTier ?? "",
|
||||
scenario.risk ?? "",
|
||||
scenario.riskLevel ?? "",
|
||||
scenario.objective,
|
||||
...scenario.successCriteria,
|
||||
...(scenario.capabilities ?? []),
|
||||
...(scenario.plugins ?? []),
|
||||
...(scenario.docsRefs ?? []),
|
||||
...(scenario.codeRefs ?? []),
|
||||
...(scenario.coverage?.primary ?? []),
|
||||
...(scenario.coverage?.secondary ?? []),
|
||||
...Object.entries(config).flatMap(([key, value]) => [
|
||||
key,
|
||||
typeof value === "string" ? value : "",
|
||||
]),
|
||||
].join("\n"),
|
||||
);
|
||||
}
|
||||
|
||||
function stringifyConfigValue(value: unknown) {
|
||||
return typeof value === "string" && value.trim() ? value.trim() : undefined;
|
||||
}
|
||||
|
||||
function summarizeScenarioSearchMatch(scenario: QaSeedScenarioWithSource): QaScenarioSearchMatch {
|
||||
const config = scenario.execution.config ?? {};
|
||||
return {
|
||||
...summarizeScenario(scenario),
|
||||
coverageIds: [
|
||||
...(scenario.coverage?.primary ?? []),
|
||||
...(scenario.coverage?.secondary ?? []),
|
||||
].toSorted((left, right) => left.localeCompare(right)),
|
||||
docsRefs: [...(scenario.docsRefs ?? [])],
|
||||
codeRefs: [...(scenario.codeRefs ?? [])],
|
||||
executionKind: scenario.execution.kind,
|
||||
...(scenario.execution.kind !== "flow" ? { executionPath: scenario.execution.path } : {}),
|
||||
runtimeParityTier: scenario.runtimeParityTier,
|
||||
requiredProviderMode: stringifyConfigValue(config.requiredProviderMode),
|
||||
requiredChannelDriver: stringifyConfigValue(config.requiredChannelDriver),
|
||||
requiredProvider: stringifyConfigValue(config.requiredProvider),
|
||||
requiredModel: stringifyConfigValue(config.requiredModel),
|
||||
};
|
||||
}
|
||||
|
||||
export function findQaScenarioMatches(
|
||||
scenarios: readonly QaSeedScenarioWithSource[],
|
||||
query: string,
|
||||
) {
|
||||
const tokens = tokenizeScenarioSearchQuery(query);
|
||||
if (tokens.length === 0) {
|
||||
return [];
|
||||
}
|
||||
return scenarios
|
||||
.filter((scenario) => {
|
||||
const haystack = scenarioSearchText(scenario);
|
||||
return tokens.every((token) => haystack.includes(token));
|
||||
})
|
||||
.map(summarizeScenarioSearchMatch)
|
||||
.toSorted((left, right) => left.id.localeCompare(right.id));
|
||||
}
|
||||
|
||||
function sortCoverageIds(coverageIds: readonly QaCoverageIdSummary[]) {
|
||||
return coverageIds.toSorted((left, right) => left.id.localeCompare(right.id));
|
||||
}
|
||||
|
||||
function buildScenarioPackSummaries(
|
||||
scenarios: readonly QaSeedScenarioWithSource[],
|
||||
): QaCoverageScenarioPackSummary[] {
|
||||
const scenariosById = new Map(scenarios.map((scenario) => [scenario.id, scenario]));
|
||||
return QA_SCENARIO_PACKS.map((pack) => {
|
||||
const coverageIds = new Set<string>();
|
||||
const missingScenarioIds: string[] = [];
|
||||
for (const scenarioId of pack.scenarioIds) {
|
||||
const scenario = scenariosById.get(scenarioId);
|
||||
if (!scenario) {
|
||||
missingScenarioIds.push(scenarioId);
|
||||
continue;
|
||||
}
|
||||
for (const coverageId of [
|
||||
...(scenario.coverage?.primary ?? []),
|
||||
...(scenario.coverage?.secondary ?? []),
|
||||
]) {
|
||||
coverageIds.add(coverageId);
|
||||
}
|
||||
}
|
||||
return {
|
||||
id: pack.id,
|
||||
title: pack.title,
|
||||
scenarioIds: [...pack.scenarioIds],
|
||||
coverageIds: [...coverageIds].toSorted(),
|
||||
missingScenarioIds,
|
||||
};
|
||||
}).toSorted((left, right) => left.id.localeCompare(right.id));
|
||||
}
|
||||
|
||||
export function buildQaCoverageInventory(
|
||||
scenarios: readonly QaSeedScenarioWithSource[],
|
||||
params?: { nonYamlScenarios?: readonly { id: string; sourcePath: string }[] },
|
||||
): QaCoverageInventory {
|
||||
assertUniqueQaScenarioIds(scenarios, params?.nonYamlScenarios ?? []);
|
||||
const byCoverageId = new Map<string, QaCoverageIdSummary>();
|
||||
const primaryCoverageIds = new Set<string>();
|
||||
const secondaryCoverageIds = new Set<string>();
|
||||
const missingCoverage: QaCoverageScenarioSummary[] = [];
|
||||
|
||||
const addFeatureCoverage = (
|
||||
scenario: QaSeedScenarioWithSource,
|
||||
coverageIds: readonly string[] | undefined,
|
||||
intent: QaCoverageIntent,
|
||||
) => {
|
||||
const summary = summarizeScenario(scenario);
|
||||
for (const coverageId of coverageIds ?? []) {
|
||||
const coverage = byCoverageId.get(coverageId) ?? {
|
||||
id: coverageId,
|
||||
scenarios: [],
|
||||
};
|
||||
coverage.scenarios.push({ ...summary, intent });
|
||||
byCoverageId.set(coverageId, coverage);
|
||||
if (intent === "primary") {
|
||||
primaryCoverageIds.add(coverageId);
|
||||
} else {
|
||||
secondaryCoverageIds.add(coverageId);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
for (const scenario of scenarios) {
|
||||
if (!scenario.coverage) {
|
||||
missingCoverage.push(summarizeScenario(scenario));
|
||||
continue;
|
||||
}
|
||||
addFeatureCoverage(scenario, scenario.coverage.primary, "primary");
|
||||
addFeatureCoverage(scenario, scenario.coverage.secondary, "secondary");
|
||||
}
|
||||
|
||||
const coverageIds = sortCoverageIds([...byCoverageId.values()]);
|
||||
const overlappingCoverage = coverageIds.filter((coverage) => coverage.scenarios.length > 1);
|
||||
const byTheme: Record<string, QaCoverageIdSummary[]> = {};
|
||||
const bySurface: Record<string, QaCoverageIdSummary[]> = {};
|
||||
|
||||
for (const coverage of coverageIds) {
|
||||
const themes = new Set(coverage.scenarios.map((scenario) => scenario.theme));
|
||||
for (const theme of themes) {
|
||||
byTheme[theme] ??= [];
|
||||
byTheme[theme].push({
|
||||
...coverage,
|
||||
scenarios: coverage.scenarios.filter((scenario) => scenario.theme === theme),
|
||||
});
|
||||
}
|
||||
const surfaces = new Set(coverage.scenarios.flatMap((scenario) => scenario.surfaces));
|
||||
for (const surface of surfaces) {
|
||||
bySurface[surface] ??= [];
|
||||
bySurface[surface].push({
|
||||
...coverage,
|
||||
scenarios: coverage.scenarios.filter((scenario) => scenario.surfaces.includes(surface)),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
scenarioCount: scenarios.length,
|
||||
coverageIdCount: coverageIds.length,
|
||||
primaryCoverageIdCount: primaryCoverageIds.size,
|
||||
secondaryCoverageIdCount: secondaryCoverageIds.size,
|
||||
coverageIds,
|
||||
overlappingCoverage,
|
||||
missingCoverage,
|
||||
byTheme,
|
||||
bySurface,
|
||||
scenarioPacks: buildScenarioPackSummaries(scenarios),
|
||||
liveTransportLanes: buildLiveTransportCoverageLaneSummaries(),
|
||||
scorecardTaxonomy: readQaScorecardTaxonomyReport(scenarios),
|
||||
};
|
||||
}
|
||||
|
||||
function pushCoverageIdLines(lines: string[], coverageIds: readonly QaCoverageIdSummary[]) {
|
||||
for (const coverage of sortCoverageIds(coverageIds)) {
|
||||
const scenarios = coverage.scenarios
|
||||
.map((scenario) => `${scenario.intent}: ${scenario.id} (${scenario.sourcePath})`)
|
||||
.join(", ");
|
||||
lines.push(`- ${coverage.id}: ${scenarios}`);
|
||||
}
|
||||
}
|
||||
|
||||
function pushLiveTransportLines(
|
||||
lines: string[],
|
||||
lanes: readonly LiveTransportCoverageLaneSummary[],
|
||||
) {
|
||||
for (const lane of lanes) {
|
||||
const members = lane.members
|
||||
.map((member) =>
|
||||
member.scenarioId
|
||||
? `${member.standardId}: ${member.scenarioId}`
|
||||
: `${member.standardId}: always-on`,
|
||||
)
|
||||
.join(", ");
|
||||
const missing =
|
||||
lane.baselineMissingStandardScenarioIds.length > 0
|
||||
? lane.baselineMissingStandardScenarioIds.join(", ")
|
||||
: "none";
|
||||
lines.push(
|
||||
`- ${lane.transportId} (${lane.commandName}): ${members}; missing baseline: ${missing}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function pushScenarioPackLines(lines: string[], packs: readonly QaCoverageScenarioPackSummary[]) {
|
||||
for (const pack of packs) {
|
||||
const missing =
|
||||
pack.missingScenarioIds.length > 0 ? pack.missingScenarioIds.join(", ") : "none";
|
||||
lines.push(
|
||||
`- ${pack.id} (${pack.title}): ${pack.scenarioIds.length} scenarios; coverage IDs: ${pack.coverageIds.join(", ")}; missing scenarios: ${missing}`,
|
||||
);
|
||||
lines.push(` - scenarios: ${pack.scenarioIds.join(", ")}`);
|
||||
}
|
||||
}
|
||||
|
||||
function pushScorecardTaxonomyLines(lines: string[], report: QaScorecardTaxonomyReport) {
|
||||
lines.push("## Scorecard Taxonomy", "");
|
||||
lines.push(`- Taxonomy: ${report.taxonomyPath ?? "missing"}`);
|
||||
lines.push(`- Categories: ${report.categoryCount}`);
|
||||
lines.push(`- Profiles: ${report.profileCount}`);
|
||||
lines.push(
|
||||
`- Fulfilled taxonomy categories: ${report.fulfilledCategoryCount}/${report.requiredCategoryCount} (${report.categoryFulfillmentPercent}%)`,
|
||||
);
|
||||
lines.push(
|
||||
`- Fulfilled taxonomy coverage IDs: ${report.fulfilledCoverageIdCount}/${report.requiredCoverageIdCount} (${report.coverageIdFulfillmentPercent}%)`,
|
||||
);
|
||||
lines.push(`- Evidence refs: ${report.evidenceRefCount}`);
|
||||
lines.push(`- Scenario coverage IDs: ${report.scenarioCoverageIdCount}`);
|
||||
lines.push(`- Unknown scenario coverage IDs: ${report.unknownCoverageIdCount}`);
|
||||
lines.push(`- Validation warnings: ${report.validationIssueCount}`, "");
|
||||
|
||||
if (report.profiles.length > 0) {
|
||||
lines.push("### Profiles", "");
|
||||
for (const profile of report.profiles) {
|
||||
const categories = profile.categoryIds.length > 0 ? profile.categoryIds.join(", ") : "none";
|
||||
lines.push(`- ${profile.id}: ${profile.categoryIds.length} categories; ${categories}`);
|
||||
}
|
||||
lines.push("");
|
||||
}
|
||||
|
||||
if (report.categories.length > 0) {
|
||||
lines.push("### Category Coverage", "");
|
||||
for (const category of report.categories) {
|
||||
const coverageIds =
|
||||
category.coverageIds.length > 0 ? category.coverageIds.join(", ") : "none";
|
||||
const evidence =
|
||||
category.evidence.length > 0
|
||||
? category.evidence
|
||||
.map((ref) => {
|
||||
const target = ref.path ?? (ref.scenarioRefs.join("|") || "discovered");
|
||||
return `${ref.role}:${ref.kind}:${target} (${ref.coverageId})`;
|
||||
})
|
||||
.join(", ")
|
||||
: "none";
|
||||
const profiles = category.profiles.length > 0 ? category.profiles.join(", ") : "none";
|
||||
lines.push(
|
||||
`- ${category.id} (${category.taxonomySurfaceId} / ${category.taxonomyCategoryName}; ${category.coverageStatus}): profiles: ${profiles}; coverage IDs: ${coverageIds}; evidence: ${evidence}`,
|
||||
);
|
||||
}
|
||||
lines.push("");
|
||||
}
|
||||
|
||||
if (report.validationIssues.length > 0) {
|
||||
lines.push("### Validation Warnings", "");
|
||||
for (const issue of report.validationIssues) {
|
||||
const category = issue.categoryId ? `${issue.categoryId}: ` : "";
|
||||
lines.push(`- ${issue.code}: ${category}${issue.message}`);
|
||||
}
|
||||
lines.push("");
|
||||
}
|
||||
|
||||
if (report.unknownCoverageIds.length > 0) {
|
||||
lines.push("### Unknown Scenario Coverage IDs", "");
|
||||
lines.push(report.unknownCoverageIds.join(", "));
|
||||
lines.push("");
|
||||
}
|
||||
}
|
||||
|
||||
export function renderQaCoverageMarkdownReport(inventory: QaCoverageInventory): string {
|
||||
const lines: string[] = [
|
||||
"# QA Coverage Inventory",
|
||||
"",
|
||||
`- Scenarios: ${inventory.scenarioCount}`,
|
||||
`- Taxonomy coverage IDs: ${inventory.coverageIdCount}`,
|
||||
`- Primary coverage IDs: ${inventory.primaryCoverageIdCount}`,
|
||||
`- Secondary coverage IDs: ${inventory.secondaryCoverageIdCount}`,
|
||||
`- Overlapping coverage IDs: ${inventory.overlappingCoverage.length}`,
|
||||
`- Missing coverage metadata: ${inventory.missingCoverage.length}`,
|
||||
"",
|
||||
];
|
||||
|
||||
if (inventory.scenarioPacks.length > 0) {
|
||||
lines.push("## Scenario Packs", "");
|
||||
pushScenarioPackLines(lines, inventory.scenarioPacks);
|
||||
lines.push("");
|
||||
}
|
||||
|
||||
lines.push("## By Theme", "");
|
||||
for (const theme of Object.keys(inventory.byTheme).toSorted()) {
|
||||
lines.push(`### ${theme}`, "");
|
||||
pushCoverageIdLines(lines, inventory.byTheme[theme] ?? []);
|
||||
lines.push("");
|
||||
}
|
||||
|
||||
lines.push("## By Surface", "");
|
||||
for (const surface of Object.keys(inventory.bySurface).toSorted()) {
|
||||
lines.push(`### ${surface}`, "");
|
||||
pushCoverageIdLines(lines, inventory.bySurface[surface] ?? []);
|
||||
lines.push("");
|
||||
}
|
||||
|
||||
if (inventory.liveTransportLanes.length > 0) {
|
||||
lines.push("## Live Transport Lanes", "");
|
||||
pushLiveTransportLines(lines, inventory.liveTransportLanes);
|
||||
lines.push("");
|
||||
}
|
||||
|
||||
pushScorecardTaxonomyLines(lines, inventory.scorecardTaxonomy);
|
||||
|
||||
if (inventory.overlappingCoverage.length > 0) {
|
||||
lines.push("## Overlap", "");
|
||||
pushCoverageIdLines(lines, inventory.overlappingCoverage);
|
||||
lines.push("");
|
||||
}
|
||||
|
||||
if (inventory.missingCoverage.length > 0) {
|
||||
lines.push("## Missing Metadata", "");
|
||||
for (const scenario of inventory.missingCoverage.toSorted((left, right) =>
|
||||
left.id.localeCompare(right.id),
|
||||
)) {
|
||||
lines.push(`- ${scenario.id}: ${scenario.sourcePath}`);
|
||||
}
|
||||
lines.push("");
|
||||
}
|
||||
|
||||
return `${lines.join("\n").trimEnd()}\n`;
|
||||
}
|
||||
|
||||
function formatOptionalScenarioMetadata(match: QaScenarioSearchMatch) {
|
||||
const metadata = [
|
||||
match.runtimeParityTier ? `runtimeParityTier=${match.runtimeParityTier}` : "",
|
||||
match.requiredProviderMode ? `providerMode=${match.requiredProviderMode}` : "",
|
||||
match.requiredChannelDriver ? `channelDriver=${match.requiredChannelDriver}` : "",
|
||||
match.requiredProvider ? `provider=${match.requiredProvider}` : "",
|
||||
match.requiredModel ? `model=${match.requiredModel}` : "",
|
||||
].filter(Boolean);
|
||||
return metadata.length > 0 ? metadata.join("; ") : "none";
|
||||
}
|
||||
|
||||
function formatSuiteCommand(matches: readonly QaScenarioSearchMatch[]) {
|
||||
const scenarioArgs = matches.map((match) => `--scenario ${match.id}`).join(" ");
|
||||
return `pnpm openclaw qa suite ${scenarioArgs}`;
|
||||
}
|
||||
|
||||
function scenarioMatchCommandGroups(matches: readonly QaScenarioSearchMatch[]) {
|
||||
const groups = new Map<QaScenarioSearchMatch["executionKind"], QaScenarioSearchMatch[]>();
|
||||
for (const match of matches) {
|
||||
const group = groups.get(match.executionKind) ?? [];
|
||||
group.push(match);
|
||||
groups.set(match.executionKind, group);
|
||||
}
|
||||
const executionOrder: QaScenarioSearchMatch["executionKind"][] = [
|
||||
"flow",
|
||||
"script",
|
||||
"vitest",
|
||||
"playwright",
|
||||
];
|
||||
return executionOrder.flatMap((executionKind) => {
|
||||
const group = groups.get(executionKind);
|
||||
return group && group.length > 0 ? [{ executionKind, matches: group }] : [];
|
||||
});
|
||||
}
|
||||
|
||||
export function renderQaScenarioMatchesMarkdownReport(params: {
|
||||
query: string;
|
||||
matches: readonly QaScenarioSearchMatch[];
|
||||
}) {
|
||||
const commandGroups = scenarioMatchCommandGroups(params.matches);
|
||||
const lines = [
|
||||
"# QA Scenario Matches",
|
||||
"",
|
||||
`- Query: ${params.query}`,
|
||||
`- Matches: ${params.matches.length}`,
|
||||
];
|
||||
|
||||
if (commandGroups.length === 1) {
|
||||
lines.push(`- Suite command: \`${formatSuiteCommand(commandGroups[0].matches)}\``);
|
||||
} else if (commandGroups.length > 1) {
|
||||
lines.push("- Suite commands:");
|
||||
for (const group of commandGroups) {
|
||||
lines.push(` - ${group.executionKind}: \`${formatSuiteCommand(group.matches)}\``);
|
||||
}
|
||||
}
|
||||
lines.push("");
|
||||
|
||||
if (params.matches.length === 0) {
|
||||
lines.push("No QA scenarios matched the query.", "");
|
||||
return lines.join("\n");
|
||||
}
|
||||
|
||||
for (const match of params.matches) {
|
||||
lines.push(`- ${match.id}: ${match.title}`);
|
||||
lines.push(` - source: ${match.sourcePath}`);
|
||||
lines.push(` - surface: ${match.surfaces.join(", ")}`);
|
||||
lines.push(
|
||||
match.executionKind === "flow"
|
||||
? " - execution: flow"
|
||||
: ` - execution: ${match.executionKind} ${match.executionPath ?? "missing"}`,
|
||||
);
|
||||
lines.push(` - coverage IDs: ${match.coverageIds.join(", ") || "none"}`);
|
||||
lines.push(` - live requirements: ${formatOptionalScenarioMetadata(match)}`);
|
||||
if (match.codeRefs.length > 0) {
|
||||
lines.push(` - code refs: ${match.codeRefs.join(", ")}`);
|
||||
}
|
||||
if (match.docsRefs.length > 0) {
|
||||
lines.push(` - docs refs: ${match.docsRefs.join(", ")}`);
|
||||
}
|
||||
}
|
||||
|
||||
return `${lines.join("\n").trimEnd()}\n`;
|
||||
}
|
||||
Reference in New Issue
Block a user