Files
PaperClipAI/packages/paperclip-runner/docs/evals-integration.md
T
DottaandPaperclip a3e1d915a7 fix(evals): preserve unknown Codex provider cost (#17158)
## Thinking Path

> - Paperclip manages AI agents and their work.
> - Direct Runner evals report usage and costs for each attempt.
> - Codex App Server supplies token counts without a provider USD
receipt.
> - The legacy Runner ledger fills an absent cost with zero.
> - The eval result exposed that ledger value as provider-reported cost.
> - This fix keeps provider cost unknown and preserves the estimate and
budget stop.

## Linked Issues or Issue Description

Refs #17009 and #17154. This is a separate direct Runner accounting fix
based on master.

## What Changed

- Codex direct eval usage now reports null provider USD cost with
unavailable provenance. Token counts and the pinned price estimate
remain available.
- Preserve the original ledger amount as optional
`legacyLedgerCostNanodollars`. Keep the original snapshot receipts
unchanged.
- Apply the existing budget ceiling to the retained ledger amount as
well as the token estimate. Correcting a label does not relax admission
or settlement checks.
- Use the unique operator UUID for Muse test company prefixes,
eliminating collisions from the previous 24-bit random suffix.
- Document the cost distinction in the Runner/Evals integration
contract. Other provider behavior and historical artifacts remain
unchanged.

## Verification

- Reproduced both zero and nonzero legacy amounts being labelled as
provider USD cost before the fix.
- The current eval-session contract and entrypoint tests pass (39
tests). The new regression uses the bounded usage consumer and actual
campaign cost projection. It checks unknown provider cost, the separate
estimate, unchanged raw evidence and the legacy ledger budget stop.
- All 15 Runner report, campaign and metrics Node tests pass. Four
adjacent CLI entrypoint/runtime tests also pass.
- Runner TypeScript validation passes with its standard dependency-build
prerequisite; `git diff --check` passes. Both Runner TypeScript
configurations also passed during the original accounting verification.
- Hosted CI run
[39960367674](https://github.com/paperclipai/paperclip/actions/runs/39960367674)
passes on `7d84f138044bac58cdced6baa2d0ec3d63767bb8`, including
typecheck, build, server tests and browser checks. Greptile and
Paperclip Review both report 5/5 on that commit with no actionable
findings.
- The previous CI failure was a PostgreSQL unique-index collision on
`companies_issue_prefix_idx` during Muse fixture setup. Local Muse
verification could not reach test execution because embedded PostgreSQL
timed out during macOS startup (initially reporting a missing zstd
library). The corrected fixture passed in hosted Linux CI.
- Verification used no model calls. Full repository checks were
performed by hosted CI; local verification was scoped to the changed
contracts and fixture.

## Risks

Consumers that assumed Codex always had a numeric provider cost must
handle null. The contract already permits null for other profiles, and
the report consumer retains missing-cost coverage. The legacy amount
remains available as unverified evidence and still triggers the existing
budget stop. Neither the estimate nor that ledger amount proves an
invoice charge. This change does not rewrite old artifacts or qualify
other provider routes.

## Model Used

OpenAI GPT-6 through Codex, with repository tools and code execution.
The exact deployment variant and context-window size are not exposed by
this session.

## 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
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-10-23 06:36:34 -07:00

120 lines
5.6 KiB
Markdown

# Paperclip Evals Integration Contract
## Stable consumer inputs
Paperclip Evals consumes two explicit App artifacts:
1. a packed/released `@paperclipai/paperclip-runner` package; and
2. an explicit `paperclip-runnerd` executable path plus its
`sha256:<lowercase hex>` digest.
The consumer must not import App source paths, search a workspace for a binary,
or infer compatibility from a process failure. The package exposes three
relevant entry points:
- package root: PRP, runner, semantic-tool, and control-plane runtime contracts;
- `./evals`: build metadata, native-attempt schema/fixture, compatibility
negotiation, semantic receipts/catalog, and explicit runnerd resolution;
- `./testing`: deterministic driver/control-plane fakes and conformance kits.
`resolvePaperclipRunnerdArtifact` resolves only the supplied path, verifies its
bytes against the supplied digest, and invokes that exact executable with
`--build-metadata`. It never searches `PATH` or the App repository.
## Versioned join
`assertPaperclipRunnerEvalCompatibility` checks the complete join before any
provider process starts:
| Dimension | V1 rule |
| --- | --- |
| Package | Exact package version matches loaded build metadata. |
| Binary | runnerd package version and binary-artifact contract match the package. |
| PRP | Package, runnerd, and consumer version ranges have a common version. |
| Semantic catalog | Contract version and canonical content digest both match. |
| Harness driver | Contract and negotiated PRP versions match and every required capability is explicitly true. |
| Native output | Consumer and runnerd both select `paperclip-runner/native-execution/v1`. |
Failures use `paperclip_runner_eval_incompatible` and include all detected
component issues with expected and received values. A catalog, driver, or PRP
mismatch cannot degrade into an attempted run.
## Native attempt bundle
`paperclip-runner/native-execution/v1` is the App-owned raw attempt. Its checked
schema is `protocol/schemas/native-execution.schema.json`; the no-spend seeded
fixture is `protocol/fixtures/evals/native-execution-seeded.json`.
The bundle pins run/case/config/attempt identity, case and config digests,
package/binary/catalog/driver versions and digests, ordered PRP events, semantic
tool definitions/calls/results/denials, terminal state, check-ready
observations, usage/cost/time/request totals, transcript completeness, and
content-addressed artifacts. Unknown additive fields survive parsing.
The parser fails closed on an unknown native schema, malformed digest, event
run mismatch, missing or conflicting terminal, semantic indexes that omit or
reinterpret their PRP tool envelopes, denied results without matching denial
receipts, inconsistent token totals, or ambiguous incomplete transcripts.
Unknown additive fields are retained at nested contract objects as well as the
bundle root. The seeded fixture deliberately preserves a rejected governed
tool effect as `denied`; it never turns that effect into a successful mutation.
Evals owns conversion of this bundle into Evalbook ledger, scores, and reports.
The App package does not implement an Evalbook environment loader, importer,
grid, comparison, or report renderer.
## Direct eval cost provenance
Codex App Server supplies token usage, not a provider USD receipt. Its direct
`eval-session` result therefore sets `providerReportedCostNanodollars` to null
and `providerReportedCostProvenance` to `unavailable`. The pinned token-price
estimate remains in `estimatedCostNanodollars` with `costCoverage: estimated`.
A missing receipt must not appear as a free request.
`legacyLedgerCostNanodollars` retains the unverified ledger amount for Codex.
This amount and the token estimate still participate in the existing budget
stop. The ledger amount is not provider billing, and consumers must not add it
to the estimate or treat its zero placeholder as proof of zero spend. Raw
usage receipts remain in the snapshot. Older artifacts remain their original
measurements; this change does not reconcile invoices or reprice history.
## Deterministic conformance
`runHarnessDriverConformance` with `DeterministicHarnessDriver` covers, without
network or provider credentials:
- capability and unsupported-feature description;
- valid/invalid config handling;
- session open, turn, snapshot, recovery, close, and cancel lifecycle;
- paired provider-neutral semantic tool events;
- PRP event validation;
- interrupt and cancelled terminal behavior;
- usage reporting; and
- complete transcript accounting.
The input fixture is
`protocol/fixtures/evals/harness-driver-conformance.json`. The packed
clean-consumer gate builds runnerd with Cargo's `release` profile, copies it
into an isolated artifact directory, packs the package, installs it offline,
resolves only declared exports, validates the native fixture, runs driver
conformance, verifies binary metadata/digest, and exercises both compatible
and incompatible negotiation. It never qualifies a debug binary.
Run it with:
```sh
pnpm --filter @paperclipai/paperclip-runner check:clean-consumers
```
Set `PAPERCLIP_CLEAN_CONSUMER_OUTPUT_DIR` to retain the qualifying inputs and
machine-readable proof outside the temporary consumer. The output contains the
package tarball, platform-named release runnerd, `SHA256SUMS`, and
`paperclip-runner-consumer-conformance.json`. That record is produced by the clean
consumer after it imports only the packed package, resolves the explicit binary
and digest, and completes deterministic conformance without provider calls.
```sh
PAPERCLIP_CLEAN_CONSUMER_OUTPUT_DIR=/absolute/release/directory \
pnpm --filter @paperclipai/paperclip-runner check:clean-consumers
```