Merge branch 'codex/work-folders-remote-recovery-refresh' into codex/work-folders-session-compat-refresh

* codex/work-folders-remote-recovery-refresh:
  fix(runner): preserve v2 event metadata and reject nonretryable campaign failures
  fix(runner): preserve exact Claude model selectors during ACP admission
  test(runner): serve goal reads while holding passive completion notices
  fix(ci): validate shared Cloud image contexts throughout the stack
This commit is contained in:
Dotta committed 2026-09-11 17:02:41 -05:00
commit b48689cb00
9 files changed
+168 -62

No files matched your search

+31
View File
@@ -2,6 +2,7 @@ import { readFileSync } from "node:fs";
import { describe, expect, it } from "vitest";
import {
PRP_V1_EVENT_TYPES,
PRP_V2_EVENT_TYPES,
REDACTED_EVENT_VALUE,
redactAgentAdapterConfig,
redactEventPayload,
@@ -23,6 +24,36 @@ describe("redaction", () => {
expect([...PRP_V1_EVENT_TYPES]).toEqual(schema.properties.eventType.enum);
});
it("keeps the discriminator allowlist in exact PRP v2 schema parity", () => {
const schema = JSON.parse(readFileSync(new URL(
"../../../packages/paperclip-runner/protocol/schemas/event-v2.schema.json",
import.meta.url,
), "utf8")) as { properties: { eventType: { enum: string[] } } };
expect([...PRP_V2_EVENT_TYPES].sort()).toEqual(schema.properties.eventType.enum.sort());
});
it("preserves v2 goal envelopes while redacting secrets and mismatched versions", () => {
for (const eventType of PRP_V2_EVENT_TYPES) {
const envelope = {
schema: "paperclip.prp.event.v2", schemaVersion: 2, eventType,
payload: { apiKey: "secret", opaque: "aaa.bbb.ccc" },
};
expect(sanitizeRecord(envelope)).toEqual({
...envelope,
payload: { apiKey: REDACTED_EVENT_VALUE, opaque: REDACTED_EVENT_VALUE },
});
}
for (const envelope of [
{ schema: "paperclip.prp.event.v1", schemaVersion: 1, eventType: "session.goal.snapshot" },
{ schema: "paperclip.prp.event.v2", schemaVersion: 1, eventType: "session.goal.snapshot" },
{ schema: "paperclip.prp.event.v1", schemaVersion: 2, eventType: "session.goal.snapshot" },
{ schema: "paperclip.prp.event.v2", schemaVersion: 2, eventType: "aaa.bbb.ccc" },
{ schema: "paperclip.prp.event.v3", schemaVersion: 3, eventType: "session.goal.snapshot" },
]) {
expect(sanitizeRecord(envelope).eventType).toBe(REDACTED_EVENT_VALUE);
}
});
it("preserves every discriminator in the cross-language replay stream", () => {
const fixture = JSON.parse(
readFileSync(
+19 -4
View File
@@ -110,6 +110,7 @@ export const PAPERCLIP_PUBLIC_SCHEMA_IDS = new Set([
"paperclip.prp.command.v1",
"paperclip.prp.contract_manifest.v1",
"paperclip.prp.event.v1",
"paperclip.prp.event.v2",
"paperclip.prp.fixture.v1",
"paperclip.prp.identity.v1",
"paperclip.prp.semantic_tool.v1",
@@ -194,7 +195,7 @@ export const PAPERCLIP_PUBLIC_SCHEMA_IDS = new Set([
// Keep this closed catalog aligned with PRP v1's event.schema.json. These
// values are public protocol discriminators, but their dotted shape overlaps
// the deliberately broad JWT heuristic. They are exempt only in the
// discriminator field of a PRP v1 event envelope; the same string anywhere
// discriminator field of a supported PRP event envelope; the same string anywhere
// else remains subject to redaction.
export const PRP_V1_EVENT_TYPES = new Set([
"runner.connected",
@@ -303,6 +304,17 @@ export const PRP_V1_EVENT_TYPES = new Set([
"issue.status.decision.superseded",
"run.terminal",
]);
// V2 removes backpressure and semantic reconciliation, and adds goal events.
// Exact parity with both protocol schemas is enforced by redaction.test.ts.
export const PRP_V2_EVENT_TYPES = new Set([
...[...PRP_V1_EVENT_TYPES].filter(
(type) => type !== "runner.backpressure" && type !== "semantic_tool.reconciled",
),
"session.capabilities.updated",
"session.goal.snapshot",
"session.goal.updated",
"session.goal.cleared",
]);
const NATIVE_RUN_SPAN_SCHEMA = "paperclip.run-performance-span.v1";
const NATIVE_RUN_SPAN_FIELDS = ["span", "parentSpan"] as const;
const NATIVE_RUN_SPAN_NAMES = new Set([
@@ -843,10 +855,13 @@ function isKnownPrpEventDiscriminator(
): value is string {
return (
key === "eventType" &&
container.schema === "paperclip.prp.event.v1" &&
container.schemaVersion === 1 &&
typeof value === "string" &&
PRP_V1_EVENT_TYPES.has(value)
((container.schema === "paperclip.prp.event.v1" &&
container.schemaVersion === 1 &&
PRP_V1_EVENT_TYPES.has(value)) ||
(container.schema === "paperclip.prp.event.v2" &&
container.schemaVersion === 2 &&
PRP_V2_EVENT_TYPES.has(value)))
);
}