test(runner): add Codex trace conformance (#12370)

## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - The Codex driver needs a controller-owned oracle for semantic
completion and replay
> - Provider proposals are advisory and must satisfy the exact task
envelope
> - Persisted events must be bounded and validated before deterministic
replay
> - Live provider behaviors also need one checked-in, schema-validated
fixture
> - This pull request adds that test and conformance layer before the
full driver
> - The runner adapter remains disabled and no production execution path
changes

## Linked Issues or Issue Description

**Subsystem affected**

`packages/paperclip-runner` Codex trace, result-validation, and replay
conformance.

**Problem or motivation**

A provider-completed turn is not sufficient authority to finalize a
Paperclip run. Results must match the controller-owned completion
contract, and persisted provider events must be validated before they
can rebuild controller state.

**Proposed solution**

Add a bounded Codex trace harness that validates result proposals, emits
controller decisions and terminals, verifies live/replay parity, and
rejects malformed persisted streams. Add a validated fixture for runtime
requests, goals, lineage, controls, reconnect identity, and redaction
cases.

**Alternatives considered**

Embedding these assertions only in the production driver would mix
controller authority with provider transport behavior and make
deterministic replay harder to review.

**Roadmap alignment**

This supports the Codex-first experimental runner. It does not enable
the runner adapter or add another provider.

## What Changed

- Added exact task-envelope result validation.
- Added bounded persisted-event validation and deterministic replay.
- Added a controller-owned Codex trace harness and parity assertions.
- Added and validated the checked-in Live console conformance fixture.
- Updated the generated protocol manifest with the fixture source.
- Added focused result and fixture tests.

## Verification

- `pnpm --filter @paperclipai/paperclip-runner test:typescript`
- `pnpm -r typecheck`
- `pnpm build`
- The focused trace-conformance test has 2 passing cases.

## Risks

The main risks are accepting a mismatched semantic result or replaying
corrupted provider history. Validation covers schema, contract revision,
criteria identity, disposition invariants, event identity, uniqueness,
ordering, terminal count, and byte limits.

## Model Used

OpenAI Codex with GPT-5.6 and repository tool use.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [ ] All Paperclip CI gates are green
- [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
This commit is contained in:
Dotta 2026-08-30 01:22:50 -05:00 committed by GitHub
parent 6f6415d07e
commit a9297c3c07
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 1358 additions and 0 deletions

View File

@ -0,0 +1,111 @@
{
"schema": "paperclip.runner.live-console.conformance.v1",
"codexVersion": "0.132.0",
"runtimeRequests": [
{
"id": "command-accept-session",
"method": "item/commandExecution/requestApproval",
"requestKind": "command_approval",
"resolution": { "action": "accept_for_session" },
"expectedResponse": { "decision": "acceptForSession" }
},
{
"id": "file-reject",
"method": "item/fileChange/requestApproval",
"requestKind": "file_approval",
"resolution": { "action": "decline" },
"expectedResponse": { "decision": "decline" }
},
{
"id": "permission-accept-turn",
"method": "item/permissions/requestApproval",
"requestKind": "permission_approval",
"resolution": { "action": "accept" },
"expectedResponse": { "permissions": {}, "scope": "turn" }
},
{
"id": "user-input-submit",
"method": "item/tool/requestUserInput",
"requestKind": "user_input",
"resolution": {
"action": "submit",
"answers": { "preferred_name": { "answers": ["Ada"] } }
},
"expectedResponse": {
"answers": { "preferred_name": { "answers": ["Ada"] } }
}
},
{
"id": "elicitation-cancel",
"method": "mcpServer/elicitation/request",
"requestKind": "elicitation",
"resolution": { "action": "cancel" },
"expectedResponse": { "action": "cancel", "content": null, "_meta": null }
}
],
"goals": [
{
"action": "get",
"method": "thread/goal/get",
"params": {}
},
{
"action": "set",
"method": "thread/goal/set",
"params": { "objective": "Ship the Live console tracer", "status": "active", "tokenBudget": 4096 }
},
{
"action": "pause",
"method": "thread/goal/set",
"params": { "status": "paused" }
},
{
"action": "resume",
"method": "thread/goal/set",
"params": { "status": "active" }
},
{
"action": "clear",
"method": "thread/goal/clear",
"params": {}
}
],
"lineage": {
"rootThreadId": "thread-root",
"childThread": {
"id": "thread-child",
"sessionId": "provider-session-1",
"source": {
"subAgent": {
"thread_spawn": {
"parent_thread_id": "thread-root",
"depth": 1,
"agent_path": ["researcher"],
"agent_nickname": "Scout",
"agent_role": "researcher"
}
}
},
"agentNickname": "Scout",
"agentRole": "researcher"
}
},
"controls": {
"sameTurnSteer": { "turnId": "turn-1", "expected": "acknowledged" },
"staleTurnSteer": { "turnId": "turn-stale", "expected": "stale_turn" },
"interruptBeforeStart": { "expected": "queued" },
"interruptAfterTerminal": { "expected": "already_terminal" }
},
"reconnect": {
"runId": "run-liveConsole",
"normalizedSessionId": "normalized-liveConsole",
"driverSessionId": "thread-root",
"providerSessionId": "provider-session-1",
"lastSourceSequence": 17
},
"redactionMarkers": [
"Bearer browser-secret",
"OPENAI_API_KEY=provider-secret",
"https://user:password@example.test/path?token=query-secret"
]
}

View File

@ -109,6 +109,12 @@
}
],
"fixtures": [
{
"path": "fixtures/codex-driver/driver-conformance.json",
"sha256": "55bbd66bb0d6641a64eedfaf02bb067c65a8b51c46d76d3052eeb99343f53176",
"expectation": "accept",
"compatibilityCase": "canonical"
},
{
"path": "fixtures/conformance-expected-output.json",
"sha256": "5a643639c9df0a2925ba95c5c5dfa2217605018bfa0dd3954ab4aca5fcfdc2c6",

View File

@ -0,0 +1,376 @@
import { mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { fileURLToPath } from "node:url";
import { describe, expect, it } from "vitest";
import {
CODEX_CODEX_PROTOCOL_VERSION,
CODEX_SKILLLESS_BASE_INSTRUCTIONS,
createCodexTaskEnvelope,
type CodexModelContextSnapshot,
} from "../contracts/codex.js";
import type {
HarnessDriver,
HarnessSession,
OpenHarnessSessionInput,
} from "../contracts/harness-driver.js";
import type {
PrpEvent,
PrpStructuredRunResult,
} from "../protocol/replay-contract.js";
import { loadLiveConsoleConformanceFixture } from "../protocol/live-console-fixture.js";
import {
runCodexCodexTracer,
validateCodexResultProposal,
} from "./codex-runner.js";
const envelope = createCodexTaskEnvelope({
objective: "Create hello.txt with the text hello.",
criteria: [{ id: "file", requirement: "hello.txt contains hello" }],
});
function completedResult(): PrpStructuredRunResult {
return {
schema: "paperclip.run_result.v1",
reportedWorkDisposition: "done",
summary: "Created hello.txt.",
completionClaim: {
contractRevision: envelope.completionContract.revision,
objectiveSatisfied: true,
criteria: [{
criterionId: "file",
status: "satisfied",
evidenceRefs: ["hello.txt"],
}],
remainingWork: [],
},
evidence: [{ ref: "hello.txt" }],
verification: [{ commandOrCheck: "read hello.txt", status: "passed" }],
attentionRequests: [],
artifacts: [{ kind: "file", ref: "hello.txt" }],
};
}
class TraceConformanceDriver implements HarnessDriver {
constructor(
private readonly result: PrpStructuredRunResult = completedResult(),
private readonly terminalEvent: "turn.completed" | "turn.interrupted" = "turn.completed",
) {}
async descriptor() {
return {
kind: "codex-trace-fixture",
displayName: "Codex trace fixture",
version: "1.0.0",
protocolVersion: CODEX_CODEX_PROTOCOL_VERSION,
capabilities: {
resume: false,
typedEvents: true,
steering: false,
interruption: false,
structuredResult: true,
dynamicTools: false,
},
};
}
async openSession(input: OpenHarnessSessionInput): Promise<HarnessSession> {
const turnId = "turn-trace";
let releaseEvents: (() => void) | undefined;
const started = new Promise<void>((resolve) => {
releaseEvents = resolve;
});
const events: PrpEvent[] = [];
const result = structuredClone(this.result);
const context: CodexModelContextSnapshot = {
protocolVersion: CODEX_CODEX_PROTOCOL_VERSION,
codexVersion: "trace-fixture",
clientInfo: {
name: "paperclip-runner",
title: "Paperclip Runner",
version: "1.0.0",
},
model: "codex-fixture",
modelProvider: "openai",
workingDirectory: input.workingDirectory,
collaborationMode: "default",
sandbox: { type: "workspaceWrite" },
approvalPolicy: "never",
baseInstructions: CODEX_SKILLLESS_BASE_INSTRUCTIONS,
instructionSources: [],
instructionPolicy: {
skillInstructions: false,
appInstructions: false,
collaborationInstructions: true,
},
environmentKeys: [],
dynamicToolNames: [],
modelInputKinds: ["text"],
envelope,
};
const event = (
sourceSeq: number,
eventType: PrpEvent["eventType"],
payload: Record<string, unknown>,
withTurn = true,
): PrpEvent => ({
schema: "paperclip.prp.event.v1",
sourceEventId: `provider:${sourceSeq}`,
sourceSeq,
sourceInstanceId: "codex-trace-provider",
sourceKind: "runner",
runId: input.runId,
normalizedSessionId: input.normalizedSessionId,
...(withTurn ? { turnId } : {}),
eventType,
schemaVersion: 1,
priority: eventType === "run.result.proposed" ? 0 : 1,
emittedAt: new Date(Date.UTC(2026, 7, 27, 12, 0, sourceSeq)).toISOString(),
payload,
});
return {
ids: () => ({
driverSessionId: "driver-trace",
providerSessionId: "provider-trace",
}),
events: async function* () {
await started;
for (const candidate of events) yield structuredClone(candidate);
},
startTurn: async () => {
events.push(
event(1, "session.started", { context }, false),
event(2, "turn.started", {}),
event(3, "run.result.proposed", result),
event(4, this.terminalEvent, {}),
);
releaseEvents?.();
return { turnId };
},
snapshot: async () => ({
driverKind: "codex-trace-fixture",
driverSessionId: "driver-trace",
providerSessionId: "provider-trace",
runId: input.runId,
normalizedSessionId: input.normalizedSessionId,
activeTurnId: null,
lastSourceSequence: 4,
}),
close: async () => undefined,
};
}
}
describe("Codex trace conformance", () => {
it("accepts only results that satisfy the exact controller envelope", () => {
expect(validateCodexResultProposal(completedResult(), envelope)).toMatchObject({
status: "accepted",
});
const wrongRevision = completedResult();
wrongRevision.completionClaim.contractRevision = "wrong-revision";
expect(validateCodexResultProposal(wrongRevision, envelope)).toMatchObject({
status: "rejected",
issues: [{ code: "contract_revision_mismatch" }],
});
const unknownCriterion = completedResult();
unknownCriterion.completionClaim.criteria = [{
criterionId: "not-in-envelope",
status: "satisfied",
evidenceRefs: [],
}];
const decision = validateCodexResultProposal(unknownCriterion, envelope);
expect(decision).toMatchObject({ status: "rejected" });
if (decision.status === "rejected") {
expect(decision.issues.map((issue) => issue.code)).toEqual(
expect.arrayContaining(["unknown_criterion", "missing_criterion"]),
);
}
});
it("loads the checked-in provider conformance fixture through validation", async () => {
const fixturePath = fileURLToPath(new URL(
"../../protocol/fixtures/codex-driver/driver-conformance.json",
import.meta.url,
));
await expect(loadLiveConsoleConformanceFixture(fixturePath)).resolves.toMatchObject({
schema: "paperclip.runner.live-console.conformance.v1",
runtimeRequests: expect.arrayContaining([
expect.objectContaining({ requestKind: "user_input" }),
]),
reconnect: { lastSourceSequence: 17 },
});
});
it("rejects malformed runtime request, goal, and control declarations", async () => {
const fixturePath = fileURLToPath(new URL(
"../../protocol/fixtures/codex-driver/driver-conformance.json",
import.meta.url,
));
const source = JSON.parse(await readFile(fixturePath, "utf8"));
const directory = await mkdtemp(join(tmpdir(), "paperclip-live-fixture-"));
const candidatePath = join(directory, "candidate.json");
try {
source.runtimeRequests[0].requestKind = "file_approval";
await writeFile(candidatePath, JSON.stringify(source));
await expect(loadLiveConsoleConformanceFixture(candidatePath))
.rejects.toThrow("invalid runtime request case");
source.runtimeRequests[0].requestKind = "command_approval";
source.runtimeRequests[0].expectedResponse.decision = "decline";
await writeFile(candidatePath, JSON.stringify(source));
await expect(loadLiveConsoleConformanceFixture(candidatePath))
.rejects.toThrow("invalid runtime request case");
source.runtimeRequests[0].expectedResponse.decision = "acceptForSession";
source.goals[0].method = "thread/goal/wrong";
await writeFile(candidatePath, JSON.stringify(source));
await expect(loadLiveConsoleConformanceFixture(candidatePath))
.rejects.toThrow("invalid goal operation");
source.goals[0].method = "thread/goal/get";
delete source.goals[1].params.objective;
await writeFile(candidatePath, JSON.stringify(source));
await expect(loadLiveConsoleConformanceFixture(candidatePath))
.rejects.toThrow("invalid goal operation");
source.goals[1].params.objective = "Ship the Live console tracer";
source.controls.sameTurnSteer.expected = 42;
await writeFile(candidatePath, JSON.stringify(source));
await expect(loadLiveConsoleConformanceFixture(candidatePath))
.rejects.toThrow("controls are invalid");
source.controls.sameTurnSteer.expected = "arbitrary_outcome";
await writeFile(candidatePath, JSON.stringify(source));
await expect(loadLiveConsoleConformanceFixture(candidatePath))
.rejects.toThrow("controls are invalid");
source.controls.sameTurnSteer.expected = "acknowledged";
source.controls.unsupportedControl = { expected: "queued" };
await writeFile(candidatePath, JSON.stringify(source));
await expect(loadLiveConsoleConformanceFixture(candidatePath))
.rejects.toThrow("controls are invalid");
delete source.controls.unsupportedControl;
delete source.controls.sameTurnSteer.turnId;
await writeFile(candidatePath, JSON.stringify(source));
await expect(loadLiveConsoleConformanceFixture(candidatePath))
.rejects.toThrow("controls are invalid");
source.controls.sameTurnSteer.turnId = "turn-active";
const spawn = source.lineage.childThread.source.subAgent.thread_spawn;
delete spawn.parent_thread_id;
await writeFile(candidatePath, JSON.stringify(source));
await expect(loadLiveConsoleConformanceFixture(candidatePath))
.rejects.toThrow("identity or lineage is incomplete");
spawn.parent_thread_id = source.lineage.rootThreadId;
spawn.agent_path = [""];
await writeFile(candidatePath, JSON.stringify(source));
await expect(loadLiveConsoleConformanceFixture(candidatePath))
.rejects.toThrow("identity or lineage is incomplete");
} finally {
await rm(directory, { recursive: true, force: true });
}
});
it("executes live trace and persisted replay with controller-owned terminal facts", async () => {
const trace = await runCodexCodexTracer({
driver: new TraceConformanceDriver(),
taskEnvelope: envelope,
workingDirectory: "/trace-workspace",
timeoutMs: 1_000,
});
expect(trace.resultDecision.status).toBe("accepted");
expect(trace.events.filter((event) => event.eventType === "run.terminal"))
.toHaveLength(1);
expect(trace.assertions).toEqual({
exactlyOneTerminalResult: true,
proposalAccepted: true,
liveReplayParity: true,
stableIdentity: true,
sourceSequenceContinuous: true,
stableItemIdentity: true,
contextIsSkillless: true,
unrelatedSkillsAbsent: true,
credentialsAbsent: true,
});
expect(trace.replaySnapshot).toEqual(trace.liveSnapshot);
});
it("fails the run when the controller rejects a completed provider proposal", async () => {
const rejectedResult = completedResult();
rejectedResult.completionClaim.contractRevision = "stale-contract-revision";
const trace = await runCodexCodexTracer({
driver: new TraceConformanceDriver(rejectedResult),
taskEnvelope: envelope,
workingDirectory: "/trace-workspace",
timeoutMs: 1_000,
});
expect(trace.resultDecision.status).toBe("rejected");
expect(trace.events.find((event) => event.eventType === "run.terminal")?.payload)
.toMatchObject({
turnTerminalState: "completed",
runTerminalState: "failed",
reportedWorkDisposition: "yielded",
});
expect(trace.liveSnapshot.terminal).toMatchObject({
turnTerminalState: "completed",
runTerminalState: "failed",
});
expect(trace.replaySnapshot).toEqual(trace.liveSnapshot);
});
it("preserves cancellation when an interrupted provider proposal is rejected", async () => {
const rejectedResult = completedResult();
rejectedResult.completionClaim.contractRevision = "stale-contract-revision";
const trace = await runCodexCodexTracer({
driver: new TraceConformanceDriver(rejectedResult, "turn.interrupted"),
taskEnvelope: envelope,
workingDirectory: "/trace-workspace",
timeoutMs: 1_000,
});
expect(trace.resultDecision.status).toBe("rejected");
expect(trace.events.find((event) => event.eventType === "run.terminal")?.payload)
.toMatchObject({
turnTerminalState: "interrupted",
runTerminalState: "cancelled",
reportedWorkDisposition: "yielded",
});
expect(trace.replaySnapshot).toEqual(trace.liveSnapshot);
});
it("does not report completed work when an accepted proposal is interrupted", async () => {
const trace = await runCodexCodexTracer({
driver: new TraceConformanceDriver(completedResult(), "turn.interrupted"),
taskEnvelope: envelope,
workingDirectory: "/trace-workspace",
timeoutMs: 1_000,
});
expect(trace.result).toBeNull();
expect(trace.resultDecision).toMatchObject({
status: "rejected",
result: null,
issues: [{ path: "/turn/terminal" }],
});
expect(trace.assertions.proposalAccepted).toBe(false);
expect(trace.events.some((event) => event.eventType === "run.result.accepted")).toBe(false);
expect(trace.events.find((event) => event.eventType === "run.result.rejected")?.payload)
.toMatchObject({ reasonCode: "provider_turn_did_not_complete" });
expect(trace.events.find((event) => event.eventType === "run.terminal")?.payload)
.toMatchObject({
turnTerminalState: "interrupted",
runTerminalState: "cancelled",
reportedWorkDisposition: "yielded",
});
expect(trace.replaySnapshot).toEqual(trace.liveSnapshot);
});
});

View File

@ -0,0 +1,574 @@
import type { HarnessDriver, HarnessSession } from "../contracts/harness-driver.js";
import {
isSkilllessCodexContext,
type CodexResultDecision,
type CodexResultValidationIssue,
type CodexRunMetadata,
type CodexRunTrace,
type CodexTaskEnvelope,
} from "../contracts/codex.js";
import type { NativeUserMessage } from "../contracts/types.js";
import {
applyPrpEvent,
createSessionSnapshotFromMetadata,
} from "../reducer/session-reducer.js";
import {
validatePrpEvent,
validatePrpStructuredRunResult,
type PrpCapabilities,
type PrpEvent,
type PrpStructuredRunResult,
type PrpTerminalState,
} from "../protocol/replay-contract.js";
const MAX_PERSISTED_CODEX_BYTES = 8 * 1024 * 1024;
const MAX_PERSISTED_CODEX_LINE_BYTES = 256 * 1024;
const MAX_PERSISTED_CODEX_EVENTS = 4096;
export interface CodexCodexTracerInput {
driver: HarnessDriver;
taskEnvelope: CodexTaskEnvelope;
workingDirectory: string;
message?: NativeUserMessage;
runId?: string;
normalizedSessionId?: string;
companyId?: string;
issueId?: string;
environmentLeaseId?: string;
runnerInstanceId?: string;
controlPlaneInstanceId?: string;
steer?: string;
interrupt?: boolean;
timeoutMs?: number;
}
function prpCapabilities(
descriptor: Awaited<ReturnType<HarnessDriver["descriptor"]>>,
): PrpCapabilities {
return {
schema: "paperclip.prp.capabilities.v1",
sessionReusePolicy: "reuse_per_issue",
driver: { kind: descriptor.kind, version: descriptor.version },
steer: descriptor.capabilities.steering,
interrupt: descriptor.capabilities.interruption,
resume: descriptor.capabilities.resume,
runtimeRequests: true,
structuredResult: descriptor.capabilities.structuredResult,
typedEvents: descriptor.capabilities.typedEvents,
...(descriptor.capabilities.unsupported?.length
? { unsupported: descriptor.capabilities.unsupported }
: {}),
};
}
function contextFrom(events: PrpEvent[]) {
const event = events.find(
(candidate) =>
candidate.eventType === "session.started" || candidate.eventType === "session.resumed",
);
const context = event?.payload.context;
if (typeof context !== "object" || context === null || Array.isArray(context)) {
throw new Error("Harness session did not emit its model-context snapshot");
}
return context as CodexRunTrace["context"];
}
function dispositionIssues(
result: PrpStructuredRunResult,
): CodexResultValidationIssue[] {
const issues: CodexResultValidationIssue[] = [];
const claim = result.completionClaim;
if (result.reportedWorkDisposition === "done") {
if (
claim.objectiveSatisfied !== true ||
claim.criteria.some((criterion) => criterion.status !== "satisfied") ||
claim.remainingWork.some((remaining) => remaining.blocksCompletion) ||
result.blocker !== undefined
) {
issues.push({
code: "invalid_disposition",
path: "/reportedWorkDisposition",
message: "done requires a satisfied objective and criteria with no blocking work or blocker",
});
}
} else if (result.reportedWorkDisposition === "needs_review") {
if (result.blocker !== undefined || result.attentionRequests.length === 0) {
issues.push({
code: "invalid_disposition",
path: "/reportedWorkDisposition",
message: "needs_review requires an attention request and must not include a blocker",
});
}
} else if (result.reportedWorkDisposition === "blocked") {
if (
claim.objectiveSatisfied !== false ||
result.blocker === undefined ||
!claim.remainingWork.some((remaining) => remaining.blocksCompletion)
) {
issues.push({
code: "invalid_disposition",
path: "/reportedWorkDisposition",
message: "blocked requires an unsatisfied objective, blocker details, and blocking remaining work",
});
}
} else {
issues.push({
code: "invalid_disposition",
path: "/reportedWorkDisposition",
message: `${result.reportedWorkDisposition} is not a Codex terminal proposal`,
});
}
return issues;
}
/** Validate an advisory provider proposal against the exact controller-owned task envelope. */
export function validateCodexResultProposal(
proposal: unknown,
envelope: CodexTaskEnvelope,
): CodexResultDecision {
const schema = validatePrpStructuredRunResult(proposal);
if (!schema.ok) {
return {
status: "rejected",
result: null,
issues: schema.issues.map((issue) => ({
code: "schema_validation",
path: issue.path,
message: issue.message,
})),
};
}
const result = schema.result;
const issues: CodexResultValidationIssue[] = [];
if (result.completionClaim.contractRevision !== envelope.completionContract.revision) {
issues.push({
code: "contract_revision_mismatch",
path: "/completionClaim/contractRevision",
message: `expected exact contract revision ${envelope.completionContract.revision}`,
});
}
const expectedIds = new Set(envelope.completionContract.criteria.map((criterion) => criterion.id));
const observed = new Map<string, number>();
result.completionClaim.criteria.forEach((criterion, index) => {
observed.set(criterion.criterionId, (observed.get(criterion.criterionId) ?? 0) + 1);
if (!expectedIds.has(criterion.criterionId)) {
issues.push({
code: "unknown_criterion",
path: `/completionClaim/criteria/${index}/criterionId`,
message: `criterion ${criterion.criterionId} is not in the task envelope`,
});
}
});
for (const criterionId of expectedIds) {
const count = observed.get(criterionId) ?? 0;
if (count === 0) {
issues.push({
code: "missing_criterion",
path: "/completionClaim/criteria",
message: `criterion ${criterionId} is missing`,
});
} else if (count > 1) {
issues.push({
code: "duplicate_criterion",
path: "/completionClaim/criteria",
message: `criterion ${criterionId} appears more than once`,
});
}
}
issues.push(...dispositionIssues(result));
return issues.length === 0
? { status: "accepted", result: structuredClone(result), issues: [] }
: { status: "rejected", result: null, issues };
}
async function consumeToTurnTerminal(
session: HarnessSession,
timeoutMs: number,
onEvent?: (event: PrpEvent) => void,
): Promise<PrpEvent[]> {
const consume = async () => {
const events: PrpEvent[] = [];
for await (const event of session.events()) {
events.push(event);
onEvent?.(event);
if (isTurnTerminal(event)) return events;
}
throw new Error("Harness event stream closed before a turn terminal fact");
};
let timer: ReturnType<typeof setTimeout> | undefined;
try {
return await Promise.race([
consume(),
new Promise<never>((_, reject) => {
timer = setTimeout(
() => reject(new Error(`Codex tracer timed out after ${timeoutMs}ms`)),
timeoutMs,
);
}),
]);
} finally {
if (timer !== undefined) clearTimeout(timer);
}
}
function isTurnTerminal(event: PrpEvent): boolean {
return ["turn.completed", "turn.failed", "turn.interrupted", "turn.cancelled"].includes(
event.eventType,
);
}
function runtimeTerminal(event: PrpEvent): Pick<PrpTerminalState, "turnTerminalState" | "runTerminalState"> {
if (event.eventType === "turn.completed") {
return { turnTerminalState: "completed", runTerminalState: "succeeded" };
}
if (event.eventType === "turn.failed") {
return { turnTerminalState: "failed", runTerminalState: "failed" };
}
if (event.eventType === "turn.interrupted") {
return { turnTerminalState: "interrupted", runTerminalState: "cancelled" };
}
return { turnTerminalState: "cancelled", runTerminalState: "cancelled" };
}
function sourceSequenceContinuous(events: PrpEvent[]): boolean {
const cursors = new Map<string, number>();
for (const event of events) {
const key = `${event.sourceKind}:${event.sourceInstanceId}`;
const previous = cursors.get(key);
if (previous !== undefined && event.sourceSeq !== previous + 1) return false;
cursors.set(key, event.sourceSeq);
}
return true;
}
/** Parse and validate the persisted boundary before replaying controller state. */
export function replayPersistedCodexEvents(
serialized: string,
metadata: CodexRunMetadata,
) {
if (!metadata.identity.driverSessionId) {
throw new Error("Persisted Codex replay requires a driver session identity");
}
if (Buffer.byteLength(serialized) > MAX_PERSISTED_CODEX_BYTES) {
throw new Error("Persisted Codex event stream exceeded its byte limit");
}
const lines = serialized.endsWith("\n")
? serialized.slice(0, -1).split("\n")
: serialized.split("\n");
if (lines.length === 0 || lines.length > MAX_PERSISTED_CODEX_EVENTS) {
throw new Error("Persisted Codex event stream exceeded its event limit");
}
const events: PrpEvent[] = [];
const sourceEventIds = new Set<string>();
const sourceSequences = new Map<string, number>();
let runTerminalCount = 0;
for (const line of lines) {
if (line.length === 0 || Buffer.byteLength(line) > MAX_PERSISTED_CODEX_LINE_BYTES) {
throw new Error("Persisted Codex event line was empty or oversized");
}
let candidate: unknown;
try {
candidate = JSON.parse(line);
} catch {
throw new Error("Persisted Codex event line was malformed JSON");
}
const validation = validatePrpEvent(candidate);
if (!validation.ok) throw new Error("Persisted Codex event failed schema validation");
const event = validation.event;
if (
event.runId !== metadata.identity.runId ||
event.normalizedSessionId !== metadata.identity.normalizedSessionId
) {
throw new Error("Persisted Codex event identity did not match the opened run");
}
if (sourceEventIds.has(event.sourceEventId)) {
throw new Error("Persisted Codex event id was duplicated");
}
sourceEventIds.add(event.sourceEventId);
const source = `${event.sourceKind}:${event.sourceInstanceId}`;
const previous = sourceSequences.get(source);
if (previous !== undefined && event.sourceSeq !== previous + 1) {
throw new Error("Persisted Codex source sequence was discontinuous");
}
sourceSequences.set(source, event.sourceSeq);
if (runTerminalCount > 0) {
throw new Error("Persisted Codex event appeared after the run terminal");
}
if (event.eventType === "run.terminal") runTerminalCount += 1;
events.push(event);
}
if (runTerminalCount !== 1) {
throw new Error("Persisted Codex event stream requires exactly one run terminal");
}
return events.reduce(
applyPrpEvent,
createSessionSnapshotFromMetadata({
fixtureName: metadata.fixtureName,
identity: {
...metadata.identity,
driverSessionId: metadata.identity.driverSessionId,
},
capabilities: metadata.capabilities,
}),
);
}
/**
* Small in-memory Codex mock core. The driver reports provider facts; this
* controller validates semantic proposals and alone emits the run terminal.
*/
export async function runCodexCodexTracer(input: CodexCodexTracerInput): Promise<CodexRunTrace> {
const runId = input.runId ?? "codex-safe-run";
const normalizedSessionId = input.normalizedSessionId ?? "codex-normalized-session";
const descriptor = await input.driver.descriptor();
const capabilities = prpCapabilities(descriptor);
const metadata: CodexRunMetadata = {
schema: "paperclip.runner.codex.metadata.v1",
fixtureName: "codex-safe-codex-task",
identity: {
schema: "paperclip.prp.identity.v1",
companyId: input.companyId ?? "mock-company",
issueId: input.issueId ?? "mock-issue",
runId,
environmentLeaseId: input.environmentLeaseId ?? "mock-lease",
runnerInstanceId: input.runnerInstanceId ?? "runner-codex",
normalizedSessionId,
},
capabilities,
};
const session = await input.driver.openSession({
runId,
normalizedSessionId,
workingDirectory: input.workingDirectory,
});
const capabilityDiagnostics: string[] = [];
let signalTurnStarted: (() => void) | undefined;
const turnStarted = new Promise<void>((resolve) => {
signalTurnStarted = resolve;
});
const consuming = consumeToTurnTerminal(session, input.timeoutMs ?? 180_000, (event) => {
if (event.eventType === "turn.started") signalTurnStarted?.();
});
try {
const turn = await session.startTurn({
message: input.message ?? { role: "user", text: "Complete the supplied task envelope." },
});
if (input.steer !== undefined || input.interrupt) {
await Promise.race([
turnStarted,
consuming.then(() => {
throw new Error("Harness turn reached terminal state before accepting the control command");
}),
]);
}
if (input.steer !== undefined) {
if (!descriptor.capabilities.steering || session.steer === undefined) {
capabilityDiagnostics.push("steering is unavailable by driver capability contract");
} else {
try {
await session.steer({
turnId: turn.turnId,
message: { role: "user", text: input.steer },
});
} catch (error) {
capabilityDiagnostics.push(`steering degraded: ${String(error)}`);
}
}
}
if (input.interrupt) {
if (!descriptor.capabilities.interruption || session.interrupt === undefined) {
capabilityDiagnostics.push("interruption is unavailable by driver capability contract");
} else {
try {
await session.interrupt({ turnId: turn.turnId, reason: "Codex tutorial interrupt" });
} catch (error) {
capabilityDiagnostics.push(`interruption degraded: ${String(error)}`);
}
}
}
const providerEvents = await consuming;
const ids = session.ids();
const identity = {
...metadata.identity,
driverSessionId: ids.driverSessionId,
...(ids.providerSessionId ? { providerSessionId: ids.providerSessionId } : {}),
};
metadata.identity = identity;
const proposals = providerEvents.filter(
(event) => event.eventType === "run.result.proposed",
);
const proposedResult = proposals.length === 1 ? structuredClone(proposals[0]!.payload) : null;
const proposalDecision: CodexResultDecision =
proposals.length === 1
? validateCodexResultProposal(proposedResult, input.taskEnvelope)
: {
status: "rejected",
result: null,
issues: [{
code: "schema_validation",
path: "/run.result.proposed",
message:
proposals.length === 0
? "the completed turn emitted no semantic proposal"
: "the completed turn emitted multiple semantic proposals",
}],
};
let controlSequence = 0;
const controlPlaneInstanceId = input.controlPlaneInstanceId ?? "mock-core-codex";
const controlEvent = (
eventType: PrpEvent["eventType"],
payload: Record<string, unknown>,
refs: { turnId?: string; itemId?: string } = {},
): PrpEvent => ({
schema: "paperclip.prp.event.v1",
sourceEventId: `${controlPlaneInstanceId}:${runId}:${++controlSequence}`,
sourceSeq: controlSequence,
sourceInstanceId: controlPlaneInstanceId,
sourceKind: "control_plane",
runId,
normalizedSessionId,
...(refs.turnId ? { turnId: refs.turnId } : {}),
...(refs.itemId ? { itemId: refs.itemId } : {}),
eventType,
schemaVersion: 1,
priority: eventType === "run.terminal" || eventType.startsWith("run.result.") ? 0 : 1,
emittedAt: new Date().toISOString(),
payload,
});
const controlEvents = capabilityDiagnostics.map((message) =>
controlEvent("runner.diagnostic", {
code: "capability_unavailable",
message,
}),
);
const providerTerminal = providerEvents.findLast(isTurnTerminal);
if (providerTerminal === undefined) throw new Error("turn terminal invariant failed");
const providerRuntime = runtimeTerminal(providerTerminal);
const resultAccepted =
proposalDecision.status === "accepted" && providerRuntime.runTerminalState === "succeeded";
const resultDecision: CodexResultDecision = resultAccepted
? proposalDecision
: proposalDecision.status === "rejected"
? proposalDecision
: {
status: "rejected",
result: null,
issues: [{
code: "schema_validation",
path: "/turn/terminal",
message: `the provider turn ended as ${providerRuntime.turnTerminalState}`,
}],
};
controlEvents.push(
resultAccepted
? controlEvent("run.result.accepted", {
contractRevision: input.taskEnvelope.completionContract.revision,
result: resultDecision.result,
})
: controlEvent("run.result.rejected", {
reasonCode:
proposalDecision.status === "rejected"
? "semantic_result_rejected"
: "provider_turn_did_not_complete",
issues: resultDecision.issues,
recovery: { required: true, recoverable: true },
}),
);
// A provider may complete its turn after proposing a result that the
// controller cannot accept. Preserve the provider's turn fact, but never
// promote a rejected semantic result to a successful run.
const runtime = resultDecision.status === "accepted" || providerRuntime.runTerminalState === "cancelled"
? providerRuntime
: { ...providerRuntime, runTerminalState: "failed" as const };
controlEvents.push(controlEvent("run.terminal", {
schema: "paperclip.prp.terminal.v1",
...runtime,
reportedWorkDisposition:
resultDecision.status === "accepted" && runtime.runTerminalState === "succeeded"
? resultDecision.result.reportedWorkDisposition
: "yielded",
}, providerTerminal.turnId ? { turnId: providerTerminal.turnId } : {}));
const events = [...providerEvents, ...controlEvents];
const liveSnapshot = events.reduce(
applyPrpEvent,
createSessionSnapshotFromMetadata({
fixtureName: metadata.fixtureName,
identity,
capabilities,
}),
);
const persistedEvents = `${events.map((event) => JSON.stringify(event)).join("\n")}\n`;
const replaySnapshot = replayPersistedCodexEvents(persistedEvents, metadata);
const context = contextFrom(events);
const terminalCount = events.filter((event) => event.eventType === "run.terminal").length;
const turnTerminalCount = events.filter(isTurnTerminal).length;
const decisionCount = events.filter(
(event) =>
event.eventType === "run.result.accepted" || event.eventType === "run.result.rejected",
).length;
const serialized = JSON.stringify(context).toLowerCase();
const itemEvents = events.filter((event) => event.eventType.startsWith("item."));
const providerSessionId = ids.providerSessionId ?? null;
return {
schema: "paperclip.runner.codex.trace.v1",
metadata,
context,
events,
proposedResult,
result: resultDecision.result,
resultDecision,
liveSnapshot,
replaySnapshot,
diagnostics: events
.filter(
(event) =>
event.eventType === "runner.diagnostic" ||
event.eventType === "harness.diagnostic" ||
event.eventType === "run.result.rejected",
)
.map((event) => String(event.payload.message ?? event.payload.reasonCode ?? event.payload.code ?? "diagnostic")),
assertions: {
exactlyOneTerminalResult:
terminalCount === 1 && turnTerminalCount === 1 && decisionCount === 1,
proposalAccepted: resultDecision.status === "accepted",
liveReplayParity: JSON.stringify(liveSnapshot) === JSON.stringify(replaySnapshot),
stableIdentity:
providerSessionId !== null &&
runId.length > 0 &&
normalizedSessionId.length > 0 &&
ids.driverSessionId.length > 0 &&
providerSessionId.length > 0 &&
events.every(
(event) =>
event.runId === runId && event.normalizedSessionId === normalizedSessionId,
),
sourceSequenceContinuous: sourceSequenceContinuous(events),
stableItemIdentity: itemEvents.every(
(event) => typeof event.itemId === "string" && event.itemId.length > 0,
),
contextIsSkillless: isSkilllessCodexContext(context, {
dynamicTools: descriptor.capabilities.dynamicTools === true,
}),
unrelatedSkillsAbsent:
context.instructionSources.length === 0 &&
context.instructionPolicy.skillInstructions === false &&
context.modelInputKinds.length === 1 &&
context.modelInputKinds[0] === "text",
credentialsAbsent:
!serialized.includes("paperclip_api_key") &&
!serialized.includes("authorization: bearer") &&
!serialized.includes("/api/issues/"),
},
};
} finally {
await session.close({ reason: "Codex tracer complete" });
}
}

View File

@ -0,0 +1,291 @@
import { readFile, stat } from "node:fs/promises";
import { isDeepStrictEqual } from "node:util";
import {
parseHarnessRuntimeRequestResolution,
type HarnessRuntimeRequest,
} from "../contracts/harness-driver.js";
import {
createCodexQuestionResponseContext,
runtimeRequestKind,
runtimeRequestResponse,
} from "../drivers/codex/codex-question-adapter.js";
export const LIVE_CONSOLE_CONFORMANCE_SCHEMA =
"paperclip.runner.live-console.conformance.v1" as const;
export interface LiveConsoleRuntimeRequestFixture {
id: string;
method: string;
requestKind: string;
resolution: Record<string, unknown> & { action: string };
expectedResponse: Record<string, unknown>;
}
export interface LiveConsoleGoalFixture {
action: "get" | "set" | "pause" | "resume" | "clear";
method: string;
params: Record<string, unknown>;
}
export interface LiveConsoleControlFixture {
turnId?: string;
expected: string;
}
export interface LiveConsoleConformanceFixture {
schema: typeof LIVE_CONSOLE_CONFORMANCE_SCHEMA;
codexVersion: string;
runtimeRequests: LiveConsoleRuntimeRequestFixture[];
goals: LiveConsoleGoalFixture[];
lineage: {
rootThreadId: string;
childThread: Record<string, unknown> & {
id: string;
sessionId: string;
agentNickname: string;
agentRole: string;
source: {
subAgent: {
thread_spawn: {
parent_thread_id: string;
depth: number;
agent_path: string[];
agent_nickname: string;
agent_role: string;
};
};
};
};
};
controls: {
sameTurnSteer: LiveConsoleControlFixture & { turnId: string };
staleTurnSteer: LiveConsoleControlFixture & { turnId: string };
interruptBeforeStart: LiveConsoleControlFixture;
interruptAfterTerminal: LiveConsoleControlFixture;
};
reconnect: {
runId: string;
normalizedSessionId: string;
driverSessionId: string;
providerSessionId: string;
lastSourceSequence: number;
};
redactionMarkers: string[];
}
const MAX_LIVE_CONSOLE_FIXTURE_BYTES = 1024 * 1024;
const MAX_RUNTIME_REQUEST_CASES = 64;
const MAX_REDACTION_MARKERS = 64;
const MAX_CONTROL_CASES = 64;
const GOAL_METHODS = {
get: "thread/goal/get",
set: "thread/goal/set",
pause: "thread/goal/set",
resume: "thread/goal/set",
clear: "thread/goal/clear",
} as const;
const CONTROL_EXPECTATIONS = {
sameTurnSteer: { expected: "acknowledged", requiresTurnId: true },
staleTurnSteer: { expected: "stale_turn", requiresTurnId: true },
interruptBeforeStart: { expected: "queued", requiresTurnId: false },
interruptAfterTerminal: {
expected: "already_terminal",
requiresTurnId: false,
},
} as const;
function record(value: unknown): Record<string, unknown> | null {
return typeof value === "object" && value !== null && !Array.isArray(value)
? (value as Record<string, unknown>)
: null;
}
function nonEmpty(value: unknown): value is string {
return typeof value === "string" && value.length > 0;
}
/** Load the deterministic Live console wire fixture without trusting its shape. */
export async function loadLiveConsoleConformanceFixture(
path: string,
): Promise<LiveConsoleConformanceFixture> {
const info = await stat(path);
if (!info.isFile() || info.size > MAX_LIVE_CONSOLE_FIXTURE_BYTES) {
throw new Error("Live console fixture exceeded its file-size limit");
}
const value = JSON.parse(await readFile(path, "utf8")) as unknown;
const fixture = record(value);
if (fixture?.schema !== LIVE_CONSOLE_CONFORMANCE_SCHEMA) {
throw new Error("Live console fixture has an unsupported schema");
}
if (!nonEmpty(fixture.codexVersion)) {
throw new Error("Live console fixture must name the observed Codex version");
}
if (
!Array.isArray(fixture.runtimeRequests) ||
fixture.runtimeRequests.length === 0 ||
fixture.runtimeRequests.length > MAX_RUNTIME_REQUEST_CASES
) {
throw new Error("Live console fixture must include runtime request cases");
}
if (!Array.isArray(fixture.goals) || fixture.goals.length !== 5) {
throw new Error("Live console fixture must include all five goal operations");
}
const requestIds = new Set<string>();
for (const candidate of fixture.runtimeRequests) {
const request = record(candidate);
const resolution = record(request?.resolution);
const kind = nonEmpty(request?.method)
? runtimeRequestKind(request.method)
: null;
if (
!nonEmpty(request?.id) ||
requestIds.has(request.id) ||
!nonEmpty(request.method) ||
kind === null ||
request.requestKind !== kind ||
!nonEmpty(resolution?.action) ||
record(request.expectedResponse) === null
) {
throw new Error("Live console fixture contains an invalid runtime request case");
}
try {
const parsedResolution = parseHarnessRuntimeRequestResolution(kind, resolution);
const harnessRequest: HarnessRuntimeRequest = {
requestId: request.id,
requestKind: kind,
method: request.method,
turnId: "fixture-turn",
itemId: request.id,
status: "pending",
prompt: "fixture request",
details: {},
};
if (
!isDeepStrictEqual(
runtimeRequestResponse(
harnessRequest,
parsedResolution,
createCodexQuestionResponseContext(),
),
request.expectedResponse,
)
) {
throw new Error("expected response does not match the declared resolution");
}
} catch {
throw new Error("Live console fixture contains an invalid runtime request case");
}
requestIds.add(request.id);
}
const actions = new Set<string>();
for (const candidate of fixture.goals) {
const goal = record(candidate);
if (goal === null) {
throw new Error("Live console fixture contains an invalid goal operation");
}
const action = goal?.action;
if (
typeof action !== "string" ||
!(action in GOAL_METHODS) ||
goal.method !== GOAL_METHODS[action as keyof typeof GOAL_METHODS] ||
!validGoalParams(action as keyof typeof GOAL_METHODS, goal.params)
) {
throw new Error("Live console fixture contains an invalid goal operation");
}
actions.add(action);
}
if (!["get", "set", "pause", "resume", "clear"].every((action) => actions.has(action))) {
throw new Error("Live console fixture goal operation set is incomplete");
}
const lineage = record(fixture.lineage);
const child = record(lineage?.childThread);
const childSource = record(child?.source);
const childSubAgent = record(childSource?.subAgent);
const childSpawn = record(childSubAgent?.thread_spawn);
const childAgentPath = childSpawn?.agent_path;
const controls = record(fixture.controls);
const reconnect = record(fixture.reconnect);
if (
!nonEmpty(lineage?.rootThreadId) ||
!nonEmpty(child?.id) ||
!nonEmpty(child.sessionId) ||
child.id === lineage.rootThreadId ||
childSpawn === null ||
childSpawn.parent_thread_id !== lineage.rootThreadId ||
!Number.isSafeInteger(childSpawn.depth) ||
(childSpawn.depth as number) < 1 ||
!Array.isArray(childAgentPath) ||
childAgentPath.length === 0 ||
childAgentPath.length > 64 ||
!childAgentPath.every(nonEmpty) ||
!nonEmpty(childSpawn.agent_nickname) ||
!nonEmpty(childSpawn.agent_role) ||
child.agentNickname !== childSpawn.agent_nickname ||
child.agentRole !== childSpawn.agent_role ||
!nonEmpty(reconnect?.runId) ||
!nonEmpty(reconnect.normalizedSessionId) ||
!nonEmpty(reconnect.driverSessionId) ||
!nonEmpty(reconnect.providerSessionId) ||
!Number.isSafeInteger(reconnect.lastSourceSequence) ||
(reconnect.lastSourceSequence as number) < 0
) {
throw new Error("Live console fixture identity or lineage is incomplete");
}
const controlNames = Object.keys(CONTROL_EXPECTATIONS);
if (
controls === null ||
Object.keys(controls).length > MAX_CONTROL_CASES ||
Object.keys(controls).length !== controlNames.length ||
controlNames.some((name) => {
const expectation =
CONTROL_EXPECTATIONS[name as keyof typeof CONTROL_EXPECTATIONS];
const control = record(controls[name]);
if (
control === null ||
control.expected !== expectation.expected ||
Object.keys(control).some(
(key) => key !== "expected" && key !== "turnId",
)
) {
return true;
}
return expectation.requiresTurnId
? !nonEmpty(control.turnId)
: control.turnId !== undefined;
})
) {
throw new Error("Live console fixture controls are invalid");
}
if (
!Array.isArray(fixture.redactionMarkers) ||
fixture.redactionMarkers.length > MAX_REDACTION_MARKERS ||
!fixture.redactionMarkers.every(nonEmpty)
) {
throw new Error("Live console fixture redaction markers are invalid");
}
return value as LiveConsoleConformanceFixture;
}
function validGoalParams(
action: keyof typeof GOAL_METHODS,
value: unknown,
): boolean {
const params = record(value);
if (params === null) return false;
if (action === "get" || action === "clear") {
return Object.keys(params).length === 0;
}
if (action === "pause" || action === "resume") {
return Object.keys(params).length === 1 &&
params.status === (action === "pause" ? "paused" : "active");
}
return nonEmpty(params.objective) &&
params.status === "active" &&
Object.keys(params).every((key) =>
key === "objective" || key === "status" || key === "tokenBudget"
) &&
(params.tokenBudget === undefined ||
params.tokenBudget === null ||
(Number.isSafeInteger(params.tokenBudget) && (params.tokenBudget as number) >= 0));
}