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

推荐订阅源

F
Fortinet All Blogs
爱范儿
爱范儿
P
Proofpoint News Feed
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
T
Tailwind CSS Blog
J
Java Code Geeks
宝玉的分享
宝玉的分享
Jina AI
Jina AI
B
Blog
N
Netflix TechBlog - Medium
Recent Announcements
Recent Announcements
aimingoo的专栏
aimingoo的专栏
腾讯CDC
C
Check Point Blog
The Cloudflare Blog
阮一峰的网络日志
阮一峰的网络日志
博客园 - Franky
罗磊的独立博客
B
Blog RSS Feed
WordPress大学
WordPress大学
小众软件
小众软件
博客园 - 叶小钗
M
MIT News - Artificial intelligence
GbyAI
GbyAI

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
fix(qa): sanitize evidence gallery failure paths · opencl...
vincentkoc · 2026-06-20 · via Recent Commits to openclaw:main
Original file line numberDiff line numberDiff line change

@@ -27,7 +27,7 @@ function vitestArtifactEvidence(params: {

2727

id: string;

2828

title: string;

2929

artifact: { kind: string; path: string };

30-

}) {

30+

}): QaEvidenceSummaryJson {

3131

return {

3232

kind: "openclaw.qa.evidence-summary",

3333

schemaVersion: 2,

@@ -144,6 +144,38 @@ describe("evidence gallery", () => {

144144

});

145145

});

146146
147+

it("sanitizes local roots from gallery failure reasons", async () => {

148+

const repoRoot = await createTempRepo();

149+

const outputDir = path.join(repoRoot, ".artifacts", "qa-e2e", "vitest");

150+

await fs.mkdir(outputDir, { recursive: true });

151+

const evidence: QaEvidenceSummaryJson = vitestArtifactEvidence({

152+

id: "qa-lab.failure-path",

153+

title: "Failure path evidence",

154+

artifact: { kind: "log", path: "missing.log" },

155+

});

156+

evidence.entries[0] = {

157+

...evidence.entries[0],

158+

result: {

159+

status: "blocked",

160+

failure: {

161+

class: "blocked",

162+

reason: `Command failed at ${repoRoot}/openclaw.mjs and file://${repoRoot}/trace.log`,

163+

},

164+

},

165+

};

166+

await writeJson(path.join(outputDir, QA_EVIDENCE_FILENAME), evidence);

167+
168+

const model = await buildQaEvidenceGalleryModel({

169+

evidencePath: outputDir,

170+

repoRoot,

171+

});

172+
173+

expect(model.entries[0].failureReason).toBe(

174+

"Command failed at <repo-root>/openclaw.mjs and file://<repo-root>/trace.log",

175+

);

176+

expect(JSON.stringify(model)).not.toContain(repoRoot);

177+

});

178+
147179

it("detects UX Matrix producer context from suite-level evidence artifacts", async () => {

148180

const repoRoot = await createTempRepo();

149181

const suiteDir = path.join(repoRoot, ".artifacts", "qa-e2e", "suite");

Original file line numberDiff line numberDiff line change

@@ -1,6 +1,8 @@

11

// Qa Lab plugin module implements generic QA evidence gallery data.

22

import fs from "node:fs/promises";

3+

import os from "node:os";

34

import path from "node:path";

5+

import { pathToFileURL } from "node:url";

46

import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";

57

import type {

68

QaEvidenceArtifactView,

@@ -61,6 +63,27 @@ function isInside(root: string, candidate: string) {

6163

return relative === "" || (!relative.startsWith("..") && !path.isAbsolute(relative));

6264

}

6365
66+

function sanitizeGalleryText(

67+

value: string,

68+

params: {

69+

extraRoots?: readonly string[];

70+

repoRoot: string;

71+

},

72+

) {

73+

const localRoots = [...new Set([params.repoRoot, ...(params.extraRoots ?? [])])];

74+

const roots = [

75+

...localRoots.flatMap((root) => [

76+

{ from: path.resolve(root), to: "<repo-root>" },

77+

{ from: pathToFileURL(path.resolve(root)).href, to: "file://<repo-root>" },

78+

]),

79+

{ from: os.homedir(), to: "<home>" },

80+

{ from: pathToFileURL(os.homedir()).href, to: "file://<home>" },

81+

].filter((entry) => entry.from && entry.from !== path.parse(entry.from).root);

82+

return roots

83+

.toSorted((a, b) => b.from.length - a.from.length)

84+

.reduce((text, entry) => text.replaceAll(entry.from, entry.to), value);

85+

}

86+
6487

async function realpathIfExists(filePath: string): Promise<string | null> {

6588

return fs.realpath(filePath).catch(() => null);

6689

}

@@ -642,7 +665,8 @@ export async function buildQaEvidenceGalleryModel(params: {

642665

evidencePath: string;

643666

repoRoot: string;

644667

}): Promise<QaEvidenceGalleryModel> {

645-

const repoRoot = await fs.realpath(path.resolve(params.repoRoot));

668+

const requestedRepoRoot = path.resolve(params.repoRoot);

669+

const repoRoot = await fs.realpath(requestedRepoRoot);

646670

const evidencePath = await resolveQaEvidenceFile({

647671

inputPath: params.evidencePath,

648672

repoRoot,

@@ -684,7 +708,12 @@ export async function buildQaEvidenceGalleryModel(params: {

684708

),

685709

),

686710

coverage: entry.coverage,

687-

failureReason: entry.result.failure?.reason ?? null,

711+

failureReason: entry.result.failure?.reason

712+

? sanitizeGalleryText(entry.result.failure.reason, {

713+

extraRoots: [requestedRepoRoot],

714+

repoRoot,

715+

})

716+

: null,

688717

id: entry.test.id,

689718

kind: entry.test.kind,

690719

sourcePath: entry.test.source?.path ?? null,