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
3 changes: 3 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ jobs:
- name: Verify Go tests
run: go test ./...

- name: Verify capability runner tests
run: npm run test:capability-runners

- name: Verify CGO-free build
run: CGO_ENABLED=0 go build ./...

Expand Down
43 changes: 43 additions & 0 deletions cli/scripts/capability-runner-utils.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { randomBytes } from "node:crypto";
import { mkdirSync, renameSync, rmSync, writeFileSync } from "node:fs";
import { dirname, resolve } from "node:path";

const requiredOptions = ["client", "expected-version", "receipt"];
const safeVersionPattern = /^[A-Za-z0-9][A-Za-z0-9._+-]{0,127}$/;

export function parseRunnerArgs(argv) {
const values = {};
for (let index = 0; index < argv.length; index += 2) {
const option = argv[index];
const value = argv[index + 1];
if (!option?.startsWith("--") || !requiredOptions.includes(option.slice(2))) throw new Error(`unknown option ${option ?? "<missing>"}`);
const name = option.slice(2);
if (Object.hasOwn(values, name)) throw new Error(`duplicate option --${name}`);
if (value === undefined || value.startsWith("--")) throw new Error(`option --${name} requires a value`);
values[name] = value;
}
for (const name of requiredOptions) if (!Object.hasOwn(values, name)) throw new Error(`missing required option --${name}`);
if (values.client === "" || values.client.startsWith("-") || /[\0\r\n]/.test(values.client)) throw new Error("--client must be a safe executable name or path");
if (!safeVersionPattern.test(values["expected-version"])) throw new Error("--expected-version must be an exact safe identity");
if (values.receipt === "" || /[\0\r\n]/.test(values.receipt) || !values.receipt.endsWith(".json")) throw new Error("--receipt must be a safe JSON path");
return {
client: values.client,
expectedVersion: values["expected-version"],
receiptPath: resolve(values.receipt),
};
}

export function publishReceiptIfSuccessful(receiptPath, receipt, successful) {
if (!successful) return false;
const directory = dirname(receiptPath);
mkdirSync(directory, { recursive: true });
const temporaryPath = `${receiptPath}.${process.pid}.${randomBytes(6).toString("hex")}.tmp`;
try {
writeFileSync(temporaryPath, `${JSON.stringify(receipt, null, 2)}\n`, { encoding: "utf8", flag: "wx", mode: 0o644 });
renameSync(temporaryPath, receiptPath);
} catch (error) {
rmSync(temporaryPath, { force: true });
throw error;
}
return true;
}
Original file line number Diff line number Diff line change
@@ -1,21 +1,19 @@
#!/usr/bin/env node

import { createHash } from "node:crypto";
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
import { existsSync, readFileSync } from "node:fs";
import { fileURLToPath } from "node:url";
import { dirname, join, resolve } from "node:path";
import { spawnSync } from "node:child_process";
import { parseRunnerArgs, publishReceiptIfSuccessful } from "./capability-runner-utils.mjs";

const scriptDir = dirname(fileURLToPath(import.meta.url));
const repoRoot = resolve(scriptDir, "../..");
const researchDir = join(repoRoot, "docs/changes/20260710-journal-reliability-foundation/research");
const evidencePath = join(researchDir, "u8-cursor-agent-candidate-preflight.json");
const expectedVersion = "2026.05.09-0afadcc";
const platform = `${process.platform}-${process.arch}`;
const candidateHooksPath = "dist/cursor/hooks.json";
const candidateBinaryPath = `bin/native/${platform}/loaf`;

export function classifyCursorPreflight(versionOutput, helpOutput) {
export function classifyCursorPreflight(versionOutput, helpOutput, expectedVersion) {
const observedVersion = versionOutput.trim();
const exactVersion = observedVersion === expectedVersion;
const noSessionPersistence = helpOutput.includes("--no-session-persistence");
Expand Down Expand Up @@ -66,18 +64,20 @@ function candidateArtifacts() {
};
}

function main() {
mkdirSync(researchDir, { recursive: true });
function main(argv = process.argv.slice(2)) {
const { client, expectedVersion, receiptPath } = parseRunnerArgs(argv);
const timestamp = new Date().toISOString();
const buildGo = run("npm", ["run", "build:go"], repoRoot);
if (buildGo.status !== 0) throw new Error("candidate Go build failed");
const buildCursor = run("bin/loaf", ["build", "--target", "cursor"], repoRoot);
if (buildCursor.status !== 0) throw new Error("candidate Cursor target build failed");
const artifacts = candidateArtifacts();
const version = run("cursor-agent", ["--version"], repoRoot);
const help = run("cursor-agent", ["--help"], repoRoot);
const version = run(client, ["--version"], repoRoot);
const help = run(client, ["--help"], repoRoot);
if (version.status !== 0 || help.status !== 0) throw new Error("installed cursor-agent version/help preflight failed");
const preflight = classifyCursorPreflight(version.stdout, help.stdout);
const preflight = classifyCursorPreflight(version.stdout, help.stdout, expectedVersion);
if (!preflight.exactVersion) throw new Error(preflight.blocker);
if (preflight.noSessionPersistence) throw new Error("installed cursor-agent exposes --no-session-persistence, but a model-visible isolated smoke is not implemented");
const smoke = {
evidence_version: 2,
timestamp,
Expand All @@ -102,8 +102,15 @@ function main() {
blocker: preflight.blocker,
retry_condition: "Rerun after the installed CLI exposes a documented no-session-persistence mode or after an explicitly approved isolated session-store boundary is available.",
};
writeFileSync(evidencePath, `${JSON.stringify(smoke, null, 2)}\n`);
process.stdout.write(`${JSON.stringify({ evidence_path: "docs/changes/20260710-journal-reliability-foundation/research/u8-cursor-agent-candidate-preflight.json", status: "unavailable", reason: smoke.blocker }, null, 2)}\n`);
publishReceiptIfSuccessful(receiptPath, smoke, true);
process.stdout.write(`${JSON.stringify({ receipt: receiptPath, status: "safely-refused", reason: smoke.blocker }, null, 2)}\n`);
}

if (process.argv[1] && resolve(process.argv[1]) === fileURLToPath(import.meta.url)) main();
if (process.argv[1] && resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
try {
main();
} catch (error) {
process.stderr.write(`${error instanceof Error ? error.message : "Cursor agent context preflight failed"}\n`);
process.exitCode = 1;
}
}
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
import test from "node:test";
import assert from "node:assert/strict";
import { classifyCursorPreflight } from "./u8-cursor-smoke.mjs";
import { classifyCursorPreflight } from "./preflight-cursor-agent-context.mjs";

test("classifies an unsafe Cursor preflight without claiming execution", () => {
const preflight = classifyCursorPreflight("2026.05.09-0afadcc\n", "Usage: agent\n");
const preflight = classifyCursorPreflight("candidate-42\n", "Usage: agent\n", "candidate-42");
assert.equal(preflight.exactVersion, true);
assert.equal(preflight.noSessionPersistence, false);
assert.equal(preflight.smokeExecuted, false);
assert.match(preflight.blocker, /no-session-persistence/);
});

test("rejects an unexpected installed version before any smoke", () => {
const preflight = classifyCursorPreflight("3.11.19\n", "--no-session-persistence\n");
const preflight = classifyCursorPreflight("unexpected\n", "--no-session-persistence\n", "candidate-42");
assert.equal(preflight.exactVersion, false);
assert.equal(preflight.noSessionPersistence, true);
assert.equal(preflight.smokeExecuted, false);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
#!/usr/bin/env node

import { createHash, randomBytes } from "node:crypto";
import { existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
import { existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync } from "node:fs";
import { fileURLToPath } from "node:url";
import { dirname, join, relative, resolve } from "node:path";
import { dirname, join, relative, resolve, sep } from "node:path";
import { spawnSync } from "node:child_process";
import { tmpdir } from "node:os";
import { parseRunnerArgs, publishReceiptIfSuccessful } from "./capability-runner-utils.mjs";

const scriptDir = dirname(fileURLToPath(import.meta.url));
const repoRoot = resolve(scriptDir, "../..");
const researchDir = join(repoRoot, "docs/changes/20260710-journal-reliability-foundation/research");
const evidencePath = join(researchDir, "u8-claude-code-2.1.218-candidate-smoke.json");
const expectedVersion = "2.1.218";
const platform = `${process.platform}-${process.arch}`;
const candidatePluginPath = "plugins/loaf";
const markerPattern = /^LOAF_CLAUDE_STARTUP_SMOKE_[A-F0-9]{12}$/;

export function parseClaudeStreamOutput(raw, marker) {
const events = raw.split(/\r?\n/).filter(Boolean).map((line) => JSON.parse(line));
Expand Down Expand Up @@ -65,12 +65,21 @@ function sha256(path) {
return createHash("sha256").update(readFileSync(path)).digest("hex");
}

function main() {
mkdirSync(researchDir, { recursive: true });
const marker = `LOAF_U8_SMOKE_${randomBytes(6).toString("hex").toUpperCase()}`;
function main(argv = process.argv.slice(2)) {
const { client, expectedVersion, receiptPath } = parseRunnerArgs(argv);
const marker = `LOAF_CLAUDE_STARTUP_SMOKE_${randomBytes(6).toString("hex").toUpperCase()}`;
if (!markerPattern.test(marker)) throw new Error("generated marker does not match the required format");
const timestamp = new Date().toISOString();
const tempRoot = mkdtempSync(join(repoRoot, ".u8-claude-smoke-"));
const cleanup = () => rmSync(tempRoot, { recursive: true, force: true });
const tempRoot = mkdtempSync(join(tmpdir(), "loaf-claude-code-startup-smoke-"));
if (resolve(tempRoot).startsWith(`${repoRoot}${sep}`)) throw new Error("disposable Claude Code smoke root must be outside the repository");
const cleanup = () => {
try {
rmSync(tempRoot, { recursive: true, force: true, maxRetries: 3, retryDelay: 50 });
} catch {
return false;
}
return !existsSync(tempRoot);
};
for (const signal of ["SIGINT", "SIGTERM", "SIGHUP"]) {
process.once(signal, () => {
cleanup();
Expand All @@ -85,14 +94,16 @@ function main() {
const candidatePlugin = join(repoRoot, candidatePluginPath);
const candidateBinary = join(candidatePlugin, "bin", "loaf");
let smoke;
let failure;
let cleanupSucceeded = false;
try {
const buildGo = run("npm", ["run", "build:go"], repoRoot);
if (buildGo.status !== 0) throw new Error("candidate Go build failed");
const buildClaude = run("bin/loaf", ["build", "--target", "claude-code"], repoRoot);
if (buildClaude.status !== 0) throw new Error("candidate Claude build failed");
if (!existsSync(candidateBinary)) throw new Error("candidate plugin binary is missing");
const version = run("claude", ["--version"], repoRoot);
if (version.status !== 0 || !claudeVersionMatches(version.stdout, expectedVersion)) throw new Error("installed Claude version is not 2.1.218");
const version = run(client, ["--version"], repoRoot);
if (version.status !== 0 || !claudeVersionMatches(version.stdout, expectedVersion)) throw new Error(`installed Claude Code version does not match ${expectedVersion}`);
if (run("git", ["init", "-q"], disposableRepo).status !== 0) throw new Error("disposable Git initialization failed");
const candidateEnv = { LOAF_DB: dbPath };
if (run(candidateBinary, ["state", "init", "--json"], disposableRepo, candidateEnv).status !== 0) throw new Error("isolated Loaf state initialization failed");
Expand All @@ -104,7 +115,7 @@ function main() {
"--include-hook-events", "--output-format", "stream-json", "-p",
"Reply with exactly the unique marker present in Loaf continuity context, and nothing else.",
];
const claude = run("claude", [
const claude = run(client, [
"--plugin-dir", candidatePlugin, "--strict-mcp-config", "--mcp-config", '{"mcpServers":{}}',
"--no-session-persistence", "--setting-sources", "", "--tools", "", "--include-hook-events",
"--output-format", "stream-json", "-p", "Reply with exactly the unique marker present in Loaf continuity context, and nothing else.",
Expand Down Expand Up @@ -139,23 +150,21 @@ function main() {
};
if (claude.status !== 0 || claude.stderr.length !== 0 || !parsed.hookObservation.additional_context_marker || !parsed.assistantMarkerMatch) throw new Error("model-visible marker smoke did not pass");
} catch (error) {
smoke ??= {
evidence_version: 2, timestamp, target: "claude-code", surface: "cli", version: expectedVersion, platform,
installed_mode: "plugin-dir", context_mode: "startup", adapter: "claude-session-start-v1", mode: "explicit-plugin-dir",
invocation: { command: "claude", args: [], cwd: "<disposable-repo>" }, setup: [], candidate_plugin_path: candidatePluginPath,
exit_code: 1, stderr_empty: false, model_visible_marker_observed: false, assistant_marker_match: false, marker,
hook_observation: { event_name: "", native_json: false, hook_event_name: "", additional_context_marker: false },
candidate_artifacts: { hooks_path: "plugins/loaf/hooks/hooks.json", hooks_sha256: "", native_binary_path: "", native_binary_sha256: "" },
failure_reason: error instanceof Error ? error.message : "smoke failed",
};
smoke.failure_reason = error instanceof Error ? error.message : "smoke failed";
smoke.exit_code = smoke.exit_code ?? 1;
failure = error;
} finally {
cleanup();
cleanupSucceeded = cleanup();
}
writeFileSync(evidencePath, `${JSON.stringify(smoke, null, 2)}\n`);
process.stdout.write(`${JSON.stringify({ evidence_path: "docs/changes/20260710-journal-reliability-foundation/research/u8-claude-code-2.1.218-candidate-smoke.json", exit_code: smoke.exit_code, assistant_marker_match: smoke.assistant_marker_match }, null, 2)}\n`);
if (smoke.exit_code !== 0 || !smoke.assistant_marker_match) process.exitCode = 1;
if (failure) throw failure;
if (!cleanupSucceeded) throw new Error("disposable Claude Code smoke cleanup failed");
publishReceiptIfSuccessful(receiptPath, smoke, true);
process.stdout.write(`${JSON.stringify({ receipt: receiptPath, exit_code: smoke.exit_code, assistant_marker_match: smoke.assistant_marker_match, cleanup_succeeded: true }, null, 2)}\n`);
}

if (process.argv[1] && resolve(process.argv[1]) === fileURLToPath(import.meta.url)) main();
if (process.argv[1] && resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
try {
main();
} catch (error) {
process.stderr.write(`${error instanceof Error ? error.message : "Claude Code startup smoke failed"}\n`);
process.exitCode = 1;
}
}
58 changes: 58 additions & 0 deletions cli/scripts/smoke-claude-code-startup.test.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import test from "node:test";
import assert from "node:assert/strict";
import { mkdtempSync, readFileSync, readdirSync, rmSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { claudeVersionMatches, parseClaudeStreamOutput } from "./smoke-claude-code-startup.mjs";
import { parseRunnerArgs, publishReceiptIfSuccessful } from "./capability-runner-utils.mjs";

test("parses native SessionStart output and exact assistant marker", () => {
const marker = "LOAF_CLAUDE_STARTUP_SMOKE_ABCDEF123456";
const raw = [
JSON.stringify({ type: "system", subtype: "hook_response", hook_event: "SessionStart", output: JSON.stringify({ hookSpecificOutput: { hookEventName: "SessionStart", additionalContext: `digest ${marker}` } }) }),
JSON.stringify({ type: "assistant", message: { content: [{ type: "text", text: marker }] } }),
JSON.stringify({ type: "result", result: marker }),
].join("\n");
const parsed = parseClaudeStreamOutput(raw, marker);
assert.equal(parsed.hookObservation.native_json, true);
assert.equal(parsed.hookObservation.additional_context_marker, true);
assert.equal(parsed.assistantMarkerMatch, true);
});

test("rejects a stream without SessionStart hook response", () => {
assert.throws(() => parseClaudeStreamOutput(JSON.stringify({ type: "result", result: "no hook" }), "marker"), /SessionStart hook response/);
});

test("requires the exact Claude Code version token", () => {
assert.equal(claudeVersionMatches("9.8.7 (Claude Code)\n", "9.8.7"), true);
assert.equal(claudeVersionMatches("9.8.70 (Claude Code)", "9.8.7"), false);
assert.equal(claudeVersionMatches("Claude Code 9.8.7", "9.8.7"), false);
});

test("requires one safe value for every runner option", () => {
const parsed = parseRunnerArgs(["--client", "/opt/claude", "--expected-version", "9.8.7", "--receipt", "proof.json"]);
assert.equal(parsed.client, "/opt/claude");
assert.equal(parsed.expectedVersion, "9.8.7");
assert.ok(parsed.receiptPath.endsWith("proof.json"));
assert.throws(() => parseRunnerArgs(["--client", "claude"]), /missing required option/);
assert.throws(() => parseRunnerArgs(["--client", "claude", "--client", "other", "--expected-version", "9.8.7", "--receipt", "proof.json"]), /duplicate option/);
assert.throws(() => parseRunnerArgs(["--unknown", "value", "--client", "claude", "--expected-version", "9.8.7", "--receipt", "proof.json"]), /unknown option/);
assert.throws(() => parseRunnerArgs(["--client", "claude\nunsafe", "--expected-version", "9.8.7", "--receipt", "proof.json"]), /safe executable/);
assert.throws(() => parseRunnerArgs(["--client", "claude", "--expected-version", "9.8.7 unsafe", "--receipt", "proof.json"]), /exact safe identity/);
assert.throws(() => parseRunnerArgs(["--client", "claude", "--expected-version", "9.8.7", "--receipt", "proof.txt"]), /safe JSON path/);
});

test("publishes success atomically and preserves an existing receipt on failure", () => {
const root = mkdtempSync(join(tmpdir(), "loaf-capability-receipt-test-"));
const receiptPath = join(root, "receipt.json");
try {
writeFileSync(receiptPath, "existing\n");
assert.equal(publishReceiptIfSuccessful(receiptPath, { status: "failed" }, false), false);
assert.equal(readFileSync(receiptPath, "utf8"), "existing\n");
assert.equal(publishReceiptIfSuccessful(receiptPath, { status: "passed" }, true), true);
assert.deepEqual(JSON.parse(readFileSync(receiptPath, "utf8")), { status: "passed" });
assert.deepEqual(readdirSync(root), ["receipt.json"]);
} finally {
rmSync(root, { recursive: true, force: true });
}
});
Loading
Loading