惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

T
Threatpost
IT之家
IT之家
Hugging Face - Blog
Hugging Face - Blog
Engineering at Meta
Engineering at Meta
爱范儿
爱范儿
博客园 - Franky
博客园 - 【当耐特】
MyScale Blog
MyScale Blog
雷峰网
雷峰网
月光博客
月光博客
云风的 BLOG
云风的 BLOG
博客园 - 司徒正美
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
P
Proofpoint News Feed
The GitHub Blog
The GitHub Blog
N
Netflix TechBlog - Medium
WordPress大学
WordPress大学
罗磊的独立博客
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
H
Hackread – Cybersecurity News, Data Breaches, AI and More
Y
Y Combinator Blog
Know Your Adversary
Know Your Adversary
宝玉的分享
宝玉的分享
L
Lohrmann on Cybersecurity
S
SegmentFault 最新的问题
L
LangChain Blog
K
Kaspersky official blog
P
Palo Alto Networks Blog
P
Privacy & Cybersecurity Law Blog
美团技术团队
Scott Helme
Scott Helme
B
Blog RSS Feed
T
Threat Research - Cisco Blogs
博客园_首页
L
LINUX DO - 热门话题
腾讯CDC
C
CERT Recently Published Vulnerability Notes
A
About on SuperTechFans
博客园 - 三生石上(FineUI控件)
J
Java Code Geeks
V
V2EX
Martin Fowler
Martin Fowler
T
The Exploit Database - CXSecurity.com
人人都是产品经理
人人都是产品经理
MongoDB | Blog
MongoDB | Blog
Latest news
Latest news
S
Schneier on Security
AWS News Blog
AWS News Blog

Recent Commits to openclaw:main

test: merge chat side-result checks · openclaw/openclaw@ddd2c2a test: merge cron history checks · openclaw/openclaw@f7eb746 test: merge responsive navigation shell checks · openclaw/openclaw@c2e4b47 docs(changelog): add codex oauth fixes · openclaw/openclaw@628e6cd test: merge navigation routing cases · openclaw/openclaw@5d8cecb Tests: mock channel registry bundled fallback · openclaw/openclaw@2b08233 Secrets: avoid broad web search discovery for single plugin config · openclaw/openclaw@a464f59 test: merge config view browser checks · openclaw/openclaw@20cf511 fix(status): align oauth health with runtime · openclaw/openclaw@eed7116 feat: add macOS screen snapshots for monitor preview (#67954) thanks … · openclaw/openclaw@f377db1 fix: report shared auth scopes in hello-ok (#67810) thanks @BunsDev · openclaw/openclaw@0b6c39b Auto-reply: avoid eager bundled route fallback · openclaw/openclaw@3ea1bf4 Tests: narrow session binding contract setup · openclaw/openclaw@54e4e16 fix(macOS): enable undo/redo in webchat composer text input (#34962) · openclaw/openclaw@00951dc Tests: speed up channel setup promotion · openclaw/openclaw@82b529a Docs: refresh agent instructions · openclaw/openclaw@5775fe2 fix(auth): serialize OAuth refresh across agents to fix #26322 (#67876) · openclaw/openclaw@8e79080 test: allow ollama public surface boundary test · openclaw/openclaw@7d4f1a6 Docs: add test performance guardrails · openclaw/openclaw@89706d3 Tests: restore context-engine usage proof · openclaw/openclaw@e4c4f95 Tests: slim context engine runtime coverage · openclaw/openclaw@74c198f ci: retry failed custom checkouts · openclaw/openclaw@0ee5baf test: trim duplicate provider auth onboarding cases · openclaw/openclaw@1ffc02e matrix: fix sessions_spawn --thread subagent session spawning (#67643) · openclaw/openclaw@1ce2596 test: reduce auth choice fixture churn · openclaw/openclaw@857b9cd test: mock health status config boundaries · openclaw/openclaw@9d5ab4a test: mock onboard config io boundary · openclaw/openclaw@299694d test: mock legacy state plugin boundaries · openclaw/openclaw@2713089 test: mock channel install boundaries · openclaw/openclaw@b945248 test: mock doctor preview channel boundaries · openclaw/openclaw@b1a3ad4 test: trim doctor command hotspots · openclaw/openclaw@c66f16a test: isolate agent auth and spawn hotspots · openclaw/openclaw@9285935 test: stabilize MCP startup disposal race · openclaw/openclaw@dd9d2eb test: merge browser contract server suites · openclaw/openclaw@5817a76 test: narrow ollama provider discovery setup · openclaw/openclaw@a0d9598 build: declare qa-lab aimock runtime dependency · openclaw/openclaw@24431e5 test: speed up safe-bins exec harness · openclaw/openclaw@ee856ab test: preserve tool helpers in embedded runner mocks · openclaw/openclaw@acd86a0 refactor: move memory embeddings into provider plugins · openclaw/openclaw@77e6e4c test: reuse system-run temp fixtures · openclaw/openclaw@7e9ff0f test: trim hotspot wait overhead · openclaw/openclaw@12a59b0 Check: avoid duplicate boundary prep · openclaw/openclaw@baf11b8 test: reduce hotspot fixture overhead · openclaw/openclaw@3a59edd feat(ui): overhaul settings and slash command UX (#67819) thanks @Bun… · openclaw/openclaw@2cfb660 QA Matrix: exit cleanly on failure · openclaw/openclaw@42805d2 QA Matrix: isolate scenario coverage · openclaw/openclaw@7e659e1 Matrix: refresh crypto bootstrap state · openclaw/openclaw@94081d8 QA Lab: add provider registry · openclaw/openclaw@bb7e982 Matrix: add plugin changelog · openclaw/openclaw@4acab55 test: trim more hotspot overhead · openclaw/openclaw@f485311 test: trim remaining hotspot tests · openclaw/openclaw@6ba8626 test: narrow hotspot mocks · openclaw/openclaw@dbc8179 test: isolate gemini embedding request helpers · openclaw/openclaw@cd330f5 test: trim memory and mcp hotspots · openclaw/openclaw@fd48dfa test: slim provider registry mocks · openclaw/openclaw@2e08c77 test: harden Parallels update smoke · openclaw/openclaw@1a98090 feat: default Anthropic to Opus 4.7 · openclaw/openclaw@628b454 fix: harden node-host shell payload mutability checks · openclaw/openclaw@75c551e fix: land node-host approval binding for native binaries (#66731) (th… · openclaw/openclaw@29919bb CI: add daily schedule to CodeQL workflow (#67645) · openclaw/openclaw@69d25f5 fix(gateway): capture config hash after plugin auto-enable to prevent… · openclaw/openclaw@8c11210 fix: repair sanitized replay tool results before send (#67620) (thank… · openclaw/openclaw@c3c7a99 fix: restrict HTML timeout short-circuit to transient statuses · openclaw/openclaw@de129a6 fix: keep TUI watchdog bound to active run (#67401) (thanks @xantorres) · openclaw/openclaw@3525273 Gateway/skills: dedupe skills prefix-match + drop dead fallback on log · openclaw/openclaw@d7f489f Extensions/lmstudio: back off inference preload after consecutive fai… · openclaw/openclaw@b555214 TUI/streaming: add watchdog that resets the activity indicator after … · openclaw/openclaw@f44ab20 Agents/tool-loop: enable unknown-tool stream guard by default · openclaw/openclaw@36ed367 Gateway/skills: invalidate session skills snapshot on config write · openclaw/openclaw@b23d59a fix: classify HTML provider error pages correctly (#67642) (thanks @s… · openclaw/openclaw@e588e90 fix(skills): remove unused model-usage import (#67641) · openclaw/openclaw@55f05df docs(changelog): credit codex fix superseded PRs · openclaw/openclaw@e485f24 fix(openai-codex): normalize stale transport metadata in resolution a… · openclaw/openclaw@90801ba CI: pin Docker-related GitHub Actions (#67632) · openclaw/openclaw@f697b01 Android: modernize WebView and discovery API usage (#67627) · openclaw/openclaw@44a6e50 fix(deps): bump hono to 4.12.14 and @hono/node-server to 1.19.14 (GHS… · openclaw/openclaw@fbccc18 fix(deps): bump dompurify to 3.4.0 (#67614) · openclaw/openclaw@2c2dc00 CI: add explicit permissions to all workflow jobs (fixes code-scannin… · openclaw/openclaw@01b7516 fix: register bundled TTS providers and route overrides correctly (#6… · openclaw/openclaw@6ea3cdd fix: align host tilde paths with OS home (#62804) (thanks @stainlu) · openclaw/openclaw@ecfaf64 fix: flush creds queue before reconnect socket open (#67464) (thanks … · openclaw/openclaw@405c63f fix: strip standalone <function> tool call tags from visible text (#6… · openclaw/openclaw@78df859 fix(agents): preserve cli session metadata before transcript persist … · openclaw/openclaw@898fd04 docs(changelog): move cli transcript entry · openclaw/openclaw@c1817c6 fix(agents): normalize cli transcript api field · openclaw/openclaw@3a3fae0 docs(changelog): note cli transcript persistence · openclaw/openclaw@6c343f1 fix(agents): persist cli transcript turns · openclaw/openclaw@b8ef507 fix(msteams): harden security-sensitive flows (#65841) · openclaw/openclaw@c56b56e [Dashboard] Fix exec approval modal overflow for long command content… · openclaw/openclaw@053c5b0 Docs: remove QA changelog entry · openclaw/openclaw@7fd5771 QA: fix private runtime source loading (#67428) · openclaw/openclaw@d5933af docs(gateway): correct protocol.md schema path, hello-ok example, aut… · openclaw/openclaw@489404d CI: pin Node 22 runners to 22.18.0 · openclaw/openclaw@4ffa621 models.authStatus: normalize provider ids + tighten env-backed escape… · openclaw/openclaw@f2fdb9d Update CHANGELOG.md · openclaw/openclaw@7694a92 test(parallels): clean up npm update guard jobs · openclaw/openclaw@045ea7b Plugins: prefer scanDir override paths · openclaw/openclaw@b2974da fix(dreaming): default storage.mode to "separate" so phase blocks sto… · openclaw/openclaw@8c392f0 fix(memory-core): skip dreaming transcript ingestion via session stor… · openclaw/openclaw@a1b01f0 fix: dedupe replayed exec.finished node events (#67281) · openclaw/openclaw@5dcf526
Simplify QA scorecard mapping shape (#92558) · openclaw/openclaw@a3e9dfe
RomneyDa · 2026-06-14 · via Recent Commits to openclaw:main

@@ -37,7 +37,7 @@ type QaCoverageScenarioReference = QaCoverageScenarioSummary & {

3737

intent: QaCoverageIntent;

3838

};

393940-

type QaCoverageFeatureSummary = {

40+

type QaCoverageIdSummary = {

4141

id: string;

4242

scenarios: QaCoverageScenarioReference[];

4343

};

@@ -55,11 +55,11 @@ type QaCoverageInventory = {

5555

coverageIdCount: number;

5656

primaryCoverageIdCount: number;

5757

secondaryCoverageIdCount: number;

58-

features: QaCoverageFeatureSummary[];

59-

overlappingCoverage: QaCoverageFeatureSummary[];

58+

coverageIds: QaCoverageIdSummary[];

59+

overlappingCoverage: QaCoverageIdSummary[];

6060

missingCoverage: QaCoverageScenarioSummary[];

61-

byTheme: Record<string, QaCoverageFeatureSummary[]>;

62-

bySurface: Record<string, QaCoverageFeatureSummary[]>;

61+

byTheme: Record<string, QaCoverageIdSummary[]>;

62+

bySurface: Record<string, QaCoverageIdSummary[]>;

6363

scenarioPacks: QaCoverageScenarioPackSummary[];

6464

liveTransportLanes: LiveTransportCoverageLaneSummary[];

6565

scorecardTaxonomy: QaScorecardTaxonomyReport;

@@ -166,8 +166,8 @@ export function findQaScenarioMatches(

166166

.toSorted((left, right) => left.id.localeCompare(right.id));

167167

}

168168169-

function sortFeatures(features: readonly QaCoverageFeatureSummary[]) {

170-

return features.toSorted((left, right) => left.id.localeCompare(right.id));

169+

function sortCoverageIds(coverageIds: readonly QaCoverageIdSummary[]) {

170+

return coverageIds.toSorted((left, right) => left.id.localeCompare(right.id));

171171

}

172172173173

function buildScenarioPackSummaries(

@@ -203,24 +203,24 @@ function buildScenarioPackSummaries(

203203

export function buildQaCoverageInventory(

204204

scenarios: readonly QaSeedScenarioWithSource[],

205205

): QaCoverageInventory {

206-

const byCoverageId = new Map<string, QaCoverageFeatureSummary>();

206+

const byCoverageId = new Map<string, QaCoverageIdSummary>();

207207

const primaryCoverageIds = new Set<string>();

208208

const secondaryCoverageIds = new Set<string>();

209209

const missingCoverage: QaCoverageScenarioSummary[] = [];

210210211-

const addCoverage = (

211+

const addFeatureCoverage = (

212212

scenario: QaSeedScenarioWithSource,

213213

coverageIds: readonly string[] | undefined,

214214

intent: QaCoverageIntent,

215215

) => {

216216

const summary = summarizeScenario(scenario);

217217

for (const coverageId of coverageIds ?? []) {

218-

const feature = byCoverageId.get(coverageId) ?? {

218+

const coverage = byCoverageId.get(coverageId) ?? {

219219

id: coverageId,

220220

scenarios: [],

221221

};

222-

feature.scenarios.push({ ...summary, intent });

223-

byCoverageId.set(coverageId, feature);

222+

coverage.scenarios.push({ ...summary, intent });

223+

byCoverageId.set(coverageId, coverage);

224224

if (intent === "primary") {

225225

primaryCoverageIds.add(coverageId);

226226

} else {

@@ -234,40 +234,40 @@ export function buildQaCoverageInventory(

234234

missingCoverage.push(summarizeScenario(scenario));

235235

continue;

236236

}

237-

addCoverage(scenario, scenario.coverage.primary, "primary");

238-

addCoverage(scenario, scenario.coverage.secondary, "secondary");

237+

addFeatureCoverage(scenario, scenario.coverage.primary, "primary");

238+

addFeatureCoverage(scenario, scenario.coverage.secondary, "secondary");

239239

}

240240241-

const features = sortFeatures([...byCoverageId.values()]);

242-

const overlappingCoverage = features.filter((feature) => feature.scenarios.length > 1);

243-

const byTheme: Record<string, QaCoverageFeatureSummary[]> = {};

244-

const bySurface: Record<string, QaCoverageFeatureSummary[]> = {};

241+

const coverageIds = sortCoverageIds([...byCoverageId.values()]);

242+

const overlappingCoverage = coverageIds.filter((coverage) => coverage.scenarios.length > 1);

243+

const byTheme: Record<string, QaCoverageIdSummary[]> = {};

244+

const bySurface: Record<string, QaCoverageIdSummary[]> = {};

245245246-

for (const feature of features) {

247-

const themes = new Set(feature.scenarios.map((scenario) => scenario.theme));

246+

for (const coverage of coverageIds) {

247+

const themes = new Set(coverage.scenarios.map((scenario) => scenario.theme));

248248

for (const theme of themes) {

249249

byTheme[theme] ??= [];

250250

byTheme[theme].push({

251-

...feature,

252-

scenarios: feature.scenarios.filter((scenario) => scenario.theme === theme),

251+

...coverage,

252+

scenarios: coverage.scenarios.filter((scenario) => scenario.theme === theme),

253253

});

254254

}

255-

const surfaces = new Set(feature.scenarios.flatMap((scenario) => scenario.surfaces));

255+

const surfaces = new Set(coverage.scenarios.flatMap((scenario) => scenario.surfaces));

256256

for (const surface of surfaces) {

257257

bySurface[surface] ??= [];

258258

bySurface[surface].push({

259-

...feature,

260-

scenarios: feature.scenarios.filter((scenario) => scenario.surfaces.includes(surface)),

259+

...coverage,

260+

scenarios: coverage.scenarios.filter((scenario) => scenario.surfaces.includes(surface)),

261261

});

262262

}

263263

}

264264265265

return {

266266

scenarioCount: scenarios.length,

267-

coverageIdCount: features.length,

267+

coverageIdCount: coverageIds.length,

268268

primaryCoverageIdCount: primaryCoverageIds.size,

269269

secondaryCoverageIdCount: secondaryCoverageIds.size,

270-

features,

270+

coverageIds,

271271

overlappingCoverage,

272272

missingCoverage,

273273

byTheme,

@@ -278,12 +278,12 @@ export function buildQaCoverageInventory(

278278

};

279279

}

280280281-

function pushFeatureLines(lines: string[], features: readonly QaCoverageFeatureSummary[]) {

282-

for (const feature of sortFeatures(features)) {

283-

const scenarios = feature.scenarios

281+

function pushCoverageIdLines(lines: string[], coverageIds: readonly QaCoverageIdSummary[]) {

282+

for (const coverage of sortCoverageIds(coverageIds)) {

283+

const scenarios = coverage.scenarios

284284

.map((scenario) => `${scenario.intent}: ${scenario.id} (${scenario.sourcePath})`)

285285

.join(", ");

286-

lines.push(`- ${feature.id}: ${scenarios}`);

286+

lines.push(`- ${coverage.id}: ${scenarios}`);

287287

}

288288

}

289289

@@ -314,28 +314,26 @@ function pushScenarioPackLines(lines: string[], packs: readonly QaCoverageScenar

314314

const missing =

315315

pack.missingScenarioIds.length > 0 ? pack.missingScenarioIds.join(", ") : "none";

316316

lines.push(

317-

`- ${pack.id} (${pack.title}): ${pack.scenarioIds.length} scenarios; coverage: ${pack.coverageIds.join(", ")}; missing scenarios: ${missing}`,

317+

`- ${pack.id} (${pack.title}): ${pack.scenarioIds.length} scenarios; coverage IDs: ${pack.coverageIds.join(", ")}; missing scenarios: ${missing}`,

318318

);

319319

lines.push(` - scenarios: ${pack.scenarioIds.join(", ")}`);

320320

}

321321

}

322322323323

function pushScorecardTaxonomyLines(lines: string[], report: QaScorecardTaxonomyReport) {

324324

lines.push("## Scorecard Taxonomy", "");

325-

lines.push(`- Mapping: ${report.taxonomyPath ?? "missing"}`);

326-

lines.push(`- Mapping ID: ${report.taxonomyId ?? "missing"}`);

327-

lines.push(`- Maturity taxonomy: ${report.taxonomy?.sourcePath ?? "missing"}`);

328-

if (report.scoreSnapshotRef) {

329-

lines.push(`- Maturity score snapshot: ${report.scoreSnapshotRef}`);

330-

}

325+

lines.push(`- Taxonomy: ${report.taxonomyPath ?? "missing"}`);

326+

lines.push(`- Categories: ${report.categoryCount}`);

327+

lines.push(`- Profiles: ${report.profileCount}`);

331328

lines.push(

332-

`- Categories: ${report.categoryCount} (${report.ltsIncludedCategoryCount} LTS-included, ${report.deferredCategoryCount} deferred, ${report.advisoryCategoryCount} advisory)`,

329+

`- Fulfilled taxonomy categories: ${report.fulfilledCategoryCount}/${report.requiredCategoryCount} (${report.categoryFulfillmentPercent}%)`,

333330

);

334-

lines.push(`- Profiles: ${report.profileCount}`);

335-

lines.push(`- Release-blocking categories: ${report.releaseBlockingCategoryCount}`);

336-

lines.push(`- Mapped coverage IDs: ${report.mappedCoverageIdCount}`);

337-

lines.push(`- Mapped scenarios: ${report.mappedScenarioCount}`);

338-

lines.push(`- Unmapped coverage IDs: ${report.unmappedCoverageIdCount}`);

331+

lines.push(

332+

`- Fulfilled taxonomy features: ${report.fulfilledFeatureCount}/${report.requiredFeatureCount} (${report.taxonomyFulfillmentPercent}%)`,

333+

);

334+

lines.push(`- Evidence refs: ${report.evidenceRefCount}`);

335+

lines.push(`- Scenario coverage IDs: ${report.scenarioCoverageIdCount}`);

336+

lines.push(`- Unmapped scenario coverage IDs: ${report.unmappedCoverageIdCount}`);

339337

lines.push(`- Validation warnings: ${report.validationIssueCount}`, "");

340338341339

if (report.profiles.length > 0) {

@@ -350,13 +348,20 @@ function pushScorecardTaxonomyLines(lines: string[], report: QaScorecardTaxonomy

350348

if (report.categories.length > 0) {

351349

lines.push("### Category Mapping", "");

352350

for (const category of report.categories) {

353-

const blocking = category.releaseBlocking ? "release-blocking" : "non-blocking";

354-

const coverage = category.coverageIds.length > 0 ? category.coverageIds.join(", ") : "none";

355-

const scenarios =

356-

category.scenarioRefs.length > 0 ? category.scenarioRefs.join(", ") : "none";

351+

const coverageIds =

352+

category.coverageIds.length > 0 ? category.coverageIds.join(", ") : "none";

353+

const evidence =

354+

category.evidence.length > 0

355+

? category.evidence

356+

.map((ref) => {

357+

const target = ref.path ?? (ref.scenarioRefs.join("|") || "discovered");

358+

return `${ref.role}:${ref.kind}:${target} (${ref.coverageId})`;

359+

})

360+

.join(", ")

361+

: "none";

357362

const profiles = category.profiles.length > 0 ? category.profiles.join(", ") : "none";

358363

lines.push(

359-

`- ${category.id} (${category.taxonomySurfaceId} / ${category.taxonomyCategoryName}; ${category.supportStatus}, ${blocking}, ${category.mappingStatus}): profiles: ${profiles}; coverage: ${coverage}; scenarios: ${scenarios}`,

364+

`- ${category.id} (${category.taxonomySurfaceId} / ${category.taxonomyCategoryName}; ${category.mappingStatus}): profiles: ${profiles}; coverage IDs: ${coverageIds}; evidence: ${evidence}`,

360365

);

361366

}

362367

lines.push("");

@@ -372,7 +377,7 @@ function pushScorecardTaxonomyLines(lines: string[], report: QaScorecardTaxonomy

372377

}

373378374379

if (report.unmappedCoverageIds.length > 0) {

375-

lines.push("### Unmapped Coverage IDs", "");

380+

lines.push("### Unmapped Scenario Coverage IDs", "");

376381

lines.push(report.unmappedCoverageIds.join(", "));

377382

lines.push("");

378383

}

@@ -383,7 +388,7 @@ export function renderQaCoverageMarkdownReport(inventory: QaCoverageInventory):

383388

"# QA Coverage Inventory",

384389

"",

385390

`- Scenarios: ${inventory.scenarioCount}`,

386-

`- Coverage IDs: ${inventory.coverageIdCount}`,

391+

`- Taxonomy coverage IDs: ${inventory.coverageIdCount}`,

387392

`- Primary coverage IDs: ${inventory.primaryCoverageIdCount}`,

388393

`- Secondary coverage IDs: ${inventory.secondaryCoverageIdCount}`,

389394

`- Overlapping coverage IDs: ${inventory.overlappingCoverage.length}`,

@@ -400,14 +405,14 @@ export function renderQaCoverageMarkdownReport(inventory: QaCoverageInventory):

400405

lines.push("## By Theme", "");

401406

for (const theme of Object.keys(inventory.byTheme).toSorted()) {

402407

lines.push(`### ${theme}`, "");

403-

pushFeatureLines(lines, inventory.byTheme[theme] ?? []);

408+

pushCoverageIdLines(lines, inventory.byTheme[theme] ?? []);

404409

lines.push("");

405410

}

406411407412

lines.push("## By Surface", "");

408413

for (const surface of Object.keys(inventory.bySurface).toSorted()) {

409414

lines.push(`### ${surface}`, "");

410-

pushFeatureLines(lines, inventory.bySurface[surface] ?? []);

415+

pushCoverageIdLines(lines, inventory.bySurface[surface] ?? []);

411416

lines.push("");

412417

}

413418

@@ -421,7 +426,7 @@ export function renderQaCoverageMarkdownReport(inventory: QaCoverageInventory):

421426422427

if (inventory.overlappingCoverage.length > 0) {

423428

lines.push("## Overlap", "");

424-

pushFeatureLines(lines, inventory.overlappingCoverage);

429+

pushCoverageIdLines(lines, inventory.overlappingCoverage);

425430

lines.push("");

426431

}

427432

@@ -456,31 +461,29 @@ function formatSuiteCommand(matches: readonly QaScenarioSearchMatch[]) {

456461

function scenarioMatchCommandGroups(matches: readonly QaScenarioSearchMatch[]) {

457462

const groups = new Map<QaScenarioSearchMatch["executionKind"], QaScenarioSearchMatch[]>();

458463

for (const match of matches) {

459-

const existing = groups.get(match.executionKind) ?? [];

460-

existing.push(match);

461-

groups.set(match.executionKind, existing);

464+

const group = groups.get(match.executionKind) ?? [];

465+

group.push(match);

466+

groups.set(match.executionKind, group);

462467

}

463468

const executionOrder: QaScenarioSearchMatch["executionKind"][] = ["flow", "vitest", "playwright"];

464-

return executionOrder

465-

.map((executionKind) => ({

466-

executionKind,

467-

matches: groups.get(executionKind) ?? [],

468-

}))

469-

.filter((group) => group.matches.length > 0);

469+

return executionOrder.flatMap((executionKind) => {

470+

const group = groups.get(executionKind);

471+

return group && group.length > 0 ? [{ executionKind, matches: group }] : [];

472+

});

470473

}

471474472475

export function renderQaScenarioMatchesMarkdownReport(params: {

473476

query: string;

474477

matches: readonly QaScenarioSearchMatch[];

475478

}) {

479+

const commandGroups = scenarioMatchCommandGroups(params.matches);

476480

const lines = [

477481

"# QA Scenario Matches",

478482

"",

479483

`- Query: ${params.query}`,

480484

`- Matches: ${params.matches.length}`,

481485

];

482486483-

const commandGroups = scenarioMatchCommandGroups(params.matches);

484487

if (commandGroups.length === 1) {

485488

lines.push(`- Suite command: \`${formatSuiteCommand(commandGroups[0].matches)}\``);

486489

} else if (commandGroups.length > 1) {

@@ -502,10 +505,10 @@ export function renderQaScenarioMatchesMarkdownReport(params: {

502505

lines.push(` - surface: ${match.surfaces.join(", ")}`);

503506

lines.push(

504507

match.executionKind === "flow"

505-

? " - execution: flow (qa-flow block)"

508+

? " - execution: qa-flow"

506509

: ` - execution: ${match.executionKind} ${match.executionPath ?? "missing"}`,

507510

);

508-

lines.push(` - coverage: ${match.coverageIds.join(", ") || "none"}`);

511+

lines.push(` - coverage IDs: ${match.coverageIds.join(", ") || "none"}`);

509512

lines.push(` - live requirements: ${formatOptionalScenarioMetadata(match)}`);

510513

if (match.codeRefs.length > 0) {

511514

lines.push(` - code refs: ${match.codeRefs.join(", ")}`);