mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-25 02:26:12 +02:00
89 lines
3.2 KiB
JavaScript
89 lines
3.2 KiB
JavaScript
import { scenarios } from "./inventory.mjs";
|
|
export function summarize(laneResults) {
|
|
return scenarios.map((scenario) => ({
|
|
...scenario,
|
|
coverage: scenario.coverage.map((ref) => {
|
|
const lane = laneResults[ref.lane];
|
|
if (!lane) return { ...ref, status: "not_run", assertions: [] };
|
|
const file = lane.testResults?.find((file) =>
|
|
file.name.replaceAll("\\", "/").endsWith("/" + ref.file),
|
|
);
|
|
const assertions = (file?.assertionResults ?? []).filter((a) =>
|
|
new RegExp(ref.pattern, "i").test(a.fullName),
|
|
);
|
|
const status =
|
|
!file || assertions.length === 0
|
|
? "harness_evidence_failure"
|
|
: assertions.some((a) => a.status === "failed")
|
|
? "assertion_failure"
|
|
: assertions.some((a) => a.status !== "passed")
|
|
? "unavailable_prerequisite"
|
|
: file.status === "failed" &&
|
|
!file.assertionResults.some((a) => a.status === "failed")
|
|
? "harness_evidence_failure"
|
|
: "pass";
|
|
return {
|
|
...ref,
|
|
status,
|
|
assertions: assertions.map((a) => ({
|
|
name: a.fullName,
|
|
status: a.status,
|
|
duration: a.duration,
|
|
failureMessages: a.failureMessages,
|
|
})),
|
|
};
|
|
}),
|
|
live: scenario.live.map((id) => ({ id, status: "not_run" })),
|
|
}));
|
|
}
|
|
export function markdown(report) {
|
|
const lines = [
|
|
"# Lifecycle behavior baseline",
|
|
"",
|
|
`Source: \`${report.commit}\``,
|
|
`Source/test fingerprint: \`${report.fingerprint}\``,
|
|
`Measured: ${report.measuredAt}`,
|
|
"",
|
|
"This is a diagnostic baseline. Assertions express intended behavior; failures are not accepted product contracts. Live cells below are not measured by this command.",
|
|
"",
|
|
"| Scenario | Layer | Result | Assertions |",
|
|
"|---|---|---|---|",
|
|
];
|
|
for (const s of report.scenarios)
|
|
for (const c of s.coverage)
|
|
lines.push(
|
|
`| ${s.id} ${s.name} | ${c.lane} | ${c.status} | ${c.assertions.length} |`,
|
|
);
|
|
lines.push(
|
|
"",
|
|
"## Executed assertions (unique per layer)",
|
|
"",
|
|
"| Layer | Total | Passed | Failed | Pending | Exit |",
|
|
"|---|---:|---:|---:|---:|---:|",
|
|
);
|
|
for (const [layer, e] of Object.entries(report.execution ?? {}))
|
|
lines.push(
|
|
`| ${layer} | ${e.total} | ${e.passed} | ${e.failed} | ${e.pending} | ${e.exitCode} |`,
|
|
);
|
|
lines.push("", "## Failures and unavailable coverage", "");
|
|
for (const s of report.scenarios)
|
|
for (const c of s.coverage.filter(
|
|
(c) => !["pass", "not_run"].includes(c.status),
|
|
)) {
|
|
lines.push(`- ${s.id}: ${c.file} — ${c.status}`);
|
|
for (const a of c.assertions.filter((a) => a.status !== "passed"))
|
|
lines.push(` - ${a.name}: ${a.status}`);
|
|
}
|
|
lines.push("", "## Live coverage (not run)", "");
|
|
for (const id of new Set(
|
|
report.scenarios.flatMap((s) => s.live.map((c) => c.id)),
|
|
))
|
|
lines.push(`- ${id}`);
|
|
lines.push(
|
|
"",
|
|
"Raw per-lane Vitest JSON and observation JSONL are retained beside this report. Skips and missing evidence never count as a pass. Assertion failures require triage before attributing them to product behavior rather than the harness.",
|
|
"",
|
|
);
|
|
return lines.join("\n");
|
|
}
|