Keep guidance checks scoped to source and record chat regression fixes

This commit is contained in:
Dotta 2026-09-11 15:10:06 -05:00
parent ae7b66330d
commit 47dfb0eb32
2 changed files with 53 additions and 4 deletions

View File

@ -143,3 +143,21 @@ these fixes. Focused live checks additionally passed legacy Codex project reuse
and repository handoff, legacy Claude plan revision/acceptance/handoff, and native
Claude planning, Stop/reset/resume, fresh sessions, and multiple repositories.
Final campaign results and broad verification are recorded below when complete.
The next full campaign (`34640536416`) reached 18/24 passing cells and identified
three additional issues. Execution prompts now include the task's persisted plan
and selected revision on both fresh and resumed runs; a plan handed off without a
description therefore still reaches its executor. Native durable redaction keeps
explicit literal/exact acceptance identifiers while continuing to redact actual
credential-shaped values. Recovery for an older conversation generation or an
already answered turn cannot block a reset or healthy idle chat. Regression tests
also preserve recovery for current unanswered turns and unprepared failures.
Fixture assertions now accept concrete clarification requests without requiring a
question mark. They check the approved revision and final execution output rather
than rejecting an old draft quoted in plan revision history. Restart verification
opens the canonical chat route after reconnecting, preserving the continuity and
no-unsolicited-run checks. Stable inconsistent idle states fail promptly instead
of waiting through a long timeout and hiding a product race behind a paid retry.
Focused native Claude project reuse and multiple-repository handoffs, and legacy
Claude multiple-repository handoff, passed on their first attempts with these fixes.

View File

@ -1,4 +1,5 @@
import { readdirSync, readFileSync } from "node:fs";
import { mkdirSync, mkdtempSync, readdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
import os from "node:os";
import path from "node:path";
import { fileURLToPath } from "node:url";
import { describe, expect, it } from "vitest";
@ -335,7 +336,14 @@ const SKIP_DIRS = new Set([
"tmp",
]);
const SKIP_PATH_PREFIXES = ["doc/logs/", "doc/plans/", "scripts/"];
const SKIP_PATH_PREFIXES = [
"doc/logs/",
"doc/plans/",
"scripts/",
// Generated paid-run transcripts contain historical copies of instructions,
// including escaped warning examples; they are not authored guidance.
"tests/runner-e2e/results/",
];
const SCAN_EXTENSIONS = new Set([
".md",
@ -355,7 +363,7 @@ function isTestFile(relPath: string): boolean {
);
}
function listGuidanceFiles(): string[] {
function listGuidanceFiles(root = repoRoot): string[] {
const found: string[] = [];
function walk(absDir: string, relDir: string): void {
@ -364,6 +372,7 @@ function listGuidanceFiles(): string[] {
const relPath = relDir ? `${relDir}/${entry.name}` : entry.name;
if (entry.isDirectory()) {
if (SKIP_DIRS.has(entry.name)) continue;
if (SKIP_PATH_PREFIXES.some((prefix) => `${relPath}/`.startsWith(prefix))) continue;
walk(path.join(absDir, entry.name), relPath);
continue;
}
@ -374,7 +383,7 @@ function listGuidanceFiles(): string[] {
}
}
walk(repoRoot, "");
walk(root, "");
return found;
}
@ -466,6 +475,28 @@ function scanForBrokenExecForm(): string[] {
}
describe("paperclipai CLI invocation safety", () => {
it("excludes generated runner evidence while preserving authored runner guidance", () => {
const root = mkdtempSync(path.join(os.tmpdir(), "paperclip-cli-guidance-"));
const sourcePaths = [
"doc/CLI.md",
"tests/runner-e2e/README.md",
"tests/runner-e2e/catalog.ts",
];
try {
for (const relPath of [
...sourcePaths,
"tests/runner-e2e/results/campaign/attempt-1/snapshots/api-state.json",
]) {
const absPath = path.join(root, relPath);
mkdirSync(path.dirname(absPath), { recursive: true });
writeFileSync(absPath, "fixture");
}
expect(listGuidanceFiles(root).sort()).toEqual(sourcePaths.sort());
} finally {
rmSync(root, { recursive: true, force: true });
}
});
it("allows only exact-allowlist pnpm paperclipai commands on every guidance surface", () => {
const offenders = scanForOffenders();
expect(