Files
PaperClipAI/tests/runner-e2e/OPERATIONS-COVERAGE.md
T
DottaandPaperclip b6e690044a feat(evals): allow reviewed bounded provisional accounting (#17365)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Product E2E campaigns include model, setup, storage and
observability costs.
> - Some billing windows close after the tests and reports finish.
> - Requiring settled receipts for retained storage can block a good
campaign for weeks.
> - A reservation alone does not prove that a pending charge has an
upper bound.
> - This change permits provisional completion only with reviewed
evidence for the exact cost configuration.
> - Reports preserve unknown amounts and holds until later
reconciliation.

## Linked Issues or Issue Description

Refs: #17360, #17354

**What existing behavior does this improve?**

The Product E2E scheduled accounting gate and its retained cost reports.

**Current behavior**

Every pending receipt fails finalization. A completed test can wait
through the retained-storage billing window. The journal has dollar
reservations but no explicit qualification of their bounds.

**Proposed behavior**

Permit provisional completion when all pending charges have reviewed
full-lifecycle bounds, no requests remain in flight, no receipts
conflict, and known costs plus held amounts fit the campaign limit. Keep
all existing holds and receipt-finality rules. Missing evidence still
fails.

**Reason and benefit**

An operator can distinguish delayed but bounded billing from unbounded
or missing costs. The campaign can finish without claiming that its
invoice is settled.

**Breaking changes**

The journal accepts optional bound metadata and an invalidation record.
New reports add an optional bounded-pending count. Current readers
retain old journals and reports without granting them bounds. Older code
must not read new bound-bearing journals. The existing daily/weekly
enable gates and budget amounts stay unchanged.

**Additional context**

This implements the operator-approved provisional-cost policy for the
existing eval harness. It does not change product budgets or the
agent-eval roadmap. A targeted GitHub search found no duplicate open PR.

## What Changed

- Accounting owns a narrow evidence contract: a reviewed audit hash,
exact configuration hash and full-lifecycle scope.
- Gateway reservations retain that evidence before dispatch. Changed
limits or rates fail before dispatch. Unexpected routing, model identity
or excessive usage invalidates a bound durably.
- Operating allowances can carry separately reviewed bounds for each
cost component, including its full storage lifetime. No existing
allowance becomes qualified by default.
- Reports and Braintrust experiment metadata distinguish provisional
accounting, complete receipts, blocked accounting and unavailable
evidence. Public output omits private audit hashes.
- Scheduled finalization allows qualified provisional costs and
explicitly rejects spending overruns, including overruns with complete
receipts.
- Add regressions for legacy data, restarts, late reconciliation,
conflicts, malformed evidence, routing uncertainty and exceeded limits.
Document the evidence and migration boundaries.

## Verification

- Full offline eval-support suite: 4,062 Vitest tests passed, two
existing tests skipped; 137 Node tests passed.
- `pnpm test:e2e:runner:typecheck`: passed.
- `git diff --check`: passed.
- Read the retained hosted qualification journal with the new policy.
All six legacy pending charges remain unqualified. Known cost
`$0.421245200`, held `$19.327680000`, and committed `$19.748925200`
remain unchanged. The journal's SHA-256 is unchanged. The original
failed workflow remains failed.
- No paid models, network provider calls, receipt imports or reservation
releases were used for this verification.
- Rebased onto master `4d90634f74`; 142 targeted tests and typecheck
passed.
- Review reproduced a lost OpenAI input-total limit check when cache
details were missing (1 failed, 47 passed). Fix
`5fef9ba7c149951a51eae468614a4b89f0b3d6db` preserves unpriced provider
usage bounds. Then 178 focused tests, full offline support and typecheck
passed.
- Current-head hosted CI passed:
https://github.com/paperclipai/paperclip/actions/runs/40020746585
(attempt 2). Typecheck, build, all general/Runner/serialized tests and
browser gates are green.
- CI attempt 1 hit a PostgreSQL deadlock in unchanged chat-test cleanup;
its skipped flag restoration caused a second assertion failure. One
failed-jobs-only retry passed. Original failure evidence is preserved in
attempt 1. No paid eval campaign was rerun.
- Greptile reviewed exact head
`5fef9ba7c149951a51eae468614a4b89f0b3d6db` at 5/5; its one finding is
fixed and resolved. The optional Paperclip review is paused and supplied
no assessment.

## Risks

- Evidence hashes bind an audit to a configuration; they do not prove
the audit is true. Operators must review real provider limits, all price
tiers, full resource lifetimes and enforceable infrastructure limits
before configuring bounds.
- Provisional completion is not invoice settlement. Later billing can
reveal overruns, which must be reconciled and inspected. The ledger
cannot stop external billing.
- Existing pilot journals, configured allowances and price cards have no
new bound approval. This PR alone does not qualify or enable recurring
paid work.
- The gateway cannot qualify a canceled Braintrust request without route
confirmation. It preserves that unknown hold.
- No new dependency, secret, repository variable, workflow, schedule or
cloud resource changes.

## Model Used

OpenAI Codex, GPT-6 family. The exact deployment ID and context-window
size are not exposed in this session. Used reasoning, repository tools
and code execution. No paid evaluation model calls were made for this
change.

## 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 13:57:57 -07:00

236 lines
15 KiB
Markdown

# Eval coverage and operating costs
Use this with [OPERATIONS.md](OPERATIONS.md). The bounded daily and weekly
selections cover Product E2E only. They do not authorize or cap the separate
Runner protocol workflow, manual matrix runs, or other live probes.
## Initial cadence
The runner E2E maintainers own these selections and their failure triage.
Both profiles use the exact model and limits in the reviewed pricecard.
| Journey | Failure detected | Feature-map association | Cadence |
| --- | --- | --- | --- |
| Native Claude message marker | A real task cannot complete and persist its expected answer through the metered route | `runs-adapters/runs` | Daily and weekly |
| Native Claude complaint and suggestion | Completed tools fail to save attributed feedback or collection loses it | `activity/agent-commentary` | Daily and weekly |
| Native Codex message marker | The corresponding Codex task/connection path fails | `runs-adapters/runs` | Weekly |
| Native Codex complaint and suggestion | The corresponding Codex feedback/tool path fails | `activity/agent-commentary` | Weekly |
These associations identify partial coverage. They do not qualify every run
lifecycle, the Commentary browser page, continuation, or remote execution.
The October 21 local pilot measured one successful sample per selected cell:
$0.427624800 and 72.757 summed attempt seconds for the daily selection,
$0.791539400 and 127.069 seconds for the weekly selection. These are historical
gateway estimates from the Linux ARM64 container. They include model setup
requests and attempt setup/teardown, but omit queue/build time and non-model
charges. Earlier failed and interrupted attempts remain in campaign spending.
`scheduled-measurements.json` retains the exact attempt, source revision,
definition, model, price version, result hash and journal hashes for each sample.
`forecast.json` includes this evidence and a snapshot hash. It separates the
historical successful selection from samples matching the current definition,
model, environment, runtime, route and pricing. A missing matching cell makes
the compatible selection total unknown. The feedback definition changed after
these four samples; its earlier results are not current qualification.
Per-cell mean and median are descriptive. P95 stays unknown below 20 samples
and then uses nearest rank; this is not a statistical confidence guarantee.
Do not sum per-cell percentiles into a campaign percentile. Hosted cost/time
and the selection's P95 remain unknown until separately measured. A passing
sample does not predict failure frequency or account for infrastructure,
storage, observability or subscriptions. Forecasts never reduce reservations
or the approved budget limits.
At the October 21, 2026 catalog audit, 47 suites declared 738 cells. `--all`
included 179; the other 559 required explicit selection. The bounded weekly
selection included four cells, and daily two. These are dated inventory counts,
not live acceptance results or an enduring catalog-size contract. Inspect the
current `pnpm test:e2e:runner -- --list` before choosing work. In particular,
Everyday Workflows, provider connections, feedback persistence, interruption,
and case handoffs must not be inferred from a passing `--all` campaign.
Keep the broader rotating lane disabled until its exact cells have a metered
route, complete retained attempts, a measured cost/time distribution, and a
budget that includes failures. Prefer targeted continuation, approval, and
cleanup cases that detect different failures over another model running the
same marker. A release lane may broaden coverage only with its own reviewed
manifest; the daily/weekly allowance does not authorize it.
## Adding an eval or changing its cadence
The change's fixture documentation and PR must declare:
- A responsible maintainer/team and the concrete failure the case detects.
- Feature-map capability IDs, or an explicit unmapped gap. State what the
journey does not exercise within a broader capability.
- Exact suite, case, profiles, models, environment, and intended cadence.
State whether it is explicit-only and how it differs from existing coverage.
- A deterministic alternative considered, and why paid behavior is necessary.
- Attempt, request, output-token, concurrency, retry, and wall-time limits.
Include setup, judges, child/background work, and cleanup in the route audit.
- Estimated cost/time before the first run, then all-attempt measurements with
sample count, retained failures, source/definition IDs, and uncertainty.
An unmeasured field is unknown, never zero.
- Feedback collection/review, trace verification, missing-receipt handling,
and a failure/cleanup owner. Novel complaints are reviewed independently
of the product grade.
Use the existing [Product E2E authoring skill](../../.agents/skills/add-product-e2e-eval/SKILL.md)
or the separate [Runner authoring skill](../../.agents/skills/add-runner-eval/SKILL.md).
Update the existing scheduled selection and forecast only after qualification;
adding a catalog entry alone must not enable recurring spending.
## Reading campaign time
The report's summed attempt time uses every retained accounting receipt,
including failed retries. A missing attempt makes the total unknown; the report
keeps the observed subtotal and missing count. The observed attempt window runs
from the earliest retained start to the latest retained finish. Parallel attempts
overlap in that window, while their individual durations add to the sum. Gaps
between attempts remain in the window. Carried-forward evidence can span older
runs, so this window is not the current workflow's duration.
Agent execution and Daytona lease time remain separate measurements. Attempt
durations include the setup and cleanup inside their recorded boundaries.
New browser-worker results retain a separate phase receipt for fixture setup,
execution, feedback capture, cleanup and worker finalization. Repeated cleanup
segments add together. Atomic checkpoints preserve completed phases if the
worker is interrupted; the active phase has no inferred end time. Parent
recovery checks identity, interval bounds and file size before retaining that
partial receipt. Missing, malformed or unavailable receipts keep phase totals
unknown, while closed segments remain visible as observed subtotals.
These phases start inside the test callback, after browser/server startup, and
end before result publication. Parent teardown/finalization, CI queue/build time
and provider-billed runtime need their own receipts. Historical results and
non-browser launchers without a worker receipt remain explicitly unmeasured.
Test details show each selected attempt's receipt status and raw timing metadata.
Do not infer missing measurements by subtracting summed agent time from the
observed window. Refreshing a report computes these views from retained
results without making provider calls or changing the original measurements.
## Costs outside the model journal
Keep a separate cost component for each resource, with its currency, service,
resource/request identity, source window, allocation basis, and status. Join
components to campaign/attempt IDs where evidence permits. Do not sum a model
estimate with the invoice that later reconciles that same usage.
| Component | Evidence and allocation | When evidence is missing |
| --- | --- | --- |
| Provider models and paid tools | Dedicated project/key usage and billing exports; match provider request IDs or the narrowest available billing window | Preserve estimates, unknown requests, held reservations and the reconciliation difference |
| Daytona | Owned sandbox ID, actual resources, billable lifecycle and provider receipt; include failed creates, retries, stopped storage and delayed deletion | Retain the reservation or an explicitly partial estimate; a requested TTL does not prove deletion |
| CI compute | Workflow/job IDs, runner type, actual billed runtime and invoice rates; include setup, builds and failed jobs | Mark CI cost unknown; a short agent turn does not imply a cheap CI job |
| Storage and transfer | Artifact size, retention window and billed storage/egress | Report the unpriced component, including retained failure evidence |
| Braintrust/gateway usage | Project usage export and its window; distinguish included allowance from incremental overage | Do not treat free-looking trace metrics as proof of zero provider charges |
| Shared subscriptions | Show the existing Braintrust Pro $249/month separately under the October 21 operator policy; allocate newly incurred subscription/capacity charges from invoices with an explicit denominator | Keep new shared charges unallocated rather than assign an invented exact per-test cost |
Retain both the service total and the allocation denominator so allocated
amounts can be reproduced. A zero incremental usage charge can coexist with a
nonzero shared subscription. Never add the entire subscription to every test.
The approved daily/weekly budgets cover additional run costs, so they exclude
the existing $249/month fee while including new usage and capacity charges.
Scheduled admission now reserves the declared CI, storage, observability and
subscription allowances inside the same $20/$100 journal limit. Missing or stale
allowances stop scheduling before the AWS test runner starts. Missing receipts
keep their reservations held. Explicit reviewed full-lifecycle bounds may allow
provisional completion while billing remains pending; missing bounds, conflicts
and overspending still fail. Legacy reservations gain no qualification from the
policy change. This is a local admission
control, not a provider billing cap. Actual resource bounds, cost collection and
reviewed allowances still need qualification before recurrence is enabled; see
[the allowance contract](OPERATIONS.md#recurring-campaigns).
Recurring evidence and reports use private S3 archives with file/byte/request
boundaries, retained transfer receipts and verified access/expiration rules;
see [private recurring history](OPERATIONS.md#private-recurring-history). The
public workflow retains only a content-free availability marker for monitoring.
Public history is a separate explicit publication step. Private retention still
needs hosted OIDC qualification and actual storage/transfer billing evidence;
local archive tests and an infrastructure template do not establish those facts.
## Separate Runner Eval audit
The October 21 audit inspected Paperclip source on master `8b873e0112` plus the
eval operations stack, and the available `paperclip-evals` checkout at
`0bae4e0947dc7959cae25b73d27533c096f23a14`. This is a source audit, not a
new live run or proof of the workflow's configured private-repository revision.
Runner reports already retain immutable attempts, retry-inclusive estimated
cost and provider list cost as separate views, compatible-cohort comparisons,
and missing-usage coverage. Its mock control plane is a different authority
from the Product E2E server/database; Product feedback persistence cannot be
claimed from a Runner tool assertion.
The private `run_live_campaign.py` requires an explicit authorization envelope.
The hosted `runner-protocol-live-evals.yml` instead fans out individual
`run_live_roster.py` cells, supplies their direct provider credentials, and uses
configured per-attempt limits. It does not use the Product E2E shared gateway
journal, Braintrust attempt parent, indexing verifier, or feedback finalizer.
The private campaign wrapper's aggregate authorization must not be assumed to
protect that different hosted path.
Before adding Runner recurrence, its owners must establish an aggregate
allowance across fanout and retries, meter setup/provider bypasses, and qualify
native tracing through its actual packaged runner. Preserve direct-provider
qualification as its own route. Implement definition/roster changes in the
maintained private repository, pin that reviewed revision in the existing
workflow, and retain Runner's existing Evalbook renderer and scores. These
gaps remain open; Product E2E qualification does not close them.
## Missing campaign detection
Workflow failure notifications cover runs that start. The independent observer
is `scripts/eval-schedule-monitor.mjs`. Run it through a separate host with an
authenticated GitHub CLI and one private state file:
```sh
node scripts/eval-schedule-monitor.mjs "$HOME/.config/paperclip-evals/schedule-monitor.json"
```
The observer uses only GitHub GET requests. It never starts or retries paid
work and does not need provider credentials. It reads the two existing enable
gates plus `RUNNER_FULL_STACK_E2E_OPERATIONS_ENABLED_AT`, an ISO timestamp set
when recurrence is enabled. Missing activation time fails visibly; occurrences
before activation are not treated as missed. Workflow run titles carry the
literal cron expression to distinguish daily and weekly runs, including Sunday.
Only the legacy `RUNNER_FULL_STACK_E2E_NIGHTLY_ENABLED` gate being off establishes
disabled recurrence. If that gate is on while the operations gate is absent or
off, the observer reports `configuration_incomplete` and retains one incident
notification. Older deployed workflows use only the legacy gate, so a missing
new gate must not imply that those paid schedules are disabled. Verify the
deployed default-branch workflow and both variables before enabling recurrence.
The observer allows 90 minutes for schedule delay. It checks the most recent due
occurrence per cadence, marks a run overdue after four hours, and requires a
successful terminal run plus a nonempty, unexpired merged-report artifact.
The workflow's finalizer remains responsible for checking report content and
accounting. Metadata inspection does not repeat that validation. Unexpected
reruns, ambiguous matching runs, incomplete API lists and API failures remain
visible. The observer reads at most 100 rows per collection and fails closed
if a response is truncated.
Retained state includes the last successful inspection and inspected run IDs.
`notifications` is a durable outbox of problems and recovery transitions. Each
entry has an ID. After confirmed delivery, the host acknowledges those IDs:
```sh
node scripts/eval-schedule-monitor.mjs "$HOME/.config/paperclip-evals/schedule-monitor.json" --ack <notification-id> ...
```
Until acknowledgment, later inspections return the same pending entries even
if the problem has recovered. The host uses IDs to deduplicate delivery. This
survives a process exit between saving state and printing output; it does not
claim exactly-once delivery across an external notification service. After
acknowledgment, an unchanged problem stays quiet. A new missed occurrence is a
new incident. Disabled recurrence and unchanged healthy runs create no new
alerts. Use one writer for both checks and acknowledgments; do not reset the
state file to clear an incident. Malformed history fails before a query or
write. The external host owns notification delivery and its own liveness.
Schedule this observer outside the paid workflow after source and recurrence
are ready. It should inspect at least daily, after both scheduled start times
and their grace windows. Until that external trigger and notification delivery
are configured and exercised, missed-run detection remains an activation gap.