Files
PaperClipAI/server/src/services/native-runtime/status-arbiter.test.ts
T
DottaandDev Agent 25cf079ec5 feat(runner): add Codex-native application integration (#12591)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - The runner package is useful only when the application can start,
observe, and recover a native Codex run safely.
> - Existing direct adapters must keep their current execution and
finalization paths.
> - The application boundary therefore needs additive persistence,
authorization, coordination, and recovery behind an explicit
experimental adapter.
> - This pull request adds that Codex-only boundary without activating
generalized providers, remote environments, or the later task/SDK
surfaces.

## Linked Issues or Issue Description

**Subsystem affected**

Shared contracts, database persistence, adapter utilities, server
native-runtime services, and the experimental Paperclip Runner adapter.

**Problem or motivation**

The already-landed runner package has a qualified Codex path, but the
application needs durable native-run state, guarded runtime selection,
authenticated coordination, tool security, finalization, and recovery
before the experimental adapter can be exercised safely.

**Proposed solution**

Add a Codex-only `paperclip_runner` application path behind the existing
default-off native-runner setting. Bind native state and coordination to
company/run identity, preserve persisted-run recovery, and leave every
direct adapter on its existing legacy execution path.

**Alternatives considered**

The earlier stack boundary introduced a generalized executor and
remote-environment lifecycle here. That made this PR depend on
implementations in higher PRs and changed reusable sandbox behavior
globally. Those pieces are now deferred together to #12592.

**Roadmap alignment**

ROADMAP.md does not list a conflicting native-runner integration
project. This change adds the application boundary for the existing
Runner architecture.

## What Changed

- Added native run/result/finalization/provider-trace persistence,
shared validators, and idempotent migration/replay coverage.
- Added guarded Codex-only runtime selection, authenticated PRP
coordination, recovery, finalization, and interaction services.
- Added run/company-bound tool-gateway authorization, credential
redaction, SSRF protections, and replay-safe behavior.
- Added the explicit `paperclip_runner` adapter behind the default-off
rollout setting.
- Preserved legacy answered-question wake projection and direct-adapter
execution/finalization paths.
- Hardened cancellation so only owned in-memory child processes are
signaled; persisted recycled PIDs/process groups are never trusted.
- Retained the narrow Claude ACPX isolated-context security follow-up
discovered after #12590.
- Deferred the generalized executor, provider ingress, remote lifecycle,
SDK/lab/eval work, release-process changes, and lockfile.

## Verification

- Changed-file delta against `master`: 133 files.
- GitHub Actions is the authoritative verification environment for this
PR.
- Full CI, security, and Greptile review will run on this lowest
unmerged stack PR.
- Local tests/build/typecheck were not run because this checkout is
resource constrained.
- Static diff/reference checks pass, and `pnpm-lock.yaml` is unchanged.

## Risks

- This touches central heartbeat and agent-route code, so legacy
compatibility is the primary risk.
- Runtime selection remains Codex-only and explicit; direct Codex,
Claude, OpenCode, process, HTTP, and plugin adapters remain on their
existing paths.
- Fresh native starts fail closed while the rollout flag is off;
persisted native records remain readable and recoverable.
- Cancellation, company/run binding, tool calls, status decisions, and
completion writes are guarded or replay-safe.

> For core feature work, check [ROADMAP.md](ROADMAP.md) first and
discuss it in #dev before opening the PR. Feature PRs that overlap with
planned core work may need to be redirected.

## Model Used

OpenAI Codex, GPT-5.6, with repository tools, code execution, and
parallel agent review.

## 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 linked existing issues or described the issue in-PR
following the relevant issue template
- [x] I have not referenced internal or instance-local Paperclip issues
or links
- [x] My branch name describes the change and contains no internal
Paperclip ticket id
- [ ] I have run tests locally and they pass — GitHub Actions is
authoritative for this resource-constrained checkout
- [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 risks above
- [ ] All Paperclip CI and security gates are green
- [ ] Greptile is 5/5 with no open actionable findings
- [x] I will address all Greptile and reviewer comments before merge

## Stack

- Position: 3 of 5 overall; lowest of 3 currently unmerged
- Base: `master`
- Previous:
[#12590](https://github.com/paperclipai/paperclip/pull/12590), qualified
Claude ACPX runtime — merged
- Next: [#12592](https://github.com/paperclipai/paperclip/pull/12592),
generalized Codex executor, task experience, and developer SDKs

---------

Co-authored-by: Dev Agent <dev@paperclip.ing>
2026-08-31 14:38:38 -05:00

319 lines
11 KiB
TypeScript

import { describe, expect, it } from "vitest";
import type { NativeEvidenceAssessment } from "./evidence-classifier.js";
import { arbitrateNativeStatus } from "./status-arbiter.js";
function assessment(overrides: Partial<NativeEvidenceAssessment> = {}): NativeEvidenceAssessment {
return {
objectiveClaimSatisfied: true,
objectiveSatisfied: true,
allCriteriaSatisfied: true,
verificationPassed: true,
hasFailedVerification: false,
hasBlockingRemainingWork: false,
reportedDisposition: "done",
summary: "Complete",
contractRevisionMatches: true,
criterionAssessments: [],
verificationAssessments: [],
verificationCaveats: [],
acceptedEvidenceRefs: ["event:2"],
missingRequirements: [],
rejectedEvidence: [],
unverifiableEvidence: [],
blocker: null,
continuation: null,
attentionRequests: [],
ignoredAttentionRequests: [],
...overrides,
};
}
function arbitrate(overrides: Partial<Parameters<typeof arbitrateNativeStatus>[0]> = {}) {
return arbitrateNativeStatus({
assessment: assessment(),
terminalState: "succeeded",
workspaceFinalizeStatus: "succeeded",
agentId: "agent",
priorIssueStatus: "in_progress",
...overrides,
});
}
describe("native status authority", () => {
it("marks done only from successful finalization and complete durable evidence", () => {
expect(arbitrate()).toEqual(expect.objectContaining({
statusAction: "done",
toStatus: "done",
reasonCode: "completion_contract_satisfied",
effects: [{ kind: "release_checkout" }],
}));
expect(arbitrate({ completionClaimPolicyAccepted: true })).toEqual(expect.objectContaining({
statusAction: "done",
reasonCode: "completion_claim_policy_accepted",
}));
expect(arbitrate({
assessment: assessment({ verificationPassed: false, missingRequirements: ["test"] }),
})).toEqual(expect.objectContaining({
statusAction: "in_review",
toStatus: "in_review",
reasonCode: "external_verification_required",
effects: [expect.objectContaining({ kind: "bind_reviewer" })],
}));
const claimOnly = assessment({
objectiveSatisfied: false,
allCriteriaSatisfied: false,
verificationPassed: false,
criterionAssessments: [{
criterionId: "objective",
claimStatus: "satisfied",
outcome: "missing",
evidenceRefs: [],
reasonCode: "criterion_evidence_missing",
}],
verificationAssessments: [{
commandOrCheck: "Answered the question",
claimStatus: "passed",
outcome: "unverifiable",
evidenceRef: null,
reasonCode: "verification_has_no_durable_reference",
reportedReasonCode: null,
detail: null,
}],
acceptedEvidenceRefs: [],
missingRequirements: ["objective"],
});
expect(arbitrate({ assessment: claimOnly })).toEqual(expect.objectContaining({
toStatus: "in_review",
reasonCode: "external_verification_required",
}));
expect(arbitrate({ assessment: claimOnly, completionClaimPolicyAccepted: true })).toEqual(expect.objectContaining({
toStatus: "done",
reasonCode: "completion_claim_policy_accepted",
}));
});
it("creates explicit liveness paths for review, continuation, cancellation, and governance", () => {
expect(arbitrate({
assessment: assessment({ reportedDisposition: "needs_review" }),
})).toEqual(expect.objectContaining({
toStatus: "in_review",
effects: [expect.objectContaining({ kind: "bind_reviewer" })],
}));
expect(arbitrate({
assessment: assessment({
reportedDisposition: "yielded",
continuation: { kind: "retry", summary: "Retry the task", idempotencyKey: "retry-task" },
}),
})).toEqual(expect.objectContaining({
toStatus: "in_progress",
effects: [expect.objectContaining({ kind: "enqueue_continuation", continuationKind: "retry" })],
}));
expect(arbitrate({ terminalState: "cancelled" })).toEqual(expect.objectContaining({
toStatus: "in_progress",
effects: [expect.objectContaining({ kind: "release_run_resources" })],
}));
expect(arbitrate({ governanceGate: { kind: "interaction", id: "interaction" } })).toEqual(expect.objectContaining({
toStatus: "in_review",
reasonCode: "governed_gate_pending",
effects: [
{ kind: "create_interaction", gate: { kind: "interaction", id: "interaction" } },
{ kind: "notify_owner", agentId: "agent", reason: "governed_gate_pending" },
],
}));
expect(arbitrate({
governanceGate: { kind: "interaction", id: "interaction" },
assessment: assessment({
reportedDisposition: "yielded",
continuation: {
kind: "response_wake",
summary: "Resume from the response",
idempotencyKey: "interaction-response:interaction",
},
}),
})).toEqual(expect.objectContaining({
toStatus: "in_review",
reasonCode: "governed_response_waiting",
effects: [{ kind: "create_interaction", gate: { kind: "interaction", id: "interaction" } }],
}));
});
it("accepts low-risk completion claims with unrun verification caveats", () => {
const withCaveat = assessment({
verificationPassed: false,
criterionAssessments: [{
criterionId: "objective",
claimStatus: "satisfied",
outcome: "missing",
evidenceRefs: [],
reasonCode: "criterion_evidence_missing",
}],
verificationAssessments: [{
commandOrCheck: "Run npm test",
claimStatus: "not_run",
outcome: "missing",
evidenceRef: null,
reasonCode: "verification_not_run",
reportedReasonCode: "tool_unavailable",
detail: "Node and npm are unavailable in this environment.",
}],
verificationCaveats: [{
commandOrCheck: "Run npm test",
reasonCode: "tool_unavailable",
detail: "Node and npm are unavailable in this environment.",
}],
});
expect(arbitrate({ assessment: withCaveat, completionClaimPolicyAccepted: true }))
.toEqual(expect.objectContaining({
toStatus: "done",
reasonCode: "completion_claim_policy_accepted",
}));
});
it("sends failed verification and actionable attention to owned review without retrying", () => {
const failed = assessment({
verificationPassed: false,
hasFailedVerification: true,
verificationAssessments: [{
commandOrCheck: "Run npm test",
claimStatus: "failed",
outcome: "rejected",
evidenceRef: null,
reasonCode: "verification_reported_failed",
reportedReasonCode: null,
detail: "One test failed.",
}],
});
expect(arbitrate({
assessment: failed,
completionClaimPolicyAccepted: true,
reviewOwnerUserId: "user-1",
})).toEqual(expect.objectContaining({
toStatus: "in_review",
reasonCode: "completion_claim_conflict",
effects: [expect.objectContaining({ kind: "bind_reviewer", ownerUserId: "user-1" })],
}));
const withAttention = assessment({
attentionRequests: [{
kind: "approval",
summary: "Approve publication",
ownerClass: "human",
targetAgentId: null,
sourceIndex: 0,
sourceKind: "approval",
legacy: false,
}],
});
expect(arbitrate({ assessment: withAttention, completionClaimPolicyAccepted: true }))
.toEqual(expect.objectContaining({
toStatus: "in_review",
reasonCode: "actionable_attention_pending",
}));
});
it("blocks only for a task-wide blocker with a named owner and action", () => {
expect(arbitrate({
assessment: assessment({
reportedDisposition: "blocked",
blocker: { boardOwned: true, scope: "task_wide", unblockAction: "Approve access" },
}),
})).toEqual(expect.objectContaining({
toStatus: "blocked",
unblockDescriptor: { owner: "board", action: "Approve access" },
effects: [
{ kind: "bind_blocker", owner: "board", action: "Approve access" },
{ kind: "notify_owner", agentId: "agent", reason: "task_wide_blocker_bound" },
],
}));
});
it("blocks a current-track result when a durable dependency already gates the issue", () => {
expect(arbitrate({
assessment: assessment({
reportedDisposition: "blocked",
blocker: {
boardOwned: false,
scope: "current_track",
unblockAction: "Wait for child task DOT-52",
},
}),
hasUnresolvedIssueBlockers: true,
})).toEqual(expect.objectContaining({
statusAction: "blocked",
toStatus: "blocked",
reasonCode: "durable_dependency_blocker_bound",
unblockDescriptor: {
owner: { agentId: "agent" },
action: "Wait for child task DOT-52",
},
effects: [],
}));
});
it("lets a durable dependency override an optimistic done result", () => {
expect(arbitrate({
assessment: assessment({ reportedDisposition: "done", blocker: null }),
completionClaimPolicyAccepted: true,
hasUnresolvedIssueBlockers: true,
})).toEqual(expect.objectContaining({
statusAction: "blocked",
toStatus: "blocked",
reasonCode: "durable_dependency_blocker_bound",
effects: [],
}));
});
it("does not create a duplicate review after this run's review was already accepted", () => {
expect(arbitrate({
assessment: assessment({
reportedDisposition: "needs_review",
attentionRequests: [{
kind: "review",
summary: "Accept the plan",
ownerClass: "human",
targetAgentId: null,
sourceIndex: 0,
sourceKind: "review",
legacy: false,
}],
}),
governanceResolvedForRun: true,
})).toEqual(expect.objectContaining({
statusAction: "in_review",
reasonCode: "governance_response_continuation_queued",
effects: [],
}));
});
it("does not create a duplicate review after this run's planning confirmation was already accepted", () => {
expect(arbitrate({
assessment: assessment({
reportedDisposition: "blocked",
attentionRequests: [{
kind: "approval",
summary: "Accept the pinned plan revision.",
ownerClass: "human",
targetAgentId: null,
sourceIndex: 0,
sourceKind: "approval",
legacy: false,
}],
}),
governanceResolvedForRun: true,
})).toEqual(expect.objectContaining({
statusAction: "in_review",
reasonCode: "governance_response_continuation_queued",
effects: [],
}));
});
it("preserves authoritative terminal statuses", () => {
expect(arbitrate({ priorIssueStatus: "done" })).toEqual(expect.objectContaining({
toStatus: "done",
reasonCode: "terminal_status_preserved",
effects: [],
}));
});
});