Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
- Added a release-prep checklist for auditing changelog, package metadata, and dry-run package contents without publishing.
- Improved bounded source grouping so large flat directories split repeated filename families like command, plugin, doctor, and runtime files into more coherent review slices.
- Fixed acpx provider error reporting by reading the terminal `result.stopReason` envelope and surfacing non-`end_turn` reasons as typed `ClawpatchError` codes (`agent-cancelled`, `agent-refused`, `agent-truncated`) instead of opaque `malformed-output`, thanks @coletebou.
- Added website crawler artifacts and a static smoke check for metadata, anchors, and social-card dimensions, thanks @zack-dev-cm.
- Improved OpenCode malformed JSON diagnostics with output length, event kinds, and a bounded preview, thanks @rohitjavvadi.
- Fixed finding signatures so equivalent evidence remains stable across re-reviews, thanks @rohitjavvadi.
- Fixed provider exit-code classification for stdout-only authentication and quota failures, thanks @rohitjavvadi.
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
"lint": "oxlint . --config oxlint.json",
"format": "oxfmt --write .",
"format:check": "oxfmt --check .",
"website:smoke": "node scripts/website-smoke.mjs",
"test": "vitest run",
"pack:smoke": "node scripts/package-smoke.mjs"
},
Expand Down
95 changes: 95 additions & 0 deletions scripts/website-smoke.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import { readFile, stat } from "node:fs/promises";
import { join } from "node:path";

const root = process.cwd();
const website = join(root, "website");
const failures = [];

function fail(message) {
failures.push(message);
}

async function mustRead(relativePath) {
try {
return await readFile(join(root, relativePath), "utf8");
} catch {
fail(`missing ${relativePath}`);
return "";
}
}

function stripTags(value) {
return value
.replace(/<br\s*\/?>/giu, " ")
.replace(/<[^>]+>/gu, "")
.replace(/\s+/gu, " ")
.trim();
}

function extractFirst(html, pattern, label) {
const match = html.match(pattern);
if (!match) {
fail(`missing ${label}`);
return "";
}
return match[1] || "";
}

const html = await mustRead("website/index.html");
const robots = await mustRead("website/robots.txt");
const sitemap = await mustRead("website/sitemap.xml");

const title = stripTags(extractFirst(html, /<title>([\s\S]*?)<\/title>/iu, "title"));
if (title !== "Clawpatch — Automated Code Review") {
fail(`unexpected title: ${title}`);
}

const description = html.match(/<meta\s+name="description"\s+content="([^"]+)"/iu)?.[1] || "";
if (!description.includes("Automated code review that lands fixes")) {
fail("meta description does not contain the product promise");
}

const h1 = stripTags(extractFirst(html, /<h1>([\s\S]*?)<\/h1>/iu, "h1"));
if (h1 !== "Code review with explicit fixes") {
fail(`unexpected h1 text: ${h1}`);
}

const ids = new Set([...html.matchAll(/\sid="([^"]+)"/giu)].map((match) => match[1]));
const anchorLinks = [...html.matchAll(/href="#([^"]+)"/giu)].map((match) => match[1]);
for (const id of anchorLinks) {
if (!ids.has(id)) fail(`missing anchor target: #${id}`);
}

if (!robots.includes("Sitemap: https://clawpatch.ai/sitemap.xml")) {
fail("robots.txt missing sitemap reference");
}

if (!sitemap.includes("<loc>https://clawpatch.ai/</loc>")) {
fail("sitemap.xml missing canonical homepage loc");
}

const socialCard = await readFile(join(website, "social-card.png"));
if (socialCard.toString("ascii", 1, 4) !== "PNG") {
fail("social-card.png is not a PNG");
} else {
const width = socialCard.readUInt32BE(16);
const height = socialCard.readUInt32BE(20);
if (width !== 1200 || height !== 630) {
fail(`social-card.png dimensions are ${width}x${height}, expected 1200x630`);
}
}

for (const file of ["website/favicon.svg", "website/CNAME", "website/.nojekyll"]) {
try {
await stat(join(root, file));
} catch {
fail(`missing ${file}`);
}
}

if (failures.length) {
console.error(failures.join("\n"));
process.exit(1);
}

console.log("Website smoke checks passed.");
4 changes: 2 additions & 2 deletions src/exec.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ describe("runCommandArgs", () => {
"import { writeFileSync } from 'node:fs';",
"process.on('SIGTERM', () => {});",
"process.send?.('ready');",
`setTimeout(() => writeFileSync(${JSON.stringify(marker)}, 'alive'), 2500);`,
`setTimeout(() => writeFileSync(${JSON.stringify(marker)}, 'alive'), 4500);`,
"setInterval(() => {}, 1000);",
].join("\n"),
"utf8",
Expand All @@ -111,7 +111,7 @@ describe("runCommandArgs", () => {
);

const result = await runCommandArgs(process.execPath, [parentScript], dir, undefined, {
timeoutMs: 1000,
timeoutMs: 3000,
});
await new Promise((resolve) => setTimeout(resolve, 1200));

Expand Down
2 changes: 2 additions & 0 deletions website/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ Files:
- `favicon.svg`: browser icon
- `social-card.svg`: link preview card
- `social-card.png`: raster link preview card for Open Graph/Twitter
- `robots.txt`: crawler policy with sitemap reference
- `sitemap.xml`: canonical single-page sitemap

Preview:

Expand Down
2 changes: 1 addition & 1 deletion website/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -1057,7 +1057,7 @@ <h2>Reference</h2>
<main>
<header class="home-hero">
<p class="eyebrow">Automated Code Review · Explicit Fixes</p>
<h1>Code review with<br />explicit fixes</h1>
<h1>Code review with <br />explicit fixes</h1>
<p class="lede">
Clawpatch maps codebases into semantic feature slices, reviews them for bugs and quality
issues, and records explicit fix attempts with validation.
Expand Down
4 changes: 4 additions & 0 deletions website/robots.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
User-agent: *
Allow: /

Sitemap: https://clawpatch.ai/sitemap.xml
9 changes: 9 additions & 0 deletions website/sitemap.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
<loc>https://clawpatch.ai/</loc>
<lastmod>2026-05-20</lastmod>
<changefreq>weekly</changefreq>
<priority>1.0</priority>
</url>
</urlset>
Loading